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