/** * @author Matt C [matt@artemisbot.uk] * @copyright Crown Copyright 2018 * @license Apache-2.0 */ import Operation from "../Operation"; import Utils from "../Utils"; import OperationError from "../errors/OperationError"; /** * Affine Cipher Decode operation */ class AffineCipherDecode extends Operation { /** * AffineCipherDecode constructor */ constructor() { super(); this.name = "Affine Cipher Decode"; this.module = "Ciphers"; this.description = "The Affine cipher is a type of monoalphabetic substitution cipher. To decrypt, each letter in an alphabet is mapped to its numeric equivalent, decrypted by a mathematical function, and converted back to a letter."; this.inputType = "string"; this.outputType = "string"; this.args = [ { "name": "a", "type": "number", "value": 1 }, { "name": "b", "type": "number", "value": 0 } ]; } /** * @param {string} input * @param {Object[]} args * @returns {string} * * @throws {OperationError} if a or b values are invalid */ run(input, args) { const alphabet = "abcdefghijklmnopqrstuvwxyz", [a, b] = args, aModInv = Utils.modInv(a, 26); // Calculates modular inverse of a let output = ""; if (!/^\+?(0|[1-9]\d*)$/.test(a) || !/^\+?(0|[1-9]\d*)$/.test(b)) { throw new OperationError("The values of a and b can only be integers."); } if (Utils.gcd(a, 26) !== 1) { throw new OperationError("The value of `a` must be coprime to 26."); } for (let i = 0; i < input.length; i++) { if (alphabet.indexOf(input[i]) >= 0) { // Uses the affine decode function (y-b * A') % m = x (where m is length of the alphabet and A' is modular inverse) output += alphabet[Utils.mod((alphabet.indexOf(input[i]) - b) * aModInv, 26)]; } else if (alphabet.indexOf(input[i].toLowerCase()) >= 0) { // Same as above, accounting for uppercase output += alphabet[Utils.mod((alphabet.indexOf(input[i].toLowerCase()) - b) * aModInv, 26)].toUpperCase(); } else { // Non-alphabetic characters output += input[i]; } } return output; } /** * Highlight Affine Cipher Decode * * @param {Object[]} pos * @param {number} pos[].start * @param {number} pos[].end * @param {Object[]} args * @returns {Object[]} pos */ highlight(pos, args) { return pos; } /** * Highlight Affine Cipher Decode in reverse * * @param {Object[]} pos * @param {number} pos[].start * @param {number} pos[].end * @param {Object[]} args * @returns {Object[]} pos */ highlightReverse(pos, args) { return pos; } } export default AffineCipherDecode;