1use std::fmt::{Debug, Display};
2use std::ops::Sub;
3
4use num_traits::One;
5use rand::distributions::Distribution;
6use rand::seq::SliceRandom;
7use rand::Rng;
8
9use crate::util::coord::Coord;
10
11pub trait Symmetry: 'static + Default + Debug + Copy + Clone + Eq + PartialEq + Send + Sync {
15 fn all() -> &'static [Self];
16 fn inverse(self) -> Self;
17 fn is_unit() -> bool {
18 Self::all().len() == 1
19 }
20}
21
22#[derive(Debug)]
23pub struct SymmetryDistribution;
24
25impl<S: Symmetry + Sized> Distribution<S> for SymmetryDistribution {
26 fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> S {
27 *S::all().choose(rng).expect("A symmetry group cannot be empty")
28 }
29}
30
31#[derive(Debug, Copy, Clone, Eq, PartialEq)]
33pub struct UnitSymmetry;
34
35impl Symmetry for UnitSymmetry {
36 fn all() -> &'static [Self] {
37 &[Self]
38 }
39 fn inverse(self) -> Self {
40 Self
41 }
42}
43
44impl Default for UnitSymmetry {
45 fn default() -> Self {
46 UnitSymmetry
47 }
48}
49
50#[derive(Default, Debug, Copy, Clone, Eq, PartialEq)]
57pub struct D1Symmetry {
58 pub mirror: bool,
59}
60
61impl D1Symmetry {
62 pub const fn new(mirror: bool) -> Self {
63 D1Symmetry { mirror }
64 }
65
66 pub fn map_axis<V: Copy + Sub<Output = V> + One>(self, x: V, size: V) -> V {
67 let max = size - V::one();
68 if self.mirror {
69 max - x
70 } else {
71 x
72 }
73 }
74}
75
76impl Symmetry for D1Symmetry {
77 fn all() -> &'static [Self] {
78 const ALL: [D1Symmetry; 2] = [D1Symmetry::new(false), D1Symmetry::new(true)];
79 &ALL
80 }
81
82 fn inverse(self) -> Self {
83 self
84 }
85}
86
87#[derive(Default, Debug, Copy, Clone, Eq, PartialEq)]
93pub struct D4Symmetry {
94 pub transpose: bool,
95 pub flip_x: bool,
96 pub flip_y: bool,
97}
98
99impl D4Symmetry {
100 pub const fn new(transpose: bool, flip_x: bool, flip_y: bool) -> Self {
101 D4Symmetry {
102 transpose,
103 flip_x,
104 flip_y,
105 }
106 }
107
108 pub fn map_xy<V: Copy + Sub<Output = V> + One + Display>(self, mut x: V, mut y: V, size: V) -> (V, V) {
109 let max = size - V::one();
110
111 if self.transpose {
112 std::mem::swap(&mut x, &mut y)
113 };
114 if self.flip_x {
115 x = max - x
116 };
117 if self.flip_y {
118 y = max - y
119 };
120
121 (x, y)
122 }
123
124 pub fn map_coord<const X: u8, const Y: u8>(self, coord: Coord<X, Y>, size: u8) -> Coord<X, Y> {
125 assert!(size <= X && size <= Y);
126 let (x, y) = self.map_xy(coord.x(), coord.y(), size);
127 Coord::from_xy(x, y)
128 }
129}
130
131impl Symmetry for D4Symmetry {
132 fn all() -> &'static [Self] {
133 const ALL: [D4Symmetry; 8] = [
134 D4Symmetry::new(false, false, false),
135 D4Symmetry::new(false, false, true),
136 D4Symmetry::new(false, true, false),
137 D4Symmetry::new(false, true, true),
138 D4Symmetry::new(true, false, false),
139 D4Symmetry::new(true, false, true),
140 D4Symmetry::new(true, true, false),
141 D4Symmetry::new(true, true, true),
142 ];
143 &ALL
144 }
145
146 fn inverse(self) -> Self {
147 D4Symmetry::new(
148 self.transpose,
149 if self.transpose { self.flip_y } else { self.flip_x },
150 if self.transpose { self.flip_x } else { self.flip_y },
151 )
152 }
153}