Skip to main content

feanor_math/rings/extension/
number_field.rs

1use std::alloc::Global;
2use std::fmt::{Debug, Formatter};
3use std::marker::PhantomData;
4
5use feanor_serde::newtype_struct::*;
6use serde::de::DeserializeSeed;
7use serde::{Deserialize, Deserializer, Serialize, Serializer};
8
9use super::extension_impl::FreeAlgebraImpl;
10use super::{Field, FreeAlgebra};
11use crate::MAX_PROBABILISTIC_REPETITIONS;
12use crate::algorithms::convolution::*;
13use crate::algorithms::eea::signed_lcm;
14use crate::algorithms::interpolate::product_except_one;
15use crate::algorithms::newton::{self, absolute_error_of_poly_eval};
16use crate::algorithms::poly_factor::extension::poly_factor_extension;
17use crate::algorithms::poly_factor::factor_locally::{FactorAndLiftModpeResult, factor_and_lift_mod_pe};
18use crate::algorithms::poly_gcd::gcd::poly_gcd_local;
19use crate::algorithms::poly_gcd::squarefree_part::poly_power_decomposition_local;
20use crate::algorithms::poly_gcd::*;
21use crate::algorithms::rational_reconstruction::balanced_rational_reconstruction;
22use crate::algorithms::resultant::ComputeResultantRing;
23use crate::computation::*;
24use crate::delegate::*;
25use crate::divisibility::*;
26use crate::integer::*;
27use crate::pid::*;
28use crate::reduce_lift::poly_factor_gcd::*;
29use crate::ring::*;
30use crate::rings::extension::extension_impl::*;
31use crate::rings::extension::number_field::newton::find_approximate_complex_root;
32use crate::rings::extension::sparse::SparseMapVector;
33use crate::rings::extension::*;
34use crate::rings::field::AsField;
35use crate::rings::float_complex::{Complex64, Complex64Base};
36use crate::rings::rational::*;
37use crate::rings::zn::ZnRingStore;
38use crate::serialization::*;
39use crate::specialization::*;
40
41const TRY_FIND_INERT_PRIME_ATTEMPTS: usize = 10;
42const TRY_FACTOR_DIRECTLY_ATTEMPTS: usize = 5;
43
44/// An algebraic number field, i.e. a finite rank field extension of the rationals.
45///
46/// This type only wraps an underlying implementation of the ring arithmetic, and adds
47/// some number-field specific functionality. However, the implementation type defaults to
48/// [`DefaultNumberFieldImpl`], which should be sufficient for almost all purposes.
49/// Note that the only way to create a number field that does not use the default
50/// implementation is via [`NumberFieldBase::create()`].
51///
52/// # Example
53///
54/// ```rust
55/// # use feanor_math::assert_el_eq;
56/// # use feanor_math::ring::*;
57/// # use feanor_math::rings::extension::*;
58/// # use feanor_math::rings::extension::number_field::*;
59/// # use feanor_math::rings::poly::*;
60/// # use feanor_math::rings::poly::dense_poly::*;
61/// # use feanor_math::rings::rational::*;
62/// # use feanor_math::integer::*;
63/// let ZZ = BigIntRing::RING;
64/// let ZZX = DensePolyRing::new(ZZ, "X");
65/// let [gen_poly] = ZZX.with_wrapped_indeterminate(|X| [X.pow_ref(2) + 1]);
66/// // the Gaussian numbers `QQ[i]`
67/// let QQi = NumberField::new(&ZZX, &gen_poly);
68/// let i = QQi.canonical_gen();
69/// assert_el_eq!(&QQi, QQi.neg_one(), QQi.pow(i, 2));
70/// ```
71/// So far, we could have done the same with just [`FreeAlgebraImpl`], which indeed
72/// is used as the default implementation of the arithmetic. However, [`NumberField`]
73/// provides additional functionality, that is not available for general extensions.
74/// ```rust
75/// # use feanor_math::assert_el_eq;
76/// # use feanor_math::ring::*;
77/// # use feanor_math::rings::extension::*;
78/// # use feanor_math::rings::extension::number_field::*;
79/// # use feanor_math::rings::poly::*;
80/// # use feanor_math::rings::poly::dense_poly::*;
81/// # use feanor_math::rings::rational::*;
82/// # use feanor_math::algorithms::poly_factor::*;
83/// # use feanor_math::integer::*;
84/// # let ZZ = BigIntRing::RING;
85/// # let ZZX = DensePolyRing::new(ZZ, "X");
86/// # let [gen_poly] = ZZX.with_wrapped_indeterminate(|X| [X.pow_ref(2) + 1]);
87/// # let QQi = NumberField::new(&ZZX, &gen_poly);
88/// # let i = QQi.canonical_gen();
89/// let QQiX = DensePolyRing::new(&QQi, "X");
90/// let [f] = QQiX.with_wrapped_indeterminate(|X| [X.pow_ref(2) + 4]);
91/// let (factorization, _) = <_ as FactorPolyField>::factor_poly(&QQiX, &f);
92/// assert_eq!(2, factorization.len());
93/// ```
94/// The internal generating polynomial of a number field is currently always
95/// integral, but you can create a number field also from a rational polynomial
96/// using [`NumberField::adjoin_root()`].
97/// ```rust
98/// # use feanor_math::assert_el_eq;
99/// # use feanor_math::ring::*;
100/// # use feanor_math::homomorphism::*;
101/// # use feanor_math::divisibility::*;
102/// # use feanor_math::rings::extension::*;
103/// # use feanor_math::rings::extension::number_field::*;
104/// # use feanor_math::rings::poly::*;
105/// # use feanor_math::rings::poly::dense_poly::*;
106/// # use feanor_math::rings::rational::*;
107/// # use feanor_math::integer::*;
108/// let ZZ = BigIntRing::RING;
109/// let QQ = RationalField::new(ZZ);
110/// let QQX = DensePolyRing::new(&QQ, "X");
111/// // take `gen_poly = X^2 + 1/4`
112/// let gen_poly = QQX.add(
113///     QQX.pow(QQX.indeterminate(), 2),
114///     QQX.inclusion()
115///         .map(QQ.invert(&QQ.int_hom().map(4)).unwrap()),
116/// );
117/// // this still gives the Gaussian numbers `QQ[i]`
118/// let (QQi, i_half) = NumberField::adjoin_root(&QQX, &gen_poly);
119/// assert_el_eq!(
120///     &QQi,
121///     QQi.neg_one(),
122///     QQi.pow(QQi.int_hom().mul_ref_map(&i_half, &2), 2)
123/// );
124/// // however the canonical generator might not be `i/2`
125/// assert!(!QQi.eq_el(&QQi.canonical_gen(), &i_half));
126/// ```
127///
128/// # Why not relative number fields?
129///
130/// Same as [`crate::rings::extension::galois_field::GaloisFieldBase`], this type represents
131/// number fields globally, i.e. always in the form `Q[X]/(f(X))`. By the primitive element
132/// theorem, each number field can be written in this form. However, it might be more natural
133/// in some applications to write it as an extension of a smaller number field, say `L =
134/// K[X]/(f(X))`.
135///
136/// I tried this before, and it turned out to be a constant fight with the type system.
137/// The final code worked more or less (see git commit b1ef445cf14733f63d035b39314c2dd66fd7fcb5),
138/// but it looks terrible, since we need quite a few "helper" traits to be able to provide all the
139/// expected functionality. Basically, every functionality must now be represented by one (or many)
140/// traits that are implemented by `QQ` and by any extension `K[X]/(f(X))` for which `K` implements
141/// it. In some cases (like polynomial factorization), we want to have "functorial" functions that
142/// map a number field to something else (e.g. one of its orders), and each of those now requires
143/// a complete parallel hierarchy of traits. If you are not yet frightened, checkout the above
144/// commit and see if you can make sense of the corresponding code.
145///
146/// To summarize, all number fields are represented absolutely, i.e. as extensions of `QQ`.
147///
148/// # Factoring out denominators
149///
150/// TODO: At next breaking release, investigate whether it is sensible to have `Impl` be an
151/// algebraic extension of `Z` instead of `Q`, and store the joint denominator once for every
152/// element.
153///
154/// # Choice of blanket implementations of [`CanHomFrom`]
155///
156/// This is done analogously to [`crate::rings::extension::galois_field::GaloisFieldBase`], see
157/// the description there.
158#[stability::unstable(feature = "enable")]
159pub struct NumberFieldBase<Impl, I>
160where
161    Impl: RingStore,
162    Impl::Type: Field + FreeAlgebra,
163    <Impl::Type as RingExtension>::BaseRing: RingStore<Type = RationalFieldBase<I>>,
164    I: RingStore,
165    I::Type: IntegerRing,
166{
167    base: Impl,
168}
169
170/// An embedding of a number field `K` into the complex numbers `CC`, represented
171/// approximately via floating point numbers.
172#[stability::unstable(feature = "enable")]
173pub struct ComplexEmbedding<K, Impl, I>
174where
175    K: RingStore<Type = NumberFieldBase<Impl, I>>,
176    Impl: RingStore,
177    Impl::Type: Field + FreeAlgebra,
178    <Impl::Type as RingExtension>::BaseRing: RingStore<Type = RationalFieldBase<I>>,
179    I: RingStore,
180    I::Type: IntegerRing,
181{
182    from: K,
183    image_of_generator: El<Complex64>,
184    absolute_error_image_of_generator: f64,
185}
186
187impl<K, Impl, I> ComplexEmbedding<K, Impl, I>
188where
189    K: RingStore<Type = NumberFieldBase<Impl, I>>,
190    Impl: RingStore,
191    Impl::Type: Field + FreeAlgebra,
192    <Impl::Type as RingExtension>::BaseRing: RingStore<Type = RationalFieldBase<I>>,
193    I: RingStore,
194    I::Type: IntegerRing,
195{
196    /// Returns `epsilon > 0` such that when evaluating this homomorphism
197    /// at point `x`, the given result is at most `epsilon` from the actual
198    /// result (i.e. the result when computed with infinite precision).
199    #[stability::unstable(feature = "enable")]
200    pub fn absolute_error_bound_at(&self, x: &<NumberFieldBase<Impl, I> as RingBase>::Element) -> f64 {
201        let CC = Complex64::RING;
202        let CCX = DensePolyRing::new(CC, "X");
203        let f = self.from.poly_repr(&CCX, x, CC.can_hom(self.from.base_ring()).unwrap());
204        return absolute_error_of_poly_eval(
205            &CCX,
206            &f,
207            self.from.rank(),
208            self.image_of_generator,
209            self.absolute_error_image_of_generator / CC.abs(self.image_of_generator),
210        );
211    }
212}
213
214impl<K, Impl, I> Homomorphism<NumberFieldBase<Impl, I>, Complex64Base> for ComplexEmbedding<K, Impl, I>
215where
216    K: RingStore<Type = NumberFieldBase<Impl, I>>,
217    Impl: RingStore,
218    Impl::Type: Field + FreeAlgebra,
219    <Impl::Type as RingExtension>::BaseRing: RingStore<Type = RationalFieldBase<I>>,
220    I: RingStore,
221    I::Type: IntegerRing,
222{
223    type DomainStore = K;
224    type CodomainStore = Complex64;
225
226    fn codomain(&self) -> &Self::CodomainStore { &Complex64::RING }
227
228    fn domain(&self) -> &Self::DomainStore { &self.from }
229
230    fn map_ref(&self, x: &<NumberFieldBase<Impl, I> as RingBase>::Element) -> <Complex64Base as RingBase>::Element {
231        let poly_ring = DensePolyRing::new(*self.codomain(), "X");
232        let hom = self.codomain().can_hom(self.from.base_ring()).unwrap();
233        return poly_ring.evaluate(
234            &self.from.poly_repr(&poly_ring, x, &hom),
235            &self.image_of_generator,
236            self.codomain().identity(),
237        );
238    }
239
240    fn map(&self, x: <NumberFieldBase<Impl, I> as RingBase>::Element) -> <Complex64Base as RingBase>::Element {
241        self.map_ref(&x)
242    }
243}
244
245#[stability::unstable(feature = "enable")]
246pub type DefaultNumberFieldImpl =
247    AsField<FreeAlgebraImpl<RationalField<BigIntRing>, Vec<El<RationalField<BigIntRing>>>, Global, KaratsubaAlgorithm>>;
248#[stability::unstable(feature = "enable")]
249pub type NumberField<Impl = DefaultNumberFieldImpl, I = BigIntRing> = RingValue<NumberFieldBase<Impl, I>>;
250
251impl NumberField {
252    /// If the given polynomial is irreducible, returns the number field generated
253    /// by it (with a root of the polynomial as canonical generator). Otherwise,
254    /// `None` is returned.
255    ///
256    /// If the given polynomial is not integral or not monic, consider using
257    /// [`NumberField::try_adjoin_root()`] instead.
258    #[stability::unstable(feature = "enable")]
259    pub fn try_new<P>(poly_ring: P, generating_poly: &El<P>) -> Option<Self>
260    where
261        P: RingStore,
262        P::Type: PolyRing,
263        <P::Type as RingExtension>::BaseRing: RingStore<Type = BigIntRingBase>,
264    {
265        assert!(poly_ring.base_ring().is_one(poly_ring.lc(generating_poly).unwrap()));
266        let QQ = RationalField::new(BigIntRing::RING);
267        let rank = poly_ring.degree(generating_poly).unwrap();
268        let modulus = (0..rank)
269            .map(|i| QQ.negate(QQ.inclusion().map_ref(poly_ring.coefficient_at(generating_poly, i))))
270            .collect::<Vec<_>>();
271        return FreeAlgebraImpl::new_with_convolution(QQ, rank, modulus, "θ", Global, STANDARD_CONVOLUTION)
272            .as_field()
273            .ok()
274            .map(Self::create);
275    }
276
277    /// Given a monic, integral and irreducible polynomial, returns the number field
278    /// generated by it (with a root of the polynomial as canonical generator).
279    ///
280    /// Panics if the polynomial is not irreducible.
281    ///
282    /// If the given polynomial is not integral or not monic, consider using
283    /// [`NumberField::adjoin_root()`] instead.
284    #[stability::unstable(feature = "enable")]
285    pub fn new<P>(poly_ring: P, generating_poly: &El<P>) -> Self
286    where
287        P: RingStore,
288        P::Type: PolyRing,
289        <P::Type as RingExtension>::BaseRing: RingStore<Type = BigIntRingBase>,
290    {
291        Self::try_new(poly_ring, generating_poly).unwrap()
292    }
293
294    /// If the given polynopmial is irreducible, computes the number field generated
295    /// by one of its roots, and returns it together with the root (which is not necessarily
296    /// the canonical generator of the number field). Otherwise, `None` is returned.
297    #[stability::unstable(feature = "enable")]
298    pub fn try_adjoin_root<P>(poly_ring: P, generating_poly: &El<P>) -> Option<(Self, El<Self>)>
299    where
300        P: RingStore,
301        P::Type: PolyRing,
302        <P::Type as RingExtension>::BaseRing: RingStore<Type = RationalFieldBase<BigIntRing>>,
303    {
304        let QQ = poly_ring.base_ring();
305        let ZZ = QQ.base_ring();
306        let denominator = poly_ring
307            .terms(generating_poly)
308            .map(|(c, _)| QQ.get_ring().den(c))
309            .fold(ZZ.one(), |a, b| signed_lcm(a, ZZ.clone_el(b), ZZ));
310        let rank = poly_ring.degree(generating_poly).unwrap();
311        let scaled_lc = ZZ
312            .checked_div(
313                &ZZ.mul_ref(&denominator, QQ.get_ring().num(poly_ring.lc(generating_poly).unwrap())),
314                QQ.get_ring().den(poly_ring.lc(generating_poly).unwrap()),
315            )
316            .unwrap();
317        let ZZX = DensePolyRing::new(ZZ, "X");
318        let new_generating_poly = ZZX.from_terms(poly_ring.terms(generating_poly).map(|(c, i)| {
319            if i == rank {
320                (ZZ.one(), rank)
321            } else {
322                (
323                    ZZ.checked_div(
324                        &ZZ.mul_ref_fst(
325                            &denominator,
326                            ZZ.mul_ref_fst(QQ.get_ring().num(c), ZZ.pow(ZZ.clone_el(&scaled_lc), rank - i - 1)),
327                        ),
328                        QQ.get_ring().den(c),
329                    )
330                    .unwrap(),
331                    i,
332                )
333            }
334        }));
335        return Self::try_new(ZZX, &new_generating_poly).map(|res| {
336            let root = res
337                .inclusion()
338                .mul_map(res.canonical_gen(), QQ.invert(&QQ.inclusion().map(scaled_lc)).unwrap());
339            return (res, root);
340        });
341    }
342
343    #[stability::unstable(feature = "enable")]
344    pub fn adjoin_root<P>(poly_ring: P, generating_poly: &El<P>) -> (Self, El<Self>)
345    where
346        P: RingStore,
347        P::Type: PolyRing,
348        <P::Type as RingExtension>::BaseRing: RingStore<Type = RationalFieldBase<BigIntRing>>,
349    {
350        Self::try_adjoin_root(poly_ring, generating_poly).unwrap()
351    }
352}
353
354impl<Impl, I> NumberField<Impl, I>
355where
356    Impl: RingStore,
357    Impl::Type: Field + FreeAlgebra,
358    <Impl::Type as RingExtension>::BaseRing: RingStore<Type = RationalFieldBase<I>>,
359    I: RingStore,
360    I::Type: IntegerRing,
361{
362    /// Creates a new number field with the given underlying implementation.
363    ///
364    /// Requires that all coefficients of the generating polynomial are integral.
365    #[stability::unstable(feature = "enable")]
366    pub fn create(implementation: Impl) -> Self {
367        let poly_ring = DensePolyRing::new(implementation.base_ring(), "X");
368        let gen_poly = implementation.generating_poly(&poly_ring, poly_ring.base_ring().identity());
369        assert!(poly_ring.terms(&gen_poly).all(|(c, _)| {
370            poly_ring
371                .base_ring()
372                .base_ring()
373                .is_one(poly_ring.base_ring().get_ring().den(c))
374        }));
375        RingValue::from(NumberFieldBase { base: implementation })
376    }
377
378    #[stability::unstable(feature = "enable")]
379    pub fn into_choose_complex_embedding(self) -> ComplexEmbedding<Self, Impl, I> {
380        let ZZ = self.base_ring().base_ring();
381        let poly_ring = DensePolyRing::new(ZZ, "X");
382        let poly = self.get_ring().generating_poly_as_int(&poly_ring);
383        let (root, error) = find_approximate_complex_root(&poly_ring, &poly).unwrap();
384        return ComplexEmbedding {
385            from: self,
386            image_of_generator: root,
387            absolute_error_image_of_generator: error,
388        };
389    }
390
391    #[stability::unstable(feature = "enable")]
392    pub fn choose_complex_embedding<'a>(&'a self) -> ComplexEmbedding<&'a Self, Impl, I> {
393        let ZZ = self.base_ring().base_ring();
394        let poly_ring = DensePolyRing::new(ZZ, "X");
395        let poly = self.get_ring().generating_poly_as_int(&poly_ring);
396        let (root, error) = find_approximate_complex_root(&poly_ring, &poly).unwrap();
397        return ComplexEmbedding {
398            from: self,
399            image_of_generator: root,
400            absolute_error_image_of_generator: error,
401        };
402    }
403}
404
405impl<Impl, I> NumberFieldBase<Impl, I>
406where
407    Impl: RingStore,
408    Impl::Type: Field + FreeAlgebra,
409    <Impl::Type as RingExtension>::BaseRing: RingStore<Type = RationalFieldBase<I>>,
410    I: RingStore,
411    I::Type: IntegerRing,
412{
413    fn generating_poly_as_int<'a>(&self, ZZX: &DensePolyRing<&'a I>) -> El<DensePolyRing<&'a I>> {
414        let ZZ = *ZZX.base_ring();
415        let assume_in_ZZ = LambdaHom::new(self.base_ring(), ZZ, |from, to, x| {
416            to.checked_div(from.get_ring().num(x), from.get_ring().den(x)).unwrap()
417        });
418        return self.base.generating_poly(ZZX, &assume_in_ZZ);
419    }
420}
421
422impl<Impl, I> Clone for NumberFieldBase<Impl, I>
423where
424    Impl: RingStore + Clone,
425    Impl::Type: Field + FreeAlgebra,
426    <Impl::Type as RingExtension>::BaseRing: RingStore<Type = RationalFieldBase<I>>,
427    I: RingStore,
428    I::Type: IntegerRing,
429{
430    fn clone(&self) -> Self {
431        Self {
432            base: self.base.clone(),
433        }
434    }
435}
436
437impl<Impl, I> Copy for NumberFieldBase<Impl, I>
438where
439    Impl: RingStore + Copy,
440    Impl::Type: Field + FreeAlgebra,
441    <Impl::Type as RingExtension>::BaseRing: RingStore<Type = RationalFieldBase<I>>,
442    I: RingStore,
443    I::Type: IntegerRing,
444    El<Impl>: Copy,
445    El<I>: Copy,
446{
447}
448
449impl<Impl, I> PartialEq for NumberFieldBase<Impl, I>
450where
451    Impl: RingStore,
452    Impl::Type: Field + FreeAlgebra,
453    <Impl::Type as RingExtension>::BaseRing: RingStore<Type = RationalFieldBase<I>>,
454    I: RingStore,
455    I::Type: IntegerRing,
456{
457    fn eq(&self, other: &Self) -> bool { self.base.get_ring() == other.base.get_ring() }
458}
459
460impl<Impl, I> DelegateRing for NumberFieldBase<Impl, I>
461where
462    Impl: RingStore,
463    Impl::Type: Field + FreeAlgebra,
464    <Impl::Type as RingExtension>::BaseRing: RingStore<Type = RationalFieldBase<I>>,
465    I: RingStore,
466    I::Type: IntegerRing,
467{
468    type Base = Impl::Type;
469    type Element = El<Impl>;
470
471    fn get_delegate(&self) -> &Self::Base { self.base.get_ring() }
472
473    fn delegate(&self, el: Self::Element) -> <Self::Base as RingBase>::Element { el }
474    fn delegate_mut<'a>(&self, el: &'a mut Self::Element) -> &'a mut <Self::Base as RingBase>::Element { el }
475    fn delegate_ref<'a>(&self, el: &'a Self::Element) -> &'a <Self::Base as RingBase>::Element { el }
476    fn rev_delegate(&self, el: <Self::Base as RingBase>::Element) -> Self::Element { el }
477}
478
479impl<Impl, I> DelegateRingImplEuclideanRing for NumberFieldBase<Impl, I>
480where
481    Impl: RingStore,
482    Impl::Type: Field + FreeAlgebra,
483    <Impl::Type as RingExtension>::BaseRing: RingStore<Type = RationalFieldBase<I>>,
484    I: RingStore,
485    I::Type: IntegerRing,
486{
487}
488
489impl<Impl, I> Debug for NumberFieldBase<Impl, I>
490where
491    Impl: RingStore,
492    Impl::Type: Field + FreeAlgebra + Debug,
493    <Impl::Type as RingExtension>::BaseRing: RingStore<Type = RationalFieldBase<I>>,
494    I: RingStore,
495    I::Type: IntegerRing,
496{
497    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "NumberField({:?})", self.base.get_ring()) }
498}
499
500impl<Impl, I> FiniteRingSpecializable for NumberFieldBase<Impl, I>
501where
502    Impl: RingStore,
503    Impl::Type: Field + FreeAlgebra,
504    <Impl::Type as RingExtension>::BaseRing: RingStore<Type = RationalFieldBase<I>>,
505    I: RingStore,
506    I::Type: IntegerRing,
507{
508    fn specialize<O: FiniteRingOperation<Self>>(op: O) -> O::Output { op.fallback() }
509}
510
511impl<Impl, I> Field for NumberFieldBase<Impl, I>
512where
513    Impl: RingStore,
514    Impl::Type: Field + FreeAlgebra,
515    <Impl::Type as RingExtension>::BaseRing: RingStore<Type = RationalFieldBase<I>>,
516    I: RingStore,
517    I::Type: IntegerRing,
518{
519}
520
521impl<Impl, I> PerfectField for NumberFieldBase<Impl, I>
522where
523    Impl: RingStore,
524    Impl::Type: Field + FreeAlgebra,
525    <Impl::Type as RingExtension>::BaseRing: RingStore<Type = RationalFieldBase<I>>,
526    I: RingStore,
527    I::Type: IntegerRing,
528{
529}
530
531impl<Impl, I> Domain for NumberFieldBase<Impl, I>
532where
533    Impl: RingStore,
534    Impl::Type: Field + FreeAlgebra,
535    <Impl::Type as RingExtension>::BaseRing: RingStore<Type = RationalFieldBase<I>>,
536    I: RingStore,
537    I::Type: IntegerRing,
538{
539}
540
541impl<Impl, I> PolyTFracGCDRing for NumberFieldBase<Impl, I>
542where
543    Impl: RingStore,
544    Impl::Type: Field + FreeAlgebra,
545    <Impl::Type as RingExtension>::BaseRing: RingStore<Type = RationalFieldBase<I>>,
546    I: RingStore,
547    I::Type: IntegerRing,
548{
549    fn gcd<P>(poly_ring: P, lhs: &El<P>, rhs: &El<P>) -> El<P>
550    where
551        P: RingStore + Copy,
552        P::Type: PolyRing + DivisibilityRing,
553        <P::Type as RingExtension>::BaseRing: RingStore<Type = Self>,
554    {
555        let self_ = NumberFieldByOrder {
556            base: RingRef::new(poly_ring.base_ring().get_ring()),
557        };
558
559        let order_poly_ring = DensePolyRing::new(RingRef::new(&self_), "X");
560        let lhs_order = self_.scale_poly_to_order(poly_ring, &order_poly_ring, lhs);
561        let rhs_order = self_.scale_poly_to_order(poly_ring, &order_poly_ring, rhs);
562
563        let result = poly_gcd_local(
564            &order_poly_ring,
565            order_poly_ring.clone_el(&lhs_order),
566            order_poly_ring.clone_el(&rhs_order),
567            DontObserve,
568        );
569
570        return self_.normalize_map_back_from_order(&order_poly_ring, poly_ring, &result);
571    }
572
573    fn power_decomposition<P>(poly_ring: P, poly: &El<P>) -> Vec<(El<P>, usize)>
574    where
575        P: RingStore + Copy,
576        P::Type: PolyRing + DivisibilityRing,
577        <P::Type as RingExtension>::BaseRing: RingStore<Type = Self>,
578    {
579        let self_ = NumberFieldByOrder {
580            base: RingRef::new(poly_ring.base_ring().get_ring()),
581        };
582        let order_poly_ring = DensePolyRing::new(RingRef::new(&self_), "X");
583        let poly_order = self_.scale_poly_to_order(poly_ring, &order_poly_ring, poly);
584
585        let result = poly_power_decomposition_local(&order_poly_ring, poly_order, DontObserve);
586
587        return result
588            .into_iter()
589            .map(|(f, k)| (self_.normalize_map_back_from_order(&order_poly_ring, poly_ring, &f), k))
590            .collect();
591    }
592}
593
594enum HeuristicFactorPolyInOrderResult<P>
595where
596    P: RingStore,
597    P::Type: PolyRing,
598{
599    PartialFactorization(Vec<(El<P>, usize)>),
600    Irreducible,
601    Unknown,
602}
603
604/// Tries to factor the polynomial directly, by first finding an inert prime `p`, so that
605/// the number ring modulo `p` becomes a finite field. Then we factor the polynomial over
606/// the finite field, and hensel-lift it to a factorization in the order. This can fail
607/// if we don't find an inert prime - note that they don't have to exist. Note also that the
608/// returned factorization may be only a partial factorization.
609///
610/// # Inert primes don't have to exist
611///
612/// E.g. `X^4 - 10 X^2 + 1` is reducible modulo every prime. In fact, it is a theorem that
613/// there exists inert primes if and only if the Galois group of the extension is cyclic.
614fn heuristic_factor_poly_directly_in_order<'a, P, Impl, I, Controller>(
615    poly_ring: P,
616    poly: &El<P>,
617    controller: Controller,
618) -> HeuristicFactorPolyInOrderResult<P>
619where
620    Impl: 'a + RingStore,
621    Impl::Type: Field + FreeAlgebra,
622    <Impl::Type as RingExtension>::BaseRing: RingStore<Type = RationalFieldBase<I>>,
623    I: 'a + RingStore,
624    I::Type: IntegerRing,
625    P: RingStore + Copy,
626    P::Type: PolyRing + DivisibilityRing,
627    <P::Type as RingExtension>::BaseRing: RingStore<Type = NumberFieldByOrder<'a, Impl, I>>,
628    Controller: ComputationController,
629{
630    controller.run_computation(
631        format_args!(
632            "factor_direct(deg={}, extdeg={})",
633            poly_ring.degree(poly).unwrap(),
634            poly_ring.base_ring().rank()
635        ),
636        |controller| {
637            let mut rng = oorandom::Rand64::new(1);
638            let self_ = poly_ring.base_ring();
639
640            // first, we try to find an inert prime `p` and lift a factorization modulo `p` to the
641            // ring
642            'try_factor_directly: for _ in 0..TRY_FACTOR_DIRECTLY_ATTEMPTS {
643                let mut inert_prime = None;
644                for _ in 0..(TRY_FIND_INERT_PRIME_ATTEMPTS * self_.rank()) {
645                    let p = self_.get_ring().random_suitable_ideal(|| rng.rand_u64());
646                    if p.minpoly_factors_mod_p.len() == 1 {
647                        inert_prime = Some(p);
648                        break;
649                    }
650                }
651                if let Some(p) = inert_prime {
652                    log_progress!(
653                        controller,
654                        "(inert_prime={})",
655                        IdealDisplayWrapper::new(self_.base_ring().base_ring().get_ring(), &p.prime)
656                    );
657                    let lc_poly = self_.clone_el(poly_ring.lc(poly).unwrap());
658                    let monic_poly = evaluate_aX(poly_ring, poly, &lc_poly);
659                    let e = 2 * self_.get_ring().heuristic_exponent(
660                        &p,
661                        poly_ring.degree(&monic_poly).unwrap(),
662                        poly_ring.terms(&monic_poly).map(|(c, _)| c),
663                    );
664                    match factor_and_lift_mod_pe(poly_ring, &p, e, &monic_poly, controller.clone()) {
665                        FactorAndLiftModpeResult::PartialFactorization(factorization) => {
666                            log_progress!(controller, "(partial_success)");
667                            debug_assert!(
668                                poly_ring.eq_el(
669                                    &monic_poly,
670                                    &poly_ring
671                                        .normalize(poly_ring.prod(factorization.iter().map(|f| poly_ring.clone_el(f))))
672                                )
673                            );
674                            let result: Vec<_> = factorization
675                                .into_iter()
676                                .map(|f| (unevaluate_aX(poly_ring, &f, &lc_poly), 1))
677                                .collect();
678                            debug_assert!(
679                                poly_ring.eq_el(
680                                    &poly_ring.normalize(poly_ring.clone_el(poly)),
681                                    &poly_ring.normalize(poly_ring.prod(
682                                        result.iter().map(|(f, e)| { poly_ring.pow(poly_ring.clone_el(f), *e) })
683                                    ))
684                                )
685                            );
686                            return HeuristicFactorPolyInOrderResult::PartialFactorization(result);
687                        }
688                        FactorAndLiftModpeResult::Irreducible => {
689                            return HeuristicFactorPolyInOrderResult::Irreducible;
690                        }
691                        FactorAndLiftModpeResult::NotSquarefreeModpe => {
692                            // probably not square-free
693                            let power_decomposition =
694                                poly_power_decomposition_local(poly_ring, poly_ring.clone_el(poly), controller.clone());
695                            if power_decomposition.len() > 1 {
696                                log_progress!(controller, "(partial_success)");
697                                return HeuristicFactorPolyInOrderResult::PartialFactorization(power_decomposition);
698                            }
699                        }
700                        FactorAndLiftModpeResult::Unknown => {}
701                    }
702                } else {
703                    break 'try_factor_directly;
704                }
705            }
706            log_progress!(controller, "(fail)");
707            return HeuristicFactorPolyInOrderResult::Unknown;
708        },
709    )
710}
711
712impl<Impl, I> FactorPolyField for NumberFieldBase<Impl, I>
713where
714    Impl: RingStore,
715    Impl::Type: Field + FreeAlgebra,
716    <Impl::Type as RingExtension>::BaseRing: RingStore<Type = RationalFieldBase<I>>,
717    I: RingStore,
718    I::Type: IntegerRing + ComputeResultantRing,
719{
720    fn factor_poly<P>(poly_ring: P, poly: &El<P>) -> (Vec<(El<P>, usize)>, Self::Element)
721    where
722        P: RingStore + Copy,
723        P::Type: PolyRing + EuclideanRing,
724        <P::Type as RingExtension>::BaseRing: RingStore<Type = Self>,
725    {
726        Self::factor_poly_with_controller(poly_ring, poly, DontObserve)
727    }
728
729    fn factor_poly_with_controller<P, Controller>(
730        poly_ring: P,
731        poly: &El<P>,
732        controller: Controller,
733    ) -> (Vec<(El<P>, usize)>, Self::Element)
734    where
735        P: RingStore + Copy,
736        P::Type: PolyRing + EuclideanRing,
737        <P::Type as RingExtension>::BaseRing: RingStore<Type = Self>,
738        Controller: ComputationController,
739    {
740        let self_ = NumberFieldByOrder {
741            base: RingRef::new(poly_ring.base_ring().get_ring()),
742        };
743        let order_poly_ring = DensePolyRing::new(RingRef::new(&self_), "X");
744
745        let mut to_factor = vec![(poly_ring.clone_el(poly), 1)];
746        let mut result = Vec::new();
747        while let Some((current, e_base)) = to_factor.pop() {
748            if poly_ring.degree(&current).unwrap() == 1 {
749                result.push((poly_ring.normalize(current), 1));
750            } else {
751                let poly_order = self_.scale_poly_to_order(poly_ring, &order_poly_ring, &current);
752                // try the direct factorization
753                match heuristic_factor_poly_directly_in_order(&order_poly_ring, &poly_order, controller.clone()) {
754                    HeuristicFactorPolyInOrderResult::PartialFactorization(partial_factorization) => {
755                        to_factor.extend(partial_factorization.into_iter().map(|(f, e)| {
756                            (
757                                self_.normalize_map_back_from_order(&order_poly_ring, poly_ring, &f),
758                                e * e_base,
759                            )
760                        }))
761                    }
762                    HeuristicFactorPolyInOrderResult::Irreducible => result.push((current, e_base)),
763                    HeuristicFactorPolyInOrderResult::Unknown => result.extend(
764                        poly_factor_extension(&poly_ring, &current, controller.clone())
765                            .0
766                            .into_iter()
767                            .map(|(f, e)| (f, e * e_base)),
768                    ),
769                }
770            }
771        }
772        return (result, poly_ring.base_ring().clone_el(poly_ring.lc(poly).unwrap()));
773    }
774}
775
776/// Implements [`PolyGCDLocallyDomain`] for [`NumberField`].
777///
778/// We don't want to expose the interface of [`PolyGCDLocallyDomain`] for number
779/// fields generally, thus use a private newtype.
780///
781/// Note that this does not actually represent the order, since during
782/// `reconstruct_ring_el()` we might reconstruct an element outside of the
783/// order. Hence, it should remain private.
784struct NumberFieldByOrder<'a, Impl, I>
785where
786    Impl: RingStore,
787    Impl::Type: Field + FreeAlgebra,
788    <Impl::Type as RingExtension>::BaseRing: RingStore<Type = RationalFieldBase<I>>,
789    I: RingStore,
790    I::Type: IntegerRing,
791{
792    base: RingRef<'a, NumberFieldBase<Impl, I>>,
793}
794
795impl<'a, Impl, I> NumberFieldByOrder<'a, Impl, I>
796where
797    Impl: RingStore,
798    Impl::Type: Field + FreeAlgebra,
799    <Impl::Type as RingExtension>::BaseRing: RingStore<Type = RationalFieldBase<I>>,
800    I: RingStore,
801    I::Type: IntegerRing,
802{
803    /// Multiplies the given polynomial with the lcm of the denominators of all coefficients,
804    /// and returns the polynomial as element of the current order.
805    fn scale_poly_to_order<'ring, P1, P2>(&self, from: P1, to: P2, poly: &El<P1>) -> El<P2>
806    where
807        P1: RingStore,
808        P1::Type: PolyRing,
809        <P1::Type as RingExtension>::BaseRing: RingStore<Type = NumberFieldBase<Impl, I>>,
810        P2: RingStore,
811        P2::Type: PolyRing,
812        <P2::Type as RingExtension>::BaseRing: RingStore<Type = Self>,
813        Self: 'ring,
814    {
815        debug_assert!(self.base.get_ring() == from.base_ring().get_ring());
816        debug_assert!(self.base.get_ring() == to.base_ring().get_ring().base.get_ring());
817        let QQ = self.base.base_ring();
818        let ZZ = QQ.base_ring();
819        let denominator = QQ.inclusion().map(
820            from.terms(poly)
821                .map(|(c, _)| {
822                    self.base
823                        .wrt_canonical_basis(c)
824                        .iter()
825                        .map(|c| ZZ.clone_el(QQ.get_ring().den(&c)))
826                        .fold(ZZ.one(), |a, b| signed_lcm(a, b, ZZ))
827                })
828                .fold(ZZ.one(), |a, b| signed_lcm(a, b, ZZ)),
829        );
830        debug_assert!(!QQ.is_zero(&denominator));
831        return to.from_terms(
832            from.terms(poly)
833                .map(|(c, i)| (self.base.inclusion().mul_ref_map(c, &denominator), i)),
834        );
835    }
836
837    fn normalize_map_back_from_order<'ring, P1, P2>(&self, from: P1, to: P2, poly: &El<P1>) -> El<P2>
838    where
839        P1: RingStore,
840        P1::Type: PolyRing,
841        <P1::Type as RingExtension>::BaseRing: RingStore<Type = Self>,
842        P2: RingStore,
843        P2::Type: PolyRing,
844        <P2::Type as RingExtension>::BaseRing: RingStore<Type = NumberFieldBase<Impl, I>>,
845        Self: 'ring,
846    {
847        debug_assert!(self.base.get_ring() == to.base_ring().get_ring());
848        debug_assert!(self.base.get_ring() == from.base_ring().get_ring().base.get_ring());
849        let result = to.from_terms(from.terms(poly).map(|(c, i)| (self.clone_el(c), i)));
850        return to.normalize(result);
851    }
852}
853
854impl<'a, Impl, I> PartialEq for NumberFieldByOrder<'a, Impl, I>
855where
856    Impl: RingStore,
857    Impl::Type: Field + FreeAlgebra,
858    <Impl::Type as RingExtension>::BaseRing: RingStore<Type = RationalFieldBase<I>>,
859    I: RingStore,
860    I::Type: IntegerRing,
861{
862    fn eq(&self, other: &Self) -> bool { self.base.get_ring() == other.base.get_ring() }
863}
864
865impl<'a, Impl, I> FiniteRingSpecializable for NumberFieldByOrder<'a, Impl, I>
866where
867    Impl: RingStore,
868    Impl::Type: Field + FreeAlgebra,
869    <Impl::Type as RingExtension>::BaseRing: RingStore<Type = RationalFieldBase<I>>,
870    I: RingStore,
871    I::Type: IntegerRing,
872{
873    fn specialize<O: FiniteRingOperation<Self>>(op: O) -> O::Output { op.fallback() }
874}
875
876impl<'a, Impl, I> DelegateRing for NumberFieldByOrder<'a, Impl, I>
877where
878    Impl: RingStore,
879    Impl::Type: Field + FreeAlgebra,
880    <Impl::Type as RingExtension>::BaseRing: RingStore<Type = RationalFieldBase<I>>,
881    I: RingStore,
882    I::Type: IntegerRing,
883{
884    type Base = Impl::Type;
885    type Element = El<Impl>;
886
887    fn get_delegate(&self) -> &Self::Base { self.base.get_ring().base.get_ring() }
888
889    fn delegate(&self, el: Self::Element) -> <Self::Base as RingBase>::Element { el }
890    fn delegate_mut<'b>(&self, el: &'b mut Self::Element) -> &'b mut <Self::Base as RingBase>::Element { el }
891    fn delegate_ref<'b>(&self, el: &'b Self::Element) -> &'b <Self::Base as RingBase>::Element { el }
892    fn rev_delegate(&self, el: <Self::Base as RingBase>::Element) -> Self::Element { el }
893}
894
895impl<'a, Impl, I> Domain for NumberFieldByOrder<'a, Impl, I>
896where
897    Impl: RingStore,
898    Impl::Type: Field + FreeAlgebra,
899    <Impl::Type as RingExtension>::BaseRing: RingStore<Type = RationalFieldBase<I>>,
900    I: RingStore,
901    I::Type: IntegerRing,
902{
903}
904
905type LocalRing<'ring, I> = <<I as RingStore>::Type as PolyGCDLocallyDomain>::LocalRing<'ring>;
906
907type ImplementationRing<'ring, I> = AsFieldBase<
908    FreeAlgebraImpl<
909        AsField<<<I as RingStore>::Type as IntegerPolyGCDRing>::LocalRingAsZn<'ring>>,
910        SparseMapVector<AsField<<<I as RingStore>::Type as IntegerPolyGCDRing>::LocalRingAsZn<'ring>>>,
911    >,
912>;
913
914struct NumberFieldOrderQuotient<'ring, I>
915where
916    I: 'ring + RingStore,
917    I::Type: IntegerRing,
918{
919    implementation: RingValue<ImplementationRing<'ring, I>>,
920}
921
922impl<'ring, I> Clone for NumberFieldOrderQuotient<'ring, I>
923where
924    I: 'ring + RingStore,
925    I::Type: IntegerRing,
926{
927    fn clone(&self) -> Self {
928        Self {
929            implementation: self.implementation.clone(),
930        }
931    }
932}
933
934impl<'ring, I> PartialEq for NumberFieldOrderQuotient<'ring, I>
935where
936    I: 'ring + RingStore,
937    I::Type: IntegerRing,
938{
939    fn eq(&self, other: &Self) -> bool { self.implementation.get_ring() == other.implementation.get_ring() }
940}
941
942impl<'ring, I> DelegateRing for NumberFieldOrderQuotient<'ring, I>
943where
944    I: 'ring + RingStore,
945    I::Type: IntegerRing,
946{
947    type Base = ImplementationRing<'ring, I>;
948    type Element = <ImplementationRing<'ring, I> as RingBase>::Element;
949
950    fn get_delegate(&self) -> &Self::Base { self.implementation.get_ring() }
951
952    fn delegate(&self, el: Self::Element) -> <Self::Base as RingBase>::Element { el }
953    fn delegate_mut<'b>(&self, el: &'b mut Self::Element) -> &'b mut <Self::Base as RingBase>::Element { el }
954    fn delegate_ref<'b>(&self, el: &'b Self::Element) -> &'b <Self::Base as RingBase>::Element { el }
955    fn rev_delegate(&self, el: <Self::Base as RingBase>::Element) -> Self::Element { el }
956}
957
958impl<'ring, I> Domain for NumberFieldOrderQuotient<'ring, I>
959where
960    I: 'ring + RingStore,
961    I::Type: IntegerRing,
962{
963}
964
965impl<'ring, I> DelegateRingImplEuclideanRing for NumberFieldOrderQuotient<'ring, I>
966where
967    I: 'ring + RingStore,
968    I::Type: IntegerRing,
969{
970}
971
972impl<'ring, I> Field for NumberFieldOrderQuotient<'ring, I>
973where
974    I: 'ring + RingStore,
975    I::Type: IntegerRing,
976{
977}
978
979impl<'ring, I> DelegateRingImplFiniteRing for NumberFieldOrderQuotient<'ring, I>
980where
981    I: 'ring + RingStore,
982    I::Type: IntegerRing,
983{
984}
985
986impl<'ring, I> CanHomFrom<Self> for NumberFieldOrderQuotient<'ring, I>
987where
988    I: 'ring + RingStore,
989    I::Type: IntegerRing,
990{
991    type Homomorphism = ();
992
993    fn has_canonical_hom(&self, from: &Self) -> Option<Self::Homomorphism> {
994        if self == from { Some(()) } else { None }
995    }
996
997    fn map_in(&self, _from: &Self, el: <Self as RingBase>::Element, _hom: &Self::Homomorphism) -> Self::Element { el }
998}
999
1000impl<'ring, I> CanIsoFromTo<Self> for NumberFieldOrderQuotient<'ring, I>
1001where
1002    I: 'ring + RingStore,
1003    I::Type: IntegerRing,
1004{
1005    type Isomorphism = <Self as CanHomFrom<Self>>::Homomorphism;
1006
1007    fn has_canonical_iso(&self, from: &Self) -> Option<Self::Isomorphism> { from.has_canonical_hom(self) }
1008
1009    fn map_out(&self, from: &Self, el: Self::Element, iso: &Self::Isomorphism) -> <Self as RingBase>::Element {
1010        from.map_in(self, el, iso)
1011    }
1012}
1013
1014/// A prime ideal of a [`NumberField`].
1015///
1016/// Used for various implementations that work on the ring modulus prime ideals,
1017/// and lift the result back to the ring.
1018pub struct NumberRingIdeal<'ring, I>
1019where
1020    I: RingStore,
1021    I::Type: IntegerRing,
1022    I: 'ring,
1023{
1024    prime: <I::Type as PolyGCDLocallyDomain>::SuitableIdeal<'ring>,
1025    ZZX: DensePolyRing<&'ring I>,
1026    number_field_poly: El<DensePolyRing<&'ring I>>,
1027    FpX: DensePolyRing<<I::Type as PolyGCDLocallyDomain>::LocalField<'ring>>,
1028    Fp_as_ring: <I::Type as PolyGCDLocallyDomain>::LocalRing<'ring>,
1029    Fp_as_zn: AsField<<I::Type as IntegerPolyGCDRing>::LocalRingAsZn<'ring>>,
1030    minpoly_factors_mod_p: Vec<El<DensePolyRing<<I::Type as PolyGCDLocallyDomain>::LocalField<'ring>>>>,
1031}
1032
1033impl<'ring, I> NumberRingIdeal<'ring, I>
1034where
1035    I: RingStore,
1036    I::Type: IntegerRing,
1037    I: 'ring,
1038{
1039    fn lifted_factorization<'a>(
1040        &'a self,
1041        e: usize,
1042    ) -> (
1043        DensePolyRing<<I::Type as PolyGCDLocallyDomain>::LocalRing<'ring>>,
1044        Vec<El<DensePolyRing<<I::Type as PolyGCDLocallyDomain>::LocalRing<'ring>>>>,
1045    ) {
1046        let ZZX = &self.ZZX;
1047        let ZZ = ZZX.base_ring();
1048        let ZpeX = DensePolyRing::new(ZZ.get_ring().local_ring_at(&self.prime, e, 0), "X");
1049        let Zpe = ZpeX.base_ring();
1050        let FpX = &self.FpX;
1051        let Zpe_to_Fp =
1052            PolyGCDLocallyIntermediateReductionMap::new(ZZ.get_ring(), &self.prime, Zpe, e, &self.Fp_as_ring, 1, 0);
1053        let ZZ_to_Zpe = PolyGCDLocallyReductionMap::new(ZZ.get_ring(), &self.prime, Zpe, e, 0);
1054
1055        let factors = hensel::hensel_lift_factorization(
1056            &Zpe_to_Fp,
1057            &ZpeX,
1058            FpX,
1059            &ZpeX.lifted_hom(ZZX, ZZ_to_Zpe).map_ref(&self.number_field_poly),
1060            &self.minpoly_factors_mod_p[..],
1061            DontObserve,
1062        );
1063
1064        return (ZpeX, factors);
1065    }
1066}
1067
1068impl<'ring, I> Debug for NumberRingIdeal<'ring, I>
1069where
1070    I: RingStore,
1071    I::Type: IntegerRing,
1072    I: 'ring,
1073    <I::Type as PolyGCDLocallyDomain>::SuitableIdeal<'ring>: Debug,
1074{
1075    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1076        f.debug_struct("NumberRingIdeal").field("prime", &self.prime).finish()
1077    }
1078}
1079
1080impl<'a, Impl, I> PolyGCDLocallyDomain for NumberFieldByOrder<'a, Impl, I>
1081where
1082    Impl: RingStore,
1083    Impl::Type: Field + FreeAlgebra,
1084    <Impl::Type as RingExtension>::BaseRing: RingStore<Type = RationalFieldBase<I>>,
1085    I: RingStore,
1086    I::Type: IntegerRing,
1087{
1088    type LocalRingBase<'ring>
1089        = FreeAlgebraImplBase<LocalRing<'ring, I>, SparseMapVector<LocalRing<'ring, I>>>
1090    where
1091        Self: 'ring;
1092
1093    type LocalRing<'ring>
1094        = RingValue<Self::LocalRingBase<'ring>>
1095    where
1096        Self: 'ring;
1097
1098    type LocalFieldBase<'ring>
1099        = NumberFieldOrderQuotient<'ring, I>
1100    where
1101        Self: 'ring;
1102
1103    type LocalField<'ring>
1104        = RingValue<Self::LocalFieldBase<'ring>>
1105    where
1106        Self: 'ring;
1107
1108    type SuitableIdeal<'ring>
1109        = NumberRingIdeal<'ring, I>
1110    where
1111        Self: 'ring;
1112
1113    fn maximal_ideal_factor_count<'ring>(&self, ideal: &Self::SuitableIdeal<'ring>) -> usize
1114    where
1115        Self: 'ring,
1116    {
1117        ideal.minpoly_factors_mod_p.len()
1118    }
1119
1120    fn heuristic_exponent<'ring, 'b, J>(
1121        &self,
1122        ideal: &Self::SuitableIdeal<'ring>,
1123        poly_deg: usize,
1124        coefficients: J,
1125    ) -> usize
1126    where
1127        J: Iterator<Item = &'b Self::Element>,
1128        Self: 'b,
1129        Self: 'ring,
1130    {
1131        const HEURISTIC_FACTOR_SIZE_OVER_POLY_SIZE_FACTOR: f64 = 0.25;
1132
1133        let QQ = self.base.base_ring();
1134        let ZZ = QQ.base_ring();
1135        // to give any mathematically justifiable value, we would probably have to consider the
1136        // canonical norm; I don't want to deal with this here, so let's just use the
1137        // coefficient norm instead...
1138        let log2_max_coeff = coefficients
1139            .map(|c| {
1140                self.base
1141                    .wrt_canonical_basis(c)
1142                    .iter()
1143                    .map(|c| ZZ.abs_log2_ceil(QQ.get_ring().num(&c)).unwrap_or(0))
1144                    .max()
1145                    .unwrap()
1146            })
1147            .max()
1148            .unwrap_or(0);
1149        let log2_p = BigIntRing::RING
1150            .to_float_approx(&ZZ.get_ring().principal_ideal_generator(&ideal.prime))
1151            .log2();
1152        return ((log2_max_coeff as f64 + poly_deg as f64 + (self.rank() as f64).log2()) / log2_p
1153            * HEURISTIC_FACTOR_SIZE_OVER_POLY_SIZE_FACTOR)
1154            .ceil() as usize
1155            + 1;
1156    }
1157
1158    fn random_suitable_ideal<'ring, F>(&'ring self, mut rng: F) -> Self::SuitableIdeal<'ring>
1159    where
1160        F: FnMut() -> u64,
1161    {
1162        let QQ = self.base.base_ring();
1163        let ZZ = QQ.base_ring();
1164        let ZZX = DensePolyRing::new(ZZ, "X");
1165        let gen_poly = self.base.generating_poly(
1166            &ZZX,
1167            LambdaHom::new(QQ, ZZ, |QQ, ZZ, x| {
1168                assert!(ZZ.is_one(QQ.get_ring().den(x)));
1169                ZZ.clone_el(QQ.get_ring().num(x))
1170            }),
1171        );
1172
1173        // search for a prime `p` such that the minimal polynomial is unramified modulo `p`
1174        for _ in 0..MAX_PROBABILISTIC_REPETITIONS {
1175            let p = ZZ.get_ring().random_suitable_ideal(&mut rng);
1176            assert_eq!(1, ZZ.get_ring().maximal_ideal_factor_count(&p));
1177
1178            let Fp_as_ring = ZZ.get_ring().local_ring_at(&p, 1, 0);
1179            let FpX = DensePolyRing::new(ZZ.get_ring().local_field_at(&p, 0), "X");
1180            let Fp = FpX.base_ring();
1181            let ZZ_to_Fp = LambdaHom::new(ZZ, Fp, |ZZ, Fp, x| {
1182                ZZ.get_ring().base_ring_to_field(
1183                    &p,
1184                    Fp_as_ring.get_ring(),
1185                    Fp.get_ring(),
1186                    0,
1187                    ZZ.get_ring()
1188                        .reduce_ring_el(&p, (Fp_as_ring.get_ring(), 1), 0, ZZ.clone_el(x)),
1189                )
1190            });
1191
1192            let gen_poly_mod_p = FpX.from_terms(ZZX.terms(&gen_poly).map(|(c, i)| (ZZ_to_Fp.map_ref(c), i)));
1193            let (factorization, _) = <_ as FactorPolyField>::factor_poly(&FpX, &gen_poly_mod_p);
1194            if factorization.iter().all(|(_, e)| *e == 1) {
1195                return NumberRingIdeal {
1196                    minpoly_factors_mod_p: factorization.into_iter().map(|(f, _)| f).collect(),
1197                    number_field_poly: gen_poly,
1198                    FpX,
1199                    ZZX,
1200                    Fp_as_zn: ZZ
1201                        .get_ring()
1202                        .local_ring_into_zn(Fp_as_ring.clone())
1203                        .as_field()
1204                        .ok()
1205                        .unwrap(),
1206                    Fp_as_ring,
1207                    prime: p,
1208                };
1209            }
1210        }
1211        unreachable!()
1212    }
1213
1214    fn local_field_at<'ring>(&self, ideal: &Self::SuitableIdeal<'ring>, idx: usize) -> Self::LocalField<'ring>
1215    where
1216        Self: 'ring,
1217    {
1218        assert!(idx < self.maximal_ideal_factor_count(ideal));
1219        let QQ = self.base.base_ring();
1220        let ZZ = QQ.base_ring();
1221        assert_eq!(1, ZZ.get_ring().maximal_ideal_factor_count(&ideal.prime));
1222        let FpX = &ideal.FpX;
1223        let Fp_to_Fp = WrapHom::to_delegate_ring(ideal.Fp_as_zn.get_ring())
1224            .compose(
1225                RingRef::new(ideal.Fp_as_zn.get_ring().get_delegate())
1226                    .into_can_hom(&ideal.Fp_as_ring)
1227                    .ok()
1228                    .unwrap(),
1229            )
1230            .compose(
1231                PolyGCDLocallyBaseRingToFieldIso::new(
1232                    ZZ.get_ring(),
1233                    &ideal.prime,
1234                    ideal.Fp_as_ring.get_ring(),
1235                    FpX.base_ring().get_ring(),
1236                    0,
1237                )
1238                .inv(),
1239            );
1240
1241        let irred_poly = &ideal.minpoly_factors_mod_p[idx];
1242        let mut x_pow_rank = SparseMapVector::new(FpX.degree(irred_poly).unwrap(), ideal.Fp_as_zn.clone());
1243        for (c, i) in FpX.terms(irred_poly) {
1244            if i < x_pow_rank.len() {
1245                *x_pow_rank.at_mut(i) = Fp_to_Fp.codomain().negate(Fp_to_Fp.map_ref(c));
1246            }
1247        }
1248        _ = x_pow_rank.at_mut(0);
1249        return RingValue::from(NumberFieldOrderQuotient {
1250            implementation: AsField::from(AsFieldBase::promise_is_perfect_field(FreeAlgebraImpl::new(
1251                ideal.Fp_as_zn.clone(),
1252                FpX.degree(irred_poly).unwrap(),
1253                x_pow_rank,
1254            ))),
1255        });
1256    }
1257
1258    fn local_ring_at<'ring>(&self, ideal: &Self::SuitableIdeal<'ring>, e: usize, idx: usize) -> Self::LocalRing<'ring>
1259    where
1260        Self: 'ring,
1261    {
1262        assert!(idx < self.maximal_ideal_factor_count(ideal));
1263        let QQ = self.base.base_ring();
1264        let ZZ = QQ.base_ring();
1265        let (ZpeX, factors) = ideal.lifted_factorization(e);
1266        let Zpe = ZZ.get_ring().local_ring_at(&ideal.prime, e, 0);
1267        assert!(Zpe.get_ring() == ZpeX.base_ring().get_ring());
1268
1269        let irred_poly = &factors[idx];
1270        let degree = ZpeX.degree(irred_poly).unwrap();
1271        let mut x_pow_rank = SparseMapVector::new(degree, Zpe.clone());
1272        for (c, i) in ZpeX.terms(irred_poly) {
1273            if i < x_pow_rank.len() {
1274                *x_pow_rank.at_mut(i) = Zpe.negate(Zpe.clone_el(c));
1275            }
1276        }
1277        _ = x_pow_rank.at_mut(0);
1278        return FreeAlgebraImpl::new(Zpe, degree, x_pow_rank);
1279    }
1280
1281    fn reduce_ring_el<'ring>(
1282        &self,
1283        ideal: &Self::SuitableIdeal<'ring>,
1284        to: (&Self::LocalRingBase<'ring>, usize),
1285        idx: usize,
1286        x: Self::Element,
1287    ) -> El<Self::LocalRing<'ring>>
1288    where
1289        Self: 'ring,
1290    {
1291        assert!(idx < self.maximal_ideal_factor_count(ideal));
1292        let QQ = self.base.base_ring();
1293        let ZZ = QQ.base_ring();
1294        let ZZX = &ideal.ZZX;
1295        let partial_QQ_to_ZZ = LambdaHom::new(QQ, ZZ, |QQ, ZZ, x| {
1296            assert!(ZZ.is_one(QQ.get_ring().den(x)));
1297            ZZ.clone_el(QQ.get_ring().num(x))
1298        });
1299        let ZZ_to_Zpe = PolyGCDLocallyReductionMap::new(ZZ.get_ring(), &ideal.prime, to.0.base_ring(), to.1, 0);
1300
1301        ZZX.evaluate(
1302            &self.base.poly_repr(ZZX, &x, partial_QQ_to_ZZ),
1303            &to.0.canonical_gen(),
1304            RingRef::new(to.0).into_inclusion().compose(ZZ_to_Zpe),
1305        )
1306    }
1307
1308    fn base_ring_to_field<'ring>(
1309        &self,
1310        ideal: &Self::SuitableIdeal<'ring>,
1311        from: &Self::LocalRingBase<'ring>,
1312        to: &Self::LocalFieldBase<'ring>,
1313        idx: usize,
1314        x: El<Self::LocalRing<'ring>>,
1315    ) -> El<Self::LocalField<'ring>>
1316    where
1317        Self: 'ring,
1318    {
1319        assert!(idx < self.maximal_ideal_factor_count(ideal));
1320        let hom = WrapHom::to_delegate_ring(to.base_ring().get_ring()).compose(
1321            RingRef::new(to.base_ring().get_ring().get_delegate())
1322                .into_can_hom(from.base_ring())
1323                .ok()
1324                .unwrap(),
1325        );
1326        to.from_canonical_basis(from.wrt_canonical_basis(&x).iter().map(|c| hom.map(c)))
1327    }
1328
1329    fn field_to_base_ring<'ring>(
1330        &self,
1331        ideal: &Self::SuitableIdeal<'ring>,
1332        from: &Self::LocalFieldBase<'ring>,
1333        to: &Self::LocalRingBase<'ring>,
1334        idx: usize,
1335        x: El<Self::LocalField<'ring>>,
1336    ) -> El<Self::LocalRing<'ring>>
1337    where
1338        Self: 'ring,
1339    {
1340        assert!(idx < self.maximal_ideal_factor_count(ideal));
1341        assert!(idx < self.maximal_ideal_factor_count(ideal));
1342        let hom = RingRef::new(from.base_ring().get_ring().get_delegate())
1343            .into_can_iso(to.base_ring())
1344            .ok()
1345            .unwrap()
1346            .compose(UnwrapHom::from_delegate_ring(from.base_ring().get_ring()));
1347        to.from_canonical_basis(from.wrt_canonical_basis(&x).iter().map(|c| hom.map(c)))
1348    }
1349
1350    fn reduce_partial<'ring>(
1351        &self,
1352        ideal: &Self::SuitableIdeal<'ring>,
1353        from: (&Self::LocalRingBase<'ring>, usize),
1354        to: (&Self::LocalRingBase<'ring>, usize),
1355        idx: usize,
1356        x: El<Self::LocalRing<'ring>>,
1357    ) -> El<Self::LocalRing<'ring>>
1358    where
1359        Self: 'ring,
1360    {
1361        assert!(idx < self.maximal_ideal_factor_count(ideal));
1362        let QQ = self.base.base_ring();
1363        let ZZ = QQ.base_ring();
1364        to.0.from_canonical_basis(from.0.wrt_canonical_basis(&x).iter().map(|c| {
1365            ZZ.get_ring().reduce_partial(
1366                &ideal.prime,
1367                (from.0.base_ring().get_ring(), from.1),
1368                (to.0.base_ring().get_ring(), to.1),
1369                0,
1370                c,
1371            )
1372        }))
1373    }
1374
1375    fn lift_partial<'ring>(
1376        &self,
1377        ideal: &Self::SuitableIdeal<'ring>,
1378        from: (&Self::LocalRingBase<'ring>, usize),
1379        to: (&Self::LocalRingBase<'ring>, usize),
1380        idx: usize,
1381        x: El<Self::LocalRing<'ring>>,
1382    ) -> El<Self::LocalRing<'ring>>
1383    where
1384        Self: 'ring,
1385    {
1386        assert!(idx < self.maximal_ideal_factor_count(ideal));
1387        let QQ = self.base.base_ring();
1388        let ZZ = QQ.base_ring();
1389        to.0.from_canonical_basis(from.0.wrt_canonical_basis(&x).iter().map(|c| {
1390            ZZ.get_ring().lift_partial(
1391                &ideal.prime,
1392                (from.0.base_ring().get_ring(), from.1),
1393                (to.0.base_ring().get_ring(), to.1),
1394                0,
1395                c,
1396            )
1397        }))
1398    }
1399
1400    fn reconstruct_ring_el<'local, 'element, 'ring, V1, V2>(
1401        &self,
1402        ideal: &Self::SuitableIdeal<'ring>,
1403        from: V1,
1404        e: usize,
1405        x: V2,
1406    ) -> Self::Element
1407    where
1408        Self: 'ring,
1409        V1: VectorFn<&'local Self::LocalRing<'ring>>,
1410        V2: VectorFn<&'element El<Self::LocalRing<'ring>>>,
1411        Self::LocalRing<'ring>: 'local,
1412        El<Self::LocalRing<'ring>>: 'element,
1413        'ring: 'local + 'element,
1414    {
1415        assert_eq!(self.maximal_ideal_factor_count(ideal), from.len());
1416        assert_eq!(self.maximal_ideal_factor_count(ideal), x.len());
1417        let QQ = self.base.base_ring();
1418        let ZZ = QQ.base_ring();
1419        let Zpe = from.at(0).base_ring();
1420        assert!(from.iter().all(|ring| ring.base_ring().get_ring() == Zpe.get_ring()));
1421        let ZpeX = DensePolyRing::new(Zpe, "X");
1422        let ZZ_to_Zpe = PolyGCDLocallyReductionMap::new(ZZ.get_ring(), &ideal.prime, Zpe, e, 0);
1423
1424        // compute data necessary for inverse CRT
1425        let mut unit_vectors = (0..self.maximal_ideal_factor_count(ideal))
1426            .map(|_| ZpeX.zero())
1427            .collect::<Vec<_>>();
1428        product_except_one(
1429            &ZpeX,
1430            (&from).map_fn(|galois_ring| galois_ring.generating_poly(&ZpeX, Zpe.identity())),
1431            &mut unit_vectors,
1432        );
1433        let complete_product = ZpeX.mul_ref_fst(&unit_vectors[0], from.at(0).generating_poly(&ZpeX, Zpe.identity()));
1434        assert_el_eq!(
1435            &ZpeX,
1436            &complete_product,
1437            &self.base.generating_poly(
1438                &ZpeX,
1439                ZZ_to_Zpe.compose(LambdaHom::new(QQ, ZZ, |QQ, ZZ, x| {
1440                    assert!(ZZ.is_one(QQ.get_ring().den(x)));
1441                    ZZ.clone_el(QQ.get_ring().num(x))
1442                }))
1443            )
1444        );
1445
1446        for i in 0..self.maximal_ideal_factor_count(ideal) {
1447            let galois_ring = from.at(i);
1448            let inv_normalization_factor = ZpeX.evaluate(
1449                unit_vectors.at(i),
1450                &galois_ring.canonical_gen(),
1451                galois_ring.inclusion(),
1452            );
1453            let normalization_factor = galois_ring.invert(&inv_normalization_factor).unwrap();
1454            let lifted_normalization_factor = galois_ring.poly_repr(&ZpeX, &normalization_factor, Zpe.identity());
1455            let unreduced_new_unit_vector = ZpeX.mul(
1456                std::mem::replace(&mut unit_vectors[i], ZpeX.zero()),
1457                lifted_normalization_factor,
1458            );
1459            unit_vectors[i] = ZpeX.div_rem_monic(unreduced_new_unit_vector, &complete_product).1;
1460        }
1461
1462        // now apply inverse CRT to get the value over ZpeX
1463        let combined = <_ as RingStore>::sum(
1464            &ZpeX,
1465            (0..self.maximal_ideal_factor_count(ideal)).map(|i| {
1466                let galois_ring = from.at(i);
1467                let unreduced_result =
1468                    ZpeX.mul_ref_snd(galois_ring.poly_repr(&ZpeX, x.at(i), Zpe.identity()), &unit_vectors[i]);
1469                ZpeX.div_rem_monic(unreduced_result, &complete_product).1
1470            }),
1471        );
1472
1473        for i in 0..self.maximal_ideal_factor_count(ideal) {
1474            let galois_ring = from.at(i);
1475            debug_assert!(galois_ring.eq_el(
1476                x.at(i),
1477                &ZpeX.evaluate(&combined, &galois_ring.canonical_gen(), galois_ring.inclusion())
1478            ));
1479        }
1480
1481        // now lift the polynomial modulo `p^e` to the rationals
1482        let Zpe_as_zn = ZZ.get_ring().local_ring_as_zn(Zpe);
1483        let Zpe_to_as_zn = Zpe_as_zn.can_hom(Zpe).unwrap();
1484        let result = self.from_canonical_basis((0..self.rank()).map(|i| {
1485            let (num, den) =
1486                balanced_rational_reconstruction(Zpe_as_zn, Zpe_to_as_zn.map_ref(ZpeX.coefficient_at(&combined, i)));
1487            return QQ.div(
1488                &QQ.inclusion().map(int_cast(num, ZZ, Zpe_as_zn.integer_ring())),
1489                &QQ.inclusion().map(int_cast(den, ZZ, Zpe_as_zn.integer_ring())),
1490            );
1491        }));
1492        return result;
1493    }
1494
1495    fn dbg_ideal<'ring>(&self, ideal: &Self::SuitableIdeal<'ring>, out: &mut std::fmt::Formatter) -> std::fmt::Result
1496    where
1497        Self: 'ring,
1498    {
1499        let QQ = self.base.base_ring();
1500        QQ.base_ring().get_ring().dbg_ideal(&ideal.prime, out)
1501    }
1502}
1503
1504impl<Impl, I> Serialize for NumberFieldBase<Impl, I>
1505where
1506    Impl: RingStore + Serialize,
1507    Impl::Type: Field + FreeAlgebra + SerializableElementRing,
1508    <Impl::Type as RingExtension>::BaseRing: RingStore<Type = RationalFieldBase<I>>,
1509    I: RingStore,
1510    I::Type: IntegerRing,
1511{
1512    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1513    where
1514        S: Serializer,
1515    {
1516        SerializableNewtypeStruct::new("NumberField", &self.base).serialize(serializer)
1517    }
1518}
1519
1520impl<'de, Impl, I> Deserialize<'de> for NumberFieldBase<Impl, I>
1521where
1522    Impl: RingStore + Deserialize<'de>,
1523    Impl::Type: Field + FreeAlgebra + SerializableElementRing,
1524    <Impl::Type as RingExtension>::BaseRing: RingStore<Type = RationalFieldBase<I>>,
1525    I: RingStore,
1526    I::Type: IntegerRing,
1527{
1528    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1529    where
1530        D: Deserializer<'de>,
1531    {
1532        DeserializeSeedNewtypeStruct::new("NumberField", PhantomData::<Impl>)
1533            .deserialize(deserializer)
1534            .map(|res| NumberField::create(res).into())
1535    }
1536}
1537
1538#[cfg(test)]
1539use crate::RANDOM_TEST_INSTANCE_COUNT;
1540#[cfg(test)]
1541use crate::iters::multi_cartesian_product;
1542
1543#[test]
1544fn test_principal_ideal_ring_axioms() {
1545    let ZZ = BigIntRing::RING;
1546    let ZZX = DensePolyRing::new(ZZ, "X");
1547
1548    let [f] = ZZX.with_wrapped_indeterminate(|X| [X.pow_ref(2) + 1]);
1549    let K = NumberField::new(&ZZX, &f);
1550
1551    let elements = multi_cartesian_product(
1552        [(-4..4), (-2..2)].into_iter(),
1553        |slice| K.from_canonical_basis(slice.iter().map(|x| K.base_ring().int_hom().map(*x))),
1554        |_, x| *x,
1555    )
1556    .collect::<Vec<_>>();
1557
1558    crate::pid::generic_tests::test_principal_ideal_ring_axioms(&K, elements.iter().map(|x| K.clone_el(x)));
1559}
1560
1561#[test]
1562fn test_adjoin_root() {
1563    let ZZ = BigIntRing::RING;
1564    let QQ = RationalField::new(ZZ);
1565    let QQX = DensePolyRing::new(QQ, "X");
1566    let [f] = QQX.with_wrapped_indeterminate(|X| [2 * X.pow_ref(3) - 1]);
1567    let (K, a) = NumberField::adjoin_root(&QQX, &f);
1568    assert_el_eq!(&K, K.zero(), K.sub(K.mul(K.int_hom().map(2), K.pow(a, 3)), K.one()));
1569}
1570
1571#[test]
1572fn test_poly_gcd_number_field() {
1573    let ZZ = BigIntRing::RING;
1574    let ZZX = DensePolyRing::new(ZZ, "X");
1575
1576    let [f] = ZZX.with_wrapped_indeterminate(|X| [X.pow_ref(2) + 1]);
1577    let K = NumberField::new(&ZZX, &f);
1578    let KY = DensePolyRing::new(&K, "Y");
1579
1580    let i = RingElementWrapper::new(&KY, KY.inclusion().map(K.canonical_gen()));
1581    let [g, h, expected] = KY.with_wrapped_indeterminate(|Y| {
1582        [
1583            (Y.pow_ref(3) + 1) * (Y - &i),
1584            (Y.pow_ref(4) + 2) * (Y.pow_ref(2) + 1),
1585            Y - i,
1586        ]
1587    });
1588    assert_el_eq!(&KY, &expected, <_ as PolyTFracGCDRing>::gcd(&KY, &g, &h));
1589
1590    let [f] = ZZX.with_wrapped_indeterminate(|X| [X.pow_ref(4) - 20 * X.pow_ref(2) + 16]);
1591    let K = NumberField::new(&ZZX, &f);
1592    let KY = DensePolyRing::new(&K, "Y");
1593
1594    let [sqrt3, sqrt7] = K.with_wrapped_generator(|a| [a.pow_ref(3) / 8 - 2 * a, a.pow_ref(3) / 8 - 3 * a]);
1595    assert_el_eq!(&K, K.int_hom().map(3), K.pow(K.clone_el(&sqrt3), 2));
1596    assert_el_eq!(&K, K.int_hom().map(7), K.pow(K.clone_el(&sqrt7), 2));
1597
1598    let half = RingElementWrapper::new(&KY, KY.inclusion().map(K.invert(&K.int_hom().map(2)).unwrap()));
1599    let sqrt3 = RingElementWrapper::new(&KY, KY.inclusion().map(sqrt3));
1600    let sqrt7 = RingElementWrapper::new(&KY, KY.inclusion().map(sqrt7));
1601    let [g, h, expected] = KY.with_wrapped_indeterminate(|Y| {
1602        [
1603            Y.pow_ref(2) - &sqrt3 * Y - 1,
1604            Y.pow_ref(2) + &sqrt7 * Y + 1,
1605            Y - (sqrt3 - sqrt7) * half,
1606        ]
1607    });
1608    let actual = <_ as PolyTFracGCDRing>::gcd(&KY, &g, &h);
1609    assert_el_eq!(&KY, &expected, &actual);
1610}
1611
1612#[test]
1613#[ignore]
1614fn random_test_poly_gcd_number_field() {
1615    let ZZ = BigIntRing::RING;
1616    let QQ = RationalField::new(ZZ);
1617    let ZZX = DensePolyRing::new(ZZ, "X");
1618    let mut rng = oorandom::Rand64::new(1);
1619    let bound = QQ.base_ring().int_hom().map(1000);
1620    let rank = 6;
1621
1622    for _ in 0..RANDOM_TEST_INSTANCE_COUNT {
1623        let genpoly = ZZX.from_terms(
1624            (0..rank)
1625                .map(|i| (ZZ.get_uniformly_random(&bound, || rng.rand_u64()), i))
1626                .chain([(ZZ.one(), rank)].into_iter()),
1627        );
1628
1629        let K = NumberField::new(&ZZX, &genpoly);
1630        let KY = DensePolyRing::new(&K, "Y");
1631
1632        let mut random_element_K = || {
1633            K.from_canonical_basis((0..6).map(|_| {
1634                QQ.inclusion()
1635                    .map(QQ.base_ring().get_uniformly_random(&bound, || rng.rand_u64()))
1636            }))
1637        };
1638        let f = KY.from_terms((0..=5).map(|i| (random_element_K(), i)));
1639        let g = KY.from_terms((0..=5).map(|i| (random_element_K(), i)));
1640        let h = KY.from_terms((0..=4).map(|i| (random_element_K(), i)));
1641        // println!("Testing gcd on ({}) * ({}) and ({}) * ({})", poly_ring.format(&f),
1642        // poly_ring.format(&h), poly_ring.format(&g), poly_ring.format(&h));
1643        let lhs = KY.mul_ref(&f, &h);
1644        let rhs = KY.mul_ref(&g, &h);
1645
1646        let gcd = <_ as PolyTFracGCDRing>::gcd(&KY, &lhs, &rhs);
1647        // println!("Result {}", poly_ring.format(&gcd));
1648
1649        assert!(KY.divides(&lhs, &gcd));
1650        assert!(KY.divides(&rhs, &gcd));
1651        assert!(KY.divides(&gcd, &h));
1652    }
1653}