| //- |
| // Copyright 2017, 2018 The proptest developers |
| // |
| // 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. |
| |
| //! Strategies for working with bit sets. |
| //! |
| //! Besides `BitSet` itself, this also defines strategies for all the primitive |
| //! integer types. These strategies are appropriate for integers which are used |
| //! as bit flags, etc; e.g., where the most reasonable simplification of `64` |
| //! is `0` (clearing one bit) and not `63` (clearing one bit but setting 6 |
| //! others). For integers treated as numeric values, see the corresponding |
| //! modules of the `num` module instead. |
| |
| use crate::std_facade::{fmt, Vec}; |
| use core::marker::PhantomData; |
| use core::mem; |
| |
| #[cfg(feature = "bit-set")] |
| use bit_set::BitSet; |
| use rand::{self, seq::IteratorRandom, Rng}; |
| |
| use crate::collection::SizeRange; |
| use crate::num::sample_uniform_incl; |
| use crate::strategy::*; |
| use crate::test_runner::*; |
| |
| /// Trait for types which can be handled with `BitSetStrategy`. |
| #[cfg_attr(feature = "cargo-clippy", allow(len_without_is_empty))] |
| pub trait BitSetLike: Clone + fmt::Debug { |
| /// Create a new value of `Self` with space for up to `max` bits, all |
| /// initialised to zero. |
| fn new_bitset(max: usize) -> Self; |
| /// Return an upper bound on the greatest bit set _plus one_. |
| fn len(&self) -> usize; |
| /// Test whether the given bit is set. |
| fn test(&self, ix: usize) -> bool; |
| /// Set the given bit. |
| fn set(&mut self, ix: usize); |
| /// Clear the given bit. |
| fn clear(&mut self, ix: usize); |
| /// Return the number of bits set. |
| /// |
| /// This has a default for backwards compatibility, which simply does a |
| /// linear scan through the bits. Implementations are strongly encouraged |
| /// to override this. |
| fn count(&self) -> usize { |
| let mut n = 0; |
| for i in 0..self.len() { |
| if self.test(i) { |
| n += 1; |
| } |
| } |
| n |
| } |
| } |
| |
| macro_rules! int_bitset { |
| ($typ:ty) => { |
| impl BitSetLike for $typ { |
| fn new_bitset(_: usize) -> Self { |
| 0 |
| } |
| fn len(&self) -> usize { |
| mem::size_of::<$typ>() * 8 |
| } |
| fn test(&self, ix: usize) -> bool { |
| 0 != (*self & ((1 as $typ) << ix)) |
| } |
| fn set(&mut self, ix: usize) { |
| *self |= (1 as $typ) << ix; |
| } |
| fn clear(&mut self, ix: usize) { |
| *self &= !((1 as $typ) << ix); |
| } |
| fn count(&self) -> usize { |
| self.count_ones() as usize |
| } |
| } |
| }; |
| } |
| int_bitset!(u8); |
| int_bitset!(u16); |
| int_bitset!(u32); |
| int_bitset!(u64); |
| int_bitset!(usize); |
| int_bitset!(i8); |
| int_bitset!(i16); |
| int_bitset!(i32); |
| int_bitset!(i64); |
| int_bitset!(isize); |
| |
| #[cfg(feature = "bit-set")] |
| impl BitSetLike for BitSet { |
| fn new_bitset(max: usize) -> Self { |
| BitSet::with_capacity(max) |
| } |
| |
| fn len(&self) -> usize { |
| self.capacity() |
| } |
| |
| fn test(&self, bit: usize) -> bool { |
| self.contains(bit) |
| } |
| |
| fn set(&mut self, bit: usize) { |
| self.insert(bit); |
| } |
| |
| fn clear(&mut self, bit: usize) { |
| self.remove(bit); |
| } |
| |
| fn count(&self) -> usize { |
| self.len() |
| } |
| } |
| |
| impl BitSetLike for Vec<bool> { |
| fn new_bitset(max: usize) -> Self { |
| vec![false; max] |
| } |
| |
| fn len(&self) -> usize { |
| self.len() |
| } |
| |
| fn test(&self, bit: usize) -> bool { |
| if bit >= self.len() { |
| false |
| } else { |
| self[bit] |
| } |
| } |
| |
| fn set(&mut self, bit: usize) { |
| if bit >= self.len() { |
| self.resize(bit + 1, false); |
| } |
| |
| self[bit] = true; |
| } |
| |
| fn clear(&mut self, bit: usize) { |
| if bit < self.len() { |
| self[bit] = false; |
| } |
| } |
| |
| fn count(&self) -> usize { |
| self.iter().filter(|&&b| b).count() |
| } |
| } |
| |
| /// Generates values as a set of bits between the two bounds. |
| /// |
| /// Values are generated by uniformly setting individual bits to 0 |
| /// or 1 between the bounds. Shrinking iteratively clears bits. |
| #[must_use = "strategies do nothing unless used"] |
| #[derive(Clone, Copy, Debug)] |
| pub struct BitSetStrategy<T: BitSetLike> { |
| min: usize, |
| max: usize, |
| mask: Option<T>, |
| } |
| |
| impl<T: BitSetLike> BitSetStrategy<T> { |
| /// Create a strategy which generates values where bits between `min` |
| /// (inclusive) and `max` (exclusive) may be set. |
| /// |
| /// Due to the generics, the functions in the typed submodules are usually |
| /// preferable to calling this directly. |
| pub fn new(min: usize, max: usize) -> Self { |
| BitSetStrategy { |
| min, |
| max, |
| mask: None, |
| } |
| } |
| |
| /// Create a strategy which generates values where any bits set (and only |
| /// those bits) in `mask` may be set. |
| pub fn masked(mask: T) -> Self { |
| BitSetStrategy { |
| min: 0, |
| max: mask.len(), |
| mask: Some(mask), |
| } |
| } |
| } |
| |
| impl<T: BitSetLike> Strategy for BitSetStrategy<T> { |
| type Tree = BitSetValueTree<T>; |
| type Value = T; |
| |
| fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> { |
| let mut inner = T::new_bitset(self.max); |
| for bit in self.min..self.max { |
| if self.mask.as_ref().map_or(true, |mask| mask.test(bit)) |
| && runner.rng().gen() |
| { |
| inner.set(bit); |
| } |
| } |
| |
| Ok(BitSetValueTree { |
| inner, |
| shrink: self.min, |
| prev_shrink: None, |
| min_count: 0, |
| }) |
| } |
| } |
| |
| /// Generates bit sets with a particular number of bits set. |
| /// |
| /// Specifically, this strategy is given both a size range and a bit range. To |
| /// produce a new value, it selects a size, then uniformly selects that many |
| /// bits from within the bit range. |
| /// |
| /// Shrinking happens as with [`BitSetStrategy`](struct.BitSetStrategy.html). |
| #[derive(Clone, Debug)] |
| #[must_use = "strategies do nothing unless used"] |
| pub struct SampledBitSetStrategy<T: BitSetLike> { |
| size: SizeRange, |
| bits: SizeRange, |
| _marker: PhantomData<T>, |
| } |
| |
| impl<T: BitSetLike> SampledBitSetStrategy<T> { |
| /// Create a strategy which generates values where bits within the bounds |
| /// given by `bits` may be set. The number of bits that are set is chosen |
| /// to be in the range given by `size`. |
| /// |
| /// Due to the generics, the functions in the typed submodules are usually |
| /// preferable to calling this directly. |
| /// |
| /// ## Panics |
| /// |
| /// Panics if `size` includes a value that is greater than the number of |
| /// bits in `bits`. |
| pub fn new(size: impl Into<SizeRange>, bits: impl Into<SizeRange>) -> Self { |
| let size = size.into(); |
| let bits = bits.into(); |
| size.assert_nonempty(); |
| |
| let available_bits = bits.end_excl() - bits.start(); |
| assert!( |
| size.end_excl() <= available_bits + 1, |
| "Illegal SampledBitSetStrategy: have {} bits available, \ |
| but requested size is {}..{}", |
| available_bits, |
| size.start(), |
| size.end_excl() |
| ); |
| SampledBitSetStrategy { |
| size, |
| bits, |
| _marker: PhantomData, |
| } |
| } |
| } |
| |
| impl<T: BitSetLike> Strategy for SampledBitSetStrategy<T> { |
| type Tree = BitSetValueTree<T>; |
| type Value = T; |
| |
| fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> { |
| let mut bits = T::new_bitset(self.bits.end_excl()); |
| let count = sample_uniform_incl( |
| runner, |
| self.size.start(), |
| self.size.end_incl(), |
| ); |
| if bits.len() < count { |
| panic!("not enough bits to sample"); |
| } |
| |
| for bit in self.bits.iter().choose_multiple(runner.rng(), count) { |
| bits.set(bit); |
| } |
| |
| Ok(BitSetValueTree { |
| inner: bits, |
| shrink: self.bits.start(), |
| prev_shrink: None, |
| min_count: self.size.start(), |
| }) |
| } |
| } |
| |
| /// Value tree produced by `BitSetStrategy` and `SampledBitSetStrategy`. |
| #[derive(Clone, Copy, Debug)] |
| pub struct BitSetValueTree<T: BitSetLike> { |
| inner: T, |
| shrink: usize, |
| prev_shrink: Option<usize>, |
| min_count: usize, |
| } |
| |
| impl<T: BitSetLike> ValueTree for BitSetValueTree<T> { |
| type Value = T; |
| |
| fn current(&self) -> T { |
| self.inner.clone() |
| } |
| |
| fn simplify(&mut self) -> bool { |
| if self.inner.count() <= self.min_count { |
| return false; |
| } |
| |
| while self.shrink < self.inner.len() && !self.inner.test(self.shrink) { |
| self.shrink += 1; |
| } |
| |
| if self.shrink >= self.inner.len() { |
| self.prev_shrink = None; |
| false |
| } else { |
| self.prev_shrink = Some(self.shrink); |
| self.inner.clear(self.shrink); |
| self.shrink += 1; |
| true |
| } |
| } |
| |
| fn complicate(&mut self) -> bool { |
| if let Some(bit) = self.prev_shrink.take() { |
| self.inner.set(bit); |
| true |
| } else { |
| false |
| } |
| } |
| } |
| |
| macro_rules! int_api { |
| ($typ:ident, $max:expr) => { |
| #[allow(missing_docs)] |
| pub mod $typ { |
| use super::*; |
| |
| /// Generates integers where all bits may be set. |
| pub const ANY: BitSetStrategy<$typ> = BitSetStrategy { |
| min: 0, |
| max: $max, |
| mask: None, |
| }; |
| |
| /// Generates values where bits between the given bounds may be |
| /// set. |
| pub fn between(min: usize, max: usize) -> BitSetStrategy<$typ> { |
| BitSetStrategy::new(min, max) |
| } |
| |
| /// Generates values where any bits set in `mask` (and no others) |
| /// may be set. |
| pub fn masked(mask: $typ) -> BitSetStrategy<$typ> { |
| BitSetStrategy::masked(mask) |
| } |
| |
| /// Create a strategy which generates values where bits within the |
| /// bounds given by `bits` may be set. The number of bits that are |
| /// set is chosen to be in the range given by `size`. |
| /// |
| /// ## Panics |
| /// |
| /// Panics if `size` includes a value that is greater than the |
| /// number of bits in `bits`. |
| pub fn sampled( |
| size: impl Into<SizeRange>, |
| bits: impl Into<SizeRange>, |
| ) -> SampledBitSetStrategy<$typ> { |
| SampledBitSetStrategy::new(size, bits) |
| } |
| } |
| }; |
| } |
| |
| int_api!(u8, 8); |
| int_api!(u16, 16); |
| int_api!(u32, 32); |
| int_api!(u64, 64); |
| int_api!(i8, 8); |
| int_api!(i16, 16); |
| int_api!(i32, 32); |
| int_api!(i64, 64); |
| |
| macro_rules! minimal_api { |
| ($md:ident, $typ:ty) => { |
| #[allow(missing_docs)] |
| pub mod $md { |
| use super::*; |
| |
| /// Generates values where bits between the given bounds may be |
| /// set. |
| pub fn between(min: usize, max: usize) -> BitSetStrategy<$typ> { |
| BitSetStrategy::new(min, max) |
| } |
| |
| /// Generates values where any bits set in `mask` (and no others) |
| /// may be set. |
| pub fn masked(mask: $typ) -> BitSetStrategy<$typ> { |
| BitSetStrategy::masked(mask) |
| } |
| |
| /// Create a strategy which generates values where bits within the |
| /// bounds given by `bits` may be set. The number of bits that are |
| /// set is chosen to be in the range given by `size`. |
| /// |
| /// ## Panics |
| /// |
| /// Panics if `size` includes a value that is greater than the |
| /// number of bits in `bits`. |
| pub fn sampled( |
| size: impl Into<SizeRange>, |
| bits: impl Into<SizeRange>, |
| ) -> SampledBitSetStrategy<$typ> { |
| SampledBitSetStrategy::new(size, bits) |
| } |
| } |
| }; |
| } |
| minimal_api!(usize, usize); |
| minimal_api!(isize, isize); |
| #[cfg(feature = "bit-set")] |
| minimal_api!(bitset, BitSet); |
| minimal_api!(bool_vec, Vec<bool>); |
| |
| pub(crate) mod varsize { |
| use super::*; |
| use core::iter::FromIterator; |
| |
| #[cfg(feature = "bit-set")] |
| type Inner = BitSet; |
| #[cfg(not(feature = "bit-set"))] |
| type Inner = Vec<bool>; |
| |
| #[derive(Debug, Clone)] |
| pub(crate) struct VarBitSet(Inner); |
| |
| impl VarBitSet { |
| pub(crate) fn saturated(len: usize) -> Self { |
| (0..len).collect::<VarBitSet>() |
| } |
| |
| #[cfg(not(feature = "bit-set"))] |
| pub(crate) fn iter<'a>(&'a self) -> impl Iterator<Item = usize> + 'a { |
| (0..self.len()).into_iter().filter(move |&ix| self.test(ix)) |
| } |
| |
| #[cfg(feature = "bit-set")] |
| pub(crate) fn iter<'a>(&'a self) -> impl Iterator<Item = usize> + 'a { |
| self.0.iter() |
| } |
| } |
| |
| impl BitSetLike for VarBitSet { |
| fn new_bitset(max: usize) -> Self { |
| VarBitSet(Inner::new_bitset(max)) |
| } |
| |
| fn len(&self) -> usize { |
| BitSetLike::len(&self.0) |
| } |
| |
| fn test(&self, bit: usize) -> bool { |
| BitSetLike::test(&self.0, bit) |
| } |
| |
| fn set(&mut self, bit: usize) { |
| BitSetLike::set(&mut self.0, bit); |
| } |
| |
| fn clear(&mut self, bit: usize) { |
| BitSetLike::clear(&mut self.0, bit); |
| } |
| |
| fn count(&self) -> usize { |
| BitSetLike::count(&self.0) |
| } |
| } |
| |
| impl FromIterator<usize> for VarBitSet { |
| fn from_iter<T: IntoIterator<Item = usize>>(iter: T) -> Self { |
| let mut bits = VarBitSet::new_bitset(0); |
| for bit in iter { |
| bits.set(bit); |
| } |
| bits |
| } |
| } |
| |
| /* |
| pub(crate) fn between(min: usize, max: usize) -> BitSetStrategy<VarBitSet> { |
| BitSetStrategy::new(min, max) |
| } |
| |
| pub(crate) fn masked(mask: VarBitSet) -> BitSetStrategy<VarBitSet> { |
| BitSetStrategy::masked(mask) |
| } |
| */ |
| |
| pub(crate) fn sampled( |
| size: impl Into<SizeRange>, |
| bits: impl Into<SizeRange>, |
| ) -> SampledBitSetStrategy<VarBitSet> { |
| SampledBitSetStrategy::new(size, bits) |
| } |
| } |
| |
| pub(crate) use self::varsize::VarBitSet; |
| |
| #[cfg(test)] |
| mod test { |
| use super::*; |
| |
| #[test] |
| fn generates_values_in_range() { |
| let input = u32::between(4, 8); |
| |
| let mut runner = TestRunner::default(); |
| for _ in 0..256 { |
| let value = input.new_tree(&mut runner).unwrap().current(); |
| assert!(0 == value & !0xF0u32, "Generate value {}", value); |
| } |
| } |
| |
| #[test] |
| fn generates_values_in_mask() { |
| let mut accum = 0; |
| |
| let mut runner = TestRunner::deterministic(); |
| let input = u32::masked(0xdeadbeef); |
| for _ in 0..1024 { |
| accum |= input.new_tree(&mut runner).unwrap().current(); |
| } |
| |
| assert_eq!(0xdeadbeef, accum); |
| } |
| |
| #[cfg(feature = "bit-set")] |
| #[test] |
| fn mask_bounds_for_bitset_correct() { |
| let mut seen_0 = false; |
| let mut seen_2 = false; |
| |
| let mut mask = BitSet::new(); |
| mask.insert(0); |
| mask.insert(2); |
| |
| let mut runner = TestRunner::deterministic(); |
| let input = bitset::masked(mask); |
| for _ in 0..32 { |
| let v = input.new_tree(&mut runner).unwrap().current(); |
| seen_0 |= v.contains(0); |
| seen_2 |= v.contains(2); |
| } |
| |
| assert!(seen_0); |
| assert!(seen_2); |
| } |
| |
| #[test] |
| fn mask_bounds_for_vecbool_correct() { |
| let mut seen_0 = false; |
| let mut seen_2 = false; |
| |
| let mask = vec![true, false, true, false]; |
| |
| let mut runner = TestRunner::deterministic(); |
| let input = bool_vec::masked(mask); |
| for _ in 0..32 { |
| let v = input.new_tree(&mut runner).unwrap().current(); |
| assert_eq!(4, v.len()); |
| seen_0 |= v[0]; |
| seen_2 |= v[2]; |
| } |
| |
| assert!(seen_0); |
| assert!(seen_2); |
| } |
| |
| #[test] |
| fn shrinks_to_zero() { |
| let input = u32::between(4, 24); |
| |
| let mut runner = TestRunner::default(); |
| for _ in 0..256 { |
| let mut value = input.new_tree(&mut runner).unwrap(); |
| let mut prev = value.current(); |
| while value.simplify() { |
| let v = value.current(); |
| assert!( |
| 1 == (prev & !v).count_ones(), |
| "Shrank from {} to {}", |
| prev, |
| v |
| ); |
| prev = v; |
| } |
| |
| assert_eq!(0, value.current()); |
| } |
| } |
| |
| #[test] |
| fn complicates_to_previous() { |
| let input = u32::between(4, 24); |
| |
| let mut runner = TestRunner::default(); |
| for _ in 0..256 { |
| let mut value = input.new_tree(&mut runner).unwrap(); |
| let orig = value.current(); |
| if value.simplify() { |
| assert!(value.complicate()); |
| assert_eq!(orig, value.current()); |
| } |
| } |
| } |
| |
| #[test] |
| fn sampled_selects_correct_sizes_and_bits() { |
| let input = u32::sampled(4..8, 10..20); |
| let mut seen_counts = [0; 32]; |
| let mut seen_bits = [0; 32]; |
| |
| let mut runner = TestRunner::deterministic(); |
| for _ in 0..2048 { |
| let value = input.new_tree(&mut runner).unwrap().current(); |
| let count = value.count_ones() as usize; |
| assert!(count >= 4 && count < 8); |
| seen_counts[count] += 1; |
| |
| for bit in 0..32 { |
| if 0 != value & (1 << bit) { |
| assert!(bit >= 10 && bit < 20); |
| seen_bits[bit] += value; |
| } |
| } |
| } |
| |
| for i in 4..8 { |
| assert!(seen_counts[i] >= 256 && seen_counts[i] < 1024); |
| } |
| |
| let least_seen_bit_count = |
| seen_bits[10..20].iter().cloned().min().unwrap(); |
| let most_seen_bit_count = |
| seen_bits[10..20].iter().cloned().max().unwrap(); |
| assert_eq!(1, most_seen_bit_count / least_seen_bit_count); |
| } |
| |
| #[test] |
| fn sampled_doesnt_shrink_below_min_size() { |
| let input = u32::sampled(4..8, 10..20); |
| |
| let mut runner = TestRunner::default(); |
| for _ in 0..256 { |
| let mut value = input.new_tree(&mut runner).unwrap(); |
| while value.simplify() {} |
| |
| assert_eq!(4, value.current().count_ones()); |
| } |
| } |
| |
| #[test] |
| fn test_sanity() { |
| check_strategy_sanity(u32::masked(0xdeadbeef), None); |
| } |
| } |