use crate::fpdec_inner::FpdecInner;
use crate::ParseError;
use int_div_cum_error::{Rounding, checked_divide};
use num_traits::{Num, cast::FromPrimitive};
use std::fmt;
#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Hash, Default)]
pub struct StaticPrecFpdec<I, const P: i32>(pub(crate) I);
impl<I, const P: i32> StaticPrecFpdec<I, P>
where I: FpdecInner
{
crate::none_prec_common::define_none_prec_common!();
pub fn checked_mul<J, const Q: i32, const R: i32>(
self,
rhs: StaticPrecFpdec<J, Q>,
) -> Option<StaticPrecFpdec<I, R>>
where J: FpdecInner
{
self.checked_mul_ext(rhs, Rounding::Round, None)
}
pub fn checked_mul_ext<J, const Q: i32, const R: i32>(
self,
rhs: StaticPrecFpdec<J, Q>,
rounding: Rounding,
cum_error: Option<&mut I>,
) -> Option<StaticPrecFpdec<I, R>>
where J: FpdecInner
{
self.0.checked_mul_ext(I::from(rhs.0)?, P + Q - R, rounding, cum_error)
.map(StaticPrecFpdec)
}
pub fn checked_div<J, const Q: i32, const R: i32>(
self,
rhs: StaticPrecFpdec<J, Q>,
) -> Option<StaticPrecFpdec<I, R>>
where J: FpdecInner
{
self.checked_div_ext(rhs, Rounding::Round, None)
}
pub fn checked_div_ext<J, const Q: i32, const R: i32>(
self,
rhs: StaticPrecFpdec<J, Q>,
rounding: Rounding,
cum_error: Option<&mut I>,
) -> Option<StaticPrecFpdec<I, R>>
where J: FpdecInner
{
self.0.checked_div_ext(I::from(rhs.0)?, P - Q - R, rounding, cum_error)
.map(StaticPrecFpdec)
}
pub fn shrink_to(self, retain_precision: i32) -> Self {
self.shrink_to_with_rounding(retain_precision, Rounding::Round)
}
pub fn shrink_to_with_rounding(
self,
retain_precision: i32,
rounding: Rounding,
) -> Self {
Self(self.0.shrink_with_rounding(P - retain_precision, rounding))
}
}
impl<I, const P: i32> fmt::Debug for StaticPrecFpdec<I, P>
where I: fmt::Display
{
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(f, "Fpdec({},{})", self.0, P)
}
}
impl<I, const P: i32> fmt::Display for StaticPrecFpdec<I, P>
where I: FpdecInner + fmt::Display
{
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
self.0.display_fmt(P, f)
}
}
impl<I, const P: i32> std::str::FromStr for StaticPrecFpdec<I, P>
where I: FpdecInner,
ParseError: From<<I as Num>::FromStrRadixErr>
{
type Err = ParseError;
fn from_str(s: &str) -> Result<Self, ParseError> {
I::try_from_str(s, P).map(Self)
}
}
macro_rules! convert_from_int {
($from_int_type:ty) => {
impl<I, const P: i32> TryFrom<$from_int_type> for StaticPrecFpdec<I, P>
where I: FpdecInner
{
type Error = ParseError;
fn try_from(i: $from_int_type) -> Result<Self, Self::Error> {
let i2 = <$from_int_type>::checked_from_int(i, P)?;
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);
macro_rules! convert_from_float {
($float_type:ty, $from_fn:ident, $to_fn:ident) => {
impl<I, const P: i32> TryFrom<$float_type> for StaticPrecFpdec<I, P>
where I: FromPrimitive + FpdecInner
{
type Error = ParseError;
fn try_from(f: $float_type) -> Result<Self, Self::Error> {
let base: $float_type = 10.0;
let inner_f = f * base.powi(P) as $float_type;
I::$from_fn(inner_f.round())
.map(Self)
.ok_or(ParseError::Overflow)
}
}
impl<I, const P: i32> From<StaticPrecFpdec<I, P>> for $float_type
where I: FpdecInner
{
fn from(dec: StaticPrecFpdec<I, P>) -> Self {
let base: $float_type = 10.0;
dec.0.$to_fn().unwrap() / base.powi(P)
}
}
}
}
convert_from_float!(f32, from_f32, to_f32);
convert_from_float!(f64, from_f64, to_f64);
impl<I, const P: i32> std::ops::Neg for StaticPrecFpdec<I, P>
where I: FpdecInner
{
type Output = Self;
fn neg(self) -> Self::Output {
Self(-self.0)
}
}
impl<I, const P: i32> std::ops::Add for StaticPrecFpdec<I, P>
where I: FpdecInner
{
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self(self.0 + rhs.0)
}
}
impl<I, const P: i32> std::ops::Sub for StaticPrecFpdec<I, P>
where I: FpdecInner
{
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
Self(self.0 - rhs.0)
}
}
impl<I, const P: i32> std::ops::AddAssign for StaticPrecFpdec<I, P>
where I: FpdecInner
{
fn add_assign(&mut self, rhs: Self) {
self.0 += rhs.0;
}
}
impl<I, const P: i32> std::ops::SubAssign for StaticPrecFpdec<I, P>
where I: FpdecInner
{
fn sub_assign(&mut self, rhs: Self) {
self.0 -= rhs.0;
}
}
#[cfg(feature="serde")]
use serde::{Serialize, Deserialize, Serializer, Deserializer};
#[cfg(feature="serde")]
impl<I, const P: i32> Serialize for StaticPrecFpdec<I, P>
where I: FpdecInner + fmt::Display
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer
{
if serializer.is_human_readable() {
serializer.collect_str(self)
} else {
Into::<f64>::into(*self).serialize(serializer)
}
}
}
#[cfg(feature="serde")]
impl<'de, I, const P: i32> Deserialize<'de> for StaticPrecFpdec<I, P>
where I: FromPrimitive + FpdecInner,
ParseError: From<<I as Num>::FromStrRadixErr>
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: Deserializer<'de>
{
use serde::de::{self, Visitor};
use std::str::FromStr;
use std::marker::PhantomData;
struct StaticPrecFpdecVistor<I, const P: i32>(PhantomData<I>);
macro_rules! visit_num {
($func_name:ident, $num_type:ty) => {
fn $func_name<E: de::Error>(self, n: $num_type) -> Result<Self::Value, E> {
StaticPrecFpdec::try_from(n)
.map_err(|_| E::custom("decimal overflow"))
}
}
}
impl<'de, I, const P: i32> Visitor<'de> for StaticPrecFpdecVistor<I, P>
where I: FromPrimitive + FpdecInner,
ParseError: From<<I as Num>::FromStrRadixErr>
{
type Value = StaticPrecFpdec<I, P>;
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> {
StaticPrecFpdec::from_str(s)
.map_err(|e| E::custom(format!("decimal {:?}", e)))
}
visit_num!(visit_f32, f32);
visit_num!(visit_f64, f64);
visit_num!(visit_i8, i8);
visit_num!(visit_i16, i16);
visit_num!(visit_i32, i32);
visit_num!(visit_i64, i64);
visit_num!(visit_i128, i128);
}
deserializer.deserialize_any(StaticPrecFpdecVistor(PhantomData))
}
}