add multiplicative order button
This commit is contained in:
parent
39c009beb4
commit
4e91b1dc14
|
|
@ -23,6 +23,7 @@
|
||||||
<output id="result" for="expr modulus"></output>
|
<output id="result" for="expr modulus"></output>
|
||||||
|
|
||||||
<div id="extra-buttons">
|
<div id="extra-buttons">
|
||||||
|
<button id="ord" type="button">ord</button>
|
||||||
<button id="sqrt" type="button">√</button>
|
<button id="sqrt" type="button">√</button>
|
||||||
<button id="inv" type="button">x<sup>-1</sup></button>
|
<button id="inv" type="button">x<sup>-1</sup></button>
|
||||||
<button id="pow" type="button">^</button>
|
<button id="pow" type="button">^</button>
|
||||||
|
|
|
||||||
1
main.js
1
main.js
|
|
@ -130,6 +130,7 @@ document.querySelector("#rparen").addEventListener("click", () => keyPress(")"))
|
||||||
document.querySelector("#pow").addEventListener("click", () => keyPress("^"));
|
document.querySelector("#pow").addEventListener("click", () => keyPress("^"));
|
||||||
document.querySelector("#inv").addEventListener("click", () => keyPress("^-1"));
|
document.querySelector("#inv").addEventListener("click", () => keyPress("^-1"));
|
||||||
document.querySelector("#sqrt").addEventListener("click", () => keyPress("sqrt("));
|
document.querySelector("#sqrt").addEventListener("click", () => keyPress("sqrt("));
|
||||||
|
document.querySelector("#ord").addEventListener("click", () => keyPress("ord("));
|
||||||
|
|
||||||
document.querySelector("#clear").addEventListener("click", clear);
|
document.querySelector("#clear").addEventListener("click", clear);
|
||||||
document.querySelector("#enter").addEventListener("click", evaluate);
|
document.querySelector("#enter").addEventListener("click", evaluate);
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { modinv, modpow, modsqrt } from "./math.js";
|
import { modinv, modpow, modsqrt, ord } from "./math.js";
|
||||||
|
|
||||||
function binaryOpPop(stack) {
|
function binaryOpPop(stack) {
|
||||||
const b = stack.pop();
|
const b = stack.pop();
|
||||||
|
|
@ -56,6 +56,10 @@ function compute(queue, modulus) {
|
||||||
const a = stack.pop();
|
const a = stack.pop();
|
||||||
const s = modsqrt(a, modulus);
|
const s = modsqrt(a, modulus);
|
||||||
stack.push(s);
|
stack.push(s);
|
||||||
|
} else if (token === "ord") {
|
||||||
|
const a = stack.pop();
|
||||||
|
const r = ord(a, modulus);
|
||||||
|
stack.push(r);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,11 @@
|
||||||
function tokenize(expr) {
|
function tokenize(expr) {
|
||||||
// NOTE: not handling whitespace
|
// NOTE: not handling whitespace
|
||||||
// NOTE: currently ends early if string doesn't match token
|
// NOTE: currently ends early if string doesn't match token
|
||||||
const regexp = /[0-9]+|[-+*/^]|\(|\)|sqrt/gy;
|
|
||||||
|
// TODO: it would probably be beneficial to create some sort of
|
||||||
|
// function token instead of updating strings in several different
|
||||||
|
// places
|
||||||
|
const regexp = /[0-9]+|[-+*/^]|\(|\)|sqrt|ord/gy;
|
||||||
const matches = expr.matchAll(regexp);
|
const matches = expr.matchAll(regexp);
|
||||||
|
|
||||||
const tokens = [];
|
const tokens = [];
|
||||||
|
|
@ -10,12 +14,14 @@ function tokenize(expr) {
|
||||||
tokens.push(BigInt(match[0]));
|
tokens.push(BigInt(match[0]));
|
||||||
} else if (/[-+*^/]/.test(match[0])) {
|
} else if (/[-+*^/]/.test(match[0])) {
|
||||||
tokens.push(match[0])
|
tokens.push(match[0])
|
||||||
} else if (match[0] == "(") {
|
} else if (match[0] === "(") {
|
||||||
tokens.push("(");
|
tokens.push("(");
|
||||||
} else if (match[0] == ")") {
|
} else if (match[0] === ")") {
|
||||||
tokens.push(")");
|
tokens.push(")");
|
||||||
} else if (match[0] == "sqrt") {
|
} else if (match[0] === "sqrt") {
|
||||||
tokens.push("sqrt")
|
tokens.push("sqrt")
|
||||||
|
} else if (match[0] === "ord") {
|
||||||
|
tokens.push("ord")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -181,4 +181,28 @@ function modsqrt(n, modulus) {
|
||||||
return tonelliShanks(n, modulus);
|
return tonelliShanks(n, modulus);
|
||||||
}
|
}
|
||||||
|
|
||||||
export { modsqrt, modinv, modpow };
|
function ord(n, modulus) {
|
||||||
|
n %= modulus;
|
||||||
|
if (n < 0n) {
|
||||||
|
n += modulus;
|
||||||
|
}
|
||||||
|
|
||||||
|
const [g, s, t] = xgcd(n, modulus);
|
||||||
|
if (g !== 1n) {
|
||||||
|
throw new Error(`can't compute multiplicative order - ${n} and ${modulus} are not coprime`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: this is a hard problem, but there are more efficient approaches
|
||||||
|
|
||||||
|
let k = 1n;
|
||||||
|
let a = n;
|
||||||
|
while (a !== 1n) {
|
||||||
|
a *= n;
|
||||||
|
a %= modulus;
|
||||||
|
k += 1n;
|
||||||
|
}
|
||||||
|
|
||||||
|
return k;
|
||||||
|
}
|
||||||
|
|
||||||
|
export { modsqrt, modinv, modpow, ord };
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,8 @@ function popOps(opstack, queue, op, powInStack) {
|
||||||
// rather than complicate things by evaluating exponent expressions
|
// rather than complicate things by evaluating exponent expressions
|
||||||
// normally instead of modularly (and consequently needing to deal with
|
// normally instead of modularly (and consequently needing to deal with
|
||||||
// fractions) we simply require exponents to be integers
|
// fractions) we simply require exponents to be integers
|
||||||
|
// NOTE: as written this allows for the exponent to be a function, not
|
||||||
|
// sure if that should change or not
|
||||||
if (powInStack && op !== "u") {
|
if (powInStack && op !== "u") {
|
||||||
throw new Error("exponent must be an integer, not an expression");
|
throw new Error("exponent must be an integer, not an expression");
|
||||||
}
|
}
|
||||||
|
|
@ -48,7 +50,8 @@ function popBetweenParens(opstack, queue, powInStack) {
|
||||||
}
|
}
|
||||||
|
|
||||||
opstack.pop();
|
opstack.pop();
|
||||||
if (opstack.at(-1) === "sqrt") {
|
const t = opstack.at(-1);
|
||||||
|
if (t === "sqrt" || t === "ord") {
|
||||||
const func = opstack.pop();
|
const func = opstack.pop();
|
||||||
queue.push(func);
|
queue.push(func);
|
||||||
}
|
}
|
||||||
|
|
@ -95,6 +98,8 @@ function shunt(tokens) {
|
||||||
powInStack = popBetweenParens(opstack, queue, powInStack);
|
powInStack = popBetweenParens(opstack, queue, powInStack);
|
||||||
} else if (token === "sqrt") {
|
} else if (token === "sqrt") {
|
||||||
opstack.push(token);
|
opstack.push(token);
|
||||||
|
} else if (token === "ord") {
|
||||||
|
opstack.push(token);
|
||||||
}
|
}
|
||||||
|
|
||||||
lastToken = token;
|
lastToken = token;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue