1use crate::galois_8;
7use core::ops::{Add, Div, Mul, Sub};
8
9const EXT_POLY: [u8; 3] = [1, 2, 128];
15
16#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
18pub struct Field;
19
20impl crate::Field for Field {
21 const ORDER: usize = 65536;
22
23 type Elem = [u8; 2];
24
25 fn add(a: [u8; 2], b: [u8; 2]) -> [u8; 2] {
26 (Element(a) + Element(b)).0
27 }
28
29 fn mul(a: [u8; 2], b: [u8; 2]) -> [u8; 2] {
30 (Element(a) * Element(b)).0
31 }
32
33 fn div(a: [u8; 2], b: [u8; 2]) -> [u8; 2] {
34 (Element(a) / Element(b)).0
35 }
36
37 fn exp(elem: [u8; 2], n: usize) -> [u8; 2] {
38 Element(elem).exp(n).0
39 }
40
41 fn zero() -> [u8; 2] {
42 [0; 2]
43 }
44
45 fn one() -> [u8; 2] {
46 [0, 1]
47 }
48
49 fn nth_internal(n: usize) -> [u8; 2] {
50 [(n >> 8) as u8, n as u8]
51 }
52
53 fn mul_slice(elem: [u8; 2], input: &[[u8; 2]], out: &mut [[u8; 2]]) {
54 gf16_mul_slice(elem, input, out, false);
55 }
56
57 fn mul_slice_add(elem: [u8; 2], input: &[[u8; 2]], out: &mut [[u8; 2]]) {
58 gf16_mul_slice(elem, input, out, true);
59 }
60}
61
62fn gf16_mul_slice(elem: [u8; 2], input: &[[u8; 2]], out: &mut [[u8; 2]], accumulate: bool) {
83 assert_eq!(input.len(), out.len());
84
85 let cx = elem[0];
86 let cc = elem[1];
87 let coef_a = cc ^ galois_8::mul(2, cx);
88 let coef_b = cx;
89 let coef_c = galois_8::mul(128, cx);
90 let coef_d = cc;
91
92 const CHUNK: usize = 1024;
93 let mut ax = [0u8; CHUNK];
94 let mut ac = [0u8; CHUNK];
95 let mut ox = [0u8; CHUNK];
96 let mut oc = [0u8; CHUNK];
97
98 let mut offset = 0;
99 while offset < input.len() {
100 let n = core::cmp::min(CHUNK, input.len() - offset);
101
102 deinterleave(
106 input[offset..offset + n].as_flattened(),
107 &mut ax[..n],
108 &mut ac[..n],
109 );
110
111 if accumulate {
112 deinterleave(
113 out[offset..offset + n].as_flattened(),
114 &mut ox[..n],
115 &mut oc[..n],
116 );
117 galois_8::mul_slice_xor(coef_a, &ax[..n], &mut ox[..n]);
118 galois_8::mul_slice_xor(coef_b, &ac[..n], &mut ox[..n]);
119 galois_8::mul_slice_xor(coef_c, &ax[..n], &mut oc[..n]);
120 galois_8::mul_slice_xor(coef_d, &ac[..n], &mut oc[..n]);
121 } else {
122 galois_8::mul_slice(coef_a, &ax[..n], &mut ox[..n]);
123 galois_8::mul_slice_xor(coef_b, &ac[..n], &mut ox[..n]);
124 galois_8::mul_slice(coef_c, &ax[..n], &mut oc[..n]);
125 galois_8::mul_slice_xor(coef_d, &ac[..n], &mut oc[..n]);
126 }
127
128 interleave(
130 &ox[..n],
131 &oc[..n],
132 out[offset..offset + n].as_flattened_mut(),
133 );
134 offset += n;
135 }
136}
137
138#[allow(clippy::needless_return)]
146#[inline]
147fn deinterleave(src: &[u8], even: &mut [u8], odd: &mut [u8]) {
148 debug_assert_eq!(src.len(), even.len() * 2);
149 debug_assert_eq!(even.len(), odd.len());
150
151 #[cfg(all(feature = "std", target_arch = "x86_64"))]
152 {
153 if is_x86_feature_detected!("ssse3") {
156 unsafe {
160 deinterleave_ssse3(src, even, odd);
161 return;
162 }
163 }
164 }
165 #[cfg(target_arch = "aarch64")]
166 {
167 unsafe {
171 deinterleave_neon(src, even, odd);
172 return;
173 }
174 }
175 #[cfg(not(target_arch = "aarch64"))]
176 deinterleave_scalar(src, even, odd);
177}
178
179#[allow(clippy::needless_return)]
184#[inline]
185fn interleave(even: &[u8], odd: &[u8], dst: &mut [u8]) {
186 debug_assert_eq!(dst.len(), even.len() * 2);
187 debug_assert_eq!(even.len(), odd.len());
188
189 #[cfg(all(feature = "std", target_arch = "x86_64"))]
190 {
191 if is_x86_feature_detected!("ssse3") {
192 unsafe {
195 interleave_ssse3(even, odd, dst);
196 return;
197 }
198 }
199 }
200 #[cfg(target_arch = "aarch64")]
201 {
202 unsafe {
205 interleave_neon(even, odd, dst);
206 return;
207 }
208 }
209 #[cfg(not(target_arch = "aarch64"))]
210 interleave_scalar(even, odd, dst);
211}
212
213fn deinterleave_scalar(src: &[u8], even: &mut [u8], odd: &mut [u8]) {
214 for (i, (e, o)) in even.iter_mut().zip(odd.iter_mut()).enumerate() {
215 *e = src[2 * i];
216 *o = src[2 * i + 1];
217 }
218}
219
220fn interleave_scalar(even: &[u8], odd: &[u8], dst: &mut [u8]) {
221 for (i, (e, o)) in even.iter().zip(odd.iter()).enumerate() {
222 dst[2 * i] = *e;
223 dst[2 * i + 1] = *o;
224 }
225}
226
227#[cfg(target_arch = "x86_64")]
228#[target_feature(enable = "ssse3")]
229unsafe fn deinterleave_ssse3(src: &[u8], even: &mut [u8], odd: &mut [u8]) {
230 use core::arch::x86_64::{_mm_loadu_si128, _mm_shuffle_epi8, _mm_storel_epi64};
231
232 #[rustfmt::skip]
234 let even_mask = unsafe { _mm_loadu_si128([
236 0u8, 2, 4, 6, 8, 10, 12, 14, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
237 ].as_ptr().cast()) };
238 #[rustfmt::skip]
239 let odd_mask = unsafe { _mm_loadu_si128([
241 1u8, 3, 5, 7, 9, 11, 13, 15, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
242 ].as_ptr().cast()) };
243
244 let batches = even.len() / 32;
245 for b in 0..batches {
246 let s = &src[b * 64..b * 64 + 64];
247 let e = &mut even[b * 32..b * 32 + 32];
248 let o = &mut odd[b * 32..b * 32 + 32];
249 unsafe {
253 let p0 = _mm_loadu_si128(s.as_ptr().cast());
254 let p1 = _mm_loadu_si128(s[16..].as_ptr().cast());
255 let p2 = _mm_loadu_si128(s[32..].as_ptr().cast());
256 let p3 = _mm_loadu_si128(s[48..].as_ptr().cast());
257 _mm_storel_epi64(e.as_mut_ptr().cast(), _mm_shuffle_epi8(p0, even_mask));
258 _mm_storel_epi64(e[8..].as_mut_ptr().cast(), _mm_shuffle_epi8(p1, even_mask));
259 _mm_storel_epi64(e[16..].as_mut_ptr().cast(), _mm_shuffle_epi8(p2, even_mask));
260 _mm_storel_epi64(e[24..].as_mut_ptr().cast(), _mm_shuffle_epi8(p3, even_mask));
261 _mm_storel_epi64(o.as_mut_ptr().cast(), _mm_shuffle_epi8(p0, odd_mask));
262 _mm_storel_epi64(o[8..].as_mut_ptr().cast(), _mm_shuffle_epi8(p1, odd_mask));
263 _mm_storel_epi64(o[16..].as_mut_ptr().cast(), _mm_shuffle_epi8(p2, odd_mask));
264 _mm_storel_epi64(o[24..].as_mut_ptr().cast(), _mm_shuffle_epi8(p3, odd_mask));
265 }
266 }
267 let done = batches * 32;
268 deinterleave_scalar(&src[done * 2..], &mut even[done..], &mut odd[done..]);
269}
270
271#[cfg(target_arch = "x86_64")]
272#[target_feature(enable = "ssse3")]
273unsafe fn interleave_ssse3(even: &[u8], odd: &[u8], dst: &mut [u8]) {
274 use core::arch::x86_64::{
275 _mm_loadu_si128, _mm_storeu_si128, _mm_unpackhi_epi8, _mm_unpacklo_epi8,
276 };
277
278 let batches = even.len() / 32;
279 for b in 0..batches {
280 let e = &even[b * 32..b * 32 + 32];
281 let o = &odd[b * 32..b * 32 + 32];
282 let d = &mut dst[b * 64..b * 64 + 64];
283 unsafe {
287 let lo = _mm_loadu_si128(e.as_ptr().cast());
288 let hi = _mm_loadu_si128(o.as_ptr().cast());
289 _mm_storeu_si128(d.as_mut_ptr().cast(), _mm_unpacklo_epi8(lo, hi));
290 _mm_storeu_si128(d[16..].as_mut_ptr().cast(), _mm_unpackhi_epi8(lo, hi));
291 let lo2 = _mm_loadu_si128(e[16..].as_ptr().cast());
292 let hi2 = _mm_loadu_si128(o[16..].as_ptr().cast());
293 _mm_storeu_si128(d[32..].as_mut_ptr().cast(), _mm_unpacklo_epi8(lo2, hi2));
294 _mm_storeu_si128(d[48..].as_mut_ptr().cast(), _mm_unpackhi_epi8(lo2, hi2));
295 }
296 }
297 let done = batches * 32;
298 interleave_scalar(&even[done..], &odd[done..], &mut dst[done * 2..]);
299}
300
301#[cfg(target_arch = "aarch64")]
302#[target_feature(enable = "neon")]
303unsafe fn deinterleave_neon(src: &[u8], even: &mut [u8], odd: &mut [u8]) {
304 use core::arch::aarch64::{vld1q_u8, vst1q_u8, vuzp1q_u8, vuzp2q_u8};
305
306 let batches = even.len() / 32;
307 for b in 0..batches {
308 let s = &src[b * 64..b * 64 + 64];
309 let e = &mut even[b * 32..b * 32 + 32];
310 let o = &mut odd[b * 32..b * 32 + 32];
311 unsafe {
316 let p0 = vld1q_u8(s.as_ptr());
317 let p1 = vld1q_u8(s[16..].as_ptr());
318 vst1q_u8(e.as_mut_ptr(), vuzp1q_u8(p0, p1));
319 vst1q_u8(o.as_mut_ptr(), vuzp2q_u8(p0, p1));
320 let p2 = vld1q_u8(s[32..].as_ptr());
321 let p3 = vld1q_u8(s[48..].as_ptr());
322 vst1q_u8(e[16..].as_mut_ptr(), vuzp1q_u8(p2, p3));
323 vst1q_u8(o[16..].as_mut_ptr(), vuzp2q_u8(p2, p3));
324 }
325 }
326 let done = batches * 32;
327 deinterleave_scalar(&src[done * 2..], &mut even[done..], &mut odd[done..]);
328}
329
330#[cfg(target_arch = "aarch64")]
331#[target_feature(enable = "neon")]
332unsafe fn interleave_neon(even: &[u8], odd: &[u8], dst: &mut [u8]) {
333 use core::arch::aarch64::{vld1q_u8, vst1q_u8, vzip1q_u8, vzip2q_u8};
334
335 let batches = even.len() / 32;
336 for b in 0..batches {
337 let e = &even[b * 32..b * 32 + 32];
338 let o = &odd[b * 32..b * 32 + 32];
339 let d = &mut dst[b * 64..b * 64 + 64];
340 unsafe {
345 let lo = vld1q_u8(e.as_ptr());
346 let hi = vld1q_u8(o.as_ptr());
347 vst1q_u8(d.as_mut_ptr(), vzip1q_u8(lo, hi));
348 vst1q_u8(d[16..].as_mut_ptr(), vzip2q_u8(lo, hi));
349 let lo2 = vld1q_u8(e[16..].as_ptr());
350 let hi2 = vld1q_u8(o[16..].as_ptr());
351 vst1q_u8(d[32..].as_mut_ptr(), vzip1q_u8(lo2, hi2));
352 vst1q_u8(d[48..].as_mut_ptr(), vzip2q_u8(lo2, hi2));
353 }
354 }
355 let done = batches * 32;
356 interleave_scalar(&even[done..], &odd[done..], &mut dst[done * 2..]);
357}
358
359pub type ReedSolomon = crate::ReedSolomon<Field>;
361
362pub type ShardByShard<'a> = crate::ShardByShard<'a, Field>;
364
365#[derive(Debug, Copy, Clone, PartialEq, Eq)]
367struct Element(pub [u8; 2]);
368
369impl Element {
370 fn zero() -> Self {
372 Element([0, 0])
373 }
374
375 fn constant(n: u8) -> Element {
377 Element([0, n])
378 }
379
380 fn is_zero(&self) -> bool {
382 self.0 == [0; 2]
383 }
384
385 fn exp(mut self, n: usize) -> Element {
386 if n == 0 {
387 Element::constant(1)
388 } else if self == Element::zero() {
389 Element::zero()
390 } else {
391 let x = self;
392 for _ in 1..n {
393 self = self * x;
394 }
395
396 self
397 }
398 }
399
400 #[inline]
402 fn reduce_from(mut x: [u8; 3]) -> Self {
403 if x[0] != 0 {
404 x[1] ^= galois_8::mul(EXT_POLY[1], x[0]);
408 x[2] ^= galois_8::mul(EXT_POLY[2], x[0]);
409 }
410
411 Element([x[1], x[2]])
412 }
413
414 fn degree(&self) -> usize {
415 if self.0[0] != 0 { 1 } else { 0 }
416 }
417}
418
419impl From<[u8; 2]> for Element {
420 fn from(c: [u8; 2]) -> Self {
421 Element(c)
422 }
423}
424
425impl Default for Element {
426 fn default() -> Self {
427 Element::zero()
428 }
429}
430
431impl Add for Element {
432 type Output = Element;
433
434 fn add(self, other: Self) -> Element {
435 Element([self.0[0] ^ other.0[0], self.0[1] ^ other.0[1]])
436 }
437}
438
439impl Sub for Element {
440 type Output = Element;
441
442 fn sub(self, other: Self) -> Element {
443 self.add(other)
444 }
445}
446
447impl Mul for Element {
448 type Output = Element;
449
450 fn mul(self, rhs: Self) -> Element {
451 let out: [u8; 3] = [
453 galois_8::mul(self.0[0], rhs.0[0]),
454 galois_8::add(
455 galois_8::mul(self.0[1], rhs.0[0]),
456 galois_8::mul(self.0[0], rhs.0[1]),
457 ),
458 galois_8::mul(self.0[1], rhs.0[1]),
459 ];
460
461 Element::reduce_from(out)
462 }
463}
464
465impl Mul<u8> for Element {
466 type Output = Element;
467
468 fn mul(self, rhs: u8) -> Element {
469 Element([galois_8::mul(rhs, self.0[0]), galois_8::mul(rhs, self.0[1])])
470 }
471}
472
473impl Div for Element {
474 type Output = Element;
475
476 #[allow(clippy::suspicious_arithmetic_impl)]
477 fn div(self, rhs: Self) -> Element {
478 self * rhs.inverse()
479 }
480}
481
482#[derive(Debug)]
485enum EgcdRhs {
486 Element(Element),
487 ExtPoly,
488}
489
490impl Element {
491 fn const_egcd(self, rhs: EgcdRhs) -> (u8, Element, Element) {
494 if self.is_zero() {
495 let rhs = match rhs {
496 EgcdRhs::Element(elem) => elem,
497 EgcdRhs::ExtPoly => {
498 debug_assert!(false, "const_egcd invoked with divisible");
499 Element::constant(1)
500 }
501 };
502 (rhs.0[1], Element::constant(0), Element::constant(1))
503 } else {
504 let (cur_quotient, cur_remainder) = match rhs {
505 EgcdRhs::Element(rhs) => rhs.polynom_div(self),
506 EgcdRhs::ExtPoly => Element::div_ext_by(self),
507 };
508
509 let (g, x, y) = cur_remainder.const_egcd(EgcdRhs::Element(self));
511 (g, y + (cur_quotient * x), x)
512 }
513 }
514
515 fn div_ext_by(rhs: Self) -> (Element, Element) {
517 if rhs.degree() == 0 {
518 return (Element::zero(), Element::zero());
522 }
523
524 let leading_mul_inv = galois_8::div(1, rhs.0[0]);
527
528 let monictized = rhs * leading_mul_inv;
529 let mut poly = EXT_POLY;
530
531 for i in 0..2 {
532 let coef = poly[i];
533 for j in 1..2 {
534 if rhs.0[j] != 0 {
535 poly[i + j] ^= galois_8::mul(monictized.0[j], coef);
536 }
537 }
538 }
539
540 let remainder = Element::constant(poly[2]);
541 let quotient = Element([poly[0], poly[1]]) * leading_mul_inv;
542
543 (quotient, remainder)
544 }
545
546 fn polynom_div(self, rhs: Self) -> (Element, Element) {
547 let divisor_degree = rhs.degree();
548 if rhs.is_zero() {
549 (Element::zero(), self)
550 } else if self.degree() < divisor_degree {
551 (Element::zero(), self)
553 } else if divisor_degree == 0 {
554 let invert = galois_8::div(1, rhs.0[1]);
556 let quotient = Element([
557 galois_8::mul(invert, self.0[0]),
558 galois_8::mul(invert, self.0[1]),
559 ]);
560
561 (quotient, Element::zero())
562 } else {
563 debug_assert_eq!(self.degree(), divisor_degree);
566 debug_assert_eq!(self.degree(), 1);
567
568 let leading_mul_inv = galois_8::div(1, rhs.0[0]);
570 let monic = Element([
571 galois_8::mul(leading_mul_inv, rhs.0[0]),
572 galois_8::mul(leading_mul_inv, rhs.0[1]),
573 ]);
574
575 let leading_coeff = self.0[0];
576 let mut remainder = self.0[1];
577
578 if monic.0[1] != 0 {
579 remainder ^= galois_8::mul(monic.0[1], self.0[0]);
580 }
581
582 (
583 Element::constant(galois_8::mul(leading_mul_inv, leading_coeff)),
584 Element::constant(remainder),
585 )
586 }
587 }
588
589 fn inverse(self) -> Element {
591 if self.is_zero() {
592 return Element::zero();
593 }
594
595 let (gcd, y) = {
598 let remainder = self;
600
601 let (g, x, _) = remainder.const_egcd(EgcdRhs::ExtPoly);
603
604 (g, x)
605 };
606
607 if gcd != 0 {
609 let normalizer = galois_8::div(1, gcd);
615 y * normalizer
616 } else {
617 Element::zero()
619 }
620 }
621}
622
623#[cfg(test)]
624mod tests {
625 extern crate alloc;
626 use alloc::{vec, vec::Vec};
627
628 use super::*;
629 use crate::Field as _;
630 use quickcheck::Arbitrary;
631
632 impl Arbitrary for Element {
633 fn arbitrary(gens: &mut quickcheck::Gen) -> Self {
634 let a = u8::arbitrary(gens);
635 let b = u8::arbitrary(gens);
636
637 Element([a, b])
638 }
639 }
640
641 quickcheck! {
642 fn qc_add_associativity(a: Element, b: Element, c: Element) -> bool {
643 a + (b + c) == (a + b) + c
644 }
645
646 fn qc_mul_associativity(a: Element, b: Element, c: Element) -> bool {
647 a * (b * c) == (a * b) * c
648 }
649
650 fn qc_additive_identity(a: Element) -> bool {
651 let zero = Element::zero();
652 a - (zero - a) == zero
653 }
654
655 fn qc_multiplicative_identity(a: Element) -> bool {
656 a.is_zero() || {
657 let one = Element([0, 1]);
658 (one / a) * a == one
659 }
660 }
661
662 fn qc_add_commutativity(a: Element, b: Element) -> bool {
663 a + b == b + a
664 }
665
666 fn qc_mul_commutativity(a: Element, b: Element) -> bool {
667 a * b == b * a
668 }
669
670 fn qc_add_distributivity(a: Element, b: Element, c: Element) -> bool {
671 a * (b + c) == (a * b) + (a * c)
672 }
673
674 fn qc_inverse(a: Element) -> bool {
675 a.is_zero() || {
676 let inv = a.inverse();
677 a * inv == Element::constant(1)
678 }
679 }
680
681 fn qc_exponent_1(a: Element, n: u8) -> bool {
682 a.is_zero() || n == 0 || {
683 let mut b = a.exp(n as usize);
684 for _ in 1..n {
685 b = b / a;
686 }
687
688 a == b
689 }
690 }
691
692 fn qc_exponent_2(a: Element, n: u8) -> bool {
693 a.is_zero() || {
694 let mut res = true;
695 let mut b = Element::constant(1);
696
697 for i in 0..n {
698 res = res && b == a.exp(i as usize);
699 b = b * a;
700 }
701
702 res
703 }
704 }
705
706 fn qc_exp_zero_is_one(a: Element) -> bool {
707 a.exp(0) == Element::constant(1)
708 }
709 }
710
711 #[test]
712 fn test_div_b_is_0() {
713 assert_eq!(Element::zero(), Element([1, 0]) / Element::zero());
714 }
715
716 #[test]
717 fn zero_to_zero_is_one() {
718 assert_eq!(Element::zero().exp(0), Element::constant(1))
719 }
720
721 #[test]
726 fn mul_slice_matches_scalar_reference() {
727 const N: usize = 2100; let mut input = [[0u8; 2]; N];
729 for (i, e) in input.iter_mut().enumerate() {
730 *e = [
731 (i.wrapping_mul(31).wrapping_add(7)) as u8,
732 (i.wrapping_mul(17).wrapping_add(3)) as u8,
733 ];
734 }
735
736 let coeffs = [
737 [0u8, 0], [0, 1], [0, 0x8e], [0x9a, 0], [0x9a, 0x3f],
742 [0xff, 0xff],
743 [0x01, 0x80],
744 ];
745 let lens = [0usize, 1, 2, 7, 16, 17, 63, 1023, 1024, 1025, N];
746
747 for &c in &coeffs {
748 for &len in &lens {
749 let inp = &input[..len];
750
751 let mut out = [[0u8; 2]; N];
753 Field::mul_slice(c, inp, &mut out[..len]);
754 for (i, &e) in inp.iter().enumerate() {
755 assert_eq!(
756 out[i],
757 Field::mul(c, e),
758 "mul_slice c={c:?} len={len} i={i}"
759 );
760 }
761
762 let mut acc = [[0u8; 2]; N];
764 for (i, e) in acc.iter_mut().enumerate() {
765 *e = [
766 (i.wrapping_mul(13).wrapping_add(5)) as u8,
767 (i.wrapping_mul(19).wrapping_add(11)) as u8,
768 ];
769 }
770 let seed = acc;
771 Field::mul_slice_add(c, inp, &mut acc[..len]);
772 for (i, &e) in inp.iter().enumerate() {
773 let expected = Field::add(seed[i], Field::mul(c, e));
774 assert_eq!(acc[i], expected, "mul_slice_add c={c:?} len={len} i={i}");
775 }
776 }
777 }
778 }
779
780 #[test]
787 fn deinterleave_interleave_match_scalar_and_round_trip() {
788 fn src_byte(i: usize) -> u8 {
791 (i.wrapping_mul(37).wrapping_add(i / 2).wrapping_add(1)) as u8
792 }
793
794 let lens = [
795 0usize, 1, 2, 3, 7, 15, 16, 17, 31, 32, 33, 47, 63, 64, 65, 96, 127, 128, 1000, 1024,
796 ];
797
798 for &n in &lens {
799 let src: Vec<u8> = (0..2 * n).map(src_byte).collect();
800
801 let mut even = vec![0u8; n];
802 let mut odd = vec![0u8; n];
803 deinterleave(&src, &mut even, &mut odd);
804
805 let mut even_ref = vec![0u8; n];
806 let mut odd_ref = vec![0u8; n];
807 deinterleave_scalar(&src, &mut even_ref, &mut odd_ref);
808 assert_eq!(even, even_ref, "deinterleave even plane, n={n}");
809 assert_eq!(odd, odd_ref, "deinterleave odd plane, n={n}");
810 for i in 0..n {
811 assert_eq!(even[i], src[2 * i], "even[{i}] != src[2i], n={n}");
812 assert_eq!(odd[i], src[2 * i + 1], "odd[{i}] != src[2i+1], n={n}");
813 }
814
815 let mut dst = vec![0u8; 2 * n];
816 interleave(&even, &odd, &mut dst);
817
818 let mut dst_ref = vec![0u8; 2 * n];
819 interleave_scalar(&even, &odd, &mut dst_ref);
820 assert_eq!(dst, dst_ref, "interleave, n={n}");
821
822 assert_eq!(dst, src, "interleave(deinterleave(src)) != src, n={n}");
824 }
825 }
826}