1use crate::common::alphabet::Alphabet;
9use crate::common::cipher::Cipher;
10use crate::common::{alphabet, substitute};
11use num::integer::gcd;
12
13pub struct Affine {
17 a: usize,
18 b: usize,
19}
20
21impl Cipher for Affine {
22 type Key = (usize, usize);
23 type Algorithm = Affine;
24
25 fn new(key: (usize, usize)) -> Affine {
32 let (a, b) = key;
33 if (a < 1 || b < 1) || (a > 26 || b > 26) {
34 panic!("The keys a & b must be within the range 1 <= n <= 26.");
35 }
36
37 if gcd(a, 26) > 1 {
38 panic!("The key 'a' cannot share a common factor with 26.");
39 }
40
41 Affine { a, b }
42 }
43
44 fn encrypt(&self, message: &str) -> Result<String, &'static str> {
57 Ok(substitute::shift_substitution(message, |idx| {
62 alphabet::STANDARD.modulo(((self.a * idx) + self.b) as isize)
63 }))
64 }
65
66 fn decrypt(&self, ciphertext: &str) -> Result<String, &'static str> {
79 let a_inv = alphabet::STANDARD
85 .multiplicative_inverse(self.a as isize)
86 .expect("Multiplicative inverse for 'a' could not be calculated.");
87
88 Ok(substitute::shift_substitution(ciphertext, |idx| {
89 alphabet::STANDARD.modulo(a_inv as isize * (idx as isize - self.b as isize))
90 }))
91 }
92}
93
94#[cfg(test)]
95mod tests {
96 use super::*;
97
98 #[test]
99 fn encrypt_message() {
100 let a = Affine::new((3, 7));
101 assert_eq!("Hmmhnl hm qhvu!", a.encrypt("Attack at dawn!").unwrap());
102 }
103
104 #[test]
105 fn decrypt_message() {
106 let a = Affine::new((3, 7));
107 assert_eq!("Attack at dawn!", a.decrypt("Hmmhnl hm qhvu!").unwrap());
108 }
109
110 #[test]
111 fn with_utf8() {
112 let a = Affine::new((15, 10));
113 let message = "Peace ✌️ Freedom and Liberty!";
114
115 assert_eq!(message, a.decrypt(&a.encrypt(message).unwrap()).unwrap());
116 }
117
118 #[test]
119 fn exhaustive_encrypt() {
120 let message = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
122
123 for a in 1..27 {
124 if gcd(a, 26) > 1 {
125 continue;
126 }
127
128 for b in 1..27 {
129 let a = Affine::new((a, b));
130 assert_eq!(message, a.decrypt(&a.encrypt(message).unwrap()).unwrap());
131 }
132 }
133 }
134
135 #[test]
136 fn valid_key() {
137 Affine::new((15, 17));
138 }
139
140 #[test]
141 fn b_shares_factor() {
142 Affine::new((15, 2));
143 }
144
145 #[test]
146 #[should_panic]
147 fn a_shares_factor() {
148 Affine::new((2, 15));
149 }
150
151 #[test]
152 #[should_panic]
153 fn keys_to_small() {
154 Affine::new((0, 10));
155 }
156
157 #[test]
158 #[should_panic]
159 fn keys_to_big() {
160 Affine::new((30, 51));
161 }
162}