1use core::iter::FusedIterator;
10
11#[cfg(feature = "alloc")]
12use alloc::vec::Vec;
13
14#[cfg(feature = "alloc")]
15use crate::AxisLocation;
16
17pub struct Circular<'a, T> {
36 ring: &'a [T],
37 offset: usize,
38 reflected: bool,
39}
40
41impl<T> Clone for Circular<'_, T> {
45 fn clone(&self) -> Self {
46 *self
47 }
48}
49impl<T> Copy for Circular<'_, T> {}
50
51impl<T: core::fmt::Debug> core::fmt::Debug for Circular<'_, T> {
52 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
53 f.debug_struct("Circular")
54 .field("ring", &self.ring)
55 .field("offset", &self.offset)
56 .field("reflected", &self.reflected)
57 .finish()
58 }
59}
60
61impl<'a, T> Circular<'a, T> {
62 #[inline]
64 pub(crate) fn new(ring: &'a [T]) -> Self {
65 Circular {
66 ring,
67 offset: 0,
68 reflected: false,
69 }
70 }
71
72 #[inline]
74 #[must_use]
75 pub fn len(self) -> usize {
76 self.ring.len()
77 }
78
79 #[inline]
81 #[must_use]
82 pub fn is_empty(self) -> bool {
83 self.ring.is_empty()
84 }
85
86 #[inline]
91 fn map_index(self, pos: usize) -> usize {
92 let n = self.ring.len();
93 debug_assert!(n > 0);
94 let p = pos % n;
95 if self.reflected {
96 (self.offset + n - p) % n
97 } else {
98 (self.offset + p) % n
99 }
100 }
101
102 #[inline]
120 #[must_use]
121 pub fn apply(self, i: isize) -> &'a T {
122 let n = self.ring.len();
123 assert!(n > 0, "cannot index into an empty ring");
124 let pos = i.rem_euclid(n as isize) as usize;
125 &self.ring[self.map_index(pos)]
126 }
127
128 #[inline]
146 #[must_use]
147 pub fn get(self, i: isize) -> Option<&'a T> {
148 if self.ring.is_empty() {
149 None
150 } else {
151 Some(self.apply(i))
152 }
153 }
154
155 #[inline]
170 #[must_use]
171 pub fn iter(self) -> CircularIter<'a, T> {
172 CircularIter {
173 view: self,
174 pos: 0,
175 remaining: self.ring.len(),
176 }
177 }
178
179 #[must_use]
198 pub fn start_at(self, i: isize) -> Circular<'a, T> {
199 let n = self.ring.len();
200 if n == 0 {
201 return self;
202 }
203 let i_pos = i.rem_euclid(n as isize) as usize;
204 let new_offset = if self.reflected {
205 (self.offset + n - i_pos) % n
206 } else {
207 (self.offset + i_pos) % n
208 };
209 Circular {
210 ring: self.ring,
211 offset: new_offset,
212 reflected: self.reflected,
213 }
214 }
215
216 #[inline]
232 #[must_use]
233 pub fn rotate_left(self, step: isize) -> Circular<'a, T> {
234 self.start_at(step)
235 }
236
237 #[inline]
253 #[must_use]
254 pub fn rotate_right(self, step: isize) -> Circular<'a, T> {
255 self.start_at(-step)
256 }
257
258 #[must_use]
274 pub fn reflect_at(self, i: isize) -> Circular<'a, T> {
275 let n = self.ring.len();
276 if n == 0 {
277 return self;
278 }
279 let i_pos = i.rem_euclid(n as isize) as usize;
280 let new_offset = if self.reflected {
281 (self.offset + n - i_pos) % n
282 } else {
283 (self.offset + i_pos) % n
284 };
285 Circular {
286 ring: self.ring,
287 offset: new_offset,
288 reflected: !self.reflected,
289 }
290 }
291
292 #[must_use]
313 pub fn slice(self, from: isize, to: isize) -> CircularIter<'a, T> {
314 if self.ring.is_empty() || to <= from {
315 return CircularIter {
316 view: self,
317 pos: 0,
318 remaining: 0,
319 };
320 }
321 let count = to.wrapping_sub(from) as usize;
324 let started = self.start_at(from);
325 CircularIter {
326 view: started,
327 pos: 0,
328 remaining: count,
329 }
330 }
331
332 pub fn take_while<P>(self, mut pred: P, from: isize) -> impl Iterator<Item = &'a T>
346 where
347 P: FnMut(&T) -> bool,
348 {
349 self.start_at(from).iter().take_while(move |x| pred(*x))
350 }
351
352 pub fn drop_while<P>(self, mut pred: P, from: isize) -> impl Iterator<Item = &'a T>
366 where
367 P: FnMut(&T) -> bool,
368 {
369 self.start_at(from).iter().skip_while(move |x| pred(*x))
370 }
371
372 #[must_use]
389 pub fn enumerate(self, from: isize) -> Enumerate<'a, T> {
390 let started = if self.ring.is_empty() {
391 self
392 } else {
393 self.start_at(from)
394 };
395 Enumerate {
396 view: started,
397 pos: 0,
398 remaining: started.ring.len(),
399 }
400 }
401
402 #[must_use]
423 pub fn rotations(self) -> Rotations<'a, T> {
424 let total = if self.ring.is_empty() {
425 1
426 } else {
427 self.ring.len()
428 };
429 Rotations {
430 base: self,
431 index: 0,
432 total,
433 }
434 }
435
436 #[must_use]
452 pub fn reflections(self) -> Reflections<'a, T> {
453 Reflections {
454 base: self,
455 state: 0,
456 }
457 }
458
459 #[must_use]
480 pub fn reversions(self) -> Reversions<'a, T> {
481 Reversions {
482 base: self,
483 state: 0,
484 }
485 }
486
487 #[must_use]
502 pub fn rotations_and_reflections(self) -> RotationsAndReflections<'a, T> {
503 let n = self.ring.len();
504 let total = if n == 0 { 1 } else { 2 * n };
505 let reflected = if n == 0 { self } else { self.reflect_at(0) };
506 RotationsAndReflections {
507 base: self,
508 reflected,
509 index: 0,
510 total,
511 n,
512 }
513 }
514
515 #[must_use]
538 pub fn windows(self, size: usize) -> Windows<'a, T> {
539 assert!(size > 0, "window size must be positive");
540 let total = if self.ring.is_empty() {
541 0
542 } else {
543 self.ring.len()
544 };
545 Windows {
546 base: self,
547 size,
548 step: 1,
549 index: 0,
550 total,
551 }
552 }
553
554 #[must_use]
577 pub fn index_from(self, i: isize) -> usize {
578 let n = self.ring.len();
579 assert!(n > 0, "cannot normalize against an empty ring");
580 let pos = i.rem_euclid(n as isize) as usize;
581 self.map_index(pos)
582 }
583
584 #[must_use]
590 pub fn is_rotation_of(self, other: &[T]) -> bool
591 where
592 T: PartialEq,
593 {
594 if self.len() != other.len() {
595 return false;
596 }
597 if self.ring.is_empty() {
598 return true;
599 }
600 self.rotations().any(|rot| rot.iter().eq(other.iter()))
601 }
602
603 #[must_use]
606 pub fn is_reflection_of(self, other: &[T]) -> bool
607 where
608 T: PartialEq,
609 {
610 if self.len() != other.len() {
611 return false;
612 }
613 self.iter().eq(other.iter()) || self.reflect_at(0).iter().eq(other.iter())
614 }
615
616 #[must_use]
618 pub fn is_reversion_of(self, other: &[T]) -> bool
619 where
620 T: PartialEq,
621 {
622 if self.len() != other.len() {
623 return false;
624 }
625 self.iter().eq(other.iter()) || self.reflect_at(-1).iter().eq(other.iter())
626 }
627
628 #[must_use]
631 pub fn is_rotation_or_reflection_of(self, other: &[T]) -> bool
632 where
633 T: PartialEq,
634 {
635 if self.len() != other.len() {
636 return false;
637 }
638 self.rotations_and_reflections()
639 .any(|v| v.iter().eq(other.iter()))
640 }
641
642 #[must_use]
645 pub fn rotation_offset(self, other: &[T]) -> Option<usize>
646 where
647 T: PartialEq,
648 {
649 if self.len() != other.len() {
650 return None;
651 }
652 if self.ring.is_empty() {
653 return Some(0);
654 }
655 let n = self.ring.len();
656 (0..n).find(|&i| self.start_at(i as isize).iter().eq(other.iter()))
657 }
658
659 #[must_use]
665 pub fn hamming_distance(self, other: &[T]) -> usize
666 where
667 T: PartialEq,
668 {
669 assert_eq!(self.len(), other.len(), "sequences must have the same size");
670 self.iter()
671 .zip(other.iter())
672 .filter(|(a, b)| a != b)
673 .count()
674 }
675
676 #[must_use]
683 pub fn min_rotational_hamming_distance(self, other: &[T]) -> usize
684 where
685 T: PartialEq,
686 {
687 assert_eq!(self.len(), other.len(), "sequences must have the same size");
688 let n = self.ring.len();
689 if n == 0 {
690 return 0;
691 }
692 let mut best = usize::MAX;
693 for rot in self.rotations() {
694 let mut count = 0;
696 for (a, b) in rot.iter().zip(other.iter()) {
697 if a != b {
698 count += 1;
699 if count >= best {
700 break;
701 }
702 }
703 }
704 if count < best {
705 best = count;
706 if best == 0 {
707 break;
708 }
709 }
710 }
711 best
712 }
713
714 #[must_use]
719 pub fn contains_slice(self, needle: &[T]) -> bool
720 where
721 T: PartialEq,
722 {
723 if needle.is_empty() {
724 return true;
725 }
726 if self.ring.is_empty() {
727 return false;
728 }
729 let n = self.ring.len();
730 let m = needle.len();
731 (0..n).any(|start| (0..m).all(|j| *self.apply((start + j) as isize) == needle[j]))
732 }
733
734 #[must_use]
740 pub fn index_of_slice(self, needle: &[T], from: isize) -> Option<usize>
741 where
742 T: PartialEq,
743 {
744 if self.ring.is_empty() {
745 return if needle.is_empty() { Some(0) } else { None };
746 }
747 let n = self.ring.len();
748 let start = from.rem_euclid(n as isize) as usize;
749 if needle.is_empty() {
750 return Some(start);
751 }
752 let m = needle.len();
753 (0..n)
754 .map(|k| (start + k) % n)
755 .find(|&i| (0..m).all(|j| *self.apply((i + j) as isize) == needle[j]))
756 }
757
758 #[must_use]
778 pub fn canonical_index(self) -> usize
779 where
780 T: Ord,
781 {
782 let n = self.ring.len();
783 if n <= 1 {
784 return 0;
785 }
786 let at = |idx: usize| &self.ring[self.map_index(idx)];
791 let (mut i, mut j, mut k) = (0usize, 1usize, 0usize);
792 while i < n && j < n && k < n {
793 let a = at(i + k);
794 let b = at(j + k);
795 if a == b {
796 k += 1;
797 continue;
798 }
799 if a > b {
800 i += k + 1;
801 } else {
802 j += k + 1;
803 }
804 if i == j {
805 j += 1;
806 }
807 k = 0;
808 }
809 core::cmp::min(i, j)
810 }
811
812 #[must_use]
821 pub fn rotational_symmetry(self) -> usize
822 where
823 T: PartialEq,
824 {
825 let n = self.ring.len();
826 if n < 2 {
827 return 1;
828 }
829 let smallest = (1..=n).find(|&d| {
830 n % d == 0
831 && (0..n - d).all(|i| *self.apply(i as isize) == *self.apply((i + d) as isize))
832 });
833 n / smallest.unwrap_or(n)
834 }
835
836 #[must_use]
843 pub fn symmetry(self) -> usize
844 where
845 T: PartialEq,
846 {
847 let n = self.ring.len();
848 if n == 0 {
849 return 0;
850 }
851 let reversed = self.reflect_at(-1);
852 (0..n)
853 .filter(|&shift| {
854 (0..n).all(|i| *self.apply(i as isize) == *reversed.apply((i + shift) as isize))
855 })
856 .count()
857 }
858
859 #[must_use]
884 pub fn chunks(self, size: usize) -> Chunks<'a, T> {
885 assert!(size > 0, "chunk size must be positive");
886 let n = self.ring.len();
887 let total = if n == 0 { 0 } else { (n + size - 1) / size };
888 Windows {
889 base: self,
890 size,
891 step: size,
892 index: 0,
893 total,
894 }
895 }
896}
897
898#[derive(Debug)]
907pub struct CircularIter<'a, T> {
908 view: Circular<'a, T>,
909 pos: usize,
910 remaining: usize,
911}
912
913impl<T> Clone for CircularIter<'_, T> {
914 fn clone(&self) -> Self {
915 CircularIter {
916 view: self.view,
917 pos: self.pos,
918 remaining: self.remaining,
919 }
920 }
921}
922
923impl<'a, T> Iterator for CircularIter<'a, T> {
924 type Item = &'a T;
925
926 #[inline]
927 fn next(&mut self) -> Option<&'a T> {
928 if self.remaining == 0 {
929 return None;
930 }
931 let item = &self.view.ring[self.view.map_index(self.pos)];
932 self.pos += 1;
933 self.remaining -= 1;
934 Some(item)
935 }
936
937 #[inline]
938 fn size_hint(&self) -> (usize, Option<usize>) {
939 (self.remaining, Some(self.remaining))
940 }
941
942 #[inline]
943 fn nth(&mut self, n: usize) -> Option<&'a T> {
944 if n >= self.remaining {
945 self.pos += self.remaining;
946 self.remaining = 0;
947 return None;
948 }
949 self.pos += n;
950 self.remaining -= n;
951 self.next()
952 }
953
954 #[inline]
955 fn count(self) -> usize {
956 self.remaining
957 }
958
959 #[inline]
960 fn last(mut self) -> Option<&'a T> {
961 self.next_back()
962 }
963}
964
965impl<'a, T> DoubleEndedIterator for CircularIter<'a, T> {
966 #[inline]
967 fn next_back(&mut self) -> Option<&'a T> {
968 if self.remaining == 0 {
969 return None;
970 }
971 self.remaining -= 1;
972 Some(&self.view.ring[self.view.map_index(self.pos + self.remaining)])
973 }
974}
975
976impl<T> ExactSizeIterator for CircularIter<'_, T> {}
977impl<T> FusedIterator for CircularIter<'_, T> {}
978
979impl<'a, T> IntoIterator for Circular<'a, T> {
981 type Item = &'a T;
982 type IntoIter = CircularIter<'a, T>;
983
984 #[inline]
985 fn into_iter(self) -> CircularIter<'a, T> {
986 self.iter()
987 }
988}
989
990impl<'a, T> IntoIterator for &Circular<'a, T> {
991 type Item = &'a T;
992 type IntoIter = CircularIter<'a, T>;
993
994 #[inline]
995 fn into_iter(self) -> CircularIter<'a, T> {
996 self.iter()
997 }
998}
999
1000impl<T> core::ops::Index<isize> for Circular<'_, T> {
1007 type Output = T;
1008
1009 #[inline]
1010 fn index(&self, i: isize) -> &T {
1011 self.apply(i)
1012 }
1013}
1014
1015#[derive(Debug)]
1025pub struct Enumerate<'a, T> {
1026 view: Circular<'a, T>,
1027 pos: usize,
1028 remaining: usize,
1029}
1030
1031impl<T> Clone for Enumerate<'_, T> {
1032 fn clone(&self) -> Self {
1033 Enumerate {
1034 view: self.view,
1035 pos: self.pos,
1036 remaining: self.remaining,
1037 }
1038 }
1039}
1040
1041impl<'a, T> Iterator for Enumerate<'a, T> {
1042 type Item = (&'a T, usize);
1043
1044 #[inline]
1045 fn next(&mut self) -> Option<Self::Item> {
1046 if self.remaining == 0 {
1047 return None;
1048 }
1049 let idx = self.view.map_index(self.pos);
1050 let item = &self.view.ring[idx];
1051 self.pos += 1;
1052 self.remaining -= 1;
1053 Some((item, idx))
1054 }
1055
1056 #[inline]
1057 fn size_hint(&self) -> (usize, Option<usize>) {
1058 (self.remaining, Some(self.remaining))
1059 }
1060}
1061
1062impl<T> ExactSizeIterator for Enumerate<'_, T> {}
1063impl<T> FusedIterator for Enumerate<'_, T> {}
1064
1065#[derive(Debug)]
1074pub struct Rotations<'a, T> {
1075 base: Circular<'a, T>,
1076 index: usize,
1077 total: usize,
1078}
1079
1080impl<T> Clone for Rotations<'_, T> {
1081 fn clone(&self) -> Self {
1082 Rotations {
1083 base: self.base,
1084 index: self.index,
1085 total: self.total,
1086 }
1087 }
1088}
1089
1090impl<'a, T> Iterator for Rotations<'a, T> {
1091 type Item = Circular<'a, T>;
1092
1093 fn next(&mut self) -> Option<Self::Item> {
1094 if self.index >= self.total {
1095 return None;
1096 }
1097 let item = if self.base.ring.is_empty() {
1098 self.base
1099 } else {
1100 self.base.start_at(self.index as isize)
1101 };
1102 self.index += 1;
1103 Some(item)
1104 }
1105
1106 fn size_hint(&self) -> (usize, Option<usize>) {
1107 let r = self.total - self.index;
1108 (r, Some(r))
1109 }
1110}
1111
1112impl<T> ExactSizeIterator for Rotations<'_, T> {}
1113impl<T> FusedIterator for Rotations<'_, T> {}
1114
1115#[derive(Debug)]
1125pub struct Reflections<'a, T> {
1126 base: Circular<'a, T>,
1127 state: u8,
1128}
1129
1130impl<T> Clone for Reflections<'_, T> {
1131 fn clone(&self) -> Self {
1132 Reflections {
1133 base: self.base,
1134 state: self.state,
1135 }
1136 }
1137}
1138
1139impl<'a, T> Iterator for Reflections<'a, T> {
1140 type Item = Circular<'a, T>;
1141
1142 fn next(&mut self) -> Option<Self::Item> {
1143 match self.state {
1144 0 => {
1145 self.state = if self.base.ring.is_empty() { 2 } else { 1 };
1146 Some(self.base)
1147 }
1148 1 => {
1149 self.state = 2;
1150 Some(self.base.reflect_at(0))
1151 }
1152 _ => None,
1153 }
1154 }
1155
1156 fn size_hint(&self) -> (usize, Option<usize>) {
1157 let r = match self.state {
1158 0 => {
1159 if self.base.ring.is_empty() {
1160 1
1161 } else {
1162 2
1163 }
1164 }
1165 1 => 1,
1166 _ => 0,
1167 };
1168 (r, Some(r))
1169 }
1170}
1171
1172impl<T> ExactSizeIterator for Reflections<'_, T> {}
1173impl<T> FusedIterator for Reflections<'_, T> {}
1174
1175#[derive(Debug)]
1184pub struct Reversions<'a, T> {
1185 base: Circular<'a, T>,
1186 state: u8,
1187}
1188
1189impl<T> Clone for Reversions<'_, T> {
1190 fn clone(&self) -> Self {
1191 Reversions {
1192 base: self.base,
1193 state: self.state,
1194 }
1195 }
1196}
1197
1198impl<'a, T> Iterator for Reversions<'a, T> {
1199 type Item = Circular<'a, T>;
1200
1201 fn next(&mut self) -> Option<Self::Item> {
1202 match self.state {
1203 0 => {
1204 self.state = if self.base.ring.is_empty() { 2 } else { 1 };
1205 Some(self.base)
1206 }
1207 1 => {
1208 self.state = 2;
1209 Some(self.base.reflect_at(-1))
1210 }
1211 _ => None,
1212 }
1213 }
1214
1215 fn size_hint(&self) -> (usize, Option<usize>) {
1216 let r = match self.state {
1217 0 => {
1218 if self.base.ring.is_empty() {
1219 1
1220 } else {
1221 2
1222 }
1223 }
1224 1 => 1,
1225 _ => 0,
1226 };
1227 (r, Some(r))
1228 }
1229}
1230
1231impl<T> ExactSizeIterator for Reversions<'_, T> {}
1232impl<T> FusedIterator for Reversions<'_, T> {}
1233
1234#[derive(Debug)]
1243pub struct RotationsAndReflections<'a, T> {
1244 base: Circular<'a, T>,
1245 reflected: Circular<'a, T>,
1246 index: usize,
1247 total: usize,
1248 n: usize,
1249}
1250
1251impl<T> Clone for RotationsAndReflections<'_, T> {
1252 fn clone(&self) -> Self {
1253 RotationsAndReflections {
1254 base: self.base,
1255 reflected: self.reflected,
1256 index: self.index,
1257 total: self.total,
1258 n: self.n,
1259 }
1260 }
1261}
1262
1263impl<'a, T> Iterator for RotationsAndReflections<'a, T> {
1264 type Item = Circular<'a, T>;
1265
1266 fn next(&mut self) -> Option<Self::Item> {
1267 if self.index >= self.total {
1268 return None;
1269 }
1270 if self.n == 0 {
1271 self.index += 1;
1272 return Some(self.base);
1273 }
1274 let item = if self.index < self.n {
1275 self.base.start_at(self.index as isize)
1276 } else {
1277 self.reflected.start_at((self.index - self.n) as isize)
1278 };
1279 self.index += 1;
1280 Some(item)
1281 }
1282
1283 fn size_hint(&self) -> (usize, Option<usize>) {
1284 let r = self.total - self.index;
1285 (r, Some(r))
1286 }
1287}
1288
1289impl<T> ExactSizeIterator for RotationsAndReflections<'_, T> {}
1290impl<T> FusedIterator for RotationsAndReflections<'_, T> {}
1291
1292#[derive(Debug)]
1302pub struct Windows<'a, T> {
1303 base: Circular<'a, T>,
1304 size: usize,
1305 step: usize,
1306 index: usize,
1307 total: usize,
1308}
1309
1310impl<T> Clone for Windows<'_, T> {
1311 fn clone(&self) -> Self {
1312 Windows {
1313 base: self.base,
1314 size: self.size,
1315 step: self.step,
1316 index: self.index,
1317 total: self.total,
1318 }
1319 }
1320}
1321
1322impl<'a, T> Iterator for Windows<'a, T> {
1323 type Item = CircularIter<'a, T>;
1324
1325 fn next(&mut self) -> Option<Self::Item> {
1326 if self.index >= self.total {
1327 return None;
1328 }
1329 let start = (self.index * self.step) as isize;
1330 let view = self.base.start_at(start);
1331 let iter = CircularIter {
1332 view,
1333 pos: 0,
1334 remaining: self.size,
1335 };
1336 self.index += 1;
1337 Some(iter)
1338 }
1339
1340 fn size_hint(&self) -> (usize, Option<usize>) {
1341 let r = self.total - self.index;
1342 (r, Some(r))
1343 }
1344}
1345
1346impl<T> ExactSizeIterator for Windows<'_, T> {}
1347impl<T> FusedIterator for Windows<'_, T> {}
1348
1349pub type Chunks<'a, T> = Windows<'a, T>;
1352
1353pub trait AsCircular<T> {
1372 fn circular(&self) -> Circular<'_, T>;
1374}
1375
1376impl<T> AsCircular<T> for [T] {
1377 #[inline]
1378 fn circular(&self) -> Circular<'_, T> {
1379 Circular::new(self)
1380 }
1381}
1382
1383#[cfg(feature = "alloc")]
1388impl<'a, T> Circular<'a, T> {
1389 #[must_use]
1391 pub fn to_vec(self) -> Vec<T>
1392 where
1393 T: Clone,
1394 {
1395 self.iter().cloned().collect()
1396 }
1397
1398 #[must_use]
1401 pub fn canonical(self) -> Vec<T>
1402 where
1403 T: Clone + Ord,
1404 {
1405 let idx = self.canonical_index();
1406 self.start_at(idx as isize).to_vec()
1407 }
1408
1409 #[must_use]
1413 pub fn bracelet(self) -> Vec<T>
1414 where
1415 T: Clone + Ord,
1416 {
1417 let a = self.canonical();
1418 let b = self.reflect_at(0).canonical();
1419 if a <= b {
1420 a
1421 } else {
1422 b
1423 }
1424 }
1425
1426 #[must_use]
1430 pub fn symmetry_indices(self) -> Vec<usize>
1431 where
1432 T: PartialEq,
1433 {
1434 let n = self.ring.len();
1435 if n == 0 {
1436 return Vec::new();
1437 }
1438 let reversed = self.reflect_at(-1);
1439 (0..n)
1440 .filter(|&shift| {
1441 (0..n).all(|i| *self.apply(i as isize) == *reversed.apply((i + shift) as isize))
1442 })
1443 .collect()
1444 }
1445
1446 #[must_use]
1450 pub fn reflectional_symmetry_axes(self) -> Vec<(AxisLocation, AxisLocation)>
1451 where
1452 T: PartialEq,
1453 {
1454 let n = self.ring.len();
1455 if n == 0 {
1456 return Vec::new();
1457 }
1458
1459 #[allow(clippy::cast_possible_wrap, clippy::cast_sign_loss)]
1460 self.symmetry_indices()
1461 .into_iter()
1462 .map(|shift| {
1463 let raw_k = (n as isize - 1 - shift as isize) % n as isize;
1464 let k = if raw_k < 0 {
1465 (raw_k + n as isize) as usize
1466 } else {
1467 raw_k as usize
1468 };
1469
1470 if n % 2 != 0 {
1471 let v = (k * (n + 1) / 2) % n;
1472 let opp = (v + n / 2) % n;
1473 (AxisLocation::Vertex(v), AxisLocation::edge(opp, n))
1474 } else if k % 2 == 0 {
1475 let v1 = k / 2;
1476 let v2 = (v1 + n / 2) % n;
1477 (AxisLocation::Vertex(v1), AxisLocation::Vertex(v2))
1478 } else {
1479 let e1 = (k - 1) / 2;
1480 let e2 = (e1 + n / 2) % n;
1481 (AxisLocation::edge(e1, n), AxisLocation::edge(e2, n))
1482 }
1483 })
1484 .collect()
1485 }
1486}
1487
1488#[cfg(test)]
1493mod tests {
1494 use super::*;
1495
1496 #[test]
1497 fn empty_ring_is_empty() {
1498 let empty: [i32; 0] = [];
1499 let c = empty.circular();
1500 assert_eq!(c.len(), 0);
1501 assert!(c.is_empty());
1502 assert_eq!(c.iter().count(), 0);
1503 }
1504
1505 #[test]
1506 fn len_and_is_empty() {
1507 let r = [1, 2, 3].circular();
1508 assert_eq!(r.len(), 3);
1509 assert!(!r.is_empty());
1510 }
1511
1512 #[test]
1513 fn apply_positive() {
1514 let r = [10, 20, 30].circular();
1515 assert_eq!(*r.apply(0), 10);
1516 assert_eq!(*r.apply(1), 20);
1517 assert_eq!(*r.apply(2), 30);
1518 }
1519
1520 #[test]
1521 fn apply_wraps_forward() {
1522 let r = [10, 20, 30].circular();
1523 assert_eq!(*r.apply(3), 10);
1524 assert_eq!(*r.apply(7), 20);
1525 }
1526
1527 #[test]
1528 fn apply_wraps_backward() {
1529 let r = [10, 20, 30].circular();
1530 assert_eq!(*r.apply(-1), 30);
1531 assert_eq!(*r.apply(-3), 10);
1532 assert_eq!(*r.apply(-4), 30);
1533 }
1534
1535 #[test]
1536 #[should_panic]
1537 fn apply_panics_on_empty() {
1538 let empty: [i32; 0] = [];
1539 let _ = empty.circular().apply(0);
1540 }
1541
1542 #[test]
1543 fn iter_yields_elements_in_order() {
1544 let r = [1, 2, 3].circular();
1545 let mut it = r.iter();
1546 assert_eq!(it.next(), Some(&1));
1547 assert_eq!(it.next(), Some(&2));
1548 assert_eq!(it.next(), Some(&3));
1549 assert_eq!(it.next(), None);
1550 }
1551
1552 #[test]
1553 fn iter_size_hint_and_exact_size() {
1554 let r = [1, 2, 3, 4].circular();
1555 let mut it = r.iter();
1556 assert_eq!(it.len(), 4);
1557 assert_eq!(it.size_hint(), (4, Some(4)));
1558 it.next();
1559 assert_eq!(it.len(), 3);
1560 }
1561
1562 #[test]
1563 fn iter_is_fused() {
1564 let r = [1, 2].circular();
1565 let mut it = r.iter();
1566 it.next();
1567 it.next();
1568 assert_eq!(it.next(), None);
1569 assert_eq!(it.next(), None); }
1571
1572 #[test]
1573 fn copy_semantics() {
1574 let r = [1, 2, 3].circular();
1575 let r2 = r; assert_eq!(r.len(), 3);
1577 assert_eq!(r2.len(), 3);
1578 }
1579
1580 #[test]
1582 fn copy_works_without_t_clone() {
1583 #[allow(dead_code)]
1584 struct NoClone(i32);
1585 let arr = [NoClone(1), NoClone(2)];
1586 let r = arr.circular();
1587 let r2 = r; assert_eq!(r.len(), 2);
1589 assert_eq!(r2.len(), 2);
1590 }
1591
1592 fn into_array<const N: usize>(c: Circular<'_, i32>) -> [i32; N] {
1595 let mut out = [0; N];
1596 for (slot, &x) in out.iter_mut().zip(c.iter()) {
1597 *slot = x;
1598 }
1599 out
1600 }
1601
1602 #[test]
1603 fn start_at_basic() {
1604 let r = [10, 20, 30, 40].circular();
1605 assert_eq!(into_array::<4>(r.start_at(0)), [10, 20, 30, 40]);
1606 assert_eq!(into_array::<4>(r.start_at(2)), [30, 40, 10, 20]);
1607 assert_eq!(into_array::<4>(r.start_at(-1)), [40, 10, 20, 30]);
1608 assert_eq!(into_array::<4>(r.start_at(5)), [20, 30, 40, 10]);
1609 }
1610
1611 #[test]
1612 fn start_at_empty() {
1613 let empty: [i32; 0] = [];
1614 assert_eq!(empty.circular().start_at(3).len(), 0);
1615 }
1616
1617 #[test]
1618 fn rotate_right_basic() {
1619 let r = [1, 2, 3, 4].circular();
1620 assert_eq!(into_array::<4>(r.rotate_right(0)), [1, 2, 3, 4]);
1621 assert_eq!(into_array::<4>(r.rotate_right(1)), [4, 1, 2, 3]);
1622 assert_eq!(into_array::<4>(r.rotate_right(-1)), [2, 3, 4, 1]);
1623 assert_eq!(into_array::<4>(r.rotate_right(5)), [4, 1, 2, 3]);
1624 }
1625
1626 #[test]
1627 fn rotate_left_basic() {
1628 let r = [1, 2, 3, 4].circular();
1629 assert_eq!(into_array::<4>(r.rotate_left(1)), [2, 3, 4, 1]);
1630 assert_eq!(into_array::<4>(r.rotate_left(-1)), [4, 1, 2, 3]);
1631 }
1632
1633 #[test]
1634 fn rotate_right_then_left_is_identity() {
1635 let ring = [1, 2, 3, 4, 5, 6, 7];
1636 let r = ring.circular();
1637 for step in -10..=10 {
1638 assert_eq!(
1639 into_array::<7>(r.rotate_right(step).rotate_left(step)),
1640 ring,
1641 "failed for step {}",
1642 step,
1643 );
1644 }
1645 }
1646
1647 #[test]
1648 fn reflect_at_basic() {
1649 let r = [10, 20, 30, 40].circular();
1650 assert_eq!(into_array::<4>(r.reflect_at(0)), [10, 40, 30, 20]);
1652 assert_eq!(into_array::<4>(r.reflect_at(2)), [30, 20, 10, 40]);
1654 }
1655
1656 #[test]
1657 fn reflect_at_is_involution() {
1658 let ring = [1, 2, 3, 4, 5, 6, 7];
1659 let r = ring.circular();
1660 for i in -3..10 {
1661 assert_eq!(
1662 into_array::<7>(r.reflect_at(i).reflect_at(i)),
1663 ring,
1664 "failed for i {}",
1665 i
1666 );
1667 }
1668 }
1669
1670 #[test]
1671 fn rotate_then_reflect_commutes() {
1672 let ring = [1, 2, 3, 4, 5];
1674 let r = ring.circular();
1675 for k in -7..=7 {
1676 assert_eq!(
1677 into_array::<5>(r.rotate_right(k).reflect_at(0)),
1678 into_array::<5>(r.reflect_at(0).rotate_left(k)),
1679 "failed for k {}",
1680 k,
1681 );
1682 }
1683 }
1684
1685 #[test]
1688 fn slice_within_one_lap() {
1689 let r = [0, 1, 2, 3, 4].circular();
1690 let v: [i32; 3] = {
1691 let mut out = [0; 3];
1692 for (s, &x) in out.iter_mut().zip(r.slice(1, 4)) {
1693 *s = x;
1694 }
1695 out
1696 };
1697 assert_eq!(v, [1, 2, 3]);
1698 }
1699
1700 #[test]
1701 fn slice_wraps() {
1702 let r = [0, 1, 2, 3, 4].circular();
1703 let mut out = [0; 5];
1704 for (s, &x) in out.iter_mut().zip(r.slice(2, 7)) {
1705 *s = x;
1706 }
1707 assert_eq!(out, [2, 3, 4, 0, 1]);
1708 }
1709
1710 #[test]
1711 fn slice_negative_from() {
1712 let r = [0, 1, 2, 3, 4].circular();
1713 let mut out = [0; 3];
1714 for (s, &x) in out.iter_mut().zip(r.slice(-2, 1)) {
1715 *s = x;
1716 }
1717 assert_eq!(out, [3, 4, 0]);
1718 }
1719
1720 #[test]
1721 fn slice_empty_range() {
1722 let r = [0, 1, 2, 3, 4].circular();
1723 assert_eq!(r.slice(3, 3).count(), 0);
1724 assert_eq!(r.slice(5, 2).count(), 0);
1725 }
1726
1727 #[test]
1728 fn slice_empty_ring() {
1729 let empty: [i32; 0] = [];
1730 assert_eq!(empty.circular().slice(0, 4).count(), 0);
1731 }
1732
1733 #[test]
1736 fn take_while_basic() {
1737 let r = [1, 2, 3, 4, 5].circular();
1738 let mut out = [0; 3];
1739 for (s, &x) in out.iter_mut().zip(r.take_while(|&x| x < 4, 0)) {
1740 *s = x;
1741 }
1742 assert_eq!(out, [1, 2, 3]);
1743 }
1744
1745 #[test]
1746 fn take_while_stops_after_one_lap() {
1747 let r = [1, 1, 1].circular();
1749 assert_eq!(r.take_while(|_| true, 0).count(), 3);
1750 }
1751
1752 #[test]
1753 fn drop_while_basic() {
1754 let r = [1, 2, 3, 4, 5].circular();
1755 let mut out = [0; 2];
1756 for (s, &x) in out.iter_mut().zip(r.drop_while(|&x| x < 4, 0)) {
1757 *s = x;
1758 }
1759 assert_eq!(out, [4, 5]);
1760 }
1761
1762 #[test]
1763 fn drop_while_all_dropped() {
1764 let r = [1, 2, 3].circular();
1765 assert_eq!(r.drop_while(|_| true, 0).count(), 0);
1766 }
1767
1768 #[test]
1771 fn enumerate_yields_ring_indices() {
1772 let r = [10, 20, 30].circular();
1773 let mut out: [(i32, usize); 3] = [(0, 0); 3];
1774 for (s, (&x, i)) in out.iter_mut().zip(r.enumerate(1)) {
1775 *s = (x, i);
1776 }
1777 assert_eq!(out, [(20, 1), (30, 2), (10, 0)]);
1778 }
1779
1780 #[test]
1781 fn enumerate_empty() {
1782 let empty: [i32; 0] = [];
1783 assert_eq!(empty.circular().enumerate(0).count(), 0);
1784 }
1785
1786 #[test]
1787 fn enumerate_is_exact_size() {
1788 let r = [10, 20, 30, 40].circular();
1789 let it = r.enumerate(2);
1790 assert_eq!(it.len(), 4);
1791 }
1792
1793 #[test]
1796 fn rotations_yields_n_views() {
1797 let ring = [1, 2, 3, 4];
1798 let r = ring.circular();
1799 let firsts: [i32; 4] = {
1800 let mut out = [0; 4];
1801 for (s, v) in out.iter_mut().zip(r.rotations()) {
1802 *s = *v.apply(0);
1803 }
1804 out
1805 };
1806 assert_eq!(firsts, [1, 2, 3, 4]);
1807 }
1808
1809 #[test]
1810 fn rotations_empty_yields_one() {
1811 let empty: [i32; 0] = [];
1812 assert_eq!(empty.circular().rotations().count(), 1);
1813 }
1814
1815 #[test]
1816 fn rotations_exact_size() {
1817 let r = [1, 2, 3, 4, 5].circular();
1818 let it = r.rotations();
1819 assert_eq!(it.len(), 5);
1820 }
1821
1822 #[test]
1823 fn rotations_preserves_reflected() {
1824 let r = [1, 2, 3].circular().reflect_at(0);
1826 let firsts: [i32; 3] = {
1827 let mut out = [0; 3];
1828 for (s, v) in out.iter_mut().zip(r.rotations()) {
1829 *s = *v.apply(0);
1830 }
1831 out
1832 };
1833 assert_eq!(firsts, [1, 3, 2]);
1835 }
1836
1837 #[test]
1838 fn reflections_yields_two() {
1839 let r = [1, 2, 3, 4].circular();
1840 let count = r.reflections().count();
1841 assert_eq!(count, 2);
1842 }
1843
1844 #[test]
1845 fn reflections_second_is_reflect_at_zero() {
1846 let r = [1, 2, 3, 4].circular();
1847 let mut it = r.reflections();
1848 let first = it.next().unwrap();
1849 let second = it.next().unwrap();
1850 let first_v: [i32; 4] = into_array(first);
1851 let second_v: [i32; 4] = into_array(second);
1852 assert_eq!(first_v, [1, 2, 3, 4]);
1853 assert_eq!(second_v, [1, 4, 3, 2]);
1854 }
1855
1856 #[test]
1857 fn reflections_empty_yields_one() {
1858 let empty: [i32; 0] = [];
1859 assert_eq!(empty.circular().reflections().count(), 1);
1860 }
1861
1862 #[test]
1863 fn reversions_yields_two() {
1864 let r = [1, 2, 3].circular();
1865 assert_eq!(r.reversions().count(), 2);
1866 }
1867
1868 #[test]
1869 fn reversions_second_is_reverse() {
1870 let r = [1, 2, 3, 4].circular();
1871 let mut it = r.reversions();
1872 let first = it.next().unwrap();
1873 let second = it.next().unwrap();
1874 let first_v: [i32; 4] = into_array(first);
1875 let second_v: [i32; 4] = into_array(second);
1876 assert_eq!(first_v, [1, 2, 3, 4]);
1877 assert_eq!(second_v, [4, 3, 2, 1]);
1878 }
1879
1880 #[test]
1881 fn rotations_and_reflections_yields_2n() {
1882 let r = [1, 2, 3, 4].circular();
1883 assert_eq!(r.rotations_and_reflections().count(), 8);
1884 }
1885
1886 #[test]
1887 fn rotations_and_reflections_distinct_views() {
1888 let r = [1, 2, 3, 4].circular();
1890 let all: [[i32; 4]; 8] = {
1891 let mut out = [[0; 4]; 8];
1892 for (s, v) in out.iter_mut().zip(r.rotations_and_reflections()) {
1893 *s = into_array(v);
1894 }
1895 out
1896 };
1897 assert_eq!(all[0], [1, 2, 3, 4]);
1898 assert_eq!(all[1], [2, 3, 4, 1]);
1899 assert_eq!(all[2], [3, 4, 1, 2]);
1900 assert_eq!(all[3], [4, 1, 2, 3]);
1901 assert_eq!(all[4], [1, 4, 3, 2]);
1903 assert_eq!(all[5], [4, 3, 2, 1]);
1904 assert_eq!(all[6], [3, 2, 1, 4]);
1905 assert_eq!(all[7], [2, 1, 4, 3]);
1906 }
1907
1908 #[test]
1909 fn rotations_and_reflections_empty_yields_one() {
1910 let empty: [i32; 0] = [];
1911 assert_eq!(empty.circular().rotations_and_reflections().count(), 1);
1912 }
1913
1914 fn collect_iter<const N: usize>(mut iter: CircularIter<'_, i32>) -> [i32; N] {
1917 let mut out = [0; N];
1918 for s in out.iter_mut() {
1919 *s = *iter.next().unwrap();
1920 }
1921 out
1922 }
1923
1924 #[test]
1925 fn windows_basic() {
1926 let r = [1, 2, 3, 4].circular();
1927 let mut it = r.windows(2);
1928 assert_eq!(collect_iter::<2>(it.next().unwrap()), [1, 2]);
1929 assert_eq!(collect_iter::<2>(it.next().unwrap()), [2, 3]);
1930 assert_eq!(collect_iter::<2>(it.next().unwrap()), [3, 4]);
1931 assert_eq!(collect_iter::<2>(it.next().unwrap()), [4, 1]); assert!(it.next().is_none());
1933 }
1934
1935 #[test]
1936 fn windows_count_equals_ring_len() {
1937 let r = [1, 2, 3, 4, 5].circular();
1938 assert_eq!(r.windows(2).count(), 5);
1939 assert_eq!(r.windows(3).count(), 5);
1940 assert_eq!(r.windows(5).count(), 5);
1941 }
1942
1943 #[test]
1944 fn windows_size_greater_than_ring() {
1945 let r = [1, 2, 3].circular();
1946 let mut it = r.windows(5);
1948 assert_eq!(collect_iter::<5>(it.next().unwrap()), [1, 2, 3, 1, 2]);
1949 assert_eq!(collect_iter::<5>(it.next().unwrap()), [2, 3, 1, 2, 3]);
1950 assert_eq!(collect_iter::<5>(it.next().unwrap()), [3, 1, 2, 3, 1]);
1951 assert!(it.next().is_none());
1952 }
1953
1954 #[test]
1955 fn windows_empty_ring() {
1956 let empty: [i32; 0] = [];
1957 assert_eq!(empty.circular().windows(3).count(), 0);
1958 }
1959
1960 #[test]
1961 #[should_panic]
1962 fn windows_zero_size_panics() {
1963 let _ = [1, 2, 3].circular().windows(0);
1964 }
1965
1966 #[test]
1967 fn chunks_evenly_divisible() {
1968 let r = [1, 2, 3, 4].circular();
1969 let mut it = r.chunks(2);
1970 assert_eq!(collect_iter::<2>(it.next().unwrap()), [1, 2]);
1971 assert_eq!(collect_iter::<2>(it.next().unwrap()), [3, 4]);
1972 assert!(it.next().is_none());
1973 }
1974
1975 #[test]
1976 fn chunks_wrap_at_seam() {
1977 let r = [1, 2, 3, 4, 5].circular();
1979 let mut it = r.chunks(2);
1980 assert_eq!(collect_iter::<2>(it.next().unwrap()), [1, 2]);
1981 assert_eq!(collect_iter::<2>(it.next().unwrap()), [3, 4]);
1982 assert_eq!(collect_iter::<2>(it.next().unwrap()), [5, 1]);
1983 assert!(it.next().is_none());
1984 }
1985
1986 #[test]
1987 fn chunks_size_one() {
1988 let r = [1, 2, 3].circular();
1989 assert_eq!(r.chunks(1).count(), 3);
1990 }
1991
1992 #[test]
1993 fn chunks_size_greater_than_ring() {
1994 let r = [1, 2, 3].circular();
1995 let mut it = r.chunks(5);
1997 assert_eq!(collect_iter::<5>(it.next().unwrap()), [1, 2, 3, 1, 2]);
1998 assert!(it.next().is_none());
1999 }
2000
2001 #[test]
2002 fn chunks_empty_ring() {
2003 let empty: [i32; 0] = [];
2004 assert_eq!(empty.circular().chunks(2).count(), 0);
2005 }
2006
2007 #[test]
2008 #[should_panic]
2009 fn chunks_zero_size_panics() {
2010 let _ = [1, 2, 3].circular().chunks(0);
2011 }
2012
2013 #[test]
2016 fn chained_rotations_first_element() {
2017 let r = [3, 1, 2].circular();
2018 let firsts: [i32; 3] = {
2019 let mut out = [0; 3];
2020 for (s, v) in out.iter_mut().zip(r.rotations()) {
2021 *s = *v.apply(0);
2022 }
2023 out
2024 };
2025 assert_eq!(firsts, [3, 1, 2]);
2026 }
2027
2028 #[test]
2029 fn chained_windows_count_satisfying() {
2030 let r = [1, 2, 3, 4, 5].circular();
2031 let n = r
2033 .windows(2)
2034 .filter(|w| {
2035 let mut clone = w.clone();
2036 clone.next().map_or(false, |x| x % 2 == 0)
2037 })
2038 .count();
2039 assert_eq!(n, 2); }
2041
2042 #[test]
2045 fn index_from_basic() {
2046 let r = [10, 20, 30].circular();
2047 assert_eq!(r.index_from(0), 0);
2048 assert_eq!(r.index_from(2), 2);
2049 assert_eq!(r.index_from(3), 0);
2050 assert_eq!(r.index_from(7), 1);
2051 assert_eq!(r.index_from(-1), 2);
2052 assert_eq!(r.index_from(-4), 2);
2053 }
2054
2055 #[test]
2056 #[should_panic]
2057 fn index_from_empty_panics() {
2058 let empty: [i32; 0] = [];
2059 let _ = empty.circular().index_from(0);
2060 }
2061
2062 #[test]
2065 fn is_rotation_of_true() {
2066 let a = [1, 2, 3, 4];
2067 let b = [3, 4, 1, 2];
2068 assert!(a.circular().is_rotation_of(&b));
2069 }
2070
2071 #[test]
2072 fn is_rotation_of_false() {
2073 let a = [1, 2, 3, 4];
2074 let b = [4, 3, 2, 1];
2075 assert!(!a.circular().is_rotation_of(&b));
2076 }
2077
2078 #[test]
2079 fn is_rotation_of_empty() {
2080 let a: [i32; 0] = [];
2081 let b: [i32; 0] = [];
2082 assert!(a.circular().is_rotation_of(&b));
2083 }
2084
2085 #[test]
2086 fn is_rotation_of_length_mismatch() {
2087 assert!(![1, 2].circular().is_rotation_of(&[1, 2, 3]));
2088 }
2089
2090 #[test]
2091 fn is_reflection_of_basic() {
2092 let a = [1, 2, 3, 4];
2093 assert!(a.circular().is_reflection_of(&[1, 4, 3, 2]));
2094 assert!(a.circular().is_reflection_of(&[1, 2, 3, 4]));
2095 assert!(!a.circular().is_reflection_of(&[2, 1, 4, 3]));
2096 }
2097
2098 #[test]
2099 fn is_reversion_of_basic() {
2100 let a = [1, 2, 3, 4];
2101 assert!(a.circular().is_reversion_of(&[1, 2, 3, 4]));
2102 assert!(a.circular().is_reversion_of(&[4, 3, 2, 1]));
2103 assert!(!a.circular().is_reversion_of(&[3, 2, 1, 4]));
2104 }
2105
2106 #[test]
2107 fn is_rotation_or_reflection_of_basic() {
2108 let a = [1, 2, 3, 4];
2109 assert!(a.circular().is_rotation_or_reflection_of(&[3, 4, 1, 2])); assert!(a.circular().is_rotation_or_reflection_of(&[2, 1, 4, 3])); assert!(!a.circular().is_rotation_or_reflection_of(&[1, 3, 2, 4])); }
2113
2114 #[test]
2115 fn rotation_offset_basic() {
2116 let a = [10, 20, 30, 40];
2117 assert_eq!(a.circular().rotation_offset(&[30, 40, 10, 20]), Some(2));
2118 assert_eq!(a.circular().rotation_offset(&[10, 20, 30, 40]), Some(0));
2119 assert_eq!(a.circular().rotation_offset(&[1, 2, 3, 4]), None);
2120 }
2121
2122 #[test]
2123 fn rotation_offset_length_mismatch() {
2124 assert_eq!([1, 2, 3].circular().rotation_offset(&[1, 2]), None);
2125 }
2126
2127 #[test]
2130 fn hamming_distance_basic() {
2131 let a = [1, 2, 3, 4];
2132 let b = [1, 2, 0, 4];
2133 assert_eq!(a.circular().hamming_distance(&b), 1);
2134 assert_eq!(a.circular().hamming_distance(&a), 0);
2135 }
2136
2137 #[test]
2138 #[should_panic]
2139 fn hamming_distance_length_mismatch_panics() {
2140 let _ = [1, 2].circular().hamming_distance(&[1, 2, 3]);
2141 }
2142
2143 #[test]
2144 fn min_rotational_hamming_distance_basic() {
2145 let a = [1, 2, 3, 4];
2146 let b = [3, 4, 1, 2]; assert_eq!(a.circular().min_rotational_hamming_distance(&b), 0);
2148 assert_eq!(a.circular().min_rotational_hamming_distance(&a), 0);
2149 let c = [3, 4, 1, 5]; assert_eq!(a.circular().min_rotational_hamming_distance(&c), 1);
2151 }
2152
2153 #[test]
2156 fn contains_slice_basic() {
2157 let r = [1, 2, 3, 4, 5].circular();
2158 assert!(r.contains_slice(&[2, 3, 4]));
2159 assert!(r.contains_slice(&[5, 1, 2])); assert!(!r.contains_slice(&[2, 4]));
2161 }
2162
2163 #[test]
2164 fn contains_slice_empty() {
2165 assert!([1, 2, 3].circular().contains_slice(&[]));
2166 let empty: [i32; 0] = [];
2167 assert!(!empty.circular().contains_slice(&[1]));
2168 assert!(empty.circular().contains_slice(&[]));
2169 }
2170
2171 #[test]
2172 fn index_of_slice_basic() {
2173 let r = [1, 2, 3, 4, 5].circular();
2174 assert_eq!(r.index_of_slice(&[3, 4], 0), Some(2));
2175 assert_eq!(r.index_of_slice(&[5, 1], 0), Some(4)); assert_eq!(r.index_of_slice(&[9], 0), None);
2177 }
2178
2179 #[test]
2185 fn index_of_slice_on_rotated_view() {
2186 let v = [0, 1, 1, 0].circular().rotate_left(1);
2188 assert_eq!(v.index_of_slice(&[1], 0), Some(0));
2189 assert_eq!(v.index_of_slice(&[1], 1), Some(1));
2190 assert_eq!(v.index_of_slice(&[1], 2), Some(0)); assert_eq!(v.index_of_slice(&[0, 0], 0), Some(2));
2192 assert_eq!(v.index_of_slice(&[0, 1], 0), Some(3)); }
2194
2195 #[test]
2196 fn index_of_slice_on_reflected_view() {
2197 let v = [10, 20, 30, 40].circular().reflect_at(0);
2199 assert_eq!(v.index_of_slice(&[40, 30], 0), Some(1));
2200 assert_eq!(v.index_of_slice(&[20], 0), Some(3));
2201 assert_eq!(v.index_of_slice(&[20, 10], 0), Some(3)); }
2203
2204 #[test]
2205 fn index_of_slice_empty_needle_returns_view_position() {
2206 let v = [10, 20, 30, 40, 50].circular().rotate_left(2);
2207 assert_eq!(v.index_of_slice(&[], 0), Some(0));
2208 assert_eq!(v.index_of_slice(&[], 7), Some(2));
2209 assert_eq!(v.index_of_slice(&[], -1), Some(4));
2210 let empty: [i32; 0] = [];
2211 assert_eq!(empty.circular().index_of_slice(&[], 3), Some(0));
2212 assert_eq!(empty.circular().index_of_slice(&[1], 0), None);
2213 }
2214
2215 #[test]
2218 fn rotational_symmetry_basic() {
2219 assert_eq!([1, 2, 3, 4].circular().rotational_symmetry(), 1);
2220 assert_eq!([1, 2, 1, 2].circular().rotational_symmetry(), 2);
2221 assert_eq!([1, 1, 1, 1].circular().rotational_symmetry(), 4);
2222 }
2223
2224 #[test]
2225 fn rotational_symmetry_short() {
2226 let empty: [i32; 0] = [];
2227 assert_eq!(empty.circular().rotational_symmetry(), 1);
2228 assert_eq!([5].circular().rotational_symmetry(), 1);
2229 }
2230
2231 #[test]
2232 fn symmetry_palindrome() {
2233 assert!([1, 2, 3, 2, 1].circular().symmetry() >= 1);
2235 }
2236
2237 #[test]
2238 fn symmetry_empty() {
2239 let empty: [i32; 0] = [];
2240 assert_eq!(empty.circular().symmetry(), 0);
2241 }
2242
2243 #[test]
2246 fn canonical_index_basic() {
2247 assert_eq!([3, 1, 2].circular().canonical_index(), 1);
2248 assert_eq!([1, 2, 3].circular().canonical_index(), 0);
2249 assert_eq!([2, 3, 0, 1].circular().canonical_index(), 2);
2250 }
2251
2252 #[test]
2253 fn canonical_index_short() {
2254 let empty: [i32; 0] = [];
2255 assert_eq!(empty.circular().canonical_index(), 0);
2256 assert_eq!([7].circular().canonical_index(), 0);
2257 }
2258
2259 #[test]
2260 fn canonical_index_periodic_returns_first() {
2261 assert_eq!([3, 1, 2, 3, 1, 2].circular().canonical_index(), 1);
2263 assert_eq!([5, 5, 5].circular().canonical_index(), 0);
2264 assert_eq!([1, 0, 1, 0].circular().canonical_index(), 1);
2265 }
2266
2267 #[test]
2268 fn canonical_index_on_transformed_views() {
2269 let r = [2, 3, 0, 1].circular();
2270 assert_eq!(r.rotate_left(2).canonical_index(), 0);
2272 assert_eq!(r.reflect_at(0).canonical_index(), 2);
2274 }
2275
2276 #[test]
2279 fn get_basic_and_empty() {
2280 let r = [10, 20, 30].circular();
2281 assert_eq!(r.get(4), Some(&20));
2282 assert_eq!(r.get(-1), Some(&30));
2283 let empty: [i32; 0] = [];
2284 assert_eq!(empty.circular().get(0), None);
2285 }
2286
2287 #[test]
2288 fn index_operator_wraps() {
2289 let r = [10, 20, 30].circular();
2290 assert_eq!(r[0], 10);
2291 assert_eq!(r[4], 20);
2292 assert_eq!(r[-1], 30);
2293 assert_eq!(r.reflect_at(0)[1], 30);
2294 }
2295
2296 #[test]
2297 #[should_panic]
2298 fn index_operator_panics_on_empty() {
2299 let empty: [i32; 0] = [];
2300 let _ = empty.circular()[0];
2301 }
2302
2303 #[test]
2306 fn into_iterator_by_value_and_by_ref() {
2307 let r = [1, 2, 3].circular();
2308 let mut sum = 0;
2309 for &x in r {
2310 sum += x;
2311 }
2312 assert_eq!(sum, 6);
2313 let mut prod = 1;
2315 for &x in &r {
2316 prod *= x;
2317 }
2318 assert_eq!(prod, 6);
2319 assert_eq!(r.len(), 3);
2320 }
2321
2322 #[test]
2325 fn iter_rev_walks_backward() {
2326 let r = [1, 2, 3, 4].circular();
2327 let mut out = [0; 4];
2328 for (s, &x) in out.iter_mut().zip(r.iter().rev()) {
2329 *s = x;
2330 }
2331 assert_eq!(out, [4, 3, 2, 1]);
2332 for (s, &x) in out.iter_mut().zip(r.rotate_left(1).iter().rev()) {
2334 *s = x;
2335 }
2336 assert_eq!(out, [1, 4, 3, 2]);
2337 }
2338
2339 #[test]
2340 fn iter_mixed_front_and_back() {
2341 let r = [1, 2, 3, 4, 5].circular();
2342 let mut it = r.iter();
2343 assert_eq!(it.next(), Some(&1));
2344 assert_eq!(it.next_back(), Some(&5));
2345 assert_eq!(it.next(), Some(&2));
2346 assert_eq!(it.next_back(), Some(&4));
2347 assert_eq!(it.next(), Some(&3));
2348 assert_eq!(it.next(), None);
2349 assert_eq!(it.next_back(), None);
2350 }
2351
2352 #[test]
2353 fn iter_rev_on_wrapping_window() {
2354 let r = [1, 2, 3].circular();
2356 let w = r.windows(5).next().unwrap();
2357 let mut out = [0; 5];
2358 for (s, &x) in out.iter_mut().zip(w.rev()) {
2359 *s = x;
2360 }
2361 assert_eq!(out, [2, 1, 3, 2, 1]);
2362 }
2363
2364 #[test]
2365 fn iter_nth_count_last() {
2366 let r = [10, 20, 30, 40, 50].circular();
2367 assert_eq!(r.iter().nth(1), Some(&20));
2368 assert_eq!(r.iter().nth(3), Some(&40));
2369 assert_eq!(r.iter().nth(5), None);
2370 let mut it = r.iter();
2371 assert_eq!(it.nth(2), Some(&30));
2372 assert_eq!(it.next(), Some(&40)); assert_eq!(it.count(), 1);
2374 assert_eq!(r.iter().count(), 5);
2375 assert_eq!(r.iter().last(), Some(&50));
2376 assert_eq!(r.slice(0, 0).last(), None);
2377 }
2378}
2379
2380#[cfg(all(test, feature = "alloc"))]
2381mod alloc_tests {
2382 use super::*;
2383 use alloc::vec;
2384 use alloc::vec::Vec;
2385
2386 #[test]
2389 fn canonical_basic() {
2390 assert_eq!([3, 1, 2].circular().canonical(), vec![1, 2, 3]);
2391 assert_eq!([1, 2, 3].circular().canonical(), vec![1, 2, 3]);
2392 }
2393
2394 #[test]
2395 fn canonical_all_equal() {
2396 assert_eq!([5, 5, 5].circular().canonical(), vec![5, 5, 5]);
2397 }
2398
2399 #[test]
2400 fn canonical_rotations_share_canonical() {
2401 let a = [3, 1, 2];
2402 let b = [1, 2, 3];
2403 let c = [2, 3, 1];
2404 assert_eq!(a.circular().canonical(), b.circular().canonical());
2405 assert_eq!(b.circular().canonical(), c.circular().canonical());
2406 }
2407
2408 #[test]
2409 fn bracelet_basic() {
2410 let a = [3, 1, 2];
2412 let b = [3, 2, 1]; assert_eq!(a.circular().bracelet(), b.circular().bracelet());
2414 }
2415
2416 #[test]
2419 fn symmetry_indices_palindrome() {
2420 let r = [1, 2, 3, 2, 1].circular();
2421 assert!(!r.symmetry_indices().is_empty());
2422 }
2423
2424 #[test]
2425 fn symmetry_indices_none() {
2426 let r = [1, 2, 3, 4].circular();
2427 assert_eq!(r.symmetry_indices(), Vec::<usize>::new());
2429 }
2430
2431 #[test]
2432 fn symmetry_indices_empty() {
2433 let empty: [i32; 0] = [];
2434 assert_eq!(empty.circular().symmetry_indices(), Vec::<usize>::new());
2435 }
2436
2437 #[test]
2438 fn reflectional_symmetry_axes_count_matches_symmetry() {
2439 let r = [1, 2, 1, 2].circular();
2440 let axes = r.reflectional_symmetry_axes();
2441 assert_eq!(axes.len(), r.symmetry());
2442 }
2443
2444 #[test]
2445 fn reflectional_symmetry_axes_square_geometry() {
2446 use crate::AxisLocation::{Edge, Vertex};
2447 let axes = [7, 7, 7, 7].circular().reflectional_symmetry_axes();
2450 assert_eq!(axes.len(), 4);
2451 assert!(axes.contains(&(Vertex(0), Vertex(2))));
2452 assert!(axes.contains(&(Vertex(1), Vertex(3))));
2453 assert!(axes.contains(&(Edge(0, 1), Edge(2, 3))));
2454 assert!(axes.contains(&(Edge(1, 2), Edge(3, 0))));
2455 }
2456
2457 #[test]
2458 fn reflectional_symmetry_axes_odd_palindrome_geometry() {
2459 use crate::AxisLocation::{Edge, Vertex};
2460 let axes = [1, 2, 3, 2, 1].circular().reflectional_symmetry_axes();
2463 assert_eq!(axes, vec![(Vertex(2), Edge(4, 0))]);
2464 }
2465
2466 #[test]
2467 fn reflectional_symmetry_axes_vertex_only_geometry() {
2468 use crate::AxisLocation::Vertex;
2469 let axes = [0, 1, 0, 1].circular().reflectional_symmetry_axes();
2472 assert_eq!(axes, vec![(Vertex(1), Vertex(3)), (Vertex(0), Vertex(2))]);
2473 }
2474
2475 #[test]
2476 fn reflectional_symmetry_axes_edge_only_geometry() {
2477 use crate::AxisLocation::Edge;
2478 let axes = [1, 1, 2, 2].circular().reflectional_symmetry_axes();
2481 assert_eq!(axes, vec![(Edge(0, 1), Edge(2, 3))]);
2482 }
2483
2484 #[test]
2487 fn to_vec_basic() {
2488 let r = [10, 20, 30].circular();
2489 assert_eq!(r.to_vec(), vec![10, 20, 30]);
2490 assert_eq!(r.rotate_right(1).to_vec(), vec![30, 10, 20]);
2491 assert_eq!(r.reflect_at(0).to_vec(), vec![10, 30, 20]);
2492 }
2493
2494 #[test]
2495 fn to_vec_empty() {
2496 let empty: [i32; 0] = [];
2497 assert_eq!(empty.circular().to_vec(), Vec::<i32>::new());
2498 }
2499
2500 #[test]
2503 fn rotations_map_canonical_index() {
2504 let r = [3, 1, 2, 3, 1, 2].circular();
2505 let indices: Vec<usize> = r.rotations().map(|v| v.canonical_index()).collect();
2506 assert_eq!(indices, vec![1, 0, 2, 1, 0, 2]);
2507 }
2508}