diff --git a/modules/compute.js b/modules/compute.js index 9ef1a9b..db3b88c 100644 --- a/modules/compute.js +++ b/modules/compute.js @@ -1,57 +1,4 @@ -function xgcd(a, b) { - let [old_r, r] = [a, b]; - let [old_s, s] = [1n, 0n]; - let [old_t, t] = [0n, 1n]; - - while (r !== 0n) { - const quotient = old_r / r; - [old_r, r] = [r, old_r - quotient * r]; - [old_s, s] = [s, old_s - quotient * s]; - [old_t, t] = [t, old_t - quotient * t]; - } - - return [old_r, old_s, old_t]; -} - -function modinv(x, modulus) { - let [r, s, t] = xgcd(x, modulus); - if (r !== 1n) { - throw new Error(`no inverse exists - ${x} and ${modulus} are not coprime`); - } - - if (s < 0n) { - s += modulus; - } - - return s; -} - -function modpow(base, exponent, modulus) { - if (exponent < 0n) { - const p = modpow(base, -exponent, modulus); - return modinv(p, modulus); - } - - if (modulus === 1n) { - return 0n; - } - - let result = 1n; - base %= modulus; - - while (exponent > 0n) { - if (exponent % 2n === 1n) { - result *= base; - result %= modulus; - } - - exponent >>= 1n; - base *= base; - base %= modulus; - } - - return result; -} +import { tonelliShanks, modinv, modpow, isprime } from "./math.js"; function binaryOpPop(stack) { const b = stack.pop(); @@ -63,53 +10,6 @@ function binaryOpPop(stack) { return [a, b]; } -function witness(a, n) { - let d = n - 1n; - let s = 0; - while (d % 2n === 0n) { - d /= 2n; - s++; - } - - let x = modpow(a, d, n); - let y = null; - for (let i = 0; i < s; i++) { - y = modpow(x, 2n, n); - if (y === 1n && x !== 1n && x !== n - 1n) { - return true; - } - x = y - } - - return y !== 1n -} - -function randbigint() { - return BigInt(Math.floor(Math.random() * Number.MAX_SAFE_INTEGER)) -} - -function isprime(n) { - if (n === 2n) { - return true; - } else if (n % 2n === 0n) { - return false; - } - - const trials = 10; - for (let i = 0; i < trials; i++) { - const a = randbigint() % (n - 1n) + 1n; - if (witness(a, n)) { - return false; - } - } - - return true; -} - -function tonelliShanks(n, p) { - throw new Error("not implemented"); -} - function compute(queue, modulus) { const stack = []; for (const token of queue) { diff --git a/modules/math.js b/modules/math.js new file mode 100644 index 0000000..30edfee --- /dev/null +++ b/modules/math.js @@ -0,0 +1,103 @@ +function xgcd(a, b) { + let [old_r, r] = [a, b]; + let [old_s, s] = [1n, 0n]; + let [old_t, t] = [0n, 1n]; + + while (r !== 0n) { + const quotient = old_r / r; + [old_r, r] = [r, old_r - quotient * r]; + [old_s, s] = [s, old_s - quotient * s]; + [old_t, t] = [t, old_t - quotient * t]; + } + + return [old_r, old_s, old_t]; +} + +function modinv(x, modulus) { + let [r, s, t] = xgcd(x, modulus); + if (r !== 1n) { + throw new Error(`no inverse exists - ${x} and ${modulus} are not coprime`); + } + + if (s < 0n) { + s += modulus; + } + + return s; +} + +function modpow(base, exponent, modulus) { + if (exponent < 0n) { + const p = modpow(base, -exponent, modulus); + return modinv(p, modulus); + } + + if (modulus === 1n) { + return 0n; + } + + let result = 1n; + base %= modulus; + + while (exponent > 0n) { + if (exponent % 2n === 1n) { + result *= base; + result %= modulus; + } + + exponent >>= 1n; + base *= base; + base %= modulus; + } + + return result; +} + +function witness(a, n) { + let d = n - 1n; + let s = 0; + while (d % 2n === 0n) { + d /= 2n; + s++; + } + + let x = modpow(a, d, n); + let y = null; + for (let i = 0; i < s; i++) { + y = modpow(x, 2n, n); + if (y === 1n && x !== 1n && x !== n - 1n) { + return true; + } + x = y + } + + return y !== 1n +} + +function randbigint() { + return BigInt(Math.floor(Math.random() * Number.MAX_SAFE_INTEGER)) +} + +function isprime(n) { + if (n === 2n) { + return true; + } else if (n % 2n === 0n) { + return false; + } + + const trials = 10; + for (let i = 0; i < trials; i++) { + const a = randbigint() % (n - 1n) + 1n; + if (witness(a, n)) { + return false; + } + } + + return true; +} + +function tonelliShanks(n, p) { + throw new Error("not implemented"); +} + +export { tonelliShanks, modinv, modpow, isprime };