feanor_math/rings/
rational.rs

1use crate::algorithms::convolution::KaratsubaHint;
2use crate::algorithms::eea::{signed_gcd, signed_lcm};
3use crate::algorithms::matmul::StrassenHint;
4use crate::algorithms::poly_gcd::PolyTFracGCDRing;
5use crate::algorithms::poly_gcd::gcd::poly_gcd_local;
6use crate::algorithms::poly_gcd::squarefree_part::poly_power_decomposition_local;
7use crate::divisibility::{DivisibilityRing, DivisibilityRingStore, Domain};
8use crate::field::*;
9use crate::homomorphism::*;
10use crate::computation::DontObserve;
11use crate::integer::*;
12use crate::ordered::{OrderedRing, OrderedRingStore};
13use crate::rings::poly::dense_poly::DensePolyRing;
14use crate::rings::poly::*;
15use crate::impl_interpolation_base_ring_char_zero;
16use crate::pid::{EuclideanRing, PrincipalIdealRing};
17use crate::ring::*;
18
19use std::fmt::Debug;
20
21///
22/// An implementation of the rational number `Q`, based on representing them
23/// as a tuple `(numerator, denominator)`.
24/// 
25/// Be careful when instantiating it with finite-precision integers, like `StaticRing<i64>`,
26/// since by nature of the rational numbers, both numerator and denominator can increase
27/// dramatically, even when the numbers itself are of moderate size.
28/// 
29/// # Example
30/// ```
31/// # use feanor_math::assert_el_eq;
32/// # use feanor_math::ring::*;
33/// # use feanor_math::primitive_int::*;
34/// # use feanor_math::rings::rational::*;
35/// # use feanor_math::homomorphism::Homomorphism;
36/// # use feanor_math::field::FieldStore;
37/// let ZZ = StaticRing::<i64>::RING;
38/// let QQ = RationalField::new(ZZ);
39/// let hom = QQ.can_hom(&ZZ).unwrap();
40/// let one_half = QQ.div(&QQ.one(), &hom.map(2));
41/// assert_el_eq!(QQ, QQ.div(&QQ.one(), &hom.map(4)), QQ.pow(one_half, 2));
42/// ```
43/// You can also retrieve numerator and denominator.
44/// ```
45/// # use feanor_math::assert_el_eq;
46/// # use feanor_math::ring::*;
47/// # use feanor_math::primitive_int::*;
48/// # use feanor_math::rings::rational::*;
49/// # use feanor_math::homomorphism::Homomorphism;
50/// # use feanor_math::field::FieldStore;
51/// # let ZZ = StaticRing::<i64>::RING;
52/// # let QQ = RationalField::new(ZZ);
53/// # let hom = QQ.can_hom(&ZZ).unwrap();
54/// # let one_half = QQ.div(&QQ.one(), &hom.map(2));
55/// assert_el_eq!(ZZ, ZZ.int_hom().map(1), QQ.num(&one_half));
56/// assert_el_eq!(ZZ, ZZ.int_hom().map(2), QQ.den(&one_half));
57/// ```
58/// 
59#[derive(Debug)]
60pub struct RationalFieldBase<I: RingStore>
61    where I::Type: IntegerRing
62{
63    integers: I
64}
65
66impl<I> Clone for RationalFieldBase<I>
67    where I: RingStore + Clone,
68        I::Type: IntegerRing
69{
70    fn clone(&self) -> Self {
71        Self {
72            integers: self.integers.clone()
73        }
74    }
75}
76
77impl<I> Copy for RationalFieldBase<I>
78    where I: RingStore + Copy,
79        I::Type: IntegerRing,
80        El<I>: Copy
81{}
82
83///
84/// [`RingStore`] corresponding to [`RationalFieldBase`]
85/// 
86pub type RationalField<I> = RingValue<RationalFieldBase<I>>;
87
88pub struct RationalFieldEl<I>(El<I>, El<I>)
89    where I: RingStore,
90        I::Type: IntegerRing;
91
92impl<I> Debug for RationalFieldEl<I>
93    where I: RingStore,
94        I::Type: IntegerRing,
95        El<I>: Debug
96{
97    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
98        write!(f, "RationalFieldEl({:?}, {:?})", &self.0, &self.1)
99    }
100}
101
102impl<I> Clone for RationalFieldEl<I>
103    where I: RingStore,
104        I::Type: IntegerRing,
105        El<I>: Clone
106{
107    fn clone(&self) -> Self {
108        RationalFieldEl(self.0.clone(), self.1.clone())
109    }
110}
111
112impl<I> Copy for RationalFieldEl<I>
113    where I: RingStore,
114        I::Type: IntegerRing,
115        El<I>: Copy
116{}
117
118impl<I> PartialEq for RationalFieldBase<I>
119    where I: RingStore,
120        I::Type: IntegerRing
121{
122    fn eq(&self, other: &Self) -> bool {
123        self.integers.get_ring() == other.integers.get_ring()
124    }
125}
126
127impl<I> RationalFieldBase<I>
128    where I: RingStore,
129        I::Type: IntegerRing
130{
131    ///
132    /// The numerator of the fully reduced fraction.
133    /// 
134    /// # Example
135    /// ```
136    /// # use feanor_math::assert_el_eq;
137    /// # use feanor_math::ring::*;
138    /// # use feanor_math::primitive_int::*;
139    /// # use feanor_math::rings::rational::*;
140    /// # use feanor_math::homomorphism::Homomorphism;
141    /// # use feanor_math::field::FieldStore;
142    /// # let ZZ = StaticRing::<i64>::RING;
143    /// # let QQ = RationalField::new(ZZ);
144    /// assert_el_eq!(ZZ, 2, QQ.num(&QQ.div(&QQ.inclusion().map(6), &QQ.inclusion().map(3))));
145    /// ```
146    /// 
147    pub fn num<'a>(&'a self, el: &'a <Self as RingBase>::Element) -> &'a El<I> {
148        debug_assert!(self.base_ring().is_one(&signed_gcd(self.base_ring().clone_el(&el.1), self.base_ring().clone_el(&el.0), self.base_ring())));
149        &el.0
150    }
151
152    ///
153    /// The denominator of the fully reduced fraction.
154    /// 
155    /// # Example
156    /// ```
157    /// # use feanor_math::assert_el_eq;
158    /// # use feanor_math::ring::*;
159    /// # use feanor_math::primitive_int::*;
160    /// # use feanor_math::rings::rational::*;
161    /// # use feanor_math::homomorphism::Homomorphism;
162    /// # use feanor_math::field::FieldStore;
163    /// # let ZZ = StaticRing::<i64>::RING;
164    /// # let QQ = RationalField::new(ZZ);
165    /// assert_el_eq!(ZZ, 3, QQ.den(&QQ.div(&QQ.inclusion().map(3), &QQ.inclusion().map(9))));
166    /// ```
167    /// 
168    pub fn den<'a>(&'a self, el: &'a <Self as RingBase>::Element) -> &'a El<I> {
169        debug_assert!(self.base_ring().is_one(&signed_gcd(self.base_ring().clone_el(&el.1), self.base_ring().clone_el(&el.0), self.base_ring())));
170        &el.1
171    }
172}
173
174impl<I> RationalField<I>
175    where I: RingStore,
176        I::Type: IntegerRing
177{
178    pub const fn new(integers: I) -> Self {
179        RingValue::from(RationalFieldBase { integers })
180    }
181
182    ///
183    /// See [`RationalFieldBase::num()`].
184    /// 
185    pub fn num<'a>(&'a self, el: &'a El<Self>) -> &'a El<I> {
186        self.get_ring().num(el)
187    }
188
189    ///
190    /// See [`RationalFieldBase::den()`].
191    /// 
192    pub fn den<'a>(&'a self, el: &'a El<Self>) -> &'a El<I> {
193        self.get_ring().den(el)
194    }
195}
196
197impl<I> RationalFieldBase<I>
198    where I: RingStore,
199        I::Type: IntegerRing
200{
201    fn reduce(&self, value: (&mut El<I>, &mut El<I>)) {
202        // take the denominator first, as in this case gcd will have the same sign, and the final denominator will be positive
203        let gcd = signed_gcd(self.integers.clone_el(&*value.1), self.integers.clone_el(&*value.0), &self.integers);
204        *value.0 = self.integers.checked_div(&*value.0, &gcd).unwrap();
205        *value.1 = self.integers.checked_div(&*value.1, &gcd).unwrap();
206    }
207
208    fn mul_assign_raw(&self, lhs: &mut <Self as RingBase>::Element, rhs: (&El<I>, &El<I>)) {
209        self.integers.mul_assign_ref(&mut lhs.0, rhs.0);
210        self.integers.mul_assign_ref(&mut lhs.1, rhs.1);
211        self.reduce((&mut lhs.0, &mut lhs.1));
212    }
213}
214
215impl<I> RingBase for RationalFieldBase<I>
216    where I: RingStore,
217        I::Type: IntegerRing
218{
219    type Element = RationalFieldEl<I>;
220
221    fn add_assign(&self, lhs: &mut Self::Element, mut rhs: Self::Element) {
222        if self.integers.is_one(&lhs.1) && self.integers.is_one(&rhs.1) {
223            self.integers.add_assign(&mut lhs.0, rhs.0);
224        } else {
225            self.integers.mul_assign_ref(&mut lhs.0, &rhs.1);
226            self.integers.mul_assign_ref(&mut rhs.0, &lhs.1);
227            self.integers.mul_assign(&mut lhs.1, rhs.1);
228            self.integers.add_assign(&mut lhs.0, rhs.0);
229            self.reduce((&mut lhs.0, &mut lhs.1));
230        }
231    }
232
233    fn clone_el(&self, val: &Self::Element) -> Self::Element {
234        RationalFieldEl(self.integers.clone_el(&val.0), self.integers.clone_el(&val.1))
235    }
236
237    fn add_assign_ref(&self, lhs: &mut Self::Element, rhs: &Self::Element) {
238        if self.integers.is_one(&lhs.1) && self.integers.is_one(&rhs.1) {
239            self.integers.add_assign_ref(&mut lhs.0, &rhs.0);
240        } else {
241            self.integers.mul_assign_ref(&mut lhs.0, &rhs.1);
242            self.integers.add_assign(&mut lhs.0, self.integers.mul_ref(&lhs.1, &rhs.0));
243            self.integers.mul_assign_ref(&mut lhs.1, &rhs.1);
244            self.reduce((&mut lhs.0, &mut lhs.1));
245        }
246    }
247
248    fn mul_assign_ref(&self, lhs: &mut Self::Element, rhs: &Self::Element) {
249        self.mul_assign_raw(lhs, (&rhs.0, &rhs.1))
250    }
251
252    fn mul_assign(&self, lhs: &mut Self::Element, rhs: Self::Element) {
253        self.integers.mul_assign(&mut lhs.0, rhs.0);
254        self.integers.mul_assign(&mut lhs.1, rhs.1);
255        self.reduce((&mut lhs.0, &mut lhs.1));
256    }
257
258    fn negate_inplace(&self, lhs: &mut Self::Element) {
259        self.integers.negate_inplace(&mut lhs.0);
260    }
261
262    fn eq_el(&self, lhs: &Self::Element, rhs: &Self::Element) -> bool {
263        self.integers.eq_el(&self.integers.mul_ref(&lhs.0, &rhs.1), &self.integers.mul_ref(&lhs.1, &rhs.0))
264    }
265
266    fn is_zero(&self, value: &Self::Element) -> bool {
267        self.integers.is_zero(&value.0)
268    }
269
270    fn is_one(&self, value: &Self::Element) -> bool {
271        self.integers.eq_el(&value.0, &value.1)
272    }
273
274    fn is_neg_one(&self, value: &Self::Element) -> bool {
275        self.integers.eq_el(&value.0, &self.integers.negate(self.integers.clone_el(&value.1)))
276    }
277
278    fn is_approximate(&self) -> bool {
279        false
280    }
281
282    fn is_commutative(&self) -> bool {
283        true
284    }
285
286    fn is_noetherian(&self) -> bool {
287        true
288    }
289
290    fn characteristic<J: RingStore + Copy>(&self, ZZ: J) -> Option<El<J>>
291        where J::Type: IntegerRing
292    {
293        Some(ZZ.zero())
294    }
295
296    fn dbg_within<'a>(&self, value: &Self::Element, out: &mut std::fmt::Formatter<'a>, env: EnvBindingStrength) -> std::fmt::Result {
297        if self.base_ring().is_one(&value.1) {
298            write!(out, "{}", self.integers.format(&value.0))
299        } else {
300            if env > EnvBindingStrength::Product {
301                write!(out, "({}/{})", self.integers.format(&value.0), self.integers.format(&value.1))
302            } else {
303                write!(out, "{}/{}", self.integers.format(&value.0), self.integers.format(&value.1))
304            }
305        }
306    }
307
308    fn from_int(&self, value: i32) -> Self::Element {
309        RationalFieldEl(self.integers.get_ring().from_int(value), self.integers.one())
310    }
311}
312
313impl<I: RingStore> StrassenHint for RationalFieldBase<I>
314    where I::Type: IntegerRing
315{
316    default fn strassen_threshold(&self) -> usize {
317        usize::MAX
318    }
319}
320
321impl<I: RingStore> KaratsubaHint for RationalFieldBase<I>
322    where I::Type: IntegerRing
323{
324    default fn karatsuba_threshold(&self) -> usize {
325        usize::MAX
326    }
327}
328
329impl<I> RingExtension for RationalFieldBase<I>
330    where I: RingStore,
331        I::Type: IntegerRing
332{
333    type BaseRing = I;
334
335    fn base_ring<'a>(&'a self) -> &'a Self::BaseRing {
336        &self.integers
337    }
338
339    fn from(&self, x: El<Self::BaseRing>) -> Self::Element {
340        RationalFieldEl(x, self.integers.one())
341    }
342
343    fn mul_assign_base(&self, lhs: &mut Self::Element, rhs: &El<Self::BaseRing>) {
344        self.integers.mul_assign_ref(&mut lhs.0, rhs);
345        self.reduce((&mut lhs.0, &mut lhs.1));
346    }
347}
348
349impl<I, J> CanHomFrom<RationalFieldBase<J>> for RationalFieldBase<I>
350    where I: RingStore,
351        I::Type: IntegerRing,
352        J: RingStore,
353        J::Type: IntegerRing
354{
355    type Homomorphism = ();
356
357    fn has_canonical_hom(&self, _from: &RationalFieldBase<J>) -> Option<Self::Homomorphism> {
358        Some(())
359    }
360
361    fn map_in(&self, from: &RationalFieldBase<J>, el: <RationalFieldBase<J> as RingBase>::Element, (): &Self::Homomorphism) -> Self::Element {
362        RationalFieldEl(int_cast(el.0, self.base_ring(), from.base_ring()), int_cast(el.1, self.base_ring(), from.base_ring()))
363    }
364}
365
366impl<I, J> CanIsoFromTo<RationalFieldBase<J>> for RationalFieldBase<I>
367    where I: RingStore,
368        I::Type: IntegerRing,
369        J: RingStore,
370        J::Type: IntegerRing
371{
372    type Isomorphism = ();
373
374    fn has_canonical_iso(&self, _from: &RationalFieldBase<J>) -> Option<Self::Isomorphism> {
375        Some(())
376    }
377
378    fn map_out(&self, from: &RationalFieldBase<J>, el: Self::Element, (): &Self::Homomorphism) -> <RationalFieldBase<J> as RingBase>::Element {
379        RationalFieldEl(int_cast(el.0, from.base_ring(), self.base_ring()), int_cast(el.1, from.base_ring(), self.base_ring()))
380    }
381}
382
383impl<I, J> CanHomFrom<J> for RationalFieldBase<I>
384    where I: RingStore,
385        I::Type: IntegerRing,
386        J: IntegerRing
387{
388    type Homomorphism = ();
389
390    fn has_canonical_hom(&self, _from: &J) -> Option<Self::Homomorphism> {
391        Some(())
392    }
393
394    fn map_in(&self, from: &J, el: <J as RingBase>::Element, (): &Self::Homomorphism) -> Self::Element {
395        RationalFieldEl(int_cast(el, self.base_ring(), &RingRef::new(from)), self.integers.one())
396    }
397}
398
399impl<I> DivisibilityRing for RationalFieldBase<I>
400    where I: RingStore,
401        I::Type: IntegerRing
402{
403    fn checked_left_div(&self, lhs: &Self::Element, rhs: &Self::Element) -> Option<Self::Element> {
404        if self.is_zero(lhs) && self.is_zero(rhs) {
405            Some(self.zero())
406        } else if self.is_zero(rhs) {
407            None
408        } else {
409            let mut result = self.clone_el(lhs);
410            self.mul_assign_raw(&mut result, (&rhs.1, &rhs.0));
411            Some(result)
412        }
413    }
414
415    fn is_unit(&self, x: &Self::Element) -> bool {
416        !self.is_zero(x)
417    }
418
419    fn balance_factor<'a, J>(&self, elements: J) -> Option<Self::Element>
420        where J: Iterator<Item = &'a Self::Element>,
421            Self:'a
422    {
423        let (num, den) = elements.fold(
424            (self.integers.zero(), self.integers.one()), 
425            |x, y| (signed_gcd(x.0, self.base_ring().clone_el(self.num(y)), self.base_ring()), signed_lcm(x.1, self.base_ring().clone_el(self.den(y)), self.base_ring())));
426        return Some(RationalFieldEl(num, den));
427    }
428}
429
430impl_interpolation_base_ring_char_zero!{ <{I}> InterpolationBaseRing for RationalFieldBase<I> where I: RingStore, I::Type: IntegerRing }
431
432impl<I> PrincipalIdealRing for RationalFieldBase<I>
433    where I: RingStore,
434        I::Type: IntegerRing
435{
436    fn checked_div_min(&self, lhs: &Self::Element, rhs: &Self::Element) -> Option<Self::Element> {
437        if self.is_zero(lhs) && self.is_zero(rhs) {
438            return Some(self.one());
439        }
440        self.checked_left_div(lhs, rhs)
441    }
442
443    fn extended_ideal_gen(&self, lhs: &Self::Element, rhs: &Self::Element) -> (Self::Element, Self::Element, Self::Element) {
444        if self.is_zero(lhs) && self.is_zero(rhs) {
445            return (self.zero(), self.zero(), self.zero());
446        } else if self.is_zero(lhs) {
447            return (self.zero(), self.one(), self.clone_el(rhs));
448        } else {
449            return (self.one(), self.zero(), self.clone_el(lhs));
450        }
451    }
452}
453
454impl<I> EuclideanRing for RationalFieldBase<I>
455    where I: RingStore,
456        I::Type: IntegerRing
457{
458    fn euclidean_deg(&self, val: &Self::Element) -> Option<usize> {
459        if self.is_zero(val) { Some(0) } else { Some(1) }
460    }
461
462    fn euclidean_div_rem(&self, lhs: Self::Element, rhs: &Self::Element) -> (Self::Element, Self::Element) {
463        assert!(!self.is_zero(rhs));
464        (self.checked_left_div(&lhs, rhs).unwrap(), self.zero())
465    }
466}
467
468impl<I> Domain for RationalFieldBase<I>
469    where I: RingStore,
470        I::Type: IntegerRing
471{}
472
473impl<I> PerfectField for RationalFieldBase<I>
474    where I: RingStore,
475        I::Type: IntegerRing
476{}
477
478impl<I> Field for RationalFieldBase<I>
479    where I: RingStore,
480        I::Type: IntegerRing
481{}
482
483impl<I> OrderedRing for RationalFieldBase<I>
484    where I: RingStore,
485        I::Type: IntegerRing
486{
487    fn cmp(&self, lhs: &Self::Element, rhs: &Self::Element) -> std::cmp::Ordering {
488        assert!(self.integers.is_pos(&lhs.1) && self.integers.is_pos(&rhs.1));
489        self.integers.cmp(&self.integers.mul_ref(&lhs.0, &rhs.1), &self.integers.mul_ref(&rhs.0, &lhs.1))
490    }
491}
492
493impl<I> PolyTFracGCDRing for RationalFieldBase<I>
494    where I: RingStore,
495        I::Type: IntegerRing
496{
497    fn power_decomposition<P>(poly_ring: P, poly: &El<P>) -> Vec<(El<P>, usize)>
498        where P: RingStore + Copy,
499            P::Type: PolyRing,
500            <P::Type as RingExtension>::BaseRing: RingStore<Type = Self>
501    {
502        assert!(!poly_ring.is_zero(poly));
503        let QQX = &poly_ring;
504        let QQ = QQX.base_ring();
505        let ZZ = QQ.base_ring();
506    
507        let den_lcm = QQX.terms(poly).map(|(c, _)| QQ.get_ring().den(c)).fold(ZZ.one(), |a, b| signed_lcm(a, ZZ.clone_el(b), ZZ));
508        
509        let ZZX = DensePolyRing::new(ZZ, "X");
510        let f = ZZX.from_terms(QQX.terms(poly).map(|(c, i)| (ZZ.checked_div(&ZZ.mul_ref(&den_lcm, QQ.get_ring().num(c)), QQ.get_ring().den(c)).unwrap(), i)));
511        let power_decomp = poly_power_decomposition_local(&ZZX, f, DontObserve);
512        let ZZX_to_QQX = QQX.lifted_hom(&ZZX, QQ.inclusion());
513    
514        return power_decomp.into_iter().map(|(f, k)| (QQX.normalize(ZZX_to_QQX.map(f)), k)).collect();
515    }
516    
517    fn gcd<P>(poly_ring: P, lhs: &El<P>, rhs: &El<P>) -> El<P>
518        where P: RingStore + Copy,
519            P::Type: PolyRing,
520            <P::Type as RingExtension>::BaseRing: RingStore<Type = Self>
521    {
522        if poly_ring.is_zero(lhs) {
523            return poly_ring.clone_el(rhs);
524        } else if poly_ring.is_zero(rhs) {
525            return poly_ring.clone_el(lhs);
526        }
527        let QQX = &poly_ring;
528        let QQ = QQX.base_ring();
529        let ZZ = QQ.base_ring();
530    
531        let den_lcm_lhs = QQX.terms(lhs).map(|(c, _)| QQ.get_ring().den(c)).fold(ZZ.one(), |a, b| signed_lcm(a, ZZ.clone_el(b), ZZ));
532        let den_lcm_rhs = QQX.terms(rhs).map(|(c, _)| QQ.get_ring().den(c)).fold(ZZ.one(), |a, b| signed_lcm(a, ZZ.clone_el(b), ZZ));
533        
534        let ZZX = DensePolyRing::new(ZZ, "X");
535        let lhs = ZZX.from_terms(QQX.terms(lhs).map(|(c, i)| (ZZ.checked_div(&ZZ.mul_ref(&den_lcm_lhs, QQ.get_ring().num(c)), QQ.get_ring().den(c)).unwrap(), i)));
536        let rhs = ZZX.from_terms(QQX.terms(rhs).map(|(c, i)| (ZZ.checked_div(&ZZ.mul_ref(&den_lcm_rhs, QQ.get_ring().num(c)), QQ.get_ring().den(c)).unwrap(), i)));
537        let result = poly_gcd_local(&ZZX, lhs, rhs, DontObserve);
538        let ZZX_to_QQX = QQX.lifted_hom(&ZZX, QQ.inclusion());
539    
540        return QQX.normalize(ZZX_to_QQX.map(result));
541    }
542}
543
544#[cfg(test)]
545use crate::primitive_int::StaticRing;
546#[cfg(test)]
547use crate::homomorphism::Homomorphism;
548
549use super::poly::PolyRing;
550
551#[cfg(test)]
552fn edge_case_elements() -> impl Iterator<Item = El<RationalField<StaticRing<i64>>>> {
553    let ring = RationalField::new(StaticRing::<i64>::RING);
554    let incl = ring.into_int_hom();
555    (-6..8).flat_map(move |x| (-2..5).filter(|y| *y != 0).map(move |y| ring.checked_div(&incl.map(x), &incl.map(y)).unwrap()))
556}
557
558#[test]
559fn test_ring_axioms() {
560    let ring = RationalField::new(StaticRing::<i64>::RING);
561
562    let half = ring.checked_div(&ring.int_hom().map(1), &ring.int_hom().map(2)).unwrap();
563    assert!(!ring.is_one(&half));
564    assert!(!ring.is_zero(&half));
565    assert_el_eq!(ring, ring.one(), ring.add_ref(&half, &half));
566    crate::ring::generic_tests::test_ring_axioms(ring, edge_case_elements());
567}
568
569#[test]
570fn test_divisibility_axioms() {
571    let ring = RationalField::new(StaticRing::<i64>::RING);
572    crate::divisibility::generic_tests::test_divisibility_axioms(ring, edge_case_elements());
573}
574
575#[test]
576fn test_principal_ideal_ring_axioms() {
577    let ring = RationalField::new(StaticRing::<i64>::RING);
578    crate::pid::generic_tests::test_euclidean_ring_axioms(ring, edge_case_elements());
579    crate::pid::generic_tests::test_principal_ideal_ring_axioms(ring, edge_case_elements());
580}
581
582#[test]
583fn test_int_hom_axioms() {
584    let ring = RationalField::new(StaticRing::<i64>::RING);
585    crate::ring::generic_tests::test_hom_axioms(&StaticRing::<i64>::RING, ring, -16..15);
586}