mirror of
https://github.moeyy.xyz/https://github.com/trekhleb/javascript-algorithms.git
synced 2024-12-26 23:21:18 +08:00
Refactor a Caesar Cipher algorithm.
This commit is contained in:
parent
bd7475ee19
commit
00242413a5
@ -135,6 +135,7 @@ a set of rules that precisely define a sequence of operations.
|
|||||||
* `A` [Travelling Salesman Problem](src/algorithms/graph/travelling-salesman) - shortest possible route that visits each city and returns to the origin city
|
* `A` [Travelling Salesman Problem](src/algorithms/graph/travelling-salesman) - shortest possible route that visits each city and returns to the origin city
|
||||||
* **Cryptography**
|
* **Cryptography**
|
||||||
* `B` [Polynomial Hash](src/algorithms/cryptography/polynomial-hash) - rolling hash function based on polynomial
|
* `B` [Polynomial Hash](src/algorithms/cryptography/polynomial-hash) - rolling hash function based on polynomial
|
||||||
|
* `B` [Caesar Cipher](src/algorithms/cryptography/caesar-cipher) - simple substitution cipher
|
||||||
* **Machine Learning**
|
* **Machine Learning**
|
||||||
* `B` [NanoNeuron](https://github.com/trekhleb/nano-neuron) - 7 simple JS functions that illustrate how machines can actually learn (forward/backward propagation)
|
* `B` [NanoNeuron](https://github.com/trekhleb/nano-neuron) - 7 simple JS functions that illustrate how machines can actually learn (forward/backward propagation)
|
||||||
* **Uncategorized**
|
* **Uncategorized**
|
||||||
|
30
src/algorithms/cryptography/caesar-cipher/README.md
Normal file
30
src/algorithms/cryptography/caesar-cipher/README.md
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
# Caesar Cipher Algorithm
|
||||||
|
|
||||||
|
In cryptography, a **Caesar cipher**, also known as **Caesar's cipher**, the **shift cipher**, **Caesar's code** or **Caesar shift**, is one of the simplest and most widely known encryption techniques. It is a type of substitution cipher in which each letter in the plaintext is replaced by a letter some fixed number of positions down the alphabet. For example, with a left shift of `3`, `D` would be replaced by `A`, `E` would become `B`, and so on. The method is named after Julius Caesar, who used it in his private correspondence.
|
||||||
|
|
||||||
|
![Caesar Cipher Algorithm](https://upload.wikimedia.org/wikipedia/commons/4/4a/Caesar_cipher_left_shift_of_3.svg)
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
The transformation can be represented by aligning two alphabets; the cipher alphabet is the plain alphabet rotated left or right by some number of positions. For instance, here is a Caesar cipher using a left rotation of three places, equivalent to a right shift of 23 (the shift parameter is used as the key):
|
||||||
|
|
||||||
|
```text
|
||||||
|
Plain: ABCDEFGHIJKLMNOPQRSTUVWXYZ
|
||||||
|
Cipher: XYZABCDEFGHIJKLMNOPQRSTUVW
|
||||||
|
```
|
||||||
|
|
||||||
|
When encrypting, a person looks up each letter of the message in the "plain" line and writes down the corresponding letter in the "cipher" line.
|
||||||
|
|
||||||
|
```text
|
||||||
|
Plaintext: THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG
|
||||||
|
Ciphertext: QEB NRFZH YOLTK CLU GRJMP LSBO QEB IXWV ALD
|
||||||
|
```
|
||||||
|
|
||||||
|
## Complexity
|
||||||
|
|
||||||
|
- Time: `O(|n|)`
|
||||||
|
- Space: `O(|n|)`
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- [Caesar cipher on Wikipedia](https://en.wikipedia.org/wiki/Caesar_cipher)
|
@ -0,0 +1,40 @@
|
|||||||
|
import { caesarCipherEncrypt, caesarCipherDecrypt } from '../caesarCipher';
|
||||||
|
|
||||||
|
describe('caesarCipher', () => {
|
||||||
|
it('should not change a string with zero shift', () => {
|
||||||
|
expect(caesarCipherEncrypt('abcd', 0)).toBe('abcd');
|
||||||
|
expect(caesarCipherDecrypt('abcd', 0)).toBe('abcd');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should cipher a string with different shifts', () => {
|
||||||
|
expect(caesarCipherEncrypt('abcde', 3)).toBe('defgh');
|
||||||
|
expect(caesarCipherDecrypt('defgh', 3)).toBe('abcde');
|
||||||
|
|
||||||
|
expect(caesarCipherEncrypt('abcde', 1)).toBe('bcdef');
|
||||||
|
expect(caesarCipherDecrypt('bcdef', 1)).toBe('abcde');
|
||||||
|
|
||||||
|
expect(caesarCipherEncrypt('xyz', 1)).toBe('yza');
|
||||||
|
expect(caesarCipherDecrypt('yza', 1)).toBe('xyz');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be case insensitive', () => {
|
||||||
|
expect(caesarCipherEncrypt('ABCDE', 3)).toBe('defgh');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should correctly handle an empty strings', () => {
|
||||||
|
expect(caesarCipherEncrypt('', 3)).toBe('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not cipher unknown chars', () => {
|
||||||
|
expect(caesarCipherEncrypt('ab2cde', 3)).toBe('de2fgh');
|
||||||
|
expect(caesarCipherDecrypt('de2fgh', 3)).toBe('ab2cde');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should encrypt and decrypt full phrases', () => {
|
||||||
|
expect(caesarCipherEncrypt('THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG', 23))
|
||||||
|
.toBe('qeb nrfzh yoltk clu grjmp lsbo qeb ixwv ald');
|
||||||
|
|
||||||
|
expect(caesarCipherDecrypt('qeb nrfzh yoltk clu grjmp lsbo qeb ixwv ald', 23))
|
||||||
|
.toBe('the quick brown fox jumps over the lazy dog');
|
||||||
|
});
|
||||||
|
});
|
58
src/algorithms/cryptography/caesar-cipher/caesarCipher.js
Normal file
58
src/algorithms/cryptography/caesar-cipher/caesarCipher.js
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
// Create alphabet array: ['a', 'b', 'c', ..., 'z'].
|
||||||
|
const englishAlphabet = 'abcdefghijklmnopqrstuvwxyz'.split('');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a cipher map out of the alphabet.
|
||||||
|
* Example with a shift 3: {'a': 'd', 'b': 'e', 'c': 'f', ...}
|
||||||
|
*
|
||||||
|
* @param {string[]} alphabet - i.e. ['a', 'b', 'c', ... , 'z']
|
||||||
|
* @param {number} shift - i.e. 3
|
||||||
|
* @return {Object} - i.e. {'a': 'd', 'b': 'e', 'c': 'f', ..., 'z': 'c'}
|
||||||
|
*/
|
||||||
|
const getCipherMap = (alphabet, shift) => {
|
||||||
|
return alphabet
|
||||||
|
.reduce((charsMap, currentChar, charIndex) => {
|
||||||
|
const charsMapClone = { ...charsMap };
|
||||||
|
// Making the shift to be cyclic (i.e. with a shift of 1 - 'z' would be mapped to 'a').
|
||||||
|
let encryptedCharIndex = (charIndex + shift) % alphabet.length;
|
||||||
|
// Support negative shifts for creating a map for decryption
|
||||||
|
// (i.e. with shift -1 - 'a' would be mapped to 'z').
|
||||||
|
if (encryptedCharIndex < 0) {
|
||||||
|
encryptedCharIndex += alphabet.length;
|
||||||
|
}
|
||||||
|
charsMapClone[currentChar] = alphabet[encryptedCharIndex];
|
||||||
|
return charsMapClone;
|
||||||
|
}, {});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} str
|
||||||
|
* @param {number} shift
|
||||||
|
* @param {string[]} alphabet
|
||||||
|
* @return {string}
|
||||||
|
*/
|
||||||
|
export const caesarCipherEncrypt = (str, shift, alphabet = englishAlphabet) => {
|
||||||
|
// Create a cipher map:
|
||||||
|
const cipherMap = getCipherMap(alphabet, shift);
|
||||||
|
return str
|
||||||
|
.toLowerCase()
|
||||||
|
.split('')
|
||||||
|
.map((char) => cipherMap[char] || char)
|
||||||
|
.join('');
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} str
|
||||||
|
* @param {number} shift
|
||||||
|
* @param {string[]} alphabet
|
||||||
|
* @return {string}
|
||||||
|
*/
|
||||||
|
export const caesarCipherDecrypt = (str, shift, alphabet = englishAlphabet) => {
|
||||||
|
// Create a cipher map:
|
||||||
|
const cipherMap = getCipherMap(alphabet, -shift);
|
||||||
|
return str
|
||||||
|
.toLowerCase()
|
||||||
|
.split('')
|
||||||
|
.map((char) => cipherMap[char] || char)
|
||||||
|
.join('');
|
||||||
|
};
|
@ -1,15 +0,0 @@
|
|||||||
# caeserCipher Algorithm
|
|
||||||
|
|
||||||
caeserCipher Algorithm is a type of substitution algorithm in which each letter in the plaintext is replaced by a letter some fixed number of positions down the alphabet. For example, with a left shift of 3, D would be replaced by A, E would become B, and so on
|
|
||||||
|
|
||||||
## Complexity
|
|
||||||
|
|
||||||
- **Time:** `O(|n|)`
|
|
||||||
- **Space:** `O(|n|)`
|
|
||||||
## Example
|
|
||||||
|
|
||||||
The the following string `abcde` which is shifted by 1 will change to `bcdef`
|
|
||||||
|
|
||||||
## References
|
|
||||||
|
|
||||||
- [Wikipedia](https://en.wikipedia.org/wiki/Caesar_cipher)
|
|
@ -1,27 +0,0 @@
|
|||||||
import caeserCipher from '../caeserCipher';
|
|
||||||
|
|
||||||
describe('caeserCipher', () => {
|
|
||||||
it('should subtitute each character by shifting up the alphabet by a given integer', () => {
|
|
||||||
|
|
||||||
|
|
||||||
expect(caeserCipher('abcd', 1)).toBe('bcde');
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
it('should wrap back to the beginning of the alphabet if it shifts more than the 26 english alphabets', () => {
|
|
||||||
|
|
||||||
|
|
||||||
expect(caeserCipher('xyz', 1)).toBe('yza');
|
|
||||||
});
|
|
||||||
it('should only shift letters and ignore non-alphabetic characters', () => {
|
|
||||||
|
|
||||||
expect(caeserCipher('gurer ner 9 qbtf!', 13)).toBe('there are 9 dogs!');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should ignore case sensitivity', () => {
|
|
||||||
|
|
||||||
expect(caeserCipher('ABCD', 13)).toBe('bcde');
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
})
|
|
@ -1,30 +0,0 @@
|
|||||||
/**
|
|
||||||
* @param {string} string
|
|
||||||
* @param {number} shift
|
|
||||||
* @return {string} result
|
|
||||||
*/
|
|
||||||
|
|
||||||
export default function caeserCipher(string, shift) {
|
|
||||||
// convert all alphabets in english language to an array
|
|
||||||
const alphabetArr = "abcdefghijklmnopqrstuvwxyz".split("")
|
|
||||||
// converting all the alphabets in string to lowercase
|
|
||||||
string = string.toLowerCase()
|
|
||||||
|
|
||||||
let result = ""
|
|
||||||
for (let i = 0; i < string.length; i++) {
|
|
||||||
const currentChar = string[i]
|
|
||||||
// checking index of character in the english alphabets
|
|
||||||
const idx = alphabetArr.indexOf(currentChar)
|
|
||||||
// skip character if the character is not an alphabet
|
|
||||||
if (idx === -1) {
|
|
||||||
result += currentChar
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
//wrap up index, incase the next shift is beyond the 26th character
|
|
||||||
const encodedIdx = (idx + shift) % 26
|
|
||||||
result += alphabetArr[encodedIdx]
|
|
||||||
|
|
||||||
}
|
|
||||||
// return the result of the shifted string
|
|
||||||
return result
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user