checkito/
primitive.rs

1use crate::{
2    any::Any,
3    generate::{FullGenerate, Generate, State},
4    nudge::Nudge,
5    shrink::Shrink,
6};
7use core::{
8    convert::TryInto,
9    marker::PhantomData,
10    ops::{self, Bound},
11};
12
13#[derive(Copy, Clone, Debug)]
14pub(crate) enum Direction {
15    None,
16    Low,
17    High,
18}
19
20#[derive(Debug)]
21pub struct Full<T: ?Sized>(PhantomData<T>);
22
23#[derive(Debug)]
24pub struct Special<T: ?Sized>(PhantomData<T>);
25
26#[derive(Clone, Debug)]
27pub struct Shrinker<T> {
28    pub(crate) start: T,
29    pub(crate) end: T,
30    pub(crate) item: T,
31    pub(crate) direction: Direction,
32}
33
34impl<T: ?Sized> Special<T> {
35    pub(crate) const NEW: Self = Self(PhantomData);
36}
37
38impl<T: ?Sized> Clone for Special<T> {
39    fn clone(&self) -> Self {
40        *self
41    }
42}
43impl<T: ?Sized> Copy for Special<T> {}
44
45impl<T: ?Sized> Full<T> {
46    pub(crate) const NEW: Self = Self(PhantomData);
47}
48
49impl<T: ?Sized> Clone for Full<T> {
50    fn clone(&self) -> Self {
51        *self
52    }
53}
54impl<T: ?Sized> Copy for Full<T> {}
55
56macro_rules! full {
57    ($t:ty) => {
58        impl FullGenerate for $t {
59            type Generator = Full<$t>;
60            type Item = $t;
61
62            fn generator() -> Self::Generator {
63                Full::<$t>::NEW
64            }
65        }
66    };
67}
68
69macro_rules! same {
70    ($t:ty) => {
71        impl Generate for $t {
72            type Item = Self;
73            type Shrink = Self;
74
75            fn generate(&self, _: &mut State) -> Self::Shrink {
76                <$t as Clone>::clone(self)
77            }
78
79            fn constant(&self) -> bool {
80                true
81            }
82        }
83
84        impl Shrink for $t {
85            type Item = Self;
86
87            fn item(&self) -> Self::Item {
88                <$t as Clone>::clone(self)
89            }
90
91            fn shrink(&mut self) -> Option<Self> {
92                None
93            }
94        }
95    };
96}
97
98macro_rules! range {
99    (CHARACTER, $t:ident, $r:ty) => {
100        impl Generate for $r {
101            type Item = $t;
102            type Shrink = Shrinker;
103
104            fn generate(&self, state: &mut State) -> Self::Shrink {
105                let (start, end) = range(self);
106                Shrinker((start..=end).generate(state))
107            }
108
109            fn constant(&self) -> bool {
110                let (start, end) = range(self);
111                (start..=end).constant()
112            }
113        }
114    };
115    (INTEGER, $t:ident, $r:ty) => {
116        impl Generate for $r {
117            type Item = $t;
118            type Shrink = Shrinker<$t>;
119
120            fn generate(&self, state: &mut State) -> Self::Shrink {
121                let (start, end) = range(self);
122                let (start, end) = shrinked((start, end), state.size());
123                let item = state.random().$t(start..=end);
124                Shrinker {
125                    start,
126                    end,
127                    item,
128                    direction: Direction::None,
129                }
130            }
131
132            fn constant(&self) -> bool {
133                let (start, end) = range(self);
134                start == end
135            }
136        }
137    };
138    (FLOATING, $t:ident, $r:ty) => {
139        impl Generate for $r {
140            type Item = $t;
141            type Shrink = Shrinker<$t>;
142
143            fn generate(&self, state: &mut State) -> Self::Shrink {
144                let (start, end) = range(self);
145                debug_assert!(start.is_finite() && end.is_finite());
146                let (start, end) = shrinked((start, end), state.size());
147                debug_assert!(start.is_finite() && end.is_finite());
148                let ratio = state.random().$t();
149                debug_assert!(ratio.is_finite() && ratio >= 0 as $t && ratio <= 1 as $t);
150                let difference = end * ratio - start * ratio;
151                let item = (difference + start).clamp(start, end);
152                debug_assert!(item.is_finite());
153                Shrinker {
154                    start,
155                    end,
156                    item,
157                    direction: Direction::None,
158                }
159            }
160
161            fn constant(&self) -> bool {
162                let (start, end) = range(self);
163                start == end
164            }
165        }
166    };
167}
168
169macro_rules! ranges {
170    ($k: ident, $t:ident) => {
171        range!($k, $t, ops::Range<$t>);
172        range!($k, $t, ops::RangeInclusive<$t>);
173        range!($k, $t, ops::RangeFrom<$t>);
174        range!($k, $t, ops::RangeTo<$t>);
175        range!($k, $t, ops::RangeToInclusive<$t>);
176    };
177}
178
179macro_rules! shrinked {
180    ($t:ident) => {
181        pub(crate) fn shrinked(pair: ($t, $t), size: f64) -> ($t, $t) {
182            fn shrink(range: f64, size: f64) -> f64 {
183                // This adjustment of the size tries to prevent large ranges (such as `u64`)
184                // from rushing into huge values as soon as the `size > 0`.
185                range * size.powf(range.abs().log2() / 12.0)
186            }
187
188            if pair.0 == pair.1 {
189                (pair.0, pair.1)
190            } else if pair.0 >= 0 as $t {
191                debug_assert!(pair.1 >= 0 as $t);
192                let range = (pair.1 - pair.0) as f64;
193                let shrunk = shrink(range, size);
194                let end = (pair.0 as f64 + shrunk) as $t;
195                (pair.0, end.clamp(pair.0, pair.1))
196            } else if pair.1 <= 0 as $t {
197                debug_assert!(pair.0 <= 0 as $t);
198                let range = (pair.0 - pair.1) as f64;
199                let shrunk = shrink(range, size);
200                let start = (pair.1 as f64 + shrunk) as $t;
201                (start.clamp(pair.0, pair.1), pair.1)
202            } else {
203                debug_assert!(pair.0 < 0 as $t);
204                debug_assert!(pair.1 > 0 as $t);
205                let start = pair.0 as f64;
206                let end = pair.1 as f64;
207                let left = shrink(start, size) * 0.5;
208                let right = shrink(end, size) * 0.5;
209                let mut ranges = (left - right, right - left);
210                if ranges.0 < start {
211                    ranges.1 += start - ranges.0;
212                } else if ranges.1 > end {
213                    ranges.0 += end - ranges.1;
214                }
215                (
216                    (ranges.0 as $t).clamp(pair.0, pair.1),
217                    (ranges.1 as $t).clamp(pair.0, pair.1),
218                )
219            }
220        }
221    };
222}
223
224macro_rules! shrink {
225    ($s:expr, $t:ident) => {{
226        // Never change `$s.item` to preserve coherence in calls to `shrinker.item()`.
227        match $s.direction {
228            Direction::None if $s.item >= 0 as $t => {
229                $s.start = $s.start.max(0 as $t);
230                if $s.start == $s.item {
231                    None
232                } else {
233                    $s.direction = Direction::High;
234                    $s.end = $s.item;
235                    Some(Shrinker {
236                        direction: $s.direction,
237                        start: $s.start,
238                        end: $s.start,
239                        item: $s.start,
240                    })
241                }
242            }
243            Direction::None => {
244                $s.end = $s.end.min(0 as $t);
245                if $s.end == $s.item {
246                    None
247                } else {
248                    $s.direction = Direction::Low;
249                    $s.start = $s.item;
250                    Some(Shrinker {
251                        direction: $s.direction,
252                        start: $s.end,
253                        end: $s.end,
254                        item: $s.end,
255                    })
256                }
257            }
258            Direction::Low => {
259                let delta = $s.end / 2 as $t - $s.start / 2 as $t;
260                let middle = $s.start + delta;
261                if middle == $s.start || middle == $s.end {
262                    None
263                } else {
264                    let mut shrinker = $s.clone();
265                    shrinker.start = middle;
266                    shrinker.item = middle;
267                    $s.end = middle;
268                    Some(shrinker)
269                }
270            }
271            Direction::High => {
272                let delta = $s.end / 2 as $t - $s.start / 2 as $t;
273                let middle = $s.start + delta;
274                if middle == $s.start || middle == $s.end {
275                    None
276                } else {
277                    let mut shrinker = $s.clone();
278                    shrinker.end = middle;
279                    shrinker.item = middle;
280                    $s.start = middle;
281                    Some(shrinker)
282                }
283            }
284        }
285    }};
286}
287
288pub mod bool {
289    use super::*;
290    use core::mem::take;
291
292    #[derive(Copy, Clone, Debug)]
293    pub struct Shrinker(bool, bool);
294
295    impl Generate for Full<bool> {
296        type Item = bool;
297        type Shrink = Shrinker;
298
299        fn generate(&self, state: &mut State) -> Self::Shrink {
300            Shrinker(true, state.random().bool())
301        }
302
303        fn constant(&self) -> bool {
304            false
305        }
306    }
307
308    impl Shrink for Shrinker {
309        type Item = bool;
310
311        fn item(&self) -> Self::Item {
312            self.1
313        }
314
315        fn shrink(&mut self) -> Option<Self> {
316            // A distinct `bool` is required to avoid modifying the `item()` if it would be
317            // called after shrink.
318            if self.1 && take(&mut self.0) {
319                Some(Shrinker(false, false))
320            } else {
321                None
322            }
323        }
324    }
325
326    full!(bool);
327    same!(bool);
328}
329
330pub mod char {
331    use super::*;
332
333    #[derive(Clone, Debug)]
334    pub struct Shrinker(super::Shrinker<u32>);
335
336    impl Generate for Special<char> {
337        type Item = char;
338        type Shrink = char;
339
340        fn generate(&self, state: &mut State) -> Self::Shrink {
341            Any((
342                '\\',
343                '\x0B',
344                '\x1B',
345                '\x7F',
346                '\u{0000}',
347                '\u{D7FF}',
348                '\u{E000}',
349                '\u{FEFF}',
350                '\u{202E}',
351                '¥',
352                'Ѩ',
353                'Ⱥ',
354                '🕴',
355                char::MAX,
356                char::REPLACEMENT_CHARACTER,
357            ))
358            .generate(state)
359            .into()
360        }
361
362        fn constant(&self) -> bool {
363            false
364        }
365    }
366
367    fn range<R: ops::RangeBounds<char>>(range: &R) -> (u32, u32) {
368        let start = match range.start_bound() {
369            Bound::Included(&bound) => Bound::Included(bound as u32),
370            Bound::Excluded(&bound) => Bound::Excluded(bound as u32),
371            Bound::Unbounded => Bound::Included(0),
372        };
373        let end = match range.end_bound() {
374            Bound::Included(&bound) => Bound::Included(bound as u32),
375            Bound::Excluded(&bound) => Bound::Excluded(bound as u32),
376            Bound::Unbounded => Bound::Included(char::MAX as u32),
377        };
378        number::u32::range(&(start, end))
379    }
380
381    pub(crate) const fn shrink(item: char) -> Shrinker {
382        Shrinker(number::u32::shrinker(item as u32))
383    }
384
385    impl Generate for Full<char> {
386        type Item = char;
387        type Shrink = Shrinker;
388
389        fn generate(&self, state: &mut State) -> Self::Shrink {
390            match state.random().u8(..) {
391                0..=249 => (0 as char..=char::MAX).generate(state),
392                250.. => shrink(Special::<char>::NEW.generate(state)),
393            }
394        }
395
396        fn constant(&self) -> bool {
397            false
398        }
399    }
400
401    impl Shrink for Shrinker {
402        type Item = char;
403
404        fn item(&self) -> Self::Item {
405            self.0
406                .item()
407                .try_into()
408                .unwrap_or(char::REPLACEMENT_CHARACTER)
409        }
410
411        fn shrink(&mut self) -> Option<Self> {
412            Some(Self(self.0.shrink()?))
413        }
414    }
415
416    full!(char);
417    same!(char);
418    ranges!(CHARACTER, char);
419}
420
421pub mod string {
422    use super::*;
423
424    same!(&str);
425    same!(Box<str>);
426    same!(String);
427}
428
429pub mod number {
430    use super::*;
431
432    pub trait Number: Sized {
433        type Full: Generate<Item = Self>;
434        type Special: Generate<Item = Self>;
435        type Positive: Generate<Item = Self>;
436        type Negative: Generate<Item = Self>;
437
438        const ZERO: Self;
439        const ONE: Self;
440        const MIN: Self;
441        const MAX: Self;
442        const FULL: Self::Full;
443        const SPECIAL: Self::Special;
444        const POSITIVE: Self::Positive;
445        const NEGATIVE: Self::Negative;
446    }
447
448    macro_rules! number {
449        ($t: ident) => {
450            impl Number for $t {
451                type Full = ops::RangeInclusive<Self>;
452                type Negative = ops::RangeInclusive<Self>;
453                type Positive = ops::RangeInclusive<Self>;
454                type Special = Special<Self>;
455
456                const FULL: Self::Full = Self::MIN..=Self::MAX;
457                const MAX: Self = $t::MAX;
458                const MIN: Self = $t::MIN;
459                const NEGATIVE: Self::Negative = Self::MIN..=Self::ZERO;
460                const ONE: Self = 1 as $t;
461                const POSITIVE: Self::Positive = Self::ZERO..=Self::MAX;
462                const SPECIAL: Self::Special = Special::<$t>::NEW;
463                const ZERO: Self = 0 as $t;
464            }
465        };
466    }
467
468    macro_rules! integer {
469        ($t:ident) => {
470            impl Generate for Special<$t> {
471                type Item = $t;
472                type Shrink = $t;
473
474                fn generate(&self, state: &mut State) -> Self::Shrink {
475                    Any((0 as $t, $t::MIN, $t::MAX)).generate(state).into()
476                }
477
478                fn constant(&self) -> bool {
479                    false
480                }
481            }
482
483            pub(crate) const fn shrinker(item: $t) -> Shrinker<$t> {
484                Shrinker { start: $t::MIN, end: $t::MAX, item, direction: Direction::None }
485            }
486
487            impl Generate for Full<$t> {
488                type Item = $t;
489                type Shrink = Shrinker<$t>;
490
491                fn generate(&self, state: &mut State) -> Self::Shrink {
492                    match state.random().u8(..) {
493                        0..=249 => ($t::MIN..=$t::MAX).generate(state),
494                        250.. => shrinker(Special::<$t>::NEW.generate(state)),
495                    }
496                }
497
498                fn constant(&self) -> bool {
499                    false
500                }
501            }
502
503            impl Shrink for Shrinker<$t> {
504                type Item = $t;
505
506                fn item(&self) -> Self::Item {
507                    self.item
508                }
509
510                fn shrink(&mut self) -> Option<Self> {
511                    shrink!(self, $t)
512                }
513            }
514
515            shrinked!($t);
516
517            /// - An empty range (0..=0) or invalid range (0..0) will use the `start` value.
518            /// - An reversed range will be flipped.
519            pub fn range<R: ops::RangeBounds<$t>>(range: &R) -> ($t, $t) {
520                let mut start = match range.start_bound() {
521                    Bound::Included(&bound) => (bound, false),
522                    Bound::Excluded(&bound) => (bound, true),
523                    Bound::Unbounded => ($t::MIN, false),
524                };
525                let mut end = match range.end_bound() {
526                    Bound::Included(&bound) => (bound, false),
527                    Bound::Excluded(&bound) => (bound, true),
528                    Bound::Unbounded => ($t::MAX, false),
529                };
530                if start.0 == end.0 {
531                    return (start.0, end.0);
532                }
533                if start.0 > end.0 {
534                    (start, end) = (end, start);
535                }
536                if start.1 {
537                    start.0 = start.0.saturating_add(1 as $t);
538                }
539                if end.1 {
540                    end.0 = end.0.saturating_sub(1 as $t);
541                }
542                (start.0.clamp($t::MIN, end.0), end.0.clamp(start.0, $t::MAX))
543            }
544
545            full!($t);
546            same!($t);
547            ranges!(INTEGER, $t);
548            number!($t);
549        };
550        ($($ts:ident),*) => { $(pub(crate) mod $ts { use super::*; integer!($ts); })* };
551    }
552
553    macro_rules! floating {
554        ($t:ident) => {
555            impl Generate for Special<$t> {
556                type Item = $t;
557                type Shrink = $t;
558
559                fn generate(&self, state: &mut State) -> Self::Shrink {
560                    Any((0 as $t, $t::MIN, $t::MAX, $t::EPSILON, $t::INFINITY, $t::NEG_INFINITY, $t::MIN_POSITIVE, $t::NAN))
561                        .generate(state)
562                        .into()
563                }
564
565                fn constant(&self) -> bool {
566                    false
567                }
568            }
569
570            pub(crate) const fn shrinker(item: $t) -> Shrinker<$t> {
571                Shrinker { start: $t::MIN, end: $t::MAX, item, direction: Direction::None }
572            }
573
574            shrinked!($t);
575
576            impl Generate for Full<$t> {
577                type Item = $t;
578                type Shrink = Shrinker<$t>;
579
580                fn generate(&self, state: &mut State) -> Self::Shrink {
581                    match state.random().u8(..) {
582                        0..=89 => ($t::MIN..=$t::MAX).generate(state),
583                        90..=179 => (-$t::EPSILON.recip()..=$t::EPSILON.recip()).generate(state),
584                        180..=214 => ($t::MIN.recip()..=$t::MAX.recip()).generate(state),
585                        215..=249 => (-$t::EPSILON..=$t::EPSILON).generate(state),
586                        250.. => shrinker(Special::<$t>::NEW.generate(state)),
587                    }
588                }
589
590                fn constant(&self) -> bool {
591                    false
592                }
593            }
594
595            impl Shrink for Shrinker<$t> {
596                type Item = $t;
597
598                fn item(&self) -> Self::Item {
599                    self.item
600                }
601
602                fn shrink(&mut self) -> Option<Self> {
603                    if self.item.is_finite() {
604                        shrink!(self, $t)
605                    } else {
606                        None
607                    }
608                }
609            }
610
611            pub(crate) fn range<R: ops::RangeBounds<$t>>(range: &R) -> ($t, $t) {
612                let mut start = match range.start_bound() {
613                    Bound::Included(&bound) => (bound, false),
614                    Bound::Excluded(&bound) => (bound, true),
615                    Bound::Unbounded => ($t::MIN, false),
616                };
617                let mut end = match range.end_bound() {
618                    Bound::Included(&bound) => (bound, false),
619                    Bound::Excluded(&bound) => (bound, true),
620                    Bound::Unbounded => ($t::MAX, false),
621                };
622                assert!(start.0.is_finite());
623                assert!(end.0.is_finite());
624
625                if start.0 == end.0 {
626                    return (start.0, end.0);
627                }
628                if start.0 > end.0 {
629                    (start, end) = (end, start);
630                }
631
632                let start = if start.1 {
633                    start.0.nudge(start.0.signum())
634                } else {
635                    start.0
636                };
637                let end = if end.1 {
638                    end.0.nudge(-end.0.signum())
639                } else {
640                    end.0
641                };
642                // `Nudge` can push a value to infinity, so clamp brings it back in valid range.
643                (start.clamp($t::MIN, end), end.clamp(start, $t::MAX))
644            }
645
646            full!($t);
647            same!($t);
648            ranges!(FLOATING, $t);
649            number!($t);
650        };
651        ($($ts:ident),*) => { $(pub mod $ts { use super::*; floating!($ts); })* };
652    }
653
654    integer!(
655        u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize
656    );
657    floating!(f32, f64);
658}