Skip to content

Commit 7d40bb4

Browse files
authored
merge: Add Affine Cipher (#1067)
* Add Affine Cipher * Replace IndexOf function to improve performance * remove leading spacing and fixed typo * Remove leading spacing in first row.
1 parent bbbf343 commit 7d40bb4

File tree

2 files changed

+122
-0
lines changed

2 files changed

+122
-0
lines changed

Ciphers/AffineCipher.js

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/**
2+
* @description - The affine cipher is a type of monoalphabetic substitution cipher, where each letter in an alphabet is mapped to its numeric equivalent, encrypted using a simple mathematical function, and converted back to a letter
3+
* @see - [wiki](https://en.wikipedia.org/wiki/Affine_cipher)
4+
*/
5+
6+
// Default key for Affine Cipher
7+
const key = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
8+
9+
/**
10+
* Fix result for negative value in modulas equation
11+
* @param {Number} n - Constant number
12+
* @param {Number} m - Modulos value
13+
* @return {Number} Return n mod m
14+
*/
15+
function mod (n, m) {
16+
return ((n % m) + m) % m
17+
}
18+
19+
/**
20+
* Modular multiplicative inverse
21+
* @param {Number} a - A coefficient
22+
* @param {Number} m - Modulos value
23+
* @return {Number} Return modular multiplicative inverse of coefficient a and modulos m
24+
*/
25+
function inverseMod (a, m) {
26+
for (let x = 1; x < m; x++) {
27+
if (mod(a * x, m) === 1) return x
28+
}
29+
}
30+
31+
/**
32+
* Argument validation
33+
* @param {String} str - String to be checked
34+
* @param {Number} a - A coefficient to be checked
35+
* @param {Number} b - B coefficient to be checked
36+
* @return {Boolean} Result of the checking
37+
*/
38+
function isCorrectFormat (str, a, b) {
39+
if (typeof a !== 'number' || typeof b !== 'number') {
40+
throw new TypeError('Coefficient a, b should be number')
41+
}
42+
43+
if (typeof str !== 'string') {
44+
throw new TypeError('Argument str should be String')
45+
}
46+
47+
return true
48+
}
49+
/**
50+
* Find character index based on ASCII order
51+
* @param {String} char - Character index to be found
52+
* @return {Boolean} Character index
53+
*/
54+
function findCharIndex (char) {
55+
return char.toUpperCase().charCodeAt(0) - ('A').charCodeAt(0)
56+
}
57+
58+
/**
59+
* Encrypt a Affine Cipher
60+
* @param {String} str - String to be encrypted
61+
* @param {Number} a - A coefficient
62+
* @param {Number} b - B coefficient
63+
* @return {String} result - Encrypted string
64+
*/
65+
function encrypt (str, a, b) {
66+
let result = ''
67+
if (isCorrectFormat(str, a, b)) {
68+
for (let x = 0; x < str.length; x++) {
69+
const charIndex = findCharIndex(str[x])
70+
if (charIndex < 0) result += '-1' + ' '
71+
else result += mod(a * charIndex + b, 26) + ' '
72+
}
73+
}
74+
return result.trim()
75+
}
76+
/**
77+
* Decrypt a Affine Cipher
78+
* @param {String} str - String to be decrypted
79+
* @param {Number} a - A coefficient
80+
* @param {Number} b - B coefficient
81+
* @return {String} result - Decrypted string
82+
*/
83+
function decrypt (str, a, b) {
84+
let result = ''
85+
if (isCorrectFormat(str, a, b)) {
86+
str = str.split(' ')
87+
for (let x = 0; x < str.length; x++) {
88+
if (str[x] === '-1') result += ' '
89+
else result += key[mod(inverseMod(a, 26) * (parseInt(str[x]) - b), 26)]
90+
}
91+
}
92+
return result
93+
}
94+
95+
export { encrypt, decrypt }

Ciphers/test/AffineCipher.test.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { encrypt, decrypt } from '../AffineCipher'
2+
3+
describe('Test Affine Cipher', () => {
4+
it('Test - 1, Pass invalid input to encrypt function', () => {
5+
expect(() => encrypt(null, null, null)).toThrow()
6+
expect(() => encrypt('null', null, null)).toThrow()
7+
expect(() => encrypt('null', 1, null)).toThrow()
8+
expect(() => encrypt('null', null, 1)).toThrow()
9+
})
10+
11+
it('Test - 2, Pass invalid input to decrypt function', () => {
12+
expect(() => decrypt(null, null, null)).toThrow()
13+
expect(() => decrypt('null', null, null)).toThrow()
14+
expect(() => decrypt('null', 1, null)).toThrow()
15+
expect(() => decrypt('null', null, 1)).toThrow()
16+
})
17+
18+
it('Test - 3 Pass string value to encrypt and ecrypt function', () => {
19+
const a = 5
20+
const b = 8
21+
expect(decrypt(encrypt('HELLO WORLD', a, b), a, b)).toBe('HELLO WORLD')
22+
expect(decrypt(encrypt('ABC DEF', a, b), a, b)).toBe('ABC DEF')
23+
expect(decrypt(encrypt('Brown fox jump over the fence', a, b), a, b)).toBe(
24+
'BROWN FOX JUMP OVER THE FENCE'
25+
)
26+
})
27+
})

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy