From e1c35c36fc5c0d8d1ecde9370d2b5afd35008110 Mon Sep 17 00:00:00 2001 From: Philip Le Riche <7701190+p-leriche@users.noreply.github.com> Date: Wed, 25 Feb 2026 20:18:15 +0000 Subject: [PATCH] Add ExtendedGCD operation --- src/core/config/Categories.json | 2 - src/core/operations/ExtendedGCD.mjs | 101 ++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+), 2 deletions(-) create mode 100644 src/core/operations/ExtendedGCD.mjs diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index ab1dafb0..7a65c054 100644 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -228,8 +228,6 @@ "Subtract", "Multiply", "Divide", - "Modular Exponentiation", - "Modular Inverse", "Extended GCD", "Mean", "Median", diff --git a/src/core/operations/ExtendedGCD.mjs b/src/core/operations/ExtendedGCD.mjs new file mode 100644 index 00000000..88069c74 --- /dev/null +++ b/src/core/operations/ExtendedGCD.mjs @@ -0,0 +1,101 @@ +/** + * @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 ---------- */ + +/** + * Extended GCD operation + */ +class ExtendedGCD extends Operation { + /** + * ExtendedGCD constructor + */ + constructor() { + super(); + + this.name = "Extended GCD"; + this.module = "Crypto"; + this.description = + "Computes the Extended Euclidean Algorithm for integers a and b.

" + + "Finds integers x and y (Bezout coefficients) such that:
" + + "a*x + b*y = gcd(a, b)

" + + "This is fundamental to many number theory algorithms including modular inverse, " + + "solving linear Diophantine equations, and cryptographic operations.

" + + "Input handling: If either a or b is left blank, " + + "its value is taken from the Input field."; + this.infoURL = "https://wikipedia.org/wiki/Extended_Euclidean_algorithm"; + this.inputType = "string"; + this.outputType = "string"; + this.args = [ + { + name: "Value a", + type: "string", + value: "" + }, + { + name: "Value b", + type: "string", + value: "" + } + ]; + } + + /** + * @param {string} input + * @param {Object[]} args + * @returns {string} + */ + run(input, args) { + const [aStr, bStr] = args; + + // Trim everything so "" and " " count as empty + const aParam = aStr?.trim(); + const bParam = bStr?.trim(); + const inputVal = input?.trim(); + + let a, b; + + if (aParam && bParam) { + // Case 1: both values given as parameters + a = aParam; + b = bParam; + } else if (!aParam && bParam) { + // Case 2: a missing - take from input + a = inputVal; + b = bParam; + if (!a) throw new OperationError("Value a must be defined"); + } else if (aParam && !bParam) { + // Case 3: b missing - take from input + a = aParam; + b = inputVal; + if (!b) throw new OperationError("Value b must be defined"); + } else if (!aParam && !bParam) { + // Case 4: both values missing + throw new OperationError("Values a and b must be defined"); + } + + const aBI = parseBigInt(a, "Value a"); + const bBI = parseBigInt(b, "Value b"); + + const [g, x, y] = egcd(aBI, bBI); + const gcd = g < 0n ? -g : g; + + // Format output string bearing in mind that crypto-grade numbers + // may greatly exceed the line length. + let output = "gcd: " + gcd.toString() + "\n\n"; + output += "Bezout coefficients:\n"; + output += "x = " + x.toString() + "\n"; + output += "y = " + y.toString() + "\n\n"; + + return output; + } +} + +export default ExtendedGCD;