Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/core/config/Categories.json
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@
"Subtract",
"Multiply",
"Divide",
"MOD",
"Mean",
"Median",
"Standard Deviation",
Expand Down
13 changes: 13 additions & 0 deletions src/core/lib/Arithmetic.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,19 @@ export function median(data) {
}


/**
* Computes modulo of two numbers and returns the value.
*
* @param {BigNumber[]} data
* @returns {BigNumber}
*/
export function mod(data) {
if (data.length > 0) {
return data.reduce((acc, curr) => acc.mod(curr));
}
}


/**
* Computes standard deviation of a number array and returns the value.
*
Expand Down
62 changes: 62 additions & 0 deletions src/core/operations/MOD.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/**
* @license Apache-2.0
*/

import BigNumber from "bignumber.js";
import Operation from "../Operation.mjs";
import { createNumArray } from "../lib/Arithmetic.mjs";
import { ARITHMETIC_DELIM_OPTIONS } from "../lib/Delim.mjs";


/**
* MOD operation
*/
class MOD extends Operation {

/**
* MOD constructor
*/
constructor() {
super();

this.name = "MOD";
this.module = "Default";
this.description = "Computes the modulo of each number in a list with a given modulus value. Numbers are extracted from the input based on the delimiter, and non-numeric values are ignored.<br><br>e.g. <code>15 4 7</code> with modulus <code>3</code> becomes <code>0 1 1</code>";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Modulus",
"type": "number",
"value": 2
},
{
"name": "Delimiter",
"type": "option",
"value": ARITHMETIC_DELIM_OPTIONS,
}
];
}

/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const modulus = new BigNumber(args[0]);
const delimiter = args[1];

if (modulus.isZero()) {
throw new Error("Modulus cannot be zero");
}

const numbers = createNumArray(input, delimiter);
const results = numbers.map(num => num.mod(modulus));

return results.join(" ");
}

}

export default MOD;
1 change: 1 addition & 0 deletions tests/operations/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ import "./tests/Magic.mjs";
import "./tests/Media.mjs";
import "./tests/MIMEDecoding.mjs";
import "./tests/Modhex.mjs";
import "./tests/MOD.mjs";
import "./tests/MorseCode.mjs";
import "./tests/MS.mjs";
import "./tests/MultipleBombe.mjs";
Expand Down
219 changes: 219 additions & 0 deletions tests/operations/tests/MOD.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
/**
* MOD tests
*
* @license Apache-2.0
*/
import TestRegister from "../../lib/TestRegister.mjs";

TestRegister.addTests([
{
name: "MOD: Basic modulo operation",
input: "15 4 7",
expectedOutput: "0 1 1",
recipeConfig: [
{
"op": "MOD",
"args": [3, "Space"]
}
],
},
{
name: "MOD: Single number",
input: "10",
expectedOutput: "1",
recipeConfig: [
{
"op": "MOD",
"args": [3, "Space"]
}
],
},
{
name: "MOD: Comma-separated numbers",
input: "15,8,23,16,5",
expectedOutput: "1 1 2 2 5",
recipeConfig: [
{
"op": "MOD",
"args": [7, "Comma"]
}
],
},
{
name: "MOD: Line feed separated numbers",
input: "25\n13\n44\n7",
expectedOutput: "0 3 4 2",
recipeConfig: [
{
"op": "MOD",
"args": [5, "Line feed"]
}
],
},
{
name: "MOD: Tab-separated numbers",
input: "20\t14\t8\t35",
expectedOutput: "2 2 2 5",
recipeConfig: [
{
"op": "MOD",
"args": [6, "Tab"]
}
],
},
{
name: "MOD: Large numbers",
input: "123456789012345 987654321098765",
expectedOutput: "123456789012345 987654321098765",
recipeConfig: [
{
"op": "MOD",
"args": [1234567890123456, "Space"]
}
],
},
{
name: "MOD: Mixed with non-numeric values",
input: "15 abc 4 def 7 xyz 23",
expectedOutput: "0 1 1 2",
recipeConfig: [
{
"op": "MOD",
"args": [3, "Space"]
}
],
},
{
name: "MOD: Decimal numbers",
input: "10.5 15.7 8.2",
expectedOutput: "1.5 0.7 2.2",
recipeConfig: [
{
"op": "MOD",
"args": [3, "Space"]
}
],
},
{
name: "MOD: Negative numbers",
input: "-15 -8 25 -10",
expectedOutput: "0 -2 1 -1",
recipeConfig: [
{
"op": "MOD",
"args": [3, "Space"]
}
],
},
{
name: "MOD: Zero in input",
input: "0 5 10 15 20",
expectedOutput: "0 2 1 0 2",
recipeConfig: [
{
"op": "MOD",
"args": [3, "Space"]
}
],
},
{
name: "MOD: Modulus of 2 (even/odd check)",
input: "1 2 3 4 5 6 7 8 9 10",
expectedOutput: "1 0 1 0 1 0 1 0 1 0",
recipeConfig: [
{
"op": "MOD",
"args": [2, "Space"]
}
],
},
{
name: "MOD: Numbers with extra whitespace",
input: " 15 4 7 ",
expectedOutput: "0 1 1",
recipeConfig: [
{
"op": "MOD",
"args": [3, "Space"]
}
],
},
{
name: "MOD: Empty input",
input: "",
expectedOutput: "",
recipeConfig: [
{
"op": "MOD",
"args": [3, "Space"]
}
],
},
{
name: "MOD: Scientific notation",
input: "1e3 2e2 5e1",
expectedOutput: "1 2 2",
recipeConfig: [
{
"op": "MOD",
"args": [3, "Space"]
}
],
},
{
name: "MOD: Floating point precision",
input: "10.123456789 20.987654321",
expectedOutput: "1.123456789 2.987654321",
recipeConfig: [
{
"op": "MOD",
"args": [3, "Space"]
}
],
},
{
name: "MOD: Zero modulus error",
input: "15 4 7",
expectedError: true,
expectedOutput: "MOD - Modulus cannot be zero",
recipeConfig: [
{
"op": "MOD",
"args": [0, "Space"]
}
],
},
{
name: "MOD: Semi-colon separated numbers",
input: "17;5;8;13",
expectedOutput: "2 0 3 3",
recipeConfig: [
{
"op": "MOD",
"args": [5, "Semi-colon"]
}
],
},
{
name: "MOD: Colon separated numbers",
input: "25:9:14:7",
expectedOutput: "1 1 2 3",
recipeConfig: [
{
"op": "MOD",
"args": [4, "Colon"]
}
],
},
{
name: "MOD: CRLF separated numbers",
input: "30\r\n18\r\n22\r\n11",
expectedOutput: "0 0 4 5",
recipeConfig: [
{
"op": "MOD",
"args": [6, "CRLF"]
}
],
},
]);
Loading