use crate::types::MathError;
use num_bigint::BigInt;
use num_rational::BigRational;
use num_traits::{FromPrimitive, One, Signed, ToPrimitive, Zero};
use std::fmt;
use std::str::FromStr;
#[derive(Debug, Clone)]
pub enum PrecisionNumber {
Exact(BigRational),
Float(f64),
Integer(BigInt),
}
impl PrecisionNumber {
pub fn from_f64(value: f64) -> Self {
if value.fract() == 0.0 && value.is_finite() {
PrecisionNumber::Integer(BigInt::from_f64(value).unwrap())
} else {
PrecisionNumber::Float(value)
}
}
pub fn from_str_with_precision(s: &str) -> Result<Self, MathError> {
if let Ok(int) = BigInt::from_str(s) {
return Ok(PrecisionNumber::Integer(int));
}
if s.contains('/') {
let parts: Vec<&str> = s.split('/').collect();
if parts.len() == 2 {
let num = BigInt::from_str(parts[0])
.map_err(|_| MathError::ParseError("Invalid numerator".to_string()))?;
let den = BigInt::from_str(parts[1])
.map_err(|_| MathError::ParseError("Invalid denominator".to_string()))?;
return Ok(PrecisionNumber::Exact(BigRational::new(num, den)));
}
}
if s.contains('.') {
let decimal_places = s.split('.').nth(1).map_or(0, |d| d.len());
let without_dot = s.replace('.', "");
if let Ok(num) = BigInt::from_str(&without_dot) {
let den = BigInt::from(10_i32).pow(decimal_places as u32);
return Ok(PrecisionNumber::Exact(BigRational::new(num, den)));
}
}
s.parse::<f64>()
.map(PrecisionNumber::Float)
.map_err(|_| MathError::ParseError(format!("Cannot parse number: {}", s)))
}
pub fn to_rational(&self) -> BigRational {
match self {
PrecisionNumber::Exact(r) => r.clone(),
PrecisionNumber::Integer(i) => BigRational::from(i.clone()),
PrecisionNumber::Float(f) => {
BigRational::from_float(*f).unwrap_or_else(BigRational::zero)
}
}
}
pub fn add(&self, other: &PrecisionNumber) -> PrecisionNumber {
match (self, other) {
(PrecisionNumber::Integer(a), PrecisionNumber::Integer(b)) => {
PrecisionNumber::Integer(a + b)
}
(PrecisionNumber::Exact(a), PrecisionNumber::Exact(b)) => PrecisionNumber::Exact(a + b),
(PrecisionNumber::Float(a), PrecisionNumber::Float(b)) => PrecisionNumber::Float(a + b),
_ => {
let a = self.to_rational();
let b = other.to_rational();
PrecisionNumber::Exact(a + b)
}
}
}
pub fn subtract(&self, other: &PrecisionNumber) -> PrecisionNumber {
match (self, other) {
(PrecisionNumber::Integer(a), PrecisionNumber::Integer(b)) => {
PrecisionNumber::Integer(a - b)
}
(PrecisionNumber::Exact(a), PrecisionNumber::Exact(b)) => PrecisionNumber::Exact(a - b),
(PrecisionNumber::Float(a), PrecisionNumber::Float(b)) => PrecisionNumber::Float(a - b),
_ => {
let a = self.to_rational();
let b = other.to_rational();
PrecisionNumber::Exact(a - b)
}
}
}
pub fn multiply(&self, other: &PrecisionNumber) -> PrecisionNumber {
match (self, other) {
(PrecisionNumber::Integer(a), PrecisionNumber::Integer(b)) => {
PrecisionNumber::Integer(a * b)
}
(PrecisionNumber::Exact(a), PrecisionNumber::Exact(b)) => PrecisionNumber::Exact(a * b),
(PrecisionNumber::Float(a), PrecisionNumber::Float(b)) => PrecisionNumber::Float(a * b),
_ => {
let a = self.to_rational();
let b = other.to_rational();
PrecisionNumber::Exact(a * b)
}
}
}
pub fn divide(&self, other: &PrecisionNumber) -> Result<PrecisionNumber, MathError> {
if other.is_zero() {
return Err(MathError::DivisionByZero);
}
match (self, other) {
(PrecisionNumber::Exact(a), PrecisionNumber::Exact(b)) => {
Ok(PrecisionNumber::Exact(a / b))
}
(PrecisionNumber::Float(a), PrecisionNumber::Float(b)) => {
Ok(PrecisionNumber::Float(a / b))
}
_ => {
let a = self.to_rational();
let b = other.to_rational();
Ok(PrecisionNumber::Exact(a / b))
}
}
}
pub fn power(&self, exponent: &PrecisionNumber) -> Result<PrecisionNumber, MathError> {
match (self, exponent) {
(PrecisionNumber::Integer(base), PrecisionNumber::Integer(exp)) => {
if let Some(exp_u32) = exp.to_u32() {
Ok(PrecisionNumber::Integer(base.pow(exp_u32)))
} else if exp.is_negative() {
let positive_exp = -exp;
if let Some(exp_u32) = positive_exp.to_u32() {
let result = BigRational::new(BigInt::one(), base.pow(exp_u32));
Ok(PrecisionNumber::Exact(result))
} else {
Err(MathError::InvalidOperation(
"Exponent too large".to_string(),
))
}
} else {
Err(MathError::InvalidOperation(
"Exponent too large".to_string(),
))
}
}
(PrecisionNumber::Float(base), PrecisionNumber::Float(exp)) => {
Ok(PrecisionNumber::Float(base.powf(*exp)))
}
_ => {
if let (Some(base_f64), Some(exp_f64)) = (self.to_f64(), exponent.to_f64()) {
Ok(PrecisionNumber::Float(base_f64.powf(exp_f64)))
} else {
Err(MathError::InvalidOperation(
"Cannot compute power with these operands".to_string(),
))
}
}
}
}
pub fn factorial(&self) -> Result<PrecisionNumber, MathError> {
match self {
PrecisionNumber::Integer(n) if !n.is_negative() => {
let mut result = BigInt::one();
let mut i = BigInt::from(2);
while i <= *n {
result *= &i;
i += 1;
}
Ok(PrecisionNumber::Integer(result))
}
_ => Err(MathError::InvalidOperation(
"Factorial requires non-negative integer".to_string(),
)),
}
}
pub fn is_zero(&self) -> bool {
match self {
PrecisionNumber::Integer(i) => i.is_zero(),
PrecisionNumber::Exact(r) => r.is_zero(),
PrecisionNumber::Float(f) => f.abs() < f64::EPSILON,
}
}
pub fn to_f64(&self) -> Option<f64> {
match self {
PrecisionNumber::Float(f) => Some(*f),
PrecisionNumber::Integer(i) => i.to_f64(),
PrecisionNumber::Exact(r) => r
.numer()
.to_f64()
.and_then(|n| r.denom().to_f64().map(|d| n / d)),
}
}
pub fn sqrt(&self) -> Result<PrecisionNumber, MathError> {
match self {
PrecisionNumber::Integer(n) if !n.is_negative() => {
let n_f64 = n.to_f64().ok_or(MathError::Overflow)?;
let sqrt_val = n_f64.sqrt();
if sqrt_val.fract() == 0.0 {
Ok(PrecisionNumber::Integer(
BigInt::from_f64(sqrt_val).unwrap(),
))
} else {
Ok(PrecisionNumber::Float(sqrt_val))
}
}
PrecisionNumber::Float(f) if *f >= 0.0 => Ok(PrecisionNumber::Float(f.sqrt())),
PrecisionNumber::Exact(r) if !r.is_negative() => {
let n_f64 = r.to_f64().ok_or(MathError::Overflow)?;
Ok(PrecisionNumber::Float(n_f64.sqrt()))
}
_ => Err(MathError::InvalidOperation(
"Cannot take square root of negative number".to_string(),
)),
}
}
}
impl fmt::Display for PrecisionNumber {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
PrecisionNumber::Integer(i) => write!(f, "{}", i),
PrecisionNumber::Exact(r) => {
if r.denom().is_one() {
write!(f, "{}", r.numer())
} else {
write!(f, "{}/{}", r.numer(), r.denom())
}
}
PrecisionNumber::Float(fl) => write!(f, "{}", fl),
}
}
}
pub struct ArbitraryPrecision;
impl ArbitraryPrecision {
pub fn compute_pi(precision: usize) -> PrecisionNumber {
let mut pi = BigRational::zero();
let sixteen = BigInt::from(16);
for k in 0..precision {
let k_bigint = BigInt::from(k);
let eight_k = &k_bigint * 8;
let term1 = BigRational::new(BigInt::from(4), &eight_k + 1);
let term2 = BigRational::new(BigInt::from(2), &eight_k + 4);
let term3 = BigRational::new(BigInt::one(), &eight_k + 5);
let term4 = BigRational::new(BigInt::one(), &eight_k + 6);
let series_term = term1 - term2 - term3 - term4;
let divisor = sixteen.pow(k as u32);
pi += BigRational::new(BigInt::one(), divisor) * series_term;
}
PrecisionNumber::Exact(pi)
}
pub fn compute_e(precision: usize) -> PrecisionNumber {
let mut e = BigRational::one();
let mut factorial = BigInt::one();
for n in 1..precision {
factorial *= n;
e += BigRational::new(BigInt::one(), factorial.clone());
}
PrecisionNumber::Exact(e)
}
pub fn compute_sqrt(n: &BigInt, precision: usize) -> PrecisionNumber {
if n.is_negative() {
return PrecisionNumber::Float(f64::NAN);
}
let mut x = n.clone();
let two = BigInt::from(2);
for _ in 0..precision {
let x_squared = &x * &x;
if x_squared == *n {
return PrecisionNumber::Integer(x);
}
x = (&x + n / &x) / &two;
}
PrecisionNumber::Integer(x)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_precision_arithmetic() {
let a = PrecisionNumber::from_str_with_precision("1/3").unwrap();
let b = PrecisionNumber::from_str_with_precision("1/6").unwrap();
let c = a.add(&b);
println!("1/3 + 1/6 = {}", c);
let d = PrecisionNumber::from_str_with_precision("2.5").unwrap();
let e = PrecisionNumber::from_str_with_precision("3.7").unwrap();
let f = d.multiply(&e);
println!("2.5 * 3.7 = {}", f);
}
#[test]
fn test_compute_constants() {
let pi = ArbitraryPrecision::compute_pi(10);
println!("π ≈ {}", pi);
let e = ArbitraryPrecision::compute_e(20);
println!("e ≈ {}", e);
}
}