#[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_str(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()
}