diff options
Diffstat (limited to 'vendor/proptest/src/strategy/shuffle.rs')
-rw-r--r-- | vendor/proptest/src/strategy/shuffle.rs | 287 |
1 files changed, 287 insertions, 0 deletions
diff --git a/vendor/proptest/src/strategy/shuffle.rs b/vendor/proptest/src/strategy/shuffle.rs new file mode 100644 index 000000000..b94a73c3b --- /dev/null +++ b/vendor/proptest/src/strategy/shuffle.rs @@ -0,0 +1,287 @@ +//- +// Copyright 2017 Jason Lingle +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use crate::std_facade::{Cell, Vec, VecDeque}; + +use rand::Rng; + +use crate::num; +use crate::strategy::traits::*; +use crate::test_runner::*; + +/// `Strategy` shuffle adaptor. +/// +/// See `Strategy::prop_shuffle()`. +#[derive(Clone, Debug)] +#[must_use = "strategies do nothing unless used"] +pub struct Shuffle<S>(pub(super) S); + +/// A value which can be used with the `prop_shuffle` combinator. +/// +/// This is not a general-purpose trait. Its methods are prefixed with +/// `shuffle_` to avoid the compiler suggesting them or this trait as +/// corrections in errors. +pub trait Shuffleable { + /// Return the length of this collection. + fn shuffle_len(&self) -> usize; + /// Swap the elements at the given indices. + fn shuffle_swap(&mut self, a: usize, b: usize); +} + +macro_rules! shuffleable { + ($($t:tt)*) => { + impl<T> Shuffleable for $($t)* { + fn shuffle_len(&self) -> usize { + self.len() + } + + fn shuffle_swap(&mut self, a: usize, b: usize) { + self.swap(a, b); + } + } + } +} + +shuffleable!([T]); +shuffleable!(Vec<T>); +shuffleable!(VecDeque<T>); +// Zero- and 1-length arrays aren't usefully shuffleable, but are included to +// simplify external macros that may try to use them anyway. +shuffleable!([T; 0]); +shuffleable!([T; 1]); +shuffleable!([T; 2]); +shuffleable!([T; 3]); +shuffleable!([T; 4]); +shuffleable!([T; 5]); +shuffleable!([T; 6]); +shuffleable!([T; 7]); +shuffleable!([T; 8]); +shuffleable!([T; 9]); +shuffleable!([T; 10]); +shuffleable!([T; 11]); +shuffleable!([T; 12]); +shuffleable!([T; 13]); +shuffleable!([T; 14]); +shuffleable!([T; 15]); +shuffleable!([T; 16]); +shuffleable!([T; 17]); +shuffleable!([T; 18]); +shuffleable!([T; 19]); +shuffleable!([T; 20]); +shuffleable!([T; 21]); +shuffleable!([T; 22]); +shuffleable!([T; 23]); +shuffleable!([T; 24]); +shuffleable!([T; 25]); +shuffleable!([T; 26]); +shuffleable!([T; 27]); +shuffleable!([T; 28]); +shuffleable!([T; 29]); +shuffleable!([T; 30]); +shuffleable!([T; 31]); +shuffleable!([T; 32]); + +impl<S: Strategy> Strategy for Shuffle<S> +where + S::Value: Shuffleable, +{ + type Tree = ShuffleValueTree<S::Tree>; + type Value = S::Value; + + fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> { + let rng = runner.new_rng(); + + self.0.new_tree(runner).map(|inner| ShuffleValueTree { + inner, + rng, + dist: Cell::new(None), + simplifying_inner: false, + }) + } +} + +/// `ValueTree` shuffling adaptor. +/// +/// See `Strategy::prop_shuffle()`. +#[derive(Clone, Debug)] +pub struct ShuffleValueTree<V> { + inner: V, + rng: TestRng, + /// The maximum amount to move any one element during shuffling. + /// + /// This is `Cell` since we can't determine the bounds of the value until + /// the first call to `current()`. (We technically _could_ by generating a + /// value in `new_tree` and checking its length, but that would be a 100% + /// slowdown.) + dist: Cell<Option<num::usize::BinarySearch>>, + /// Whether we've started simplifying `inner`. After this point, we can no + /// longer simplify or complicate `dist`. + simplifying_inner: bool, +} + +impl<V: ValueTree> ShuffleValueTree<V> +where + V::Value: Shuffleable, +{ + fn init_dist(&self, dflt: usize) -> usize { + if self.dist.get().is_none() { + self.dist.set(Some(num::usize::BinarySearch::new(dflt))); + } + + self.dist.get().unwrap().current() + } + + fn force_init_dist(&self) { + if self.dist.get().is_none() { + self.init_dist(self.current().shuffle_len()); + } + } +} + +impl<V: ValueTree> ValueTree for ShuffleValueTree<V> +where + V::Value: Shuffleable, +{ + type Value = V::Value; + + fn current(&self) -> V::Value { + let mut value = self.inner.current(); + let len = value.shuffle_len(); + // The maximum distance to swap elements. This could be larger than + // `value` if `value` has reduced size during shrinking; that's OK, + // since we only use this to filter swaps. + let max_swap = self.init_dist(len); + + // If empty collection or all swaps will be filtered out, there's + // nothing to shuffle. + if 0 == len || 0 == max_swap { + return value; + } + + let mut rng = self.rng.clone(); + + for start_index in 0..len - 1 { + // Determine the other index to be swapped, then skip the swap if + // it is too far. This ordering is critical, as it ensures that we + // generate the same sequence of random numbers every time. + let end_index = rng.gen_range(start_index..len); + if end_index - start_index <= max_swap { + value.shuffle_swap(start_index, end_index); + } + } + + value + } + + fn simplify(&mut self) -> bool { + if self.simplifying_inner { + self.inner.simplify() + } else { + // Ensure that we've initialised `dist` to *something* to give + // consistent non-panicking behaviour even if called in an + // unexpected sequence. + self.force_init_dist(); + if self.dist.get_mut().as_mut().unwrap().simplify() { + true + } else { + self.simplifying_inner = true; + self.inner.simplify() + } + } + } + + fn complicate(&mut self) -> bool { + if self.simplifying_inner { + self.inner.complicate() + } else { + self.force_init_dist(); + self.dist.get_mut().as_mut().unwrap().complicate() + } + } +} + +#[cfg(test)] +mod test { + use std::borrow::ToOwned; + use std::collections::HashSet; + use std::i32; + + use super::*; + use crate::collection; + use crate::strategy::just::Just; + + static VALUES: &'static [i32] = &[ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + ]; + + #[test] + fn generates_different_permutations() { + let mut runner = TestRunner::default(); + let mut seen = HashSet::<Vec<i32>>::new(); + + let input = Just(VALUES.to_owned()).prop_shuffle(); + + for _ in 0..1024 { + let mut value = input.new_tree(&mut runner).unwrap().current(); + + assert!( + seen.insert(value.clone()), + "Value {:?} generated more than once", + value + ); + + value.sort(); + assert_eq!(VALUES, &value[..]); + } + } + + #[test] + fn simplify_reduces_shuffle_amount() { + let mut runner = TestRunner::default(); + + let input = Just(VALUES.to_owned()).prop_shuffle(); + for _ in 0..1024 { + let mut value = input.new_tree(&mut runner).unwrap(); + + let mut prev_dist = i32::MAX; + loop { + let v = value.current(); + // Compute the "shuffle distance" by summing the absolute + // distance of each element's displacement. + let mut dist = 0; + for (ix, &nominal) in v.iter().enumerate() { + dist += (nominal - ix as i32).abs(); + } + + assert!( + dist <= prev_dist, + "dist = {}, prev_dist = {}", + dist, + prev_dist + ); + + prev_dist = dist; + if !value.simplify() { + break; + } + } + + // When fully simplified, the result is in the original order. + assert_eq!(0, prev_dist); + } + } + + #[test] + fn simplify_complicate_contract_upheld() { + check_strategy_sanity( + collection::vec(0i32..1000, 5..10).prop_shuffle(), + None, + ); + } +} |