pub(super) struct Plan<const W: usize> {
pub(super) gather_index: [[usize; W]; 3],
pub(super) gather_mask: [[u32; 2]; 3],
pub(super) scatter_index: [[usize; W]; 3],
pub(super) scatter_mask: [[u32; 2]; 3],
}
impl<const W: usize> Plan<W> {
pub(super) const NEW: Self = Self::new();
const fn new() -> Self {
let mut plan = Self {
gather_index: [[0; W]; 3],
gather_mask: [[0; 2]; 3],
scatter_index: [[0; W]; 3],
scatter_mask: [[0; 2]; 3],
};
let mut f = 0;
while f < 3 * W {
let (k, d) = (f / 3, f % 3);
let (r, p) = (f / W, f % W);
plan.gather_index[d][k] = p;
if r > 0 {
plan.gather_mask[d][r - 1] |= 1 << p;
}
plan.scatter_index[d][p] = k;
if d > 0 {
plan.scatter_mask[r][d - 1] |= 1 << p;
}
f += 1;
}
plan
}
}
#[cfg(test)]
mod tests {
use super::Plan;
fn round_trips<const W: usize>() {
let plan = Plan::<W>::NEW;
let registers: [[usize; W]; 3] =
core::array::from_fn(|r| core::array::from_fn(|p| W * r + p));
let coordinates: [[usize; W]; 3] = core::array::from_fn(|d| {
let pick = |p: usize| {
let [second, third] = plan.gather_mask[d];
match (second >> p & 1, third >> p & 1) {
(0, 0) => registers[0][p],
(1, 0) => registers[1][p],
_ => registers[2][p],
}
};
core::array::from_fn(|k| pick(plan.gather_index[d][k]))
});
for (d, row) in coordinates.iter().enumerate() {
for (k, &quadword) in row.iter().enumerate() {
assert_eq!(quadword, 3 * k + d, "gather d {d} k {k}");
}
}
let permuted: [[usize; W]; 3] = core::array::from_fn(|d| {
core::array::from_fn(|p| coordinates[d][plan.scatter_index[d][p]])
});
for (r, (register, &[second, third])) in
registers.iter().zip(&plan.scatter_mask).enumerate()
{
for (p, &quadword) in register.iter().enumerate() {
let d = match (second >> p & 1, third >> p & 1) {
(0, 0) => 0,
(1, 0) => 1,
_ => 2,
};
assert_eq!(permuted[d][p], quadword, "scatter r {r} p {p}");
}
}
}
#[test]
fn the_plans_round_trip_at_both_widths() {
round_trips::<4>();
round_trips::<8>();
}
}