Skip to content

Commit 2e153dc

Browse files
committed
feat: add integer_bounds
1 parent fbdc29a commit 2e153dc

File tree

2 files changed

+234
-0
lines changed

2 files changed

+234
-0
lines changed

src/integer_bounds.rs

Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
use core::num::Wrapping;
2+
use core::num::{
3+
NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize, NonZeroU128,
4+
NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize,
5+
};
6+
use core::{f32, f64};
7+
use core::{i128, i16, i32, i64, i8, isize};
8+
use core::{u128, u16, u32, u64, u8, usize};
9+
10+
/// Numbers which have upper and lower bounds
11+
pub trait IntegerBounded : LowerIntegerBounded + UpperIntegerBounded {}
12+
13+
impl<T: LowerIntegerBounded + UpperIntegerBounded> IntegerBounded for T {}
14+
15+
/// Numbers which have lower bounds
16+
pub trait LowerIntegerBounded {
17+
/// Returns the smallest integer number this type can represent
18+
const MIN_INTEGER_VALUE: Self;
19+
}
20+
21+
/// Numbers which have upper bounds
22+
pub trait UpperIntegerBounded {
23+
/// Returns the largest integer number this type can represent
24+
const MAX_INTEGER_VALUE: Self;
25+
}
26+
27+
// FIXME: With a major version bump, this should be a supertrait instead
28+
const fn max_integer_value<T : IntegerBounded>() -> T {
29+
T::MAX_INTEGER_VALUE
30+
}
31+
32+
macro_rules! integer_bounded_impl {
33+
($t:ty, $min:expr, $max:expr) => {
34+
impl LowerIntegerBounded for $t {
35+
const MIN_INTEGER_VALUE: $t = $min;
36+
}
37+
impl UpperIntegerBounded for $t {
38+
const MAX_INTEGER_VALUE: $t = $max;
39+
}
40+
};
41+
}
42+
43+
integer_bounded_impl!(usize, usize::MIN, usize::MAX);
44+
integer_bounded_impl!(u8, u8::MIN, u8::MAX);
45+
integer_bounded_impl!(u16, u16::MIN, u16::MAX);
46+
integer_bounded_impl!(u32, u32::MIN, u32::MAX);
47+
integer_bounded_impl!(u64, u64::MIN, u64::MAX);
48+
integer_bounded_impl!(u128, u128::MIN, u128::MAX);
49+
50+
integer_bounded_impl!(isize, isize::MIN, isize::MAX);
51+
integer_bounded_impl!(i8, i8::MIN, i8::MAX);
52+
integer_bounded_impl!(i16, i16::MIN, i16::MAX);
53+
integer_bounded_impl!(i32, i32::MIN, i32::MAX);
54+
integer_bounded_impl!(i64, i64::MIN, i64::MAX);
55+
integer_bounded_impl!(i128, i128::MIN, i128::MAX);
56+
57+
58+
macro_rules! integer_bounded_impl_nonzero {
59+
($t:ty, $min:expr, $max:expr) => {
60+
impl LowerIntegerBounded for $t {
61+
const MIN_INTEGER_VALUE: $t = match <$t>::new($min) {
62+
Some(nz) => nz,
63+
None => panic!("bad nonzero bound!"),
64+
};
65+
}
66+
impl UpperIntegerBounded for $t {
67+
const MAX_INTEGER_VALUE: $t = match <$t>::new($max) {
68+
Some(nz) => nz,
69+
None => panic!("bad nonzero bound!"),
70+
};
71+
}
72+
};
73+
}
74+
75+
integer_bounded_impl_nonzero!(NonZeroUsize, 1, usize::MAX);
76+
integer_bounded_impl_nonzero!(NonZeroU8, 1, u8::MAX);
77+
integer_bounded_impl_nonzero!(NonZeroU16, 1, u16::MAX);
78+
integer_bounded_impl_nonzero!(NonZeroU32, 1, u32::MAX);
79+
integer_bounded_impl_nonzero!(NonZeroU64, 1, u64::MAX);
80+
integer_bounded_impl_nonzero!(NonZeroU128, 1, u128::MAX);
81+
82+
integer_bounded_impl_nonzero!(NonZeroIsize, isize::MIN, isize::MAX);
83+
integer_bounded_impl_nonzero!(NonZeroI8, i8::MIN, i8::MAX);
84+
integer_bounded_impl_nonzero!(NonZeroI16, i16::MIN, i16::MAX);
85+
integer_bounded_impl_nonzero!(NonZeroI32, i32::MIN, i32::MAX);
86+
integer_bounded_impl_nonzero!(NonZeroI64, i64::MIN, i64::MAX);
87+
integer_bounded_impl_nonzero!(NonZeroI128, i128::MIN, i128::MAX);
88+
89+
impl<T: LowerIntegerBounded> LowerIntegerBounded for Wrapping<T> {
90+
const MIN_INTEGER_VALUE: Self = Wrapping(T::MIN_INTEGER_VALUE);
91+
}
92+
impl<T: UpperIntegerBounded> UpperIntegerBounded for Wrapping<T> {
93+
const MAX_INTEGER_VALUE: Self = Wrapping(T::MAX_INTEGER_VALUE);
94+
}
95+
96+
integer_bounded_impl!(
97+
f32,
98+
-(1 << f32::MANTISSA_DIGITS) as f32,
99+
(1 << f32::MANTISSA_DIGITS) as f32
100+
);
101+
102+
macro_rules! for_each_tuple_ {
103+
( $m:ident !! ) => (
104+
$m! { }
105+
);
106+
( $m:ident !! $h:ident, $($t:ident,)* ) => (
107+
$m! { $h $($t)* }
108+
for_each_tuple_! { $m !! $($t,)* }
109+
);
110+
}
111+
macro_rules! for_each_tuple {
112+
($m:ident) => {
113+
for_each_tuple_! { $m !! A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, }
114+
};
115+
}
116+
117+
macro_rules! integer_bounded_tuple {
118+
( $($name:ident)* ) => (
119+
impl<$($name: LowerIntegerBounded,)*> LowerIntegerBounded for ($($name,)*) {
120+
const MIN_INTEGER_VALUE: Self = ($($name::MIN_INTEGER_VALUE,)*);
121+
}
122+
impl<$($name: UpperIntegerBounded,)*> UpperIntegerBounded for ($($name,)*) {
123+
const MAX_INTEGER_VALUE: Self = ($($name::MAX_INTEGER_VALUE,)*);
124+
}
125+
);
126+
}
127+
128+
for_each_tuple!(integer_bounded_tuple);
129+
integer_bounded_impl!(
130+
f64,
131+
-(1_i64 << f64::MANTISSA_DIGITS) as f64,
132+
(1_i64 << f64::MANTISSA_DIGITS) as f64
133+
);
134+
135+
#[test]
136+
fn wrapping_integer_bounded() {
137+
macro_rules! test_wrapping_integer_bounded {
138+
($($t:ty)+) => {
139+
$(
140+
assert_eq!(<Wrapping<$t> as LowerIntegerBounded>::MIN_INTEGER_VALUE.0, <$t>::MIN_INTEGER_VALUE);
141+
assert_eq!(<Wrapping<$t> as UpperIntegerBounded>::MAX_INTEGER_VALUE.0, <$t>::MAX_INTEGER_VALUE);
142+
)+
143+
};
144+
}
145+
146+
test_wrapping_integer_bounded!(usize u8 u16 u32 u64 isize i8 i16 i32 i64);
147+
}
148+
149+
#[test]
150+
fn wrapping_integer_bounded_i128() {
151+
macro_rules! test_wrapping_integer_bounded {
152+
($($t:ty)+) => {
153+
$(
154+
assert_eq!(<Wrapping<$t> as LowerIntegerBounded>::MIN_INTEGER_VALUE.0, <$t>::MIN_INTEGER_VALUE);
155+
assert_eq!(<Wrapping<$t> as UpperIntegerBounded>::MAX_INTEGER_VALUE.0, <$t>::MAX_INTEGER_VALUE);
156+
)+
157+
};
158+
}
159+
160+
test_wrapping_integer_bounded!(u128 i128);
161+
}
162+
163+
#[test]
164+
fn wrapping_is_integer_bounded() {
165+
fn require_integer_bounded<T: IntegerBounded>(_: &T) {}
166+
require_integer_bounded(&Wrapping(42_u32));
167+
require_integer_bounded(&Wrapping(-42));
168+
}
169+
170+
#[test]
171+
fn integer_bounded_unsigned_nonzero() {
172+
macro_rules! test_integer_bounded_impl_unsigned_nonzero {
173+
($t:ty, $base_ty:ty) => {
174+
assert_eq!(<$t as LowerIntegerBounded>::MIN_INTEGER_VALUE.get(), 1);
175+
assert_eq!(
176+
<$t as UpperIntegerBounded>::MAX_INTEGER_VALUE.get(),
177+
<$base_ty>::MAX
178+
);
179+
};
180+
}
181+
182+
test_integer_bounded_impl_unsigned_nonzero!(NonZeroUsize, usize);
183+
test_integer_bounded_impl_unsigned_nonzero!(NonZeroU8, u8);
184+
test_integer_bounded_impl_unsigned_nonzero!(NonZeroU16, u16);
185+
test_integer_bounded_impl_unsigned_nonzero!(NonZeroU32, u32);
186+
test_integer_bounded_impl_unsigned_nonzero!(NonZeroU64, u64);
187+
test_integer_bounded_impl_unsigned_nonzero!(NonZeroU128, u128);
188+
}
189+
190+
#[test]
191+
fn integer_bounded_signed_nonzero() {
192+
macro_rules! test_integer_bounded_impl_signed_nonzero {
193+
($t:ty, $base_ty:ty) => {
194+
assert_eq!(
195+
<$t as LowerIntegerBounded>::MIN_INTEGER_VALUE.get(),
196+
<$base_ty>::MIN
197+
);
198+
assert_eq!(
199+
<$t as UpperIntegerBounded>::MAX_INTEGER_VALUE.get(),
200+
<$base_ty>::MAX
201+
);
202+
};
203+
}
204+
205+
test_integer_bounded_impl_signed_nonzero!(NonZeroIsize, isize);
206+
test_integer_bounded_impl_signed_nonzero!(NonZeroI8, i8);
207+
test_integer_bounded_impl_signed_nonzero!(NonZeroI16, i16);
208+
test_integer_bounded_impl_signed_nonzero!(NonZeroI32, i32);
209+
test_integer_bounded_impl_signed_nonzero!(NonZeroI64, i64);
210+
test_integer_bounded_impl_signed_nonzero!(NonZeroI128, i128);
211+
}
212+
213+
#[test]
214+
fn float_last_integer_values() {
215+
// f32: integers are exact up to 2^MANTISSA_DIGITS. Adding 1 to the max should
216+
// not change because spacing is > 1 beyond this value; subtracting 1 should
217+
// change.
218+
let f32_min = <f32 as LowerIntegerBounded>::MIN_INTEGER_VALUE;
219+
let f32_max = <f32 as UpperIntegerBounded>::MAX_INTEGER_VALUE;
220+
assert_eq!(f32_max + 1.0_f32, f32_max);
221+
assert_ne!(f32_max - 1.0_f32, f32_max);
222+
assert_eq!(f32_min - 1.0_f32, f32_min);
223+
assert_ne!(f32_min + 1.0_f32, f32_min);
224+
225+
// f64 similarly: integers are exact up to 2^MANTISSA_DIGITS. Adding 1 to the
226+
// max should not change, subtracting should.
227+
let f64_min = <f64 as LowerIntegerBounded>::MIN_INTEGER_VALUE;
228+
let f64_max = <f64 as UpperIntegerBounded>::MAX_INTEGER_VALUE;
229+
assert_eq!(f64_max + 1.0_f64, f64_max);
230+
assert_ne!(f64_max - 1.0_f64, f64_max);
231+
assert_eq!(f64_min - 1.0_f64, f64_min);
232+
assert_ne!(f64_min + 1.0_f64, f64_min);
233+
}

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ pub use crate::sign::{abs, abs_sub, signum, Signed, Unsigned};
5353
mod macros;
5454

5555
pub mod bounds;
56+
pub mod integer_bounds;
5657
pub mod cast;
5758
pub mod float;
5859
pub mod identities;

0 commit comments

Comments
 (0)