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 From<f64> for Probability {
44 fn from(prob: f64) -> Self {
50 Probability::new(prob)
51 }
52}
53
54impl Probability {
55 pub fn new(prob: f64) -> Self {
61 assert!(prob >= 0.0 && prob <= 1.0);
62 Probability(prob)
63 }
64
65 pub fn with<X>(self, and: X) -> product_type![Self, X] {
72 product_pack![self, and]
73 }
74
75 pub fn lift<X: Default>(self) -> product_type![Self, X] {
80 self.with(Default::default())
81 }
82}
83
84#[cfg(feature = "frunk")]
85use frunk_core::generic::Generic;
86
87#[cfg(feature = "frunk")]
88impl Generic for Probability {
89 type Repr = f64;
90
91 fn into(self) -> Self::Repr {
93 self.0
94 }
95
96 fn from(r: Self::Repr) -> Self {
102 r.into()
103 }
104}
105
106impl From<Probability> for f64 {
107 fn from(p: Probability) -> Self {
108 p.0
109 }
110}
111
112#[derive(Clone, Copy, PartialEq, Debug)]
114pub struct Probability(f64);
115
116mapfn! {
121 [] fn WrapSome[<T : fmt::Debug>](t: T) -> Option<T> {
122 Some(t)
123 }
124}
125
126#[must_use = "strategies do nothing unless used"]
127struct NoneStrategy<T>(PhantomData<T>);
128impl<T> Clone for NoneStrategy<T> {
129 fn clone(&self) -> Self {
130 *self
131 }
132}
133impl<T> Copy for NoneStrategy<T> {}
134impl<T> fmt::Debug for NoneStrategy<T> {
135 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
136 write!(f, "NoneStrategy")
137 }
138}
139impl<T: fmt::Debug> Strategy for NoneStrategy<T> {
140 type Tree = Self;
141 type Value = Option<T>;
142
143 fn new_tree(&self, _: &mut TestRunner) -> NewTree<Self> {
144 Ok(*self)
145 }
146}
147impl<T: fmt::Debug> ValueTree for NoneStrategy<T> {
148 type Value = Option<T>;
149
150 fn current(&self) -> Option<T> {
151 None
152 }
153 fn simplify(&mut self) -> bool {
154 false
155 }
156 fn complicate(&mut self) -> bool {
157 false
158 }
159}
160
161opaque_strategy_wrapper! {
162 #[derive(Clone)]
167 pub struct OptionStrategy[<T>][where T : Strategy]
168 (TupleUnion<(WA<NoneStrategy<T::Value>>,
169 WA<statics::Map<T, WrapSome>>)>)
170 -> OptionValueTree<T>;
171 pub struct OptionValueTree[<T>][where T : Strategy]
173 (TupleUnionValueTree<(
174 LazyValueTree<NoneStrategy<T::Value>>,
175 Option<LazyValueTree<statics::Map<T, WrapSome>>>,
176 )>)
177 -> Option<T::Value>;
178}
179
180impl<T: Strategy + fmt::Debug> fmt::Debug for OptionStrategy<T> {
184 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
185 write!(f, "OptionStrategy({:?})", self.0)
186 }
187}
188
189impl<T: Strategy> Clone for OptionValueTree<T>
190where
191 T::Tree: Clone,
192{
193 fn clone(&self) -> Self {
194 OptionValueTree(self.0.clone())
195 }
196}
197
198impl<T: Strategy> fmt::Debug for OptionValueTree<T>
199where
200 T::Tree: fmt::Debug,
201{
202 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
203 write!(f, "OptionValueTree({:?})", self.0)
204 }
205}
206
207pub fn of<T: Strategy>(t: T) -> OptionStrategy<T> {
214 weighted(Probability::default(), t)
215}
216
217pub fn weighted<T: Strategy>(
225 probability_of_some: impl Into<Probability>,
226 t: T,
227) -> OptionStrategy<T> {
228 let prob = probability_of_some.into().into();
229 let (weight_some, weight_none) = float_to_weight(prob);
230
231 OptionStrategy(TupleUnion::new((
232 (weight_none, Arc::new(NoneStrategy(PhantomData))),
233 (weight_some, Arc::new(statics::Map::new(t, WrapSome))),
234 )))
235}
236
237#[cfg(test)]
238mod test {
239 use super::*;
240
241 fn count_some_of_1000(s: OptionStrategy<Just<i32>>) -> u32 {
242 let mut runner = TestRunner::deterministic();
243 let mut count = 0;
244 for _ in 0..1000 {
245 count +=
246 s.new_tree(&mut runner).unwrap().current().is_some() as u32;
247 }
248
249 count
250 }
251
252 #[test]
253 fn probability_defaults_to_0p5() {
254 let count = count_some_of_1000(of(Just(42i32)));
255 assert!(count > 450 && count < 550);
256 }
257
258 #[test]
259 fn probability_handled_correctly() {
260 let count = count_some_of_1000(weighted(0.9, Just(42i32)));
261 assert!(count > 800 && count < 950);
262
263 let count = count_some_of_1000(weighted(0.1, Just(42i32)));
264 assert!(count > 50 && count < 150);
265 }
266
267 #[test]
268 fn test_sanity() {
269 check_strategy_sanity(of(0i32..1000i32), None);
270 }
271}