pyinrs/
fraction.rs

1use std::{
2    cmp::Ordering,
3    fmt::Display,
4    ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, RemAssign, Sub, SubAssign},
5    str::FromStr,
6};
7
8use crate::{detail, Decimal};
9
10/// Fraction provides support for rational number arithmetic.
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
12pub struct Fraction {
13    // Numerator.
14    num: i128,
15
16    // Denominator.
17    den: i128,
18}
19
20impl Fraction {
21    /// Construct a new zero fraction.
22    pub fn new() -> Self {
23        Self { num: 0, den: 1 }
24    }
25
26    /// Return the absolute value of the fraction.
27    pub fn abs(&self) -> Self {
28        Self {
29            num: self.num.abs(),
30            den: self.den,
31        }
32    }
33
34    /// Get the numerator of self.
35    pub fn numerator(&self) -> i128 {
36        self.num
37    }
38
39    /// Get the denominator of self.
40    pub fn denominator(&self) -> i128 {
41        self.den
42    }
43
44    /// Convert to decimal.
45    pub fn as_decimal(&self) -> Decimal {
46        (*self).into()
47    }
48
49    /// Calculate the greatest common divisor of two fractions.
50    pub fn gcd(a: Self, b: Self) -> Self {
51        detail::gcd(a.abs(), b.abs())
52    }
53
54    /// Calculate the least common multiple of two fractions.
55    pub fn lcm(a: Self, b: Self) -> Self {
56        if a == 0.into() || b == 0.into() {
57            return Self::new();
58        }
59
60        (a * b).abs() / Self::gcd(a, b) // LCM = |a * b| / GCD
61    }
62
63    fn from_integer<T: Into<i128>>(value: T) -> Self {
64        Self { num: value.into(), den: 1 }
65    }
66
67    fn from_ratio<T: Into<i128>>(num: T, den: T) -> Self {
68        let (mut num, mut den) = (num.into(), den.into());
69
70        // make sure the denominator is not zero
71        detail::check_zero(den);
72
73        // make sure the denominator is a positive number
74        if den < 0 {
75            num = -num;
76            den = -den;
77        }
78
79        // simplify
80        let gcd = detail::gcd(num.abs(), den.abs());
81        num /= gcd;
82        den /= gcd;
83
84        Self { num, den }
85    }
86
87    pub const MAX: Fraction = Fraction { num: i128::MAX, den: 1 };
88    pub const MIN: Fraction = Fraction { num: i128::MIN, den: 1 };
89    pub const EPSILON: Fraction = Fraction { num: 1, den: i128::MAX };
90}
91
92/*
93Construct
94*/
95
96macro_rules! impl_from_integer {
97    ($($t:ty),+ $(,)?) => { $(
98        impl From<$t> for Fraction {
99            fn from(value: $t) -> Self {
100                Fraction::from_integer(value)
101            }
102        }
103
104        impl From<($t, $t)> for Fraction {
105            fn from(value: ($t, $t)) -> Self {
106                Fraction::from_ratio(value.0, value.1)
107            }
108        }
109    )+ };
110}
111
112// Implement `From` for integer types, u128 is not included because it may overflow when negated
113impl_from_integer!(i8, i16, i32, i64, i128, u8, u16, u32, u64);
114
115impl From<f64> for Fraction {
116    fn from(value: f64) -> Self {
117        if !value.is_finite() {
118            panic!("Error: Invalid floating-point number.");
119        }
120
121        let int_part = value.floor();
122        let dec_part = value - int_part;
123        let precision = i128::pow(10, f64::DIGITS);
124
125        let gcd = detail::gcd((dec_part * (precision as f64)).round() as i128, precision);
126        let mut num = (dec_part * precision as f64).round() as i128 / gcd;
127        let den = precision / gcd;
128        num += int_part as i128 * den;
129
130        Self { num, den }
131    }
132}
133
134impl From<f32> for Fraction {
135    fn from(value: f32) -> Self {
136        Fraction::from(value as f64)
137    }
138}
139
140impl From<Decimal> for Fraction {
141    fn from(value: Decimal) -> Self {
142        value.as_fraction()
143    }
144}
145
146impl From<&str> for Fraction {
147    fn from(value: &str) -> Self {
148        Self::from_str(value).unwrap_or_else(|_| panic!("expect format `numerator/denominator` but got `{}`", value))
149    }
150}
151
152#[derive(Debug, PartialEq, Eq)]
153pub struct ParseFractionError;
154
155impl FromStr for Fraction {
156    type Err = ParseFractionError;
157
158    fn from_str(s: &str) -> Result<Self, Self::Err> {
159        let s = s.trim();
160        if let Ok(num) = s.parse() {
161            return Ok(Self { num, den: 1 });
162        }
163
164        let (num, den) = s.split_once('/').ok_or(ParseFractionError)?;
165
166        let num = num.parse::<i128>().map_err(|_| ParseFractionError)?;
167        let den = den.parse::<i128>().map_err(|_| ParseFractionError)?;
168
169        Ok(Self::from((num, den)))
170    }
171}
172
173impl Default for Fraction {
174    fn default() -> Self {
175        Self::new()
176    }
177}
178
179/*
180Function
181*/
182
183impl PartialOrd for Fraction {
184    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
185        Some(self.cmp(other))
186    }
187}
188
189impl Ord for Fraction {
190    fn cmp(&self, other: &Self) -> Ordering {
191        // self = a/b; other = c/d;
192        // so, self - other = a/b - c/d = (ad - bc)/(bd)
193        // since bd is always positive, compute (ad-bc) only
194
195        (self.num * other.den - self.den * other.num).cmp(&0)
196    }
197}
198
199impl Neg for Fraction {
200    type Output = Self;
201
202    fn neg(self) -> Self::Output {
203        Self { num: -self.num, den: self.den }
204    }
205}
206
207#[auto_impl_ops::auto_ops]
208impl Add for Fraction {
209    type Output = Self;
210
211    fn add(self, rhs: Self) -> Self::Output {
212        Self::from((self.num * rhs.den + self.den * rhs.num, self.den * rhs.den))
213    }
214}
215
216#[auto_impl_ops::auto_ops]
217impl Sub for Fraction {
218    type Output = Self;
219
220    fn sub(self, rhs: Self) -> Self::Output {
221        Self::from((self.num * rhs.den - self.den * rhs.num, self.den * rhs.den))
222    }
223}
224
225#[auto_impl_ops::auto_ops]
226impl Mul for Fraction {
227    type Output = Self;
228
229    fn mul(self, rhs: Self) -> Self::Output {
230        Self::from((self.num * rhs.num, self.den * rhs.den))
231    }
232}
233
234#[auto_impl_ops::auto_ops]
235impl Div for Fraction {
236    type Output = Self;
237
238    fn div(self, rhs: Self) -> Self::Output {
239        Self::from((self.num * rhs.den, self.den * rhs.num))
240    }
241}
242
243#[auto_impl_ops::auto_ops]
244impl Rem for Fraction {
245    type Output = Self;
246
247    fn rem(self, rhs: Self) -> Self::Output {
248        detail::check_zero(rhs.num);
249
250        Self::from(((self.num * rhs.den) % (rhs.num * self.den), self.den * rhs.den))
251    }
252}
253
254/*
255Display
256*/
257
258impl Display for Fraction {
259    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
260        if self.den == 1 {
261            write!(f, "{}", self.num)
262        } else {
263            write!(f, "{}/{}", self.num, self.den)
264        }
265    }
266}
267
268/*
269Transform
270*/
271
272impl From<Fraction> for f64 {
273    fn from(value: Fraction) -> Self {
274        value.num as f64 / value.den as f64
275    }
276}
277
278impl From<Fraction> for f32 {
279    fn from(value: Fraction) -> Self {
280        value.num as f32 / value.den as f32
281    }
282}