1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
#[derive(Debug, Clone, PartialEq)] pub enum RomanNumeralError { InvalidStr, NumberOutOfRange, } #[derive(Debug, Clone)] pub struct RomanNumeral { value: u32, } impl RomanNumeral { fn get_roman_symbols() -> Vec<char> { let roman_symbols = "MDCLXVI"; roman_symbols.chars().collect() } fn get_symbol_value(symbol: char) -> Result<u32, RomanNumeralError> { let roman_symbols = Self::get_roman_symbols(); let index = roman_symbols.into_iter().rev().position(|s| s == symbol); match index { Some(i) => Ok(5u32.pow(i as u32 % 2) * 10u32.pow(i as u32 / 2)), None => Err(RomanNumeralError::InvalidStr), } } pub fn max_value() -> u32 { let n = Self::get_roman_symbols().len() as u32; 4u32.pow(n % 2) * 10u32.pow(n / 2) - 1 } pub fn get(&self) -> String { let roman_symbols = Self::get_roman_symbols(); let mut roman_numeral = vec![]; let mut num = self.value; let mut symbol_index = roman_symbols.len() - 1; while num > 0 { let cipher = (num % 10) as usize; let rom_sym_1 = roman_symbols[symbol_index]; let rom_sym_5 = roman_symbols[symbol_index.saturating_sub(1)]; let rom_sym_10 = roman_symbols[symbol_index.saturating_sub(2)]; let roman_symbol = match cipher { 1..=3 => rom_sym_1.to_string().repeat(cipher), 4 => vec![rom_sym_1, rom_sym_5].into_iter().collect(), 5 => rom_sym_5.to_string(), 6..=8 => std::iter::once(rom_sym_5) .chain(std::iter::repeat(rom_sym_1).take(cipher - 5)) .collect(), 9 => vec![rom_sym_1, rom_sym_10].into_iter().collect(), _ => String::from(""), }; roman_numeral.push(roman_symbol); symbol_index = symbol_index.saturating_sub(2); num /= 10; } roman_numeral.into_iter().rev().collect::<Vec<String>>().join("") } pub fn get_u32(&self) -> u32 { self.value } pub fn from_u32(value: u32) -> Result<RomanNumeral, RomanNumeralError> { if value == 0 || value > Self::max_value() { return Err(RomanNumeralError::NumberOutOfRange); } Ok(RomanNumeral { value, }) } pub fn from_string(value: &str) -> Result<RomanNumeral, RomanNumeralError> { let mut total = 0; let mut minus = 0; let symbols: Vec<_> = value.chars().collect(); for i in 0..symbols.len() { let num = Self::get_symbol_value(symbols[i])?; if i == value.len() - 1 || num >= Self::get_symbol_value(symbols[i + 1])? { total += num - minus; minus = 0; continue; } minus = num; } let result = RomanNumeral::from_u32(total); if result.is_err() { return Err(RomanNumeralError::InvalidStr); } let s = result.clone().unwrap().get(); if s != value { return Err(RomanNumeralError::InvalidStr); } result } }