1use alloc::vec::Vec;
14use core::ops::{Add, Mul, Sub};
15use core::{ptr, slice};
16
17use p3_field::extension::HasFrobenius;
18use p3_field::op_assign_macros::{impl_add_base_field, impl_sub_base_field};
19use p3_field::{
20 Algebra, BasedVectorSpace, ExtensionField, Field, PackedFieldExtension, Powers,
21 PrimeCharacteristicRing,
22};
23
24use crate::tower::TowerLevel;
25use crate::{BinaryField8, BinaryField16, BinaryField32, BinaryField64, BinaryField128};
26
27macro_rules! binary_tower_extension {
32 ($upper:ty, $upper_repr:ty, $lower:ty, $lower_repr:ty) => {
33 impl From<$lower> for $upper {
34 #[inline]
36 fn from(x: $lower) -> Self {
37 Self::from_repr(<$upper_repr>::from(x.to_repr()))
38 }
39 }
40
41 impl_add_base_field!($upper, $lower);
42 impl_sub_base_field!($upper, $lower);
43 impl Mul<$lower> for $upper {
44 type Output = Self;
45
46 #[inline]
47 fn mul(self, rhs: $lower) -> Self {
48 if crate::clmul::HAS_HARDWARE_CLMUL
54 && matches!(<$upper>::BITS, 64 | 128)
55 && matches!(<$lower>::BITS, 16 | 32)
56 {
57 return self * Self::from(rhs);
58 }
59 <Self as BasedVectorSpace<$lower>>::from_basis_coefficients_fn(|i| {
60 <$lower>::from_repr((self.to_repr() >> (i * <$lower>::BITS)) as $lower_repr) * rhs
61 })
62 }
63 }
64
65 impl Mul<$upper> for $lower {
66 type Output = $upper;
67
68 #[inline]
69 fn mul(self, rhs: $upper) -> $upper {
70 rhs * self
71 }
72 }
73
74 impl Algebra<$lower> for $upper {}
75
76 impl p3_field::AlgebraIdentity<$lower> for $upper {
77 fn algebra_id() -> Vec<u8> {
78 alloc::format!("p3-Wiedemann-tower-v1:chunk-basis:{}:{}", <$lower>::BITS, <$upper>::BITS).into_bytes()
79 }
80 }
81
82 impl BasedVectorSpace<$lower> for $upper {
83 const DIMENSION: usize = <$upper>::BITS / <$lower>::BITS;
84
85 #[inline]
86 fn as_basis_coefficients_slice(&self) -> &[$lower] {
87 const {
88 assert!(
89 cfg!(target_endian = "little"),
90 "the tower basis coincides with the memory layout only on little-endian targets"
91 );
92 assert!(<$lower>::BITS == <$lower_repr>::BITS as usize);
95 assert!(
96 size_of::<$upper>()
97 == <$upper as BasedVectorSpace<$lower>>::DIMENSION
98 * size_of::<$lower>()
99 );
100 assert!(align_of::<$lower>() <= align_of::<$upper>());
101 }
102
103 unsafe {
109 slice::from_raw_parts(
110 ptr::from_ref(self).cast::<$lower>(),
111 <$upper as BasedVectorSpace<$lower>>::DIMENSION,
112 )
113 }
114 }
115
116 #[inline]
117 fn from_basis_coefficients_fn<Fn: FnMut(usize) -> $lower>(mut f: Fn) -> Self {
118 let mut repr: $upper_repr = 0;
119 for i in 0..<$upper as BasedVectorSpace<$lower>>::DIMENSION {
120 repr |= <$upper_repr>::from(f(i).to_repr()) << (i * <$lower>::BITS);
121 }
122 Self::from_repr(repr)
123 }
124
125 #[inline]
134 fn flatten_to_base(vec: Vec<Self>) -> Vec<$lower> {
135 const {
136 assert!(
137 cfg!(target_endian = "little"),
138 "the tower basis coincides with the memory layout only on little-endian targets"
139 );
140 assert!(<$lower>::BITS == <$lower_repr>::BITS as usize);
141 assert!(
142 size_of::<$upper>()
143 == <$upper as BasedVectorSpace<$lower>>::DIMENSION
144 * size_of::<$lower>()
145 );
146 assert!(align_of::<$lower>() <= align_of::<$upper>());
147 }
148
149 let coefficients: &[$lower] = unsafe {
158 slice::from_raw_parts(
159 vec.as_ptr().cast::<$lower>(),
160 vec.len() * <$upper as BasedVectorSpace<$lower>>::DIMENSION,
161 )
162 };
163 coefficients.to_vec()
164 }
165
166 #[inline]
167 fn from_basis_coefficients_iter<I: ExactSizeIterator<Item = $lower>>(
168 iter: I,
169 ) -> Option<Self> {
170 let dimension = <$upper as BasedVectorSpace<$lower>>::DIMENSION;
171 (iter.len() == dimension).then(|| {
172 let mut repr: $upper_repr = 0;
173 for (i, c) in (0..dimension).zip(iter) {
175 repr |= <$upper_repr>::from(c.to_repr()) << (i * <$lower>::BITS);
176 }
177 Self::from_repr(repr)
178 })
179 }
180
181 #[cfg(target_endian = "little")]
182 #[inline]
183 fn reconstitute_from_base(vec: Vec<$lower>) -> Vec<Self> {
184 let dimension = <Self as BasedVectorSpace<$lower>>::DIMENSION;
185 assert_eq!(vec.len() % dimension, 0);
186 let len = vec.len() / dimension;
187 let mut result = Vec::<Self>::with_capacity(len);
188 unsafe {
192 ptr::copy_nonoverlapping(
193 vec.as_ptr().cast::<u8>(),
194 result.as_mut_ptr().cast::<u8>(),
195 vec.len() * size_of::<$lower>(),
196 );
197 result.set_len(len);
198 }
199 result
200 }
201 }
202
203 impl ExtensionField<$lower> for $upper {
204 type ExtensionPacking = Self;
205
206 #[inline]
207 fn is_in_basefield(&self) -> bool {
208 (self.to_repr() >> <$lower>::BITS) == 0
211 }
212
213 #[inline]
214 fn as_base(&self) -> Option<$lower> {
215 <Self as ExtensionField<$lower>>::is_in_basefield(self)
216 .then(|| <$lower>::from_repr(self.to_repr() as $lower_repr))
217 }
218 }
219
220 impl PackedFieldExtension<$lower, $upper> for $upper {
222 #[inline]
223 fn from_ext_fn(f: impl Fn(usize) -> Self) -> Self {
224 f(0)
225 }
226
227 #[inline]
228 fn from_ext_slice(slice: &[Self]) -> Self {
229 assert_eq!(slice.len(), 1);
230 slice[0]
231 }
232
233 #[inline]
234 fn extract(&self, lane: usize) -> Self {
235 assert_eq!(lane, 0, "lane index out of range");
236 *self
237 }
238
239 #[inline]
240 fn add_assign_lane(&mut self, lane: usize, value: Self) {
241 assert_eq!(lane, 0, "lane index out of range");
242 *self += value;
243 }
244
245 #[inline]
246 fn packed_ext_powers(base: Self) -> Powers<Self> {
247 base.powers()
248 }
249 }
250
251 impl HasFrobenius<$lower> for $upper {
252 #[inline]
254 fn frobenius(&self) -> Self {
255 if <$upper as BasedVectorSpace<$lower>>::DIMENSION == 2 {
261 let (a0, a1) = self.split();
264 Self::join(a0 + a1.mul_alpha(), a1)
265 } else {
266 self.exp_power_of_2(<$lower>::BITS)
267 }
268 }
269
270 #[inline]
271 fn repeated_frobenius(&self, count: usize) -> Self {
272 let count = count % <$upper as BasedVectorSpace<$lower>>::DIMENSION;
274 if <$upper as BasedVectorSpace<$lower>>::DIMENSION == 2 {
275 if count == 0 {
276 *self
277 } else {
278 <Self as HasFrobenius<$lower>>::frobenius(self)
279 }
280 } else {
281 self.exp_power_of_2(<$lower>::BITS * count)
282 }
283 }
284
285 #[inline]
286 fn pseudo_inv(&self) -> Self {
287 self.try_inverse().unwrap_or(Self::ZERO)
290 }
291 }
292 };
293}
294
295binary_tower_extension!(BinaryField16, u16, BinaryField8, u8);
296binary_tower_extension!(BinaryField32, u32, BinaryField8, u8);
297binary_tower_extension!(BinaryField64, u64, BinaryField8, u8);
298binary_tower_extension!(BinaryField128, u128, BinaryField8, u8);
299binary_tower_extension!(BinaryField32, u32, BinaryField16, u16);
300binary_tower_extension!(BinaryField64, u64, BinaryField16, u16);
301binary_tower_extension!(BinaryField128, u128, BinaryField16, u16);
302binary_tower_extension!(BinaryField64, u64, BinaryField32, u32);
303binary_tower_extension!(BinaryField128, u128, BinaryField32, u32);
304binary_tower_extension!(BinaryField128, u128, BinaryField64, u64);
305
306#[cfg(test)]
307mod tests {
308 use alloc::vec::Vec;
309
310 use p3_field::extension::HasFrobenius;
311 use p3_field::{BasedVectorSpace, ExtensionField, PrimeCharacteristicRing};
312 use proptest::prelude::*;
313
314 use crate::tower::TowerLevel;
315 use crate::{BinaryField8, BinaryField16, BinaryField32, BinaryField64, BinaryField128};
316
317 const A128: u128 = 0x1234_5678_9abc_def0_0fed_cba9_8765_4321;
319
320 macro_rules! assert_basis_round_trip {
323 ($upper:ty, $lower:ty, $val:expr) => {{
324 let a = <$upper>::from_repr($val);
325 let dim = <$upper as BasedVectorSpace<$lower>>::DIMENSION;
326 let coeffs = BasedVectorSpace::<$lower>::as_basis_coefficients_slice(&a);
327 assert_eq!(coeffs.len(), dim);
328 for len in [0, 1, 19] {
329 let values = alloc::vec![a; len];
330 let flat = <$upper as BasedVectorSpace<$lower>>::flatten_to_base(values.clone());
331 assert_eq!(<$upper as BasedVectorSpace<$lower>>::reconstitute_from_base(flat), values);
332 }
333
334 let recomposed = (0..dim)
336 .map(|i| {
337 <$upper as BasedVectorSpace<$lower>>::ith_basis_element(i).unwrap() * coeffs[i]
338 })
339 .sum::<$upper>();
340 assert_eq!(recomposed, a);
341
342 assert_eq!(
343 <$upper as BasedVectorSpace<$lower>>::from_basis_coefficients_slice(coeffs),
344 Some(a)
345 );
346 assert_eq!(
347 <$upper as BasedVectorSpace<$lower>>::from_basis_coefficients_fn(|i| coeffs[i]),
348 a
349 );
350 }};
351 }
352
353 macro_rules! assert_mixed_ops_match_embedding {
355 ($upper:ty, $lower:ty, $val:expr, $scalar:expr) => {{
356 let a = <$upper>::from_repr($val);
357 let s = <$lower>::from_repr($scalar);
358 let s_up = <$upper>::from(s);
359
360 assert_eq!(a + s, a + s_up);
361 assert_eq!(a - s, a - s_up);
362 assert_eq!(a * s, a * s_up);
363 assert_eq!(s * a, a * s_up);
364
365 let mut acc = a;
366 acc += s;
367 assert_eq!(acc, a + s_up);
368 let mut acc = a;
369 acc -= s;
370 assert_eq!(acc, a - s_up);
371 let mut acc = a;
372 acc *= s;
373 assert_eq!(acc, a * s_up);
374 }};
375 }
376
377 macro_rules! assert_frobenius_over {
380 ($upper:ty, $lower:ty, $val:expr) => {{
381 let dim = <$upper as BasedVectorSpace<$lower>>::DIMENSION;
382
383 for i in 0..=u8::MAX {
385 let embedded = <$upper>::from(<$lower>::from_repr(i.into()));
386 assert_eq!(
387 HasFrobenius::<$lower>::frobenius(&embedded),
388 embedded,
389 "Frobenius must fix the embedded base field"
390 );
391 }
392
393 let x = <$upper as BasedVectorSpace<$lower>>::ith_basis_element(1).unwrap();
396 assert_ne!(
397 HasFrobenius::<$lower>::frobenius(&x),
398 x,
399 "Frobenius must move elements outside the base field"
400 );
401
402 let a = <$upper>::from_repr($val);
404 let mut acc = a;
405 for count in 0..(2 * dim + 3) {
406 assert_eq!(HasFrobenius::<$lower>::repeated_frobenius(&a, count), acc);
407 acc = HasFrobenius::<$lower>::frobenius(&acc);
408 }
409
410 assert_eq!(
412 HasFrobenius::<$lower>::pseudo_inv(&<$upper>::ZERO),
413 <$upper>::ZERO
414 );
415 assert_eq!(a * HasFrobenius::<$lower>::pseudo_inv(&a), <$upper>::ONE);
416 }};
417 }
418
419 #[test]
420 fn gf2_128_over_gf2_8_basis_coefficients_are_bytes() {
421 let a = BinaryField128::from_repr(0x0f0e_0d0c_0b0a_0908_0706_0504_0302_0100);
422 let coeffs = BasedVectorSpace::<BinaryField8>::as_basis_coefficients_slice(&a);
423 assert_eq!(coeffs.len(), 16);
424 for (i, c) in coeffs.iter().enumerate() {
425 assert_eq!(*c, BinaryField8::from_repr(i as u8));
426 }
427 assert_eq!(
428 <BinaryField128 as BasedVectorSpace<BinaryField8>>::from_basis_coefficients_slice(
429 coeffs
430 ),
431 Some(a)
432 );
433 }
434
435 #[test]
436 fn mixed_mul_matches_embedding() {
437 let a = BinaryField128::from_repr(A128);
438 let s = BinaryField8::from_repr(0xa7);
439 assert_eq!(a * s, a * BinaryField128::from(s));
440 }
441
442 #[test]
443 fn dimension_is_the_ratio_of_bit_widths() {
444 assert_eq!(
445 <BinaryField16 as BasedVectorSpace<BinaryField8>>::DIMENSION,
446 2
447 );
448 assert_eq!(
449 <BinaryField32 as BasedVectorSpace<BinaryField8>>::DIMENSION,
450 4
451 );
452 assert_eq!(
453 <BinaryField64 as BasedVectorSpace<BinaryField8>>::DIMENSION,
454 8
455 );
456 assert_eq!(
457 <BinaryField128 as BasedVectorSpace<BinaryField8>>::DIMENSION,
458 16
459 );
460 assert_eq!(
461 <BinaryField32 as BasedVectorSpace<BinaryField16>>::DIMENSION,
462 2
463 );
464 assert_eq!(
465 <BinaryField64 as BasedVectorSpace<BinaryField16>>::DIMENSION,
466 4
467 );
468 assert_eq!(
469 <BinaryField128 as BasedVectorSpace<BinaryField16>>::DIMENSION,
470 8
471 );
472 assert_eq!(
473 <BinaryField64 as BasedVectorSpace<BinaryField32>>::DIMENSION,
474 2
475 );
476 assert_eq!(
477 <BinaryField128 as BasedVectorSpace<BinaryField32>>::DIMENSION,
478 4
479 );
480 assert_eq!(
481 <BinaryField128 as BasedVectorSpace<BinaryField64>>::DIMENSION,
482 2
483 );
484 }
485
486 #[test]
487 fn basis_coefficients_round_trip_on_every_pair() {
488 assert_basis_round_trip!(BinaryField16, BinaryField8, 0xfedc);
489 assert_basis_round_trip!(BinaryField32, BinaryField8, 0xfedc_ba98);
490 assert_basis_round_trip!(BinaryField64, BinaryField8, 0xfedc_ba98_7654_3210);
491 assert_basis_round_trip!(BinaryField128, BinaryField8, A128);
492 assert_basis_round_trip!(BinaryField32, BinaryField16, 0xfedc_ba98);
493 assert_basis_round_trip!(BinaryField64, BinaryField16, 0xfedc_ba98_7654_3210);
494 assert_basis_round_trip!(BinaryField128, BinaryField16, A128);
495 assert_basis_round_trip!(BinaryField64, BinaryField32, 0xfedc_ba98_7654_3210);
496 assert_basis_round_trip!(BinaryField128, BinaryField32, A128);
497 assert_basis_round_trip!(BinaryField128, BinaryField64, A128);
498 }
499
500 #[test]
501 fn mixed_ops_match_embedding_on_every_pair() {
502 assert_mixed_ops_match_embedding!(BinaryField16, BinaryField8, 0xfedc, 0xa7);
503 assert_mixed_ops_match_embedding!(BinaryField32, BinaryField8, 0xfedc_ba98, 0xa7);
504 assert_mixed_ops_match_embedding!(BinaryField64, BinaryField8, 0xfedc_ba98_7654_3210, 0xa7);
505 assert_mixed_ops_match_embedding!(BinaryField128, BinaryField8, A128, 0xa7);
506 assert_mixed_ops_match_embedding!(BinaryField32, BinaryField16, 0xfedc_ba98, 0xa73b);
507 assert_mixed_ops_match_embedding!(
508 BinaryField64,
509 BinaryField16,
510 0xfedc_ba98_7654_3210,
511 0xa73b
512 );
513 assert_mixed_ops_match_embedding!(BinaryField128, BinaryField16, A128, 0xa73b);
514 assert_mixed_ops_match_embedding!(
515 BinaryField64,
516 BinaryField32,
517 0xfedc_ba98_7654_3210,
518 0xa73b_c91d
519 );
520 assert_mixed_ops_match_embedding!(BinaryField128, BinaryField32, A128, 0xa73b_c91d);
521 assert_mixed_ops_match_embedding!(
522 BinaryField128,
523 BinaryField64,
524 A128,
525 0xa73b_c91d_5566_7788
526 );
527 }
528
529 #[test]
530 fn frobenius_fixes_exactly_the_lower_level_on_every_pair() {
531 assert_frobenius_over!(BinaryField16, BinaryField8, 0xfedc);
532 assert_frobenius_over!(BinaryField32, BinaryField8, 0xfedc_ba98);
533 assert_frobenius_over!(BinaryField64, BinaryField8, 0xfedc_ba98_7654_3210);
534 assert_frobenius_over!(BinaryField128, BinaryField8, A128);
535 assert_frobenius_over!(BinaryField32, BinaryField16, 0xfedc_ba98);
536 assert_frobenius_over!(BinaryField64, BinaryField16, 0xfedc_ba98_7654_3210);
537 assert_frobenius_over!(BinaryField128, BinaryField16, A128);
538 assert_frobenius_over!(BinaryField64, BinaryField32, 0xfedc_ba98_7654_3210);
539 assert_frobenius_over!(BinaryField128, BinaryField32, A128);
540 assert_frobenius_over!(BinaryField128, BinaryField64, A128);
541 }
542
543 #[test]
546 fn frobenius_exponent_is_the_order_of_the_base_field() {
547 let a = BinaryField128::from_repr(A128);
548 assert_eq!(
549 HasFrobenius::<BinaryField8>::frobenius(&a),
550 a.exp_power_of_2(8)
551 );
552 assert_eq!(
553 HasFrobenius::<BinaryField64>::frobenius(&a),
554 a.exp_power_of_2(64)
555 );
556 }
557
558 #[test]
559 fn is_in_basefield_detects_the_embedded_subfield() {
560 let low = BinaryField128::from(BinaryField8::from_repr(0xa7));
561 assert!(ExtensionField::<BinaryField8>::is_in_basefield(&low));
562 assert_eq!(
563 ExtensionField::<BinaryField8>::as_base(&low),
564 Some(BinaryField8::from_repr(0xa7))
565 );
566
567 let mid = BinaryField128::from_repr(0xa73b);
569 assert!(!ExtensionField::<BinaryField8>::is_in_basefield(&mid));
570 assert_eq!(ExtensionField::<BinaryField8>::as_base(&mid), None);
571 assert!(ExtensionField::<BinaryField16>::is_in_basefield(&mid));
572 assert_eq!(
573 ExtensionField::<BinaryField16>::as_base(&mid),
574 Some(BinaryField16::from_repr(0xa73b))
575 );
576 }
577
578 #[test]
579 fn from_basis_coefficients_iter_checks_the_length() {
580 let coeffs = [BinaryField64::ONE, BinaryField64::ZERO];
581 assert_eq!(
582 <BinaryField128 as BasedVectorSpace<BinaryField64>>::from_basis_coefficients_iter(
583 coeffs.into_iter()
584 ),
585 Some(BinaryField128::ONE)
586 );
587 assert_eq!(
588 <BinaryField128 as BasedVectorSpace<BinaryField64>>::from_basis_coefficients_iter(
589 coeffs[..1].iter().copied()
590 ),
591 None
592 );
593 assert_eq!(
594 <BinaryField128 as BasedVectorSpace<BinaryField64>>::from_basis_coefficients_slice(&[]),
595 None
596 );
597 }
598
599 #[test]
602 fn flatten_and_reconstitute_round_trip() {
603 let elems: Vec<BinaryField128> = (0..64u128)
604 .map(|i| {
605 BinaryField128::from_repr(i.wrapping_mul(0x9e37_79b9_7f4a_7c15_f39c_c060_5ced_c834))
606 })
607 .collect();
608
609 for pos in 0..16 {
612 assert!(
613 elems.iter().any(|e| {
614 BasedVectorSpace::<BinaryField8>::as_basis_coefficients_slice(e)[pos]
615 != BinaryField8::ZERO
616 }),
617 "coefficient position {pos} is never nonzero"
618 );
619 }
620
621 let flat =
622 <BinaryField128 as BasedVectorSpace<BinaryField8>>::flatten_to_base(elems.clone());
623 assert_eq!(flat.len(), elems.len() * 16);
624 for (i, e) in elems.iter().enumerate() {
625 assert_eq!(&flat[i * 16..(i + 1) * 16], e.as_basis_coefficients_slice());
626 }
627
628 let back = <BinaryField128 as BasedVectorSpace<BinaryField8>>::reconstitute_from_base(flat);
629 assert_eq!(back, elems);
630 }
631
632 #[test]
633 #[should_panic]
634 fn reconstitute_rejects_an_incomplete_element() {
635 let _ = <BinaryField128 as BasedVectorSpace<BinaryField64>>::reconstitute_from_base(
636 alloc::vec![BinaryField64::ONE],
637 );
638 }
639
640 #[test]
642 fn reconstitute_is_exhaustive_over_the_byte_level() {
643 let all: Vec<BinaryField8> = (0..=u8::MAX).map(BinaryField8::from_repr).collect();
644 let packed =
645 <BinaryField16 as BasedVectorSpace<BinaryField8>>::reconstitute_from_base(all.clone());
646 assert_eq!(packed.len(), 128);
647 let flat = <BinaryField16 as BasedVectorSpace<BinaryField8>>::flatten_to_base(packed);
648 assert_eq!(flat, all);
649 }
650
651 macro_rules! assert_frobenius_is_repeated_squaring {
654 ($upper:ty, $lower:ty, $val:expr) => {{
655 let a = <$upper>::from_repr($val);
656 let dim = <$upper as BasedVectorSpace<$lower>>::DIMENSION;
657 assert_eq!(dim, 2, "the specialisation only covers adjacent levels");
658
659 prop_assert_eq!(
660 HasFrobenius::<$lower>::frobenius(&a),
661 a.exp_power_of_2(<$lower>::BITS)
662 );
663 for count in 0..(2 * dim + 3) {
664 prop_assert_eq!(
665 HasFrobenius::<$lower>::repeated_frobenius(&a, count),
666 a.exp_power_of_2(<$lower>::BITS * (count % dim))
667 );
668 }
669 }};
670 }
671
672 proptest! {
673 #![proptest_config(ProptestConfig::with_cases(2000))]
674
675 #[test]
678 fn frobenius_over_the_adjacent_level_is_repeated_squaring(
679 a in any::<u128>(),
680 ) {
681 assert_frobenius_is_repeated_squaring!(BinaryField16, BinaryField8, a as u16);
682 assert_frobenius_is_repeated_squaring!(BinaryField32, BinaryField16, a as u32);
683 assert_frobenius_is_repeated_squaring!(BinaryField64, BinaryField32, a as u64);
684 assert_frobenius_is_repeated_squaring!(BinaryField128, BinaryField64, a);
685 }
686
687 #[test]
690 fn frobenius_over_a_deeper_level_is_repeated_squaring(a in any::<u128>()) {
691 macro_rules! assert_unspecialised {
692 ($upper:ty, $lower:ty, $val:expr) => {{
693 let x = <$upper>::from_repr($val);
694 prop_assert_eq!(
695 HasFrobenius::<$lower>::frobenius(&x),
696 x.exp_power_of_2(<$lower>::BITS)
697 );
698 }};
699 }
700
701 assert_unspecialised!(BinaryField32, BinaryField8, a as u32);
702 assert_unspecialised!(BinaryField64, BinaryField8, a as u64);
703 assert_unspecialised!(BinaryField128, BinaryField8, a);
704 assert_unspecialised!(BinaryField64, BinaryField16, a as u64);
705 assert_unspecialised!(BinaryField128, BinaryField16, a);
706 assert_unspecialised!(BinaryField128, BinaryField32, a);
707 }
708 }
709
710 macro_rules! assert_flatten_matches_concatenation {
713 ($upper:ty, $lower:ty, $vals:expr) => {{
714 let elements: Vec<$upper> = $vals.iter().map(|&v| <$upper>::from_repr(v)).collect();
715
716 let expected: Vec<$lower> = elements
717 .iter()
718 .flat_map(|x| {
719 <$upper as BasedVectorSpace<$lower>>::as_basis_coefficients_slice(x).to_vec()
720 })
721 .collect();
722
723 let flat = <$upper as BasedVectorSpace<$lower>>::flatten_to_base(elements.clone());
724 assert_eq!(flat, expected);
725 assert_eq!(
726 flat.len(),
727 elements.len() * <$upper as BasedVectorSpace<$lower>>::DIMENSION
728 );
729 assert_eq!(
730 <$upper as BasedVectorSpace<$lower>>::reconstitute_from_base(flat),
731 elements
732 );
733 }};
734 }
735
736 #[test]
737 fn flatten_to_base_concatenates_the_coefficients_on_every_pair() {
738 assert_flatten_matches_concatenation!(BinaryField128, BinaryField8, [0u128; 0]);
740
741 let vals16 = [0u16, 1, 0xfedc, 0xffff];
742 let vals32 = [0u32, 1, 0xfedc_ba98, 0xffff_ffff];
743 let vals64 = [0u64, 1, 0xfedc_ba98_7654_3210, u64::MAX];
744 let vals128 = [0u128, 1, A128, u128::MAX];
745
746 assert_flatten_matches_concatenation!(BinaryField16, BinaryField8, vals16);
747 assert_flatten_matches_concatenation!(BinaryField32, BinaryField8, vals32);
748 assert_flatten_matches_concatenation!(BinaryField64, BinaryField8, vals64);
749 assert_flatten_matches_concatenation!(BinaryField128, BinaryField8, vals128);
750 assert_flatten_matches_concatenation!(BinaryField32, BinaryField16, vals32);
751 assert_flatten_matches_concatenation!(BinaryField64, BinaryField16, vals64);
752 assert_flatten_matches_concatenation!(BinaryField128, BinaryField16, vals128);
753 assert_flatten_matches_concatenation!(BinaryField64, BinaryField32, vals64);
754 assert_flatten_matches_concatenation!(BinaryField128, BinaryField32, vals128);
755 assert_flatten_matches_concatenation!(BinaryField128, BinaryField64, vals128);
756 }
757
758 #[test]
760 fn embedding_is_a_ring_homomorphism() {
761 for a in 0..=u8::MAX {
762 for b in 0..=u8::MAX {
763 let (x, y) = (BinaryField8::from_repr(a), BinaryField8::from_repr(b));
764 assert_eq!(
765 BinaryField128::from(x * y),
766 BinaryField128::from(x) * BinaryField128::from(y)
767 );
768 assert_eq!(
769 BinaryField128::from(x + y),
770 BinaryField128::from(x) + BinaryField128::from(y)
771 );
772 }
773 }
774 assert_eq!(BinaryField128::from(BinaryField8::ONE), BinaryField128::ONE);
775 }
776}