pub fn letters_to_num(letters: &str) -> Option { let mut num: u8 = 0; for (i, letter) in letters.to_ascii_uppercase().chars().rev().enumerate() { let n = letter as u8; if !(65..=90).contains(&n) { return None; } num = num.checked_add((26_i32.pow(i as u32) * (n as i32 - 64)).try_into().ok()?)?; } Some(num) } pub fn num_to_letters(num: u8) -> Option { if num == 0 { return None; } // Otherwise, we will not return none! // As another note, we can't represent higher than "IV" anyway; // thus there's no reason for a loop (26^n with n>1 will NOT occur). let mut text = "".to_string(); let mut digit1 = num.div_euclid(26u8); let mut digit2 = num.rem_euclid(26u8); if digit1 > 0 && digit2 == 0u8 { digit1 -= 1; digit2 = 26; } if digit1 != 0 { text.push((64 + digit1) as char) } text.push((64 + digit2) as char); Some(text.to_string()) } #[cfg(test)] mod tests { use super::{letters_to_num, num_to_letters}; #[test] fn test_letters_to_num() { assert_eq!(letters_to_num("D"), Some(4)); assert_eq!(letters_to_num("d"), None); assert_eq!(letters_to_num("AD"), Some(26 + 4)); assert_eq!(letters_to_num("CG"), Some(3 * 26 + 7)); } #[test] fn test_num_to_letters() { println!("27 is {:?}", num_to_letters(27)); assert_eq!(num_to_letters(1), Some("A".to_string())); assert_eq!(num_to_letters(26), Some("Z".to_string())); assert_eq!(num_to_letters(27), Some("AA".to_string())); assert_eq!(num_to_letters(111), Some("DG".to_string())); } #[test] fn test_l2n_and_n2l() { assert_eq!( num_to_letters(letters_to_num("A").unwrap()), Some("A".to_string()) ); assert_eq!( num_to_letters(letters_to_num("BJ").unwrap()), Some("BJ".to_string()) ); for i in 1..=255 { assert_eq!(letters_to_num(&num_to_letters(i).unwrap()), Some(i)); } } }