use crate::{
error::{CoreError, CoreResult, ErrorContext},
numeric::precision_tracking::PrecisionContext,
validation::check_positive,
};
use num_bigint::BigInt;
use oxinum_int::{is_prime, IBig, UBig};
use oxinum_float::{
compute_e, compute_ln2, compute_pi, cos, cosh, exp, ln, precision::with_precision, sin, sinh,
sqrt, tan, tanh, DBig,
};
use oxinum_rational::{IBig as RIBig, RBig, UBig as RUBig};
use rug::{ops::Pow, Complex as RugComplex, Float as RugFloat};
use std::cmp::Ordering;
use std::fmt;
use std::ops::{Add, Div, Mul, Neg, Sub};
use std::str::FromStr;
use std::sync::RwLock;
static DEFAULT_PRECISION: RwLock<u32> = RwLock::new(256);
const BITS_PER_DECIMAL_DIGIT: f64 = std::f64::consts::LOG2_10;
fn bits_to_decimal_digits(bits: u32) -> usize {
((bits as f64) / BITS_PER_DECIMAL_DIGIT).ceil() as usize
}
fn f64_to_dbig(v: f64) -> DBig {
if v.is_nan() {
return DBig::from(0u32);
}
if v.is_infinite() {
return DBig::from_str("1e308").unwrap_or_else(|_| DBig::from(0u32));
}
let s = format!("{v:.17e}");
DBig::from_str(&s).unwrap_or_else(|_| {
let s2 = format!("{v}");
DBig::from_str(&s2).unwrap_or_else(|_| DBig::from(0u32))
})
}
fn dbig_to_f64(v: &DBig) -> f64 {
v.to_f64().value()
}
#[allow(dead_code)]
pub fn get_defaultprecision() -> u32 {
*DEFAULT_PRECISION.read().expect("Operation failed")
}
#[allow(dead_code)]
pub fn setprecision(prec: u32) -> CoreResult<()> {
check_positive(prec as f64, "precision")?;
*DEFAULT_PRECISION.write().expect("Operation failed") = prec;
Ok(())
}
#[derive(Debug, Clone)]
pub struct ArbitraryPrecisionContext {
pub floatprecision: u32,
pub maxprecision: u32,
pub rounding_mode: RoundingMode,
pub trackprecision: bool,
pub precision_context: Option<PrecisionContext>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RoundingMode {
Nearest,
Zero,
Up,
Down,
Away,
}
impl Default for ArbitraryPrecisionContext {
fn default() -> Self {
Self {
floatprecision: get_defaultprecision(),
maxprecision: 4096,
rounding_mode: RoundingMode::Nearest,
trackprecision: false,
precision_context: None,
}
}
}
impl ArbitraryPrecisionContext {
pub fn withprecision(precision: u32) -> CoreResult<Self> {
check_positive(precision as f64, "precision")?;
Ok(Self {
floatprecision: precision,
..Default::default()
})
}
pub fn withprecision_tracking(precision: u32) -> CoreResult<Self> {
let mut ctx = Self::withprecision(precision)?;
ctx.trackprecision = true;
let mut precision_ctx = PrecisionContext::new();
precision_ctx.precision = precision as f64 / BITS_PER_DECIMAL_DIGIT;
ctx.precision_context = Some(precision_ctx);
Ok(ctx)
}
pub fn with_rounding(mut self, mode: RoundingMode) -> Self {
self.rounding_mode = mode;
self
}
pub fn with_maxprecision(mut self, maxprec: u32) -> Self {
self.maxprecision = maxprec;
self
}
fn decimal_digits(&self) -> usize {
bits_to_decimal_digits(self.floatprecision)
}
}
#[derive(Clone, PartialEq, Eq)]
pub struct ArbitraryInt {
value: IBig,
}
impl ArbitraryInt {
pub fn new() -> Self {
Self {
value: IBig::from(0i32),
}
}
pub fn from_i64(n: i64) -> Self {
Self {
value: IBig::from(n),
}
}
pub fn from_str_radix(s: &str, radix: i32) -> CoreResult<Self> {
if !(2..=36).contains(&radix) {
return Err(CoreError::ValidationError(ErrorContext::new(format!(
"Invalid radix {radix}: must be 2..=36"
))));
}
oxinum_int::ibig_from_radix(s, radix as u32)
.map(|value| Self { value })
.map_err(|e| {
CoreError::ValidationError(ErrorContext::new(format!(
"Failed to parse integer from string '{s}': {e}"
)))
})
}
pub fn to_bigint(&self) -> BigInt {
BigInt::from_str(&self.value.to_string()).expect("Operation failed")
}
pub fn is_probably_prime(&self, reps: u32) -> bool {
if self.value <= IBig::from(1i32) {
return false;
}
let s = self.value.to_string();
match UBig::from_str(&s) {
Ok(u) => is_prime(&u, reps),
Err(_) => false,
}
}
pub fn factorial(n: u32) -> Self {
let u = oxinum_int::factorial(n);
Self {
value: IBig::from(u),
}
}
pub fn binomial(n: u32, k: u32) -> Self {
if k > n {
return Self::new();
}
let u = oxinum_int::binomial(n, k);
Self {
value: IBig::from(u),
}
}
pub fn gcd(&self, other: &Self) -> Self {
use oxinum_int::Gcd;
let a = self.value.clone();
let b = other.value.clone();
let g: UBig = a.gcd(&b);
Self {
value: IBig::from(g),
}
}
pub fn lcm(&self, other: &Self) -> Self {
if self.value == IBig::from(0i32) || other.value == IBig::from(0i32) {
return Self::new();
}
let gcd = self.gcd(other);
let product = self.value.clone() * other.value.clone();
Self {
value: product / gcd.value,
}
}
pub fn mod_pow(&self, exp: &Self, modulus: &Self) -> CoreResult<Self> {
if modulus.value == IBig::from(0i32) {
return Err(CoreError::DomainError(ErrorContext::new(
"Modulus cannot be zero",
)));
}
let base_str = self.value.to_string();
let exp_str = exp.value.to_string();
let mod_str = modulus.value.to_string();
let base_u = UBig::from_str(&base_str).map_err(|_| {
CoreError::DomainError(ErrorContext::new("base must be non-negative for mod_pow"))
})?;
let exp_u = UBig::from_str(&exp_str).map_err(|_| {
CoreError::DomainError(ErrorContext::new(
"exponent must be non-negative for mod_pow",
))
})?;
let mod_u = UBig::from_str(&mod_str).map_err(|_| {
CoreError::DomainError(ErrorContext::new(
"modulus must be non-negative for mod_pow",
))
})?;
let result = oxinum_int::mod_pow(&base_u, &exp_u, &mod_u)
.map_err(|e| CoreError::DomainError(ErrorContext::new(format!("{e}"))))?;
Ok(Self {
value: IBig::from(result),
})
}
pub fn abs(&self) -> Self {
use oxinum_core::Abs;
Self {
value: self.value.clone().abs(),
}
}
pub fn signum(&self) -> i32 {
use oxinum_core::Signed;
if self.value == IBig::from(0i32) {
return 0;
}
match self.value.sign() {
oxinum_core::Sign::Positive => 1,
oxinum_core::Sign::Negative => -1,
}
}
}
impl fmt::Display for ArbitraryInt {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.value)
}
}
impl fmt::Debug for ArbitraryInt {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "ArbitraryInt({})", self.value)
}
}
impl Default for ArbitraryInt {
fn default() -> Self {
Self::new()
}
}
impl Add for ArbitraryInt {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self {
value: self.value + rhs.value,
}
}
}
impl Sub for ArbitraryInt {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
Self {
value: self.value - rhs.value,
}
}
}
impl Mul for ArbitraryInt {
type Output = Self;
fn mul(self, rhs: Self) -> Self::Output {
Self {
value: self.value * rhs.value,
}
}
}
impl Div for ArbitraryInt {
type Output = Self;
fn div(self, rhs: Self) -> Self::Output {
Self {
value: self.value / rhs.value,
}
}
}
impl Neg for ArbitraryInt {
type Output = Self;
fn neg(self) -> Self::Output {
Self { value: -self.value }
}
}
#[derive(Clone)]
pub struct ArbitraryFloat {
value: DBig,
context: ArbitraryPrecisionContext,
}
impl ArbitraryFloat {
pub fn new() -> Self {
let prec = get_defaultprecision();
let context = ArbitraryPrecisionContext::default();
let digits = bits_to_decimal_digits(prec);
let value = with_precision(&DBig::from(0u32), digits);
Self { value, context }
}
pub fn withprecision(prec: u32) -> CoreResult<Self> {
let context = ArbitraryPrecisionContext::withprecision(prec)?;
let digits = bits_to_decimal_digits(prec);
let value = with_precision(&DBig::from(0u32), digits);
Ok(Self { value, context })
}
pub fn with_context(context: ArbitraryPrecisionContext) -> Self {
let digits = context.decimal_digits();
let value = with_precision(&DBig::from(0u32), digits);
Self { value, context }
}
pub fn from_f64(v: f64) -> Self {
let prec = get_defaultprecision();
let context = ArbitraryPrecisionContext::default();
let digits = bits_to_decimal_digits(prec);
let raw = f64_to_dbig(v);
let value = with_precision(&raw, digits);
Self { value, context }
}
pub fn from_f64_withprecision(v: f64, prec: u32) -> CoreResult<Self> {
let context = ArbitraryPrecisionContext::withprecision(prec)?;
let digits = bits_to_decimal_digits(prec);
let raw = f64_to_dbig(v);
let value = with_precision(&raw, digits);
Ok(Self { value, context })
}
pub fn from_strprec(s: &str, prec: u32) -> CoreResult<Self> {
let context = ArbitraryPrecisionContext::withprecision(prec)?;
let digits = bits_to_decimal_digits(prec);
let parsed = DBig::from_str(s)
.map_err(|e| CoreError::ValidationError(ErrorContext::new(format!("{e}"))))?;
let value = with_precision(&parsed, digits);
Ok(Self { value, context })
}
pub fn precision(&self) -> u32 {
self.context.floatprecision
}
pub fn decimalprecision(&self) -> u32 {
self.context.decimal_digits() as u32
}
pub fn setprecision(&self, prec: u32) -> CoreResult<Self> {
let mut context = self.context.clone();
context.floatprecision = prec;
let digits = bits_to_decimal_digits(prec);
let value = with_precision(&self.value, digits);
Ok(Self { value, context })
}
pub fn to_f64(&self) -> f64 {
dbig_to_f64(&self.value)
}
pub fn is_finite(&self) -> bool {
true
}
pub fn is_infinite(&self) -> bool {
false
}
pub fn is_nan(&self) -> bool {
false
}
pub fn is_zero(&self) -> bool {
self.value == DBig::from(0u32)
}
pub fn abs(&self) -> Self {
use oxinum_core::Abs;
Self {
value: self.value.clone().abs(),
context: self.context.clone(),
}
}
pub fn sqrt(&self) -> CoreResult<Self> {
let zero = DBig::from(0u32);
if self.value < zero {
return Err(CoreError::DomainError(ErrorContext::new(
"Square root of negative number",
)));
}
let digits = self.context.decimal_digits();
let result = sqrt(&self.value, digits)
.map_err(|e| CoreError::DomainError(ErrorContext::new(format!("{e}"))))?;
Ok(Self {
value: result,
context: self.context.clone(),
})
}
pub fn ln(&self) -> CoreResult<Self> {
let zero = DBig::from(0u32);
if self.value <= zero {
return Err(CoreError::DomainError(ErrorContext::new(
"Logarithm of non-positive number",
)));
}
let digits = self.context.decimal_digits();
let result = ln(&self.value, digits)
.map_err(|e| CoreError::DomainError(ErrorContext::new(format!("{e}"))))?;
Ok(Self {
value: result,
context: self.context.clone(),
})
}
pub fn exp(&self) -> Self {
let digits = self.context.decimal_digits();
let result = exp(&self.value, digits).unwrap_or_else(|_| DBig::from(1u32));
Self {
value: result,
context: self.context.clone(),
}
}
pub fn pow(&self, exponent: &Self) -> Self {
use oxinum_float::pow as oxinum_pow;
let digits = self.context.decimal_digits();
let result =
oxinum_pow(&self.value, &exponent.value, digits).unwrap_or_else(|_| DBig::from(1u32));
Self {
value: result,
context: self.context.clone(),
}
}
pub fn sin(&self) -> Self {
let digits = self.context.decimal_digits();
let result = sin(&self.value, digits).unwrap_or_else(|_| DBig::from(0u32));
Self {
value: result,
context: self.context.clone(),
}
}
pub fn cos(&self) -> Self {
let digits = self.context.decimal_digits();
let result = cos(&self.value, digits).unwrap_or_else(|_| DBig::from(1u32));
Self {
value: result,
context: self.context.clone(),
}
}
pub fn tan(&self) -> Self {
let digits = self.context.decimal_digits();
let result = tan(&self.value, digits).unwrap_or_else(|_| DBig::from(0u32));
Self {
value: result,
context: self.context.clone(),
}
}
pub fn asin(&self) -> CoreResult<Self> {
let one = DBig::from(1u32);
let abs_val = {
use oxinum_core::Abs;
self.value.clone().abs()
};
if abs_val > one {
return Err(CoreError::DomainError(ErrorContext::new(
"Arcsine argument out of range [-1, 1]",
)));
}
let digits = self.context.decimal_digits();
let one_minus_x2 = {
let x2 = self.value.clone() * self.value.clone();
DBig::from(1u32) - x2
};
let denom = sqrt(&one_minus_x2, digits + 4)
.map_err(|e| CoreError::DomainError(ErrorContext::new(format!("{e}"))))?;
let zero = DBig::from(0u32);
if denom == zero {
let pi = compute_pi(digits);
let two = DBig::from(2u32);
let half_pi = pi / two;
let result = if self.value < zero { -half_pi } else { half_pi };
return Ok(Self {
value: result,
context: self.context.clone(),
});
}
let ratio = self.value.clone() / denom;
use oxinum_float::atan as oxinum_atan;
let result = oxinum_atan(&ratio, digits)
.map_err(|e| CoreError::DomainError(ErrorContext::new(format!("{e}"))))?;
Ok(Self {
value: result,
context: self.context.clone(),
})
}
pub fn acos(&self) -> CoreResult<Self> {
let one = DBig::from(1u32);
let abs_val = {
use oxinum_core::Abs;
self.value.clone().abs()
};
if abs_val > one {
return Err(CoreError::DomainError(ErrorContext::new(
"Arccosine argument out of range [-1, 1]",
)));
}
let digits = self.context.decimal_digits();
let pi = compute_pi(digits + 4);
let two = DBig::from(2u32);
let half_pi = pi / two;
let asin_val = self.asin()?;
Ok(Self {
value: half_pi - asin_val.value,
context: self.context.clone(),
})
}
pub fn atan(&self) -> Self {
use oxinum_float::atan as oxinum_atan;
let digits = self.context.decimal_digits();
let result = oxinum_atan(&self.value, digits).unwrap_or_else(|_| DBig::from(0u32));
Self {
value: result,
context: self.context.clone(),
}
}
pub fn atan2(&self, x: &Self) -> Self {
use oxinum_float::atan2 as oxinum_atan2;
let digits = self.context.decimal_digits();
let result =
oxinum_atan2(&self.value, &x.value, digits).unwrap_or_else(|_| DBig::from(0u32));
Self {
value: result,
context: self.context.clone(),
}
}
pub fn sinh(&self) -> Self {
let digits = self.context.decimal_digits();
let result = sinh(&self.value, digits).unwrap_or_else(|_| DBig::from(0u32));
Self {
value: result,
context: self.context.clone(),
}
}
pub fn cosh(&self) -> Self {
let digits = self.context.decimal_digits();
let result = cosh(&self.value, digits).unwrap_or_else(|_| DBig::from(1u32));
Self {
value: result,
context: self.context.clone(),
}
}
pub fn tanh(&self) -> Self {
let digits = self.context.decimal_digits();
let result = tanh(&self.value, digits).unwrap_or_else(|_| DBig::from(0u32));
Self {
value: result,
context: self.context.clone(),
}
}
pub fn prec_2(prec: u32) -> CoreResult<Self> {
let context = ArbitraryPrecisionContext::withprecision(prec)?;
let digits = bits_to_decimal_digits(prec);
let value = with_precision(&compute_pi(digits), digits);
Ok(Self { value, context })
}
pub fn prec_3(prec: u32) -> CoreResult<Self> {
let context = ArbitraryPrecisionContext::withprecision(prec)?;
let digits = bits_to_decimal_digits(prec);
let value = with_precision(&compute_e(digits), digits);
Ok(Self { value, context })
}
pub fn prec_4(prec: u32) -> CoreResult<Self> {
let context = ArbitraryPrecisionContext::withprecision(prec)?;
let digits = bits_to_decimal_digits(prec);
let value = with_precision(&compute_ln2(digits), digits);
Ok(Self { value, context })
}
}
impl fmt::Display for ArbitraryFloat {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.value)
}
}
impl fmt::Debug for ArbitraryFloat {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"ArbitraryFloat({}, {} bits)",
self.value,
self.precision()
)
}
}
impl PartialEq for ArbitraryFloat {
fn eq(&self, other: &Self) -> bool {
self.value == other.value
}
}
impl PartialOrd for ArbitraryFloat {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.value.partial_cmp(&other.value)
}
}
impl Default for ArbitraryFloat {
fn default() -> Self {
Self::new()
}
}
impl Add for ArbitraryFloat {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self {
value: self.value + rhs.value,
context: self.context,
}
}
}
impl Sub for ArbitraryFloat {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
Self {
value: self.value - rhs.value,
context: self.context,
}
}
}
impl Mul for ArbitraryFloat {
type Output = Self;
fn mul(self, rhs: Self) -> Self::Output {
Self {
value: self.value * rhs.value,
context: self.context,
}
}
}
impl Div for ArbitraryFloat {
type Output = Self;
fn div(self, rhs: Self) -> Self::Output {
Self {
value: self.value / rhs.value,
context: self.context,
}
}
}
impl Neg for ArbitraryFloat {
type Output = Self;
fn neg(self) -> Self::Output {
Self {
value: -self.value,
context: self.context,
}
}
}
#[derive(Clone, PartialEq, Eq)]
pub struct ArbitraryRational {
value: RBig,
}
impl ArbitraryRational {
pub fn new() -> Self {
Self {
value: RBig::from(0u32),
}
}
pub fn num(num: i64, den: i64) -> CoreResult<Self> {
if den == 0 {
return Err(CoreError::DomainError(ErrorContext::new(
"Denominator cannot be zero",
)));
}
let n = RIBig::from(num);
let d = RUBig::from(den.unsigned_abs());
let signed_n = if den < 0 { -n } else { n };
Ok(Self {
value: RBig::from_parts(signed_n, d),
})
}
pub fn num_2(num: &ArbitraryInt, den: &ArbitraryInt) -> CoreResult<Self> {
if den.value == IBig::from(0i32) {
return Err(CoreError::DomainError(ErrorContext::new(
"Denominator cannot be zero",
)));
}
let n = RIBig::from_str(&num.value.to_string()).map_err(|_| {
CoreError::ValidationError(ErrorContext::new("numerator conversion failed"))
})?;
let d_ibig = IBig::from_str(&den.value.to_string()).map_err(|_| {
CoreError::ValidationError(ErrorContext::new("denominator conversion failed"))
})?;
use oxinum_core::Abs;
let (n_final, d_ubig) = if d_ibig < IBig::from(0i32) {
let neg_n = RIBig::from_str(&(-num.value.clone()).to_string())
.unwrap_or_else(|_| RIBig::from(0i32));
let d_abs = RUBig::from_str(&d_ibig.clone().abs().to_string()).unwrap_or(RUBig::ONE);
(neg_n, d_abs)
} else {
let d_abs = RUBig::from_str(&d_ibig.to_string()).unwrap_or(RUBig::ONE);
(n, d_abs)
};
Ok(Self {
value: RBig::from_parts(n_final, d_ubig),
})
}
#[deprecated(note = "Use str::parse() instead")]
pub fn parse_rational(s: &str) -> CoreResult<Self> {
s.parse()
}
pub fn to_f64(&self) -> f64 {
use oxinum_rational::to_f64 as rbig_to_f64;
rbig_to_f64(&self.value)
}
pub fn to_arbitrary_float(&self, prec: u32) -> CoreResult<ArbitraryFloat> {
let context = ArbitraryPrecisionContext::withprecision(prec)?;
let digits = bits_to_decimal_digits(prec);
let num_str = self.value.numerator().to_string();
let den_str = self.value.denominator().to_string();
let n = DBig::from_str(&num_str)
.map_err(|e| CoreError::ValidationError(ErrorContext::new(format!("{e}"))))?;
let d = DBig::from_str(&den_str)
.map_err(|e| CoreError::ValidationError(ErrorContext::new(format!("{e}"))))?;
let n_prec = with_precision(&n, digits + 4);
let d_prec = with_precision(&d, digits + 4);
let value = with_precision(&(n_prec / d_prec), digits);
Ok(ArbitraryFloat { value, context })
}
pub fn numerator(&self) -> ArbitraryInt {
let n_str = self.value.numerator().to_string();
let v = IBig::from_str(&n_str).unwrap_or_else(|_| IBig::from(0i32));
ArbitraryInt { value: v }
}
pub fn denominator(&self) -> ArbitraryInt {
let d_str = self.value.denominator().to_string();
let v = IBig::from_str(&d_str).unwrap_or_else(|_| IBig::from(1i32));
ArbitraryInt { value: v }
}
pub fn abs(&self) -> Self {
use oxinum_rational::rational_abs;
Self {
value: rational_abs(&self.value),
}
}
pub fn recip(&self) -> CoreResult<Self> {
if self.value == RBig::from(0u32) {
return Err(CoreError::DomainError(ErrorContext::new(
"Cannot take reciprocal of zero",
)));
}
use oxinum_rational::rational_reciprocal;
let recip = rational_reciprocal(&self.value)
.map_err(|e| CoreError::DomainError(ErrorContext::new(format!("{e}"))))?;
Ok(Self { value: recip })
}
}
impl fmt::Display for ArbitraryRational {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.value)
}
}
impl fmt::Debug for ArbitraryRational {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "ArbitraryRational({})", self.value)
}
}
impl Default for ArbitraryRational {
fn default() -> Self {
Self::new()
}
}
impl FromStr for ArbitraryRational {
type Err = CoreError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if let Some(slash) = s.find('/') {
let num_s = &s[..slash];
let den_s = &s[slash + 1..];
let n = RIBig::from_str(num_s.trim()).map_err(|_| {
CoreError::ValidationError(ErrorContext::new(format!(
"Failed to parse rational from string: {s}"
)))
})?;
let d = RUBig::from_str(den_s.trim()).map_err(|_| {
CoreError::ValidationError(ErrorContext::new(format!(
"Failed to parse rational from string: {s}"
)))
})?;
return Ok(Self {
value: RBig::from_parts(n, d),
});
}
let n = RIBig::from_str(s.trim()).map_err(|_| {
CoreError::ValidationError(ErrorContext::new(format!(
"Failed to parse rational from string: {s}"
)))
})?;
Ok(Self {
value: RBig::from_parts(n, RUBig::ONE),
})
}
}
impl Add for ArbitraryRational {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self {
value: self.value + rhs.value,
}
}
}
impl Sub for ArbitraryRational {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
Self {
value: self.value - rhs.value,
}
}
}
impl Mul for ArbitraryRational {
type Output = Self;
fn mul(self, rhs: Self) -> Self::Output {
Self {
value: self.value * rhs.value,
}
}
}
impl Div for ArbitraryRational {
type Output = Self;
fn div(self, rhs: Self) -> Self::Output {
Self {
value: self.value / rhs.value,
}
}
}
impl Neg for ArbitraryRational {
type Output = Self;
fn neg(self) -> Self::Output {
Self { value: -self.value }
}
}
#[derive(Clone)]
pub struct ArbitraryComplex {
value: RugComplex,
context: ArbitraryPrecisionContext,
}
impl ArbitraryComplex {
pub fn new() -> Self {
let prec = get_defaultprecision();
Self {
value: RugComplex::new(prec),
context: ArbitraryPrecisionContext::default(),
}
}
pub fn prec(prec: u32) -> CoreResult<Self> {
let context = ArbitraryPrecisionContext::withprecision(prec)?;
Ok(Self {
value: RugComplex::new(prec),
context,
})
}
pub fn re(re: &ArbitraryFloat, im: &ArbitraryFloat) -> Self {
let prec = re.precision().max(im.precision());
let context = re.context.clone();
let re_f = re.to_f64();
let im_f = im.to_f64();
Self {
value: RugComplex::with_val(prec, (re_f, im_f)),
context,
}
}
pub fn re_2(re: f64, im: f64) -> Self {
let prec = get_defaultprecision();
Self {
value: RugComplex::with_val(prec, (re, im)),
context: ArbitraryPrecisionContext::default(),
}
}
pub fn real(&self) -> ArbitraryFloat {
let re_f64 = self.value.real().to_f64();
ArbitraryFloat::from_f64(re_f64)
}
pub fn imag(&self) -> ArbitraryFloat {
let im_f64 = self.value.imag().to_f64();
ArbitraryFloat::from_f64(im_f64)
}
pub fn abs(&self) -> ArbitraryFloat {
let mag = RugFloat::with_val(self.context.floatprecision, self.value.abs_ref());
ArbitraryFloat::from_f64(mag.to_f64())
}
pub fn arg(&self) -> ArbitraryFloat {
let arg = RugFloat::with_val(self.context.floatprecision, self.value.arg_ref());
ArbitraryFloat::from_f64(arg.to_f64())
}
pub fn conj(&self) -> Self {
let mut result = self.clone();
result.value.conj_mut();
result
}
pub fn ln(&self) -> Self {
let mut result = self.clone();
result.value.ln_mut();
result
}
pub fn exp(&self) -> Self {
let mut result = self.clone();
result.value.exp_mut();
result
}
pub fn pow(&self, exp: &Self) -> Self {
let ln_self = self.ln();
let product = ln_self * exp.clone();
product.exp()
}
pub fn sqrt(&self) -> Self {
let mut result = self.clone();
result.value.sqrt_mut();
result
}
}
impl fmt::Display for ArbitraryComplex {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let re = self.value.real();
let im = self.value.imag();
if im.is_sign_positive() {
write!(f, "{} + {}i", re, im)
} else {
write!(f, "{} - {}i", re, -im.clone())
}
}
}
impl fmt::Debug for ArbitraryComplex {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"ArbitraryComplex({}, {} bits)",
self, self.context.floatprecision
)
}
}
impl PartialEq for ArbitraryComplex {
fn eq(&self, other: &Self) -> bool {
self.value.eq(&other.value)
}
}
impl Default for ArbitraryComplex {
fn default() -> Self {
Self::new()
}
}
impl Add for ArbitraryComplex {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self {
value: self.value + rhs.value,
context: self.context,
}
}
}
impl Sub for ArbitraryComplex {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
Self {
value: self.value - rhs.value,
context: self.context,
}
}
}
impl Mul for ArbitraryComplex {
type Output = Self;
fn mul(self, rhs: Self) -> Self::Output {
Self {
value: self.value * rhs.value,
context: self.context,
}
}
}
impl Div for ArbitraryComplex {
type Output = Self;
fn div(self, rhs: Self) -> Self::Output {
Self {
value: self.value / rhs.value,
context: self.context,
}
}
}
impl Neg for ArbitraryComplex {
type Output = Self;
fn neg(self) -> Self::Output {
Self {
value: -self.value,
context: self.context,
}
}
}
pub trait ToArbitraryPrecision {
type ArbitraryType;
fn to_arbitrary(&self) -> Self::ArbitraryType;
fn to_arbitraryprec(&self, prec: u32) -> CoreResult<Self::ArbitraryType>;
}
impl ToArbitraryPrecision for i32 {
type ArbitraryType = ArbitraryInt;
fn to_arbitrary(&self) -> Self::ArbitraryType {
ArbitraryInt::from_i64(*self as i64)
}
fn to_arbitraryprec(&self, _prec: u32) -> CoreResult<Self::ArbitraryType> {
Ok(self.to_arbitrary())
}
}
impl ToArbitraryPrecision for i64 {
type ArbitraryType = ArbitraryInt;
fn to_arbitrary(&self) -> Self::ArbitraryType {
ArbitraryInt::from_i64(*self)
}
fn to_arbitraryprec(&self, _prec: u32) -> CoreResult<Self::ArbitraryType> {
Ok(self.to_arbitrary())
}
}
impl ToArbitraryPrecision for f32 {
type ArbitraryType = ArbitraryFloat;
fn to_arbitrary(&self) -> Self::ArbitraryType {
ArbitraryFloat::from_f64(*self as f64)
}
fn to_arbitraryprec(&self, prec: u32) -> CoreResult<Self::ArbitraryType> {
ArbitraryFloat::from_f64_withprecision(*self as f64, prec)
}
}
impl ToArbitraryPrecision for f64 {
type ArbitraryType = ArbitraryFloat;
fn to_arbitrary(&self) -> Self::ArbitraryType {
ArbitraryFloat::from_f64(*self)
}
fn to_arbitraryprec(&self, prec: u32) -> CoreResult<Self::ArbitraryType> {
ArbitraryFloat::from_f64_withprecision(*self, prec)
}
}
pub struct ArbitraryPrecisionBuilder {
context: ArbitraryPrecisionContext,
}
impl ArbitraryPrecisionBuilder {
pub fn new() -> Self {
Self {
context: ArbitraryPrecisionContext::default(),
}
}
pub fn precision(mut self, prec: u32) -> Self {
self.context.floatprecision = prec;
self
}
pub fn decimalprecision(mut self, digits: u32) -> Self {
self.context.floatprecision = ((digits as f64) * BITS_PER_DECIMAL_DIGIT) as u32;
self
}
pub fn rounding(mut self, mode: RoundingMode) -> Self {
self.context.rounding_mode = mode;
self
}
pub fn trackprecision(mut self, track: bool) -> Self {
self.context.trackprecision = track;
if track && self.context.precision_context.is_none() {
let mut precision_ctx = PrecisionContext::new();
precision_ctx.precision = self.context.floatprecision as f64 / BITS_PER_DECIMAL_DIGIT;
self.context.precision_context = Some(precision_ctx);
}
self
}
pub fn build_float(self) -> ArbitraryFloat {
ArbitraryFloat::with_context(self.context)
}
pub fn build_complex(self) -> CoreResult<ArbitraryComplex> {
ArbitraryComplex::prec(self.context.floatprecision)
}
pub fn calculate<F, R>(self, f: F) -> R
where
F: FnOnce(&ArbitraryPrecisionContext) -> R,
{
f(&self.context)
}
}
impl Default for ArbitraryPrecisionBuilder {
fn default() -> Self {
Self::new()
}
}
pub mod utils {
use super::*;
pub fn pi(prec: u32) -> CoreResult<ArbitraryFloat> {
ArbitraryFloat::prec_2(prec)
}
pub fn e(prec: u32) -> CoreResult<ArbitraryFloat> {
ArbitraryFloat::prec_3(prec)
}
pub fn ln2(prec: u32) -> CoreResult<ArbitraryFloat> {
ArbitraryFloat::prec_4(prec)
}
pub fn sqrt2(prec: u32) -> CoreResult<ArbitraryFloat> {
let two = ArbitraryFloat::from_f64_withprecision(2.0, prec)?;
two.sqrt()
}
pub fn golden_ratio(prec: u32) -> CoreResult<ArbitraryFloat> {
let one = ArbitraryFloat::from_f64_withprecision(1.0, prec)?;
let five = ArbitraryFloat::from_f64_withprecision(5.0, prec)?;
let sqrt5 = five.sqrt()?;
let two = ArbitraryFloat::from_f64_withprecision(2.0, prec)?;
Ok((one + sqrt5) / two)
}
pub fn factorial(n: u32) -> ArbitraryInt {
ArbitraryInt::factorial(n)
}
pub fn binomial(n: u32, k: u32) -> ArbitraryInt {
ArbitraryInt::binomial(n, k)
}
pub fn is_probably_prime(n: &ArbitraryInt, certainty: u32) -> bool {
n.is_probably_prime(certainty)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_arbitrary_int_basic() {
let a = ArbitraryInt::from_i64(123);
let b = ArbitraryInt::from_i64(456);
let sum = a.clone() + b.clone();
assert_eq!(sum.to_string(), "579");
let product = a.clone() * b.clone();
assert_eq!(product.to_string(), "56088");
let factorial = ArbitraryInt::factorial(20);
assert_eq!(factorial.to_string(), "2432902008176640000");
}
#[test]
fn test_arbitrary_float_basic() {
let a = ArbitraryFloat::from_f64_withprecision(1.0, 128).expect("Operation failed");
let b = ArbitraryFloat::from_f64_withprecision(3.0, 128).expect("Operation failed");
let c = a / b;
let c_str = c.to_string();
assert!(
c_str.starts_with("3.333333333333333") || c_str.starts_with("0.333333333333333"),
"unexpected value: {c_str}"
);
assert!(c_str.len() > 10, "result too short: {c_str}");
}
#[test]
fn test_arbitrary_rational() {
let r = ArbitraryRational::num(22, 7).expect("Operation failed");
assert_eq!(r.to_string(), "22/7");
let a = ArbitraryRational::num(1, 3).expect("Operation failed");
let b = ArbitraryRational::num(1, 6).expect("Operation failed");
let sum = a + b;
assert_eq!(sum.to_string(), "1/2");
}
#[test]
fn test_arbitrary_complex() {
let z = ArbitraryComplex::re_2(3.0, 4.0);
let mag = z.abs();
assert!((mag.to_f64() - 5.0).abs() < 1e-10);
let conj = z.conj();
assert_eq!(conj.real().to_f64(), 3.0);
assert_eq!(conj.imag().to_f64(), -4.0);
}
#[test]
fn testprecision_builder() {
let x = ArbitraryPrecisionBuilder::new()
.decimalprecision(50)
.rounding(RoundingMode::Nearest)
.build_float();
assert!(x.decimalprecision() >= 49); }
#[test]
fn test_constants() {
let pi = utils::pi(256).expect("Operation failed");
let pi_str = pi.to_string();
assert!(pi_str.starts_with("3.14159265358979"), "pi = {pi_str}");
let e = utils::e(256).expect("Operation failed");
let e_str = e.to_string();
assert!(e_str.starts_with("2.71828182845904"), "e = {e_str}");
}
#[test]
fn test_prime_checking() {
let prime = ArbitraryInt::from_i64(97);
assert!(prime.is_probably_prime(20));
let composite = ArbitraryInt::from_i64(98);
assert!(!composite.is_probably_prime(20));
}
#[test]
fn test_gcd_lcm() {
let a = ArbitraryInt::from_i64(48);
let b = ArbitraryInt::from_i64(18);
let gcd = a.gcd(&b);
assert_eq!(gcd.to_string(), "6");
let lcm = a.lcm(&b);
assert_eq!(lcm.to_string(), "144");
}
#[test]
fn test_transcendental_functions() {
let x = ArbitraryFloat::from_f64_withprecision(0.5, 128).expect("Operation failed");
let sin_x = x.sin();
let cos_x = x.cos();
let identity = sin_x.clone() * sin_x + cos_x.clone() * cos_x;
assert!(
(identity.to_f64() - 1.0).abs() < 1e-10,
"identity = {}",
identity.to_f64()
);
let ln_x = x.ln().expect("Operation failed");
let exp_ln_x = ln_x.exp();
assert!(
(exp_ln_x.to_f64() - 0.5).abs() < 1e-10,
"exp_ln = {}",
exp_ln_x.to_f64()
);
}
#[test]
fn testerror_handling() {
let zero = ArbitraryRational::new();
assert!(zero.recip().is_err());
let neg = ArbitraryFloat::from_f64(-1.0);
assert!(neg.sqrt().is_err());
assert!(neg.ln().is_err());
let out_of_range = ArbitraryFloat::from_f64(2.0);
assert!(out_of_range.asin().is_err());
}
}