use crate::stable_hash;
use bigdecimal::BigDecimal as BD;
pub use inner::BigDecimal;
use num_traits::{FromPrimitive, One, ToPrimitive, Zero};
use super::{BigInt, Sign};
mod inner {
use super::super::BigInt;
use super::*;
#[repr(transparent)]
#[derive(
Eq, PartialEq, PartialOrd, Ord, Clone, Hash, Default, serde::Serialize, serde::Deserialize,
)]
#[serde(transparent)]
pub struct BigDecimal(BD);
impl BigDecimal {
pub fn new_with_scale(big_int: BigInt, scale: i64) -> Self {
Self::new(BD::new(big_int.into_inner(), scale))
}
pub fn new_with_exp(big_int: BigInt, exp: i64) -> Self {
Self::new_with_scale(big_int, -exp)
}
pub fn new(mut inner: BD) -> Self {
if inner.is_zero() {
return Self(BD::zero());
}
for _ in 0..2 {
let big_decimal = inner.with_prec(Self::MAX_SIGNIFICANT_DIGITS);
let (bigint, exp) = big_decimal.into_bigint_and_exponent();
let (sign, mut digits) = bigint.to_radix_be(10);
let trailing_count = digits.iter().rev().take_while(|i| **i == 0).count();
digits.truncate(digits.len() - trailing_count);
let int_val = num_bigint::BigInt::from_radix_be(sign, &digits, 10).unwrap();
let scale = exp - trailing_count as i64;
inner = BD::new(int_val, scale);
}
Self(inner)
}
#[inline(always)]
pub(crate) fn into_inner(self) -> BD {
self.0
}
#[inline(always)]
pub(crate) fn as_inner(&self) -> &BD {
&self.0
}
#[inline(always)]
pub(super) fn as_mut_inner(&mut self) -> MutInner<'_> {
MutInner(self, false)
}
}
pub struct MutInner<'a>(&'a mut BigDecimal, bool);
impl<'a> Drop for MutInner<'a> {
fn drop(&mut self) {
if self.1 {
*self.0 = BigDecimal::new(std::mem::take(self.0).into_inner());
}
}
}
impl<'a> core::ops::Deref for MutInner<'a> {
type Target = BD;
fn deref(&self) -> &Self::Target {
&self.0.0
}
}
impl<'a> core::ops::DerefMut for MutInner<'a> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.1 = true;
&mut self.0.0
}
}
}
impl BigDecimal {
const MAX_SIGNIFICANT_DIGITS: u64 = 34;
pub fn zero() -> Self {
Self::new(BD::zero())
}
pub fn one() -> Self {
Self::new(BD::one())
}
}
impl BigDecimal {
pub fn abs(&self) -> Self {
Self::new(self.as_inner().abs())
}
pub fn sign(&self) -> Sign {
self.as_inner().sign().into()
}
pub fn digits(&self) -> u64 {
self.as_inner().digits()
}
pub fn as_bigint_and_exponent(&self) -> (BigInt, i64) {
let (bi, exp) = self.as_inner().as_bigint_and_exponent();
(bi.into(), exp)
}
pub fn into_bigint_and_exponent(self) -> (BigInt, i64) {
let (bi, exp) = self.into_inner().into_bigint_and_exponent();
(bi.into(), exp)
}
pub fn sqrt(&self) -> Option<Self> {
self.as_inner().sqrt().map(Self::new)
}
pub fn half(&self) -> Self {
Self::new(self.as_inner().half())
}
pub fn double(&self) -> Self {
Self::new(self.as_inner().double())
}
}
impl From<BD> for BigDecimal {
fn from(value: BD) -> Self {
Self::new(value)
}
}
impl From<&BD> for BigDecimal {
fn from(value: &BD) -> Self {
Self::from(value.clone())
}
}
impl From<BigInt> for BigDecimal {
fn from(value: BigInt) -> Self {
Self::new(BD::from(value.into_inner()))
}
}
impl From<&BigInt> for BigDecimal {
fn from(value: &BigInt) -> Self {
Self::from(value.clone())
}
}
impl From<num_bigint::BigInt> for BigDecimal {
fn from(value: num_bigint::BigInt) -> Self {
Self::new(BD::from(value))
}
}
impl From<&num_bigint::BigInt> for BigDecimal {
fn from(value: &num_bigint::BigInt) -> Self {
Self::from(value.clone())
}
}
impl From<&BigDecimal> for BigDecimal {
fn from(value: &BigDecimal) -> Self {
value.clone()
}
}
macro_rules! impl_from {
($ty:ty) => {
impl From<$ty> for BigDecimal {
fn from(t: $ty) -> Self {
Self::new(BD::from(t))
}
}
};
}
impl_from!(u8);
impl_from!(u16);
impl_from!(u32);
impl_from!(u64);
impl_from!(u128);
impl_from!(i8);
impl_from!(i16);
impl_from!(i32);
impl_from!(i64);
impl_from!(i128);
impl TryFrom<f64> for BigDecimal {
type Error = <BD as TryFrom<f64>>::Error;
fn try_from(value: f64) -> Result<Self, Self::Error> {
BD::try_from(value).map(Self::new)
}
}
impl TryFrom<f32> for BigDecimal {
type Error = <BD as TryFrom<f32>>::Error;
fn try_from(value: f32) -> Result<Self, Self::Error> {
BD::try_from(value).map(Self::new)
}
}
impl<'x> std::ops::Add for &'x BigDecimal {
type Output = BigDecimal;
fn add(self, rhs: &'x BigDecimal) -> Self::Output {
BigDecimal::new(self.as_inner() + rhs.as_inner())
}
}
impl<'x> std::ops::Add<BigDecimal> for &'x BigDecimal {
type Output = BigDecimal;
fn add(self, rhs: BigDecimal) -> Self::Output {
BigDecimal::new(self.as_inner() + rhs.into_inner())
}
}
impl<T: Into<BigDecimal>> std::ops::Add<T> for BigDecimal {
type Output = BigDecimal;
fn add(self, rhs: T) -> Self::Output {
Self::new(self.into_inner() + rhs.into().into_inner())
}
}
impl<T: Into<BigDecimal>> std::ops::AddAssign<T> for BigDecimal {
fn add_assign(&mut self, rhs: T) {
*self.as_mut_inner() += rhs.into().into_inner();
}
}
impl<'x> std::ops::Sub for &'x BigDecimal {
type Output = BigDecimal;
fn sub(self, rhs: &'x BigDecimal) -> Self::Output {
BigDecimal::new(self.as_inner() - rhs.as_inner())
}
}
impl<'x> std::ops::Sub<BigDecimal> for &'x BigDecimal {
type Output = BigDecimal;
fn sub(self, rhs: BigDecimal) -> Self::Output {
BigDecimal::new(self.as_inner() - rhs.into_inner())
}
}
impl<T: Into<BigDecimal>> std::ops::Sub<T> for BigDecimal {
type Output = BigDecimal;
fn sub(self, rhs: T) -> Self::Output {
Self::new(self.into_inner() - rhs.into().into_inner())
}
}
impl<T: Into<BigDecimal>> std::ops::SubAssign<T> for BigDecimal {
fn sub_assign(&mut self, rhs: T) {
*self.as_mut_inner() -= rhs.into().into_inner();
}
}
impl<'x> std::ops::Mul for &'x BigDecimal {
type Output = BigDecimal;
fn mul(self, rhs: &'x BigDecimal) -> Self::Output {
BigDecimal::new(self.as_inner() * rhs.as_inner())
}
}
impl<'x> std::ops::Mul<BigDecimal> for &'x BigDecimal {
type Output = BigDecimal;
fn mul(self, rhs: BigDecimal) -> Self::Output {
BigDecimal::new(self.as_inner() * rhs.into_inner())
}
}
impl<T: Into<BigDecimal>> std::ops::Mul<T> for BigDecimal {
type Output = BigDecimal;
fn mul(self, rhs: T) -> Self::Output {
Self::new(self.into_inner() * rhs.into().into_inner())
}
}
impl<T: Into<BigDecimal>> std::ops::MulAssign<T> for BigDecimal {
fn mul_assign(&mut self, rhs: T) {
*self.as_mut_inner() *= rhs.into().into_inner();
}
}
impl<'x> std::ops::Div for &'x BigDecimal {
type Output = BigDecimal;
fn div(self, rhs: &'x BigDecimal) -> Self::Output {
BigDecimal::new(self.as_inner() / rhs.as_inner())
}
}
impl<'x> std::ops::Div<BigDecimal> for &'x BigDecimal {
type Output = BigDecimal;
fn div(self, rhs: BigDecimal) -> Self::Output {
BigDecimal::new(self.as_inner() / rhs.into_inner())
}
}
impl<T: Into<BigDecimal>> std::ops::Div<T> for BigDecimal {
type Output = BigDecimal;
fn div(self, rhs: T) -> Self::Output {
Self::new(self.into_inner() / rhs.into().into_inner())
}
}
impl<T: Into<BigDecimal>> std::ops::DivAssign<T> for BigDecimal {
fn div_assign(&mut self, rhs: T) {
Self::new(self.as_inner() / rhs.into().into_inner());
}
}
impl std::ops::Neg for BigDecimal {
type Output = BigDecimal;
fn neg(self) -> Self::Output {
Self::new(self.into_inner().neg())
}
}
impl One for BigDecimal {
fn one() -> Self {
Self::new(BD::one())
}
}
impl Zero for BigDecimal {
fn zero() -> Self {
Self::new(BD::zero())
}
fn is_zero(&self) -> bool {
self.as_inner().is_zero()
}
}
impl ToPrimitive for BigDecimal {
fn to_i64(&self) -> Option<i64> {
self.as_inner().to_i64()
}
fn to_u64(&self) -> Option<u64> {
self.as_inner().to_u64()
}
fn to_isize(&self) -> Option<isize> {
self.as_inner().to_isize()
}
fn to_i8(&self) -> Option<i8> {
self.as_inner().to_i8()
}
fn to_i16(&self) -> Option<i16> {
self.as_inner().to_i16()
}
fn to_i32(&self) -> Option<i32> {
self.as_inner().to_i32()
}
fn to_i128(&self) -> Option<i128> {
self.as_inner().to_i128()
}
fn to_usize(&self) -> Option<usize> {
self.as_inner().to_usize()
}
fn to_u8(&self) -> Option<u8> {
self.as_inner().to_u8()
}
fn to_u16(&self) -> Option<u16> {
self.as_inner().to_u16()
}
fn to_u32(&self) -> Option<u32> {
self.as_inner().to_u32()
}
fn to_u128(&self) -> Option<u128> {
self.as_inner().to_u128()
}
fn to_f32(&self) -> Option<f32> {
self.as_inner().to_f32()
}
fn to_f64(&self) -> Option<f64> {
self.as_inner().to_f64()
}
}
impl FromPrimitive for BigDecimal {
fn from_i64(n: i64) -> Option<Self> {
BD::from_i64(n).map(Self::new)
}
fn from_u64(n: u64) -> Option<Self> {
BD::from_u64(n).map(Self::new)
}
fn from_isize(n: isize) -> Option<Self> {
BD::from_isize(n).map(Self::new)
}
fn from_i8(n: i8) -> Option<Self> {
BD::from_i8(n).map(Self::new)
}
fn from_i16(n: i16) -> Option<Self> {
BD::from_i16(n).map(Self::new)
}
fn from_i32(n: i32) -> Option<Self> {
BD::from_i32(n).map(Self::new)
}
fn from_i128(n: i128) -> Option<Self> {
BD::from_i128(n).map(Self::new)
}
fn from_usize(n: usize) -> Option<Self> {
BD::from_usize(n).map(Self::new)
}
fn from_u8(n: u8) -> Option<Self> {
BD::from_u8(n).map(Self::new)
}
fn from_u16(n: u16) -> Option<Self> {
BD::from_u16(n).map(Self::new)
}
fn from_u32(n: u32) -> Option<Self> {
BD::from_u32(n).map(Self::new)
}
fn from_u128(n: u128) -> Option<Self> {
BD::from_u128(n).map(Self::new)
}
fn from_f32(n: f32) -> Option<Self> {
BD::from_f32(n).map(Self::new)
}
fn from_f64(n: f64) -> Option<Self> {
BD::from_f64(n).map(Self::new)
}
}
impl std::str::FromStr for BigDecimal {
type Err = <BD as std::str::FromStr>::Err;
#[inline]
fn from_str(s: &str) -> Result<BigDecimal, Self::Err> {
BD::from_str(s).map(Self::new)
}
}
impl core::fmt::Display for BigDecimal {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.as_inner().fmt(f)
}
}
impl core::fmt::Debug for BigDecimal {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
<BD as core::fmt::Display>::fmt(self.as_inner(), f)
}
}
pub(crate) mod sql {
use super::*;
use sqlx::Postgres;
impl sqlx::Type<Postgres> for BigDecimal {
fn type_info() -> <Postgres as sqlx::Database>::TypeInfo {
<BD as sqlx::Type<Postgres>>::type_info()
}
}
impl<'q> sqlx::Encode<'q, Postgres> for BigDecimal {
fn encode_by_ref(
&self,
buf: &mut <Postgres as sqlx::Database>::ArgumentBuffer<'q>,
) -> Result<sqlx::encode::IsNull, sqlx::error::BoxDynError> {
self.as_inner().encode_by_ref(buf)
}
}
impl<'r> sqlx::Decode<'r, Postgres> for BigDecimal {
fn decode(value: sqlx::postgres::PgValueRef) -> Result<Self, sqlx::error::BoxDynError> {
BD::decode(value).map(Self::new)
}
}
impl sqlx::postgres::PgHasArrayType for BigDecimal {
fn array_type_info() -> sqlx::postgres::PgTypeInfo {
<BD as sqlx::postgres::PgHasArrayType>::array_type_info()
}
}
}
mod graphql {
use super::{BD, BigDecimal};
use async_graphql::{InputValueError, InputValueResult, Scalar, ScalarType, Value};
#[Scalar(name = "BigDecimal")]
impl ScalarType for BigDecimal {
fn parse(value: Value) -> InputValueResult<Self> {
match &value {
Value::Number(n) => {
if let Some(f) = n.as_f64() {
return BD::try_from(f)
.map_err(InputValueError::custom)
.map(Self::new);
}
if let Some(f) = n.as_i64() {
return Ok(Self::from(f));
}
Ok(Self::from(n.as_u64().unwrap()))
}
Value::String(s) => <BD as std::str::FromStr>::from_str(s)
.map(Self::new)
.map_err(Into::into),
_ => Err(InputValueError::expected_type(value)),
}
}
fn to_value(&self) -> Value {
Value::String(self.as_inner().to_string())
}
}
}
impl stable_hash::StableHash for BigDecimal {
fn stable_hash<H: stable_hash::StableHasher>(&self, field_address: H::Addr, state: &mut H) {
use stable_hash::FieldAddress;
let (int, exp) = self.as_bigint_and_exponent();
stable_hash::StableHash::stable_hash(&exp, field_address.child(1), state);
int.stable_hash(field_address, state);
}
}
#[cfg(test)]
mod test {
use crate::types::BigDecimal;
use bigdecimal::BigDecimal as BD;
use bigdecimal::BigDecimal as BDO;
#[test]
fn test_mul() {
let l = "6625.0776824274553963848109507954";
let r = "-43.399999999999999998";
let res: BigDecimal = l.parse::<BigDecimal>().unwrap() * r.parse::<BigDecimal>().unwrap();
let result = l.parse::<BD>().unwrap() * r.parse::<BD>().unwrap();
let result_old = l.parse::<BDO>().unwrap() * r.parse::<BDO>().unwrap();
println!(
"digits: {}, {}, {}",
res.digits(),
result.digits(),
result_old.digits()
);
println!(
"with_prec: {}, {}, {}",
res,
result.with_prec(34),
result_old.with_prec(34),
);
assert_eq!(res.to_string(), result_old.with_prec(34).to_string(),);
}
#[test]
fn test_price_bug() {
let tvl0 = "69.443015385421607094";
let tvl1 = "-6063484.465072869046808563";
let tvl0_b = tvl0.parse::<BigDecimal>().unwrap();
let tvl1_b = tvl1.parse::<BigDecimal>().unwrap();
let price_b = &tvl1_b / &tvl0_b;
let expected_price = "-87315.97312443024485924387921881404";
assert_eq!(price_b.to_string(), expected_price);
}
}