cp_library_rs/number_theory/
frac.rs1use std::cmp::Ordering;
4use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
5
6use num::{Integer, One, Zero};
7use num_integer::gcd;
8
9#[derive(Debug, Clone, Copy)]
12pub struct Frac<T: Integer>(T, T);
13
14impl<T: Integer + Copy> Frac<T> {
15 pub fn new(a: T, b: T) -> Self {
16 assert!(!b.is_zero(), "denominator must be non-zero");
17 let (mut a, mut b) = (a, b);
18 if b < T::zero() {
19 a = T::zero() - a;
20 b = T::zero() - b;
21 }
22 let c = gcd(a, b);
23 Self(a / c, b / c)
24 }
25
26 pub fn numer(&self) -> T {
28 self.0
29 }
30
31 pub fn denom(&self) -> T {
33 self.1
34 }
35
36 pub fn as_integer(&self) -> Option<T> {
38 let &Self(a, b) = self;
39 (a % b == T::zero()).then_some(a / b)
40 }
41}
42
43impl<T> Zero for Frac<T>
44where
45 T: Integer + Copy,
46{
47 fn zero() -> Self {
48 Self(T::zero(), T::one())
49 }
50 fn is_zero(&self) -> bool {
51 !self.1.is_zero() && self.0.is_zero()
52 }
53}
54
55impl<T> One for Frac<T>
56where
57 T: Integer + Copy,
58{
59 fn one() -> Self {
60 Self(T::one(), T::one())
61 }
62 fn is_one(&self) -> bool
63 where
64 Self: PartialEq,
65 {
66 !self.1.is_zero() && self.0 == self.1
67 }
68}
69
70impl<T> From<T> for Frac<T>
71where
72 T: Integer + Copy,
73{
74 fn from(value: T) -> Self {
75 Self::new(value, T::one())
76 }
77}
78
79impl<T> Add for Frac<T>
80where
81 T: Integer + Copy,
82{
83 type Output = Self;
84 fn add(self, rhs: Self) -> Self::Output {
85 let Frac(a, b) = self;
86 let Frac(c, d) = rhs;
87 Self::new(a * d + c * b, b * d)
88 }
89}
90
91impl<T> Sub for Frac<T>
92where
93 T: Integer + Copy,
94{
95 type Output = Self;
96 fn sub(self, rhs: Self) -> Self::Output {
97 let Frac(a, b) = self;
98 let Frac(c, d) = rhs;
99 Self::new(a * d - c * b, b * d)
100 }
101}
102
103impl<T> Mul for Frac<T>
104where
105 T: Integer + Copy,
106{
107 type Output = Self;
108 fn mul(self, rhs: Self) -> Self::Output {
109 let Frac(a, b) = self;
110 let Frac(c, d) = rhs;
111 Self::new(a * c, b * d)
112 }
113}
114
115impl<T> Div for Frac<T>
116where
117 T: Integer + Copy,
118{
119 type Output = Self;
120 fn div(self, rhs: Self) -> Self::Output {
121 let Frac(a, b) = self;
122 let Frac(c, d) = rhs;
123 assert!(!c.is_zero(), "division by zero fraction");
124 Self::new(a * d, b * c)
125 }
126}
127
128impl<T> AddAssign for Frac<T>
129where
130 T: Integer + Copy,
131{
132 fn add_assign(&mut self, rhs: Self) {
133 *self = *self + rhs;
134 }
135}
136
137impl<T> SubAssign for Frac<T>
138where
139 T: Integer + Copy,
140{
141 fn sub_assign(&mut self, rhs: Self) {
142 *self = *self - rhs;
143 }
144}
145
146impl<T> MulAssign for Frac<T>
147where
148 T: Integer + Copy,
149{
150 fn mul_assign(&mut self, rhs: Self) {
151 *self = *self * rhs;
152 }
153}
154
155impl<T> DivAssign for Frac<T>
156where
157 T: Integer + Copy,
158{
159 fn div_assign(&mut self, rhs: Self) {
160 *self = *self / rhs;
161 }
162}
163
164impl<T> Neg for Frac<T>
165where
166 T: Integer + Copy + Neg<Output = T>,
167{
168 type Output = Self;
169 fn neg(self) -> Self::Output {
170 let Frac(a, b) = self;
171 Self::new(-a, b)
172 }
173}
174
175impl<T> Add<T> for Frac<T>
176where
177 T: Integer + Copy,
178{
179 type Output = Self;
180 fn add(self, rhs: T) -> Self::Output {
181 self + Self::from(rhs)
182 }
183}
184
185impl<T> Sub<T> for Frac<T>
186where
187 T: Integer + Copy,
188{
189 type Output = Self;
190 fn sub(self, rhs: T) -> Self::Output {
191 self - Self::from(rhs)
192 }
193}
194
195impl<T> Mul<T> for Frac<T>
196where
197 T: Integer + Copy,
198{
199 type Output = Self;
200 fn mul(self, rhs: T) -> Self::Output {
201 let Frac(a, b) = self;
202 Self::new(a * rhs, b)
203 }
204}
205
206impl<T> Div<T> for Frac<T>
207where
208 T: Integer + Copy,
209{
210 type Output = Self;
211 fn div(self, rhs: T) -> Self::Output {
212 assert!(!rhs.is_zero(), "division by zero scalar");
213 let Frac(a, b) = self;
214 Self::new(a, b * rhs)
215 }
216}
217
218impl<T> AddAssign<T> for Frac<T>
219where
220 T: Integer + Copy,
221{
222 fn add_assign(&mut self, rhs: T) {
223 *self = *self + rhs;
224 }
225}
226
227impl<T> SubAssign<T> for Frac<T>
228where
229 T: Integer + Copy,
230{
231 fn sub_assign(&mut self, rhs: T) {
232 *self = *self - rhs;
233 }
234}
235
236impl<T> MulAssign<T> for Frac<T>
237where
238 T: Integer + Copy,
239{
240 fn mul_assign(&mut self, rhs: T) {
241 *self = *self * rhs;
242 }
243}
244
245impl<T> DivAssign<T> for Frac<T>
246where
247 T: Integer + Copy,
248{
249 fn div_assign(&mut self, rhs: T) {
250 *self = *self / rhs;
251 }
252}
253
254impl<T> PartialEq for Frac<T>
255where
256 T: Integer + Copy,
257{
258 fn eq(&self, other: &Self) -> bool {
259 let &Frac(a1, b1) = self;
260 let &Frac(a2, b2) = other;
261 a1 * b2 == a2 * b1
262 }
263}
264
265impl<T> Eq for Frac<T>
266where
267 T: Integer + Copy,
268{
269 fn assert_receiver_is_total_eq(&self) {}
270}
271
272impl<T> PartialOrd for Frac<T>
273where
274 T: Integer + Copy,
275{
276 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
277 Some(self.cmp(other))
278 }
279}
280
281impl<T> Ord for Frac<T>
282where
283 T: Integer + Copy,
284{
285 fn cmp(&self, other: &Self) -> Ordering {
286 let &Frac(a1, b1) = self;
287 let &Frac(a2, b2) = other;
288 (a1 * b2).cmp(&(a2 * b1))
289 }
290}