light_ranged_integers 0.1.3

Ranged integers for stable Rust compiler, zero-dependencies and no unsafe code.
Documentation
/*
Copyright © 2024 - Massimo Gismondi

This file is part of light-ranged-integers.

light-ranged-integers is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

light-ranged-integers is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with light-ranged-integers.  If not, see <http://www.gnu.org/licenses/>.
*/

use crate::op_mode::{Clamp, Panic, Wrap};
use crate::*;
use paste::paste;

macro_rules! test_generator {
    ($i:ty) => {
        paste! {
            /// Verify Ranged type size is the same as the pure int type
            #[test]
            fn [<detect_size_ $i>]()
            {
                assert_eq!(
                    std::mem::size_of::<[<Ranged $i:upper>]>(),
                    std::mem::size_of::<$i>()
                )
            }

            /// Check Clamping works
            #[test]
            fn [<check_clamping_ $i>]()
            {
                assert!(
                    [<Ranged $i:upper>]::<{$i::MIN}, {$i::MAX}, Clamp>::new($i::MAX)
                        + [<Ranged $i:upper>]::new(1)
                    ==
                        $i::MAX
                );
                assert!(
                    [<Ranged $i:upper>]::<{$i::MIN}, {$i::MAX}, Clamp>::new($i::MIN)
                        - [<Ranged $i:upper>]::new(1)
                    ==
                        $i::MIN
                );
                assert!(
                    [<Ranged $i:upper>]::<{$i::MIN}, {$i::MAX}, Clamp>::new($i::MAX/2)
                        + [<Ranged $i:upper>]::new($i::MAX)
                    ==
                        $i::MAX
                );
                assert_eq!(
                    [<Ranged $i:upper>]::<{$i::MIN}, {$i::MAX}, Clamp>::new($i::MAX/2)
                        - [<Ranged $i:upper>]::new($i::MAX) - [<Ranged $i:upper>]::new($i::MAX)
                    ,
                        $i::MIN
                );

                // Smaller ranges
                assert!(
                    [<Ranged $i:upper>]::<{$i::MIN/2}, {$i::MAX/2}, Clamp>::new_adjust($i::MAX)
                        + [<Ranged $i:upper>]::new_adjust($i::MAX)
                    ==
                        $i::MAX/2
                );

                assert!(
                    [<Ranged $i:upper>]::<{$i::MIN/2}, {$i::MAX/2}, Clamp>::new_adjust($i::MIN)
                        - [<Ranged $i:upper>]::new_adjust($i::MAX)
                    ==
                        $i::MIN/2
                );
                // Small interval
                assert!(
                    [<Ranged $i:upper>]::<0, 1, Clamp>::new_adjust($i::MAX)
                        + [<Ranged $i:upper>]::new_adjust($i::MAX)
                    ==
                        1
                );
                assert!(
                    [<Ranged $i:upper>]::<0,1, Clamp>::new_adjust($i::MIN)
                        - [<Ranged $i:upper>]::new_adjust($i::MAX)
                    ==
                        0
                );
            }
        }
    };
}

test_generator!(u8);
test_generator!(i8);
test_generator!(u16);
test_generator!(i16);
test_generator!(u32);
test_generator!(i32);
test_generator!(u64);
test_generator!(i64);

#[test]
fn from_trait()
{
    let _: RangedI16<0, 15> = 5.into();
}

#[test]
fn sum()
{
    assert_eq!(RangedU16::<2, 5, Clamp>::new_adjust(0).inner(), 2);
    assert_eq!(RangedU16::<2, 5, Clamp>::new_adjust(500).inner(), 5);

    assert_eq!(RangedU16::<2, 500, Clamp>::new(500) + 5u16, 500)
}

#[test]
fn const_rangeds()
{
    let _: RangedU8<0, 10, Wrap> = RangedU8::new_const::<5>();
}

#[test]
fn result_range()
{
    let n1 = RangedU16::<1, 6, Clamp>::new(3);
    assert_eq!(n1, 3);
    let n2 = RangedU16::<1, 6, Clamp>::new_adjust(10);
    assert_eq!(n2, 6);

    assert_eq!(n1 + n2, 6);

    let mut n3 = RangedU16::<1, 6, Wrap>::new(3);
    n3 += 1;
}

