haar_lib/num/
rational.rs

1//! 有理数
2
3use 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/// 有理数
12#[derive(Clone, Copy, PartialEq, Eq)]
13pub struct Rational {
14    numerator: i64,
15    denominator: i64,
16}
17
18impl Rational {
19    /// `numerator / denominator`を表す有理数を生成する
20    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    /// 分子を返す
40    pub fn numerator(self) -> i64 {
41        self.numerator
42    }
43
44    /// 分母を返す
45    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}