feanor_math/algorithms/
splitting_field.rs

1use std::alloc::*;
2use std::borrow::Borrow;
3
4use crate::algorithms::convolution::STANDARD_CONVOLUTION;
5use crate::computation::*;
6use crate::homomorphism::*;
7use crate::matrix::OwnedMatrix;
8use crate::rings::extension::galois_field::*;
9use crate::rings::extension::number_field::*;
10use crate::pid::*;
11use crate::primitive_int::StaticRing;
12use crate::rings::extension::extension_impl::*;
13use crate::rings::extension::*;
14use crate::rings::field::{AsField, AsFieldBase};
15use crate::rings::finite::FiniteRingStore;
16use crate::rings::multivariate::{MultivariatePolyRing, MultivariatePolyRingStore};
17use crate::rings::poly::dense_poly::DensePolyRing;
18use crate::rings::poly::*;
19use crate::divisibility::*;
20use crate::field::*;
21use crate::seq::sparse::SparseMapVector;
22use crate::MAX_PROBABILISTIC_REPETITIONS;
23use crate::integer::*;
24use crate::seq::*;
25use crate::ring::*;
26use crate::algorithms::linsolve::*;
27use super::poly_factor::FactorPolyField;
28use super::poly_gcd::PolyTFracGCDRing;
29
30///
31/// Given a number field `K` and an irreducible polynomial `f`, computes a representation of
32/// the number field `L = K[X]/(f)`. The result is returned by the inclusion `K -> L` and 
33/// the element that corresponds to the coset of `X`, i.e. a root of `f` in `L`. Note that the 
34/// canonical generator of `L` does not have to be a root of `f` (this might even be impossible,
35/// e.g. if `f in ZZ[X]` but `K != QQ`).
36/// 
37/// The number field `K` is taken to be the base ring of the given polynomial ring.
38/// 
39/// As opposed to [`extend_number_field_promise_is_irreducible()`], this checks that `f` is 
40/// indeed irreducible.
41/// 
42#[stability::unstable(feature = "enable")]
43pub fn extend_number_field<K, Controller>(poly_ring: DensePolyRing<K>, irred_poly: &El<DensePolyRing<K>>, controller: Controller) -> (
44    FreeAlgebraHom<K, NumberField>,
45    El<NumberField>
46)
47    where K: RingStore<Type = NumberFieldBase<DefaultNumberFieldImpl, BigIntRing>>,
48        Controller: ComputationController
49{
50    assert!(!poly_ring.is_zero(&irred_poly));
51    assert!(poly_ring.degree(&irred_poly).unwrap() > 1);
52    assert!(<NumberFieldBase<_, _> as FactorPolyField>::is_irred(&poly_ring, irred_poly));
53
54    extend_number_field_promise_is_irreducible(poly_ring, irred_poly, controller)
55}
56
57///
58/// If the powers of `potential_primitive_element` up to `[L : K] [K : k]` generate `L`,
59/// this returns the minimal polynomial of `potential_primitive_element`, as well as polynomials
60/// `f, g` such that `f(potential_primitive_element)` and `g(potential_primitive_element)` give
61/// the canonical generators of `K` resp. `L`.
62/// 
63fn test_primitive_element<R>(L: R, potential_primitive_element: El<R>) -> Option<(
64    DensePolyRing<<<<R::Type as RingExtension>::BaseRing as RingStore>::Type as RingExtension>::BaseRing>, 
65    El<DensePolyRing<<<<R::Type as RingExtension>::BaseRing as RingStore>::Type as RingExtension>::BaseRing>>,
66    El<DensePolyRing<<<<R::Type as RingExtension>::BaseRing as RingStore>::Type as RingExtension>::BaseRing>>,
67    El<DensePolyRing<<<<R::Type as RingExtension>::BaseRing as RingStore>::Type as RingExtension>::BaseRing>>
68)>
69    where R: RingStore,
70        R::Type: FreeAlgebra,
71        <<R::Type as RingExtension>::BaseRing as RingStore>::Type: FreeAlgebra,
72        <<<R::Type as RingExtension>::BaseRing as RingStore>::Type as RingExtension>::BaseRing: Clone,
73        <<<<R::Type as RingExtension>::BaseRing as RingStore>::Type as RingExtension>::BaseRing as RingStore>::Type: LinSolveRing
74{
75    let K = L.base_ring();
76    let k = K.base_ring();
77    let total_rank = L.rank() * K.rank();
78
79    // lhs is the matrix whose columns are the powers of `potential_primitive_element`, w.r.t. the tensor basis
80    let mut lhs = OwnedMatrix::zero(total_rank, total_rank, k);
81    let mut current = L.one();
82    for j in 0..total_rank {
83        let current_wrt_basis = L.wrt_canonical_basis(&current);
84        for i1 in 0..L.rank() {
85            let c = current_wrt_basis.at(i1);
86            let c_wrt_basis = K.wrt_canonical_basis(&c);
87            for i2 in 0..K.rank() {
88                *lhs.at_mut(i1 * K.rank() + i2, j) = c_wrt_basis.at(i2);
89            }
90        }
91        drop(current_wrt_basis);
92        L.mul_assign_ref(&mut current, &potential_primitive_element);
93    }
94
95    // rhs has three columns: 
96    // - first the `total_rank`-th power of `x + ka` -> this will later give us the minpoly
97    // - second the generator of `K` -> this will give us a repr of this generator in the result field
98    // - third the generator of `L` -> this will give us a repr of this generator in the result field
99    let mut rhs = OwnedMatrix::zero(total_rank, 3, k);
100    let current_wrt_basis = L.wrt_canonical_basis(&current);
101    for i1 in 0..L.rank() {
102        let c = current_wrt_basis.at(i1);
103        let c_wrt_basis = K.wrt_canonical_basis(&c);
104        for i2 in 0..K.rank() {
105            *rhs.at_mut(i1 * K.rank() + i2, 0) = c_wrt_basis.at(i2);
106        }
107    }
108    if K.rank() > 1 {
109        *rhs.at_mut(1, 1) = k.one();
110    } else {
111        *rhs.at_mut(0, 1) = K.wrt_canonical_basis(&K.canonical_gen()).at(0);
112    }
113    *rhs.at_mut(K.rank(), 2) = k.one();
114
115    let mut sol = OwnedMatrix::zero(total_rank, 3, k);
116    let has_sol = k.solve_right(lhs.data_mut(), rhs.data_mut(), sol.data_mut()).is_solved();
117
118    if has_sol {
119        let kX = DensePolyRing::new(k.clone(), "X");
120        let gen_poly = kX.from_terms((0..total_rank).map(|i| (k.negate(k.clone_el(sol.at(i, 0))), i)).chain([(k.one(), total_rank)].into_iter()));
121        let K_generator = kX.from_terms((0..total_rank).map(|i| (k.clone_el(sol.at(i, 1)), i)));
122        let L_generator = kX.from_terms((0..total_rank).map(|i| (k.clone_el(sol.at(i, 2)), i)));
123        return Some((
124            kX,
125            gen_poly,
126            K_generator,
127            L_generator
128        ));
129    } else {
130        return None;
131    }
132}
133
134#[stability::unstable(feature = "enable")]
135pub fn extend_galois_field<K>(poly_ring: DensePolyRing<K>, irred_poly: &El<DensePolyRing<K>>) -> (
136    FreeAlgebraHom<K, GaloisField>,
137    El<GaloisField>
138)
139    where K: RingStore<Type = GaloisFieldBase<DefaultGaloisFieldImpl>>
140{
141    
142    assert!(!poly_ring.is_zero(&irred_poly));
143    assert!(poly_ring.degree(&irred_poly).unwrap() > 1);
144
145    // I wonder what is the better method: Either factoring the polynomial (taking `O(d^3 log(q))`
146    // operations in `GF(q)`, or up to `d/log(d)` less with better convolution algorithms), or solving
147    // the linear system (taking `O(d^3)` operations in `GF(q)`, or up to `d^(3 - omega)` less with
148    // better matrix inversion algirhtms). I decide for solving the system, it feels better.
149
150    let K = poly_ring.base_ring();
151    let Fp = K.base_ring();
152    
153    let L = AsField::from(
154        AsFieldBase::promise_is_perfect_field(
155            FreeAlgebraImpl::new_with_convolution(
156                K, 
157                poly_ring.degree(&irred_poly).unwrap(), 
158                (0..poly_ring.degree(&irred_poly).unwrap()).map(|i| K.negate(K.clone_el(poly_ring.coefficient_at(&irred_poly, i)))).collect::<Vec<_>>(),
159                "X",
160                Global,
161                STANDARD_CONVOLUTION
162            )
163        )
164    );
165    let total_rank = L.rank() * K.rank();
166
167    let mut rng = oorandom::Rand64::new(1);
168
169    for _ in 0..MAX_PROBABILISTIC_REPETITIONS {
170        let potential_primitive_element = L.random_element(|| rng.rand_u64());
171
172        if let Some((FpX, gen_poly, K_gen, L_gen)) = test_primitive_element(&L, potential_primitive_element) {
173
174            let mut modulus = SparseMapVector::new(total_rank, Fp.clone());
175            for (x, i) in FpX.terms(&gen_poly).filter(|(_, i)| *i < total_rank) {
176                *modulus.at_mut(i) = Fp.clone_el(x);
177            }
178            _ = modulus.at_mut(0);
179            let potential_result = FreeAlgebraImpl::new(
180                Fp.clone(),
181                total_rank,
182                modulus
183            );
184
185            if let Ok(result) = potential_result.as_field() {
186                let result = GaloisField::create(result);
187
188                // note that `sol` contains coefficients w.r.t. `x` and not `result.canonical_gen()`
189                let K_generator = result.from_canonical_basis((0..total_rank).map(|i| Fp.clone_el(FpX.coefficient_at(&K_gen, i))));
190                let L_generator = result.from_canonical_basis((0..total_rank).map(|i| Fp.clone_el(FpX.coefficient_at(&L_gen, i))));
191
192                let result = FreeAlgebraHom::promise_is_well_defined(poly_ring.into().into_base_ring(), result, K_generator);
193                return (result, L_generator);
194            }
195        }
196    }
197    unreachable!()
198}
199
200///
201/// Given a number field `K` and an irreducible polynomial `f`, computes a representation of
202/// the number field `L = K[X]/(f)`. The result is returned by the inclusion `K -> L` and 
203/// the element that corresponds to the coset of `X`, i.e. a root of `f` in `L`. Note that the 
204/// canonical generator of `L` does not have to be a root of `f` (this might even be impossible,
205/// e.g. if `f in ZZ[X]` but `K != QQ`).
206/// 
207/// The number field `K` is taken to be the base ring of the given polynomial ring.
208/// 
209/// This function assumes that the given polynomial is irreducible. If it is not, the results
210/// may be nonsensical (but of course not UB).
211/// 
212#[stability::unstable(feature = "enable")]
213pub fn extend_number_field_promise_is_irreducible<K, Controller>(poly_ring: DensePolyRing<K>, irred_poly: &El<DensePolyRing<K>>, controller: Controller) -> (
214    FreeAlgebraHom<K, NumberField>,
215    El<NumberField>
216)
217    where K: RingStore<Type = NumberFieldBase<DefaultNumberFieldImpl, BigIntRing>>,
218        Controller: ComputationController
219{
220    static_assert_impls!(NumberFieldBase<DefaultNumberFieldImpl, BigIntRing>: FactorPolyField);
221    static_assert_impls!(NumberFieldBase<DefaultNumberFieldImpl, BigIntRing>: PolyTFracGCDRing);
222
223    assert!(!poly_ring.is_zero(&irred_poly));
224    assert!(poly_ring.degree(&irred_poly).unwrap() > 1);
225
226    controller.run_computation(format_args!("extend_number_field(deg={}, extdeg={})", poly_ring.base_ring().rank(), poly_ring.degree(irred_poly).unwrap()), |controller| {
227
228        let K = poly_ring.base_ring();
229
230        let L = AsField::from(
231            AsFieldBase::promise_is_perfect_field(
232                FreeAlgebraImpl::new_with_convolution(
233                    K, 
234                    poly_ring.degree(&irred_poly).unwrap(), 
235                    (0..poly_ring.degree(&irred_poly).unwrap()).map(|i| K.negate(K.clone_el(poly_ring.coefficient_at(&irred_poly, i)))).collect::<Vec<_>>(),
236                    "X",
237                    Global,
238                    STANDARD_CONVOLUTION
239                )
240            )
241        );
242        
243        let total_rank = K.rank() * L.rank();
244
245        // the main task is to find a primitive element of `L`, i.e. that generates it over `K`.
246        // we use the following approach:
247        //  - Consider generator `a` of `K` and `b` of `L` over `K`
248        //  - Consider the set `A_n = { b, b + a, b + 2a, ..., b + (n - 1)a }`
249        //  - Consider also the subset `B_n = { x in A_n | QQ[x] != L }`
250        //  - The idea is now to choose a random element from `A_n`, and hope that it is not in `B_n`
251        //  - To estimate the probability, consider the map `B_n -> { maximal proper subfields of L }` that maps any `x`
252        //    to some maximal proper subfield containing `QQ[x]`
253        //  - Then this map is injective, as any field containing `a + ib` and `a + jb`, `j > i` must contain `a, b`, thus be `L`
254        //  - In other words, to find `n`, we need a bound on the maximal proper subfields
255        //  - I believe the degree `[L : QQ]` is such a bound (in the Galois case it is, at least)
256
257        // take `A` twice as large, so that we find a good element with probability >= 1/2
258        let size_of_A: i32 = (2 * total_rank).try_into().unwrap();
259
260        let mut rng = oorandom::Rand64::new(1);
261
262        for _ in 0..MAX_PROBABILISTIC_REPETITIONS {
263
264            let a = StaticRing::<i32>::RING.get_uniformly_random(&size_of_A, || rng.rand_u64());
265            let potential_primitive_element = L.add(L.canonical_gen(), L.inclusion().map(K.int_hom().mul_map(K.canonical_gen(), a)));
266        
267            if let Some((QQX, gen_poly, K_gen, L_gen)) = test_primitive_element(&L, potential_primitive_element) {
268                if let Some((result, x)) = NumberField::try_adjoin_root(&QQX, &gen_poly) {
269
270                    // note that `sol` contains coefficients w.r.t. `x` and not `result.canonical_gen()`
271                    let K_generator = QQX.evaluate(&K_gen, &x, result.inclusion());
272                    let L_generator = QQX.evaluate(&L_gen, &x, result.inclusion());
273
274                    let result = FreeAlgebraHom::promise_is_well_defined(poly_ring.into().into_base_ring(), result, K_generator);
275
276                    log_progress!(controller, "success");
277                    return (result, L_generator);
278                } else {
279                    unreachable!()
280                }
281            } else {
282                log_progress!(controller, "(not_primitive)");
283            }
284        }
285
286        unreachable!()
287})
288}
289
290///
291/// Given a polynomial `f in K[X]` over a field `K`, this computes an 
292/// extension `L` of `K` such that `f` splits in `L`.
293/// 
294/// This function returns the embedding `K -> L` and the list of roots of `f`.
295/// 
296/// The closure `create_field` should, when given a field `K'` and an irreducible
297/// polynomial `g in K'[X]`, compute an extension field `K''`, and return both the
298/// embedding `K' -> K''` and a root `a in K''` of `g`.
299/// 
300#[stability::unstable(feature = "enable")]
301pub fn splitting_field<K, F, Controller>(
302    mut poly_ring: DensePolyRing<K>, 
303    f: El<DensePolyRing<K>>, 
304    mut create_field: F,
305    controller: Controller
306) -> (
307    FreeAlgebraHom<K, K>,
308    Vec<(El<K>, usize)>
309)
310    where K: RingStore + Clone,
311        K::Type: FactorPolyField + FreeAlgebra,
312        F: for<'a> FnMut(DensePolyRing<&'a K>, El<DensePolyRing<&'a K>>, Controller) -> (FreeAlgebraHom<&'a K, K>, El<K>),
313        Controller: ComputationController
314{
315    assert!(!poly_ring.is_zero(&f));
316    let mut to_split = vec![(f, 1)];
317    let mut roots = Vec::new();
318    let mut base_field = None;
319    let mut image_of_base_can_gen = poly_ring.base_ring().canonical_gen();
320
321    controller.run_computation(format_args!("splitting_field(base_deg={}, deg={})", poly_ring.base_ring().rank(), poly_ring.degree(&to_split[0].0).unwrap()), |controller| {
322
323        while let Some((next_to_split, multiplicity)) = to_split.pop() {
324            let (mut factorization, _) = <_ as FactorPolyField>::factor_poly_with_controller(&poly_ring, &next_to_split, controller.clone());
325            let extend_idx = factorization.iter().enumerate().max_by_key(|(_, (f, _))| poly_ring.degree(f).unwrap()).unwrap().0;
326            
327            let extend_with_poly = if poly_ring.degree(&factorization[extend_idx].0).unwrap() > 1 {
328                Some(factorization.remove(extend_idx))
329            } else {
330                None
331            };
332
333            for (g, e) in factorization.into_iter() {
334                if poly_ring.degree(&g).unwrap() > 1 {
335                    to_split.push((g, e * multiplicity));
336                } else {
337                    roots.push((poly_ring.base_ring().negate(poly_ring.base_ring().div(poly_ring.coefficient_at(&g, 0), poly_ring.coefficient_at(&g, 1))), e * multiplicity));
338                }
339            }
340
341            if let Some((extend_with_poly, e)) = extend_with_poly {
342                log_progress!(controller, "({})", poly_ring.degree(&extend_with_poly).unwrap());
343                let ref_poly_ring = DensePolyRing::new(poly_ring.base_ring(), "X");
344                let ref_extend_with_poly = ref_poly_ring.lifted_hom(&poly_ring, poly_ring.base_ring().identity()).map_ref(&extend_with_poly);
345                let (into_new_field, root) = create_field(ref_poly_ring, ref_extend_with_poly, controller.clone());
346                let (old_field, new_field, image) = into_new_field.destruct();
347                let new_poly_ring = DensePolyRing::new(new_field, "X");
348                let into_new_field = FreeAlgebraHom::promise_is_well_defined(old_field, new_poly_ring.base_ring(), image);
349                
350                image_of_base_can_gen = into_new_field.map(image_of_base_can_gen);
351                roots = roots.into_iter().map(|(r, e)| (into_new_field.map(r), e)).collect();
352                let lifted_hom = new_poly_ring.lifted_hom(&poly_ring, &into_new_field);
353                to_split = to_split.into_iter().map(|(f, e)| (lifted_hom.map(f), e)).collect();
354
355                to_split.push((new_poly_ring.checked_div(
356                    &lifted_hom.map(extend_with_poly), 
357                    &new_poly_ring.sub(new_poly_ring.indeterminate(), new_poly_ring.inclusion().map_ref(&root))
358                ).unwrap(), multiplicity * e));
359                roots.push((root, multiplicity * e));
360
361                if base_field.is_none() {
362                    base_field = Some(poly_ring.into().into_base_ring());
363                }
364                poly_ring = new_poly_ring;
365            }
366        }
367        return (
368            FreeAlgebraHom::promise_is_well_defined(
369                base_field.unwrap_or_else(|| poly_ring.clone().into().into_base_ring()),
370                poly_ring.into().into_base_ring(),
371                image_of_base_can_gen
372            ),
373            roots
374        );
375})
376}
377
378///
379/// The closure `create_field` should, when given a field `K'` and an irreducible
380/// polynomial `g in K'[X]`, compute an extension field `K''`, and return both the
381/// embedding `K' -> K''` and a root `a in K''` of `g`.
382/// 
383#[stability::unstable(feature = "enable")]
384pub fn variety_from_lex_gb<K, P, F, Controller>(
385    poly_ring: P, 
386    lex_gb: &[El<P>], 
387    mut create_field: F,
388    controller: Controller
389) -> (FreeAlgebraHom<K, K>, Vec<Vec<El<K>>>)
390    where P: RingStore,
391        K: RingStore + Clone,
392        K::Type: FactorPolyField + FreeAlgebra,
393        P::Type: MultivariatePolyRing,
394        <P::Type as RingExtension>::BaseRing: Borrow<K> + RingStore<Type = K::Type>,
395        F: for<'a> FnMut(DensePolyRing<&'a K>, El<DensePolyRing<&'a K>>, Controller) -> (FreeAlgebraHom<&'a K, K>, El<K>),
396        Controller: ComputationController
397{
398    let n = poly_ring.indeterminate_count();
399    let constant_monomial = poly_ring.create_monomial((0..n).map(|_| 0));
400    if lex_gb.iter().any(|f| poly_ring.appearing_indeterminates(f).len() == 0 && !poly_ring.base_ring().is_zero(poly_ring.coefficient_at(f, &constant_monomial))) {
401        return (FreeAlgebraHom::identity(poly_ring.base_ring().borrow().clone()), Vec::new());
402    }
403
404    let mut relevant_indeterminates = Vec::new();
405    for f in lex_gb {
406        relevant_indeterminates.extend(poly_ring.appearing_indeterminates(f).into_iter().map(|(v, _)| v));
407    }
408    relevant_indeterminates.sort_unstable();
409    relevant_indeterminates.dedup();
410    let contains_indeterminate = |f, indet| poly_ring.appearing_indeterminates(f).iter().any(|(v, _)| v == indet);
411    let has_no_indeterminate_before = |f, indet| poly_ring.appearing_indeterminates(f).iter().all(|(v, _)| v >= indet);
412    
413    let polys_for_indeterminate = relevant_indeterminates.iter().map(|indet| {
414        let relevant_polys = lex_gb.iter().filter(|f| contains_indeterminate(f, indet) && has_no_indeterminate_before(f, indet)).collect::<Vec<_>>();
415        assert!(relevant_polys.len() > 0, "basis is either not a lex-GB or does not generate a zero-dimensional ideal");
416        return relevant_polys;
417    }).collect::<Vec<_>>();
418
419    let mut current_embedding: FreeAlgebraHom<_, K> = FreeAlgebraHom::identity(poly_ring.base_ring().borrow().clone());
420    let mut current_roots: Vec<(usize, Vec<El<K>>)> = vec![(0, (0..relevant_indeterminates.len()).map(|_| current_embedding.codomain().zero()).collect())];
421    let mut final_roots: Vec<Vec<El<K>>> = Vec::new();
422
423    while let Some((set_indeterminates, current_root)) = current_roots.pop() {
424        let next_indet_idx = relevant_indeterminates.len() - set_indeterminates - 1;
425        let next_indet = relevant_indeterminates[next_indet_idx];
426        
427        let (base_field, current_field, base_field_gen) = current_embedding.destruct();
428        let KX = DensePolyRing::new(current_field, "X");
429        let ref_current_embedding = FreeAlgebraHom::promise_is_well_defined(&base_field, KX.base_ring(), base_field_gen);
430        let next_roots_from: El<DensePolyRing<K>> = polys_for_indeterminate[next_indet_idx].iter().map(|f| 
431            poly_ring.evaluate(
432                f, 
433                (0..n).map_fn(|i| if i == next_indet {
434                    KX.indeterminate()
435                } else {
436                    KX.inclusion().map_ref(&current_root[i])
437                }),
438                KX.inclusion().compose(&ref_current_embedding)
439            )
440        ).fold(KX.zero(), |current, next| KX.ideal_gen(&current, &next));
441
442        assert!(!KX.is_zero(&next_roots_from), "basis is either not a lex-GB or does not generate a zero-dimensional ideal");
443        if KX.degree(&next_roots_from).unwrap() == 0 {
444            let (_, _, base_field_gen) = ref_current_embedding.destruct();
445            current_embedding = FreeAlgebraHom::promise_is_well_defined(base_field, KX.into().into_base_ring(), base_field_gen);
446            // continue
447        } else if KX.degree(&next_roots_from).unwrap() == 1 {
448            let mut new_root = current_root;
449            new_root[next_indet_idx] = KX.base_ring().negate(KX.base_ring().div(KX.coefficient_at(&next_roots_from, 0), KX.coefficient_at(&next_roots_from, 1)));
450            if set_indeterminates + 1 < relevant_indeterminates.len() {
451                current_roots.push((set_indeterminates + 1, new_root));
452            } else {
453                final_roots.push(new_root);
454            }
455            let (_, _, base_field_gen) = ref_current_embedding.destruct();
456            current_embedding = FreeAlgebraHom::promise_is_well_defined(base_field, KX.into().into_base_ring(), base_field_gen);
457        } else {
458            let (_, _, base_field_gen) = ref_current_embedding.destruct();
459            let (into_new_field, roots) = splitting_field(KX, next_roots_from, &mut create_field, controller.clone());
460
461            final_roots = final_roots.into_iter().map(|root| root.into_iter().map(|x| into_new_field.map(x)).collect()).collect();
462            current_roots = current_roots.into_iter().map(|(l, root)| (l, root.into_iter().map(|x| into_new_field.map(x)).collect())).collect();
463            for (r, _) in roots.into_iter() {
464                let mut new_root: Vec<El<K>> = current_root.iter().map(|x| into_new_field.map_ref(x)).collect();
465                new_root[next_indet_idx] = r;
466                if set_indeterminates + 1 < relevant_indeterminates.len() {
467                    current_roots.push((set_indeterminates + 1, new_root));
468                } else {
469                    final_roots.push(new_root);
470                }
471            }
472            let base_field_gen = into_new_field.map(base_field_gen);
473            let (_, new_field, _) = into_new_field.destruct();
474            current_embedding = FreeAlgebraHom::promise_is_well_defined(base_field, new_field, base_field_gen);
475        }
476    }
477    return (current_embedding, final_roots);
478}
479
480#[cfg(test)]
481use crate::wrapper::RingElementWrapper;
482#[cfg(test)]
483use crate::rings::rational::RationalField;
484#[cfg(test)]
485use crate::rings::multivariate::multivariate_impl::MultivariatePolyRingImpl;
486
487#[test]
488fn test_extend_field() {
489    let ZZ = BigIntRing::RING;
490    let QQ = RationalField::new(ZZ);
491    let ZZX = DensePolyRing::new(&ZZ, "X");
492    let [f] = ZZX.with_wrapped_indeterminate(|X| [X.pow_ref(2) + 1]);
493    let K = NumberField::new(ZZX, &f);
494    let KX = DensePolyRing::new(&K, "X");
495
496    // extend `QQ[i]` by `X^4 - i`
497    let [g] = KX.with_wrapped_indeterminate(|X| [X.pow_ref(4) - RingElementWrapper::new(&KX, KX.inclusion().map(K.canonical_gen()))]);
498    let (extension_field_embedding, x) = extend_number_field(KX.clone(), &g, TEST_LOG_PROGRESS);
499    let ext_field = extension_field_embedding.codomain();
500    assert_eq!(8, ext_field.rank());
501    assert_el_eq!(ext_field, ext_field.neg_one(), ext_field.pow(extension_field_embedding.map(K.canonical_gen()), 2));
502    assert_el_eq!(ext_field, extension_field_embedding.map(K.canonical_gen()), ext_field.pow(x, 4));
503
504    crate::homomorphism::generic_tests::test_homomorphism_axioms(
505        &extension_field_embedding, 
506        [(0, 0), (0, 1), (1, 0), (2, 0), (-1, 0), (0, -1), (-1, -1)].into_iter().map(|(a, b)| K.from_canonical_basis([QQ.int_hom().map(a), QQ.int_hom().map(b)]))
507    );
508
509    // extend `QQ[i]` by `X^3 - 2`
510    let [g] = KX.with_wrapped_indeterminate(|X| [X.pow_ref(3) - 2]);
511    let (extension_field_embedding, x) = extend_number_field(KX, &g, TEST_LOG_PROGRESS);
512    let ext_field = extension_field_embedding.codomain();
513    assert_eq!(6, ext_field.rank());
514    assert_el_eq!(ext_field, ext_field.neg_one(), ext_field.pow(extension_field_embedding.map(K.canonical_gen()), 2));
515    assert_el_eq!(ext_field, ext_field.int_hom().map(2), ext_field.pow(x, 3));
516    
517    crate::homomorphism::generic_tests::test_homomorphism_axioms(
518        &extension_field_embedding, 
519        [(0, 0), (0, 1), (1, 0), (2, 0), (-1, 0), (0, -1), (-1, -1)].into_iter().map(|(a, b)| K.from_canonical_basis([QQ.int_hom().map(a), QQ.int_hom().map(b)]))
520    );
521}
522
523#[test]
524fn test_variety_from_lex_gb() {
525    unimplemented!("Currently super slow");
526    let ZZX = DensePolyRing::new(BigIntRing::RING, "X");
527    let [f] = ZZX.with_wrapped_indeterminate(|X| [X - 1]);
528    let QQ = NumberField::new(ZZX, &f);
529    let QQXYZ = MultivariatePolyRingImpl::new(&QQ, 3);
530    let intersection_unitsphere_twistedcubic = QQXYZ.with_wrapped_indeterminates(|[x, y, z]| [
531        x.pow_ref(2) + y.pow_ref(2) + z.pow_ref(2) - 1, 
532        y - x.pow_ref(2), 
533        z - x.pow_ref(2)
534    ]);
535    let lex_gb = QQXYZ.with_wrapped_indeterminates(|[x, y, z]| [
536        z.pow_ref(6) - 5 * z.pow_ref(4) + 7 * z.pow_ref(2) - 1,
537        y - z.pow_ref(4) + 3 * z.pow_ref(2) - 1,
538        x + z.pow_ref(3) - 2 * z
539    ]);
540    let (into_L, variety) = variety_from_lex_gb(&QQXYZ, &lex_gb, |poly_ring, poly, controller| extend_number_field_promise_is_irreducible(poly_ring, &poly, controller), TEST_LOG_PROGRESS);
541    let L = into_L.codomain();
542
543    assert_eq!(6, variety.len());
544    for point in &variety {
545        assert_el_eq!(L, L.zero(), QQXYZ.evaluate(&intersection_unitsphere_twistedcubic[0], point.clone_ring_els(L), &into_L));
546        assert_el_eq!(L, L.zero(), QQXYZ.evaluate(&intersection_unitsphere_twistedcubic[1], point.clone_ring_els(L), &into_L));
547        assert_el_eq!(L, L.zero(), QQXYZ.evaluate(&intersection_unitsphere_twistedcubic[2], point.clone_ring_els(L), &into_L));
548    }
549}