Skip to content

Commit c4035de

Browse files
added Substitution Cipher and its test
1 parent 5f1ed3e commit c4035de

File tree

2 files changed

+90
-0
lines changed

2 files changed

+90
-0
lines changed

Diff for: Ciphers/SubstitutionCipher.js

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/**
2+
* Substitution Cipher
3+
*
4+
* A monoalphabetic substitution cipher replaces each letter of the plaintext
5+
* with another letter based on a fixed permutation (key) of the alphabet.
6+
* https://en.wikipedia.org/wiki/Substitution_cipher
7+
*/
8+
9+
const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
10+
const defaultKey = 'QWERTYUIOPASDFGHJKLZXCVBNM'
11+
12+
/**
13+
* Encrypts a string using a monoalphabetic substitution cipher
14+
* @param {string} text - The text to encrypt
15+
* @param {string} key - The substitution key (must be 26 uppercase letters)
16+
* @returns {string}
17+
*/
18+
export function substitutionCipherEncryption(text, key = defaultKey) {
19+
if (key.length !== 26 || !/^[A-Z]+$/.test(key)) {
20+
throw new RangeError('Key must be 26 uppercase English letters.')
21+
}
22+
23+
let result = ''
24+
const textUpper = text.toUpperCase()
25+
for (let i = 0; i < textUpper.length; i++) {
26+
const char = textUpper[i]
27+
const index = alphabet.indexOf(char)
28+
if (index !== -1) {
29+
result += key[index]
30+
} else {
31+
result += char
32+
}
33+
}
34+
return result
35+
}
36+
/**
37+
* Decrypts a string encrypted with the substitution cipher
38+
* @param {string} text - The encrypted text
39+
* @param {string} key - The substitution key used during encryption
40+
* @returns {string}
41+
*/
42+
export function substitutionCipherDecryption(text, key = defaultKey) {
43+
if (key.length !== 26 || !/^[A-Z]+$/.test(key)) {
44+
throw new RangeError('Key must be 26 uppercase English letters.')
45+
}
46+
47+
let result = ''
48+
const textUpper = text.toUpperCase()
49+
for (let i = 0; i < textUpper.length; i++) {
50+
const char = textUpper[i]
51+
const index = key.indexOf(char)
52+
if (index !== -1) {
53+
result += alphabet[index]
54+
} else {
55+
result += char
56+
}
57+
}
58+
return result
59+
}

Diff for: Ciphers/test/SubstitutionCipher.test.js

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { describe, it, expect } from 'vitest'
2+
import {
3+
substitutionCipherEncryption,
4+
substitutionCipherDecryption
5+
} from '../SubstitutionCipher.js'
6+
7+
8+
describe('Substitution Cipher', () => {
9+
const key = 'QWERTYUIOPASDFGHJKLZXCVBNM'
10+
11+
it('correctly encrypts a message', () => {
12+
const encrypted = substitutionCipherEncryption('HELLO WORLD', key)
13+
expect(encrypted).toBe('ITSSG VGKSR')
14+
})
15+
16+
it('correctly decrypts a message', () => {
17+
const decrypted = substitutionCipherDecryption('ITSSG VGKSR', key)
18+
expect(decrypted).toBe('HELLO WORLD')
19+
})
20+
21+
it('handles non-alphabetic characters', () => {
22+
const encrypted = substitutionCipherEncryption('Test! 123', key)
23+
expect(encrypted).toBe('ZTLZ! 123')
24+
})
25+
26+
it('throws error for invalid key', () => {
27+
expect(() => substitutionCipherEncryption('HELLO', 'BADKEY')).toThrow(
28+
RangeError
29+
)
30+
})
31+
})

0 commit comments

Comments
 (0)