160 lines
4.1 KiB
JavaScript
160 lines
4.1 KiB
JavaScript
// https://github.com/juanelas/bigint-mod-arith
|
|
|
|
window["bigint-mod-arith"] = function () {
|
|
/**
|
|
* Absolute value. abs(a)==a if a>=0. abs(a)==-a if a<0
|
|
*
|
|
* @param a
|
|
*
|
|
* @returns The absolute value of a
|
|
*/
|
|
function abs(a) {
|
|
return (a >= 0) ? a : -a;
|
|
}
|
|
|
|
/**
|
|
* An iterative implementation of the extended euclidean algorithm or extended greatest common divisor algorithm.
|
|
* Take positive integers a, b as input, and return a triple (g, x, y), such that ax + by = g = gcd(a, b).
|
|
*
|
|
* @param a
|
|
* @param b
|
|
*
|
|
* @throws {RangeError}
|
|
* This excepction is thrown if a or b are less than 0
|
|
*
|
|
* @returns A triple (g, x, y), such that ax + by = g = gcd(a, b).
|
|
*/
|
|
function eGcd(a, b) {
|
|
if (typeof a === 'number')
|
|
a = BigInt(a);
|
|
if (typeof b === 'number')
|
|
b = BigInt(b);
|
|
if (a <= 0n || b <= 0n)
|
|
throw new RangeError('a and b MUST be > 0'); // a and b MUST be positive
|
|
let x = 0n;
|
|
let y = 1n;
|
|
let u = 1n;
|
|
let v = 0n;
|
|
while (a !== 0n) {
|
|
const q = b / a;
|
|
const r = b % a;
|
|
const m = x - (u * q);
|
|
const n = y - (v * q);
|
|
b = a;
|
|
a = r;
|
|
x = u;
|
|
y = v;
|
|
u = m;
|
|
v = n;
|
|
}
|
|
return {
|
|
g: b,
|
|
x: x,
|
|
y: y
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Finds the smallest positive element that is congruent to a in modulo n
|
|
*
|
|
* @remarks
|
|
* a and b must be the same type, either number or bigint
|
|
*
|
|
* @param a - An integer
|
|
* @param n - The modulo
|
|
*
|
|
* @throws {RangeError}
|
|
* Excpeption thrown when n is not > 0
|
|
*
|
|
* @returns A bigint with the smallest positive representation of a modulo n
|
|
*/
|
|
function toZn(a, n) {
|
|
if (typeof a === 'number')
|
|
a = BigInt(a);
|
|
if (typeof n === 'number')
|
|
n = BigInt(n);
|
|
if (n <= 0n) {
|
|
throw new RangeError('n must be > 0');
|
|
}
|
|
const aZn = a % n;
|
|
return (aZn < 0n) ? aZn + n : aZn;
|
|
}
|
|
|
|
/**
|
|
* Modular inverse.
|
|
*
|
|
* @param a The number to find an inverse for
|
|
* @param n The modulo
|
|
*
|
|
* @throws {RangeError}
|
|
* Excpeption thorwn when a does not have inverse modulo n
|
|
*
|
|
* @returns The inverse modulo n
|
|
*/
|
|
function modInv(a, n) {
|
|
const egcd = eGcd(toZn(a, n), n);
|
|
if (egcd.g !== 1n) {
|
|
throw new RangeError(`${a.toString()} does not have inverse modulo ${n.toString()}`); // modular inverse does not exist
|
|
} else {
|
|
return toZn(egcd.x, n);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Modular exponentiation b**e mod n. Currently using the right-to-left binary method
|
|
*
|
|
* @param b base
|
|
* @param e exponent
|
|
* @param n modulo
|
|
*
|
|
* @throws {RangeError}
|
|
* Excpeption thrown when n is not > 0
|
|
*
|
|
* @returns b**e mod n
|
|
*/
|
|
function modPow(b, e, n) {
|
|
if (typeof b === 'number')
|
|
b = BigInt(b);
|
|
if (typeof e === 'number')
|
|
e = BigInt(e);
|
|
if (typeof n === 'number')
|
|
n = BigInt(n);
|
|
if (n <= 0n) {
|
|
throw new RangeError('n must be > 0');
|
|
} else if (n === 1n) {
|
|
return 0n;
|
|
}
|
|
b = toZn(b, n);
|
|
if (e < 0n) {
|
|
return modInv(modPow(b, abs(e), n), n);
|
|
}
|
|
let r = 1n;
|
|
while (e > 0) {
|
|
if ((e % 2n) === 1n) {
|
|
r = r * b % n;
|
|
}
|
|
e = e / 2n;
|
|
b = b ** 2n % n;
|
|
}
|
|
return r;
|
|
}
|
|
|
|
function bytesToBigInt(bytes) {
|
|
return BigInt("0x" + Array.from(bytes, byte => {
|
|
return ('0' + (byte & 0xFF).toString(16)).slice(-2);
|
|
}).join(''));
|
|
}
|
|
|
|
function bigIntToBytes(bigInt) {
|
|
let hex = bigInt.toString(16);
|
|
|
|
// Convert hex to bytes
|
|
let bytes = [];
|
|
for (let c = 0; c < hex.length; c += 2) {
|
|
bytes.push(parseInt(hex.substr(c, 2), 16));
|
|
}
|
|
return bytes;
|
|
}
|
|
|
|
return {bigIntToBytes, bytesToBigInt, modPow};
|
|
}(); |