1use core::{
2 marker::PhantomData,
3 ops::{Add, Sub},
4};
5
6use generic_array::ArrayLength;
7
8use crate::{
9 Finite, FitsInUsize,
10 typenum::{B1, Unsigned},
11};
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
17pub struct InRange<A: Unsigned, B: Unsigned>
18where
19 B: Sub<A>,
20 <B as Sub<A>>::Output: Unsigned,
21{
22 value: usize,
23 _phantom: PhantomData<(A, B)>,
24}
25
26#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
30pub struct InRangeInclusive<A: Unsigned, B: Unsigned>
31where
32 B: Sub<A>,
33 <B as Sub<A>>::Output: Add<B1>,
34 <<B as Sub<A>>::Output as Add<B1>>::Output: ArrayLength,
35{
36 value: usize,
37 _phantom: PhantomData<(A, B)>,
38}
39
40pub trait InRangeBounds: Copy + Sized {
41 type MIN: Unsigned;
43
44 type INHABITANTS: ArrayLength + FitsInUsize;
46
47 #[must_use]
54 unsafe fn new_unchecked(i: usize) -> Self;
55
56 #[must_use]
58 fn get(self) -> usize;
59
60 #[must_use]
62 fn new_from_start_offset(offset: usize) -> Option<Self> {
63 Self::new(Self::MIN::USIZE + offset)
64 }
65
66 #[must_use]
68 fn offset_from_start(i: usize) -> Option<usize> {
69 let offset = i.checked_sub(Self::MIN::USIZE)?;
70 if offset < Self::INHABITANTS::USIZE {
71 Some(offset)
72 } else {
73 None
74 }
75 }
76
77 #[must_use]
79 fn in_bounds(i: usize) -> bool {
80 Self::offset_from_start(i).is_some()
81 }
82
83 #[must_use]
85 fn new(i: usize) -> Option<Self> {
86 if Self::in_bounds(i) {
87 Some(unsafe { Self::new_unchecked(i) })
89 } else {
90 None
91 }
92 }
93}
94
95impl<A: Unsigned, B: Unsigned> InRangeBounds for InRange<A, B>
96where
97 B: Sub<A>,
98 <B as Sub<A>>::Output: ArrayLength + FitsInUsize,
99{
100 type MIN = A;
101 type INHABITANTS = <B as Sub<A>>::Output;
102
103 unsafe fn new_unchecked(i: usize) -> Self {
104 Self {
105 value: i,
106 _phantom: PhantomData,
107 }
108 }
109
110 fn get(self) -> usize {
111 self.value
112 }
113}
114
115impl<A: Unsigned, B: Unsigned> InRangeBounds for InRangeInclusive<A, B>
116where
117 B: Sub<A>,
118 <B as Sub<A>>::Output: Add<B1>,
119 <<B as Sub<A>>::Output as Add<B1>>::Output: ArrayLength + FitsInUsize,
120{
121 type MIN = A;
122 type INHABITANTS = <<B as Sub<A>>::Output as Add<B1>>::Output;
123
124 unsafe fn new_unchecked(i: usize) -> Self {
125 Self {
126 value: i,
127 _phantom: PhantomData,
128 }
129 }
130
131 fn get(self) -> usize {
132 self.value
133 }
134}
135
136impl<A: Unsigned, B: Unsigned> Finite for InRange<A, B>
137where
138 B: Sub<A>,
139 <B as Sub<A>>::Output: ArrayLength + FitsInUsize,
140{
141 type INHABITANTS = <Self as InRangeBounds>::INHABITANTS;
142
143 fn to_usize(&self) -> usize {
144 self.get() - <Self as InRangeBounds>::MIN::USIZE
145 }
146
147 fn from_usize(i: usize) -> Option<Self> {
148 Self::new_from_start_offset(i)
149 }
150}
151
152impl<A: Unsigned, B: Unsigned> Finite for InRangeInclusive<A, B>
153where
154 B: Sub<A>,
155 <B as Sub<A>>::Output: Add<B1>,
156 <<B as Sub<A>>::Output as Add<B1>>::Output: ArrayLength + FitsInUsize,
157{
158 type INHABITANTS = <Self as InRangeBounds>::INHABITANTS;
159
160 fn to_usize(&self) -> usize {
161 self.get() - <Self as InRangeBounds>::MIN::USIZE
162 }
163
164 fn from_usize(i: usize) -> Option<Self> {
165 Self::new_from_start_offset(i)
166 }
167}
168
169#[cfg(all(test, feature = "std"))]
170mod test {
171 use std::{fmt::Debug, ops::RangeBounds};
172
173 use super::*;
174 use crate::typenum::{Pow, Sub1, U, U0, U1, U3, U256};
175
176 type UsizeMax = Sub1<<U256 as Pow<U<{ std::mem::size_of::<usize>() }>>>::Output>;
177
178 fn test_range<T: InRangeBounds + Debug + PartialEq, R: RangeBounds<usize>>(expected_range: R) {
179 for i in (0..10).chain(usize::MAX - 10..=usize::MAX) {
180 let v = T::new(i);
181 if expected_range.contains(&i) {
182 assert_eq!(v.map(InRangeBounds::get), Some(i));
183 } else {
184 assert_eq!(v, None);
185 }
186 }
187 }
188
189 #[test]
190 fn test_in_range_full() {
191 test_range::<InRange<U0, UsizeMax>, _>(0..usize::MAX);
192 }
193
194 #[test]
195 fn test_in_range_inclusive_almost_full() {
196 test_range::<InRangeInclusive<U1, UsizeMax>, _>(1..=usize::MAX);
197 }
198
199 #[test]
200 fn test_in_range() {
201 test_range::<InRange<U1, U3>, _>(1..3);
202 }
203
204 #[test]
205 fn test_in_range_inclusive() {
206 test_range::<InRangeInclusive<U1, U3>, _>(1..=3);
207 }
208}