pub extern crate muldiv;
pub extern crate num_traits;
pub extern crate typenum;
pub mod aliases;
pub mod fix_value;
pub mod prelude;
pub mod util;
use core::cmp::{Eq, Ord, Ordering, PartialEq, PartialOrd};
use core::fmt::{Debug, Display, Error, Formatter};
use core::hash::{Hash, Hasher};
use core::marker::PhantomData;
use core::ops::{Add, Div, Mul, Neg, Rem, Sub};
use core::ops::{AddAssign, DivAssign, MulAssign, RemAssign, SubAssign};
use muldiv::MulDiv;
use num_traits::{
CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, ConstZero, SaturatingAdd, SaturatingSub,
};
use paste::paste;
use typenum::consts::{U10, Z0};
use typenum::marker_traits::{Bit, Integer, Unsigned};
use typenum::operator_aliases::{AbsVal, Diff, Le, Sum};
use typenum::type_operators::{Abs, IsLess};
pub struct Fix<Bits, Base, Exp> {
pub bits: Bits,
marker: PhantomData<(Base, Exp)>,
}
impl<Bits, Base, Exp> Fix<Bits, Base, Exp> {
pub fn new(bits: Bits) -> Self {
Fix {
bits,
marker: PhantomData,
}
}
pub const fn constant(bits: Bits) -> Self {
Fix {
bits,
marker: PhantomData,
}
}
pub fn convert<ToExp>(self) -> Fix<Bits, Base, ToExp>
where
Bits: FromUnsigned + Pow + Mul<Output = Bits> + Div<Output = Bits>,
Base: Unsigned,
Exp: Sub<ToExp>,
Diff<Exp, ToExp>: Abs + IsLess<Z0>,
AbsVal<Diff<Exp, ToExp>>: Integer,
{
let base = Bits::from_unsigned::<Base>();
let diff = AbsVal::<Diff<Exp, ToExp>>::to_i32();
let inverse = Le::<Diff<Exp, ToExp>, Z0>::to_bool();
let ratio = base.pow(diff.unsigned_abs());
if inverse {
Fix::new(self.bits / ratio)
} else {
Fix::new(self.bits * ratio)
}
}
pub fn widen<ToBits>(self) -> Fix<ToBits, Base, Exp>
where
ToBits: From<Bits>,
{
Fix::<ToBits, Base, Exp>::new(self.bits.into())
}
pub fn narrow<ToBits>(self) -> Option<Fix<ToBits, Base, Exp>>
where
ToBits: TryFrom<Bits>,
{
self.bits.try_into().ok().map(Fix::<ToBits, Base, Exp>::new)
}
}
pub trait FromUnsigned {
fn from_unsigned<U>() -> Self
where
U: Unsigned;
}
macro_rules! impl_from_unsigned {
($ty:ident) => {
impl FromUnsigned for $ty {
fn from_unsigned<U: Unsigned>() -> Self {
paste! { U::[<to_$ty>]() }
}
}
};
}
impl_from_unsigned!(u8);
impl_from_unsigned!(u16);
impl_from_unsigned!(u32);
impl_from_unsigned!(u64);
impl_from_unsigned!(u128);
impl_from_unsigned!(usize);
impl_from_unsigned!(i8);
impl_from_unsigned!(i16);
impl_from_unsigned!(i32);
impl_from_unsigned!(i64);
impl_from_unsigned!(i128);
impl_from_unsigned!(isize);
pub trait Pow {
#[must_use]
fn pow(self, exp: u32) -> Self;
}
macro_rules! impl_pow {
($ty:ident) => {
impl Pow for $ty {
#[inline]
fn pow(self, exp: u32) -> Self {
self.pow(exp)
}
}
};
}
impl_pow!(u8);
impl_pow!(u16);
impl_pow!(u32);
impl_pow!(u64);
impl_pow!(u128);
impl_pow!(usize);
impl_pow!(i8);
impl_pow!(i16);
impl_pow!(i32);
impl_pow!(i64);
impl_pow!(i128);
impl_pow!(isize);
impl<Bits, Base, Exp> Copy for Fix<Bits, Base, Exp> where Bits: Copy {}
impl<Bits, Base, Exp> Clone for Fix<Bits, Base, Exp>
where
Bits: Clone,
{
fn clone(&self) -> Self {
Self::new(self.bits.clone())
}
}
impl<Bits, Base, Exp> Default for Fix<Bits, Base, Exp>
where
Bits: Default,
{
fn default() -> Self {
Self::new(Bits::default())
}
}
impl<Bits, Base, Exp> Fix<Bits, Base, Exp>
where
Bits: ConstZero,
{
#[must_use]
pub const fn zero() -> Self {
Self::constant(Bits::ZERO)
}
}
impl<Bits, Base, Exp> Hash for Fix<Bits, Base, Exp>
where
Bits: Hash,
{
fn hash<H>(&self, state: &mut H)
where
H: Hasher,
{
self.bits.hash(state);
}
}
impl<Bits, Base, Exp> Debug for Fix<Bits, Base, Exp>
where
Bits: Debug,
Base: Unsigned,
Exp: Integer,
{
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
write!(f, "{:?}x{}^{}", self.bits, Base::to_u64(), Exp::to_i64())
}
}
impl<Bits, Exp> Display for Fix<Bits, U10, Exp>
where
Bits: Display,
Exp: Integer,
{
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
let exp = Exp::to_i32();
let decimals = usize::try_from(exp.unsigned_abs()).map_err(|_| Error)?;
let raw = self.bits.to_string();
let (sign, digits) = raw
.strip_prefix('-')
.map_or(("", raw.as_str()), |d| ("-", d));
match exp {
0.. => write!(f, "{sign}{digits}{}", "0".repeat(decimals)),
_ if digits.len() > decimals => {
let (integer, fraction) = digits.split_at(digits.len() - decimals);
write!(f, "{sign}{integer}.{fraction}")
}
_ => {
let padding = "0".repeat(decimals - digits.len());
write!(f, "{sign}0.{padding}{digits}")
}
}
}
}
impl<Bits, Base, Exp> Eq for Fix<Bits, Base, Exp> where Bits: Eq {}
impl<Bits, Base, Exp> PartialEq for Fix<Bits, Base, Exp>
where
Bits: PartialEq,
{
fn eq(&self, rhs: &Self) -> bool {
self.bits == rhs.bits
}
}
impl<Bits, Base, Exp> PartialOrd for Fix<Bits, Base, Exp>
where
Bits: PartialOrd,
{
fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
self.bits.partial_cmp(&rhs.bits)
}
}
impl<Bits, Base, Exp> Ord for Fix<Bits, Base, Exp>
where
Bits: Ord,
{
fn cmp(&self, rhs: &Self) -> Ordering {
self.bits.cmp(&rhs.bits)
}
}
impl<Bits, Base, Exp> Neg for Fix<Bits, Base, Exp>
where
Bits: Neg<Output = Bits>,
{
type Output = Self;
fn neg(self) -> Self {
Self::new(-self.bits)
}
}
impl<Bits, Base, Exp> Add for Fix<Bits, Base, Exp>
where
Bits: Add<Output = Bits>,
{
type Output = Self;
fn add(self, rhs: Self) -> Self {
Self::new(self.bits + rhs.bits)
}
}
impl<Bits, Base, Exp> Sub for Fix<Bits, Base, Exp>
where
Bits: Sub<Output = Bits>,
{
type Output = Self;
fn sub(self, rhs: Self) -> Self {
Self::new(self.bits - rhs.bits)
}
}
impl<Bits, Base, LExp, RExp> Mul<Fix<Bits, Base, RExp>> for Fix<Bits, Base, LExp>
where
Bits: Mul<Output = Bits>,
LExp: Add<RExp>,
{
type Output = Fix<Bits, Base, Sum<LExp, RExp>>;
fn mul(self, rhs: Fix<Bits, Base, RExp>) -> Self::Output {
Self::Output::new(self.bits * rhs.bits)
}
}
impl<Bits, Base, LExp, RExp> Div<Fix<Bits, Base, RExp>> for Fix<Bits, Base, LExp>
where
Bits: Div<Output = Bits>,
LExp: Sub<RExp>,
{
type Output = Fix<Bits, Base, Diff<LExp, RExp>>;
fn div(self, rhs: Fix<Bits, Base, RExp>) -> Self::Output {
Self::Output::new(self.bits / rhs.bits)
}
}
impl<Bits, Base, Exp> Rem for Fix<Bits, Base, Exp>
where
Bits: Rem<Output = Bits>,
{
type Output = Self;
fn rem(self, rhs: Self) -> Self {
Self::new(self.bits % rhs.bits)
}
}
impl<Bits, Base, Exp> Mul<Bits> for Fix<Bits, Base, Exp>
where
Bits: Mul<Output = Bits>,
{
type Output = Self;
fn mul(self, rhs: Bits) -> Self {
Self::new(self.bits * rhs)
}
}
impl<Bits, Base, Exp> Div<Bits> for Fix<Bits, Base, Exp>
where
Bits: Div<Output = Bits>,
{
type Output = Self;
fn div(self, rhs: Bits) -> Self {
Self::new(self.bits / rhs)
}
}
impl<Bits, Base, Exp> Rem<Bits> for Fix<Bits, Base, Exp>
where
Bits: Rem<Output = Bits>,
{
type Output = Self;
fn rem(self, rhs: Bits) -> Self {
Self::new(self.bits % rhs)
}
}
impl<Bits, Base, Exp> AddAssign for Fix<Bits, Base, Exp>
where
Bits: AddAssign,
{
fn add_assign(&mut self, rhs: Self) {
self.bits += rhs.bits;
}
}
impl<Bits, Base, Exp> SubAssign for Fix<Bits, Base, Exp>
where
Bits: SubAssign,
{
fn sub_assign(&mut self, rhs: Self) {
self.bits -= rhs.bits;
}
}
impl<Bits, Base, Exp> MulAssign<Bits> for Fix<Bits, Base, Exp>
where
Bits: MulAssign,
{
fn mul_assign(&mut self, rhs: Bits) {
self.bits *= rhs;
}
}
impl<Bits, Base, Exp> DivAssign<Bits> for Fix<Bits, Base, Exp>
where
Bits: DivAssign,
{
fn div_assign(&mut self, rhs: Bits) {
self.bits /= rhs;
}
}
impl<Bits, Base, LExp, RExp> RemAssign<Fix<Bits, Base, RExp>> for Fix<Bits, Base, LExp>
where
Bits: RemAssign,
{
fn rem_assign(&mut self, rhs: Fix<Bits, Base, RExp>) {
self.bits %= rhs.bits;
}
}
impl<Bits, Base, Exp> RemAssign<Bits> for Fix<Bits, Base, Exp>
where
Bits: RemAssign,
{
fn rem_assign(&mut self, rhs: Bits) {
self.bits %= rhs;
}
}
impl<Bits, Base, Exp> CheckedAdd for Fix<Bits, Base, Exp>
where
Bits: CheckedAdd,
{
fn checked_add(&self, v: &Self) -> Option<Self> {
self.bits.checked_add(&v.bits).map(Self::new)
}
}
impl<Bits, Base, Exp> CheckedSub for Fix<Bits, Base, Exp>
where
Bits: CheckedSub,
{
fn checked_sub(&self, v: &Self) -> Option<Self> {
self.bits.checked_sub(&v.bits).map(Self::new)
}
}
impl<Bits, Base, Exp> Fix<Bits, Base, Exp>
where
Self: CheckedSub,
Bits: Copy,
{
#[must_use]
pub fn abs_diff(&self, v: &Self) -> Fix<Bits, Base, Exp> {
self.checked_sub(v).unwrap_or_else(|| *v - *self)
}
}
pub trait CheckedMulFix<Rhs> {
type Output;
fn checked_mul(&self, v: &Rhs) -> Option<Self::Output>;
}
impl<Bits, Base, LExp, RExp> CheckedMulFix<Fix<Bits, Base, RExp>> for Fix<Bits, Base, LExp>
where
Bits: CheckedMul,
LExp: Add<RExp>,
{
type Output = Fix<Bits, Base, Sum<LExp, RExp>>;
fn checked_mul(&self, v: &Fix<Bits, Base, RExp>) -> Option<Self::Output> {
self.bits.checked_mul(&v.bits).map(Self::Output::new)
}
}
pub trait CheckedDivFix<Rhs> {
type Output;
fn checked_div(&self, v: &Rhs) -> Option<Self::Output>;
}
impl<Bits, Base, LExp, RExp> CheckedDivFix<Fix<Bits, Base, RExp>> for Fix<Bits, Base, LExp>
where
Bits: CheckedDiv,
LExp: Sub<RExp>,
{
type Output = Fix<Bits, Base, Diff<LExp, RExp>>;
fn checked_div(&self, v: &Fix<Bits, Base, RExp>) -> Option<Self::Output> {
self.bits.checked_div(&v.bits).map(Self::Output::new)
}
}
impl<Bits, Base, LExp, RExp> MulDiv<Fix<Bits, Base, RExp>> for Fix<Bits, Base, LExp>
where
Bits: MulDiv,
{
type Output = Fix<<Bits as MulDiv>::Output, Base, LExp>;
fn mul_div_ceil(
self,
num: Fix<Bits, Base, RExp>,
denom: Fix<Bits, Base, RExp>,
) -> Option<Self::Output> {
self.bits
.mul_div_ceil(num.bits, denom.bits)
.map(Self::Output::new)
}
fn mul_div_floor(
self,
num: Fix<Bits, Base, RExp>,
denom: Fix<Bits, Base, RExp>,
) -> Option<Self::Output> {
self.bits
.mul_div_floor(num.bits, denom.bits)
.map(Self::Output::new)
}
fn mul_div_round(
self,
num: Fix<Bits, Base, RExp>,
denom: Fix<Bits, Base, RExp>,
) -> Option<Self::Output> {
self.bits
.mul_div_round(num.bits, denom.bits)
.map(Self::Output::new)
}
}
impl<Bits, Base, Exp> SaturatingAdd for Fix<Bits, Base, Exp>
where
Bits: SaturatingAdd,
{
fn saturating_add(&self, v: &Self) -> Self {
Self::new(self.bits.saturating_add(&v.bits))
}
}
impl<Bits, Base, Exp> SaturatingSub for Fix<Bits, Base, Exp>
where
Bits: SaturatingSub,
{
fn saturating_sub(&self, v: &Self) -> Self {
Self::new(self.bits.saturating_sub(&v.bits))
}
}
#[cfg(test)]
mod tests {
use num_traits::{SaturatingAdd, SaturatingSub};
use typenum::{N3, P3, Z0};
use crate::aliases::decimal::{IFix64, UFix64};
use crate::aliases::si::{Kilo, Micro, Milli, Nano, Unit};
use crate::{CheckedAdd, CheckedDivFix, CheckedMulFix, CheckedSub, MulDiv};
#[test]
fn convert_milli_to_kilo() {
assert_eq!(Kilo::new(15), Milli::new(15_000_000).convert());
}
#[test]
fn convert_kilo_to_milli() {
assert_eq!(Milli::new(15_000_000), Kilo::new(15).convert());
}
#[test]
fn cmp() {
assert!(Kilo::new(1) < Kilo::new(2));
}
#[test]
fn neg() {
assert_eq!(Kilo::new(-1), -Kilo::new(1i32));
}
#[test]
fn add() {
assert_eq!(Kilo::new(3), Kilo::new(1) + Kilo::new(2));
}
#[test]
fn sub() {
assert_eq!(Kilo::new(1), Kilo::new(3) - Kilo::new(2));
}
#[test]
fn mul() {
assert_eq!(Unit::new(6), Kilo::new(2) * Milli::new(3));
}
#[test]
fn div() {
assert_eq!(Unit::new(3), Kilo::new(6) / Kilo::new(2));
}
#[test]
fn rem() {
assert_eq!(Kilo::new(1), Kilo::new(6) % Kilo::new(5));
}
#[test]
fn mul_bits() {
assert_eq!(Kilo::new(6), Kilo::new(2) * 3);
}
#[test]
fn div_bits() {
assert_eq!(Kilo::new(3), Kilo::new(6) / 2);
}
#[test]
fn rem_bits() {
assert_eq!(Kilo::new(1), Kilo::new(6) % 5);
}
#[test]
fn add_assign() {
let mut a = Kilo::new(1);
a += Kilo::new(2);
assert_eq!(Kilo::new(3), a);
}
#[test]
fn sub_assign() {
let mut a = Kilo::new(3);
a -= Kilo::new(2);
assert_eq!(Kilo::new(1), a);
}
#[test]
fn mul_assign_bits() {
let mut a = Kilo::new(2);
a *= 3;
assert_eq!(Kilo::new(6), a);
}
#[test]
fn div_assign_bits() {
let mut a = Kilo::new(6);
a /= 2;
assert_eq!(Kilo::new(3), a);
}
#[test]
fn rem_assign() {
let mut a = Kilo::new(6);
a %= Milli::new(5);
assert_eq!(Kilo::new(1), a);
}
#[test]
fn rem_assign_bits() {
let mut a = Kilo::new(6);
a %= 5;
assert_eq!(Kilo::new(1), a);
}
#[test]
fn checked_add_neg() {
let max = Kilo::new(u8::MAX);
let one = Kilo::new(1);
assert!(max.checked_add(&one).is_none());
}
#[test]
fn checked_add_pos() {
let forty = Kilo::new(40);
let two = Kilo::new(2);
assert_eq!(forty.checked_add(&two), Some(Kilo::new(42)));
}
#[test]
fn checked_sub_neg() {
let one = Kilo::new(1);
let max = Kilo::new(u8::MAX);
assert!(one.checked_sub(&max).is_none());
}
#[test]
fn checked_sub_pos() {
let fifty = Kilo::new(50);
let eight = Kilo::new(8);
assert_eq!(fifty.checked_sub(&eight), Some(Kilo::new(42)));
}
#[test]
fn checked_mul_neg() {
let fifty = Kilo::new(50);
let max = Kilo::new(u8::MAX);
assert!(fifty.checked_mul(&max).is_none());
}
#[test]
fn checked_mul_pos() {
let fifty = Kilo::new(50_u64);
assert_eq!(
fifty.checked_mul(&fifty).map(super::Fix::convert),
Some(Kilo::new(2_500_000_u64))
);
}
#[test]
fn checked_div_neg() {
let one = Unit::new(0);
assert!(one.checked_div(&one).is_none());
}
#[test]
fn checked_div_pos() {
let hundred = Kilo::new(100);
let five = Kilo::new(5);
assert_eq!(hundred.checked_div(&five), Some(Unit::new(20)));
}
#[test]
fn narrow_succeeds() {
let one = Milli::new(1000u128);
let mapped = one.narrow::<u64>();
assert_eq!(mapped, Some(Milli::new(1000u64)));
}
#[test]
fn narrow_fails() {
let one = Milli::new(1699u64);
let mapped = one.narrow::<u8>();
assert_eq!(mapped, None);
}
#[test]
fn widen_succeeds() {
let one = Milli::new(1_340_191u64);
let mapped = one.widen::<u128>();
assert_eq!(mapped, Milli::new(1_340_191_u128));
}
#[test]
fn mul_div_ceil() {
let start = Milli::new(313_459u64);
let mul = Milli::new(1200u64);
let div = Milli::new(2450u64);
assert_eq!(start.mul_div_ceil(mul, div), Some(Milli::new(153_531)));
}
#[test]
fn mul_div_ceil_unit() {
let start = Milli::new(31_345_934u64);
let mul = Milli::new(1000u64);
let div = Milli::new(2000u64);
assert_eq!(
start.mul_div_ceil(mul, div),
Some(Milli::new(15_672_967_u64))
);
}
#[test]
fn mul_div_floor() {
let start = Milli::new(69_693u64);
let mul = Milli::new(5_192u64);
let div = Milli::new(190u64);
assert_eq!(
start.mul_div_floor(mul, div),
Some(Milli::new(1_904_452_u64))
);
}
#[test]
fn mul_div_floor_unit() {
let start = Milli::new(69_693u64);
let mul = Milli::new(1000u64);
let div = Milli::new(9u64);
assert_eq!(
start.mul_div_floor(mul, div),
Some(Milli::new(7_743_666_u64))
);
}
#[test]
fn mul_div_round() {
let start = Milli::new(1892u64);
let mul = Milli::new(3222u64);
let div = Milli::new(9999u64);
assert_eq!(start.mul_div_round(mul, div), Some(Milli::new(610u64)));
}
#[test]
fn mul_div_round_unit() {
let start = Milli::new(1892u64);
let mul = Milli::new(1000u64);
let div = Milli::new(322u64);
assert_eq!(start.mul_div_round(mul, div), Some(Milli::new(5876u64)));
}
#[test]
fn abs_diff() {
let start = Milli::new(u128::MIN);
let end = Milli::new(u128::MAX);
assert_eq!(start.abs_diff(&end), end);
}
#[test]
fn constant() {
assert_eq!(Kilo::constant(69u64), Kilo::new(69u64));
}
#[test]
fn saturating_sub() {
let zero = Kilo::constant(0);
let result = zero.saturating_sub(&Kilo::new(69u64));
assert_eq!(zero, result);
}
#[test]
fn saturating_add() {
let max = Kilo::new(u64::MAX);
let result = max.saturating_add(&Kilo::new(69u64));
assert_eq!(max, result);
}
#[test]
fn zero_is_zero() {
assert_eq!(Kilo::<u64>::zero().bits, 0);
assert_eq!(Milli::<u64>::zero().bits, 0);
assert_eq!(Nano::<u64>::zero().bits, 0);
}
#[test]
fn one_is_correct() {
assert_eq!(Milli::<u64>::one().bits, 1_000);
assert_eq!(Micro::<u64>::one().bits, 1_000_000);
assert_eq!(Nano::<u64>::one().bits, 1_000_000_000);
}
#[test]
fn checked_convert_upconvert() {
assert_eq!(
Milli::new(5u64).checked_convert(),
Some(Micro::new(5_000u64)),
);
}
#[test]
fn checked_convert_downconvert() {
assert_eq!(
Micro::new(5_000u64).checked_convert(),
Some(Milli::new(5u64)),
);
}
#[test]
fn checked_convert_identity() {
assert_eq!(Milli::new(42u64).checked_convert(), Some(Milli::new(42u64)),);
}
#[test]
fn checked_convert_overflow() {
assert_eq!(Milli::new(u64::MAX).checked_convert::<typenum::N9>(), None,);
}
#[test]
fn checked_convert_matches_convert() {
let value = Milli::new(15u64);
assert_eq!(
value.checked_convert::<typenum::N6>(),
Some(value.convert::<typenum::N6>()),
);
}
#[test]
fn display_negative_exp() {
assert_eq!(UFix64::<N3>::new(1_234).to_string(), "1.234");
assert_eq!(UFix64::<N3>::new(1).to_string(), "0.001");
assert_eq!(UFix64::<N3>::new(0).to_string(), "0.000");
}
#[test]
fn display_negative_exp_signed() {
assert_eq!(IFix64::<N3>::new(-1_234).to_string(), "-1.234");
assert_eq!(IFix64::<N3>::new(-1).to_string(), "-0.001");
}
#[test]
fn display_positive_exp() {
assert_eq!(UFix64::<P3>::new(5).to_string(), "5000");
}
#[test]
fn display_zero_exp() {
assert_eq!(UFix64::<Z0>::new(42).to_string(), "42");
}
}