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}