letter_sequence/sequence/helper.rs
1use super::*;
2
3/// Converts any interger into base26 for alphabetic representation
4/// Tested against
5/// perl -E'my $x = "A"; say "$_ " . $x++ for 0 .. 900'
6/// ```
7/// use letter_sequence::sequence::helper;
8/// const A: u64 = 0;
9/// const Z: u64 = 25;
10///
11/// assert_eq!( helper::int_to_string(A).unwrap(), "A");
12/// assert_eq!( helper::int_to_string(Z).unwrap(), "Z");
13/// assert_eq!( helper::int_to_string(Z + 1).unwrap(), "AA");
14/// assert_eq!( helper::int_to_string(Z + 2).unwrap(), "AB");
15/// assert_eq!( helper::int_to_string(28).unwrap(), "AC");
16/// assert_eq!( helper::int_to_string(900).unwrap(), "AHQ");
17/// assert_eq!( helper::int_to_string(1000000).unwrap(), "BDWGO");
18/// ```
19pub fn int_to_string(i: u64) -> Result<String, SequenceError> {
20 let (quot, rem) = (i / (LETTERS + 1), i % (LETTERS + 1));
21 if quot == 0 {
22 return Ok(String::from(int_to_char(rem as u32)?))
23 }
24 return Ok(
25 format!(
26 "{}{}",
27 // We do this because int_to_char is 0-based.
28 // But, 26 // 26 == 1; 26 % 26 == 1 (and we want that to be A)
29 int_to_string(quot - 1)?,
30 String::from(int_to_char(rem as u32)?),
31 )
32 )
33}
34
35/// Accepts 0-25 Ordinal number and converts it to char
36/// ```
37/// use letter_sequence::sequence::helper;
38/// assert_eq!( helper::int_to_char(0).unwrap(), 'A' );
39/// assert_eq!( helper::int_to_char(25).unwrap(), 'Z' );
40/// ```
41pub fn int_to_char(i: u32) -> Result<char, SequenceError> {
42 const ASCII_A_OFFSET: u32 = 'A' as u32;
43 if i > LETTERS as u32 {
44 return Err(SequenceError::NotAsciiCharacter)
45 }
46 let letter = std::char::from_u32(i + ASCII_A_OFFSET)
47 .unwrap();
48 Ok(letter)
49}
50
51
52/// Accepts a character, and returns a representation of the ordinal position
53/// ```
54/// use letter_sequence::sequence::helper;
55/// assert_eq!( helper::char_to_int('A').unwrap(), 0 );
56/// assert_eq!( helper::char_to_int('B').unwrap(), 1 );
57/// assert_eq!( helper::char_to_int('C').unwrap(), 2 );
58/// assert_eq!( helper::char_to_int('Z').unwrap(), 25 );
59/// ```
60pub fn char_to_int(c: char) -> Result<u8, SequenceError> {
61 if c.is_ascii_alphabetic() {
62 Ok(c.to_ascii_uppercase() as u8 - 'A' as u8)
63 }
64 else {
65 Err(SequenceError::InvalidCharacter(c))
66 }
67}
68
69/// Accepts a character, and returns a representation of the ordinal position
70/// ```
71/// use letter_sequence::sequence::helper;
72/// assert_eq!( helper::string_to_int("A").unwrap(), 0 );
73/// assert_eq!( helper::string_to_int("Z").unwrap(), 25 );
74/// assert_eq!( helper::string_to_int("AA").unwrap(), 26 );
75/// use letter_sequence::SequenceError;
76/// assert!( matches!(helper::string_to_int(",").unwrap_err(), SequenceError::InvalidCharacter(',')) );
77/// ```
78pub fn string_to_int(s: &str) -> Result<u64, SequenceError> {
79 let mut max: u64 = 0;
80 for (i,val) in s.chars().rev().enumerate() {
81 let ord = char_to_int(val)? as u64;
82 max += (1+ord) * 26u64.pow(i as u32);
83 }
84 Ok(max - 1)
85}
86
87#[cfg(test)]
88mod test{
89 use super::*;
90
91 mod char {
92 use super::*;
93 #[test]
94 fn simple() {
95 for c in 'A'..'Z' {
96 assert_eq!(int_to_char(c as u32 - 'A' as u32).unwrap(), c);
97 }
98 assert!(
99 matches!(
100 int_to_char('Z' as u32 + 1).unwrap_err(),
101 SequenceError::NotAsciiCharacter
102 )
103 );
104 }
105 }
106
107}