use generic_array::{GenericArray, sequence::GenericSequence, typenum::Unsigned};
use crate::register::{CoreRegister, MaskRegister, Register, Storage};
#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub struct StreamGroup<T, const TAIL: usize> {
pub head: T,
pub tail: [T; TAIL],
}
#[inline(always)]
pub fn flat_groups<T, const TAIL: usize, const M: usize>(groups: &[StreamGroup<T, TAIL>; M]) -> &[T] {
const {
assert!(
size_of::<StreamGroup<T, TAIL>>() == (TAIL + 1) * size_of::<T>(),
"StreamGroup must have no padding for the flat view to be sound"
);
}
unsafe { core::slice::from_raw_parts(groups.as_ptr() as *const T, M * (TAIL + 1)) }
}
#[inline(always)]
pub fn flat_groups_mut<T, const TAIL: usize, const M: usize>(groups: &mut [StreamGroup<T, TAIL>; M]) -> &mut [T] {
const {
assert!(
size_of::<StreamGroup<T, TAIL>>() == (TAIL + 1) * size_of::<T>(),
"StreamGroup must have no padding for the flat view to be sound"
);
}
unsafe { core::slice::from_raw_parts_mut(groups.as_mut_ptr() as *mut T, M * (TAIL + 1)) }
}
#[inline(always)]
const fn choose_radix(size: usize) -> usize {
let mut m = size;
while m % 2 == 0 {
m /= 2;
}
while m % 3 == 0 {
m /= 3;
}
if m > 1 {
m
} else if size % 3 == 0 {
3
} else {
2
}
}
#[inline(always)]
const fn stream_pos(j: usize, n: usize) -> usize {
if n == 1 {
return 0;
}
let p = choose_radix(n);
let sub = n / p;
(j % p) * sub + stream_pos(j / p, sub)
}
#[inline(always)]
fn gather_deinterleaved<R: Register>(group: &[Storage<R>], off: usize, j: usize, p: usize) -> Storage<R> {
let lanes = R::lanes();
let local: GenericArray<u32, R::Lanes> = GenericArray::generate(|l| ((l * p + j) % lanes) as u32);
let first = j / lanes;
let last = ((lanes - 1) * p + j) / lanes;
let mut acc = R::permutev(unsafe { *group.get_unchecked(off + first) }, local.clone());
let mut k = first + 1;
while k <= last {
let l0 = (k * lanes - j).div_ceil(p);
if l0 * p + j < (k + 1) * lanes {
let selected: GenericArray<bool, <R::Mask as CoreRegister>::Lanes> =
GenericArray::generate(|l| (l * p + j) / lanes == k);
let mask = <R::Mask as MaskRegister>::new_mask(selected);
acc = R::blendv(
mask,
acc,
R::permutev(unsafe { *group.get_unchecked(off + k) }, local.clone()),
);
}
k += 1;
}
acc
}
#[inline(always)]
fn gather_interleaved<R: Register>(
block: &[Storage<R>],
base: usize,
sub: usize,
i: usize,
t: usize,
p: usize,
) -> Storage<R> {
let lanes = R::lanes();
let local: GenericArray<u32, R::Lanes> = GenericArray::generate(|l| ((t * lanes + l) / p) as u32);
let r0 = (t * lanes) % p;
let mut acc = R::permutev(unsafe { *block.get_unchecked(base + r0 * sub + i) }, local.clone());
let mut dr = 1;
while dr < p && dr < lanes {
let r = (r0 + dr) % p;
let selected: GenericArray<bool, <R::Mask as CoreRegister>::Lanes> =
GenericArray::generate(|l| (t * lanes + l) % p == r);
let mask = <R::Mask as MaskRegister>::new_mask(selected);
acc = R::blendv(
mask,
acc,
R::permutev(unsafe { *block.get_unchecked(base + r * sub + i) }, local.clone()),
);
dr += 1;
}
acc
}
#[inline(always)]
const fn leftover(n: usize) -> usize {
let mut m = n;
while m % 2 == 0 {
m /= 2;
}
while m % 3 == 0 {
m /= 3;
}
m
}
macro_rules! unroll {
(for $i:ident < $n:ident $body:block) => {
unroll!(@emit $i $n $body
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31);
let mut $i = 32;
while $i < $n {
$body
$i += 1;
}
};
(@emit $i:ident $n:ident $body:block $($lit:literal)*) => {
$(if $lit < $n {
let $i = $lit;
$body
})*
};
}
#[inline(always)]
fn stages_deinterleave_flat<R: Register>(buf: &mut [Storage<R>], tmp: &mut [Storage<R>], n: usize, lo: usize) {
let mut size = n;
if lo > 1 {
let sub = size / lo;
let mut i = 0;
while i < sub {
unroll!(for r < lo {
unsafe { *tmp.get_unchecked_mut(r * sub + i) = gather_deinterleaved::<R>(buf, lo * i, r, lo) };
});
i += 1;
}
buf.copy_from_slice(tmp);
size = sub;
}
while size % 3 == 0 {
let sub = size / 3;
let mut base = 0;
while base < n {
let mut i = 0;
while i < sub {
let g = base + 3 * i;
unsafe {
let [p0, p1, p2] = R::deinterleave_radix::<3>([
*buf.get_unchecked(g),
*buf.get_unchecked(g + 1),
*buf.get_unchecked(g + 2),
]);
*tmp.get_unchecked_mut(base + i) = p0;
*tmp.get_unchecked_mut(base + sub + i) = p1;
*tmp.get_unchecked_mut(base + 2 * sub + i) = p2;
}
i += 1;
}
base += size;
}
buf.copy_from_slice(tmp);
size = sub;
}
while size > 1 {
let sub = size / 2;
let mut base = 0;
while base < n {
let mut i = 0;
while i < sub {
let g = base + 2 * i;
unsafe {
let (evens, odds) = R::deinterleave(*buf.get_unchecked(g), *buf.get_unchecked(g + 1));
*tmp.get_unchecked_mut(base + i) = evens;
*tmp.get_unchecked_mut(base + sub + i) = odds;
}
i += 1;
}
base += size;
}
buf.copy_from_slice(tmp);
size = sub;
}
}
#[inline(always)]
fn stages_interleave_flat<R: Register>(buf: &mut [Storage<R>], tmp: &mut [Storage<R>], n: usize, lo: usize) {
let smooth = n / lo; let pow2 = 1usize << smooth.trailing_zeros();
let mut size = 2;
while size <= pow2 {
let sub = size / 2;
let mut base = 0;
while base < n {
let mut i = 0;
while i < sub {
let g = base + 2 * i;
unsafe {
let (lo, hi) = R::interleave(*buf.get_unchecked(base + i), *buf.get_unchecked(base + sub + i));
*tmp.get_unchecked_mut(g) = lo;
*tmp.get_unchecked_mut(g + 1) = hi;
}
i += 1;
}
base += size;
}
buf.copy_from_slice(tmp);
size *= 2;
}
let mut size = pow2 * 3;
while size <= smooth {
let sub = size / 3;
let mut base = 0;
while base < n {
let mut i = 0;
while i < sub {
let g = base + 3 * i;
unsafe {
let [r0, r1, r2] = R::interleave_radix::<3>([
*buf.get_unchecked(base + i),
*buf.get_unchecked(base + sub + i),
*buf.get_unchecked(base + 2 * sub + i),
]);
*tmp.get_unchecked_mut(g) = r0;
*tmp.get_unchecked_mut(g + 1) = r1;
*tmp.get_unchecked_mut(g + 2) = r2;
}
i += 1;
}
base += size;
}
buf.copy_from_slice(tmp);
size *= 3;
}
if lo > 1 {
let sub = smooth;
let mut i = 0;
while i < sub {
unroll!(for t < lo {
unsafe { *tmp.get_unchecked_mut(lo * i + t) = gather_interleaved::<R>(buf, 0, sub, i, t, lo) };
});
i += 1;
}
buf.copy_from_slice(tmp);
}
}
#[inline(always)]
fn deinterleave_any_flat<R: Register>(src: &[Storage<R>], out: &mut [Storage<R>], p: usize) {
unroll!(for j < p {
unsafe { *out.get_unchecked_mut(j) = gather_deinterleaved::<R>(src, 0, j, p) };
});
}
#[inline(always)]
fn interleave_any_flat<R: Register>(values: &[Storage<R>], out: &mut [Storage<R>], p: usize) {
unroll!(for t < p {
unsafe { *out.get_unchecked_mut(t) = gather_interleaved::<R>(values, 0, 1, 0, t, p) };
});
}
#[inline(always)]
pub fn deinterleave_any<R: Register, const N: usize>(src: [Storage<R>; N]) -> [Storage<R>; N] {
let mut out = [R::EMPTY; N];
deinterleave_any_flat::<R>(&src, &mut out, N);
out
}
#[inline(always)]
pub fn interleave_any<R: Register, const N: usize>(values: [Storage<R>; N]) -> [Storage<R>; N] {
let mut out = [R::EMPTY; N];
interleave_any_flat::<R>(&values, &mut out, N);
out
}
#[inline(always)]
pub fn deinterleave_radix_default<R: Register, const N: usize>(inputs: [Storage<R>; N]) -> [Storage<R>; N] {
if const { N == 2 } {
let (a, b) = unsafe { (*inputs.get_unchecked(0), *inputs.get_unchecked(1)) };
let (r0, r1) = R::deinterleave(a, b);
let mut out = [R::EMPTY; N];
unsafe {
*out.get_unchecked_mut(0) = r0;
*out.get_unchecked_mut(1) = r1;
}
out
} else {
deinterleave_any::<R, N>(inputs)
}
}
#[inline(always)]
pub fn interleave_radix_default<R: Register, const N: usize>(inputs: [Storage<R>; N]) -> [Storage<R>; N] {
if const { N == 2 } {
let (a, b) = unsafe { (*inputs.get_unchecked(0), *inputs.get_unchecked(1)) };
let (r0, r1) = R::interleave(a, b);
let mut out = [R::EMPTY; N];
unsafe {
*out.get_unchecked_mut(0) = r0;
*out.get_unchecked_mut(1) = r1;
}
out
} else {
interleave_any::<R, N>(inputs)
}
}
#[inline(always)]
pub fn interleave_by<R: Register>(a: Storage<R>, b: Storage<R>, group: usize) -> (Storage<R>, Storage<R>) {
let lanes = R::lanes();
let groups = lanes / group;
let mut lo = R::EMPTY;
let mut hi = R::EMPTY;
{
let sa = R::as_slice(&a);
let sb = R::as_slice(&b);
let dlo = R::as_mut_slice(&mut lo);
let dhi = R::as_mut_slice(&mut hi);
for e in 0..lanes {
let k = e / group;
let sub = e % group;
dlo[e] = if k % 2 == 0 {
sa[(k / 2) * group + sub]
} else {
sb[(k / 2) * group + sub]
};
let kh = groups + k;
dhi[e] = if kh % 2 == 0 {
sa[(kh / 2) * group + sub]
} else {
sb[(kh / 2) * group + sub]
};
}
}
(lo, hi)
}
#[inline(always)]
pub fn deinterleave_by<R: Register>(a: Storage<R>, b: Storage<R>, group: usize) -> (Storage<R>, Storage<R>) {
let lanes = R::lanes();
let groups = lanes / group;
let mut o0 = R::EMPTY;
let mut o1 = R::EMPTY;
{
let sa = R::as_slice(&a);
let sb = R::as_slice(&b);
let d0 = R::as_mut_slice(&mut o0);
let d1 = R::as_mut_slice(&mut o1);
for e in 0..lanes {
let m = e / group;
let sub = e % group;
let (s0, p0) = if 2 * m < groups {
(sa, 2 * m)
} else {
(sb, 2 * m - groups)
};
let (s1, p1) = if 2 * m + 1 < groups {
(sa, 2 * m + 1)
} else {
(sb, 2 * m + 1 - groups)
};
d0[e] = s0[p0 * group + sub];
d1[e] = s1[p1 * group + sub];
}
}
(o0, o1)
}
#[inline(always)]
pub fn interleave_by_default<R: Register, const GROUP: usize>(
a: Storage<R>,
b: Storage<R>,
) -> (Storage<R>, Storage<R>) {
if const { GROUP == 1 } {
R::interleave(a, b)
} else {
interleave_by::<R>(a, b, GROUP)
}
}
#[inline(always)]
pub fn deinterleave_by_default<R: Register, const GROUP: usize>(
a: Storage<R>,
b: Storage<R>,
) -> (Storage<R>, Storage<R>) {
if const { GROUP == 1 } {
R::deinterleave(a, b)
} else {
deinterleave_by::<R>(a, b, GROUP)
}
}
#[inline(always)]
fn deinterleave_radix_by_pow2<R: Register, const N: usize, const GROUP: usize>(
inputs: [Storage<R>; N],
) -> [Storage<R>; N] {
let mut buf = inputs;
let mut tmp = [R::EMPTY; N];
let mut size = N;
while size > 1 {
let sub = size / 2;
let mut base = 0;
while base < N {
let mut i = 0;
while i < sub {
let g = base + 2 * i;
unsafe {
let (evens, odds) = R::deinterleave_by::<GROUP>(*buf.get_unchecked(g), *buf.get_unchecked(g + 1));
*tmp.get_unchecked_mut(base + i) = evens;
*tmp.get_unchecked_mut(base + sub + i) = odds;
}
i += 1;
}
base += size;
}
buf.copy_from_slice(&tmp);
size = sub;
}
let perm = const { digit_reversal::<N>() };
let mut out = [R::EMPTY; N];
let mut j = 0;
while j < N {
unsafe { *out.get_unchecked_mut(j) = *buf.get_unchecked(*perm.get_unchecked(j)) };
j += 1;
}
out
}
#[inline(always)]
fn deinterleave_radix_by_lanewise<R: Register, const N: usize, const GROUP: usize>(
inputs: [Storage<R>; N],
) -> [Storage<R>; N] {
let lanes = R::lanes();
let groups = lanes / GROUP;
let mut out = [R::EMPTY; N];
for (r, slot) in out.iter_mut().enumerate() {
let d = R::as_mut_slice(slot);
for q in 0..groups {
let c = q * N + r;
let src_reg = c / groups;
let src_grp = c % groups;
let s = R::as_slice(&inputs[src_reg]);
for sub in 0..GROUP {
d[q * GROUP + sub] = s[src_grp * GROUP + sub];
}
}
}
out
}
#[inline(always)]
pub fn deinterleave_radix_by<R: Register, const N: usize, const GROUP: usize>(
inputs: [Storage<R>; N],
) -> [Storage<R>; N] {
if const { N.is_power_of_two() && (N <= 8 || 2 * GROUP >= <R::Lanes as Unsigned>::USIZE) } {
deinterleave_radix_by_pow2::<R, N, GROUP>(inputs)
} else {
deinterleave_radix_by_lanewise::<R, N, GROUP>(inputs)
}
}
#[inline(always)]
fn interleave_radix_by_pow2<R: Register, const N: usize, const GROUP: usize>(
inputs: [Storage<R>; N],
) -> [Storage<R>; N] {
let perm = const { digit_reversal::<N>() };
let mut buf = [R::EMPTY; N];
let mut j = 0;
while j < N {
unsafe { *buf.get_unchecked_mut(*perm.get_unchecked(j)) = *inputs.get_unchecked(j) };
j += 1;
}
let mut tmp = [R::EMPTY; N];
let mut size = 2;
while size <= N {
let sub = size / 2;
let mut base = 0;
while base < N {
let mut i = 0;
while i < sub {
let g = base + 2 * i;
unsafe {
let (lo, hi) =
R::interleave_by::<GROUP>(*buf.get_unchecked(base + i), *buf.get_unchecked(base + sub + i));
*tmp.get_unchecked_mut(g) = lo;
*tmp.get_unchecked_mut(g + 1) = hi;
}
i += 1;
}
base += size;
}
buf.copy_from_slice(&tmp);
size *= 2;
}
buf
}
#[inline(always)]
fn interleave_radix_by_lanewise<R: Register, const N: usize, const GROUP: usize>(
inputs: [Storage<R>; N],
) -> [Storage<R>; N] {
let lanes = R::lanes();
let groups = lanes / GROUP;
let mut out = [R::EMPTY; N];
for (out_reg, slot) in out.iter_mut().enumerate() {
let d = R::as_mut_slice(slot);
for lg in 0..groups {
let c = out_reg * groups + lg;
let src_reg = c % N;
let src_grp = c / N;
let s = R::as_slice(&inputs[src_reg]);
for sub in 0..GROUP {
d[lg * GROUP + sub] = s[src_grp * GROUP + sub];
}
}
}
out
}
#[inline(always)]
pub fn interleave_radix_by<R: Register, const N: usize, const GROUP: usize>(
inputs: [Storage<R>; N],
) -> [Storage<R>; N] {
if const { N.is_power_of_two() && (N <= 8 || 2 * GROUP >= <R::Lanes as Unsigned>::USIZE) } {
interleave_radix_by_pow2::<R, N, GROUP>(inputs)
} else {
interleave_radix_by_lanewise::<R, N, GROUP>(inputs)
}
}
#[inline(always)]
pub fn deinterleave_radix_by_default<R: Register, const N: usize, const GROUP: usize>(
inputs: [Storage<R>; N],
) -> [Storage<R>; N] {
if const { GROUP == 1 } {
deinterleave_n::<R, N>(inputs)
} else if const { N == 2 } {
let (a, b) = unsafe { (*inputs.get_unchecked(0), *inputs.get_unchecked(1)) };
let (o0, o1) = R::deinterleave_by::<GROUP>(a, b);
let mut out = [R::EMPTY; N];
unsafe {
*out.get_unchecked_mut(0) = o0;
*out.get_unchecked_mut(1) = o1;
}
out
} else {
deinterleave_radix_by::<R, N, GROUP>(inputs)
}
}
#[inline(always)]
pub fn interleave_radix_by_default<R: Register, const N: usize, const GROUP: usize>(
inputs: [Storage<R>; N],
) -> [Storage<R>; N] {
if const { GROUP == 1 } {
interleave_n::<R, N>(inputs)
} else if const { N == 2 } {
let (a, b) = unsafe { (*inputs.get_unchecked(0), *inputs.get_unchecked(1)) };
let (o0, o1) = R::interleave_by::<GROUP>(a, b);
let mut out = [R::EMPTY; N];
unsafe {
*out.get_unchecked_mut(0) = o0;
*out.get_unchecked_mut(1) = o1;
}
out
} else {
interleave_radix_by::<R, N, GROUP>(inputs)
}
}
#[inline(always)]
const fn digit_reversal<const N: usize>() -> [usize; N] {
let mut t = [0usize; N];
let mut j = 0;
while j < N {
t[j] = stream_pos(j, N);
j += 1;
}
t
}
#[inline(always)]
const fn digit_reversal_grouped<const M: usize, const TAIL: usize>() -> [StreamGroup<usize, TAIL>; M] {
let mut t = [StreamGroup {
head: 0usize,
tail: [0usize; TAIL],
}; M];
let c = TAIL + 1;
let n = M * c;
let mut j = 0;
while j < n {
let pos = stream_pos(j, n);
if j % c == 0 {
t[j / c].head = pos;
} else {
t[j / c].tail[j % c - 1] = pos;
}
j += 1;
}
t
}
#[inline(always)]
pub fn deinterleave_n<R: Register, const N: usize>(src: [Storage<R>; N]) -> [Storage<R>; N] {
let mut buf = src;
let mut tmp = [R::EMPTY; N];
stages_deinterleave_flat::<R>(&mut buf, &mut tmp, N, const { leftover(N) });
let perm = const { digit_reversal::<N>() };
let mut j = 0;
while j < N {
unsafe { *tmp.get_unchecked_mut(j) = *buf.get_unchecked(perm[j]) };
j += 1;
}
tmp
}
#[inline(always)]
pub fn interleave_n<R: Register, const N: usize>(values: [Storage<R>; N]) -> [Storage<R>; N] {
let perm = const { digit_reversal::<N>() };
let mut buf = [R::EMPTY; N];
let mut j = 0;
while j < N {
unsafe { *buf.get_unchecked_mut(perm[j]) = *values.get_unchecked(j) };
j += 1;
}
let mut tmp = [R::EMPTY; N];
stages_interleave_flat::<R>(&mut buf, &mut tmp, N, const { leftover(N) });
buf
}
#[inline(always)]
pub fn flat_arrays<T, const C: usize, const M: usize>(records: &[[T; C]; M]) -> &[T] {
unsafe { core::slice::from_raw_parts(records.as_ptr() as *const T, M * C) }
}
#[inline(always)]
pub fn flat_arrays_mut<T, const C: usize, const M: usize>(records: &mut [[T; C]; M]) -> &mut [T] {
unsafe { core::slice::from_raw_parts_mut(records.as_mut_ptr() as *mut T, M * C) }
}
#[inline(always)]
const fn digit_reversal_arrays<const M: usize, const C: usize>() -> [[usize; C]; M] {
let mut t = [[0usize; C]; M];
let n = M * C;
let mut j = 0;
while j < n {
t[j / C][j % C] = stream_pos(j, n);
j += 1;
}
t
}
#[inline(always)]
pub fn deinterleave_arrays<R: Register, const M: usize, const C: usize>(
src: [[Storage<R>; C]; M],
) -> [[Storage<R>; C]; M] {
const { assert!(M >= 1 && C >= 1) };
let n = M * C;
let mut buf = src;
let mut tmp = [[R::EMPTY; C]; M];
stages_deinterleave_flat::<R>(
flat_arrays_mut(&mut buf),
flat_arrays_mut(&mut tmp),
n,
const { leftover(M * C) },
);
let perm = const { digit_reversal_arrays::<M, C>() };
let perm = flat_arrays(&perm);
let src_flat = flat_arrays(&buf);
let out_flat = flat_arrays_mut(&mut tmp);
let mut j = 0;
while j < n {
unsafe { *out_flat.get_unchecked_mut(j) = *src_flat.get_unchecked(*perm.get_unchecked(j)) };
j += 1;
}
tmp
}
#[inline(always)]
pub fn interleave_arrays<R: Register, const M: usize, const C: usize>(
values: [[Storage<R>; C]; M],
) -> [[Storage<R>; C]; M] {
const { assert!(M >= 1 && C >= 1) };
let n = M * C;
let perm = const { digit_reversal_arrays::<M, C>() };
let perm = flat_arrays(&perm);
let mut buf = [[R::EMPTY; C]; M];
{
let src_flat = flat_arrays(&values);
let buf_flat = flat_arrays_mut(&mut buf);
let mut j = 0;
while j < n {
unsafe { *buf_flat.get_unchecked_mut(*perm.get_unchecked(j)) = *src_flat.get_unchecked(j) };
j += 1;
}
}
let mut tmp = [[R::EMPTY; C]; M];
stages_interleave_flat::<R>(
flat_arrays_mut(&mut buf),
flat_arrays_mut(&mut tmp),
n,
const { leftover(M * C) },
);
buf
}
#[inline(always)]
pub fn deinterleave_grouped<R: Register, const M: usize, const TAIL: usize>(
src: [StreamGroup<Storage<R>, TAIL>; M],
) -> [StreamGroup<Storage<R>, TAIL>; M] {
const { assert!(M >= 1) };
let n = M * (TAIL + 1);
let empty = StreamGroup {
head: R::EMPTY,
tail: [R::EMPTY; TAIL],
};
let mut buf = src;
let mut tmp = [empty; M];
stages_deinterleave_flat::<R>(
flat_groups_mut(&mut buf),
flat_groups_mut(&mut tmp),
n,
const { leftover(M * (TAIL + 1)) },
);
let perm = const { digit_reversal_grouped::<M, TAIL>() };
let perm = flat_groups(&perm);
let src_flat = flat_groups(&buf);
let out_flat = flat_groups_mut(&mut tmp);
let mut j = 0;
while j < n {
unsafe { *out_flat.get_unchecked_mut(j) = *src_flat.get_unchecked(*perm.get_unchecked(j)) };
j += 1;
}
tmp
}
#[inline(always)]
pub fn interleave_grouped<R: Register, const M: usize, const TAIL: usize>(
values: [StreamGroup<Storage<R>, TAIL>; M],
) -> [StreamGroup<Storage<R>, TAIL>; M] {
const { assert!(M >= 1) };
let n = M * (TAIL + 1);
let empty = StreamGroup {
head: R::EMPTY,
tail: [R::EMPTY; TAIL],
};
let perm = const { digit_reversal_grouped::<M, TAIL>() };
let perm = flat_groups(&perm);
let mut buf = [empty; M];
{
let src_flat = flat_groups(&values);
let buf_flat = flat_groups_mut(&mut buf);
let mut j = 0;
while j < n {
unsafe { *buf_flat.get_unchecked_mut(*perm.get_unchecked(j)) = *src_flat.get_unchecked(j) };
j += 1;
}
}
let mut tmp = [empty; M];
stages_interleave_flat::<R>(
flat_groups_mut(&mut buf),
flat_groups_mut(&mut tmp),
n,
const { leftover(M * (TAIL + 1)) },
);
buf
}