1use std::{
28 iter::Sum,
29 ops::{Add, AddAssign, Sub, SubAssign},
30};
31
32use bytemuck::TransparentWrapper;
33
34use crate::{
35 Divisible, Ghash128b, GhashSq256b, PackedBinaryGhash2x128b, PackedField, WideMul,
36 arch::{
37 Divide, GhashSqWideMul1x, M128, M256, M512, MulFromWideMul, PackedPrimitiveType,
38 portable::packed_macros::{portable_macros::*, *},
39 },
40 arithmetic_traits::{InvertOrZero, Square},
41 packed_extension::PackedExtension,
42 sliced_packed_field::SlicedPackedField,
43 underlier::{UnderlierType, WithUnderlier},
44};
45
46type Ghash<U> = PackedPrimitiveType<U, Ghash128b>;
48
49pub type SlicedGhashSq256b<U> = SlicedPackedField<GhashSq256b, Ghash<U>, 2>;
51pub type SlicedGhashSq1x256b = SlicedGhashSq256b<M128>;
53pub type SlicedGhashSq2x256b = SlicedGhashSq256b<M256>;
55pub type SlicedGhashSq4x256b = SlicedGhashSq256b<M512>;
57
58type GhashWide<U> = <Ghash<U> as WideMul>::Output;
60
61#[inline]
68fn ghash_mul_x<U: Divisible<M128>>(u: U) -> U {
69 U::from_iter(
70 Divisible::<M128>::value_iter(u)
71 .map(|lane| Ghash128b::from_underlier(lane).mul_x().to_underlier()),
72 )
73}
74
75#[inline]
77fn mul_x<U: UnderlierType + Divisible<M128>>(coord: Ghash<U>) -> Ghash<U> {
78 Ghash::<U>::from_underlier(ghash_mul_x(coord.to_underlier()))
79}
80
81#[derive(Clone, Copy, Debug, Default)]
88pub struct SlicedGhashSqWide<W> {
89 pub(crate) t0: W,
91 pub(crate) t2: W,
93 pub(crate) t1: W,
95}
96
97impl<W: Add<Output = W>> Add for SlicedGhashSqWide<W> {
98 type Output = Self;
99
100 #[inline]
101 fn add(self, rhs: Self) -> Self {
102 Self {
103 t0: self.t0 + rhs.t0,
104 t2: self.t2 + rhs.t2,
105 t1: self.t1 + rhs.t1,
106 }
107 }
108}
109
110impl<W: Sub<Output = W>> Sub for SlicedGhashSqWide<W> {
111 type Output = Self;
112
113 #[inline]
114 fn sub(self, rhs: Self) -> Self {
115 Self {
116 t0: self.t0 - rhs.t0,
117 t2: self.t2 - rhs.t2,
118 t1: self.t1 - rhs.t1,
119 }
120 }
121}
122
123impl<W: AddAssign> AddAssign for SlicedGhashSqWide<W> {
124 #[inline]
125 fn add_assign(&mut self, rhs: Self) {
126 self.t0 += rhs.t0;
127 self.t2 += rhs.t2;
128 self.t1 += rhs.t1;
129 }
130}
131
132impl<W: SubAssign> SubAssign for SlicedGhashSqWide<W> {
133 #[inline]
134 fn sub_assign(&mut self, rhs: Self) {
135 self.t0 -= rhs.t0;
136 self.t2 -= rhs.t2;
137 self.t1 -= rhs.t1;
138 }
139}
140
141impl<W: Default + Add<Output = W>> Sum for SlicedGhashSqWide<W> {
142 #[inline]
143 fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
144 iter.fold(Self::default(), |acc, x| acc + x)
145 }
146}
147
148impl<U> WideMul for SlicedGhashSq256b<U>
149where
150 U: UnderlierType + Divisible<M128>,
151 Ghash<U>: PackedField<Scalar = Ghash128b> + WideMul,
152{
153 type Output = SlicedGhashSqWide<GhashWide<U>>;
154
155 #[inline]
157 fn wide_mul(lhs: Self, rhs: Self) -> Self::Output {
158 let [a, b] = lhs.to_coords();
159 let [e, f] = rhs.to_coords();
160
161 SlicedGhashSqWide {
162 t0: <Ghash<U> as WideMul>::wide_mul(a, e),
163 t2: <Ghash<U> as WideMul>::wide_mul(b, f),
164 t1: <Ghash<U> as WideMul>::wide_mul(a + b, e + f),
165 }
166 }
167
168 #[inline]
171 fn reduce(wide: Self::Output) -> Self {
172 let t0 = <Ghash<U> as WideMul>::reduce(wide.t0);
173 let t2 = <Ghash<U> as WideMul>::reduce(wide.t2);
174 let t1 = <Ghash<U> as WideMul>::reduce(wide.t1);
175
176 let z0 = t0 + mul_x(t2);
177 Self::from_coords([z0, z0 + t1 + t2])
178 }
179}
180
181impl<U> Square for SlicedGhashSq256b<U>
182where
183 U: UnderlierType + Divisible<M128>,
184 Ghash<U>: PackedField<Scalar = Ghash128b>,
185{
186 #[inline]
189 fn square(self) -> Self {
190 let [a, b] = self.to_coords();
191
192 let t0 = Square::square(a);
193 let t2 = Square::square(b);
194
195 let x_t2 = mul_x(t2);
196 Self::from_coords([t0 + x_t2, x_t2])
197 }
198}
199
200impl<U> InvertOrZero for SlicedGhashSq256b<U>
201where
202 U: UnderlierType + Divisible<M128>,
203 Ghash<U>: PackedField<Scalar = Ghash128b>,
204{
205 #[inline]
210 fn invert_or_zero(self) -> Self {
211 let [a, b] = self.to_coords();
212
213 let norm = Square::square(a) + mul_x(a * b + Square::square(b));
214 let norm_inv = norm.invert_or_zero();
215
216 Self::from_coords([(a + mul_x(b)) * norm_inv, b * norm_inv])
217 }
218}
219
220#[inline]
238pub(crate) fn ghash_sq_coords(elem: PackedGhashSq1x256b) -> [Ghash128b; 2] {
239 let coords = PackedExtension::<Ghash128b>::cast_base(elem);
240 [coords.get(0), coords.get(1)]
241}
242
243#[inline]
245pub(crate) fn ghash_sq_from_coords(coords: [Ghash128b; 2]) -> PackedGhashSq1x256b {
246 PackedExtension::<Ghash128b>::cast_ext(PackedBinaryGhash2x128b::from_scalars(coords))
247}
248
249#[repr(transparent)]
251#[derive(TransparentWrapper)]
252pub struct GhashSqSquare<T>(T);
253
254impl Square for GhashSqSquare<PackedGhashSq1x256b> {
255 #[inline]
257 fn square(self) -> Self {
258 let sq = Square::square(PackedExtension::<Ghash128b>::cast_base(Self::peel(self)));
259
260 let x_t2 = sq.get(1).mul_x();
261 Self::wrap(ghash_sq_from_coords([sq.get(0) + x_t2, x_t2]))
262 }
263}
264
265#[repr(transparent)]
267#[derive(TransparentWrapper)]
268pub struct GhashSqInvert<T>(T);
269
270impl InvertOrZero for GhashSqInvert<PackedGhashSq1x256b> {
271 #[inline]
274 fn invert_or_zero(self) -> Self {
275 let [a, b] = ghash_sq_coords(Self::peel(self));
276
277 let norm = Square::square(a) + (a * b + Square::square(b)).mul_x();
278 let norm_inv = norm.invert_or_zero();
279
280 Self::wrap(ghash_sq_from_coords([(a + b.mul_x()) * norm_inv, b * norm_inv]))
281 }
282}
283
284type GhashSqDivide2x<T> = Divide<M256, T, 2>;
286
287define_packed_binary_field!(
288 PackedGhashSq1x256b,
289 GhashSq256b,
290 M256,
291 (MulFromWideMul),
292 (GhashSqSquare),
293 (GhashSqInvert),
294 (GhashSqWideMul1x)
295);
296
297define_packed_binary_field!(
298 PackedGhashSq2x256b,
299 GhashSq256b,
300 M512,
301 (MulFromWideMul),
302 (GhashSqDivide2x),
303 (GhashSqDivide2x),
304 (GhashSqDivide2x)
305);
306
307#[cfg(test)]
308mod tests {
309 use rand::{Rng, SeedableRng, rngs::StdRng};
310
311 use super::*;
312 use crate::{
313 Field, PackedField, Random,
314 arithmetic_traits::{InvertOrZero, Square},
315 field::FieldOps,
316 };
317
318 fn check_arithmetic<P: PackedField<Scalar = GhashSq256b>>(mut rng: impl Rng) {
322 let a = P::random(&mut rng);
323 let b = P::random(&mut rng);
324
325 let sum = a + b;
326 let diff = a - b;
327 let prod = a * b;
328 let sq = Square::square(a);
329 let inv = InvertOrZero::invert_or_zero(a);
330
331 for i in 0..P::WIDTH {
332 let (x, y) = (a.get(i), b.get(i));
333 assert_eq!(sum.get(i), x + y);
334 assert_eq!(diff.get(i), x - y);
335 assert_eq!(prod.get(i), x * y);
336 assert_eq!(sq.get(i), Square::square(x));
337 assert_eq!(inv.get(i), x.invert_or_zero());
338 if x != GhashSq256b::ZERO {
340 assert_eq!(x * inv.get(i), GhashSq256b::ONE);
341 }
342 }
343 }
344
345 fn check_wide_mul<P>(mut rng: impl Rng)
346 where
347 P: PackedField<Scalar = GhashSq256b> + WideMul,
348 {
349 let (a1, b1) = (P::random(&mut rng), P::random(&mut rng));
353 let (a2, b2) = (P::random(&mut rng), P::random(&mut rng));
354
355 assert_eq!(P::reduce(P::wide_mul(a1, b1)), a1 * b1);
356 let deferred = P::reduce(P::wide_mul(a1, b1) + P::wide_mul(a2, b2));
357 assert_eq!(deferred, a1 * b1 + a2 * b2);
358 }
359
360 fn check_scalar_ops<P: PackedField<Scalar = GhashSq256b>>(mut rng: impl Rng) {
361 let a = P::random(&mut rng);
362 let s = GhashSq256b::random(&mut rng);
363
364 let broadcast = P::broadcast(s);
365 let scaled = a * s;
366 for i in 0..P::WIDTH {
367 assert_eq!(broadcast.get(i), s);
368 assert_eq!(scaled.get(i), a.get(i) * s);
369 }
370
371 assert_eq!(a * <P as FieldOps>::one(), a);
373 }
374
375 fn check_get_set_iter<P: PackedField<Scalar = GhashSq256b>>(mut rng: impl Rng) {
376 let mut a = P::random(&mut rng);
377 for i in 0..P::WIDTH {
378 let v = GhashSq256b::random(&mut rng);
379 a.set(i, v);
380 assert_eq!(a.get(i), v);
381 }
382
383 let scalars: Vec<_> = a.iter().collect();
385 assert_eq!(P::from_scalars(scalars.iter().copied()), a);
386 }
387
388 fn ref_interleave<S: Copy>(a: &[S], b: &[S], lbl: usize) -> (Vec<S>, Vec<S>) {
392 let s = 1usize << lbl;
393 let nb = a.len() / s;
394 let build = |x: usize| -> Vec<S> {
395 let mut out = Vec::with_capacity(a.len());
396 for t in 0..nb {
397 let (src, blk) = if t % 2 == 0 {
398 (a, t + x)
399 } else {
400 (b, t - 1 + x)
401 };
402 out.extend_from_slice(&src[blk * s..blk * s + s]);
403 }
404 out
405 };
406 (build(0), build(1))
407 }
408
409 fn ref_unzip<S: Copy>(a: &[S], b: &[S], lbl: usize) -> (Vec<S>, Vec<S>) {
413 let s = 1usize << lbl;
414 let nb = a.len() / s;
415 let block = |i: usize| -> &[S] {
416 if i < nb {
417 &a[i * s..i * s + s]
418 } else {
419 &b[(i - nb) * s..(i - nb) * s + s]
420 }
421 };
422 let (mut out_a, mut out_b) = (Vec::with_capacity(a.len()), Vec::with_capacity(a.len()));
423 for i in 0..2 * nb {
424 if i % 2 == 0 {
425 out_a.extend_from_slice(block(i));
426 } else {
427 out_b.extend_from_slice(block(i));
428 }
429 }
430 (out_a, out_b)
431 }
432
433 fn check_interleave_unzip<P: PackedField<Scalar = GhashSq256b>>(mut rng: impl Rng) {
434 let a = P::random(&mut rng);
435 let b = P::random(&mut rng);
436 let (sa, sb): (Vec<_>, Vec<_>) = (a.iter().collect(), b.iter().collect());
437
438 for log_block_len in 0..P::LOG_WIDTH {
439 let (c, d) = a.interleave(b, log_block_len);
440 let (ec, ed) = ref_interleave(&sa, &sb, log_block_len);
441 assert_eq!(c, P::from_scalars(ec));
442 assert_eq!(d, P::from_scalars(ed));
443
444 let (u, v) = a.unzip(b, log_block_len);
445 let (eu, ev) = ref_unzip(&sa, &sb, log_block_len);
446 assert_eq!(u, P::from_scalars(eu));
447 assert_eq!(v, P::from_scalars(ev));
448 }
449 }
450
451 macro_rules! width_tests {
452 ($mod:ident, $ty:ty) => {
453 mod $mod {
454 use super::*;
455
456 #[test]
457 fn arithmetic() {
458 for seed in 0..64 {
459 check_arithmetic::<$ty>(StdRng::seed_from_u64(seed));
460 }
461 }
462
463 #[test]
464 fn wide_mul() {
465 for seed in 0..64 {
466 check_wide_mul::<$ty>(StdRng::seed_from_u64(seed));
467 }
468 }
469
470 #[test]
471 fn scalar_ops() {
472 for seed in 0..64 {
473 check_scalar_ops::<$ty>(StdRng::seed_from_u64(seed));
474 }
475 }
476
477 #[test]
478 fn get_set_iter() {
479 for seed in 0..64 {
480 check_get_set_iter::<$ty>(StdRng::seed_from_u64(seed));
481 }
482 }
483
484 #[test]
485 fn interleave_unzip() {
486 for seed in 0..64 {
487 check_interleave_unzip::<$ty>(StdRng::seed_from_u64(seed));
488 }
489 }
490 }
491 };
492 }
493
494 width_tests!(width1, SlicedGhashSq1x256b);
495 width_tests!(width2, SlicedGhashSq2x256b);
496 width_tests!(width4, SlicedGhashSq4x256b);
497
498 width_tests!(packed_width1, PackedGhashSq1x256b);
501 width_tests!(packed_width2, PackedGhashSq2x256b);
502}