use crate::puzzle::{
label::label::{BijectiveLabel, Label},
size::Size,
};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
macro_rules! define_sym {
($($(#[$annot:meta])* $name:ident),* $(,)?) => {
$(
$(#[$annot])*
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct $name<L: Label>(pub L);
)*
};
}
define_sym!(
Id,
RotateCw,
RotateCcw,
RotateHalf,
ReflectVertical,
ReflectHorizontal,
ReflectDiagonal,
ReflectAntidiagonal,
);
impl<L: Label> Label for Id<L> {
fn position_label(&self, size: Size, pos: (u64, u64)) -> u64 {
self.0.position_label(size, pos)
}
fn num_labels(&self, size: Size) -> u64 {
self.0.num_labels(size)
}
}
impl<L: Label> Label for RotateCw<L> {
fn position_label(&self, size: Size, (x, y): (u64, u64)) -> u64 {
self.0
.position_label(size.transpose(), (y, size.width() - 1 - x))
}
fn num_labels(&self, size: Size) -> u64 {
self.0.num_labels(size.transpose())
}
}
impl<L: Label> Label for RotateCcw<L> {
fn position_label(&self, size: Size, (x, y): (u64, u64)) -> u64 {
self.0
.position_label(size.transpose(), (size.height() - 1 - y, x))
}
fn num_labels(&self, size: Size) -> u64 {
self.0.num_labels(size.transpose())
}
}
impl<L: Label> Label for RotateHalf<L> {
fn position_label(&self, size: Size, (x, y): (u64, u64)) -> u64 {
let (width, height) = size.into();
self.0.position_label(size, (width - 1 - x, height - 1 - y))
}
fn num_labels(&self, size: Size) -> u64 {
self.0.num_labels(size)
}
}
impl<L: Label> Label for ReflectVertical<L> {
fn position_label(&self, size: Size, (x, y): (u64, u64)) -> u64 {
self.0.position_label(size, (x, size.height() - 1 - y))
}
fn num_labels(&self, size: Size) -> u64 {
self.0.num_labels(size)
}
}
impl<L: Label> Label for ReflectHorizontal<L> {
fn position_label(&self, size: Size, (x, y): (u64, u64)) -> u64 {
self.0.position_label(size, (size.width() - 1 - x, y))
}
fn num_labels(&self, size: Size) -> u64 {
self.0.num_labels(size)
}
}
impl<L: Label> Label for ReflectDiagonal<L> {
fn position_label(&self, size: Size, (x, y): (u64, u64)) -> u64 {
self.0.position_label(size.transpose(), (y, x))
}
fn num_labels(&self, size: Size) -> u64 {
self.0.num_labels(size.transpose())
}
}
impl<L: Label> Label for ReflectAntidiagonal<L> {
fn position_label(&self, size: Size, (x, y): (u64, u64)) -> u64 {
let (width, height) = size.into();
self.0
.position_label(size.transpose(), (height - 1 - y, width - 1 - x))
}
fn num_labels(&self, size: Size) -> u64 {
self.0.num_labels(size.transpose())
}
}
impl<L: BijectiveLabel> BijectiveLabel for Id<L> {}
impl<L: BijectiveLabel> BijectiveLabel for RotateCw<L> {}
impl<L: BijectiveLabel> BijectiveLabel for RotateCcw<L> {}
impl<L: BijectiveLabel> BijectiveLabel for RotateHalf<L> {}
impl<L: BijectiveLabel> BijectiveLabel for ReflectVertical<L> {}
impl<L: BijectiveLabel> BijectiveLabel for ReflectHorizontal<L> {}
impl<L: BijectiveLabel> BijectiveLabel for ReflectDiagonal<L> {}
impl<L: BijectiveLabel> BijectiveLabel for ReflectAntidiagonal<L> {}
#[cfg(test)]
mod tests {
use super::*;
macro_rules! test_label {
($label:ty, $($w:literal x $h:literal : $labels:expr),+ $(,)?) => {
paste::paste! {
mod [< $label:snake >] {
use crate::puzzle::{label::label::{Label as _, RowGrids}, size::Size};
use super::{$label};
$(#[test]
fn [< test_ $label:snake _ $w x $h >] () {
let size = Size::new($w, $h).unwrap();
let labels = (0..$w * $h)
.map(|i| $label(RowGrids).position_label(size, (i % $w, i / $w)))
.collect::<Vec<_>>();
let num_labels = $label(RowGrids).num_labels(size);
let expected_num_labels = $labels.iter().max().unwrap() + 1;
assert_eq!(labels, $labels);
assert_eq!(num_labels, expected_num_labels);
})*
}
}
};
}
test_label!(
Id,
4 x 4: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
4 x 6: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23],
6 x 4: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23],
);
test_label!(
RotateCw,
4 x 4: [12, 8, 4, 0, 13, 9, 5, 1, 14, 10, 6, 2, 15, 11, 7, 3],
4 x 6: [18, 12, 6, 0, 19, 13, 7, 1, 20, 14, 8, 2, 21, 15, 9, 3, 22, 16, 10, 4, 23, 17, 11, 5],
6 x 4: [20, 16, 12, 8, 4, 0, 21, 17, 13, 9, 5, 1, 22, 18, 14, 10, 6, 2, 23, 19, 15, 11, 7, 3],
);
test_label!(
RotateCcw,
4 x 4: [3, 7, 11, 15, 2, 6, 10, 14, 1, 5, 9, 13, 0, 4, 8, 12],
4 x 6: [5, 11, 17, 23, 4, 10, 16, 22, 3, 9, 15, 21, 2, 8, 14, 20, 1, 7, 13, 19, 0, 6, 12, 18],
6 x 4: [3, 7, 11, 15, 19, 23, 2, 6, 10, 14, 18, 22, 1, 5, 9, 13, 17, 21, 0, 4, 8, 12, 16, 20],
);
test_label!(
RotateHalf,
4 x 4: [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0],
4 x 6: [23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0],
6 x 4: [23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0],
);
test_label!(
ReflectVertical,
4 x 4: [12, 13, 14, 15, 8, 9, 10, 11, 4, 5, 6, 7, 0, 1, 2, 3],
4 x 6: [20, 21, 22, 23, 16, 17, 18, 19, 12, 13, 14, 15, 8, 9, 10, 11, 4, 5, 6, 7, 0, 1, 2, 3],
6 x 4: [18, 19, 20, 21, 22, 23, 12, 13, 14, 15, 16, 17, 6, 7, 8, 9, 10, 11, 0, 1, 2, 3, 4, 5],
);
test_label!(
ReflectHorizontal,
4 x 4: [3, 2, 1, 0, 7, 6, 5, 4, 11, 10, 9, 8, 15, 14, 13, 12],
4 x 6: [3, 2, 1, 0, 7, 6, 5, 4, 11, 10, 9, 8, 15, 14, 13, 12, 19, 18, 17, 16, 23, 22, 21, 20],
6 x 4: [5, 4, 3, 2, 1, 0, 11, 10, 9, 8, 7, 6, 17, 16, 15, 14, 13, 12, 23, 22, 21, 20, 19, 18],
);
test_label!(
ReflectDiagonal,
4 x 4: [0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15],
4 x 6: [0, 6, 12, 18, 1, 7, 13, 19, 2, 8, 14, 20, 3, 9, 15, 21, 4, 10, 16, 22, 5, 11, 17, 23],
6 x 4: [0, 4, 8, 12, 16, 20, 1, 5, 9, 13, 17, 21, 2, 6, 10, 14, 18, 22, 3, 7, 11, 15, 19, 23],
);
test_label!(
ReflectAntidiagonal,
4 x 4: [15, 11, 7, 3, 14, 10, 6, 2, 13, 9, 5, 1, 12, 8, 4, 0],
4 x 6: [23, 17, 11, 5, 22, 16, 10, 4, 21, 15, 9, 3, 20, 14, 8, 2, 19, 13, 7, 1, 18, 12, 6, 0],
6 x 4: [23, 19, 15, 11, 7, 3, 22, 18, 14, 10, 6, 2, 21, 17, 13, 9, 5, 1, 20, 16, 12, 8, 4, 0],
);
}