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}