src/math/u64.bend source
src/math/u64.bend on the hub · documented module
import Base# 64-bit integers as two native U32 limbs (Bend 2 has no native 64-bit word).# The same bits read as unsigned or as two's-complement signed, as in C.## zero(), of_u32(x) construction# is_zero(a) a == 0# add(a, b), neg(a) wrapping addition and negation# le_signed(a, b) a <= b as signed 64-bit integers# div_small(a, d) unsigned a / d, for 0 < d <= 2^20# div_small_signed(a, d) signed a / d truncated toward zero, 0 < d <= 2^20type U64 is Data: U64{lo: U32, hi: U32}def zero() -> U64: U64{0, 0}def of_u32(+x: U32) -> U64: U64{x, 0}def is_zero(a: U64) -> Bool: U64{lo, hi} = a Bool.and(U32.is_zero(lo), U32.is_zero(hi))def carry(b: Bool) -> U32: match b: case True{}: 1 case False{}: 0def add_fin(+lo: U32, +alo: U32, +ahi: U32, +bhi: U32) -> U64: U64{lo, U32.add(U32.add(ahi, bhi), carry(U32.is_lt(lo, alo)))}# Wrapping 64-bit addition.def add(a: U64, b: U64) -> U64: U64{+alo, +ahi} = a U64{+blo, +bhi} = b add_fin(U32.add(alo, blo), alo, ahi, bhi)def neg_fin(+lo2: U32, +hi: U32) -> U64: U64{lo2, U32.add(U32.not(hi), carry(U32.is_zero(lo2)))}# Two's-complement negation.def neg(a: U64) -> U64: U64{+lo, +hi} = a neg_fin(U32.inc(U32.not(lo)), hi)def order(high: Cmp, low: Cmp) -> Cmp: match high: case EQ{}: low case LT{}: LT{} case GT{}: GT{}def le_sign(+alo: U32, +ahi: U32, +blo: U32, +bhi: U32, negative_a: Bool, negative_b: Bool) -> Bool: match negative_a negative_b: case True{} False{}: True{} case False{} True{}: False{} case _ _: Cmp.is_le(order(U32.cmp(ahi, bhi), U32.cmp(alo, blo)))# a <= b as signed 64-bit integers.def le_signed(a: U64, b: U64) -> Bool: U64{alo, +ahi} = a U64{blo, +bhi} = b le_sign(alo, ahi, blo, bhi, U32.is_ge(ahi, 2147483648), U32.is_ge(bhi, 2147483648))# Long division in 32/12/12/8-bit digits: every partial remainder is below# d <= 2^20, so each candidate r * 2^12 + digit stays below 2^32.def div_fin(+lo: U32, +hi: U32, +d: U32, +t1: U32, +t2: U32, +t3: U32) -> U64: U64{U32.add(U32.add(U32.mul(U32.div(t1, d), 1048576), U32.mul(U32.div(t2, d), 256)), U32.div(t3, d)), U32.div(hi, d)}def div_t3(+lo: U32, +hi: U32, +d: U32, +t1: U32, +t2: U32) -> U64: div_fin(lo, hi, d, t1, t2, U32.add(U32.mul(U32.mod(t2, d), 256), U32.and(lo, 255)))def div_t2(+lo: U32, +hi: U32, +d: U32, +t1: U32) -> U64: div_t3(lo, hi, d, t1, U32.add(U32.mul(U32.mod(t1, d), 4096), U32.and(U32.div(lo, 256), 4095)))# Unsigned a / d, for 0 < d <= 2^20.def div_small(a: U64, +d: U32) -> U64: U64{+lo, +hi} = a div_t2(lo, hi, d, U32.add(U32.mul(U32.mod(hi, d), 4096), U32.div(lo, 1048576)))def div_sign(a: U64, +d: U32, negative: Bool) -> U64: match negative: case False{}: div_small(a, d) case True{}: neg(div_small(neg(a), d))# Signed a / d truncated toward zero (C semantics), for 0 < d <= 2^20.def div_small_signed(a: U64, +d: U32) -> U64: U64{lo, +hi} = a div_sign(U64{lo, hi}, d, U32.is_ge(hi, 2147483648))