diff --git a/src/core/config/Categories.js b/src/core/config/Categories.js index 77eafb4b..8d5476be 100755 --- a/src/core/config/Categories.js +++ b/src/core/config/Categories.js @@ -116,9 +116,11 @@ const Categories = [ // "Object Identifier to Hex", // ] // }, - // { - // name: "Arithmetic / Logic", - // ops: [ + { + name: "Arithmetic / Logic", + ops: [ + "Set Union", + "Set Intersection" // "XOR", // "XOR Brute Force", // "OR", @@ -138,8 +140,8 @@ const Categories = [ // "Rotate left", // "Rotate right", // "ROT13", - // ] - // }, + ] + }, // { // name: "Networking", // ops: [ diff --git a/src/core/config/OperationConfig.json b/src/core/config/OperationConfig.json index 1486c66d..dfb0cc81 100644 --- a/src/core/config/OperationConfig.json +++ b/src/core/config/OperationConfig.json @@ -156,7 +156,7 @@ ] }, "Raw Deflate": { - "module": "Compression", + "module": "Default", "description": "Compresses data using the deflate algorithm with no headers.", "inputType": "byteArray", "outputType": "byteArray", @@ -210,6 +210,63 @@ } ] }, + "Set Intersection": { + "module": "Default", + "description": "Get the intersection of two sets", + "inputType": "string", + "outputType": "string", + "flowControl": false, + "args": [ + { + "name": "Sample delimiter", + "type": "binaryString", + "value": "\n\n" + }, + { + "name": "Item delimiter", + "type": "binaryString", + "value": "," + } + ] + }, + "": { + "module": "Default", + "description": "", + "inputType": "string", + "outputType": "string", + "flowControl": false, + "args": [ + { + "name": "Sample delimiter", + "type": "binaryString", + "value": "\n\n" + }, + { + "name": "Item delimiter", + "type": "binaryString", + "value": "," + } + ] + }, + "Set Union": { + "module": "Default", + "description": "Get the union of two sets", + "inputType": "string", + "outputType": "string", + "flowControl": false, + "args": [ + { + "name": "Sample delimiter", + "type": "binaryString", + "value": "\n\n" + }, + { + "name": "Item delimiter", + "type": "binaryString", + "value": "," + } + ] + }, "Show Base64 offsets": { "module": "Default", "description": "When a string is within a block of data and the whole block is Base64'd, the string itself could be represented in Base64 in three distinct ways depending on its offset within the block.

This operation shows all possible offsets for a given string so that each possible encoding can be considered.", @@ -338,7 +395,7 @@ "module": "Compression", "description": "Decompresses data using the PKZIP algorithm and displays it per file, with support for passwords.", "inputType": "byteArray", - "outputType": "byteArray", + "outputType": "html", "flowControl": false, "args": [ { diff --git a/src/core/config/modules/Compression.mjs b/src/core/config/modules/Compression.mjs index 61b2ed2d..241f278b 100644 --- a/src/core/config/modules/Compression.mjs +++ b/src/core/config/modules/Compression.mjs @@ -7,7 +7,6 @@ */ import Gunzip from "../../operations/Gunzip"; import Gzip from "../../operations/Gzip"; -import RawDeflate from "../../operations/RawDeflate"; import RawInflate from "../../operations/RawInflate"; import Unzip from "../../operations/Unzip"; import Zip from "../../operations/Zip"; @@ -19,7 +18,6 @@ const OpModules = typeof self === "undefined" ? {} : self.OpModules || {}; OpModules.Compression = { "Gunzip": Gunzip, "Gzip": Gzip, - "Raw Deflate": RawDeflate, "Raw Inflate": RawInflate, "Unzip": Unzip, "Zip": Zip, diff --git a/src/core/config/modules/Default.js b/src/core/config/modules/Default.js index bbdafc25..9b5762cf 100644 --- a/src/core/config/modules/Default.js +++ b/src/core/config/modules/Default.js @@ -30,7 +30,6 @@ import Tidy from "../../operations/Tidy.js"; import Unicode from "../../operations/Unicode.js"; import UUID from "../../operations/UUID.js"; import XKCD from "../../operations/XKCD.js"; -import SetOps from "../../operations/SetOperations.js"; /** diff --git a/src/core/config/modules/Default.mjs b/src/core/config/modules/Default.mjs index 34043043..28528882 100644 --- a/src/core/config/modules/Default.mjs +++ b/src/core/config/modules/Default.mjs @@ -8,6 +8,10 @@ import FromBase32 from "../../operations/FromBase32"; import FromBase64 from "../../operations/FromBase64"; import FromHex from "../../operations/FromHex"; +import RawDeflate from "../../operations/RawDeflate"; +import SetIntersection from "../../operations/SetIntersection"; +import SetOps from "../../operations/SetOps"; +import SetUnion from "../../operations/SetUnion"; import ShowBase64Offsets from "../../operations/ShowBase64Offsets"; import ToBase32 from "../../operations/ToBase32"; import ToBase64 from "../../operations/ToBase64"; @@ -19,6 +23,10 @@ OpModules.Default = { "From Base32": FromBase32, "From Base64": FromBase64, "From Hex": FromHex, + "Raw Deflate": RawDeflate, + "Set Intersection": SetIntersection, + "": SetOps, + "Set Union": SetUnion, "Show Base64 offsets": ShowBase64Offsets, "To Base32": ToBase32, "To Base64": ToBase64, diff --git a/src/core/operations/RawDeflate.mjs b/src/core/operations/RawDeflate.mjs index a1a30981..921d04fd 100644 --- a/src/core/operations/RawDeflate.mjs +++ b/src/core/operations/RawDeflate.mjs @@ -28,7 +28,7 @@ class RawDeflate extends Operation { super(); this.name = "Raw Deflate"; - this.module = "Compression"; + this.module = "Default"; this.description = "Compresses data using the deflate algorithm with no headers."; this.inputType = "byteArray"; this.outputType = "byteArray"; diff --git a/src/core/operations/SetIntersection.mjs b/src/core/operations/SetIntersection.mjs new file mode 100644 index 00000000..efc3503f --- /dev/null +++ b/src/core/operations/SetIntersection.mjs @@ -0,0 +1,33 @@ +import SetOp from "./SetOps"; + +/** + * + */ +class SetIntersection extends SetOp { + + /** + * + */ + constructor() { + super(); + this.setOp = this.runIntersection; + + this.name = "Set Intersection"; + this.description = "Get the intersection of two sets"; + } + + /** + * Get the intersection of the two sets. + * + * @param {Object[]} a + * @param {Object[]} b + * @returns {Object[]} + */ + runIntersection(a, b) { + return a.filter((item) => { + return b.indexOf(item) > -1; + }).join(this.itemDelimiter); + } +} + +export default SetIntersection; diff --git a/src/core/operations/SetOperations.js b/src/core/operations/SetOperations.js deleted file mode 100644 index 332886cb..00000000 --- a/src/core/operations/SetOperations.js +++ /dev/null @@ -1,202 +0,0 @@ -import Utils from "../Utils.js"; - -/** - * Set operations. - * - * @author d98762625 [d98762625@gmail.com] - * @copyright Crown Copyright 2018 - * @license APache-2.0 - * - * @namespace - */ -class SetOps { - - /** - * Set default options for operation - */ - constructor() { - this._sampleDelimiter = "\\n\\n"; - this._operation = ["Union", "Intersection", "Set Difference", "Symmetric Difference", "Cartesian Product", "Power Set"]; - this._itemDelimiter = ","; - } - - /** - * Get operations array - * @returns {String[]} - */ - get OPERATION() { - return this._operation; - } - - /** - * Get sample delimiter - * @returns {String} - */ - get SAMPLE_DELIMITER() { - return this._sampleDelimiter; - } - - /** - * Get item delimiter - * @returns {String} - */ - get ITEM_DELIMITER() { - return this._itemDelimiter; - } - - - /** - * Run the configured set operation. - * - * @param {String} input - * @param {String[]} args - * @returns {html} - */ - runSetOperation(input, args) { - const [sampleDelim, itemDelimiter, operation] = args; - const sets = input.split(sampleDelim); - - if (!sets || (sets.length !== 2 && operation !== "Power Set") || (sets.length !== 1 && operation === "Power Set")) { - return "Incorrect number of sets, perhaps you need to modify the sample delimiter or add more samples?"; - } - - if (this._operation.indexOf(operation) === -1) { - return "Invalid 'Operation' option."; - } - - let result = { - "Union": this.runUnion, - "Intersection": this.runIntersect, - "Set Difference": this.runSetDifference, - "Symmetric Difference": this.runSymmetricDifference, - "Cartesian Product": this.runCartesianProduct, - "Power Set": this.runPowerSet.bind(undefined, itemDelimiter), - }[operation] - .apply(this, sets.map(s => s.split(itemDelimiter))); - - // Formatting issues due to the nested characteristics of power set. - if (operation === "Power Set") { - result = result.map(i => `${i}\n`).join(""); - } else { - result = result.join(itemDelimiter); - } - - return Utils.escapeHtml(result); - } - - /** - * Get the union of the two sets. - * - * @param {Object[]} a - * @param {Object[]} b - * @returns {Object[]} - */ - runUnion(a, b) { - - const result = {}; - - /** - * Only add non-existing items - * @param {Object} hash - */ - const addUnique = (hash) => (item) => { - if (!hash[item]) { - hash[item] = true; - } - }; - - a.map(addUnique(result)); - b.map(addUnique(result)); - - return Object.keys(result); - } - - /** - * Get the intersection of the two sets. - * - * @param {Object[]} a - * @param {Object[]} b - * @returns {Object[]} - */ - runIntersect(a, b) { - return a.filter((item) => { - return b.indexOf(item) > -1; - }); - } - - /** - * Get elements in set a that are not in set b - * - * @param {Object[]} a - * @param {Object[]} b - * @returns {Object[]} - */ - runSetDifference(a, b) { - return a.filter((item) => { - return b.indexOf(item) === -1; - }); - } - - /** - * Get elements of each set that aren't in the other set. - * - * @param {Object[]} a - * @param {Object[]} b - * @return {Object[]} - */ - runSymmetricDifference(a, b) { - return this.runSetDifference(a, b) - .concat(this.runSetDifference(b, a)); - } - - /** - * Return the cartesian product of the two inputted sets. - * - * @param {Object[]} a - * @param {Object[]} b - * @returns {String[]} - */ - runCartesianProduct(a, b) { - return Array(Math.max(a.length, b.length)) - .fill(null) - .map((item, index) => `(${a[index] || undefined},${b[index] || undefined})`); - } - - /** - * Return the power set of the inputted set. - * - * @param {Object[]} a - * @returns {Object[]} - */ - runPowerSet(delimiter, a) { - // empty array items getting picked up - a = a.filter(i => i.length); - if (!a.length) { - return []; - } - - /** - * Decimal to binary function - * @param {*} dec - */ - const toBinary = (dec) => (dec >>> 0).toString(2); - const result = new Set(); - // Get the decimal number to make a binary as long as the input - const maxBinaryValue = parseInt(Number(a.map(i => "1").reduce((p, c) => p + c)), 2); - // Make an array of each binary number from 0 to maximum - const binaries = [...Array(maxBinaryValue + 1).keys()] - .map(toBinary) - .map(i => i.padStart(toBinary(maxBinaryValue).length, "0")); - - // XOR the input with each binary to get each unique permutation - binaries.forEach((binary) => { - const split = binary.split(""); - result.add(a.filter((item, index) => split[index] === "1")); - }); - - // map for formatting & put in length order. - return [...result].map(r => r.join(delimiter)).sort((a, b) => a.length - b.length); - } -} - -export default new SetOps(); diff --git a/src/core/operations/SetOps.mjs b/src/core/operations/SetOps.mjs new file mode 100644 index 00000000..219c99ab --- /dev/null +++ b/src/core/operations/SetOps.mjs @@ -0,0 +1,84 @@ +import Operation from "../Operation"; +import Utils from "../Utils"; + +/** + * + */ +class SetOp extends Operation { + + /** + * + * @param {*} runOp + */ + constructor(runOp) { + super(); + + this.module = "Default"; + this.inputType = "string"; + this.outputType = "string"; + this.args = [ + { + name: "Sample delimiter", + type: "binaryString", + value: "\n\n" + }, + { + name: "Item delimiter", + type: "binaryString", + value: "," + }, + ]; + + this.runOp = runOp; + } + + /** + * + * @param sets + */ + validateSampleNumbers(sets) { + if (!sets || (sets.length !== 2)) { + throw "Incorrect number of sets, perhaps you need to modify the sample delimiter or add more samples?"; + } + } + + /** + * + * @param {*} input + * @param {*} args + */ + run(input, args) { + + [this.sampleDelim, this.itemDelimiter] = args; + const sets = input.split(this.sampleDelim); + + try { + this.validateSampleNumbers(sets); + } catch (e) { + return e; + } + + const result = this.setOp.apply(this, sets.map(s => s.split(this.itemDelimiter))); + + // let result = { + // "Union": this.runUnion, + // "Intersection": this.runIntersect, + // "Set Difference": this.runSetDifference, + // "Symmetric Difference": this.runSymmetricDifference, + // "Cartesian Product": this.runCartesianProduct, + // "Power Set": this.runPowerSet.bind(undefined, itemDelimiter), + // }[operation] + // .apply(this, sets.map(s => s.split(itemDelimiter))); + + // Formatting issues due to the nested characteristics of power set. + // if (operation === "Power Set") { + // result = result.map(i => `${i}\n`).join(""); + // } else { + // result = result.join(itemDelimiter); + // } + + return Utils.escapeHtml(result); + } +} + +export default SetOp; diff --git a/src/core/operations/SetUnion.mjs b/src/core/operations/SetUnion.mjs new file mode 100644 index 00000000..99aabc0a --- /dev/null +++ b/src/core/operations/SetUnion.mjs @@ -0,0 +1,46 @@ +import SetOp from "./SetOps"; + +/** + * + */ +class SetUnion extends SetOp { + + /** + * + */ + constructor() { + super(); + this.setOp = this.runUnion; + + this.name = "Set Union"; + this.description = "Get the union of two sets"; + } + + /** + * Get the union of the two sets. + * + * @param {Object[]} a + * @param {Object[]} b + * @returns {Object[]} + */ + runUnion(a, b) { + const result = {}; + + /** + * Only add non-existing items + * @param {Object} hash + */ + const addUnique = (hash) => (item) => { + if (!hash[item]) { + hash[item] = true; + } + }; + + a.map(addUnique(result)); + b.map(addUnique(result)); + + return Object.keys(result).join(this.itemDelimiter); + } +} + +export default SetUnion; diff --git a/src/core/operations/index.mjs b/src/core/operations/index.mjs index 55a8e79a..ae6c12df 100644 --- a/src/core/operations/index.mjs +++ b/src/core/operations/index.mjs @@ -12,6 +12,9 @@ import Gunzip from "./Gunzip"; import Gzip from "./Gzip"; import RawDeflate from "./RawDeflate"; import RawInflate from "./RawInflate"; +import SetIntersection from "./SetIntersection"; +import SetOps from "./SetOps"; +import SetUnion from "./SetUnion"; import ShowBase64Offsets from "./ShowBase64Offsets"; import ToBase32 from "./ToBase32"; import ToBase64 from "./ToBase64"; @@ -29,6 +32,9 @@ export { Gzip, RawDeflate, RawInflate, + SetIntersection, + SetOps, + SetUnion, ShowBase64Offsets, ToBase32, ToBase64,