v2 / vlib / math / unsigned / uint128.v
491 lines · 420 sloc · 10.77 KB · ee9eefd81b028a81920d2965fee52311a1c94d3b
Raw
1module unsigned
2
3import math.bits
4import encoding.binary
5
6pub struct Uint128 {
7pub mut:
8 lo u64
9 hi u64
10}
11
12pub const uint128_zero = Uint128{}
13pub const uint128_max = Uint128{18446744073709551615, 18446744073709551615}
14
15// is_zero returns true if u == 0
16pub fn (u Uint128) is_zero() bool {
17 return u == Uint128{}
18}
19
20// equals returns true if u == v.
21//
22// Uint128 values can be compared directly with ==, but use of the Equals method
23// is preferred for consistency.
24pub fn (u Uint128) equals(v Uint128) bool {
25 return u == v
26}
27
28// equals_64 returns true if u == v
29pub fn (u Uint128) equals_64(v u64) bool {
30 return u.lo == v && u.hi == 0
31}
32
33// cmp compares u and v and returns:
34//
35// -1 if u < v
36// 0 if u == v
37// +1 if u > v
38//
39pub fn (u Uint128) cmp(v Uint128) int {
40 if u == v {
41 return 0
42 } else if u.hi < v.hi || (u.hi == v.hi && u.lo < v.lo) {
43 return -1
44 } else {
45 return 1
46 }
47}
48
49// cmp_64 compares u and v and returns:
50//
51// -1 if u < v
52// 0 if u == v
53// +1 if u > v
54//
55pub fn (u Uint128) cmp_64(v u64) int {
56 if u.hi == 0 && u.lo == v {
57 return 0
58 } else if u.hi == 0 && u.lo < v {
59 return -1
60 } else {
61 return 1
62 }
63}
64
65// and returns u & v
66pub fn (u Uint128) and(v Uint128) Uint128 {
67 return Uint128{u.lo & v.lo, u.hi & v.hi}
68}
69
70// and_64 returns u & v
71pub fn (u Uint128) and_64(v u64) Uint128 {
72 return Uint128{u.lo & v, u.hi & 0}
73}
74
75// or returns u | v
76pub fn (u Uint128) or_(v Uint128) Uint128 {
77 return Uint128{u.lo | v.lo, u.hi | v.hi}
78}
79
80// or returns u | v
81pub fn (u Uint128) or_64(v u64) Uint128 {
82 return Uint128{u.lo | v, u.hi | 0}
83}
84
85// xor returns u ^ v
86pub fn (u Uint128) xor(v Uint128) Uint128 {
87 return Uint128{u.lo ^ v.lo, u.hi ^ v.hi}
88}
89
90// xor_64 returns u ^ v
91pub fn (u Uint128) xor_64(v u64) Uint128 {
92 return Uint128{u.lo ^ v, u.hi ^ 0}
93}
94
95// add returns u + v with wraparound semantics
96pub fn (u Uint128) add(v Uint128) Uint128 {
97 lo, carry := bits.add_64(u.lo, v.lo, 0)
98 hi, _ := bits.add_64(u.hi, v.hi, carry)
99 return Uint128{lo, hi}
100}
101
102// add_128 return u + v and the carry
103pub fn add_128(x Uint128, y Uint128, carry u64) (Uint128, u64) {
104 mut sum := Uint128{}
105 mut carry_out := u64(0)
106 sum.lo, carry_out = bits.add_64(x.lo, y.lo, carry)
107 sum.hi, carry_out = bits.add_64(x.hi, y.hi, carry_out)
108 return sum, carry_out
109}
110
111// sub_128 returns u - v and the borrow
112pub fn sub_128(x Uint128, y Uint128, borrow u64) (Uint128, u64) {
113 mut diff := Uint128{}
114 mut borrow_out := u64(0)
115 diff.lo, borrow_out = bits.sub_64(x.lo, y.lo, borrow)
116 diff.hi, borrow_out = bits.sub_64(x.hi, y.hi, borrow_out)
117 return diff, borrow_out
118}
119
120// mul_128 returns u x v
121pub fn mul_128(x Uint128, y Uint128) (Uint128, Uint128) {
122 mut lo := Uint128{}
123 mut hi := Uint128{}
124 lo.hi, lo.lo = bits.mul_64(x.lo, y.lo)
125 hi.hi, hi.lo = bits.mul_64(x.hi, y.hi)
126 t0, t1 := bits.mul_64(x.lo, y.hi)
127 t2, t3 := bits.mul_64(x.hi, y.lo)
128
129 mut c0 := u64(0)
130 mut c1 := u64(0)
131 lo.hi, c0 = bits.add_64(lo.hi, t1, 0)
132 lo.hi, c1 = bits.add_64(lo.hi, t3, 0)
133 hi.lo, c0 = bits.add_64(hi.lo, t0, c0)
134 hi.lo, c1 = bits.add_64(hi.lo, t2, c1)
135 hi.hi += c0 + c1
136 return hi, lo
137}
138
139// div_128 returns u / v
140pub fn div_128(a_hi Uint128, a_lo Uint128, b Uint128) (Uint128, Uint128) {
141 a := uint256_new(a_lo, a_hi)
142 b_256 := uint256_new(b, uint128_zero)
143 if a < b_256 {
144 return uint128_zero, a_lo
145 }
146 if b.is_zero() {
147 panic('Division by zero')
148 }
149 if a.hi.is_zero() {
150 return a.lo.quo_rem(b)
151 }
152 shift := u32(b_256.leading_zeros() - a.leading_zeros())
153 mut af := a
154 mut bf := b_256.lsh(shift)
155 mut quotient := uint128_zero
156
157 for _ in 0 .. shift + 1 {
158 quotient = quotient.lsh(1)
159 diff_result := bf.add(af.not())
160 s := u64(i64(diff_result.hi.hi) >> 63)
161 quotient.lo |= s & 1
162 mask := uint256_new(uint128_new(s, s), uint128_new(s, s))
163 af = af.sub(bf.and(mask))
164 bf = bf.rsh(1)
165 }
166 return quotient, af.lo
167}
168
169// add_64 returns u + v with wraparound semantics
170pub fn (u Uint128) add_64(v u64) Uint128 {
171 lo, carry := bits.add_64(u.lo, v, 0)
172 hi := u.hi + carry
173 return Uint128{lo, hi}
174}
175
176// sub returns u - v with wraparound semantics
177pub fn (u Uint128) sub(v Uint128) Uint128 {
178 lo, borrow := bits.sub_64(u.lo, v.lo, 0)
179 hi, _ := bits.sub_64(u.hi, v.hi, borrow)
180 return Uint128{lo, hi}
181}
182
183// sub_64 returns u - v with wraparound semantics
184pub fn (u Uint128) sub_64(v u64) Uint128 {
185 lo, borrow := bits.sub_64(u.lo, v, 0)
186 hi := u.hi - borrow
187 return Uint128{lo, hi}
188}
189
190// mul returns u * v with wraparound semantics
191pub fn (u Uint128) mul(v Uint128) Uint128 {
192 mut hi, lo := bits.mul_64(u.lo, v.lo)
193 hi += u.hi * v.lo + u.lo * v.hi
194 return Uint128{lo, hi}
195}
196
197// mul_64 returns u * v with wraparound semantics
198pub fn (u Uint128) mul_64(v u64) Uint128 {
199 mut hi, lo := bits.mul_64(u.lo, v)
200 hi += u.hi * v
201 return Uint128{lo, hi}
202}
203
204// overflowing_mul_64 returns u x v even if result size > 128
205pub fn (u Uint128) overflowing_mul_64(v u64) (Uint128, bool) {
206 hi, lo := bits.mul_64(u.lo, v)
207 p0, p1 := bits.mul_64(u.hi, v)
208 hi2, c0 := bits.add_64(hi, p1, 0)
209
210 return Uint128{lo, hi2}, p0 != 0 || c0 != 0
211}
212
213// overflowing_add_64 returns u + v even if result size > 128
214pub fn (u Uint128) overflowing_add_64(v u64) (Uint128, u64) {
215 lo, carry := bits.add_64(u.lo, v, 0)
216 hi, carry2 := bits.add_64(u.hi, 0, carry)
217
218 return Uint128{lo, hi}, carry2
219}
220
221// div returns u / v
222pub fn (u Uint128) div(v Uint128) Uint128 {
223 q, _ := u.quo_rem(v)
224 return q
225}
226
227// mod returns r = u % v
228pub fn (u Uint128) mod(v Uint128) Uint128 {
229 _, r := u.quo_rem(v)
230 return r
231}
232
233// mod_64 returns r = u % v
234pub fn (u Uint128) mod_64(v u64) u64 {
235 _, r := u.quo_rem_64(v)
236 return r
237}
238
239// quo_rem_64 returns q = u/v and r = u%v
240pub fn (u Uint128) quo_rem_64(v u64) (Uint128, u64) {
241 if u.hi < v {
242 mut r := u64(0)
243 mut q := Uint128{0, 0}
244 q.lo, r = bits.div_64(u.hi, u.lo, v)
245
246 return q, r
247 } else {
248 mut q := Uint128{0, 0}
249 mut r := u64(0)
250 mut r2 := u64(0)
251 q.hi, r = bits.div_64(0, u.hi, v)
252 q.lo, r2 = bits.div_64(r, u.lo, v)
253 return q, r2
254 }
255}
256
257// quo_rem returns q = u/v and r = u%v
258pub fn (a Uint128) quo_rem(b Uint128) (Uint128, Uint128) {
259 if a < b {
260 return uint128_zero, a
261 }
262 if b.is_zero() {
263 panic('Division by zero')
264 }
265 if b.hi == 0 {
266 quotient, remainder := a.quo_rem_64(b.lo)
267 return quotient, uint128_from_64(remainder)
268 }
269 shift := u32(b.leading_zeros() - a.leading_zeros())
270 mut af := a
271 mut bf := b.lsh(shift)
272 mut quotient := uint128_zero
273
274 for _ in 0 .. shift + 1 {
275 quotient = quotient.lsh(1)
276 diff_result := bf.add(af.not())
277 s := u64(i64(diff_result.hi) >> 63)
278 quotient.lo |= s & 1
279 mask := uint128_new(s, s)
280 af = af.sub(bf.and(mask))
281 bf = bf.rsh(1)
282 }
283 return quotient, af
284}
285
286// lsh returns u << n
287pub fn (u Uint128) lsh(n u32) Uint128 {
288 mut s := Uint128{}
289 if n == 0 {
290 s.lo = u.lo
291 s.hi = u.hi
292 } else if n >= 128 {
293 s.lo = 0
294 s.hi = 0
295 } else if n == 64 {
296 s.lo = 0
297 s.hi = u.lo
298 } else if n > 64 {
299 s.lo = 0
300 s.hi = u.lo << (n - 64)
301 } else {
302 s.lo = u.lo << n
303 s.hi = u.hi << n | u.lo >> (64 - n)
304 }
305
306 return s
307}
308
309// rsh returns u >> n
310pub fn (u Uint128) rsh(n u32) Uint128 {
311 mut s := Uint128{}
312 if n == 0 {
313 s.lo = u.lo
314 s.hi = u.hi
315 } else if n >= 128 {
316 s.lo = 0
317 s.hi = 0
318 } else if n == 64 {
319 s.hi = 0
320 s.lo = u.hi
321 } else if n > 64 {
322 s.hi = 0
323 s.lo = u.hi >> (n - 64)
324 } else {
325 s.lo = u.lo >> n | u.hi << (64 - n)
326 s.hi = u.hi >> n
327 }
328
329 return s
330}
331
332// leading_zeros returns the number of leading zero bits in u; the result is 128
333// for u == 0.
334pub fn (u Uint128) leading_zeros() int {
335 if u.hi > 0 {
336 return bits.leading_zeros_64(u.hi)
337 }
338 return 64 + bits.leading_zeros_64(u.lo)
339}
340
341// trailing_zeros returns the number of trailing zero bits in u; the result is
342// 128 for u == 0.
343pub fn (u Uint128) trailing_zeros() int {
344 if u.lo > 0 {
345 return bits.trailing_zeros_64(u.lo)
346 }
347 return 64 + bits.trailing_zeros_64(u.hi)
348}
349
350// ones_count returns the number of one bits ("population count" in u)
351pub fn (u Uint128) ones_count() int {
352 return bits.ones_count_64(u.hi) + bits.ones_count_64(u.lo)
353}
354
355// rotate_left returns the value of u rotated left by (k mod 128) bits.
356pub fn (u Uint128) rotate_left(k int) Uint128 {
357 n := u32(128)
358 s := u32(k) & (n - 1)
359 return u.lsh(s).or_(u.rsh(n - s))
360}
361
362// rotate_right returns the value of u rotated right by (k mod 128) bits.
363pub fn (u Uint128) rotate_right(k int) Uint128 {
364 return u.rotate_left(-k)
365}
366
367// reverse returns the value of u with its bits in reversed order.
368pub fn (u Uint128) reverse() Uint128 {
369 return Uint128{bits.reverse_64(u.hi), bits.reverse_64(u.lo)}
370}
371
372// reverse_bytes returns the value of u with its bytes in reversed order.
373pub fn (u Uint128) reverse_bytes() Uint128 {
374 return Uint128{bits.reverse_bytes_64(u.hi), bits.reverse_bytes_64(u.lo)}
375}
376
377// not returns a binary negation of the Uint128 value
378pub fn (u Uint128) not() Uint128 {
379 return Uint128{~u.lo, ~u.hi}
380}
381
382// len returns the minimum number of bits required to represent u; the result is
383// 0 for u == 0.
384pub fn (u Uint128) len() int {
385 return 128 - u.leading_zeros()
386}
387
388// string returns the base-10 representation of u as a string
389pub fn (u_ Uint128) str() string {
390 mut u := u_
391 if u.is_zero() {
392 return '0'
393 }
394
395 mut buf := '0000000000000000000000000000000000000000'.bytes() // log10(2^128) < 40
396
397 for i := buf.len; true; i -= 19 {
398 q, mut r := u.quo_rem_64(u64(1e19))
399
400 mut n := int(0)
401 for ; r != 0; r /= 10 {
402 n++
403 buf[i - n] += u8(r % 10)
404 }
405 if q.is_zero() {
406 return buf[i - n..].bytestr()
407 }
408 u = q
409 }
410
411 return ''
412}
413
414// put_bytes stores u in b in little-endian order
415pub fn (u Uint128) put_bytes(mut b []u8) {
416 binary.little_endian_put_u64(mut b[..8], u.lo)
417 binary.little_endian_put_u64(mut b[8..], u.hi)
418}
419
420// uint128_from_64 converts v to a Uint128 value
421pub fn uint128_from_64(v u64) Uint128 {
422 return uint128_new(v, 0)
423}
424
425// uint128_new creates new Uint128 with given `lo` and `hi`
426pub fn uint128_new(lo u64, hi u64) Uint128 {
427 return Uint128{lo, hi}
428}
429
430// unint_from_dec_str returns an error or new Uint128 from given string.
431// The `_` character is allowed as a separator.
432pub fn uint128_from_dec_str(value string) !Uint128 {
433 mut res := uint128_zero
434 underscore := `_`
435
436 for b_ in value {
437 b := b_ - `0`
438 if b > 9 {
439 // allow _ as a separator in decimal strings
440 if b_ == underscore {
441 continue
442 }
443
444 return error('invalid character "${b}"')
445 }
446
447 r, overflow := res.overflowing_mul_64(10)
448
449 if overflow {
450 return error('invalid length')
451 }
452 r2, overflow2 := r.overflowing_add_64(u64(b))
453
454 if overflow2 > 0 {
455 return error('invalid length')
456 }
457
458 res = r2
459 }
460 return res
461}
462
463// / -> returns u / v
464pub fn (u Uint128) / (v Uint128) Uint128 {
465 return u.div(v)
466}
467
468// % -> returns u % v
469pub fn (u Uint128) % (v Uint128) Uint128 {
470 return u.mod(v)
471}
472
473// + -> returns u + v
474pub fn (u Uint128) + (v Uint128) Uint128 {
475 return u.add(v)
476}
477
478// - -> returns u - v
479pub fn (u Uint128) - (v Uint128) Uint128 {
480 return u.sub(v)
481}
482
483// * -> returns u * v
484pub fn (u Uint128) * (v Uint128) Uint128 {
485 return u.mul(v)
486}
487
488// < -> returns true if u < v
489pub fn (u Uint128) < (v Uint128) bool {
490 return u.cmp(v) == -1
491}
492