use core::{
cmp::{Ordering, PartialOrd},
ops::{BitAnd, Shr},
};
use num_traits::{
Bounded, Signed,
cast::AsPrimitive,
identities::Zero,
ops::wrapping::{WrappingAdd, WrappingSub},
};
use serde::{Deserialize, Serialize};
use dsp_process::{Inplace, Process};
#[repr(i8)]
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum Wrap {
Negative = -1,
#[default]
None = 0,
Positive = 1,
}
impl From<Wrap> for Ordering {
fn from(value: Wrap) -> Self {
match value {
Wrap::Negative => Self::Less,
Wrap::None => Self::Equal,
Wrap::Positive => Self::Greater,
}
}
}
impl From<Ordering> for Wrap {
fn from(value: Ordering) -> Self {
match value {
Ordering::Less => Self::Negative,
Ordering::Equal => Self::None,
Ordering::Greater => Self::Positive,
}
}
}
impl core::ops::Add for Wrap {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
(self as i32 + rhs as i32).cmp(&0).into()
}
}
impl core::ops::AddAssign for Wrap {
fn add_assign(&mut self, rhs: Self) {
*self = *self + rhs
}
}
#[inline(always)]
pub fn overflowing_sub<T>(y: T, x: T) -> (T, Wrap)
where
T: WrappingSub + Zero + PartialOrd,
{
let delta = y.wrapping_sub(&x);
let wrap = (delta >= T::zero()).cmp(&(y >= x)).into();
(delta, wrap)
}
pub fn saturating_scale(lo: i32, hi: i32, shift: u32) -> i32 {
debug_assert!(shift > 0);
debug_assert!(shift <= 32);
let hi_range = -1 << (shift - 1);
if hi <= hi_range {
i32::MIN - hi_range
} else if -hi <= hi_range {
hi_range - i32::MIN
} else {
(lo >> shift) + (hi << (32 - shift))
}
}
#[derive(Copy, Clone, Default, Deserialize, Serialize)]
pub struct Unwrapper<Q> {
pub y: Q,
}
impl<Q> Unwrapper<Q>
where
Q: 'static + WrappingAdd + Copy,
{
pub fn wraps<P, const S: u32>(&self) -> P
where
Q: AsPrimitive<P> + Shr<u32, Output = Q>,
P: 'static + Copy + WrappingAdd + Signed + BitAnd<u32, Output = P>,
{
(self.y >> S)
.as_()
.wrapping_add(&((self.y >> (S - 1)).as_() & 1))
}
pub fn phase<P>(&self) -> P
where
P: 'static + Copy,
Q: AsPrimitive<P>,
{
self.y.as_()
}
}
impl<P, Q> Process<P> for Unwrapper<Q>
where
P: AsPrimitive<Q> + WrappingSub,
Q: AsPrimitive<P> + WrappingAdd,
{
fn process(&mut self, x: P) -> P {
let dx = x.wrapping_sub(&self.y.as_());
self.y = self.y.wrapping_add(&dx.as_());
dx
}
}
impl<P: Copy, Q> Inplace<P> for Unwrapper<Q> where Self: Process<P> {}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct ClampWrap<Q> {
pub x0: Q,
pub clamp: Wrap,
}
impl<Q> Process<Q> for ClampWrap<Q>
where
Q: 'static + Zero + PartialOrd + WrappingSub + Copy + Bounded,
{
fn process(&mut self, x: Q) -> Q {
let (_dx, wrap) = overflowing_sub(x, self.x0);
self.x0 = x;
self.clamp += wrap;
match self.clamp {
Wrap::Negative => Q::min_value(),
Wrap::None => x,
Wrap::Positive => Q::max_value(),
}
}
}
impl<Q: Copy> Inplace<Q> for ClampWrap<Q> where Self: Process<Q> {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn overflowing_sub_correctness() {
for (x0, x1, v) in [
(0i32, 0i32, Wrap::None),
(0, 1, Wrap::None),
(0, -1, Wrap::None),
(1, 0, Wrap::None),
(-1, 0, Wrap::None),
(0, 0x7fff_ffff, Wrap::None),
(-1, 0x7fff_ffff, Wrap::Negative),
(-2, 0x7fff_ffff, Wrap::Negative),
(-1, -0x8000_0000, Wrap::None),
(0, -0x8000_0000, Wrap::None),
(1, -0x8000_0000, Wrap::Positive),
(-0x6000_0000, 0x6000_0000, Wrap::Negative),
(0x6000_0000, -0x6000_0000, Wrap::Positive),
(-0x4000_0000, 0x3fff_ffff, Wrap::None),
(-0x4000_0000, 0x4000_0000, Wrap::Negative),
(-0x4000_0000, 0x4000_0001, Wrap::Negative),
(0x4000_0000, -0x3fff_ffff, Wrap::None),
(0x4000_0000, -0x4000_0000, Wrap::None),
(0x4000_0000, -0x4000_0001, Wrap::Positive),
]
.iter()
{
let (dx, w) = overflowing_sub(*x1, *x0);
assert_eq!(*v, w, " = overflowing_sub({:#x}, {:#x})", *x0, *x1);
let (dx0, w0) = x1.overflowing_sub(*x0);
assert_eq!(w0, w != Wrap::None);
assert_eq!(dx, dx0);
}
}
#[test]
fn saturating_scale_correctness() {
let shift = 8;
for (lo, hi, res) in [
(0i32, 0i32, 0i32),
(0, 1, 0x0100_0000),
(0, -1, -0x0100_0000),
(0x100, 0, 1),
(-1 << 31, 0, -1 << 23),
(0x7fffffff, 0, 0x007f_ffff),
(0x7fffffff, 1, 0x0017f_ffff),
(-0x7fffffff, -1, -0x0180_0000),
(0x1234_5600, 0x7f, 0x7f12_3456),
(0x1234_5600, -0x7f, -0x7f00_0000 + 0x12_3456),
(0, 0x7f, 0x7f00_0000),
(0, 0x80, 0x7fff_ff80),
(0, -0x7f, -0x7f00_0000),
(0, -0x80, -0x7fff_ff80),
(0x7fff_ffff, 0x7f, 0x7f7f_ffff),
(-0x8000_0000, 0x7f, 0x7e80_0000),
(-0x8000_0000, -0x7f, -0x7f80_0000),
(0x7fff_ffff, -0x7f, -0x7e80_0001),
(0x100, 0x7f, 0x7f00_0001),
(0, -0x80, -0x7fff_ff80),
(-1 << 31, 0x80, 0x7fff_ff80),
(-1 << 31, -0x80, -0x7fff_ff80),
]
.iter()
{
let s = saturating_scale(*lo, *hi, shift);
assert_eq!(
*res, s,
"{:#x} != {:#x} = saturating_scale({:#x}, {:#x}, {:#x})",
*res, s, *lo, *hi, shift
);
}
}
}