1use std::{collections::HashMap, hash::Hash};
2
3use crate::transform::InvertableTransform;
4
5#[derive(Clone)]
24pub struct Alphabet<T: Copy + Hash + Eq> {
25 alphabet: Vec<T>,
26 inv_index: HashMap<T, u8>,
27}
28
29impl<T: Copy + Hash + Eq> Alphabet<T> {
30 pub fn new(alphabet: &[T]) -> Self {
34 let alphabet: Vec<T> = alphabet.to_vec();
35
36 assert!(
37 alphabet.len() <= u8::MAX as usize,
38 "Alphabet is too long (up to 256 elements supported)."
39 );
40
41 let inv_index: HashMap<T, u8> = alphabet
42 .iter()
43 .enumerate()
44 .map(|(k, v)| (*v, k as u8))
45 .collect();
46
47 assert_eq!(
48 alphabet.len(),
49 inv_index.len(),
50 "Alphabet contained duplicate value(s)."
51 );
52
53 Self {
54 alphabet,
55 inv_index,
56 }
57 }
58
59 #[allow(clippy::len_without_is_empty)]
60 pub fn len(&self) -> u8 {
61 self.alphabet.len() as u8
62 }
63}
64
65impl Alphabet<char> {
66 pub fn alphanumeric() -> Alphabet<char> {
68 let alpha: Vec<char> = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
69 .chars()
70 .collect();
71 Self::new(&alpha)
72 }
73
74 pub fn lowercase_alphanumeric() -> Alphabet<char> {
76 let alpha: Vec<char> = "0123456789abcdefghijklmnopqrstuvwxyz".chars().collect();
77 Self::new(&alpha)
78 }
79
80 pub fn lowercase_alpha() -> Alphabet<char> {
82 let alpha: Vec<char> = "abcdefghijklmnopqrstuvwxyz".chars().collect();
83 Self::new(&alpha)
84 }
85}
86
87impl<T: Copy + Hash + Eq> InvertableTransform for Alphabet<T> {
88 type Input = u8;
89
90 type Output = T;
91
92 fn forward(&self, index: u8) -> Option<T> {
93 self.alphabet.get(index as usize).copied()
94 }
95
96 fn backward(&self, value: T) -> Option<u8> {
97 self.inv_index.get(&value).copied()
98 }
99}
100
101#[cfg(test)]
102mod test {
103 use super::*;
104
105 #[test]
106 #[should_panic(expected = "Alphabet contained duplicate value(s).")]
107 fn test_duplicate() {
108 Alphabet::new(&['b', 'l', 'a', 'h', 'b', 'c']);
109 }
110
111 #[test]
112 #[should_panic(expected = "Alphabet is too long (up to 256 elements supported).")]
113 fn test_too_long() {
114 let chars: Vec<u16> = (0..30000).collect();
115 Alphabet::new(&chars);
116 }
117
118 #[test]
119 fn test_invalid_character() {
120 let chars = Alphabet::lowercase_alpha();
121 assert_eq!(None, chars.backward('!'));
122
123 assert_eq!(None, chars.forward(26));
124 }
125
126 #[test]
127 fn test_forward() {
128 let chars: Vec<char> = "abcdefghijklmnopqrstuvwxyz".chars().collect();
129 let alpha = Alphabet::new(&chars);
130
131 assert_eq!(26, alpha.len());
132
133 assert_eq!('a', alpha.forward(0).unwrap());
134 assert_eq!('z', alpha.forward(25).unwrap());
135 }
136
137 #[test]
138 fn test_backward() {
139 let chars: Vec<char> = "abcdefghijklmnopqrstuvwxyz".chars().collect();
140 let alpha = Alphabet::new(&chars);
141
142 assert_eq!(0, alpha.backward('a').unwrap());
143 assert_eq!(25, alpha.backward('z').unwrap());
144 }
145
146 #[test]
147 fn test_round_trip() {
148 let chars: Vec<char> = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
149 .chars()
150 .collect();
151 let alpha = Alphabet::new(&chars);
152
153 assert_eq!(52, alpha.len());
154
155 for i in 0..chars.len() as u8 {
156 let c = alpha.forward(i).unwrap();
157 let v = alpha.backward(c).unwrap();
158
159 assert_eq!(i, v);
160 }
161 }
162}