1use crate::math::gcd_lcm::GcdLcm;
4use crate::{impl_from, impl_ops};
5use std::{
6 cmp::Ordering,
7 fmt,
8 fmt::{Debug, Formatter},
9};
10
11#[derive(Clone, Copy, PartialEq, Eq)]
13pub struct Rational {
14 numerator: i64,
15 denominator: i64,
16}
17
18impl Rational {
19 pub fn new(mut numerator: i64, mut denominator: i64) -> Self {
21 if denominator == 0 {
22 panic!("denominator must not be 0.");
23 }
24
25 if denominator < 0 {
26 denominator = -denominator;
27 numerator = -numerator;
28 }
29 let g = numerator.abs().gcd(denominator);
30 numerator /= g;
31 denominator /= g;
32
33 Self {
34 numerator,
35 denominator,
36 }
37 }
38
39 pub fn numerator(self) -> i64 {
41 self.numerator
42 }
43
44 pub fn denominator(self) -> i64 {
46 self.denominator
47 }
48
49 #[inline]
50 fn __add(self, other: Self) -> Self {
51 let l = self.denominator.lcm(other.denominator);
52 Self::new(
53 l / self.denominator * self.numerator + l / other.denominator * other.numerator,
54 l,
55 )
56 }
57
58 #[inline]
59 fn __sub(self, other: Self) -> Self {
60 let l = self.denominator.lcm(other.denominator);
61 Self::new(
62 l / self.denominator * self.numerator - l / other.denominator * other.numerator,
63 l,
64 )
65 }
66
67 #[inline]
68 fn __mul(self, other: Self) -> Self {
69 Self::new(
70 self.numerator * other.numerator,
71 self.denominator * other.denominator,
72 )
73 }
74
75 #[inline]
76 fn __div(self, other: Self) -> Self {
77 Self::new(
78 self.numerator * other.denominator,
79 self.denominator * other.numerator,
80 )
81 }
82}
83
84impl Debug for Rational {
85 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
86 write!(f, "{} / {}", self.numerator, self.denominator)
87 }
88}
89
90impl_from!(Rational => f64, |value: Rational| (value.numerator as f64) / (value.denominator as f64));
91impl_from!(i64 => Rational, |value| Self { numerator: value, denominator: 1 });
92
93impl_ops!(Add for Rational, |s: Self, rhs| s.__add(rhs));
94impl_ops!(Sub for Rational, |s: Self, rhs| s.__sub(rhs));
95impl_ops!(Mul for Rational, |s: Self, rhs| s.__mul(rhs));
96impl_ops!(Div for Rational, |s: Self, rhs| s.__div(rhs));
97
98impl_ops!(AddAssign for Rational, |s: &mut Self, rhs| *s = *s + rhs);
99impl_ops!(SubAssign for Rational, |s: &mut Self, rhs| *s = *s - rhs);
100impl_ops!(MulAssign for Rational, |s: &mut Self, rhs| *s = *s * rhs);
101impl_ops!(DivAssign for Rational, |s: &mut Self, rhs| *s = *s / rhs);
102
103impl_ops!(Neg for Rational, |s: Self| Self::new(
104 -s.numerator,
105 s.denominator
106));
107
108impl PartialOrd for Rational {
109 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
110 Some((self.numerator * other.denominator).cmp(&(other.numerator * self.denominator)))
111 }
112}
113
114impl Ord for Rational {
115 fn cmp(&self, other: &Self) -> Ordering {
116 (self.numerator * other.denominator).cmp(&(other.numerator * self.denominator))
117 }
118}
119
120#[cfg(test)]
121mod tests {
122 use super::*;
123
124 #[test]
125 fn test() {
126 assert_eq!(
127 Rational::new(2, 3) + Rational::new(4, 5),
128 Rational::new(22, 15)
129 );
130
131 assert_eq!(
132 Rational::new(3, 4) - Rational::new(2, 3),
133 Rational::new(1, 12)
134 );
135
136 assert_eq!(
137 Rational::new(8, 9) * Rational::new(3, 4),
138 Rational::new(2, 3)
139 );
140
141 assert_eq!(
142 Rational::new(1, 3) / Rational::new(7, 6),
143 Rational::new(2, 7)
144 );
145 }
146}