From 77b098c5feb405fafdcdf274ccd188e43e1373db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karsten=20Silkenb=C3=A4umer?= Date: Sat, 2 Mar 2019 15:00:42 +0100 Subject: [PATCH 1/6] Add Bacon cipher decoding --- src/core/config/Categories.json | 1 + src/core/lib/Bacon.mjs | 37 ++++ src/core/operations/BaconCipherDecode.mjs | 108 ++++++++++ tests/operations/index.mjs | 1 + tests/operations/tests/BaconCipher.mjs | 246 ++++++++++++++++++++++ 5 files changed, 393 insertions(+) create mode 100644 src/core/lib/Bacon.mjs create mode 100644 src/core/operations/BaconCipherDecode.mjs create mode 100644 tests/operations/tests/BaconCipher.mjs diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index 8235ab10..846e3e8e 100755 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -85,6 +85,7 @@ "Vigenère Decode", "To Morse Code", "From Morse Code", + "Bacon Cipher Decode", "Bifid Cipher Encode", "Bifid Cipher Decode", "Affine Cipher Encode", diff --git a/src/core/lib/Bacon.mjs b/src/core/lib/Bacon.mjs new file mode 100644 index 00000000..a2be4b56 --- /dev/null +++ b/src/core/lib/Bacon.mjs @@ -0,0 +1,37 @@ +/** + * Bacon resources. + * + * @author Karsten Silkenbäumer [kassi@users.noreply.github.com] + * @copyright Karsten Silkenbäumer 2019 + * @license Apache-2.0 + */ + +/** + * Bacon definitions. + */ + +export const BACON_ALPHABET_REDUCED = "ABCDEFGHIKLMNOPQRSTUWXYZ"; +export const BACON_ALPHABET_COMPLETE = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; +export const BACON_TRANSLATION_01 = "0/1"; +export const BACON_TRANSLATION_AB = "A/B"; +export const BACON_TRANSLATION_CASE = "Case"; +export const BACON_TRANSLATION_AMNZ = "A-M/N-Z first letter"; +export const BACON_TRANSLATIONS = [ + BACON_TRANSLATION_01, + BACON_TRANSLATION_AB, + BACON_TRANSLATION_CASE, + BACON_TRANSLATION_AMNZ, +]; +export const BACON_CLEARER_MAP = { + [BACON_TRANSLATIONS[0]]: /[^01]/g, + [BACON_TRANSLATIONS[1]]: /[^ABab]/g, + [BACON_TRANSLATIONS[2]]: /[^A-Za-z]/g, +}; +export const BACON_NORMALIZE_MAP = { + [BACON_TRANSLATIONS[1]]: { + "A": "0", + "B": "1", + "a": "0", + "b": "1" + }, +}; diff --git a/src/core/operations/BaconCipherDecode.mjs b/src/core/operations/BaconCipherDecode.mjs new file mode 100644 index 00000000..2d767538 --- /dev/null +++ b/src/core/operations/BaconCipherDecode.mjs @@ -0,0 +1,108 @@ +/** + * BaconCipher operation. + * +* @author kassi [kassi@users.noreply.github.com] +* @copyright Karsten Silkenbäumer 2019 +* @license Apache-2.0 +*/ + +import Operation from "../Operation"; +import { + BACON_ALPHABET_REDUCED, BACON_ALPHABET_COMPLETE, + BACON_TRANSLATION_CASE, BACON_TRANSLATION_AMNZ, BACON_TRANSLATIONS, BACON_CLEARER_MAP, BACON_NORMALIZE_MAP +} from "../lib/Bacon"; + +/** +* BaconCipherDecode operation +*/ +class BaconCipherDecode extends Operation { + /** + * BaconCipherDecode constructor + */ + constructor() { + super(); + + this.name = "Bacon Cipher Decode"; + this.module = "Default"; + this.description = "Bacon's cipher or the Baconian cipher is a method of steganography(a method of hiding a secret message as opposed to just a cipher) devised by Francis Bacon in 1605.[1][2][3] A message is concealed in the presentation of text, rather than its content."; + this.infoURL = "https://en.wikipedia.org/wiki/Bacon%27s_cipher"; + this.inputType = "string"; + this.outputType = "string"; + this.args = [ + { + "name": "Alphabet", + "type": "option", + "value": [BACON_ALPHABET_REDUCED, BACON_ALPHABET_COMPLETE] + }, + { + "name": "Translation", + "type": "option", + "value": BACON_TRANSLATIONS + }, + { + "name": "Invert Translation", + "type": "boolean", + "value": false + } + ]; + } + + /** + * @param {String} input + * @param {Object[]} args + * @returns {String} + */ + run(input, args) { + const [alphabet, translation, invert] = args; + // split text into groups of 5 characters + + // remove invalid characters + input = input.replace(BACON_CLEARER_MAP[translation], ""); + // normalize to unique alphabet + if (BACON_NORMALIZE_MAP[translation] !== undefined) { + input = input.replace(/./g, function (c) { + return BACON_NORMALIZE_MAP[translation][c]; + }); + } else if (translation === BACON_TRANSLATION_CASE) { + const codeA = "A".charCodeAt(0); + const codeZ = "Z".charCodeAt(0); + input = input.replace(/./g, function (c) { + const code = c.charCodeAt(0); + if (code >= codeA && code <= codeZ) { + return "1"; + } else { + return "0"; + } + }); + } else if (translation === BACON_TRANSLATION_AMNZ) { + const words = input.split(" "); + const letters = words.map(function (e) { + const code = e[0].toUpperCase().charCodeAt(0); + return code >= "N".charCodeAt(0) ? "1" : "0"; + }); + input = letters.join(""); + } + + if (invert) { + input = input.replace(/./g, function (c) { + return { + "0": "1", + "1": "0" + }[c]; + }); + } + + // group into 5 + const inputArray = input.match(/(.{5})/g) || []; + + let output = ""; + for (let index = 0; index < inputArray.length; index++) { + const code = inputArray[index]; + const number = parseInt(code, 2); + output += number < alphabet.length ? alphabet[number] : "?"; + } + return output; + } +} + +export default BaconCipherDecode; diff --git a/tests/operations/index.mjs b/tests/operations/index.mjs index fb68ed9c..e61f886a 100644 --- a/tests/operations/index.mjs +++ b/tests/operations/index.mjs @@ -26,6 +26,7 @@ global.ENVIRONMENT_IS_WEB = function() { import TestRegister from "./TestRegister"; import "./tests/BCD"; import "./tests/BSON"; +import "./tests/BaconCipher"; import "./tests/Base58"; import "./tests/Base64"; import "./tests/Base62"; diff --git a/tests/operations/tests/BaconCipher.mjs b/tests/operations/tests/BaconCipher.mjs new file mode 100644 index 00000000..b4b63b8f --- /dev/null +++ b/tests/operations/tests/BaconCipher.mjs @@ -0,0 +1,246 @@ +/** + * BaconCipher tests. + * + * @author Karsten Silkenbäumer [kassi@users.noreply.github.com] + * @copyright Karsten Silkenbäumer 2019 + * @license Apache-2.0 + */ +import TestRegister from "../TestRegister"; +import { BACON_ALPHABET_REDUCED, BACON_ALPHABET_COMPLETE, BACON_TRANSLATIONS } from "../../../src/core/lib/Bacon"; + +const alphabets = [BACON_ALPHABET_REDUCED, BACON_ALPHABET_COMPLETE]; +const translations = BACON_TRANSLATIONS; + +TestRegister.addTests([ + { + name: "Bacon Decode: no input", + input: "", + expectedOutput: "", + recipeConfig: [ + { + op: "Bacon Cipher Decode", + args: [alphabets[0], translations[0], false] + } + ], + }, + { + name: "Bacon Decode: reduced alphabet 0/1", + input: "00011 00100 00010 01101 00011 01000 01100 00110 00001 00000 00010 01101 01100 10100 01101 10000 01001 10001", + expectedOutput: "DECODINGBACONWORKS", + recipeConfig: [ + { + op: "Bacon Cipher Decode", + args: [alphabets[0], translations[0], false] + } + ], + }, + { + name: "Bacon Decode: reduced alphabet 0/1 inverse", + input: "11100 11011 11101 10010 11100 10111 10011 11001 11110 11111 11101 10010 10011 01011 10010 01111 10110 01110", + expectedOutput: "DECODINGBACONWORKS", + recipeConfig: [ + { + op: "Bacon Cipher Decode", + args: [alphabets[0], translations[0], true] + } + ], + }, + { + name: "Bacon Decode: reduced alphabet A/B lower case", + input: "aaabb aabaa aaaba abbab aaabb abaaa abbaa aabba aaaab aaaaa aaaba abbab abbaa babaa abbab baaaa abaab baaab", + expectedOutput: "DECODINGBACONWORKS", + recipeConfig: [ + { + op: "Bacon Cipher Decode", + args: [alphabets[0], translations[1], false] + } + ], + }, + { + name: "Bacon Decode: reduced alphabet A/B lower case inverse", + input: "bbbaa bbabb bbbab baaba bbbaa babbb baabb bbaab bbbba bbbbb bbbab baaba baabb ababb baaba abbbb babba abbba", + expectedOutput: "DECODINGBACONWORKS", + recipeConfig: [ + { + op: "Bacon Cipher Decode", + args: [alphabets[0], translations[1], true] + } + ], + }, + { + name: "Bacon Decode: reduced alphabet A/B upper case", + input: "AAABB AABAA AAABA ABBAB AAABB ABAAA ABBAA AABBA AAAAB AAAAA AAABA ABBAB ABBAA BABAA ABBAB BAAAA ABAAB BAAAB", + expectedOutput: "DECODINGBACONWORKS", + recipeConfig: [ + { + op: "Bacon Cipher Decode", + args: [alphabets[0], translations[1], false] + } + ], + }, + { + name: "Bacon Decode: reduced alphabet A/B upper case inverse", + input: "BBBAA BBABB BBBAB BAABA BBBAA BABBB BAABB BBAAB BBBBA BBBBB BBBAB BAABA BAABB ABABB BAABA ABBBB BABBA ABBBA", + expectedOutput: "DECODINGBACONWORKS", + recipeConfig: [ + { + op: "Bacon Cipher Decode", + args: [alphabets[0], translations[1], true] + } + ], + }, + { + name: "Bacon Decode: reduced alphabet case code", + input: "thiS IsaN exampLe oF ThE bacON cIpher WIth upPPercasE letters tRanSLaTiNG to OnEs anD LoWErcase To zERoes. KS", + expectedOutput: "DECODINGBACONWORKS", + recipeConfig: [ + { + op: "Bacon Cipher Decode", + args: [alphabets[0], translations[2], false] + } + ], + }, + { + name: "Bacon Decode: reduced alphabet case code inverse", + input: "THIs iS An EXAMPlE Of tHe BACon CiPHER wiTH UPppERCASe LETTERS TrANslAtIng TO oNeS ANd lOweRCASE tO ZerOES. ks", + expectedOutput: "DECODINGBACONWORKS", + recipeConfig: [ + { + op: "Bacon Cipher Decode", + args: [alphabets[0], translations[2], true] + } + ], + }, + { + name: "Bacon Decode: reduced alphabet case code", + input: "A little example of the Bacon Cipher to be decoded. It is a working example and shorter than my others, but it anyways works tremendously. And just that's important, correct?", + expectedOutput: "DECODE", + recipeConfig: [ + { + op: "Bacon Cipher Decode", + args: [alphabets[0], translations[3], false] + } + ], + }, + { + name: "Bacon Decode: reduced alphabet case code inverse", + input: "Well, there's now another example which will be not only strange to read but sound weird for everyone not knowing what the thing is about. Nevertheless, works great out of the box.", + expectedOutput: "DECODE", + recipeConfig: [ + { + op: "Bacon Cipher Decode", + args: [alphabets[0], translations[3], true] + } + ], + }, + { + name: "Bacon Decode: complete alphabet 0/1", + input: "00011 00100 00010 01110 00011 01000 01101 00110 00001 00000 00010 01110 01101 10110 01110 10001 01010 10010", + expectedOutput: "DECODINGBACONWORKS", + recipeConfig: [ + { + op: "Bacon Cipher Decode", + args: [alphabets[1], translations[0], false] + } + ], + }, + { + name: "Bacon Decode: complete alphabet 0/1 inverse", + input: "11100 11011 11101 10001 11100 10111 10010 11001 11110 11111 11101 10001 10010 01001 10001 01110 10101 01101", + expectedOutput: "DECODINGBACONWORKS", + recipeConfig: [ + { + op: "Bacon Cipher Decode", + args: [alphabets[1], translations[0], true] + } + ], + }, + { + name: "Bacon Decode: complete alphabet A/B lower case", + input: "aaabb aabaa aaaba abbba aaabb abaaa abbab aabba aaaab aaaaa aaaba abbba abbab babba abbba baaab ababa baaba", + expectedOutput: "DECODINGBACONWORKS", + recipeConfig: [ + { + op: "Bacon Cipher Decode", + args: [alphabets[1], translations[1], false] + } + ], + }, + { + name: "Bacon Decode: complete alphabet A/B lower case inverse", + input: "bbbaa bbabb bbbab baaab bbbaa babbb baaba bbaab bbbba bbbbb bbbab baaab baaba abaab baaab abbba babab abbab", + expectedOutput: "DECODINGBACONWORKS", + recipeConfig: [ + { + op: "Bacon Cipher Decode", + args: [alphabets[1], translations[1], true] + } + ], + }, + { + name: "Bacon Decode: complete alphabet A/B upper case", + input: "AAABB AABAA AAABA ABBBA AAABB ABAAA ABBAB AABBA AAAAB AAAAA AAABA ABBBA ABBAB BABBA ABBBA BAAAB ABABA BAABA", + expectedOutput: "DECODINGBACONWORKS", + recipeConfig: [ + { + op: "Bacon Cipher Decode", + args: [alphabets[1], translations[1], false] + } + ], + }, + { + name: "Bacon Decode: complete alphabet A/B upper case inverse", + input: "BBBAA BBABB BBBAB BAAAB BBBAA BABBB BAABA BBAAB BBBBA BBBBB BBBAB BAAAB BAABA ABAAB BAAAB ABBBA BABAB ABBAB", + expectedOutput: "DECODINGBACONWORKS", + recipeConfig: [ + { + op: "Bacon Cipher Decode", + args: [alphabets[1], translations[1], true] + } + ], + }, + { + name: "Bacon Decode: complete alphabet case code", + input: "thiS IsaN exampLe oF THe bacON cIpher WItH upPPercasE letters tRanSLAtiNG tO OnES anD LOwErcaSe To ZeRoeS. kS", + expectedOutput: "DECODINGBACONWORKS", + recipeConfig: [ + { + op: "Bacon Cipher Decode", + args: [alphabets[1], translations[2], false] + } + ], + }, + { + name: "Bacon Decode: complete alphabet case code inverse", + input: "THIs iSAn EXAMPlE Of thE BACon CiPHER wiTh UPppERCASe LETTERS TrANslaTIng To zEroES and LoWERcAsE tO oNEs. Ks", + expectedOutput: "DECODINGBACONWORKS", + recipeConfig: [ + { + op: "Bacon Cipher Decode", + args: [alphabets[1], translations[2], true] + } + ], + }, + { + name: "Bacon Decode: complete alphabet case code", + input: "A little example of the Bacon Cipher to be decoded. It is a working example and shorter than the first, but it anyways works tremendously. And just that's important, correct?", + expectedOutput: "DECODE", + recipeConfig: [ + { + op: "Bacon Cipher Decode", + args: [alphabets[1], translations[3], false] + } + ], + }, + { + name: "Bacon Decode: complete alphabet case code inverse", + input: "Well, there's now another example which will be not only strange to read but sound weird for everyone knowing nothing what the thing is about. Nevertheless, works great out of the box.", + expectedOutput: "DECODE", + recipeConfig: [ + { + op: "Bacon Cipher Decode", + args: [alphabets[1], translations[3], true] + } + ], + }, +]); From a262d70b88584d33dee99dee9062ee7c9687a620 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karsten=20Silkenb=C3=A4umer?= Date: Sat, 2 Mar 2019 17:33:17 +0100 Subject: [PATCH 2/6] Add Bacon cipher encoding --- src/core/config/Categories.json | 1 + src/core/lib/Bacon.mjs | 24 +++ src/core/operations/BaconCipherDecode.mjs | 10 +- src/core/operations/BaconCipherEncode.mjs | 103 ++++++++++++ tests/operations/tests/BaconCipher.mjs | 189 ++++++++++++++++++++++ 5 files changed, 320 insertions(+), 7 deletions(-) create mode 100644 src/core/operations/BaconCipherEncode.mjs diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index 846e3e8e..05ab4524 100755 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -85,6 +85,7 @@ "Vigenère Decode", "To Morse Code", "From Morse Code", + "Bacon Cipher Encode", "Bacon Cipher Decode", "Bifid Cipher Encode", "Bifid Cipher Decode", diff --git a/src/core/lib/Bacon.mjs b/src/core/lib/Bacon.mjs index a2be4b56..e45c3b74 100644 --- a/src/core/lib/Bacon.mjs +++ b/src/core/lib/Bacon.mjs @@ -12,6 +12,7 @@ export const BACON_ALPHABET_REDUCED = "ABCDEFGHIKLMNOPQRSTUWXYZ"; export const BACON_ALPHABET_COMPLETE = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; +export const BACON_CODES_REDUCED = [0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 23]; export const BACON_TRANSLATION_01 = "0/1"; export const BACON_TRANSLATION_AB = "A/B"; export const BACON_TRANSLATION_CASE = "Case"; @@ -22,6 +23,10 @@ export const BACON_TRANSLATIONS = [ BACON_TRANSLATION_CASE, BACON_TRANSLATION_AMNZ, ]; +export const BACON_TRANSLATIONS_FOR_ENCODING = [ + BACON_TRANSLATION_01, + BACON_TRANSLATION_AB +]; export const BACON_CLEARER_MAP = { [BACON_TRANSLATIONS[0]]: /[^01]/g, [BACON_TRANSLATIONS[1]]: /[^ABab]/g, @@ -35,3 +40,22 @@ export const BACON_NORMALIZE_MAP = { "b": "1" }, }; + +/** + * Swaps zeros to ones and ones to zeros. + * + * @param {string} data + * @returns {string} + * + * @example + * // returns "11001 01010" + * swapZeroAndOne("00110 10101"); + */ +export function swapZeroAndOne(string) { + return string.replace(/[01]/g, function (c) { + return { + "0": "1", + "1": "0" + }[c]; + }); +} diff --git a/src/core/operations/BaconCipherDecode.mjs b/src/core/operations/BaconCipherDecode.mjs index 2d767538..6aa5aac8 100644 --- a/src/core/operations/BaconCipherDecode.mjs +++ b/src/core/operations/BaconCipherDecode.mjs @@ -9,7 +9,8 @@ import Operation from "../Operation"; import { BACON_ALPHABET_REDUCED, BACON_ALPHABET_COMPLETE, - BACON_TRANSLATION_CASE, BACON_TRANSLATION_AMNZ, BACON_TRANSLATIONS, BACON_CLEARER_MAP, BACON_NORMALIZE_MAP + BACON_TRANSLATION_CASE, BACON_TRANSLATION_AMNZ, BACON_TRANSLATIONS, BACON_CLEARER_MAP, BACON_NORMALIZE_MAP, + swapZeroAndOne } from "../lib/Bacon"; /** @@ -84,12 +85,7 @@ class BaconCipherDecode extends Operation { } if (invert) { - input = input.replace(/./g, function (c) { - return { - "0": "1", - "1": "0" - }[c]; - }); + input = swapZeroAndOne(input); } // group into 5 diff --git a/src/core/operations/BaconCipherEncode.mjs b/src/core/operations/BaconCipherEncode.mjs new file mode 100644 index 00000000..4761df46 --- /dev/null +++ b/src/core/operations/BaconCipherEncode.mjs @@ -0,0 +1,103 @@ +/** + * BaconCipher operation. + * +* @author kassi [kassi@users.noreply.github.com] +* @copyright Karsten Silkenbäumer 2019 +* @license Apache-2.0 +*/ + +import Operation from "../Operation"; +import { + BACON_ALPHABET_REDUCED, BACON_ALPHABET_COMPLETE, + BACON_TRANSLATIONS_FOR_ENCODING, BACON_TRANSLATION_AB, + swapZeroAndOne +} from "../lib/Bacon"; +import { BACON_CODES_REDUCED } from "../lib/Bacon.mjs"; + +/** +* BaconCipherEncode operation +*/ +class BaconCipherEncode extends Operation { + /** + * BaconCipherEncode constructor + */ + constructor() { + super(); + + this.name = "Bacon Cipher Encode"; + this.module = "Default"; + this.description = "Bacon's cipher or the Baconian cipher is a method of steganography(a method of hiding a secret message as opposed to just a cipher) devised by Francis Bacon in 1605.[1][2][3] A message is concealed in the presentation of text, rather than its content."; + this.infoURL = "https://en.wikipedia.org/wiki/Bacon%27s_cipher"; + this.inputType = "string"; + this.outputType = "string"; + this.args = [ + { + "name": "Alphabet", + "type": "option", + "value": [BACON_ALPHABET_REDUCED, BACON_ALPHABET_COMPLETE] + }, + { + "name": "Translation", + "type": "option", + "value": BACON_TRANSLATIONS_FOR_ENCODING + }, + { + "name": "Keep extra characters", + "type": "boolean", + "value": false + }, + { + "name": "Invert Translation", + "type": "boolean", + "value": false + } + ]; + } + + /** + * @param {String} input + * @param {Object[]} args + * @returns {String} + */ + run(input, args) { + const [alphabet, translation, keep, invert] = args; + + const charCodeA = "A".charCodeAt(0); + const charCodeZ = "Z".charCodeAt(0); + + let output = input.replace(/./g, function (c) { + const charCode = c.toUpperCase().charCodeAt(0); + if (charCode >= charCodeA && charCode <= charCodeZ) { + let code = charCode - charCodeA; + if (alphabet === BACON_ALPHABET_REDUCED) { + code = BACON_CODES_REDUCED[code]; + } + const bacon = ("00000" + code.toString(2)).substr(-5, 5); + return bacon; + } else { + return c; + } + }); + + if (invert) { + output = swapZeroAndOne(output); + } + if (!keep) { + output = output.replace(/[^01]/g, ""); + const outputArray = output.match(/(.{5})/g) || []; + output = outputArray.join(" "); + } + if (translation === BACON_TRANSLATION_AB) { + output = output.replace(/[01]/g, function (c) { + return { + "0": "A", + "1": "B" + }[c]; + }); + } + + return output; + } +} + +export default BaconCipherEncode; diff --git a/tests/operations/tests/BaconCipher.mjs b/tests/operations/tests/BaconCipher.mjs index b4b63b8f..dce51659 100644 --- a/tests/operations/tests/BaconCipher.mjs +++ b/tests/operations/tests/BaconCipher.mjs @@ -242,5 +242,194 @@ TestRegister.addTests([ args: [alphabets[1], translations[3], true] } ], + } +]); +TestRegister.addTests([ + { + name: "Bacon Encode: no input", + input: "", + expectedOutput: "", + recipeConfig: [ + { + op: "Bacon Cipher Encode", + args: [alphabets[0], translations[0], false, false] + } + ], + }, + { + name: "Bacon Encode: reduced alphabet 0/1", + input: "There's a fox, and it jumps over the fence.", + expectedOutput: "10010 00111 00100 10000 00100 10001 00000 00101 01101 10101 00000 01100 00011 01000 10010 01000 10011 01011 01110 10001 01101 10011 00100 10000 10010 00111 00100 00101 00100 01100 00010 00100", + recipeConfig: [ + { + op: "Bacon Cipher Encode", + args: [alphabets[0], translations[0], false, false] + } + ], + }, + { + name: "Bacon Encode: reduced alphabet 0/1 inverse", + input: "There's a fox, and it jumps over the fence.", + expectedOutput: "01101 11000 11011 01111 11011 01110 11111 11010 10010 01010 11111 10011 11100 10111 01101 10111 01100 10100 10001 01110 10010 01100 11011 01111 01101 11000 11011 11010 11011 10011 11101 11011", + recipeConfig: [ + { + op: "Bacon Cipher Encode", + args: [alphabets[0], translations[0], false, true] + } + ], + }, + { + name: "Bacon Encode: reduced alphabet 0/1, keeping extra characters", + input: "There's a fox, and it jumps over the fence.", + expectedOutput: "1001000111001001000000100'10001 00000 001010110110101, 000000110000011 0100010010 0100010011010110111010001 01101100110010010000 100100011100100 0010100100011000001000100.", + recipeConfig: [ + { + op: "Bacon Cipher Encode", + args: [alphabets[0], translations[0], true, false] + } + ], + }, + { + name: "Bacon Encode: reduced alphabet 0/1 inverse, keeping extra characters", + input: "There's a fox, and it jumps over the fence.", + expectedOutput: "0110111000110110111111011'01110 11111 110101001001010, 111111001111100 1011101101 1011101100101001000101110 10010011001101101111 011011100011011 1101011011100111110111011.", + recipeConfig: [ + { + op: "Bacon Cipher Encode", + args: [alphabets[0], translations[0], true, true] + } + ], + }, + { + name: "Bacon Encode: reduced alphabet A/B", + input: "There's a fox, and it jumps over the fence.", + expectedOutput: "BAABA AABBB AABAA BAAAA AABAA BAAAB AAAAA AABAB ABBAB BABAB AAAAA ABBAA AAABB ABAAA BAABA ABAAA BAABB ABABB ABBBA BAAAB ABBAB BAABB AABAA BAAAA BAABA AABBB AABAA AABAB AABAA ABBAA AAABA AABAA", + recipeConfig: [ + { + op: "Bacon Cipher Encode", + args: [alphabets[0], translations[1], false, false] + } + ], + }, + { + name: "Bacon Encode: reduced alphabet A/B inverse", + input: "There's a fox, and it jumps over the fence.", + expectedOutput: "ABBAB BBAAA BBABB ABBBB BBABB ABBBA BBBBB BBABA BAABA ABABA BBBBB BAABB BBBAA BABBB ABBAB BABBB ABBAA BABAA BAAAB ABBBA BAABA ABBAA BBABB ABBBB ABBAB BBAAA BBABB BBABA BBABB BAABB BBBAB BBABB", + recipeConfig: [ + { + op: "Bacon Cipher Encode", + args: [alphabets[0], translations[1], false, true] + } + ], + }, + { + name: "Bacon Encode: reduced alphabet A/B, keeping extra characters", + input: "There's a fox, and it jumps over the fence.", + expectedOutput: "BAABAAABBBAABAABAAAAAABAA'BAAAB AAAAA AABABABBABBABAB, AAAAAABBAAAAABB ABAAABAABA ABAAABAABBABABBABBBABAAAB ABBABBAABBAABAABAAAA BAABAAABBBAABAA AABABAABAAABBAAAAABAAABAA.", + recipeConfig: [ + { + op: "Bacon Cipher Encode", + args: [alphabets[0], translations[1], true, false] + } + ], + }, + { + name: "Bacon Encode: reduced alphabet A/B inverse, keeping extra characters", + input: "There's a fox, and it jumps over the fence.", + expectedOutput: "ABBABBBAAABBABBABBBBBBABB'ABBBA BBBBB BBABABAABAABABA, BBBBBBAABBBBBAA BABBBABBAB BABBBABBAABABAABAAABABBBA BAABAABBAABBABBABBBB ABBABBBAAABBABB BBABABBABBBAABBBBBABBBABB.", + recipeConfig: [ + { + op: "Bacon Cipher Encode", + args: [alphabets[0], translations[1], true, true] + } + ], + }, + { + name: "Bacon Encode: complete alphabet 0/1", + input: "There's a fox, and it jumps over the fence.", + expectedOutput: "10011 00111 00100 10001 00100 10010 00000 00101 01110 10111 00000 01101 00011 01000 10011 01001 10100 01100 01111 10010 01110 10101 00100 10001 10011 00111 00100 00101 00100 01101 00010 00100", + recipeConfig: [ + { + op: "Bacon Cipher Encode", + args: [alphabets[1], translations[0], false, false] + } + ], + }, + { + name: "Bacon Encode: complete alphabet 0/1 inverse", + input: "There's a fox, and it jumps over the fence.", + expectedOutput: "01100 11000 11011 01110 11011 01101 11111 11010 10001 01000 11111 10010 11100 10111 01100 10110 01011 10011 10000 01101 10001 01010 11011 01110 01100 11000 11011 11010 11011 10010 11101 11011", + recipeConfig: [ + { + op: "Bacon Cipher Encode", + args: [alphabets[1], translations[0], false, true] + } + ], + }, + { + name: "Bacon Encode: complete alphabet 0/1, keeping extra characters", + input: "There's a fox, and it jumps over the fence.", + expectedOutput: "1001100111001001000100100'10010 00000 001010111010111, 000000110100011 0100010011 0100110100011000111110010 01110101010010010001 100110011100100 0010100100011010001000100.", + recipeConfig: [ + { + op: "Bacon Cipher Encode", + args: [alphabets[1], translations[0], true, false] + } + ], + }, + { + name: "Bacon Encode: complete alphabet 0/1 inverse, keeping extra characters", + input: "There's a fox, and it jumps over the fence.", + expectedOutput: "0110011000110110111011011'01101 11111 110101000101000, 111111001011100 1011101100 1011001011100111000001101 10001010101101101110 011001100011011 1101011011100101110111011.", + recipeConfig: [ + { + op: "Bacon Cipher Encode", + args: [alphabets[1], translations[0], true, true] + } + ], + }, + { + name: "Bacon Encode: complete alphabet A/B", + input: "There's a fox, and it jumps over the fence.", + expectedOutput: "BAABB AABBB AABAA BAAAB AABAA BAABA AAAAA AABAB ABBBA BABBB AAAAA ABBAB AAABB ABAAA BAABB ABAAB BABAA ABBAA ABBBB BAABA ABBBA BABAB AABAA BAAAB BAABB AABBB AABAA AABAB AABAA ABBAB AAABA AABAA", + recipeConfig: [ + { + op: "Bacon Cipher Encode", + args: [alphabets[1], translations[1], false, false] + } + ], + }, + { + name: "Bacon Encode: complete alphabet A/B inverse", + input: "There's a fox, and it jumps over the fence.", + expectedOutput: "ABBAA BBAAA BBABB ABBBA BBABB ABBAB BBBBB BBABA BAAAB ABAAA BBBBB BAABA BBBAA BABBB ABBAA BABBA ABABB BAABB BAAAA ABBAB BAAAB ABABA BBABB ABBBA ABBAA BBAAA BBABB BBABA BBABB BAABA BBBAB BBABB", + recipeConfig: [ + { + op: "Bacon Cipher Encode", + args: [alphabets[1], translations[1], false, true] + } + ], + }, + { + name: "Bacon Encode: complete alphabet A/B, keeping extra characters", + input: "There's a fox, and it jumps over the fence.", + expectedOutput: "BAABBAABBBAABAABAAABAABAA'BAABA AAAAA AABABABBBABABBB, AAAAAABBABAAABB ABAAABAABB ABAABBABAAABBAAABBBBBAABA ABBBABABABAABAABAAAB BAABBAABBBAABAA AABABAABAAABBABAAABAAABAA.", + recipeConfig: [ + { + op: "Bacon Cipher Encode", + args: [alphabets[1], translations[1], true, false] + } + ], + }, + { + name: "Bacon Encode: complete alphabet A/B inverse, keeping extra characters", + input: "There's a fox, and it jumps over the fence.", + expectedOutput: "ABBAABBAAABBABBABBBABBABB'ABBAB BBBBB BBABABAAABABAAA, BBBBBBAABABBBAA BABBBABBAA BABBAABABBBAABBBAAAAABBAB BAAABABABABBABBABBBA ABBAABBAAABBABB BBABABBABBBAABABBBABBBABB.", + recipeConfig: [ + { + op: "Bacon Cipher Encode", + args: [alphabets[1], translations[1], true, true] + } + ], }, ]); From d36cede0c7706c90fa579991022029946779d589 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karsten=20Silkenb=C3=A4umer?= Date: Sat, 2 Mar 2019 17:55:03 +0100 Subject: [PATCH 3/6] Use better names for the alphabet selection --- src/core/lib/Bacon.mjs | 13 +++++++++---- src/core/operations/BaconCipherDecode.mjs | 9 +++++---- src/core/operations/BaconCipherEncode.mjs | 10 +++++----- tests/operations/tests/BaconCipher.mjs | 4 ++-- 4 files changed, 21 insertions(+), 15 deletions(-) diff --git a/src/core/lib/Bacon.mjs b/src/core/lib/Bacon.mjs index e45c3b74..3ed39336 100644 --- a/src/core/lib/Bacon.mjs +++ b/src/core/lib/Bacon.mjs @@ -9,10 +9,15 @@ /** * Bacon definitions. */ - -export const BACON_ALPHABET_REDUCED = "ABCDEFGHIKLMNOPQRSTUWXYZ"; -export const BACON_ALPHABET_COMPLETE = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; -export const BACON_CODES_REDUCED = [0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 23]; +export const BACON_ALPHABETS = { + "Standard (I=J and U=V)": { + alphabet: "ABCDEFGHIKLMNOPQRSTUWXYZ", + codes: [0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 23] + }, + "Complete": { + alphabet: "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + } +}; export const BACON_TRANSLATION_01 = "0/1"; export const BACON_TRANSLATION_AB = "A/B"; export const BACON_TRANSLATION_CASE = "Case"; diff --git a/src/core/operations/BaconCipherDecode.mjs b/src/core/operations/BaconCipherDecode.mjs index 6aa5aac8..ecd1bc92 100644 --- a/src/core/operations/BaconCipherDecode.mjs +++ b/src/core/operations/BaconCipherDecode.mjs @@ -8,7 +8,7 @@ import Operation from "../Operation"; import { - BACON_ALPHABET_REDUCED, BACON_ALPHABET_COMPLETE, + BACON_ALPHABETS, BACON_TRANSLATION_CASE, BACON_TRANSLATION_AMNZ, BACON_TRANSLATIONS, BACON_CLEARER_MAP, BACON_NORMALIZE_MAP, swapZeroAndOne } from "../lib/Bacon"; @@ -33,7 +33,7 @@ class BaconCipherDecode extends Operation { { "name": "Alphabet", "type": "option", - "value": [BACON_ALPHABET_REDUCED, BACON_ALPHABET_COMPLETE] + "value": Object.keys(BACON_ALPHABETS) }, { "name": "Translation", @@ -55,10 +55,11 @@ class BaconCipherDecode extends Operation { */ run(input, args) { const [alphabet, translation, invert] = args; - // split text into groups of 5 characters + const alphabetObject = BACON_ALPHABETS[alphabet]; // remove invalid characters input = input.replace(BACON_CLEARER_MAP[translation], ""); + // normalize to unique alphabet if (BACON_NORMALIZE_MAP[translation] !== undefined) { input = input.replace(/./g, function (c) { @@ -95,7 +96,7 @@ class BaconCipherDecode extends Operation { for (let index = 0; index < inputArray.length; index++) { const code = inputArray[index]; const number = parseInt(code, 2); - output += number < alphabet.length ? alphabet[number] : "?"; + output += number < alphabetObject.alphabet.length ? alphabetObject.alphabet[number] : "?"; } return output; } diff --git a/src/core/operations/BaconCipherEncode.mjs b/src/core/operations/BaconCipherEncode.mjs index 4761df46..e163792e 100644 --- a/src/core/operations/BaconCipherEncode.mjs +++ b/src/core/operations/BaconCipherEncode.mjs @@ -8,11 +8,10 @@ import Operation from "../Operation"; import { - BACON_ALPHABET_REDUCED, BACON_ALPHABET_COMPLETE, + BACON_ALPHABETS, BACON_TRANSLATIONS_FOR_ENCODING, BACON_TRANSLATION_AB, swapZeroAndOne } from "../lib/Bacon"; -import { BACON_CODES_REDUCED } from "../lib/Bacon.mjs"; /** * BaconCipherEncode operation @@ -34,7 +33,7 @@ class BaconCipherEncode extends Operation { { "name": "Alphabet", "type": "option", - "value": [BACON_ALPHABET_REDUCED, BACON_ALPHABET_COMPLETE] + "value": Object.keys(BACON_ALPHABETS) }, { "name": "Translation", @@ -62,6 +61,7 @@ class BaconCipherEncode extends Operation { run(input, args) { const [alphabet, translation, keep, invert] = args; + const alphabetObject = BACON_ALPHABETS[alphabet]; const charCodeA = "A".charCodeAt(0); const charCodeZ = "Z".charCodeAt(0); @@ -69,8 +69,8 @@ class BaconCipherEncode extends Operation { const charCode = c.toUpperCase().charCodeAt(0); if (charCode >= charCodeA && charCode <= charCodeZ) { let code = charCode - charCodeA; - if (alphabet === BACON_ALPHABET_REDUCED) { - code = BACON_CODES_REDUCED[code]; + if (alphabetObject.codes !== undefined) { + code = alphabetObject.codes[code]; } const bacon = ("00000" + code.toString(2)).substr(-5, 5); return bacon; diff --git a/tests/operations/tests/BaconCipher.mjs b/tests/operations/tests/BaconCipher.mjs index dce51659..16f4bac1 100644 --- a/tests/operations/tests/BaconCipher.mjs +++ b/tests/operations/tests/BaconCipher.mjs @@ -6,9 +6,9 @@ * @license Apache-2.0 */ import TestRegister from "../TestRegister"; -import { BACON_ALPHABET_REDUCED, BACON_ALPHABET_COMPLETE, BACON_TRANSLATIONS } from "../../../src/core/lib/Bacon"; +import { BACON_ALPHABETS, BACON_TRANSLATIONS } from "../../../src/core/lib/Bacon"; -const alphabets = [BACON_ALPHABET_REDUCED, BACON_ALPHABET_COMPLETE]; +const alphabets = Object.keys(BACON_ALPHABETS); const translations = BACON_TRANSLATIONS; TestRegister.addTests([ From 282f02f4d579f408842853bccac93dc4039f2357 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karsten=20Silkenb=C3=A4umer?= Date: Sat, 2 Mar 2019 22:17:44 +0100 Subject: [PATCH 4/6] Fix error when decoding a text with 2+ whitespaces in AMNZ mode --- src/core/operations/BaconCipherDecode.mjs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/core/operations/BaconCipherDecode.mjs b/src/core/operations/BaconCipherDecode.mjs index ecd1bc92..5d830603 100644 --- a/src/core/operations/BaconCipherDecode.mjs +++ b/src/core/operations/BaconCipherDecode.mjs @@ -77,10 +77,14 @@ class BaconCipherDecode extends Operation { } }); } else if (translation === BACON_TRANSLATION_AMNZ) { - const words = input.split(" "); + const words = input.split(/\s+/); const letters = words.map(function (e) { - const code = e[0].toUpperCase().charCodeAt(0); - return code >= "N".charCodeAt(0) ? "1" : "0"; + if (e) { + const code = e[0].toUpperCase().charCodeAt(0); + return code >= "N".charCodeAt(0) ? "1" : "0"; + } else { + return ""; + } }); input = letters.join(""); } From 14d924f6c74f5d8cdd5450e318dfdc38a7a36ac7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karsten=20Silkenb=C3=A4umer?= Date: Sat, 2 Mar 2019 22:27:53 +0100 Subject: [PATCH 5/6] Add test for the error fixed before --- tests/operations/tests/BaconCipher.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/operations/tests/BaconCipher.mjs b/tests/operations/tests/BaconCipher.mjs index 16f4bac1..ba3922d7 100644 --- a/tests/operations/tests/BaconCipher.mjs +++ b/tests/operations/tests/BaconCipher.mjs @@ -234,7 +234,7 @@ TestRegister.addTests([ }, { name: "Bacon Decode: complete alphabet case code inverse", - input: "Well, there's now another example which will be not only strange to read but sound weird for everyone knowing nothing what the thing is about. Nevertheless, works great out of the box.", + input: "Well, there's now another example which will be not only strange to read but sound weird for everyone knowing nothing what the thing is about. Nevertheless, works great out of the box. ", expectedOutput: "DECODE", recipeConfig: [ { From ad571e60190fdd7c01e7241f38d6a6feb554ee76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karsten=20Silkenb=C3=A4umer?= Date: Sun, 3 Mar 2019 17:20:54 +0100 Subject: [PATCH 6/6] Change author URL --- src/core/lib/Bacon.mjs | 2 +- src/core/operations/BaconCipherDecode.mjs | 2 +- src/core/operations/BaconCipherEncode.mjs | 2 +- tests/operations/tests/BaconCipher.mjs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/lib/Bacon.mjs b/src/core/lib/Bacon.mjs index 3ed39336..9e664cce 100644 --- a/src/core/lib/Bacon.mjs +++ b/src/core/lib/Bacon.mjs @@ -1,7 +1,7 @@ /** * Bacon resources. * - * @author Karsten Silkenbäumer [kassi@users.noreply.github.com] + * @author Karsten Silkenbäumer [github.com/kassi] * @copyright Karsten Silkenbäumer 2019 * @license Apache-2.0 */ diff --git a/src/core/operations/BaconCipherDecode.mjs b/src/core/operations/BaconCipherDecode.mjs index 5d830603..624765d6 100644 --- a/src/core/operations/BaconCipherDecode.mjs +++ b/src/core/operations/BaconCipherDecode.mjs @@ -1,7 +1,7 @@ /** * BaconCipher operation. * -* @author kassi [kassi@users.noreply.github.com] +* @author Karsten Silkenbäumer [github.com/kassi] * @copyright Karsten Silkenbäumer 2019 * @license Apache-2.0 */ diff --git a/src/core/operations/BaconCipherEncode.mjs b/src/core/operations/BaconCipherEncode.mjs index e163792e..aba7483b 100644 --- a/src/core/operations/BaconCipherEncode.mjs +++ b/src/core/operations/BaconCipherEncode.mjs @@ -1,7 +1,7 @@ /** * BaconCipher operation. * -* @author kassi [kassi@users.noreply.github.com] +* @author Karsten Silkenbäumer [github.com/kassi] * @copyright Karsten Silkenbäumer 2019 * @license Apache-2.0 */ diff --git a/tests/operations/tests/BaconCipher.mjs b/tests/operations/tests/BaconCipher.mjs index ba3922d7..b4634bd4 100644 --- a/tests/operations/tests/BaconCipher.mjs +++ b/tests/operations/tests/BaconCipher.mjs @@ -1,7 +1,7 @@ /** * BaconCipher tests. * - * @author Karsten Silkenbäumer [kassi@users.noreply.github.com] + * @author Karsten Silkenbäumer [github.com/kassi] * @copyright Karsten Silkenbäumer 2019 * @license Apache-2.0 */