Skip to main content

feanor_math/algorithms/
discrete_log.rs

1use std::alloc::Global;
2use std::collections::HashMap;
3use std::marker::PhantomData;
4use std::mem::replace;
5use std::rc::Rc;
6
7use feanor_serde::dependent_tuple::DeserializeSeedDependentTuple;
8use feanor_serde::impl_deserialize_seed_for_dependent_struct;
9use feanor_serde::map::DeserializeSeedMapped;
10use feanor_serde::newtype_struct::{DeserializeSeedNewtypeStruct, SerializableNewtypeStruct};
11use feanor_serde::seq::{DeserializeSeedSeq, SerializableSeq};
12use serde::{Deserialize, Serialize};
13
14use crate::algorithms::eea::{inv_crt, signed_gcd};
15use crate::algorithms::int_bisect::root_floor;
16use crate::algorithms::int_factor::factor;
17use crate::algorithms::linsolve::LinSolveRingStore;
18use crate::algorithms::linsolve::smith::{determinant_using_pre_smith, pre_smith};
19use crate::algorithms::lll::exact::lll;
20use crate::algorithms::matmul::{MatmulAlgorithm, STANDARD_MATMUL};
21use crate::divisibility::{DivisibilityRing, DivisibilityRingStore};
22use crate::field::FieldStore;
23use crate::group::{HashableGroupEl, MultGroup, *};
24use crate::homomorphism::Homomorphism;
25use crate::integer::{BigIntRing, int_cast};
26use crate::iters::multi_cartesian_product;
27use crate::matrix::transform::TransformTarget;
28use crate::matrix::*;
29use crate::ordered::OrderedRingStore;
30use crate::pid::PrincipalIdealRingStore;
31use crate::primitive_int::StaticRing;
32use crate::ring::*;
33use crate::rings::finite::FiniteRingStore;
34use crate::rings::rational::RationalField;
35use crate::rings::zn::{ZnRing, ZnRingStore, zn_big};
36use crate::serialization::{DeserializeWithRing, SerializeWithRing};
37
38const ZZ: StaticRing<i64> = StaticRing::<i64>::RING;
39const ZZbig: BigIntRing = BigIntRing::RING;
40
41/// Represents a subgroup of an [`AbelianGroupBase`] by a set of generators.
42/// Supports computing discrete logarithms, i.e. representing a given element
43/// as a combination of the generators.
44///
45/// Note that the used algorithms have a worst case complexity of `O(sqrt(ord^n))`
46/// where `ord` is the given multiple of the orders of each generator, and `n`
47/// is the number of generators. However, if `ord` is smooth, much faster algorithms
48/// are used.
49#[stability::unstable(feature = "enable")]
50pub struct SubgroupBase<G: AbelianGroupStore> {
51    parent: G,
52    generators: Vec<GroupEl<G>>,
53    order_multiple: El<BigIntRing>,
54    order_factorization: Vec<(i64, usize)>,
55    /// the `(i, j)`-th entry has rows that form a basis of the relation lattice of
56    /// the set `n/pi^j g1, ..., n/pi^j gk` (where `n` is the order of the group,
57    /// and the `pi^ei` are its prime power factors)
58    scaled_relation_lattices: Vec<Vec<OwnedMatrix<i64>>>,
59    /// the `(i, j, k)`-th entry contains `sum_l row[l] n/pi^(j + 1) gl`, where
60    /// `row` is the `k`-th row of `scaled_relation_lattice[i, j]`; These values
61    /// are important, since they form a basis of the `p`-torsion subgroup of
62    /// `< n/pi^(j + 1) g1, ..., n/pi^(j + 1) gk >`
63    scaled_generating_sets: Vec<Vec<Vec<GroupEl<G>>>>,
64}
65
66/// [`GroupStore`] of [`SubgroupBase`]
67#[stability::unstable(feature = "enable")]
68#[allow(type_alias_bounds)]
69pub type Subgroup<G: AbelianGroupStore> = GroupValue<SubgroupBase<G>>;
70
71impl<G: AbelianGroupStore> Subgroup<G> {
72    /// Creates a new [`GeneratingSet`] representing the subgroup generated
73    /// by the given generators.
74    ///
75    /// The value `order_multiple` should be a multiple of the order of every
76    /// generator, including generators that will be added later on via
77    /// [`GeneratingSet::add_generator()`].
78    #[stability::unstable(feature = "enable")]
79    pub fn new(group: G, order_multiple: El<BigIntRing>, generators: Vec<GroupEl<G>>) -> Self {
80        let n = generators.len();
81        if n == 0 {
82            return GroupValue::from(SubgroupBase {
83                parent: group,
84                generators: Vec::new(),
85                order_multiple: ZZbig.clone_el(&order_multiple),
86                order_factorization: factor(ZZbig, order_multiple)
87                    .into_iter()
88                    .map(|(p, e)| (int_cast(p, ZZ, ZZbig), e))
89                    .collect(),
90                scaled_generating_sets: Vec::new(),
91                scaled_relation_lattices: Vec::new(),
92            });
93        } else {
94            let mut result = Self::new(group, order_multiple, Vec::new());
95            for g in generators {
96                result = result.add_generator(g);
97            }
98            return result;
99        }
100    }
101
102    /// Returns the group that this group is a subgroup of.
103    #[stability::unstable(feature = "enable")]
104    pub fn parent(&self) -> &G { self.get_group().parent() }
105
106    /// Returns the order of the subgroup, i.e. the number of elements.
107    #[stability::unstable(feature = "enable")]
108    pub fn subgroup_order(&self) -> El<BigIntRing> { self.get_group().subgroup_order() }
109
110    /// Returns the stored generating set of the subgroup.
111    #[stability::unstable(feature = "enable")]
112    pub fn generators(&self) -> &[GroupEl<G>] { self.get_group().generators() }
113
114    /// Adds a generator to this subgroup, returning a new, larger subgroup.
115    #[stability::unstable(feature = "enable")]
116    pub fn add_generator(self, new_gen_base: GroupEl<G>) -> Self { Self::from(self.into().add_generator(new_gen_base)) }
117
118    /// Checks whether the given element of the parent group is contained
119    /// in the subgroup.
120    #[stability::unstable(feature = "enable")]
121    pub fn contains(&self, element: &GroupEl<G>) -> bool { self.get_group().contains(element) }
122
123    /// Writes the given element of the parent group as a combination of the
124    /// subgroup generators, if this exists.
125    #[stability::unstable(feature = "enable")]
126    pub fn dlog(&self, target: &GroupEl<G>) -> Option<Vec<i64>> { self.get_group().dlog(target) }
127
128    /// Returns an iterator over all elements of the subgroup.
129    #[stability::unstable(feature = "enable")]
130    pub fn enumerate_elements<'a>(&'a self) -> impl use<'a, G> + Clone + Iterator<Item = GroupEl<G>> {
131        self.get_group().enumerate_elements()
132    }
133}
134
135impl<G: AbelianGroupStore> SubgroupBase<G> {
136    #[stability::unstable(feature = "enable")]
137    pub fn parent(&self) -> &G { &self.parent }
138
139    /// The number of elements in the subgroup generated by this generating set.
140    #[stability::unstable(feature = "enable")]
141    pub fn subgroup_order(&self) -> El<BigIntRing> {
142        let mut result = ZZbig.one();
143        let n = self.generators.len();
144        if n == 0 {
145            return result;
146        }
147        for i in 0..self.order_factorization.len() {
148            let (p, e) = self.order_factorization[i];
149            let relation_lattice = self.scaled_relation_lattices[i][e].data();
150            let Zpne = zn_big::Zn::new(ZZbig, ZZbig.pow(int_cast(p, ZZbig, StaticRing::<i64>::RING), e * n));
151            let mod_pne = Zpne.can_hom(&StaticRing::<i64>::RING).unwrap();
152            let relation_lattice_det = determinant_using_pre_smith(
153                &Zpne,
154                OwnedMatrix::from_fn(relation_lattice.row_count(), relation_lattice.col_count(), |k, l| {
155                    mod_pne.map(*relation_lattice.at(k, l))
156                })
157                .data_mut(),
158                Global,
159            );
160            ZZbig.mul_assign(
161                &mut result,
162                signed_gcd(
163                    ZZbig.clone_el(Zpne.modulus()),
164                    Zpne.smallest_positive_lift(relation_lattice_det),
165                    ZZbig,
166                ),
167            );
168        }
169        return result;
170    }
171
172    /// Returns a multiple of the order of each element in the subgroup
173    /// generated by this generating set.
174    #[stability::unstable(feature = "enable")]
175    pub fn order_multiple(&self) -> &El<BigIntRing> { &self.order_multiple }
176
177    /// Returns a set of generators of this subgroup.
178    #[stability::unstable(feature = "enable")]
179    pub fn generators(&self) -> &[GroupEl<G>] { &self.generators }
180
181    /// Extends the generating set by an additional generator, which is likely
182    /// to grow the represented subgroup.
183    ///
184    /// The new generator must be of order dividing [`GeneratingSet::order_multiple()`].
185    #[stability::unstable(feature = "enable")]
186    pub fn add_generator(self, new_gen_base: GroupEl<G>) -> Self {
187        let group = &self.parent;
188        assert!(group.is_identity(&group.pow(&new_gen_base, &self.order_multiple)));
189        let ZZ_to_ZZbig = ZZbig.can_hom(&ZZ).unwrap();
190
191        let mut scaled_relation_lattices = Vec::new();
192        let mut scaled_generating_sets = Vec::new();
193        for p_idx in 0..self.order_factorization.len() {
194            let (p, e) = self.order_factorization[p_idx];
195            let p_bigint = ZZ_to_ZZbig.map(p);
196            let power = ZZbig
197                .checked_div(&self.order_multiple, &ZZbig.pow(ZZbig.clone_el(&p_bigint), e))
198                .unwrap();
199            let gens = self.generators.iter().map(|g| group.pow(g, &power)).collect::<Vec<_>>();
200            let new_gen = group.pow(&new_gen_base, &power);
201
202            let n = self.generators.len();
203
204            let mut main_relation_matrix = OwnedMatrix::zero(n + 1, n + 1, ZZ);
205            for i in 0..n {
206                for j in 0..n {
207                    *main_relation_matrix.at_mut(i, j) = *self.scaled_relation_lattices[p_idx][e].at(i, j);
208                }
209            }
210            *main_relation_matrix.at_mut(n, n) = -ZZ.pow(p, e);
211            for k in 0..e {
212                if let Some(dlog) =
213                    self.padic_dlog(p_idx, e, &group.pow(&new_gen, &ZZbig.pow(ZZbig.clone_el(&p_bigint), k)))
214                {
215                    *main_relation_matrix.at_mut(n, n) = -ZZ.pow(p, k);
216                    for j in 0..n {
217                        *main_relation_matrix.at_mut(n, j) = dlog[j];
218                    }
219                    break;
220                }
221            }
222            debug_assert!(main_relation_matrix.data().row_iter().all(|row| group.is_identity(
223                &(0..n).fold(group.pow(&new_gen, &ZZ_to_ZZbig.map(row[n])), |current, i| {
224                    group.op(current, group.pow(&gens[i], &ZZ_to_ZZbig.map(row[i])))
225                })
226            )));
227
228            let mut result = Vec::with_capacity(e + 1);
229            result.push(main_relation_matrix);
230            for _ in 0..e {
231                result.push(Self::relation_lattice_basis_downscale_p(
232                    result.last().unwrap().data(),
233                    p,
234                ));
235            }
236            result.reverse();
237            scaled_relation_lattices.push(result);
238
239            let mut generating_sets = Vec::new();
240            for i in 0..e {
241                let generating_set = scaled_relation_lattices.last().unwrap()[i]
242                    .data()
243                    .row_iter()
244                    .map(|row| {
245                        let scale = ZZbig.pow(ZZbig.clone_el(&p_bigint), e - i - 1);
246                        let result = (0..n).fold(
247                            group.pow(&new_gen, &ZZ_to_ZZbig.mul_ref_map(&scale, &row[n])),
248                            |current, j| {
249                                group.op(current, group.pow(&gens[j], &ZZ_to_ZZbig.mul_ref_map(&scale, &row[j])))
250                            },
251                        );
252                        debug_assert!(group.is_identity(&group.pow(&result, &p_bigint)));
253                        result
254                    })
255                    .collect::<Vec<_>>();
256                generating_sets.push(generating_set);
257            }
258            scaled_generating_sets.push(generating_sets);
259        }
260
261        return Self {
262            generators: self
263                .generators
264                .iter()
265                .map(|g| group.clone_el(g))
266                .chain([new_gen_base])
267                .collect(),
268            order_multiple: ZZbig.clone_el(&self.order_multiple),
269            order_factorization: self.order_factorization.clone(),
270            scaled_generating_sets,
271            scaled_relation_lattices,
272            parent: self.parent,
273        };
274    }
275
276    /// # Algorithm
277    ///  
278    /// We are working over `G = ord/p^e global_group`, in which every element
279    /// has order dividing `p^e`. Clearly, it is generated by the global generators,
280    /// scaled by `ord/p^e`.
281    ///
282    /// We want to compute a dlog of `x` w.r.t. `g1, ..., gn`. For this, we use the exact sequence
283    /// ```text
284    ///   0  ->  H  ->  G  ->  G/H  ->  0
285    /// ```
286    /// where `H = { a in G | p a = 0 }` is the `p`-torsion subgroup. Note that the
287    /// power-of-`p` map gives an isomorphism `G/H -> pG`, which allows us to recursively
288    /// solve dlog in `G/H`. Hence, we want to solve dlog in `H`, which we can do using
289    /// the baby-giant step method - if we can find a generating set of `H`. We find it
290    /// using the already provided basis of the relation modulo of the generators.
291    fn padic_dlog(&self, p_idx: usize, e: usize, target: &GroupEl<G>) -> Option<Vec<i64>> {
292        let group = &self.parent;
293        let ZZ_to_ZZbig = ZZbig.can_hom(&ZZ).unwrap();
294
295        let n = self.generators.len();
296        if n == 0 {
297            return if group.is_identity(target) {
298                Some(Vec::new())
299            } else {
300                None
301            };
302        } else if e == 0 {
303            debug_assert!(group.is_identity(target));
304            return Some((0..n).map(|_| 0).collect());
305        }
306
307        let p = self.order_factorization[p_idx].0;
308        debug_assert!(group.is_identity(&group.pow(target, &ZZbig.pow(ZZ_to_ZZbig.map(p), e))));
309
310        let power = ZZbig
311            .checked_div(&self.order_multiple, &ZZbig.pow(int_cast(p, ZZbig, ZZ), e))
312            .unwrap();
313        let gens = self.generators.iter().map(|g| group.pow(g, &power)).collect::<Vec<_>>();
314
315        // here we use the power-of-`p` map and the fact that `G/H ~ pG` to compute the dlog in
316        // `G/H`
317        let G_mod_H_dlog = self.padic_dlog(p_idx, e - 1, &group.pow(target, &ZZ_to_ZZbig.map(p)))?;
318        debug_assert!(group.eq_el(
319            &group.pow(target, &ZZ_to_ZZbig.map(p)),
320            &(0..n).fold(group.identity(), |current, i| {
321                group.op(current, group.pow(&gens[i], &ZZ_to_ZZbig.map(p * G_mod_H_dlog[i])))
322            })
323        ));
324
325        // delta is now in H, i.e. is a p-torsion element
326        let delta = (0..n).fold(group.clone_el(target), |current, i| {
327            group.op(current, group.pow(&gens[i], &ZZ_to_ZZbig.map(-G_mod_H_dlog[i])))
328        });
329        debug_assert!(group.is_identity(&group.pow(&delta, &ZZ_to_ZZbig.map(p))));
330
331        let H_generators = &self.scaled_generating_sets[p_idx][e - 1];
332
333        let H_dlog_wrt_H_gens = baby_giant_step(
334            group,
335            delta,
336            H_generators,
337            &(0..n).map(|_| int_cast(p, ZZbig, ZZ)).collect::<Vec<_>>(),
338        )?;
339        let H_dlog = {
340            let mut result = (0..n).map(|_| 0).collect::<Vec<_>>();
341            STANDARD_MATMUL.matmul(
342                TransposableSubmatrix::from(Submatrix::from_1d(&H_dlog_wrt_H_gens, 1, n)),
343                TransposableSubmatrix::from(self.scaled_relation_lattices[p_idx][e - 1].data()),
344                TransposableSubmatrixMut::from(SubmatrixMut::from_1d(&mut result, 1, n)),
345                ZZ,
346            );
347            result
348        };
349
350        let result = G_mod_H_dlog
351            .into_iter()
352            .zip(H_dlog)
353            .map(|(x, y)| x + y)
354            .collect::<Vec<_>>();
355        debug_assert!(group.eq_el(
356            target,
357            &(0..n).fold(group.identity(), |current, i| {
358                group.op(current, group.pow(&gens[i], &ZZ_to_ZZbig.map(result[i])))
359            })
360        ));
361
362        return Some(result);
363    }
364
365    /// Returns `true` if the given element is contained in this subgroup.
366    #[stability::unstable(feature = "enable")]
367    pub fn contains(&self, element: &GroupEl<G>) -> bool { self.dlog(element).is_some() }
368
369    /// Computes a discrete logarithm of `target` w.r.t. the stored set
370    /// if generators, or `None` if `target` is not in the subgroup generated by
371    /// these generators
372    #[stability::unstable(feature = "enable")]
373    pub fn dlog(&self, target: &GroupEl<G>) -> Option<Vec<i64>> {
374        let group = &self.parent;
375        let ZZ_to_ZZbig = ZZbig.can_hom(&ZZ).unwrap();
376
377        let n = self.generators.len();
378        if n == 0 {
379            return if group.is_identity(target) {
380                Some(Vec::new())
381            } else {
382                None
383            };
384        }
385
386        let mut current_dlog = (0..n).map(|_| 0).collect::<Vec<_>>();
387        let mut current_order = (0..n).map(|_| 1).collect::<Vec<_>>();
388
389        for p_idx in 0..self.order_factorization.len() {
390            let (p, e) = self.order_factorization[p_idx];
391            let power = ZZbig
392                .checked_div(&self.order_multiple, &ZZbig.pow(int_cast(p, ZZbig, ZZ), e))
393                .unwrap();
394            let padic_dlog = self.padic_dlog(p_idx, e, &group.pow(target, &power))?;
395            for j in 0..n {
396                current_dlog[j] = inv_crt(current_dlog[j], padic_dlog[j], &current_order[j], &ZZ.pow(p, e), ZZ);
397                current_order[j] *= ZZ.pow(p, e);
398            }
399        }
400        debug_assert!(group.eq_el(
401            target,
402            &(0..n).fold(group.identity(), |current, i| group.op(
403                current,
404                group.pow(&self.generators[i], &ZZ_to_ZZbig.map(current_dlog[i]))
405            ))
406        ));
407
408        return Some(current_dlog);
409    }
410
411    fn padic_rectangular_form(&self, p_idx: usize) -> Vec<(GroupEl<G>, usize)> {
412        let group = &self.parent;
413        let (p, e) = self.order_factorization[p_idx];
414        let power = ZZbig
415            .checked_div(&self.order_multiple, &ZZbig.pow(int_cast(p, ZZbig, ZZ), e))
416            .unwrap();
417        let n = self.generators.len();
418
419        if n == 0 {
420            return Vec::new();
421        }
422
423        let Zpne = zn_big::Zn::new(ZZbig, ZZbig.pow(int_cast(p, ZZbig, StaticRing::<i64>::RING), e * n));
424        let mod_pne = Zpne.can_hom(&StaticRing::<i64>::RING).unwrap();
425        let relation_lattice = self.scaled_relation_lattices[p_idx][e].data();
426        let mut relation_lattice_mod_pne =
427            OwnedMatrix::from_fn(relation_lattice.row_count(), relation_lattice.col_count(), |k, l| {
428                mod_pne.map(*relation_lattice.at(k, l))
429            });
430        let mut generators = self.generators.iter().map(|g| group.pow(g, &power)).collect::<Vec<_>>();
431
432        struct TransformGenerators<'a, G: AbelianGroupStore> {
433            group: &'a G,
434            generators: &'a mut [GroupEl<G>],
435        }
436        impl<'a, G: AbelianGroupStore> TransformTarget<zn_big::ZnBase<BigIntRing>> for TransformGenerators<'a, G> {
437            fn transform<S: Copy + RingStore<Type = zn_big::ZnBase<BigIntRing>>>(
438                &mut self,
439                ring: S,
440                i: usize,
441                j: usize,
442                transform: &[El<zn_big::Zn<BigIntRing>>; 4],
443            ) {
444                let transform_inv_det = ring
445                    .invert(&ring.sub(
446                        ring.mul_ref(&transform[0], &transform[3]),
447                        ring.mul_ref(&transform[1], &transform[2]),
448                    ))
449                    .unwrap();
450                let inv_transform = [
451                    ring.smallest_positive_lift(ring.mul_ref(&transform[3], &transform_inv_det)),
452                    ring.smallest_positive_lift(ring.negate(ring.mul_ref(&transform[1], &transform_inv_det))),
453                    ring.smallest_positive_lift(ring.negate(ring.mul_ref(&transform[2], &transform_inv_det))),
454                    ring.smallest_positive_lift(ring.mul_ref(&transform[0], &transform_inv_det)),
455                ];
456                let new_gens = (
457                    self.group.op(
458                        self.group.pow(&self.generators[i], &inv_transform[0]),
459                        self.group.pow(&self.generators[j], &inv_transform[1]),
460                    ),
461                    self.group.op(
462                        self.group.pow(&self.generators[i], &inv_transform[2]),
463                        self.group.pow(&self.generators[j], &inv_transform[3]),
464                    ),
465                );
466                self.generators[i] = new_gens.0;
467                self.generators[j] = new_gens.1;
468            }
469        }
470
471        pre_smith(
472            &Zpne,
473            &mut (),
474            &mut TransformGenerators {
475                group,
476                generators: &mut generators,
477            },
478            relation_lattice_mod_pne.data_mut(),
479        );
480
481        return generators
482            .into_iter()
483            .enumerate()
484            .map(|(i, g)| {
485                (
486                    g,
487                    int_cast(
488                        ZZbig.ideal_gen(
489                            Zpne.modulus(),
490                            &Zpne.smallest_positive_lift(Zpne.clone_el(relation_lattice_mod_pne.at(i, i))),
491                        ),
492                        ZZ,
493                        ZZbig,
494                    ) as usize,
495                )
496            })
497            .collect();
498    }
499
500    /// Returns a list (g[i], l[i]) such that every element of the subgroup
501    /// can be uniquely written as `prod_i g[i]^k[i]` with `0 <= k[i] < l[i]`.
502    #[stability::unstable(feature = "enable")]
503    pub fn rectangular_form<'a>(&'a self) -> Vec<(GroupEl<G>, usize)> {
504        (0..self.order_factorization.len())
505            .flat_map(|p_idx| self.padic_rectangular_form(p_idx))
506            .collect()
507    }
508
509    /// Returns an iterator that yields every element contained in the subgroup
510    /// exactly once.
511    #[stability::unstable(feature = "enable")]
512    pub fn enumerate_elements<'a>(&'a self) -> impl use<'a, G> + Clone + Iterator<Item = GroupEl<G>> {
513        let rectangular_form = Rc::new(self.rectangular_form());
514        multi_cartesian_product(
515            rectangular_form
516                .iter()
517                .map(|(_, l)| 0..*l)
518                .collect::<Vec<_>>()
519                .into_iter(),
520            move |pows| {
521                pows.iter()
522                    .enumerate()
523                    .fold(self.parent().identity(), |current, (i, e)| {
524                        self.parent().op(
525                            current,
526                            self.parent()
527                                .pow(&rectangular_form[i].0, &int_cast(*e as i64, ZZbig, ZZ)),
528                        )
529                    })
530            },
531            |_, x| *x,
532        )
533    }
534
535    /// Takes a matrix whose rows form a basis of the relation lattice w.r.t. group
536    /// elements `g1, ..., gn` and computes a matrix whose rows form a basis of the relation
537    /// lattice of `p g1, ..., p gn`.
538    fn relation_lattice_basis_downscale_p<V>(basis: Submatrix<V, i64>, p: i64) -> OwnedMatrix<i64>
539    where
540        V: AsPointerToSlice<i64>,
541    {
542        let n = basis.row_count();
543        assert_eq!(n, basis.col_count());
544
545        let QQ = RationalField::new(ZZbig);
546        let ZZ_to_QQ = QQ.inclusion().compose(QQ.base_ring().can_hom(&ZZ).unwrap());
547        let as_ZZ = |x| int_cast(ZZbig.checked_div(QQ.num(x), QQ.den(x)).unwrap(), ZZ, ZZbig);
548
549        let mut dual_basis = OwnedMatrix::identity(n, 2 * n, &QQ);
550        let mut Binv = dual_basis.data_mut().submatrix(0..n, n..(2 * n));
551        let mut rhs = OwnedMatrix::identity(n, n, &QQ);
552        QQ.solve_right(
553            OwnedMatrix::from_fn(n, n, |i, j| ZZ_to_QQ.map_ref(basis.at(i, j))).data_mut(),
554            rhs.data_mut(),
555            Binv.reborrow(),
556        )
557        .assert_solved();
558        Binv.reborrow()
559            .row_iter()
560            .flat_map(|row| row.iter_mut())
561            .for_each(|x| ZZ_to_QQ.mul_assign_map(x, p));
562
563        let mut identity = OwnedMatrix::identity(n, n, &QQ);
564        lll(
565            &QQ,
566            identity.data(),
567            dual_basis.data_mut(),
568            &QQ.div(&ZZ_to_QQ.map(9), &ZZ_to_QQ.map(10)),
569            Global,
570            false,
571        );
572
573        let mut result_QQ = rhs;
574        QQ.solve_right(
575            dual_basis.data_mut().submatrix(0..n, n..(2 * n)),
576            identity.data_mut(),
577            result_QQ.data_mut(),
578        )
579        .assert_solved();
580
581        let result = OwnedMatrix::from_fn(n, n, |i, j| as_ZZ(result_QQ.at(i, j)));
582
583        return result;
584    }
585}
586
587impl<G: AbelianGroupStore> PartialEq for SubgroupBase<G> {
588    fn eq(&self, other: &Self) -> bool {
589        self.parent().get_group() == other.parent().get_group()
590            && other.generators().iter().all(|g| self.contains(g))
591            && self.generators().iter().all(|g| other.contains(g))
592    }
593}
594
595impl<G: AbelianGroupStore> AbelianGroupBase for SubgroupBase<G> {
596    type Element = GroupEl<G>;
597
598    fn clone_el(&self, x: &Self::Element) -> Self::Element { self.parent().clone_el(x) }
599
600    fn eq_el(&self, lhs: &Self::Element, rhs: &Self::Element) -> bool { self.parent().eq_el(lhs, rhs) }
601
602    fn hash<H: std::hash::Hasher>(&self, x: &Self::Element, hasher: &mut H) { self.parent().hash(x, hasher) }
603
604    fn identity(&self) -> Self::Element { self.parent().identity() }
605
606    fn inv(&self, x: &Self::Element) -> Self::Element { self.parent().inv(x) }
607
608    fn is_identity(&self, x: &Self::Element) -> bool { self.parent().is_identity(x) }
609
610    fn op(&self, lhs: Self::Element, rhs: Self::Element) -> Self::Element { self.parent().op(lhs, rhs) }
611
612    fn pow(&self, x: &Self::Element, e: &El<BigIntRing>) -> Self::Element { self.parent().pow(x, e) }
613
614    fn op_ref(&self, lhs: &Self::Element, rhs: &Self::Element) -> Self::Element { self.parent().op_ref(lhs, rhs) }
615
616    fn op_ref_snd(&self, lhs: Self::Element, rhs: &Self::Element) -> Self::Element {
617        self.parent().op_ref_snd(lhs, rhs)
618    }
619}
620
621impl<G: AbelianGroupStore + Serialize> Serialize for SubgroupBase<G>
622where
623    G::Type: SerializableElementGroup,
624{
625    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
626    where
627        S: serde::Serializer,
628    {
629        #[derive(Serialize)]
630        struct SubgroupData<'a, Gens: Serialize> {
631            order_multiple: SerializeWithRing<'a, BigIntRing>,
632            generators: Gens,
633            group: (),
634        }
635        SerializableNewtypeStruct::new(
636            "Subgroup",
637            (
638                self.parent(),
639                SubgroupData {
640                    order_multiple: SerializeWithRing::new(&self.order_multiple, ZZbig),
641                    generators: SerializableSeq::new(
642                        self.generators
643                            .iter()
644                            .map(|g| SerializeWithGroup::new(g, self.parent())),
645                    ),
646                    group: (),
647                },
648            ),
649        )
650        .serialize(serializer)
651    }
652}
653
654impl<'de, G: AbelianGroupStore + Clone + Deserialize<'de>> Deserialize<'de> for SubgroupBase<G>
655where
656    G::Type: SerializableElementGroup,
657{
658    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
659    where
660        D: serde::Deserializer<'de>,
661    {
662        use serde::de::DeserializeSeed;
663
664        struct DeserializeSeedSubgroupData<G: AbelianGroupStore>
665        where
666            G::Type: SerializableElementGroup,
667        {
668            group: G,
669        }
670
671        impl_deserialize_seed_for_dependent_struct! {
672            <{ 'de, G }> pub struct SubgroupData<{'de, G}> using DeserializeSeedSubgroupData<G> {
673                order_multiple: El<BigIntRing>: |_| DeserializeWithRing::new(ZZbig),
674                generators: Vec<GroupEl<G>>: |master: &DeserializeSeedSubgroupData<G>| {
675                    let group_clone = master.group.clone();
676                    DeserializeSeedSeq::new((0..).map(move |_| DeserializeWithGroup::new(group_clone.clone())), Vec::new(), |mut current, next| { current.push(next); current })
677                },
678                group: G: |master: &DeserializeSeedSubgroupData<G>| {
679                    let group_clone = master.group.clone();
680                    DeserializeSeedMapped::new(PhantomData::<()>, move |()| group_clone)
681                }
682            } where G: AbelianGroupStore + Clone, G::Type: SerializableElementGroup
683        }
684
685        DeserializeSeedNewtypeStruct::new(
686            "Subgroup",
687            DeserializeSeedDependentTuple::new(PhantomData::<G>, |group| DeserializeSeedSubgroupData { group }),
688        )
689        .deserialize(deserializer)
690        .map(|data| Subgroup::new(data.group, data.order_multiple, data.generators).into())
691    }
692}
693
694impl<G: AbelianGroupStore> Clone for SubgroupBase<G>
695where
696    G: Clone,
697{
698    fn clone(&self) -> Self {
699        Self {
700            parent: self.parent.clone(),
701            generators: self.generators.iter().map(|g| self.parent.clone_el(g)).collect(),
702            order_factorization: self.order_factorization.clone(),
703            order_multiple: self.order_multiple.clone(),
704            scaled_generating_sets: self
705                .scaled_generating_sets
706                .iter()
707                .map(|sets| {
708                    sets.iter()
709                        .map(|set| set.iter().map(|g| self.parent.clone_el(g)).collect())
710                        .collect()
711                })
712                .collect(),
713            scaled_relation_lattices: self
714                .scaled_relation_lattices
715                .iter()
716                .map(|x| x.iter().map(|x| x.clone_matrix(StaticRing::<i64>::RING)).collect())
717                .collect(),
718        }
719    }
720}
721
722impl<R> Subgroup<MultGroup<R>>
723where
724    R: RingStore,
725    R::Type: ZnRing + HashableElRing + DivisibilityRing,
726{
727    #[stability::unstable(feature = "enable")]
728    pub fn for_zn(group: MultGroup<R>, generators: Vec<GroupEl<MultGroup<R>>>) -> Self {
729        let n = generators.len();
730        if n == 0 {
731            let n_factorization = factor(ZZbig, group.underlying_ring().size(ZZbig).unwrap());
732            let mut order_factorization = n_factorization
733                .into_iter()
734                .flat_map(|(p, e)| {
735                    factor(ZZbig, ZZbig.sub_ref_fst(&p, ZZbig.one()))
736                        .into_iter()
737                        .chain([(p, e - 1)])
738                })
739                .collect::<Vec<_>>();
740            order_factorization.sort_unstable_by(|(pl, _), (pr, _)| ZZbig.cmp(pl, pr));
741            order_factorization.dedup_by(|(p1, e1), (p2, e2)| {
742                if ZZbig.eq_el(p1, p2) {
743                    *e2 += *e1;
744                    true
745                } else {
746                    false
747                }
748            });
749            order_factorization.retain(|(_, e)| *e > 0);
750            let order = ZZbig.prod(
751                order_factorization
752                    .iter()
753                    .map(|(p, e)| ZZbig.pow(ZZbig.clone_el(p), *e)),
754            );
755            let order_factorization = order_factorization
756                .into_iter()
757                .map(|(p, e)| (int_cast(p, ZZ, ZZbig), e))
758                .collect();
759            return Self::from(SubgroupBase {
760                parent: group,
761                generators: Vec::new(),
762                order_multiple: order,
763                order_factorization,
764                scaled_generating_sets: Vec::new(),
765                scaled_relation_lattices: Vec::new(),
766            });
767        } else {
768            let mut result = Self::for_zn(group, Vec::new());
769            for g in generators {
770                result = result.add_generator(g);
771            }
772            return result;
773        }
774    }
775}
776
777/// Computes the a vector `k` with entries `1 <= k[i] <= dlog_bounds[i]` such that
778/// `generators^k = value` (`generators` is a list of elements of an abelian group).
779///
780/// If there is no such vector, then `None` is returned. If there are multiple such
781/// vectors, any one of them is returned. In the 1d-case, it is guaranteed that this
782/// is the smallest one, but in the multidimensional case, no such guarantee can be made
783/// (in particular, the vector in general won't be the shortest one w.r.t. any natural
784/// ordering like lex or degrevlex).
785///
786/// Note: The vector `k` is required to have positive entries. In particular, this
787/// function won't return the zero vector if the given element is the identity.
788/// This can have unexpected consequences, like
789/// ```
790/// # use feanor_math::algorithms::discrete_log::*;
791/// # use feanor_math::integer::*;
792/// # use feanor_math::group::*;
793/// # use feanor_math::primitive_int::*;
794/// let group = AddGroup::new(StaticRing::<i64>::RING);
795/// assert_eq!(
796///     Some(vec![1]),
797///     baby_giant_step(&group, 0, &[0], &[BigIntRing::RING.power_of_two(10)])
798/// );
799/// ```
800///
801/// # Implementation notes
802///
803/// The complexity of the algorithm is `O(sqrt(prod_i dlog_bounds[i]))`.
804/// Thus, when possible, `order_bound[i]` should be the order of `generators[i]`
805/// in the group.
806///
807/// Why do we need a group? Because we search for collisions `ab = ac`, and assume
808/// that this implies `b = c`. So actually,  a cancelable abelian monoid would be sufficient...
809///
810/// Why don't we use Pollard's rhos? Because Pollard's rho cannot deterministically
811/// detect the case that `value` is not in the subgroup generated by `generators`.
812/// It can do so with high probability, but only if the used hash function satisfies
813/// certain properties. With BSGS, the correctness does not depend on the used hash
814/// function (although performance does, of course).
815///
816/// # Example
817///
818/// ```rust
819/// # use feanor_math::ring::*;
820/// # use feanor_math::group::*;
821/// # use feanor_math::homomorphism::*;
822/// # use feanor_math::rings::zn::*;
823/// # use feanor_math::integer::*;
824/// # use feanor_math::primitive_int::StaticRing;
825/// # use feanor_math::rings::zn::zn_64::*;
826/// # use feanor_math::wrapper::*;
827/// # use feanor_math::algorithms::discrete_log::*;
828/// let ring = Zn::new(17);
829/// let group = MultGroup::new(ring);
830/// let x = group.from_ring_el(ring.int_hom().map(9)).unwrap();
831/// assert_eq!(
832///     Some(vec![3]),
833///     baby_giant_step(
834///         &group,
835///         group.pow(&x, &int_cast(3, BigIntRing::RING, StaticRing::<i64>::RING)),
836///         &[x],
837///         &[BigIntRing::RING.power_of_two(4)]
838///     )
839/// );
840/// ```
841#[stability::unstable(feature = "enable")]
842pub fn baby_giant_step<G>(
843    group: G,
844    value: GroupEl<G>,
845    generators: &[GroupEl<G>],
846    dlog_bounds: &[El<BigIntRing>],
847) -> Option<Vec<i64>>
848where
849    G: AbelianGroupStore,
850{
851    let n = generators.len();
852    assert_eq!(n, dlog_bounds.len());
853    if generators.is_empty() {
854        if group.is_identity(&value) {
855            return Some(Vec::new());
856        } else {
857            return None;
858        }
859    }
860    let ns = dlog_bounds
861        .iter()
862        .map(|n| int_cast(root_floor(ZZbig, n.clone(), 2), ZZ, ZZbig) + 1)
863        .collect::<Vec<_>>();
864    let count = int_cast(ZZbig.prod(ns.iter().map(|n| int_cast(*n, ZZbig, ZZ))), ZZ, ZZbig);
865    let mut baby_step_table: HashMap<HashableGroupEl<_>, i64> = HashMap::with_capacity(count as usize);
866
867    // fill baby step table
868    {
869        let mut current_els = (0..n).map(|_| group.clone_el(&value)).collect::<Vec<_>>();
870        let mut current_idxs = (0..n).map(|_| 0).collect::<Vec<_>>();
871        for idx in 0..count {
872            _ = baby_step_table.insert(HashableGroupEl::new(&group, group.clone_el(&current_els[n - 1])), idx);
873
874            let mut i = n - 1;
875            while current_idxs[i] == ns[i] - 1 {
876                if i == 0 {
877                    assert!(idx + 1 == count);
878                    break;
879                }
880                current_idxs[i] = 0;
881                i -= 1;
882            }
883            current_idxs[i] += 1;
884            current_els[i] = group.op_ref_snd(replace(&mut current_els[i], group.identity()), &generators[i]);
885            for j in (i + 1)..n {
886                current_els[j] = group.clone_el(&current_els[i]);
887            }
888        }
889    }
890
891    let giant_steps = generators
892        .iter()
893        .zip(ns.iter())
894        .map(|(g, n)| group.pow(g, &int_cast(*n, ZZbig, ZZ)))
895        .collect::<Vec<_>>();
896    // iterate through giant steps
897    {
898        let start_el = giant_steps.iter().fold(group.identity(), |x, y| group.op_ref_snd(x, y));
899        let mut current_els = (0..n).map(|_| group.clone_el(&start_el)).collect::<Vec<_>>();
900        let mut current_idxs = (0..n).map(|_| 1).collect::<Vec<_>>();
901        for idx in 0..count {
902            if let Some(bs_idx) =
903                baby_step_table.get(&HashableGroupEl::new(&group, group.clone_el(&current_els[n - 1])))
904            {
905                let mut bs_idx = *bs_idx;
906                let mut result = current_idxs.clone();
907                for j in (0..n).rev() {
908                    let bs_idxs_j = bs_idx % ns[j];
909                    bs_idx /= ns[j];
910                    result[j] = result[j] * ns[j] - bs_idxs_j;
911                }
912                if (0..dlog_bounds.len()).all(|j| ZZbig.is_leq(&int_cast(result[j], ZZbig, ZZ), &dlog_bounds[j])) {
913                    debug_assert_eq!(n, result.len());
914                    return Some(result);
915                }
916            }
917
918            let mut i = n - 1;
919            while current_idxs[i] == ns[i] {
920                if i == 0 {
921                    assert!(idx + 1 == count);
922                    break;
923                }
924                current_idxs[i] = 1;
925                i -= 1;
926            }
927            current_idxs[i] += 1;
928            current_els[i] = group.op_ref_snd(replace(&mut current_els[i], group.identity()), &giant_steps[i]);
929            for j in (i + 1)..n {
930                current_els[j] = group.clone_el(&current_els[i]);
931            }
932        }
933    }
934
935    return None;
936}
937
938/// Computes the discrete log in the group `(Z/nZ)*`.
939///
940/// This only called `finite_field_discrete_log` for backwards compatibility,
941/// it now completely supports `(Z/nZ)*` for any `n`.
942pub fn finite_field_discrete_log<R: RingStore>(value: El<R>, base: El<R>, Zn: R) -> Option<i64>
943where
944    R::Type: ZnRing + HashableElRing,
945{
946    let group = MultGroup::new(Zn);
947    let generators = vec![group.from_ring_el(base).unwrap()];
948    let subgroup = Subgroup::for_zn(group, generators);
949    return subgroup
950        .dlog(&subgroup.parent().from_ring_el(value).unwrap())
951        .map(|res| res[0]);
952}
953
954/// Computes the multiplicative order in the group `(Z/nZ)*`.
955pub fn multiplicative_order<R: RingStore>(x: El<R>, Zn: R) -> i64
956where
957    R::Type: ZnRing + HashableElRing,
958{
959    let group = MultGroup::new(Zn);
960    let gen_set = Subgroup::for_zn(group, Vec::new());
961    let Zn = gen_set.parent().underlying_ring();
962
963    let mut result = ZZbig.one();
964    for (p, e) in &gen_set.get_group().order_factorization {
965        let mut current = Zn.pow_gen(
966            Zn.clone_el(&x),
967            &ZZbig
968                .checked_div(
969                    &gen_set.get_group().order_multiple,
970                    &ZZbig.pow(int_cast(*p, ZZbig, ZZ), *e),
971                )
972                .unwrap(),
973            ZZbig,
974        );
975        while !Zn.is_one(&current) {
976            current = Zn.pow(current, *p as usize);
977            ZZbig.mul_assign(&mut result, int_cast(*p, ZZbig, ZZ));
978        }
979    }
980    return int_cast(result, ZZ, ZZbig);
981}
982
983#[cfg(test)]
984struct ProdGroupBase<G: AbelianGroupStore, const N: usize>(G);
985
986#[cfg(test)]
987impl<G: AbelianGroupStore, const N: usize> PartialEq for ProdGroupBase<G, N> {
988    fn eq(&self, other: &Self) -> bool { self.0.get_group() == other.0.get_group() }
989}
990
991#[cfg(test)]
992impl<G: AbelianGroupStore, const N: usize> AbelianGroupBase for ProdGroupBase<G, N> {
993    type Element = [GroupEl<G>; N];
994
995    fn clone_el(&self, x: &Self::Element) -> Self::Element { from_fn(|i| self.0.clone_el(&x[i])) }
996
997    fn eq_el(&self, lhs: &Self::Element, rhs: &Self::Element) -> bool { (0..N).all(|i| self.0.eq_el(&lhs[i], &rhs[i])) }
998
999    fn op(&self, lhs: Self::Element, rhs: Self::Element) -> Self::Element {
1000        from_fn(|i| self.0.op_ref(&lhs[i], &rhs[i]))
1001    }
1002
1003    fn hash<H: std::hash::Hasher>(&self, x: &Self::Element, hasher: &mut H) {
1004        for i in 0..N {
1005            self.0.hash(&x[i], hasher)
1006        }
1007    }
1008
1009    fn inv(&self, x: &Self::Element) -> Self::Element { from_fn(|i| self.0.inv(&x[i])) }
1010
1011    fn identity(&self) -> Self::Element { from_fn(|_| self.0.identity()) }
1012}
1013
1014#[cfg(test)]
1015use std::array::from_fn;
1016
1017#[cfg(test)]
1018use oorandom::Rand64;
1019
1020#[cfg(test)]
1021use crate::RANDOM_TEST_INSTANCE_COUNT;
1022#[cfg(test)]
1023use crate::algorithms::matmul::ComputeInnerProduct;
1024#[cfg(test)]
1025use crate::assert_matrix_eq;
1026#[cfg(test)]
1027use crate::group::AddGroup;
1028#[cfg(test)]
1029use crate::rings::zn::zn_static::Zn;
1030
1031#[test]
1032fn test_baby_giant_step() {
1033    for base_bound in [21, 26, 31, 37] {
1034        let dlog_bound = [int_cast(base_bound, ZZbig, ZZ)];
1035        let G = AddGroup::new(ZZ);
1036        assert_eq!(Some(vec![6]), baby_giant_step(&G, 6, &[1], &dlog_bound));
1037        assert_eq!(None, baby_giant_step(&G, 0, &[1], &dlog_bound));
1038
1039        let G = AddGroup::new(Zn::<20>::RING);
1040        assert_eq!(Some(vec![20]), baby_giant_step(&G, 0, &[1], &dlog_bound));
1041        assert_eq!(Some(vec![10]), baby_giant_step(&G, 10, &[1], &dlog_bound));
1042        assert_eq!(Some(vec![5]), baby_giant_step(&G, 0, &[16], &dlog_bound));
1043    }
1044
1045    let G = AddGroup::new(ZZ);
1046
1047    // the collision point is at 96
1048    assert_eq!(
1049        Some(vec![9 - 1, 6 - 1]),
1050        baby_giant_step(&G, 85, &[10, 1], &from_fn::<_, 2, _>(|_| int_cast(8, ZZbig, ZZ)))
1051    );
1052    // the collision point is at 105
1053    assert_eq!(
1054        Some(vec![10 - 2, 5 - 0]),
1055        baby_giant_step(&G, 85, &[10, 1], &from_fn::<_, 2, _>(|_| int_cast(21, ZZbig, ZZ)))
1056    );
1057    // the collision point is at 90
1058    assert_eq!(
1059        Some(vec![6 - 0, 30 - 5]),
1060        baby_giant_step(&G, 85, &[10, 1], &from_fn::<_, 2, _>(|_| int_cast(31, ZZbig, ZZ)))
1061    );
1062}
1063
1064#[test]
1065fn test_padic_relation_lattice() {
1066    let G = AddGroup::new(Zn::<81>::RING);
1067
1068    let subgroup = Subgroup::new(&G, int_cast(81, ZZbig, ZZ), vec![1]);
1069    assert_matrix_eq!(ZZ, [[-81]], subgroup.get_group().scaled_relation_lattices[0][4]);
1070    assert_matrix_eq!(ZZ, [[-27]], subgroup.get_group().scaled_relation_lattices[0][3]);
1071    assert_matrix_eq!(ZZ, [[-9]], subgroup.get_group().scaled_relation_lattices[0][2]);
1072    assert_matrix_eq!(ZZ, [[-3]], subgroup.get_group().scaled_relation_lattices[0][1]);
1073    assert_matrix_eq!(ZZ, [[1]], subgroup.get_group().scaled_relation_lattices[0][0]);
1074
1075    let subgroup = Subgroup::new(&G, int_cast(81, ZZbig, ZZ), vec![3, 6]);
1076    let matrix = &subgroup.get_group().scaled_relation_lattices[0][4];
1077    assert_eq!(-27, *matrix.at(0, 0));
1078    assert_eq!(-1, *matrix.at(1, 1));
1079    assert_eq!(
1080        0,
1081        ZZ.get_ring()
1082            .inner_product_ref(matrix.data().row_at(1).iter().zip([3, 6].iter()))
1083            % 81
1084    );
1085
1086    let subgroup = Subgroup::new(&G, int_cast(81, ZZbig, ZZ), vec![3, 9]);
1087    let matrix = &subgroup.get_group().scaled_relation_lattices[0][4];
1088    assert_eq!(-27, *matrix.at(0, 0));
1089    assert_eq!(-1, *matrix.at(1, 1));
1090    assert_eq!(
1091        0,
1092        ZZ.get_ring()
1093            .inner_product_ref(matrix.data().row_at(1).iter().zip([3, 9].iter()))
1094            % 81
1095    );
1096
1097    let subgroup = Subgroup::new(&G, int_cast(81, ZZbig, ZZ), vec![6, 18, 9]);
1098    let matrix = &subgroup.get_group().scaled_relation_lattices[0][4];
1099    assert_eq!(-27, *matrix.at(0, 0));
1100    assert_eq!(-1, *matrix.at(1, 1));
1101    assert_eq!(-1, *matrix.at(2, 2));
1102    assert_eq!(
1103        0,
1104        ZZ.get_ring()
1105            .inner_product_ref(matrix.data().row_at(1).iter().zip([6, 18, 9].iter()))
1106            % 81
1107    );
1108    assert_eq!(
1109        0,
1110        ZZ.get_ring()
1111            .inner_product_ref(matrix.data().row_at(2).iter().zip([6, 18, 9].iter()))
1112            % 81
1113    );
1114
1115    let G = GroupValue::from(ProdGroupBase(AddGroup::new(Zn::<81>::RING)));
1116
1117    let subgroup = Subgroup::new(&G, int_cast(81, ZZbig, ZZ), vec![[1, 4], [1, 1]]);
1118    let matrix = &subgroup.get_group().scaled_relation_lattices[0][4];
1119    assert_eq!(-81, *matrix.at(0, 0));
1120    assert_eq!(-27, *matrix.at(1, 1));
1121    assert_eq!(
1122        0,
1123        ZZ.get_ring()
1124            .inner_product_ref(matrix.data().row_at(1).iter().zip([1, 1].iter()))
1125            % 81
1126    );
1127    assert_eq!(
1128        0,
1129        ZZ.get_ring()
1130            .inner_product_ref(matrix.data().row_at(1).iter().zip([4, 1].iter()))
1131            % 81
1132    );
1133
1134    let G = GroupValue::from(ProdGroupBase(AddGroup::new(Zn::<8>::RING)));
1135
1136    let subgroup = Subgroup::new(&G, int_cast(8, ZZbig, ZZ), vec![[6, 3, 5], [6, 2, 6], [4, 5, 7]]);
1137    let matrix = &subgroup.get_group().scaled_relation_lattices[0][3];
1138    assert_eq!(-8, *matrix.at(0, 0));
1139    assert_eq!(-4, *matrix.at(1, 1));
1140    assert_eq!(-2, *matrix.at(2, 2));
1141    assert_eq!(
1142        0,
1143        ZZ.get_ring()
1144            .inner_product_ref(matrix.data().row_at(1).iter().zip([6, 6, 4].iter()))
1145            % 8
1146    );
1147    assert_eq!(
1148        0,
1149        ZZ.get_ring()
1150            .inner_product_ref(matrix.data().row_at(1).iter().zip([3, 2, 5].iter()))
1151            % 8
1152    );
1153    assert_eq!(
1154        0,
1155        ZZ.get_ring()
1156            .inner_product_ref(matrix.data().row_at(1).iter().zip([5, 6, 7].iter()))
1157            % 8
1158    );
1159    assert_eq!(
1160        0,
1161        ZZ.get_ring()
1162            .inner_product_ref(matrix.data().row_at(2).iter().zip([6, 6, 4].iter()))
1163            % 8
1164    );
1165    assert_eq!(
1166        0,
1167        ZZ.get_ring()
1168            .inner_product_ref(matrix.data().row_at(2).iter().zip([3, 2, 5].iter()))
1169            % 8
1170    );
1171    assert_eq!(
1172        0,
1173        ZZ.get_ring()
1174            .inner_product_ref(matrix.data().row_at(2).iter().zip([5, 6, 7].iter()))
1175            % 8
1176    );
1177}
1178
1179#[test]
1180fn random_test_dlog() {
1181    let ring = Zn::<1400>::RING;
1182    let i = ring.can_hom(&ZZ).unwrap();
1183    let mut rng = Rand64::new(0);
1184    let G = GroupValue::from(ProdGroupBase(AddGroup::new(ring)));
1185    let rand_gs = |rng: &mut Rand64| from_fn::<_, 3, _>(|_| ring.random_element(|| rng.rand_u64()));
1186
1187    for _ in 0..RANDOM_TEST_INSTANCE_COUNT {
1188        let gs = from_fn::<_, 3, _>(|_| rand_gs(&mut rng));
1189        let subgroup = Subgroup::new(&G, int_cast(1400, ZZbig, ZZ), gs.into());
1190
1191        let coeffs = rand_gs(&mut rng);
1192        let val = (0..3).fold(G.identity(), |current, i| {
1193            G.op(current, G.pow(&gs[i], &int_cast(coeffs[i] as i64, ZZbig, ZZ)))
1194        });
1195        let dlog = subgroup.dlog(&val);
1196        println!(
1197            "{:?} * x + {:?} * y + {:?} * z = {:?} mod 1400",
1198            gs[0], gs[1], gs[2], val
1199        );
1200        if let Some(dlog) = dlog {
1201            for k in 0..3 {
1202                assert_el_eq!(
1203                    ring,
1204                    val[k],
1205                    ring.sum([
1206                        i.mul_map(gs[0][k], dlog[0]),
1207                        i.mul_map(gs[1][k], dlog[1]),
1208                        i.mul_map(gs[2][k], dlog[2])
1209                    ])
1210                );
1211            }
1212            println!("checked solution");
1213        }
1214    }
1215
1216    for _ in 0..RANDOM_TEST_INSTANCE_COUNT {
1217        let gs = from_fn::<_, 3, _>(|_| rand_gs(&mut rng));
1218        let subgroup = Subgroup::new(&G, int_cast(1400, ZZbig, ZZ), gs.into());
1219
1220        let val = rand_gs(&mut rng);
1221        let dlog = subgroup.dlog(&val);
1222        println!(
1223            "{:?} * x + {:?} * y + {:?} * z = {:?} mod 1400",
1224            gs[0], gs[1], gs[2], val
1225        );
1226        if let Some(dlog) = dlog {
1227            for k in 0..3 {
1228                assert_el_eq!(
1229                    ring,
1230                    val[k],
1231                    ring.sum([
1232                        i.mul_map(gs[0][k], dlog[0]),
1233                        i.mul_map(gs[1][k], dlog[1]),
1234                        i.mul_map(gs[2][k], dlog[2])
1235                    ])
1236                );
1237            }
1238            println!("checked solution");
1239        } else {
1240            let mut gen_matrix = OwnedMatrix::from_fn(3, 3, |i, j| gs[j][i]);
1241            let mut value = OwnedMatrix::from_fn(3, 1, |i, _| val[i]);
1242            let mut res = OwnedMatrix::zero(3, 1, ring);
1243            let solved = ring.solve_right(gen_matrix.data_mut(), value.data_mut(), res.data_mut());
1244            println!("[{}, {}, {}]", res.at(0, 0), res.at(1, 0), res.at(2, 0));
1245            if solved.is_solved() {
1246                for k in 0..3 {
1247                    assert_el_eq!(
1248                        ring,
1249                        val[k],
1250                        ring.sum([
1251                            ring.mul(gs[0][k], *res.at(0, 0)),
1252                            ring.mul(gs[1][k], *res.at(1, 0)),
1253                            ring.mul(gs[2][k], *res.at(2, 0))
1254                        ])
1255                    );
1256                }
1257                assert!(solved == crate::algorithms::linsolve::SolveResult::NoSolution);
1258            }
1259            println!("has no solution");
1260        }
1261    }
1262}
1263
1264#[test]
1265fn test_zn_dlog() {
1266    let ring = Zn::<51>::RING;
1267    let g1 = ring.int_hom().map(37);
1268    let g2 = ring.int_hom().map(35);
1269
1270    assert_eq!(0, finite_field_discrete_log(ring.one(), g1, ring).unwrap() % 16);
1271    assert_eq!(0, finite_field_discrete_log(ring.one(), g2, ring).unwrap() % 2);
1272    assert_eq!(1, finite_field_discrete_log(g2, g2, ring).unwrap() % 2);
1273    for i in 0..16 {
1274        assert_eq!(
1275            i,
1276            finite_field_discrete_log(ring.pow(g1, i as usize), g1, ring).unwrap() % 16
1277        );
1278    }
1279    for i in 0..16 {
1280        assert_eq!(
1281            None,
1282            finite_field_discrete_log(ring.mul(ring.pow(g1, i as usize), g2), g1, ring)
1283        );
1284    }
1285
1286    assert_eq!(16, multiplicative_order(g1, ring));
1287    assert_eq!(8, multiplicative_order(ring.pow(g1, 2), ring));
1288    assert_eq!(4, multiplicative_order(ring.pow(g1, 4), ring));
1289    assert_eq!(2, multiplicative_order(ring.pow(g1, 8), ring));
1290    assert_eq!(2, multiplicative_order(g2, ring));
1291}
1292
1293#[test]
1294fn test_zn_subgroup_size() {
1295    let ring = Zn::<153>::RING;
1296    let group = MultGroup::new(ring);
1297    let g1 = group.from_ring_el(ring.int_hom().map(2)).unwrap();
1298    let g2 = group.from_ring_el(ring.int_hom().map(37)).unwrap();
1299
1300    let mut subgroup = Subgroup::for_zn(group.clone(), Vec::new());
1301    assert_el_eq!(ZZbig, ZZbig.int_hom().map(1), subgroup.subgroup_order());
1302
1303    let next_gen = subgroup.parent().clone_el(&g1);
1304    subgroup = subgroup.add_generator(next_gen);
1305    assert_el_eq!(ZZbig, ZZbig.int_hom().map(24), subgroup.subgroup_order());
1306
1307    let next_gen = subgroup.parent().pow(&g1, &ZZbig.int_hom().map(2));
1308    subgroup = subgroup.add_generator(next_gen);
1309    assert_el_eq!(ZZbig, ZZbig.int_hom().map(24), subgroup.subgroup_order());
1310
1311    let next_gen = subgroup.parent().clone_el(&g2);
1312    subgroup = subgroup.add_generator(next_gen);
1313    assert_el_eq!(ZZbig, ZZbig.int_hom().map(96), subgroup.subgroup_order());
1314
1315    let generating_set = Subgroup::for_zn(group, vec![g2]);
1316    assert_el_eq!(ZZbig, ZZbig.int_hom().map(16), generating_set.subgroup_order());
1317}
1318
1319#[test]
1320fn test_enumerate_elements() {
1321    let ring = Zn::<45>::RING;
1322    let group = AddGroup::new(ring);
1323
1324    assert_eq!(
1325        vec![ring.zero()],
1326        Subgroup::new(group.clone(), int_cast(45, ZZbig, ZZ), Vec::new())
1327            .enumerate_elements()
1328            .collect::<Vec<_>>()
1329    );
1330
1331    let subgroup = Subgroup::new(group, int_cast(45, ZZbig, ZZ), vec![9, 15]);
1332    let mut elements = subgroup.enumerate_elements().collect::<Vec<_>>();
1333    elements.sort_unstable();
1334    assert_eq!((0..45).step_by(3).collect::<Vec<_>>(), elements);
1335}