mathhook_core/core/polynomial/
traits.rs

1//! Algebraic trait hierarchy for polynomial coefficient types
2//!
3//! Defines Ring, EuclideanDomain, and Field traits to enable generic polynomial arithmetic.
4
5use num_rational::Ratio;
6use std::fmt::Debug;
7use std::ops::{Add, Div, Mul, Neg, Rem, Sub};
8
9/// Ring: addition, subtraction, multiplication with identity elements
10///
11/// A ring is an algebraic structure with two binary operations (+ and *)
12/// satisfying these properties:
13/// - Additive identity: exists 0 such that a + 0 = a
14/// - Multiplicative identity: exists 1 such that a * 1 = a
15/// - Additive inverse: for all a, exists -a such that a + (-a) = 0
16/// - Associative: (a + b) + c = a + (b + c), (a * b) * c = a * (b * c)
17/// - Distributive: a * (b + c) = a * b + a * c
18pub 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
49/// Euclidean domain: ring with division algorithm
50///
51/// A Euclidean domain is a ring where division with remainder is defined.
52/// Examples: integers Z, polynomials `K[x]` over field K, Gaussian integers `Z[i]`
53///
54/// Key property: For any a, b ≠ 0, exists q, r such that a = q*b + r
55/// where either r = 0 or deg(r) < deg(b)
56pub trait EuclideanDomain: Ring + Div<Output = Self> + Rem<Output = Self> {
57    /// Division with remainder: returns (quotient, remainder)
58    ///
59    /// # Example
60    /// ```ignore
61    /// let (q, r) = a.div_rem(&b);
62    /// assert_eq!(a, &(&q * &b) + &r);
63    /// ```
64    fn div_rem(&self, other: &Self) -> (Self, Self);
65
66    /// Greatest common divisor using Euclidean algorithm
67    ///
68    /// # Example
69    /// ```ignore
70    /// assert_eq!(gcd(12, 18), 6);
71    /// ```
72    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    /// Absolute value (for content extraction and normalization)
84    fn abs(&self) -> Self;
85}
86
87/// Field: Euclidean domain where every non-zero element has multiplicative inverse
88///
89/// Examples: rational numbers Q, real numbers R, finite fields Z_p (p prime)
90///
91/// Key property: For any a ≠ 0, exists a⁻¹ such that a * a⁻¹ = 1
92pub trait Field: EuclideanDomain {
93    /// Multiplicative inverse
94    ///
95    /// Returns None if element is zero
96    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}