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
163impl 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}