1use protest_extras::shrinking::{
11 CascadingShrinker, ConfigurableShrinker, GuidedShrinker, ShrinkStrategy,
12};
13
14fn main() {
15 println!("=== Advanced Shrinking Strategies Demo ===\n");
16
17 example_1_cascading_shrinker();
18 println!("\n{}\n", "=".repeat(60));
19
20 example_2_guided_shrinker();
21 println!("\n{}\n", "=".repeat(60));
22
23 example_3_configurable_shrinker_dfs();
24 println!("\n{}\n", "=".repeat(60));
25
26 example_4_configurable_shrinker_bfs();
27 println!("\n{}\n", "=".repeat(60));
28
29 example_5_comparing_strategies();
30}
31
32fn example_1_cascading_shrinker() {
34 println!("Example 1: CascadingShrinker - Thorough Exploration");
35 println!("{}", "-".repeat(60));
36
37 let original = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
38 println!("Original vector: {:?}", original);
39 println!("Length: {}", original.len());
40
41 let shrinker = CascadingShrinker::new(original.clone());
42 let candidates: Vec<_> = shrinker.shrink().collect();
43
44 println!(
45 "\nCascading shrinking generates {} candidates:",
46 candidates.len()
47 );
48 println!(" - Single element removals: {}", original.len());
49 println!(" - Chunk removals (halves, thirds): ~6");
50 println!(" - Empty vector: 1");
51 println!(
52 " - Adjacent pair removals: {}",
53 original.len().saturating_sub(1)
54 );
55
56 println!("\nSample candidates:");
58 for (i, candidate) in candidates.iter().take(5).enumerate() {
59 println!(" {}: {:?} (len={})", i + 1, candidate, candidate.len());
60 }
61
62 println!("\nUse Case:");
65 println!(" Best for: Exploring all shrinking possibilities systematically");
66 println!(" When to use: When you don't know what minimal form looks like");
67}
68
69fn example_2_guided_shrinker() {
71 println!("Example 2: GuidedShrinker - Test Feedback-Driven");
72 println!("{}", "-".repeat(60));
73
74 let original = vec![10, 20, 30, 40, 50, 60, 70, 80, 90, 100];
75 println!("Original vector: {:?}", original);
76 println!("Sum: {}", original.iter().sum::<i32>());
77
78 let shrinker = GuidedShrinker::new(original.clone());
80
81 println!("\nFinding minimal subset where sum > 200...");
82 let (minimal, iterations) = shrinker.find_minimal_with_stats(|v| v.iter().sum::<i32>() > 200);
83
84 println!("Minimal vector: {:?}", minimal);
85 println!("Sum: {}", minimal.iter().sum::<i32>());
86 println!(
87 "Reduced from {} to {} elements",
88 original.len(),
89 minimal.len()
90 );
91 println!("Iterations used: {}", iterations);
92
93 let original2 = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
95 let shrinker2 = GuidedShrinker::new(original2.clone());
96
97 println!("\nFinding minimal subset containing element 7...");
98 let (minimal2, iterations2) =
99 shrinker2.find_minimal_with_stats(|v| v.contains(&7) && !v.is_empty());
100
101 println!("Minimal vector: {:?}", minimal2);
102 println!("Iterations used: {}", iterations2);
103
104 println!("\nUse Case:");
105 println!(" Best for: Efficiently finding minimal failing examples");
106 println!(
107 " When to use: When you can run the test many times and want the smallest counterexample"
108 );
109}
110
111fn example_3_configurable_shrinker_dfs() {
113 println!("Example 3: ConfigurableShrinker - Depth-First Search");
114 println!("{}", "-".repeat(60));
115
116 let original = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
117 println!("Original vector: {:?}", original);
118
119 let shrinker =
120 ConfigurableShrinker::new(original.clone(), ShrinkStrategy::DepthFirst).with_max_depth(20);
121
122 println!("\nFinding minimal subset with length >= 3 (DFS)...");
123 let minimal = shrinker.find_minimal(|v| v.len() >= 3);
124
125 println!("Minimal vector: {:?}", minimal);
126 println!("Length: {}", minimal.len());
127
128 println!("\nDepth-First Strategy:");
129 println!(" - Follows the first successful shrink immediately");
130 println!(" - Goes deep before exploring alternatives");
131 println!(" - Fast but may not find the absolute minimal");
132 println!(" - Good when any reasonably small counterexample is sufficient");
133}
134
135fn example_4_configurable_shrinker_bfs() {
137 println!("Example 4: ConfigurableShrinker - Breadth-First Search");
138 println!("{}", "-".repeat(60));
139
140 let original = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
141 println!("Original vector: {:?}", original);
142
143 let shrinker = ConfigurableShrinker::new(original.clone(), ShrinkStrategy::BreadthFirst)
144 .with_max_depth(20);
145
146 println!("\nFinding minimal subset with length >= 3 (BFS)...");
147 let minimal = shrinker.find_minimal(|v| v.len() >= 3);
148
149 println!("Minimal vector: {:?}", minimal);
150 println!("Length: {}", minimal.len());
151
152 println!("\nBreadth-First Strategy:");
153 println!(" - Explores all shrinks at current depth before going deeper");
154 println!(" - More thorough exploration");
155 println!(" - Finds the truly minimal counterexample");
156 println!(" - Slower but more comprehensive");
157}
158
159fn example_5_comparing_strategies() {
161 println!("Example 5: Strategy Comparison");
162 println!("{}", "-".repeat(60));
163
164 let original = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
166 let sum_of_squares = |v: &Vec<i32>| v.iter().map(|x| x * x).sum::<i32>();
167
168 println!("Original vector: {:?}", original);
169 println!("Sum of squares: {}", sum_of_squares(&original));
170 println!("\nFinding minimal subset where sum of squares > 500\n");
171
172 let cascading = CascadingShrinker::new(original.clone());
174 let candidates: Vec<_> = cascading.shrink().collect();
175 let cascading_result = candidates
176 .iter()
177 .filter(|v| sum_of_squares(v) > 500)
178 .min_by_key(|v| v.len())
179 .cloned();
180
181 if let Some(result) = cascading_result {
182 println!("1. CascadingShrinker:");
183 println!(" Result: {:?}", result);
184 println!(" Length: {}", result.len());
185 println!(" Sum of squares: {}", sum_of_squares(&result));
186 println!(" Total candidates explored: {}", candidates.len());
187 }
188
189 let guided = GuidedShrinker::new(original.clone());
191 let (guided_result, guided_iterations) =
192 guided.find_minimal_with_stats(|v| sum_of_squares(v) > 500);
193
194 println!("\n2. GuidedShrinker:");
195 println!(" Result: {:?}", guided_result);
196 println!(" Length: {}", guided_result.len());
197 println!(" Sum of squares: {}", sum_of_squares(&guided_result));
198 println!(" Iterations: {}", guided_iterations);
199
200 let dfs =
202 ConfigurableShrinker::new(original.clone(), ShrinkStrategy::DepthFirst).with_max_depth(20);
203 let dfs_result = dfs.find_minimal(|v| sum_of_squares(v) > 500);
204
205 println!("\n3. ConfigurableShrinker (DFS):");
206 println!(" Result: {:?}", dfs_result);
207 println!(" Length: {}", dfs_result.len());
208 println!(" Sum of squares: {}", sum_of_squares(&dfs_result));
209
210 let bfs = ConfigurableShrinker::new(original.clone(), ShrinkStrategy::BreadthFirst)
212 .with_max_depth(20);
213 let bfs_result = bfs.find_minimal(|v| sum_of_squares(v) > 500);
214
215 println!("\n4. ConfigurableShrinker (BFS):");
216 println!(" Result: {:?}", bfs_result);
217 println!(" Length: {}", bfs_result.len());
218 println!(" Sum of squares: {}", sum_of_squares(&bfs_result));
219
220 println!("\n{}", "=".repeat(60));
221 println!("Summary:");
222 println!(" - Cascading: Generates all candidates upfront, manual filtering");
223 println!(" - Guided: Fast, iterative, good balance");
224 println!(" - DFS: Quick to find *a* minimal, may not be *the* minimal");
225 println!(" - BFS: Slowest but finds truly minimal counterexample");
226 println!("\nChoose based on:");
227 println!(" - How expensive is your test?");
228 println!(" - How minimal does the result need to be?");
229 println!(" - How much time do you have?");
230}