feanor_math/
pid.rs

1use crate::computation::ComputationController;
2use crate::ring::*;
3use crate::divisibility::*;
4
5///
6/// Trait for rings that are principal ideal rings, i.e. every ideal is generated
7/// by a single element. 
8/// 
9/// A principal ideal ring currently must be commutative, since otherwise we would
10/// have to distinguish left-, right-and two-sided ideals.
11/// 
12pub trait PrincipalIdealRing: DivisibilityRing {
13
14    ///
15    /// Similar to [`DivisibilityRing::checked_left_div()`] this computes a "quotient" `q`
16    /// of `lhs` and `rhs`, if it exists. However, we impose the additional constraint
17    /// that this quotient be minimal, i.e. there is no `q'` with `q' | q` properly and
18    /// `q' * rhs = lhs`.
19    /// 
20    /// In domains, this is always satisfied, i.e. this function behaves exactly like
21    /// [`DivisibilityRing::checked_left_div()`]. However, if there are zero-divisors, weird
22    /// things can happen. For example in `Z/6Z`, `checked_div(4, 4)` may return `4`, however
23    /// this is not minimal since `1 | 4` and `1 * 4 = 4`.
24    /// 
25    /// In particular, this function can be used to compute a generator of the annihilator ideal
26    /// of an element `x`, by using it as `checked_div_min(0, x)`.
27    /// 
28    fn checked_div_min(&self, lhs: &Self::Element, rhs: &Self::Element) -> Option<Self::Element>;
29
30    ///
31    /// Returns the (w.r.t. divisibility) smallest element `x` such that `x * val = 0`.
32    /// 
33    /// If the ring is a domain, this returns `0` for all ring elements except zero (for
34    /// which it returns any unit).
35    /// 
36    /// # Example
37    /// ```rust
38    /// # use feanor_math::assert_el_eq;
39    /// # use feanor_math::ring::*;
40    /// # use feanor_math::pid::*;
41    /// # use feanor_math::homomorphism::*;
42    /// # use feanor_math::rings::zn::zn_64::*;
43    /// let Z6 = Zn::new(6);
44    /// assert_el_eq!(Z6, Z6.int_hom().map(3), Z6.annihilator(&Z6.int_hom().map(2)));
45    /// ```
46    /// 
47    fn annihilator(&self, val: &Self::Element) -> Self::Element {
48        self.checked_div_min(&self.zero(), val).unwrap()
49    }
50
51    ///
52    /// Computes a Bezout identity for the generator `g` of the ideal `(lhs, rhs)`
53    /// as `g = s * lhs + t * rhs`.
54    /// 
55    /// More concretely, this returns (s, t, g) such that g is a generator 
56    /// of the ideal `(lhs, rhs)` and `g = s * lhs + t * rhs`. This `g` is also known
57    /// as the greatest common divisor of `lhs` and `rhs`, since `g | lhs, rhs` and
58    /// for every `g'` with this property, have `g' | g`. Note that this `g` is only
59    /// unique up to multiplication by units.
60    /// 
61    fn extended_ideal_gen(&self, lhs: &Self::Element, rhs: &Self::Element) -> (Self::Element, Self::Element, Self::Element);
62
63    ///
64    /// Creates a matrix `A` of unit determinant such that `A * (a, b)^T = (d, 0)`.
65    /// Returns `(A, d)`.
66    /// 
67    #[stability::unstable(feature = "enable")]
68    fn create_elimination_matrix(&self, a: &Self::Element, b: &Self::Element) -> ([Self::Element; 4], Self::Element) {
69        assert!(self.is_commutative());
70        let old_gcd = self.ideal_gen(a, b);
71        let new_a = self.checked_div_min(a, &old_gcd).unwrap();
72        let new_b = self.checked_div_min(b, &old_gcd).unwrap();
73        let (s, t, gcd_new) = self.extended_ideal_gen(&new_a, &new_b);
74        debug_assert!(self.is_unit(&gcd_new));
75        
76        let subtract_factor = self.checked_left_div(&self.sub(self.mul_ref(b, &new_a), self.mul_ref(a, &new_b)), &gcd_new).unwrap();
77        // this has unit determinant and will map `(a, b)` to `(d, b * new_a - a * new_b)`; after a subtraction step, we are done
78        let mut result = [s, t, self.negate(new_b), new_a];
79    
80        let sub1 = self.mul_ref(&result[0], &subtract_factor);
81        self.sub_assign(&mut result[2], sub1);
82        let sub2 = self.mul_ref_fst(&result[1], subtract_factor);
83        self.sub_assign(&mut result[3], sub2);
84        debug_assert!(self.is_unit(&self.sub(self.mul_ref(&result[0], &result[3]), self.mul_ref(&result[1], &result[2]))));
85        return (result, gcd_new);
86    }
87    
88
89    ///
90    /// Computes a generator `g` of the ideal `(lhs, rhs) = (g)`, also known as greatest
91    /// common divisor.
92    /// 
93    /// If you require also a Bezout identiy, i.e. `g = s * lhs + t * rhs`, consider
94    /// using [`PrincipalIdealRing::extended_ideal_gen()`].
95    /// 
96    fn ideal_gen(&self, lhs: &Self::Element, rhs: &Self::Element) -> Self::Element {
97        self.extended_ideal_gen(lhs, rhs).2
98    }
99
100    ///
101    /// As [`PrincipalIdealRing::ideal_gen()`], this computes a generator of the ideal `(lhs, rhs)`.
102    /// However, it additionally accepts a [`ComputationController`] to customize the performed
103    /// computation.
104    /// 
105    fn ideal_gen_with_controller<Controller>(&self, lhs: &Self::Element, rhs: &Self::Element, _: Controller) -> Self::Element
106        where Controller: ComputationController
107    {
108        self.ideal_gen(lhs, rhs)
109    }
110
111    ///
112    /// Computes a generator of the ideal `(lhs) ∩ (rhs)`, also known as least common
113    /// multiple.
114    /// 
115    /// In other words, computes a ring element `g` such that `lhs, rhs | g` and for every
116    /// `g'` with this property, have `g | g'`. Note that such an `g` is only unique up to
117    /// multiplication by units.
118    /// 
119    fn lcm(&self, lhs: &Self::Element, rhs: &Self::Element) -> Self::Element {
120        self.checked_left_div(&self.mul_ref(lhs, rhs), &self.ideal_gen(lhs, rhs)).unwrap()
121    }
122}
123
124///
125/// Trait for [`RingStore`]s that store [`PrincipalIdealRing`]s. Mainly used
126/// to provide a convenient interface to the `PrincipalIdealRing`-functions.
127/// 
128pub trait PrincipalIdealRingStore: RingStore
129    where Self::Type: PrincipalIdealRing
130{
131    delegate!{ PrincipalIdealRing, fn checked_div_min(&self, lhs: &El<Self>, rhs: &El<Self>) -> Option<El<Self>> }
132    delegate!{ PrincipalIdealRing, fn extended_ideal_gen(&self, lhs: &El<Self>, rhs: &El<Self>) -> (El<Self>, El<Self>, El<Self>) }
133    delegate!{ PrincipalIdealRing, fn ideal_gen(&self, lhs: &El<Self>, rhs: &El<Self>) -> El<Self> }
134    delegate!{ PrincipalIdealRing, fn annihilator(&self, val: &El<Self>) -> El<Self> }
135    delegate!{ PrincipalIdealRing, fn lcm(&self, lhs: &El<Self>, rhs: &El<Self>) -> El<Self> }
136
137    ///
138    /// Alias for [`PrincipalIdealRingStore::ideal_gen()`].
139    /// 
140    fn gcd(&self, lhs: &El<Self>, rhs: &El<Self>) -> El<Self> {
141        self.ideal_gen(lhs, rhs)
142    }
143}
144
145impl<R> PrincipalIdealRingStore for R
146    where R: RingStore,
147        R::Type: PrincipalIdealRing
148{}
149
150///
151/// Trait for rings that support euclidean division.
152/// 
153/// In other words, there is a degree function d(.) 
154/// returning nonnegative integers such that for every `x, y` 
155/// with `y != 0` there are `q, r` with `x = qy + r` and 
156/// `d(r) < d(y)`. Note that `q, r` do not have to be unique, 
157/// and implementations are free to use any choice.
158/// 
159/// # Example
160/// ```rust
161/// # use feanor_math::assert_el_eq;
162/// # use feanor_math::ring::*;
163/// # use feanor_math::pid::*;
164/// # use feanor_math::primitive_int::*;
165/// let ring = StaticRing::<i64>::RING;
166/// let (q, r) = ring.euclidean_div_rem(14, &6);
167/// assert_el_eq!(ring, 14, ring.add(ring.mul(q, 6), r));
168/// assert!(ring.euclidean_deg(&r) < ring.euclidean_deg(&6));
169/// ```
170/// 
171pub trait EuclideanRing: PrincipalIdealRing {
172
173    ///
174    /// Computes euclidean division with remainder.
175    /// 
176    /// In general, the euclidean division of `lhs` by `rhs` is a tuple `(q, r)` such that
177    /// `lhs = q * rhs + r`, and `r` is "smaller" than "rhs". The notion of smallness is
178    /// given by the smallness of the euclidean degree function [`EuclideanRing::euclidean_deg()`].
179    /// 
180    fn euclidean_div_rem(&self, lhs: Self::Element, rhs: &Self::Element) -> (Self::Element, Self::Element);
181
182    ///
183    /// Defines how "small" an element is. For details, see [`EuclideanRing`].
184    /// 
185    fn euclidean_deg(&self, val: &Self::Element) -> Option<usize>;
186
187    ///
188    /// Computes euclidean division without remainder.
189    /// 
190    /// In general, the euclidean division of `lhs` by `rhs` is a tuple `(q, r)` such that
191    /// `lhs = q * rhs + r`, and `r` is "smaller" than "rhs". The notion of smallness is
192    /// given by the smallness of the euclidean degree function [`EuclideanRing::euclidean_deg()`].
193    /// 
194    fn euclidean_div(&self, lhs: Self::Element, rhs: &Self::Element) -> Self::Element {
195        self.euclidean_div_rem(lhs, rhs).0
196    }
197
198    ///
199    /// Computes only the remainder of euclidean division.
200    /// 
201    /// In general, the euclidean division of `lhs` by `rhs` is a tuple `(q, r)` such that
202    /// `lhs = q * rhs + r`, and `r` is "smaller" than "rhs". The notion of smallness is
203    /// given by the smallness of the euclidean degree function [`EuclideanRing::euclidean_deg()`].
204    /// 
205    fn euclidean_rem(&self, lhs: Self::Element, rhs: &Self::Element) -> Self::Element {
206        self.euclidean_div_rem(lhs, rhs).1
207    }
208}
209
210///
211/// [`RingStore`] for [`EuclideanRing`]s
212/// 
213pub trait EuclideanRingStore: RingStore + DivisibilityRingStore
214    where Self::Type: EuclideanRing
215{
216    delegate!{ EuclideanRing, fn euclidean_div_rem(&self, lhs: El<Self>, rhs: &El<Self>) -> (El<Self>, El<Self>) }
217    delegate!{ EuclideanRing, fn euclidean_div(&self, lhs: El<Self>, rhs: &El<Self>) -> El<Self> }
218    delegate!{ EuclideanRing, fn euclidean_rem(&self, lhs: El<Self>, rhs: &El<Self>) -> El<Self> }
219    delegate!{ EuclideanRing, fn euclidean_deg(&self, val: &El<Self>) -> Option<usize> }
220}
221
222impl<R> EuclideanRingStore for R
223    where R: RingStore, R::Type: EuclideanRing
224{}
225
226#[allow(missing_docs)]
227#[cfg(any(test, feature = "generic_tests"))]
228pub mod generic_tests {
229    use super::*;
230    use crate::{algorithms::int_factor::factor, homomorphism::Homomorphism, integer::{int_cast, BigIntRing, IntegerRingStore}, ordered::OrderedRingStore, primitive_int::StaticRing, ring::El};
231
232    pub fn test_euclidean_ring_axioms<R: RingStore, I: Iterator<Item = El<R>>>(ring: R, edge_case_elements: I) 
233        where R::Type: EuclideanRing
234    {
235        assert!(ring.is_commutative());
236        assert!(ring.is_noetherian());
237        let elements = edge_case_elements.collect::<Vec<_>>();
238        for a in &elements {
239            for b in &elements {
240                if ring.is_zero(b) {
241                    continue;
242                }
243                let (q, r) = ring.euclidean_div_rem(ring.clone_el(a), b);
244                assert!(ring.euclidean_deg(b).is_none() || ring.euclidean_deg(&r).unwrap_or(usize::MAX) < ring.euclidean_deg(b).unwrap());
245                assert_el_eq!(ring, a, ring.add(ring.mul(q, ring.clone_el(b)), r));
246            }
247        }
248    }
249
250    pub fn test_principal_ideal_ring_axioms<R: RingStore, I: Iterator<Item = El<R>>>(ring: R, edge_case_elements: I)
251        where R::Type: PrincipalIdealRing
252    {
253        assert!(ring.is_commutative());
254        assert!(ring.is_noetherian());
255        let elements = edge_case_elements.collect::<Vec<_>>();
256
257        let expected_unit = ring.checked_div_min(&ring.zero(), &ring.zero()).unwrap();
258        assert!(ring.is_unit(&expected_unit), "checked_div_min() returned a non-minimal quotient {} * {} = {}", ring.format(&expected_unit), ring.format(&ring.zero()), ring.format(&ring.zero()));
259        let expected_zero = ring.checked_div_min(&ring.zero(), &ring.one()).unwrap();
260        assert!(ring.is_zero(&expected_zero), "checked_div_min() returned a wrong quotient {} * {} = {}", ring.format(&expected_zero), ring.format(&ring.one()), ring.format(&ring.zero()));
261
262        for a in &elements {
263            let expected_unit = ring.checked_div_min(a, a).unwrap();
264            assert!(ring.is_unit(&expected_unit), "checked_div_min() returned a non-minimal quotient {} * {} = {}", ring.format(&expected_unit), ring.format(a), ring.format(a));
265        }
266
267        for a in &elements {
268            for b in &elements {
269                for c in &elements {
270                    let g1 = ring.mul_ref(a, b);
271                    let g2 = ring.mul_ref(a, c);
272                    let (s, t, g) = ring.extended_ideal_gen(&g1, &g2);
273                    assert!(ring.checked_div(&g, a).is_some(), "Wrong ideal generator: ({}) contains the ideal I = ({}, {}), but extended_ideal_gen() found a generator I = ({}) that does not satisfy {} | {}", ring.format(a), ring.format(&g1), ring.format(&g2), ring.format(&g), ring.format(a), ring.format(&g));
274                    assert_el_eq!(ring, g, ring.add(ring.mul_ref(&s, &g1), ring.mul_ref(&t, &g2)));
275                }
276            }
277        }
278        for a in &elements {
279            for b in &elements {
280                let g1 = ring.mul_ref(a, b);
281                let g2 = ring.mul_ref_fst(a, ring.add_ref_fst(b, ring.one()));
282                let (s, t, g) = ring.extended_ideal_gen(&g1, &g2);
283                assert!(ring.checked_div(&g, a).is_some() && ring.checked_div(a, &g).is_some(), "Expected ideals ({}) and I = ({}, {}) to be equal, but extended_ideal_gen() returned generator {} of I", ring.format(a), ring.format(&g1), ring.format(&g2), ring.format(&g));
284                assert_el_eq!(ring, g, ring.add(ring.mul_ref(&s, &g1), ring.mul_ref(&t, &g2)));
285            }
286        }
287
288        let ZZbig = BigIntRing::RING;
289        let char = ring.characteristic(ZZbig).unwrap();
290        if !ZZbig.is_zero(&char) && ZZbig.is_leq(&char, &ZZbig.power_of_two(30)) {
291            let p = factor(ZZbig, ZZbig.clone_el(&char)).into_iter().next().unwrap().0;
292            let expected = ring.int_hom().map(int_cast(ZZbig.checked_div(&char, &p).unwrap(), StaticRing::<i32>::RING, ZZbig));
293            let ann_p = ring.annihilator(&ring.int_hom().map(int_cast(p, StaticRing::<i32>::RING, ZZbig)));
294            assert!(ring.checked_div(&ann_p, &expected).is_some());
295            assert!(ring.checked_div(&expected, &ann_p).is_some());
296        }
297    }
298}