1use smallvec::SmallVec;
2
3use crate::delegate::DelegateRing;
4use crate::divisibility::DivisibilityRing;
5use crate::homomorphism::*;
6use crate::integer::*;
7use crate::pid::*;
8use crate::primitive_int::{StaticRing, StaticRingBase};
9use crate::ring::*;
10use crate::rings::rust_bigint::{RustBigintRing, RustBigintRingBase};
11use crate::rings::zn::*;
12use crate::specialization::*;
13use crate::{
14 impl_field_wrap_unwrap_homs, impl_field_wrap_unwrap_isos, impl_localpir_wrap_unwrap_homs,
15 impl_localpir_wrap_unwrap_isos,
16};
17
18const DEFAULT_SMALLVEC_SIZE: usize = 4;
19
20#[derive(Clone)]
47pub struct Z2kBase<const N: usize = DEFAULT_SMALLVEC_SIZE> {
48 k: usize,
49 n_limbs: usize,
50 last_limb_mask: u64,
51 modulus: El<RustBigintRing>,
52}
53impl<const N: usize> Z2kBase<N>
54where
55 [u64; N]: smallvec::Array<Item = u64>,
56{
57 pub fn new(k: usize) -> Self {
58 assert!(k >= 1);
59 let n_limbs = k.div_ceil(64);
60 assert!(
61 n_limbs <= 16,
62 "This implementation is not optimized for such a large modulus."
63 );
64 let modulus = RustBigintRing::RING.power_of_two(k);
65
66 let last_limb_mask = if k % 64 == 0 {
67 0xFFFFFFFFFFFFFFFF
68 } else {
69 0xFFFFFFFFFFFFFFFF >> (64 - (k % 64))
70 };
71 Self {
72 k,
73 n_limbs,
74 last_limb_mask,
75 modulus,
76 }
77 }
78
79 fn mask_el(&self, el: &mut Z2kEl<N>) {
80 el.0[self.n_limbs - 1] &= self.last_limb_mask;
81 el.0.truncate(self.n_limbs);
82 }
83
84 fn bigint_to_el(&self, ZZ: &RustBigintRing, x: &El<RustBigintRing>) -> Z2kEl<N> {
85 let mut rem = ZZ.euclidean_rem(ZZ.clone_el(x), self.modulus());
86
87 if ZZ.is_neg(&rem) {
89 ZZ.add_assign_ref(&mut rem, self.modulus());
90 }
91
92 let mut limbs = SmallVec::from_elem(0, self.n_limbs);
93 for (i, d) in ZZ.get_ring().abs_base_u64_repr(&rem).take(self.n_limbs).enumerate() {
94 limbs[i] = d;
95 }
96 Z2kEl(limbs)
97 }
98
99 fn el_to_bigint(&self, ZZ: &RustBigintRing, mut el: Z2kEl<N>) -> El<RustBigintRing> {
100 self.mask_el(&mut el);
101 let mut acc = ZZ.zero();
102 let i128_ring = StaticRing::<i128>::RING;
103 for i in 0..self.n_limbs {
104 let limb = int_cast(el.0[i] as i128, ZZ, &i128_ring);
105 if !ZZ.is_zero(&limb) {
106 let shifted = ZZ.mul(limb, ZZ.power_of_two(64 * i));
107 ZZ.add_assign(&mut acc, shifted);
108 }
109 }
110 acc
111 }
112
113 pub fn abs_base_u64_repr(&self, el: &Z2kEl<N>) -> impl Iterator<Item = u64> {
116 el.0.iter().take(self.n_limbs).enumerate().map(|(i, &x)| {
117 if i == self.n_limbs - 1 {
118 x & self.last_limb_mask
119 } else {
120 x
121 }
122 })
123 }
124
125 pub fn from_base_u64_repr<I>(&self, data: I) -> Z2kEl<N>
128 where
129 I: Iterator<Item = u64>,
130 {
131 let data = data.take(self.n_limbs).enumerate().map(|(i, x)| {
132 if i == self.n_limbs - 1 {
133 x & self.last_limb_mask
134 } else {
135 x
136 }
137 });
138 let mut data = SmallVec::from_iter(data);
139 data.resize(self.n_limbs, 0);
140 Z2kEl(data)
141 }
142}
143
144impl<const N: usize> PartialEq for Z2kBase<N>
145where
146 [u64; N]: smallvec::Array<Item = u64>,
147{
148 fn eq(&self, other: &Self) -> bool { self.k == other.k }
149}
150
151impl<const N: usize> Eq for Z2kBase<N> where [u64; N]: smallvec::Array<Item = u64> {}
152
153pub type Z2k<const N: usize = DEFAULT_SMALLVEC_SIZE> = RingValue<Z2kBase<N>>;
154impl<const N: usize> Z2k<N>
155where
156 [u64; N]: smallvec::Array<Item = u64>,
157{
158 pub fn new(log_modulus: usize) -> Self { RingValue::from(Z2kBase::<N>::new(log_modulus)) }
159}
160
161#[derive(Clone)]
162pub struct Z2kEl<const N: usize>(SmallVec<[u64; N]>)
163where
164 [u64; N]: smallvec::Array<Item = u64>;
165impl<const N: usize> RingBase for Z2kBase<N>
166where
167 [u64; N]: smallvec::Array<Item = u64>,
168{
169 type Element = Z2kEl<N>;
170
171 fn clone_el(&self, val: &Self::Element) -> Self::Element { val.clone() }
172
173 fn add_assign_ref(&self, lhs: &mut Self::Element, rhs: &Self::Element) {
174 let mut carry = 0u64;
175 for i in 0..self.n_limbs {
176 let (sum, o0) = lhs.0[i].overflowing_add(rhs.0[i]);
177 let (sum, o1) = sum.overflowing_add(carry);
178 lhs.0[i] = sum;
179 carry = (o0 || o1) as u64;
180 }
181 }
182
183 fn add_assign(&self, lhs: &mut Self::Element, rhs: Self::Element) { self.add_assign_ref(lhs, &rhs); }
184
185 fn sub_assign_ref(&self, lhs: &mut Self::Element, rhs: &Self::Element) {
186 let mut borrow = 0u64;
187 for i in 0..self.n_limbs {
188 let (diff, o0) = lhs.0[i].overflowing_sub(rhs.0[i]);
189 let (diff, o1) = diff.overflowing_sub(borrow);
190 lhs.0[i] = diff;
191 borrow = (o0 || o1) as u64;
192 }
193 }
194
195 fn negate_inplace(&self, lhs: &mut Self::Element) {
196 let mut z = self.zero();
197 self.sub_assign_ref(&mut z, lhs);
198 *lhs = z;
199 }
200
201 fn mul_assign(&self, lhs: &mut Self::Element, rhs: Self::Element) { self.mul_assign_ref(lhs, &rhs); }
202
203 fn mul_assign_ref(&self, lhs: &mut Self::Element, rhs: &Self::Element) { *lhs = mul_z2k(lhs, rhs, self.n_limbs); }
204
205 fn from_int(&self, value: i32) -> Self::Element {
206 let ZZ = &RustBigintRing::RING;
207 let x = ZZ.int_hom().map(value);
208 self.bigint_to_el(ZZ, &x)
209 }
210
211 fn eq_el(&self, lhs: &Self::Element, rhs: &Self::Element) -> bool {
212 let mut a = lhs.clone();
213 let mut b = rhs.clone();
214 self.mask_el(&mut a);
215 self.mask_el(&mut b);
216 a.0 == b.0
217 }
218
219 fn is_zero(&self, value: &Self::Element) -> bool {
220 let mut v = value.clone();
221 self.mask_el(&mut v);
222 v.0.iter().all(|x| *x == 0)
223 }
224
225 fn is_one(&self, value: &Self::Element) -> bool { self.eq_el(value, &self.one()) }
226
227 fn is_commutative(&self) -> bool { true }
228 fn is_noetherian(&self) -> bool { true }
229
230 fn dbg_within<'a>(
231 &self,
232 value: &Self::Element,
233 out: &mut std::fmt::Formatter<'a>,
234 _: EnvBindingStrength,
235 ) -> std::fmt::Result {
236 let ZZ = RustBigintRing::RING;
237 write!(out, "{}", ZZ.format(&self.el_to_bigint(&ZZ, value.clone())))
238 }
239
240 fn characteristic<J: RingStore + Copy>(&self, ZZ: J) -> Option<El<J>>
241 where
242 J::Type: IntegerRing,
243 {
244 self.size(ZZ)
245 }
246
247 fn is_approximate(&self) -> bool { false }
248}
249
250fn mul_z2k<const N: usize>(Z2kEl(a): &Z2kEl<N>, Z2kEl(b): &Z2kEl<N>, n_limbs: usize) -> Z2kEl<N>
252where
253 [u64; N]: smallvec::Array<Item = u64>,
254{
255 let mut res = SmallVec::from_elem(0, n_limbs);
256 for i in 0..n_limbs {
257 let mut carry = 0u128;
258 for j in 0..n_limbs - i {
259 let idx = i + j;
260 let sum = a[i] as u128 * b[j] as u128 + res[idx] as u128 + carry;
261 res[idx] = sum as u64;
262 carry = sum >> 64;
263 }
264 }
265 Z2kEl(res)
266}
267
268impl<const N: usize> DivisibilityRing for Z2kBase<N>
269where
270 [u64; N]: smallvec::Array<Item = u64>,
271{
272 fn checked_left_div(&self, lhs: &Self::Element, rhs: &Self::Element) -> Option<Self::Element> {
273 super::generic_impls::checked_left_div(RingRef::new(self), lhs, rhs)
274 }
275}
276
277impl<const N: usize> CanHomFrom<Z2kBase<N>> for Z2kBase<N>
278where
279 [u64; N]: smallvec::Array<Item = u64>,
280{
281 type Homomorphism = ();
282
283 fn has_canonical_hom(&self, from: &Z2kBase<N>) -> Option<Self::Homomorphism> {
284 (from.k == self.k).then_some(())
286 }
287
288 fn map_in(
289 &self,
290 _from: &Z2kBase<N>,
291 el: <Z2kBase<N> as RingBase>::Element,
292 _: &Self::Homomorphism,
293 ) -> Self::Element {
294 el
295 }
296}
297
298impl<const N: usize> CanIsoFromTo<Z2kBase<N>> for Z2kBase<N>
299where
300 [u64; N]: smallvec::Array<Item = u64>,
301{
302 type Isomorphism = ();
303
304 fn has_canonical_iso(&self, from: &Z2kBase<N>) -> Option<Self::Isomorphism> { (self.k == from.k).then_some(()) }
305
306 fn map_out(
307 &self,
308 _from: &Z2kBase<N>,
309 el: Self::Element,
310 _: &Self::Isomorphism,
311 ) -> <Z2kBase<N> as RingBase>::Element {
312 el
313 }
314}
315
316#[derive(Clone)]
317pub struct Z2KBaseElementsIter<'a, const N: usize>
318where
319 [u64; N]: smallvec::Array<Item = u64>,
320{
321 ring: &'a Z2kBase<N>,
322 next_index: Z2kEl<N>,
323 wrap_around: bool,
324}
325
326impl<'a, const N: usize> Iterator for Z2KBaseElementsIter<'a, N>
327where
328 [u64; N]: smallvec::Array<Item = u64>,
329{
330 type Item = Z2kEl<N>;
331
332 fn next(&mut self) -> Option<Self::Item> {
333 if self.wrap_around {
334 return None;
335 }
336
337 let out = self.next_index.clone();
338 self.ring.add_assign(&mut self.next_index, self.ring.one());
339 if self.ring.is_zero(&self.next_index) {
340 self.wrap_around = true;
341 }
342 Some(out)
343 }
344}
345
346impl<const N: usize> FiniteRingSpecializable for Z2kBase<N>
347where
348 [u64; N]: smallvec::Array<Item = u64>,
349{
350 fn specialize<O: FiniteRingOperation<Self>>(op: O) -> O::Output { op.execute() }
351}
352
353impl<const N: usize> FiniteRing for Z2kBase<N>
354where
355 [u64; N]: smallvec::Array<Item = u64>,
356{
357 type ElementsIter<'a>
358 = Z2KBaseElementsIter<'a, N>
359 where
360 Self: 'a;
361
362 fn elements<'a>(&'a self) -> Self::ElementsIter<'a> {
363 Z2KBaseElementsIter {
364 ring: self,
365 next_index: self.zero(),
366 wrap_around: false,
367 }
368 }
369
370 fn random_element<G: FnMut() -> u64>(&self, rng: G) -> <Self as RingBase>::Element {
371 super::generic_impls::random_element(self, rng)
372 }
373
374 fn size<I: RingStore + Copy>(&self, other_ZZ: I) -> Option<El<I>>
375 where
376 I::Type: IntegerRing,
377 {
378 {
379 if other_ZZ.get_ring().representable_bits().is_none()
380 || self.k < other_ZZ.get_ring().representable_bits().unwrap()
381 {
382 Some(int_cast(
383 RustBigintRing::RING.clone_el(&self.modulus),
384 other_ZZ,
385 &RustBigintRing::RING,
386 ))
387 } else {
388 None
389 }
390 }
391 }
392}
393
394impl<const N: usize> PrincipalIdealRing for Z2kBase<N>
395where
396 [u64; N]: smallvec::Array<Item = u64>,
397{
398 fn checked_div_min(&self, lhs: &Self::Element, rhs: &Self::Element) -> Option<Self::Element> {
399 super::generic_impls::checked_div_min(RingRef::new(self), lhs, rhs)
400 }
401
402 fn extended_ideal_gen(
403 &self,
404 lhs: &Self::Element,
405 rhs: &Self::Element,
406 ) -> (Self::Element, Self::Element, Self::Element) {
407 let ZZ = RustBigintRing::RING;
408 let l = self.el_to_bigint(&ZZ, lhs.clone());
409 let r = self.el_to_bigint(&ZZ, rhs.clone());
410 let (s, t, d) = ZZ.extended_ideal_gen(&l, &r);
411 let [s, t, d] = [s, t, d].map(|x| self.bigint_to_el(&ZZ, &x));
412 (s, t, d)
413 }
414}
415
416impl<const N: usize, I: ?Sized + IntegerRing> CanHomFrom<I> for Z2kBase<N>
417where
418 [u64; N]: smallvec::Array<Item = u64>,
419{
420 type Homomorphism = super::generic_impls::BigIntToZnHom<I, RustBigintRingBase, Self>;
421
422 fn has_canonical_hom(&self, from: &I) -> Option<Self::Homomorphism> {
423 super::generic_impls::has_canonical_hom_from_bigint(from, self, RustBigintRing::RING.get_ring(), None)
424 }
425
426 default fn map_in(&self, from: &I, el: I::Element, hom: &Self::Homomorphism) -> Self::Element {
427 let ZZ = &RustBigintRing::RING;
428 super::generic_impls::map_in_from_bigint(
429 from,
430 self,
431 ZZ.get_ring(),
432 el,
433 hom,
434 |n| self.bigint_to_el(ZZ, &n),
435 |n| self.bigint_to_el(ZZ, &n),
436 )
437 }
438}
439
440impl<const N: usize> CanHomFrom<RustBigintRingBase> for Z2kBase<N>
441where
442 [u64; N]: smallvec::Array<Item = u64>,
443{
444 fn map_in(
445 &self,
446 _from: &RustBigintRingBase,
447 el: <RustBigintRingBase as RingBase>::Element,
448 _: &Self::Homomorphism,
449 ) -> Self::Element {
450 self.bigint_to_el(&RustBigintRing::RING, &el)
451 }
452}
453
454macro_rules! impl_static_int_to_z2k {
455 ($($int:ident),*) => {
456 $(
457 impl<const N: usize> CanHomFrom<StaticRingBase<$int>> for Z2kBase<N>
458 where
459 [u64; N]: smallvec::Array<Item = u64>,
460 {
461 fn map_in(
462 &self,
463 from: &StaticRingBase<$int>,
464 el: $int,
465 _: &Self::Homomorphism,
466 ) -> Self::Element {
467 let ZZ = &RustBigintRing::RING;
468 let x = int_cast(el, ZZ, RingRef::new(from));
469 self.bigint_to_el(ZZ, &x)
470 }
471 }
472 )*
473 };
474}
475
476impl_static_int_to_z2k! { i8, i16, i32, i64, i128 }
477
478impl<const N: usize> ZnRing for Z2kBase<N>
479where
480 [u64; N]: smallvec::Array<Item = u64>,
481{
482 type IntegerRingBase = RustBigintRingBase;
483 type IntegerRing = RustBigintRing;
484
485 fn integer_ring(&self) -> &Self::IntegerRing { &RustBigintRing::RING }
486
487 fn smallest_positive_lift(&self, el: Self::Element) -> El<Self::IntegerRing> {
488 let ZZ = RustBigintRing::RING;
489 self.el_to_bigint(&ZZ, el)
490 }
491
492 fn smallest_lift(&self, el: Self::Element) -> El<Self::IntegerRing> {
493 let result = self.smallest_positive_lift(el);
494 let mut mod_half = self.integer_ring().clone_el(self.modulus());
495 self.integer_ring().euclidean_div_pow_2(&mut mod_half, 1);
496 if self.integer_ring().is_gt(&result, &mod_half) {
497 self.integer_ring().sub_ref_snd(result, self.modulus())
498 } else {
499 result
500 }
501 }
502
503 fn modulus(&self) -> &El<Self::IntegerRing> { &self.modulus }
504
505 fn any_lift(&self, el: Self::Element) -> El<Self::IntegerRing> { self.smallest_positive_lift(el) }
506
507 fn from_int_promise_reduced(&self, x: El<Self::IntegerRing>) -> Self::Element {
508 debug_assert!({
509 let ZZ = RustBigintRing::RING;
510 !ZZ.is_neg(&x) && ZZ.is_lt(&x, &self.modulus)
511 });
512 self.bigint_to_el(&RustBigintRing::RING, &x)
513 }
514}
515
516impl_field_wrap_unwrap_homs! { Z2kBase, Z2kBase }
517impl_field_wrap_unwrap_isos! { Z2kBase, Z2kBase }
518impl_localpir_wrap_unwrap_homs! { Z2kBase, Z2kBase }
519impl_localpir_wrap_unwrap_isos! { Z2kBase, Z2kBase }
520
521#[cfg(test)]
522mod tests {
523 use super::*;
524 use crate::primitive_int::StaticRing;
525 use crate::ring::generic_tests as ring_generic_tests;
526 use crate::rings::zn::generic_tests as zn_generic_tests;
527
528 const SMALL_Ks: [usize; 2] = [3, 8];
529 const ZZ: BigIntRing = BigIntRing::RING;
530
531 #[test]
532 fn test_z2k_can_hom_map_in_large_power() {
533 let r: Z2k = Z2k::new(256);
534 zn_generic_tests::test_map_in_large_int(r);
535 }
536
537 #[test]
538 fn test_z2k_can_hom_axioms_static_i32() {
539 let to: Z2k = Z2k::new(14);
540 ring_generic_tests::test_hom_axioms(StaticRing::<i32>::RING, to, -12i32..=12);
541 }
542
543 #[test]
544 fn test_z2k_can_hom_axioms_bigint_ring() {
545 let to: Z2k = Z2k::new(20);
546 let edge = [
547 ZZ.zero(),
548 ZZ.one(),
549 ZZ.negate(ZZ.one()),
550 ZZ.int_hom().map(17),
551 ZZ.int_hom().map(-42),
552 ZZ.pow(ZZ.int_hom().map(2), 200),
553 ];
554 ring_generic_tests::test_hom_axioms(&ZZ, to, edge.into_iter());
555 }
556
557 #[test]
558 fn test_z2k_pow2_64_add_mul_edge_cases() {
559 let r: Z2k = Z2k::new(64);
560
561 let almost_mod = ZZ.sub(ZZ.power_of_two(64), ZZ.int_hom().map(5));
562 let a = r.coerce(&ZZ, almost_mod);
563 let b = r.int_hom().map(5);
564 assert_el_eq!(r, r.zero(), r.add_ref(&a, &b));
565
566 let pow32 = ZZ.power_of_two(32);
568 let u = r.coerce(&ZZ, pow32);
569 assert_el_eq!(r, r.zero(), r.mul_ref(&u, &u));
570 }
571
572 #[test]
573 fn test_z2k_pow2_130_add_mul_edge_cases() {
574 let r: Z2k = Z2k::new(130);
575
576 let almost_mod = ZZ.sub(ZZ.power_of_two(130), ZZ.int_hom().map(11));
578 let a = r.coerce(&ZZ, almost_mod);
579 let b = r.int_hom().map(11);
580 assert_el_eq!(r, r.zero(), r.add_ref(&a, &b));
581
582 let hi = ZZ.add(ZZ.power_of_two(100), ZZ.power_of_two(129));
584 let expected = ZZ.euclidean_rem(ZZ.mul_ref(&hi, &hi), &r.modulus());
585 let x = r.coerce(&ZZ, hi);
586 let y = r.mul_ref(&x, &x);
587 assert!(ZZ.eq_el(&expected, &r.smallest_positive_lift(y)));
588 }
589
590 #[test]
591 fn test_ring_axioms_z2kbase() {
592 for k in SMALL_Ks {
593 let ring: Z2k = Z2k::new(k);
594 crate::ring::generic_tests::test_ring_axioms(&ring, ring.elements())
595 }
596 }
597
598 #[test]
599 fn test_zn_ring_axioms_znbase() {
600 for k in SMALL_Ks {
601 let ring: Z2k = Z2k::new(k);
602 crate::rings::zn::generic_tests::test_zn_axioms(ring);
603 }
604 }
605
606 #[test]
607 fn test_divisibility_axioms() {
608 for k in SMALL_Ks {
609 let R: Z2k = Z2k::new(k);
610 crate::divisibility::generic_tests::test_divisibility_axioms(&R, R.elements());
611 }
612 }
613
614 #[test]
615 fn test_principal_ideal_ring_axioms() {
616 for k in SMALL_Ks {
617 let R: Z2k = Z2k::new(k);
618 crate::pid::generic_tests::test_principal_ideal_ring_axioms(&R, R.elements());
619 }
620 }
621
622 #[test]
623 fn test_finite_field_axioms() {
624 for k in SMALL_Ks {
625 let R: Z2k = Z2k::new(k);
626 crate::rings::finite::generic_tests::test_finite_ring_axioms(&R);
627 }
628 }
629
630 #[test]
631 fn test_abs_from_base_u64_repr_roundtrip() {
632 let r: Z2k = Z2k::new(130);
633 let rb = r.get_ring();
634 let x = rb.from_base_u64_repr([1u64, 2, 0xFFFF_FFFF_FFFF_FFFDu64].into_iter());
635 let y = rb.from_base_u64_repr(rb.abs_base_u64_repr(&x));
636 assert_el_eq!(r, x, y);
637 }
638
639 #[test]
640 fn test_from_base_u64_repr_iterator_shorter_than_limbs() {
641 let r: Z2k = Z2k::new(130);
642 let rb = r.get_ring();
643 assert_eq!(rb.n_limbs, 3);
644
645 let padded = rb.from_base_u64_repr([0xDEAD_BEEF_CAFEu64].into_iter());
646 let full = rb.from_base_u64_repr([0xDEAD_BEEF_CAFEu64, 0, 0].into_iter());
647 assert_el_eq!(r, padded, full);
648 }
649}