-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
262 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
# Fractionated Morse | ||
|
||
> A doubling substitution cipher based on the Morse keyAlphabet. | ||
## Cipher behavior information | ||
|
||
* Case sensitive? ❌ | ||
* Alphabet: `ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890 .,:;?-_()'=+/@` | ||
* Characters not in keyAlphabet will be: **omitted** or **throwing an error (default)** | ||
|
||
## Default options object | ||
|
||
The options are the same for both methods, `encode` and `decode` | ||
|
||
``` | ||
const options = { | ||
keyAlphabet: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', // Order of letters to encode/decode the input. Must contain 26 unique characters | ||
failOnUnknownCharacter: true, // Should an error be thrown when a character is not included in the alphabet | ||
} | ||
``` | ||
|
||
## Usage | ||
|
||
### Encoding | ||
|
||
#### Default | ||
|
||
``` | ||
import { fractionatedMorse } from 'cipher-collection' | ||
console.log(fractionatedMorse.encode('Hello World')) // AGTCDHOTQODTCJ | ||
``` | ||
|
||
#### With custom alphabet | ||
|
||
``` | ||
import { fractionatedMorse } from 'cipher-collection' | ||
const options = { alphabet: 'BCADEFGHIJKLMNOPQRSTUVWXYZ' } | ||
console.log(fractionatedMorse.encode('Hello World', options)) // BGTADHOTQODTAJ | ||
``` | ||
|
||
#### Without error throwing | ||
|
||
|
||
``` | ||
import { fractionatedMorse } from 'cipher-collection' | ||
const silentFailOptions = { | ||
failOnUnknownCharacter: false, | ||
} | ||
// Characters will be omitted | ||
console.log(fractionatedMorse.encode('€€€Hello World', silentFailOptions)) //AGTCDHOTQODTCJ | ||
``` | ||
|
||
|
||
### Decoding | ||
|
||
#### Default | ||
|
||
``` | ||
import { fractionatedMorse } from 'cipher-collection' | ||
console.log(fractionatedMorse.decode('AGTCDHOTQODTCJ')) // Hello World | ||
``` | ||
|
||
#### With custom alphabet | ||
|
||
``` | ||
import { fractionatedMorse } from 'cipher-collection' | ||
const options = { alphabet: 'BCADEFGHIJKLMNOPQRSTUVWXYZ' } | ||
console.log(fractionatedMorse.decode('BGTADHOTQODTAJ', options)) // Hello World | ||
``` | ||
|
||
#### Without error throwing | ||
|
||
|
||
``` | ||
import { fractionatedMorse } from 'cipher-collection' | ||
const silentFailOptions = { | ||
failOnUnknownCharacter: false, | ||
} | ||
console.log(fractionatedMorse.decode('€€€AGTCDHOTQODTCJ', silentFailOptions)) //Hello World | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,4 +9,5 @@ | |
|
||
* [ROT-N](./ciphers/rot.md) | ||
* [Morse](./ciphers/morse.md) | ||
* [Fractionated Morse](./ciphers/fractionated-morse.md) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
import morse from './morse' | ||
|
||
export const decode = (input, options = {}) => { | ||
options = { ...DEFAULT_OPTIONS, ...options } | ||
const morseOptions = { ...DEFAULT_MORSE_OPTION, ...{ failOnUnknownCharacter: options.failOnUnknownCharacter } } | ||
|
||
let morseCode = [...input].map(c => { | ||
const decodedCharacterIndex = options.keyAlphabet.indexOf(c) | ||
if (decodedCharacterIndex !== -1) { | ||
return ENCODED_ALPHABET[decodedCharacterIndex] | ||
} | ||
if (options.failOnUnknownCharacter) { | ||
throw Error(`Undecodable character ${c}`) | ||
} | ||
return '' | ||
}) | ||
.join('') | ||
|
||
// Remove padding if needed | ||
.replace(/x{1,2}$/, '') | ||
// Fix unwanted space encoding | ||
.replace(/xx/g, SPACE_STRING) | ||
|
||
return morse.decode(morseCode, morseOptions) | ||
} | ||
|
||
export const encode = (input, options = {}) => { | ||
options = { ...DEFAULT_OPTIONS, ...options } | ||
const morseOptions = { ...DEFAULT_MORSE_OPTION, ...{ failOnUnknownCharacter: options.failOnUnknownCharacter } } | ||
|
||
return morseCodeFromInput(input, morseOptions) | ||
// Split into arrays containing three-character strings | ||
.match(/.{3}/g) | ||
.map(c => { | ||
const encodedCharacterIndex = ENCODED_ALPHABET.indexOf(c) | ||
|
||
if (encodedCharacterIndex !== -1) { | ||
return options.keyAlphabet[encodedCharacterIndex] | ||
} | ||
|
||
if (options.failOnUnknownCharacter) { | ||
throw Error(`Unencodable character ${c}`) | ||
} | ||
|
||
return '' | ||
}).join('') | ||
} | ||
|
||
const morseCodeFromInput = (input, morseOptions) => { | ||
let morseCode = morse.encode(input.toUpperCase(), morseOptions) | ||
|
||
// Add padding if needed | ||
if (morseCode.length % 3) { | ||
morseCode += morseOptions.separator.repeat(3 - (morseCode.length % 3)) | ||
} | ||
|
||
return morseCode | ||
.replace(new RegExp(`${morseOptions.separator}`, 'g'), 'x') | ||
// Fix unwanted space encoding | ||
.replace(new RegExp(`${SPACE_STRING}`, 'g'), 'xx') | ||
} | ||
|
||
const ENCODED_ALPHABET = [ | ||
'...', | ||
'..-', | ||
'..x', | ||
'.-.', | ||
'.--', | ||
'.-x', | ||
'.x.', | ||
'.x-', | ||
'.xx', | ||
'-..', | ||
'-.-', | ||
'-.x', | ||
'--.', | ||
'---', | ||
'--x', | ||
'-x.', | ||
'-x-', | ||
'-xx', | ||
'x..', | ||
'x.-', | ||
'x.x', | ||
'x-.', | ||
'x--', | ||
'x-x', | ||
'xx.', | ||
'xx-' | ||
] | ||
|
||
const SPACE_STRING = 'x/x' | ||
|
||
const DEFAULT_OPTIONS = { | ||
keyAlphabet: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', | ||
failOnUnknownCharacter: true | ||
} | ||
|
||
const DEFAULT_MORSE_OPTION = { | ||
separator: 'x' | ||
} | ||
|
||
export default { | ||
decode, | ||
encode | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,9 @@ | ||
import rot from './rot' | ||
import morse from './morse' | ||
import fractionatedMorse from './fractionatedMorse' | ||
|
||
export default { | ||
rot, | ||
morse | ||
morse, | ||
fractionatedMorse | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import fractionatedMorse from 'fractionatedMorse' | ||
|
||
const alphabetOptions = { | ||
keyAlphabet: 'BCADEFGHIJKLMNOPQRSTUVWXYZ' | ||
} | ||
|
||
const silentFailOptions = { | ||
failOnUnknownCharacter: false | ||
} | ||
|
||
describe('encoding', () => { | ||
test('default', () => { | ||
expect(fractionatedMorse.encode('ZZZZ')).toBe('MHJWCMI') | ||
expect(fractionatedMorse.encode('Hello World')).toBe('AGTCDHOTQODTCJ') | ||
expect(fractionatedMorse.encode('OKAY LETS TRY SOMETHING WEIRD')).toBe('NVPQEYJUPCXDVOSHOOHSCCLMYOGGLJ') | ||
expect(fractionatedMorse.encode('A')).toBe('F') | ||
expect(fractionatedMorse.encode('D')).toBe('J') | ||
expect(fractionatedMorse.encode('E')).toBe('I') | ||
expect(fractionatedMorse.encode('1234567890.,:;?-_()\'=+/@')).toBe('EOBOAOAFACJCMCNCNLNODKWBQMCKDSMHAFBKVMVMPNLJFDLJLED') | ||
expect(fractionatedMorse.encode('A D E F')).toBe('FVIIBI') | ||
|
||
expect(() => { fractionatedMorse.encode('€€€') }).toThrowError('Unencodable character') | ||
expect(() => { fractionatedMorse.encode('Ü') }).toThrowError('Unencodable character') | ||
expect(() => { fractionatedMorse.encode('A A') }).toThrowError('Unencodable character') | ||
}) | ||
|
||
test('with different keyAlphabet', () => { | ||
expect(fractionatedMorse.encode('AI', alphabetOptions)).toBe('FA') | ||
expect(fractionatedMorse.encode('HELLO WORLD', alphabetOptions)).toBe('BGTADHOTQODTAJ') | ||
}) | ||
test('with silent fail', () => { | ||
expect(fractionatedMorse.encode('€€€A', silentFailOptions)).toBe('F') | ||
expect(fractionatedMorse.encode('€€€Hello World', silentFailOptions)).toBe('AGTCDHOTQODTCJ') | ||
}) | ||
}) | ||
|
||
describe('decoding', () => { | ||
test('default', () => { | ||
expect(fractionatedMorse.decode('F')).toBe('A') | ||
expect(fractionatedMorse.decode('J')).toBe('D') | ||
expect(fractionatedMorse.decode('I')).toBe('E') | ||
expect(fractionatedMorse.decode('AGTCDHOTQODTCJ')).toBe('HELLO WORLD') | ||
expect(fractionatedMorse.decode('EOBOAOAFACJCMCNCNLNODKWBQMCKDSMHAFBKVMVMPNLJFDLJLED')).toBe('1234567890.,:;?-_()\'=+/@') | ||
expect(fractionatedMorse.decode('FT')).toBe('A A') | ||
expect(fractionatedMorse.decode('FVIIBI')).toBe('A D E F') | ||
expect(() => { fractionatedMorse.decode('ÜÄÖ') }).toThrowError('Undecodable character') | ||
}) | ||
|
||
test('with different keyAlphabet', () => { | ||
expect(fractionatedMorse.decode('FA', alphabetOptions)).toBe('AI') | ||
expect(fractionatedMorse.decode('BGTADHOTQODTAJ', alphabetOptions)).toBe('HELLO WORLD') | ||
}) | ||
|
||
test('with silent fail', () => { | ||
expect(fractionatedMorse.decode('€€€F', silentFailOptions)).toBe('A') | ||
}) | ||
}) |