{"record":{"id":"ac68008d42bce7fd","repo":"gchq/CyberChef","slug":"reflector-must-have-exactly-13-pairs-covering-ever","errorCode":null,"errorMessage":"Reflector must have exactly 13 pairs covering every letter","messagePattern":"Reflector must have exactly 13 pairs covering every letter","errorType":"exception","errorClass":"OperationError","httpStatus":null,"severity":"error","filePath":"src/core/lib/Enigma.mjs","lineNumber":237,"sourceCode":"        return this.transform(c);\n    }\n}\n\n/**\n * Reflector. PairMapBase but requires that all characters are accounted for.\n *\n * Includes a couple of optimisations on that basis.\n */\nexport class Reflector extends PairMapBase {\n    /**\n     * Reflector constructor. See PairMapBase.\n     * Additional restriction: every character must be accounted for.\n     */\n    constructor(pairs) {\n        super(pairs, \"Reflector\");\n        const s = Object.keys(this.map).length;\n        if (s !== 26) {\n            throw new OperationError(\"Reflector must have exactly 13 pairs covering every letter\");\n        }\n        const optMap = new Array(26);\n        for (const x of Object.keys(this.map)) {\n            optMap[x] = this.map[x];\n        }\n        this.map = optMap;\n    }\n\n    /**\n     * Transform a character through this object.\n     *\n     * @param {number} c - The character.\n     * @returns {number}\n     */\n    transform(c) {\n        return this.map[c];\n    }\n}","sourceCodeStart":219,"sourceCodeEnd":255,"githubUrl":"https://github.com/gchq/CyberChef/blob/4290ea753912378913b1f3f54e0fc5720afeda5d/src/core/lib/Enigma.mjs#L219-L255","documentation":"Thrown by the Reflector constructor after PairMapBase finishes building its map. A reflector must be a complete involution: every one of the 26 letters A-Z must be paired with exactly one partner. The constructor counts connected letters (`Object.keys(this.map).length`) and rejects anything other than 26. Fewer than 13 disjoint pairs (or pairs that were silently skipped as self-stecker) cause the count to fall short.","triggerScenarios":"Calling `new Reflector(\"AB CD EF GH IJ KL MN OP QR ST\")` (only 10 pairs / 20 letters), including self-stecker pairs like \"AA\" which do not populate the map, or supplying a duplicate-letter set that PairMapBase already accepted but left fewer than 26 distinct entries.","commonSituations":"Partial reflector wiring copied from a source that omitted pairs; accidental inclusion of 'AA'-style entries in a reflector spec; hand-typing a known reflector (B/C) and missing a pair.","solutions":["Provide exactly 13 disjoint pairs that collectively cover all 26 letters A-Z.","Remove any self-stecker pairs (e.g. 'AA') from reflector input, since they are skipped and reduce the effective count.","Validate with a uniqueness/completeness check before constructing the Reflector."],"exampleFix":"// before\nnew Reflector(\"AB CD EF GH IJ KL MN OP QR ST UV\"); // 22 letters\n\n// after (reflects every letter)\nnew Reflector(\"AY BR CU DH EQ FS GL IP JX KN MO TZ VW\");","handlingStrategy":"validation","validationCode":"function validateReflector(pairs) {\n  const covered = new Set();\n  for (const pair of pairs.split(/\\s+/)) {\n    if (!/^[A-Z]{2}$/.test(pair)) throw new Error(`Bad pair: ${pair}`);\n    if (pair[0] === pair[1]) continue; // skipped, does not count\n    covered.add(pair[0]); covered.add(pair[1]);\n  }\n  if (covered.size !== 26) throw new Error(`Reflector covers ${covered.size}/26 letters; need exactly 13 disjoint pairs`);\n}\nvalidateReflector(reflectorStr);\nnew Reflector(reflectorStr);","typeGuard":"function isCompleteReflector(pairs) {\n  const covered = new Set();\n  for (const pair of pairs.split(/\\s+/)) {\n    if (!/^[A-Z]{2}$/.test(pair)) return false;\n    if (pair[0] === pair[1]) continue;\n    if (covered.has(pair[0]) || covered.has(pair[1])) return false;\n    covered.add(pair[0]); covered.add(pair[1]);\n  }\n  return covered.size === 26;\n}","tryCatchPattern":"try {\n  new Reflector(reflectorStr);\n} catch (err) {\n  if (err instanceof OperationError && /exactly 13 pairs/.test(err.message)) {\n    // prompt user to supply a complete reflector wiring\n  } else throw err;\n}","preventionTips":["Reuse known-good historical reflector wirings (B, C) verbatim rather than retyping.","Avoid self-stecker pairs in reflector specs since they are skipped and reduce coverage.","Assert the covered-letter count is exactly 26 before constructing."],"tags":["enigma","reflector","validation","configuration","completeness"],"backgroundTag":null,"analyzedSha":"4290ea753912378913b1f3f54e0fc5720afeda5d","analyzedAt":"2026-08-13T06:05:50.210Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}