use crate::biginteger::BigInteger;
use core::fmt::{Display, Formatter};
use core::str::FromStr;
use cosmwasm_schema::cw_serde;
use cosmwasm_std::{Decimal256, StdError, Uint128, Uint256};
use std::iter::Sum;
use std::ops::{Add, AddAssign, Div, Mul, MulAssign, Sub, SubAssign};
#[cw_serde]
#[derive(Copy, Default, Ord, PartialOrd, Eq)]
pub struct BigDecimal(pub Decimal256);
impl BigDecimal {
pub const MAX: Self = Self(Decimal256::MAX);
pub const MIN: Self = Self(Decimal256::MIN);
pub fn new(bigint: BigInteger) -> Self {
Self(Decimal256::new(bigint.0))
}
pub fn from(bigint: BigInteger, decimals: u32) -> Self {
Self(Decimal256::from_ratio(
bigint.0,
Uint128::from(10u64).pow(decimals),
))
}
pub fn is_zero(&self) -> bool {
self.0.is_zero()
}
pub fn percent(x: u64) -> BigDecimal {
Self(Decimal256::percent(x))
}
pub fn zero() -> Self {
Self(Decimal256::zero())
}
pub fn one() -> Self {
Self(Decimal256::one())
}
pub fn from_ratio(numerator: impl Into<Uint256>, denominator: impl Into<Uint256>) -> Self {
Self(Decimal256::from_ratio(numerator, denominator))
}
pub fn saturating_sub(&self, rhs: Self) -> Self {
Self(Decimal256::saturating_sub(self.0, rhs.0))
}
pub fn scale_up(&self, decimals: u32) -> BigInteger {
BigInteger((self.0 * Decimal256::from_ratio(10u128.pow(decimals), 1u128)).to_uint_floor())
}
pub fn move_point_right(&self, decimals: u32) -> BigDecimal {
*self * BigDecimal::from_ratio(10u128.pow(decimals), 1u128)
}
pub fn move_point_left(&self, decimals: u32) -> BigDecimal {
*self / BigDecimal::from_ratio(10u128.pow(decimals), 1u128)
}
pub fn is_ratio(&self) -> bool {
*self >= BigDecimal::zero() && *self <= BigDecimal::one()
}
pub fn from_be_bytes(bytes: [u8; 32]) -> Self {
Self(Decimal256::new(Uint256::from_be_bytes(bytes)))
}
pub fn from_le_bytes(bytes: [u8; 32]) -> Self {
Self(Decimal256::new(Uint256::from_le_bytes(bytes)))
}
pub fn to_be_bytes(&self) -> [u8; 32] {
self.0.atomics().to_be_bytes()
}
pub fn to_le_bytes(&self) -> [u8; 32] {
self.0.atomics().to_le_bytes()
}
}
impl Sub<BigDecimal> for BigDecimal {
type Output = BigDecimal;
fn sub(self, rhs: BigDecimal) -> Self::Output {
Self(self.0 - rhs.0)
}
}
impl Add<BigDecimal> for BigDecimal {
type Output = BigDecimal;
fn add(self, rhs: BigDecimal) -> Self::Output {
Self(self.0 + rhs.0)
}
}
impl Div<BigDecimal> for BigDecimal {
type Output = BigDecimal;
fn div(self, rhs: BigDecimal) -> Self::Output {
BigDecimal(self.0 / rhs.0)
}
}
impl Div<BigInteger> for BigDecimal {
type Output = BigDecimal;
fn div(self, rhs: BigInteger) -> Self::Output {
self / BigDecimal::from(rhs, 0)
}
}
impl Mul<BigDecimal> for BigDecimal {
type Output = BigDecimal;
fn mul(self, rhs: BigDecimal) -> Self::Output {
Self(self.0 * rhs.0)
}
}
impl Mul<BigInteger> for BigDecimal {
type Output = BigDecimal;
fn mul(self, rhs: BigInteger) -> Self::Output {
self * BigDecimal::from(rhs, 0)
}
}
impl AddAssign for BigDecimal {
fn add_assign(&mut self, rhs: Self) {
self.0 += rhs.0;
}
}
impl SubAssign for BigDecimal {
fn sub_assign(&mut self, rhs: Self) {
self.0 -= rhs.0;
}
}
impl MulAssign for BigDecimal {
fn mul_assign(&mut self, rhs: Self) {
self.0 *= rhs.0;
}
}
impl Display for BigDecimal {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
std::fmt::Display::fmt(&self.0, f)
}
}
impl FromStr for BigDecimal {
type Err = StdError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
const DECIMAL_FRACTIONAL: Uint256 = Uint256::new(1_000_000_000_000_000_000);
let mut parts_iter = s.split('.');
let whole_part = parts_iter.next().unwrap(); let whole = whole_part
.parse::<Uint256>()
.map_err(|_| StdError::msg("Error parsing whole"))?;
let mut atomics = whole
.checked_mul(DECIMAL_FRACTIONAL)
.map_err(|_| StdError::msg("Value too big"))?;
if let Some(fractional_part) = parts_iter.next() {
let fractional = fractional_part
.parse::<Uint256>()
.map_err(|_| StdError::msg("Error parsing fractional"))?;
let exp = (Decimal256::DECIMAL_PLACES.checked_sub(fractional_part.len() as u32))
.ok_or_else(|| {
StdError::msg(format!(
"Cannot parse more than {} fractional digits",
Decimal256::DECIMAL_PLACES
))
})?;
let fractional_factor = Uint256::from(10u128).pow(exp);
atomics = atomics
.checked_add(
fractional.checked_mul(fractional_factor).unwrap(),
)
.map_err(|_| StdError::msg("Value too big"))?;
}
if parts_iter.next().is_some() {
return Err(StdError::msg("Unexpected number of dots"));
}
Ok(Self(Decimal256::new(atomics)))
}
}
impl Sum for BigDecimal {
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
iter.fold(Self::zero(), Add::add)
}
}
impl<'a> Sum<&'a BigDecimal> for BigDecimal {
fn sum<I: Iterator<Item = &'a Self>>(iter: I) -> Self {
iter.fold(Self::zero(), |a, b| a + *b)
}
}
#[cfg(test)]
mod tests {
use crate::bigdecimal::BigDecimal;
use crate::biginteger::BigInteger;
use cosmwasm_std::Uint256;
#[test]
fn test_bytes() {
let bigdecimal = BigDecimal::from(BigInteger(Uint256::from(1000000u64)), 0);
assert_eq!(
BigDecimal::from_be_bytes(bigdecimal.to_be_bytes()),
bigdecimal
);
}
}