use crate::const_scale_fpdec::ConstScaleFpdec;
use crate::fpdec_inner::FpdecInner;
use crate::{IntoRatioInt, ParseError, Rounding};
use core::{fmt, num::ParseIntError, ops, str::FromStr};
use num_traits::{cast::FromPrimitive, float::FloatCore, Num, Signed};
#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Hash, Default, Debug)]
#[repr(transparent)]
pub struct OobScaleFpdec<I>(I);
impl<I> OobScaleFpdec<I>
where
I: FpdecInner,
{
crate::none_scale_common::define_none_scale_common!();
#[must_use]
pub fn checked_mul<J>(
self,
rhs: OobScaleFpdec<J>,
diff_scale: i32, ) -> Option<Self>
where
J: FpdecInner,
{
self.checked_mul_ext(rhs, diff_scale, Rounding::Round)
}
#[must_use]
pub fn checked_mul_ext<J>(
self,
rhs: OobScaleFpdec<J>,
diff_scale: i32, rounding: Rounding,
) -> Option<Self>
where
J: FpdecInner,
{
self.0
.checked_mul_ext(I::from(rhs.0)?, diff_scale, rounding)
.map(Self)
}
#[must_use]
pub fn checked_mul_const_scale<J, const S: i32>(
self,
rhs: ConstScaleFpdec<J, S>,
) -> Option<Self>
where
J: FpdecInner,
{
self.checked_mul_const_scale_ext(rhs, Rounding::Round)
}
#[must_use]
pub fn checked_mul_const_scale_ext<J, const S: i32>(
self,
rhs: ConstScaleFpdec<J, S>,
rounding: Rounding,
) -> Option<Self>
where
J: FpdecInner,
{
self.0
.checked_mul_ext(I::from(rhs.mantissa())?, S, rounding)
.map(Self)
}
#[must_use]
pub fn checked_div<J>(
self,
rhs: OobScaleFpdec<J>,
diff_scale: i32, ) -> Option<Self>
where
J: FpdecInner,
{
self.checked_div_ext(rhs, diff_scale, Rounding::Round)
}
#[must_use]
pub fn checked_div_ext<J>(
self,
rhs: OobScaleFpdec<J>,
diff_scale: i32, rounding: Rounding,
) -> Option<Self>
where
J: FpdecInner,
{
self.0
.checked_div_ext(I::from(rhs.0)?, diff_scale, rounding)
.map(Self)
}
#[must_use]
pub fn checked_div_const_scale<J, const S: i32>(
self,
rhs: ConstScaleFpdec<J, S>,
) -> Option<Self>
where
J: FpdecInner,
{
self.checked_div_const_scale_ext(rhs, Rounding::Round)
}
#[must_use]
pub fn checked_div_const_scale_ext<J, const S: i32>(
self,
rhs: ConstScaleFpdec<J, S>,
rounding: Rounding,
) -> Option<Self>
where
J: FpdecInner,
{
self.0
.checked_div_ext(I::from(rhs.mantissa())?, -S, rounding)
.map(Self)
}
#[must_use]
pub fn round_diff(self, diff_scale: i32) -> Self {
self.round_diff_ext(diff_scale, Rounding::Round)
}
#[must_use]
pub fn round_diff_ext(self, diff_scale: i32, rounding: Rounding) -> Self {
Self(self.0.round_diff_with_rounding(diff_scale, rounding))
}
pub fn try_from_str(s: &str, scale: i32) -> Result<Self, ParseError>
where
I: Num<FromStrRadixErr = ParseIntError>,
{
I::try_from_str(s, scale).map(Self)
}
#[must_use]
pub fn to_f32(self, scale: i32) -> f32 {
let f = self.0.to_f32().unwrap();
if scale > 0 {
f / 10.0.powi(scale)
} else if scale < 0 {
f * 10.0.powi(-scale)
} else {
f
}
}
#[must_use]
pub fn to_f64(self, scale: i32) -> f64 {
let f = self.0.to_f64().unwrap();
if scale > 0 {
f / 10.0.powi(scale)
} else if scale < 0 {
f * 10.0.powi(-scale)
} else {
f
}
}
}
impl<I> OobScaleFpdec<I>
where
I: FpdecInner + Signed,
{
crate::none_scale_common::define_none_scale_common_signed!();
}
impl<I, const S: i32> From<ConstScaleFpdec<I, S>> for OobScaleFpdec<I>
where
I: FpdecInner,
{
fn from(sd: ConstScaleFpdec<I, S>) -> Self {
Self(sd.mantissa())
}
}
macro_rules! convert_from_int {
($from_int_type:ty) => {
impl<I> TryFrom<($from_int_type, i32)> for OobScaleFpdec<I>
where
I: FpdecInner,
{
type Error = ParseError;
fn try_from(i: ($from_int_type, i32)) -> Result<Self, Self::Error> {
if i.1 > 0 {
let i2 = I::from(i.0).ok_or(ParseError::Overflow)?;
I::checked_from_int(i2, i.1).map(Self)
} else {
let i2 = i.0.checked_from_int(i.1)?;
I::from(i2).ok_or(ParseError::Overflow).map(Self)
}
}
}
};
}
convert_from_int!(i8);
convert_from_int!(i16);
convert_from_int!(i32);
convert_from_int!(i64);
convert_from_int!(i128);
convert_from_int!(u8);
convert_from_int!(u16);
convert_from_int!(u32);
convert_from_int!(u64);
convert_from_int!(u128);
macro_rules! convert_from_float {
($float_type:ty, $from_fn:ident) => {
impl<I> TryFrom<($float_type, i32)> for OobScaleFpdec<I>
where
I: FromPrimitive + FpdecInner,
{
type Error = ParseError;
fn try_from(t: ($float_type, i32)) -> Result<Self, Self::Error> {
let (f, scale) = t;
let inner_f = if scale > 0 {
f * 10.0.powi(scale)
} else if scale < 0 {
f / 10.0.powi(-scale)
} else {
f
};
I::$from_fn(inner_f.round())
.map(Self)
.ok_or(ParseError::Overflow)
}
}
};
}
convert_from_float!(f32, from_f32);
convert_from_float!(f64, from_f64);
impl<I> ops::Neg for OobScaleFpdec<I>
where
I: FpdecInner + Signed,
{
type Output = Self;
fn neg(self) -> Self::Output {
Self(-self.0)
}
}
impl<I> ops::Add for OobScaleFpdec<I>
where
I: FpdecInner,
{
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self(self.0 + rhs.0)
}
}
impl<I> ops::Sub for OobScaleFpdec<I>
where
I: FpdecInner,
{
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
Self(self.0 - rhs.0)
}
}
impl<I, J> ops::Mul<J> for OobScaleFpdec<I>
where
I: FpdecInner,
J: Into<I> + Num,
{
type Output = Self;
fn mul(self, rhs: J) -> Self::Output {
self.checked_mul_int(rhs)
.expect("overflow in decimal multiplication")
}
}
impl<I, J, const S: i32> ops::Mul<ConstScaleFpdec<J, S>> for OobScaleFpdec<I>
where
I: FpdecInner,
J: FpdecInner,
{
type Output = Self;
fn mul(self, rhs: ConstScaleFpdec<J, S>) -> Self::Output {
self.checked_mul_const_scale(rhs)
.expect("overflow in decimal multiplication")
}
}
impl<I, J> ops::Div<J> for OobScaleFpdec<I>
where
I: FpdecInner,
J: Into<I> + Num,
{
type Output = Self;
fn div(self, rhs: J) -> Self::Output {
self.checked_div_int(rhs).expect("fail in decimal division")
}
}
impl<I, J, const S: i32> ops::Div<ConstScaleFpdec<J, S>> for OobScaleFpdec<I>
where
I: FpdecInner,
J: FpdecInner,
{
type Output = Self;
fn div(self, rhs: ConstScaleFpdec<J, S>) -> Self::Output {
self.checked_div_const_scale(rhs)
.expect("overflow in decimal multiplication")
}
}
impl<I> ops::AddAssign for OobScaleFpdec<I>
where
I: FpdecInner,
{
fn add_assign(&mut self, rhs: Self) {
self.0 += rhs.0;
}
}
impl<I> ops::SubAssign for OobScaleFpdec<I>
where
I: FpdecInner,
{
fn sub_assign(&mut self, rhs: Self) {
self.0 -= rhs.0;
}
}
impl<I, J> ops::MulAssign<J> for OobScaleFpdec<I>
where
I: FpdecInner,
J: Into<I> + Num,
{
fn mul_assign(&mut self, rhs: J) {
*self = *self * rhs;
}
}
impl<I, J, const S: i32> ops::MulAssign<ConstScaleFpdec<J, S>> for OobScaleFpdec<I>
where
I: FpdecInner,
J: FpdecInner,
{
fn mul_assign(&mut self, rhs: ConstScaleFpdec<J, S>) {
*self = *self * rhs;
}
}
impl<I, J> ops::DivAssign<J> for OobScaleFpdec<I>
where
I: FpdecInner,
J: Into<I> + Num,
{
fn div_assign(&mut self, rhs: J) {
*self = *self / rhs;
}
}
impl<I, J, const S: i32> ops::DivAssign<ConstScaleFpdec<J, S>> for OobScaleFpdec<I>
where
I: FpdecInner,
J: FpdecInner,
{
fn div_assign(&mut self, rhs: ConstScaleFpdec<J, S>) {
*self = *self / rhs;
}
}
impl<I> core::iter::Sum for OobScaleFpdec<I>
where
I: FpdecInner,
{
fn sum<Iter: Iterator<Item = Self>>(iter: Iter) -> Self {
iter.fold(Self::ZERO, |acc, d| acc + d)
}
}
impl<'a, I> core::iter::Sum<&'a Self> for OobScaleFpdec<I>
where
I: FpdecInner,
{
fn sum<Iter: Iterator<Item = &'a Self>>(iter: Iter) -> Self {
iter.fold(Self::ZERO, |acc, d| acc + *d)
}
}
#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Hash, Default, Debug)]
pub struct OobFmt<I>(pub OobScaleFpdec<I>, pub i32);
impl<I> fmt::Display for OobFmt<I>
where
I: FpdecInner + fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
let scale = self.1;
self.0 .0.display_fmt(scale, f)
}
}
impl<I> FromStr for OobFmt<I>
where
I: FpdecInner + Num<FromStrRadixErr = ParseIntError>,
{
type Err = ParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let (inner, scale) = I::try_from_str_only(s)?;
Ok(Self(OobScaleFpdec(inner), scale))
}
}
impl<I> OobFmt<I>
where
I: FpdecInner,
{
pub fn rescale(self, scale2: i32) -> Result<OobScaleFpdec<I>, ParseError> {
let Self(dec, scale0) = self;
if scale2 == scale0 {
Ok(dec)
} else if scale2 > scale0 {
let inner = I::get_exp((scale2 - scale0) as usize)
.ok_or(ParseError::Overflow)?
.checked_mul(&dec.0)
.ok_or(ParseError::Overflow)?;
Ok(OobScaleFpdec(inner))
} else {
let diff_exp = I::get_exp((scale0 - scale2) as usize).ok_or(ParseError::Precision)?;
let inner = dec.0 / diff_exp;
if (dec.0 % diff_exp).is_zero() {
Ok(OobScaleFpdec(inner))
} else {
Err(ParseError::Precision)
}
}
}
}
impl<I, J> IntoRatioInt<J> for OobScaleFpdec<I>
where
I: FpdecInner + Into<J>,
{
fn to_int(self) -> J {
self.mantissa().into()
}
}
#[cfg(feature = "serde")]
use serde::{Deserialize, Deserializer, Serialize, Serializer};
#[cfg(feature = "serde")]
impl<I> Serialize for OobFmt<I>
where
I: FpdecInner + fmt::Display,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.collect_str(self)
}
}
#[cfg(feature = "serde")]
impl<'de, I> Deserialize<'de> for OobFmt<I>
where
I: FromPrimitive + FpdecInner + Num<FromStrRadixErr = ParseIntError>,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
use core::marker::PhantomData;
use core::str::FromStr;
use serde::de::{self, Visitor};
struct OobFmtVistor<I>(PhantomData<I>);
impl<'de, I> Visitor<'de> for OobFmtVistor<I>
where
I: FromPrimitive + FpdecInner + Num<FromStrRadixErr = ParseIntError>,
{
type Value = OobFmt<I>;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "decimal")
}
fn visit_str<E: de::Error>(self, s: &str) -> Result<Self::Value, E> {
OobFmt::from_str(s).map_err(E::custom)
}
}
deserializer.deserialize_str(OobFmtVistor(PhantomData))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate as primitive_fixed_point_decimal;
use crate::fpdec;
type Dec32 = OobScaleFpdec<i32>;
type Fmt32 = OobFmt<i32>;
#[allow(non_snake_case)]
fn Fmt32(d: Dec32, s: i32) -> Fmt32 {
OobFmt::<i32>(d, s)
}
#[test]
fn test_mul() {
let two = Dec32::from_mantissa(2);
let four = Dec32::from_mantissa(4);
let zero = Dec32::ZERO;
assert_eq!(two.checked_mul(two, 0), Some(four));
assert_eq!(two.checked_mul(two, 3), Some(zero));
assert_eq!(two.checked_mul(two, -3), four.checked_mul_int(1000));
assert_eq!(two.checked_mul(two, 10), None);
assert_eq!(two.checked_mul(two, -10), None);
}
#[test]
fn test_mul_overflow() {
let max = Dec32::MAX;
let min = Dec32::MIN;
let ten_p6: Dec32 = fpdec!(10, 6);
let half_min = Dec32::MIN.checked_div_int(2).unwrap();
let half_max = Dec32::MAX.checked_div_int_ext(2, Rounding::Floor).unwrap();
assert_eq!(max.checked_mul_int(2), None);
assert_eq!(min.checked_mul_int(2), None);
assert_eq!(half_min.checked_mul_int(2), Some(min));
assert_eq!(half_max.checked_mul_int(2), max.checked_sub(fpdec!(1, 0)));
assert_eq!(max.checked_mul(ten_p6, 7), Some(max));
assert_eq!(min.checked_mul(ten_p6, 7), Some(min));
assert_eq!(max.checked_mul(max, 6), None);
assert_eq!(max.checked_mul(ten_p6, 6), None);
assert_eq!(half_max.checked_mul(ten_p6, 6), None);
assert_eq!(min.checked_mul(min, 6), None);
assert_eq!(min.checked_mul(ten_p6, 6), None);
assert_eq!(half_min.checked_mul(ten_p6, 6), None);
assert_eq!(max.checked_mul(max, 10), None);
assert_eq!(max.checked_mul(ten_p6, 10), None);
assert_eq!(max.checked_mul(max, -10), None);
assert_eq!(max.checked_mul(ten_p6, -10), None);
}
#[test]
fn test_div() {
let two = Dec32::from_mantissa(2);
let four = Dec32::from_mantissa(4);
let zero = Dec32::ZERO;
assert_eq!(four.checked_div(two, 0), Some(two));
assert_eq!(four.checked_div(two, 3), Some(zero));
assert_eq!(four.checked_div(two, -3), two.checked_mul_int(1000));
assert_eq!(four.checked_div(two, 10), None);
assert_eq!(four.checked_div(two, -10), None);
}
#[test]
fn test_div_overflow() {
let max = Dec32::MAX;
let min = Dec32::MIN;
let cent_p6: Dec32 = fpdec!(0.1, 6);
let half_min = Dec32::MIN.checked_div_int(2).unwrap();
let half_max = Dec32::MAX.checked_div_int_ext(2, Rounding::Floor).unwrap();
assert_eq!(max.checked_div(cent_p6, -5), Some(max));
assert_eq!(min.checked_div(cent_p6, -5), Some(min));
assert_eq!(max.checked_div(cent_p6, -6), None);
assert_eq!(half_max.checked_div(cent_p6, -6), None);
assert_eq!(min.checked_div(cent_p6, -6), None);
assert_eq!(half_min.checked_div(cent_p6, -6), None);
assert_eq!(max.checked_div(max, 10), None);
assert_eq!(max.checked_div(cent_p6, 10), None);
assert_eq!(max.checked_div(max, -10), None);
assert_eq!(max.checked_div(cent_p6, -10), None);
}
#[test]
fn test_from_int() {
assert_eq!(Dec32::try_from((1_i16, 2)).unwrap().mantissa(), 100);
assert_eq!(Dec32::try_from((i32::MAX, 2)), Err(ParseError::Overflow));
assert_eq!(
Dec32::try_from((i16::MAX, 2)).unwrap().mantissa(),
i16::MAX as i32 * 100
);
assert_eq!(
Dec32::try_from((i32::MAX as i64 * 100, -2))
.unwrap()
.mantissa(),
i32::MAX
);
assert_eq!(Dec32::try_from((i32::MAX, 2)), Err(ParseError::Overflow));
assert_eq!(
Dec32::try_from((i32::MAX as i64 * 1000, -2)),
Err(ParseError::Overflow)
);
}
#[test]
fn test_from_float() {
assert_eq!(Dec32::try_from((3.1415, 2)).unwrap().mantissa(), 314);
assert_eq!(Dec32::try_from((31415.16, -2)).unwrap().mantissa(), 314);
assert_eq!(Dec32::try_from((3.14e10, 2)), Err(ParseError::Overflow));
assert_eq!(Dec32::try_from((3.14e16, -2)), Err(ParseError::Overflow));
}
#[test]
fn test_fmt() {
assert_eq!(Fmt32::from_str("0"), Ok(Fmt32(Dec32::ZERO, 0)));
assert_eq!(Fmt32::from_str("1000"), Ok(Fmt32(fpdec!(1000, -3), -3)));
assert_eq!(Fmt32::from_str("-1000"), Ok(Fmt32(fpdec!(-1000, -3), -3)));
assert_eq!(Fmt32::from_str("0.12"), Ok(Fmt32(fpdec!(0.12, 2), 2)));
assert_eq!(Fmt32::from_str("-0.12"), Ok(Fmt32(fpdec!(-0.12, 2), 2)));
assert_eq!(Fmt32::from_str("3.14"), Ok(Fmt32(fpdec!(3.14, 2), 2)));
assert_eq!(Fmt32::from_str("-3.14"), Ok(Fmt32(fpdec!(-3.14, 2), 2)));
assert_eq!(
Fmt32::from_str("3.14159265359879"),
Err(ParseError::Overflow)
);
}
#[test]
fn test_sum() {
let v: [Dec32; 3] = [fpdec!(0.1, 2), fpdec!(0.2, 2), fpdec!(0.3, 2)];
let s: Dec32 = v.iter().sum();
assert_eq!(s, fpdec!(0.6, 2));
}
}