1#![no_std]
2
3use core::marker::PhantomData;
19#[macro_use]
20extern crate typenum;
21use core::ops::{Add, Mul, Rem, Sub};
22use typenum::{op, Integer, IsLessOrEqual, Max, Min, NInt, NonZero, PInt, UInt, Unsigned, U0, U1};
23
24pub mod reexports {
27 pub use typenum::{type_operators::IsLessOrEqual, Unsigned, U0, U1};
28}
29
30#[derive(Debug)]
41pub struct Ranged<Min, Max, Underlying>(Underlying, PhantomData<Min>, PhantomData<Max>);
42
43pub type UZeroTo<Max> = Ranged<U0, Max, usize>;
53
54pub trait CanMake<V> {
59 fn make() -> V;
71}
72
73pub trait AddU<U>
87where
88 U: Unsigned,
89{
90 type Output;
91}
92
93impl<U, B, T> AddU<T> for UInt<U, B>
94where
95 T: Unsigned,
96 UInt<U, B>: Add<T>,
97{
98 type Output = <UInt<U, B> as Add<T>>::Output;
99}
100
101impl<U, T> AddU<T> for PInt<U>
102where
103 T: Unsigned + NonZero,
104 U: Unsigned + NonZero,
105 PInt<U>: Add<PInt<T>>,
106{
107 type Output = <PInt<U> as Add<PInt<T>>>::Output;
108}
109
110impl<U, T> AddU<T> for NInt<U>
111where
112 T: Unsigned + NonZero,
113 U: Unsigned + NonZero,
114 NInt<U>: Add<PInt<T>>,
115{
116 type Output = <NInt<U> as Add<PInt<T>>>::Output;
117}
118
119macro_rules! define_can_make {
120 ($thing:ident => $type:ty : $assoc:ident) => {
121 impl<T> CanMake<$type> for T
122 where
123 T: $thing,
124 {
125 fn make() -> $type {
126 T::$assoc.into()
127 }
128 }
129 };
130}
131
132define_can_make!(Integer => isize : ISIZE);
133define_can_make!(Integer => i8 : I8);
134define_can_make!(Integer => i16 : I16);
135define_can_make!(Integer => i32 : I32);
136define_can_make!(Integer => i64 : I64);
137define_can_make!(Integer => f32 : I16);
138define_can_make!(Integer => f64 : I32);
139
140define_can_make!(Unsigned => usize : USIZE);
141define_can_make!(Unsigned => u8 : U8);
142define_can_make!(Unsigned => u16 : U16);
143define_can_make!(Unsigned => u32 : U32);
144define_can_make!(Unsigned => u64 : U64);
145
146impl<Min, Max, Underlying> Ranged<Min, Max, Underlying> {
147 pub fn new<T>() -> Self
157 where
158 T: IsLessOrEqual<Max> + CanMake<Underlying>,
159 Min: IsLessOrEqual<T>,
160 {
161 Ranged(T::make(), PhantomData, PhantomData)
162 }
163
164 pub fn try_new(u: Underlying) -> Option<Self>
179 where
180 Min: CanMake<Underlying>,
181 Max: CanMake<Underlying>,
182 Underlying: PartialOrd<Underlying>,
183 {
184 if u < Min::make() {
185 None
186 } else if u > Max::make() {
187 None
188 } else {
189 Some(Ranged(u, PhantomData, PhantomData))
190 }
191 }
192
193 pub fn wrapped_add(&self, u: Underlying) -> Self
206 where
207 Min: CanMake<Underlying>,
208 Max: CanMake<Underlying> + AddU<U1>,
209 <Max as AddU<U1>>::Output: Sub<Min>,
210 <<Max as AddU<U1>>::Output as Sub<Min>>::Output: CanMake<Underlying>,
211 Underlying: Copy
212 + Add<Underlying, Output = Underlying>
213 + Sub<Underlying, Output = Underlying>
214 + Rem<Underlying, Output = Underlying>,
215 {
216 let sum = self.0 + u;
217 let range = <<Max as AddU<U1>>::Output as Sub<Min>>::Output::make();
218 Ranged(
219 ((sum - Min::make()) % range) + Min::make(),
220 PhantomData,
221 PhantomData,
222 )
223 }
224
225 pub fn offset<T>(&self) -> Ranged<op!(Min + T), op!(Max + T), Underlying>
239 where
240 Min: Add<T>,
241 Max: Add<T>,
242 T: CanMake<Underlying>,
243 Underlying: Copy + Add<Underlying, Output = Underlying>,
244 {
245 Ranged(self.value() + T::make(), PhantomData, PhantomData)
246 }
247
248 pub fn value(&self) -> Underlying
259 where
260 Underlying: Copy,
261 {
262 self.0
263 }
264
265 pub fn max_value(&self) -> Underlying
277 where
278 Max: CanMake<Underlying>,
279 {
280 Max::make()
281 }
282
283 pub fn min_value(&self) -> Underlying
295 where
296 Min: CanMake<Underlying>,
297 {
298 Min::make()
299 }
300}
301
302impl<Min1, Max1, Min2, Max2, Underlying> Add<Ranged<Min2, Max2, Underlying>>
303 for Ranged<Min1, Max1, Underlying>
304where
305 Min1: Add<Min2>,
306 Max1: Add<Max2>,
307 Underlying: Add<Underlying, Output = Underlying>,
308{
309 type Output = Ranged<op!(Min1 + Min2), op!(Max1 + Max2), Underlying>;
310
311 fn add(self, other: Ranged<Min2, Max2, Underlying>) -> Self::Output {
312 Ranged(self.0 + other.0, PhantomData, PhantomData)
313 }
314}
315
316impl<Min1, Max1, Min2, Max2, Underlying> Sub<Ranged<Min2, Max2, Underlying>>
317 for Ranged<Min1, Max1, Underlying>
318where
319 Min1: Sub<Max2>,
320 Max1: Sub<Min2>,
321 Underlying: Sub<Underlying, Output = Underlying>,
322{
323 type Output = Ranged<op!(Min1 - Max2), op!(Max1 - Min2), Underlying>;
324
325 fn sub(self, other: Ranged<Min2, Max2, Underlying>) -> Self::Output {
326 Ranged(self.0 - other.0, PhantomData, PhantomData)
327 }
328}
329
330impl<Min1, Max1, Min2, Max2, Underlying> Mul<Ranged<Min2, Max2, Underlying>>
331 for Ranged<Min1, Max1, Underlying>
332where
333 Min1: Mul<Min2> + Mul<Max2>,
334 Max1: Mul<Min2> + Mul<Max2>,
335 op!(Min1 * Min2): Min<op!(Min1 * Max2)>,
336 op!(Max1 * Min2): Min<op!(Max1 * Max2)>,
337 op!(min(Min1 * Min2, Min1 * Max2)): Min<op!(min(Max1 * Min2, Max1 * Max2))>,
338 op!(Min1 * Min2): Max<op!(Min1 * Max2)>,
339 op!(Max1 * Min2): Max<op!(Max1 * Max2)>,
340 op!(max(Min1 * Min2, Min1 * Max2)): Max<op!(max(Max1 * Min2, Max1 * Max2))>,
341 Underlying: Mul<Underlying, Output = Underlying>,
342{
343 type Output = Ranged<
344 op!(min(
345 min(Min1 * Min2, Min1 * Max2),
346 min(Max1 * Min2, Max1 * Max2)
347 )),
348 op!(max(
349 max(Min1 * Min2, Min1 * Max2),
350 max(Max1 * Min2, Max1 * Max2)
351 )),
352 Underlying,
353 >;
354
355 fn mul(self, other: Ranged<Min2, Max2, Underlying>) -> Self::Output {
356 Ranged(self.0 * other.0, PhantomData, PhantomData)
357 }
358}
359
360impl<Min1, Max1, Min2, Max2, Underlying> Rem<Ranged<Min2, Max2, Underlying>>
361 for Ranged<Min1, Max1, Underlying>
362where
363 Underlying: Rem<Underlying, Output = Underlying>,
364 Max2: Sub<U1>,
365{
366 type Output = Ranged<U0, op!(Max2 - U1), Underlying>;
367
368 fn rem(self, other: Ranged<Min2, Max2, Underlying>) -> Self::Output {
369 Ranged(self.0 % other.0, PhantomData, PhantomData)
370 }
371}
372
373#[macro_export]
390macro_rules! define_ranged_enum {
391 (@inner_count ; $sum:ty ; $fst:ident, ) => {
392 $sum
393 };
394 (@inner_count ; $sum:ty ; $fst:ident, $($rest:ident,)*) => {
395 $crate::define_ranged_enum!(@inner_count ; <$sum as ::core::ops::Add<$crate::reexports::U1>>::Output ; $($rest,)*)
396 };
397 (@inner_match_from ; $idx:expr; $sum:ty ; $($i:ty : $arm:ident)*; ) => {
398 match $idx {
399 $(<$i as $crate::reexports::Unsigned>::USIZE => Self::$arm,)*
400 _ => unreachable!()
401 }
402 };
403 (@inner_match_from ; $idx:expr; $sum:ty ; $($i:ty : $arm:ident)*; $fst:ident, $($rest:ident,)*) => {
404 $crate::define_ranged_enum!(@inner_match_from ; $idx;
405 <$sum as ::core::ops::Add<$crate::reexports::U1>>::Output ;
406 $($i : $arm)* $sum : $fst;
407 $($rest,)*)
408 };
409 (@inner_match_to ; $name:ident; $idx:expr; $sum:ty ; $($i:ty : $arm:ident)*; ) => {
410 match $idx {
411 $($name::$arm => <$i as $crate::reexports::Unsigned>::USIZE,)*
412 }
413 };
414 (@inner_match_to ; $name:ident; $idx:expr; $sum:ty ; $($i:ty : $arm:ident)*; $fst:ident, $($rest:ident,)*) => {
415 $crate::define_ranged_enum!(@inner_match_to; $name ; $idx;
416 <$sum as ::core::ops::Add<$crate::reexports::U1>>::Output ;
417 $($i : $arm)* $sum : $fst;
418 $($rest,)*)
419 };
420 ($name:ident, Derive($($derive:ident),* $(,)?), $($x:ident),+ $(,)?) => {
421 #[derive($($derive),*)]
422 pub enum $name {
423 $($x),+
424 }
425
426 impl From<$crate::Ranged<$crate::reexports::U0, define_ranged_enum!(@inner_count; $crate::reexports::U0; $($x,)+), usize>> for $name {
427 fn from(x: $crate::Ranged<$crate::reexports::U0, define_ranged_enum!(@inner_count; $crate::reexports::U0; $($x,)+), usize>)
428 -> Self {
429 $crate::define_ranged_enum!(@inner_match_from; x.value(); $crate::reexports::U0; ; $($x,)+)
430 }
431 }
432
433 impl From<$name> for $crate::Ranged<$crate::reexports::U0, define_ranged_enum!(@inner_count; $crate::reexports::U0; $($x,)+), usize> {
434 fn from(x: $name)
435 -> $crate::Ranged<$crate::reexports::U0, define_ranged_enum!(@inner_count; $crate::reexports::U0; $($x,)+), usize> {
436 $crate::Ranged::<$crate::reexports::U0, define_ranged_enum!(@inner_count; $crate::reexports::U0; $($x,)+), usize>::try_new($crate::define_ranged_enum!(@inner_match_to; $name; x; $crate::reexports::U0; ; $($x,)+)).unwrap()
437 }
438 }
439 };
440 ($name:ident, $($x:ident),+ $(,)?) => {
441 $crate::define_ranged_enum!($name, Derive(), $($x),+);
442 };
443}
444
445#[cfg(test)]
446mod tests {
447 use super::*;
448 use typenum::*;
449
450 #[test]
451 fn cant_over() {
452 let x = Ranged::<U2, U4, u8>::try_new(5);
453 assert!(x.is_none());
454 }
455
456 #[test]
457 fn add_bounds() {
458 let x = Ranged::<N2, P4, isize>::new::<P4>();
459 let y = Ranged::<N3, P4, isize>::new::<P4>();
460 let sum: Ranged<N5, P8, isize> = x + y;
461 assert_eq!(sum.value(), 8);
462 }
463
464 #[test]
465 fn sub_bounds() {
466 let x = Ranged::<N2, P4, isize>::new::<P3>();
467 let y = Ranged::<N3, P4, isize>::new::<P4>();
468 let sum: Ranged<N6, P7, isize> = x - y;
469 assert_eq!(sum.value(), -1);
470 }
471
472 #[test]
473 fn mul_bounds() {
474 let x = Ranged::<N2, P4, isize>::new::<P4>();
475 let y = Ranged::<N3, P4, isize>::new::<P4>();
476 let prod: Ranged<N12, P16, isize> = x * y;
477 assert_eq!(prod.value(), 16);
478 }
479
480 #[test]
481 fn mod_bounds() {
482 let x = Ranged::<U3, U8, usize>::new::<U4>();
483 let y = Ranged::<U2, U4, usize>::new::<U3>();
484 let rem: Ranged<U0, U3, usize> = x % y;
485 assert_eq!(rem.value(), 1);
486 }
487
488 #[test]
489 fn add_bounds_u() {
490 let x = Ranged::<U2, U4, usize>::new::<U4>();
491 let y = Ranged::<U3, U4, usize>::new::<U4>();
492 let sum: Ranged<U5, U8, usize> = x + y;
493 assert_eq!(sum.value(), 8);
494 }
495
496 #[test]
497 fn add_const() {
498 let x = Ranged::<U1, U4, usize>::new::<U4>();
499 let y: Ranged<U2, U5, usize> = x.offset::<U1>();
500 assert_eq!(y.value(), 5);
501 }
502
503 #[test]
504 fn wrapped_add_no_wrap() {
505 let x = Ranged::<N2, P4, isize>::new::<P1>();
506 let y: Ranged<N2, P4, isize> = x.wrapped_add(2);
507 assert_eq!(y.value(), 3);
508 }
509
510 #[test]
511 fn wrapped_add_overflow() {
512 let x = Ranged::<N2, P4, isize>::new::<P1>();
513 let y: Ranged<N2, P4, isize> = x.wrapped_add(4);
514 assert_eq!(y.value(), -2);
515 }
516
517 #[test]
518 fn define_ranged_enum_test() {
519 define_ranged_enum!(RangedEnumTest, Derive(Debug, PartialEq, Eq), A, B, C);
520
521 let x = RangedEnumTest::from(UZeroTo::new::<U2>());
522 assert_eq!(x, RangedEnumTest::C);
523 }
524
525 #[test]
526 fn define_ranged_enum_test_2() {
527 define_ranged_enum!(RangedEnumTest, Derive(Debug, PartialEq, Eq), A, B, C);
528
529 let x: UZeroTo<U2> = RangedEnumTest::C.into();
530 assert_eq!(x.value(), 2);
531 }
532
533 #[test]
534 fn define_ranged_enum_test_3() {
535 define_ranged_enum!(Example, Derive(Debug, PartialEq, Eq), A, B, C);
536
537 let x: UZeroTo<_> = Example::A.into();
538 assert_eq!(x.value(), 0);
539
540 let x = x.wrapped_add(5);
541
542 let y: Example = x.into();
543 assert_eq!(y, Example::C);
544 }
545
546 #[test]
547 fn test_add_u() {
548 assert_type_eq!(<P2 as AddU<U3>>::Output, P5);
549 assert_type_eq!(<N2 as AddU<U3>>::Output, P1);
550 assert_type_eq!(<U2 as AddU<U3>>::Output, U5);
551 }
552
553 #[test]
554 fn test_bounded_float() {
555 let x = Ranged::<N3, P5, f64>::try_new(4.5).unwrap();
556 let y = Ranged::<P1, P2, f64>::try_new(1.6).unwrap();
557
558 let sum = x + y;
559
560 assert_eq!(sum.max_value(), 7f64);
561 assert_eq!(sum.min_value(), -2f64);
562 assert!((sum.value() - (4.5 + 1.6)).abs() < 0.001);
563
564 assert!(Ranged::<N3, P5, f64>::try_new(-4.5).is_none());
565 }
566}