mod i_min_max;
#[cfg(test)]
mod unit_tests;
use std::{
fmt::Debug,
ops::{Add, Div, Sub},
};
use arith_traits::{IMinMax, IUnaryWrappingOps, IWrappingOps};
use num_traits::{NumOps, One, Zero};
use crate::{
consts::msg,
traits::{IRange, IRangeFrom, IRangeTo, IRangeToInclusive, IRanged},
ErrInt, Error, Result,
};
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Ranged<TRange>(TRange::ValueType)
where
TRange: IRange;
impl<TRange> Ranged<TRange>
where
TRange: IRange + IRangeFrom + IRangeToInclusive,
ErrInt: From<TRange::ValueType>,
{
#[must_use]
#[allow(clippy::let_unit_value, clippy::no_effect_underscore_binding)]
pub const fn from(value: TRange::ValueType) -> Self
where
TRange: ~const IRange + ~const IRangeFrom + ~const IRangeTo, {
#[allow(clippy::match_wild_err_arm)]
match Self::try_from(value) {
Ok(instance) => instance,
Err(_err) => panic!("{}", msg::ERR_VALUE_OUT_OF_INCLUSIVE_BOUNDS),
}
}
#[allow(clippy::let_unit_value, clippy::no_effect_underscore_binding)]
pub const fn try_from(value: TRange::ValueType) -> Result<Self>
where
TRange: ~const IRange + ~const IRangeFrom + ~const IRangeTo, {
let _invariants = TRange::INVARIANTS;
match TRange::contains(&value) {
true => Ok(Self(value)),
false => Err(Error::ValueOutOfInclusiveBounds(
<TRange as IRangeFrom>::start().into(),
<TRange as IRangeTo>::end().into(),
value.into(),
)),
}
}
#[allow(unsafe_code)]
#[must_use]
pub const unsafe fn unchecked_from(value: TRange::ValueType) -> Self { Self(value) }
}
impl<TRange> const IRanged<TRange> for Ranged<TRange>
where
TRange: ~const IRange + ~const IRangeFrom + ~const IRangeTo + ~const IRangeToInclusive,
{
#[must_use]
fn end(&self) -> TRange::ValueType { TRange::end() }
#[must_use]
fn start(&self) -> TRange::ValueType { TRange::start() }
#[must_use]
fn value(&self) -> &TRange::ValueType { &self.0 }
}
impl<TRangeLhs, TRangeRhs> IWrappingOps<Ranged<TRangeRhs>> for Ranged<TRangeLhs>
where
ErrInt: From<TRangeLhs::ValueType>,
Self: PartialOrd,
TRangeLhs: Clone + IMinMax<TRangeLhs::ValueType> + IRangeFrom + IRangeToInclusive + PartialOrd,
TRangeLhs::ValueType: Debug + PartialOrd + IWrappingOps,
TRangeLhs::WidenedValueType: Add<TRangeRhs::WidenedValueType, Output = TRangeLhs::WidenedValueType>
+ Clone
+ Debug
+ Div<Output = TRangeLhs::WidenedValueType>
+ IWrappingOps<Output = <TRangeLhs as IRange>::WidenedValueType>
+ NumOps<<TRangeLhs as IRange>::WidenedValueType, <TRangeLhs as IRange>::WidenedValueType>
+ One
+ PartialOrd
+ Sub<TRangeRhs::WidenedValueType, Output = TRangeLhs::WidenedValueType>
+ Zero,
TRangeRhs: IRangeFrom + IRangeToInclusive,
{
fn wrapping_add(self, rhs: Ranged<TRangeRhs>) -> Self::Output {
let sum = TRangeLhs::WidenedValueType::from(self.0) + TRangeRhs::WidenedValueType::from(rhs.0)
- TRangeLhs::WidenedValueType::from(TRangeLhs::MIN);
Self::from(wrapping_total::<TRangeLhs>(TRangeLhs::MIN, TRangeLhs::MAX, sum))
}
fn wrapping_div(self, _rhs: Ranged<TRangeRhs>) -> Self::Output { todo!() }
fn wrapping_div_euclid(self, _rhs: Ranged<TRangeRhs>) -> Self::Output { todo!() }
fn wrapping_mul(self, _rhs: Ranged<TRangeRhs>) -> Self::Output { todo!() }
fn wrapping_rem(self, _rhs: Ranged<TRangeRhs>) -> Self::Output { todo!() }
fn wrapping_rem_euclid(self, _rhs: Ranged<TRangeRhs>) -> Self::Output { todo!() }
fn wrapping_sub(self, rhs: Ranged<TRangeRhs>) -> Self::Output {
let diff = TRangeLhs::WidenedValueType::from(self.0)
- TRangeRhs::WidenedValueType::from(rhs.0)
- TRangeLhs::WidenedValueType::from(TRangeLhs::MIN);
Self::from(wrapping_total::<TRangeLhs>(TRangeLhs::MIN, TRangeLhs::MAX, diff))
}
}
impl<TRangeLhs> IUnaryWrappingOps for Ranged<TRangeLhs>
where
Self: PartialOrd,
TRangeLhs: Clone + IMinMax<TRangeLhs::ValueType> + IRangeFrom + IRangeToInclusive,
TRangeLhs::ValueType: PartialOrd + IWrappingOps,
<TRangeLhs as IRange>::WidenedValueType: IWrappingOps<Output = <TRangeLhs as IRange>::WidenedValueType>
+ Clone
+ NumOps<<TRangeLhs as IRange>::WidenedValueType, <TRangeLhs as IRange>::WidenedValueType>
+ One
+ PartialOrd
+ Zero,
{
type Output = Self;
fn wrapping_abs(self) -> Self::Output {
let (start, end) = (
<TRangeLhs as IRange>::WidenedValueType::from(<TRangeLhs as IRangeFrom>::start()),
<TRangeLhs as IRange>::WidenedValueType::from(<TRangeLhs as IRangeTo>::end()),
);
let abs_value = {
let tmp_value = <TRangeLhs as IRange>::WidenedValueType::from(self.0);
match tmp_value >= <TRangeLhs as IRange>::WidenedValueType::zero() {
true => tmp_value,
false => <TRangeLhs as IRange>::WidenedValueType::zero() - tmp_value,
}
};
let working_value = match abs_value <= end {
true => abs_value,
false => {
#[allow(clippy::integer_arithmetic)]
let range_len = end.clone() - start.clone() + <TRangeLhs as IRange>::WidenedValueType::one();
#[allow(clippy::integer_arithmetic)]
let overflow_magnitude = abs_value - end;
let range_offset = overflow_magnitude % range_len;
start + range_offset
},
};
Self(working_value.try_into().unwrap_or_else(|_err| unreachable!()))
}
fn wrapping_neg(self) -> Self::Output { todo!() }
fn wrapping_pow(self, _rhs: u32) -> Self::Output { todo!() }
fn wrapping_shl(self, _rhs: u32) -> Self::Output { todo!() }
fn wrapping_shr(self, _rhs: u32) -> Self::Output { todo!() }
}
#[allow(clippy::inline_always)]
#[inline(always)]
fn wrapping_total<TRange>(
start: TRange::ValueType,
end: TRange::ValueType,
total: TRange::WidenedValueType,
) -> TRange::ValueType
where
TRange: IRange,
TRange::WidenedValueType: Add<TRange::WidenedValueType, Output = TRange::WidenedValueType>
+ Clone
+ IUnaryWrappingOps<Output = TRange::WidenedValueType>
+ One
+ Sub<TRange::WidenedValueType, Output = TRange::WidenedValueType>, {
let (start, end) = (TRange::WidenedValueType::from(start), TRange::WidenedValueType::from(end));
let span = end - start.clone() + <TRange as IRange>::WidenedValueType::one();
let offset = total.wrapping_rem_euclid(span);
(start + offset).try_into().unwrap_or_else(|_err| unreachable!())
}