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
#[cfg(test)] mod tests; use std::collections::HashMap; use std::fmt::Write; use std::str::FromStr; use num::{BigRational, FromPrimitive, BigInt, Zero, One}; use regex::{Regex, Captures}; lazy_static::lazy_static! { static ref TEN: BigInt = BigInt::from_u32(10).unwrap(); static ref NINE: BigInt = BigInt::from_u32(9).unwrap(); static ref REGEX: Regex = Regex::new(r"^(?P<int>\d+)(\.(?P<frac>\d*)(\((?P<repeat>\d+)\))?)?$").unwrap(); } pub fn big_rational_to_string(n: BigRational) -> String { let mut fraction = String::new(); let mut map = HashMap::new(); let mut rem = n.numer() % n.denom(); while !rem.is_zero() && !map.contains_key(&rem) { map.insert(rem.clone(), fraction.len()); rem *= TEN.clone(); fraction.push_str(&(rem.clone() / n.denom()).to_string()); rem = rem % n.denom(); } let mut output = (n.numer() / n.denom()).to_string(); if rem.is_zero() { if fraction.len() != 0 { write!(output, ".{}", &fraction); } } else { write!(output, ".{}({})", &fraction[..map[&rem]], &fraction[map[&rem]..]); } output } pub fn str_to_big_rational(mut string: &str) -> Result<BigRational, ()> { match REGEX.captures(string) { Some(captures) => { let int = &captures["int"]; let fraction = captures.name("frac").map(|a| a.as_str()); let repeating = captures.name("repeat").map(|a| a.as_str()); Ok(match fraction { None => BigRational::from_str(int).unwrap(), Some(fraction) => match repeating { None => frac(int, fraction), Some(repeating) => repeat(int, fraction, repeating) } }) } None => Err(()) } } fn frac(integer: &str, fractional: &str) -> BigRational { let mut a = BigInt::one(); for _ in 0..fractional.len() { a *= &*TEN; } BigRational::new(BigInt::from_str(fractional).unwrap(), a) + BigInt::from_str(integer).unwrap() } fn repeat(integer: &str, fractional: &str, repeating: &str) -> BigRational { let mut a = BigRational::one(); for _ in 0..fractional.len() { a *= &*TEN; } let b = match BigRational::from_str(fractional) { Ok(a) => a, Err(_) => BigRational::zero() } / &a; let mut c = NINE.clone(); for _ in 1..repeating.len() { c *= &*TEN; c += &*NINE; } let d = BigRational::from_str(repeating).unwrap() / c / a; b + d + BigInt::from_str(integer).unwrap() }