diff --git a/modules/compute.js b/modules/compute.js index b0dc7d3..9ef1a9b 100644 --- a/modules/compute.js +++ b/modules/compute.js @@ -63,6 +63,53 @@ 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) { @@ -105,6 +152,14 @@ function compute(queue, modulus) { const [a, b] = binaryOpPop(stack); const c = modpow(a, b, modulus); stack.push(c); + } else if (token === "sqrt") { + if (!isprime(modulus)) { + throw new Error("modulus must be prime to compute square root"); + } + + const a = stack.pop(); + const s = tonelliShanks(a, modulus); + stack.push(s); } } diff --git a/modules/lexer.js b/modules/lexer.js index e44267b..b83bbdd 100644 --- a/modules/lexer.js +++ b/modules/lexer.js @@ -1,7 +1,7 @@ function tokenize(expr) { // NOTE: not handling whitespace // NOTE: currently ends early if string doesn't match token - const regexp = /[0-9]+|[-+*/^]|\(|\)/gy; + const regexp = /[0-9]+|[-+*/^]|\(|\)|sqrt/gy; const matches = expr.matchAll(regexp); const tokens = []; @@ -14,6 +14,8 @@ function tokenize(expr) { tokens.push("("); } else if (match[0] == ")") { tokens.push(")"); + } else if (match[0] == "sqrt") { + tokens.push("sqrt") } } diff --git a/modules/parser.js b/modules/parser.js index 45990a9..7d7bc77 100644 --- a/modules/parser.js +++ b/modules/parser.js @@ -48,6 +48,10 @@ function popBetweenParens(opstack, queue, powInStack) { } opstack.pop(); + if (opstack.at(-1) === "sqrt") { + const func = opstack.pop(); + queue.push(func); + } return powInStack; } @@ -89,6 +93,8 @@ function shunt(tokens) { opstack.push(token); } else if (token === ")") { powInStack = popBetweenParens(opstack, queue, powInStack); + } else if (token === "sqrt") { + opstack.push(token); } lastToken = token;