1use crate::poly::{BinaryPoly64, BinaryPoly128, BinaryPoly256};
3use crate::elem::BinaryElem32;
4
5pub fn carryless_mul_64(a: BinaryPoly64, b: BinaryPoly64) -> BinaryPoly128 {
7 #[cfg(all(feature = "hardware-accel", target_arch = "x86_64", target_feature = "pclmulqdq"))]
9 {
10 use core::arch::x86_64::*;
11
12 unsafe {
13 let a_vec = _mm_set_epi64x(0, a.value() as i64);
14 let b_vec = _mm_set_epi64x(0, b.value() as i64);
15
16 let result = _mm_clmulepi64_si128(a_vec, b_vec, 0x00);
17
18 let lo = _mm_extract_epi64(result, 0) as u64;
19 let hi = _mm_extract_epi64(result, 1) as u64;
20
21 return BinaryPoly128::new(((hi as u128) << 64) | (lo as u128));
22 }
23 }
24
25 #[cfg(all(feature = "hardware-accel", target_arch = "wasm32", target_feature = "simd128"))]
27 {
28 return carryless_mul_64_wasm_simd(a, b);
29 }
30
31 #[cfg(not(any(
33 all(feature = "hardware-accel", target_arch = "x86_64", target_feature = "pclmulqdq"),
34 all(feature = "hardware-accel", target_arch = "wasm32", target_feature = "simd128")
35 )))]
36 {
37 carryless_mul_64_soft(a, b)
39 }
40}
41
42#[allow(dead_code)]
44fn carryless_mul_64_soft(a: BinaryPoly64, b: BinaryPoly64) -> BinaryPoly128 {
45 let mut result = 0u128;
46 let a_val = a.value();
47 let b_val = b.value();
48
49 for i in 0..64 {
50 let mask = 0u128.wrapping_sub(((b_val >> i) & 1) as u128);
51 result ^= ((a_val as u128) << i) & mask;
52 }
53
54 BinaryPoly128::new(result)
55}
56
57#[cfg(all(feature = "hardware-accel", target_arch = "wasm32", target_feature = "simd128"))]
59fn carryless_mul_64_wasm_simd(a: BinaryPoly64, b: BinaryPoly64) -> BinaryPoly128 {
60 unsafe {
61 let a_val = a.value();
62 let b_val = b.value();
63
64 let a_lo = (a_val & 0xFFFFFFFF) as u32;
66 let a_hi = (a_val >> 32) as u32;
67 let b_lo = (b_val & 0xFFFFFFFF) as u32;
68 let b_hi = (b_val >> 32) as u32;
69
70 let z0 = mul_32x32_to_64_simd(a_lo, b_lo);
72 let z2 = mul_32x32_to_64_simd(a_hi, b_hi);
73 let z1 = mul_32x32_to_64_simd(a_lo ^ a_hi, b_lo ^ b_hi);
74
75 let middle = z1 ^ z0 ^ z2;
77
78 let result_lo = z0 ^ (middle << 32);
80 let result_hi = (middle >> 32) ^ z2;
81
82 BinaryPoly128::new(((result_hi as u128) << 64) | (result_lo as u128))
83 }
84}
85
86#[cfg(all(feature = "hardware-accel", target_arch = "wasm32", target_feature = "simd128"))]
89#[inline(always)]
90unsafe fn mul_32x32_to_64_simd(a: u32, b: u32) -> u64 {
91 let mut result = 0u64;
93 let a64 = a as u64;
94
95 let nibble0 = b & 0xF;
98 if nibble0 & 1 != 0 { result ^= a64 << 0; }
99 if nibble0 & 2 != 0 { result ^= a64 << 1; }
100 if nibble0 & 4 != 0 { result ^= a64 << 2; }
101 if nibble0 & 8 != 0 { result ^= a64 << 3; }
102
103 let nibble1 = (b >> 4) & 0xF;
104 if nibble1 & 1 != 0 { result ^= a64 << 4; }
105 if nibble1 & 2 != 0 { result ^= a64 << 5; }
106 if nibble1 & 4 != 0 { result ^= a64 << 6; }
107 if nibble1 & 8 != 0 { result ^= a64 << 7; }
108
109 let nibble2 = (b >> 8) & 0xF;
110 if nibble2 & 1 != 0 { result ^= a64 << 8; }
111 if nibble2 & 2 != 0 { result ^= a64 << 9; }
112 if nibble2 & 4 != 0 { result ^= a64 << 10; }
113 if nibble2 & 8 != 0 { result ^= a64 << 11; }
114
115 let nibble3 = (b >> 12) & 0xF;
116 if nibble3 & 1 != 0 { result ^= a64 << 12; }
117 if nibble3 & 2 != 0 { result ^= a64 << 13; }
118 if nibble3 & 4 != 0 { result ^= a64 << 14; }
119 if nibble3 & 8 != 0 { result ^= a64 << 15; }
120
121 let nibble4 = (b >> 16) & 0xF;
122 if nibble4 & 1 != 0 { result ^= a64 << 16; }
123 if nibble4 & 2 != 0 { result ^= a64 << 17; }
124 if nibble4 & 4 != 0 { result ^= a64 << 18; }
125 if nibble4 & 8 != 0 { result ^= a64 << 19; }
126
127 let nibble5 = (b >> 20) & 0xF;
128 if nibble5 & 1 != 0 { result ^= a64 << 20; }
129 if nibble5 & 2 != 0 { result ^= a64 << 21; }
130 if nibble5 & 4 != 0 { result ^= a64 << 22; }
131 if nibble5 & 8 != 0 { result ^= a64 << 23; }
132
133 let nibble6 = (b >> 24) & 0xF;
134 if nibble6 & 1 != 0 { result ^= a64 << 24; }
135 if nibble6 & 2 != 0 { result ^= a64 << 25; }
136 if nibble6 & 4 != 0 { result ^= a64 << 26; }
137 if nibble6 & 8 != 0 { result ^= a64 << 27; }
138
139 let nibble7 = (b >> 28) & 0xF;
140 if nibble7 & 1 != 0 { result ^= a64 << 28; }
141 if nibble7 & 2 != 0 { result ^= a64 << 29; }
142 if nibble7 & 4 != 0 { result ^= a64 << 30; }
143 if nibble7 & 8 != 0 { result ^= a64 << 31; }
144
145 result
146}
147
148pub fn carryless_mul_128(a: BinaryPoly128, b: BinaryPoly128) -> BinaryPoly128 {
150 #[cfg(all(feature = "hardware-accel", target_arch = "x86_64", target_feature = "pclmulqdq"))]
151 {
152 use core::arch::x86_64::*;
153
154 unsafe {
155 let a_lo = a.value() as u64;
157 let a_hi = (a.value() >> 64) as u64;
158 let b_lo = b.value() as u64;
159 let b_hi = (b.value() >> 64) as u64;
160
161 let lo_lo = _mm_clmulepi64_si128(
163 _mm_set_epi64x(0, a_lo as i64),
164 _mm_set_epi64x(0, b_lo as i64),
165 0x00
166 );
167
168 let lo_hi = _mm_clmulepi64_si128(
169 _mm_set_epi64x(0, a_lo as i64),
170 _mm_set_epi64x(0, b_hi as i64),
171 0x00
172 );
173
174 let hi_lo = _mm_clmulepi64_si128(
175 _mm_set_epi64x(0, a_hi as i64),
176 _mm_set_epi64x(0, b_lo as i64),
177 0x00
178 );
179
180 let r0 = (_mm_extract_epi64(lo_lo, 0) as u64) as u128
182 | ((_mm_extract_epi64(lo_lo, 1) as u64) as u128) << 64;
183 let r1 = (_mm_extract_epi64(lo_hi, 0) as u64) as u128
184 | ((_mm_extract_epi64(lo_hi, 1) as u64) as u128) << 64;
185 let r2 = (_mm_extract_epi64(hi_lo, 0) as u64) as u128
186 | ((_mm_extract_epi64(hi_lo, 1) as u64) as u128) << 64;
187
188 let result = r0 ^ (r1 << 64) ^ (r2 << 64);
190
191 return BinaryPoly128::new(result);
192 }
193 }
194
195 #[cfg(not(all(feature = "hardware-accel", target_arch = "x86_64", target_feature = "pclmulqdq")))]
196 {
197 carryless_mul_128_soft(a, b)
199 }
200}
201
202#[allow(dead_code)]
204fn carryless_mul_128_soft(a: BinaryPoly128, b: BinaryPoly128) -> BinaryPoly128 {
205 let a_lo = a.value() as u64;
206 let a_hi = (a.value() >> 64) as u64;
207 let b_lo = b.value() as u64;
208 let b_hi = (b.value() >> 64) as u64;
209
210 let z0 = mul_64x64_to_128(a_lo, b_lo);
211 let z1 = mul_64x64_to_128(a_lo ^ a_hi, b_lo ^ b_hi);
212 let z2 = mul_64x64_to_128(a_hi, b_hi);
213
214 let result = z0 ^ (z1 << 64) ^ (z0 << 64) ^ (z2 << 64);
216 BinaryPoly128::new(result)
217}
218
219pub fn carryless_mul_128_full(a: BinaryPoly128, b: BinaryPoly128) -> BinaryPoly256 {
221 #[cfg(all(feature = "hardware-accel", target_arch = "x86_64", target_feature = "pclmulqdq"))]
222 {
223 use core::arch::x86_64::*;
224
225 unsafe {
226 let a_lo = a.value() as u64;
227 let a_hi = (a.value() >> 64) as u64;
228 let b_lo = b.value() as u64;
229 let b_hi = (b.value() >> 64) as u64;
230
231 let lo_lo = _mm_clmulepi64_si128(
233 _mm_set_epi64x(0, a_lo as i64),
234 _mm_set_epi64x(0, b_lo as i64),
235 0x00
236 );
237
238 let lo_hi = _mm_clmulepi64_si128(
239 _mm_set_epi64x(0, a_lo as i64),
240 _mm_set_epi64x(0, b_hi as i64),
241 0x00
242 );
243
244 let hi_lo = _mm_clmulepi64_si128(
245 _mm_set_epi64x(0, a_hi as i64),
246 _mm_set_epi64x(0, b_lo as i64),
247 0x00
248 );
249
250 let hi_hi = _mm_clmulepi64_si128(
251 _mm_set_epi64x(0, a_hi as i64),
252 _mm_set_epi64x(0, b_hi as i64),
253 0x00
254 );
255
256 let r0_lo = _mm_extract_epi64(lo_lo, 0) as u64;
258 let r0_hi = _mm_extract_epi64(lo_lo, 1) as u64;
259 let r1_lo = _mm_extract_epi64(lo_hi, 0) as u64;
260 let r1_hi = _mm_extract_epi64(lo_hi, 1) as u64;
261 let r2_lo = _mm_extract_epi64(hi_lo, 0) as u64;
262 let r2_hi = _mm_extract_epi64(hi_lo, 1) as u64;
263 let r3_lo = _mm_extract_epi64(hi_hi, 0) as u64;
264 let r3_hi = _mm_extract_epi64(hi_hi, 1) as u64;
265
266 let mut lo = r0_lo as u128 | ((r0_hi as u128) << 64);
268 let mut hi = 0u128;
269
270 lo ^= (r1_lo as u128) << 64;
272 hi ^= (r1_lo as u128) >> 64;
273 hi ^= r1_hi as u128;
274
275 lo ^= (r2_lo as u128) << 64;
277 hi ^= (r2_lo as u128) >> 64;
278 hi ^= r2_hi as u128;
279
280 hi ^= r3_lo as u128 | ((r3_hi as u128) << 64);
282
283 return BinaryPoly256::from_parts(hi, lo);
284 }
285 }
286
287 #[cfg(not(all(feature = "hardware-accel", target_arch = "x86_64", target_feature = "pclmulqdq")))]
288 {
289 carryless_mul_128_full_soft(a, b)
291 }
292}
293
294#[allow(dead_code)]
296fn carryless_mul_128_full_soft(a: BinaryPoly128, b: BinaryPoly128) -> BinaryPoly256 {
297 let a_lo = a.value() as u64;
298 let a_hi = (a.value() >> 64) as u64;
299 let b_lo = b.value() as u64;
300 let b_hi = (b.value() >> 64) as u64;
301
302 let z0 = mul_64x64_to_128(a_lo, b_lo);
303 let z2 = mul_64x64_to_128(a_hi, b_hi);
304 let z1 = mul_64x64_to_128(a_lo ^ a_hi, b_lo ^ b_hi) ^ z0 ^ z2;
305
306 let mut lo = z0;
308 let mut hi = 0u128;
309
310 lo ^= z1 << 64;
312 hi ^= z1 >> 64;
313
314 hi ^= z2;
316
317 BinaryPoly256::from_parts(hi, lo)
318}
319
320#[inline(always)]
322#[allow(dead_code)]
323fn mul_64x64_to_128(a: u64, b: u64) -> u128 {
324 let mut result = 0u128;
325 let mut a_shifted = a as u128;
326
327 for i in 0..64 {
328 let mask = 0u128.wrapping_sub(((b >> i) & 1) as u128);
329 result ^= a_shifted & mask;
330 a_shifted <<= 1;
331 }
332
333 result
334}
335
336use crate::{BinaryElem128, BinaryFieldElement};
339
340pub fn batch_mul_gf128(a: &[BinaryElem128], b: &[BinaryElem128], out: &mut [BinaryElem128]) {
343 assert_eq!(a.len(), b.len());
344 assert_eq!(a.len(), out.len());
345
346 #[cfg(all(feature = "hardware-accel", target_arch = "x86_64", target_feature = "pclmulqdq"))]
347 {
348 return batch_mul_gf128_hw(a, b, out);
349 }
350
351 #[cfg(not(all(feature = "hardware-accel", target_arch = "x86_64", target_feature = "pclmulqdq")))]
352 {
353 for i in 0..a.len() {
355 out[i] = a[i].mul(&b[i]);
356 }
357 }
358}
359
360pub fn batch_add_gf128(a: &[BinaryElem128], b: &[BinaryElem128], out: &mut [BinaryElem128]) {
362 assert_eq!(a.len(), b.len());
363 assert_eq!(a.len(), out.len());
364
365 for i in 0..a.len() {
367 out[i] = a[i].add(&b[i]);
368 }
369}
370
371#[cfg(all(feature = "hardware-accel", target_arch = "x86_64", target_feature = "pclmulqdq"))]
373fn batch_mul_gf128_hw(a: &[BinaryElem128], b: &[BinaryElem128], out: &mut [BinaryElem128]) {
374 for i in 0..a.len() {
375 let a_poly = a[i].poly();
376 let b_poly = b[i].poly();
377 let product = carryless_mul_128_full(a_poly, b_poly);
378 let reduced = reduce_gf128(product);
379 out[i] = BinaryElem128::from_value(reduced.value());
380 }
381}
382
383#[inline(always)]
387pub fn reduce_gf128(product: BinaryPoly256) -> BinaryPoly128 {
388 let (hi, lo) = product.split();
389 let high = hi.value();
390 let low = lo.value();
391
392 let tmp = high ^ (high >> 127) ^ (high >> 126) ^ (high >> 121);
396
397 let res = low ^ tmp ^ (tmp << 1) ^ (tmp << 2) ^ (tmp << 7);
401
402 BinaryPoly128::new(res)
403}
404
405#[cfg(all(feature = "hardware-accel", target_arch = "x86_64", target_feature = "pclmulqdq"))]
412pub fn fft_butterfly_gf32_avx512(u: &mut [BinaryElem32], w: &mut [BinaryElem32], lambda: BinaryElem32) {
413 #[cfg(target_arch = "x86_64")]
416 {
417 return fft_butterfly_gf32_sse(u, w, lambda);
420 }
421
422 #[cfg(not(target_arch = "x86_64"))]
423 {
424 fft_butterfly_gf32_scalar(u, w, lambda)
425 }
426}
427
428#[cfg(all(feature = "hardware-accel", target_arch = "x86_64", target_feature = "pclmulqdq"))]
430#[target_feature(enable = "avx512f,vpclmulqdq")]
431#[allow(dead_code)]
432unsafe fn fft_butterfly_gf32_avx512_impl(u: &mut [BinaryElem32], w: &mut [BinaryElem32], lambda: BinaryElem32) {
433 use core::arch::x86_64::*;
434
435 assert_eq!(u.len(), w.len());
436 let len = u.len();
437
438 const IRREDUCIBLE_32: u64 = (1u64 << 32) | 0b11001 | (1 << 7) | (1 << 9) | (1 << 15);
439
440 let lambda_val = lambda.poly().value() as u64;
441 let lambda_vec = _mm256_set1_epi64x(lambda_val as i64);
442
443 let mut i = 0;
444
445 while i + 4 <= len {
447 let w0 = w[i].poly().value() as u64;
449 let w1 = w[i+1].poly().value() as u64;
450 let w2 = w[i+2].poly().value() as u64;
451 let w3 = w[i+3].poly().value() as u64;
452
453 let w_vec = _mm256_set_epi64x(w3 as i64, w2 as i64, w1 as i64, w0 as i64);
454
455 let prod = _mm256_clmulepi64_epi128(lambda_vec, w_vec, 0x00);
457
458 let p0 = _mm256_extract_epi64(prod, 0) as u64;
460 let p1 = _mm256_extract_epi64(prod, 1) as u64;
461 let p2 = _mm256_extract_epi64(prod, 2) as u64;
462 let p3 = _mm256_extract_epi64(prod, 3) as u64;
463
464 let lambda_w0 = reduce_gf32(p0, IRREDUCIBLE_32);
465 let lambda_w1 = reduce_gf32(p1, IRREDUCIBLE_32);
466 let lambda_w2 = reduce_gf32(p2, IRREDUCIBLE_32);
467 let lambda_w3 = reduce_gf32(p3, IRREDUCIBLE_32);
468
469 let u0 = u[i].poly().value() ^ (lambda_w0 as u32);
471 let u1 = u[i+1].poly().value() ^ (lambda_w1 as u32);
472 let u2 = u[i+2].poly().value() ^ (lambda_w2 as u32);
473 let u3 = u[i+3].poly().value() ^ (lambda_w3 as u32);
474
475 let w0_new = w[i].poly().value() ^ u0;
477 let w1_new = w[i+1].poly().value() ^ u1;
478 let w2_new = w[i+2].poly().value() ^ u2;
479 let w3_new = w[i+3].poly().value() ^ u3;
480
481 u[i] = BinaryElem32::from(u0);
482 u[i+1] = BinaryElem32::from(u1);
483 u[i+2] = BinaryElem32::from(u2);
484 u[i+3] = BinaryElem32::from(u3);
485 w[i] = BinaryElem32::from(w0_new);
486 w[i+1] = BinaryElem32::from(w1_new);
487 w[i+2] = BinaryElem32::from(w2_new);
488 w[i+3] = BinaryElem32::from(w3_new);
489
490 i += 4;
491 }
492
493 while i < len {
495 let lambda_w = lambda.mul(&w[i]);
496 u[i] = u[i].add(&lambda_w);
497 w[i] = w[i].add(&u[i]);
498 i += 1;
499 }
500}
501
502#[cfg(all(feature = "hardware-accel", target_arch = "x86_64", target_feature = "pclmulqdq"))]
506pub fn fft_butterfly_gf32_sse(u: &mut [BinaryElem32], w: &mut [BinaryElem32], lambda: BinaryElem32) {
507 use core::arch::x86_64::*;
508
509 assert_eq!(u.len(), w.len());
510 let len = u.len();
511
512 const IRREDUCIBLE_32: u64 = (1u64 << 32) | 0b11001 | (1 << 7) | (1 << 9) | (1 << 15);
515
516 unsafe {
517 let lambda_val = lambda.poly().value() as u64;
518 let lambda_vec = _mm_set1_epi64x(lambda_val as i64);
519
520 let mut i = 0;
521
522 while i + 2 <= len {
524 let w0 = w[i].poly().value() as u64;
526 let w1 = w[i+1].poly().value() as u64;
527 let w_vec = _mm_set_epi64x(w1 as i64, w0 as i64);
528
529 let prod_lo = _mm_clmulepi64_si128(lambda_vec, w_vec, 0x00); let prod_hi = _mm_clmulepi64_si128(lambda_vec, w_vec, 0x11); let p0 = _mm_extract_epi64(prod_lo, 0) as u64;
535 let p1 = _mm_extract_epi64(prod_hi, 0) as u64;
536
537 let lambda_w0 = reduce_gf32(p0, IRREDUCIBLE_32);
538 let lambda_w1 = reduce_gf32(p1, IRREDUCIBLE_32);
539
540 let u0 = u[i].poly().value() ^ (lambda_w0 as u32);
542 let u1 = u[i+1].poly().value() ^ (lambda_w1 as u32);
543
544 let w0_new = w[i].poly().value() ^ u0;
546 let w1_new = w[i+1].poly().value() ^ u1;
547
548 u[i] = BinaryElem32::from(u0);
549 u[i+1] = BinaryElem32::from(u1);
550 w[i] = BinaryElem32::from(w0_new);
551 w[i+1] = BinaryElem32::from(w1_new);
552
553 i += 2;
554 }
555
556 if i < len {
558 let lambda_w = lambda.mul(&w[i]);
559 u[i] = u[i].add(&lambda_w);
560 w[i] = w[i].add(&u[i]);
561 }
562 }
563}
564
565#[inline(always)]
568fn reduce_gf32(p: u64, _irr: u64) -> u64 {
569 let hi = (p >> 32) as u64;
573 let lo = (p & 0xFFFFFFFF) as u64;
574
575 let tmp = hi
579 ^ (hi >> 29) ^ (hi >> 25) ^ (hi >> 23) ^ (hi >> 17); lo ^ tmp ^ (tmp << 3) ^ (tmp << 7) ^ (tmp << 9) ^ (tmp << 15)
586}
587
588pub fn fft_butterfly_gf32_scalar(u: &mut [BinaryElem32], w: &mut [BinaryElem32], lambda: BinaryElem32) {
590 assert_eq!(u.len(), w.len());
591
592 for i in 0..u.len() {
593 let lambda_w = lambda.mul(&w[i]);
594 u[i] = u[i].add(&lambda_w);
595 w[i] = w[i].add(&u[i]);
596 }
597}
598
599pub fn fft_butterfly_gf32(u: &mut [BinaryElem32], w: &mut [BinaryElem32], lambda: BinaryElem32) {
601 #[cfg(all(feature = "hardware-accel", target_arch = "x86_64", target_feature = "pclmulqdq"))]
602 {
603 return fft_butterfly_gf32_avx512(u, w, lambda);
605 }
606
607 #[cfg(not(all(feature = "hardware-accel", target_arch = "x86_64", target_feature = "pclmulqdq")))]
608 {
609 fft_butterfly_gf32_scalar(u, w, lambda)
610 }
611}
612
613#[cfg(test)]
614mod tests {
615 use super::*;
616
617 #[test]
618 fn test_fft_butterfly_gf32() {
619 let mut u_simd = vec![
621 BinaryElem32::from(1),
622 BinaryElem32::from(2),
623 BinaryElem32::from(3),
624 BinaryElem32::from(4),
625 ];
626 let mut w_simd = vec![
627 BinaryElem32::from(5),
628 BinaryElem32::from(6),
629 BinaryElem32::from(7),
630 BinaryElem32::from(8),
631 ];
632 let lambda = BinaryElem32::from(3);
633
634 let mut u_scalar = u_simd.clone();
635 let mut w_scalar = w_simd.clone();
636
637 fft_butterfly_gf32(&mut u_simd, &mut w_simd, lambda);
638 fft_butterfly_gf32_scalar(&mut u_scalar, &mut w_scalar, lambda);
639
640 for i in 0..u_simd.len() {
641 assert_eq!(u_simd[i], u_scalar[i], "u mismatch at index {}", i);
642 assert_eq!(w_simd[i], w_scalar[i], "w mismatch at index {}", i);
643 }
644 }
645
646 #[test]
647 fn test_batch_add() {
648 let a = vec![
649 BinaryElem128::from(1),
650 BinaryElem128::from(2),
651 BinaryElem128::from(3),
652 ];
653 let b = vec![
654 BinaryElem128::from(4),
655 BinaryElem128::from(5),
656 BinaryElem128::from(6),
657 ];
658 let mut out = vec![BinaryElem128::zero(); 3];
659
660 batch_add_gf128(&a, &b, &mut out);
661
662 for i in 0..3 {
663 assert_eq!(out[i], a[i].add(&b[i]));
664 }
665 }
666
667 #[test]
668 fn test_batch_mul() {
669 let a = vec![
670 BinaryElem128::from(7),
671 BinaryElem128::from(11),
672 BinaryElem128::from(13),
673 ];
674 let b = vec![
675 BinaryElem128::from(3),
676 BinaryElem128::from(5),
677 BinaryElem128::from(7),
678 ];
679 let mut out = vec![BinaryElem128::zero(); 3];
680
681 batch_mul_gf128(&a, &b, &mut out);
682
683 for i in 0..3 {
684 assert_eq!(out[i], a[i].mul(&b[i]));
685 }
686 }
687
688 #[test]
689 fn test_batch_mul_large() {
690 let a = vec![
692 BinaryElem128::from(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0),
693 BinaryElem128::from(u128::MAX),
694 ];
695 let b = vec![
696 BinaryElem128::from(0x123456789ABCDEF0123456789ABCDEF0),
697 BinaryElem128::from(0x8000000000000000_0000000000000000),
698 ];
699 let mut out = vec![BinaryElem128::zero(); 2];
700
701 batch_mul_gf128(&a, &b, &mut out);
702
703 for i in 0..2 {
704 assert_eq!(out[i], a[i].mul(&b[i]));
705 }
706 }
707}