From 8bfa6b9cf4d2d3ac6aec2ab56a8a2d8cb440ea9e Mon Sep 17 00:00:00 2001 From: sarthakgupta1907-cell Date: Mon, 2 Nov 2026 18:18:35 +0530 Subject: [PATCH] feat: add Caesar cipher algorithm --- Ciphers/CaesarCipher.js | 62 +++++++++++++++++-------------- Ciphers/test/CaesarCipher.test.js | 62 +++++++++++++++++++++---------- 2 files changed, 77 insertions(+), 47 deletions(-) diff --git a/Ciphers/CaesarCipher.js b/Ciphers/CaesarCipher.js index 8a942564e9..4b9ad6d9a7 100644 --- a/Ciphers/CaesarCipher.js +++ b/Ciphers/CaesarCipher.js @@ -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 } diff --git a/Ciphers/test/CaesarCipher.test.js b/Ciphers/test/CaesarCipher.test.js index c39c09d061..5fa67f6a9e 100644 --- a/Ciphers/test/CaesarCipher.test.js +++ b/Ciphers/test/CaesarCipher.test.js @@ -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) }) })