pub struct ConfigurableShrinker<T> { /* private fields */ }Expand description
Configurable shrinker with search strategy control
Allows choosing between breadth-first and depth-first search strategies when exploring the shrink space.
§Examples
use protest_extras::shrinking::{ConfigurableShrinker, ShrinkStrategy};
let value = vec![1, 2, 3, 4, 5];
// Depth-first: aggressive shrinking
let shrinker = ConfigurableShrinker::new(value.clone(), ShrinkStrategy::DepthFirst);
let depth_result = shrinker.find_minimal(|v| v.len() >= 2);
// Breadth-first: explore more evenly
let shrinker = ConfigurableShrinker::new(value, ShrinkStrategy::BreadthFirst);
let breadth_result = shrinker.find_minimal(|v| v.len() >= 2);
assert!(depth_result.len() >= 2);
assert!(breadth_result.len() >= 2);Implementations§
Source§impl<T: Clone> ConfigurableShrinker<T>
impl<T: Clone> ConfigurableShrinker<T>
Sourcepub fn new(value: T, strategy: ShrinkStrategy) -> Self
pub fn new(value: T, strategy: ShrinkStrategy) -> Self
Create a new configurable shrinker
Examples found in repository?
examples/advanced_shrinking.rs (line 120)
112fn 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
135/// Example 4: ConfigurableShrinker with Breadth-First Search
136fn 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
159/// Example 5: Comparing all strategies on the same problem
160fn example_5_comparing_strategies() {
161 println!("Example 5: Strategy Comparison");
162 println!("{}", "-".repeat(60));
163
164 // Problem: Find minimal subset where sum of squares > 500
165 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 // Strategy 1: Cascading (manual exploration)
173 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 // Strategy 2: Guided
190 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 // Strategy 3: DFS
201 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 // Strategy 4: BFS
211 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}Sourcepub fn with_max_depth(self, max_depth: usize) -> Self
pub fn with_max_depth(self, max_depth: usize) -> Self
Set maximum search depth
Examples found in repository?
examples/advanced_shrinking.rs (line 120)
112fn 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
135/// Example 4: ConfigurableShrinker with Breadth-First Search
136fn 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
159/// Example 5: Comparing all strategies on the same problem
160fn example_5_comparing_strategies() {
161 println!("Example 5: Strategy Comparison");
162 println!("{}", "-".repeat(60));
163
164 // Problem: Find minimal subset where sum of squares > 500
165 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 // Strategy 1: Cascading (manual exploration)
173 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 // Strategy 2: Guided
190 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 // Strategy 3: DFS
201 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 // Strategy 4: BFS
211 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}Sourcepub fn strategy(&self) -> ShrinkStrategy
pub fn strategy(&self) -> ShrinkStrategy
Get the current strategy
Source§impl<T: Clone + Debug + PartialEq + 'static> ConfigurableShrinker<Vec<T>>
impl<T: Clone + Debug + PartialEq + 'static> ConfigurableShrinker<Vec<T>>
Sourcepub fn find_minimal<F>(&self, test: F) -> Vec<T>
pub fn find_minimal<F>(&self, test: F) -> Vec<T>
Find minimal case using the configured strategy
Examples found in repository?
examples/advanced_shrinking.rs (line 123)
112fn 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
135/// Example 4: ConfigurableShrinker with Breadth-First Search
136fn 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
159/// Example 5: Comparing all strategies on the same problem
160fn example_5_comparing_strategies() {
161 println!("Example 5: Strategy Comparison");
162 println!("{}", "-".repeat(60));
163
164 // Problem: Find minimal subset where sum of squares > 500
165 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 // Strategy 1: Cascading (manual exploration)
173 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 // Strategy 2: Guided
190 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 // Strategy 3: DFS
201 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 // Strategy 4: BFS
211 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}Trait Implementations§
Source§impl<T: Clone> Clone for ConfigurableShrinker<T>
impl<T: Clone> Clone for ConfigurableShrinker<T>
Auto Trait Implementations§
impl<T> Freeze for ConfigurableShrinker<T>where
T: Freeze,
impl<T> RefUnwindSafe for ConfigurableShrinker<T>where
T: RefUnwindSafe,
impl<T> Send for ConfigurableShrinker<T>where
T: Send,
impl<T> Sync for ConfigurableShrinker<T>where
T: Sync,
impl<T> Unpin for ConfigurableShrinker<T>where
T: Unpin,
impl<T> UnsafeUnpin for ConfigurableShrinker<T>where
T: UnsafeUnpin,
impl<T> UnwindSafe for ConfigurableShrinker<T>where
T: UnwindSafe,
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more