prop_check_rs/
gen.rs

1pub mod choose;
2pub mod one;
3
4use crate::gen::choose::Choose;
5use crate::gen::one::One;
6use crate::rng::{NextRandValue, RNG};
7use crate::state::State;
8use bigdecimal::Num;
9use std::cell::RefCell;
10use std::collections::BTreeMap;
11use std::fmt::Debug;
12use std::rc::Rc;
13
14/// Factory responsibility for generating Gens.
15pub struct Gens;
16
17impl Gens {
18  /// Generates a Gen that returns `()`.
19  ///
20  /// # Returns
21  /// - A `Gen<()>` that generates the unit value
22  pub fn unit() -> Gen<()> {
23    Self::pure(())
24  }
25
26  /// Generates a Gen that returns a constant value.
27  ///
28  /// # Arguments
29  /// - `value` - The value to be wrapped in a Gen
30  ///
31  /// # Returns
32  /// - A `Gen<B>` that always generates the provided value
33  pub fn pure<B>(value: B) -> Gen<B>
34  where
35    B: Clone + 'static, {
36    Gen::<B>::new(State::value(value))
37  }
38
39  /// Generates a Gen that returns a value from a lazily evaluated function.
40  ///
41  /// # Arguments
42  /// - `f` - A closure that produces the value when called
43  ///
44  /// # Returns
45  /// - A `Gen<B>` that generates the value by calling the provided function
46  ///
47  /// # Examples
48  /// ```
49  /// use prop_check_rs::gen::Gens;
50  /// let expensive_computation = || {
51  ///     // some expensive computation
52  ///     42
53  /// };
54  /// let gen = Gens::pure_lazy(expensive_computation);
55  /// ```
56  pub fn pure_lazy<B, F>(f: F) -> Gen<B>
57  where
58    F: Fn() -> B + 'static,
59    B: Clone + 'static, {
60    Self::pure(()).map(move |_| f())
61  }
62
63  /// Generates a Gen that wraps the value of another Gen in Some.
64  ///
65  /// # Arguments
66  /// - `gen` - The Gen whose values will be wrapped in Some
67  ///
68  /// # Returns
69  /// - A `Gen<Option<B>>` that always generates Some containing the value from the input Gen
70  ///
71  /// # Type Parameters
72  /// - `B` - The type of value to be wrapped in Some, must implement Clone and have a 'static lifetime
73  ///
74  /// # Examples
75  /// ```
76  /// use prop_check_rs::gen::Gens;
77  /// let number_gen = Gens::choose(1, 10);
78  /// let some_number_gen = Gens::some(number_gen);  // Generates Some(1)..Some(10)
79  /// ```
80  pub fn some<B>(gen: Gen<B>) -> Gen<Option<B>>
81  where
82    B: Clone + 'static, {
83    gen.map(Some)
84  }
85
86  /// Generates a Gen that returns Some or None based on the value of Gen.
87  /// The probability distribution is 90% for Some and 10% for None.
88  ///
89  /// # Arguments
90  /// - `gen` - The Gen to be wrapped in an Option
91  ///
92  /// # Returns
93  /// - A `Gen<Option<B>>` that generates Some(value) 90% of the time and None 10% of the time
94  ///
95  /// # Type Parameters
96  /// - `B` - The type of value to be generated, must implement Debug, Clone and have a 'static lifetime
97  pub fn option<B>(gen: Gen<B>) -> Gen<Option<B>>
98  where
99    B: Debug + Clone + 'static, {
100    Self::frequency([(1, Self::pure(None)), (9, Self::some(gen))])
101  }
102
103  /// Generates a Gen that produces a Result type by combining two Gens.
104  ///
105  /// # Arguments
106  /// - `gt` - The Gen that produces the Ok variant values
107  /// - `ge` - The Gen that produces the Err variant values
108  ///
109  /// # Returns
110  /// - A `Gen<Result<T, E>>` that randomly generates either Ok(T) or Err(E)
111  ///
112  /// # Type Parameters
113  /// - `T` - The success type, must implement Choose, Clone and have a 'static lifetime
114  /// - `E` - The error type, must implement Clone and have a 'static lifetime
115  ///
116  /// # Examples
117  /// ```
118  /// use prop_check_rs::gen::Gens;
119  /// let success_gen = Gens::choose(1, 10);
120  /// let error_gen = Gens::pure("error");
121  /// let result_gen = Gens::either(success_gen, error_gen);
122  /// ```
123  pub fn either<T, E>(gt: Gen<T>, ge: Gen<E>) -> Gen<Result<T, E>>
124  where
125    T: Choose + Clone + 'static,
126    E: Clone + 'static, {
127    Self::one_of([gt.map(Ok), ge.map(Err)])
128  }
129
130  /// Generates a Gen that produces values according to specified weights.
131  ///
132  /// # Arguments
133  /// - `values` - An iterator of tuples where the first element is the weight (u32) and
134  ///             the second element is the value to be generated. The probability of each
135  ///             value being generated is proportional to its weight.
136  ///
137  /// # Returns
138  /// - A `Gen<B>` that generates values with probabilities determined by their weights
139  ///
140  /// # Panics
141  /// - Panics if all weights are 0
142  /// - Panics if no values are provided
143  ///
144  /// # Examples
145  /// ```
146  /// use prop_check_rs::gen::Gens;
147  /// let weighted_gen = Gens::frequency_values([
148  ///     (2, "common"),    // 2/3 probability
149  ///     (1, "rare"),      // 1/3 probability
150  /// ]);
151  /// ```
152  pub fn frequency_values<B>(values: impl IntoIterator<Item = (u32, B)>) -> Gen<B>
153  where
154    B: Debug + Clone + 'static, {
155    Self::frequency(values.into_iter().map(|(n, value)| (n, Gens::pure(value))))
156  }
157
158  /// Generates a Gen that produces values from other Gens according to specified weights.
159  ///
160  /// # Arguments
161  /// - `values` - An iterator of tuples where the first element is the weight (u32) and
162  ///             the second element is another Gen. The probability of each Gen being
163  ///             chosen is proportional to its weight.
164  ///
165  /// # Returns
166  /// - A `Gen<B>` that generates values by selecting and running other Gens based on their weights
167  ///
168  /// # Panics
169  /// - Panics if all weights are 0
170  /// - Panics if no values are provided
171  ///
172  /// # Implementation Notes
173  /// - Uses a BTreeMap for efficient weighted selection
174  /// - Filters out entries with zero weight
175  /// - Maintains cumulative weights for probability calculations
176  pub fn frequency<B>(values: impl IntoIterator<Item = (u32, Gen<B>)>) -> Gen<B>
177  where
178    B: Debug + Clone + 'static, {
179    // Using Vec to pre-allocate capacity
180    let mut filtered = Vec::new();
181    let mut total_weight = 0;
182
183    // Filter only items with weight greater than 0
184    for (weight, value) in values.into_iter() {
185      if weight > 0 {
186        filtered.push((weight, value));
187      }
188    }
189
190    // Build BTreeMap (using cumulative weights as keys)
191    let mut tree = BTreeMap::new();
192    for (weight, value) in filtered {
193      total_weight += weight;
194      tree.insert(total_weight, value);
195    }
196
197    // Avoid error if empty
198    if total_weight == 0 {
199      panic!("Empty frequency distribution");
200    }
201
202    // Generate a random number and return the corresponding value
203    Self::choose_u32(1, total_weight).flat_map(move |n| {
204      // Get the entry with the smallest key greater than or equal to n
205      let entry = tree.range(n..).next().unwrap();
206      entry.1.clone()
207    })
208  }
209
210  /// Generates a Gen that produces a vector of n values generated by another Gen.
211  ///
212  /// # Arguments
213  /// - `n` - The number of values to generate
214  /// - `gen` - The Gen used to generate each value
215  ///
216  /// # Returns
217  /// - A `Gen<Vec<B>>` that generates a vector containing n values
218  ///
219  /// # Performance
220  /// - Uses lazy evaluation internally for better memory efficiency
221  /// - For large n (>= 1000), consider using `list_of_n_chunked` or `list_of_n_chunked_optimal`
222  ///   which may provide better performance through chunk-based processing
223  ///
224  /// # Examples
225  /// ```
226  /// use prop_check_rs::gen::Gens;
227  /// let number_gen = Gens::choose(1, 100);
228  /// let numbers_gen = Gens::list_of_n(5, number_gen);
229  /// // Could generate vec![42, 17, 89, 3, 71]
230  /// ```
231  pub fn list_of_n<B>(n: usize, gen: Gen<B>) -> Gen<Vec<B>>
232  where
233    B: Clone + 'static, {
234    Self::list_of_n_lazy(n, gen)
235  }
236
237  /// Generates a Gen whose elements are the values generated by the specified number of Gen,
238  /// using the optimal chunk size based on benchmarks.
239  pub fn list_of_n_chunked_optimal<B>(n: usize, gen: Gen<B>) -> Gen<Vec<B>>
240  where
241    B: Clone + 'static, {
242    Self::list_of_n_chunked(n, usize::MAX, gen)
243  }
244
245  /// Generates a Gen that produces a vector of values using chunk-based processing for better performance.
246  ///
247  /// # Arguments
248  /// - `n` - The number of values to generate
249  /// - `chunk_size` - The size of chunks for batch processing. For optimal performance:
250  ///                  - Use 1000 for large n (>= 1000)
251  ///                  - For smaller n, the provided chunk_size is used as is
252  /// - `gen` - The Gen used to generate each value
253  ///
254  /// # Returns
255  /// - A `Gen<Vec<B>>` that generates a vector containing n values
256  ///
257  /// # Panics
258  /// - Panics if chunk_size is 0
259  ///
260  /// # Performance Notes
261  /// - Processes values in chunks to reduce memory allocation overhead
262  /// - Automatically adjusts chunk size based on total number of elements
263  /// - More efficient than `list_of_n` for large datasets
264  /// - Consider using `list_of_n_chunked_optimal` for automatic chunk size optimization
265  ///
266  /// # Examples
267  /// ```
268  /// use prop_check_rs::gen::Gens;
269  /// let number_gen = Gens::choose(1, 100);
270  /// let numbers_gen = Gens::list_of_n_chunked(1000, 100, number_gen);
271  /// ```
272  pub fn list_of_n_chunked<B>(n: usize, chunk_size: usize, gen: Gen<B>) -> Gen<Vec<B>>
273  where
274    B: Clone + 'static, {
275    if chunk_size == 0 {
276      panic!("Chunk size must be greater than 0");
277    }
278
279    // Calculate optimal chunk size based on benchmark results
280    let optimal_chunk_size = if n < 1000 {
281      // For small sizes, use the specified chunk size
282      chunk_size
283    } else {
284      // From benchmark results, 1000 is the most efficient chunk size
285      if chunk_size == usize::MAX {
286        // If the default value (usize::MAX) is specified, use 1000
287        1000
288      } else {
289        // Use the specified chunk size
290        chunk_size
291      }
292    };
293
294    Gen::<Vec<B>>::new(State::<RNG, Vec<B>>::new(move |rng: RNG| {
295      let mut result = Vec::with_capacity(n);
296      let mut current_rng = rng;
297
298      // Process in chunks
299      for chunk_start in (0..n).step_by(optimal_chunk_size) {
300        // Calculate the size of the current chunk (the last chunk may be smaller)
301        let current_chunk_size = std::cmp::min(optimal_chunk_size, n - chunk_start);
302
303        // Generate States for the chunk size
304        let mut chunk_states: Vec<State<RNG, B>> = Vec::with_capacity(current_chunk_size);
305        chunk_states.resize_with(current_chunk_size, || gen.clone().sample);
306
307        // Process the chunk
308        let (chunk_result, new_rng) = State::sequence(chunk_states).run(current_rng);
309        result.extend(chunk_result);
310        current_rng = new_rng;
311      }
312
313      (result, current_rng)
314    }))
315  }
316
317  /// Generates a Gen that produces a vector of values using lazy evaluation.
318  ///
319  /// # Arguments
320  /// - `n` - The number of values to generate
321  /// - `gen` - The Gen used to generate each value
322  ///
323  /// # Returns
324  /// - A `Gen<Vec<B>>` that generates a vector containing n values
325  ///
326  /// # Performance Notes
327  /// - Uses lazy evaluation to generate values one at a time
328  /// - Minimizes memory usage by not pre-allocating all states
329  /// - More memory efficient than eager evaluation for large n
330  /// - May be slower than chunk-based processing for very large datasets
331  /// - Maintains consistent memory usage regardless of n
332  ///
333  /// # Examples
334  /// ```
335  /// use prop_check_rs::gen::Gens;
336  /// let number_gen = Gens::choose(1, 100);
337  /// let numbers_gen = Gens::list_of_n_lazy(5, number_gen);
338  /// ```
339  pub fn list_of_n_lazy<B>(n: usize, gen: Gen<B>) -> Gen<Vec<B>>
340  where
341    B: Clone + 'static, {
342    Gen::<Vec<B>>::new(State::<RNG, Vec<B>>::new(move |rng: RNG| {
343      let mut result = Vec::with_capacity(n);
344      let mut current_rng = rng;
345
346      // Generate values using lazy evaluation
347      for _ in 0..n {
348        let (value, new_rng) = gen.clone().run(current_rng);
349        result.push(value);
350        current_rng = new_rng;
351      }
352
353      (result, current_rng)
354    }))
355  }
356
357  /// Generates a Gen that returns a single value using the One trait.
358  ///
359  /// # Type Parameters
360  /// - `T` - The type that implements the One trait
361  ///
362  /// # Returns
363  /// - A `Gen<T>` that generates values using the One trait implementation
364  ///
365  /// # Implementation Notes
366  /// - Uses the `one()` method from the One trait to generate values
367  /// - Useful for types that have a natural "one" or "unit" value
368  ///
369  /// # Examples
370  /// ```
371  /// use prop_check_rs::gen::Gens;
372  /// let number_gen = Gens::one::<i32>();  // Generates using i32's One implementation
373  /// let bool_gen = Gens::one::<bool>();   // Generates using bool's One implementation
374  /// ```
375  pub fn one<T: One>() -> Gen<T> {
376    One::one()
377  }
378
379  /// Generates a Gen that produces random i64 values.
380  ///
381  /// # Returns
382  /// - A `Gen<i64>` that generates random 64-bit signed integers
383  ///
384  /// # Examples
385  /// ```
386  /// use prop_check_rs::gen::Gens;
387  /// let gen = Gens::one_i64();  // Generates random i64 values
388  /// ```
389  pub fn one_i64() -> Gen<i64> {
390    Gen {
391      sample: State::<RNG, i64>::new(move |rng: RNG| rng.next_i64()),
392    }
393  }
394
395  /// Generates a Gen that produces random u64 values.
396  ///
397  /// # Returns
398  /// - A `Gen<u64>` that generates random 64-bit unsigned integers
399  ///
400  /// # Examples
401  /// ```
402  /// use prop_check_rs::gen::Gens;
403  /// let gen = Gens::one_u64();  // Generates random u64 values
404  /// ```
405  pub fn one_u64() -> Gen<u64> {
406    Gen {
407      sample: State::<RNG, u64>::new(move |rng: RNG| rng.next_u64()),
408    }
409  }
410
411  /// Generates a Gen that produces random i32 values.
412  ///
413  /// # Returns
414  /// - A `Gen<i32>` that generates random 32-bit signed integers
415  ///
416  /// # Examples
417  /// ```
418  /// use prop_check_rs::gen::Gens;
419  /// let gen = Gens::one_i32();  // Generates random i32 values
420  /// ```
421  pub fn one_i32() -> Gen<i32> {
422    Gen {
423      sample: State::<RNG, i32>::new(move |rng: RNG| rng.next_i32()),
424    }
425  }
426
427  /// Generates a Gen that produces random u32 values.
428  ///
429  /// # Returns
430  /// - A `Gen<u32>` that generates random 32-bit unsigned integers
431  ///
432  /// # Examples
433  /// ```
434  /// use prop_check_rs::gen::Gens;
435  /// let gen = Gens::one_u32();  // Generates random u32 values
436  /// ```
437  pub fn one_u32() -> Gen<u32> {
438    Gen {
439      sample: State::<RNG, u32>::new(move |rng: RNG| rng.next_u32()),
440    }
441  }
442
443  /// Generates a Gen that produces random i16 values.
444  ///
445  /// # Returns
446  /// * A `Gen<i16>` that generates random 16-bit signed integers
447  ///
448  /// # Examples
449  /// ```
450  /// use prop_check_rs::gen::Gens;
451  /// let gen = Gens::one_i16();  // Generates random i16 values
452  /// ```
453  pub fn one_i16() -> Gen<i16> {
454    Gen {
455      sample: State::<RNG, i16>::new(move |rng: RNG| rng.next_i16()),
456    }
457  }
458
459  /// Generates a Gen that produces random u16 values.
460  ///
461  /// # Returns
462  /// * A `Gen<u16>` that generates random 16-bit unsigned integers
463  ///
464  /// # Examples
465  /// ```
466  /// use prop_check_rs::gen::Gens;
467  /// let gen = Gens::one_u16();  // Generates random u16 values
468  /// ```
469  pub fn one_u16() -> Gen<u16> {
470    Gen {
471      sample: State::<RNG, u16>::new(move |rng: RNG| rng.next_u16()),
472    }
473  }
474
475  /// Generates a Gen that produces random i8 values.
476  ///
477  /// # Returns
478  /// * A `Gen<i8>` that generates random 8-bit signed integers
479  ///
480  /// # Examples
481  /// ```
482  /// use prop_check_rs::gen::Gens;
483  /// let gen = Gens::one_i8();  // Generates random i8 values
484  /// ```
485  pub fn one_i8() -> Gen<i8> {
486    Gen {
487      sample: State::<RNG, i8>::new(move |rng: RNG| rng.next_i8()),
488    }
489  }
490
491  /// Generates a Gen that produces random u8 values.
492  ///
493  /// # Returns
494  /// * A `Gen<u8>` that generates random 8-bit unsigned integers
495  ///
496  /// # Examples
497  /// ```
498  /// use prop_check_rs::gen::Gens;
499  /// let gen = Gens::one_u8();  // Generates random u8 values
500  /// ```
501  pub fn one_u8() -> Gen<u8> {
502    Gen {
503      sample: State::<RNG, u8>::new(move |rng: RNG| rng.next_u8()),
504    }
505  }
506
507  /// Generates a Gen that produces random char values.
508  ///
509  /// # Returns
510  /// * A `Gen<char>` that generates random characters
511  ///
512  /// # Examples
513  /// ```
514  /// use prop_check_rs::gen::Gens;
515  /// let gen = Gens::one_char();  // Generates random characters
516  /// ```
517  pub fn one_char() -> Gen<char> {
518    Self::one_u8().map(|v| v as char)
519  }
520
521  /// Generates a Gen that produces random boolean values.
522  ///
523  /// # Returns
524  /// * A `Gen<bool>` that generates random true/false values
525  ///
526  /// # Examples
527  /// ```
528  /// use prop_check_rs::gen::Gens;
529  /// let gen = Gens::one_bool();  // Generates random booleans
530  /// ```
531  pub fn one_bool() -> Gen<bool> {
532    Gen {
533      sample: State::<RNG, bool>::new(|rng: RNG| rng.next_bool()),
534    }
535  }
536
537  /// Generates a Gen that produces random f64 values.
538  ///
539  /// # Returns
540  /// * A `Gen<f64>` that generates random 64-bit floating point numbers
541  ///
542  /// # Examples
543  /// ```
544  /// use prop_check_rs::gen::Gens;
545  /// let gen = Gens::one_f64();  // Generates random f64 values
546  /// ```
547  pub fn one_f64() -> Gen<f64> {
548    Gen {
549      sample: State::<RNG, f64>::new(move |rng: RNG| rng.next_f64()),
550    }
551  }
552
553  /// Generates a Gen that produces random f32 values.
554  ///
555  /// # Returns
556  /// * A `Gen<f32>` that generates random 32-bit floating point numbers
557  ///
558  /// # Examples
559  /// ```
560  /// use prop_check_rs::gen::Gens;
561  /// let gen = Gens::one_f32();  // Generates random f32 values
562  /// ```
563  pub fn one_f32() -> Gen<f32> {
564    Gen {
565      sample: State::<RNG, f32>::new(move |rng: RNG| rng.next_f32()),
566    }
567  }
568
569  /// Generates a Gen that produces values by randomly selecting from other Gens.
570  ///
571  /// # Arguments
572  /// * `values` - An iterator of Gens to choose from, with equal probability
573  ///
574  /// # Returns
575  /// * A `Gen<T>` that generates values by randomly selecting and running one of the input Gens
576  ///
577  /// # Type Parameters
578  /// * `T` - The type of value to generate, must implement Choose, Clone and have a 'static lifetime
579  ///
580  /// # Panics
581  /// * Panics if the input iterator is empty
582  ///
583  /// # Examples
584  /// ```
585  /// use prop_check_rs::gen::Gens;
586  /// let small = Gens::choose(1, 10);
587  /// let large = Gens::choose(100, 200);
588  /// let combined = Gens::one_of([small, large]);  // 50% chance of each range
589  /// ```
590  pub fn one_of<T: Choose + Clone + 'static>(values: impl IntoIterator<Item = Gen<T>>) -> Gen<T> {
591    // Convert directly to Vec
592    let vec: Vec<_> = values.into_iter().collect();
593
594    // Avoid error if empty
595    if vec.is_empty() {
596      panic!("Empty one_of distribution");
597    }
598
599    // Select an index and return the corresponding value
600    Self::choose(0usize, vec.len() - 1).flat_map(move |idx| {
601      let gen = &vec[idx as usize];
602      gen.clone()
603    })
604  }
605
606  /// Generates a Gen that randomly selects from a set of fixed values.
607  ///
608  /// # Arguments
609  /// * `values` - An iterator of values to choose from, with equal probability
610  ///
611  /// # Returns
612  /// * A `Gen<T>` that generates values by randomly selecting one from the input set
613  ///
614  /// # Type Parameters
615  /// * `T` - The type of value to generate, must implement Choose, Clone and have a 'static lifetime
616  ///
617  /// # Panics
618  /// * Panics if the input iterator is empty
619  ///
620  /// # Examples
621  /// ```
622  /// use prop_check_rs::gen::Gens;
623  /// // Note: &str doesn't implement Choose trait
624  /// // let colors = Gens::one_of_values(["red", "green", "blue"]);
625  /// let numbers = Gens::one_of_values([1, 10, 100]);  // Equal probability for each number
626  /// ```
627  pub fn one_of_values<T: Choose + Clone + 'static>(values: impl IntoIterator<Item = T>) -> Gen<T> {
628    // Convert directly to Vec
629    let vec: Vec<_> = values.into_iter().collect();
630
631    // Avoid error if empty
632    if vec.is_empty() {
633      panic!("Empty one_of_values distribution");
634    }
635
636    // Select an index and return the corresponding value
637    Self::choose(0usize, vec.len() - 1).map(move |idx| vec[idx as usize].clone())
638  }
639
640  /// Generates a Gen that returns one randomly selected value from the specified maximum and minimum ranges of generic type.
641  pub fn choose<T: Choose>(min: T, max: T) -> Gen<T> {
642    Choose::choose(min, max)
643  }
644
645  /// Generates a Gen that returns one randomly selected value from a specified maximum and minimum range of type char.
646  pub fn choose_char(min: char, max: char) -> Gen<char> {
647    // Calculate range using character codes
648    let min_code = min as u32;
649    let max_code = max as u32;
650
651    // Avoid error if range is invalid
652    if min_code > max_code {
653      panic!("Invalid char range");
654    }
655
656    // Select from character code range and convert to character
657    Self::choose_u32(min_code, max_code).map(move |code| std::char::from_u32(code).unwrap_or(min))
658  }
659
660  /// Generates a Gen that returns one randomly selected value from a specified maximum and minimum range of type i64.
661  ///
662  /// # Arguments
663  /// * `min` - The minimum value (inclusive) of the range
664  /// * `max` - The maximum value (inclusive) of the range
665  ///
666  /// # Returns
667  /// * A `Gen<i64>` that generates random i64 values in the range [min, max]
668  ///
669  /// # Panics
670  /// * Panics if `min > max` (invalid range)
671  pub fn choose_i64(min: i64, max: i64) -> Gen<i64> {
672    if min > max {
673      panic!("Invalid range: min > max");
674    }
675
676    // Calculate the size of the range
677    let range = max - min + 1;
678
679    // Use absolute value to prevent overflow
680    Gen {
681      sample: State::<RNG, i64>::new(move |rng: RNG| {
682        let (n, new_rng) = rng.next_i64();
683        // Convert negative values to positive and keep within range
684        let abs_n = if n < 0 { -n } else { n };
685        let value = min + (abs_n % range);
686        (value, new_rng)
687      }),
688    }
689  }
690
691  /// Generates a Gen that returns one randomly selected value from a specified maximum and minimum range of type u64.
692  pub fn choose_u64(min: u64, max: u64) -> Gen<u64> {
693    Gen {
694      sample: State::<RNG, u64>::new(move |rng: RNG| rng.next_u64()),
695    }
696    .map(move |n| min + n % (max - min + 1))
697  }
698
699  /// Generates a Gen that returns one randomly selected value from a specified maximum and minimum range of type i32.
700  ///
701  /// # Arguments
702  /// * `min` - The minimum value (inclusive) of the range
703  /// * `max` - The maximum value (inclusive) of the range
704  ///
705  /// # Returns
706  /// * A `Gen<i32>` that generates random i32 values in the range [min, max]
707  ///
708  /// # Panics
709  /// * Panics if `min > max` (invalid range)
710  pub fn choose_i32(min: i32, max: i32) -> Gen<i32> {
711    if min > max {
712      panic!("Invalid range: min > max");
713    }
714
715    // Calculate the size of the range
716    let range = max - min + 1;
717
718    // Use absolute value to prevent overflow
719    Gen {
720      sample: State::<RNG, i32>::new(move |rng: RNG| {
721        let (n, new_rng) = rng.next_i32();
722        // Convert negative values to positive and keep within range
723        let abs_n = if n < 0 { -n } else { n };
724        let value = min + (abs_n % range);
725        (value, new_rng)
726      }),
727    }
728  }
729
730  /// Generates a Gen that returns one randomly selected value from a specified maximum and minimum range of type u32.
731  pub fn choose_u32(min: u32, max: u32) -> Gen<u32> {
732    Gen {
733      sample: State::<RNG, u32>::new(move |rng: RNG| rng.next_u32()),
734    }
735    .map(move |n| min + n % (max - min + 1))
736  }
737
738  /// Generates a Gen that returns one randomly selected value from a specified maximum and minimum range of type i16.
739  pub fn choose_i16(min: i16, max: i16) -> Gen<i16> {
740    Gen {
741      sample: State::<RNG, i16>::new(move |rng: RNG| rng.next_i16()),
742    }
743    .map(move |n| min + n % (max - min + 1))
744  }
745
746  /// Generates a Gen that returns one randomly selected value from a specified maximum and minimum range of type u16.
747  pub fn choose_u16(min: u16, max: u16) -> Gen<u16> {
748    Gen {
749      sample: State::<RNG, u16>::new(move |rng: RNG| rng.next_u16()),
750    }
751    .map(move |n| min + n % (max - min + 1))
752  }
753
754  /// Generates a Gen that returns one randomly selected value from a specified maximum and minimum range of type i8.
755  pub fn choose_i8(min: i8, max: i8) -> Gen<i8> {
756    Gen {
757      sample: State::<RNG, i8>::new(move |rng: RNG| rng.next_i8()),
758    }
759    .map(move |n| min + n % (max - min + 1))
760  }
761
762  /// Generates a Gen that returns one randomly selected value from a specified maximum and minimum range of type u8.
763  pub fn choose_u8(min: u8, max: u8) -> Gen<u8> {
764    Gen {
765      sample: State::<RNG, u8>::new(move |rng: RNG| rng.next_u8()),
766    }
767    .map(move |n| min + n % (max - min + 1))
768  }
769
770  /// Generates a Gen that returns one randomly selected value from a specified maximum and minimum range of type f64.
771  ///
772  /// # Arguments
773  /// * `min` - The minimum value (inclusive) of the range
774  /// * `max` - The maximum value (inclusive) of the range
775  ///
776  /// # Returns
777  /// * A `Gen<f64>` that generates random f64 values in the range [min, max]
778  ///
779  /// # Note
780  /// * The distribution is uniform across the range
781  pub fn choose_f64(min: f64, max: f64) -> Gen<f64> {
782    Gen {
783      sample: State::<RNG, f64>::new(move |rng: RNG| rng.next_f64()),
784    }
785    .map(move |d| min + d * (max - min))
786  }
787
788  /// Generates a Gen that returns one randomly selected value from a specified maximum and minimum range of type f32.
789  ///
790  /// # Arguments
791  /// * `min` - The minimum value (inclusive) of the range
792  /// * `max` - The maximum value (inclusive) of the range
793  ///
794  /// # Returns
795  /// * A `Gen<f32>` that generates random f32 values in the range [min, max]
796  ///
797  /// # Note
798  /// * The distribution is uniform across the range
799  pub fn choose_f32(min: f32, max: f32) -> Gen<f32> {
800    Gen {
801      sample: State::<RNG, f32>::new(move |rng: RNG| rng.next_f32()),
802    }
803    .map(move |d| min + d * (max - min))
804  }
805
806  /// Generates a Gen that returns randomly selected even numbers from a specified range.
807  ///
808  /// # Arguments
809  /// * `start` - The inclusive start of the range
810  /// * `stop_exclusive` - The exclusive end of the range
811  ///
812  /// # Type Parameters
813  /// * `T` - A numeric type that implements Choose, Num, Copy, and 'static
814  ///
815  /// # Returns
816  /// * A `Gen<T>` that generates even numbers in the range [start, stop_exclusive)
817  ///
818  /// # Implementation Notes
819  /// * If the start value is odd, the first even number after it will be used
820  /// * If stop_exclusive is odd, stop_exclusive - 1 will be used as the end of the range
821  /// * Maintains uniform distribution over even numbers in the range
822  ///
823  /// # Examples
824  /// ```
825  /// use prop_check_rs::gen::Gens;
826  /// let even_gen = Gens::even(1, 10);  // Generates from {2, 4, 6, 8}
827  /// ```
828  pub fn even<T: Choose + Num + Copy + 'static>(start: T, stop_exclusive: T) -> Gen<T> {
829    let two = T::one().add(T::one());
830    Self::choose(
831      start,
832      if stop_exclusive % two == T::zero() {
833        stop_exclusive - T::one()
834      } else {
835        stop_exclusive
836      },
837    )
838    .map(move |n| if n % two != T::zero() { n + T::one() } else { n })
839  }
840
841  /// Generates a Gen that returns randomly selected odd numbers from a specified range.
842  ///
843  /// # Arguments
844  /// * `start` - The inclusive start of the range
845  /// * `stop_exclusive` - The exclusive end of the range
846  ///
847  /// # Type Parameters
848  /// * `T` - A numeric type that implements Choose, Num, Copy, and 'static
849  ///
850  /// # Returns
851  /// * A `Gen<T>` that generates odd numbers in the range [start, stop_exclusive)
852  ///
853  /// # Implementation Notes
854  /// * If the start value is even, the first odd number after it will be used
855  /// * If stop_exclusive is even, stop_exclusive - 1 will be used as the end of the range
856  /// * Maintains uniform distribution over odd numbers in the range
857  ///
858  /// # Examples
859  /// ```
860  /// use prop_check_rs::gen::Gens;
861  /// let odd_gen = Gens::odd(1, 10);  // Generates from {1, 3, 5, 7, 9}
862  /// ```
863  pub fn odd<T: Choose + Num + Copy + 'static>(start: T, stop_exclusive: T) -> Gen<T> {
864    let two = T::one().add(T::one());
865    Self::choose(
866      start,
867      if stop_exclusive % two != T::zero() {
868        stop_exclusive - T::one()
869      } else {
870        stop_exclusive
871      },
872    )
873    .map(move |n| if n % two == T::zero() { n + T::one() } else { n })
874  }
875}
876
877/// Generator that generates values.
878#[derive(Debug)]
879pub struct Gen<A> {
880  sample: State<RNG, A>,
881}
882
883impl<A: Clone + 'static> Clone for Gen<A> {
884  fn clone(&self) -> Self {
885    Self {
886      sample: self.sample.clone(),
887    }
888  }
889}
890
891impl<A: Clone + 'static> Gen<A> {
892  /// Evaluates the Gen with a given RNG to produce a value.
893  ///
894  /// # Arguments
895  /// * `rng` - The random number generator to use
896  ///
897  /// # Returns
898  /// * A tuple `(A, RNG)` containing the generated value and the updated RNG state
899  ///
900  /// # Examples
901  /// ```
902  /// use prop_check_rs::gen::Gens;
903  /// use prop_check_rs::rng::RNG;
904  /// let gen = Gens::choose(1, 10);
905  /// let (value, new_rng) = gen.run(RNG::new());
906  /// assert!(value >= 1 && value <= 10);
907  /// ```
908  pub fn run(self, rng: RNG) -> (A, RNG) {
909    self.sample.run(rng)
910  }
911
912  /// Creates a new Gen by wrapping a State.
913  ///
914  /// # Arguments
915  /// * `b` - The State to wrap in a Gen
916  ///
917  /// # Returns
918  /// * A new `Gen<B>` containing the provided State
919  pub fn new<B>(b: State<RNG, B>) -> Gen<B> {
920    Gen { sample: b }
921  }
922
923  /// Transforms the output of a Gen using a function.
924  ///
925  /// # Arguments
926  /// * `f` - A function to apply to the generated value
927  ///
928  /// # Returns
929  /// * A new `Gen<B>` that applies the function to generated values
930  ///
931  /// # Type Parameters
932  /// * `B` - The result type after applying the function
933  /// * `F` - The function type, must be Fn(A) -> B + 'static
934  ///
935  /// # Examples
936  /// ```
937  /// use prop_check_rs::gen::Gens;
938  /// let numbers = Gens::choose(1, 10);
939  /// let doubled = numbers.map(|x| x * 2);  // Generates numbers from 2 to 20
940  /// ```
941  pub fn map<B, F>(self, f: F) -> Gen<B>
942  where
943    F: Fn(A) -> B + 'static,
944    B: Clone + 'static, {
945    Self::new(self.sample.map(f))
946  }
947
948  /// Combines two Gens using a function that takes both of their results.
949  ///
950  /// # Arguments
951  /// * `g` - Another Gen to combine with this one
952  /// * `f` - A function that combines the results of both Gens
953  ///
954  /// # Returns
955  /// * A new `Gen<C>` that combines the results of both Gens
956  ///
957  /// # Type Parameters
958  /// * `B` - The type of the second Gen's output
959  /// * `C` - The result type after combining both outputs
960  /// * `F` - The function type, must be Fn(A, B) -> C + 'static
961  ///
962  /// # Examples
963  /// ```
964  /// use prop_check_rs::gen::Gens;
965  /// let x = Gens::choose(1, 5);
966  /// let y = Gens::choose(6, 10);
967  /// let sum = x.and_then(y, |a, b| a + b);  // Generates sums from 7 to 15
968  /// ```
969  pub fn and_then<B, C, F>(self, g: Gen<B>, f: F) -> Gen<C>
970  where
971    F: Fn(A, B) -> C + 'static,
972    A: Clone,
973    B: Clone + 'static,
974    C: Clone + 'static, {
975    Self::new(self.sample.and_then(g.sample).map(move |(a, b)| f(a, b)))
976  }
977
978  /// Chains this Gen with a function that returns another Gen.
979  ///
980  /// # Arguments
981  /// * `f` - A function that takes the result of this Gen and returns a new Gen
982  ///
983  /// # Returns
984  /// * A new `Gen<B>` that represents the chained computation
985  ///
986  /// # Type Parameters
987  /// * `B` - The type of the resulting Gen
988  /// * `F` - The function type, must be Fn(A) -> Gen<B> + 'static
989  ///
990  /// # Examples
991  /// ```
992  /// use prop_check_rs::gen::Gens;
993  /// let numbers = Gens::choose(1, 3);
994  /// let repeated = numbers.flat_map(|n| Gens::list_of_n(n, Gens::pure(n)));
995  /// ```
996  pub fn flat_map<B, F>(self, f: F) -> Gen<B>
997  where
998    F: Fn(A) -> Gen<B> + 'static,
999    B: Clone + 'static, {
1000    Self::new(self.sample.flat_map(move |a| f(a).sample))
1001  }
1002}
1003
1004/// Generator with size information.
1005pub enum SGen<A> {
1006  /// Generator with size information.
1007  Sized(Rc<RefCell<dyn Fn(u32) -> Gen<A>>>),
1008  /// Generator without size information.
1009  Unsized(Gen<A>),
1010}
1011
1012impl<A: Clone + 'static> Clone for SGen<A> {
1013  fn clone(&self) -> Self {
1014    match self {
1015      SGen::Sized(f) => SGen::Sized(f.clone()),
1016      SGen::Unsized(g) => SGen::Unsized(g.clone()),
1017    }
1018  }
1019}
1020
1021impl<A: Clone + 'static> SGen<A> {
1022  /// Creates a sized generator that can produce different Gens based on a size parameter.
1023  ///
1024  /// # Arguments
1025  /// * `f` - A function that takes a size parameter and returns a Gen
1026  ///
1027  /// # Returns
1028  /// * An `SGen<A>` that can generate size-dependent values
1029  ///
1030  /// # Type Parameters
1031  /// * `F` - The function type, must be Fn(u32) -> Gen<A> + 'static
1032  ///
1033  /// # Examples
1034  /// ```
1035  /// use prop_check_rs::gen::SGen;
1036  /// use prop_check_rs::gen::Gens;
1037  /// let sized_gen = SGen::of_sized(|size| Gens::list_of_n(size as usize, Gens::choose(1, 10)));
1038  /// ```
1039  pub fn of_sized<F>(f: F) -> SGen<A>
1040  where
1041    F: Fn(u32) -> Gen<A> + 'static, {
1042    SGen::Sized(Rc::new(RefCell::new(f)))
1043  }
1044
1045  /// Creates an unsized generator that wraps a fixed Gen.
1046  ///
1047  /// # Arguments
1048  /// * `gen` - The Gen to wrap
1049  ///
1050  /// # Returns
1051  /// * An `SGen<A>` that always uses the provided Gen
1052  ///
1053  /// # Examples
1054  /// ```
1055  /// use prop_check_rs::gen::SGen;
1056  /// use prop_check_rs::gen::Gens;
1057  /// let fixed_gen = SGen::of_unsized(Gens::choose(1, 10));
1058  /// ```
1059  pub fn of_unsized(gen: Gen<A>) -> SGen<A> {
1060    SGen::Unsized(gen)
1061  }
1062
1063  /// Runs the generator with an optional size parameter to produce a Gen.
1064  ///
1065  /// # Arguments
1066  /// * `i` - Optional size parameter, required for Sized variants
1067  ///
1068  /// # Returns
1069  /// * A `Gen<A>` that can generate values
1070  ///
1071  /// # Panics
1072  /// * Panics if a Sized variant is run without a size parameter
1073  ///
1074  /// # Examples
1075  /// ```
1076  /// use prop_check_rs::gen::SGen;
1077  /// use prop_check_rs::gen::Gens;
1078  /// let sized_gen = SGen::of_sized(|n| Gens::list_of_n(n as usize, Gens::pure(1)));
1079  /// let gen = sized_gen.run(Some(5));  // Creates a Gen that produces a vector of 5 ones
1080  /// ```
1081  pub fn run(&self, i: Option<u32>) -> Gen<A> {
1082    match self {
1083      SGen::Sized(f) => {
1084        let mf = f.borrow_mut();
1085        mf(i.unwrap())
1086      }
1087      SGen::Unsized(g) => g.clone(),
1088    }
1089  }
1090}
1091
1092#[cfg(test)]
1093mod tests {
1094  use super::*;
1095  use crate::prop;
1096  use anyhow::Result;
1097
1098  use std::cell::RefCell;
1099  use std::collections::HashMap;
1100  use std::env;
1101  use std::rc::Rc;
1102
1103  fn init() {
1104    env::set_var("RUST_LOG", "info");
1105    let _ = env_logger::builder().is_test(true).try_init();
1106  }
1107
1108  fn new_rng() -> RNG {
1109    RNG::new()
1110  }
1111
1112  pub mod laws {
1113    use super::*;
1114
1115    #[test]
1116    fn test_left_identity_law() -> Result<()> {
1117      init();
1118      let gen = Gens::choose_i32(1, i32::MAX / 2).map(|e| (RNG::new().with_seed(e as u64), e));
1119      let f = |x| Gens::pure(x);
1120      let laws_prop = prop::for_all_gen(gen, move |(s, n)| {
1121        Gens::pure(n).flat_map(f).run(s.clone()) == f(n).run(s)
1122      });
1123      prop::test_with_prop(laws_prop, 1, 100, new_rng())
1124    }
1125
1126    #[test]
1127    fn test_right_identity_law() -> Result<()> {
1128      init();
1129      let gen = Gens::choose_i32(1, i32::MAX / 2).map(|e| (RNG::new().with_seed(e as u64), e));
1130
1131      let laws_prop = prop::for_all_gen(gen, move |(s, x)| {
1132        Gens::pure(x).flat_map(|y| Gens::pure(y)).run(s.clone()) == Gens::pure(x).run(s)
1133      });
1134
1135      prop::test_with_prop(laws_prop, 1, 100, new_rng())
1136    }
1137
1138    #[test]
1139    fn test_associativity_law() -> Result<()> {
1140      init();
1141      let gen = Gens::choose_i32(1, i32::MAX / 2).map(|e| (RNG::new().with_seed(e as u64), e));
1142      let f = |x| Gens::pure(x * 2);
1143      let g = |x| Gens::pure(x + 1);
1144      let laws_prop = prop::for_all_gen(gen, move |(s, x)| {
1145        Gens::pure(x).flat_map(f).flat_map(g).run(s.clone()) == f(x).flat_map(g).run(s)
1146      });
1147      prop::test_with_prop(laws_prop, 1, 100, new_rng())
1148    }
1149  }
1150
1151  #[test]
1152  fn test_frequency() -> Result<()> {
1153    let result = Rc::new(RefCell::new(HashMap::new()));
1154    let cloned_map = result.clone();
1155
1156    let gens = [
1157      (1, Gens::choose(1u32, 10)),
1158      (3, Gens::choose(50u32, 100)),
1159      (1, Gens::choose(200u32, 300)),
1160    ];
1161    let gen = Gens::frequency(gens);
1162    let prop = prop::for_all_gen(gen, move |a| {
1163      let mut map = result.borrow_mut();
1164      log::info!("a: {}", a);
1165      if a >= 1 && a <= 10 {
1166        let r = map.entry(1).or_insert_with(|| 0);
1167        *r += 1;
1168        true
1169      } else if a >= 50 && a <= 100 {
1170        let r = map.entry(2).or_insert_with(|| 0);
1171        *r += 1;
1172        true
1173      } else if a >= 200 && a <= 300 {
1174        let r = map.entry(3).or_insert_with(|| 0);
1175        *r += 1;
1176        true
1177      } else {
1178        false
1179      }
1180    });
1181    let r = prop::test_with_prop(prop, 1, 100, new_rng());
1182
1183    let map = cloned_map.borrow();
1184    let a_count = map.get(&1).unwrap();
1185    let b_count = map.get(&2).unwrap();
1186    let c_count = map.get(&3).unwrap();
1187
1188    assert_eq!(*a_count + *b_count + *c_count, 100);
1189    println!("{cloned_map:?}");
1190    r
1191  }
1192
1193  #[test]
1194  fn test_frequency_values() -> Result<()> {
1195    let result = Rc::new(RefCell::new(HashMap::new()));
1196    let cloned_map = result.clone();
1197
1198    let gens = [(1, "a"), (1, "b"), (8, "c")];
1199    let gen = Gens::frequency_values(gens);
1200    let prop = prop::for_all_gen(gen, move |a| {
1201      let mut map = result.borrow_mut();
1202      let r = map.entry(a).or_insert_with(|| 0);
1203      *r += 1;
1204      true
1205    });
1206    let r = prop::test_with_prop(prop, 1, 100, new_rng());
1207
1208    let map = cloned_map.borrow();
1209    let a_count = map.get(&"a").unwrap();
1210    let b_count = map.get(&"b").unwrap();
1211    let c_count = map.get(&"c").unwrap();
1212
1213    assert_eq!(*a_count + *b_count + *c_count, 100);
1214    println!("{cloned_map:?}");
1215    r
1216  }
1217
1218  #[test]
1219  fn test_list_of_n_chunked() {
1220    init();
1221    // Test with small chunk size
1222    let n = 100;
1223    let chunk_size = 10;
1224    let gen = Gens::one_i32();
1225    let chunked_gen = Gens::list_of_n_chunked(n, chunk_size, gen.clone());
1226
1227    let (result, _) = chunked_gen.run(new_rng());
1228
1229    // Verify the result size is correct
1230    assert_eq!(result.len(), n);
1231
1232    // Test with large chunk size
1233    let large_chunk_size = 1000;
1234    let large_chunked_gen = Gens::list_of_n_chunked(n, large_chunk_size, gen);
1235
1236    let (large_result, _) = large_chunked_gen.run(new_rng());
1237
1238    // Verify the result size is correct
1239    assert_eq!(large_result.len(), n);
1240  }
1241
1242  #[test]
1243  fn test_list_of_n_lazy() {
1244    init();
1245    let n = 100;
1246    let gen = Gens::one_i32();
1247    let lazy_gen = Gens::list_of_n_lazy(n, gen.clone());
1248
1249    let (result, _) = lazy_gen.run(new_rng());
1250
1251    // Verify the result size is correct
1252    assert_eq!(result.len(), n);
1253
1254    // Compare results with normal list_of_n
1255    let normal_gen = Gens::list_of_n(n, gen);
1256    let (normal_result, _) = normal_gen.run(new_rng().with_seed(42));
1257    let (lazy_result, _) = Gens::list_of_n_lazy(n, Gens::one_i32()).run(new_rng().with_seed(42));
1258
1259    // Verify that results are the same when using the same seed
1260    assert_eq!(normal_result, lazy_result);
1261  }
1262
1263  #[test]
1264  fn test_large_data_generation() {
1265    init();
1266    // Generate large amount of data
1267    let n = 10000;
1268
1269    // Normal method
1270    let start_time = std::time::Instant::now();
1271    let gen = Gens::one_i32();
1272    let normal_gen = Gens::list_of_n(n, gen.clone());
1273    let (normal_result, _) = normal_gen.run(new_rng());
1274    let normal_duration = start_time.elapsed();
1275
1276    // Using chunk processing
1277    let start_time = std::time::Instant::now();
1278    let chunk_size = 1000;
1279    let chunked_gen = Gens::list_of_n_chunked(n, chunk_size, gen.clone());
1280    let (chunked_result, _) = chunked_gen.run(new_rng());
1281    let chunked_duration = start_time.elapsed();
1282
1283    // Using lazy evaluation
1284    let start_time = std::time::Instant::now();
1285    let lazy_gen = Gens::list_of_n_lazy(n, gen);
1286    let (lazy_result, _) = lazy_gen.run(new_rng());
1287    let lazy_duration = start_time.elapsed();
1288
1289    // Verify the result size is correct
1290    assert_eq!(normal_result.len(), n);
1291    assert_eq!(chunked_result.len(), n);
1292    assert_eq!(lazy_result.len(), n);
1293
1294    // Log performance information
1295    log::info!("Normal generation time: {:?}", normal_duration);
1296    log::info!("Chunked generation time: {:?}", chunked_duration);
1297    log::info!("Lazy generation time: {:?}", lazy_duration);
1298  }
1299}