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}