symrs/expr/
rational.rs

1use num::{Float, NumCast, integer::gcd};
2
3use super::*;
4
5#[derive(Clone, Copy)]
6pub struct Rational {
7    pub num: isize,
8    pub denom: isize,
9}
10
11impl Expr for Rational {
12    fn known_expr(&self) -> KnownExpr {
13        KnownExpr::Rational(self)
14    }
15    fn for_each_arg(&self, f: &mut dyn FnMut(&dyn Arg) -> ()) {
16        f(&self.num);
17        f(&self.denom);
18    }
19
20    fn from_args(&self, args: Vec<Box<dyn Arg>>) -> Box<dyn Expr> {
21        Box::new(Rational {
22            num: *args[0].as_any().downcast_ref::<isize>().unwrap(),
23            denom: *args[1].as_any().downcast_ref::<isize>().unwrap(),
24        })
25    }
26
27    fn clone_box(&self) -> Box<dyn Expr> {
28        Box::new(self.clone())
29    }
30
31    fn str(&self) -> String {
32        format!("{}/{}", self.num, self.denom)
33    }
34
35    fn to_cpp(&self) -> String {
36        format!("{}./{}.", self.num, self.denom)
37    }
38
39    fn get_ref<'a>(&'a self) -> &'a dyn Expr {
40        self as &dyn Expr
41    }
42
43    fn is_one(&self) -> bool {
44        self.num == self.denom
45    }
46
47    fn is_zero(&self) -> bool {
48        self.num == 0
49    }
50
51    fn is_neg_one(&self) -> bool {
52        self.num == -self.denom
53    }
54    fn is_number(&self) -> bool {
55        true
56    }
57
58    fn is_negative_number(&self) -> bool {
59        self.num * self.denom < 0
60    }
61
62    fn as_f64(&self) -> Option<f64> {
63        Some(self.num.to_f64().unwrap() / self.denom.to_f64().unwrap())
64    }
65    fn simplify(&self) -> Box<dyn Expr> {
66        let mut res = self.clone();
67        if self.num < 0 && self.denom < 0 {
68            res.num *= -1;
69            res.denom *= -1;
70        }
71        let d = gcd(self.num, self.denom);
72        res.num /= d;
73        res.denom /= d;
74        if self.num % self.denom == 0 {
75            Integer::new_box(self.num / self.denom)
76        } else {
77            Box::new(res)
78        }
79    }
80}
81
82impl Rational {
83    pub fn new(num: isize, denom: isize) -> Self {
84        Rational { num, denom }
85    }
86
87    pub fn new_box(num: isize, denom: isize) -> Box<dyn Expr> {
88        Box::new(Rational { num, denom })
89    }
90    pub fn one() -> Self {
91        Rational { num: 1, denom: 1 }
92    }
93
94    pub fn zero() -> Self {
95        Rational { num: 0, denom: 1 }
96    }
97
98    pub fn invert(&mut self) {
99        std::mem::swap(&mut self.num, &mut self.denom);
100    }
101}
102
103// pub enum Coeff {
104//     Rational(Rational),
105//     Integer(Integer),
106// }
107
108impl std::fmt::Debug for Rational {
109    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
110        write!(f, "{}", self.srepr())
111    }
112}
113
114pub trait ToInteger {
115    fn to_integer(&self) -> Integer;
116}
117
118impl ToInteger for i32 {
119    fn to_integer(&self) -> Integer {
120        Integer::new(*self as isize)
121    }
122}
123
124impl<N: ToInteger> From<N> for Rational {
125    fn from(value: N) -> Self {
126        Rational {
127            num: value.to_integer().value,
128            denom: 1,
129        }
130    }
131}
132
133// impl<N: num::Integer> From<N> for Rational {
134//     fn from(value: N) -> Self {
135//         Rational {
136//             num: value.to_isize().unwrap(),
137//             denom: 1,
138//         }
139//     }
140// }
141
142impl Rational {
143    pub fn from_float<N: Float + ToString>(value: N) -> Rational {
144        let srepr = value.to_string();
145        let decimals = srepr.split('.').last().unwrap_or("");
146        let num_decimals = decimals.len();
147
148        let mut rational = Rational {
149            num: srepr.replace(".", "").parse().expect("valid integer"),
150            denom: (10 as isize).pow(num_decimals as u32),
151        };
152
153        let gcd = gcd(rational.num, rational.denom);
154        rational.num /= gcd;
155        rational.denom /= gcd;
156
157        rational
158    }
159}
160
161impl<I: ToPrimitive> std::ops::Mul<I> for Rational {
162    type Output = Rational;
163
164    fn mul(self, rhs: I) -> Self::Output {
165        Rational {
166            num: self.num * rhs.to_isize().unwrap(),
167            denom: self.denom,
168        }
169    }
170}
171
172// impl<T> PartialEq<T> for Rational where &T: Into<Rational> {}
173
174impl<T: Copy + Into<Rational>> PartialEq<T> for Rational {
175    fn eq(&self, other: &T) -> bool {
176        let other: Rational = (*other).into();
177        self.num * other.denom == self.denom * other.num
178    }
179}
180
181impl std::cmp::Eq for Rational {}
182
183// impl From<isize> for Rational {
184//     fn from(value: isize) -> Self {
185//         Rational {
186//             num: value,
187//             denom: 1,
188//         }
189//     }
190// }
191
192impl<T: Copy + Into<Rational>> PartialOrd<T> for Rational {
193    fn partial_cmp(&self, other: &T) -> Option<std::cmp::Ordering> {
194        let a: f64 = self.into();
195        let b: Rational = (*other).into();
196        let b: f64 = b.into();
197
198        a.partial_cmp(&b)
199    }
200}
201
202impl Ord for Rational {
203    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
204        let a: f64 = self.into();
205        let b: f64 = other.into();
206        a.partial_cmp(&b).unwrap()
207    }
208}
209
210impl From<&Rational> for f64 {
211    fn from(Rational { num, denom }: &Rational) -> Self {
212        *num as f64 / *denom as f64
213    }
214}
215
216impl From<Rational> for f64 {
217    fn from(Rational { num, denom }: Rational) -> Self {
218        num as f64 / denom as f64
219    }
220}
221
222impl std::ops::Add for &Rational {
223    type Output = Rational;
224
225    fn add(self, rhs: Self) -> Self::Output {
226        Rational {
227            num: self.num * rhs.denom + rhs.num * self.denom,
228            denom: self.denom * rhs.denom,
229        }
230    }
231}
232
233impl std::ops::Add for Rational {
234    type Output = Rational;
235
236    fn add(self, rhs: Self) -> Self::Output {
237        Rational {
238            num: self.num * rhs.denom + rhs.num * self.denom,
239            denom: self.denom * rhs.denom,
240        }
241    }
242}
243
244impl std::ops::AddAssign for Rational {
245    fn add_assign(&mut self, rhs: Self) {
246        let res = *self + rhs;
247
248        self.num = res.num;
249        self.denom = res.denom;
250    }
251}
252
253impl std::ops::Add<&Integer> for &Rational {
254    type Output = Box<dyn Expr>;
255
256    fn add(self, rhs: &Integer) -> Self::Output {
257        Rational::new_box(self.num + rhs.value * self.denom, self.denom).simplify()
258    }
259}
260
261impl std::ops::Add<&Rational> for &Integer {
262    type Output = Box<dyn Expr>;
263
264    fn add(self, rhs: &Rational) -> Self::Output {
265        Rational::new_box(self.value * rhs.denom + rhs.num, rhs.denom).simplify()
266    }
267}
268
269impl std::ops::Neg for &Rational {
270    type Output = Rational;
271
272    fn neg(self) -> Self::Output {
273        Rational {
274            num: -self.num,
275            denom: self.denom,
276        }
277    }
278}
279
280impl std::ops::Sub for Rational {
281    type Output = Rational;
282
283    fn sub(self, rhs: Rational) -> Self::Output {
284        Rational {
285            num: self.num * rhs.denom - rhs.num * self.denom,
286            denom: self.denom * rhs.denom,
287        }
288    }
289}
290
291impl<N: NumCast> std::ops::Sub<N> for Rational {
292    type Output = Box<dyn Expr>;
293
294    fn sub(self, rhs: N) -> Self::Output {
295        let rhs: Rational = rhs.to_i32().unwrap().into();
296
297        (self - rhs).simplify()
298    }
299}
300
301impl<I: ToPrimitive> std::ops::MulAssign<&I> for Rational {
302    fn mul_assign(&mut self, rhs: &I) {
303        self.num *= rhs.to_isize().unwrap();
304    }
305}
306
307impl std::ops::Mul<Rational> for Rational {
308    type Output = Rational;
309
310    fn mul(self, rhs: Rational) -> Self::Output {
311        Rational {
312            num: self.num * rhs.num,
313            denom: self.denom * rhs.denom,
314        }
315    }
316}
317
318impl std::ops::Div<Rational> for Rational {
319    type Output = Rational;
320
321    fn div(self, rhs: Rational) -> Self::Output {
322        Rational {
323            num: self.num * rhs.denom,
324            denom: self.denom * rhs.num,
325        }
326    }
327}
328
329impl std::ops::MulAssign<&Rational> for Rational {
330    fn mul_assign(&mut self, rhs: &Rational) {
331        self.num *= rhs.num;
332        self.denom *= rhs.denom;
333    }
334}
335
336impl std::ops::DivAssign<&Rational> for Rational {
337    fn div_assign(&mut self, rhs: &Rational) {
338        self.num *= rhs.denom;
339        self.denom *= rhs.num;
340    }
341}
342
343impl std::ops::DivAssign for Rational {
344    fn div_assign(&mut self, rhs: Rational) {
345        self.num *= rhs.denom;
346        self.denom *= rhs.num;
347    }
348}
349
350impl From<&str> for Rational {
351    fn from(value: &str) -> Self {
352        let (num, denom) = value.split_once('/').unwrap();
353        Rational {
354            num: num.parse().unwrap(),
355            denom: denom.parse().unwrap(),
356        }
357    }
358}
359
360#[cfg(test)]
361mod tests {
362
363    use super::*;
364
365    #[test]
366    fn test_mul_rational_int() {
367        let r = Rational::new(3, 4);
368        let i = 3;
369        let expected = Rational::new(3 * 3, 4);
370
371        assert_eq!(r * i, expected);
372    }
373
374    #[test]
375    fn test_simplify_basic() {
376        let expr = Rational::new(3, 4);
377        let expected = Rational::new_box(3, 4);
378        assert_eq!(&expr.simplify(), &expected)
379    }
380
381    #[test]
382    fn test_simpify_to_int() {
383        let expr = Rational::new(9, 3);
384        let expected = Integer::new_box(3);
385        assert_eq!(&expr.simplify(), &expected)
386    }
387
388    #[test]
389    fn test_add() {
390        assert_eq!(
391            Rational::new(3, 4) + Rational::new(2, 5),
392            Rational::new(23, 20)
393        )
394    }
395
396    #[test]
397    fn test_ord() {
398        let [a, b] = [Rational::from("1/2"), Rational::from("1/3")];
399
400        assert!(a > b)
401    }
402
403    #[test]
404    fn test_add_bis() {
405        assert_eq!(
406            Rational::new(1, 4) + Rational::new(-1, 2),
407            Rational::new(-1, 4)
408        )
409    }
410}