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_mul_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_base_field!($upper, $lower);
44
45 impl Algebra<$lower> for $upper {}
46
47 impl BasedVectorSpace<$lower> for $upper {
48 const DIMENSION: usize = <$upper>::BITS / <$lower>::BITS;
49
50 #[inline]
51 fn as_basis_coefficients_slice(&self) -> &[$lower] {
52 const {
53 assert!(
54 cfg!(target_endian = "little"),
55 "the tower basis coincides with the memory layout only on little-endian targets"
56 );
57 assert!(<$lower>::BITS == <$lower_repr>::BITS as usize);
60 assert!(
61 size_of::<$upper>()
62 == <$upper as BasedVectorSpace<$lower>>::DIMENSION
63 * size_of::<$lower>()
64 );
65 assert!(align_of::<$lower>() <= align_of::<$upper>());
66 }
67
68 unsafe {
74 slice::from_raw_parts(
75 ptr::from_ref(self).cast::<$lower>(),
76 <$upper as BasedVectorSpace<$lower>>::DIMENSION,
77 )
78 }
79 }
80
81 #[inline]
82 fn from_basis_coefficients_fn<Fn: FnMut(usize) -> $lower>(mut f: Fn) -> Self {
83 let mut repr: $upper_repr = 0;
84 for i in 0..<$upper as BasedVectorSpace<$lower>>::DIMENSION {
85 repr |= <$upper_repr>::from(f(i).to_repr()) << (i * <$lower>::BITS);
86 }
87 Self::from_repr(repr)
88 }
89
90 #[inline]
91 fn from_basis_coefficients_iter<I: ExactSizeIterator<Item = $lower>>(
92 iter: I,
93 ) -> Option<Self> {
94 let dimension = <$upper as BasedVectorSpace<$lower>>::DIMENSION;
95 (iter.len() == dimension).then(|| {
96 let mut repr: $upper_repr = 0;
97 for (i, c) in (0..dimension).zip(iter) {
99 repr |= <$upper_repr>::from(c.to_repr()) << (i * <$lower>::BITS);
100 }
101 Self::from_repr(repr)
102 })
103 }
104
105 #[inline]
114 fn flatten_to_base(vec: Vec<Self>) -> Vec<$lower> {
115 let d = <$upper as BasedVectorSpace<$lower>>::DIMENSION;
116
117 let flat: &[$lower] =
123 unsafe { slice::from_raw_parts(vec.as_ptr().cast::<$lower>(), vec.len() * d) };
124 flat.to_vec()
125 }
126 }
127
128 impl ExtensionField<$lower> for $upper {
129 type ExtensionPacking = Self;
130
131 #[inline]
132 fn is_in_basefield(&self) -> bool {
133 (self.to_repr() >> <$lower>::BITS) == 0
136 }
137
138 #[inline]
139 fn as_base(&self) -> Option<$lower> {
140 <Self as ExtensionField<$lower>>::is_in_basefield(self)
141 .then(|| <$lower>::from_repr(self.to_repr() as $lower_repr))
142 }
143 }
144
145 impl PackedFieldExtension<$lower, $upper> for $upper {
147 #[inline]
148 fn from_ext_fn(f: impl Fn(usize) -> Self) -> Self {
149 f(0)
150 }
151
152 #[inline]
153 fn from_ext_slice(slice: &[Self]) -> Self {
154 assert_eq!(slice.len(), 1);
155 slice[0]
156 }
157
158 #[inline]
159 fn extract(&self, lane: usize) -> Self {
160 assert_eq!(lane, 0, "lane index out of range");
161 *self
162 }
163
164 #[inline]
165 fn add_assign_lane(&mut self, lane: usize, value: Self) {
166 assert_eq!(lane, 0, "lane index out of range");
167 *self += value;
168 }
169
170 #[inline]
171 fn packed_ext_powers(base: Self) -> Powers<Self> {
172 base.powers()
173 }
174 }
175
176 impl HasFrobenius<$lower> for $upper {
177 #[inline]
179 fn frobenius(&self) -> Self {
180 self.exp_power_of_2(<$lower>::BITS)
181 }
182
183 #[inline]
184 fn repeated_frobenius(&self, count: usize) -> Self {
185 let count = count % <$upper as BasedVectorSpace<$lower>>::DIMENSION;
187 self.exp_power_of_2(<$lower>::BITS * count)
188 }
189
190 #[inline]
191 fn pseudo_inv(&self) -> Self {
192 self.try_inverse().unwrap_or(Self::ZERO)
195 }
196 }
197 };
198}
199
200binary_tower_extension!(BinaryField16, u16, BinaryField8, u8);
201binary_tower_extension!(BinaryField32, u32, BinaryField8, u8);
202binary_tower_extension!(BinaryField64, u64, BinaryField8, u8);
203binary_tower_extension!(BinaryField128, u128, BinaryField8, u8);
204binary_tower_extension!(BinaryField32, u32, BinaryField16, u16);
205binary_tower_extension!(BinaryField64, u64, BinaryField16, u16);
206binary_tower_extension!(BinaryField128, u128, BinaryField16, u16);
207binary_tower_extension!(BinaryField64, u64, BinaryField32, u32);
208binary_tower_extension!(BinaryField128, u128, BinaryField32, u32);
209binary_tower_extension!(BinaryField128, u128, BinaryField64, u64);
210
211#[cfg(test)]
212mod tests {
213 use alloc::vec::Vec;
214
215 use p3_field::extension::HasFrobenius;
216 use p3_field::{BasedVectorSpace, ExtensionField, PrimeCharacteristicRing};
217
218 use crate::tower::TowerLevel;
219 use crate::{BinaryField8, BinaryField16, BinaryField32, BinaryField64, BinaryField128};
220
221 const A128: u128 = 0x1234_5678_9abc_def0_0fed_cba9_8765_4321;
223
224 macro_rules! assert_basis_round_trip {
227 ($upper:ty, $lower:ty, $val:expr) => {{
228 let a = <$upper>::from_repr($val);
229 let dim = <$upper as BasedVectorSpace<$lower>>::DIMENSION;
230 let coeffs = BasedVectorSpace::<$lower>::as_basis_coefficients_slice(&a);
231 assert_eq!(coeffs.len(), dim);
232
233 let recomposed = (0..dim)
235 .map(|i| {
236 <$upper as BasedVectorSpace<$lower>>::ith_basis_element(i).unwrap() * coeffs[i]
237 })
238 .sum::<$upper>();
239 assert_eq!(recomposed, a);
240
241 assert_eq!(
242 <$upper as BasedVectorSpace<$lower>>::from_basis_coefficients_slice(coeffs),
243 Some(a)
244 );
245 assert_eq!(
246 <$upper as BasedVectorSpace<$lower>>::from_basis_coefficients_fn(|i| coeffs[i]),
247 a
248 );
249 }};
250 }
251
252 macro_rules! assert_mixed_ops_match_embedding {
254 ($upper:ty, $lower:ty, $val:expr, $scalar:expr) => {{
255 let a = <$upper>::from_repr($val);
256 let s = <$lower>::from_repr($scalar);
257 let s_up = <$upper>::from(s);
258
259 assert_eq!(a + s, a + s_up);
260 assert_eq!(a - s, a - s_up);
261 assert_eq!(a * s, a * s_up);
262
263 let mut acc = a;
264 acc += s;
265 assert_eq!(acc, a + s_up);
266 let mut acc = a;
267 acc -= s;
268 assert_eq!(acc, a - s_up);
269 let mut acc = a;
270 acc *= s;
271 assert_eq!(acc, a * s_up);
272 }};
273 }
274
275 macro_rules! assert_frobenius_over {
278 ($upper:ty, $lower:ty, $val:expr) => {{
279 let dim = <$upper as BasedVectorSpace<$lower>>::DIMENSION;
280
281 for i in 0..=u8::MAX {
283 let embedded = <$upper>::from(<$lower>::from_repr(i.into()));
284 assert_eq!(
285 HasFrobenius::<$lower>::frobenius(&embedded),
286 embedded,
287 "Frobenius must fix the embedded base field"
288 );
289 }
290
291 let x = <$upper as BasedVectorSpace<$lower>>::ith_basis_element(1).unwrap();
294 assert_ne!(
295 HasFrobenius::<$lower>::frobenius(&x),
296 x,
297 "Frobenius must move elements outside the base field"
298 );
299
300 let a = <$upper>::from_repr($val);
302 let mut acc = a;
303 for count in 0..(2 * dim + 3) {
304 assert_eq!(HasFrobenius::<$lower>::repeated_frobenius(&a, count), acc);
305 acc = HasFrobenius::<$lower>::frobenius(&acc);
306 }
307
308 assert_eq!(
310 HasFrobenius::<$lower>::pseudo_inv(&<$upper>::ZERO),
311 <$upper>::ZERO
312 );
313 assert_eq!(a * HasFrobenius::<$lower>::pseudo_inv(&a), <$upper>::ONE);
314 }};
315 }
316
317 #[test]
318 fn gf2_128_over_gf2_8_basis_coefficients_are_bytes() {
319 let a = BinaryField128::from_repr(0x0f0e_0d0c_0b0a_0908_0706_0504_0302_0100);
320 let coeffs = BasedVectorSpace::<BinaryField8>::as_basis_coefficients_slice(&a);
321 assert_eq!(coeffs.len(), 16);
322 for (i, c) in coeffs.iter().enumerate() {
323 assert_eq!(*c, BinaryField8::from_repr(i as u8));
324 }
325 assert_eq!(
326 <BinaryField128 as BasedVectorSpace<BinaryField8>>::from_basis_coefficients_slice(
327 coeffs
328 ),
329 Some(a)
330 );
331 }
332
333 #[test]
334 fn mixed_mul_matches_embedding() {
335 let a = BinaryField128::from_repr(A128);
336 let s = BinaryField8::from_repr(0xa7);
337 assert_eq!(a * s, a * BinaryField128::from(s));
338 }
339
340 #[test]
341 fn dimension_is_the_ratio_of_bit_widths() {
342 assert_eq!(
343 <BinaryField16 as BasedVectorSpace<BinaryField8>>::DIMENSION,
344 2
345 );
346 assert_eq!(
347 <BinaryField32 as BasedVectorSpace<BinaryField8>>::DIMENSION,
348 4
349 );
350 assert_eq!(
351 <BinaryField64 as BasedVectorSpace<BinaryField8>>::DIMENSION,
352 8
353 );
354 assert_eq!(
355 <BinaryField128 as BasedVectorSpace<BinaryField8>>::DIMENSION,
356 16
357 );
358 assert_eq!(
359 <BinaryField32 as BasedVectorSpace<BinaryField16>>::DIMENSION,
360 2
361 );
362 assert_eq!(
363 <BinaryField64 as BasedVectorSpace<BinaryField16>>::DIMENSION,
364 4
365 );
366 assert_eq!(
367 <BinaryField128 as BasedVectorSpace<BinaryField16>>::DIMENSION,
368 8
369 );
370 assert_eq!(
371 <BinaryField64 as BasedVectorSpace<BinaryField32>>::DIMENSION,
372 2
373 );
374 assert_eq!(
375 <BinaryField128 as BasedVectorSpace<BinaryField32>>::DIMENSION,
376 4
377 );
378 assert_eq!(
379 <BinaryField128 as BasedVectorSpace<BinaryField64>>::DIMENSION,
380 2
381 );
382 }
383
384 #[test]
385 fn basis_coefficients_round_trip_on_every_pair() {
386 assert_basis_round_trip!(BinaryField16, BinaryField8, 0xfedc);
387 assert_basis_round_trip!(BinaryField32, BinaryField8, 0xfedc_ba98);
388 assert_basis_round_trip!(BinaryField64, BinaryField8, 0xfedc_ba98_7654_3210);
389 assert_basis_round_trip!(BinaryField128, BinaryField8, A128);
390 assert_basis_round_trip!(BinaryField32, BinaryField16, 0xfedc_ba98);
391 assert_basis_round_trip!(BinaryField64, BinaryField16, 0xfedc_ba98_7654_3210);
392 assert_basis_round_trip!(BinaryField128, BinaryField16, A128);
393 assert_basis_round_trip!(BinaryField64, BinaryField32, 0xfedc_ba98_7654_3210);
394 assert_basis_round_trip!(BinaryField128, BinaryField32, A128);
395 assert_basis_round_trip!(BinaryField128, BinaryField64, A128);
396 }
397
398 #[test]
399 fn mixed_ops_match_embedding_on_every_pair() {
400 assert_mixed_ops_match_embedding!(BinaryField16, BinaryField8, 0xfedc, 0xa7);
401 assert_mixed_ops_match_embedding!(BinaryField32, BinaryField8, 0xfedc_ba98, 0xa7);
402 assert_mixed_ops_match_embedding!(BinaryField64, BinaryField8, 0xfedc_ba98_7654_3210, 0xa7);
403 assert_mixed_ops_match_embedding!(BinaryField128, BinaryField8, A128, 0xa7);
404 assert_mixed_ops_match_embedding!(BinaryField32, BinaryField16, 0xfedc_ba98, 0xa73b);
405 assert_mixed_ops_match_embedding!(
406 BinaryField64,
407 BinaryField16,
408 0xfedc_ba98_7654_3210,
409 0xa73b
410 );
411 assert_mixed_ops_match_embedding!(BinaryField128, BinaryField16, A128, 0xa73b);
412 assert_mixed_ops_match_embedding!(
413 BinaryField64,
414 BinaryField32,
415 0xfedc_ba98_7654_3210,
416 0xa73b_c91d
417 );
418 assert_mixed_ops_match_embedding!(BinaryField128, BinaryField32, A128, 0xa73b_c91d);
419 assert_mixed_ops_match_embedding!(
420 BinaryField128,
421 BinaryField64,
422 A128,
423 0xa73b_c91d_5566_7788
424 );
425 }
426
427 #[test]
428 fn frobenius_fixes_exactly_the_lower_level_on_every_pair() {
429 assert_frobenius_over!(BinaryField16, BinaryField8, 0xfedc);
430 assert_frobenius_over!(BinaryField32, BinaryField8, 0xfedc_ba98);
431 assert_frobenius_over!(BinaryField64, BinaryField8, 0xfedc_ba98_7654_3210);
432 assert_frobenius_over!(BinaryField128, BinaryField8, A128);
433 assert_frobenius_over!(BinaryField32, BinaryField16, 0xfedc_ba98);
434 assert_frobenius_over!(BinaryField64, BinaryField16, 0xfedc_ba98_7654_3210);
435 assert_frobenius_over!(BinaryField128, BinaryField16, A128);
436 assert_frobenius_over!(BinaryField64, BinaryField32, 0xfedc_ba98_7654_3210);
437 assert_frobenius_over!(BinaryField128, BinaryField32, A128);
438 assert_frobenius_over!(BinaryField128, BinaryField64, A128);
439 }
440
441 #[test]
444 fn frobenius_exponent_is_the_order_of_the_base_field() {
445 let a = BinaryField128::from_repr(A128);
446 assert_eq!(
447 HasFrobenius::<BinaryField8>::frobenius(&a),
448 a.exp_power_of_2(8)
449 );
450 assert_eq!(
451 HasFrobenius::<BinaryField64>::frobenius(&a),
452 a.exp_power_of_2(64)
453 );
454 }
455
456 #[test]
457 fn is_in_basefield_detects_the_embedded_subfield() {
458 let low = BinaryField128::from(BinaryField8::from_repr(0xa7));
459 assert!(ExtensionField::<BinaryField8>::is_in_basefield(&low));
460 assert_eq!(
461 ExtensionField::<BinaryField8>::as_base(&low),
462 Some(BinaryField8::from_repr(0xa7))
463 );
464
465 let mid = BinaryField128::from_repr(0xa73b);
467 assert!(!ExtensionField::<BinaryField8>::is_in_basefield(&mid));
468 assert_eq!(ExtensionField::<BinaryField8>::as_base(&mid), None);
469 assert!(ExtensionField::<BinaryField16>::is_in_basefield(&mid));
470 assert_eq!(
471 ExtensionField::<BinaryField16>::as_base(&mid),
472 Some(BinaryField16::from_repr(0xa73b))
473 );
474 }
475
476 #[test]
477 fn from_basis_coefficients_iter_checks_the_length() {
478 let coeffs = [BinaryField64::ONE, BinaryField64::ZERO];
479 assert_eq!(
480 <BinaryField128 as BasedVectorSpace<BinaryField64>>::from_basis_coefficients_iter(
481 coeffs.into_iter()
482 ),
483 Some(BinaryField128::ONE)
484 );
485 assert_eq!(
486 <BinaryField128 as BasedVectorSpace<BinaryField64>>::from_basis_coefficients_iter(
487 coeffs[..1].iter().copied()
488 ),
489 None
490 );
491 assert_eq!(
492 <BinaryField128 as BasedVectorSpace<BinaryField64>>::from_basis_coefficients_slice(&[]),
493 None
494 );
495 }
496
497 #[test]
500 fn flatten_and_reconstitute_round_trip() {
501 let elems: Vec<BinaryField128> = (0..64u128)
502 .map(|i| {
503 BinaryField128::from_repr(i.wrapping_mul(0x9e37_79b9_7f4a_7c15_f39c_c060_5ced_c834))
504 })
505 .collect();
506
507 for pos in 0..16 {
510 assert!(
511 elems.iter().any(|e| {
512 BasedVectorSpace::<BinaryField8>::as_basis_coefficients_slice(e)[pos]
513 != BinaryField8::ZERO
514 }),
515 "coefficient position {pos} is never nonzero"
516 );
517 }
518
519 let flat =
520 <BinaryField128 as BasedVectorSpace<BinaryField8>>::flatten_to_base(elems.clone());
521 assert_eq!(flat.len(), elems.len() * 16);
522 for (i, e) in elems.iter().enumerate() {
523 assert_eq!(&flat[i * 16..(i + 1) * 16], e.as_basis_coefficients_slice());
524 }
525
526 let back = <BinaryField128 as BasedVectorSpace<BinaryField8>>::reconstitute_from_base(flat);
527 assert_eq!(back, elems);
528 }
529
530 #[test]
532 fn reconstitute_is_exhaustive_over_the_byte_level() {
533 let all: Vec<BinaryField8> = (0..=u8::MAX).map(BinaryField8::from_repr).collect();
534 let packed =
535 <BinaryField16 as BasedVectorSpace<BinaryField8>>::reconstitute_from_base(all.clone());
536 assert_eq!(packed.len(), 128);
537 let flat = <BinaryField16 as BasedVectorSpace<BinaryField8>>::flatten_to_base(packed);
538 assert_eq!(flat, all);
539 }
540
541 #[test]
543 fn embedding_is_a_ring_homomorphism() {
544 for a in 0..=u8::MAX {
545 for b in 0..=u8::MAX {
546 let (x, y) = (BinaryField8::from_repr(a), BinaryField8::from_repr(b));
547 assert_eq!(
548 BinaryField128::from(x * y),
549 BinaryField128::from(x) * BinaryField128::from(y)
550 );
551 assert_eq!(
552 BinaryField128::from(x + y),
553 BinaryField128::from(x) + BinaryField128::from(y)
554 );
555 }
556 }
557 assert_eq!(BinaryField128::from(BinaryField8::ONE), BinaryField128::ONE);
558 }
559}