1use crate::algebra::impl_binary_field_extras;
22use crate::towers::bit::Bit;
23use crate::towers::block8::Block8;
24use crate::towers::block16::Block16;
25use crate::towers::block32::Block32;
26use crate::towers::block64::Block64;
27use crate::{
28 BinaryFieldExtras, CanonicalDeserialize, CanonicalSerialize, Flat, FlatPromote, HardwareField,
29 PackableField, PackedFlat, TowerField, constants,
30};
31use core::ops::{Add, AddAssign, BitXor, BitXorAssign, Mul, MulAssign, Sub, SubAssign};
32use serde::{Deserialize, Serialize};
33use zeroize::Zeroize;
34
35#[cfg(not(feature = "table-math"))]
36#[repr(align(64))]
37struct CtConvertBasisU128<const N: usize>([u128; N]);
38
39#[cfg(not(feature = "table-math"))]
40static TOWER_TO_FLAT_BASIS_128: CtConvertBasisU128<128> =
41 CtConvertBasisU128(constants::RAW_TOWER_TO_FLAT_128);
42
43#[cfg(not(feature = "table-math"))]
44static FLAT_TO_TOWER_BASIS_128: CtConvertBasisU128<128> =
45 CtConvertBasisU128(constants::RAW_FLAT_TO_TOWER_128);
46
47#[derive(Copy, Clone, Default, Debug, Eq, PartialEq, Serialize, Deserialize, Zeroize)]
50#[repr(transparent)]
51pub struct Block128(pub u128);
52
53impl Block128 {
54 const TAU: Self = Block128(0x2000_0000_0000_0000_0000_0000_0000_0000);
56
57 pub fn new(lo: Block64, hi: Block64) -> Self {
59 Self((hi.0 as u128) << 64 | (lo.0 as u128))
60 }
61
62 #[inline(always)]
64 pub fn split(self) -> (Block64, Block64) {
65 (Block64(self.0 as u64), Block64((self.0 >> 64) as u64))
66 }
67
68 #[inline(always)]
69 pub(crate) fn mul_tau(self) -> Self {
70 let (a0, a1) = self.split();
71 let t = a1.mul_tau();
72
73 Self::new(t.mul_tau(), (a0 + a1).mul_tau())
74 }
75}
76
77impl TowerField for Block128 {
78 const BITS: usize = 128;
79 const ZERO: Self = Block128(0);
80 const ONE: Self = Block128(1);
81
82 const EXTENSION_TAU: Self = Self::TAU;
83
84 fn invert(&self) -> Self {
85 let (l, h) = self.split();
86
87 let norm = h.square().mul_tau() + h * l + l.square();
88 let norm_inv = norm.invert();
89
90 let res_hi = h * norm_inv;
91 let res_lo = (h + l) * norm_inv;
92
93 Self::new(res_lo, res_hi)
94 }
95
96 fn from_uniform_bytes(bytes: &[u8; 32]) -> Self {
97 let mut buf = [0u8; 16];
98 buf.copy_from_slice(&bytes[0..16]);
99
100 Self(u128::from_le_bytes(buf))
101 }
102}
103
104impl Add for Block128 {
105 type Output = Self;
106
107 fn add(self, rhs: Self) -> Self {
108 Self(self.0.bitxor(rhs.0))
109 }
110}
111
112impl Sub for Block128 {
113 type Output = Self;
114
115 fn sub(self, rhs: Self) -> Self {
116 self.add(rhs)
117 }
118}
119
120impl Mul for Block128 {
121 type Output = Self;
122
123 #[inline]
124 fn mul(self, rhs: Self) -> Self {
125 #[cfg(all(pmull, feature = "table-math"))]
126 {
127 mul_iso_128(self, rhs)
128 }
129
130 #[cfg(not(all(pmull, feature = "table-math")))]
131 {
132 let (a0, a1) = self.split();
133 let (b0, b1) = rhs.split();
134
135 let v0 = a0 * b0;
136 let v1 = a1 * b1;
137 let vs = (a0 + a1) * (b0 + b1);
138
139 Self::new(v0 + v1.mul_tau(), v0 + vs)
140 }
141 }
142}
143
144impl AddAssign for Block128 {
145 fn add_assign(&mut self, rhs: Self) {
146 self.0.bitxor_assign(rhs.0);
147 }
148}
149
150impl SubAssign for Block128 {
151 fn sub_assign(&mut self, rhs: Self) {
152 self.0.bitxor_assign(rhs.0);
153 }
154}
155
156impl MulAssign for Block128 {
157 fn mul_assign(&mut self, rhs: Self) {
158 *self = *self * rhs;
159 }
160}
161
162impl CanonicalSerialize for Block128 {
163 fn serialized_size(&self) -> usize {
164 16
165 }
166
167 fn serialize(&self, writer: &mut [u8]) -> Result<(), ()> {
168 if writer.len() < 16 {
169 return Err(());
170 }
171
172 writer[..16].copy_from_slice(&self.0.to_le_bytes());
173
174 Ok(())
175 }
176}
177
178impl CanonicalDeserialize for Block128 {
179 fn deserialize(bytes: &[u8]) -> Result<Self, ()> {
180 if bytes.len() < 16 {
181 return Err(());
182 }
183
184 let mut buf = [0u8; 16];
185 buf.copy_from_slice(&bytes[0..16]);
186
187 Ok(Self(u128::from_le_bytes(buf)))
188 }
189}
190
191impl From<u8> for Block128 {
192 fn from(val: u8) -> Self {
193 Self(val as u128)
194 }
195}
196
197impl From<u32> for Block128 {
198 #[inline]
199 fn from(val: u32) -> Self {
200 Self(val as u128)
201 }
202}
203
204impl From<u64> for Block128 {
205 #[inline]
206 fn from(val: u64) -> Self {
207 Self::from(val as u128)
208 }
209}
210
211impl From<u128> for Block128 {
212 #[inline]
213 fn from(val: u128) -> Self {
214 Self(val)
215 }
216}
217
218impl From<Bit> for Block128 {
223 #[inline(always)]
224 fn from(val: Bit) -> Self {
225 Self(val.get() as u128)
226 }
227}
228
229impl From<Block8> for Block128 {
230 #[inline(always)]
231 fn from(val: Block8) -> Self {
232 Self(val.0 as u128)
233 }
234}
235
236impl From<Block16> for Block128 {
237 #[inline(always)]
238 fn from(val: Block16) -> Self {
239 Self(val.0 as u128)
240 }
241}
242
243impl From<Block32> for Block128 {
244 #[inline(always)]
245 fn from(val: Block32) -> Self {
246 Self(val.0 as u128)
247 }
248}
249
250impl From<Block64> for Block128 {
251 #[inline(always)]
252 fn from(val: Block64) -> Self {
253 Self(val.0 as u128)
254 }
255}
256
257pub const PACKED_WIDTH_128: usize = 4;
263
264#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
267#[repr(C, align(32))]
268pub struct PackedBlock128(pub [Block128; PACKED_WIDTH_128]);
269
270impl PackedBlock128 {
271 #[inline(always)]
273 pub fn zero() -> Self {
274 Self([Block128::ZERO; PACKED_WIDTH_128])
275 }
276
277 #[inline(always)]
279 pub fn broadcast(val: Block128) -> Self {
280 Self([val; PACKED_WIDTH_128])
281 }
282}
283
284impl PackableField for Block128 {
285 type Packed = PackedBlock128;
286
287 const WIDTH: usize = PACKED_WIDTH_128;
288
289 #[inline(always)]
290 fn pack(chunk: &[Self]) -> Self::Packed {
291 assert!(
292 chunk.len() >= PACKED_WIDTH_128,
293 "PackableField::pack: input slice too short",
294 );
295
296 let mut arr = [Self::ZERO; PACKED_WIDTH_128];
297 arr.copy_from_slice(&chunk[..PACKED_WIDTH_128]);
298
299 PackedBlock128(arr)
300 }
301
302 #[inline(always)]
303 fn unpack(packed: Self::Packed, output: &mut [Self]) {
304 assert!(
305 output.len() >= PACKED_WIDTH_128,
306 "PackableField::unpack: output slice too short",
307 );
308
309 output[..PACKED_WIDTH_128].copy_from_slice(&packed.0);
310 }
311}
312
313impl Add for PackedBlock128 {
318 type Output = Self;
319
320 #[inline(always)]
321 fn add(self, rhs: Self) -> Self {
322 let mut res = [Block128::ZERO; PACKED_WIDTH_128];
323 for ((out, l), r) in res.iter_mut().zip(self.0.iter()).zip(rhs.0.iter()) {
324 *out = *l + *r;
325 }
326
327 Self(res)
328 }
329}
330
331impl AddAssign for PackedBlock128 {
332 #[inline(always)]
333 fn add_assign(&mut self, rhs: Self) {
334 for (l, r) in self.0.iter_mut().zip(rhs.0.iter()) {
335 *l += *r;
336 }
337 }
338}
339
340impl Sub for PackedBlock128 {
343 type Output = Self;
344
345 #[inline(always)]
346 fn sub(self, rhs: Self) -> Self {
347 self.add(rhs)
348 }
349}
350
351impl SubAssign for PackedBlock128 {
352 #[inline(always)]
353 fn sub_assign(&mut self, rhs: Self) {
354 self.add_assign(rhs);
355 }
356}
357
358impl Mul for PackedBlock128 {
361 type Output = Self;
362
363 #[inline(always)]
364 fn mul(self, rhs: Self) -> Self {
365 let mut res = [Block128::ZERO; PACKED_WIDTH_128];
366 for ((out, l), r) in res.iter_mut().zip(self.0.iter()).zip(rhs.0.iter()) {
367 *out = *l * *r;
368 }
369
370 Self(res)
371 }
372}
373
374impl MulAssign for PackedBlock128 {
375 #[inline(always)]
376 fn mul_assign(&mut self, rhs: Self) {
377 for (l, r) in self.0.iter_mut().zip(rhs.0.iter()) {
378 *l *= *r;
379 }
380 }
381}
382
383impl Mul<Block128> for PackedBlock128 {
387 type Output = Self;
388
389 #[inline(always)]
390 fn mul(self, rhs: Block128) -> Self {
391 let mut res = [Block128::ZERO; PACKED_WIDTH_128];
392 for (out, v) in res.iter_mut().zip(self.0.iter()) {
393 *out = *v * rhs;
394 }
395
396 Self(res)
397 }
398}
399
400impl MulAssign<Block128> for PackedBlock128 {
401 #[inline(always)]
402 fn mul_assign(&mut self, rhs: Block128) {
403 for v in self.0.iter_mut() {
404 *v *= rhs;
405 }
406 }
407}
408
409impl HardwareField for Block128 {
414 #[inline(always)]
415 fn to_hardware(self) -> Flat<Self> {
416 #[cfg(feature = "table-math")]
417 {
418 Flat::from_raw(apply_matrix_128(self, &constants::TOWER_TO_FLAT_128))
419 }
420
421 #[cfg(not(feature = "table-math"))]
422 {
423 Flat::from_raw(Block128(map_ct_128_split(
424 self.0,
425 &TOWER_TO_FLAT_BASIS_128.0,
426 )))
427 }
428 }
429
430 #[inline(always)]
431 fn from_hardware(value: Flat<Self>) -> Self {
432 let value = value.into_raw();
433
434 #[cfg(feature = "table-math")]
435 {
436 apply_matrix_128(value, &constants::FLAT_TO_TOWER_128)
437 }
438
439 #[cfg(not(feature = "table-math"))]
440 {
441 Block128(map_ct_128_split(value.0, &FLAT_TO_TOWER_BASIS_128.0))
442 }
443 }
444
445 #[inline(always)]
446 fn add_hardware(lhs: Flat<Self>, rhs: Flat<Self>) -> Flat<Self> {
447 let lhs = lhs.into_raw();
448 let rhs = rhs.into_raw();
449
450 #[cfg(target_arch = "aarch64")]
451 {
452 Flat::from_raw(neon::add_flat_128(lhs, rhs))
453 }
454
455 #[cfg(not(target_arch = "aarch64"))]
456 {
457 Flat::from_raw(lhs + rhs)
458 }
459 }
460
461 #[inline(always)]
462 fn add_hardware_packed(lhs: PackedFlat<Self>, rhs: PackedFlat<Self>) -> PackedFlat<Self> {
463 let lhs = lhs.into_raw();
464 let rhs = rhs.into_raw();
465
466 #[cfg(target_arch = "aarch64")]
467 {
468 PackedFlat::from_raw(neon::add_packed_128(lhs, rhs))
469 }
470
471 #[cfg(not(target_arch = "aarch64"))]
472 {
473 PackedFlat::from_raw(lhs + rhs)
474 }
475 }
476
477 #[inline(always)]
478 fn mul_hardware(lhs: Flat<Self>, rhs: Flat<Self>) -> Flat<Self> {
479 let lhs = lhs.into_raw();
480 let rhs = rhs.into_raw();
481
482 #[cfg(pmull)]
483 {
484 Flat::from_raw(neon::mul_flat_128(lhs, rhs))
485 }
486
487 #[cfg(not(pmull))]
488 {
489 let a_tower = Self::from_hardware(Flat::from_raw(lhs));
490 let b_tower = Self::from_hardware(Flat::from_raw(rhs));
491
492 (a_tower * b_tower).to_hardware()
493 }
494 }
495
496 #[inline(always)]
497 fn mul_hardware_packed(lhs: PackedFlat<Self>, rhs: PackedFlat<Self>) -> PackedFlat<Self> {
498 let lhs = lhs.into_raw();
499 let rhs = rhs.into_raw();
500
501 #[cfg(pmull)]
502 {
503 let mut res = [Block128::ZERO; PACKED_WIDTH_128];
504 for ((out, l), r) in res.iter_mut().zip(lhs.0.iter()).zip(rhs.0.iter()) {
505 *out = neon::mul_flat_128(*l, *r);
506 }
507
508 PackedFlat::from_raw(PackedBlock128(res))
509 }
510
511 #[cfg(not(pmull))]
512 {
513 let mut l = [Self::ZERO; <Self as PackableField>::WIDTH];
514 let mut r = [Self::ZERO; <Self as PackableField>::WIDTH];
515 let mut res = [Self::ZERO; <Self as PackableField>::WIDTH];
516
517 Self::unpack(lhs, &mut l);
518 Self::unpack(rhs, &mut r);
519
520 for i in 0..<Self as PackableField>::WIDTH {
521 res[i] = Self::mul_hardware(Flat::from_raw(l[i]), Flat::from_raw(r[i])).into_raw();
522 }
523
524 PackedFlat::from_raw(Self::pack(&res))
525 }
526 }
527
528 #[inline(always)]
529 fn mul_hardware_scalar_packed(lhs: PackedFlat<Self>, rhs: Flat<Self>) -> PackedFlat<Self> {
530 let broadcasted = PackedBlock128::broadcast(rhs.into_raw());
531 Self::mul_hardware_packed(lhs, PackedFlat::from_raw(broadcasted))
532 }
533
534 #[inline(always)]
535 fn tower_bit_from_hardware(value: Flat<Self>, bit_idx: usize) -> u8 {
536 let mask = constants::FLAT_TO_TOWER_BIT_MASKS_128[bit_idx];
537
538 let mut v = value.into_raw().0 & mask;
542 v ^= v >> 64;
543 v ^= v >> 32;
544 v ^= v >> 16;
545 v ^= v >> 8;
546 v ^= v >> 4;
547 v ^= v >> 2;
548 v ^= v >> 1;
549
550 (v & 1) as u8
551 }
552}
553
554#[cfg(not(feature = "table-math"))]
563impl FlatPromote<Block8> for Block128 {
564 #[inline(always)]
565 fn promote_flat(val: Flat<Block8>) -> Flat<Self> {
566 let val = val.into_raw();
567 Flat::from_raw(Block128(lift_ct::<8>(
568 val.0 as u64,
569 &constants::LIFT_BASIS_8.0,
570 )))
571 }
572
573 #[cfg(target_arch = "aarch64")]
574 fn promote_flat_batch(input: &[Flat<Block8>], output: &mut [Flat<Self>]) {
575 promote_batch_neon(input, output, neon::promote_batch_8_to_128);
576 }
577}
578
579#[cfg(not(feature = "table-math"))]
580impl FlatPromote<Block16> for Block128 {
581 #[inline(always)]
582 fn promote_flat(val: Flat<Block16>) -> Flat<Self> {
583 Flat::from_raw(Block128(lift_ct::<16>(
584 val.into_raw().0 as u64,
585 &constants::LIFT_BASIS_16.0,
586 )))
587 }
588
589 #[cfg(target_arch = "aarch64")]
590 fn promote_flat_batch(input: &[Flat<Block16>], output: &mut [Flat<Self>]) {
591 promote_batch_neon(input, output, neon::promote_batch_16_to_128);
592 }
593}
594
595#[cfg(not(feature = "table-math"))]
596impl FlatPromote<Block32> for Block128 {
597 #[inline(always)]
598 fn promote_flat(val: Flat<Block32>) -> Flat<Self> {
599 Flat::from_raw(Block128(lift_ct::<32>(
600 val.into_raw().0 as u64,
601 &constants::LIFT_BASIS_32.0,
602 )))
603 }
604
605 #[cfg(target_arch = "aarch64")]
606 fn promote_flat_batch(input: &[Flat<Block32>], output: &mut [Flat<Self>]) {
607 promote_batch_neon(input, output, neon::promote_batch_32_to_128);
608 }
609}
610
611#[cfg(not(feature = "table-math"))]
612impl FlatPromote<Block64> for Block128 {
613 #[inline(always)]
614 fn promote_flat(val: Flat<Block64>) -> Flat<Self> {
615 Flat::from_raw(Block128(lift_ct::<64>(
616 val.into_raw().0,
617 &constants::LIFT_BASIS_64.0,
618 )))
619 }
620
621 #[cfg(target_arch = "aarch64")]
622 fn promote_flat_batch(input: &[Flat<Block64>], output: &mut [Flat<Self>]) {
623 promote_batch_neon(input, output, neon::promote_batch_64_to_128);
624 }
625}
626
627#[cfg(feature = "table-math")]
629impl FlatPromote<Block8> for Block128 {
630 #[inline(always)]
631 fn promote_flat(val: Flat<Block8>) -> Flat<Self> {
632 let idx = val.into_raw().0 as usize;
633 Flat::from_raw(Block128(unsafe {
634 *constants::LIFT_TABLE_8_TO_128.get_unchecked(idx)
635 }))
636 }
637}
638
639#[cfg(feature = "table-math")]
640impl FlatPromote<Block16> for Block128 {
641 #[inline(always)]
642 fn promote_flat(val: Flat<Block16>) -> Flat<Self> {
643 let v = val.into_raw().0;
644 let res = unsafe {
645 *constants::PROMOTE_16_BYTE_0_TO_128.get_unchecked((v & 0xFF) as usize)
646 ^ *constants::PROMOTE_16_BYTE_1_TO_128.get_unchecked(((v >> 8) & 0xFF) as usize)
647 };
648
649 Flat::from_raw(Block128(res))
650 }
651}
652
653#[cfg(feature = "table-math")]
654impl FlatPromote<Block32> for Block128 {
655 #[inline(always)]
656 fn promote_flat(val: Flat<Block32>) -> Flat<Self> {
657 let v = val.into_raw().0;
658 let res = unsafe {
659 *constants::PROMOTE_32_BYTE_0_TO_128.get_unchecked((v & 0xFF) as usize)
660 ^ *constants::PROMOTE_32_BYTE_1_TO_128.get_unchecked(((v >> 8) & 0xFF) as usize)
661 ^ *constants::PROMOTE_32_BYTE_2_TO_128.get_unchecked(((v >> 16) & 0xFF) as usize)
662 ^ *constants::PROMOTE_32_BYTE_3_TO_128.get_unchecked(((v >> 24) & 0xFF) as usize)
663 };
664
665 Flat::from_raw(Block128(res))
666 }
667}
668
669#[cfg(feature = "table-math")]
670impl FlatPromote<Block64> for Block128 {
671 #[inline(always)]
672 fn promote_flat(val: Flat<Block64>) -> Flat<Self> {
673 let v = val.into_raw().0;
674 let res = unsafe {
675 *constants::PROMOTE_64_BYTE_0_TO_128.get_unchecked((v & 0xFF) as usize)
676 ^ *constants::PROMOTE_64_BYTE_1_TO_128.get_unchecked(((v >> 8) & 0xFF) as usize)
677 ^ *constants::PROMOTE_64_BYTE_2_TO_128.get_unchecked(((v >> 16) & 0xFF) as usize)
678 ^ *constants::PROMOTE_64_BYTE_3_TO_128.get_unchecked(((v >> 24) & 0xFF) as usize)
679 ^ *constants::PROMOTE_64_BYTE_4_TO_128.get_unchecked(((v >> 32) & 0xFF) as usize)
680 ^ *constants::PROMOTE_64_BYTE_5_TO_128.get_unchecked(((v >> 40) & 0xFF) as usize)
681 ^ *constants::PROMOTE_64_BYTE_6_TO_128.get_unchecked(((v >> 48) & 0xFF) as usize)
682 ^ *constants::PROMOTE_64_BYTE_7_TO_128.get_unchecked(((v >> 56) & 0xFF) as usize)
683 };
684
685 Flat::from_raw(Block128(res))
686 }
687}
688
689impl_binary_field_extras!(
694 Block128,
695 map_ct_128_split,
696 TRACE_MASK_128,
697 SOLVE_QUADRATIC_BASIS_128
698);
699
700#[cfg(pmull)]
707#[inline(always)]
708pub fn mul_iso_128(a: Block128, b: Block128) -> Block128 {
709 let a_flat = a.to_hardware();
710 let b_flat = b.to_hardware();
711 let c_flat = Flat::from_raw(neon::mul_flat_128(a_flat.into_raw(), b_flat.into_raw()));
712
713 c_flat.to_tower()
714}
715
716#[cfg(feature = "table-math")]
717#[inline(always)]
718pub(crate) fn apply_matrix_128(val: Block128, table: &[u128; 4096]) -> Block128 {
719 let mut res = 0u128;
720 let v = val.0;
721
722 for i in 0..16 {
726 let byte = (v >> (i * 8)) & 0xFF;
727 let idx = (i * 256) + (byte as usize);
728
729 res ^= unsafe { *table.get_unchecked(idx) };
732 }
733
734 Block128(res)
735}
736
737#[inline(always)]
738fn map_ct_128_split(x: u128, basis: &[u128; 128]) -> u128 {
739 let lo = x as u64;
740 let hi = (x >> 64) as u64;
741
742 let mut acc_lo = 0u64;
743 let mut acc_hi = 0u64;
744 let mut i = 0usize;
745
746 while i < 64 {
747 let m_lo = 0u64.wrapping_sub((lo >> i) & 1);
748 let m_hi = 0u64.wrapping_sub((hi >> i) & 1);
749
750 let a = basis[i];
751 let b = basis[64 + i];
752
753 acc_lo ^= ((a as u64) & m_lo) ^ ((b as u64) & m_hi);
754 acc_hi ^= (((a >> 64) as u64) & m_lo) ^ (((b >> 64) as u64) & m_hi);
755
756 i += 1;
757 }
758
759 (acc_lo as u128) | ((acc_hi as u128) << 64)
760}
761
762#[cfg(not(feature = "table-math"))]
763#[inline(always)]
764fn lift_ct<const N: usize>(x: u64, basis: &'static [u128; N]) -> u128 {
765 let mut acc = 0u128;
766 let mut i = 0usize;
767
768 while i < N {
769 let bit = (x >> i) & 1;
770 let mask = 0u128.wrapping_sub(bit as u128);
771 acc ^= basis[i] & mask;
772 i += 1;
773 }
774
775 acc
776}
777
778#[cfg(all(target_arch = "aarch64", not(feature = "table-math")))]
779#[inline(always)]
780fn promote_batch_neon<Src>(
781 input: &[Flat<Src>],
782 output: &mut [Flat<Block128>],
783 kernel: unsafe fn(*const u8, *mut u128),
784) where
785 Src: HardwareField,
786 Block128: FlatPromote<Src>,
787{
788 let n = input.len().min(output.len());
789 let full = n / 16;
790
791 for chunk in 0..full {
792 let i = chunk * 16;
793
794 unsafe {
798 kernel(
799 input.as_ptr().add(i).cast::<u8>(),
800 output.as_mut_ptr().add(i).cast::<u128>(),
801 );
802 }
803 }
804
805 for i in full * 16..n {
806 output[i] = <Block128 as FlatPromote<Src>>::promote_flat(input[i]);
807 }
808}
809
810#[cfg(target_arch = "aarch64")]
815mod neon {
816 use super::*;
817 use core::arch::aarch64::*;
818 use core::mem::transmute;
819
820 const _: () = assert!(constants::POLY_128 == 0x87, "verus twins hardcode R = 0x87");
821
822 #[inline(always)]
823 pub fn add_packed_128(lhs: PackedBlock128, rhs: PackedBlock128) -> PackedBlock128 {
824 unsafe {
825 let l: [uint8x16_t; 4] = transmute(lhs.0);
828 let r: [uint8x16_t; 4] = transmute(rhs.0);
829
830 let res = [
831 veorq_u8(l[0], r[0]),
832 veorq_u8(l[1], r[1]),
833 veorq_u8(l[2], r[2]),
834 veorq_u8(l[3], r[3]),
835 ];
836
837 transmute(res)
838 }
839 }
840
841 #[inline(always)]
842 pub fn add_flat_128(a: Block128, b: Block128) -> Block128 {
843 unsafe {
844 let l: uint8x16_t = transmute(a.0);
847 let r: uint8x16_t = transmute(b.0);
848
849 Block128(transmute::<uint8x16_t, u128>(veorq_u8(l, r)))
850 }
851 }
852
853 #[cfg(pmull)]
854 #[inline(always)]
855 pub fn mul_flat_128(a: Block128, b: Block128) -> Block128 {
856 unsafe {
857 let a8: uint8x16_t = transmute(a.0);
858 let b8: uint8x16_t = transmute(b.0);
859 let ap: poly64x2_t = transmute(a8);
860 let bp: poly64x2_t = transmute(b8);
861
862 let bs: poly64x2_t = transmute(vextq_u8::<8>(b8, b8));
865
866 let d0: uint8x16_t =
867 transmute(vmull_p64(vgetq_lane_p64::<0>(ap), vgetq_lane_p64::<0>(bp)));
868 let d2: uint8x16_t = transmute(vmull_high_p64(ap, bp));
869 let x0: uint8x16_t =
870 transmute(vmull_p64(vgetq_lane_p64::<0>(ap), vgetq_lane_p64::<0>(bs)));
871 let x1: uint8x16_t = transmute(vmull_high_p64(ap, bs));
872
873 let mid = veorq_u8(x0, x1);
874
875 let zero = vdupq_n_u8(0);
878 let lo = veorq_u8(d0, vextq_u8::<8>(zero, mid));
879 let hi = veorq_u8(d2, vextq_u8::<8>(mid, zero));
880
881 let rv: poly64x2_t = vdupq_n_p64(constants::POLY_128 as u64);
885 let hip: poly64x2_t = transmute(hi);
886
887 let p0: uint8x16_t =
888 transmute(vmull_p64(vgetq_lane_p64::<0>(hip), vgetq_lane_p64::<0>(rv)));
889 let p1: uint8x16_t = transmute(vmull_high_p64(hip, rv));
890 let p1p: poly64x2_t = transmute(p1);
891
892 let fold = veorq_u8(p0, vextq_u8::<8>(zero, p1));
893
894 let carry_mul: uint8x16_t = transmute(vmull_high_p64(p1p, rv));
897
898 let res = veorq_u8(veorq_u8(lo, fold), carry_mul);
899
900 Block128(transmute::<uint8x16_t, u128>(res))
901 }
902 }
903
904 #[cfg(not(feature = "table-math"))]
911 #[inline(always)]
912 pub unsafe fn promote_batch_8_to_128(input: *const u8, output: *mut u128) {
913 unsafe {
914 let vals = vld1q_u8(input);
915
916 let mask_0f = vdupq_n_u8(0x0F);
917 let lo_nib = vandq_u8(vals, mask_0f);
918 let hi_nib = vshrq_n_u8::<4>(vals);
919
920 let mut out = [vdupq_n_u8(0); 16];
921
922 macro_rules! lookup {
923 ($j:expr, $lo:ident, $hi:ident, $dst:ident) => {{
924 let t0 = vld1q_u8(constants::NIBBLE_PROMOTE_8_0_TO_128[$j].as_ptr());
925 let t1 = vld1q_u8(constants::NIBBLE_PROMOTE_8_1_TO_128[$j].as_ptr());
926
927 $dst[$j] = veorq_u8(vqtbl1q_u8(t0, $lo), vqtbl1q_u8(t1, $hi));
928 }};
929 }
930
931 lookup!(0, lo_nib, hi_nib, out);
932 lookup!(1, lo_nib, hi_nib, out);
933 lookup!(2, lo_nib, hi_nib, out);
934 lookup!(3, lo_nib, hi_nib, out);
935 lookup!(4, lo_nib, hi_nib, out);
936 lookup!(5, lo_nib, hi_nib, out);
937 lookup!(6, lo_nib, hi_nib, out);
938 lookup!(7, lo_nib, hi_nib, out);
939 lookup!(8, lo_nib, hi_nib, out);
940 lookup!(9, lo_nib, hi_nib, out);
941 lookup!(10, lo_nib, hi_nib, out);
942 lookup!(11, lo_nib, hi_nib, out);
943 lookup!(12, lo_nib, hi_nib, out);
944 lookup!(13, lo_nib, hi_nib, out);
945 lookup!(14, lo_nib, hi_nib, out);
946 lookup!(15, lo_nib, hi_nib, out);
947
948 let elems = transpose_16x16(&out);
949 for (i, elem) in elems.iter().enumerate() {
950 vst1q_u8(output.add(i).cast::<u8>(), *elem);
951 }
952 }
953 }
954
955 #[cfg(not(feature = "table-math"))]
962 #[inline(always)]
963 pub unsafe fn promote_batch_16_to_128(input: *const u8, output: *mut u128) {
964 unsafe {
965 let raw0 = vld1q_u8(input);
966 let raw1 = vld1q_u8(input.add(16));
967
968 let lo_bytes = vuzp1q_u8(raw0, raw1);
969 let hi_bytes = vuzp2q_u8(raw0, raw1);
970
971 let mask_0f = vdupq_n_u8(0x0F);
972 let n0 = vandq_u8(lo_bytes, mask_0f);
973 let n1 = vshrq_n_u8::<4>(lo_bytes);
974 let n2 = vandq_u8(hi_bytes, mask_0f);
975 let n3 = vshrq_n_u8::<4>(hi_bytes);
976
977 let mut out = [vdupq_n_u8(0); 16];
978
979 macro_rules! lookup {
980 ($j:expr, $n0:ident, $n1:ident, $n2:ident, $n3:ident, $dst:ident) => {{
981 let t0 = vld1q_u8(constants::NIBBLE_PROMOTE_16_0_TO_128[$j].as_ptr());
982 let t1 = vld1q_u8(constants::NIBBLE_PROMOTE_16_1_TO_128[$j].as_ptr());
983 let t2 = vld1q_u8(constants::NIBBLE_PROMOTE_16_2_TO_128[$j].as_ptr());
984 let t3 = vld1q_u8(constants::NIBBLE_PROMOTE_16_3_TO_128[$j].as_ptr());
985
986 $dst[$j] = veorq_u8(
987 veorq_u8(vqtbl1q_u8(t0, $n0), vqtbl1q_u8(t1, $n1)),
988 veorq_u8(vqtbl1q_u8(t2, $n2), vqtbl1q_u8(t3, $n3)),
989 );
990 }};
991 }
992
993 lookup!(0, n0, n1, n2, n3, out);
994 lookup!(1, n0, n1, n2, n3, out);
995 lookup!(2, n0, n1, n2, n3, out);
996 lookup!(3, n0, n1, n2, n3, out);
997 lookup!(4, n0, n1, n2, n3, out);
998 lookup!(5, n0, n1, n2, n3, out);
999 lookup!(6, n0, n1, n2, n3, out);
1000 lookup!(7, n0, n1, n2, n3, out);
1001 lookup!(8, n0, n1, n2, n3, out);
1002 lookup!(9, n0, n1, n2, n3, out);
1003 lookup!(10, n0, n1, n2, n3, out);
1004 lookup!(11, n0, n1, n2, n3, out);
1005 lookup!(12, n0, n1, n2, n3, out);
1006 lookup!(13, n0, n1, n2, n3, out);
1007 lookup!(14, n0, n1, n2, n3, out);
1008 lookup!(15, n0, n1, n2, n3, out);
1009
1010 let elems = transpose_16x16(&out);
1011 for (i, elem) in elems.iter().enumerate() {
1012 vst1q_u8(output.add(i).cast::<u8>(), *elem);
1013 }
1014 }
1015 }
1016
1017 #[cfg(not(feature = "table-math"))]
1024 #[inline(always)]
1025 pub unsafe fn promote_batch_32_to_128(input: *const u8, output: *mut u128) {
1026 unsafe {
1027 let raw0 = vld1q_u8(input);
1028 let raw1 = vld1q_u8(input.add(16));
1029 let raw2 = vld1q_u8(input.add(32));
1030 let raw3 = vld1q_u8(input.add(48));
1031
1032 let a02 = vuzp1q_u8(raw0, raw1);
1033 let a13 = vuzp2q_u8(raw0, raw1);
1034 let b02 = vuzp1q_u8(raw2, raw3);
1035 let b13 = vuzp2q_u8(raw2, raw3);
1036
1037 let byte0 = vuzp1q_u8(a02, b02);
1038 let byte2 = vuzp2q_u8(a02, b02);
1039 let byte1 = vuzp1q_u8(a13, b13);
1040 let byte3 = vuzp2q_u8(a13, b13);
1041
1042 let mask_0f = vdupq_n_u8(0x0F);
1043 let n0 = vandq_u8(byte0, mask_0f);
1044 let n1 = vshrq_n_u8::<4>(byte0);
1045 let n2 = vandq_u8(byte1, mask_0f);
1046 let n3 = vshrq_n_u8::<4>(byte1);
1047 let n4 = vandq_u8(byte2, mask_0f);
1048 let n5 = vshrq_n_u8::<4>(byte2);
1049 let n6 = vandq_u8(byte3, mask_0f);
1050 let n7 = vshrq_n_u8::<4>(byte3);
1051
1052 let mut out = [vdupq_n_u8(0); 16];
1053
1054 macro_rules! lookup {
1055 ($j:expr, $n0:ident, $n1:ident, $n2:ident, $n3:ident,
1056 $n4:ident, $n5:ident, $n6:ident, $n7:ident, $dst:ident) => {{
1057 let t0 = vld1q_u8(constants::NIBBLE_PROMOTE_32_0_TO_128[$j].as_ptr());
1058 let t1 = vld1q_u8(constants::NIBBLE_PROMOTE_32_1_TO_128[$j].as_ptr());
1059 let t2 = vld1q_u8(constants::NIBBLE_PROMOTE_32_2_TO_128[$j].as_ptr());
1060 let t3 = vld1q_u8(constants::NIBBLE_PROMOTE_32_3_TO_128[$j].as_ptr());
1061 let t4 = vld1q_u8(constants::NIBBLE_PROMOTE_32_4_TO_128[$j].as_ptr());
1062 let t5 = vld1q_u8(constants::NIBBLE_PROMOTE_32_5_TO_128[$j].as_ptr());
1063 let t6 = vld1q_u8(constants::NIBBLE_PROMOTE_32_6_TO_128[$j].as_ptr());
1064 let t7 = vld1q_u8(constants::NIBBLE_PROMOTE_32_7_TO_128[$j].as_ptr());
1065
1066 $dst[$j] = veorq_u8(
1067 veorq_u8(
1068 veorq_u8(vqtbl1q_u8(t0, $n0), vqtbl1q_u8(t1, $n1)),
1069 veorq_u8(vqtbl1q_u8(t2, $n2), vqtbl1q_u8(t3, $n3)),
1070 ),
1071 veorq_u8(
1072 veorq_u8(vqtbl1q_u8(t4, $n4), vqtbl1q_u8(t5, $n5)),
1073 veorq_u8(vqtbl1q_u8(t6, $n6), vqtbl1q_u8(t7, $n7)),
1074 ),
1075 );
1076 }};
1077 }
1078
1079 lookup!(0, n0, n1, n2, n3, n4, n5, n6, n7, out);
1080 lookup!(1, n0, n1, n2, n3, n4, n5, n6, n7, out);
1081 lookup!(2, n0, n1, n2, n3, n4, n5, n6, n7, out);
1082 lookup!(3, n0, n1, n2, n3, n4, n5, n6, n7, out);
1083 lookup!(4, n0, n1, n2, n3, n4, n5, n6, n7, out);
1084 lookup!(5, n0, n1, n2, n3, n4, n5, n6, n7, out);
1085 lookup!(6, n0, n1, n2, n3, n4, n5, n6, n7, out);
1086 lookup!(7, n0, n1, n2, n3, n4, n5, n6, n7, out);
1087 lookup!(8, n0, n1, n2, n3, n4, n5, n6, n7, out);
1088 lookup!(9, n0, n1, n2, n3, n4, n5, n6, n7, out);
1089 lookup!(10, n0, n1, n2, n3, n4, n5, n6, n7, out);
1090 lookup!(11, n0, n1, n2, n3, n4, n5, n6, n7, out);
1091 lookup!(12, n0, n1, n2, n3, n4, n5, n6, n7, out);
1092 lookup!(13, n0, n1, n2, n3, n4, n5, n6, n7, out);
1093 lookup!(14, n0, n1, n2, n3, n4, n5, n6, n7, out);
1094 lookup!(15, n0, n1, n2, n3, n4, n5, n6, n7, out);
1095
1096 let elems = transpose_16x16(&out);
1097 for (i, elem) in elems.iter().enumerate() {
1098 vst1q_u8(output.add(i).cast::<u8>(), *elem);
1099 }
1100 }
1101 }
1102
1103 #[cfg(not(feature = "table-math"))]
1110 #[inline(always)]
1111 pub unsafe fn promote_batch_64_to_128(input: *const u8, output: *mut u128) {
1112 unsafe {
1113 let raw0 = vld1q_u8(input);
1114 let raw1 = vld1q_u8(input.add(16));
1115 let raw2 = vld1q_u8(input.add(32));
1116 let raw3 = vld1q_u8(input.add(48));
1117 let raw4 = vld1q_u8(input.add(64));
1118 let raw5 = vld1q_u8(input.add(80));
1119 let raw6 = vld1q_u8(input.add(96));
1120 let raw7 = vld1q_u8(input.add(112));
1121
1122 let e01 = vuzp1q_u8(raw0, raw1);
1123 let o01 = vuzp2q_u8(raw0, raw1);
1124 let e23 = vuzp1q_u8(raw2, raw3);
1125 let o23 = vuzp2q_u8(raw2, raw3);
1126 let e45 = vuzp1q_u8(raw4, raw5);
1127 let o45 = vuzp2q_u8(raw4, raw5);
1128 let e67 = vuzp1q_u8(raw6, raw7);
1129 let o67 = vuzp2q_u8(raw6, raw7);
1130
1131 let e0_lo = vuzp1q_u8(e01, e23);
1132 let e2_lo = vuzp2q_u8(e01, e23);
1133 let o1_lo = vuzp1q_u8(o01, o23);
1134 let o3_lo = vuzp2q_u8(o01, o23);
1135 let e0_hi = vuzp1q_u8(e45, e67);
1136 let e2_hi = vuzp2q_u8(e45, e67);
1137 let o1_hi = vuzp1q_u8(o45, o67);
1138 let o3_hi = vuzp2q_u8(o45, o67);
1139
1140 let byte0 = vuzp1q_u8(e0_lo, e0_hi);
1141 let byte4 = vuzp2q_u8(e0_lo, e0_hi);
1142 let byte1 = vuzp1q_u8(o1_lo, o1_hi);
1143 let byte5 = vuzp2q_u8(o1_lo, o1_hi);
1144 let byte2 = vuzp1q_u8(e2_lo, e2_hi);
1145 let byte6 = vuzp2q_u8(e2_lo, e2_hi);
1146 let byte3 = vuzp1q_u8(o3_lo, o3_hi);
1147 let byte7 = vuzp2q_u8(o3_lo, o3_hi);
1148
1149 let mask_0f = vdupq_n_u8(0x0F);
1150 let nib = [
1151 vandq_u8(byte0, mask_0f),
1152 vshrq_n_u8::<4>(byte0),
1153 vandq_u8(byte1, mask_0f),
1154 vshrq_n_u8::<4>(byte1),
1155 vandq_u8(byte2, mask_0f),
1156 vshrq_n_u8::<4>(byte2),
1157 vandq_u8(byte3, mask_0f),
1158 vshrq_n_u8::<4>(byte3),
1159 vandq_u8(byte4, mask_0f),
1160 vshrq_n_u8::<4>(byte4),
1161 vandq_u8(byte5, mask_0f),
1162 vshrq_n_u8::<4>(byte5),
1163 vandq_u8(byte6, mask_0f),
1164 vshrq_n_u8::<4>(byte6),
1165 vandq_u8(byte7, mask_0f),
1166 vshrq_n_u8::<4>(byte7),
1167 ];
1168
1169 let mut out = [vdupq_n_u8(0); 16];
1170
1171 macro_rules! lookup {
1172 ($j:expr, $nib:ident, $dst:ident) => {{
1173 let t0 = vld1q_u8(constants::NIBBLE_PROMOTE_64_0_TO_128[$j].as_ptr());
1174 let t1 = vld1q_u8(constants::NIBBLE_PROMOTE_64_1_TO_128[$j].as_ptr());
1175 let t2 = vld1q_u8(constants::NIBBLE_PROMOTE_64_2_TO_128[$j].as_ptr());
1176 let t3 = vld1q_u8(constants::NIBBLE_PROMOTE_64_3_TO_128[$j].as_ptr());
1177 let t4 = vld1q_u8(constants::NIBBLE_PROMOTE_64_4_TO_128[$j].as_ptr());
1178 let t5 = vld1q_u8(constants::NIBBLE_PROMOTE_64_5_TO_128[$j].as_ptr());
1179 let t6 = vld1q_u8(constants::NIBBLE_PROMOTE_64_6_TO_128[$j].as_ptr());
1180 let t7 = vld1q_u8(constants::NIBBLE_PROMOTE_64_7_TO_128[$j].as_ptr());
1181 let t8 = vld1q_u8(constants::NIBBLE_PROMOTE_64_8_TO_128[$j].as_ptr());
1182 let t9 = vld1q_u8(constants::NIBBLE_PROMOTE_64_9_TO_128[$j].as_ptr());
1183 let t10 = vld1q_u8(constants::NIBBLE_PROMOTE_64_10_TO_128[$j].as_ptr());
1184 let t11 = vld1q_u8(constants::NIBBLE_PROMOTE_64_11_TO_128[$j].as_ptr());
1185 let t12 = vld1q_u8(constants::NIBBLE_PROMOTE_64_12_TO_128[$j].as_ptr());
1186 let t13 = vld1q_u8(constants::NIBBLE_PROMOTE_64_13_TO_128[$j].as_ptr());
1187 let t14 = vld1q_u8(constants::NIBBLE_PROMOTE_64_14_TO_128[$j].as_ptr());
1188 let t15 = vld1q_u8(constants::NIBBLE_PROMOTE_64_15_TO_128[$j].as_ptr());
1189
1190 $dst[$j] = veorq_u8(
1191 veorq_u8(
1192 veorq_u8(
1193 veorq_u8(vqtbl1q_u8(t0, $nib[0]), vqtbl1q_u8(t1, $nib[1])),
1194 veorq_u8(vqtbl1q_u8(t2, $nib[2]), vqtbl1q_u8(t3, $nib[3])),
1195 ),
1196 veorq_u8(
1197 veorq_u8(vqtbl1q_u8(t4, $nib[4]), vqtbl1q_u8(t5, $nib[5])),
1198 veorq_u8(vqtbl1q_u8(t6, $nib[6]), vqtbl1q_u8(t7, $nib[7])),
1199 ),
1200 ),
1201 veorq_u8(
1202 veorq_u8(
1203 veorq_u8(vqtbl1q_u8(t8, $nib[8]), vqtbl1q_u8(t9, $nib[9])),
1204 veorq_u8(vqtbl1q_u8(t10, $nib[10]), vqtbl1q_u8(t11, $nib[11])),
1205 ),
1206 veorq_u8(
1207 veorq_u8(vqtbl1q_u8(t12, $nib[12]), vqtbl1q_u8(t13, $nib[13])),
1208 veorq_u8(vqtbl1q_u8(t14, $nib[14]), vqtbl1q_u8(t15, $nib[15])),
1209 ),
1210 ),
1211 );
1212 }};
1213 }
1214
1215 lookup!(0, nib, out);
1216 lookup!(1, nib, out);
1217 lookup!(2, nib, out);
1218 lookup!(3, nib, out);
1219 lookup!(4, nib, out);
1220 lookup!(5, nib, out);
1221 lookup!(6, nib, out);
1222 lookup!(7, nib, out);
1223 lookup!(8, nib, out);
1224 lookup!(9, nib, out);
1225 lookup!(10, nib, out);
1226 lookup!(11, nib, out);
1227 lookup!(12, nib, out);
1228 lookup!(13, nib, out);
1229 lookup!(14, nib, out);
1230 lookup!(15, nib, out);
1231
1232 let elems = transpose_16x16(&out);
1233 for (i, elem) in elems.iter().enumerate() {
1234 vst1q_u8(output.add(i).cast::<u8>(), *elem);
1235 }
1236 }
1237 }
1238
1239 #[cfg(not(feature = "table-math"))]
1241 #[inline(always)]
1242 unsafe fn transpose_16x16(r: &[uint8x16_t; 16]) -> [uint8x16_t; 16] {
1243 #[inline(always)]
1246 const fn u8_to_u16(v: uint8x16_t) -> uint16x8_t {
1247 unsafe { transmute::<uint8x16_t, uint16x8_t>(v) }
1248 }
1249
1250 #[inline(always)]
1251 const fn u16_to_u32(v: uint16x8_t) -> uint32x4_t {
1252 unsafe { transmute::<uint16x8_t, uint32x4_t>(v) }
1253 }
1254
1255 #[inline(always)]
1256 const fn u32_to_u64(v: uint32x4_t) -> uint64x2_t {
1257 unsafe { transmute::<uint32x4_t, uint64x2_t>(v) }
1258 }
1259
1260 #[inline(always)]
1261 const fn u64_to_u8(v: uint64x2_t) -> uint8x16_t {
1262 unsafe { transmute::<uint64x2_t, uint8x16_t>(v) }
1263 }
1264
1265 unsafe {
1266 let a0 = vtrn1q_u8(r[0], r[1]);
1269 let a1 = vtrn2q_u8(r[0], r[1]);
1270 let a2 = vtrn1q_u8(r[2], r[3]);
1271 let a3 = vtrn2q_u8(r[2], r[3]);
1272 let a4 = vtrn1q_u8(r[4], r[5]);
1273 let a5 = vtrn2q_u8(r[4], r[5]);
1274 let a6 = vtrn1q_u8(r[6], r[7]);
1275 let a7 = vtrn2q_u8(r[6], r[7]);
1276 let a8 = vtrn1q_u8(r[8], r[9]);
1277 let a9 = vtrn2q_u8(r[8], r[9]);
1278 let a10 = vtrn1q_u8(r[10], r[11]);
1279 let a11 = vtrn2q_u8(r[10], r[11]);
1280 let a12 = vtrn1q_u8(r[12], r[13]);
1281 let a13 = vtrn2q_u8(r[12], r[13]);
1282 let a14 = vtrn1q_u8(r[14], r[15]);
1283 let a15 = vtrn2q_u8(r[14], r[15]);
1284
1285 let b0 = vtrn1q_u16(u8_to_u16(a0), u8_to_u16(a2));
1288 let b2 = vtrn2q_u16(u8_to_u16(a0), u8_to_u16(a2));
1289 let b1 = vtrn1q_u16(u8_to_u16(a1), u8_to_u16(a3));
1290 let b3 = vtrn2q_u16(u8_to_u16(a1), u8_to_u16(a3));
1291 let b4 = vtrn1q_u16(u8_to_u16(a4), u8_to_u16(a6));
1292 let b6 = vtrn2q_u16(u8_to_u16(a4), u8_to_u16(a6));
1293 let b5 = vtrn1q_u16(u8_to_u16(a5), u8_to_u16(a7));
1294 let b7 = vtrn2q_u16(u8_to_u16(a5), u8_to_u16(a7));
1295 let b8 = vtrn1q_u16(u8_to_u16(a8), u8_to_u16(a10));
1296 let b10 = vtrn2q_u16(u8_to_u16(a8), u8_to_u16(a10));
1297 let b9 = vtrn1q_u16(u8_to_u16(a9), u8_to_u16(a11));
1298 let b11 = vtrn2q_u16(u8_to_u16(a9), u8_to_u16(a11));
1299 let b12 = vtrn1q_u16(u8_to_u16(a12), u8_to_u16(a14));
1300 let b14 = vtrn2q_u16(u8_to_u16(a12), u8_to_u16(a14));
1301 let b13 = vtrn1q_u16(u8_to_u16(a13), u8_to_u16(a15));
1302 let b15 = vtrn2q_u16(u8_to_u16(a13), u8_to_u16(a15));
1303
1304 let c0 = vtrn1q_u32(u16_to_u32(b0), u16_to_u32(b4));
1307 let c4 = vtrn2q_u32(u16_to_u32(b0), u16_to_u32(b4));
1308 let c1 = vtrn1q_u32(u16_to_u32(b1), u16_to_u32(b5));
1309 let c5 = vtrn2q_u32(u16_to_u32(b1), u16_to_u32(b5));
1310 let c2 = vtrn1q_u32(u16_to_u32(b2), u16_to_u32(b6));
1311 let c6 = vtrn2q_u32(u16_to_u32(b2), u16_to_u32(b6));
1312 let c3 = vtrn1q_u32(u16_to_u32(b3), u16_to_u32(b7));
1313 let c7 = vtrn2q_u32(u16_to_u32(b3), u16_to_u32(b7));
1314 let c8 = vtrn1q_u32(u16_to_u32(b8), u16_to_u32(b12));
1315 let c12 = vtrn2q_u32(u16_to_u32(b8), u16_to_u32(b12));
1316 let c9 = vtrn1q_u32(u16_to_u32(b9), u16_to_u32(b13));
1317 let c13 = vtrn2q_u32(u16_to_u32(b9), u16_to_u32(b13));
1318 let c10 = vtrn1q_u32(u16_to_u32(b10), u16_to_u32(b14));
1319 let c14 = vtrn2q_u32(u16_to_u32(b10), u16_to_u32(b14));
1320 let c11 = vtrn1q_u32(u16_to_u32(b11), u16_to_u32(b15));
1321 let c15 = vtrn2q_u32(u16_to_u32(b11), u16_to_u32(b15));
1322
1323 [
1326 u64_to_u8(vtrn1q_u64(u32_to_u64(c0), u32_to_u64(c8))),
1327 u64_to_u8(vtrn1q_u64(u32_to_u64(c1), u32_to_u64(c9))),
1328 u64_to_u8(vtrn1q_u64(u32_to_u64(c2), u32_to_u64(c10))),
1329 u64_to_u8(vtrn1q_u64(u32_to_u64(c3), u32_to_u64(c11))),
1330 u64_to_u8(vtrn1q_u64(u32_to_u64(c4), u32_to_u64(c12))),
1331 u64_to_u8(vtrn1q_u64(u32_to_u64(c5), u32_to_u64(c13))),
1332 u64_to_u8(vtrn1q_u64(u32_to_u64(c6), u32_to_u64(c14))),
1333 u64_to_u8(vtrn1q_u64(u32_to_u64(c7), u32_to_u64(c15))),
1334 u64_to_u8(vtrn2q_u64(u32_to_u64(c0), u32_to_u64(c8))),
1335 u64_to_u8(vtrn2q_u64(u32_to_u64(c1), u32_to_u64(c9))),
1336 u64_to_u8(vtrn2q_u64(u32_to_u64(c2), u32_to_u64(c10))),
1337 u64_to_u8(vtrn2q_u64(u32_to_u64(c3), u32_to_u64(c11))),
1338 u64_to_u8(vtrn2q_u64(u32_to_u64(c4), u32_to_u64(c12))),
1339 u64_to_u8(vtrn2q_u64(u32_to_u64(c5), u32_to_u64(c13))),
1340 u64_to_u8(vtrn2q_u64(u32_to_u64(c6), u32_to_u64(c14))),
1341 u64_to_u8(vtrn2q_u64(u32_to_u64(c7), u32_to_u64(c15))),
1342 ]
1343 }
1344 }
1345}
1346
1347#[cfg(test)]
1352mod tests {
1353 use super::*;
1354 use proptest::prelude::*;
1355 use rand::{RngExt, rng};
1356
1357 #[test]
1362 fn tower_constants() {
1363 let tau128 = Block128::EXTENSION_TAU;
1366 let (lo128, hi128) = tau128.split();
1367 assert_eq!(lo128, Block64::ZERO);
1368 assert_eq!(hi128, Block64::TAU);
1369 }
1370
1371 #[test]
1372 fn add_truth() {
1373 let zero = Block128::ZERO;
1374 let one = Block128::ONE;
1375
1376 assert_eq!(zero + zero, zero);
1377 assert_eq!(zero + one, one);
1378 assert_eq!(one + zero, one);
1379 assert_eq!(one + one, zero);
1380 }
1381
1382 #[test]
1383 fn mul_truth() {
1384 let zero = Block128::ZERO;
1385 let one = Block128::ONE;
1386
1387 assert_eq!(zero * zero, zero);
1388 assert_eq!(zero * one, zero);
1389 assert_eq!(one * one, one);
1390 }
1391
1392 #[test]
1393 fn add() {
1394 assert_eq!(Block128(5) + Block128(3), Block128(6));
1397 }
1398
1399 #[test]
1400 fn mul_simple() {
1401 assert_eq!(Block128(2) * Block128(2), Block128(4));
1404 }
1405
1406 #[test]
1407 fn mul_overflow() {
1408 assert_eq!(Block128(0x57) * Block128(0x83), Block128(0xC1));
1412 }
1413
1414 #[test]
1415 fn karatsuba_correctness() {
1416 let x = Block128::new(Block64::ZERO, Block64::ONE);
1428 let squared = x * x;
1429
1430 let (res_lo, res_hi) = squared.split();
1432
1433 assert_eq!(res_hi, Block64::ONE, "X^2 should contain X component");
1434 assert_eq!(
1435 res_lo,
1436 Block64(0x2000_0000_0000_0000),
1437 "X^2 should contain tau component (0x2000_0000_0000_0000)"
1438 );
1439 }
1440
1441 #[test]
1442 fn mul_tau_matches_mul() {
1443 let mut rng = rng();
1444 let edges = [
1445 Block128::ZERO,
1446 Block128::ONE,
1447 Block128::TAU,
1448 Block128(u128::MAX),
1449 ];
1450
1451 let random = (0..1000).map(|_| Block128(rng.random()));
1452
1453 for x in edges.into_iter().chain(random) {
1454 assert_eq!(
1455 x.mul_tau(),
1456 x * Block128::EXTENSION_TAU,
1457 "Block128 mul_tau mismatch at {:#034x}",
1458 x.0
1459 );
1460 }
1461 }
1462
1463 #[test]
1464 fn security_zeroize() {
1465 let mut secret_val = Block128::from(0xDEAD_BEEF_CAFE_BABE_u128);
1467 assert_ne!(secret_val, Block128::ZERO);
1468
1469 secret_val.zeroize();
1471
1472 assert_eq!(secret_val, Block128::ZERO, "Memory was not wiped!");
1474
1475 assert_eq!(secret_val.0, 0u128, "Underlying memory leak detected");
1477 }
1478
1479 #[test]
1480 fn invert_zero() {
1481 assert_eq!(
1483 Block128::ZERO.invert(),
1484 Block128::ZERO,
1485 "invert(0) must return 0"
1486 );
1487 }
1488
1489 #[test]
1490 fn inversion_random() {
1491 let mut rng = rng();
1492 for _i in 0..1000 {
1493 let val = Block128(rng.random());
1494
1495 if val != Block128::ZERO {
1496 let inv = val.invert();
1497 let identity = val * inv;
1498
1499 assert_eq!(
1500 identity,
1501 Block128::ONE,
1502 "Inversion identity failed: a * a^-1 != 1"
1503 );
1504 }
1505 }
1506 }
1507
1508 #[test]
1509 fn tower_embedding() {
1510 let mut rng = rng();
1511 for _ in 0..100 {
1512 let a = Block64(rng.random());
1513 let b = Block64(rng.random());
1514
1515 let a_lifted: Block128 = a.into();
1517 let (lo, hi) = a_lifted.split();
1518
1519 assert_eq!(lo, a, "Embedding structure failed: low part mismatch");
1520 assert_eq!(
1521 hi,
1522 Block64::ZERO,
1523 "Embedding structure failed: high part must be zero"
1524 );
1525
1526 let sum_sub = a + b;
1528 let sum_lifted: Block128 = sum_sub.into();
1529 let sum_in_super = Block128::from(a) + Block128::from(b);
1530
1531 assert_eq!(sum_lifted, sum_in_super, "Homomorphism failed: add");
1532
1533 let prod_sub = a * b;
1538 let prod_lifted: Block128 = prod_sub.into();
1539 let prod_in_super = Block128::from(a) * Block128::from(b);
1540
1541 assert_eq!(prod_lifted, prod_in_super, "Homomorphism failed: mul");
1542 }
1543 }
1544
1545 #[test]
1550 fn isomorphism_roundtrip() {
1551 let mut rng = rng();
1552 for _ in 0..1000 {
1553 let val = Block128(rng.random::<u128>());
1554 assert_eq!(val.to_hardware().to_tower(), val);
1555 }
1556 }
1557
1558 #[cfg(not(feature = "table-math"))]
1559 #[test]
1560 fn map_ct_128_split_is_column_map() {
1561 let mut rng = rng();
1562
1563 for basis in [&TOWER_TO_FLAT_BASIS_128.0, &FLAT_TO_TOWER_BASIS_128.0] {
1564 for (i, &col) in basis.iter().enumerate() {
1565 assert_eq!(map_ct_128_split(1u128 << i, basis), col, "unit vector {i}");
1566 }
1567
1568 for _ in 0..1000 {
1569 let x = rng.random::<u128>();
1570 let expected = basis
1571 .iter()
1572 .enumerate()
1573 .filter(|&(i, _)| (x >> i) & 1 == 1)
1574 .fold(0, |acc, (_, &col)| acc ^ col);
1575
1576 assert_eq!(map_ct_128_split(x, basis), expected, "x = {x:#x}");
1577 }
1578 }
1579 }
1580
1581 #[test]
1582 fn flat_mul_homomorphism() {
1583 let mut rng = rng();
1584 for _ in 0..1000 {
1585 let a = Block128(rng.random());
1586 let b = Block128(rng.random());
1587
1588 let expected_flat = (a * b).to_hardware();
1589 let actual_flat = a.to_hardware() * b.to_hardware();
1590
1591 assert_eq!(
1592 actual_flat, expected_flat,
1593 "Block128 flat multiplication mismatch: (a*b)^H != a^H * b^H"
1594 );
1595 }
1596 }
1597
1598 #[test]
1599 fn packed_consistency() {
1600 let mut rng = rng();
1601 for _ in 0..100 {
1602 let mut a_vals = [Block128::ZERO; 4];
1603 let mut b_vals = [Block128::ZERO; 4];
1604
1605 for i in 0..4 {
1606 a_vals[i] = Block128(rng.random::<u128>());
1607 b_vals[i] = Block128(rng.random::<u128>());
1608 }
1609
1610 let a_flat_vals = a_vals.map(|x| x.to_hardware());
1611 let b_flat_vals = b_vals.map(|x| x.to_hardware());
1612 let a_packed = Flat::<Block128>::pack(&a_flat_vals);
1613 let b_packed = Flat::<Block128>::pack(&b_flat_vals);
1614
1615 let add_res = Block128::add_hardware_packed(a_packed, b_packed);
1617
1618 let mut add_out = [Block128::ZERO.to_hardware(); 4];
1619 Flat::<Block128>::unpack(add_res, &mut add_out);
1620
1621 for i in 0..4 {
1622 assert_eq!(
1623 add_out[i],
1624 (a_vals[i] + b_vals[i]).to_hardware(),
1625 "Block128 SIMD add mismatch at index {}",
1626 i
1627 );
1628 }
1629
1630 let mul_res = Block128::mul_hardware_packed(a_packed, b_packed);
1632
1633 let mut mul_out = [Block128::ZERO.to_hardware(); 4];
1634 Flat::<Block128>::unpack(mul_res, &mut mul_out);
1635
1636 for i in 0..4 {
1637 let expected_flat = (a_vals[i] * b_vals[i]).to_hardware();
1638 assert_eq!(
1639 mul_out[i], expected_flat,
1640 "Block128 SIMD mul mismatch at index {}",
1641 i
1642 );
1643 }
1644 }
1645 }
1646
1647 #[test]
1652 fn pack_unpack_roundtrip() {
1653 let mut rng = rng();
1654 let mut data = [Block128::ZERO; PACKED_WIDTH_128];
1655 for v in data.iter_mut() {
1656 *v = Block128(rng.random());
1657 }
1658
1659 let packed = Block128::pack(&data);
1660 let mut unpacked = [Block128::ZERO; PACKED_WIDTH_128];
1661 Block128::unpack(packed, &mut unpacked);
1662 assert_eq!(data, unpacked);
1663 }
1664
1665 #[test]
1666 fn packed_add_consistency() {
1667 let mut rng = rng();
1668 let mut a_vals = [Block128::ZERO; PACKED_WIDTH_128];
1669 let mut b_vals = [Block128::ZERO; PACKED_WIDTH_128];
1670
1671 for i in 0..PACKED_WIDTH_128 {
1672 a_vals[i] = Block128(rng.random());
1673 b_vals[i] = Block128(rng.random());
1674 }
1675
1676 let res_packed = Block128::pack(&a_vals) + Block128::pack(&b_vals);
1677 let mut res_unpacked = [Block128::ZERO; PACKED_WIDTH_128];
1678 Block128::unpack(res_packed, &mut res_unpacked);
1679
1680 for i in 0..PACKED_WIDTH_128 {
1681 assert_eq!(res_unpacked[i], a_vals[i] + b_vals[i]);
1682 }
1683 }
1684
1685 #[test]
1686 fn packed_mul_consistency() {
1687 let mut rng = rng();
1688
1689 for _ in 0..1000 {
1690 let mut a_arr = [Block128::ZERO; PACKED_WIDTH_128];
1693 let mut b_arr = [Block128::ZERO; PACKED_WIDTH_128];
1694
1695 for i in 0..PACKED_WIDTH_128 {
1696 let val_a: u128 = rng.random();
1698 let val_b: u128 = rng.random();
1699 a_arr[i] = Block128(val_a);
1700 b_arr[i] = Block128(val_b);
1701 }
1702
1703 let a_packed = PackedBlock128(a_arr);
1704 let b_packed = PackedBlock128(b_arr);
1705
1706 let c_packed = a_packed * b_packed;
1708
1709 let mut c_expected = [Block128::ZERO; PACKED_WIDTH_128];
1711 for i in 0..PACKED_WIDTH_128 {
1712 c_expected[i] = a_arr[i] * b_arr[i];
1713 }
1714
1715 assert_eq!(c_packed.0, c_expected, "SIMD multiplication mismatch!");
1716 }
1717 }
1718
1719 #[inline(always)]
1724 fn promote_block8_tables(val: Block8) -> Block128 {
1725 let idx_flat = val.0 as usize;
1727 let tower_byte = unsafe { *constants::FLAT_TO_TOWER_8.get_unchecked(idx_flat) };
1728 let idx_tower = tower_byte as usize;
1729
1730 Block128(unsafe { *constants::TOWER_TO_FLAT_128.get_unchecked(idx_tower) })
1731 }
1732
1733 #[inline(always)]
1734 fn promote_block16_tables(val: Block16) -> Block128 {
1735 let v_flat = val.0;
1736
1737 let mut v_tower = 0u16;
1738 for i in 0..2 {
1739 let byte = ((v_flat >> (i * 8)) & 0xFF) as usize;
1740 let idx = (i * 256) + byte;
1741 v_tower ^= unsafe { *constants::FLAT_TO_TOWER_16.get_unchecked(idx) };
1742 }
1743
1744 let mut res = 0u128;
1745 for i in 0..2 {
1746 let byte = ((v_tower >> (i * 8)) & 0xFF) as usize;
1747 let idx = (i * 256) + byte;
1748 res ^= unsafe { *constants::TOWER_TO_FLAT_128.get_unchecked(idx) };
1749 }
1750
1751 Block128(res)
1752 }
1753
1754 #[inline(always)]
1755 fn promote_block32_tables(val: Block32) -> Block128 {
1756 let v_flat = val.0;
1757
1758 let mut v_tower = 0u32;
1759 for i in 0..4 {
1760 let byte = ((v_flat >> (i * 8)) & 0xFF) as usize;
1761 let idx = (i * 256) + byte;
1762 v_tower ^= unsafe { *constants::FLAT_TO_TOWER_32.get_unchecked(idx) };
1763 }
1764
1765 let mut res = 0u128;
1766 for i in 0..4 {
1767 let byte = ((v_tower >> (i * 8)) & 0xFF) as usize;
1768 let idx = (i * 256) + byte;
1769 res ^= unsafe { *constants::TOWER_TO_FLAT_128.get_unchecked(idx) };
1770 }
1771
1772 Block128(res)
1773 }
1774
1775 #[inline(always)]
1776 fn promote_block64_tables(val: Block64) -> Block128 {
1777 let v_flat = val.0;
1778
1779 let mut v_tower = 0u64;
1780 for i in 0..8 {
1781 let byte = ((v_flat >> (i * 8)) & 0xFF) as usize;
1782 let idx = (i * 256) + byte;
1783 v_tower ^= unsafe { *constants::FLAT_TO_TOWER_64.get_unchecked(idx) };
1784 }
1785
1786 let mut res = 0u128;
1787 for i in 0..8 {
1788 let byte = ((v_tower >> (i * 8)) & 0xFF) as usize;
1789 let idx = (i * 256) + byte;
1790 res ^= unsafe { *constants::TOWER_TO_FLAT_128.get_unchecked(idx) };
1791 }
1792
1793 Block128(res)
1794 }
1795
1796 #[test]
1797 fn lift_from_partial_hardware_matches_tables_block8_exhaustive() {
1798 for x in 0u16..=u8::MAX as u16 {
1799 let v = Block8(x as u8);
1800 let got = Block128::promote_flat(Flat::from_raw(v)).into_raw();
1801 let expected = promote_block8_tables(v);
1802
1803 assert_eq!(got, expected);
1804 }
1805 }
1806
1807 #[test]
1808 fn lift_from_partial_hardware_matches_tables_block16_exhaustive() {
1809 for x in 0..=u16::MAX {
1810 let v = Block16(x);
1811 let got = Block128::promote_flat(Flat::from_raw(v)).into_raw();
1812 let expected = promote_block16_tables(v);
1813
1814 assert_eq!(got, expected);
1815 }
1816 }
1817
1818 #[test]
1819 fn lift_from_partial_hardware_matches_tables_block32_random() {
1820 let mut rng = rng();
1821 for _ in 0..10_000 {
1822 let v = Block32(rng.random::<u32>());
1823 let got = Block128::promote_flat(Flat::from_raw(v)).into_raw();
1824 let expected = promote_block32_tables(v);
1825
1826 assert_eq!(got, expected);
1827 }
1828 }
1829
1830 #[test]
1831 fn lift_from_partial_hardware_matches_tables_block64_random() {
1832 let mut rng = rng();
1833 for _ in 0..10_000 {
1834 let v = Block64(rng.random::<u64>());
1835 let got = Block128::promote_flat(Flat::from_raw(v)).into_raw();
1836 let expected = promote_block64_tables(v);
1837
1838 assert_eq!(got, expected);
1839 }
1840 }
1841
1842 #[test]
1847 fn promote_flat_batch_matches_scalar_block8() {
1848 let mut rng = rng();
1849 let input: Vec<Flat<Block8>> = (0..64)
1850 .map(|_| Block8(rng.random::<u8>()).to_hardware())
1851 .collect();
1852
1853 let mut batch_out = vec![Flat::from_raw(Block128::ZERO); 64];
1854 Block128::promote_flat_batch(&input, &mut batch_out);
1855
1856 for (i, &v) in input.iter().enumerate() {
1857 let scalar = Block128::promote_flat(v);
1858 assert_eq!(batch_out[i], scalar, "batch/scalar mismatch at index {}", i);
1859 }
1860 }
1861
1862 #[test]
1863 fn promote_flat_batch_matches_scalar_block16() {
1864 let mut rng = rng();
1865 let input: Vec<Flat<Block16>> = (0..32)
1866 .map(|_| Block16(rng.random::<u16>()).to_hardware())
1867 .collect();
1868
1869 let mut batch_out = vec![Flat::from_raw(Block128::ZERO); 32];
1870 Block128::promote_flat_batch(&input, &mut batch_out);
1871
1872 for (i, &v) in input.iter().enumerate() {
1873 assert_eq!(
1874 batch_out[i],
1875 Block128::promote_flat(v),
1876 "batch/scalar mismatch at index {}",
1877 i
1878 );
1879 }
1880 }
1881
1882 #[test]
1883 fn promote_flat_batch_matches_scalar_block32() {
1884 let mut rng = rng();
1885 let input: Vec<Flat<Block32>> = (0..16)
1886 .map(|_| Block32(rng.random::<u32>()).to_hardware())
1887 .collect();
1888
1889 let mut batch_out = vec![Flat::from_raw(Block128::ZERO); 16];
1890 Block128::promote_flat_batch(&input, &mut batch_out);
1891
1892 for (i, &v) in input.iter().enumerate() {
1893 assert_eq!(
1894 batch_out[i],
1895 Block128::promote_flat(v),
1896 "batch/scalar mismatch at index {}",
1897 i
1898 );
1899 }
1900 }
1901
1902 #[test]
1903 fn promote_flat_batch_matches_scalar_block64() {
1904 let mut rng = rng();
1905 let input: Vec<Flat<Block64>> = (0..40)
1906 .map(|_| Block64(rng.random::<u64>()).to_hardware())
1907 .collect();
1908
1909 let mut batch_out = vec![Flat::from_raw(Block128::ZERO); 40];
1910 Block128::promote_flat_batch(&input, &mut batch_out);
1911
1912 for (i, &v) in input.iter().enumerate() {
1913 assert_eq!(
1914 batch_out[i],
1915 Block128::promote_flat(v),
1916 "batch/scalar mismatch at index {}",
1917 i
1918 );
1919 }
1920 }
1921
1922 #[test]
1923 fn promote_flat_batch_partial_slice() {
1924 let input: Vec<Flat<Block8>> = (0..16).map(|i| Block8(i as u8).to_hardware()).collect();
1925
1926 let mut out_short = vec![Flat::from_raw(Block128::ZERO); 5];
1928 Block128::promote_flat_batch(&input, &mut out_short);
1929
1930 for i in 0..5 {
1931 assert_eq!(out_short[i], Block128::promote_flat(input[i]));
1932 }
1933
1934 let short_input = &input[..3];
1936 let mut out_long = vec![Flat::from_raw(Block128::ZERO); 10];
1937
1938 Block128::promote_flat_batch(short_input, &mut out_long);
1939
1940 for i in 0..3 {
1941 assert_eq!(out_long[i], Block128::promote_flat(short_input[i]));
1942 }
1943
1944 for val in &out_long[3..10] {
1946 assert_eq!(*val, Flat::from_raw(Block128::ZERO));
1947 }
1948 }
1949
1950 #[test]
1951 fn promote_edge_zero() {
1952 let zero = Flat::from_raw(Block8(0));
1953 let promoted = Block128::promote_flat(zero);
1954
1955 assert_eq!(
1956 promoted,
1957 Flat::from_raw(Block128::ZERO),
1958 "promote(0) must be 0"
1959 );
1960
1961 let input = vec![zero; 16];
1964 let mut output = vec![Flat::from_raw(Block128(0xDEAD)); 16];
1965
1966 Block128::promote_flat_batch(&input, &mut output);
1967
1968 for o in &output {
1969 assert_eq!(*o, Flat::from_raw(Block128::ZERO));
1970 }
1971 }
1972
1973 #[test]
1974 fn promote_edge_one() {
1975 let one_flat8 = Block8::ONE.to_hardware();
1976 let one_flat128 = Block128::ONE.to_hardware();
1977
1978 assert_eq!(
1979 Block128::promote_flat(one_flat8),
1980 one_flat128,
1981 "promote(1) must equal 1 in target field"
1982 );
1983 }
1984
1985 #[test]
1986 fn promote_edge_max_block8() {
1987 let max = Flat::from_raw(Block8(0xFF));
1988 let promoted = Block128::promote_flat(max);
1989
1990 assert_ne!(promoted, Flat::from_raw(Block128::ZERO));
1992
1993 let tower_8 = max.to_tower();
1996 let tower_128 = Block128::from(tower_8);
1997
1998 assert_eq!(promoted.to_tower(), tower_128);
1999 }
2000
2001 #[test]
2002 fn promote_edge_single_bits() {
2003 for bit in 0..8 {
2004 let val = Flat::from_raw(Block8(1u8 << bit));
2005 let promoted = Block128::promote_flat(val);
2006
2007 assert_ne!(
2009 promoted,
2010 Flat::from_raw(Block128::ZERO),
2011 "single-bit {} promoted to zero",
2012 bit
2013 );
2014
2015 let tower_8 = val.to_tower();
2017 let tower_128 = Block128::from(tower_8);
2018
2019 assert_eq!(
2020 promoted.to_tower(),
2021 tower_128,
2022 "tower roundtrip failed for bit {}",
2023 bit
2024 );
2025 }
2026 }
2027
2028 #[test]
2029 fn promote_edge_alternating_packed() {
2030 let input: Vec<Flat<Block8>> = (0..16)
2031 .map(|i| {
2032 if i % 2 == 0 {
2033 Flat::from_raw(Block8(0x00))
2034 } else {
2035 Flat::from_raw(Block8(0xFF))
2036 }
2037 })
2038 .collect();
2039
2040 let mut output = vec![Flat::from_raw(Block128::ZERO); 16];
2041 Block128::promote_flat_batch(&input, &mut output);
2042
2043 for (i, &v) in input.iter().enumerate() {
2044 assert_eq!(
2045 output[i],
2046 Block128::promote_flat(v),
2047 "alternating mismatch at {}",
2048 i
2049 );
2050 }
2051 }
2052
2053 #[test]
2054 fn promote_edge_all_same_packed() {
2055 let val = Flat::from_raw(Block8(0x42));
2056 let expected = Block128::promote_flat(val);
2057
2058 let input = vec![val; 16];
2059 let mut output = vec![Flat::from_raw(Block128::ZERO); 16];
2060
2061 Block128::promote_flat_batch(&input, &mut output);
2062
2063 for (i, o) in output.iter().enumerate() {
2064 assert_eq!(*o, expected, "all-same mismatch at {}", i);
2065 }
2066 }
2067
2068 #[test]
2069 fn promote_tower_roundtrip_block8() {
2070 for x in 0u16..=u8::MAX as u16 {
2071 let b8 = Block8(x as u8);
2072 let promoted = Block128::promote_flat(b8.to_hardware());
2073 let tower_128 = promoted.to_tower();
2074 let embedded = Block128::from(b8);
2075
2076 assert_eq!(
2077 tower_128, embedded,
2078 "tower roundtrip failed for Block8({})",
2079 x
2080 );
2081 }
2082 }
2083
2084 #[test]
2085 fn promote_tower_roundtrip_block16() {
2086 let mut rng = rng();
2087 for _ in 0..10_000 {
2088 let v = Block16(rng.random::<u16>());
2089 let promoted = Block128::promote_flat(v.to_hardware());
2090 let tower_128 = promoted.to_tower();
2091 let embedded = Block128::from(v);
2092
2093 assert_eq!(
2094 tower_128, embedded,
2095 "tower roundtrip failed for Block16({})",
2096 v.0
2097 );
2098 }
2099 }
2100
2101 #[test]
2102 fn promote_tower_roundtrip_block32() {
2103 let mut rng = rng();
2104 for _ in 0..10_000 {
2105 let v = Block32(rng.random::<u32>());
2106 let promoted = Block128::promote_flat(v.to_hardware());
2107 let tower_128 = promoted.to_tower();
2108 let embedded = Block128::from(v);
2109
2110 assert_eq!(
2111 tower_128, embedded,
2112 "tower roundtrip failed for Block32({})",
2113 v.0
2114 );
2115 }
2116 }
2117
2118 #[test]
2119 fn promote_tower_roundtrip_block64() {
2120 let mut rng = rng();
2121 for _ in 0..10_000 {
2122 let v = Block64(rng.random::<u64>());
2123 let promoted = Block128::promote_flat(v.to_hardware());
2124 let tower_128 = promoted.to_tower();
2125 let embedded = Block128::from(v);
2126
2127 assert_eq!(
2128 tower_128, embedded,
2129 "tower roundtrip failed for Block64({})",
2130 v.0
2131 );
2132 }
2133 }
2134
2135 #[test]
2136 fn promote_algebraic_homomorphism_add_block8() {
2137 let mut rng = rng();
2138 for _ in 0..1000 {
2139 let a = Block8(rng.random::<u8>());
2140 let b = Block8(rng.random::<u8>());
2141
2142 let promote_a = Block128::promote_flat(a.to_hardware());
2143 let promote_b = Block128::promote_flat(b.to_hardware());
2144 let promote_sum = Block128::promote_flat((a + b).to_hardware());
2145
2146 assert_eq!(
2147 promote_a + promote_b,
2148 promote_sum,
2149 "add homomorphism: promote(a)+promote(b) != promote(a+b)"
2150 );
2151 }
2152 }
2153
2154 #[test]
2155 fn promote_algebraic_homomorphism_mul_block8() {
2156 let mut rng = rng();
2157 for _ in 0..1000 {
2158 let a = Block8(rng.random::<u8>());
2159 let b = Block8(rng.random::<u8>());
2160
2161 let promote_a = Block128::promote_flat(a.to_hardware());
2162 let promote_b = Block128::promote_flat(b.to_hardware());
2163 let promote_prod = Block128::promote_flat((a * b).to_hardware());
2164
2165 assert_eq!(
2168 promote_a * promote_b,
2169 promote_prod,
2170 "mul homomorphism: promote(a)*promote(b) != promote(a*b)"
2171 );
2172 }
2173 }
2174
2175 #[test]
2176 fn promote_algebraic_homomorphism_add_block16() {
2177 let mut rng = rng();
2178 for _ in 0..1000 {
2179 let a = Block16(rng.random::<u16>());
2180 let b = Block16(rng.random::<u16>());
2181
2182 let pa = Block128::promote_flat(a.to_hardware());
2183 let pb = Block128::promote_flat(b.to_hardware());
2184 let p_sum = Block128::promote_flat((a + b).to_hardware());
2185
2186 assert_eq!(pa + pb, p_sum, "Block16 add homomorphism failed");
2187 }
2188 }
2189
2190 #[test]
2191 fn promote_algebraic_homomorphism_mul_block16() {
2192 let mut rng = rng();
2193 for _ in 0..1000 {
2194 let a = Block16(rng.random::<u16>());
2195 let b = Block16(rng.random::<u16>());
2196
2197 let pa = Block128::promote_flat(a.to_hardware());
2198 let pb = Block128::promote_flat(b.to_hardware());
2199 let p_prod = Block128::promote_flat((a * b).to_hardware());
2200
2201 assert_eq!(pa * pb, p_prod, "Block16 mul homomorphism failed");
2202 }
2203 }
2204
2205 #[test]
2206 fn promote_algebraic_homomorphism_add_block32() {
2207 let mut rng = rng();
2208 for _ in 0..1000 {
2209 let a = Block32(rng.random::<u32>());
2210 let b = Block32(rng.random::<u32>());
2211
2212 let pa = Block128::promote_flat(a.to_hardware());
2213 let pb = Block128::promote_flat(b.to_hardware());
2214 let p_sum = Block128::promote_flat((a + b).to_hardware());
2215
2216 assert_eq!(pa + pb, p_sum, "Block32 add homomorphism failed");
2217 }
2218 }
2219
2220 #[test]
2221 fn promote_algebraic_homomorphism_mul_block32() {
2222 let mut rng = rng();
2223 for _ in 0..1000 {
2224 let a = Block32(rng.random::<u32>());
2225 let b = Block32(rng.random::<u32>());
2226
2227 let pa = Block128::promote_flat(a.to_hardware());
2228 let pb = Block128::promote_flat(b.to_hardware());
2229 let p_prod = Block128::promote_flat((a * b).to_hardware());
2230
2231 assert_eq!(pa * pb, p_prod, "Block32 mul homomorphism failed");
2232 }
2233 }
2234
2235 #[test]
2236 fn promote_algebraic_homomorphism_add_block64() {
2237 let mut rng = rng();
2238 for _ in 0..1000 {
2239 let a = Block64(rng.random::<u64>());
2240 let b = Block64(rng.random::<u64>());
2241
2242 let pa = Block128::promote_flat(a.to_hardware());
2243 let pb = Block128::promote_flat(b.to_hardware());
2244 let p_sum = Block128::promote_flat((a + b).to_hardware());
2245
2246 assert_eq!(pa + pb, p_sum, "Block64 add homomorphism failed");
2247 }
2248 }
2249
2250 #[test]
2251 fn promote_algebraic_homomorphism_mul_block64() {
2252 let mut rng = rng();
2253 for _ in 0..1000 {
2254 let a = Block64(rng.random::<u64>());
2255 let b = Block64(rng.random::<u64>());
2256
2257 let pa = Block128::promote_flat(a.to_hardware());
2258 let pb = Block128::promote_flat(b.to_hardware());
2259 let p_prod = Block128::promote_flat((a * b).to_hardware());
2260
2261 assert_eq!(pa * pb, p_prod, "Block64 mul homomorphism failed");
2262 }
2263 }
2264
2265 #[test]
2266 fn promote_generator_preserves_order() {
2267 let g = Block8(3);
2269 let g_promoted = Block128::promote_flat(g.to_hardware());
2270
2271 let mut acc8 = Block8::ONE;
2274 for _ in 0..255 {
2275 acc8 *= g;
2276 }
2277
2278 assert_eq!(acc8, Block8::ONE, "Block8 Fermat: g^255 must be 1");
2279
2280 let mut acc128 = Flat::from_raw(Block128::ONE);
2283 for _ in 0..255 {
2284 acc128 *= g_promoted;
2285 }
2286
2287 assert_eq!(
2288 acc128,
2289 Flat::from_raw(Block128::ONE),
2290 "promoted element lost multiplicative order"
2291 );
2292 }
2293
2294 proptest! {
2295 #[test]
2296 fn parity_masks_match_from_hardware(x_flat in any::<u128>()) {
2297 let tower = Block128::from_hardware(Flat::from_raw(Block128(x_flat))).0;
2298
2299 for k in 0..128 {
2300 let bit = ((tower >> k) & 1) as u8;
2301 let via_api = Flat::from_raw(Block128(x_flat)).tower_bit(k);
2302
2303 prop_assert_eq!(
2304 via_api, bit,
2305 "Block128 tower_bit_from_hardware mismatch at x_flat={:#034x}, bit_idx={}",
2306 x_flat, k
2307 );
2308 }
2309 }
2310 }
2311}