numera/number/traits/
bound.rs

1// numera::number::traits::bounds
2//
3//! The bound properties of numbers.
4//!
5//! Also implements them for all the supported primitives and external types.
6//
7// TOC
8//
9// - definitions
10//   - *Bound*
11//
12//   - Bounded
13//   - LowerBounded
14//   - UpperBounded
15//
16//   - ConstBounded
17//   - ConstLowerBounded
18//   - ConstUpperBounded
19//
20//   - NonBounded
21//   - NonLowerBounded
22//   - NonUpperBounded
23//
24// - macros
25//   - impl_bounded
26//
27// - impls
28
29use core::num::{
30    NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize, NonZeroU128,
31    NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize,
32};
33
34/* definitions */
35
36/// The bound properties of a number.
37///
38/// # Relevant traits
39/// - [`LowerBounded`], [`UpperBounded`], [`Bounded`].
40/// - [`ConstLowerBounded`], [`ConstUpperBounded`], [`ConstBounded`].
41/// - [`NonLowerBounded`], [`NonUpperBounded`], [`NonBounded`].
42pub trait Bound {
43    /// Returns true if the number is lower bounded.
44    fn is_lower_bounded(&self) -> bool;
45
46    /// Returns true if the number is upper bounded.
47    fn is_upper_bounded(&self) -> bool;
48
49    /// Returns the lower bound, if any.
50    fn lower_bound(&self) -> Option<Self>
51    where
52        Self: Sized;
53
54    /// Returns the upper bound, if any.
55    fn upper_bound(&self) -> Option<Self>
56    where
57        Self: Sized;
58}
59
60/// A number that is both lower and upper bounded. Auto-trait
61///
62/// This trait is mutually exclusive with [`NonBounded`].
63pub trait Bounded: LowerBounded + UpperBounded {}
64
65/// A number that is lower bounded.
66///
67/// This trait is mutually exclusive with [`NonLowerBounded`].
68pub trait LowerBounded: Bound {
69    /// The smallest value that can be represented with this type.
70    fn new_min() -> Self;
71}
72
73/// A number that is upper bounded.
74///
75/// This trait is mutually exclusive with [`NonUpperBounded`].
76pub trait UpperBounded: Bound {
77    /// The largest value that can be represented with this type.
78    fn new_max() -> Self;
79}
80
81/// A number that is both *const* lower and upper bounded. Auto-trait.
82///
83/// This trait is mutually exclusive with both [`NonLowerBounded`] and [`NonUpperBounded`].
84pub trait ConstBounded: ConstLowerBounded + ConstUpperBounded {}
85
86/// A number that is *const* lower bounded.
87///
88/// This trait is mutually exclusive with [`NonLowerBounded`].
89pub trait ConstLowerBounded: Bound {
90    /// The smallest value that can be represented with this type.
91    const MIN: Self;
92}
93
94/// A number that is *const* upper bounded.
95///
96/// This trait is mutually exclusive with [`NonUpperBounded`].
97pub trait ConstUpperBounded: Bound {
98    /// The largest value that can be represented with this type.
99    const MAX: Self;
100}
101
102/// A number that is *not* lower bounded.
103///
104/// This trait is mutually exclusive with [`LowerBounded`].
105pub trait NonLowerBounded: Bound {}
106
107/// A number that is *not* upper bounded.
108///
109/// This trait is mutually exclusive with [`UpperBounded`].
110pub trait NonUpperBounded: Bound {}
111
112/// A number that is both *not* lower nor upper bounded. Auto-trait.
113///
114/// This trait is mutually exclusive with both [`LowerBounded`] and [`UpperBounded`].
115pub trait NonBounded: NonLowerBounded + NonUpperBounded {}
116
117/* macros */
118
119/// implement both *const* & *non-const*, lower and/or upper `Bounded` traits.
120macro_rules! impl_bounded {
121    (many_both: $($t:ty),+) => {
122        $( impl_bounded![both: $t]; )+
123    };
124    (both: $t:ty) => {
125        impl Bound for $t {
126            fn is_lower_bounded(&self) -> bool { true }
127            fn is_upper_bounded(&self) -> bool { true }
128            fn lower_bound(&self) -> Option<Self> where Self: Sized { Some(<$t>::MIN) }
129            fn upper_bound(&self) -> Option<Self> where Self: Sized { Some(<$t>::MAX) }
130        }
131        impl ConstLowerBounded for $t { const MIN: Self = <$t>::MIN; }
132        impl LowerBounded for $t { fn new_min() -> Self { <$t>::MIN }}
133        impl ConstUpperBounded for $t { const MAX: Self = <$t>::MAX; }
134        impl UpperBounded for $t { fn new_max() -> Self { <$t>::MAX }}
135    };
136}
137
138/// implement both *const* & *non-const*, lower and/or upper `Bounded` traits
139/// for non-zero primitives.
140macro_rules! impl_bounded_nonzero {
141    (many_both: $($t:ty, $lb:expr, $ub:expr),+) => {
142        $( impl_bounded_nonzero![both: $t, $lb, $ub]; )+
143    };
144
145    (both: $t:ty, $lb:expr, $ub:expr) => {
146        impl Bound for $t {
147            fn is_lower_bounded(&self) -> bool { true }
148            fn is_upper_bounded(&self) -> bool { true }
149            fn lower_bound(&self) -> Option<Self> { Some(<$t as ConstLowerBounded>::MIN) }
150            fn upper_bound(&self) -> Option<Self> { Some(<$t as ConstUpperBounded>::MAX) }
151        }
152        impl ConstLowerBounded for $t {
153            #[cfg(feature = "safe")]
154            const MIN: Self = if let Some(n) = <$t>::new($lb)
155                { n } else { unreachable!() };
156
157            // SAFETY: constant value
158            #[cfg(not(feature = "safe"))]
159            const MIN: Self = unsafe { <$t>::new_unchecked($lb) };
160        }
161
162        impl ConstUpperBounded for $t {
163            #[cfg(feature = "safe")]
164            const MAX: Self = if let Some(n) = <$t>::new($ub)
165                { n } else { unreachable!() };
166
167            // SAFETY: constant value
168            #[cfg(not(feature = "safe"))]
169            const MAX: Self = unsafe { <$t>::new_unchecked($ub) };
170        }
171        impl LowerBounded for $t {
172            fn new_min() -> Self {
173                #[cfg(feature = "safe")]
174                return <$t>::new($lb).unwrap();
175
176                // SAFETY: constant value
177                #[cfg(not(feature = "safe"))]
178                return unsafe { <$t>::new_unchecked($lb) };
179            }
180        }
181        impl UpperBounded for $t {
182            fn new_max() -> Self {
183                #[cfg(feature = "safe")]
184                return <$t>::new($ub).unwrap();
185
186                // SAFETY: constant value
187                #[cfg(not(feature = "safe"))]
188                return unsafe { <$t>::new_unchecked($ub) };
189            }
190        }
191    };
192}
193
194/// implement just the *non-const* `LowerBounded` or `UpperBounded` traits.
195//
196// Used for `dashu_int::UBig`.
197#[rustfmt::skip]
198#[allow(unused_macros)]
199macro_rules! impl_nonconst_bounded {
200    // impl only non-const lower bound `$b`
201    (only_lower: $t:ty, $b:expr) => {
202        impl Bound for $t {
203            fn is_lower_bounded(&self) -> bool { true }
204            fn is_upper_bounded(&self) -> bool { false }
205            fn lower_bound(&self) -> Option<Self> { Some($b) }
206            fn upper_bound(&self) -> Option<Self> { None }
207        }
208        impl LowerBounded for $t { fn new_min() -> Self { $b } }
209        impl NonUpperBounded for UBig {}
210    };
211
212    // impl only non-const upper bound `$b`
213    (only_upper: $t:ty, $b:expr) => {
214        impl Bound for $t {
215            fn is_lower_bounded(&self) -> bool { false }
216            fn is_upper_bounded(&self) -> bool { true }
217            fn lower_bound(&self) -> Option<Self> { None }
218            fn upper_bound(&self) -> Option<Self> { Some($b) }
219        }
220        impl UpperBounded for $t { fn new_max() -> Self { $b } }
221        impl NonLowerBounded for UBig {}
222    };
223}
224
225/// implement `NonBounded`.
226//
227// Used for `dashu_int::IBig`.
228#[allow(unused_macros)]
229macro_rules! impl_nonbounded {
230    ($t:ty) => {
231        impl Bound for $t {
232            fn is_lower_bounded(&self) -> bool {
233                false
234            }
235            fn is_upper_bounded(&self) -> bool {
236                false
237            }
238            fn lower_bound(&self) -> Option<Self> {
239                None
240            }
241            fn upper_bound(&self) -> Option<Self> {
242                None
243            }
244        }
245        impl NonUpperBounded for $t {}
246        impl NonLowerBounded for $t {}
247    };
248}
249
250/* impls */
251
252/// auto-impl `Bounded`.
253impl<T: LowerBounded + UpperBounded> Bounded for T {}
254
255/// auto-impl `ConstBounded`.
256impl<T: ConstLowerBounded + ConstUpperBounded> ConstBounded for T {}
257
258/// auto-impl `NonBounded`.
259impl<T: NonLowerBounded + NonUpperBounded> NonBounded for T {}
260
261#[rustfmt::skip]
262impl_bounded![many_both:
263    f32, f64, i8, u8, i16, u16, i32, u32, i64, u64, i128, u128, isize, usize];
264
265#[rustfmt::skip]
266impl_bounded_nonzero![many_both:
267    NonZeroU8, 1, u8::MAX, NonZeroU16, 1, u16::MAX, NonZeroU32, 1, u32::MAX,
268    NonZeroU64, 1, u64::MAX, NonZeroU128, 1, u128::MAX, NonZeroUsize, 1, usize::MAX,
269    NonZeroI8, i8::MIN, i8::MAX, NonZeroI16, i16::MIN, i16::MAX,
270    NonZeroI32, i32::MIN, i32::MAX, NonZeroI64, i64::MIN, i64::MAX,
271    NonZeroI128, i128::MIN, i128::MAX, NonZeroIsize, isize::MIN, isize::MAX
272];
273
274/* impls external */
275
276#[cfg(feature = "twofloat")]
277impl_bounded![both: twofloat::TwoFloat];
278
279#[cfg(feature = "half")]
280impl_bounded![many_both: half::bf16, half::f16];
281
282#[cfg(feature = "dashu-int")]
283mod impl_big {
284    use super::*;
285    use dashu_int::{IBig, UBig};
286
287    impl_nonconst_bounded![only_lower: UBig, UBig::from(0u8)];
288
289    impl_nonbounded![IBig];
290}