diff --git a/vlib/math/unsigned/uint128.v b/vlib/math/unsigned/uint128.v new file mode 100644 index 0000000000..248f65c559 --- /dev/null +++ b/vlib/math/unsigned/uint128.v @@ -0,0 +1,462 @@ +module uint + +import math.bits +import encoding.binary + +pub struct Uint128 { +pub mut: + lo u64 + hi u64 +} + +pub const ( + uint128_zero = Uint128{} + uint128_max = Uint128{18446744073709551615, 18446744073709551615} +) + +// is_zero returns true if u == 0 +pub fn (u Uint128) is_zero() bool { + return u == Uint128{} +} + +// equals returns true if u == v. +// +// Uint128 values can be compared directly with ==, but use of the Equals method +// is preferred for consistency. +pub fn (u Uint128) equals(v Uint128) bool { + return u == v +} + +// equals_64 returns true if u == v +pub fn (u Uint128) equals_64(v u64) bool { + return u.lo == v && u.hi == 0 +} + +// cmp compares u and v and returns: +// +// -1 if u < v +// 0 if u == v +// +1 if u > v +// +pub fn (u Uint128) cmp(v Uint128) int { + if u == v { + return 0 + } else if u.hi < v.hi || (u.hi == v.hi && u.lo < v.lo) { + return -1 + } else { + return 1 + } +} + +// cmp_64 compares u and v and returns: +// +// -1 if u < v +// 0 if u == v +// +1 if u > v +// +pub fn (u Uint128) cmp_64(v u64) int { + if u.hi == 0 && u.lo == v { + return 0 + } else if u.hi == 0 && u.lo < v { + return -1 + } else { + return 1 + } +} + +// and returns u & v +pub fn (u Uint128) and(v Uint128) Uint128 { + return Uint128{u.lo & v.lo, u.hi & v.hi} +} + +// and_64 rreturns u & v +pub fn (u Uint128) and_64(v u64) Uint128 { + return Uint128{u.lo & v, u.hi & 0} +} + +// or returns u | v +pub fn (u Uint128) or_(v Uint128) Uint128 { + return Uint128{u.lo | v.lo, u.hi | v.hi} +} + +// or returns u | v +pub fn (u Uint128) or_64(v u64) Uint128 { + return Uint128{u.lo | v, u.hi | 0} +} + +// xor returns u ^ v +pub fn (u Uint128) xor(v Uint128) Uint128 { + return Uint128{u.lo ^ v.lo, u.hi ^ v.hi} +} + +// xor_64 returns u ^ v +pub fn (u Uint128) xor_64(v u64) Uint128 { + return Uint128{u.lo ^ v, u.hi ^ 0} +} + +// add returns u + v with wraparound semantics +pub fn (u Uint128) add(v Uint128) Uint128 { + lo, carry := bits.add_64(u.lo, v.lo, 0) + hi, _ := bits.add_64(u.hi, v.hi, carry) + return Uint128{lo, hi} +} + +pub fn add_128(x Uint128, y Uint128, carry u64) (Uint128, u64) { + mut sum := Uint128{} + mut carry_out := u64(0) + sum.lo, carry_out = bits.add_64(x.lo, y.lo, carry) + sum.hi, carry_out = bits.add_64(x.hi, y.hi, carry_out) + return sum, carry_out +} + +pub fn sub_128(x Uint128, y Uint128, borrow u64) (Uint128, u64) { + mut diff := Uint128{} + mut borrow_out := u64(0) + diff.lo, borrow_out = bits.sub_64(x.lo, y.lo, borrow) + diff.hi, borrow_out = bits.sub_64(x.hi, y.hi, borrow_out) + return diff, borrow_out +} + +pub fn mul_128(x Uint128, y Uint128) (Uint128, Uint128) { + mut lo := Uint128{} + mut hi := Uint128{} + lo.hi, lo.lo = bits.mul_64(x.lo, y.lo) + hi.hi, hi.lo = bits.mul_64(x.hi, y.hi) + t0, t1 := bits.mul_64(x.lo, y.hi) + t2, t3 := bits.mul_64(x.hi, y.lo) + + mut c0 := u64(0) + mut c1 := u64(0) + lo.hi, c0 = bits.add_64(lo.hi, t1, 0) + lo.hi, c1 = bits.add_64(lo.hi, t3, 0) + hi.lo, c0 = bits.add_64(hi.lo, t0, c0) + hi.lo, c1 = bits.add_64(hi.lo, t2, c1) + hi.hi += c0 + c1 + return hi, lo +} + +pub fn div_128(hi Uint128, lo Uint128, y_ Uint128) (Uint128, Uint128) { + mut y := y_ + if y.is_zero() { + panic('integer divide by zero') + } + + if y.cmp(hi) <= 0 { + panic('integer overflow') + } + + s := u32(y.leading_zeros()) + y = y.lsh(s) + + un32 := hi.lsh(s).or_(lo.rsh(128 - s)) + un10 := lo.lsh(s) + mut q1, rhat := un32.quo_rem_64(y.hi) + mut r1 := uint128_from_64(rhat) + tmp := Uint128{r1.lo, un10.hi} + for q1.hi != 0 || q1.mul_64(y.lo).cmp(tmp) > 0 { + q1 = q1.sub_64(1) + r1 = r1.add_64(y.hi) + if r1.hi != 0 { + break + } + } + + un21 := Uint128{un32.lo, un10.hi}.sub(q1.mul(y)) + mut q0, rhat2 := un21.quo_rem_64(y.hi) + mut r0 := uint128_from_64(rhat2) + tmp2 := Uint128{r0.lo, un10.lo} + for q0.hi != 0 || q0.mul_64(y.lo).cmp(tmp2) > 0 { + q0 = q0.sub_64(1) + r0 = r0.add_64(y.hi) + if r0.hi != 0 { + break + } + } + + return Uint128{q1.lo, q0.lo}, Uint128{un21.lo, un10.lo}.sub(q0.mul(y)).rsh(s) +} + +// add_64 returns u + v with wraparound semantics +pub fn (u Uint128) add_64(v u64) Uint128 { + lo, carry := bits.add_64(u.lo, v, 0) + hi := u.hi + carry + return Uint128{lo, hi} +} + +// sub returns u - v with wraparound semantics +pub fn (u Uint128) sub(v Uint128) Uint128 { + lo, borrow := bits.sub_64(u.lo, v.lo, 0) + hi, _ := bits.sub_64(u.hi, v.hi, borrow) + return Uint128{lo, hi} +} + +// sub_64 returns u - v with wraparound semantics +pub fn (u Uint128) sub_64(v u64) Uint128 { + lo, borrow := bits.sub_64(u.lo, v, 0) + hi := u.hi - borrow + return Uint128{lo, hi} +} + +// mul returns u * v with wraparound semantics +pub fn (u Uint128) mul(v Uint128) Uint128 { + mut hi, lo := bits.mul_64(u.lo, v.lo) + hi += u.hi * v.lo + u.lo * v.hi + return Uint128{lo, hi} +} + +// mul_64 returns u * v with wraparound semantics +pub fn (u Uint128) mul_64(v u64) Uint128 { + mut hi, lo := bits.mul_64(u.lo, v) + hi += u.hi * v + return Uint128{lo, hi} +} + +pub fn (u Uint128) overflowing_mul_64(v u64) (Uint128, bool) { + hi, lo := bits.mul_64(u.lo, v) + p0, p1 := bits.mul_64(u.hi, v) + hi2, c0 := bits.add_64(hi, p1, 0) + + return Uint128{lo, hi2}, p0 != 0 || c0 != 0 +} + +pub fn (u Uint128) overflowing_add_64(v u64) (Uint128, u64) { + lo, carry := bits.add_64(u.lo, v, 0) + hi, carry2 := bits.add_64(u.hi, 0, carry) + + return Uint128{lo, hi}, carry2 +} + +// div returns u / v +pub fn (u Uint128) div(v Uint128) Uint128 { + q, _ := u.quo_rem(v) + return q +} + +// mod returns r = u % v +pub fn (u Uint128) mod(v Uint128) Uint128 { + _, r := u.quo_rem(v) + return r +} + +// mod_64 returns r = u % v +pub fn (u Uint128) mod_64(v u64) u64 { + _, r := u.quo_rem_64(v) + return r +} + +// quo_rem_64 returns q = u/v and r = u%v +pub fn (u Uint128) quo_rem_64(v u64) (Uint128, u64) { + if u.hi < v { + mut r := u64(0) + mut q := Uint128{0, 0} + q.lo, r = bits.div_64(u.hi, u.lo, v) + + return q, r + } else { + mut q := Uint128{0, 0} + mut r := u64(0) + mut r2 := u64(0) + q.hi, r = bits.div_64(0, u.hi, v) + q.lo, r2 = bits.div_64(r, u.lo, v) + return q, r2 + } +} + +// quo_rem returns q = u/v and r = u%v +pub fn (u Uint128) quo_rem(v Uint128) (Uint128, Uint128) { + mut q := Uint128{} + mut r := Uint128{} + if v.hi == 0 { + mut r64 := u64(0) + q, r64 = u.quo_rem_64(v.lo) + r = Uint128{r64, 0} + } else { + n := bits.leading_zeros_64(v.hi) + v1 := v.lsh(u32(n)) + u1 := v.rsh(1) + + mut tq, _ := bits.div_64(u1.hi, u1.lo, v1.hi) + tq >>= u64(64 - n) + if tq != 0 { + tq -= 1 + } + + q = Uint128{tq, 0} + r = u - v.mul_64(tq) + + if r.cmp(v) >= 0 { + q = q.add_64(1) + r = r - v + } + } + + return q, r +} + +// lsh returns u << n +pub fn (u Uint128) lsh(n u32) Uint128 { + mut s := Uint128{} + if n > 64 { + s.lo = 0 + s.hi = u.lo << (n - 64) + } else { + s.lo = u.lo << n + s.hi = u.hi << n | u.lo >> (64 - n) + } + + return s +} + +// lsh returns u >> n +pub fn (u Uint128) rsh(n u32) Uint128 { + mut s := Uint128{} + if n > 64 { + s.hi = 0 + s.lo = u.hi << (n - 64) + } else { + s.lo = u.lo >> n | u.hi << (64 - n) + s.hi = u.hi >> n + } + + return s +} + +// leading_zeros returns the number of leading zero bits in u; the result is 128 +// for u == 0. +pub fn (u Uint128) leading_zeros() int { + if u.hi > 0 { + return bits.leading_zeros_64(u.hi) + } + return 64 + bits.leading_zeros_64(u.lo) +} + +// trailing_zeros returns the number of trailing zero bits in u; the result is +// 128 for u == 0. +pub fn (u Uint128) trailing_zeros() int { + if u.lo > 0 { + return bits.trailing_zeros_64(u.lo) + } + return 64 + bits.trailing_zeros_64(u.hi) +} + +// ones_count returns the number of one bits ("population count" in u) +pub fn (u Uint128) ones_count() int { + return bits.ones_count_64(u.hi) + bits.ones_count_64(u.lo) +} + +// rotate_left returns the value of u rotated left by (k mod 128) bits. +pub fn (u Uint128) rotate_left(k int) Uint128 { + n := u32(128) + s := u32(k) & (n - 1) + return u.lsh(s).or_(u.rsh(n - s)) +} + +// rotate_right returns the value of u rotated right by (k mod 128) bits. +pub fn (u Uint128) rotate_right(k int) Uint128 { + return u.rotate_left(-k) +} + +// reverse returns the value of u with its bits in reversed order. +pub fn (u Uint128) reverse() Uint128 { + return Uint128{bits.reverse_64(u.hi), bits.reverse_64(u.lo)} +} + +// reverse_bytes returns the value of u with its bytes in reversed order. +pub fn (u Uint128) reverse_bytes() Uint128 { + return Uint128{bits.reverse_bytes_64(u.hi), bits.reverse_bytes_64(u.lo)} +} + +pub fn (u Uint128) not() Uint128 { + return Uint128{~u.lo, ~u.hi} +} + +// len returns the minimum number of bits required to represent u; the result is +// 0 for u == 0. +pub fn (u Uint128) len() int { + return 128 - u.leading_zeros() +} + +// string returns the base-10 representation of u as a string +pub fn (u_ Uint128) str() string { + mut u := u_ + if u.is_zero() { + return '0' + } + + mut buf := '0000000000000000000000000000000000000000'.bytes() // log10(2^128) < 40 + + for i := buf.len; true; i -= 19 { + q, mut r := u.quo_rem_64(u64(1e19)) + + mut n := int(0) + for ; r != 0; r /= 10 { + n++ + buf[i - n] += byte(r % 10) + } + if q.is_zero() { + return buf[i - n..].bytestr() + } + u = q + } + + return '' +} + +// put_bytes stores u in b in little-endian order +pub fn (u Uint128) put_bytes(mut b []byte) { + binary.little_endian_put_u64(mut b, u.lo) + binary.little_endian_put_u64(mut b, u.hi) +} + +// uint128_from_64 converts v to a Uint128 value +pub fn uint128_from_64(v u64) Uint128 { + return uint128_new(v, 0) +} + +pub fn uint128_new(lo u64, hi u64) Uint128 { + return Uint128{lo, hi} +} + +pub fn uint128_from_dec_str(value string) ?Uint128 { + mut res := uint.uint128_zero + for b_ in value.bytes() { + b := b_ - '0'.bytes()[0] + if b > 9 { + return error('invalid character "$b"') + } + + r, overflow := res.overflowing_mul_64(10) + + if overflow { + return error('invalid length') + } + r2, overflow2 := r.overflowing_add_64(u64(b)) + + if overflow2 > 0 { + return error('invalid length') + } + + res = r2 + } + return res +} + +pub fn (u Uint128) / (v Uint128) Uint128 { + return u.div(v) +} + +pub fn (u Uint128) % (v Uint128) Uint128 { + return u.mod(v) +} + +pub fn (u Uint128) + (v Uint128) Uint128 { + return u.add(v) +} + +pub fn (u Uint128) - (v Uint128) Uint128 { + return u.sub(v) +} + +pub fn (u Uint128) * (v Uint128) Uint128 { + return u.mul(v) +} diff --git a/vlib/math/unsigned/uint256.v b/vlib/math/unsigned/uint256.v new file mode 100644 index 0000000000..5b9d93adc3 --- /dev/null +++ b/vlib/math/unsigned/uint256.v @@ -0,0 +1,376 @@ +module uint + +import math.bits + +pub const ( + uint256_zero = Uint256{Uint128{}, Uint128{}} + uint256_max = Uint256{uint128_max, uint128_max} +) + +// Uint256 is an unsigned 256-bit number +pub struct Uint256 { +pub mut: + lo Uint128 = uint128_zero // lower 128 bit half + hi Uint128 = uint128_zero // upper 128 bit half +} + +pub fn uint256_from_128(v Uint128) Uint256 { + return Uint256{v, uint128_zero} +} + +pub fn uint256_from_64(v u64) Uint256 { + return uint256_from_128(uint128_from_64(v)) +} + +pub fn (u Uint256) is_zero() bool { + return u.lo.is_zero() && u.hi.is_zero() +} + +pub fn (u Uint256) equals(v Uint256) bool { + return u.lo.equals(v.lo) && u.hi.equals(v.hi) +} + +pub fn (u Uint256) euqals_128(v Uint128) bool { + return u.lo.equals(v) && u.hi.is_zero() +} + +pub fn (u Uint256) cmp(v Uint256) int { + h := u.hi.cmp(v.hi) + if h != 0 { + return h + } + return u.lo.cmp(v.lo) +} + +pub fn (u Uint256) cmp_128(v Uint128) int { + if !u.hi.is_zero() { + return 1 + } + return u.lo.cmp(v) +} + +pub fn (u Uint256) not() Uint256 { + return Uint256{u.lo.not(), u.hi.not()} +} + +pub fn (u Uint256) and(v Uint256) Uint256 { + return Uint256{u.lo.and(v.lo), u.hi.and(v.hi)} +} + +pub fn (u Uint256) and_128(v Uint128) Uint256 { + return Uint256{u.lo.and(v), uint128_zero} +} + +pub fn (u Uint256) or_(v Uint256) Uint256 { + return Uint256{u.lo.or_(v.lo), u.hi.or_(v.hi)} +} + +pub fn (u Uint256) or_128(v Uint128) Uint256 { + return Uint256{u.lo.or_(v), u.hi} +} + +pub fn (u Uint256) xor(v Uint256) Uint256 { + return Uint256{u.lo.xor(v.lo), u.hi.xor(v.hi)} +} + +pub fn (u Uint256) xor_128(v Uint128) Uint256 { + return Uint256{u.lo.xor(v), u.hi} +} + +pub fn add_256(x Uint256, y Uint256, carry u64) (Uint256, u64) { + mut sum := Uint256{} + mut carry_out := u64(0) + sum.lo, carry_out = add_128(x.lo, y.lo, carry) + sum.hi, carry_out = add_128(x.hi, y.hi, carry_out) + return sum, carry_out +} + +pub fn sub_256(x Uint256, y Uint256, borrow u64) (Uint256, u64) { + mut diff := Uint256{} + mut borrow_out := u64(0) + diff.lo, borrow_out = sub_128(x.lo, y.lo, borrow) + diff.hi, borrow_out = sub_128(x.hi, y.hi, borrow_out) + return diff, borrow_out +} + +pub fn mul_256(x Uint256, y Uint256) (Uint256, Uint256) { + mut hi := Uint256{} + mut lo := Uint256{} + + lo.hi, lo.lo = mul_128(x.lo, y.lo) + hi.hi, hi.lo = mul_128(x.hi, y.hi) + t0, t1 := mul_128(x.lo, y.hi) + t2, t3 := mul_128(x.hi, y.lo) + + mut c0 := u64(0) + mut c1 := u64(0) + + lo.hi, c0 = add_128(lo.hi, t1, 0) + lo.hi, c1 = add_128(lo.hi, t3, 0) + hi.lo, c0 = add_128(hi.lo, t0, c0) + hi.lo, c1 = add_128(hi.lo, t2, c1) + hi.hi = hi.hi.add_64(c0 + c1) + + return hi, lo +} + +pub fn (u Uint256) add(v Uint256) Uint256 { + sum, _ := add_256(u, v, 0) + return sum +} + +pub fn (u Uint256) overflowing_add(v Uint256) (Uint256, u64) { + sum, overflow := add_256(u, v, 0) + return sum, overflow +} + +pub fn (u Uint256) add_128(v Uint128) Uint256 { + lo, c0 := add_128(u.lo, v, 0) + return Uint256{lo, u.hi.add_64(c0)} +} + +pub fn (u Uint256) sub(v Uint256) Uint256 { + diff, _ := sub_256(u, v, 0) + return diff +} + +pub fn (u Uint256) sub_128(v Uint128) Uint256 { + lo, b0 := sub_128(u.lo, v, 0) + return Uint256{lo, u.hi.sub_64(b0)} +} + +pub fn (u Uint256) mul(v Uint256) Uint256 { + mut hi, mut lo := mul_128(u.lo, v.lo) + hi = hi.add(u.hi.mul(v.lo)) + hi = hi.add(u.lo.mul(v.hi)) + return Uint256{lo, hi} +} + +pub fn (u Uint256) mul_128(v Uint128) Uint256 { + hi, lo := mul_128(u.lo, v) + return Uint256{lo, hi.add(u.hi.mul(v))} +} + +pub fn (u Uint256) quo_rem(v Uint256) (Uint256, Uint256) { + if v.hi.is_zero() { + q, r := u.quo_rem_128(v.lo) + return q, uint256_from_128(r) + } + + n := u64(v.hi.leading_zeros()) + u1, v1 := u.rsh(1), v.lsh(u32(n)) + mut tq, _ := div_128(u1.hi, u1.lo, v1.hi) + tq = tq.rsh(u32(127 - n)) + if !tq.is_zero() { + tq = tq.sub_64(1) + } + + mut q, mut r := uint256_from_128(tq), u.sub(v.mul_128(tq)) + if r.cmp(v) >= 0 { + q = q.add_128(Uint128{1, 0}) + r = r.sub(v) + } + return q, r +} + +pub fn (u Uint256) quo_rem_128(v Uint128) (Uint256, Uint128) { + if u.hi.cmp(v) < 0 { + lo, r := div_128(u.hi, u.lo, v) + return Uint256{lo, uint128_zero}, r + } + + hi, r := div_128(uint128_zero, u.hi, v) + lo, r2 := div_128(r, u.lo, v) + return Uint256{lo, hi}, r2 +} + +pub fn (u Uint256) quo_rem_64(v u64) (Uint256, u64) { + mut q := Uint256{} + mut r := u64(0) + q.lo.hi, r = bits.div_64(r, u.lo.hi, v) + q.lo.lo, r = bits.div_64(r, u.lo.lo, v) + return q, r +} + +pub fn (u Uint256) rsh(n_ u32) Uint256 { + mut n := n_ + if n > 128 { + return Uint256{u.hi.rsh(n - 128), uint128_zero} + } + + if n > 64 { + n -= 64 + return Uint256{Uint128{u.lo.hi >> n | u.hi.lo << (64 - n), u.hi.lo >> n | u.hi.hi << (64 - n)}, Uint128{u.hi.hi >> n, 0}} + } + return Uint256{Uint128{u.lo.lo >> n | u.lo.hi << (64 - n), u.lo.hi >> n | u.hi.lo << (64 - n)}, Uint128{u.hi.lo >> n | u.hi.hi << (64 - n), u.hi.hi >> n}} +} + +pub fn (u Uint256) lsh(n_ u32) Uint256 { + mut n := n_ + if n > 128 { + return Uint256{u.lo.lsh(n - 128), uint128_zero} + } + + if n > 64 { + n -= 64 + return Uint256{Uint128{u.lo.lo << n, 0}, Uint128{u.lo.hi << n | u.lo.lo >> (64 - n), u.hi.lo << n | u.lo.hi >> (64 - n)}} + } + + return Uint256{Uint128{u.lo.lo << n, u.lo.hi << n | u.lo.lo >> (64 - n)}, Uint128{u.hi.lo << n | u.lo.hi >> (64 - n), u.hi.hi << n | u.hi.lo >> (64 - n)}} +} + +pub fn (u Uint256) div(v Uint256) Uint256 { + q, _ := u.quo_rem(v) + return q +} + +pub fn (u Uint256) div_128(v Uint128) Uint256 { + q, _ := u.quo_rem_128(v) + return q +} + +pub fn (u Uint256) div_64(v u64) Uint256 { + q, _ := u.quo_rem_64(v) + return q +} + +pub fn (u Uint256) mod(v Uint256) Uint256 { + _, r := u.quo_rem(v) + return r +} + +pub fn (u Uint256) mod_128(v Uint128) Uint128 { + _, r := u.quo_rem_128(v) + return r +} + +pub fn (u Uint256) mod_64(v u64) u64 { + _, r := u.quo_rem_64(v) + return r +} + +pub fn (u Uint256) rotate_left(k int) Uint256 { + mut n := u32(k) & 255 + if n < 64 { + if n == 0 { + return u + } + + return Uint256{Uint128{u.lo.lo << n | u.hi.hi >> (64 - n), u.lo.hi << n | u.lo.lo >> (64 - n)}, Uint128{u.hi.lo << n | u.lo.hi >> (64 - n), u.hi.hi << n | u.hi.lo >> (64 - n)}} + } + n -= 64 + if n < 64 { + if n == 0 { + return Uint256{Uint128{u.hi.hi, u.lo.lo}, Uint128{u.lo.hi, u.hi.lo}} + } + + return Uint256{Uint128{u.hi.hi << n | u.hi.lo >> (64 - n), u.lo.lo << n | u.hi.hi >> (64 - n)}, Uint128{u.lo.hi << n | u.lo.lo >> (64 - n), u.hi.lo << n | u.lo.hi >> (64 - n)}} + } + + n -= 64 + if n < 64 { + if n == 0 { + return Uint256{u.hi, u.lo} + } + + return Uint256{Uint128{u.hi.lo << n | u.lo.hi >> (64 - n), u.hi.hi << n | u.hi.lo >> (64 - n)}, Uint128{}} + } + n -= 64 + if n == 0 { + return Uint256{Uint128{u.lo.hi, u.hi.lo}, Uint128{u.hi.hi, u.lo.lo}} + } + + return Uint256{Uint128{u.lo.hi << n | u.lo.lo >> (64 - n), u.hi.lo << n | u.lo.hi >> (64 - n)}, Uint128{u.hi.hi << n | u.hi.lo >> (64 - n), u.lo.lo << n | u.hi.hi >> (64 - n)}} +} + +pub fn (u Uint256) rotate_right(k int) Uint256 { + return u.rotate_left(-k) +} + +pub fn (u Uint256) len() int { + if !u.hi.is_zero() { + return 128 + u.hi.len() + } + return u.lo.len() +} + +pub fn (u Uint256) leading_zeros() int { + if !u.hi.is_zero() { + return u.hi.leading_zeros() + } + return 128 + u.lo.leading_zeros() +} + +pub fn (u Uint256) trailing_zeros() int { + if !u.lo.is_zero() { + return u.lo.trailing_zeros() + } + + return 128 + u.hi.trailing_zeros() +} + +pub fn (u Uint256) ones_count() int { + return u.lo.ones_count() + u.hi.ones_count() +} + +pub fn (u_ Uint256) str() string { + mut u := u_ + if u.hi.is_zero() { + if u.lo.is_zero() { + return '0' + } + return u.lo.str() + } + + mut buf := '000000000000000000000000000000000000000000000000000000000000000000000000000000'.bytes() + + for i := buf.len; true; i -= 19 { + q, mut r := u.quo_rem_64(u64(1e19)) + mut n := 0 + for ; r != 0; r /= 10 { + n++ + buf[i - n] += byte(r % 10) + } + if q.is_zero() { + return buf[i - n..].bytestr() + } + u = q + } + return '' +} + +pub fn uint256_from_dec_str(value string) ?Uint256 { + mut res := uint.uint256_zero + for b_ in value.bytes() { + b := b_ - '0'.bytes()[0] + if b > 9 { + return error('invalid character "$b"') + } + + r := res.mul_128(uint128_from_64(10)) + + r2 := r.add_128(uint128_from_64(u64(b))) + res = r2 + } + return res +} + +pub fn (u Uint256) / (v Uint256) Uint256 { + return u.div(v) +} + +pub fn (u Uint256) % (v Uint256) Uint256 { + return u.mod(v) +} + +pub fn (u Uint256) + (v Uint256) Uint256 { + return u.add(v) +} + +pub fn (u Uint256) - (v Uint256) Uint256 { + return u.sub(v) +} + +pub fn (u Uint256) * (v Uint256) Uint256 { + return u.mul(v) +}