use std::collections::HashMap;
use std::fmt;
use arrayvec::*;
use derive_more::*;
use crate::bitlib::*;
#[derive(
Copy,
Clone,
Eq,
PartialEq,
Hash,
PartialOrd,
Ord,
BitAnd,
BitAndAssign,
BitOr,
BitOrAssign,
BitXor,
BitXorAssign,
)]
pub struct BitMatrix8(pub u64);
impl core::ops::BitAnd<u64> for BitMatrix8 {
type Output = Self;
fn bitand(self, rhs: u64) -> Self {
Self(self.0 & rhs)
}
}
impl core::ops::BitOr<u64> for BitMatrix8 {
type Output = Self;
fn bitor(self, rhs: u64) -> Self {
Self(self.0 | rhs)
}
}
impl From<u64> for BitMatrix8 {
fn from(raw_matrix: u64) -> Self {
BitMatrix8(raw_matrix)
}
}
impl fmt::Debug for BitMatrix8 {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "BitMatrix8({:#010x})", self.0)
}
}
impl fmt::Display for BitMatrix8 {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}",
(0..64)
.map(|x| format!(
"{}{}",
if (self.0 >> x) & 0x1 == 1 { "1" } else { "." },
if x % 8 == 7 { "\n" } else { "" }
))
.collect::<String>()
)
}
}
impl Iterator for BitMatrix8 {
type Item = Self;
fn next(&mut self) -> Option<Self::Item> {
let grid: u64 = self.0;
if grid != 0 {
let bit: u64 = grid.isolate_lowest_one();
*self = Self(grid ^ bit);
Some(Self(bit))
} else {
None
}
}
}
impl BitMatrix8 {
pub fn pentomino_map() -> HashMap<char, BitMatrix8> {
HashMap::<char, BitMatrix8>::from([
('F', BitMatrix8(0x20306)),
('I', BitMatrix8(0x101010101)),
('L', BitMatrix8(0x3010101)),
('N', BitMatrix8(0xe03)),
('P', BitMatrix8(0x10303)),
('T', BitMatrix8(0x20207)),
('U', BitMatrix8(0x705)),
('V', BitMatrix8(0x70101)),
('W', BitMatrix8(0x60301)),
('X', BitMatrix8(0x20702)),
('Y', BitMatrix8(0xf04)),
('Z', BitMatrix8(0x60203)),
])
}
pub fn origin_rotate_all(self) -> ArrayVec<BitMatrix8, 4> {
let mut vec = ArrayVec::<BitMatrix8, 4>::new();
let mut x = self.shift_to_origin();
vec.push(x);
for _ in 0..3 {
x = x.rotate_cc().shift_to_origin();
vec.push(x);
}
vec.sort_unstable();
let symmetries = vec.partition_dedup().0.len(); vec.truncate(symmetries);
vec
}
pub fn origin_dihedral_all(self) -> ArrayVec<BitMatrix8, 8> {
let mut vec = ArrayVec::<BitMatrix8, 8>::new();
let mut x = self.shift_to_origin();
vec.push(x);
for _ in 0..3 {
x = x.rotate_cc().shift_to_origin();
vec.push(x);
}
x = x.flip_x().shift_to_origin();
vec.push(x);
for _ in 0..3 {
x = x.rotate_cc().shift_to_origin();
vec.push(x);
}
vec.sort_unstable();
let symmetries = vec.partition_dedup().0.len(); vec.truncate(symmetries);
vec
}
pub fn rotate_all_vec(self) -> ArrayVec<BitMatrix8, 4> {
let mut vec = ArrayVec::<BitMatrix8, 4>::new();
let mut x = self;
vec.push(x);
for _ in 0..3 {
x = x.rotate_cc();
vec.push(x);
}
vec.sort_unstable();
let symmetries = vec.partition_dedup().0.len(); vec.truncate(symmetries);
vec
}
pub fn rotate(self) -> Self {
let mut square = self.0;
swap_mask_shift_u64(&mut square, 0x0f0f_0f0f_u64, 36);
swap_mask_shift_u64(&mut square, 0xffff_ffff_u64, 32);
swap_mask_shift_u64(&mut square, 0x0000_3333_0000_3333_u64, 18);
swap_mask_shift_u64(&mut square, 0xffff_0000_ffff_u64, 16);
swap_mask_shift_u64(&mut square, 0x0055_0055_0055_0055_u64, 9);
swap_mask_shift_u64(&mut square, 0x00ff_00ff_00ff_00ff_u64, 8);
BitMatrix8(square)
}
pub fn rotate_cc(self) -> Self {
let mut square = self.0;
swap_mask_shift_u64(&mut square, 0xffff_ffff_u64, 32);
swap_mask_shift_u64(&mut square, 0x0f0f_0f0f_u64, 36);
swap_mask_shift_u64(&mut square, 0xffff_0000_ffff_u64, 16);
swap_mask_shift_u64(&mut square, 0x0000_3333_0000_3333_u64, 18);
swap_mask_shift_u64(&mut square, 0x00ff_00ff_00ff_00ff_u64, 8);
swap_mask_shift_u64(&mut square, 0x0055_0055_0055_0055_u64, 9);
BitMatrix8(square)
}
pub fn flip_x(self) -> Self {
let mut square = self.0;
swap_mask_shift_u64(&mut square, 0xffff_ffff_u64, 32);
swap_mask_shift_u64(&mut square, 0xffff_0000_ffff_u64, 16);
swap_mask_shift_u64(&mut square, 0x00ff_00ff_00ff_00ff_u64, 8);
BitMatrix8(square)
}
pub fn shift_to_origin(self) -> Self {
let mut shape = self.0;
let y_shift = (shape.trailing_zeros() / 8) * 8;
shape = shape.unbounded_shr(y_shift);
let mut x_shift = shape | shape.unbounded_shr(32);
x_shift |= x_shift.unbounded_shr(32);
x_shift |= x_shift.unbounded_shr(16);
x_shift |= x_shift.unbounded_shr(8);
shape = shape.unbounded_shr(x_shift.trailing_zeros());
Self(shape)
}
pub fn bounding_box(self) -> (u8, u8) {
let mut shape = self.0;
shape |= ((shape >> 1) & 0x7f7f7f7f7f7f7f7f) | shape >> 8;
shape |= ((shape >> 2) & 0x3f3f3f3f3f3f3f3f) | shape >> 16;
shape |= ((shape >> 4) & 0x0f0f0f0f0f0f0f0f) | shape >> 32;
let len_x = (shape & 0xff).count_ones();
let len_y = (shape & 0x0101010101010101).count_ones();
(len_x as u8, len_y as u8)
}
pub fn shift_x(self, shift: i32) -> Self {
if !(-7..=7).contains(&shift) {
return Self(0);
};
if shift == 0 {
return self;
};
let sign = shift > 0;
let shift: u32 = shift.unsigned_abs();
let mask: u64 = ((1_u64 << (8_u32 - shift)) - 1_u64) * 0x0101_0101_0101_0101_u64;
if sign {
BitMatrix8((mask & self.0).unbounded_shl(shift))
} else {
BitMatrix8(mask & self.0.unbounded_shr(shift))
}
}
pub fn checked_shift_x(self, shift: i32) -> Option<Self> {
let shifted = self.shift_x(shift);
if self.0.count_ones() == shifted.0.count_ones() {
Some(shifted)
} else {
None
}
}
pub fn shift_y(self, shift: i32) -> Self {
if !(-7..=7).contains(&shift) {
return Self(0);
};
if shift == 0 {
return self;
};
let sign = shift > 0;
let shift: u32 = shift.unsigned_abs().unbounded_shl(3); let mask: u64 = 0xffff_ffff_ffff_ffff_u64.unbounded_shr(shift);
if sign {
BitMatrix8(mask & self.0.unbounded_shr(shift))
} else {
BitMatrix8((mask & self.0).unbounded_shl(shift))
}
}
pub fn checked_shift_y(self, shift: i32) -> Option<Self> {
let shifted = self.shift_y(shift);
if self.0.count_ones() == shifted.0.count_ones() {
Some(shifted)
} else {
None
}
}
pub fn unbounded_shift_n(self) -> Self {
Self(self.0.unbounded_shr(8))
}
pub fn unbounded_shift_s(self) -> Self {
Self(self.0.unbounded_shl(8))
}
pub fn checked_shift_n(self) -> Option<Self> {
let result = self.0.unbounded_shr(8);
if result.count_ones() == self.0.count_ones() {
Some(Self(result))
} else {
None
}
}
pub fn shift_w(self) -> Option<Self> {
let result = self.0.rotate_right(1);
if result & 0x0101010101010101 == 0 {
Some(Self(result))
} else {
None
}
}
pub fn shift_s(self) -> Self {
Self(self.0 << 8)
}
pub fn shift_e(self) -> Self {
Self(self.0 << 1)
}
pub fn rank(self) -> u32 {
let mut m = self.0;
let mut r = 0u32;
let mut pivot;
println!("{}", Self(m));
while m != 0 {
pivot = m & ma64::REP_01;
if pivot != 0 {
r += 1;
m ^= (m.unbounded_shr(pivot.trailing_zeros()) & 0xff) * pivot;
}
m = m.unbounded_shr(1) & ma64::REP_7F;
println!("{}", Self(m));
}
r
}
}
pub mod ma64 {
use super::*;
pub const REP_01: u64 = 0x0101010101010101u64;
pub const REP_7F: u64 = 0x7f7f7f7f7f7f7f7fu64;
pub const CENTER_XY: BitMatrix8 = BitMatrix8(0x1818_18ff_ff18_1818);
pub const FULL: BitMatrix8 = BitMatrix8(0xffff_ffff_ffff_ffff_u64);
pub const UPPER_LEFT: BitMatrix8 = BitMatrix8(0x0f0f_0f0f_u64);
pub const UPPER_RIGHT: BitMatrix8 = BitMatrix8(0xf0f0_f0f0_u64);
pub const LOWER_LEFT: BitMatrix8 = BitMatrix8(0x0f0f_0f0f_0000_0000_u64);
pub const LOWER_RIGHT: BitMatrix8 = BitMatrix8(0xf0f0_f0f0_0000_0000_u64);
pub const HIGHFIVE: BitMatrix8 = BitMatrix8(0xff80ff01ff);
pub const SMALL_FIVE: BitMatrix8 = BitMatrix8(0x00f080f010f);
pub const CHECKER2: BitMatrix8 = BitMatrix8(0x0c0c_0303);
pub const IDENTITY: BitMatrix8 = BitMatrix8(0x8040201008040201);
pub const ANTIDIAG: BitMatrix8 = BitMatrix8(0x0102040810204080);
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_rank() {
assert_eq!(BitMatrix8::from(IDENTITY).rank(), 8);
assert_eq!(BitMatrix8(0xfedcba9876543210u64).rank(), 4);
}
#[test]
fn test_bitmatrix8_display() {
assert_eq!(
format!("{}", BitMatrix8(0x8040201008040201)),
"1.......\n.1......\n..1.....\n...1....\n....1...\n.....1..\n......1.\n.......1\n"
);
assert_eq!(
format!("{}", BitMatrix8(0x1)),
"1.......\n........\n........\n........\n........\n........\n........\n........\n"
);
}
#[test]
fn test_rotate() {
assert_eq!(
BitMatrix8(0x171515151515151d).rotate(),
BitMatrix8::from(HIGHFIVE)
);
assert_eq!(
BitMatrix8(0x1715151d00000000).rotate(),
BitMatrix8::from(SMALL_FIVE)
);
assert_eq!(BitMatrix8::from(FULL).rotate(), BitMatrix8::from(FULL));
assert_eq!(
BitMatrix8::from(BM8_UPPER_LEFT),
BitMatrix8::from(BM8_LOWER_LEFT).rotate()
);
assert_eq!(
BitMatrix8::from(ANTIDIAG),
BitMatrix8::from(IDENTITY).rotate()
);
}
#[test]
fn test_rotate_cc() {
assert_eq!(
BitMatrix8::from(HIGHFIVE).rotate_cc(),
BitMatrix8(0x171515151515151d)
);
assert_eq!(
BitMatrix8::from(SMALL_FIVE).rotate_cc(),
BitMatrix8(0x1715151d00000000)
);
assert_eq!(
BitMatrix8::from(ANTIDIAG),
BitMatrix8::from(IDENTITY).rotate_cc()
);
assert_eq!(BitMatrix8::from(FULL).rotate_cc(), BitMatrix8::from(FULL));
assert_eq!(
BitMatrix8::from(BM8_UPPER_LEFT).rotate_cc(),
BitMatrix8::from(BM8_LOWER_LEFT)
);
}
#[test]
fn test_rotate_composition() {
assert_eq!(
BitMatrix8(0x9288_7746_3521_0076).rotate().rotate_cc(),
BitMatrix8(0x9288_7746_3521_0076)
);
assert_eq!(
BitMatrix8(0x9288_7746_3521_0076).rotate_cc().rotate(),
BitMatrix8(0x9288_7746_3521_0076)
);
}
#[test]
fn test_flipx() {
assert_eq!(
BitMatrix8::from(ANTIDIAG),
BitMatrix8::from(IDENTITY).flip_x()
);
assert_eq!(BitMatrix8::from(FULL).flip_x(), BitMatrix8::from(FULL));
assert_eq!(
BitMatrix8::from(BM8_UPPER_LEFT).flip_x(),
BitMatrix8::from(BM8_LOWER_LEFT)
);
assert_eq!(
BitMatrix8::from(HIGHFIVE).flip_x(),
BitMatrix8(0xff01_ff80_ff00_0000)
);
}
#[test]
fn test_rotate_all_vec() {
assert_eq!(
BitMatrix8::rotate_all_vec(BitMatrix8::from(CENTER_XY)).as_slice(),
&[BitMatrix8::from(CENTER_XY)]
);
assert_eq!(
BitMatrix8::rotate_all_vec(BitMatrix8::from(BM8_UPPER_LEFT)).as_slice(),
&[
BitMatrix8::from(BM8_UPPER_LEFT),
BitMatrix8::from(BM8_UPPER_RIGHT),
BitMatrix8::from(BM8_LOWER_LEFT),
BitMatrix8::from(BM8_LOWER_RIGHT)
]
);
assert_eq!(
BitMatrix8::rotate_all_vec(BitMatrix8::from(IDENTITY)).len(),
2
);
assert_eq!(
BitMatrix8::rotate_all_vec(BitMatrix8::from(CHECKER2)).len(),
4
);
}
#[test]
fn test_origin_rotate_all() {
assert_eq!(
BitMatrix8::origin_rotate_all(BitMatrix8::from(CENTER_XY)).as_slice(),
&[BitMatrix8::from(CENTER_XY)]
);
assert_eq!(
BitMatrix8::origin_rotate_all(BitMatrix8::from(BM8_UPPER_LEFT)).len(),
1
);
assert_eq!(
BitMatrix8::origin_rotate_all(BitMatrix8::from(HIGHFIVE)).len(),
2
);
assert_eq!(
BitMatrix8::origin_rotate_all(BitMatrix8::from(CHECKER2)).len(),
2
);
assert_eq!(BitMatrix8::origin_rotate_all(BitMatrix8(0x103)).len(), 4);
}
#[test]
fn test_origin_dihedral_all() {
assert_eq!(
BitMatrix8::origin_dihedral_all(BitMatrix8::from(CENTER_XY)).as_slice(),
&[BitMatrix8::from(CENTER_XY)]
);
assert_eq!(
BitMatrix8::origin_dihedral_all(BitMatrix8::from(BM8_UPPER_LEFT)).len(),
1
);
assert_eq!(
BitMatrix8::origin_dihedral_all(BitMatrix8::from(HIGHFIVE)).len(),
4
);
assert_eq!(
BitMatrix8::origin_dihedral_all(BitMatrix8::from(CHECKER2)).len(),
2
);
assert_eq!(BitMatrix8::origin_dihedral_all(BitMatrix8(0x103)).len(), 4);
}
#[test]
fn test_origin_dihedral_all_pentomino() {
let pentomino = BitMatrix8::pentomino_map();
assert_eq!(pentomino[&'F'].origin_dihedral_all().len(), 8);
assert_eq!(pentomino[&'N'].origin_dihedral_all().len(), 8);
assert_eq!(pentomino[&'P'].origin_dihedral_all().len(), 8);
assert_eq!(pentomino[&'Y'].origin_dihedral_all().len(), 8);
assert_eq!(pentomino[&'V'].origin_dihedral_all().len(), 4);
assert_eq!(pentomino[&'W'].origin_dihedral_all().len(), 4);
}
#[test]
fn test_unbounded_shift_n() {
assert_eq!(BitMatrix8(0xf00f).unbounded_shift_n(), BitMatrix8(0xf0));
assert_eq!(BitMatrix8(0xf00f00).unbounded_shift_n(), BitMatrix8(0xf00f));
}
#[test]
fn test_checked_shift_n() {
assert_eq!(BitMatrix8(0xf00f).checked_shift_n(), None);
assert_eq!(
BitMatrix8(0xf00f00).checked_shift_n(),
Some(BitMatrix8(0xf00f))
);
}
#[test]
fn test_pentomino_map() {
let pentomino = BitMatrix8::pentomino_map();
assert_eq!(
&pentomino[&'X'],
&pentomino[&'X'].rotate_cc().shift_to_origin()
);
assert_eq!(
&pentomino[&'X'],
&pentomino[&'X'].rotate().shift_to_origin()
);
assert_eq!(pentomino[&'X'].origin_rotate_all().len(), 1);
assert_eq!(pentomino[&'F'].origin_rotate_all().len(), 4);
assert_eq!(pentomino[&'Z'].origin_rotate_all().len(), 2);
}
#[test]
fn test_blsi() {
let n: u64 = 0b_01100100;
assert_eq!(n.isolate_lowest_one(), 0b_00000100);
assert_eq!(0_u64.isolate_lowest_one(), 0);
}
}