numerals-0.1.4/.cargo_vcs_info.json0000644000000001121365104562100127570ustar00{ "git": { "sha1": "42700ad17491ba9bd145dfd3e90539a97b2be301" } } numerals-0.1.4/.gitignore010064400007650000024000000000221323633517200135500ustar0000000000000000target Cargo.lock numerals-0.1.4/Cargo.toml0000644000000016211365104562100107630ustar00# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] name = "numerals" version = "0.1.4" authors = ["Benjamin Sago "] exclude = ["/README.md", "/LICENCE", "/.rustfmt.toml", "/.travis.yml"] description = "Library for numeric systems, both ancient and modern" homepage = "https://github.com/ogham/rust-numerals" documentation = "https://docs.rs/numerals" readme = "README.md" license = "MIT" [lib] name = "numerals" numerals-0.1.4/Cargo.toml.orig010064400007650000024000000006051365104550200144520ustar0000000000000000[package] name = "numerals" description = "Library for numeric systems, both ancient and modern" authors = ["Benjamin Sago "] documentation = "https://docs.rs/numerals" exclude = ["/README.md", "/LICENCE", "/.rustfmt.toml", "/.travis.yml"] homepage = "https://github.com/ogham/rust-numerals" license = "MIT" readme = "README.md" version = "0.1.4" [lib] name = "numerals" numerals-0.1.4/src/bt.rs010064400007650000024000000040711365104350300133250ustar0000000000000000//! [Balanced ternary](https://en.wikipedia.org/wiki/Balanced_ternary) #![allow(missing_docs)] // TODO use std::convert::From; use std::fmt; use self::Trit::*; #[derive(PartialEq, Debug, Copy, Clone)] pub enum Trit { Minus, Zero, Plus } impl Trit { fn value(self) -> i64 { match self { Minus => -1, Zero => 0, Plus => 1, } } fn ascii(self) -> char { match self { Minus => '-', Zero => '0', Plus => '+', } } pub fn from_char(input: char) -> Option { match input { '-' => Some(Minus), '0' => Some(Zero), '+' => Some(Plus), _ => None, } } } pub struct BalancedTernary { trits: Vec, } impl BalancedTernary { pub fn parse(input: &str) -> Option { let mut trits = Vec::new(); for c in input.chars() { match Trit::from_char(c) { Some(trit) => trits.push(trit), None => return None, } } Some(Self { trits }) } pub fn value(&self) -> i64 { self.trits.iter().fold(0, |sum, trit| sum * 3 + trit.value()) } } impl fmt::Display for BalancedTernary { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { for t in &self.trits { write!(f, "{}", t.ascii())?; } Ok(()) } } impl From for BalancedTernary { fn from(mut input: i64) -> Self { let mut trits = Vec::new(); while input != 0 { match input % 3 { 0 => { trits.push(Zero); input /= 3; }, 1 => { trits.push(Plus); input /= 3; }, 2 => { trits.push(Minus); input += 1; input /= 3; }, _ => unreachable!(), } } trits.reverse(); Self { trits } } } #[cfg(test)] mod test { use super::BalancedTernary; #[test] fn test_many_numbers() { for i in 0 .. 4321 { assert_eq!(i, BalancedTernary::from(i).value()); } } } numerals-0.1.4/src/lib.rs010064400007650000024000000005421365104462400134720ustar0000000000000000//! This is a set of libraries for converting to and from various numeric systems. //! //! For more information, see the documentation for a particular module. #![warn(missing_copy_implementations)] #![warn(missing_docs)] #![warn(nonstandard_style)] #![warn(trivial_numeric_casts)] #![warn(unreachable_pub)] #![warn(unused)] pub mod bt; pub mod roman; numerals-0.1.4/src/roman.rs010064400007650000024000000205571365104421100140400ustar0000000000000000//! [Ancient Roman numerals](https://en.wikipedia.org/wiki/Roman_numerals) //! //! This is a library for converting between computer integers, Roman //! numerals, and ASCII strings. //! //! Operations are done by converting your input into a `Roman` value, then //! calling functions on it. For example: //! //! ``` //! use numerals::roman::Roman; //! //! let string = format!("{:X}", Roman::from(134)); //! assert_eq!(string, "CXXXIV"); //! ``` //! //! //! Converting numbers to Roman //! --------------------------- //! //! The `From` function in `std::convert` can turn either an `i16`, or a //! vector of `Numeral` values, into a `Roman` value. //! //! ``` //! use numerals::roman::{Roman, Numeral::{I, V, X}}; //! //! let input = Roman::from(27); //! let expected = Roman::from(vec![ X, X, V, I, I ]); //! assert_eq!(expected, input); //! ``` //! //! //! Converting Roman to numbers //! --------------------------- //! //! The `value` function translates a sequence of numerals into their computer //! value equivalent. //! //! ``` //! use numerals::roman::{Roman, Numeral::{I, V, X}}; //! //! let input = Roman::from(vec![ X, X, V, I, I ]).value(); //! assert_eq!(27, input); //! ``` //! //! //! Converting strings to Roman //! --------------------------- //! //! You can translate an existing sequence of characters with the `parse` //! constructor, which scans an input string, returning `None` if it //! encounters a character with no Roman meaning. //! //! It accepts both uppercase and lowercase ASCII characters. //! //! ``` //! use numerals::roman::{Roman, Numeral::{I, V, X}}; //! //! let input = Roman::parse("XXVII").unwrap(); //! let expected = Roman::from(vec![ X, X, V, I, I ]); //! assert_eq!(expected, input); //! ``` //! //! //! Converting Roman to strings //! --------------------------- //! //! There are two ways to convert numerals into strings: //! //! - For uppercase, use the `UpperHex` trait with the `{:X}` format string. //! - For lowercase, use the `LowerHex` trait with the `{:x}` format string. //! //! ``` //! use numerals::roman::{Roman, Numeral::{I, V, X}}; //! //! let input = format!("{:X}", Roman::from(vec![ X, X, V, I, I ])); //! assert_eq!("XXVII", input); //! ``` //! //! //! Limitations //! ----------- //! //! - The `Roman::from(i16)` function will **panic when given zero or a //! negative number!** The Romans had the *concept* of zero, but no numeral //! for it, so it’s not relevant here. Be sure to check your input values. //! - Similarly, there is no *common* way to handle numbers in the tens of //! thousands, which is why this library uses `i16`-sized integers. Numbers //! in the tens of thousands will work, but will be prefixed with many, many //! `M`s. use std::convert::From; use std::fmt; use self::Numeral::*; /// An individual Roman numeral, without a position. #[derive(PartialEq, Debug, Copy, Clone)] pub enum Numeral { /// The ‘Ⅰ’ numeral, representing 1. I, /// The ‘Ⅴ’ numeral, representing 5. V, /// The ‘Ⅹ’ numeral, representing 10. X, /// The ‘Ⅼ’ numeral, representing 50. L, /// The ‘C’ numeral, representing 100. C, /// The ‘Ⅾ’ numeral, representing 500. D, /// The ‘Ⅿ’ numeral, representing 1000. M, } impl Numeral { /// The value that this numeral is “worth”. “Worth” is in scare quotes, as /// the value can change depending on its position in the string. pub fn value(self) -> i16 { match self { I => 1, V => 5, X => 10, L => 50, C => 100, D => 500, M => 1000, } } fn ascii_upper(self) -> char { match self { I => 'I', V => 'V', X => 'X', L => 'L', C => 'C', D => 'D', M => 'M', } } fn ascii_lower(self) -> char { match self { I => 'i', V => 'v', X => 'x', L => 'l', C => 'c', D => 'd', M => 'm', } } /// Turn an individual character into its numeral equivalent, if there is /// one. Returns `None` otherwise. /// /// This accepts either uppercase or lowercase ASCII characters. pub fn from_char(input: char) -> Option { match input { 'I' | 'i' => Some(I), 'V' | 'v' => Some(V), 'X' | 'x' => Some(X), 'L' | 'l' => Some(L), 'C' | 'c' => Some(C), 'D' | 'd' => Some(D), 'M' | 'm' => Some(M), _ => None, } } } /// A sequence of Roman numerals. #[derive(PartialEq, Debug)] pub struct Roman { numerals: Vec, } impl Roman { /// Parses a string of characters into a sequence of numerals. Returns /// `None` if there’s a character in the input string that doesn’t map to /// a numeral. /// /// This accepts either uppercase or lowercase ASCII characters. pub fn parse(input: &str) -> Option { let mut numerals = Vec::new(); for c in input.chars() { numerals.push(Numeral::from_char(c)?); } Some(Self { numerals }) } /// Converts this string of numerals into a `i32` actual number. /// /// # Panics /// /// - This function panics when passed in a negative number or zero, as /// the Romans didn’t have a way to write those down! pub fn value(&self) -> i16 { let mut total = 0; let mut max = 0; for n in self.numerals.iter().map(|n| n.value()).rev() { total += if n >= max { n } else { -n }; if max < n { max = n; } } total } /// Converts this string of numerals into a `i32` actual number. /// Unlike `value`, this returns `None` on numbers that can't be converted to an `i32`. pub fn value_checked(&self) -> Option { let mut total: i16 = 0; let mut max = 0; for n in self.numerals.iter().map(|n| n.value()).rev() { let amount_to_add = if n >= max { n } else { -n }; total = total.checked_add(amount_to_add)?; if max < n { max = n; } } Some(total) } } impl fmt::LowerHex for Roman { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { for n in &self.numerals { write!(f, "{}", n.ascii_lower())? } Ok(()) } } impl fmt::UpperHex for Roman { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { for n in &self.numerals { write!(f, "{}", n.ascii_upper())?; } Ok(()) } } impl From> for Roman { fn from(numerals: Vec) -> Self { Self { numerals } } } impl From for Roman { fn from(mut number: i16) -> Self { assert!(number > 0); let mut numerals = Vec::new(); for &(secondary, primary) in &[ (C, M), (C, D), (X, C), (X, L), (I, X), (I, V) ] { while number >= primary.value() { number -= primary.value(); numerals.push(primary); } let difference = primary.value() - secondary.value(); if number >= difference { number -= difference; numerals.push(secondary); numerals.push(primary); } } while number > 0 { number -= 1; numerals.push(I); } Self { numerals } } } #[cfg(test)] mod test { use super::*; #[test] fn test_many_numbers() { for i in 1 .. 4321 { assert_eq!(i, Roman::from(i).value()) } for i in 1 .. 4321 { assert_eq!(Some(i), Roman::from(i).value_checked()) } } #[test] fn test_big_numbers() { for i in 32700 .. 32767 { assert_eq!(i, Roman::from(i).value()); } for i in 32700 .. 32767 { assert_eq!(Some(i), Roman::from(i).value_checked()); } } #[test] fn value_checked_err_on_large() { assert_eq!( Roman::parse("MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM") .unwrap() .value_checked(), None ); } #[test] #[should_panic] fn value_panic_on_large() { Roman::parse("MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM") .unwrap() .value(); } }