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