Skip to main content

ocas_domain/
domain.rs

1//! Core domain trait for generic algebraic algorithms.
2//!
3//! A [`Domain`] describes a set of values together with the basic arithmetic
4//! operations needed by polynomial and matrix algorithms. Implementations are
5//! provided for integers, rationals, and finite fields.
6
7/// A coefficient domain for generic computer-algebra routines.
8///
9/// Domains describe operations on their elements. The domain object itself may
10/// carry parameters (such as a finite-field modulus), so every operation
11/// takes `&self`. This mirrors the conventional "domain object" pattern used
12/// by Flint, SymPy's `Domain`, and other CAS libraries.
13///
14/// # Example
15///
16/// ```
17/// use ocas_domain::{Domain, Integer, IntegerDomain};
18///
19/// let domain = IntegerDomain;
20/// let a = Integer::from(3);
21/// let b = Integer::from(5);
22/// assert_eq!(domain.add(&a, &b), Integer::from(8));
23/// assert_eq!(domain.mul(&a, &b), Integer::from(15));
24/// ```
25pub trait Domain: Clone + PartialEq + Eq + std::fmt::Debug + Sized {
26    /// The type of elements in the domain.
27    type Element: Clone + PartialEq + Eq + std::fmt::Debug;
28
29    /// The additive identity.
30    fn zero(&self) -> Self::Element;
31
32    /// The multiplicative identity.
33    fn one(&self) -> Self::Element;
34
35    /// Add two elements.
36    fn add(&self, a: &Self::Element, b: &Self::Element) -> Self::Element;
37
38    /// Subtract `b` from `a`.
39    fn sub(&self, a: &Self::Element, b: &Self::Element) -> Self::Element;
40
41    /// Negate an element.
42    fn neg(&self, a: &Self::Element) -> Self::Element;
43
44    /// Multiply two elements.
45    fn mul(&self, a: &Self::Element, b: &Self::Element) -> Self::Element;
46
47    /// Divide `a` by `b`.
48    ///
49    /// Returns `None` if division is not exact or `b` is zero.
50    fn div(&self, a: &Self::Element, b: &Self::Element) -> Option<Self::Element>;
51
52    /// Return the multiplicative inverse of `a`.
53    ///
54    /// Returns `None` if `a` is zero.
55    fn inv(&self, a: &Self::Element) -> Option<Self::Element>;
56
57    /// Test whether an element is the additive identity.
58    fn is_zero(&self, a: &Self::Element) -> bool {
59        *a == self.zero()
60    }
61
62    /// Test whether an element is the multiplicative identity.
63    fn is_one(&self, a: &Self::Element) -> bool {
64        *a == self.one()
65    }
66
67    /// In-place multiply: `*a *= b`.
68    ///
69    /// The default implementation creates a new element via [`mul`](Self::mul).
70    /// Domains with in-place arithmetic (e.g. finite fields backed by GMP)
71    /// may override this for better performance in hot loops such as the
72    /// F4 row-reduction step.
73    fn mul_assign(&self, a: &mut Self::Element, b: &Self::Element) {
74        *a = self.mul(a, b);
75    }
76
77    /// Fused subtract-multiply: `*a -= b * c`.
78    ///
79    /// Equivalent to `*a = self.sub(a, &self.mul(b, c))` but potentially
80    /// faster when the domain can avoid an intermediate allocation.
81    /// Used extensively in the F4 echelonize step.
82    fn sub_mul_assign(&self, a: &mut Self::Element, b: &Self::Element, c: &Self::Element) {
83        let bc = self.mul(b, c);
84        *a = self.sub(a, &bc);
85    }
86
87    /// Return `a` raised to the non-negative integer power `n`.
88    ///
89    /// The default implementation uses binary exponentiation. Domains that
90    /// can do better (e.g. modular exponentiation) may override it.
91    fn pow(&self, a: &Self::Element, n: u64) -> Self::Element {
92        let mut base = a.clone();
93        let mut result = self.one();
94        let mut exp = n;
95        while exp > 0 {
96            if exp & 1 == 1 {
97                result = self.mul(&result, &base);
98            }
99            base = self.mul(&base, &base);
100            exp >>= 1;
101        }
102        result
103    }
104
105    /// Convert a `u64` into an element of the domain.
106    ///
107    /// This is used by generic algorithms (e.g. polynomial differentiation)
108    /// that need small positive integer coefficients. Domains that cannot
109    /// represent every `u64` may wrap or truncate as appropriate for their
110    /// semantics.
111    fn cast_u64(&self, n: u64) -> Self::Element {
112        let mut result = self.zero();
113        let one = self.one();
114        for _ in 0..n {
115            result = self.add(&result, &one);
116        }
117        result
118    }
119}
120
121/// Marker trait for domains that support exact division with remainder.
122///
123/// Euclidean domains provide `div_rem`, which returns the quotient and
124/// remainder. The remainder must satisfy `rem == 0` or `deg(rem) < deg(b)`.
125///
126/// # Example
127///
128/// ```
129/// use ocas_domain::{EuclideanDomain, Integer, IntegerDomain};
130///
131/// let domain = IntegerDomain;
132/// let a = Integer::from(17);
133/// let b = Integer::from(5);
134/// let (q, r) = domain.div_rem(&a, &b).unwrap();
135/// assert_eq!(q, Integer::from(3));
136/// assert_eq!(r, Integer::from(2));
137/// ```
138pub trait EuclideanDomain: Domain {
139    /// Divide `a` by `b` returning `(quotient, remainder)`.
140    ///
141    /// Returns `None` if `b` is zero.
142    fn div_rem(
143        &self,
144        a: &Self::Element,
145        b: &Self::Element,
146    ) -> Option<(Self::Element, Self::Element)>;
147
148    /// Compute the greatest common divisor of `a` and `b`.
149    ///
150    /// The default implementation uses the Euclidean algorithm.
151    fn gcd(&self, a: &Self::Element, b: &Self::Element) -> Self::Element {
152        let mut a = a.clone();
153        let mut b = b.clone();
154        while !self.is_zero(&b) {
155            if let Some((_, r)) = self.div_rem(&a, &b) {
156                a = b;
157                b = r;
158            } else {
159                break;
160            }
161        }
162        a
163    }
164
165    /// Compute the extended GCD: returns `(g, x, y)` such that
166    /// `g = gcd(a, b) = a*x + b*y`.
167    ///
168    /// The default implementation uses the extended Euclidean algorithm.
169    fn extended_gcd(
170        &self,
171        a: &Self::Element,
172        b: &Self::Element,
173    ) -> (Self::Element, Self::Element, Self::Element) {
174        let mut old_r = a.clone();
175        let mut r = b.clone();
176        let mut old_s = self.one();
177        let mut s = self.zero();
178        let mut old_t = self.zero();
179        let mut t = self.one();
180
181        while !self.is_zero(&r) {
182            if let Some((q, rem)) = self.div_rem(&old_r, &r) {
183                old_r = r;
184                r = rem;
185
186                // s = old_s - q * s
187                let qs = self.mul(&q, &s);
188                let new_s = self.sub(&old_s, &qs);
189                old_s = s;
190                s = new_s;
191
192                // t = old_t - q * t
193                let qt = self.mul(&q, &t);
194                let new_t = self.sub(&old_t, &qt);
195                old_t = t;
196                t = new_t;
197            } else {
198                break;
199            }
200        }
201
202        (old_r, old_s, old_t)
203    }
204}