UNPKG

as-soroban-sdk

Version:

AssemblyScript SDK for writing contracts for Soroban.

738 lines (629 loc) 19.2 kB
// 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; }