use crate::op_mode::{BringInRange, Wrap};
use super::op_mode;
use crate::*;
macro_rules! gen_wrap_bringinrange {
($i:ty, $bigger:ty) => {
impl BringInRange<$i> for Wrap
{
fn bring_in_range(v: $i, min: $i, max: $i) -> $i
{
if v >= min && v <= max
{
return v;
}
let v: $i = {
let min = min as $bigger;
let max = max as $bigger;
let v = v as $bigger;
let len = max - min + 1;
(v.rem_euclid(len) + (len - (min.rem_euclid(len))))
.rem_euclid(len)
.checked_add(min)
.unwrap()
.try_into()
.unwrap()
};
debug_assert!(v >= min && v <= max);
return v;
}
}
};
}
gen_wrap_bringinrange!(u8, u16);
gen_wrap_bringinrange!(u16, u32);
gen_wrap_bringinrange!(u32, u64);
gen_wrap_bringinrange!(u64, u128);
gen_wrap_bringinrange!(i8, i16);
gen_wrap_bringinrange!(i16, i32);
gen_wrap_bringinrange!(i32, i64);
gen_wrap_bringinrange!(i64, i128);
macro_rules! gen_wrap_impl {
($i:ty, $typename:ident) => {
impl<const MIN: $i, const MAX: $i> std::ops::Add for $typename<MIN, MAX, op_mode::Wrap>
{
type Output = Self;
fn add(self, rhs: $typename<MIN, MAX, op_mode::Wrap>) -> Self::Output
{
self + rhs.inner()
}
}
impl<const MIN: $i, const MAX: $i> std::ops::AddAssign
for $typename<MIN, MAX, op_mode::Wrap>
{
fn add_assign(&mut self, rhs: $typename<MIN, MAX, op_mode::Wrap>)
{
*self += rhs.inner();
}
}
impl<const MIN: $i, const MAX: $i> std::ops::AddAssign<$i>
for $typename<MIN, MAX, op_mode::Wrap>
{
fn add_assign(&mut self, rhs: $i)
{
*self = *self + rhs;
}
}
impl<const MIN: $i, const MAX: $i> std::ops::Sub for $typename<MIN, MAX, op_mode::Wrap>
{
type Output = Self;
fn sub(self, rhs: $typename<MIN, MAX, op_mode::Wrap>) -> Self::Output
{
self - rhs.inner()
}
}
impl<const MIN: $i, const MAX: $i> std::ops::SubAssign
for $typename<MIN, MAX, op_mode::Wrap>
{
fn sub_assign(&mut self, rhs: $typename<MIN, MAX, op_mode::Wrap>)
{
*self -= rhs.inner();
}
}
impl<const MIN: $i, const MAX: $i> std::ops::SubAssign<$i>
for $typename<MIN, MAX, op_mode::Wrap>
{
fn sub_assign(&mut self, rhs: $i)
{
*self = *self - rhs;
}
}
};
}
gen_wrap_impl!(u8, RangedU8);
gen_wrap_impl!(u16, RangedU16);
gen_wrap_impl!(u32, RangedU32);
gen_wrap_impl!(u64, RangedU64);
gen_wrap_impl!(i8, RangedI8);
gen_wrap_impl!(i16, RangedI16);
gen_wrap_impl!(i32, RangedI32);
gen_wrap_impl!(i64, RangedI64);
#[cfg(test)]
mod tests
{
use std::fmt::Display;
use crate::{
op_mode::{BringInRange, Wrap},
RangedI8, RangedU8
};
fn run_test<T>(input: &[T], output: &[T], interval_min: T, interval_max: T)
where
T: Display + PartialEq + std::fmt::Debug + Copy,
Wrap: BringInRange<T>
{
for (i, o) in input.iter().zip(output)
{
println!(
"{} {}",
Wrap::bring_in_range(*i, interval_min, interval_max),
*o
);
}
for (i, o) in input.iter().zip(output)
{
assert_eq!(Wrap::bring_in_range(*i, interval_min, interval_max), *o);
}
}
#[test]
fn test_against_stdlib()
{
assert_eq!(
Wrap::bring_in_range(255 + 2, 0, 255),
255u8.wrapping_add(2).into()
);
assert_eq!(
Wrap::bring_in_range(255 + 255 + 2, 0, 255),
255u8.wrapping_add(2).wrapping_add(255).into()
);
assert_eq!(
Wrap::bring_in_range(255 + 2, 0, 255),
255u8.wrapping_add(2).into()
);
assert_eq!(
Wrap::bring_in_range(255 + 255 + 2, 0, 255),
255u8.wrapping_add(2).wrapping_add(255).into()
);
}
#[test]
fn test_1()
{
assert_eq!(Wrap::bring_in_range(6, 0, 5), 0);
assert_eq!(Wrap::bring_in_range(6, 1, 5), 1);
assert_eq!(Wrap::bring_in_range(15, 0, 5), 15 % 6);
assert_eq!(Wrap::bring_in_range(15, 0, 5), 3);
}
#[test]
fn test_2()
{
const INPUTS: [u32; 16] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
const OUTPUTS: [u32; 16] = [3, 4, 2, 3, 4, 2, 3, 4, 2, 3, 4, 2, 3, 4, 2, 3];
run_test(&INPUTS, &OUTPUTS, 2, 4);
}
#[test]
fn test_3()
{
const INTERVAL: [u32; 2] = [6, 8];
const INPUTS: [u32; 16] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
const OUTPUTS: [u32; 16] = [6, 7, 8, 6, 7, 8, 6, 7, 8, 6, 7, 8, 6, 7, 8, 6];
run_test(&INPUTS, &OUTPUTS, INTERVAL[0], INTERVAL[1]);
}
#[test]
fn test_4()
{
const INTERVAL: [i32; 2] = [11, 15];
const INPUTS: [i32; 24] = [
-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18
];
const OUTPUTS: [i32; 24] = [
15, 11, 12, 13, 14, 15, 11, 12, 13, 14, 15, 11, 12, 13, 14, 15, 11, 12, 13, 14, 15, 11,
12, 13
];
run_test(&INPUTS, &OUTPUTS, INTERVAL[0], INTERVAL[1]);
}
#[test]
fn test_type_overflow()
{
let n1: RangedU8<10, 250, Wrap> = RangedU8::new(245);
let n2: RangedU8<10, 250, Wrap> = RangedU8::new(13);
assert_eq!(n1 + n2, 17);
let n1: RangedI8<-120, 100, Wrap> = RangedI8::new(-118);
let n2: RangedI8<-120, 100, Wrap> = RangedI8::new(-13);
let n3: RangedI8<-120, 100, Wrap> = RangedI8::new(13);
assert_eq!(n1 + n2, 90);
assert_eq!(n1 - n3, 90);
let m = RangedU8::<5, 250, Wrap>::new(5);
assert_eq!(m - 1, 250);
assert_eq!(m - 10, 241);
}
#[test]
fn test_unsigned_simple()
{
assert_eq!(RangedU8::<0, 255, Wrap>::new(250) + 6, 0);
assert_eq!(RangedU8::<0, 254, Wrap>::new(250) + 6, 1);
assert_eq!(RangedU8::<10, 255, Wrap>::new(255) + 1, 10);
assert_eq!(RangedU8::<10, 255, Wrap>::new(254) + 2, 10);
assert_eq!(RangedU8::<10, 255, Wrap>::new(254) + 4, 12);
assert_eq!(RangedU8::<10, 19, Wrap>::new(10) + 255, 15);
}
#[test]
fn test_unsigned_subtraction()
{
assert_eq!(RangedU8::<0, 255, Wrap>::new(0) - 6, 250);
assert_eq!(RangedU8::<0, 254, Wrap>::new(1) - 6, 250);
assert_eq!(RangedU8::<10, 255, Wrap>::new(10) - 1, 255);
assert_eq!(RangedU8::<10, 255, Wrap>::new(10) - 2, 254);
assert_eq!(RangedU8::<10, 255, Wrap>::new(11) - 4, 253);
assert_eq!(RangedU8::<10, 19, Wrap>::new(10) - 255, 15);
}
#[test]
fn signed_add()
{
use std::ops::Add;
assert_eq!(RangedI8::<-128, 127, Wrap>::new(127) + 1, -128);
assert_eq!(RangedI8::<0, 127, Wrap>::new(127) + 1, 0);
assert_eq!(RangedI8::<0, 127, Wrap>::new(127) + 1, 0);
assert_eq!(RangedI8::<0, 127, Wrap>::new(0).add(-1), 127);
assert_eq!(RangedI8::<-10, 37, Wrap>::new(-8).add(-5), 35);
assert_eq!(RangedI8::<-35, 127, Wrap>::new(125) + 4, -34);
}
#[test]
fn signed_sub()
{
use std::ops::Sub;
assert_eq!(RangedI8::<-128, 127, Wrap>::new(-128).sub(1), 127);
assert_eq!(RangedI8::<0, 127, Wrap>::new(0).sub(1), 127);
assert_eq!(RangedI8::<-128, -10, Wrap>::new(-128).sub(1), -10);
assert_eq!(RangedI8::<-128, -10, Wrap>::new(-128).sub(-3), -125);
assert_eq!(RangedI8::<0, 127, Wrap>::new(125).sub(-5), 2);
assert_eq!(RangedI8::<-10, 37, Wrap>::new(-8).sub(5), 35);
assert_eq!(RangedI8::<-35, 127, Wrap>::new(125).sub(-4), -34);
}
}