pyoe2_craftpath/utils/
fraction_utils.rs

1use std::fmt;
2use std::ops::{Add, Div, Mul, Sub};
3
4use serde::{Deserialize, Serialize};
5
6#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
7#[cfg_attr(feature = "python", pyo3_stub_gen::derive::gen_stub_pyclass)]
8#[cfg_attr(feature = "python", pyo3::prelude::pyclass)]
9#[cfg_attr(feature = "python", pyo3(eq, weakref, from_py_object, get_all, str))]
10pub struct Fraction {
11    pub num: u32,
12    pub den: u32,
13}
14
15#[cfg(feature = "python")]
16#[cfg_attr(feature = "python", pyo3_stub_gen::derive::gen_stub_pymethods)]
17#[cfg_attr(feature = "python", pyo3::pymethods)]
18impl Fraction {
19    #[new]
20    pub fn new(num: u32, den: u32) -> Self {
21        assert!(den != 0, "denominator must not be zero");
22        let mut f = Fraction { num, den };
23        f.simplify();
24        f
25    }
26
27    #[staticmethod]
28    pub fn zero() -> Self {
29        Fraction { num: 0, den: 1 }
30    }
31
32    #[staticmethod]
33    pub fn one() -> Self {
34        Fraction { num: 1, den: 1 }
35    }
36
37    #[staticmethod]
38    pub fn from_int(value: u32) -> Self {
39        Fraction { num: value, den: 1 }
40    }
41
42    #[staticmethod]
43    fn gcd(mut a: u32, mut b: u32) -> u32 {
44        while b != 0 {
45            let r = a % b;
46            a = b;
47            b = r;
48        }
49        a
50    }
51
52    pub fn simplify(&mut self) {
53        if self.num == 0 {
54            self.den = 1;
55            return;
56        }
57        let g = Self::gcd(self.num, self.den);
58        if g > 1 {
59            self.num /= g;
60            self.den /= g;
61        }
62    }
63
64    pub fn to_f64(&self) -> f64 {
65        self.num as f64 / self.den as f64
66    }
67}
68
69#[cfg(not(feature = "python"))]
70impl Fraction {
71    pub fn new(num: u32, den: u32) -> Self {
72        assert!(den != 0, "denominator must not be zero");
73        let mut f = Fraction { num, den };
74        f.simplify();
75        f
76    }
77
78    pub fn zero() -> Self {
79        Fraction { num: 0, den: 1 }
80    }
81
82    pub fn one() -> Self {
83        Fraction { num: 1, den: 1 }
84    }
85
86    pub fn from_int(value: u32) -> Self {
87        Fraction { num: value, den: 1 }
88    }
89
90    fn gcd(mut a: u32, mut b: u32) -> u32 {
91        while b != 0 {
92            let r = a % b;
93            a = b;
94            b = r;
95        }
96        a
97    }
98
99    pub fn simplify(&mut self) {
100        if self.num == 0 {
101            self.den = 1;
102            return;
103        }
104        let g = Self::gcd(self.num, self.den);
105        if g > 1 {
106            self.num /= g;
107            self.den /= g;
108        }
109    }
110
111    pub fn to_f64(&self) -> f64 {
112        self.num as f64 / self.den as f64
113    }
114}
115
116impl fmt::Display for Fraction {
117    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
118        if self.den == 1 {
119            write!(f, "{}", self.num)
120        } else {
121            write!(f, "{}/{}", self.num, self.den)
122        }
123    }
124}
125
126impl Add for Fraction {
127    type Output = Self;
128
129    fn add(self, rhs: Self) -> Self::Output {
130        Fraction::new(self.num * rhs.den + rhs.num * self.den, self.den * rhs.den)
131    }
132}
133
134impl Sub for Fraction {
135    type Output = Self;
136
137    fn sub(self, rhs: Self) -> Self::Output {
138        assert!(
139            self.num * rhs.den >= rhs.num * self.den,
140            "Result would be negative"
141        );
142        Fraction::new(self.num * rhs.den - rhs.num * self.den, self.den * rhs.den)
143    }
144}
145
146impl Mul for Fraction {
147    type Output = Self;
148
149    fn mul(self, rhs: Self) -> Self::Output {
150        Fraction::new(self.num * rhs.num, self.den * rhs.den)
151    }
152}
153
154impl Div for Fraction {
155    type Output = Self;
156
157    fn div(self, rhs: Self) -> Self::Output {
158        assert!(rhs.num != 0, "division by zero fraction");
159        Fraction::new(self.num * rhs.den, self.den * rhs.num)
160    }
161}
162
163// Fraction <op> integer
164impl Add<u32> for Fraction {
165    type Output = Fraction;
166    fn add(self, rhs: u32) -> Self::Output {
167        self + Fraction::from_int(rhs)
168    }
169}
170
171impl Sub<u32> for Fraction {
172    type Output = Fraction;
173    fn sub(self, rhs: u32) -> Self::Output {
174        self - Fraction::from_int(rhs)
175    }
176}
177
178impl Mul<u32> for Fraction {
179    type Output = Fraction;
180    fn mul(self, rhs: u32) -> Self::Output {
181        self * Fraction::from_int(rhs)
182    }
183}
184
185impl Div<u32> for Fraction {
186    type Output = Fraction;
187    fn div(self, rhs: u32) -> Self::Output {
188        assert!(rhs != 0, "division by zero integer");
189        self / Fraction::from_int(rhs)
190    }
191}
192
193#[cfg(test)]
194mod tests {
195    use super::*;
196
197    #[test]
198    fn test_fraction_fraction() {
199        let a = Fraction::new(1, 2);
200        let b = Fraction::new(1, 3);
201        assert_eq!(a + b, Fraction::new(5, 6));
202        assert_eq!(a * b, Fraction::new(1, 6));
203        assert_eq!(a / b, Fraction::new(3, 2));
204        assert_eq!(a - Fraction::new(0, 1), Fraction::new(1, 2));
205    }
206
207    #[test]
208    fn test_fraction_int() {
209        let a = Fraction::new(3, 4);
210        assert_eq!(a + 1, Fraction::new(7, 4));
211        assert_eq!(a * 2, Fraction::new(3, 2));
212        assert_eq!(a / 2, Fraction::new(3, 8));
213    }
214
215    #[test]
216    fn test_fraction_chained_operations() {
217        let a = Fraction::new(1, 2);
218        let b = Fraction::new(1, 3);
219        let c = Fraction::new(3, 4);
220
221        let result = ((a + b) * c) / 2;
222        assert_eq!(result, Fraction::new(5, 16));
223
224        let result2 = ((a * 2) + 1) / b;
225        assert_eq!(result2, Fraction::new(6, 1));
226    }
227
228    #[test]
229    fn test_to_f64() {
230        let f = Fraction::new(1, 4);
231        assert_eq!(f.to_f64(), 0.25);
232    }
233}