use super::{FixedUInt, MachineWord, const_ct_select};
use crate::machineword::ConstMachineWord;
use const_num_traits::{AbsDiff, ConstZero, OverflowingSub, WrappingSub};
use const_num_traits::{Personality, PersonalityTag};
c0nst::c0nst! {
c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> AbsDiff for FixedUInt<T, N, P> {
type Output = Self;
fn abs_diff(self, other: Self) -> Self {
match P::TAG {
PersonalityTag::Nct => {
if self >= other {
self - other
} else {
other - self
}
}
PersonalityTag::Ct => {
let (diff, borrow) =
<Self as OverflowingSub>::overflowing_sub(self, other);
let neg_diff =
<Self as WrappingSub>::wrapping_sub(<Self as ConstZero>::ZERO, diff);
const_ct_select(diff, neg_diff, borrow as u8)
}
}
}
}
c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> AbsDiff for &FixedUInt<T, N, P> {
type Output = FixedUInt<T, N, P>;
fn abs_diff(self, other: Self) -> FixedUInt<T, N, P> {
<FixedUInt<T, N, P> as AbsDiff>::abs_diff(*self, *other)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_abs_diff() {
type U16 = FixedUInt<u8, 2>;
assert_eq!(
AbsDiff::abs_diff(U16::from(10u8), U16::from(3u8)),
U16::from(7u8)
);
assert_eq!(
AbsDiff::abs_diff(U16::from(3u8), U16::from(10u8)),
U16::from(7u8)
);
assert_eq!(
AbsDiff::abs_diff(U16::from(5u8), U16::from(5u8)),
U16::from(0u8)
);
assert_eq!(
AbsDiff::abs_diff(U16::from(0u8), U16::from(100u8)),
U16::from(100u8)
);
assert_eq!(
AbsDiff::abs_diff(U16::from(255u8), U16::from(0u8)),
U16::from(255u8)
);
}
c0nst::c0nst! {
pub c0nst fn const_abs_diff<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality>(
a: FixedUInt<T, N, P>,
b: FixedUInt<T, N, P>,
) -> FixedUInt<T, N, P> {
AbsDiff::abs_diff(a, b)
}
}
#[test]
fn test_const_abs_diff() {
type U16 = FixedUInt<u8, 2>;
assert_eq!(
const_abs_diff(U16::from(10u8), U16::from(3u8)),
U16::from(7u8)
);
#[cfg(feature = "nightly")]
{
const A: U16 = FixedUInt::from_array([10, 0]);
const B: U16 = FixedUInt::from_array([3, 0]);
const DIFF: U16 = const_abs_diff(A, B);
assert_eq!(DIFF, FixedUInt::from_array([7, 0]));
}
}
}