1use std::fmt::Debug;
46
47pub trait SmartShrink: Clone {
56 fn shrink_preserving<F>(&self, invariant: F) -> Box<dyn Iterator<Item = Self>>
79 where
80 F: Fn(&Self) -> bool + 'static;
81}
82
83impl<T: Clone + Debug + 'static> SmartShrink for Vec<T> {
85 fn shrink_preserving<F>(&self, invariant: F) -> Box<dyn Iterator<Item = Self>>
86 where
87 F: Fn(&Self) -> bool + 'static,
88 {
89 let original = self.clone();
90 let invariant = std::rc::Rc::new(invariant);
91
92 let mut candidates = Vec::new();
93
94 if !original.is_empty() {
96 let v = Vec::new();
97 if invariant(&v) {
98 candidates.push(v);
99 }
100 }
101
102 for i in 0..original.len().min(3) {
104 if original.len() > 1 {
105 let mut v = original.clone();
106 v.remove(i);
107 if invariant(&v) {
108 candidates.push(v);
109 }
110 }
111 }
112
113 if original.len() > 1 {
115 let v = original[..original.len() / 2].to_vec();
116 if invariant(&v) {
117 candidates.push(v);
118 }
119 }
120
121 Box::new(candidates.into_iter())
122 }
123}
124
125impl SmartShrink for i32 {
127 fn shrink_preserving<F>(&self, invariant: F) -> Box<dyn Iterator<Item = Self>>
128 where
129 F: Fn(&Self) -> bool + 'static,
130 {
131 let current = *self;
132 let invariant = std::rc::Rc::new(invariant);
133
134 let candidates: Vec<i32> = (0..current)
135 .rev()
136 .filter(|x| invariant(x))
137 .take(10)
138 .collect();
139
140 Box::new(candidates.into_iter())
141 }
142}
143
144impl SmartShrink for u32 {
145 fn shrink_preserving<F>(&self, invariant: F) -> Box<dyn Iterator<Item = Self>>
146 where
147 F: Fn(&Self) -> bool + 'static,
148 {
149 let current = *self;
150 let invariant = std::rc::Rc::new(invariant);
151
152 let candidates: Vec<u32> = (0..current)
153 .rev()
154 .filter(|x| invariant(x))
155 .take(10)
156 .collect();
157
158 Box::new(candidates.into_iter())
159 }
160}
161
162#[derive(Debug, Clone)]
188pub struct DeltaDebugShrinker<T> {
189 items: Vec<T>,
190}
191
192impl<T: Clone + Debug + PartialEq> DeltaDebugShrinker<T> {
193 pub fn new(items: Vec<T>) -> Self {
195 Self { items }
196 }
197
198 pub fn find_minimal<F>(self, predicate: F) -> Vec<T>
202 where
203 F: Fn(&[T]) -> bool,
204 {
205 let mut current = self.items;
206
207 if !predicate(¤t) {
209 return current;
210 }
211
212 let mut changed = true;
213
214 while changed {
215 changed = false;
216 let n = current.len();
217
218 if n <= 1 {
219 break;
220 }
221
222 let chunk_size = n / 2;
224
225 for i in 0..=(n - chunk_size) {
226 let candidate: Vec<T> = current
227 .iter()
228 .enumerate()
229 .filter(|(idx, _)| *idx < i || *idx >= i + chunk_size)
230 .map(|(_, item)| item.clone())
231 .collect();
232
233 if !candidate.is_empty() && predicate(&candidate) {
234 current = candidate;
235 changed = true;
236 break;
237 }
238 }
239
240 if !changed {
242 for i in 0..current.len() {
243 let mut candidate = current.clone();
244 candidate.remove(i);
245
246 if !candidate.is_empty() && predicate(&candidate) {
247 current = candidate;
248 changed = true;
249 break;
250 }
251 }
252 }
253 }
254
255 current
256 }
257
258 pub fn shrink_steps<F>(self, predicate: F) -> DeltaDebugIterator<T, F>
263 where
264 F: Fn(&[T]) -> bool,
265 {
266 DeltaDebugIterator {
267 current: self.items,
268 predicate,
269 phase: 0,
270 chunk_size: 0,
271 index: 0,
272 }
273 }
274}
275
276pub struct DeltaDebugIterator<T, F>
278where
279 F: Fn(&[T]) -> bool,
280{
281 current: Vec<T>,
282 predicate: F,
283 phase: usize,
284 chunk_size: usize,
285 index: usize,
286}
287
288impl<T: Clone + Debug, F: Fn(&[T]) -> bool> Iterator for DeltaDebugIterator<T, F> {
289 type Item = Vec<T>;
290
291 fn next(&mut self) -> Option<Self::Item> {
292 if self.current.len() <= 1 {
293 return None;
294 }
295
296 loop {
297 match self.phase {
298 0 => {
299 self.chunk_size = self.current.len() / 2;
301 self.index = 0;
302 self.phase = 1;
303 }
304 1 => {
305 if self.index + self.chunk_size <= self.current.len() {
307 let candidate: Vec<T> = self
308 .current
309 .iter()
310 .enumerate()
311 .filter(|(idx, _)| {
312 *idx < self.index || *idx >= self.index + self.chunk_size
313 })
314 .map(|(_, item)| item.clone())
315 .collect();
316
317 self.index += 1;
318
319 if !candidate.is_empty() && (self.predicate)(&candidate) {
320 self.current = candidate.clone();
321 self.phase = 0; return Some(candidate);
323 }
324 } else {
325 self.phase = 2;
326 self.index = 0;
327 }
328 }
329 2 => {
330 if self.index < self.current.len() {
332 let mut candidate = self.current.clone();
333 candidate.remove(self.index);
334 self.index += 1;
335
336 if !candidate.is_empty() && (self.predicate)(&candidate) {
337 self.current = candidate.clone();
338 self.phase = 0; return Some(candidate);
340 }
341 } else {
342 return None; }
344 }
345 _ => return None,
346 }
347 }
348 }
349}
350
351#[derive(Debug, Clone)]
373pub struct TargetedShrinker<T> {
374 current: T,
375 target: T,
376}
377
378impl TargetedShrinker<i32> {
379 pub fn new_int(current: i32, target: i32) -> Self {
381 Self { current, target }
382 }
383
384 pub fn shrink(self) -> TargetedShrinkIterator<i32> {
386 TargetedShrinkIterator {
387 current: self.current,
388 target: self.target,
389 step: 0,
390 }
391 }
392}
393
394impl TargetedShrinker<f64> {
395 pub fn new_float(current: f64, target: f64) -> Self {
397 Self { current, target }
398 }
399
400 pub fn shrink(self) -> TargetedShrinkIterator<f64> {
402 TargetedShrinkIterator {
403 current: self.current,
404 target: self.target,
405 step: 0,
406 }
407 }
408}
409
410pub struct TargetedShrinkIterator<T> {
412 current: T,
413 target: T,
414 step: usize,
415}
416
417impl Iterator for TargetedShrinkIterator<i32> {
418 type Item = i32;
419
420 fn next(&mut self) -> Option<Self::Item> {
421 if self.current == self.target {
422 return None;
423 }
424
425 self.step += 1;
426
427 let diff = self.current - self.target;
429 let step_size = (diff.abs() / 2).max(1);
430
431 self.current = if diff > 0 {
432 (self.current - step_size).max(self.target)
433 } else {
434 (self.current + step_size).min(self.target)
435 };
436
437 Some(self.current)
438 }
439}
440
441impl Iterator for TargetedShrinkIterator<f64> {
442 type Item = f64;
443
444 fn next(&mut self) -> Option<Self::Item> {
445 if (self.current - self.target).abs() < f64::EPSILON {
446 return None;
447 }
448
449 self.step += 1;
450
451 let diff = self.current - self.target;
453 self.current = self.target + diff / 2.0;
454
455 Some(self.current)
456 }
457}
458
459#[derive(Debug, Clone)]
483pub struct CascadingShrinker<T> {
484 value: T,
485}
486
487impl<T: Clone> CascadingShrinker<T> {
488 pub fn new(value: T) -> Self {
490 Self { value }
491 }
492
493 pub fn value(&self) -> &T {
495 &self.value
496 }
497}
498
499impl<T: Clone + Debug + 'static> CascadingShrinker<Vec<T>> {
500 pub fn shrink(&self) -> Box<dyn Iterator<Item = Vec<T>>> {
502 let original = self.value.clone();
503 let mut candidates = Vec::new();
504
505 for i in 0..original.len() {
507 let mut v = original.clone();
508 v.remove(i);
509 candidates.push(v);
510 }
511
512 if original.len() > 1 {
514 candidates.push(original[original.len() / 2..].to_vec());
516 candidates.push(original[..original.len() / 2].to_vec());
518
519 if original.len() >= 3 {
521 candidates.push(original[original.len() / 3..].to_vec());
522 }
523
524 if original.len() >= 3 {
526 let third = original.len() / 3;
527 let mut v = original[..third].to_vec();
528 v.extend_from_slice(&original[2 * third..]);
529 candidates.push(v);
530 }
531 }
532
533 if original.len() > 2 {
535 for i in 0..original.len() - 1 {
536 candidates.push(original[i..i + 2].to_vec());
537 }
538 }
539
540 candidates.push(Vec::new());
542
543 Box::new(candidates.into_iter())
544 }
545}
546
547impl CascadingShrinker<i32> {
548 pub fn shrink(&self) -> Box<dyn Iterator<Item = i32>> {
550 let current = self.value;
551 let mut candidates = Vec::new();
552
553 if current == 0 {
554 return Box::new(std::iter::empty());
555 }
556
557 let half = current / 2;
559 if half != current {
560 candidates.push(half);
561 }
562
563 for i in 0..10 {
565 let pow = 1 << i;
566 if pow < current.abs() {
567 let next = if current > 0 {
568 current - pow
569 } else {
570 current + pow
571 };
572 if next != current {
573 candidates.push(next);
574 }
575 }
576 }
577
578 candidates.push(0);
580
581 candidates.sort_unstable();
583 candidates.dedup();
584
585 Box::new(candidates.into_iter())
586 }
587}
588
589#[derive(Debug, Clone)]
614pub struct GuidedShrinker<T> {
615 value: T,
616 max_iterations: usize,
617}
618
619impl<T: Clone> GuidedShrinker<T> {
620 pub fn new(value: T) -> Self {
622 Self {
623 value,
624 max_iterations: 1000,
625 }
626 }
627
628 pub fn with_max_iterations(value: T, max_iterations: usize) -> Self {
630 Self {
631 value,
632 max_iterations,
633 }
634 }
635}
636
637impl<T: Clone + Debug + PartialEq + 'static> GuidedShrinker<Vec<T>> {
638 pub fn find_minimal<F>(&self, test: F) -> Vec<T>
643 where
644 F: Fn(&Vec<T>) -> bool,
645 {
646 let mut current = self.value.clone();
647
648 if !test(¤t) {
649 return current;
651 }
652
653 for _ in 0..self.max_iterations {
654 let mut found_smaller = false;
655
656 for i in (0..current.len()).rev() {
658 let mut candidate = current.clone();
659 candidate.remove(i);
660
661 if test(&candidate) {
662 current = candidate;
664 found_smaller = true;
665 break;
666 }
667 }
668
669 if !found_smaller {
670 if current.len() > 1 {
672 let half = current.len() / 2;
673
674 let first_half = current[..half].to_vec();
676 if test(&first_half) {
677 current = first_half;
678 found_smaller = true;
679 } else {
680 let second_half = current[half..].to_vec();
682 if test(&second_half) {
683 current = second_half;
684 found_smaller = true;
685 }
686 }
687 }
688 }
689
690 if !found_smaller {
691 break;
693 }
694 }
695
696 current
697 }
698
699 pub fn find_minimal_with_stats<F>(&self, test: F) -> (Vec<T>, usize)
703 where
704 F: Fn(&Vec<T>) -> bool,
705 {
706 let mut current = self.value.clone();
707 let mut iterations = 0;
708
709 if !test(¤t) {
710 return (current, iterations);
711 }
712
713 for _ in 0..self.max_iterations {
714 iterations += 1;
715 let mut found_smaller = false;
716
717 for i in (0..current.len()).rev() {
718 let mut candidate = current.clone();
719 candidate.remove(i);
720
721 if test(&candidate) {
722 current = candidate;
723 found_smaller = true;
724 break;
725 }
726 }
727
728 if !found_smaller && current.len() > 1 {
729 let half = current.len() / 2;
730
731 let first_half = current[..half].to_vec();
732 if test(&first_half) {
733 current = first_half;
734 found_smaller = true;
735 } else {
736 let second_half = current[half..].to_vec();
737 if test(&second_half) {
738 current = second_half;
739 found_smaller = true;
740 }
741 }
742 }
743
744 if !found_smaller {
745 break;
746 }
747 }
748
749 (current, iterations)
750 }
751}
752
753#[derive(Debug, Clone, Copy, PartialEq, Eq)]
759pub enum ShrinkStrategy {
760 BreadthFirst,
762 DepthFirst,
764}
765
766#[derive(Debug, Clone)]
790pub struct ConfigurableShrinker<T> {
791 value: T,
792 strategy: ShrinkStrategy,
793 max_depth: usize,
794}
795
796impl<T: Clone> ConfigurableShrinker<T> {
797 pub fn new(value: T, strategy: ShrinkStrategy) -> Self {
799 Self {
800 value,
801 strategy,
802 max_depth: 100,
803 }
804 }
805
806 pub fn with_max_depth(mut self, max_depth: usize) -> Self {
808 self.max_depth = max_depth;
809 self
810 }
811
812 pub fn strategy(&self) -> ShrinkStrategy {
814 self.strategy
815 }
816}
817
818impl<T: Clone + Debug + PartialEq + 'static> ConfigurableShrinker<Vec<T>> {
819 pub fn find_minimal<F>(&self, test: F) -> Vec<T>
821 where
822 F: Fn(&Vec<T>) -> bool,
823 {
824 match self.strategy {
825 ShrinkStrategy::DepthFirst => self.find_minimal_dfs(&test, self.value.clone(), 0),
826 ShrinkStrategy::BreadthFirst => self.find_minimal_bfs(&test),
827 }
828 }
829
830 fn find_minimal_dfs<F>(&self, test: &F, current: Vec<T>, depth: usize) -> Vec<T>
831 where
832 F: Fn(&Vec<T>) -> bool,
833 {
834 if depth >= self.max_depth || !test(¤t) {
835 return current;
836 }
837
838 for i in (0..current.len()).rev() {
840 let mut candidate = current.clone();
841 candidate.remove(i);
842
843 if test(&candidate) {
844 return self.find_minimal_dfs(test, candidate, depth + 1);
846 }
847 }
848
849 if current.len() > 1 {
851 let half = current.len() / 2;
852
853 let first_half = current[..half].to_vec();
854 if test(&first_half) {
855 return self.find_minimal_dfs(test, first_half, depth + 1);
856 }
857
858 let second_half = current[half..].to_vec();
859 if test(&second_half) {
860 return self.find_minimal_dfs(test, second_half, depth + 1);
861 }
862 }
863
864 current
865 }
866
867 fn find_minimal_bfs<F>(&self, test: &F) -> Vec<T>
868 where
869 F: Fn(&Vec<T>) -> bool,
870 {
871 use std::collections::VecDeque;
872
873 let mut queue = VecDeque::new();
874 queue.push_back((self.value.clone(), 0));
875
876 let mut best = self.value.clone();
877 let mut visited = std::collections::HashSet::new();
878
879 while let Some((current, depth)) = queue.pop_front() {
880 if depth >= self.max_depth {
881 continue;
882 }
883
884 if !test(¤t) {
885 continue;
886 }
887
888 if current.len() < best.len() {
890 best = current.clone();
891 }
892
893 for i in 0..current.len() {
895 let mut candidate = current.clone();
896 candidate.remove(i);
897
898 let hash = format!("{:?}", candidate);
900 if visited.insert(hash) {
901 queue.push_back((candidate, depth + 1));
902 }
903 }
904
905 if current.len() > 1 {
907 let half = current.len() / 2;
908
909 let first_half = current[..half].to_vec();
910 let hash = format!("{:?}", first_half);
911 if visited.insert(hash.clone()) {
912 queue.push_back((first_half, depth + 1));
913 }
914
915 let second_half = current[half..].to_vec();
916 let hash = format!("{:?}", second_half);
917 if visited.insert(hash) {
918 queue.push_back((second_half, depth + 1));
919 }
920 }
921 }
922
923 best
924 }
925}
926
927#[cfg(test)]
928mod tests {
929 use super::*;
930
931 #[test]
932 fn test_smart_shrink_vec_preserving_sorted() {
933 let vec = vec![1, 3, 5, 7, 9];
934
935 let shrunk: Vec<_> = vec
936 .shrink_preserving(|v| v.windows(2).all(|w| w[0] <= w[1]))
937 .collect();
938
939 for candidate in shrunk {
941 assert!(candidate.windows(2).all(|w| w[0] <= w[1]));
942 }
943 }
944
945 #[test]
946 fn test_smart_shrink_vec_preserving_even() {
947 let vec = vec![2, 4, 6, 8];
948
949 let shrunk: Vec<_> = vec
950 .shrink_preserving(|v| v.iter().all(|x| x % 2 == 0))
951 .collect();
952
953 for candidate in shrunk {
955 assert!(candidate.iter().all(|x| x % 2 == 0));
956 }
957 }
958
959 #[test]
960 fn test_smart_shrink_int_preserving_positive() {
961 let num = 100;
962
963 let shrunk: Vec<_> = num.shrink_preserving(|x| *x > 0).take(10).collect();
964
965 for candidate in shrunk {
967 assert!(candidate > 0);
968 }
969 }
970
971 #[test]
972 fn test_delta_debug_minimal_subset() {
973 let items = vec![1, 2, 3, 4, 5, 6, 7, 8];
974 let shrinker = DeltaDebugShrinker::new(items);
975
976 let minimal = shrinker.find_minimal(|subset| subset.contains(&3) && subset.contains(&7));
978
979 assert!(minimal.contains(&3));
980 assert!(minimal.contains(&7));
981 assert!(minimal.len() <= 2);
982 }
983
984 #[test]
985 fn test_delta_debug_single_element() {
986 let items = vec![1, 2, 3, 4, 5];
987 let shrinker = DeltaDebugShrinker::new(items);
988
989 let minimal = shrinker.find_minimal(|subset| subset.contains(&3));
991
992 assert_eq!(minimal, vec![3]);
993 }
994
995 #[test]
996 fn test_delta_debug_iterator() {
997 let items = vec![1, 2, 3, 4, 5, 6];
998 let shrinker = DeltaDebugShrinker::new(items);
999
1000 let steps: Vec<_> = shrinker
1001 .shrink_steps(|subset| subset.contains(&2) && subset.contains(&5))
1002 .collect();
1003
1004 assert!(!steps.is_empty());
1006 let last = steps.last().unwrap();
1007 assert!(last.contains(&2));
1008 assert!(last.contains(&5));
1009 }
1010
1011 #[test]
1012 fn test_targeted_shrink_int_toward_zero() {
1013 let shrinker = TargetedShrinker::new_int(100, 0);
1014 let shrunk: Vec<_> = shrinker.shrink().collect();
1015
1016 assert!(!shrunk.is_empty());
1017 assert_eq!(*shrunk.last().unwrap(), 0);
1018
1019 for window in shrunk.windows(2) {
1021 assert!(window[0] > window[1]);
1022 }
1023 }
1024
1025 #[test]
1026 fn test_targeted_shrink_int_toward_target() {
1027 let shrinker = TargetedShrinker::new_int(100, 42);
1028 let shrunk: Vec<_> = shrinker.shrink().collect();
1029
1030 assert!(!shrunk.is_empty());
1031 assert_eq!(*shrunk.last().unwrap(), 42);
1032
1033 for value in shrunk {
1035 assert!((42..=100).contains(&value));
1036 }
1037 }
1038
1039 #[test]
1040 fn test_targeted_shrink_float() {
1041 let shrinker = TargetedShrinker::new_float(100.0, 42.0);
1042 let shrunk: Vec<_> = shrinker.shrink().take(10).collect();
1043
1044 assert!(!shrunk.is_empty());
1045
1046 let mut prev = 100.0_f64;
1048 for value in shrunk {
1049 let prev_diff = (prev - 42.0_f64).abs();
1050 let curr_diff = (value - 42.0_f64).abs();
1051 assert!(curr_diff < prev_diff);
1052 prev = value;
1053 }
1054 }
1055
1056 #[test]
1057 fn test_cascading_shrinker_vec() {
1058 let value = vec![1, 2, 3, 4, 5, 6, 7, 8];
1059 let shrinker = CascadingShrinker::new(value);
1060
1061 let shrunk: Vec<_> = shrinker.shrink().collect();
1062
1063 assert!(!shrunk.is_empty());
1064
1065 assert!(shrunk.contains(&vec![]));
1070
1071 assert!(shrunk.iter().any(|v| v.len() == 7));
1073 }
1074
1075 #[test]
1076 fn test_cascading_shrinker_int() {
1077 let shrinker = CascadingShrinker::new(100);
1078 let shrunk: Vec<_> = shrinker.shrink().collect();
1079
1080 assert!(!shrunk.is_empty());
1081
1082 assert!(shrunk.contains(&0));
1084
1085 assert!(shrunk.contains(&50));
1087
1088 for value in &shrunk {
1090 assert!(value.abs() <= 100);
1091 }
1092 }
1093
1094 #[test]
1095 fn test_guided_shrinker_finds_minimal() {
1096 let value = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
1097 let shrinker = GuidedShrinker::new(value);
1098
1099 let minimal = shrinker.find_minimal(|v| v.iter().sum::<i32>() > 15);
1101
1102 assert!(minimal.len() < 10);
1104
1105 assert!(minimal.iter().sum::<i32>() > 15);
1107
1108 assert!(minimal.len() <= 6); }
1111
1112 #[test]
1113 fn test_guided_shrinker_with_stats() {
1114 let value = vec![1, 2, 3, 4, 5];
1115 let shrinker = GuidedShrinker::new(value);
1116
1117 let (minimal, iterations) = shrinker.find_minimal_with_stats(|v| v.len() >= 2);
1118
1119 assert_eq!(minimal.len(), 2);
1121
1122 assert!(iterations > 0);
1124 assert!(iterations < 100); }
1126
1127 #[test]
1128 fn test_guided_shrinker_no_shrink_if_passes() {
1129 let value = vec![1, 2, 3];
1130 let shrinker = GuidedShrinker::new(value.clone());
1131
1132 let minimal = shrinker.find_minimal(|_v| false);
1134
1135 assert_eq!(minimal, value);
1137 }
1138
1139 #[test]
1140 fn test_configurable_shrinker_depth_first() {
1141 let value = vec![1, 2, 3, 4, 5, 6, 7, 8];
1142 let shrinker = ConfigurableShrinker::new(value, ShrinkStrategy::DepthFirst);
1143
1144 let minimal = shrinker.find_minimal(|v| v.len() >= 3);
1145
1146 assert_eq!(minimal.len(), 3);
1148 }
1149
1150 #[test]
1151 fn test_configurable_shrinker_breadth_first() {
1152 let value = vec![1, 2, 3, 4, 5, 6, 7, 8];
1153 let shrinker = ConfigurableShrinker::new(value, ShrinkStrategy::BreadthFirst);
1154
1155 let minimal = shrinker.find_minimal(|v| v.len() >= 3);
1156
1157 assert_eq!(minimal.len(), 3);
1159 }
1160
1161 #[test]
1162 fn test_configurable_shrinker_with_max_depth() {
1163 let value = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
1164 let shrinker =
1165 ConfigurableShrinker::new(value, ShrinkStrategy::DepthFirst).with_max_depth(5);
1166
1167 let minimal = shrinker.find_minimal(|v| v.len() >= 2);
1168
1169 assert_eq!(minimal.len(), 5);
1172 }
1173
1174 #[test]
1175 fn test_shrink_strategy_enum() {
1176 assert_eq!(ShrinkStrategy::DepthFirst, ShrinkStrategy::DepthFirst);
1178 assert_eq!(ShrinkStrategy::BreadthFirst, ShrinkStrategy::BreadthFirst);
1179 assert_ne!(ShrinkStrategy::DepthFirst, ShrinkStrategy::BreadthFirst);
1180 }
1181
1182 #[test]
1183 fn test_cascading_shrinker_aggressive() {
1184 let value = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
1185 let shrinker = CascadingShrinker::new(value);
1186
1187 let shrunk: Vec<_> = shrinker.shrink().collect();
1188
1189 assert!(shrunk.len() > 15);
1191
1192 assert!(shrunk.iter().any(|v| v.len() == 8));
1194
1195 assert!(shrunk.iter().any(|v| v.len() == 2));
1197 }
1198}