use std::ops::{Add, Neg, Shl, Shr, Sub};
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
pub(crate) struct Fixed<const FRAC: u32>(i32);
pub(crate) type Q14 = Fixed<14>;
pub(crate) type Q12 = Fixed<12>;
pub(crate) type Q8 = Fixed<8>;
pub(crate) type Q6 = Fixed<6>;
impl<const F: u32> Fixed<F> {
pub(crate) const ZERO: Self = Self(0);
pub(crate) const ONE: Self = Self(1 << F);
#[inline]
pub(crate) const fn from_bits(bits: i32) -> Self {
Self(bits)
}
#[inline]
pub(crate) const fn to_bits(self) -> i32 {
self.0
}
#[inline]
pub(crate) fn from_f64(value: f64) -> Self {
Self((value * f64::from(1u32 << F)) as i32)
}
#[inline]
pub(crate) fn mul<const B: u32, const R: u32>(self, rhs: Fixed<B>) -> Fixed<R> {
const { assert!(F + B >= R, "product cannot gain fraction bits") };
Fixed(self.0.wrapping_mul(rhs.0).wrapping_shr(F + B - R))
}
#[inline]
pub(crate) fn div<const B: u32, const R: u32>(self, rhs: Fixed<B>) -> Fixed<R> {
const { assert!(R + B >= F, "quotient cannot lose fraction bits") };
Fixed(self.0.wrapping_shl(R + B - F).wrapping_div(rhs.0))
}
#[inline]
pub(crate) fn rescale<const R: u32>(self) -> Fixed<R> {
if R >= F {
Fixed(self.0.wrapping_shl(R - F))
} else {
Fixed(self.0.wrapping_shr(F - R))
}
}
#[inline]
pub(crate) fn round<const R: u32>(self) -> Fixed<R> {
const { assert!(F > R, "rounding must drop fraction bits") };
let half = 1 << (F - R - 1);
let bits = if self.0 >= 0 {
self.0.wrapping_add(half)
} else {
self.0.wrapping_sub(half)
};
Fixed(bits >> (F - R))
}
#[inline]
pub(crate) fn abs(self) -> Self {
Self(self.0.wrapping_abs())
}
}
impl<const F: u32> Add for Fixed<F> {
type Output = Self;
#[inline]
fn add(self, rhs: Self) -> Self {
Self(self.0.wrapping_add(rhs.0))
}
}
impl<const F: u32> Sub for Fixed<F> {
type Output = Self;
#[inline]
fn sub(self, rhs: Self) -> Self {
Self(self.0.wrapping_sub(rhs.0))
}
}
impl<const F: u32> Neg for Fixed<F> {
type Output = Self;
#[inline]
fn neg(self) -> Self {
Self(self.0.wrapping_neg())
}
}
impl<const F: u32> Shr<u32> for Fixed<F> {
type Output = Self;
#[inline]
fn shr(self, bits: u32) -> Self {
Self(self.0.wrapping_shr(bits))
}
}
impl<const F: u32> Shl<u32> for Fixed<F> {
type Output = Self;
#[inline]
fn shl(self, bits: u32) -> Self {
Self(self.0.wrapping_shl(bits))
}
}