use std::cmp::Ordering::{self, *};
use std::f64::consts::{E, LN_10, LOG2_10, PI};
use std::fmt::{Display, Formatter, Result};
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
mod macros;
#[cfg(test)]
mod test;
pub const MAX_SAFE_INTEGER: f64 = 9007199254740991.0;
pub const MAX_SIGNIFICANT_DIGITS: u32 = 17;
pub const EXP_LIMIT: f64 = 1.79e308;
pub const ROUND_TOLERANCE: f64 = f64::EPSILON;
pub const NUMBER_EXP_MIN: i32 = -324;
pub const NUMBER_EXP_MAX: i32 = 308;
pub const LENGTH: usize = (NUMBER_EXP_MAX - NUMBER_EXP_MIN + 1) as usize;
lazy_static::lazy_static! {
pub static ref CACHED_POWERS : [f64; LENGTH] = {
let mut arr = [0.0; LENGTH];
for (i, item) in &mut arr.iter_mut().enumerate() {
*item = 10.0_f64.powi((i as i32) + NUMBER_EXP_MIN);
}
arr
};
}
pub fn pad_end(string: String, max_length: u32, fill_string: String) -> String {
if f32::is_nan(max_length as f32) || f32::is_infinite(max_length as f32) {
return string;
}
let length = string.chars().count() as u32;
if length >= max_length {
return string;
}
let mut filled = fill_string;
if filled.is_empty() {
filled = String::from(" ");
}
let fill_len = max_length - length;
while filled.chars().count() < fill_len as usize {
filled = format!("{}{}", filled, filled);
}
let truncated = if filled.chars().count() > fill_len as usize {
String::from(&filled.as_str()[0..(fill_len as usize)])
} else {
filled
};
return string + truncated.as_str();
}
pub fn to_fixed(num: f64, places: u32) -> String {
format!("{:.*}", places as usize, num)
}
pub fn to_fixed_num(num: f64, places: u32) -> f64 {
to_fixed(num, places).parse::<f64>().unwrap()
}
fn power_of_10(power: i32) -> f64 {
CACHED_POWERS[(power - NUMBER_EXP_MIN) as usize]
}
pub fn from_mantissa_exponent_no_normalize(mantissa: f64, exponent: f64) -> Decimal {
Decimal { mantissa, exponent }
}
pub fn from_mantissa_exponent(mantissa: f64, exponent: f64) -> Decimal {
if !f64::is_finite(mantissa) || !f64::is_finite(exponent) {
return Decimal {
mantissa: f64::NAN,
exponent: f64::NAN,
};
}
let decimal = from_mantissa_exponent_no_normalize(mantissa, exponent);
decimal.normalize()
}
#[derive(Clone, Copy, Debug)]
pub struct Decimal {
mantissa: f64,
exponent: f64,
}
impl Display for Decimal {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
if f64::is_nan(self.mantissa) || f64::is_nan(self.exponent) {
return write!(f, "NaN");
} else if self.exponent >= EXP_LIMIT {
return if self.mantissa > 0.0 {
write!(f, "Infinity")
} else {
write!(f, "-Infinity")
};
} else if self.exponent <= -EXP_LIMIT || self.mantissa == 0.0 {
return write!(f, "0");
} else if self.exponent < 21.0 && self.exponent > -7.0 {
return if let Some(places) = f.precision() {
write!(f, "{:.*}", places, self.to_number().to_string())
} else {
write!(f, "{}", self.to_number().to_string())
};
}
let form = if let Some(places) = f.precision() {
self.to_exponential(places as u32)
} else {
self.to_exponential(16)
};
write!(f, "{}", form)
}
}
impl Add<Decimal> for Decimal {
type Output = Decimal;
fn add(self, decimal: Decimal) -> Decimal {
if self.mantissa == 0.0 {
return decimal;
}
if decimal.mantissa == 0.0 {
return self;
}
let bigger_decimal;
let smaller_decimal;
if self.exponent >= decimal.exponent {
bigger_decimal = self;
smaller_decimal = decimal;
} else {
bigger_decimal = decimal;
smaller_decimal = self;
}
if bigger_decimal.exponent - smaller_decimal.exponent > MAX_SIGNIFICANT_DIGITS as f64 {
return bigger_decimal;
}
from_mantissa_exponent(
(1e14 * bigger_decimal.mantissa)
+ 1e14 * &smaller_decimal.mantissa * power_of_10((smaller_decimal.exponent - bigger_decimal.exponent) as i32),
bigger_decimal.exponent - 14.0,
)
}
}
impl Add<&Decimal> for Decimal {
type Output = Decimal;
fn add(self, decimal: &Decimal) -> Decimal {
self + *decimal
}
}
impl Add<Decimal> for &Decimal {
type Output = Decimal;
fn add(self, decimal: Decimal) -> Decimal {
*self + decimal
}
}
impl Add<&Decimal> for &Decimal {
type Output = Decimal;
fn add(self, decimal: &Decimal) -> Decimal {
*self + *decimal
}
}
impl AddAssign<&Decimal> for Decimal {
fn add_assign(&mut self, rhs: &Decimal) {
*self = *self + rhs;
}
}
impl AddAssign<Decimal> for Decimal {
fn add_assign(&mut self, rhs: Decimal) {
*self = *self + rhs;
}
}
impl Sub<&Decimal> for &Decimal {
type Output = Decimal;
fn sub(self, decimal: &Decimal) -> Decimal {
*self - *decimal
}
}
impl Sub<&Decimal> for Decimal {
type Output = Decimal;
fn sub(self, decimal: &Decimal) -> Decimal {
self - *decimal
}
}
impl Sub<Decimal> for &Decimal {
type Output = Decimal;
fn sub(self, decimal: Decimal) -> Decimal {
*self - decimal
}
}
impl Sub<Decimal> for Decimal {
type Output = Decimal;
#[allow(clippy::suspicious_arithmetic_impl)]
fn sub(self, decimal: Decimal) -> Decimal {
self + decimal.neg()
}
}
impl SubAssign<&Decimal> for Decimal {
fn sub_assign(&mut self, rhs: &Decimal) {
*self = *self - rhs;
}
}
impl SubAssign<Decimal> for Decimal {
fn sub_assign(&mut self, rhs: Decimal) {
*self = *self - rhs;
}
}
impl Mul<Decimal> for Decimal {
type Output = Decimal;
fn mul(self, decimal: Decimal) -> Decimal {
from_mantissa_exponent(self.mantissa * decimal.mantissa, self.exponent + decimal.exponent)
}
}
impl Mul<&Decimal> for Decimal {
type Output = Decimal;
fn mul(self, decimal: &Decimal) -> Decimal {
self * *decimal
}
}
impl Mul<Decimal> for &Decimal {
type Output = Decimal;
fn mul(self, decimal: Decimal) -> Decimal {
*self * decimal
}
}
impl Mul<&Decimal> for &Decimal {
type Output = Decimal;
fn mul(self, decimal: &Decimal) -> Decimal {
*self * *decimal
}
}
impl MulAssign<&Decimal> for Decimal {
fn mul_assign(&mut self, rhs: &Decimal) {
*self = *self * rhs;
}
}
impl MulAssign<Decimal> for Decimal {
fn mul_assign(&mut self, rhs: Decimal) {
*self = *self * rhs;
}
}
impl Div<Decimal> for Decimal {
type Output = Decimal;
#[allow(clippy::suspicious_arithmetic_impl)]
fn div(self, decimal: Decimal) -> Decimal {
self * decimal.recip()
}
}
impl Div<&Decimal> for Decimal {
type Output = Decimal;
fn div(self, decimal: &Decimal) -> Decimal {
self / *decimal
}
}
impl Div<Decimal> for &Decimal {
type Output = Decimal;
fn div(self, decimal: Decimal) -> Decimal {
*self / decimal
}
}
impl Div<&Decimal> for &Decimal {
type Output = Decimal;
fn div(self, decimal: &Decimal) -> Decimal {
*self / *decimal
}
}
impl DivAssign<&Decimal> for Decimal {
fn div_assign(&mut self, rhs: &Decimal) {
*self = *self / rhs;
}
}
impl DivAssign<Decimal> for Decimal {
fn div_assign(&mut self, rhs: Decimal) {
*self = *self / rhs;
}
}
impl Neg for &Decimal {
type Output = Decimal;
fn neg(self) -> Decimal {
from_mantissa_exponent_no_normalize(-self.mantissa, self.exponent)
}
}
impl Neg for Decimal {
type Output = Decimal;
fn neg(self) -> Decimal {
let decimal = &self.clone();
from_mantissa_exponent_no_normalize(-decimal.mantissa, decimal.exponent)
}
}
impl PartialOrd for Decimal {
fn partial_cmp(&self, decimal: &Self) -> Option<Ordering> {
if f64::is_nan(self.mantissa) || f64::is_nan(self.exponent) || f64::is_nan(decimal.mantissa) || f64::is_nan(decimal.exponent) {
None
} else if (f64::is_infinite(self.mantissa) && self.mantissa.is_sign_negative())
|| (f64::is_infinite(decimal.mantissa) && decimal.mantissa.is_sign_positive())
{
Some(Less)
} else if (f64::is_infinite(self.mantissa) && self.mantissa.is_sign_negative())
|| (f64::is_infinite(decimal.mantissa) && decimal.mantissa.is_sign_positive())
{
Some(Greater)
} else if self.mantissa == 0.0 {
if decimal.mantissa == 0.0 {
Some(Equal)
} else if decimal.mantissa < 0.0 {
Some(Greater)
} else {
Some(Less)
}
} else if decimal.mantissa == 0.0 {
if self.mantissa < 0.0 {
Some(Less)
} else {
Some(Greater)
}
} else if self.mantissa > 0.0 {
if self.exponent > decimal.exponent || decimal.mantissa < 0.0 {
Some(Greater)
} else if self.exponent < decimal.exponent {
Some(Less)
} else if self.mantissa > decimal.mantissa {
Some(Greater)
} else if self.mantissa < decimal.mantissa {
Some(Less)
} else {
Some(Equal)
}
} else if self.exponent > decimal.exponent || decimal.mantissa > 0.0 {
Some(Less)
} else if self.mantissa > decimal.mantissa || self.exponent < decimal.exponent {
Some(Greater)
} else if self.mantissa < decimal.mantissa {
Some(Less)
} else {
Some(Equal)
}
}
fn lt(&self, other: &Decimal) -> bool {
self.partial_cmp(other).map(Ordering::is_lt).unwrap_or(false)
}
fn le(&self, other: &Decimal) -> bool {
self.partial_cmp(other).map(Ordering::is_le).unwrap_or(false)
}
fn gt(&self, other: &Decimal) -> bool {
self.partial_cmp(other).map(Ordering::is_gt).unwrap_or(false)
}
fn ge(&self, other: &Decimal) -> bool {
self.partial_cmp(other).map(Ordering::is_ge).unwrap_or(false)
}
}
impl PartialEq<Decimal> for Decimal {
fn eq(&self, decimal: &Decimal) -> bool {
self.mantissa == decimal.mantissa && self.exponent == decimal.exponent
}
}
impl Eq for Decimal {}
impl From<&str> for Decimal {
#[allow(dead_code)]
fn from(string: &str) -> Decimal {
return if string.find('e') != None {
let parts: Vec<&str> = string.split('e').collect();
let decimal = Decimal {
mantissa: String::from(parts[0]).parse().unwrap(),
exponent: String::from(parts[1]).parse().unwrap(),
};
decimal.normalize()
} else if string == "NaN" {
Decimal {
mantissa: f64::NAN,
exponent: f64::NAN,
}
} else {
Decimal::new(String::from(string).parse().unwrap())
};
}
}
impl_from!(i8);
impl_from!(i16);
impl_from!(i32);
impl_from!(i64);
impl_from!(i128);
impl_from!(isize);
impl_from!(u8);
impl_from!(u16);
impl_from!(u32);
impl_from!(u64);
impl_from!(u128);
impl_from!(usize);
impl_from!(f32);
impl_from!(f64);
impl Decimal {
pub fn new(value: f64) -> Decimal {
if f64::is_nan(value) {
return Decimal {
mantissa: f64::NAN,
exponent: f64::NAN,
};
} else if value == 0.0 {
return Decimal {
mantissa: 0.0,
exponent: 0.0,
};
} else if f64::is_infinite(value) && f64::is_sign_positive(value) {
return Decimal {
mantissa: 1.0,
exponent: EXP_LIMIT,
};
} else if f64::is_infinite(value) && f64::is_sign_negative(value) {
return Decimal {
mantissa: -1.0,
exponent: EXP_LIMIT,
};
}
let e = value.abs().log10().floor();
let m = if (e - NUMBER_EXP_MIN as f64).abs() < f64::EPSILON {
value * 10.0 / ("1e".to_owned() + (NUMBER_EXP_MIN + 1).to_string().as_str()).parse::<f64>().unwrap()
} else {
let power_10 = power_of_10(e as i32);
((value / power_10) * 1000000000000000.0).round() / 1000000000000000.0
};
let decimal = Decimal { mantissa: m, exponent: e };
decimal.normalize()
}
fn normalize(&self) -> Decimal {
if self.mantissa >= 1.0 && self.mantissa < 10.0 {
return *self;
} else if self.mantissa == 0.0 {
return Decimal {
mantissa: 0.0,
exponent: 0.0,
};
}
let temp_exponent = self.mantissa.abs().log10().floor();
Decimal {
mantissa: if (temp_exponent as i32) == NUMBER_EXP_MIN {
self.mantissa * 10.0 / 1e-323
} else {
self.mantissa / power_of_10(temp_exponent as i32)
},
exponent: self.exponent + temp_exponent,
}
}
pub fn to_number(&self) -> f64 {
if !f64::is_finite(self.exponent) {
return f64::NAN;
}
if self.exponent > NUMBER_EXP_MAX as f64 {
return if self.mantissa > 0.0 { f64::INFINITY } else { f64::NEG_INFINITY };
}
if self.exponent < NUMBER_EXP_MIN as f64 {
return 0.0;
}
if (self.exponent - NUMBER_EXP_MIN as f64).abs() < f64::EPSILON {
return if self.mantissa > 0.0 { 5e-324 } else { -5e-324 };
}
let result: f64 = self.mantissa * power_of_10(self.exponent as i32);
if !f64::is_finite(result) || self.exponent < 0.0 {
return result;
}
let result_rounded = result.round();
if (result_rounded - result).abs() < ROUND_TOLERANCE {
return result_rounded;
}
result
}
pub fn to_exponential(&self, mut places: u32) -> String {
if f64::is_nan(self.mantissa) || f64::is_nan(self.exponent) {
return String::from("NaN");
} else if self.exponent >= EXP_LIMIT {
return if self.mantissa > 0.0 {
String::from("Infinity")
} else {
String::from("-Infinity")
};
}
let tmp = pad_end(String::from("."), places + 1, String::from("0"));
if self.exponent <= -EXP_LIMIT || self.mantissa == 0.0 {
let str = if places > 0 { tmp.as_str() } else { "" };
return "0".to_owned() + str + "e+0";
} else if !f32::is_finite(places as f32) {
places = MAX_SIGNIFICANT_DIGITS;
}
let len = places + 1;
let num_digits = self.mantissa.abs().log10().max(1.0) as u32;
let rounded = (self.mantissa * 10.0_f64.powi(len as i32 - num_digits as i32)).round() * 10.0_f64.powi(num_digits as i32 - len as i32);
return to_fixed(rounded, 0_u32.max(len - num_digits))
+ "e" + if self.exponent >= 0.0 { "+" } else { "" }
+ self.exponent.to_string().as_str();
}
pub fn to_fixed(&self, places: u32) -> String {
if f64::is_nan(self.mantissa) || f64::is_nan(self.exponent) {
return String::from("NaN");
} else if self.exponent >= EXP_LIMIT {
return if self.mantissa > 0.0 {
String::from("Infinity")
} else {
String::from("-Infinity")
};
}
let tmp = pad_end(String::from("."), places + 1, String::from("0"));
if self.exponent <= -EXP_LIMIT || self.mantissa == 0.0 {
let str = if places > 0 { tmp.as_str() } else { "" };
return "0".to_owned() + str;
} else if self.exponent >= MAX_SIGNIFICANT_DIGITS as f64 {
let str = pad_end(
self.mantissa.to_string().replace(".", ""),
(self.exponent + 1.0) as u32,
String::from("0"),
) + if places > 0 { tmp.as_str() } else { "" };
return str;
}
to_fixed(self.to_number(), places)
}
pub fn to_precision(&self, places: u32) -> String {
if self.exponent <= -7.0 {
return self.to_exponential(places - 1);
}
if (places as f64) > self.exponent {
return self.to_fixed((places as f64 - self.exponent - 1.0) as u32);
}
self.to_exponential(places - 1)
}
pub fn mantissa_with_decimal_places(&self, places: u32) -> f64 {
if f64::is_nan(self.mantissa) || f64::is_nan(self.exponent) {
return f64::NAN;
} else if self.mantissa == 0.0 {
return 0.0;
}
let len = places + 1;
let num_digits = self.mantissa.abs().log10().ceil() as u32;
let rounded = (self.mantissa * 10.0_f64.powi(len as i32 - num_digits as i32)).round() * 10.0_f64.powi(num_digits as i32 - len as i32);
to_fixed_num(rounded, 0.max(len - num_digits))
}
pub fn abs(&self) -> Decimal {
from_mantissa_exponent_no_normalize(self.mantissa.abs(), self.exponent)
}
pub fn sgn(&self) -> i32 {
if self.mantissa.is_sign_positive() {
1
} else if self.mantissa.is_sign_negative() {
-1
} else {
0
}
}
pub fn sign(&self) -> i32 {
self.sgn()
}
pub fn round(&self) -> Decimal {
if self.exponent < -1.0 {
return Decimal::new(0.0);
} else if self.exponent < MAX_SIGNIFICANT_DIGITS as f64 {
return Decimal::new(self.to_number().round());
}
*self
}
pub fn trunc(&self) -> Decimal {
if self.exponent < 0.0 {
return Decimal::new(0.0);
} else if self.exponent < MAX_SIGNIFICANT_DIGITS as f64 {
return Decimal::new(self.to_number().trunc());
}
*self
}
pub fn floor(&self) -> Decimal {
if self.exponent < -1.0 {
return if self.sign() >= 0 { Decimal::new(0.0) } else { Decimal::new(-1.0) };
} else if self.exponent < MAX_SIGNIFICANT_DIGITS as f64 {
return Decimal::new(self.to_number().floor());
}
*self
}
pub fn ceil(&self) -> Decimal {
if self.exponent < -1.0 {
return if self.sign() > 0 { Decimal::new(1.0) } else { Decimal::new(0.0) };
} else if self.exponent < MAX_SIGNIFICANT_DIGITS as f64 {
return Decimal::new(self.to_number().ceil());
}
*self
}
pub fn recip(&self) -> Decimal {
from_mantissa_exponent(1.0 / self.mantissa, -self.exponent)
}
pub fn reciprocal(&self) -> Decimal {
self.recip()
}
pub fn reciprocate(&self) -> Decimal {
self.recip()
}
pub fn compare(&self, decimal: &Decimal) -> Option<Ordering> {
self.partial_cmp(decimal)
}
pub fn equals(&self, decimal: &Decimal) -> bool {
self.eq(decimal)
}
pub fn neq(&self, decimal: &Decimal) -> bool {
!self.eq(decimal)
}
pub fn not_equals(&self, decimal: &Decimal) -> bool {
!self.neq(decimal)
}
pub fn lt(&self, decimal: &Decimal) -> bool {
self < decimal
}
pub fn lte(&self, decimal: &Decimal) -> bool {
self <= decimal
}
pub fn gt(&self, decimal: &Decimal) -> bool {
self > decimal
}
pub fn gte(&self, decimal: &Decimal) -> bool {
self >= decimal
}
pub fn less_than_or_equal_to(&self, other: &Decimal) -> bool {
self.lte(other)
}
pub fn less_than(&self, other: &Decimal) -> bool {
self.lt(other)
}
pub fn greater_than_or_equal_to(&self, other: &Decimal) -> bool {
self.gte(other)
}
pub fn greater_than(&self, other: &Decimal) -> bool {
self.gt(other)
}
pub fn max(&self, other: &Decimal) -> Decimal {
if self > other {
self.clone()
} else {
other.clone()
}
}
pub fn min(&self, other: &Decimal) -> Decimal {
if self < other {
self.clone()
} else {
other.clone()
}
}
pub fn clamp(&self, min: &Decimal, max: &Decimal) -> Decimal {
self.min(max).max(min)
}
pub fn cmp_tolerance(&self, decimal: &Decimal, tolerance: &Decimal) -> Option<Ordering> {
if self.eq_tolerance(decimal, tolerance) {
Some(Equal)
} else {
self.partial_cmp(decimal)
}
}
pub fn compare_tolerance(&self, decimal: &Decimal, tolerance: &Decimal) -> Option<Ordering> {
self.cmp_tolerance(decimal, tolerance)
}
pub fn eq_tolerance(&self, decimal: &Decimal, tolerance: &Decimal) -> bool {
(self - decimal).abs().lte(&self.abs().max(&(decimal.abs() * tolerance)))
}
pub fn equals_tolerance(&self, decimal: &Decimal, tolerance: &Decimal) -> bool {
self.eq_tolerance(decimal, tolerance)
}
pub fn neq_tolerance(&self, decimal: &Decimal, tolerance: &Decimal) -> bool {
!self.eq_tolerance(decimal, tolerance)
}
pub fn not_equals_tolerance(&self, decimal: &Decimal, tolerance: &Decimal) -> bool {
self.neq_tolerance(decimal, tolerance)
}
pub fn lt_tolerance(&self, decimal: &Decimal, tolerance: &Decimal) -> bool {
!self.eq_tolerance(decimal, tolerance) && self.lt(decimal)
}
pub fn lte_tolerance(&self, decimal: &Decimal, tolerance: &Decimal) -> bool {
self.eq_tolerance(decimal, tolerance) || self.lt(decimal)
}
pub fn gt_tolerance(&self, decimal: &Decimal, tolerance: &Decimal) -> bool {
!self.eq_tolerance(decimal, tolerance) && self.gt(decimal)
}
pub fn gte_tolerance(&self, decimal: &Decimal, tolerance: &Decimal) -> bool {
self.eq_tolerance(decimal, tolerance) || self.gt(decimal)
}
pub fn log10(&self) -> f64 {
self.exponent + self.mantissa.log10()
}
pub fn abs_log10(&self) -> f64 {
self.exponent + self.mantissa.abs().log10()
}
pub fn p_log10(&self) -> f64 {
if self.mantissa <= 0.0 || self.exponent < 0.0 {
0.0
} else {
self.log10()
}
}
pub fn log(&self, base: f64) -> f64 {
LN_10 / base.ln() * self.log10()
}
pub fn logarithm(&self, base: f64) -> f64 {
self.log(base)
}
pub fn log2(&self) -> f64 {
LOG2_10 * self.log10()
}
pub fn ln(&self) -> f64 {
LN_10 * self.log10()
}
pub fn pow(&self, decimal: &Decimal) -> Decimal {
let number = decimal.to_number();
let temp = self.exponent * number;
let mut new_mantissa;
if temp < MAX_SAFE_INTEGER {
new_mantissa = self.mantissa.powf(number);
if f64::is_finite(new_mantissa) && new_mantissa != 0.0 {
return from_mantissa_exponent(new_mantissa, temp);
}
}
let new_exponent = temp.trunc();
let residue = temp - new_exponent;
new_mantissa = 10.0_f64.powf(number * self.mantissa.log10() + residue);
if f64::is_finite(new_mantissa) && new_mantissa != 0.0 {
return from_mantissa_exponent(new_mantissa, new_exponent);
}
let result = Decimal::new(10.0).pow(&Decimal::new(number * self.abs_log10()));
if self.sign() == -1 && (number % 2.0 - 1.0).abs() < f64::EPSILON {
return result.neg();
}
result
}
pub fn pow_base(&self, decimal: &Decimal) -> Decimal {
decimal.pow(self)
}
pub fn factorial(&self) -> Decimal {
let n = self.to_number() + 1.0;
Decimal::new(n / E * (n * f64::sinh(1.0 / n) + 1.0 / (810.0 * n.powi(6)))).pow(&Decimal::new(n)) * Decimal::new(f64::sqrt(2.0 * PI / n))
}
pub fn exp(&self) -> Decimal {
let number = self.to_number();
if -706.0 < number && number < 709.0 {
return Decimal::new(f64::exp(number));
}
Decimal::new(E).pow(self)
}
pub fn sqr(&self) -> Decimal {
from_mantissa_exponent(self.mantissa.powi(2), self.exponent * 2.0)
}
pub fn sqrt(&self) -> Decimal {
if self.mantissa < 0.0 {
return Decimal::new(f64::NAN);
} else if self.exponent % 2.0 != 0.0 {
return from_mantissa_exponent(f64::sqrt(self.mantissa) * 3.16227766016838, (self.exponent / 2.0).floor());
}
from_mantissa_exponent(f64::sqrt(self.mantissa), (self.exponent / 2.0).floor())
}
pub fn cube(&self) -> Decimal {
from_mantissa_exponent(self.mantissa.powi(3), self.exponent * 3.0)
}
pub fn cbrt(&self) -> Decimal {
let mut sign = 1;
let mut mantissa = self.mantissa;
if mantissa < 0.0 {
sign = -1;
mantissa = -mantissa;
}
let new_mantissa = sign as f64 * mantissa.powf((1 / 3) as f64);
let remainder = (self.exponent % 3.0) as i32;
if remainder == 1 || remainder == -1 {
return from_mantissa_exponent(new_mantissa * 2.154_434_690_031_884, (self.exponent / 3.0).floor());
}
if remainder != 0 {
return from_mantissa_exponent(new_mantissa * 4.641_588_833_612_779, (self.exponent / 3.0).floor());
}
from_mantissa_exponent(new_mantissa, (self.exponent / 3.0).floor())
}
pub fn sinh(&self) -> Decimal {
(self.exp() - self.neg().exp()) / Decimal::new(2.0)
}
pub fn cosh(&self) -> Decimal {
(self.exp() + self.neg().exp()) / Decimal::new(2.0)
}
pub fn tanh(&self) -> Decimal {
self.sinh() / self.cosh()
}
pub fn asinh(&self) -> f64 {
(self + (self.sqr() + Decimal::new(1.0)).sqrt()).ln()
}
pub fn acosh(&self) -> f64 {
(self + (self.sqr() - Decimal::new(1.0)).sqrt()).ln()
}
pub fn atanh(&self) -> f64 {
if self.abs().gte(&Decimal::new(1.0)) {
return f64::NAN;
}
((Decimal::new(1.0) + self) / (Decimal::new(1.0) - self)).ln() / 2.0
}
pub fn dp(&self) -> i32 {
if !f64::is_finite(self.mantissa) {
return f64::NAN as i32;
} else if self.exponent >= MAX_SIGNIFICANT_DIGITS as f64 {
return 0;
}
let mantissa = self.mantissa;
let mut places = -self.exponent as i32;
let mut e = 1.0;
while (mantissa * e).round().abs() / e - mantissa > ROUND_TOLERANCE {
e *= 10.0;
places += 1;
}
if places > 0 {
places
} else {
0
}
}
pub fn decimal_places(&self) -> i32 {
self.dp()
}
pub fn ascension_penalty(&self, ascensions: f64) -> Decimal {
if ascensions == 0.0 {
return *self;
}
self.pow(&Decimal::new(10.0_f64.powf(-ascensions)))
}
pub fn egg(&self) -> Decimal {
self + Decimal::new(9.0)
}
}
pub fn afford_geometric_series(resources_available: &Decimal, price_start: &Decimal, price_ratio: &Decimal, current_owned: &Decimal) -> Decimal {
let actual_start = price_start * price_ratio.pow(current_owned);
Decimal::new((resources_available / actual_start * (price_ratio - Decimal::new(1.0)) + Decimal::new(1.0)).log10() / price_ratio.log10()).floor()
}
pub fn sum_geometric_series(num_items: &Decimal, price_start: &Decimal, price_ratio: &Decimal, current_owned: &Decimal) -> Decimal {
price_start * price_ratio.pow(current_owned) * (Decimal::new(1.0) - price_ratio.pow(num_items)) / (Decimal::new(1.0) - price_ratio)
}
pub fn afford_arithmetic_series(resources_available: &Decimal, price_start: &Decimal, price_add: &Decimal, current_owned: &Decimal) -> Decimal {
let actual_start = price_start + (current_owned * price_add);
let b = actual_start - (price_add / Decimal::new(2.0));
let b2 = b.pow(&Decimal::new(2.0));
(b.neg() + ((b2 + ((price_add * resources_available) * Decimal::new(2.0))).sqrt() / price_add)).floor()
}
pub fn sum_arithmetic_series(num_items: &Decimal, price_start: &Decimal, price_add: &Decimal, current_owned: &Decimal) -> Decimal {
let actual_start = price_start + (current_owned * price_add);
num_items / Decimal::new(2.0) * (actual_start * Decimal::new(2.0) + (num_items - Decimal::new(1.0)) + num_items - Decimal::new(1.0)) * price_add
}
pub fn efficiency_of_purchase(cost: &Decimal, current_rp_s: &Decimal, delta_rp_s: &Decimal) -> Decimal {
cost / (current_rp_s + (cost / delta_rp_s))
}