binius_field/packed_fields/
ghash_sq.rs1use std::{
28 iter::Sum,
29 ops::{Add, AddAssign, Sub, SubAssign},
30};
31
32use bytemuck::TransparentWrapper;
33
34use crate::{
35 Divisible, Ghash128b, GhashSq256b, PackedField, PackedGhash2x128b, WideMul,
36 arch::{Divide, GhashSqWideMul1x, M128, M256, M512, portable::packed_macros::*},
37 arithmetic_traits::{InvertOrZero, MulX, Square},
38 packed_extension,
39 packed_fields::{primitive::PackedPrimitiveType, sliced::SlicedPackedField},
40 underlier::Underlier,
41};
42
43type Ghash<U> = PackedPrimitiveType<U, Ghash128b>;
45
46pub type SlicedGhashSq256b<U> = SlicedPackedField<GhashSq256b, Ghash<U>, 2>;
48pub type SlicedGhashSq1x256b = SlicedGhashSq256b<M128>;
50pub type SlicedGhashSq2x256b = SlicedGhashSq256b<M256>;
52pub type SlicedGhashSq4x256b = SlicedGhashSq256b<M512>;
54
55type GhashWide<U> = <Ghash<U> as WideMul>::Output;
57
58#[derive(Clone, Copy, Debug, Default)]
65pub struct SlicedGhashSqWide<W> {
66 pub(crate) t0: W,
68 pub(crate) t2: W,
70 pub(crate) t1: W,
72}
73
74impl<W: Add<Output = W>> Add for SlicedGhashSqWide<W> {
75 type Output = Self;
76
77 #[inline]
78 fn add(self, rhs: Self) -> Self {
79 Self {
80 t0: self.t0 + rhs.t0,
81 t2: self.t2 + rhs.t2,
82 t1: self.t1 + rhs.t1,
83 }
84 }
85}
86
87impl<W: Sub<Output = W>> Sub for SlicedGhashSqWide<W> {
88 type Output = Self;
89
90 #[inline]
91 fn sub(self, rhs: Self) -> Self {
92 Self {
93 t0: self.t0 - rhs.t0,
94 t2: self.t2 - rhs.t2,
95 t1: self.t1 - rhs.t1,
96 }
97 }
98}
99
100impl<W: AddAssign> AddAssign for SlicedGhashSqWide<W> {
101 #[inline]
102 fn add_assign(&mut self, rhs: Self) {
103 self.t0 += rhs.t0;
104 self.t2 += rhs.t2;
105 self.t1 += rhs.t1;
106 }
107}
108
109impl<W: SubAssign> SubAssign for SlicedGhashSqWide<W> {
110 #[inline]
111 fn sub_assign(&mut self, rhs: Self) {
112 self.t0 -= rhs.t0;
113 self.t2 -= rhs.t2;
114 self.t1 -= rhs.t1;
115 }
116}
117
118impl<W: Default + Add<Output = W>> Sum for SlicedGhashSqWide<W> {
119 #[inline]
120 fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
121 iter.fold(Self::default(), |acc, x| acc + x)
122 }
123}
124
125impl<U> WideMul for SlicedGhashSq256b<U>
126where
127 U: Underlier,
128 Ghash<U>: PackedField<Scalar = Ghash128b> + WideMul + MulX,
129{
130 type Output = SlicedGhashSqWide<GhashWide<U>>;
131
132 #[inline]
134 fn wide_mul(lhs: Self, rhs: Self) -> Self::Output {
135 let [a, b] = lhs.to_coords();
136 let [e, f] = rhs.to_coords();
137
138 SlicedGhashSqWide {
139 t0: <Ghash<U> as WideMul>::wide_mul(a, e),
140 t2: <Ghash<U> as WideMul>::wide_mul(b, f),
141 t1: <Ghash<U> as WideMul>::wide_mul(a + b, e + f),
142 }
143 }
144
145 #[inline]
148 fn reduce(wide: Self::Output) -> Self {
149 let t0 = <Ghash<U> as WideMul>::reduce(wide.t0);
150 let t2 = <Ghash<U> as WideMul>::reduce(wide.t2);
151 let t1 = <Ghash<U> as WideMul>::reduce(wide.t1);
152
153 let z0 = t0 + t2.mul_x();
154 Self::from_coords([z0, z0 + t1 + t2])
155 }
156}
157
158impl<U> Square for SlicedGhashSq256b<U>
159where
160 U: Underlier,
161 Ghash<U>: PackedField<Scalar = Ghash128b> + MulX,
162{
163 #[inline]
166 fn square(self) -> Self {
167 let [a, b] = self.to_coords();
168
169 let t0 = Square::square(a);
170 let t2 = Square::square(b);
171
172 let x_t2 = t2.mul_x();
173 Self::from_coords([t0 + x_t2, x_t2])
174 }
175}
176
177impl<U> InvertOrZero for SlicedGhashSq256b<U>
178where
179 U: Underlier,
180 Ghash<U>: PackedField<Scalar = Ghash128b> + MulX,
181{
182 #[inline]
187 fn invert_or_zero(self) -> Self {
188 let [a, b] = self.to_coords();
189
190 let norm = Square::square(a) + (a * b + Square::square(b)).mul_x();
191 let norm_inv = norm.invert_or_zero();
192
193 Self::from_coords([(a + b.mul_x()) * norm_inv, b * norm_inv])
194 }
195}
196
197#[inline]
215pub(crate) fn ghash_sq_coords(elem: PackedGhashSq1x256b) -> [Ghash128b; 2] {
216 let coords = packed_extension::cast_base::<Ghash128b, _>(elem);
217 [coords.get(0), coords.get(1)]
218}
219
220#[inline]
222pub(crate) fn ghash_sq_from_coords(coords: [Ghash128b; 2]) -> PackedGhashSq1x256b {
223 packed_extension::cast_ext::<Ghash128b, _>(PackedGhash2x128b::from_scalars(coords))
224}
225
226#[repr(transparent)]
228#[derive(TransparentWrapper)]
229pub struct GhashSqSquare<T>(T);
230
231impl Square for GhashSqSquare<PackedGhashSq1x256b> {
232 #[inline]
234 fn square(self) -> Self {
235 let sq = Square::square(packed_extension::cast_base::<Ghash128b, _>(Self::peel(self)));
236
237 let x_t2 = sq.get(1).mul_x();
238 Self::wrap(ghash_sq_from_coords([sq.get(0) + x_t2, x_t2]))
239 }
240}
241
242#[repr(transparent)]
244#[derive(TransparentWrapper)]
245pub struct GhashSqInvert<T>(T);
246
247impl InvertOrZero for GhashSqInvert<PackedGhashSq1x256b> {
248 #[inline]
251 fn invert_or_zero(self) -> Self {
252 let [a, b] = ghash_sq_coords(Self::peel(self));
253
254 let norm = Square::square(a) + (a * b + Square::square(b)).mul_x();
255 let norm_inv = norm.invert_or_zero();
256
257 Self::wrap(ghash_sq_from_coords([(a + b.mul_x()) * norm_inv, b * norm_inv]))
258 }
259}
260
261type GhashSqDivide2x<T> = Divide<M256, T, 2>;
263
264define_packed_binary_field!(
265 PackedGhashSq1x256b,
266 GhashSq256b,
267 M256,
268 (GhashSqSquare),
269 (GhashSqInvert),
270 (GhashSqWideMul1x)
271);
272
273define_packed_binary_field!(
274 PackedGhashSq2x256b,
275 GhashSq256b,
276 M512,
277 (GhashSqDivide2x),
278 (GhashSqDivide2x),
279 (GhashSqDivide2x)
280);
281
282#[cfg(test)]
283mod tests {
284 use rand::{Rng, SeedableRng, rngs::StdRng};
285
286 use super::*;
287 use crate::{
288 Field, PackedField, Random,
289 arithmetic_traits::{InvertOrZero, Square},
290 field::FieldOps,
291 };
292
293 fn check_arithmetic<P: PackedField<Scalar = GhashSq256b>>(mut rng: impl Rng) {
297 let a = P::random(&mut rng);
298 let b = P::random(&mut rng);
299
300 let sum = a + b;
301 let diff = a - b;
302 let prod = a * b;
303 let sq = Square::square(a);
304 let inv = InvertOrZero::invert_or_zero(a);
305
306 for i in 0..P::WIDTH {
307 let (x, y) = (a.get(i), b.get(i));
308 assert_eq!(sum.get(i), x + y);
309 assert_eq!(diff.get(i), x - y);
310 assert_eq!(prod.get(i), x * y);
311 assert_eq!(sq.get(i), Square::square(x));
312 assert_eq!(inv.get(i), x.invert_or_zero());
313 if x != GhashSq256b::ZERO {
315 assert_eq!(x * inv.get(i), GhashSq256b::ONE);
316 }
317 }
318 }
319
320 fn check_wide_mul<P>(mut rng: impl Rng)
321 where
322 P: PackedField<Scalar = GhashSq256b> + WideMul,
323 {
324 let (a1, b1) = (P::random(&mut rng), P::random(&mut rng));
328 let (a2, b2) = (P::random(&mut rng), P::random(&mut rng));
329
330 assert_eq!(P::reduce(P::wide_mul(a1, b1)), a1 * b1);
331 let deferred = P::reduce(P::wide_mul(a1, b1) + P::wide_mul(a2, b2));
332 assert_eq!(deferred, a1 * b1 + a2 * b2);
333 }
334
335 fn check_scalar_ops<P: PackedField<Scalar = GhashSq256b>>(mut rng: impl Rng) {
336 let a = P::random(&mut rng);
337 let s = GhashSq256b::random(&mut rng);
338
339 let broadcast = P::broadcast(s);
340 let scaled = a * s;
341 for i in 0..P::WIDTH {
342 assert_eq!(broadcast.get(i), s);
343 assert_eq!(scaled.get(i), a.get(i) * s);
344 }
345
346 assert_eq!(a * <P as FieldOps>::one(), a);
348 }
349
350 fn check_get_set_iter<P: PackedField<Scalar = GhashSq256b>>(mut rng: impl Rng) {
351 let mut a = P::random(&mut rng);
352 for i in 0..P::WIDTH {
353 let v = GhashSq256b::random(&mut rng);
354 a.set(i, v);
355 assert_eq!(a.get(i), v);
356 }
357
358 let scalars: Vec<_> = a.iter().collect();
360 assert_eq!(P::from_scalars(scalars.iter().copied()), a);
361 }
362
363 fn ref_interleave<S: Copy>(a: &[S], b: &[S], lbl: usize) -> (Vec<S>, Vec<S>) {
367 let s = 1usize << lbl;
368 let nb = a.len() / s;
369 let build = |x: usize| -> Vec<S> {
370 let mut out = Vec::with_capacity(a.len());
371 for t in 0..nb {
372 let (src, blk) = if t % 2 == 0 {
373 (a, t + x)
374 } else {
375 (b, t - 1 + x)
376 };
377 out.extend_from_slice(&src[blk * s..blk * s + s]);
378 }
379 out
380 };
381 (build(0), build(1))
382 }
383
384 fn ref_unzip<S: Copy>(a: &[S], b: &[S], lbl: usize) -> (Vec<S>, Vec<S>) {
388 let s = 1usize << lbl;
389 let nb = a.len() / s;
390 let block = |i: usize| -> &[S] {
391 if i < nb {
392 &a[i * s..i * s + s]
393 } else {
394 &b[(i - nb) * s..(i - nb) * s + s]
395 }
396 };
397 let (mut out_a, mut out_b) = (Vec::with_capacity(a.len()), Vec::with_capacity(a.len()));
398 for i in 0..2 * nb {
399 if i % 2 == 0 {
400 out_a.extend_from_slice(block(i));
401 } else {
402 out_b.extend_from_slice(block(i));
403 }
404 }
405 (out_a, out_b)
406 }
407
408 fn check_interleave_unzip<P: PackedField<Scalar = GhashSq256b>>(mut rng: impl Rng) {
409 let a = P::random(&mut rng);
410 let b = P::random(&mut rng);
411 let (sa, sb): (Vec<_>, Vec<_>) = (a.iter().collect(), b.iter().collect());
412
413 for log_block_len in 0..P::LOG_WIDTH {
414 let (c, d) = a.interleave(b, log_block_len);
415 let (ec, ed) = ref_interleave(&sa, &sb, log_block_len);
416 assert_eq!(c, P::from_scalars(ec));
417 assert_eq!(d, P::from_scalars(ed));
418
419 let (u, v) = a.unzip(b, log_block_len);
420 let (eu, ev) = ref_unzip(&sa, &sb, log_block_len);
421 assert_eq!(u, P::from_scalars(eu));
422 assert_eq!(v, P::from_scalars(ev));
423 }
424 }
425
426 macro_rules! width_tests {
427 ($mod:ident, $ty:ty) => {
428 mod $mod {
429 use super::*;
430
431 #[test]
432 fn arithmetic() {
433 for seed in 0..64 {
434 check_arithmetic::<$ty>(StdRng::seed_from_u64(seed));
435 }
436 }
437
438 #[test]
439 fn wide_mul() {
440 for seed in 0..64 {
441 check_wide_mul::<$ty>(StdRng::seed_from_u64(seed));
442 }
443 }
444
445 #[test]
446 fn scalar_ops() {
447 for seed in 0..64 {
448 check_scalar_ops::<$ty>(StdRng::seed_from_u64(seed));
449 }
450 }
451
452 #[test]
453 fn get_set_iter() {
454 for seed in 0..64 {
455 check_get_set_iter::<$ty>(StdRng::seed_from_u64(seed));
456 }
457 }
458
459 #[test]
460 fn interleave_unzip() {
461 for seed in 0..64 {
462 check_interleave_unzip::<$ty>(StdRng::seed_from_u64(seed));
463 }
464 }
465 }
466 };
467 }
468
469 width_tests!(width1, SlicedGhashSq1x256b);
470 width_tests!(width2, SlicedGhashSq2x256b);
471 width_tests!(width4, SlicedGhashSq4x256b);
472
473 width_tests!(packed_width1, PackedGhashSq1x256b);
476 width_tests!(packed_width2, PackedGhashSq2x256b);
477}