Skip to content

Commit

Permalink
Conversion to roman numerals
Browse files Browse the repository at this point in the history
  • Loading branch information
printfn committed Mar 12, 2024
1 parent afdadcc commit d27b811
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 1 deletion.
37 changes: 36 additions & 1 deletion core/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::serialize::{Deserialize, Serialize};
use crate::value::{built_in_function::BuiltInFunction, ApplyMulHandling, Value};
use crate::Attrs;
use std::sync::Arc;
use std::{cmp, fmt, io};
use std::{borrow, cmp, fmt, io};

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum BitwiseBop {
Expand Down Expand Up @@ -621,6 +621,15 @@ fn evaluate_as<I: Interrupt>(
}
return Err(FendError::ExpectedANumber);
}
"roman" | "roman_numeral" => {
let a = evaluate(a, scope, attrs, context, int)?
.expect_num()?
.try_as_usize(int)?;
if a == 0 {
return Err(FendError::RomanNumeralZero);
}
return Ok(Value::String(borrow::Cow::Owned(to_roman(a))));
}
_ => (),
}
}
Expand Down Expand Up @@ -754,3 +763,29 @@ pub(crate) fn resolve_identifier<I: Interrupt>(
_ => return crate::units::query_unit(ident.as_str(), attrs, context, int),
})
}

fn to_roman(mut num: usize) -> String {
let mut result = String::new();
for (r, n) in [
("M", 1000),
("CM", 900),
("D", 500),
("CD", 400),
("C", 100),
("XC", 90),
("L", 50),
("XL", 40),
("X", 10),
("IX", 9),
("V", 5),
("IV", 4),
("I", 1),
] {
let q = num / n;
num -= q * n;
for _ in 0..q {
result.push_str(r);
}
}
result
}
2 changes: 2 additions & 0 deletions core/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ pub(crate) enum FendError {
before: date::Date,
after: date::Date,
},
RomanNumeralZero,
}

impl fmt::Display for FendError {
Expand Down Expand Up @@ -248,6 +249,7 @@ impl fmt::Display for FendError {
"{month} {expected_day}, {year} does not exist, did you mean {before} or {after}?",
)
}
Self::RomanNumeralZero => write!(f, "zero cannot be represented as a roman numeral"),
}
}
}
Expand Down
37 changes: 37 additions & 0 deletions core/tests/integration_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5899,3 +5899,40 @@ fn test_equality() {
test_eval("2.010m == 200cm", "false");
test_eval("2.000m == approx. 200cm", "true");
}

#[test]
fn test_roman() {
expect_error(
"0 to roman",
Some("zero cannot be represented as a roman numeral"),
);
test_eval_simple("1 to roman", "I");
test_eval_simple("2 to roman", "II");
test_eval_simple("3 to roman", "III");
test_eval_simple("4 to roman", "IV");
test_eval_simple("5 to roman", "V");
test_eval_simple("6 to roman", "VI");
test_eval_simple("7 to roman", "VII");
test_eval_simple("8 to roman", "VIII");
test_eval_simple("9 to roman", "IX");
test_eval_simple("10 to roman", "X");
test_eval_simple("11 to roman", "XI");
test_eval_simple("12 to roman", "XII");
test_eval_simple("13 to roman", "XIII");
test_eval_simple("14 to roman", "XIV");
test_eval_simple("15 to roman", "XV");
test_eval_simple("16 to roman", "XVI");
test_eval_simple("17 to roman", "XVII");
test_eval_simple("18 to roman", "XVIII");
test_eval_simple("19 to roman", "XIX");
test_eval_simple("20 to roman", "XX");
test_eval_simple("21 to roman", "XXI");
test_eval_simple("22 to roman", "XXII");
test_eval_simple("45 to roman", "XLV");
test_eval_simple("134 to roman", "CXXXIV");
test_eval_simple("1965 to roman", "MCMLXV");
test_eval_simple("2020 to roman", "MMXX");
test_eval_simple("3456 to roman", "MMMCDLVI");
test_eval_simple("1452 to roman", "MCDLII");
test_eval_simple("20002 to roman", "MMMMMMMMMMMMMMMMMMMMII");
}

0 comments on commit d27b811

Please sign in to comment.