as-soroban-sdk
Version:
AssemblyScript SDK for writing contracts for Soroban.
738 lines (629 loc) • 19.2 kB
text/typescript
// Offers arithmetic helper functions for u128 (unsigned integer 128) numbers using bitwise operations.
// Inspired by: https://github.com/MaxGraey/as-bignum and ported to run on soroban
import * as context from "./context";
/**
* Used for returning hi part of u128 operations that return u128.
*/
export var __hi: u64 = 0;
/**
* Checks if first u128 is lower then - operator('<') - second u128.
* @param alo Low part of the first u128.
* @param ahi High part of the first u128.
* @param blo Low part of the second u128.
* @param bhi High part of the second u128.
* @returns True if the first u128 is lower then - operator('<') - the second. Otherwise false.
*/
export function lt(alo: u64, ahi: u64, blo: u64, bhi: u64): bool {
return ahi == bhi ? alo < blo : ahi < bhi;
}
/**
* Checks if first u128 is greater then - operator('>') - second u128.
* @param alo Low part of the first u128.
* @param ahi High part of the first u128.
* @param blo Low part of the second u128.
* @param bhi High part of the second u128.
* @returns True if the first u128 is greater then - operator('>') - the second. Otherwise false.
*/
export function gt(alo: u64, ahi: u64, blo: u64, bhi: u64): bool {
return ahi == bhi ? alo > blo : ahi > bhi;
}
/**
* Checks if first u128 is lower or equal - operator('<=') - second u128.
* @param alo Low part of the first u128.
* @param ahi High part of the first u128.
* @param blo Low part of the second u128.
* @param bhi High part of the second u128.
* @returns True if the first u128 is lower or equal - operator('<=') - the second. Otherwise false.
*/
export function le(alo: u64, ahi: u64, blo: u64, bhi: u64): bool {
return !gt(alo, ahi, blo, bhi);
}
/**
* Checks if first u128 is greater or equal - operator('>=') - second u128.
* @param alo Low part of the first u128.
* @param ahi High part of the first u128.
* @param blo Low part of the second u128.
* @param bhi High part of the second u128.
* @returns True if the first u128 is greater or equak - operator('>=') - the second. Otherwise false.
*/
export function ge(alo: u64, ahi: u64, blo: u64, bhi: u64): bool {
return !lt(alo, ahi, blo, bhi);
}
/**
* Checks if first u128 equals - operator('==') - the second u128.
* @param alo Low part of the first u128.
* @param ahi High part of the first u128.
* @param blo Low part of the second u128.
* @param bhi High part of the second u128.
* @returns True if the first u128 equals - operator('==') - the second. Otherwise false.
*/
export function eq(alo: u64, ahi: u64, blo:u64, bhi:u64): bool {
return ahi == bhi && alo == blo;
}
/**
* Get ordering
* if a > b then result is 1
* if a < b then result is -1
* if a = b then result is 0
* @param alo Low part of the first u128.
* @param ahi High part of the first u128.
* @param blo Low part of the second u128.
* @param bhi High part of the second u128.
*/
export function ord(alo: u64, ahi: u64, blo:u64, bhi:u64): i32 {
let dlo = alo - blo;
let dhi = ahi - bhi;
let cmp = <i32>select<i64>(dhi, dlo, dhi != 0);
// normalize to [-1, 0, 1]
return i32(cmp > 0) - i32(cmp < 0);
}
/**
* Checks if the given u128 is zero.
* @param vlo Low part of the u128 to check.
* @param vhi High part of the u128 to check.
* @returns True if the
*/
export function isZero(vlo: u64, vhi:u64): bool {
return !(vlo | vhi);
}
/**
* Adds two u128 numbers (a + b).
* @param alo Low part of the first u128.
* @param ahi High part of the first u128.
* @param blo Low part of the second u128.
* @param bhi High part of the second u128.
* @returns Low part of the u128 result. The high part is stored in __hi.
*/
export function add(alo: u64, ahi: u64, blo:u64, bhi:u64): u64 {
let lo = alo + blo;
__hi = ahi + bhi + u64(lo < alo);
return lo;
}
/**
* Substacts an u128 from another u128 (a - b).
* @param alo Low part of the first u128.
* @param ahi High part of the first u128.
* @param blo Low part of the second u128.
* @param bhi High part of the second u128.
* @returns Low part of the u128 result. The high part is stored in __hi.
*/
export function sub(alo: u64, ahi: u64, blo:u64, bhi:u64): u64 {
let lo = alo - blo;
__hi = ahi - bhi - u64(lo > alo);
return lo;
}
/**
* Increments an u128.
* @param vlo Low part of the u128 to increment.
* @param vhi High part of the u128 to increment.
* @returns Low part of the u128 result. The high part is stored in __hi.
*/
export function inc(vlo: u64, vhi: u64): u64 {
let lo = vlo + 1;
__hi = vhi;
__hi += u64(lo < vlo);
return lo;
}
/**
* Decrements an u128.
* @param vlo Low part of the u128 to decrement.
* @param vhi High part of the u128 to decrement.
* @returns Low part of the u128 result. The high part is stored in __hi.
*/
export function dec(vlo: u64, vhi: u64): u64 {
let lo = vlo - 1;
__hi = vhi;
__hi -= u64(lo > vlo);
return lo;
}
/**
* Multiplies two u128 numbers (a * b).
* @param alo Low part of the first u128.
* @param ahi High part of the first u128.
* @param blo Low part of the second u128.
* @param bhi High part of the second u128.
* @returns Low part of the u128 result. The high part is stored in __hi.
*/
export function mul(alo: u64, ahi: u64, blo:u64, bhi:u64): u64 {
let u = alo, v = blo;
let w: u64, k: u64;
let u1 = u & 0xFFFFFFFF; u >>= 32;
let v1 = v & 0xFFFFFFFF; v >>= 32;
let t = u1 * v1;
let w1 = t & 0xFFFFFFFF;
t = u * v1 + (t >> 32);
k = t & 0xFFFFFFFF;
w = t >> 32;
t = u1 * v + k;
let lo = (t << 32) | w1;
let hi = u * v + w;
hi += ahi * blo;
hi += alo * bhi;
hi += t >> 32;
__hi = hi;
return lo;
}
/**
* Devides two u128 numbers (a / b).
* @param alo Low part of the first u128.
* @param ahi High part of the first u128.
* @param blo Low part of the second u128.
* @param bhi High part of the second u128.
* @returns Low part of the u128 result. The high part is stored in __hi.
*/
export function div(alo: u64, ahi: u64, blo: u64, bhi: u64): u64 {
let lo = __udivmod128(alo, ahi, blo, bhi);
__hi = __divmod_quot_hi;
return lo;
}
/**
* Binary not of an u128 (~val).
* @param vlo Low part of the u128 to binary not.
* @param vhi High part of the u128 to binary not.
* @returns Low part of the u128 result. The high part is stored in __hi.
*/
export function not(vlo: u64, vhi: u64): u64 {
__hi = ~vhi;
return ~vlo;
}
/**
* Binary or two u128 numbers - (a | b).
* @param alo Low part of the first u128.
* @param ahi High part of the first u128.
* @param blo Low part of the second u128.
* @param bhi High part of the second u128.
* @returns Low part of the u128 result. The high part is stored in __hi.
*/
export function or(alo: u64, ahi: u64, blo: u64, bhi: u64): u64 {
__hi = ahi | bhi;
return alo | blo;
}
/**
* Binary xor two u128 numbers - (a ^ b).
* @param alo Low part of the first u128.
* @param ahi High part of the first u128.
* @param blo Low part of the second u128.
* @param bhi High part of the second u128.
* @returns Low part of the u128 result. The high part is stored in __hi.
*/
export function xor(alo: u64, ahi: u64, blo: u64, bhi: u64): u64 {
__hi = ahi ^ bhi;
return alo ^ blo;
}
/**
* Binary and two u128 numbers - (a & b).
* @param alo Low part of the first u128.
* @param ahi High part of the first u128.
* @param blo Low part of the second u128.
* @param bhi High part of the second u128.
* @returns Low part of the u128 result. The high part is stored in __hi.
*/
export function and(alo: u64, ahi: u64, blo: u64, bhi: u64): u64 {
__hi = ahi & bhi;
return alo & blo;
}
/**
* Binary shift left - (a << b).
* @param alo Low part of the u128 to be shifted.
* @param ahi High part of the u128 to be shifted.
* @param blo Low part of the u128 to shift with.
* @param bhi High part of the u128 to shift with.
* @returns Low part of the u128 result. The high part is stored in __hi.
*/
export function shl(vlo: u64, vhi: u64, shift: i32): u64 {
shift &= 127;
// need for preventing redundant i32 -> u64 extends
let shift64 = shift as u64;
let mod1 = ((((shift64 + 127) | shift64) & 64) >> 6) - 1;
let mod2 = (shift64 >> 6) - 1;
shift64 &= 63;
let lo = vlo << shift64;
let hi = lo & ~mod2;
hi |= ((vhi << shift64) | ((vlo >> (64 - shift64)) & mod1)) & mod2;
__hi = hi;
return lo & mod2;
}
/**
* Binary shift right - (a >> b).
* @param alo Low part of the u128 to be shifted.
* @param ahi High part of the u128 to be shifted.
* @param blo Low part of the u128 to shift with.
* @param bhi High part of the u128 to shift with.
* @returns Low part of the u128 result. The high part is stored in __hi.
*/
export function shr(vlo: u64, vhi: u64, shift: i32): u64 {
shift &= 127;
// need for preventing redundant i32 -> u64 extends
let shift64 = shift as u64;
let mod1 = ((((shift64 + 127) | shift64) & 64) >> 6) - 1;
let mod2 = (shift64 >> 6) - 1;
shift64 &= 63;
let hi = vhi >> shift64;
let lo = hi & ~mod2;
lo |= ((vlo >> shift64) | ((vhi << (64 - shift64)) & mod1)) & mod2;
__hi = hi & mod2;
return lo;
}
/**
* Compute bit count of leading zeros.
* @param lo Low part of the u128.
* @param hi High part of the u128.
* @returns The result as i32.
*/
export function clz128(lo: u64, hi: u64): i32 {
let mask: u64 = <i64>(hi ^ (hi - 1)) >> 63;
return <i32>clz((hi & ~mask) | (lo & mask)) + (<i32>mask & 64);
}
/**
* Compute bit count of trailing zeros.
* @param lo Low part of the u128.
* @param hi High part of the u128.
* @returns The result as i32.
*/
export function ctz128(lo: u64, hi: u64): i32 {
let mask: u64 = <i64>(lo ^ (lo - 1)) >> 63;
return <i32>ctz((hi & mask) | (lo & ~mask)) + (<i32>mask & 64);
}
/**
* Calculate multiply and division as `number * numerator / denominator` without overflow in multiplication part.
* @param number_lo Low part of u128 number.
* @param number_hi High part of u128 number.
* @param numerator_lo Low part of u128 numerator.
* @param numerator_hi High part of u128 numerator.
* @param denominator_lo Low part of u128 denominator.
* @param denominator_hi High part of u128 denominator.
* @returns Low part of the u128 result. The high part is stored in __hi.
*/
export function muldiv(number_lo: u64, number_hi: u64, numerator_lo: u64, numerator_hi: u64, denominator_lo: u64, denominator_hi: u64): u64 {
let alo = number_lo;
let ahi = number_hi;
let blo = numerator_lo;
let bhi = numerator_hi;
let clo = denominator_lo;
let chi = denominator_hi;
//let qn = new u128(ql, __divmod_quot_hi); // b / c
let qnlo = __udivmod128(blo, bhi, clo, chi);
let qnhi = __divmod_quot_hi;
//let rn = new u128(__divmod_rem_lo, __divmod_rem_hi); // b % c
let rnlo = __divmod_rem_lo;
let rnhi = __divmod_rem_hi;
let qlo:u64 = 0;
let qhi:u64 = 0;
let rlo:u64 = 0;
let rhi:u64 = 0;
let nlo = alo;
let nhi = ahi;
while (!isZero(nlo, nhi)) {
if (nlo & 1) {
//q += qn;
qlo = add(qlo, qhi, qnlo, qnhi);
qhi = __hi;
//r += rn;
rlo = add(rlo, rhi, rnlo, rnhi);
rhi = __hi;
// if (r >= c)
if (ge(rlo, rhi, clo, chi)) {
//++q;
qlo = inc(qlo, qhi);
qhi = __hi;
//r -= c;
rlo = sub(rlo, rhi, clo, chi);
rhi = __hi;
}
}
// n >>= 1;
nlo = shr(nlo, nhi, 1);
nhi = __hi;
// qn <<= 1;
qnlo = shl(qnlo, qnhi, 1);
qnhi = __hi;
// rn <<= 1;
rnlo = shl(rnlo, rnhi, 1);
rnhi = __hi;
// if (rn >= c) {
if (ge(rnlo, rnhi, clo, chi)) {
// ++qn;
qnlo = inc(qnlo, qnhi);
qnhi = __hi;
//rn -= c;
rnlo = sub(rnlo, rnhi, clo, chi);
rnhi = __hi;
}
}
__hi = qhi;
return qlo;
}
/**
* Used for returning high part of quotient from __divmod128.
*/
export var __divmod_quot_hi: u64 = 0;
/**
* Used for returning low part of reminder from __divmod128.
*/
export var __divmod_rem_lo: u64 = 0;
/**
* Used for returning high part of reminder from __divmod128.
*/
export var __divmod_rem_hi: u64 = 0;
export function __udivmod128(alo: u64, ahi: u64, blo: u64, bhi: u64): u64 {
let bzn = clz128(blo, bhi); // N
// b == 0
if (bzn == 128) {
context.fail(); // division by zero
}
// var azn = __clz128(alo, ahi); // M
let btz = ctz128(blo, bhi); // N
// a == 0
if (!(alo | ahi)) {
__divmod_quot_hi = 0;
__divmod_rem_lo = 0;
__divmod_rem_hi = 0;
return 0;
}
// a / 1
if (bzn == 127) {
__divmod_quot_hi = ahi;
__divmod_rem_lo = 0;
__divmod_rem_hi = 0;
return alo;
}
// a == b
if (alo == blo && ahi == bhi) {
__divmod_quot_hi = 0;
__divmod_rem_lo = 0;
__divmod_rem_hi = 0;
return 1;
}
if (!(ahi | bhi)) {
__divmod_quot_hi = 0;
__divmod_rem_hi = 0;
// if `blo` is power of two
if (!(blo & (blo - 1))) {
__divmod_rem_lo = alo & (blo - 1);
return alo >> btz;
} else {
let dlo = alo / blo;
__divmod_rem_lo = alo - dlo * blo;
return dlo;
}
}
return __udivmod128core(alo, ahi, blo, bhi);
}
function __udivmod128core(alo: u64, ahi: u64, blo: u64, bhi: u64): u64 {
// get leading zeros for left alignment
let alz = clz128(alo, ahi);
let blz = clz128(blo, bhi);
let off = blz - alz;
let nblo = shl(blo, bhi, off);
let nbhi = __hi;
//var q = u128.Zero;
let qlo:u64 = 0;
let qhi:u64 = 0;
//var n = a.clone();
let nlo = alo;
let nhi = ahi;
// create a mask with the length of b
// var mask = u128.One;
let masklo:u64 = 1;
let maskhi:u64 = 0
//mask <<= 128 - blz;
masklo = shl(masklo, maskhi, 128 - blz);
maskhi = __hi;
//--mask;
masklo = dec(masklo, maskhi);
maskhi = __hi;
//mask <<= off;
masklo = shl(masklo, maskhi, off);
maskhi = __hi;
var i = 0;
//while (n >= b) {
while (ge(nlo, nhi, blo, bhi)) {
++i;
//q <<= 1;
qlo = shl(qlo, qhi, 1);
qhi = __hi;
//if ((n & mask) >= nb) {
let nmlo = and(nlo, nhi, masklo, maskhi);
let nmhi = __hi;
if (ge(nmlo, nmhi, nblo, nbhi)) {
// ++q;
qlo = inc(qlo, qhi);
qhi = __hi;
//n -= nb;
nlo = sub(nlo, nhi, nblo, nbhi);
nhi = __hi;
}
// mask |= mask >> 1;
let smlo = shr(masklo, maskhi, 1);
let smhi = __hi;
masklo = or(masklo, maskhi, smlo, smhi);
maskhi = __hi;
// nb >>= 1;
nblo = shr(nblo, nbhi, 1);
nbhi = __hi;
}
// q <<= (blz - alz - i + 1);
qlo = shl(qlo, qhi, (blz - alz - i + 1));
qhi = __hi;
__divmod_quot_hi = qhi;
__divmod_rem_lo = nlo;
__divmod_rem_hi = nhi;
return qlo;
}
/**
* Compute floor(sqrt(v)).
* @param vlo low part of value to compute.
* @param vhi high part of value to compute.
* @returns lo part of the result. high part is stored in __hi.
*/
export function sqrt(vlo:u64, vhi:u64): u64 {
if (lt(vlo, vhi, 2, 0)) {
__hi = vhi;
return vlo;
}
let valuelo = vlo;
let valuehi = vhi;
let remlo = vlo;
let remhi = vhi;
let reslo:u64 = 0;
let reshi:u64 = 0;
let poslo = shl(1, 0, (127 - (clz128(vlo, vhi) | 1)));
let poshi = __hi;
while (!isZero(poslo, poshi)) {
valuelo = add(reslo, reshi, poslo, poshi);
valuehi = __hi;
if (ge(remlo, remhi, valuelo, valuehi)) {
remlo = sub(remlo, remhi, valuelo, valuehi);
remhi = __hi;
reslo = add(poslo, poshi, valuelo, valuehi);
reshi = __hi;
}
reslo = shr(reslo, reshi, 1);
reshi = __hi;
poslo = shr(poslo, poshi, 2);
poshi = __hi;
}
__hi = reshi;
return reslo;
}
/**
* Calculate inplace squared 128-bit unsigned integer (value ** 2)
* @param vlo low part of value.
* @param vhi hi part of value.
* @returns low part of the result. Hi part is stored in __hi.
*/
function sqr(vlo:u64, vhi:u64): u64 {
let u = vlo, v = vhi;
let u1 = u & 0xFFFFFFFF;
let t = u1 * u1;
let w = t & 0xFFFFFFFF;
let k = t >> 32;
u >>= 32;
let m = u * u1;
t = m + k;
let w1 = t >> 32;
t = m + (t & 0xFFFFFFFF);
let lo = (t << 32) + w;
let hi = u * u;
hi += w1 + (t >> 32);
hi += u * v << 1;
__hi = hi;
return lo;
}
/**
* Calculate power of base with exponent.
* @param baselo low part of base.
* @param basehi high part of base.
* @param exponent exponent.
* @returns the low part of the result. High part is stored in __hi.
*/
export function pow(baselo: u64, basehi:u64, exponent: i32): u64 {
// any negative exponent produce zero
let resultlo:u64 = 1;
let resulthi:u64 = 0;
if (eq(baselo, basehi, resultlo, resulthi)) {
__hi = resulthi;
return resultlo;
}
let tmplo = baselo;
let tmphi = basehi;
if (exponent <= 1) {
if (exponent < 0) {
__hi = 0;
return 0;
}
if (exponent == 0) {
__hi = resulthi;
return resultlo;
} else {
__hi = tmphi;
return tmplo;
}
}
if (ASC_SHRINK_LEVEL < 1) {
let lo = baselo;
let hi = basehi;
// if base > u64::max and exp > 1 always return "0"
if (!lo) {
__hi = 0;
return 0;
}
if (!hi) {
let lo1 = lo - 1;
// "1 ^ exponent" always return "1"
if (!lo1){
__hi = resulthi;
return resultlo;
}
// if base is power of two do "1 << log2(base) * exp"
if (!(lo & lo1)) {
let shift = <i32>(64 - clz(lo1)) * exponent;
if (shift < 128) {
return shl(resultlo, resulthi, shift);
} else {
__hi = 0;
return 0;
}
}
}
if (exponent <= 4) {
tmplo = sqr(tmplo, tmphi);
tmphi = __hi;
let baseSqlo = tmplo;
let baseSqhi = tmphi;
switch (exponent) {
case 2: {
__hi = baseSqhi;
return baseSqlo; // base ^ 2
}
case 3: {
return mul(baseSqlo, baseSqhi, baselo, basehi); // base ^ 2 * base
}
case 4: {
return sqr(baseSqlo, baseSqhi); // base ^ 2 * base ^ 2
}
default: break;
}
}
let log = 32 - clz(exponent);
if (log <= 7) {
if (exponent & 1) {
resultlo = mul(resultlo, resulthi, tmplo, tmphi);
resulthi = __hi;
}
__hi = resulthi;
return resultlo;
}
}
while (exponent > 0) {
if (exponent & 1) {
resultlo = mul(resultlo, resulthi, tmplo, tmphi);
resulthi = __hi;
}
exponent >>= 1;
tmplo = sqr(tmplo, tmphi);
tmphi = __hi;
}
__hi = resulthi;
return resultlo;
}