use g2p::GaloisField;
use std::iter::FusedIterator;
use std::mem::size_of;
pub fn gf_bitgroups(
groups: usize,
m: usize,
) -> impl Iterator<
Item = impl Iterator<Item = u8> + DoubleEndedIterator + ExactSizeIterator + FusedIterator,
> + DoubleEndedIterator
+ ExactSizeIterator
+ FusedIterator {
assert!(m % groups == 0, "Parameter `m` must divide by `groups`");
assert!(m <= 8);
let group_len = m / groups;
(0..groups).map(move |group| {
(1..=1_u8
.checked_shl(group_len as u32)
.map_or(u8::MAX, |v| v - 1))
.map(move |v| v << group * group_len)
})
}
#[derive(Debug, PartialEq)]
pub enum MatrixGenError {
UnsupportedInput,
FieldTooSmall,
}
pub fn gen_encode_matrix<'a, GF: 'a + From<u8> + Into<u8> + GaloisField>(
k: usize,
l: usize,
r: usize,
) -> Result<impl Iterator<Item = u8> + DoubleEndedIterator + FusedIterator + 'a, MatrixGenError> {
if r > 2 {
return Err(MatrixGenError::UnsupportedInput);
}
if GF::SIZE / l - 1 < k / l {
return Err(MatrixGenError::FieldTooSmall);
}
let m = size_of::<usize>() * 8 - GF::SIZE.leading_zeros() as usize - 1;
let data_rows = (0..k).flat_map(move |row| (0..k).map(move |col| (row == col) as u8));
let local_parity_rows = (0..l).flat_map(move |row| {
(0..k).map(move |col| ((col < (row + 1) * k / l) && (col >= row * k / l)) as u8)
});
let global_parity_rows = (0..r).flat_map(move |row| {
gf_bitgroups(l, m)
.flat_map(move |group| group.take(k / l))
.map(move |value| (GF::from(value).pow(row + 1)).into())
});
Ok(data_rows.chain(local_parity_rows).chain(global_parity_rows))
}
#[cfg(test)]
pub mod tests {
use crate::gf_bitgroups;
g2p::g2p!(GF16, 4, modulus: 0b10011);
#[test]
fn test_gf_bitgroups() {
(1..=8)
.flat_map(|groups| (1..=8).map(move |m| (groups, m)))
.filter(|(groups, m)| m % groups == 0)
.inspect(|(groups, m)| println!("Input: groups = {}, m = {}", groups, m))
.map(|(groups, m)| ((groups, m), gf_bitgroups(groups, m)))
.for_each(|((groups, m), actual)| {
assert_eq!(actual.len(), groups);
actual
.enumerate()
.map(|(i, group)| {
(
!((2_usize.pow((m / groups) as u32) - 1) << i * (m / groups)) as u8,
group,
)
})
.for_each(|(bitmask, group)| {
println!("Expected bitmask: {:b}", bitmask);
group.for_each(|val| {
print!("{:0len$b} ", val, len = m);
assert_ne!(val, 0);
assert_eq!(val & bitmask as u8, 0);
});
println!();
});
println!();
})
}
}