use std::{
cmp::Ordering,
fmt::Display,
ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, RemAssign, Sub, SubAssign},
str::FromStr,
};
use regex::Regex;
use crate::Fraction;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct Decimal {
value: Fraction,
}
impl Decimal {
pub const MAX: Self = Self { value: Fraction::MAX };
pub const MIN: Self = Self { value: Fraction::MIN };
pub const EPSILON: Self = Self { value: Fraction::EPSILON };
pub fn new() -> Self {
Self::default()
}
pub fn as_fraction(&self) -> Fraction {
self.value
}
pub fn abs(&self) -> Self {
Self::from(self.value.abs())
}
fn find_cyclic(a: i128, b: i128) -> Option<(usize, usize)> {
let mut remainders = Vec::new();
let mut remainder = a % b;
while remainder != 0 {
if remainders.contains(&remainder) {
let start = remainders.iter().position(|r| *r == remainder).unwrap();
let length = remainders.len() - start;
return Some((start, length));
}
remainders.push(remainder);
remainder = (remainder * 10) % b; }
None
}
}
macro_rules! impl_from_integer {
($($t:ty),+ $(,)?) => { $(
impl From<$t> for Decimal {
fn from(value: $t) -> Self {
Self::from(Fraction::from(value))
}
}
)+ };
}
impl_from_integer!(i8, i16, i32, i64, i128, u8, u16, u32, u64);
impl From<f64> for Decimal {
fn from(value: f64) -> Self {
Self::from(Fraction::from(value))
}
}
impl From<f32> for Decimal {
fn from(value: f32) -> Self {
Self::from(Fraction::from(value))
}
}
impl From<Fraction> for Decimal {
fn from(value: Fraction) -> Self {
Self { value }
}
}
impl From<&str> for Decimal {
fn from(value: &str) -> Self {
Self::from_str(value).unwrap_or_else(|_| panic!("expect format: `[+-]integer[.decimal][~cyclic][#radix]` but got `{}`", value))
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct ParseDecimalError;
impl FromStr for Decimal {
type Err = ParseDecimalError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = s.trim();
let re_dec = Regex::new(r"^([-+])?(\d+)\.?(\d+)?~?(\d+)?#?(\d+)?$").unwrap();
let caps = re_dec.captures(s).ok_or(ParseDecimalError)?;
let sign = caps.get(1).map_or("+", |m| m.as_str());
let radix = caps.get(5).map_or(10, |m| m.as_str().parse::<u32>().unwrap());
let integral = i128::from_str_radix(&caps[2], radix).unwrap();
let decimal = caps.get(3).map(|x| i128::from_str_radix(x.as_str(), radix).unwrap());
let cyclic = caps.get(4).map(|x| i128::from_str_radix(x.as_str(), radix).unwrap());
let decimal_len = caps.get(3).map_or(0, |m| m.len()) as u32;
let cyclic_len = caps.get(4).map_or(0, |m| m.len()) as u32;
let scale = i128::pow(radix as i128, decimal_len);
let repeat = i128::pow(radix as i128, decimal_len + cyclic_len) - scale;
let value = match (integral, decimal, cyclic) {
(i, None, None) => Fraction::from(i),
(i, Some(d), None) => Fraction::from((i * scale + d, scale)),
(i, None, Some(c)) => Fraction::from(i) + Fraction::from((c, repeat)),
(i, Some(d), Some(c)) => Fraction::from((i * scale + d, scale)) + Fraction::from((c, repeat)),
};
Ok(if sign == "-" { -Self { value } } else { Self { value } })
}
}
impl PartialOrd for Decimal {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Decimal {
fn cmp(&self, other: &Self) -> Ordering {
self.value.cmp(&other.value)
}
}
impl Neg for Decimal {
type Output = Self;
fn neg(self) -> Self::Output {
Self::from(-self.value)
}
}
#[auto_impl_ops::auto_ops]
impl Add for Decimal {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self::from(self.value + rhs.value)
}
}
#[auto_impl_ops::auto_ops]
impl Sub for Decimal {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
Self::from(self.value - rhs.value)
}
}
#[auto_impl_ops::auto_ops]
impl Mul for Decimal {
type Output = Self;
fn mul(self, rhs: Self) -> Self::Output {
Self::from(self.value * rhs.value)
}
}
#[auto_impl_ops::auto_ops]
impl Div for Decimal {
type Output = Self;
fn div(self, rhs: Self) -> Self::Output {
Self::from(self.value / rhs.value)
}
}
#[auto_impl_ops::auto_ops]
impl Rem for Decimal {
type Output = Self;
fn rem(self, rhs: Self) -> Self::Output {
Self::from(self.value % rhs.value)
}
}
impl Display for Decimal {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
if let Some((start, length)) = Self::find_cyclic(self.value.numerator(), self.value.denominator()) {
let s = f64::from(self.value).to_string();
write!(f, "{}...", str::from_utf8(&s.as_bytes()[..=(s.find('.').unwrap() + start + length * 3)]).unwrap())
} else {
write!(f, "{}", f64::from(self.value))
}
}
}
impl From<Decimal> for f64 {
fn from(value: Decimal) -> Self {
f64::from(value.value)
}
}
impl From<Decimal> for f32 {
fn from(value: Decimal) -> Self {
f32::from(value.value)
}
}