Skip to content
Merged
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 @@ -244,6 +244,7 @@
"Subtract",
"Multiply",
"Divide",
"MOD",
"Extended GCD",
"Modular Inverse",
"Mean",
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 @@ -124,6 +124,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
2 changes: 1 addition & 1 deletion src/core/operations/FromDecimal.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {DELIM_OPTIONS} from "../lib/Delim.mjs";
import {fromDecimal} from "../lib/Decimal.mjs";

/**
* From Decimal delimiters, plus auto-detection.
* From Decimal delimiters, plus auto-detection.
*/
const FROM_DECIMAL_DELIM_OPTIONS = [...DELIM_OPTIONS, "Auto"];

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;
208 changes: 208 additions & 0 deletions tests/operations/tests/MOD.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
/**
* 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: 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"]
}
],
},
]);