Skip to main content

ConfigurableShrinker

Struct ConfigurableShrinker 

Source
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>

Source

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}
Source

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}
Source

pub fn strategy(&self) -> ShrinkStrategy

Get the current strategy

Source§

impl<T: Clone + Debug + PartialEq + 'static> ConfigurableShrinker<Vec<T>>

Source

pub fn find_minimal<F>(&self, test: F) -> Vec<T>
where F: Fn(&Vec<T>) -> bool,

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>

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl<T: Debug> Debug for ConfigurableShrinker<T>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

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> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V