ranged_num/
lib.rs

1#![no_std]
2
3//! ranged-num is a crate for representing a single number that is
4//! in some compile-time known range of values. You can use any underlying
5//! numeric type to hold the actual value. The main type to look at is
6//! [Ranged](struct.Ranged.html). 
7//! 
8//! Example:
9//! 
10//! ```
11//! use typenum::{N2, P3, P4};
12//! use ranged_num::Ranged;
13//!
14//! let ranged = Ranged::<N2, P4, isize>::new::<P3>();
15//! assert_eq!(ranged.value(), 3);
16//! ```
17
18use 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
24/// These are used by a macro. In order for them to be accessible to the macro
25/// I re-exported them. If you know of a better way to do this, let me know.  
26pub mod reexports {
27    pub use typenum::{type_operators::IsLessOrEqual, Unsigned, U0, U1};
28}
29
30/// Holds a single value between `Min` and `Max`. That value is stored in a single
31/// variable of type `Underlying`.
32/// 
33/// Example:
34/// ```
35/// use typenum::{N5, P5};
36/// use ranged_num::Ranged;
37///
38/// let ranged = Ranged::<N5, P5, i8>::try_new(-1).unwrap();
39/// assert_eq!(ranged.value(), -1);
40#[derive(Debug)]
41pub struct Ranged<Min, Max, Underlying>(Underlying, PhantomData<Min>, PhantomData<Max>);
42
43/// Represent a usize in `[0, Max]` (inclusive).
44/// 
45/// Example:
46/// ```
47/// use typenum::U5;
48/// use ranged_num::UZeroTo;
49/// 
50/// let x: UZeroTo<U5> = UZeroTo::try_new(4).unwrap();
51/// ```
52pub type UZeroTo<Max> = Ranged<U0, Max, usize>;
53
54/// `typenum` doesn't currently allow you to easily specify any numbers
55/// (signed or unsigned) that can be converted to a given type. I've added
56/// `CanMake<V>` as a trait to indicate which types can easily make a runtime
57/// value of type `V` to use in type bounds. 
58pub trait CanMake<V> {
59    /// Create the `V` that goes with this type. 
60    /// 
61    /// Example:
62    /// 
63    /// ```
64    /// use typenum::P5;
65    /// use ranged_num::CanMake;
66    /// 
67    /// let x: i8 = <P5 as CanMake<i8>>::make();
68    /// assert_eq!(x, 5);
69    /// ```
70    fn make() -> V;
71}
72
73/// `typenum` doesn't currently allow you to easily add an `Integer` with an `Unsigned`.
74/// The `AddU` trait lets you add some `Unsigned` to both `Integer`s and `Unsigned`s.
75/// 
76/// Example:
77/// 
78/// ```
79/// use typenum::*;
80/// use ranged_num::AddU;
81/// 
82/// assert_type_eq!(<U6 as AddU<U3>>::Output, U9);
83/// assert_type_eq!(<P6 as AddU<U3>>::Output, P9);
84/// assert_type_eq!(<N6 as AddU<U3>>::Output, N3);
85/// ```
86pub 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    /// Create a new `Ranged` holding the compile time known `T`.
148    ///
149    /// ```
150    /// use typenum::{N2, P3, P4};
151    /// use ranged_num::Ranged;
152    ///
153    /// let ranged = Ranged::<N2, P4, isize>::new::<P3>();
154    /// assert_eq!(ranged.value(), 3);
155    /// ```
156    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    /// Create a new `Randed` from `Min` to `Max` (inclusive) from the given value.
165    /// Returns Some(Ranged) if the given value is between `Min` and `Max`.
166    ///
167    /// Example:
168    /// ```
169    /// use typenum::{U2, U4};
170    /// use ranged_num::Ranged;
171    ///
172    /// let ranged = Ranged::<U2, U4, usize>::try_new(3).unwrap();
173    /// assert_eq!(ranged.value(), 3);
174    ///
175    /// let x = Ranged::<U2, U4, usize>::try_new(1);
176    /// assert!(x.is_none());
177    /// ```
178    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    /// Add `u` to the value held by this `Ranged`. If the sum wraps past
194    /// `Max` start again from `Min`.
195    ///
196    /// Example:
197    /// ```
198    /// use typenum::{N2, P2, P4};
199    /// use ranged_num::Ranged;
200    ///
201    /// let x = Ranged::<N2, P4, isize>::new::<P2>();
202    /// let y: Ranged<N2, P4, isize> = x.wrapped_add(4);
203    /// assert_eq!(y.value(), -1);
204    /// ```
205    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    /// Offset the `Ranged` by `T`.
226    ///
227    /// Example:
228    /// ```
229    /// use typenum::*;
230    /// use ranged_num::Ranged;
231    ///
232    /// let x = Ranged::<N2, P4, isize>::new::<P2>();
233    /// assert_eq!(x.value(), 2);
234    ///
235    /// let x: Ranged<N3, P3, isize> = x.offset::<N1>();
236    /// assert_eq!(x.value(), 1);
237    /// ```
238    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    /// Retrieve the value of this Ranged. It will be between Min and Max (inclusive).
249    ///
250    /// Example:
251    /// ```
252    /// use typenum::{N2, P3, P4};
253    /// use ranged_num::Ranged;
254    ///
255    /// let ranged = Ranged::<N2, P4, isize>::new::<P3>();
256    /// assert_eq!(ranged.value(), 3);
257    /// ```
258    pub fn value(&self) -> Underlying
259    where
260        Underlying: Copy,
261    {
262        self.0
263    }
264
265    /// Calculate the maximum value that this Ranged could hold. That is
266    /// get the runtime value of Max.
267    ///
268    /// Example:
269    /// ```
270    /// use typenum::{N2, P3, P4};
271    /// use ranged_num::Ranged;
272    ///
273    /// let ranged = Ranged::<N2, P4, isize>::new::<P3>();
274    /// assert_eq!(ranged.max_value(), 4);
275    /// ```
276    pub fn max_value(&self) -> Underlying
277    where
278        Max: CanMake<Underlying>,
279    {
280        Max::make()
281    }
282
283    /// Calculate the minimum value that this Ranged could hold. That is
284    /// get the runtime value of Min.
285    ///
286    /// Example:
287    /// ```
288    /// use typenum::{N2, P3, P4};
289    /// use ranged_num::Ranged;
290    ///
291    /// let ranged = Ranged::<N2, P4, isize>::new::<P3>();
292    /// assert_eq!(ranged.min_value(), -2);
293    /// ```
294    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/// Define a new enum which can be convered to and from a Ranged usize.
374///
375/// Example:
376/// ```
377/// use ranged_num::{define_ranged_enum, UZeroTo};
378///
379/// define_ranged_enum!(Example, Derive(Debug, PartialEq, Eq), A, B, C);
380///
381/// let x: UZeroTo<_> = Example::A.into();
382/// assert_eq!(x.value(), 0);
383///
384/// let x = x.wrapped_add(5);
385///
386/// let y: Example = x.into();
387/// assert_eq!(y, Example::C);
388/// ```
389#[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}