feanor_math/
pid.rs

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