// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. use alloc::str::FromStr; use core::cmp::{Ord, Ordering}; use core::fmt::{Debug, Formatter}; use anyhow::{anyhow, bail, Result}; use serde::ser::Serializer; use serde::Serialize; use crate::*; pub type BigInt = i128; type BigFloat = scientific::Scientific; const PRECISION: scientific::Precision = scientific::Precision::Digits(100); #[derive(Clone, Debug, PartialEq)] pub struct BigDecimal { d: BigFloat, } impl AsRef for BigDecimal { fn as_ref(&self) -> &BigFloat { &self.d } } impl AsMut for BigDecimal { fn as_mut(&mut self) -> &mut BigFloat { &mut self.d } } impl From for BigDecimal { fn from(value: BigFloat) -> Self { BigDecimal { d: value } } } impl From for BigDecimal { fn from(value: i128) -> Self { BigDecimal { d: Into::::into(value), } } } impl BigDecimal { fn is_integer(&self) -> bool { self.d.decimals() <= 0 } } #[derive(Clone)] pub enum Number { // TODO: maybe specialize for u64, i64, f64 Big(Rc), } impl Debug for Number { fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), core::fmt::Error> { match self { Number::Big(b) => b.d.fmt(f), } } } impl Serialize for Number { fn serialize(&self, serializer: S) -> Result where S: Serializer, { match self { Big(_) => { let s = self.format_decimal(); let v = serde_json::Number::from_str(&s) .map_err(|_| serde::ser::Error::custom("could not serialize big number"))?; v.serialize(serializer) } } } } use Number::*; impl From for Number { fn from(n: BigFloat) -> Self { Self::Big(BigDecimal::from(n).into()) } } impl From for Number { fn from(n: u64) -> Self { BigFloat::from(n).into() } } impl From for Number { fn from(n: usize) -> Self { BigFloat::from(n).into() } } impl From for Number { fn from(n: u128) -> Self { BigFloat::from(n).into() } } impl From for Number { fn from(n: i128) -> Self { BigFloat::from(n).into() } } impl From for Number { fn from(n: i64) -> Self { BigFloat::from(n).into() } } impl From for Number { fn from(n: f64) -> Self { // Reading from float is not precise. Therefore, serialize to string and read. match Self::from_str(&format!("{n}")) { Ok(v) => v, _ => BigFloat::ZERO.into(), } } } impl Number { pub fn as_u128(&self) -> Option { match self { Big(b) if b.is_integer() => match u128::try_from(&b.d) { Ok(v) => Some(v), _ => None, }, _ => None, } } pub fn as_i128(&self) -> Option { match self { Big(b) if b.is_integer() => match i128::try_from(&b.d) { Ok(v) => Some(v), _ => None, }, _ => None, } } pub fn as_u64(&self) -> Option { match self { Big(b) if b.is_integer() => match u64::try_from(&b.d) { Ok(v) => Some(v), _ => None, }, _ => None, } } pub fn as_i64(&self) -> Option { match self { Big(b) if b.is_integer() => match i64::try_from(&b.d) { Ok(v) => Some(v), _ => None, }, _ => None, } } pub fn as_f64(&self) -> Option { match self { Big(b) => { let f = f64::from(&b.d); match BigFloat::try_from(f) { Ok(bf) if bf == b.d => Some(f), _ => None, } } } } pub fn as_big(&self) -> Option> { Some(match self { Big(b) => b.clone(), }) } pub fn to_big(&self) -> Result> { match self.as_big() { Some(b) => Ok(b), _ => bail!("Number::to_big failed"), } } } #[derive(Debug, PartialEq, Eq)] pub struct ParseNumberError; impl FromStr for Number { type Err = ParseNumberError; fn from_str(s: &str) -> Result { if let Ok(v) = BigFloat::from_str(s) { return Ok(v.into()); } Ok(f64::from_str(s).map_err(|_| ParseNumberError)?.into()) } } impl Eq for Number {} impl PartialEq for Number { fn eq(&self, other: &Self) -> bool { match (self, other) { (Big(a), Big(b)) => a.d == b.d, } } } impl Ord for Number { fn cmp(&self, other: &Self) -> Ordering { match (self, other) { (Big(a), Big(b)) => a.d.cmp(&b.d), } } } impl PartialOrd for Number { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } impl Number { pub fn add_assign(&mut self, rhs: &Self) -> Result<()> { *self = self.add(rhs)?; Ok(()) } pub fn add(&self, rhs: &Self) -> Result { match (self, rhs) { (Big(a), Big(b)) => Ok(Big(BigDecimal::from(&a.d + &b.d).into())), } } pub fn sub_assign(&mut self, rhs: &Self) -> Result<()> { *self = self.sub(rhs)?; Ok(()) } pub fn sub(&self, rhs: &Self) -> Result { match (self, rhs) { (Big(a), Big(b)) => Ok(Big(BigDecimal::from(&a.d - &b.d).into())), } } pub fn mul_assign(&mut self, rhs: &Self) -> Result<()> { *self = self.mul(rhs)?; Ok(()) } pub fn mul(&self, rhs: &Self) -> Result { match (self, rhs) { (Big(a), Big(b)) => Ok(Big(BigDecimal::from(&a.d * &b.d).into())), } } pub fn divide(self, rhs: &Self) -> Result { match (self, rhs) { (Big(a), Big(b)) => { let c = a.d.div_truncate(&b.d, PRECISION) .map_err(|e| anyhow!("{e}"))?; Ok(Big(BigDecimal::from(c).into())) } } } pub fn modulo(self, rhs: &Self) -> Result { match (self, rhs) { (Big(a), Big(b)) => { let (_, c) = a.d.div_rem(&b.d).map_err(|e| anyhow!("{e}"))?; Ok(Big(BigDecimal::from(c).into())) } } } pub fn is_integer(&self) -> bool { match self { Big(b) => b.is_integer(), } } pub fn is_positive(&self) -> bool { match self { Big(b) => b.d.is_sign_positive(), } } fn ensure_integers(a: &Number, b: &Number) -> Option<(BigInt, BigInt)> { match (a, b) { (Big(a), Big(b)) if a.is_integer() && b.is_integer() => { match (BigInt::try_from(&a.d), BigInt::try_from(&b.d)) { (Ok(a), Ok(b)) => Some((a, b)), _ => None, } } _ => None, } } fn ensure_integer(&self) -> Option { match self { Big(a) if a.is_integer() => match BigInt::try_from(&a.d) { Ok(v) => Some(v), _ => None, }, _ => None, } } pub fn and(&self, rhs: &Self) -> Option { match Self::ensure_integers(self, rhs) { Some((a, b)) => Some((a & b).into()), _ => None, } } pub fn or(&self, rhs: &Self) -> Option { match Self::ensure_integers(self, rhs) { Some((a, b)) => Some((a | b).into()), _ => None, } } pub fn xor(&self, rhs: &Self) -> Option { match Self::ensure_integers(self, rhs) { Some((a, b)) => Some((a ^ b).into()), _ => None, } } pub fn lsh(&self, rhs: &Self) -> Option { match Self::ensure_integers(self, rhs) { Some((a, b)) => a.checked_shl(b as u32).map(|v| v.into()), _ => None, } } pub fn rsh(&self, rhs: &Self) -> Option { match Self::ensure_integers(self, rhs) { Some((a, b)) => a.checked_shr(b as u32).map(|v| v.into()), _ => None, } } pub fn neg(&self) -> Option { self.ensure_integer().map(|a| (!a).into()) } pub fn abs(&self) -> Number { match self { Big(b) => b.d.clone().abs().into(), } } pub fn floor(&self) -> Number { match self { Big(b) => Big(BigDecimal::from(b.d.round( scientific::Precision::Decimals(0), scientific::Rounding::RoundDown, )) .into()), } } pub fn ceil(&self) -> Number { match self { Big(b) => Big(BigDecimal::from(b.d.round( scientific::Precision::Decimals(0), scientific::Rounding::RoundUp, )) .into()), } } pub fn round(&self) -> Number { match self { Big(b) => Big(BigDecimal::from(b.d.round( scientific::Precision::Decimals(0), scientific::Rounding::RoundHalfAwayFromZero, )) .into()), } } pub fn two_pow(e: i32) -> Result { if e >= 0 { Ok(BigFloat::from(2).powi(e as usize).into()) } else { Number::from(1u64).divide(&BigFloat::from(2).powi(-e as usize).into()) } } pub fn ten_pow(e: i32) -> Result { if e >= 0 { Ok(BigFloat::from(10).powi(e as usize).into()) } else { Number::from(1u64).divide(&BigFloat::from(10).powi(-e as usize).into()) } } pub fn format_bin(&self) -> String { self.ensure_integer() .map(|a| format!("{:b}", a)) .unwrap_or("".to_string()) } pub fn format_octal(&self) -> String { self.ensure_integer() .map(|a| format!("{:o}", a)) .unwrap_or("".to_string()) } pub fn format_scientific(&self) -> String { match self { Big(b) => format!("{}", b.d), } } pub fn format_decimal(&self) -> String { if let Some(u) = self.as_u64() { u.to_string() } else if let Some(i) = self.as_i64() { i.to_string() } else if let Some(f) = self.as_f64() { f.to_string() } else { let s = match self { Big(b) => format!("{}", b.d), }; // Remove trailing e0 if s.ends_with("e0") { return s[..s.len() - 2].to_string(); } // Avoid e notation if full mantissa is written out. let parts: Vec<&str> = s.split('e').collect(); match self { Big(b) => { if b.d.is_sign_positive() { if parts[0].len() == b.d.exponent1() as usize + 2 { return parts[0].replace('.', ""); } } else if parts[0].len() == b.d.exponent1() as usize + 3 { return parts[0].replace('.', ""); } } } s } } pub fn format_decimal_with_width(&self, d: u32) -> String { match self { Big(b) => { let n = Big(BigDecimal::from(b.d.round( scientific::Precision::Decimals(d as isize), scientific::Rounding::RoundHalfAwayFromZero, )) .into()); n.format_decimal() } } } pub fn format_hex(&self) -> String { self.ensure_integer() .map(|a| format!("{:x}", a)) .unwrap_or("".to_string()) } pub fn format_big_hex(&self) -> String { self.ensure_integer() .map(|a| format!("{:X}", a)) .unwrap_or("".to_string()) } } #[cfg(test)] mod test { use crate::number::*; #[test] fn display_number() { let n = Number::from(123456f64); assert_eq!(format!("{}", n.format_decimal()), "123456"); } }