function isLeftAssociative(op) { return op === "+" || op === "-" || op === "*" || op === "/"; } function popOps(opstack, queue, op, powInStack) { const prec = {"+": 1, "-": 1, "*": 2, "/": 2, "u": 3, "^": 3} while (opstack.length > 0) { const op2 = opstack.at(-1); if (op2 === "(") { break; } if ((prec[op2] > prec[op]) || (prec[op2] === prec[op] && isLeftAssociative(op))) { opstack.pop(); queue.push(op2); if (op2 === "^") { powInStack = false; } } else { break; } } // practically, we want 2^(3+4) mod 7 to evaluate to 2^7 mod 7 = 2 // however, since our operators are all modular, this would instead // evaluate as 2^0 mod 7 = 1 // rather than complicate things by evaluating exponent expressions // normally instead of modularly (and consequently needing to deal with // fractions) we simply require exponents to be integers if (powInStack && op !== "u") { throw new Error("exponent must be an integer, not an expression"); } return powInStack; } function popBetweenParens(opstack, queue, powInStack) { while (opstack.at(-1) !== "(") { if (opstack.length === 0) { throw new Error("mismatched parentheses"); } const op = opstack.pop(); if (op === "^") { powInStack = false; } queue.push(op); } opstack.pop(); if (opstack.at(-1) === "sqrt") { const func = opstack.pop(); queue.push(func); } return powInStack; } function empty(opstack, queue) { while (opstack.length !== 0) { const op = opstack.pop(); if (op === "(") { throw new Error("mismatched parentheses"); } queue.push(op); } } function isUnaryMinus(token, lastToken) { return token === "-" && (lastToken === null || /[(-+*/^]/.test(lastToken)); } function shunt(tokens) { const queue = []; const opstack = []; let lastToken = null; let powInStack = false; for (let token of tokens) { if (typeof token === "bigint") { queue.push(token); } else if (/[-+*/^]/.test(token)) { if (isUnaryMinus(token, lastToken)) { token = "u"; } powInStack = popOps(opstack, queue, token, powInStack); opstack.push(token); if (token === "^") { powInStack = true; } } else if (token === "(") { opstack.push(token); } else if (token === ")") { powInStack = popBetweenParens(opstack, queue, powInStack); } else if (token === "sqrt") { opstack.push(token); } lastToken = token; } empty(opstack, queue); return queue; } export { shunt };