1use std::collections::HashMap;
2use crate::config::EncodingMode;
3
4#[derive(Debug, Clone)]
5pub struct Alphabet {
6 chars: Vec<char>,
7 char_to_index: HashMap<char, usize>,
8 mode: EncodingMode,
9 padding: Option<char>,
10}
11
12impl Alphabet {
13 pub fn new(chars: Vec<char>) -> Result<Self, String> {
14 Self::new_with_mode(chars, EncodingMode::BaseConversion, None)
15 }
16
17 pub fn new_with_mode(chars: Vec<char>, mode: EncodingMode, padding: Option<char>) -> Result<Self, String> {
18 if chars.is_empty() {
19 return Err("Alphabet cannot be empty".to_string());
20 }
21
22 if mode == EncodingMode::Chunked {
24 let base = chars.len();
25 if !base.is_power_of_two() {
26 return Err(format!("Chunked mode requires power-of-two alphabet size, got {}", base));
27 }
28 }
29
30 let mut char_to_index = HashMap::new();
31 for (i, &c) in chars.iter().enumerate() {
32 if char_to_index.insert(c, i).is_some() {
33 return Err(format!("Duplicate character in alphabet: {}", c));
34 }
35 }
36
37 Ok(Alphabet {
38 chars,
39 char_to_index,
40 mode,
41 padding,
42 })
43 }
44
45 pub fn from_str(s: &str) -> Result<Self, String> {
46 let chars: Vec<char> = s.chars().collect();
47 Self::new(chars)
48 }
49
50 pub fn base(&self) -> usize {
51 self.chars.len()
52 }
53
54 pub fn mode(&self) -> &EncodingMode {
55 &self.mode
56 }
57
58 pub fn padding(&self) -> Option<char> {
59 self.padding
60 }
61
62 pub fn encode_digit(&self, digit: usize) -> Option<char> {
63 self.chars.get(digit).copied()
64 }
65
66 pub fn decode_char(&self, c: char) -> Option<usize> {
67 self.char_to_index.get(&c).copied()
68 }
69}
70
71