use super::decode_location;
fn rot(n: u32, x: &mut u32, y: &mut u32, rx: bool, ry: bool) {
if ry {
return;
}
if rx {
*x = n - 1 - *x;
*y = n - 1 - *y;
}
core::mem::swap(x, y);
}
fn bool2int(x: bool) -> u32 {
if x {
1
} else {
0
}
}
pub fn xy2d(nh: u8, mut x: u32, mut y: u32) -> u32 {
let n = 1 << nh;
(0..nh)
.rev()
.map(|sh| {
let s = 1u32 << sh;
let rx = (x & s) != 0;
let ry = (y & s) != 0;
rot(n, &mut x, &mut y, rx, ry);
s * s * ((3 * bool2int(rx)) ^ bool2int(ry))
})
.sum()
}
#[derive(Clone, Copy)]
pub struct X2dhc<'a> {
xybits: u8,
data: &'a [u8],
}
#[derive(Clone, Copy)]
pub enum X2dhcDim {
X(u8),
Y(u8),
}
impl X2dhcDim {
pub fn value(&self) -> u8 {
match *self {
X2dhcDim::X(x) => x,
X2dhcDim::Y(y) => y,
}
}
}
impl<'a> X2dhc<'a> {
pub fn parse(data: &'a [u8]) -> Option<Self> {
if data.len() < 32 {
return None;
}
let xybits = data[0];
if xybits >= 8 {
return None;
}
let exp_len = 1usize.checked_shl(xybits.into())?.checked_mul(16)?;
Some(Self {
xybits,
data: data.get(16..16 + exp_len)?,
})
}
fn lookup_internal(data: &'a [u8], xybits2: u8, x: u8, y: u8) -> (u64, u64) {
let loc = decode_location(xy2d(xybits2, x.into(), y.into())).unwrap();
let dloc = &data[loc..loc + 16];
(
u64::from_be_bytes(dloc[0..8].try_into().unwrap()),
u64::from_be_bytes(dloc[8..16].try_into().unwrap()),
)
}
pub fn lookup(&self, x: u8, y: u8) -> (u64, u64) {
let xybr = 8 - self.xybits;
Self::lookup_internal(self.data, 2 * self.xybits, x >> xybr, y >> xybr)
}
pub fn iter_dim(&self, dim: X2dhcDim) -> impl Iterator<Item = ((u8, u8), (u64, u64))> + 'a {
let X2dhc { xybits, data } = *self;
let (xybr, xybdup) = (8 - xybits, 2 * xybits);
let xy = dim.value() >> xybr;
let xyv = xy << xybr;
(0u8..=(0xff >> xybr)).map(move |i| {
let xy2 = i << xybr;
let (x, y) = match dim {
X2dhcDim::X(_) => (xyv, xy2),
X2dhcDim::Y(_) => (xy2, xyv),
};
((x, y), Self::lookup_internal(data, xybdup, x, y))
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn simple() {
let exp: [u32; 16] = [0, 3, 4, 5, 1, 2, 7, 6, 14, 13, 8, 9, 15, 12, 11, 10];
for x in 0..4u16 {
for y in 0..4u16 {
assert_eq!(exp[usize::from(x * 4 + y)], xy2d(4, x.into(), y.into()));
}
}
}
}