#![cfg_attr(not(feature = "std"), no_std)]
#![doc = include_str!("../README.md")]
#[cfg(not(feature = "std"))]
#[allow(unused_imports)]
use num_traits::float::FloatCore;
use num_traits::{AsPrimitive, ConstOne, ConstZero, One, Zero};
use core::{
fmt, iter,
marker::PhantomData,
num::Wrapping,
ops::{Add, Div, Mul, Neg, Rem, Shl, Shr, Sub},
ops::{AddAssign, DivAssign, MulAssign, RemAssign, ShlAssign, ShrAssign, SubAssign},
ops::{BitAnd, BitOr, BitXor, Not},
ops::{BitAndAssign, BitOrAssign, BitXorAssign},
};
pub trait Shift: Copy + Shl<usize, Output = Self> + Shr<usize, Output = Self> {
fn shs(self, f: i8) -> Self;
}
impl<T: Copy + Shl<usize, Output = T> + Shr<usize, Output = T>> Shift for T {
#[inline(always)]
fn shs(self, f: i8) -> Self {
if f >= 0 {
self << (f as _)
} else {
self >> (-f as _)
}
}
}
pub trait Accu<A> {
fn up(self) -> A;
fn down(a: A) -> Self;
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Default)]
#[repr(transparent)]
#[cfg_attr(feature = "serde", serde(transparent))]
pub struct Q<T, A, const F: i8> {
_accu: PhantomData<A>,
pub inner: T,
}
impl<T: Clone, A, const F: i8> Clone for Q<T, A, F> {
#[inline]
fn clone(&self) -> Self {
Self {
_accu: PhantomData,
inner: self.inner.clone(),
}
}
}
impl<T: Copy, A, const F: i8> Copy for Q<T, A, F> {}
impl<T: PartialEq, A, const F: i8> PartialEq for Q<T, A, F> {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.inner.eq(&other.inner)
}
}
impl<T: Eq, A, const F: i8> Eq for Q<T, A, F> where Self: PartialEq {}
impl<T: PartialOrd, A, const F: i8> PartialOrd for Q<T, A, F> {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
self.inner.partial_cmp(&other.inner)
}
}
impl<T: Ord, A, const F: i8> Ord for Q<T, A, F>
where
Self: PartialOrd,
{
#[inline]
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
self.inner.cmp(&other.inner)
}
}
impl<T: One + Shift, A, const F: i8> One for Q<T, A, F>
where
Self: Mul<Output = Self>,
{
fn one() -> Self {
Self::new(T::one().shs(F))
}
}
impl<T: Zero, A, const F: i8> Zero for Q<T, A, F>
where
Self: Add<Output = Self>,
{
fn zero() -> Self {
Self::new(T::zero())
}
fn is_zero(&self) -> bool {
self.inner.is_zero()
}
}
impl<T: ConstZero, A, const F: i8> ConstZero for Q<T, A, F> {
const ZERO: Self = Self::new(T::ZERO);
}
impl<T, A, const F: i8> Q<T, A, F> {
pub const DELTA: f32 = if F > 0 {
1.0 / (1u128 << F) as f32
} else {
(1u128 << -F) as f32
};
#[inline]
pub const fn new(inner: T) -> Self {
Self {
_accu: PhantomData,
inner,
}
}
}
impl<T: Shift, A, const F: i8> Q<T, A, F> {
#[inline]
pub fn scale<const F1: i8>(self) -> Q<T, A, F1> {
Q::new(self.inner.shs(F1 - F))
}
#[inline]
pub fn trunc(self) -> T {
self.inner.shs(-F)
}
#[inline]
pub fn from_int(value: T) -> Self {
Self::new(value.shs(F))
}
}
impl<A: Shift, T: Accu<A>, const F: i8> Q<A, T, F> {
#[inline]
pub fn quantize(self) -> T {
T::down(self.trunc())
}
}
impl<T: Accu<A> + Shift, A, const F: i8> From<(T, i8)> for Q<T, A, F> {
fn from(value: (T, i8)) -> Self {
Self::new(value.0.shs(F - value.1))
}
}
impl<T, A, const F: i8> From<Q<T, A, F>> for (T, i8) {
fn from(value: Q<T, A, F>) -> Self {
(value.inner, F)
}
}
macro_rules! impl_as_float {
($ty:ident) => {
impl<T: 'static + Copy, A: 'static, const F: i8> AsPrimitive<Q<T, A, F>> for $ty
where
$ty: AsPrimitive<T>,
{
#[inline]
fn as_(self) -> Q<T, A, F> {
Q::new(
(self * const { 1.0 / Q::<T, A, F>::DELTA as $ty })
.round()
.as_(),
)
}
}
impl<T: AsPrimitive<$ty>, A: 'static, const F: i8> AsPrimitive<$ty> for Q<T, A, F> {
#[inline]
fn as_(self) -> $ty {
self.inner.as_() * Self::DELTA as $ty
}
}
};
}
impl_as_float!(f32);
impl_as_float!(f64);
impl<T, A, const F: i8> Q<T, A, F>
where
f32: AsPrimitive<Q<T, A, F>>,
Self: Copy + 'static,
{
#[inline]
pub fn from_f32(value: f32) -> Self {
value.as_()
}
}
impl<T, A, const F: i8> Q<T, A, F>
where
f64: AsPrimitive<Q<T, A, F>>,
Self: Copy + 'static,
{
#[inline]
pub fn from_f64(value: f64) -> Self {
value.as_()
}
}
impl<T, A, const F: i8> Q<T, A, F>
where
Self: 'static + Copy + AsPrimitive<f32>,
{
#[inline]
pub fn as_f32(self) -> f32 {
self.as_()
}
}
impl<T, A, const F: i8> Q<T, A, F>
where
Self: 'static + Copy + AsPrimitive<f64>,
{
#[inline]
pub fn as_f64(self) -> f64 {
self.as_()
}
}
impl<T, A, const F: i8> AsPrimitive<Self> for Q<T, A, F>
where
Self: Copy + 'static,
{
fn as_(self) -> Self {
self
}
}
macro_rules! forward_unop {
($tr:ident::$m:ident) => {
impl<T: $tr<Output = T>, A, const F: i8> $tr for Q<T, A, F> {
type Output = Self;
#[inline]
fn $m(self) -> Self::Output {
Self::new(<T as $tr>::$m(self.inner))
}
}
};
}
forward_unop!(Neg::neg);
forward_unop!(Not::not);
macro_rules! forward_sh_op {
($tr:ident::$m:ident) => {
impl<U, T: $tr<U, Output = T>, A, const F: i8> $tr<U> for Q<T, A, F> {
type Output = Self;
#[inline]
fn $m(self, rhs: U) -> Self::Output {
Self::new(<T as $tr<U>>::$m(self.inner, rhs))
}
}
};
}
forward_sh_op!(Shr::shr);
forward_sh_op!(Shl::shl);
macro_rules! forward_sh_assign_op {
($tr:ident::$m:ident) => {
impl<T: $tr<U>, U, A, const F: i8> $tr<U> for Q<T, A, F> {
#[inline]
fn $m(&mut self, rhs: U) {
<T as $tr<U>>::$m(&mut self.inner, rhs)
}
}
};
}
forward_sh_assign_op!(ShrAssign::shr_assign);
forward_sh_assign_op!(ShlAssign::shl_assign);
macro_rules! forward_binop {
($tr:ident::$m:ident) => {
impl<T: $tr<T, Output = T>, A, const F: i8> $tr for Q<T, A, F> {
type Output = Self;
#[inline]
fn $m(self, rhs: Self) -> Self::Output {
Self::new(<T as $tr>::$m(self.inner, rhs.inner))
}
}
};
}
forward_binop!(Rem::rem);
forward_binop!(Add::add);
forward_binop!(Sub::sub);
forward_binop!(BitAnd::bitand);
forward_binop!(BitOr::bitor);
forward_binop!(BitXor::bitxor);
impl<T: Accu<A>, A: Mul<Output = A>, const F: i8> Mul<T> for Q<T, A, F> {
type Output = Q<A, T, F>;
#[inline]
fn mul(self, rhs: T) -> Q<A, T, F> {
Q::new(self.inner.up() * rhs.up())
}
}
impl<T: Div<Output = T>, A, const F: i8> Div<T> for Q<T, A, F> {
type Output = Self;
#[inline]
fn div(self, rhs: T) -> Self {
Q::new(self.inner / rhs)
}
}
macro_rules! forward_assign_op_foreign {
($tr:ident::$m:ident) => {
impl<T: $tr<T>, A, const F: i8> $tr<T> for Q<T, A, F> {
#[inline]
fn $m(&mut self, rhs: T) {
<T as $tr>::$m(&mut self.inner, rhs)
}
}
};
}
forward_assign_op_foreign!(MulAssign::mul_assign);
forward_assign_op_foreign!(DivAssign::div_assign);
macro_rules! forward_assign_op {
($tr:ident::$m:ident) => {
impl<T: $tr<T>, A, const F: i8> $tr for Q<T, A, F> {
#[inline]
fn $m(&mut self, rhs: Self) {
<T as $tr>::$m(&mut self.inner, rhs.inner)
}
}
};
}
forward_assign_op!(RemAssign::rem_assign);
forward_assign_op!(AddAssign::add_assign);
forward_assign_op!(SubAssign::sub_assign);
forward_assign_op!(BitAndAssign::bitand_assign);
forward_assign_op!(BitOrAssign::bitor_assign);
forward_assign_op!(BitXorAssign::bitxor_assign);
impl<T: Copy + Accu<A>, A: Shift + Mul<A, Output = A>, const F: i8, const F1: i8>
MulAssign<Q<T, A, F1>> for Q<T, A, F>
{
#[inline]
fn mul_assign(&mut self, rhs: Q<T, A, F1>) {
self.inner = T::down((self.inner.up() * rhs.inner.up()).shs(-F1));
}
}
impl<
T: Copy + Shift + Accu<A> + Div<T, Output = T>,
A: Shift + Div<A, Output = A>,
const F: i8,
const F1: i8,
> DivAssign<Q<T, A, F1>> for Q<T, A, F>
{
#[inline]
fn div_assign(&mut self, rhs: Q<T, A, F1>) {
self.inner = if F1 > 0 {
T::down(self.inner.up().shs(F1) / rhs.inner.up())
} else {
self.inner.shs(F1) / rhs.inner
};
}
}
impl<T, A, const F: i8> Mul for Q<T, A, F>
where
Self: MulAssign,
{
type Output = Self;
#[inline]
fn mul(mut self, rhs: Self) -> Self::Output {
self *= rhs;
self
}
}
impl<T, A, const F: i8> Div for Q<T, A, F>
where
Self: DivAssign,
{
type Output = Self;
#[inline]
fn div(mut self, rhs: Self) -> Self::Output {
self /= rhs;
self
}
}
impl<T: iter::Sum, A, const F: i8> iter::Sum for Q<T, A, F> {
#[inline]
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
Self::new(iter.map(|i| i.inner).sum())
}
}
macro_rules! impl_fmt {
($tr:path) => {
impl<T, A, const F: i8> $tr for Q<T, A, F>
where
Self: Copy + AsPrimitive<f64>,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
<f64 as $tr>::fmt(&(*self).as_(), f)
}
}
};
}
impl_fmt!(fmt::Display);
impl_fmt!(fmt::UpperExp);
impl_fmt!(fmt::LowerExp);
macro_rules! impl_dot_fmt {
($tr:path) => {
impl<T: $tr, A, const F: i8> $tr for Q<T, A, F> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.inner.fmt(f)
}
}
};
}
impl_dot_fmt!(fmt::Debug);
impl_dot_fmt!(fmt::Binary);
impl_dot_fmt!(fmt::Octal);
impl_dot_fmt!(fmt::UpperHex);
impl_dot_fmt!(fmt::LowerHex);
macro_rules! impl_q {
($alias:ident<$t:ty, $a:ty>) => {
impl_q!($alias<$t, $a>, $t, |x| x as _, core::convert::identity);
};
($alias:ident<$t:ty, $a:ty>, $wrap:tt) => {
impl_q!($alias<$wrap<$t>, $wrap<$a>>, $t, |x: $wrap<_>| $wrap(x.0 as _), $wrap);
};
($alias:ident<$t:ty, $a:ty>, $inner:ty, $as:expr, $wrap:expr) => {
impl Accu<$a> for $t {
#[inline(always)]
fn up(self) -> $a {
$as(self)
}
#[inline(always)]
fn down(a: $a) -> Self {
$as(a)
}
}
#[doc = concat!("Fixed point [`", stringify!($t), "`] with [`", stringify!($a), "`] accumulator")]
pub type $alias<const F: i8> = Q<$t, $a, F>;
impl<const F: i8> ConstOne for Q<$t, $a, F> {
const ONE: Self = Self::new($wrap(if F >= 0 {1 << F as usize} else {0}));
}
impl<const F: i8> AsPrimitive<$t> for Q<$a, $t, F> {
#[inline]
fn as_(self) -> $t {
self.quantize()
}
}
impl<const F: i8> Mul<Q<$t, $a, F>> for $t {
type Output = $t;
#[inline]
fn mul(self, rhs: Q<$t, $a, F>) -> Self::Output {
(rhs * self).quantize()
}
}
impl<const F: i8> Div<Q<$t, $a, F>> for $t {
type Output = $t;
#[inline]
fn div(self, rhs: Q<$t, $a, F>) -> Self::Output {
if F > 0 {
<$t>::down(self.up().shs(F) / rhs.inner.up())
} else {
self.shs(F) / rhs.inner
}
}
}
};
}
impl_q!(Q8<i8, i16>);
impl_q!(Q16<i16, i32>);
impl_q!(Q32<i32, i64>);
impl_q!(Q64<i64, i128>);
impl_q!(P8<u8, u16>);
impl_q!(P16<u16, u32>);
impl_q!(P32<u32, u64>);
impl_q!(P64<u64, u128>);
impl_q!(W8<i8, i16>, Wrapping);
impl_q!(W16<i16, i32>, Wrapping);
impl_q!(W32<i32, i64>, Wrapping);
impl_q!(W64<i64, i128>, Wrapping);
impl_q!(V8<u8, u16>, Wrapping);
impl_q!(V16<u16, u32>, Wrapping);
impl_q!(V32<u32, u64>, Wrapping);
impl_q!(V64<u64, u128>, Wrapping);
#[cfg(test)]
mod test {
use super::*;
#[test]
fn simple() {
assert_eq!(
Q32::<5>::from_int(4) * Q32::<5>::from_int(3),
Q32::from_int(3 * 4)
);
assert_eq!(
Q32::<5>::from_int(12) / Q32::<5>::from_int(6),
Q32::from_int(2)
);
assert_eq!(7 * Q32::<4>::new(0x33), 7 * 3 + ((3 * 7) >> 4));
}
#[test]
fn display() {
assert_eq!(format!("{}", Q32::<9>::new(0x12345)), "145.634765625");
assert_eq!(format!("{}", Q32::<9>::from_int(99)), "99");
}
}