use crate::{Assert, IsAllowed, OperationPossibility, Ranged, allow_range, irang, memlayout, value_check::allow_if};
impl<const MIN: irang, const MAX: irang> core::ops::Neg
for Ranged<MIN, MAX>
where
Assert<{allow_range(memlayout(MIN, MAX))}>: IsAllowed,
Assert<{allow_range(memlayout(-MAX, -MIN))}>: IsAllowed,
{
type Output = Ranged<{ -MAX }, { -MIN }>;
fn neg(self) -> Self::Output {Self::neg(self)}
}
impl<const AMIN: irang, const AMAX: irang, const BMIN: irang, const BMAX: irang> core::ops::Add<Ranged<BMIN, BMAX>>
for Ranged<AMIN, AMAX>
where
Assert<{allow_range(memlayout(AMIN, AMAX))}>: IsAllowed,
Assert<{allow_range(memlayout(BMIN, BMAX))}>: IsAllowed,
Assert<{allow_range(memlayout(AMIN + BMIN, AMAX + BMAX))}>: IsAllowed,
{
type Output = Ranged<{ AMIN + BMIN }, { AMAX + BMAX }>;
fn add(self, rhs: Ranged<BMIN, BMAX>) -> Self::Output {
Self::add(self, rhs)
}
}
impl<const AMIN: irang, const AMAX: irang, const BMIN: irang, const BMAX: irang>
core::ops::Sub<Ranged<BMIN, BMAX>> for Ranged<AMIN, AMAX>
where
Assert<{allow_range(memlayout(AMIN, AMAX))}>: IsAllowed,
Assert<{allow_range(memlayout(BMIN, BMAX))}>: IsAllowed,
Assert<{allow_range(memlayout(AMIN - BMAX, AMAX - BMIN))}>: IsAllowed,
{
type Output = Ranged<{ AMIN - BMAX }, { AMAX - BMIN }>;
fn sub(self, rhs: Ranged<BMIN, BMAX>) -> Self::Output {Self::sub(self, rhs) }
}
macro_rules! reduce {
($fn:ident, $a:expr) => ( $a );
($fn:ident, $a:expr, $($args:expr),+) => {
{
$fn($a, reduce!($fn, $($args),+ ))
}
};
}
#[doc(hidden)] #[must_use]
pub const fn min_irang(x: irang, y: irang) -> irang {
if x < y {x} else {y}
}
#[doc(hidden)] #[must_use]
pub const fn max_irang(x: irang, y: irang) -> irang {
if x > y {x} else {y}
}
const fn max_4(vals: (irang, irang, irang, irang)) -> irang {
reduce!(max_irang, vals.0, vals.1, vals.2, vals.3)
}
const fn min_4(vals: (irang, irang, irang, irang)) -> irang {
reduce!(min_irang, vals.0, vals.1, vals.2, vals.3)
}
#[must_use] #[doc(hidden)]
pub const fn max_cross(a_min: irang, a_max: irang, b_min: irang, b_max: irang) -> irang {
max_4((a_min * b_min, a_min * b_max, a_max * b_min, a_max * b_max))
}
#[must_use] #[doc(hidden)]
pub const fn min_cross(a_min: irang, a_max: irang, b_min: irang, b_max: irang) -> irang {
min_4((a_min * b_min, a_min * b_max, a_max * b_min, a_max * b_max))
}
impl<const AMIN: irang, const AMAX: irang, const BMIN: irang, const BMAX: irang>
core::ops::Mul<Ranged<BMIN, BMAX>> for Ranged<AMIN, AMAX>
where
Assert<{allow_range(memlayout(AMIN, AMAX))}>: IsAllowed,
Assert<{allow_range(memlayout(BMIN, BMAX))}>: IsAllowed,
Assert<{allow_range(memlayout(min_cross(AMIN, AMAX, BMIN, BMAX), max_cross(AMIN, AMAX, BMIN, BMAX)))}>: IsAllowed,
{
type Output =
Ranged<{ min_cross(AMIN, AMAX, BMIN, BMAX) }, { max_cross(AMIN, AMAX, BMIN, BMAX) }>;
fn mul(self, rhs: Ranged<BMIN, BMAX>) -> Self::Output {Self::mul(self, rhs)}
}
#[must_use]
#[doc(hidden)]
pub const fn allow_division(b_min: irang, b_max: irang) -> OperationPossibility {
if ((b_min > 0) && (b_max > 0)) || ((b_min < 0) && (b_max < 0)) {
OperationPossibility::Allowed
} else {
OperationPossibility::Forbidden
}
}
#[must_use]
#[doc(hidden)]
pub const fn singleside_div_min(a_min: irang, a_max: irang, b_min: irang, b_max: irang) -> irang {
min_4((a_min / b_min, a_min / b_max, a_max / b_min, a_max / b_max))
}
#[must_use]
#[doc(hidden)]
pub const fn singleside_div_max(a_min: irang, a_max: irang, b_min: irang, b_max: irang) -> irang {
max_4((a_min / b_min, a_min / b_max, a_max / b_min, a_max / b_max))
}
impl<const AMIN: irang, const AMAX: irang, const BMIN: irang, const BMAX: irang>
core::ops::Div<Ranged<BMIN, BMAX>> for Ranged<AMIN, AMAX>
where
Assert<{allow_range(memlayout(AMIN, AMAX))}>: IsAllowed, Assert<{allow_range(memlayout(BMIN, BMAX))}>: IsAllowed, Assert<{allow_range(memlayout(singleside_div_min(AMIN, AMAX, BMIN, BMAX), singleside_div_max(AMIN, AMAX, BMIN, BMAX)))}>: IsAllowed,
Assert<{ allow_division(BMIN, BMAX) }>: IsAllowed,
{
type Output = Ranged<{singleside_div_min(AMIN, AMAX, BMIN, BMAX) }, {singleside_div_max(AMIN, AMAX, BMIN, BMAX) }>;
fn div(self, rhs: Ranged<BMIN, BMAX>) -> Self::Output { Self::div(self, rhs) }
}
#[must_use]
#[doc(hidden)]
pub const fn singleside_rem_min(a_min: irang, a_max: irang, b_min: irang, b_max: irang) -> irang {
if b_min == b_max { if a_min == a_max {return a_min % b_min} else if a_min > 0 {
let base = a_max / b_min.abs() * b_min.abs();
if a_min >= base {
return a_min % b_min;
}
}
else if a_max < 0 {
let base = a_min / b_min.abs() * b_min.abs();
if a_max <= base {
return a_min % b_min;
}
}
}
if a_min >= 0 {
0 }
else {
max_irang(1 - max_irang(b_max.abs(), b_min.abs()), a_min)
}
}
#[must_use]
#[doc(hidden)]
pub const fn singleside_rem_max(a_min: irang, a_max: irang, b_min: irang, b_max: irang) -> irang {
if b_min == b_max {
if a_min == a_max {return a_min % b_min}
else if a_min > 0 {
let base = a_max / b_min.abs() * b_min.abs();
if a_min >= base {
return a_max % b_min;
}
}
else if a_max < 0 {
let base = a_min / b_min.abs() * b_min.abs();
if a_max <= base {
return a_max % b_min;
}
}
}
if a_max <= 0 {0}
else {
min_irang(max_irang(b_max.abs(), b_min.abs()) - 1, a_max)
}
}
impl<const AMIN: irang, const AMAX: irang, const BMIN: irang, const BMAX: irang>
core::ops::Rem<Ranged<BMIN, BMAX>> for Ranged<AMIN, AMAX>
where
Assert<{allow_range(memlayout(AMIN, AMAX))}>: IsAllowed,
Assert<{allow_range(memlayout(BMIN, BMAX))}>: IsAllowed,
Assert<{allow_range(memlayout(singleside_rem_min(AMIN, AMAX, BMIN, BMAX), singleside_rem_max(AMIN, AMAX, BMIN, BMAX)))}>: IsAllowed,
Assert<{ allow_division(BMIN, BMAX) }>: IsAllowed,
Assert<{ allow_if(true) }>: IsAllowed,
{
type Output = Ranged<
{ singleside_rem_min(AMIN, AMAX, BMIN, BMAX) },
{ singleside_rem_max(AMIN, AMAX, BMIN, BMAX) },
>;
fn rem(self, rhs: Ranged<BMIN, BMAX>) -> Self::Output { Self::rem(self, rhs) }
}
#[must_use]
#[doc(hidden)]
pub const fn singleside_div_euclid_min(a_min: irang, a_max: irang, b_min: irang, b_max: irang) -> irang {
min_4((a_min.div_euclid(b_min), a_min.div_euclid(b_max), a_max.div_euclid(b_min), a_max.div_euclid(b_max)))
}
#[must_use]
#[doc(hidden)]
pub const fn singleside_div_euclid_max(a_min: irang, a_max: irang, b_min: irang, b_max: irang) -> irang {
max_4((a_min.div_euclid(b_min), a_min.div_euclid(b_max), a_max.div_euclid(b_min), a_max.div_euclid(b_max)))
}
#[must_use]
#[doc(hidden)]
pub const fn singleside_rem_euclid_min(a_min: irang, a_max: irang, b_min: irang, b_max: irang) -> irang {
if b_min == b_max {
if a_min == a_max {return a_min.rem_euclid(b_min)}
let base_min = a_min.div_euclid(b_min);
let base_max = a_max.div_euclid(b_min);
if base_min==base_max {
return a_min.rem_euclid(b_min);
}
}
0
}
#[must_use]
#[doc(hidden)]
pub const fn singleside_rem_euclid_max(a_min: irang, a_max: irang, b_min: irang, b_max: irang) -> irang {
if b_min == b_max {
if a_min == a_max {return a_min.rem_euclid(b_min)}
let base_min = a_min.div_euclid(b_min);
let base_max = a_max.div_euclid(b_min);
if base_min==base_max {
return a_max.rem_euclid(b_min);
}
}
let absb_max = max_irang(b_min.abs(), b_max.abs());
if a_min > 0 && a_max < absb_max {a_max}
else {absb_max-1}
}
#[must_use] #[doc(hidden)]
pub const fn abs_min(min: irang, max: irang) -> irang {
if min.signum() == max.signum() {min_irang(min.abs(), max.abs())}
else {0}
}
#[must_use] #[doc(hidden)]
pub const fn abs_max(min: irang, max: irang) -> irang {
max_irang(min.abs(), max.abs())
}
impl<const MIN: irang, const MAX: irang> Ranged<MIN, MAX>
where Assert<{allow_range(memlayout(MIN, MAX))}>: IsAllowed,
{
#[must_use]
pub const fn add<const BMIN: irang, const BMAX: irang>
(self, rhs: Ranged<BMIN, BMAX>)
-> Ranged<{ MIN + BMIN }, { MAX + BMAX }>
where
Assert<{allow_range(memlayout(BMIN, BMAX))}>: IsAllowed,
Assert<{allow_range(memlayout(MIN + BMIN, MAX + BMAX))}>: IsAllowed,
{
unsafe { Ranged::unchecked_new(self.get() + rhs.get()) }
}
#[must_use]
pub const fn sub<const BMIN: irang, const BMAX: irang>(self, rhs: Ranged<BMIN, BMAX>)
-> Ranged<{ MIN - BMAX }, { MAX - BMIN }>
where
Assert<{allow_range(memlayout(BMIN, BMAX))}>: IsAllowed,
Assert<{allow_range(memlayout(MIN - BMAX, MAX - BMIN))}>: IsAllowed,
{
unsafe { Ranged::unchecked_new(self.get() - rhs.get()) }
}
#[must_use]
pub const fn mul<const BMIN: irang, const BMAX: irang>(self, rhs: Ranged<BMIN, BMAX>)
-> Ranged<{ min_cross(MIN, MAX, BMIN, BMAX) }, { max_cross(MIN, MAX, BMIN, BMAX) }>
where
Assert<{allow_range(memlayout(BMIN, BMAX))}>: IsAllowed,
Assert<{allow_range(memlayout(min_cross(MIN, MAX, BMIN, BMAX), max_cross(MIN, MAX, BMIN, BMAX)))}>: IsAllowed,
{
unsafe { Ranged::unchecked_new(self.get() * rhs.get()) }
}
#[must_use]
pub const fn div<const BMIN: irang, const BMAX: irang>(self, rhs: Ranged<BMIN, BMAX>)
-> Ranged<{ singleside_div_min(MIN, MAX, BMIN, BMAX) }, { singleside_div_max(MIN, MAX, BMIN, BMAX) }>
where
Assert<{allow_range(memlayout(BMIN, BMAX))}>: IsAllowed,
Assert<{allow_range(memlayout(singleside_div_min(MIN, MAX, BMIN, BMAX), singleside_div_max(MIN, MAX, BMIN, BMAX)))}>: IsAllowed,
Assert<{ allow_division(BMIN, BMAX) }>: IsAllowed,
{
unsafe { Ranged::unchecked_new(self.get() / rhs.get()) }
}
#[must_use]
pub const fn rem<const BMIN: irang, const BMAX: irang>(self, rhs: Ranged<BMIN, BMAX>)
-> Ranged<{ singleside_rem_min(MIN, MAX, BMIN, BMAX) }, { singleside_rem_max(MIN, MAX, BMIN, BMAX) }>
where
Assert<{allow_range(memlayout(BMIN, BMAX))}>: IsAllowed,
Assert<{allow_range(memlayout(singleside_rem_min(MIN, MAX, BMIN, BMAX), singleside_rem_max(MIN, MAX, BMIN, BMAX)))}>: IsAllowed,
Assert<{ allow_division(BMIN, BMAX) }>: IsAllowed,
{
unsafe { Ranged::unchecked_new(self.get() % rhs.get()) }
}
#[must_use]
pub const fn min<const BMIN: irang, const BMAX: irang>(self, other: Ranged<BMIN,BMAX>)
-> Ranged< {min_irang(MIN, BMIN)}, {min_irang(MAX, BMAX)} >
where
Assert<{allow_range(memlayout(BMIN, BMAX))}>: IsAllowed,
Assert<{allow_range(memlayout(min_irang(MIN, BMIN), min_irang(MAX, BMAX)))}>: IsAllowed,
{
unsafe { Ranged::unchecked_new(min_irang(self.get(), other.get() )) }
}
#[must_use]
pub const fn max<const BMIN: irang, const BMAX: irang>(self, other: Ranged<BMIN,BMAX>)
-> Ranged< {max_irang(MIN, BMIN)}, {max_irang(MAX, BMAX)} >
where
Assert<{allow_range(memlayout(BMIN, BMAX))}>: IsAllowed,
Assert<{allow_range(memlayout(max_irang(MIN, BMIN), max_irang(MAX, BMAX)))}>: IsAllowed,
{
unsafe { Ranged::unchecked_new(max_irang(self.get(), other.get() )) }
}
#[must_use]
pub const fn abs(self) -> Ranged< {abs_min(MIN, MAX)}, {abs_max(MIN, MAX)} >
where Assert<{allow_range(memlayout(abs_min(MIN, MAX), abs_max(MIN, MAX)))}>: IsAllowed,
{
unsafe { Ranged::unchecked_new(self.get().abs()) }
}
#[must_use]
pub const fn neg(self) -> Ranged<{ -MAX }, { -MIN }>
where
Assert<{allow_range(memlayout(-MAX, -MIN))}>: IsAllowed ,
{
unsafe { Ranged::unchecked_new(-self.get()) }
}
#[must_use]
pub const fn div_euclid<const BMIN: irang, const BMAX: irang>(self, rhs: Ranged<BMIN,BMAX>)
-> Ranged< {singleside_div_euclid_min(MIN, MAX, BMIN, BMAX)}, {singleside_div_euclid_max(MIN, MAX, BMIN, BMAX)} >
where
Assert<{allow_range(memlayout(BMIN, BMAX))}>: IsAllowed,
Assert<{allow_range(memlayout(singleside_div_euclid_min(MIN, MAX, BMIN, BMAX), singleside_div_euclid_max(MIN, MAX, BMIN, BMAX)))}>: IsAllowed,
Assert<{ allow_division(BMIN, BMAX) }>: IsAllowed,
Assert<{ allow_if(true) }>: IsAllowed,
{
unsafe { Ranged::unchecked_new(self.get().div_euclid(rhs.get())) }
}
#[must_use]
pub const fn rem_euclid<const BMIN: irang, const BMAX: irang>(self, rhs: Ranged<BMIN,BMAX>)
-> Ranged< {singleside_rem_euclid_min(MIN, MAX, BMIN, BMAX)}, {singleside_rem_euclid_max(MIN, MAX, BMIN, BMAX)} >
where
Assert<{allow_range(memlayout(BMIN, BMAX))}>: IsAllowed,
Assert<{allow_range(memlayout(singleside_rem_euclid_min(MIN, MAX, BMIN, BMAX), singleside_rem_euclid_max(MIN, MAX, BMIN, BMAX)))}>: IsAllowed,
Assert<{allow_division(BMIN, BMAX)}>: IsAllowed,
Assert<{allow_if(true)}>: IsAllowed,
{
unsafe { Ranged::unchecked_new(self.get().rem_euclid(rhs.get())) }
}
#[must_use]
pub const fn eq<const BMIN: irang, const BMAX: irang>(&self, rhs: &Ranged<BMIN, BMAX>) -> bool
where Assert<{allow_range(memlayout(BMIN, BMAX))}>: IsAllowed
{
self.get() == rhs.get()
}
#[must_use]
pub const fn ne<const BMIN: irang, const BMAX: irang>(&self, other: &Ranged<BMIN, BMAX>) -> bool
where Assert<{allow_range(memlayout(BMIN, BMAX))}>: IsAllowed
{
!self.eq(other)
}
}
#[allow(clippy::use_self)] impl<const MIN: irang, const MAX: irang>
core::cmp::PartialEq<Ranged<MIN, MAX>> for irang
where
Assert<{allow_range(memlayout(MIN, MAX))}>: IsAllowed ,
{
fn eq(&self, other: &Ranged<MIN, MAX>) -> bool {
*self == other.get()
}
#[allow(clippy::partialeq_ne_impl)] fn ne(&self, other: &Ranged<MIN, MAX>) -> bool {
!self.eq(other)
}
}
impl<const MIN: irang, const MAX: irang>
core::cmp::PartialEq<irang> for Ranged<MIN, MAX>
where
Assert<{allow_range(memlayout(MIN, MAX))}>: IsAllowed ,
{
fn eq(&self, other: &irang) -> bool {
self.get() == *other
}
}
impl<const AMIN: irang, const AMAX: irang, const BMIN: irang, const BMAX: irang>
core::cmp::PartialEq<Ranged<BMIN, BMAX>> for Ranged<AMIN, AMAX>
where
Assert<{allow_range(memlayout(AMIN, AMAX))}>: IsAllowed ,
Assert<{allow_range(memlayout(BMIN, BMAX))}>: IsAllowed ,
{
fn eq(&self, rhs: &Ranged<BMIN, BMAX>) -> bool {
Self::eq(self, rhs)
}
}
impl<const AMIN: irang, const AMAX: irang> core::cmp::Eq for Ranged<AMIN, AMAX>
where Assert<{allow_range(memlayout(AMIN, AMAX))}>: IsAllowed ,
{}
impl<const AMIN: irang, const AMAX: irang, const BMIN: irang, const BMAX: irang>
core::cmp::PartialOrd<Ranged<BMIN, BMAX>> for Ranged<AMIN, AMAX>
where
Assert<{allow_range(memlayout(AMIN, AMAX))}>: IsAllowed ,
Assert<{allow_range(memlayout(BMIN, BMAX))}>: IsAllowed ,
{
fn partial_cmp(&self, other: &Ranged<BMIN, BMAX>) -> Option<core::cmp::Ordering> {
let s = self.get();
let o = other.get();
#[allow(clippy::comparison_chain)]
Some(if s>o {core::cmp::Ordering::Greater}
else if s==o {core::cmp::Ordering::Equal}
else {core::cmp::Ordering::Less})
}
fn lt(&self, other: &Ranged<BMIN, BMAX>) -> bool {
self.get() < other.get()
}
fn le(&self, other: &Ranged<BMIN, BMAX>) -> bool {
self.get() <= other.get()
}
fn gt(&self, other: &Ranged<BMIN, BMAX>) -> bool {
self.get() > other.get()
}
fn ge(&self, other: &Ranged<BMIN, BMAX>) -> bool {
self.get() >= other.get()
}
}
impl<const AMIN: irang, const AMAX: irang> core::cmp::Ord for Ranged<AMIN, AMAX>
where
Assert<{allow_range(memlayout(AMIN, AMAX))}>: IsAllowed ,
{
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
let s = self.get();
let o = other.get();
#[allow(clippy::comparison_chain)]
if s>o {core::cmp::Ordering::Greater}
else if s==o {core::cmp::Ordering::Equal}
else {core::cmp::Ordering::Less}
}
fn max(self, other: Self) -> Self where Self: Sized {
unsafe { Self::unchecked_new(max_irang(self.get(), other.get() )) }
}
fn min(self, other: Self) -> Self where Self: Sized {
unsafe { Self::unchecked_new(min_irang(self.get(), other.get() )) }
}
fn clamp(self, min: Self, max: Self) -> Self where Self: Sized {
<Self as core::cmp::Ord>::min(<Self as core::cmp::Ord>::max(self, min), max)
}
}
impl<const AMIN: irang, const AMAX: irang>
core::cmp::PartialOrd<irang> for Ranged<AMIN, AMAX>
where
Assert<{allow_range(memlayout(AMIN, AMAX))}>: IsAllowed ,
{
fn partial_cmp(&self, other: &irang) -> Option<core::cmp::Ordering> {
let s = self.get();
#[allow(clippy::comparison_chain)]
Some(if s>*other {core::cmp::Ordering::Greater}
else if s==*other {core::cmp::Ordering::Equal}
else {core::cmp::Ordering::Less})
}
fn lt(&self, other: &irang) -> bool {
matches!(self.partial_cmp(other), Some(core::cmp::Ordering::Less))
}
fn le(&self, other: &irang) -> bool {
!matches!(self.partial_cmp(other), None | Some(core::cmp::Ordering::Greater))
}
fn gt(&self, other: &irang) -> bool {
matches!(self.partial_cmp(other), Some(core::cmp::Ordering::Greater))
}
fn ge(&self, other: &irang) -> bool {
matches!(self.partial_cmp(other), Some(core::cmp::Ordering::Greater | core::cmp::Ordering::Equal))
}
}
#[allow(clippy::use_self)] impl<const AMIN: irang, const AMAX: irang>
core::cmp::PartialOrd<Ranged<AMIN, AMAX>> for irang
where
Assert<{allow_range(memlayout(AMIN, AMAX))}>: IsAllowed ,
{
fn partial_cmp(&self, other: &Ranged<AMIN, AMAX>) -> Option<core::cmp::Ordering> {
let o = other.get();
#[allow(clippy::comparison_chain)]
Some(if *self>o {core::cmp::Ordering::Greater}
else if *self==o {core::cmp::Ordering::Equal}
else {core::cmp::Ordering::Less})
}
fn lt(&self, other: &Ranged<AMIN, AMAX>) -> bool {
matches!(self.partial_cmp(other), Some(core::cmp::Ordering::Less))
}
fn le(&self, other: &Ranged<AMIN, AMAX>) -> bool {
!matches!(self.partial_cmp(other), None | Some(core::cmp::Ordering::Greater))
}
fn gt(&self, other: &Ranged<AMIN, AMAX>) -> bool {
matches!(self.partial_cmp(other), Some(core::cmp::Ordering::Greater))
}
fn ge(&self, other: &Ranged<AMIN, AMAX>) -> bool {
matches!(self.partial_cmp(other), Some(core::cmp::Ordering::Greater | core::cmp::Ordering::Equal))
}
}