From e68a00a62cf02b2e6bc01b2a1b1426ebb36d6dd5 Mon Sep 17 00:00:00 2001 From: filifa Date: Thu, 11 Dec 2025 23:49:34 -0500 Subject: [PATCH] split into compute module --- main.js | 121 +-------------------------------------------- modules/compute.js | 120 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 122 insertions(+), 119 deletions(-) create mode 100644 modules/compute.js diff --git a/main.js b/main.js index 2ae4635..a88ff5f 100644 --- a/main.js +++ b/main.js @@ -1,126 +1,9 @@ import { tokenize } from "./modules/lexer.js"; import { shunt } from "./modules/parser.js"; - -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 > 0) { - if (exponent % 2n === 1n) { - result *= base; - result %= modulus; - } - - exponent >>= 1n; - base *= base; - base %= modulus; - } - - return result; -} - -function binaryOpPop(stack) { - const b = stack.pop(); - const a = stack.pop(); - if (a === undefined || b === undefined) { - throw new Error("invalid expression"); - } - - return [a, b]; -} - -function compute(queue, modulus) { - const stack = []; - for (const token of queue) { - if (typeof token === "bigint") { - stack.push(token); - } else if (token === "+") { - let [a, b] = binaryOpPop(stack); - a %= modulus; - b %= modulus; - const c = (a + b) % modulus; - stack.push(c); - } else if (token === "-") { - let [a, b] = binaryOpPop(stack); - a %= modulus; - b %= modulus; - const c = (a - b) % modulus; - stack.push(c); - } else if (token === "*") { - let [a, b] = binaryOpPop(stack); - a %= modulus; - b %= modulus; - const c = (a * b) % modulus; - stack.push(c); - } else if (token === "/") { - let [a, b] = binaryOpPop(stack); - a %= modulus; - b %= modulus; - const binv = modinv(b, modulus); - const c = (a * binv) % modulus; - stack.push(c); - } else if (token === "^") { - // FIXME: not always valid - consider 2^3^4 mod 7 - // also 2^(3+4) - // solution might be to change parser - // might also be better to just mod once at the end - // instead of with every op - const [a, b] = binaryOpPop(stack); - const c = modpow(a, b, modulus); - stack.push(c); - } - } - - if (stack.length !== 1) { - throw new Error("error evaluating expression"); - } - - let result = stack[0] % modulus; - if (result < 0n) { - result += modulus; - } - - return result; -} +import { compute } from "./modules/compute.js"; function calculate() { + // TODO: also have enter run this when expr/modulus is focused const expr = document.querySelector("#expr"); const modulus = document.querySelector("#modulus"); const m = BigInt(modulus.value); diff --git a/modules/compute.js b/modules/compute.js new file mode 100644 index 0000000..408417b --- /dev/null +++ b/modules/compute.js @@ -0,0 +1,120 @@ +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 > 0) { + if (exponent % 2n === 1n) { + result *= base; + result %= modulus; + } + + exponent >>= 1n; + base *= base; + base %= modulus; + } + + return result; +} + +function binaryOpPop(stack) { + const b = stack.pop(); + const a = stack.pop(); + if (a === undefined || b === undefined) { + throw new Error("invalid expression"); + } + + return [a, b]; +} + +function compute(queue, modulus) { + const stack = []; + for (const token of queue) { + if (typeof token === "bigint") { + stack.push(token); + } else if (token === "+") { + let [a, b] = binaryOpPop(stack); + a %= modulus; + b %= modulus; + const c = (a + b) % modulus; + stack.push(c); + } else if (token === "-") { + let [a, b] = binaryOpPop(stack); + a %= modulus; + b %= modulus; + const c = (a - b) % modulus; + stack.push(c); + } else if (token === "*") { + let [a, b] = binaryOpPop(stack); + a %= modulus; + b %= modulus; + const c = (a * b) % modulus; + stack.push(c); + } else if (token === "/") { + let [a, b] = binaryOpPop(stack); + a %= modulus; + b %= modulus; + const binv = modinv(b, modulus); + const c = (a * binv) % modulus; + stack.push(c); + } else if (token === "^") { + // FIXME: not always valid - consider 2^3^4 mod 7 + // also 2^(3+4) + // solution might be to change parser + // might also be better to just mod once at the end + // instead of with every op + const [a, b] = binaryOpPop(stack); + const c = modpow(a, b, modulus); + stack.push(c); + } + } + + if (stack.length !== 1) { + throw new Error("error evaluating expression"); + } + + let result = stack[0] % modulus; + if (result < 0n) { + result += modulus; + } + + return result; +} + +export { compute };