1#![cfg_attr(clippy, allow(expl_impl_clone_on_copy))]
13
14use core::fmt;
15use core::marker::PhantomData;
16
17use crate::std_facade::Arc;
18use crate::strategy::*;
19use crate::test_runner::*;
20
21pub fn prob(from: impl Into<Probability>) -> Probability {
33 from.into()
34}
35
36impl Default for Probability {
37 fn default() -> Self {
39 prob(0.5)
40 }
41}
42
43impl Probability {
44 pub fn new(prob: f64) -> Self {
50 assert!(prob >= 0.0 && prob <= 1.0);
51 Probability(prob)
52 }
53
54 pub fn with<X>(self, and: X) -> product_type![Self, X] {
61 product_pack![self, and]
62 }
63
64 pub fn lift<X: Default>(self) -> product_type![Self, X] {
69 self.with(Default::default())
70 }
71}
72
73impl From<f64> for Probability {
74 fn from(prob: f64) -> Self {
80 Probability::new(prob)
81 }
82}
83
84impl From<Probability> for f64 {
85 fn from(p: Probability) -> Self {
86 p.0
87 }
88}
89
90#[derive(Clone, Copy, PartialEq, Debug)]
92pub struct Probability(f64);
93
94mapfn! {
99 [] fn WrapSome[<T : fmt::Debug>](t: T) -> Option<T> {
100 Some(t)
101 }
102}
103
104#[must_use = "strategies do nothing unless used"]
105struct NoneStrategy<T>(PhantomData<T>);
106impl<T> Clone for NoneStrategy<T> {
107 fn clone(&self) -> Self {
108 *self
109 }
110}
111impl<T> Copy for NoneStrategy<T> {}
112impl<T> fmt::Debug for NoneStrategy<T> {
113 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
114 write!(f, "NoneStrategy")
115 }
116}
117impl<T: fmt::Debug> Strategy for NoneStrategy<T> {
118 type Tree = Self;
119 type Value = Option<T>;
120
121 fn new_tree(&self, _: &mut TestRunner) -> NewTree<Self> {
122 Ok(*self)
123 }
124}
125impl<T: fmt::Debug> ValueTree for NoneStrategy<T> {
126 type Value = Option<T>;
127
128 fn current(&self) -> Option<T> {
129 None
130 }
131 fn simplify(&mut self) -> bool {
132 false
133 }
134 fn complicate(&mut self) -> bool {
135 false
136 }
137}
138
139opaque_strategy_wrapper! {
140 #[derive(Clone)]
145 pub struct OptionStrategy[<T>][where T : Strategy]
146 (TupleUnion<(WA<NoneStrategy<T::Value>>,
147 WA<statics::Map<T, WrapSome>>)>)
148 -> OptionValueTree<T>;
149 pub struct OptionValueTree[<T>][where T : Strategy]
151 (TupleUnionValueTree<(
152 LazyValueTree<NoneStrategy<T::Value>>,
153 Option<LazyValueTree<statics::Map<T, WrapSome>>>,
154 )>)
155 -> Option<T::Value>;
156}
157
158impl<T: Strategy + fmt::Debug> fmt::Debug for OptionStrategy<T> {
162 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
163 write!(f, "OptionStrategy({:?})", self.0)
164 }
165}
166
167impl<T: Strategy> Clone for OptionValueTree<T>
168where
169 T::Tree: Clone,
170{
171 fn clone(&self) -> Self {
172 OptionValueTree(self.0.clone())
173 }
174}
175
176impl<T: Strategy> fmt::Debug for OptionValueTree<T>
177where
178 T::Tree: fmt::Debug,
179{
180 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
181 write!(f, "OptionValueTree({:?})", self.0)
182 }
183}
184
185pub fn of<T: Strategy>(t: T) -> OptionStrategy<T> {
192 weighted(Probability::default(), t)
193}
194
195pub fn weighted<T: Strategy>(
203 probability_of_some: impl Into<Probability>,
204 t: T,
205) -> OptionStrategy<T> {
206 let prob = probability_of_some.into().into();
207 let (weight_some, weight_none) = float_to_weight(prob);
208
209 OptionStrategy(TupleUnion::new((
210 (weight_none, Arc::new(NoneStrategy(PhantomData))),
211 (weight_some, Arc::new(statics::Map::new(t, WrapSome))),
212 )))
213}
214
215#[cfg(test)]
216mod test {
217 use super::*;
218
219 fn count_some_of_1000(s: OptionStrategy<Just<i32>>) -> u32 {
220 let mut runner = TestRunner::deterministic();
221 let mut count = 0;
222 for _ in 0..1000 {
223 count +=
224 s.new_tree(&mut runner).unwrap().current().is_some() as u32;
225 }
226
227 count
228 }
229
230 #[test]
231 fn probability_defaults_to_0p5() {
232 let count = count_some_of_1000(of(Just(42i32)));
233 assert!(count > 450 && count < 550);
234 }
235
236 #[test]
237 fn probability_handled_correctly() {
238 let count = count_some_of_1000(weighted(0.9, Just(42i32)));
239 assert!(count > 800 && count < 950);
240
241 let count = count_some_of_1000(weighted(0.1, Just(42i32)));
242 assert!(count > 50 && count < 150);
243 }
244
245 #[test]
246 fn test_sanity() {
247 check_strategy_sanity(of(0i32..1000i32), None);
248 }
249}