use std::cmp::{self, Ordering};
use std::fmt;
use std::ops;
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct Decimal {
exp: i32,
mantissa: i64,
}
#[derive(Debug)]
pub enum Error {
}
impl Decimal {
pub const MAX: Decimal = Decimal {
exp: 63,
mantissa: i64::MAX,
};
pub const MIN: Decimal = Decimal {
exp: 63,
mantissa: i64::MIN,
};
pub const ONE: Decimal = Decimal {
exp: 0,
mantissa: 1,
};
pub const NEG_ONE: Decimal = Decimal {
exp: 0,
mantissa: -1,
};
pub const ZERO: Decimal = Decimal {
exp: 0,
mantissa: 0,
};
pub const MIN_POSITIVE: Decimal = Decimal {
exp: -63,
mantissa: 1,
};
pub fn new(mantissa: i64, exp: i32) -> Self {
Self::new_unchecked(mantissa, exp.max(-16).min(16))
}
pub fn new_unchecked(mantissa: i64, exp: i32) -> Self {
Self { exp, mantissa }.normalize()
}
fn exp_is_maxed_out(&self) -> bool {
self.exp() >= 16 || self.exp() <= -16
}
fn normalize(&self) -> Self {
let mut me = *self;
while me.mantissa % 10 == 0 && !me.exp_is_maxed_out() {
me.mantissa /= 10;
me.exp += 1;
}
me
}
pub const fn exp(&self) -> i32 {
self.exp
}
pub const fn mantissa(&self) -> i64 {
self.mantissa
}
pub fn signum(&self) -> Self {
const SIGNUMS: &[Decimal; 3] = &[Decimal::NEG_ONE, Decimal::ZERO, Decimal::ONE];
let index = (self.mantissa().signum() + 1) as usize;
SIGNUMS[index]
}
pub fn abs(&self) -> Self {
debug_assert_ne!(self.mantissa(), i64::MIN);
Self {
exp: self.exp(),
mantissa: self.mantissa().abs(),
}
}
pub fn checked_abs(&self) -> Option<Self> {
let mantissa = self.mantissa().checked_abs()?;
Some(Self {
exp: self.exp(),
mantissa,
})
}
pub fn checked_add(&self, other: Decimal) -> Option<Self> {
let exp_diff = self.exp() - other.exp();
let mantissa_1 = self.mantissa();
let mantissa_2 = other
.mantissa()
.checked_mul(10i64.checked_pow(exp_diff as u32)?)?;
Some(Self {
exp: self.exp(),
mantissa: mantissa_1.checked_add(mantissa_2)?,
})
}
pub fn checked_sub(&self, other: Decimal) -> Option<Self> {
let neg_other = other.checked_neg()?;
self.checked_add(neg_other)
}
pub fn checked_mul(&self, other: Decimal) -> Option<Self> {
let exp = self.exp() + other.exp();
let mantissa = self.mantissa().checked_mul(other.mantissa())?;
Some(Self { exp, mantissa })
}
pub fn checked_div(self, _other: Decimal) -> Option<Self> {
unimplemented!()
}
pub fn checked_neg(self) -> Option<Self> {
let mantissa = self.mantissa().checked_neg()?;
let exp = self.exp();
Some(Self { exp, mantissa })
}
pub const fn is_negative(&self) -> bool {
self.mantissa().is_negative()
}
pub const fn is_positive(&self) -> bool {
self.mantissa().is_positive()
}
pub fn pow(&self, exp: i32) -> Self {
match exp.signum() {
0 => Self::ONE,
1 if exp % 2 == 0 => (*self * *self).pow(exp / 2),
1 => *self * ((*self * *self).pow(exp / 2)),
_ => todo!(),
}
}
pub fn truncate(&self) -> Self {
let mut me = *self;
me.mantissa -= me.mantissa() % 10i64.pow(me.exp().abs() as u32);
me.normalize()
}
pub fn fract(&self) -> Self {
let mut me = *self;
me.mantissa %= 10i64.pow(me.exp().abs() as u32);
me
}
pub fn pow_of_ten(&self) -> i64 {
10i64.pow(self.exp().abs() as u32)
}
pub fn to_be_bytes(self) -> [u8; 9] {
let mut bytes = [0u8; 9];
bytes[0] = (self.exp() + 16) as u8;
bytes[1..9].clone_from_slice(&self.mantissa().to_be_bytes()[..]);
bytes
}
pub fn from_be_bytes(mut bytes: [u8; 9]) -> Self {
let mut mantissa_bytes = [0u8; 8];
mantissa_bytes.clone_from_slice(&mut bytes[1..]);
Self {
exp: (bytes[0] as i32) - 16,
mantissa: i64::from_be_bytes(mantissa_bytes),
}
}
}
impl Default for Decimal {
fn default() -> Self {
Self::ZERO
}
}
impl fmt::Display for Decimal {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.is_negative() {
write!(f, "-")?;
}
if self.mantissa().abs() < self.pow_of_ten() {
write!(f, "0")?;
} else {
let digits = self.mantissa().abs().to_string();
let len = digits.chars().count() as i32 + self.exp();
for digit in digits
.chars()
.chain(std::iter::repeat('0'))
.take(len as usize)
{
write!(f, "{}", digit)?;
}
}
if self.fract().is_positive() {
write!(f, ".{}", "")?;
let digits = self.fract().mantissa().to_string();
let digits_len = digits.chars().count();
let len = -self.exp();
debug_assert!(len > 0);
for digit in std::iter::repeat('0')
.take(len as usize - digits_len)
.chain(digits.chars())
{
write!(f, "{}", digit)?;
}
}
Ok(())
}
}
impl cmp::PartialOrd for Decimal {
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
Some(self.cmp(other))
}
}
impl cmp::Ord for Decimal {
fn cmp(&self, other: &Self) -> cmp::Ordering {
let cmp_sign = self.mantissa().signum().cmp(&other.mantissa().signum());
if cmp_sign != Ordering::Equal {
return cmp_sign;
}
let cmp_exp = self.exp().signum().cmp(&other.exp().signum());
let cmp_mantissa = self.mantissa().cmp(&other.mantissa());
cmp_exp.then(cmp_mantissa)
}
}
impl ops::Neg for Decimal {
type Output = Self;
fn neg(self) -> Self::Output {
Self {
exp: self.exp(),
mantissa: -self.mantissa(),
}
}
}
impl ops::Add for Decimal {
type Output = Self;
fn add(self, other: Self::Output) -> Self::Output {
let a = self.normalize();
let b = other.normalize();
Decimal {
exp: a.exp(),
mantissa: a.mantissa() - b.mantissa(),
}
.normalize()
}
}
impl ops::Sub for Decimal {
type Output = Self;
fn sub(self, other: Self::Output) -> Self::Output {
let a = self.normalize();
let b = other.normalize();
Decimal {
exp: a.exp(),
mantissa: a.mantissa() - b.mantissa(),
}
.normalize()
}
}
impl ops::Mul for Decimal {
type Output = Self;
fn mul(self, other: Self) -> Self {
let exp = self.exp() + other.exp();
let mantissa = self.mantissa() * other.mantissa();
Decimal { exp, mantissa }
}
}