mathhook_core/core/polynomial/
traits.rs1use num_rational::Ratio;
6use std::fmt::Debug;
7use std::ops::{Add, Div, Mul, Neg, Rem, Sub};
8
9pub trait Ring:
19 Sized
20 + Clone
21 + PartialEq
22 + Debug
23 + Add<Output = Self>
24 + Sub<Output = Self>
25 + Mul<Output = Self>
26 + Neg<Output = Self>
27{
28 fn zero() -> Self;
29 fn one() -> Self;
30 fn is_zero(&self) -> bool;
31 fn is_one(&self) -> bool;
32
33 #[inline(always)]
34 fn wrapping_add(&self, other: &Self) -> Self {
35 self.clone() + other.clone()
36 }
37
38 #[inline(always)]
39 fn wrapping_sub(&self, other: &Self) -> Self {
40 self.clone() - other.clone()
41 }
42
43 #[inline(always)]
44 fn wrapping_mul(&self, other: &Self) -> Self {
45 self.clone() * other.clone()
46 }
47}
48
49pub trait EuclideanDomain: Ring + Div<Output = Self> + Rem<Output = Self> {
57 fn div_rem(&self, other: &Self) -> (Self, Self);
65
66 fn gcd(&self, other: &Self) -> Self {
73 let mut a = self.clone();
74 let mut b = other.clone();
75 while !b.is_zero() {
76 let (_, r) = a.div_rem(&b);
77 a = b;
78 b = r;
79 }
80 a
81 }
82
83 fn abs(&self) -> Self;
85}
86
87pub trait Field: EuclideanDomain {
93 fn inv(&self) -> Option<Self>;
97}
98
99impl Ring for i64 {
100 #[inline(always)]
101 fn zero() -> Self {
102 0
103 }
104
105 #[inline(always)]
106 fn one() -> Self {
107 1
108 }
109
110 #[inline(always)]
111 fn is_zero(&self) -> bool {
112 *self == 0
113 }
114
115 #[inline(always)]
116 fn is_one(&self) -> bool {
117 *self == 1
118 }
119
120 #[inline(always)]
121 fn wrapping_add(&self, other: &Self) -> Self {
122 i64::wrapping_add(*self, *other)
123 }
124
125 #[inline(always)]
126 fn wrapping_sub(&self, other: &Self) -> Self {
127 i64::wrapping_sub(*self, *other)
128 }
129
130 #[inline(always)]
131 fn wrapping_mul(&self, other: &Self) -> Self {
132 i64::wrapping_mul(*self, *other)
133 }
134}
135
136impl EuclideanDomain for i64 {
137 #[inline(always)]
138 fn div_rem(&self, other: &Self) -> (Self, Self) {
139 (*self / *other, *self % *other)
140 }
141
142 #[inline(always)]
143 fn abs(&self) -> Self {
144 i64::abs(*self)
145 }
146}
147
148impl Ring for Ratio<i64> {
149 #[inline(always)]
150 fn zero() -> Self {
151 Ratio::new(0, 1)
152 }
153
154 #[inline(always)]
155 fn one() -> Self {
156 Ratio::new(1, 1)
157 }
158
159 #[inline(always)]
160 fn is_zero(&self) -> bool {
161 self.numer() == &0
162 }
163
164 #[inline(always)]
165 fn is_one(&self) -> bool {
166 self.numer() == &1 && self.denom() == &1
167 }
168}
169
170impl EuclideanDomain for Ratio<i64> {
171 #[inline(always)]
172 fn div_rem(&self, other: &Self) -> (Self, Self) {
173 (self / other, Ratio::zero())
174 }
175
176 #[inline(always)]
177 fn abs(&self) -> Self {
178 Ratio::new(self.numer().abs(), *self.denom())
179 }
180}
181
182impl Field for Ratio<i64> {
183 #[inline]
184 fn inv(&self) -> Option<Self> {
185 if self.is_zero() {
186 None
187 } else {
188 Some(self.recip())
189 }
190 }
191}
192
193#[cfg(test)]
194mod tests {
195 use super::*;
196
197 #[test]
198 fn test_i64_ring_properties() {
199 assert_eq!(i64::zero(), 0);
200 assert_eq!(i64::one(), 1);
201 assert!(0i64.is_zero());
202 assert!(1i64.is_one());
203 assert!(!5i64.is_zero());
204 assert!(!5i64.is_one());
205 }
206
207 #[test]
208 fn test_i64_euclidean_domain() {
209 let (q, r) = 17i64.div_rem(&5);
210 assert_eq!(q, 3);
211 assert_eq!(r, 2);
212 assert_eq!(17, 3 * 5 + 2);
213 }
214
215 #[test]
216 fn test_i64_gcd() {
217 assert_eq!(12i64.gcd(&18), 6);
218 assert_eq!(17i64.gcd(&19), 1);
219 assert_eq!(0i64.gcd(&5), 5);
220 assert_eq!(5i64.gcd(&0), 5);
221 }
222
223 #[test]
224 fn test_i64_abs() {
225 assert_eq!((-5i64).abs(), 5);
226 assert_eq!(5i64.abs(), 5);
227 assert_eq!(0i64.abs(), 0);
228 }
229
230 #[test]
231 fn test_wrapping_arithmetic() {
232 let a = i64::MAX;
233 let b = 1i64;
234 let sum = Ring::wrapping_add(&a, &b);
235 assert_eq!(sum, i64::MIN);
236 }
237
238 #[test]
239 fn test_ratio_ring_properties() {
240 assert_eq!(Ratio::<i64>::zero(), Ratio::new(0, 1));
241 assert_eq!(Ratio::<i64>::one(), Ratio::new(1, 1));
242 assert!(Ratio::new(0, 1).is_zero());
243 assert!(Ratio::new(1, 1).is_one());
244 assert!(!Ratio::new(5, 1).is_zero());
245 assert!(!Ratio::new(5, 1).is_one());
246 }
247
248 #[test]
249 fn test_ratio_arithmetic() {
250 let a = Ratio::new(1, 2);
251 let b = Ratio::new(1, 3);
252 let sum = a + b;
253 assert_eq!(sum, Ratio::new(5, 6));
254
255 let diff = a - b;
256 assert_eq!(diff, Ratio::new(1, 6));
257
258 let prod = a * b;
259 assert_eq!(prod, Ratio::new(1, 6));
260
261 let quot = a / b;
262 assert_eq!(quot, Ratio::new(3, 2));
263 }
264
265 #[test]
266 fn test_ratio_euclidean_domain() {
267 let a = Ratio::new(5, 2);
268 let b = Ratio::new(3, 4);
269 let (q, r) = a.div_rem(&b);
270 assert_eq!(q, Ratio::new(10, 3));
271 assert_eq!(r, Ratio::zero());
272 }
273
274 #[test]
275 fn test_ratio_abs() {
276 assert_eq!(Ratio::new(-5, 2).abs(), Ratio::new(5, 2));
277 assert_eq!(Ratio::new(5, 2).abs(), Ratio::new(5, 2));
278 assert_eq!(Ratio::new(0, 1).abs(), Ratio::new(0, 1));
279 }
280
281 #[test]
282 fn test_ratio_field() {
283 let a = Ratio::new(2, 3);
284 let inv = a.inv().unwrap();
285 assert_eq!(inv, Ratio::new(3, 2));
286 assert_eq!(a * inv, Ratio::one());
287
288 assert!(Ratio::zero().inv().is_none());
289 }
290}