Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 35 additions & 27 deletions Ciphers/CaesarCipher.js
Original file line number Diff line number Diff line change
@@ -1,32 +1,40 @@
/**
* @function caesarsCipher
* @description - 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.
* @see - [wiki](https://en.wikipedia.org/wiki/Caesar_cipher)
* @param {string} str - string to be encrypted
* @param {number} rotation - the number of rotation, expect real number ( > 0)
* @return {string} - decrypted string
* @function caesarCipher
* @description Caesar cipher is one of the simplest encryption techniques.
* It works by shifting each letter in the plaintext by a fixed number
* of positions in the alphabet.
* @param {string} str - The input string to encrypt/decrypt
* @param {number} shift - The number of positions to shift (can be negative)
* @returns {string} - The encrypted/decrypted string
* @see https://en.wikipedia.org/wiki/Caesar_cipher
* @example caesarCipher("ABC", 1) => "BCD"
* @example caesarCipher("XYZ", 3) => "ABC"
* @example caesarCipher("Hello World", 13) => "Uryyb Jbeyq"
*/
const caesarCipher = (str, rotation) => {
if (typeof str !== 'string' || !Number.isInteger(rotation) || rotation < 0) {
throw new TypeError('Arguments are invalid')
const caesarCipher = (str, shift) => {
if (typeof str !== 'string') {
throw new TypeError('Input must be a string')
}

const alphabets = new Array(26)
.fill()
.map((_, index) => String.fromCharCode(97 + index)) // generate all lower alphabets array a-z

const cipherMap = alphabets.reduce(
(map, char, index) => map.set(char, alphabets[(rotation + index) % 26]),
new Map()
)

return str.replace(/[a-z]/gi, (char) => {
if (/[A-Z]/.test(char)) {
return cipherMap.get(char.toLowerCase()).toUpperCase()
}

return cipherMap.get(char)
})
if (!Number.isInteger(shift)) {
throw new TypeError('Shift must be an integer')
}
const normalizedShift = ((shift % 26) + 26) % 26
return str
.split('')
.map((char) => {
if (char.match(/[a-z]/)) {
return String.fromCharCode(
((char.charCodeAt(0) - 97 + normalizedShift) % 26) + 97
)
}
if (char.match(/[A-Z]/)) {
return String.fromCharCode(
((char.charCodeAt(0) - 65 + normalizedShift) % 26) + 65
)
}
return char
})
.join('')
}

export default caesarCipher
export { caesarCipher }
62 changes: 42 additions & 20 deletions Ciphers/test/CaesarCipher.test.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,44 @@
import caesarCipher from '../CaesarCipher'

describe('Testing the caesarsCipher function', () => {
it('Test - 1, Testing for invalid types', () => {
expect(() => caesarCipher(false, 3)).toThrow()
expect(() => caesarCipher('false', -1)).toThrow()
expect(() => caesarCipher('true', null)).toThrow()
})

it('Test - 2, Testing for valid string and rotation', () => {
expect(caesarCipher('middle-Outz', 2)).toBe('okffng-Qwvb')
expect(caesarCipher('abcdefghijklmnopqrstuvwxyz', 3)).toBe(
'defghijklmnopqrstuvwxyzabc'
)
expect(caesarCipher('Always-Look-on-the-Bright-Side-of-Life', 5)).toBe(
'Fqbfdx-Qttp-ts-ymj-Gwnlmy-Xnij-tk-Qnkj'
)
expect(
caesarCipher('THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG', 23)
).toBe('QEB NRFZH YOLTK CLU GRJMP LSBO QEB IXWV ALD')
import { caesarCipher } from '../CaesarCipher.js'

describe('caesarCipher', () => {
test('shifts letters by 1', () => {
expect(caesarCipher('ABC', 1)).toBe('BCD')
})

test('wraps around Z to A', () => {
expect(caesarCipher('XYZ', 3)).toBe('ABC')
})

test('preserves case', () => {
expect(caesarCipher('Hello', 13)).toBe('Uryyb')
})

test('preserves spaces and punctuation', () => {
expect(caesarCipher('Hello World!', 13)).toBe('Uryyb Jbeyq!')
})

test('shift of 0 returns same string', () => {
expect(caesarCipher('ABC', 0)).toBe('ABC')
})

test('shift of 26 returns same string', () => {
expect(caesarCipher('ABC', 26)).toBe('ABC')
})

test('negative shift works', () => {
expect(caesarCipher('BCD', -1)).toBe('ABC')
})

test('decryption reverses encryption', () => {
const original = 'Hello World'
expect(caesarCipher(caesarCipher(original, 13), -13)).toBe(original)
})

test('throws TypeError for non-string input', () => {
expect(() => caesarCipher(123, 1)).toThrow(TypeError)
})

test('throws TypeError for non-integer shift', () => {
expect(() => caesarCipher('ABC', 1.5)).toThrow(TypeError)
})
})
Loading