use crate::grid_alignment::GridTransform;
pub const GRID_TRANSFORMS_D6: [GridTransform; 12] = [
GridTransform {
a: 1,
b: 0,
c: 0,
d: 1,
},
GridTransform {
a: 0,
b: -1,
c: 1,
d: 1,
},
GridTransform {
a: -1,
b: -1,
c: 1,
d: 0,
},
GridTransform {
a: -1,
b: 0,
c: 0,
d: -1,
},
GridTransform {
a: 0,
b: 1,
c: -1,
d: -1,
},
GridTransform {
a: 1,
b: 1,
c: -1,
d: 0,
},
GridTransform {
a: 1,
b: 1,
c: 0,
d: -1,
},
GridTransform {
a: 1,
b: 0,
c: -1,
d: -1,
},
GridTransform {
a: 0,
b: -1,
c: -1,
d: 0,
},
GridTransform {
a: -1,
b: -1,
c: 0,
d: 1,
},
GridTransform {
a: -1,
b: 0,
c: 1,
d: 1,
},
GridTransform {
a: 0,
b: 1,
c: 1,
d: 0,
},
];
#[cfg(test)]
mod tests {
use super::*;
use std::collections::HashSet;
fn compose(a: &GridTransform, b: &GridTransform) -> GridTransform {
GridTransform {
a: a.a * b.a + a.b * b.c,
b: a.a * b.b + a.b * b.d,
c: a.c * b.a + a.d * b.c,
d: a.c * b.b + a.d * b.d,
}
}
fn det(t: &GridTransform) -> i32 {
t.a * t.d - t.b * t.c
}
fn as_tuple(t: &GridTransform) -> (i32, i32, i32, i32) {
(t.a, t.b, t.c, t.d)
}
#[test]
fn all_twelve_distinct() {
let set: HashSet<_> = GRID_TRANSFORMS_D6.iter().map(as_tuple).collect();
assert_eq!(set.len(), 12);
}
#[test]
fn all_unimodular() {
for t in &GRID_TRANSFORMS_D6 {
let d = det(t);
assert!(d == 1 || d == -1, "det = {d} for {t:?}");
}
}
#[test]
fn rotations_det_plus_one() {
for t in &GRID_TRANSFORMS_D6[0..6] {
assert_eq!(det(t), 1, "rotation {t:?} should have det +1");
}
}
#[test]
fn reflections_det_minus_one() {
for t in &GRID_TRANSFORMS_D6[6..12] {
assert_eq!(det(t), -1, "reflection {t:?} should have det -1");
}
}
#[test]
fn rotation_order_six() {
let rot60 = &GRID_TRANSFORMS_D6[1];
let identity = &GRID_TRANSFORMS_D6[0];
let mut acc = *identity;
for k in 1..=6 {
acc = compose(&acc, rot60);
if k < 6 {
assert_ne!(
as_tuple(&acc),
as_tuple(identity),
"rot60^{k} should not be identity"
);
}
}
assert_eq!(
as_tuple(&acc),
as_tuple(identity),
"rot60^6 must be identity"
);
}
#[test]
fn reflections_are_involutions() {
for (i, t) in GRID_TRANSFORMS_D6[6..12].iter().enumerate() {
let t_sq = compose(t, t);
assert_eq!(
as_tuple(&t_sq),
as_tuple(&GRID_TRANSFORMS_D6[0]),
"reflection[{i}]^2 must be identity"
);
}
}
#[test]
fn closure_under_composition() {
let set: HashSet<_> = GRID_TRANSFORMS_D6.iter().map(as_tuple).collect();
for a in &GRID_TRANSFORMS_D6 {
for b in &GRID_TRANSFORMS_D6 {
let c = compose(a, b);
assert!(
set.contains(&as_tuple(&c)),
"product of {a:?} and {b:?} = {c:?} not in D6"
);
}
}
}
#[test]
fn rotations_match_successive_composition() {
let rot60 = &GRID_TRANSFORMS_D6[1];
let identity = &GRID_TRANSFORMS_D6[0];
let mut acc = *identity;
for (k, expected) in GRID_TRANSFORMS_D6.iter().enumerate().take(6) {
assert_eq!(as_tuple(&acc), as_tuple(expected), "rot60^{k} mismatch");
acc = compose(&acc, rot60);
}
}
}