as-pedersen
Version:
A pedersen hash implementation in AssemblyScript
1,611 lines (1,463 loc) • 44.6 kB
text/typescript
// multiple precision integer
export class BigInt {
private d: Uint32Array; // digits
private n: i32 = 0; // digits used
private isNeg: boolean; // sign
get isNegative(): boolean {
return this.isNeg;
}
// private static readonly q: i32 = 2;
private static readonly p: i32 = 28; // bits used in digit
// private static readonly b: u32 = BigInt.q ** BigInt.p; // digit basis
private static readonly actualBits: i32 = 32; // bits available in type (single precision)
// private static readonly doubleActualBits: i32 = 64 // 2 * BigIntMP.actualBits -> "double precision" actual bits
private static readonly maxComba: i32 = 256; // 2^(doubleActualBits - 2 * p) = 2^8 = 256
private static readonly digitMask: u32 = <u32>((1 << BigInt.p) - 1); // mask p least significant bits
private static readonly precision: i32 = 5; // base array size fits 140 bit integers
// private static readonly maxBits: i32 = I32.MAX_VALUE;
// private static readonly maxN: i32 = BigInt.maxBits / BigInt.p;
// CONSTRUCTORS //////////////////////////////////////////////////////////////////////////////////////////////////////
private constructor(
size: i32 = BigInt.precision,
isNegative: boolean = false
) {
this.d = new Uint32Array(size);
this.isNeg = isNegative;
}
// generic constructor based on https://github.com/ttulka/as-big/blob/main/assembly/Big.ts#L84
/**
* Returns a new {BigInt} instance from generic type {T}.
*
* @param val the number as {BigInt}, {string}, or {number}
* @return BigInt the new {BigInt} instance
*/
static from<T>(val: T): BigInt {
if (val instanceof BigInt) return val;
// @ts-ignore
if (val instanceof string) return BigInt.fromString(val);
// @ts-ignore
if (val instanceof i8) return BigInt.fromInt16(<i16>val);
// @ts-ignore
if (val instanceof u8) return BigInt.fromUInt16(<u16>val);
// @ts-ignore
if (val instanceof i16) return BigInt.fromInt16(val);
// @ts-ignore
if (val instanceof u16) return BigInt.fromUInt16(val);
// @ts-ignore
if (val instanceof i32) return BigInt.fromInt32(val);
// @ts-ignore
if (val instanceof u32) return BigInt.fromUInt32(val);
// @ts-ignore
if (val instanceof i64) return BigInt.fromInt64(val);
// @ts-ignore
if (val instanceof u64) return BigInt.fromUInt64(val);
throw new TypeError('Unsupported generic type ' + nameof<T>(val));
}
static fromString(bigInteger: string, radix: i32 = 10): BigInt {
if (radix < 2 || radix > 16) {
throw new RangeError('BigInt only reads strings of radix 2 through 16');
}
let i: i32 = 0;
let isNegative: boolean = false;
if (bigInteger.charAt(0) == '-') {
i++;
isNegative = true;
}
if (
(radix == 16 || radix == 10) &&
bigInteger.charAt(i) == '0' &&
bigInteger.charAt(i + 1) == 'x'
) {
i += 2;
radix = 16;
}
let res: BigInt = BigInt.fromUInt16(0);
const radixU: u16 = <u16>radix;
for (; i < bigInteger.length; i++) {
const code: i32 = bigInteger.charCodeAt(i);
let val: u16;
if (code >= 48 && code <= 57) {
val = <u16>(code - 48);
} else if (code >= 65 && code <= 70) {
val = <u16>(code - 55);
} else if (code >= 97 && code <= 102) {
val = <u16>(code - 87);
} else {
throw new RangeError(
'Character ' +
bigInteger.charAt(i) +
' is not supported for radix ' +
radix.toString()
);
}
res = res.inplaceMulInt(radixU).add(BigInt.fromUInt16(val));
}
res.isNeg = isNegative;
res.trimLeadingZeros();
return res;
}
static fromUInt16(val: u16): BigInt {
const res = new BigInt(BigInt.precision, false);
res.d[0] = (<u32>val) & BigInt.digitMask;
res.n = res.d[0] != 0 ? 1 : 0;
return res;
}
static fromUInt32(val: u32): BigInt {
const res = new BigInt(BigInt.precision, false);
let i = 0;
while (val != 0) {
res.d[i++] = val & BigInt.digitMask;
val >>= BigInt.p;
}
res.n = i;
res.trimLeadingZeros();
return res;
}
static fromUInt64(val: u64): BigInt {
const res = new BigInt(BigInt.precision, false);
let i = 0;
while (val != 0) {
res.d[i++] = (<u32>val) & BigInt.digitMask;
val >>= BigInt.p;
}
res.n = i;
res.trimLeadingZeros();
return res;
}
static fromInt16(val: i16): BigInt {
const isNeg: boolean = val < 0;
const res = new BigInt(BigInt.precision, isNeg);
const unsignedDigit: u16 = <u16>(isNeg ? -1 * val : val);
res.d[0] = (<u32>unsignedDigit) & BigInt.digitMask;
res.n = res.d[0] != 0 ? 1 : 0;
return res;
}
static fromInt32(val: i32): BigInt {
const isNeg: boolean = val < 0;
const res = new BigInt(BigInt.precision, isNeg);
let unsignedDigit: u32 = <u32>(isNeg ? -1 * val : val);
let i = 0;
while (unsignedDigit != 0) {
res.d[i++] = unsignedDigit & BigInt.digitMask;
unsignedDigit >>= BigInt.p;
}
res.n = i;
res.trimLeadingZeros();
return res;
}
static fromInt64(val: i64): BigInt {
const isNeg: boolean = val < 0;
const res = new BigInt(BigInt.precision, isNeg);
let unsignedDigit: u64 = <u64>(isNeg ? -1 * val : val);
let i = 0;
while (unsignedDigit != 0) {
res.d[i++] = (<u32>unsignedDigit) & BigInt.digitMask;
unsignedDigit >>= BigInt.p;
}
res.n = i;
res.trimLeadingZeros();
return res;
}
// O(N)
private static fromDigits(
digits: Uint32Array,
isNegative: boolean = false,
n: i32 = digits.length,
minSize: i32 = digits.length
): BigInt {
let size = minSize;
if (size < digits.length) {
size = digits.length;
}
const extra = size % BigInt.precision;
if (extra != 0) {
size += BigInt.precision - extra;
}
const res: BigInt = new BigInt(size, isNegative);
for (let i = 0; i < digits.length; i++) {
res.d[i] = digits[i];
}
res.n = n;
return res;
}
// O(N)
copy(): BigInt {
return BigInt.fromDigits(this.d, this.isNeg, this.n);
}
// O(N)
opposite(): BigInt {
return BigInt.fromDigits(this.d, this.n > 0 && !this.isNeg, this.n);
}
// O(N)
abs(): BigInt {
return BigInt.fromDigits(this.d, false, this.n);
}
private static getEmptyResultContainer(
minSize: i32,
isNegative: boolean,
n: i32
): BigInt {
const size: i32 = minSize + BigInt.precision - (minSize % BigInt.precision);
const res: BigInt = new BigInt(size, isNegative);
res.n = n;
return res;
}
// MAINTENANCE FUNCTIONS /////////////////////////////////////////////////////////////////////////////////////////////
private trimLeadingZeros(): void {
while (this.n > 0 && this.d[this.n - 1] == 0) {
this.n--;
}
if (this.n == 0) {
this.isNeg = false;
}
}
private resize(max: i32): void {
const temp: Uint32Array = new Uint32Array(max);
for (let i = 0; i < this.n; i++) {
temp[i] = this.d[i];
}
this.d = temp;
}
private grow(size: i32): void {
if (this.d.length >= size) return;
this.resize(size + 2 * BigInt.precision - (size % BigInt.precision));
}
// OUTPUT /////////////////////////////////////////////////////////////////////////////////////////////////////
toString(radix: i32 = 10): string {
if (radix < 2 || radix > 16) {
throw new RangeError('BigInt only prints strings in radix 2 through 16');
}
if (this.n == 0) return '0';
let res: string = this.isNeg ? '-' : '';
let t: BigInt = this.abs();
const zero: BigInt = BigInt.fromUInt16(0);
const codes: i32[] = [];
const radixU: u32 = <u32>radix;
while (t.ne(zero)) {
const d: i32 = <i32>t.modInt(radixU);
t = t.inplaceDivInt(radixU);
if (d < 10) {
codes.push(d + 48);
} else {
codes.push(d + 87);
}
}
codes.reverse();
res += String.fromCharCodes(codes);
return res;
}
toInt32(): i32 {
if (this.n <= 1) {
return this.n == 0 ? <i32>0 : <i32>this.d[0] * (this.isNeg ? -1 : 1);
}
const bitCount: i32 = this.countBits();
if (bitCount > 32) {
throw new Error(
`Integer overflow: cannot output i32 from an integer that uses ${bitCount} bits`
);
}
const biString: string = this.toString();
const result: i32 = I32.parseInt(biString);
if (bitCount == 32 && result.toString() != biString) {
throw new Error('Signed integer overflow');
}
return result;
}
toInt64(): i64 {
if (this.n <= 1) {
return this.n == 0 ? <i64>0 : <i64>this.d[0] * (this.isNeg ? -1 : 1);
}
const bitCount: i32 = this.countBits();
if (bitCount > 64) {
throw new Error(
`Integer overflow: cannot output i64 from an integer that uses ${bitCount} bits`
);
}
const biString: string = this.toString();
const result: i64 = I64.parseInt(biString);
if (bitCount == 64 && result.toString() != biString) {
throw new Error('Signed integer overflow');
}
return result;
}
toUInt32(): u32 {
if (this.isNeg) {
throw new Error('Cannot cast negative integer to u32');
}
if (this.n <= 1) {
return this.n == 0 ? <u32>0 : <u32>this.d[0];
}
const bitCount: i32 = this.countBits();
if (bitCount > 32) {
throw new Error(
`Integer overflow: cannot output u32 from an integer that uses ${bitCount} bits`
);
}
return U32.parseInt(this.toString());
}
toUInt64(): u64 {
if (this.isNeg) {
throw new Error('Cannot cast negative integer to u64');
}
if (this.n <= 1) {
return this.n == 0 ? <u64>0 : <u64>this.d[0];
}
const bitCount: i32 = this.countBits();
if (bitCount > 64) {
throw new Error(
`Integer overflow: cannot output u64 from an integer that uses ${bitCount} bits`
);
}
return U64.parseInt(this.toString());
}
// COMPARISON OPERATORS //////////////////////////////////////////////////////////////////////////////////////////////
eq<T>(other: T): boolean {
return this.compareTo(BigInt.from(other)) == 0;
}
ne<T>(other: T): boolean {
return !this.eq(BigInt.from(other));
}
lt<T>(other: T): boolean {
return this.compareTo(BigInt.from(other)) < 0;
}
lte<T>(other: T): boolean {
return this.compareTo(BigInt.from(other)) <= 0;
}
gt<T>(other: T): boolean {
return this.compareTo(BigInt.from(other)) > 0;
}
gte<T>(other: T): boolean {
return this.compareTo(BigInt.from(other)) >= 0;
}
compareTo(other: BigInt): i32 {
// opposite signs
if (this.isNeg && !other.isNeg) {
return -1;
} else if (!this.isNeg && other.isNeg) {
return 1;
} else if (this.isNeg) {
return other.magCompareTo(this);
} else {
return this.magCompareTo(other);
}
}
magCompareTo(other: BigInt): i32 {
if (this.n > other.n) return 1;
if (this.n < other.n) return -1;
for (let i = this.n - 1; i >= 0; i--) {
if (this.d[i] != other.d[i]) {
if (this.d[i] < other.d[i]) return -1;
else return 1;
}
}
return 0;
}
// CORE MATH OPERATIONS //////////////////////////////////////////////////////////////////////////////////////////////
// signed addition
add<T>(other: T): BigInt {
const addend: BigInt = BigInt.from(other);
if (this.isNeg == addend.isNeg) {
return this._add(addend, this.isNeg);
} else if (this.magCompareTo(addend) < 0) {
return addend._sub(this, addend.isNeg);
} else {
return this._sub(addend, this.isNeg);
}
}
// signed subtraction
sub<T>(other: T): BigInt {
const subtrahend: BigInt = BigInt.from(other);
if (this.isNeg != subtrahend.isNeg) {
return this._add(subtrahend, this.isNeg);
} else if (this.magCompareTo(subtrahend) >= 0) {
return this._sub(subtrahend, this.isNeg);
} else {
return subtrahend._sub(this, !this.isNeg);
}
}
// unsigned addition
private _add(other: BigInt, resultIsNegative: boolean): BigInt {
// determine which summand is larger
let min: i32;
let max: i32;
let x: BigInt;
if (this.n > other.n) {
min = other.n;
max = this.n;
x = this;
} else {
min = this.n;
max = other.n;
x = other;
}
// initialize result
const res: BigInt = BigInt.getEmptyResultContainer(
max + 1,
resultIsNegative,
max
);
// add
let carry: u32 = 0;
let i: i32 = 0;
for (; i < min; i++) {
res.d[i] = this.d[i] + other.d[i] + carry;
carry = res.d[i] >> BigInt.p;
res.d[i] &= BigInt.digitMask;
}
if (min != max) {
for (; i < max; i++) {
res.d[i] = x.d[i] + carry;
carry = res.d[i] >> BigInt.p;
res.d[i] &= BigInt.digitMask;
}
}
if (carry > 0) {
res.d[max] = carry;
res.n++;
}
return res;
}
// unsigned subtraction
private _sub(other: BigInt, resultIsNegative: boolean): BigInt {
const min: i32 = other.n;
const max: i32 = this.n;
// initialize result
const res: BigInt = BigInt.getEmptyResultContainer(
max,
resultIsNegative,
max
);
// subtract
let carry: u32 = 0;
let i: i32 = 0;
for (; i < min; i++) {
res.d[i] = this.d[i] - other.d[i] - carry;
carry = res.d[i] >> (BigInt.actualBits - 1);
res.d[i] &= BigInt.digitMask;
}
if (min < max) {
for (; i < max; i++) {
res.d[i] = this.d[i] - carry;
carry = res.d[i] >> (BigInt.actualBits - 1);
res.d[i] &= BigInt.digitMask;
}
}
// trim and return
res.trimLeadingZeros();
return res;
}
// unsigned addition of 1
private _addOne(resultIsNegative: boolean): BigInt {
const res: BigInt = BigInt.getEmptyResultContainer(
this.n + 1,
resultIsNegative,
this.n
);
let carry = 1;
for (let i = 0; i < this.n; i++) {
res.d[i] = this.d[i] + carry;
carry = res.d[i] >> BigInt.p;
res.d[i] &= BigInt.digitMask;
}
if (carry > 0) {
res.d[this.n] = carry;
res.n++;
}
res.trimLeadingZeros();
return res;
}
// unsigned subtraction of 1
private _subOne(resultIsNegative: boolean): BigInt {
const res: BigInt = BigInt.getEmptyResultContainer(
this.n,
resultIsNegative,
this.n
);
let carry = 1;
for (let i = 0; i < this.n; i++) {
res.d[i] = this.d[i] - carry;
carry = res.d[i] >> (BigInt.actualBits - 1);
res.d[i] &= BigInt.digitMask;
}
res.trimLeadingZeros();
return res;
}
// efficient multiply by 2
mul2(): BigInt {
const res: BigInt = BigInt.getEmptyResultContainer(
this.n + 1,
this.isNeg,
this.n
);
let r: u32 = 0;
for (let i = 0; i < this.n; i++) {
const rr: u32 = this.d[i] >> (BigInt.p - 1);
res.d[i] = ((this.d[i] << 1) | r) & BigInt.digitMask;
r = rr;
}
if (r != 0) {
res.d[res.n++] = 1;
}
return res;
}
// efficient div by 2
div2(): BigInt {
const res: BigInt = BigInt.getEmptyResultContainer(
this.n,
this.isNeg,
this.n
);
let r: u32 = 0;
for (let i = this.n - 1; i >= 0; i--) {
const rr: u32 = this.d[i] % 2;
res.d[i] = (this.d[i] >> 1) | (r << (BigInt.p - 1));
r = rr;
}
res.trimLeadingZeros();
return res;
}
// multiples BigInt by power of basis
// *** mutates BigInt ***
private mulBasisPow(b: i32): void {
if (b <= 0) return;
this.grow(this.n + b);
this.n += b;
let i: i32 = this.n - 1;
let j: i32 = this.n - 1 - b;
for (; i >= b; i--, j--) {
this.d[i] = this.d[j];
}
for (; i >= 0; i--) {
this.d[i] = 0;
}
}
// divides BigInt by power of basis
// *** mutates BigInt ***
private divBasisPow(b: i32): void {
if (b <= 0) return;
// integer division with denominator > numerator = 0
if (this.n <= b) {
this.n = 0;
this.trimLeadingZeros();
return;
}
// division
let i: i32 = 0;
let j: i32 = b;
for (; i < this.n - b; i++, j++) {
this.d[i] = this.d[j];
}
for (; i < this.n; i++) {
this.d[i] = 0;
}
this.n -= b;
}
// multiply by power of 2
// O(2N)
mulPowTwo(k: i32): BigInt {
if (k <= 0) {
return this.copy();
}
const minSize: i32 = this.n + k / BigInt.p + 1;
const res = BigInt.fromDigits(this.d, this.isNeg, this.n, minSize);
// shift by entire digits
if (k >= BigInt.p) {
res.mulBasisPow(k / BigInt.p);
}
// shift by k % p bits
const remK: i32 = k % BigInt.p;
if (remK != 0) {
const mask: u32 = <u32>((1 << remK) - 1);
const shift: i32 = BigInt.p - remK;
let r: u32 = 0;
for (let i = 0; i < res.n; i++) {
const rr: u32 = (res.d[i] >> shift) & mask;
res.d[i] = ((res.d[i] << remK) | r) & BigInt.digitMask;
r = rr;
}
if (r != 0) {
res.d[res.n++] = r;
}
}
return res;
}
// divide by power of 2
divPowTwo(k: i32): BigInt {
const res = this.copy();
if (k <= 0) {
return res;
}
if (k >= BigInt.p) {
res.divBasisPow(k / BigInt.p);
}
const remK: i32 = k % BigInt.p;
if (remK != 0) {
const mask: u32 = <u32>((1 << remK) - 1);
const shift: i32 = BigInt.p - remK;
let r: u32 = 0;
for (let i = res.n - 1; i >= 0; i--) {
const rr: u32 = res.d[i] & mask;
res.d[i] = (res.d[i] >> remK) | (r << shift);
r = rr;
}
}
res.trimLeadingZeros();
return res;
}
// remainder of division by power of 2
modPowTwo(k: i32): BigInt {
if (k == 0) {
return BigInt.fromUInt16(<u16>0);
}
const res = this.copy();
// if 2^k > BigInt, then BigInt % 2^k == BigInt
if (k > this.n * BigInt.p) {
return res;
}
// zero out unused digits (any digit greater than 2^b)
const kDivP: i32 = k / BigInt.p;
let i: i32 = kDivP + (k % BigInt.p) == 0 ? 0 : 1; // ceil of k / p
for (; i < res.n; i++) {
res.d[i] = 0;
}
// mod the remaining leading digit (which includes 2^b) using bitmask
// remK = k % BigIntMP.p
res.d[kDivP] &= ((<u32>1) << k % BigInt.p) - <u32>1;
// trim and return
res.trimLeadingZeros();
return res;
}
// left bit shift
leftShift(k: i32): BigInt {
if (k == 0) return this.copy();
if (k < 0) return this.rightShiftByAbsolute(k);
return this.leftShiftByAbsolute(k);
}
// signed right bit shift
rightShift(k: i32): BigInt {
if (k == 0) return this.copy();
if (k < 0) return this.leftShiftByAbsolute(k);
return this.rightShiftByAbsolute(k);
}
private leftShiftByAbsolute(k: i32): BigInt {
return this.mulPowTwo(k >>> 0);
}
private rightShiftByAbsolute(k: i32): BigInt {
const shift: i32 = k >>> 0;
// shift by max if result would equal 0
if (this.n - shift / BigInt.p <= 0) {
return BigInt.rightShiftByMaximum(this.isNeg);
}
// arithmetic shift
const res: BigInt = this.divPowTwo(shift);
// for negative numbers, round down if a bit would be shifted out
// Since the result is negative, rounding down means adding one to its absolute value. This cannot overflow.
if (this.rightShiftMustRoundDown(shift)) {
return res._addOne(true);
}
return res;
}
// For negative numbers, round down if any bit was shifted out (so that
// e.g. -5n >> 1n == -3n and not -2n). Check now whether this will happen
// and whether it can cause overflow into a new digit. If we allocate the
// result large enough up front, it avoids having to do grow it later.
private rightShiftMustRoundDown(k: i32): boolean {
if (this.isNeg) {
const digitShift: i32 = k / BigInt.p;
const remK: i32 = k % BigInt.p;
const mask: u32 = <u32>((1 << remK) - 1);
if ((this.d[digitShift] & mask) != 0) {
return true;
} else {
for (let i = 0; i < digitShift; i++) {
if (this.d[i] != 0) {
return true;
}
}
}
}
return false;
}
private static rightShiftByMaximum(isNeg: boolean): BigInt {
if (isNeg) {
return BigInt.NEG_ONE;
}
return BigInt.ZERO;
}
// MULTIPLICATION ////////////////////////////////////////////////////////////////////////////////////////////////////
// chooses best multiplication algorithm for situation and handles sign
mul<T>(other: T): BigInt {
const multiplier: BigInt = BigInt.from(other);
let res: BigInt;
const digitsNeeded: i32 = this.n + multiplier.n + 1;
const minN: i32 = this.n <= multiplier.n ? this.n : multiplier.n;
if (digitsNeeded < BigInt.maxComba && minN < BigInt.maxComba) {
res = this._mulComba(multiplier, digitsNeeded);
} else {
res = this._mulPartial(multiplier, digitsNeeded);
}
res.isNeg = this.isNeg != multiplier.isNeg && res.n > 0;
return res;
}
// unsigned multiplication that returns at most maxDigits
private _mulPartial(other: BigInt, maxDigits: i32): BigInt {
// const min: i32 = this.n <= other.n ? this.n : other.n;
// optimization -> use Comba multiplication if possible
// if (maxDigits < BigInt.maxComba && min < BigInt.maxComba) {
// return this._mulComba(other, maxDigits);
// }
const res = BigInt.getEmptyResultContainer(maxDigits, false, maxDigits);
// multiply using standard O(N^2) method taught in schools
for (let i = 0; i < this.n; i++) {
let r: u32 = 0;
const digsSubI: i32 = maxDigits - i;
const limitedN: i32 = other.n < digsSubI ? other.n : digsSubI;
for (let j = 0; j < limitedN; j++) {
const rr: u64 = <u64>res.d[i + j] + <u64>this.d[i] * other.d[j] + r;
res.d[i + j] = <u32>(rr & (<u64>BigInt.digitMask));
r = <u32>(rr >> BigInt.p);
}
if (i + limitedN < maxDigits) {
res.d[i + limitedN] = r;
}
}
res.trimLeadingZeros();
return res;
}
// fast unsigned multiplication using Comba method
private _mulComba(other: BigInt, maxDigits: i32): BigInt {
const totalN = this.n + other.n;
const outerN: i32 = maxDigits < totalN ? maxDigits : totalN; // number of output digits to produce
const res = BigInt.getEmptyResultContainer(outerN, false, outerN);
let w: u64 = 0;
// multiply, ignoring carries
for (let i = 0; i < outerN; i++) {
// calculate tY and tX, offsets into the multiplicands
const maxJ: i32 = other.n - 1;
const tY: i32 = maxJ < i ? maxJ : i;
const tX: i32 = i - tY;
// calculate innerN, the number of times inner loop will iterate
const distFromEnd: i32 = this.n - tX;
const currentN: i32 = tY + 1;
const innerN: i32 = distFromEnd < currentN ? distFromEnd : currentN;
for (let j = 0; j < innerN; j++) {
w += <u64>this.d[tX + j] * other.d[tY - j];
}
res.d[i] = (<u32>w) & BigInt.digitMask;
w = w >> BigInt.p;
}
res.trimLeadingZeros();
return res;
}
// EXPONENTIATION ////////////////////////////////////////////////////////////////////////////////////////////////////
pow(k: i32): BigInt {
if (k < 0) {
throw new RangeError('BigInt does not support negative exponentiation');
}
let temp: BigInt = this.copy();
let res: BigInt = BigInt.ONE;
while (k > 0) {
/* if the bit is set multiply */
if ((k & 1) != 0) res = res.mul(temp);
/* square */
if (k > 1) temp = temp.square();
/* shift to next bit */
k >>= 1;
}
return res;
}
square(): BigInt {
const digitsNeeded: i32 = this.n + this.n + 1;
if (digitsNeeded < BigInt.maxComba) {
return this._squareComba();
} else {
return this._baseSquare();
}
}
private _baseSquare(): BigInt {
const size: i32 = this.n + this.n + 1;
const res = BigInt.getEmptyResultContainer(size, false, size);
for (let i = 0; i < size; i++) {
let j: i32;
// first calculate the digit at 2*i and the double precision result
let r: u64 = <u64>this.d[i] * this.d[i] + res.d[i + i];
// store lower part in result
res.d[i + i] = <u32>(r & BigInt.digitMask);
// get the carry
let u: u32 = <u32>(r >> BigInt.p);
for (j = i + 1; j < size; j++) {
// first calculate the product
r = <u64>this.d[i] * this.d[j];
// now calculate the double precision result
r = <u64>res.d[i + j] + r + r + u;
// store lower part
res.d[i + j] = <u32>(r & BigInt.digitMask);
// get carry
u = <u32>(r >> BigInt.p);
}
// propagate upwards
while (u != 0) {
r = <u64>res.d[i + j] + u;
res.d[i + j] = <u32>(r & BigInt.digitMask);
u = <u32>(r >> BigInt.p);
++j;
}
}
res.trimLeadingZeros();
return res;
}
private _squareComba(): BigInt {
const size: i32 = this.n + this.n;
const res = BigInt.getEmptyResultContainer(size, false, size);
let u: u64 = 0;
for (let i = 0; i < size; i++) {
/* clear accumulator */
let accum: u64 = 0;
/* get offsets into the two BigInts */
const nSub1: i32 = this.n - 1;
const y: i32 = nSub1 < i ? nSub1 : i; // min
const x: i32 = i - y;
/* this is the number of times the loop will iterate, essentially
while (x++ < this.n && y-- >= 0) { ... }
*/
const nSubX: i32 = this.n - x;
const yAdd1: i32 = y + 1;
let j: i32 = nSubX < yAdd1 ? nSubX : yAdd1; // min
/* now for squaring x can never equal y
* we halve the distance since they approach at a rate of 2*
* and we have to round because odd cases need to be executed
*/
const shiftedDiff: i32 = (y - x + 1) >> 1;
j = j < shiftedDiff ? j : shiftedDiff;
/* execute loop */
for (let k = 0; k < j; k++) {
accum += <u64>this.d[x + k] * this.d[y - k];
}
/* double the inner product and add carry */
accum = accum + accum + u;
/* even columns have the square term in them */
if (((<u32>i) & 1) == 0) {
accum += <u64>this.d[i >> 1] * this.d[i >> 1];
}
/* store it */
res.d[i] = (<u32>accum) & BigInt.digitMask;
/* make next carry */
u = accum >> BigInt.p;
}
res.trimLeadingZeros();
return res;
}
sqrt(): BigInt {
if (this.isNeg)
throw new RangeError('Square root of negative numbers is not supported');
if (this.n == 0) return this.copy();
// rely on built in sqrt if possible
if (this.lte(BigInt.fromUInt64(<u64>F64.MAX_SAFE_INTEGER))) {
const fVal: f64 = <f64>this.toUInt64();
const fSqrt: f64 = Math.floor(Math.sqrt(fVal));
return BigInt.fromUInt64(<u64>fSqrt);
}
// Newton Raphson iteration
let z: BigInt = this; // eslint-disable-line @typescript-eslint/no-this-alias
let x: BigInt = BigInt.fromUInt16(1).mulPowTwo(this.countBits() / 2);
x = this.div(x).add(x).div2();
while (x < z) {
z = x;
x = this.div(x).add(x).div2();
}
return z;
}
// DIVISION //////////////////////////////////////////////////////////////////////////////////////////////////////////
// handles sign and allows for easy replacement of algorithm in future update
div<T>(other: T): BigInt {
return this._div(BigInt.from(other));
}
// handles sign and allows for easy replacement of algorithm in future update
mod<T>(other: T): BigInt {
return this._divRemainder(BigInt.from(other));
}
// returns [quotient, remainder]
divMod<T>(other: T): BigInt[] {
return this._divMod(BigInt.from(other));
}
private _div(other: BigInt): BigInt {
if (other.eq(BigInt.fromUInt16(0))) {
throw new Error('Divide by zero');
}
const cmp: i32 = this.magCompareTo(other);
if (cmp < 0) {
return BigInt.fromUInt16(0);
} else if (cmp == 0) {
const q = BigInt.fromUInt16(1);
q.isNeg = this.isNeg != other.isNeg;
return q;
}
const res: BigInt[] = this._divCore(other);
const q: BigInt = res[0];
q.isNeg = this.isNeg != other.isNeg;
q.trimLeadingZeros();
return q;
}
private _divRemainder(other: BigInt): BigInt {
if (other.eq(BigInt.fromUInt16(0))) {
throw new Error('Divide zero error');
}
const cmp: i32 = this.magCompareTo(other);
if (cmp < 0) {
return this.copy();
} else if (cmp == 0) {
return BigInt.fromUInt16(0);
}
const res: BigInt[] = this._divCore(other);
const r: BigInt = res[1];
r.isNeg = this.isNeg;
r.trimLeadingZeros();
return r;
}
// returns [quotient, remainder]
private _divMod(other: BigInt): BigInt[] {
if (other.eq(BigInt.fromUInt16(0))) {
throw new Error('Divide by zero');
}
const cmp: i32 = this.magCompareTo(other);
if (cmp < 0) {
return [BigInt.fromUInt16(0), this.copy()];
} else if (cmp == 0) {
const q = BigInt.fromUInt16(1);
q.isNeg = this.isNeg != other.isNeg;
return [q, BigInt.fromUInt16(0)];
}
const res: BigInt[] = this._divCore(other);
const q: BigInt = res[0];
const r: BigInt = res[1];
r.isNeg = this.isNeg;
r.trimLeadingZeros();
q.isNeg = this.isNeg != other.isNeg;
q.trimLeadingZeros();
return [q, r];
}
private _divCore(other: BigInt): BigInt[] {
let q: BigInt = BigInt.fromUInt16(0);
let tempQ = BigInt.fromUInt16(1);
let n: i32 = this.countBits() - other.countBits();
let tempA = this.abs();
let tempB = other.abs();
tempB = tempB.mulPowTwo(n);
tempQ = tempQ.mulPowTwo(n);
for (; n >= 0; n--) {
if (tempB.magCompareTo(tempA) <= 0) {
tempA = tempA.sub(tempB);
q = q.add(tempQ);
}
tempB = tempB.div2();
tempQ = tempQ.div2();
}
return [q, tempA];
}
// divides and rounds to nearest integer
roundedDiv<T>(other: T): BigInt {
const divisor: BigInt = BigInt.from(other);
if (divisor.eq(BigInt.fromUInt16(0))) {
throw new Error('Divide by zero');
}
if (this.isZero()) {
return BigInt.fromUInt16(0);
}
const r: BigInt = divisor.div2();
if (this.isNeg != divisor.isNeg) {
r.isNeg = !r.isNeg;
}
return this.add(r).div(divisor);
}
// SINGLE-DIGIT HELPERS //////////////////////////////////////////////////////////////////////////////////////////////
addInt(b: u32): BigInt {
return this.add(BigInt.fromUInt32(b));
}
subInt(b: u32): BigInt {
return this.sub(BigInt.fromUInt32(b));
}
mulInt(b: u32): BigInt {
if (b > 268435456) {
return this.mul(BigInt.fromUInt32(b));
}
const res = BigInt.fromDigits(this.d, this.isNeg, this.n, this.n + 1);
let r: u32 = 0;
for (let i = 0; i < this.n; i++) {
const rr: u64 = <u64>this.d[i] * <u64>b + <u64>r;
res.d[i] = <u32>(rr & (<u64>BigInt.digitMask));
r = <u32>(rr >> BigInt.p);
}
if (r != 0) {
res.d[res.n++] = r;
}
return res;
}
// MUTATES
private inplaceMulInt(b: u32): BigInt {
if (b > 268435456) {
return this.mul(BigInt.fromUInt32(b));
}
this.grow(this.n + 1);
let r: u32 = 0;
for (let i = 0; i < this.n; i++) {
const rr: u64 = <u64>this.d[i] * <u64>b + <u64>r;
this.d[i] = <u32>(rr & (<u64>BigInt.digitMask));
r = <u32>(rr >> BigInt.p);
}
if (r != 0) {
this.d[this.n++] = r;
}
return this;
}
divInt(b: u32): BigInt {
if (b == 0) throw new Error('Divide by zero');
// try optimizations
if (b == 1 || this.n == 0) return this.copy();
const pow2Bit: i32 = BigInt.isPow2(b);
if (pow2Bit != 0) return this.divPowTwo(pow2Bit);
// divide
const q = BigInt.getEmptyResultContainer(this.n, this.isNeg, this.n);
let r: u64 = 0;
let val: u32;
for (let i = this.n - 1; i >= 0; i--) {
r = (r << BigInt.p) | (<u64>this.d[i]);
if (r >= b) {
val = <u32>(r / b);
r -= <u64>val * <u64>b;
} else {
val = 0;
}
q.d[i] = val;
}
q.trimLeadingZeros();
return q;
}
// MUTATES
private inplaceDivInt(b: u32): BigInt {
if (b == 0) throw new Error('Divide by zero');
// try optimizations
if (b == 1 || this.n == 0) return this;
const pow2Bit: i32 = BigInt.isPow2(b);
if (pow2Bit != 0) return this.divPowTwo(pow2Bit);
// divide
let r: u64 = 0;
let val: u32;
for (let i = this.n - 1; i >= 0; i--) {
r = (r << BigInt.p) | (<u64>this.d[i]);
if (r >= b) {
val = <u32>(r / b);
r -= <u64>val * <u64>b;
} else {
val = 0;
}
this.d[i] = val;
}
this.trimLeadingZeros();
return this;
}
modInt(b: u32): u32 {
if (b == 0) throw new Error('Divide by zero');
// try optimizations
if (b == 1 || this.n == 0) {
return 0;
}
const pow2Bit: i32 = BigInt.isPow2(b);
if (pow2Bit != 0) {
return this.d[0] & (((<u32>1) << pow2Bit) - <u32>1);
}
// divide
let r: u64 = 0;
let val: u32;
for (let i = this.n - 1; i >= 0; i--) {
r = (r << BigInt.p) | (<u64>this.d[i]);
if (r >= b) {
val = <u32>(r / b);
r -= <u64>val * <u64>b;
} else {
val = 0;
}
}
return <u32>r;
}
// returns [quotient, remainder]
divModInt(b: u32): BigInt[] {
if (b == 0) throw new Error('Divide by zero');
// try optimizations
if (b == 1 || this.n == 0) {
return [this.copy(), BigInt.ZERO];
}
const pow2Bit: i32 = BigInt.isPow2(b);
if (pow2Bit != 0) {
const q: BigInt = this.divPowTwo(pow2Bit);
const r: u32 = this.d[0] & (((<u32>1) << pow2Bit) - <u32>1);
return [q, BigInt.fromUInt32(r)];
}
// divide
const q = BigInt.getEmptyResultContainer(this.n, this.isNeg, this.n);
let r: u64 = 0;
let val: u32;
for (let i = this.n - 1; i >= 0; i--) {
r = (r << BigInt.p) | (<u64>this.d[i]);
if (r >= b) {
val = <u32>(r / b);
r -= <u64>val * <u64>b;
} else {
val = 0;
}
q.d[i] = val;
}
q.trimLeadingZeros();
return [q, BigInt.fromUInt32(<u32>r)];
}
// divides and rounds to nearest integer
roundedDivInt(b: u32): BigInt {
if (b == 0) throw new Error('Divide by zero');
if (this.isZero()) {
return BigInt.fromUInt16(0);
}
const r: BigInt = BigInt.fromUInt32(b >> 1);
if (this.isNeg) {
r.isNeg = true;
}
return this.add(r).divInt(b);
}
// BITWISE OPERATIONS ////////////////////////////////////////////////////////////////////////////////////////////////
.prefix('~')
bitwiseNot(): BigInt {
if (this.isNeg) {
// ~(-x) == ~(~(x-1)) == x-1
return this._subOne(false);
}
// ~x == -x-1 == -(x+1)
return this._addOne(true);
}
bitwiseAnd<T>(other: T): BigInt {
// eslint-disable-next-line @typescript-eslint/no-this-alias
let a: BigInt = this;
let b: BigInt = BigInt.from(other);
if (!a.isNeg && !b.isNeg) {
return BigInt._and(a, b);
} else if (a.isNeg && b.isNeg) {
// (-x) & (-y) == ~(x-1) & ~(y-1) == ~((x-1) | (y-1))
// == -(((x-1) | (y-1)) + 1)
const a1 = a._subOne(false);
const b1 = b._subOne(false);
return BigInt._or(a1, b1)._addOne(true);
}
// Assume that 'a' is the positive BigInt
if (a.isNeg) {
const temp: BigInt = a;
a = b;
b = temp;
}
// x & (-y) == x & ~(y-1) == x &~ (y-1)
const b1 = b._subOne(false);
return a._andNot(b1);
}
bitwiseOr<T>(other: T): BigInt {
// eslint-disable-next-line @typescript-eslint/no-this-alias
let a: BigInt = this;
let b: BigInt = BigInt.from(other);
if (!a.isNeg && !b.isNeg) {
return BigInt._or(a, b);
} else if (a.isNeg && b.isNeg) {
// (-x) | (-y) == ~(x-1) | ~(y-1) == ~((x-1) & (y-1))
// == -(((x-1) & (y-1)) + 1)
const a1: BigInt = a._subOne(false);
const b1: BigInt = b._subOne(false);
return BigInt._and(a1, b1)._addOne(true);
} else {
// Assume that 'a' is the positive BigInt
if (a.isNeg) {
const temp: BigInt = a;
a = b;
b = temp;
}
// x | (-y) == x | ~(y-1) == ~((y-1) &~ x) == -(((y-1) ~& x) + 1)
const b1: BigInt = b._subOne(false);
return b1._andNot(a)._addOne(true);
}
}
bitwiseXor<T>(other: T): BigInt {
// eslint-disable-next-line @typescript-eslint/no-this-alias
let a: BigInt = this;
let b: BigInt = BigInt.from(other);
if (!a.isNeg && !b.isNeg) {
return BigInt._xor(a, b);
} else if (a.isNeg && b.isNeg) {
// (-x) ^ (-y) == ~(x-1) ^ ~(y-1) == (x-1) ^ (y-1)
const a1: BigInt = a._subOne(false);
const b1: BigInt = b._subOne(false);
return BigInt._xor(a1, b1);
} else {
// Assume that 'a' is the positive BigInt
if (a.isNeg) {
const temp: BigInt = a;
a = b;
b = temp;
}
// x ^ (-y) == x ^ ~(y-1) == ~(x ^ (y-1)) == -((x ^ (y-1)) + 1)
const b1: BigInt = b._subOne(false);
return BigInt._xor(a, b1)._addOne(true);
}
}
// unsigned bitwise AND
private static _and(a: BigInt, b: BigInt): BigInt {
const numPairs: i32 = a.n < b.n ? a.n : b.n;
const res: BigInt = BigInt.getEmptyResultContainer(
numPairs,
false,
numPairs
);
let i = 0;
for (; i < numPairs; i++) {
res.d[i] = a.d[i] & b.d[i];
}
return res;
}
// unsigned bitwise AND NOT (i.e. a & ~b)
private _andNot(other: BigInt): BigInt {
const numPairs: i32 = this.n < other.n ? this.n : other.n;
const res: BigInt = BigInt.getEmptyResultContainer(this.n, false, this.n);
let i = 0;
for (; i < numPairs; i++) {
res.d[i] = this.d[i] & ~other.d[i];
}
for (; i < this.n; i++) {
res.d[i] = this.d[i];
}
return res;
}
// unsigned bitwise OR
private static _or(a: BigInt, b: BigInt): BigInt {
let numPairs: i32;
let resLength: i32;
if (a.n > b.n) {
numPairs = b.n;
resLength = a.n;
} else {
numPairs = a.n;
resLength = b.n;
}
const res: BigInt = BigInt.getEmptyResultContainer(
resLength,
false,
resLength
);
let i = 0;
for (; i < numPairs; i++) {
res.d[i] = a.d[i] | b.d[i];
}
for (; i < a.n; i++) {
res.d[i] = a.d[i];
}
for (; i < b.n; i++) {
res.d[i] = b.d[i];
}
return res;
}
// unsigned bitwise XOR
private static _xor(a: BigInt, b: BigInt): BigInt {
let numPairs: i32;
let resLength: i32;
if (a.n > b.n) {
numPairs = b.n;
resLength = a.n;
} else {
numPairs = a.n;
resLength = b.n;
}
const res: BigInt = BigInt.getEmptyResultContainer(
resLength,
false,
resLength
);
let i = 0;
for (; i < numPairs; i++) {
res.d[i] = a.d[i] ^ b.d[i];
}
for (; i < a.n; i++) {
res.d[i] = a.d[i];
}
for (; i < b.n; i++) {
res.d[i] = b.d[i];
}
return res;
}
// UTILITY ///////////////////////////////////////////////////////////////////////////////////////////////////////////
countBits(): i32 {
if (this.n == 0) return 0;
// initialize to bits in fully used digits
let bits: i32 = (this.n - 1) * BigInt.p;
// count bits used in most significant digit
let q: u32 = this.d[this.n - 1];
while (q > 0) {
++bits;
q >>= 1;
}
return bits;
}
isOdd(): boolean {
return this.n > 0 && (this.d[0] & 1) == 1;
}
isZero(): boolean {
return this.n == 0;
}
private static isPow2(b: u32): i32 {
for (let i = 1; i < BigInt.p; i++) {
if (b == (<u32>1) << i) {
return i;
}
}
return 0;
}
// SYNTAX SUGAR ///////////////////////////////////////////////////////////////////////////////////////////////////
// BigInt with value 0
static get ZERO(): BigInt {
return BigInt.fromUInt16(0);
}
// BigInt with value 1
static get ONE(): BigInt {
return BigInt.fromUInt16(1);
}
// BigInt with value -1
static get NEG_ONE(): BigInt {
const res: BigInt = BigInt.fromUInt16(1);
res.isNeg = true;
return res;
}
static eq<T, U>(left: T, right: U): boolean {
const a: BigInt = BigInt.from(left);
return a.eq(right);
}
private static eqOp(left: BigInt, right: BigInt): boolean {
return left.eq(right);
}
static ne<T, U>(left: T, right: U): boolean {
const a: BigInt = BigInt.from(left);
return a.ne(right);
}
private static neOp(left: BigInt, right: BigInt): boolean {
return left.ne(right);
}
static lt<T, U>(left: T, right: U): boolean {
const a: BigInt = BigInt.from(left);
return a.lt(right);
}
private static ltOp(left: BigInt, right: BigInt): boolean {
return left.lt(right);
}
static lte<T, U>(left: T, right: U): boolean {
const a: BigInt = BigInt.from(left);
return a.lte(right);
}
private static lteOp(left: BigInt, right: BigInt): boolean {
return left.lte(right);
}
static gt<T, U>(left: T, right: U): boolean {
const a: BigInt = BigInt.from(left);
return a.gt(right);
}
private static gtOp(left: BigInt, right: BigInt): boolean {
return left.gt(right);
}
static gte<T, U>(left: T, right: U): boolean {
const a: BigInt = BigInt.from(left);
return a.gte(right);
}
private static gteOp(left: BigInt, right: BigInt): boolean {
return left.gte(right);
}
static add<T, U>(left: T, right: U): BigInt {
const a: BigInt = BigInt.from(left);
return a.add(right);
}
private static addOp(left: BigInt, right: BigInt): BigInt {
return left.add(right);
}
static sub<T, U>(left: T, right: U): BigInt {
const a: BigInt = BigInt.from(left);
return a.sub(right);
}
private static subOp(left: BigInt, right: BigInt): BigInt {
return left.sub(right);
}
static mul<T, U>(left: T, right: U): BigInt {
const a: BigInt = BigInt.from(left);
return a.mul(right);
}
private static mulOp(left: BigInt, right: BigInt): BigInt {
return left.mul(right);
}
static div<T, U>(left: T, right: U): BigInt {
const a: BigInt = BigInt.from(left);
return a.div(right);
}
static divOp(left: BigInt, right: BigInt): BigInt {
return left.div(right);
}
static mod<T, U>(left: T, right: U): BigInt {
const a: BigInt = BigInt.from(left);
return a.mod(right);
}
private static modOp(left: BigInt, right: BigInt): BigInt {
return left.mod(right);
}
static pow<T>(base: T, k: i32): BigInt {
const x: BigInt = BigInt.from(base);
return x.pow(k);
}
// note: the right-hand operand must be a positive integer that fits in an i32
private static powOp(left: BigInt, right: BigInt): BigInt {
return left.pow(right.toInt32());
}
// note: the right-hand operand must be a positive integer that fits in an i32
private static leftShift(left: BigInt, right: BigInt): BigInt {
return left.leftShift(right.toInt32());
}
// note: the right-hand operand must be a positive integer that fits in an i32
private static rightShift(left: BigInt, right: BigInt): BigInt {
return left.rightShift(right.toInt32());
}
static bitwiseNot<T>(a: T): BigInt {
return BigInt.from(a).bitwiseNot();
}
static bitwiseAnd<T, U>(a: T, b: U): BigInt {
const left: BigInt = BigInt.from(a);
return left.bitwiseAnd(b);
}
private static bitwiseAndOp(a: BigInt, b: BigInt): BigInt {
return a.bitwiseAnd(b);
}
static bitwiseOr<T, U>(a: T, b: U): BigInt {
const left: BigInt = BigInt.from(a);
return left.bitwiseOr(b);
}
private static bitwiseOrOp(a: BigInt, b: BigInt): BigInt {
return a.bitwiseOr(b);
}
static bitwiseXor<T, U>(a: T, b: U): BigInt {
const left: BigInt = BigInt.from(a);
return left.bitwiseXor(b);
}
private static bitwiseXorOp(a: BigInt, b: BigInt): BigInt {
return a.bitwiseXor(b);
}
}