1mod iterators;
54
55pub use iterators::{Reflections, Reversions, Rotations, RotationsAndReflections, SlidingO};
56
57#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
70pub enum AxisLocation {
71 Vertex(usize),
73 Edge(usize, usize),
76}
77
78impl AxisLocation {
79 #[must_use]
97 pub fn edge(i: usize, n: usize) -> Self {
98 assert!(n > 0, "ring size must be positive");
99 let ii = i % n;
100 AxisLocation::Edge(ii, (ii + 1) % n)
101 }
102}
103
104pub trait RingSeq<T> {
116 #[must_use]
135 fn index_from(&self, i: isize) -> usize;
136
137 #[must_use]
152 fn apply_o(&self, i: isize) -> &T;
153
154 #[must_use]
170 fn rotate_right(&self, step: isize) -> Vec<T>
171 where
172 T: Clone;
173
174 #[must_use]
187 fn rotate_left(&self, step: isize) -> Vec<T>
188 where
189 T: Clone;
190
191 #[must_use]
204 fn start_at(&self, i: isize) -> Vec<T>
205 where
206 T: Clone;
207
208 #[must_use]
221 fn reflect_at(&self, i: isize) -> Vec<T>
222 where
223 T: Clone;
224
225 #[must_use]
238 fn segment_length(&self, pred: impl Fn(&T) -> bool, from: isize) -> usize;
239
240 #[must_use]
251 fn take_while(&self, pred: impl Fn(&T) -> bool, from: isize) -> Vec<T>
252 where
253 T: Clone;
254
255 #[must_use]
266 fn drop_while(&self, pred: impl Fn(&T) -> bool, from: isize) -> Vec<T>
267 where
268 T: Clone;
269
270 #[must_use]
285 fn span(&self, pred: impl Fn(&T) -> bool, from: isize) -> (Vec<T>, Vec<T>)
286 where
287 T: Clone;
288
289 #[must_use]
306 fn slice_o(&self, from: isize, to: isize) -> Vec<T>
307 where
308 T: Clone;
309
310 #[must_use]
325 fn contains_slice(&self, slice: &[T]) -> bool
326 where
327 T: PartialEq;
328
329 #[must_use]
342 fn index_of_slice(&self, slice: &[T], from: isize) -> Option<usize>
343 where
344 T: PartialEq;
345
346 #[must_use]
362 fn last_index_of_slice(&self, slice: &[T], end: isize) -> Option<usize>
363 where
364 T: PartialEq;
365
366 #[must_use]
384 fn circular_windows(&self, size: usize, step: usize) -> SlidingO<T>
385 where
386 T: Clone;
387
388 #[must_use]
407 fn circular_chunks(&self, size: usize) -> SlidingO<T>
408 where
409 T: Clone;
410
411 #[must_use]
425 fn circular_enumerate(&self, from: isize) -> Vec<(T, usize)>
426 where
427 T: Clone;
428
429 #[must_use]
443 fn rotations(&self) -> Rotations<'_, T>;
444
445 #[must_use]
459 fn reflections(&self) -> Reflections<'_, T>;
460
461 #[must_use]
475 fn reversions(&self) -> Reversions<'_, T>;
476
477 #[must_use]
495 fn rotations_and_reflections(&self) -> RotationsAndReflections<'_, T>
496 where
497 T: Clone;
498
499 #[must_use]
516 fn is_rotation_of(&self, that: &[T]) -> bool
517 where
518 T: PartialEq;
519
520 #[must_use]
534 fn is_reflection_of(&self, that: &[T]) -> bool
535 where
536 T: PartialEq + Clone;
537
538 #[must_use]
548 fn is_reversion_of(&self, that: &[T]) -> bool
549 where
550 T: PartialEq;
551
552 #[must_use]
563 fn is_rotation_or_reflection_of(&self, that: &[T]) -> bool
564 where
565 T: PartialEq + Clone;
566
567 #[must_use]
581 fn rotation_offset(&self, that: &[T]) -> Option<usize>
582 where
583 T: PartialEq;
584
585 #[must_use]
599 fn hamming_distance(&self, that: &[T]) -> usize
600 where
601 T: PartialEq;
602
603 #[must_use]
619 fn min_rotational_hamming_distance(&self, that: &[T]) -> usize
620 where
621 T: PartialEq + Clone;
622
623 #[must_use]
639 fn canonical_index(&self) -> usize
640 where
641 T: Ord;
642
643 #[must_use]
657 fn canonical(&self) -> Vec<T>
658 where
659 T: Clone + Ord;
660
661 #[must_use]
677 fn bracelet(&self) -> Vec<T>
678 where
679 T: Clone + Ord;
680
681 #[must_use]
699 fn rotational_symmetry(&self) -> usize
700 where
701 T: PartialEq;
702
703 #[must_use]
719 fn symmetry_indices(&self) -> Vec<usize>
720 where
721 T: PartialEq;
722
723 #[must_use]
736 fn reflectional_symmetry_axes(&self) -> Vec<(AxisLocation, AxisLocation)>
737 where
738 T: PartialEq;
739
740 #[must_use]
751 fn symmetry(&self) -> usize
752 where
753 T: PartialEq;
754}
755
756impl<T> RingSeq<T> for [T] {
761 #[inline]
764 #[allow(clippy::cast_possible_wrap, clippy::cast_sign_loss)]
765 fn index_from(&self, i: isize) -> usize {
766 i.rem_euclid(self.len() as isize) as usize
767 }
768
769 #[inline]
770 fn apply_o(&self, i: isize) -> &T {
771 &self[self.index_from(i)]
772 }
773
774 fn rotate_right(&self, step: isize) -> Vec<T>
777 where
778 T: Clone,
779 {
780 if self.is_empty() {
781 return vec![];
782 }
783 let n = self.len();
784 let j = n - self.index_from(step);
785 let mut v = Vec::with_capacity(n);
786 v.extend_from_slice(&self[j..]);
787 v.extend_from_slice(&self[..j]);
788 v
789 }
790
791 #[inline]
792 fn rotate_left(&self, step: isize) -> Vec<T>
793 where
794 T: Clone,
795 {
796 self.rotate_right(-step)
797 }
798
799 #[inline]
800 fn start_at(&self, i: isize) -> Vec<T>
801 where
802 T: Clone,
803 {
804 self.rotate_left(i)
805 }
806
807 fn reflect_at(&self, i: isize) -> Vec<T>
808 where
809 T: Clone,
810 {
811 let mut v = self.start_at(i + 1);
812 v.reverse();
813 v
814 }
815
816 fn segment_length(&self, pred: impl Fn(&T) -> bool, from: isize) -> usize {
819 if self.is_empty() {
820 return 0;
821 }
822 let n = self.len();
823 let start = self.index_from(from);
824 let mut count = 0;
825 for k in 0..n {
826 if pred(&self[(start + k) % n]) {
827 count += 1;
828 } else {
829 break;
830 }
831 }
832 count
833 }
834
835 fn take_while(&self, pred: impl Fn(&T) -> bool, from: isize) -> Vec<T>
836 where
837 T: Clone,
838 {
839 if self.is_empty() {
840 return vec![];
841 }
842 let n = self.len();
843 let start = self.index_from(from);
844 let mut result = Vec::new();
845 for k in 0..n {
846 let elem = &self[(start + k) % n];
847 if pred(elem) {
848 result.push(elem.clone());
849 } else {
850 break;
851 }
852 }
853 result
854 }
855
856 fn drop_while(&self, pred: impl Fn(&T) -> bool, from: isize) -> Vec<T>
857 where
858 T: Clone,
859 {
860 if self.is_empty() {
861 return vec![];
862 }
863 let n = self.len();
864 let start = self.index_from(from);
865 let prefix_len = self.segment_length(&pred, from);
866 let mut result = Vec::with_capacity(n - prefix_len);
867 for k in prefix_len..n {
868 result.push(self[(start + k) % n].clone());
869 }
870 result
871 }
872
873 fn span(&self, pred: impl Fn(&T) -> bool, from: isize) -> (Vec<T>, Vec<T>)
874 where
875 T: Clone,
876 {
877 if self.is_empty() {
878 return (vec![], vec![]);
879 }
880 let n = self.len();
881 let start = self.index_from(from);
882 let prefix_len = self.segment_length(&pred, from);
883 let mut taken = Vec::with_capacity(prefix_len);
884 for k in 0..prefix_len {
885 taken.push(self[(start + k) % n].clone());
886 }
887 let mut dropped = Vec::with_capacity(n - prefix_len);
888 for k in prefix_len..n {
889 dropped.push(self[(start + k) % n].clone());
890 }
891 (taken, dropped)
892 }
893
894 fn slice_o(&self, from: isize, to: isize) -> Vec<T>
895 where
896 T: Clone,
897 {
898 if self.is_empty() || from >= to {
899 return vec![];
900 }
901 #[allow(clippy::cast_sign_loss)] let length = (to - from) as usize;
903 let start = self.index_from(from);
904 self[start..]
905 .iter()
906 .chain(self.iter().cycle())
907 .take(length)
908 .cloned()
909 .collect()
910 }
911
912 fn contains_slice(&self, slice: &[T]) -> bool
913 where
914 T: PartialEq,
915 {
916 if slice.is_empty() {
917 return true;
918 }
919 if self.is_empty() {
920 return false;
921 }
922 let n = self.len();
923 let m = slice.len();
924 (0..n).any(|start| (0..m).all(|j| self[(start + j) % n] == slice[j]))
925 }
926
927 fn index_of_slice(&self, slice: &[T], from: isize) -> Option<usize>
928 where
929 T: PartialEq,
930 {
931 if slice.is_empty() {
932 return if self.is_empty() {
933 Some(0)
934 } else {
935 Some(self.index_from(from))
936 };
937 }
938 if self.is_empty() {
939 return None;
940 }
941 let n = self.len();
942 let m = slice.len();
943 let start = self.index_from(from);
944 for k in 0..n {
945 let i = (start + k) % n;
946 if (0..m).all(|j| self[(i + j) % n] == slice[j]) {
947 return Some(i);
948 }
949 }
950 None
951 }
952
953 fn last_index_of_slice(&self, slice: &[T], end: isize) -> Option<usize>
954 where
955 T: PartialEq,
956 {
957 if slice.is_empty() {
958 return if self.is_empty() {
959 Some(0)
960 } else {
961 Some(self.index_from(end))
962 };
963 }
964 if self.is_empty() {
965 return None;
966 }
967 let n = self.len();
968 let m = slice.len();
969 let end_idx = self.index_from(end);
970 for k in 0..n {
971 let i = (end_idx + n - k) % n;
972 if (0..m).all(|j| self[(i + j) % n] == slice[j]) {
973 return Some(i);
974 }
975 }
976 None
977 }
978
979 fn circular_windows(&self, size: usize, step: usize) -> SlidingO<T>
982 where
983 T: Clone,
984 {
985 assert!(size > 0, "window size must be positive");
986 assert!(step > 0, "step must be positive");
987 if self.is_empty() {
988 return SlidingO {
989 data: vec![],
990 window_size: size,
991 step,
992 pos: 0,
993 };
994 }
995 let total_len = step * (self.len() - 1) + size;
996 #[allow(clippy::cast_possible_wrap)]
997 SlidingO {
998 data: self.slice_o(0, total_len as isize),
999 window_size: size,
1000 step,
1001 pos: 0,
1002 }
1003 }
1004
1005 fn circular_chunks(&self, size: usize) -> SlidingO<T>
1006 where
1007 T: Clone,
1008 {
1009 assert!(size > 0, "group size must be positive");
1010 if self.is_empty() {
1011 return SlidingO {
1012 data: vec![],
1013 window_size: size,
1014 step: size,
1015 pos: 0,
1016 };
1017 }
1018 self.circular_windows(size, size)
1019 }
1020
1021 fn circular_enumerate(&self, from: isize) -> Vec<(T, usize)>
1022 where
1023 T: Clone,
1024 {
1025 let n = self.len();
1026 if n == 0 {
1027 return vec![];
1028 }
1029 let start = self.index_from(from);
1030 (0..n)
1031 .map(|k| {
1032 let idx = (start + k) % n;
1033 (self[idx].clone(), idx)
1034 })
1035 .collect()
1036 }
1037
1038 fn rotations(&self) -> Rotations<'_, T> {
1039 Rotations {
1040 ring: self,
1041 index: 0,
1042 total: if self.is_empty() { 1 } else { self.len() },
1043 }
1044 }
1045
1046 fn reflections(&self) -> Reflections<'_, T> {
1047 Reflections {
1048 ring: self,
1049 state: 0,
1050 }
1051 }
1052
1053 fn reversions(&self) -> Reversions<'_, T> {
1054 Reversions {
1055 ring: self,
1056 state: 0,
1057 }
1058 }
1059
1060 fn rotations_and_reflections(&self) -> RotationsAndReflections<'_, T>
1061 where
1062 T: Clone,
1063 {
1064 let reflected = self.reflect_at(0);
1065 RotationsAndReflections {
1066 ring: self,
1067 reflected,
1068 index: 0,
1069 total: if self.is_empty() { 1 } else { self.len() * 2 },
1070 }
1071 }
1072
1073 fn is_rotation_of(&self, that: &[T]) -> bool
1076 where
1077 T: PartialEq,
1078 {
1079 if self.len() != that.len() {
1080 return false;
1081 }
1082 if self.is_empty() {
1083 return true;
1084 }
1085 contains_as_rotation(self, that)
1086 }
1087
1088 fn is_reflection_of(&self, that: &[T]) -> bool
1089 where
1090 T: PartialEq + Clone,
1091 {
1092 if self.len() != that.len() {
1093 return false;
1094 }
1095 self == that || self.reflect_at(0) == that
1096 }
1097
1098 fn is_reversion_of(&self, that: &[T]) -> bool
1099 where
1100 T: PartialEq,
1101 {
1102 if self.len() != that.len() {
1103 return false;
1104 }
1105 self == that || self.iter().rev().eq(that.iter())
1106 }
1107
1108 fn is_rotation_or_reflection_of(&self, that: &[T]) -> bool
1109 where
1110 T: PartialEq + Clone,
1111 {
1112 if self.len() != that.len() {
1113 return false;
1114 }
1115 contains_as_rotation(self, that) || contains_as_rotation(&self.reflect_at(0), that)
1116 }
1117
1118 fn rotation_offset(&self, that: &[T]) -> Option<usize>
1119 where
1120 T: PartialEq,
1121 {
1122 if self.len() != that.len() {
1123 return None;
1124 }
1125 if self.is_empty() {
1126 return Some(0);
1127 }
1128 let n = self.len();
1129 (0..n).find(|&i| (0..n).all(|j| self[(i + j) % n] == that[j]))
1130 }
1131
1132 fn hamming_distance(&self, that: &[T]) -> usize
1133 where
1134 T: PartialEq,
1135 {
1136 assert_eq!(self.len(), that.len(), "sequences must have the same size");
1137 self.iter().zip(that.iter()).filter(|(a, b)| a != b).count()
1138 }
1139
1140 fn min_rotational_hamming_distance(&self, that: &[T]) -> usize
1141 where
1142 T: PartialEq + Clone,
1143 {
1144 assert_eq!(self.len(), that.len(), "sequences must have the same size");
1145 if self.is_empty() {
1146 return 0;
1147 }
1148 let n = self.len();
1149 (0..n)
1150 .map(|rot| (0..n).filter(|&j| self[(rot + j) % n] != that[j]).count())
1151 .min()
1152 .unwrap()
1153 }
1154
1155 fn canonical_index(&self) -> usize
1158 where
1159 T: Ord,
1160 {
1161 if self.len() <= 1 {
1162 0
1163 } else {
1164 booth_least_rotation(self)
1165 }
1166 }
1167
1168 fn canonical(&self) -> Vec<T>
1169 where
1170 T: Clone + Ord,
1171 {
1172 #[allow(clippy::cast_possible_wrap)]
1173 self.start_at(self.canonical_index() as isize)
1174 }
1175
1176 fn bracelet(&self) -> Vec<T>
1177 where
1178 T: Clone + Ord,
1179 {
1180 let a = self.canonical();
1181 let b = self.reflect_at(0).canonical();
1182 if a <= b {
1183 a
1184 } else {
1185 b
1186 }
1187 }
1188
1189 fn rotational_symmetry(&self) -> usize
1192 where
1193 T: PartialEq,
1194 {
1195 let n = self.len();
1196 if n < 2 {
1197 return 1;
1198 }
1199 let smallest_period = (1..=n)
1200 .find(|&shift| n % shift == 0 && (0..n - shift).all(|i| self[i] == self[i + shift]));
1201 n / smallest_period.unwrap_or(n)
1202 }
1203
1204 fn symmetry_indices(&self) -> Vec<usize>
1205 where
1206 T: PartialEq,
1207 {
1208 let n = self.len();
1209 if n == 0 {
1210 return vec![];
1211 }
1212 let reversed: Vec<&T> = self.iter().rev().collect();
1213 (0..n)
1214 .filter(|&shift| (0..n).all(|i| self[i] == *reversed[(i + shift) % n]))
1215 .collect()
1216 }
1217
1218 fn reflectional_symmetry_axes(&self) -> Vec<(AxisLocation, AxisLocation)>
1219 where
1220 T: PartialEq,
1221 {
1222 let n = self.len();
1223 if n == 0 {
1224 return vec![];
1225 }
1226
1227 #[allow(clippy::cast_possible_wrap, clippy::cast_sign_loss)]
1228 self.symmetry_indices()
1229 .into_iter()
1230 .map(|shift| {
1231 let raw_k = (n as isize - 1 - shift as isize) % n as isize;
1232 let k = if raw_k < 0 {
1233 (raw_k + n as isize) as usize
1234 } else {
1235 raw_k as usize
1236 };
1237
1238 if n % 2 != 0 {
1239 let v = (k * (n + 1) / 2) % n;
1241 let opp = (v + n / 2) % n;
1242 (AxisLocation::Vertex(v), AxisLocation::edge(opp, n))
1243 } else if k % 2 == 0 {
1244 let v1 = k / 2;
1246 let v2 = (v1 + n / 2) % n;
1247 (AxisLocation::Vertex(v1), AxisLocation::Vertex(v2))
1248 } else {
1249 let e1 = (k - 1) / 2;
1251 let e2 = (e1 + n / 2) % n;
1252 (AxisLocation::edge(e1, n), AxisLocation::edge(e2, n))
1253 }
1254 })
1255 .collect()
1256 }
1257
1258 fn symmetry(&self) -> usize
1259 where
1260 T: PartialEq,
1261 {
1262 self.symmetry_indices().len()
1263 }
1264}
1265
1266fn contains_as_rotation<T: PartialEq>(ring: &[T], that: &[T]) -> bool {
1274 if ring.is_empty() {
1275 return true;
1276 }
1277 let n = ring.len();
1278 let doubled_len = 2 * n - 1;
1281 (0..n).any(|start| {
1282 (0..n).all(|j| {
1283 let idx = (start + j) % doubled_len;
1284 let elem = if idx < n { &ring[idx] } else { &ring[idx - n] };
1285 *elem == that[j]
1286 })
1287 })
1288}
1289
1290#[allow(
1293 clippy::cast_sign_loss,
1294 clippy::cast_possible_wrap,
1295 clippy::many_single_char_names
1296)]
1297fn booth_least_rotation<T: Ord>(s: &[T]) -> usize {
1298 let n = s.len();
1299 let len = 2 * n;
1300 let mut f: Vec<isize> = vec![-1; len];
1301 let mut k: usize = 0;
1302 let at = |idx: usize| &s[idx % n];
1303
1304 for j in 1..len {
1305 let sj = at(j);
1306 let mut i = f[j - k - 1];
1307 while i != -1 && at(k + i as usize + 1) != sj {
1308 if sj < at(k + i as usize + 1) {
1309 k = j - i as usize - 1;
1310 }
1311 i = f[i as usize];
1312 }
1313 if i == -1 && at(k) != sj {
1314 if sj < at(k) {
1315 k = j;
1316 }
1317 f[j - k] = -1;
1318 } else {
1319 f[j - k] = i + 1;
1320 }
1321 }
1322 k
1323}
1324
1325#[cfg(test)]
1330mod tests {
1331 use super::*;
1332
1333 #[test]
1336 fn index_from_positive() {
1337 assert_eq!([10, 20, 30].index_from(0), 0);
1338 assert_eq!([10, 20, 30].index_from(2), 2);
1339 assert_eq!([10, 20, 30].index_from(3), 0);
1340 assert_eq!([10, 20, 30].index_from(7), 1);
1341 }
1342
1343 #[test]
1344 fn index_from_negative() {
1345 assert_eq!([10, 20, 30].index_from(-1), 2);
1346 assert_eq!([10, 20, 30].index_from(-3), 0);
1347 assert_eq!([10, 20, 30].index_from(-4), 2);
1348 }
1349
1350 #[test]
1351 #[should_panic]
1352 fn index_from_empty_panics() {
1353 let empty: &[i32] = &[];
1354 let _ = empty.index_from(0);
1355 }
1356
1357 #[test]
1358 fn apply_o_wraps() {
1359 assert_eq!(*[10, 20, 30].apply_o(0), 10);
1360 assert_eq!(*[10, 20, 30].apply_o(3), 10);
1361 assert_eq!(*[10, 20, 30].apply_o(-1), 30);
1362 }
1363
1364 #[test]
1365 #[should_panic]
1366 fn apply_o_empty_panics() {
1367 let empty: &[i32] = &[];
1368 let _ = empty.apply_o(0);
1369 }
1370
1371 #[test]
1372 fn apply_o_single_element() {
1373 assert_eq!(*[42].apply_o(0), 42);
1374 assert_eq!(*[42].apply_o(100), 42);
1375 assert_eq!(*[42].apply_o(-99), 42);
1376 }
1377
1378 #[test]
1381 fn rotate_right_basic() {
1382 assert_eq!([0, 1, 2].rotate_right(1), vec![2, 0, 1]);
1383 assert_eq!([0, 1, 2].rotate_right(2), vec![1, 2, 0]);
1384 assert_eq!([0, 1, 2].rotate_right(3), vec![0, 1, 2]);
1385 }
1386
1387 #[test]
1388 fn rotate_right_negative() {
1389 assert_eq!([0, 1, 2].rotate_right(-1), vec![1, 2, 0]);
1390 }
1391
1392 #[test]
1393 fn rotate_right_empty() {
1394 let empty: &[i32] = &[];
1395 assert_eq!(empty.rotate_right(5), Vec::<i32>::new());
1396 }
1397
1398 #[test]
1399 fn rotate_left_basic() {
1400 assert_eq!([0, 1, 2].rotate_left(1), vec![1, 2, 0]);
1401 }
1402
1403 #[test]
1404 fn start_at_basic() {
1405 assert_eq!([0, 1, 2].start_at(1), vec![1, 2, 0]);
1406 assert_eq!([0, 1, 2].start_at(-1), vec![2, 0, 1]);
1407 }
1408
1409 #[test]
1410 fn reflect_at_basic() {
1411 assert_eq!([0, 1, 2].reflect_at(0), vec![0, 2, 1]);
1412 assert_eq!([0, 1, 2].reflect_at(1), vec![1, 0, 2]);
1413 }
1414
1415 #[test]
1416 fn reflect_at_empty() {
1417 let empty: &[i32] = &[];
1418 assert_eq!(empty.reflect_at(0), Vec::<i32>::new());
1419 }
1420
1421 #[test]
1422 fn reflect_at_single() {
1423 assert_eq!([42].reflect_at(0), vec![42]);
1424 }
1425
1426 #[test]
1429 fn segment_length_basic() {
1430 assert_eq!([0, 1, 2].segment_length(|x| x % 2 == 0, 2), 2);
1431 assert_eq!([0, 1, 2].segment_length(|_| true, 0), 3);
1432 assert_eq!([0, 1, 2].segment_length(|_| false, 0), 0);
1433 }
1434
1435 #[test]
1436 fn segment_length_empty() {
1437 let empty: &[i32] = &[];
1438 assert_eq!(empty.segment_length(|_| true, 0), 0);
1439 }
1440
1441 #[test]
1442 fn take_while_basic() {
1443 assert_eq!([0, 1, 2, 3, 4].take_while(|&x| x < 3, 1), vec![1, 2]);
1444 }
1445
1446 #[test]
1447 fn drop_while_basic() {
1448 assert_eq!([0, 1, 2, 3, 4].drop_while(|&x| x < 3, 1), vec![3, 4, 0]);
1449 }
1450
1451 #[test]
1452 fn span_basic() {
1453 let (a, b) = [0, 1, 2, 3, 4].span(|&x| x < 3, 1);
1454 assert_eq!(a, vec![1, 2]);
1455 assert_eq!(b, vec![3, 4, 0]);
1456 }
1457
1458 #[test]
1459 fn span_all_pass() {
1460 let (a, b) = [1, 2, 3].span(|_| true, 0);
1461 assert_eq!(a, vec![1, 2, 3]);
1462 assert!(b.is_empty());
1463 }
1464
1465 #[test]
1466 fn span_none_pass() {
1467 let (a, b) = [1, 2, 3].span(|_| false, 0);
1468 assert!(a.is_empty());
1469 assert_eq!(b, vec![1, 2, 3]);
1470 }
1471
1472 #[test]
1473 fn slice_o_basic() {
1474 assert_eq!([0, 1, 2].slice_o(1, 3), vec![1, 2]);
1475 }
1476
1477 #[test]
1478 fn slice_o_wrapping() {
1479 assert_eq!([0, 1, 2].slice_o(-1, 4), vec![2, 0, 1, 2, 0]);
1480 }
1481
1482 #[test]
1483 fn slice_o_empty_result() {
1484 assert_eq!([0, 1, 2].slice_o(3, 3), Vec::<i32>::new());
1485 assert_eq!([0, 1, 2].slice_o(5, 2), Vec::<i32>::new());
1486 }
1487
1488 #[test]
1489 fn slice_o_empty_ring() {
1490 let empty: &[i32] = &[];
1491 assert_eq!(empty.slice_o(0, 5), Vec::<i32>::new());
1492 }
1493
1494 #[test]
1495 fn contains_slice_basic() {
1496 assert!([0, 1, 2].contains_slice(&[2, 0]));
1497 assert!([0, 1, 2].contains_slice(&[2, 0, 1, 2, 0]));
1498 assert!(![0, 1, 2].contains_slice(&[1, 0]));
1499 }
1500
1501 #[test]
1502 fn contains_slice_empty_slice() {
1503 assert!([0, 1, 2].contains_slice(&[]));
1504 }
1505
1506 #[test]
1507 fn contains_slice_empty_ring() {
1508 let empty: &[i32] = &[];
1509 assert!(!empty.contains_slice(&[1]));
1510 assert!(empty.contains_slice(&[]));
1511 }
1512
1513 #[test]
1514 fn index_of_slice_basic() {
1515 assert_eq!([0, 1, 2].index_of_slice(&[2, 0, 1, 2, 0], 0), Some(2));
1516 assert_eq!([0, 1, 2].index_of_slice(&[0, 1], 0), Some(0));
1517 assert_eq!([0, 1, 2].index_of_slice(&[9], 0), None);
1518 }
1519
1520 #[test]
1521 fn index_of_slice_with_from() {
1522 assert_eq!([0, 1, 2, 0, 1, 2].index_of_slice(&[0, 1], 1), Some(3));
1523 }
1524
1525 #[test]
1526 fn last_index_of_slice_basic() {
1527 assert_eq!([0, 1, 2, 0, 1, 2].last_index_of_slice(&[2, 0], -1), Some(5));
1528 }
1529
1530 #[test]
1533 fn circular_windows_basic() {
1534 let windows: Vec<_> = [0, 1, 2].circular_windows(2, 1).collect();
1535 assert_eq!(windows, vec![vec![0, 1], vec![1, 2], vec![2, 0]]);
1536 }
1537
1538 #[test]
1539 fn circular_windows_empty() {
1540 let windows: Vec<Vec<i32>> = ([] as [i32; 0]).circular_windows(2, 1).collect();
1541 assert!(windows.is_empty());
1542 }
1543
1544 #[test]
1545 fn circular_windows_exact_size() {
1546 let iter = [0, 1, 2].circular_windows(2, 1);
1547 assert_eq!(iter.len(), 3);
1548 }
1549
1550 #[test]
1551 #[should_panic]
1552 fn circular_windows_zero_size_panics() {
1553 let _ = [0, 1, 2].circular_windows(0, 1);
1554 }
1555
1556 #[test]
1557 #[should_panic]
1558 fn circular_windows_zero_step_panics() {
1559 let _ = [0, 1, 2].circular_windows(1, 0);
1560 }
1561
1562 #[test]
1563 fn circular_chunks_basic() {
1564 let groups: Vec<_> = [0, 1, 2, 3, 4].circular_chunks(2).collect();
1565 assert_eq!(
1566 groups,
1567 vec![vec![0, 1], vec![2, 3], vec![4, 0], vec![1, 2], vec![3, 4]]
1568 );
1569 }
1570
1571 #[test]
1572 fn circular_chunks_evenly_divisible() {
1573 let groups: Vec<_> = [0, 1, 2, 3].circular_chunks(2).collect();
1574 assert_eq!(groups, vec![vec![0, 1], vec![2, 3], vec![0, 1], vec![2, 3]]);
1575 }
1576
1577 #[test]
1578 fn circular_enumerate_basic() {
1579 assert_eq!(
1580 ['a', 'b', 'c'].circular_enumerate(1),
1581 vec![('b', 1), ('c', 2), ('a', 0)]
1582 );
1583 }
1584
1585 #[test]
1586 fn circular_enumerate_empty() {
1587 let empty: &[i32] = &[];
1588 assert!(empty.circular_enumerate(0).is_empty());
1589 }
1590
1591 #[test]
1592 fn rotations_basic() {
1593 let rots: Vec<_> = [0, 1, 2].rotations().collect();
1594 assert_eq!(rots, vec![vec![0, 1, 2], vec![1, 2, 0], vec![2, 0, 1]]);
1595 }
1596
1597 #[test]
1598 fn rotations_empty() {
1599 let empty: &[i32] = &[];
1600 let rots: Vec<_> = empty.rotations().collect();
1601 assert_eq!(rots, vec![Vec::<i32>::new()]);
1602 }
1603
1604 #[test]
1605 fn rotations_exact_size() {
1606 assert_eq!([0, 1, 2].rotations().len(), 3);
1607 let empty: &[i32] = &[];
1608 assert_eq!(empty.rotations().len(), 1);
1609 }
1610
1611 #[test]
1612 fn reflections_basic() {
1613 let refs: Vec<_> = [0, 1, 2].reflections().collect();
1614 assert_eq!(refs, vec![vec![0, 1, 2], vec![0, 2, 1]]);
1615 }
1616
1617 #[test]
1618 fn reflections_empty() {
1619 let empty: &[i32] = &[];
1620 let refs: Vec<_> = empty.reflections().collect();
1621 assert_eq!(refs, vec![Vec::<i32>::new()]);
1622 }
1623
1624 #[test]
1625 fn reversions_basic() {
1626 let revs: Vec<_> = [0, 1, 2].reversions().collect();
1627 assert_eq!(revs, vec![vec![0, 1, 2], vec![2, 1, 0]]);
1628 }
1629
1630 #[test]
1631 fn rotations_and_reflections_basic() {
1632 let all: Vec<_> = [0, 1, 2].rotations_and_reflections().collect();
1633 assert_eq!(
1634 all,
1635 vec![
1636 vec![0, 1, 2],
1637 vec![1, 2, 0],
1638 vec![2, 0, 1],
1639 vec![0, 2, 1],
1640 vec![2, 1, 0],
1641 vec![1, 0, 2],
1642 ]
1643 );
1644 }
1645
1646 #[test]
1647 fn rotations_and_reflections_empty() {
1648 let empty: &[i32] = &[];
1649 let all: Vec<_> = empty.rotations_and_reflections().collect();
1650 assert_eq!(all, vec![Vec::<i32>::new()]);
1651 }
1652
1653 #[test]
1654 fn rotations_and_reflections_exact_size() {
1655 assert_eq!([0, 1, 2].rotations_and_reflections().len(), 6);
1656 }
1657
1658 #[test]
1661 fn is_rotation_of_true() {
1662 assert!([0, 1, 2].is_rotation_of(&[1, 2, 0]));
1663 assert!([0, 1, 2].is_rotation_of(&[0, 1, 2]));
1664 }
1665
1666 #[test]
1667 fn is_rotation_of_false() {
1668 assert!(![0, 1, 2].is_rotation_of(&[0, 2, 1]));
1669 assert!(![0, 1, 2].is_rotation_of(&[0, 1]));
1670 }
1671
1672 #[test]
1673 fn is_rotation_of_empty() {
1674 let empty: &[i32] = &[];
1675 assert!(empty.is_rotation_of(&[]));
1676 }
1677
1678 #[test]
1679 fn is_reflection_of_true() {
1680 assert!([0, 1, 2].is_reflection_of(&[0, 2, 1]));
1681 assert!([0, 1, 2].is_reflection_of(&[0, 1, 2])); }
1683
1684 #[test]
1685 fn is_reflection_of_false() {
1686 assert!(![0, 1, 2].is_reflection_of(&[1, 0, 2]));
1687 }
1688
1689 #[test]
1690 fn is_reversion_of_true() {
1691 assert!([0, 1, 2].is_reversion_of(&[2, 1, 0]));
1692 assert!([0, 1, 2].is_reversion_of(&[0, 1, 2])); }
1694
1695 #[test]
1696 fn is_reversion_of_false() {
1697 assert!(![0, 1, 2].is_reversion_of(&[0, 2, 1]));
1698 }
1699
1700 #[test]
1701 fn is_rotation_or_reflection_of_true() {
1702 assert!([0, 1, 2].is_rotation_or_reflection_of(&[2, 0, 1])); assert!([0, 1, 2].is_rotation_or_reflection_of(&[0, 2, 1])); assert!([0, 1, 2].is_rotation_or_reflection_of(&[1, 0, 2])); }
1706
1707 #[test]
1708 fn align_to_found() {
1709 assert_eq!([0, 1, 2].rotation_offset(&[2, 0, 1]), Some(2));
1710 assert_eq!([0, 1, 2].rotation_offset(&[0, 1, 2]), Some(0));
1711 }
1712
1713 #[test]
1714 fn align_to_not_found() {
1715 assert_eq!([0, 1, 2].rotation_offset(&[0, 2, 1]), None);
1716 }
1717
1718 #[test]
1719 fn align_to_different_sizes() {
1720 assert_eq!([0, 1, 2].rotation_offset(&[0, 1]), None);
1721 }
1722
1723 #[test]
1724 fn align_to_empty() {
1725 let empty: &[i32] = &[];
1726 assert_eq!(empty.rotation_offset(&[]), Some(0));
1727 }
1728
1729 #[test]
1730 fn hamming_distance_basic() {
1731 assert_eq!([1, 0, 1, 1].hamming_distance(&[1, 1, 0, 1]), 2);
1732 assert_eq!([1, 2, 3].hamming_distance(&[1, 2, 3]), 0);
1733 }
1734
1735 #[test]
1736 #[should_panic(expected = "sequences must have the same size")]
1737 fn hamming_distance_different_sizes_panics() {
1738 let _ = [1, 2, 3].hamming_distance(&[1, 2]);
1739 }
1740
1741 #[test]
1742 fn min_rotational_hamming_distance_basic() {
1743 assert_eq!(
1744 [1, 0, 1, 1].min_rotational_hamming_distance(&[1, 1, 0, 1]),
1745 0
1746 );
1747 }
1748
1749 #[test]
1750 fn min_rotational_hamming_distance_nonzero() {
1751 assert_eq!(
1752 [0, 0, 0, 1].min_rotational_hamming_distance(&[1, 1, 0, 0]),
1753 1
1754 );
1755 }
1756
1757 #[test]
1758 fn min_rotational_hamming_distance_empty() {
1759 let empty: &[i32] = &[];
1760 assert_eq!(empty.min_rotational_hamming_distance(&[]), 0);
1761 }
1762
1763 #[test]
1766 fn canonical_index_basic() {
1767 assert_eq!([2, 0, 1].canonical_index(), 1);
1768 assert_eq!([0, 1, 2].canonical_index(), 0);
1769 }
1770
1771 #[test]
1772 fn canonical_index_empty_and_single() {
1773 let empty: &[i32] = &[];
1774 assert_eq!(empty.canonical_index(), 0);
1775 assert_eq!([42].canonical_index(), 0);
1776 }
1777
1778 #[test]
1779 fn canonical_basic() {
1780 assert_eq!([2, 0, 1].canonical(), vec![0, 1, 2]);
1781 assert_eq!([0, 1, 2].canonical(), vec![0, 1, 2]);
1782 }
1783
1784 #[test]
1785 fn canonical_all_equal() {
1786 assert_eq!([5, 5, 5].canonical(), vec![5, 5, 5]);
1787 }
1788
1789 #[test]
1790 fn canonical_rotations_are_equal() {
1791 let ring = [3, 1, 4, 1, 5];
1792 let canon = ring.canonical();
1793 for rot in ring.rotations() {
1794 assert_eq!(rot.canonical(), canon);
1795 }
1796 }
1797
1798 #[test]
1799 fn bracelet_basic() {
1800 assert_eq!([2, 1, 0].bracelet(), vec![0, 1, 2]);
1801 assert_eq!([0, 1, 2].bracelet(), vec![0, 1, 2]);
1802 }
1803
1804 #[test]
1805 fn bracelet_reflection_equivalence() {
1806 let a = [0, 1, 2];
1807 let b = [0, 2, 1];
1808 assert_eq!(a.bracelet(), b.bracelet());
1809 }
1810
1811 #[test]
1814 fn rotational_symmetry_basic() {
1815 assert_eq!([0, 1, 2, 0, 1, 2].rotational_symmetry(), 2);
1816 assert_eq!([0, 1, 2].rotational_symmetry(), 1);
1817 assert_eq!([5, 5, 5].rotational_symmetry(), 3);
1818 }
1819
1820 #[test]
1821 fn rotational_symmetry_small() {
1822 let empty: &[i32] = &[];
1823 assert_eq!(empty.rotational_symmetry(), 1);
1824 assert_eq!([42].rotational_symmetry(), 1);
1825 assert_eq!([1, 1].rotational_symmetry(), 2);
1826 assert_eq!([1, 2].rotational_symmetry(), 1);
1827 }
1828
1829 #[test]
1830 fn symmetry_indices_basic() {
1831 assert_eq!(
1832 [2, 1, 2, 2, 1, 2, 2, 1, 2, 2, 1, 2].symmetry_indices(),
1833 vec![0, 3, 6, 9]
1834 );
1835 }
1836
1837 #[test]
1838 fn symmetry_indices_none() {
1839 assert!([0, 1, 2].symmetry_indices().is_empty());
1840 }
1841
1842 #[test]
1843 fn symmetry_indices_palindrome() {
1844 assert_eq!([0, 1, 0].symmetry_indices(), vec![0]);
1845 }
1846
1847 #[test]
1848 fn symmetry_basic() {
1849 assert_eq!([2, 1, 2, 2, 1, 2, 2, 1, 2, 2, 1, 2].symmetry(), 4);
1850 assert_eq!([0, 1, 2].symmetry(), 0);
1851 }
1852
1853 #[test]
1854 fn symmetry_empty() {
1855 let empty: &[i32] = &[];
1856 assert_eq!(empty.symmetry(), 0);
1857 }
1858
1859 #[test]
1860 fn reflectional_symmetry_axes_odd() {
1861 let axes = [0, 1, 0].reflectional_symmetry_axes();
1862 assert_eq!(axes.len(), 1);
1863 assert_eq!(axes[0], (AxisLocation::Vertex(1), AxisLocation::Edge(2, 0)));
1864 }
1865
1866 #[test]
1867 fn reflectional_symmetry_axes_even_vertices() {
1868 let axes = [0, 1, 1, 0].reflectional_symmetry_axes();
1870 assert!(!axes.is_empty());
1871 }
1872
1873 #[test]
1874 fn reflectional_symmetry_axes_empty() {
1875 let empty: &[i32] = &[];
1876 assert!(empty.reflectional_symmetry_axes().is_empty());
1877 }
1878
1879 #[test]
1882 fn axis_location_edge_constructor() {
1883 assert_eq!(AxisLocation::edge(2, 3), AxisLocation::Edge(2, 0));
1884 assert_eq!(AxisLocation::edge(0, 4), AxisLocation::Edge(0, 1));
1885 assert_eq!(AxisLocation::edge(3, 4), AxisLocation::Edge(3, 0));
1886 }
1887
1888 #[test]
1889 #[should_panic(expected = "ring size must be positive")]
1890 fn axis_location_edge_zero_size_panics() {
1891 let _ = AxisLocation::edge(0, 0);
1892 }
1893
1894 #[test]
1897 fn works_on_vec() {
1898 let v = vec![0, 1, 2];
1899 assert_eq!(v.rotate_right(1), vec![2, 0, 1]);
1900 assert!(v.is_rotation_of(&[1, 2, 0]));
1901 }
1902
1903 #[test]
1904 fn works_on_boxed_slice() {
1905 let b: Box<[i32]> = vec![0, 1, 2].into_boxed_slice();
1906 assert_eq!(b.rotate_right(1), vec![2, 0, 1]);
1907 }
1908
1909 #[test]
1910 fn chained_operations() {
1911 let result = [0, 1, 2].rotate_right(1).reflect_at(0);
1913 assert_eq!(result, vec![2, 1, 0]);
1916 }
1917
1918 #[test]
1921 fn booth_all_same() {
1922 assert_eq!(booth_least_rotation(&[5, 5, 5, 5]), 0);
1923 }
1924
1925 #[test]
1926 fn booth_sorted() {
1927 assert_eq!(booth_least_rotation(&[0, 1, 2, 3]), 0);
1928 }
1929
1930 #[test]
1931 fn booth_rotated() {
1932 assert_eq!(booth_least_rotation(&[2, 3, 0, 1]), 2);
1933 }
1934
1935 #[test]
1936 fn booth_two_elements() {
1937 assert_eq!(booth_least_rotation(&[1, 0]), 1);
1938 }
1939
1940 #[test]
1943 fn rotate_right_then_left_is_identity() {
1944 let ring = [3, 1, 4, 1, 5, 9];
1945 for step in -10..=10 {
1946 assert_eq!(
1947 ring.rotate_right(step).rotate_left(step),
1948 ring.to_vec(),
1949 "failed for step = {step}"
1950 );
1951 }
1952 }
1953
1954 #[test]
1955 fn start_at_then_apply_o_recovers_element() {
1956 let ring = [10, 20, 30, 40, 50];
1957 for i in -10..10 {
1958 let rotated = ring.start_at(i);
1959 assert_eq!(rotated[0], *ring.apply_o(i));
1960 }
1961 }
1962
1963 #[test]
1964 fn all_rotations_have_same_canonical() {
1965 let ring = [5, 3, 1, 4, 2];
1966 let canon = ring.canonical();
1967 for rot in ring.rotations() {
1968 assert_eq!(rot.canonical(), canon);
1969 }
1970 }
1971
1972 #[test]
1973 fn all_rotations_and_reflections_have_same_bracelet() {
1974 let ring = [5, 3, 1, 4, 2];
1975 let brace = ring.bracelet();
1976 for variant in ring.rotations_and_reflections() {
1977 assert_eq!(variant.bracelet(), brace);
1978 }
1979 }
1980
1981 #[test]
1982 fn is_rotation_of_all_rotations() {
1983 let ring = [1, 2, 3, 4];
1984 for rot in ring.rotations() {
1985 assert!(ring.is_rotation_of(&rot));
1986 }
1987 }
1988
1989 #[test]
1990 fn reflect_at_is_involution() {
1991 let ring = [0, 1, 2, 3];
1992 let once = ring.reflect_at(0);
1994 let twice = once.reflect_at(0);
1995 assert_eq!(twice, ring.to_vec());
1996 }
1997
1998 #[test]
1999 fn symmetry_count_divides_twice_size_for_nonempty() {
2000 for ring in [
2003 vec![0, 1, 2],
2004 vec![1, 1, 1],
2005 vec![0, 1, 0, 1],
2006 vec![0, 1, 2, 3],
2007 vec![2, 1, 2, 2, 1, 2, 2, 1, 2, 2, 1, 2],
2008 ] {
2009 let n = ring.len();
2010 let s = ring.symmetry();
2011 if s > 0 {
2012 assert_eq!(
2013 n % s,
2014 0,
2015 "symmetry {s} does not divide ring size {n} for {ring:?}"
2016 );
2017 }
2018 }
2019 }
2020}