#![allow(dead_code)]
use std::fmt::Debug;
use std::ops::{Add, Div, Mul, Neg, Sub};
#[cfg(feature = "highprec")]
use rug::ops::Pow;
#[cfg(feature = "highprec")]
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
pub enum ParseError {
#[error("Invalid float literal: {0}")]
InvalidFloatLiteral(String),
}
#[cfg(feature = "highprec")]
pub const DEFAULT_PRECISION: u32 = 256;
pub trait RiesFloat:
Clone
+ PartialOrd
+ Debug
+ Send
+ Sync
+ Add<Output = Self>
+ Sub<Output = Self>
+ Mul<Output = Self>
+ Div<Output = Self>
+ Neg<Output = Self>
{
fn zero() -> Self;
fn one() -> Self;
fn from_f64(v: f64) -> Self;
fn to_f64(&self) -> f64;
fn from_u8(v: u8) -> Self {
Self::from_f64(v as f64)
}
fn sqrt(self) -> Self;
fn square(self) -> Self
where
Self: Clone,
{
self.clone() * self
}
fn ln(self) -> Self;
fn exp(self) -> Self;
fn sin(self) -> Self;
fn cos(self) -> Self;
fn tan(self) -> Self;
fn pow(self, exp: Self) -> Self;
fn abs(self) -> Self;
fn is_nan(&self) -> bool;
fn is_infinite(&self) -> bool;
fn is_finite(&self) -> bool {
!self.is_nan() && !self.is_infinite()
}
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering>
where
Self: Sized,
{
if self.is_nan() || other.is_nan() {
None
} else {
PartialOrd::partial_cmp(self, other)
}
}
}
impl RiesFloat for f64 {
#[inline]
fn zero() -> Self {
0.0
}
#[inline]
fn one() -> Self {
1.0
}
#[inline]
fn from_f64(v: f64) -> Self {
v
}
#[inline]
fn to_f64(&self) -> f64 {
*self
}
#[inline]
fn sqrt(self) -> Self {
f64::sqrt(self)
}
#[inline]
fn ln(self) -> Self {
f64::ln(self)
}
#[inline]
fn exp(self) -> Self {
f64::exp(self)
}
#[inline]
fn sin(self) -> Self {
f64::sin(self)
}
#[inline]
fn cos(self) -> Self {
f64::cos(self)
}
#[inline]
fn tan(self) -> Self {
f64::tan(self)
}
#[inline]
fn pow(self, exp: Self) -> Self {
f64::powf(self, exp)
}
#[inline]
fn abs(self) -> Self {
f64::abs(self)
}
#[inline]
fn is_nan(&self) -> bool {
f64::is_nan(*self)
}
#[inline]
fn is_infinite(&self) -> bool {
f64::is_infinite(*self)
}
}
#[cfg(feature = "highprec")]
#[derive(Clone, Debug)]
pub struct HighPrec {
inner: rug::Float,
}
#[cfg(feature = "highprec")]
impl HighPrec {
pub fn with_precision(precision: u32) -> Self {
Self {
inner: rug::Float::with_val(precision, 0),
}
}
pub fn from_f64_default(v: f64) -> Self {
Self::from_f64_with_prec(v, DEFAULT_PRECISION)
}
pub fn from_f64_with_prec(v: f64, precision: u32) -> Self {
Self {
inner: rug::Float::with_val(precision, v),
}
}
pub fn precision(&self) -> u32 {
self.inner.prec()
}
pub fn pi() -> Self {
Self {
inner: rug::Float::with_val(DEFAULT_PRECISION, rug::float::Constant::Pi),
}
}
pub fn e() -> Self {
let one = rug::Float::with_val(DEFAULT_PRECISION, 1u32);
Self { inner: one.exp() }
}
pub fn phi() -> Self {
let five = Self::from_f64_default(5.0);
let sqrt5 = five.sqrt();
let one = Self::one();
(one + sqrt5) / Self::from_f64_default(2.0)
}
pub fn format(&self, decimal_places: u32) -> String {
format!("{:.1$}", self.inner, decimal_places as usize)
}
pub fn pi_with_prec(prec_bits: u32) -> Self {
Self {
inner: rug::Float::with_val(prec_bits, rug::float::Constant::Pi),
}
}
pub fn e_with_prec(prec_bits: u32) -> Self {
let one = rug::Float::with_val(prec_bits, 1u32);
Self { inner: one.exp() }
}
pub fn from_str_with_prec(s: &str, prec_bits: u32) -> Self {
Self::try_from_str_with_prec(s, prec_bits).unwrap_or_else(|e| panic!("{e}"))
}
pub fn try_from_str_with_prec(s: &str, prec_bits: u32) -> Result<Self, ParseError> {
let parsed = rug::Float::parse(s)
.map_err(|e| ParseError::InvalidFloatLiteral(format!("{s:?}: {e}")))?;
Ok(Self {
inner: rug::Float::with_val(prec_bits, parsed),
})
}
pub fn phi_with_prec(prec_bits: u32) -> Self {
let five = Self::from_str_with_prec("5", prec_bits);
let two = Self::from_str_with_prec("2", prec_bits);
let one = Self::one_with_prec(prec_bits);
let sqrt5 = five.sqrt();
(one + sqrt5) / two
}
pub fn one_with_prec(prec_bits: u32) -> Self {
Self::from_str_with_prec("1", prec_bits)
}
pub fn zero_with_prec(prec_bits: u32) -> Self {
Self::from_str_with_prec("0", prec_bits)
}
pub fn gamma_with_prec(prec_bits: u32) -> Self {
Self::from_str_with_prec(
"0.5772156649015328606065120900824024310421593359399235988057672348848677267776646709369470632917467495",
prec_bits,
)
}
pub fn apery_with_prec(prec_bits: u32) -> Self {
Self::from_str_with_prec(
"1.2020569031595942853997381615114499907649862923404988817922715553418382057863130901864558736093352581",
prec_bits,
)
}
pub fn catalan_with_prec(prec_bits: u32) -> Self {
Self::from_str_with_prec(
"0.9159655941772190150546035149323841107741493742816721342664981196217630197762547694793565129261151062",
prec_bits,
)
}
pub fn plastic_with_prec(prec_bits: u32) -> Self {
Self::from_str_with_prec(
"1.3247179572447460259609088544780973407344040569017333645340150503028278512455475940546993479817872807",
prec_bits,
)
}
}
#[cfg(feature = "highprec")]
impl PartialEq for HighPrec {
fn eq(&self, other: &Self) -> bool {
self.inner == other.inner
}
}
#[cfg(feature = "highprec")]
impl PartialOrd for HighPrec {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
self.inner.partial_cmp(&other.inner)
}
}
#[cfg(feature = "highprec")]
impl Add for HighPrec {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self {
inner: self.inner + rhs.inner,
}
}
}
#[cfg(feature = "highprec")]
impl Sub for HighPrec {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
Self {
inner: self.inner - rhs.inner,
}
}
}
#[cfg(feature = "highprec")]
impl Mul for HighPrec {
type Output = Self;
fn mul(self, rhs: Self) -> Self::Output {
Self {
inner: self.inner * rhs.inner,
}
}
}
#[cfg(feature = "highprec")]
impl Div for HighPrec {
type Output = Self;
fn div(self, rhs: Self) -> Self::Output {
Self {
inner: self.inner / rhs.inner,
}
}
}
#[cfg(feature = "highprec")]
impl Neg for HighPrec {
type Output = Self;
fn neg(self) -> Self::Output {
Self { inner: -self.inner }
}
}
#[cfg(feature = "highprec")]
impl RiesFloat for HighPrec {
fn zero() -> Self {
Self::from_f64_default(0.0)
}
fn one() -> Self {
Self::from_f64_default(1.0)
}
fn from_f64(v: f64) -> Self {
Self::from_f64_default(v)
}
fn to_f64(&self) -> f64 {
self.inner.to_f64()
}
fn sqrt(self) -> Self {
Self {
inner: self.inner.sqrt(),
}
}
fn ln(self) -> Self {
Self {
inner: self.inner.ln(),
}
}
fn exp(self) -> Self {
Self {
inner: self.inner.exp(),
}
}
fn sin(self) -> Self {
Self {
inner: self.inner.sin(),
}
}
fn cos(self) -> Self {
Self {
inner: self.inner.cos(),
}
}
fn tan(self) -> Self {
Self {
inner: self.inner.tan(),
}
}
fn pow(self, exp: Self) -> Self {
Self {
inner: self.inner.pow(&exp.inner),
}
}
fn abs(self) -> Self {
Self {
inner: self.inner.abs(),
}
}
fn is_nan(&self) -> bool {
self.inner.is_nan()
}
fn is_infinite(&self) -> bool {
self.inner.is_infinite()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_f64_ries_float() {
let x: f64 = RiesFloat::from_f64(4.0);
assert!((x.sqrt() - 2.0).abs() < 1e-10);
assert!((x.ln() - 4.0_f64.ln()).abs() < 1e-10);
assert!((x.exp() - 4.0_f64.exp()).abs() < 1e-10);
}
#[test]
fn test_f64_arithmetic() {
let a: f64 = RiesFloat::from_f64(3.0);
let b: f64 = RiesFloat::from_f64(2.0);
assert!((a + b - 5.0).abs() < 1e-10);
assert!((a - b - 1.0).abs() < 1e-10);
assert!((a * b - 6.0).abs() < 1e-10);
assert!((a / b - 1.5).abs() < 1e-10);
}
#[test]
fn test_f64_is_finite() {
let x: f64 = 1.0;
assert!(x.is_finite());
let x: f64 = f64::NAN;
assert!(!x.is_finite());
let x: f64 = f64::INFINITY;
assert!(!x.is_finite());
}
#[cfg(feature = "highprec")]
#[test]
fn test_highprec_basic() {
let a = HighPrec::from_f64(4.0);
let b = a.sqrt();
assert!((b.to_f64() - 2.0).abs() < 1e-50);
}
#[cfg(feature = "highprec")]
#[test]
fn test_highprec_arithmetic() {
let a = HighPrec::from_f64(3.0);
let b = HighPrec::from_f64(2.0);
let sum = a.clone() + b.clone();
assert!((sum.to_f64() - 5.0).abs() < 1e-50);
let diff = a.clone() - b.clone();
assert!((diff.to_f64() - 1.0).abs() < 1e-50);
let prod = a.clone() * b.clone();
assert!((prod.to_f64() - 6.0).abs() < 1e-50);
let quot = a / b;
assert!((quot.to_f64() - 1.5).abs() < 1e-50);
}
#[cfg(feature = "highprec")]
#[test]
fn test_highprec_transcendental() {
let e = HighPrec::from_f64(1.0).exp();
let expected_e = std::f64::consts::E;
assert!((e.to_f64() - expected_e).abs() < 1e-15);
let ln2 = HighPrec::from_f64(2.0).ln();
assert!((ln2.to_f64() - 2.0_f64.ln()).abs() < 1e-15);
}
#[cfg(feature = "highprec")]
#[test]
fn test_pi_precision_exceeds_f64() {
let pi_hp = HighPrec::pi_with_prec(256);
let pi_f64 = HighPrec::from_f64_with_prec(std::f64::consts::PI, 256);
let hp_str = format!("{:.40}", pi_hp.inner);
let f64_str = format!("{:.40}", pi_f64.inner);
assert!(hp_str.starts_with("3.14159265358979323846"));
let common_prefix_len = hp_str
.chars()
.zip(f64_str.chars())
.take_while(|(a, b)| a == b)
.count();
assert!((17..25).contains(&common_prefix_len));
}
#[cfg(feature = "highprec")]
#[test]
fn test_e_precision_exceeds_f64() {
let e_hp = HighPrec::e_with_prec(256);
let e_f64 = HighPrec::from_f64_with_prec(std::f64::consts::E, 256);
let hp_str = format!("{:.40}", e_hp.inner);
let f64_str = format!("{:.40}", e_f64.inner);
assert!(
hp_str.starts_with("2.71828182845904523536"),
"e hp_str was: {hp_str}"
);
let common_prefix_len = hp_str
.chars()
.zip(f64_str.chars())
.take_while(|(a, b)| a == b)
.count();
assert!((17..25).contains(&common_prefix_len));
}
#[cfg(feature = "highprec")]
#[test]
fn test_gamma_precision_exceeds_f64() {
let gamma_hp = HighPrec::gamma_with_prec(256);
let gamma_f64_approx = 0.5772156649015329;
let gamma_f64 = HighPrec::from_f64_with_prec(gamma_f64_approx, 256);
let hp_str = format!("{:.40}", gamma_hp.inner);
let f64_str = format!("{:.40}", gamma_f64.inner);
assert!(
hp_str.starts_with("5.7721566490153286060"),
"gamma hp_str was: {hp_str}"
);
let common_prefix_len = hp_str
.chars()
.zip(f64_str.chars())
.take_while(|(a, b)| a == b)
.count();
assert!((16..22).contains(&common_prefix_len));
}
#[cfg(feature = "highprec")]
#[test]
fn test_apery_precision_exceeds_f64() {
let apery_hp = HighPrec::apery_with_prec(256);
let apery_f64_approx = 1.2020569031595942;
let apery_f64 = HighPrec::from_f64_with_prec(apery_f64_approx, 256);
let hp_str = format!("{:.40}", apery_hp.inner);
let f64_str = format!("{:.40}", apery_f64.inner);
assert!(hp_str.starts_with("1.2020569031595942853"));
let common_prefix_len = hp_str
.chars()
.zip(f64_str.chars())
.take_while(|(a, b)| a == b)
.count();
assert!((16..22).contains(&common_prefix_len));
}
#[cfg(feature = "highprec")]
#[test]
fn test_catalan_precision() {
let catalan_hp = HighPrec::catalan_with_prec(256);
let hp_str = format!("{:.40}", catalan_hp.inner);
assert!(
hp_str.starts_with("9.1596559417721901505"),
"catalan hp_str was: {hp_str}"
);
}
#[cfg(feature = "highprec")]
#[test]
fn test_plastic_precision() {
let plastic_hp = HighPrec::plastic_with_prec(256);
let hp_str = format!("{:.40}", plastic_hp.inner);
assert!(hp_str.starts_with("1.32471795724474602596"));
}
#[cfg(feature = "highprec")]
#[test]
fn test_phi_with_prec() {
let phi_hp = HighPrec::phi_with_prec(256);
let hp_str = format!("{:.40}", phi_hp.inner);
assert!(hp_str.starts_with("1.61803398874989484820"));
}
#[cfg(feature = "highprec")]
#[test]
fn test_from_str_with_prec_invalid_input() {
let result = HighPrec::try_from_str_with_prec("not_a_number", 256);
assert!(
result.is_err(),
"Should return error for invalid float literal"
);
if let Err(e) = result {
assert!(e.to_string().contains("Invalid float literal"));
}
}
#[cfg(feature = "highprec")]
#[test]
fn test_from_str_with_prec_valid_input() {
let result = HighPrec::try_from_str_with_prec("1.23456", 256);
assert!(result.is_ok(), "Should succeed for valid float literal");
let hp = result.unwrap();
let value = hp.to_f64();
assert!((value - 1.23456).abs() < 1e-10);
}
#[cfg(feature = "highprec")]
#[test]
fn test_from_str_with_prec_empty_string() {
let result = HighPrec::try_from_str_with_prec("", 256);
assert!(result.is_err(), "Should return error for empty string");
}
}