use fixed::types::extra::*;
use fixed::{
traits::{FromFixed, ToFixed},
FixedI128,
};
use serde::{
de::{self, Visitor},
Deserialize, Deserializer, Serialize, Serializer,
};
use std::fmt::{self, Display};
use std::iter::{Product, Sum};
use std::ops::{
Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Not, Rem, RemAssign, Shl, ShlAssign, Shr, ShrAssign, Sub,
SubAssign,
};
#[repr(transparent)]
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct FixNum<T: LeEqU128>(FixedI128<T>);
impl<T: LeEqU128> Not for FixNum<T> {
type Output = FixNum<T>;
fn not(self) -> Self::Output {
Self(self.0.not())
}
}
impl<T: LeEqU128> Neg for FixNum<T> {
type Output = FixNum<T>;
fn neg(self) -> Self::Output {
Self(self.0.neg())
}
}
macro_rules! op1 {
($Op:ident $fun:ident) => {
impl<T: LeEqU128> $Op<FixNum<T>> for FixNum<T> {
type Output = FixNum<T>;
fn $fun(self, other: FixNum<T>) -> Self {
Self((self.0).$fun(other.0))
}
}
impl<T: LeEqU128, U: ToFixed> $Op<U> for FixNum<T> {
type Output = FixNum<T>;
fn $fun(self, other: U) -> Self {
Self((self.0).$fun(FixedI128::saturating_from_num(other)))
}
}
impl<T: LeEqU128> $Op<FixNum<T>> for &FixNum<T> {
type Output = FixNum<T>;
fn $fun(self, other: FixNum<T>) -> FixNum<T> {
FixNum((self.0).$fun(other.0))
}
}
impl<T: LeEqU128, U: ToFixed> $Op<U> for &FixNum<T> {
type Output = FixNum<T>;
fn $fun(self, other: U) -> FixNum<T> {
FixNum((self.0).$fun(FixedI128::saturating_from_num(other)))
}
}
impl<T: LeEqU128> $Op<&FixNum<T>> for FixNum<T> {
type Output = FixNum<T>;
fn $fun(self, other: &FixNum<T>) -> Self {
Self((self.0).$fun(other.0))
}
}
impl<T: LeEqU128> $Op<&FixNum<T>> for &FixNum<T> {
type Output = FixNum<T>;
fn $fun(self, other: &FixNum<T>) -> FixNum<T> {
FixNum((self.0).$fun(other.0))
}
}
};
}
op1!(Add add);
op1!(Sub sub);
op1!(Mul mul);
op1!(Div div);
op1!(Rem rem);
macro_rules! op2 {
($Op:ident $fun:ident) => {
impl<T: LeEqU128> $Op<FixNum<T>> for FixNum<T> {
fn $fun(&mut self, other: FixNum<T>) {
(&mut self.0).$fun(other.0);
}
}
impl<T: LeEqU128> $Op<&FixNum<T>> for FixNum<T> {
fn $fun(&mut self, other: &FixNum<T>) {
(&mut self.0).$fun(other.0);
}
}
impl<T: LeEqU128, U: ToFixed> $Op<U> for FixNum<T> {
fn $fun(&mut self, other: U) {
(&mut self.0).$fun(FixedI128::<T>::saturating_from_num(other));
}
}
};
}
op2!(AddAssign add_assign);
op2!(SubAssign sub_assign);
op2!(MulAssign mul_assign);
op2!(DivAssign div_assign);
op2!(RemAssign rem_assign);
macro_rules! op3 {
($Op:ident $fun:ident $OpA:ident $funA:ident) => {
impl<T: LeEqU128> $Op<i32> for FixNum<T> {
type Output = Self;
fn $fun(self, other: i32) -> Self {
Self((self.0).$fun(other))
}
}
impl<T: LeEqU128> $Op<i32> for &FixNum<T> {
type Output = FixNum<T>;
fn $fun(self, other: i32) -> FixNum<T> {
FixNum((self.0).$fun(other))
}
}
impl<T: LeEqU128> $OpA<i32> for FixNum<T> {
fn $funA(&mut self, other: i32) {
(&mut self.0).$funA(other)
}
}
};
}
op3!(Shl shl ShlAssign shl_assign);
op3!(Shr shr ShrAssign shr_assign);
impl<T: LeEqU128> Sum for FixNum<T> {
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
FixNum(FixedI128::<T>::sum(iter.map(|x| x.0)))
}
}
impl<'a, T: 'a + LeEqU128> Sum<&'a FixNum<T>> for FixNum<T> {
fn sum<I: Iterator<Item = &'a FixNum<T>>>(iter: I) -> Self {
FixNum(FixedI128::<T>::sum(iter.map(|x| x.0)))
}
}
impl<T: LeEqU128> Product for FixNum<T> {
fn product<I: Iterator<Item = Self>>(iter: I) -> Self {
FixNum(FixedI128::<T>::product(iter.map(|x| x.0)))
}
}
impl<'a, T: 'a + LeEqU128> Product<&'a FixNum<T>> for FixNum<T> {
fn product<I: Iterator<Item = &'a FixNum<T>>>(iter: I) -> Self {
FixNum(FixedI128::<T>::product(iter.map(|x| x.0)))
}
}
impl<T: LeEqU128> From<FixedI128<T>> for FixNum<T> {
fn from(x: FixedI128<T>) -> Self {
Self(x)
}
}
impl<T: LeEqU128> From<FixNum<T>> for FixedI128<T> {
fn from(o: FixNum<T>) -> FixedI128<T> {
o.0
}
}
impl<T: LeEqU128> Display for FixNum<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
impl<T: LeEqU128> FixNum<T> {
pub fn panicking<Src: ToFixed>(src: Src) -> FixNum<T> {
FixNum(src.to_fixed())
}
pub fn checked<Src: ToFixed>(src: Src) -> Option<FixNum<T>> {
src.checked_to_fixed().map(FixNum)
}
pub fn saturating<Src: ToFixed>(src: Src) -> FixNum<T> {
FixNum(src.saturating_to_fixed())
}
pub fn wrapping<Src: ToFixed>(src: Src) -> FixNum<T> {
FixNum(src.wrapping_to_fixed())
}
pub fn overflowing<Src: ToFixed>(src: Src) -> (FixNum<T>, bool) {
let (f, b) = src.overflowing_to_fixed();
(FixNum(f), b)
}
pub fn to_num_panicking<Dst: FromFixed>(self) -> Dst {
Dst::from_fixed(self.0)
}
pub fn to_num_checked<Dst: FromFixed>(self) -> Option<Dst> {
Dst::checked_from_fixed(self.0)
}
pub fn to_num_saturating<Dst: FromFixed>(self) -> Dst {
Dst::saturating_from_fixed(self.0)
}
pub fn to_num_wrapping<Dst: FromFixed>(self) -> Dst {
Dst::wrapping_from_fixed(self.0)
}
pub fn to_num_overflowing<Dst: FromFixed>(self) -> (Dst, bool) {
Dst::overflowing_from_fixed(self.0)
}
}
impl<T: LeEqU128> Serialize for FixNum<T> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
self.0.to_num::<f64>().serialize(serializer)
}
}
impl<'de, T: LeEqU128> Deserialize<'de> for FixNum<T> {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<FixNum<T>, D::Error> {
use std::marker::PhantomData;
struct X<T>(PhantomData<T>);
impl<'de, T: LeEqU128> Visitor<'de> for X<T> {
type Value = FixNum<T>;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("number")
}
fn visit_i64<E: de::Error>(self, v: i64) -> Result<Self::Value, E> {
Ok(FixNum::saturating(v))
}
fn visit_u64<E: de::Error>(self, v: u64) -> Result<Self::Value, E> {
Ok(FixNum::saturating(v))
}
fn visit_f64<E: de::Error>(self, v: f64) -> Result<Self::Value, E> {
if v.is_nan() {
Err(E::custom("FixNum cannot parse NaN"))
} else {
Ok(FixNum::saturating(v))
}
}
}
deserializer.deserialize_any(X(PhantomData))
}
}
#[cfg(feature = "dataflow")]
impl<T: LeEqU128> abomonation::Abomonation for FixNum<T> {}
#[cfg(test)]
#[allow(clippy::unusual_byte_groupings)]
mod tests {
use super::*;
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "dataflow", derive(Abomonation))]
struct S {
x: FixNum<U5>,
y: Option<FixNum<U10>>,
}
#[test]
pub fn must_serde() {
#[allow(clippy::approx_constant)]
let js = json!({"x": 6213412, "y": 3.1415926});
let s = serde_json::from_value::<S>(js).unwrap();
assert_eq!(s.x, FixNum::panicking(6213412));
assert_eq!(s.y.unwrap(), FixedI128::<U10>::from_bits(0b11__00100_10001).into());
assert_eq!(
s.x + FixedI128::<U5>::from_num(0.1),
FixedI128::<U5>::from_bits(0b101_11101_10011_11001_00100__00011).into()
);
let out = serde_json::to_string(&s).unwrap();
let s2 = serde_json::from_str::<S>(out.as_str()).unwrap();
assert_eq!(s2, s);
}
#[cfg(feature = "dataflow")]
#[rustfmt::skip]
fn expected_bits() -> Vec<u8> {
if cfg!(target_arch = "x86_64") {
vec![
0b100__00011, 0b1_11001_00, 0b1101_1001, 0b101_1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0b100_10001, 0b11__00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ]
} else if cfg!(target_arch = "aarch64") {
vec![
0b100__00011, 0b1_11001_00, 0b1101_1001, 0b101_1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0b100_10001, 0b11__00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ]
} else {
panic!("unsupported CPU architecture")
}
}
#[test]
#[cfg(feature = "dataflow")]
pub fn must_abomonate() {
let mut s = S {
x: FixNum(FixedI128::<U5>::from_bits(0b101_11101_10011_11001_00100__00011)),
y: Some(FixNum(FixedI128::<U10>::from_bits(0b11__00100_10001))),
};
let mut bytes = Vec::new();
unsafe { abomonation::encode(&s, &mut bytes).unwrap() };
assert_eq!(bytes, expected_bits());
bytes[0] += 64;
assert_eq!(
s.x,
FixedI128::<U5>::from_bits(0b101_11101_10011_11001_00100__00011).into()
);
s.x += 2;
let (value, bytes) = unsafe { abomonation::decode::<S>(&mut bytes) }.unwrap();
assert_eq!(value, &s);
assert!(bytes.is_empty());
}
fn get_value(v: serde_json::Value) -> FixNum<U110> {
serde_json::from_value(v).unwrap()
}
fn get_error(v: serde_json::Value) -> String {
serde_json::from_value::<FixNum<U110>>(v).unwrap_err().to_string()
}
#[test]
pub fn must_handle_edge_cases() {
assert_eq!(get_error(json!({})), "invalid type: map, expected number");
let max = FixNum::<U110>::saturating(262144);
assert!(max > FixNum::<U110>::panicking(131071.9999999));
assert_eq!(get_value(json!(1000000)), max);
let min = FixNum::<U110>::saturating(-262144);
assert!(min < FixNum::<U110>::panicking(-131071.9999999));
assert_eq!(get_value(json!(-1000000)), min);
}
#[test]
#[allow(clippy::eq_op)]
pub fn must_compute() {
use crate::types::fixnum_types::*;
let mut x = FixNum::<U30>::panicking(12);
assert_eq!(x + 3, FixNum::panicking(15));
assert_eq!(x + x, FixNum::panicking(24));
assert_eq!(x - 3, FixNum::panicking(9));
assert_eq!(x - x, FixNum::panicking(0));
assert_eq!(x * 3, FixNum::panicking(36));
assert_eq!(x * x, FixNum::panicking(144));
assert_eq!(x / 4, FixNum::panicking(3));
assert_eq!(x / x, FixNum::panicking(1));
assert_eq!(x >> 3, FixNum::panicking(1.5));
assert_eq!(x << 1, FixNum::panicking(24));
assert_eq!(x % 5, FixNum::panicking(2));
assert_eq!(x % FixNum::panicking(7), FixNum::panicking(5));
x += -5.5;
x -= FixNum::panicking(2);
x /= 8;
x *= FixNum::panicking(1.75);
x %= FixNum::panicking(3);
x >>= 5;
x <<= 8;
assert_eq!(x, FixNum::panicking(63) / FixNum::panicking(8));
assert_eq!(!x, FixNum::panicking(-7.875000001));
assert_eq!(-x, FixNum::panicking(-63) >> 3);
let v: Vec<FixNum<U10>> = vec![FixNum::panicking(1), FixNum::panicking(2), FixNum::panicking(3)];
assert_eq!(v.iter().sum::<FixNum<U10>>(), FixNum::panicking(6));
assert_eq!(v.clone().into_iter().sum::<FixNum<U10>>(), FixNum::panicking(6));
assert_eq!(v.iter().product::<FixNum<U10>>(), FixNum::panicking(6));
assert_eq!(v.clone().into_iter().product::<FixNum<U10>>(), FixNum::panicking(6));
assert_eq!(v.iter().map(|x| x + 3).sum::<FixNum<U10>>(), FixNum::panicking(15));
}
}