Add Modular Inverse operation (#2207)
This commit is contained in:
parent
d771ea2cc0
commit
d172282755
@ -244,6 +244,7 @@
|
|||||||
"Multiply",
|
"Multiply",
|
||||||
"Divide",
|
"Divide",
|
||||||
"Extended GCD",
|
"Extended GCD",
|
||||||
|
"Modular Inverse",
|
||||||
"Mean",
|
"Mean",
|
||||||
"Median",
|
"Median",
|
||||||
"Standard Deviation",
|
"Standard Deviation",
|
||||||
|
|||||||
107
src/core/operations/ModularInverse.mjs
Normal file
107
src/core/operations/ModularInverse.mjs
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
/**
|
||||||
|
* @author p-leriche [philip.leriche@cantab.net]
|
||||||
|
* @copyright Crown Copyright 2025
|
||||||
|
* @license Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Operation from "../Operation.mjs";
|
||||||
|
import OperationError from "../errors/OperationError.mjs";
|
||||||
|
import { parseBigInt, egcd } from "../lib/BigIntUtils.mjs";
|
||||||
|
|
||||||
|
|
||||||
|
/* ---------- operation class ---------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modular Inverse operation
|
||||||
|
*/
|
||||||
|
class ModularInverse extends Operation {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ModularInverse constructor
|
||||||
|
*/
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.name = "Modular Inverse";
|
||||||
|
this.module = "Crypto";
|
||||||
|
this.description =
|
||||||
|
"Computes the modular multiplicative inverse of <i>a</i> modulo <i>m</i>.<br><br>" +
|
||||||
|
"Finds <i>x</i> such that a*x = 1 (mod m).<br><br>" +
|
||||||
|
"<b>Input handling:</b> If either <i>a</i> or <i>m</i> is left blank, " +
|
||||||
|
"its value is taken from the Input field.";
|
||||||
|
this.infoURL = "https://wikipedia.org/wiki/Modular_multiplicative_inverse";
|
||||||
|
this.inputType = "string";
|
||||||
|
this.outputType = "string";
|
||||||
|
this.args = [
|
||||||
|
{
|
||||||
|
name: "Value (a)",
|
||||||
|
type: "string",
|
||||||
|
value: ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Modulus (m)",
|
||||||
|
type: "string",
|
||||||
|
value: ""
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} input
|
||||||
|
* @param {Object[]} args
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
run(input, args) {
|
||||||
|
const [aStr, mStr] = args;
|
||||||
|
|
||||||
|
// Trim everything so "" and " " count as empty
|
||||||
|
const aParam = aStr?.trim();
|
||||||
|
const mParam = mStr?.trim();
|
||||||
|
const inputVal = input?.trim();
|
||||||
|
|
||||||
|
let a, m;
|
||||||
|
|
||||||
|
if (aParam && mParam) {
|
||||||
|
// Case 1: value and modulus both given as parameters
|
||||||
|
a = aParam;
|
||||||
|
m = mParam;
|
||||||
|
} else if (!aParam && mParam) {
|
||||||
|
// Case 2: value missing - take from input
|
||||||
|
a = inputVal;
|
||||||
|
m = mParam;
|
||||||
|
if (!a) throw new OperationError("Value (a) must be defined");
|
||||||
|
} else if (aParam && !mParam) {
|
||||||
|
// Case 3: modulus missing - take from input
|
||||||
|
a = aParam;
|
||||||
|
m = inputVal;
|
||||||
|
if (!m) throw new OperationError("Modulus (m) must be defined");
|
||||||
|
} else if (!aParam && !mParam) {
|
||||||
|
// Case 4: value and modulus both missing
|
||||||
|
throw new OperationError("Value (a) and Modulus (m) must be defined");
|
||||||
|
}
|
||||||
|
|
||||||
|
const aBI = parseBigInt(a, "Value (a)");
|
||||||
|
const mBI = parseBigInt(m, "Modulus (m)");
|
||||||
|
|
||||||
|
if (mBI <= 0n) {
|
||||||
|
throw new OperationError("Modulus must be greater than zero");
|
||||||
|
}
|
||||||
|
|
||||||
|
const aNorm = ((aBI % mBI) + mBI) % mBI;
|
||||||
|
const [g, x] = egcd(aNorm, mBI);
|
||||||
|
|
||||||
|
if (g !== 1n && g !== -1n) {
|
||||||
|
throw new OperationError("Inverse does not exist because gcd(a, m) ≠ 1");
|
||||||
|
}
|
||||||
|
|
||||||
|
let inv = x;
|
||||||
|
if (g === -1n) inv = -inv;
|
||||||
|
|
||||||
|
inv = ((inv % mBI) + mBI) % mBI;
|
||||||
|
|
||||||
|
|
||||||
|
return inv.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ModularInverse;
|
||||||
78
tests/operations/tests/ModularInverse.mjs
Normal file
78
tests/operations/tests/ModularInverse.mjs
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
/**
|
||||||
|
* Modular Inverse tests.
|
||||||
|
*
|
||||||
|
* @author p-leriche [philip.leriche@cantab.net]
|
||||||
|
*
|
||||||
|
* @copyright Crown Copyright 2025
|
||||||
|
* @license Apache-2.0
|
||||||
|
*/
|
||||||
|
import TestRegister from "../../lib/TestRegister.mjs";
|
||||||
|
|
||||||
|
TestRegister.addTests([
|
||||||
|
{
|
||||||
|
name: "Modular Inverse: basic example (3 mod 11)",
|
||||||
|
input: "",
|
||||||
|
expectedOutput: "4",
|
||||||
|
recipeConfig: [
|
||||||
|
{
|
||||||
|
op: "Modular Inverse",
|
||||||
|
args: ["3", "11"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Modular Inverse: another coprime pair (7 mod 26)",
|
||||||
|
input: "",
|
||||||
|
expectedOutput: "15",
|
||||||
|
recipeConfig: [
|
||||||
|
{
|
||||||
|
op: "Modular Inverse",
|
||||||
|
args: ["7", "26"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Modular Inverse: hexadecimal input (0x10 mod 0x11)",
|
||||||
|
input: "",
|
||||||
|
expectedOutput: "16",
|
||||||
|
recipeConfig: [
|
||||||
|
{
|
||||||
|
op: "Modular Inverse",
|
||||||
|
args: ["0x10", "0x11"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Modular Inverse: using input field for value",
|
||||||
|
input: "5",
|
||||||
|
expectedOutput: "21",
|
||||||
|
recipeConfig: [
|
||||||
|
{
|
||||||
|
op: "Modular Inverse",
|
||||||
|
args: ["", "26"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Modular Inverse: using input field for modulus",
|
||||||
|
input: "17",
|
||||||
|
expectedOutput: "7",
|
||||||
|
recipeConfig: [
|
||||||
|
{
|
||||||
|
op: "Modular Inverse",
|
||||||
|
args: ["5", ""],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Modular Inverse: large number (RSA-like)",
|
||||||
|
input: "",
|
||||||
|
expectedOutput: "934281398294",
|
||||||
|
recipeConfig: [
|
||||||
|
{
|
||||||
|
op: "Modular Inverse",
|
||||||
|
args: ["65537", "9999999999999"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]);
|
||||||
Loading…
x
Reference in New Issue
Block a user