#[test]
fn partial_ord_eq()
{
    let n1 = RangedU16::<1, 6, Clamp>::new(1);
    let n2 = RangedU16::<1, 6, Clamp>::new(1);
    let n3 = RangedU16::<1, 6, Clamp>::new(6);
    assert!(n1 == n1);
    assert!(n1 == n2);
    assert!(n3 > n2);
}

#[test]
fn limit_expand()
{
    let n1 = RangedU16::<1, 6, Clamp>::new(3);
    assert_eq!(n1, 3);
    let n2 = RangedU16::<1, 6, Clamp>::new_adjust(10);
    assert_eq!(n2, 6);

    assert_eq!(n1.limit::<1, 15>() + n2.limit::<1, 15>(), 9);
}

#[test]
#[should_panic]
fn outside_range_low()
{
    RangedU16::<1, 6, Panic>::new(0);
}

#[test]
#[should_panic]
fn outside_range_high()
{
    RangedU16::<1, 6, Panic>::new(7);
}

// Testing wrapping against deranged tests
// either both are broken, or both are correct ;)
macro_rules! test_deranged {
    ([ $($i:ty),+ ]) => {
$(
paste!{
    #[test]
    fn [<test_deranged_ $i>]()
    {
        use std::ops::Add;
        assert_eq!([<Ranged $i:upper>]::<5, 10, Wrap>::new(10).add(0), 10);
        assert_eq!([<Ranged $i:upper>]::<5, 10, Wrap>::new(10).add(1), 5);
        assert_eq!([<Ranged $i:upper>]::<{$i::MIN}, {$i::MAX}, Wrap>::new($i::MAX).add(1), $i::MIN);
        for i in 1..127
        {
            assert_eq!([<Ranged $i:upper>]::<{$i::MIN}, {$i::MAX}, Wrap>::new($i::MAX).add(i), $i::MIN+i-1);
        }
    }
}
)+
}}

test_deranged!([u8, i8, u16, i16, u32, i32, u64, i64]);

macro_rules! create_range_test_u8 {
        ($min:expr, $max:expr, $i:ty) => {
            paste!{
            #[test]
            fn [<test_range_ $min _ $max _ $i>]()
            {
                use std::ops::{Add, Sub};
                for base in $min..=$max
                {
                    for rhs in  <$i>::MIN..=<$i>::MAX
                    {
                        assert_eq!(
                            [<Ranged $i:upper>]::<$min, $max, Wrap>::new(base).add(rhs).inner(),
                            deranged::[<Ranged $i:upper>]::<$min, $max>::new(base).unwrap().wrapping_add(rhs).get()
                        );
                        assert_eq!(
                            [<Ranged $i:upper>]::<$min, $max, Wrap>::new(base).sub(rhs).inner(),
                            deranged::[<Ranged $i:upper>]::<$min, $max>::new(base).unwrap().wrapping_sub(rhs).get()
                        )
                    }
                }
            }
        }
        };
    }
create_range_test_u8!(48, 50, i8);
create_range_test_u8!(10, 20, i8);
create_range_test_u8!(2, 255, u8);
create_range_test_u8!(8, 223, u8);

#[test]
fn test_derange_signed_i8()
{
    use std::ops::{Add, Sub};
    for base in -5..=126
    {
        for rhs in i8::MIN..=i8::MAX
        {
            assert_eq!(
                crate::RangedI8::<-5, 126, Wrap>::new(base).add(rhs).inner(),
                deranged::RangedI8::<-5, 126>::new(base)
                    .unwrap()
                    .wrapping_add(rhs)
                    .get()
            );
            assert_eq!(
                crate::RangedI8::<-5, 126, Wrap>::new(base).sub(rhs).inner(),
                deranged::RangedI8::<-5, 126>::new(base)
                    .unwrap()
                    .wrapping_sub(rhs)
                    .get()
            )
        }
    }
}