diff --git a/src/core/config/Categories.js b/src/core/config/Categories.js index 8b828bc0..e2ee57cf 100755 --- a/src/core/config/Categories.js +++ b/src/core/config/Categories.js @@ -113,7 +113,7 @@ const Categories = [ ] }, { - name: "Logical operations", + name: "Arithmetic / Logic", ops: [ "XOR", "XOR Brute Force", @@ -122,6 +122,13 @@ const Categories = [ "AND", "ADD", "SUB", + "Sum", + "Subtract", + "Multiply", + "Divide", + "Mean", + "Median", + "Standard Deviation", "Bit shift left", "Bit shift right", "Rotate left", diff --git a/src/core/config/OperationConfig.js b/src/core/config/OperationConfig.js index 1e975118..2f1d6781 100755 --- a/src/core/config/OperationConfig.js +++ b/src/core/config/OperationConfig.js @@ -1,3 +1,4 @@ +import Arithmetic from "../operations/Arithmetic.js"; import Base from "../operations/Base.js"; import Base58 from "../operations/Base58.js"; import Base64 from "../operations/Base64.js"; @@ -519,6 +520,97 @@ const OperationConfig = { } ] }, + "Sum": { + module: "Default", + description: "Adds together a list of numbers. If an item in the string is not a number it is excluded from the list.

e.g. 0x0a 8 .5 becomes 18.5", + inputType: "string", + outputType: "number", + args: [ + { + name: "Delimiter", + type: "option", + value: Arithmetic.DELIM_OPTIONS + } + ] + }, + "Subtract": { + module: "Default", + description: "Subtracts a list of numbers. If an item in the string is not a number it is excluded from the list.

e.g. 0x0a 8 .5 becomes 1.5", + inputType: "string", + outputType: "number", + args: [ + { + name: "Delimiter", + type: "option", + value: Arithmetic.DELIM_OPTIONS + } + ] + }, + "Multiply": { + module: "Default", + description: "Multiplies a list of numbers. If an item in the string is not a number it is excluded from the list.

e.g. 0x0a 8 .5 becomes 40", + inputType: "string", + outputType: "number", + args: [ + { + name: "Delimiter", + type: "option", + value: Arithmetic.DELIM_OPTIONS + } + ] + }, + "Divide": { + module: "Default", + description: "Divides a list of numbers. If an item in the string is not a number it is excluded from the list.

e.g. 0x0a 8 .5 becomes 2.5", + inputType: "string", + outputType: "number", + args: [ + { + name: "Delimiter", + type: "option", + value: Arithmetic.DELIM_OPTIONS + } + ] + }, + "Mean": { + module: "Default", + description: "Computes the mean (average) of a number list. If an item in the string is not a number it is excluded from the list.

e.g. 0x0a 8 .5 .5 becomes 4.75", + inputType: "string", + outputType: "number", + args: [ + { + name: "Delimiter", + type: "option", + value: Arithmetic.DELIM_OPTIONS + } + ] + }, + "Median": { + module: "Default", + description: "Computes the median of a number list. If an item in the string is not a number it is excluded from the list.

e.g. 0x0a 8 1 .5 becomes 4.5", + inputType: "string", + outputType: "number", + args: [ + { + name: "Delimiter", + type: "option", + value: Arithmetic.DELIM_OPTIONS + } + ] + }, + "Standard Deviation": { + module: "Default", + description: "Computes the standard deviation of a number list. If an item in the string is not a number it is excluded from the list.

e.g. 0x0a 8 .5 becomes 4.089281382128433", + inputType: "string", + outputType: "number", + args: [ + { + name: "Delimiter", + type: "option", + value: Arithmetic.DELIM_OPTIONS + } + ] + }, "From Hex": { module: "Default", description: "Converts a hexadecimal byte string back into its raw value.

e.g. ce 93 ce b5 ce b9 ce ac 20 cf 83 ce bf cf 85 0a becomes the UTF-8 encoded string Γειά σου", diff --git a/src/core/config/modules/Default.js b/src/core/config/modules/Default.js index a0e8bd23..4fb76887 100644 --- a/src/core/config/modules/Default.js +++ b/src/core/config/modules/Default.js @@ -1,4 +1,5 @@ import FlowControl from "../../FlowControl.js"; +import Arithmetic from "../../operations/Arithmetic.js"; import Base from "../../operations/Base.js"; import Base58 from "../../operations/Base58.js"; import Base64 from "../../operations/Base64.js"; @@ -159,6 +160,13 @@ OpModules.Default = { "Return": FlowControl.runReturn, "Comment": FlowControl.runComment, "PHP Deserialize": PHP.runDeserialize, + "Sum": Arithmetic.runSum, + "Subtract": Arithmetic.runSub, + "Multiply": Arithmetic.runMulti, + "Divide": Arithmetic.runDiv, + "Mean": Arithmetic.runMean, + "Median": Arithmetic.runMedian, + "Standard Deviation": Arithmetic.runStdDev, /* diff --git a/src/core/operations/Arithmetic.js b/src/core/operations/Arithmetic.js new file mode 100644 index 00000000..1fe73ac1 --- /dev/null +++ b/src/core/operations/Arithmetic.js @@ -0,0 +1,252 @@ +import Utils from "../Utils.js"; + + +/** + * Math operations on numbers. + * + * @author bwhitn [brian.m.whitney@outlook.com] + * @copyright Crown Copyright 2016 + * @license Apache-2.0 + * + * @namespace + */ +const Arithmetic = { + + /** + * @constant + * @default + */ + DELIM_OPTIONS: ["Line feed", "Space", "Comma", "Semi-colon", "Colon", "CRLF"], + + + /** + * Splits a string based on a delimiter and calculates the sum of numbers. + * + * @param {string} input + * @param {Object[]} args + * @returns {number} + */ + runSum: function(input, args) { + const val = Arithmetic._sum(Arithmetic._createNumArray(input, args[0])); + return typeof(val) === "number" ? val : NaN; + }, + + + /** + * Splits a string based on a delimiter and subtracts all the numbers. + * + * @param {string} input + * @param {Object[]} args + * @returns {number} + */ + runSub: function(input, args) { + let val = Arithmetic._sub(Arithmetic._createNumArray(input, args[0])); + return typeof(val) === "number" ? val : NaN; + }, + + + /** + * Splits a string based on a delimiter and multiplies the numbers. + * + * @param {string} input + * @param {Object[]} args + * @returns {number} + */ + runMulti: function(input, args) { + let val = Arithmetic._multi(Arithmetic._createNumArray(input, args[0])); + return typeof(val) === "number" ? val : NaN; + }, + + + /** + * Splits a string based on a delimiter and divides the numbers. + * + * @param {string} input + * @param {Object[]} args + * @returns {number} + */ + runDiv: function(input, args) { + let val = Arithmetic._div(Arithmetic._createNumArray(input, args[0])); + return typeof(val) === "number" ? val : NaN; + }, + + + /** + * Splits a string based on a delimiter and computes the mean (average). + * + * @param {string} input + * @param {Object[]} args + * @returns {number} + */ + runMean: function(input, args) { + let val = Arithmetic._mean(Arithmetic._createNumArray(input, args[0])); + return typeof(val) === "number" ? val : NaN; + }, + + + /** + * Splits a string based on a delimiter and finds the median. + * + * @param {string} input + * @param {Object[]} args + * @returns {number} + */ + runMedian: function(input, args) { + let val = Arithmetic._median(Arithmetic._createNumArray(input, args[0])); + return typeof(val) === "number" ? val : NaN; + }, + + + /** + * splits a string based on a delimiter and computes the standard deviation. + * + * @param {string} input + * @param {Object[]} args + * @returns {number} + */ + runStdDev: function(input, args) { + let val = Arithmetic._stdDev(Arithmetic._createNumArray(input, args[0])); + return typeof(val) === "number" ? val : NaN; + }, + + + /** + * Converts a string array to a number array. + * + * @private + * @param {string[]} input + * @param {string} delim + * @returns {number[]} + */ + _createNumArray: function(input, delim) { + delim = Utils.charRep[delim || "Space"]; + let splitNumbers = input.split(delim), + numbers = [], + num; + + for (let i = 0; i < splitNumbers.length; i++) { + if (splitNumbers[i].indexOf(".") >= 0) { + num = parseFloat(splitNumbers[i].trim()); + } else { + num = parseInt(splitNumbers[i].trim(), 0); + } + if (!isNaN(num)) { + numbers.push(num); + } + } + return numbers; + }, + + + /** + * Adds an array of numbers and returns the value. + * + * @private + * @param {number[]} data + * @returns {number} + */ + _sum: function(data) { + if (data.length > 0) { + return data.reduce((acc, curr) => acc + curr); + } + }, + + + /** + * Subtracts an array of numbers and returns the value. + * + * @private + * @param {number[]} data + * @returns {number} + */ + _sub: function(data) { + if (data.length > 0) { + return data.reduce((acc, curr) => acc - curr); + } + }, + + + /** + * Multiplies an array of numbers and returns the value. + * + * @private + * @param {number[]} data + * @returns {number} + */ + _multi: function(data) { + if (data.length > 0) { + return data.reduce((acc, curr) => acc * curr); + } + }, + + + /** + * Divides an array of numbers and returns the value. + * + * @private + * @param {number[]} data + * @returns {number} + */ + _div: function(data) { + if (data.length > 0) { + return data.reduce((acc, curr) => acc / curr); + } + }, + + + /** + * Computes mean of a number array and returns the value. + * + * @private + * @param {number[]} data + * @returns {number} + */ + _mean: function(data) { + if (data.length > 0) { + return Arithmetic._sum(data) / data.length; + } + }, + + + /** + * Computes median of a number array and returns the value. + * + * @private + * @param {number[]} data + * @returns {number} + */ + _median: function (data) { + if ((data.length % 2) === 0) { + let first, second; + data.sort(function(a, b){ + return a - b; + }); + first = data[Math.floor(data.length / 2)]; + second = data[Math.floor(data.length / 2) - 1]; + return Arithmetic._mean([first, second]); + } else { + return data[Math.floor(data.length / 2)]; + } + }, + + + /** + * Computes standard deviation of a number array and returns the value. + * + * @private + * @param {number[]} data + * @returns {number} + */ + _stdDev: function (data) { + if (data.length > 0) { + let avg = Arithmetic._mean(data); + let devSum = 0; + for (let i = 0; i < data.length; i++) { + devSum += (data[i] - avg) ** 2; + } + return Math.sqrt(devSum / data.length); + } + }, +}; + +export default Arithmetic;