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#[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 scaled_relation_lattices: Vec<Vec<OwnedMatrix<i64>>>,
59 scaled_generating_sets: Vec<Vec<Vec<GroupEl<G>>>>,
64}
65
66#[stability::unstable(feature = "enable")]
68#[allow(type_alias_bounds)]
69pub type Subgroup<G: AbelianGroupStore> = GroupValue<SubgroupBase<G>>;
70
71impl<G: AbelianGroupStore> Subgroup<G> {
72 #[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 #[stability::unstable(feature = "enable")]
104 pub fn parent(&self) -> &G { self.get_group().parent() }
105
106 #[stability::unstable(feature = "enable")]
108 pub fn subgroup_order(&self) -> El<BigIntRing> { self.get_group().subgroup_order() }
109
110 #[stability::unstable(feature = "enable")]
112 pub fn generators(&self) -> &[GroupEl<G>] { self.get_group().generators() }
113
114 #[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 #[stability::unstable(feature = "enable")]
121 pub fn contains(&self, element: &GroupEl<G>) -> bool { self.get_group().contains(element) }
122
123 #[stability::unstable(feature = "enable")]
126 pub fn dlog(&self, target: &GroupEl<G>) -> Option<Vec<i64>> { self.get_group().dlog(target) }
127
128 #[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 #[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 #[stability::unstable(feature = "enable")]
175 pub fn order_multiple(&self) -> &El<BigIntRing> { &self.order_multiple }
176
177 #[stability::unstable(feature = "enable")]
179 pub fn generators(&self) -> &[GroupEl<G>] { &self.generators }
180
181 #[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 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 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 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 #[stability::unstable(feature = "enable")]
367 pub fn contains(&self, element: &GroupEl<G>) -> bool { self.dlog(element).is_some() }
368
369 #[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], ¤t_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 #[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 #[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 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#[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 {
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(¤t_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(¤t_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 {
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(¤t_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(¤t_els[i]);
931 }
932 }
933 }
934
935 return None;
936}
937
938pub 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
954pub 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(¤t) {
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 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 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 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}