Skip to main content

scirs2_core/numeric/
arbitrary_precision.rs

1//! # Arbitrary Precision Numerical Computation Support
2//!
3//! This module provides arbitrary precision arithmetic capabilities for scientific computing,
4//! enabling calculations with user-defined precision levels for both integers and floating-point numbers.
5//!
6//! ## Backend
7//!
8//! All types are backed by **oxinum-\*** (Pure Rust, GMP/MPFR-free, C/Fortran-free):
9//! - Integer arithmetic: `oxinum_int::IBig`
10//! - Float arithmetic: `oxinum_float::DBig`
11//! - Rational arithmetic: `oxinum_rational::RBig`
12//! - Complex arithmetic: `oxinum_complex::CBig` (replaces the former `rug::Complex` backend)
13//!
14//! ## Precision model
15//!
16//! `ArbitraryPrecisionContext.floatprecision` continues to store bit-precision for API stability.
17//! Conversion to the decimal-digit precision used by `oxinum-float`'s `DBig` is done at the
18//! oxinum API boundary using the ratio 1 decimal digit ≈ 3.32 bits (log2(10)).
19//!
20//! ## Features
21//!
22//! - Arbitrary precision integers (`ArbitraryInt`) backed by `oxinum_int::IBig`
23//! - Arbitrary precision floating-point (`ArbitraryFloat`) backed by `oxinum_float::DBig`
24//! - Exact rational arithmetic (`ArbitraryRational`) backed by `oxinum_rational::RBig`
25//! - Arbitrary precision complex numbers (`ArbitraryComplex`) backed by `oxinum_complex::CBig`
26//! - Integration with existing ScientificNumber traits
27//! - Automatic precision tracking and management
28//! - Configurable precision contexts
29
30use crate::{
31    error::{CoreError, CoreResult, ErrorContext},
32    numeric::precision_tracking::PrecisionContext,
33    validation::check_positive,
34};
35use num_bigint::BigInt;
36// oxinum-int types (Pure Rust, GMP-free)
37use oxinum_int::{is_prime, IBig, UBig};
38// oxinum-float types and functions (Pure Rust, MPFR-free)
39use oxinum_float::{
40    compute_e, compute_ln2, compute_pi, cos, cosh, exp, ln, precision::with_precision, sin, sinh,
41    sqrt, tan, tanh, DBig,
42};
43// oxinum-rational types (Pure Rust)
44use oxinum_rational::{IBig as RIBig, RBig, UBig as RUBig};
45// oxinum-complex: Pure Rust arbitrary-precision complex (replaces rug::Complex)
46use oxinum_complex::CBig;
47use std::cmp::Ordering;
48use std::fmt;
49use std::ops::{Add, Div, Mul, Neg, Sub};
50use std::str::FromStr;
51use std::sync::RwLock;
52
53/// Global default precision for arbitrary precision operations (in bits).
54static DEFAULT_PRECISION: RwLock<u32> = RwLock::new(256);
55
56/// Bits per decimal digit constant (log2(10)).
57const BITS_PER_DECIMAL_DIGIT: f64 = std::f64::consts::LOG2_10;
58
59/// Convert bit precision to decimal digit count for oxinum APIs.
60fn bits_to_decimal_digits(bits: u32) -> usize {
61    ((bits as f64) / BITS_PER_DECIMAL_DIGIT).ceil() as usize
62}
63
64/// Convert an `f64` to `DBig`.
65///
66/// `dashu-float` does not implement `From<f64>` for `DBig`.  The most reliable
67/// route is to format the value with enough significant digits (17 is sufficient
68/// to represent any `f64` uniquely) and parse the resulting string.
69fn f64_to_dbig(v: f64) -> DBig {
70    if v.is_nan() {
71        return DBig::from(0u32);
72    }
73    if v.is_infinite() {
74        // DBig has no infinity; clamp to a very large value via a string.
75        return DBig::from_str("1e308").unwrap_or_else(|_| DBig::from(0u32));
76    }
77    let s = format!("{v:.17e}");
78    DBig::from_str(&s).unwrap_or_else(|_| {
79        // Fallback: use less precision.
80        let s2 = format!("{v}");
81        DBig::from_str(&s2).unwrap_or_else(|_| DBig::from(0u32))
82    })
83}
84
85/// Convert a `DBig` to `f64`.
86///
87/// `dashu-float`'s `to_f64()` returns `dashu_base::Approximation<f64>` (a `Rounded<f64>`).
88/// We extract the inner value via `.value()`.
89fn dbig_to_f64(v: &DBig) -> f64 {
90    v.to_f64().value()
91}
92
93/// Get the default precision for arbitrary precision operations (in bits).
94#[allow(dead_code)]
95pub fn get_defaultprecision() -> u32 {
96    *DEFAULT_PRECISION.read().expect("Operation failed")
97}
98
99/// Set the default precision for arbitrary precision operations (in bits).
100#[allow(dead_code)]
101pub fn setprecision(prec: u32) -> CoreResult<()> {
102    check_positive(prec as f64, "precision")?;
103    *DEFAULT_PRECISION.write().expect("Operation failed") = prec;
104    Ok(())
105}
106
107/// Precision context for arbitrary precision arithmetic.
108#[derive(Debug, Clone)]
109pub struct ArbitraryPrecisionContext {
110    /// Precision in bits for floating-point operations.
111    pub floatprecision: u32,
112    /// Maximum precision allowed.
113    pub maxprecision: u32,
114    /// Rounding mode.
115    pub rounding_mode: RoundingMode,
116    /// Whether to track precision loss.
117    pub trackprecision: bool,
118    /// Precision tracking context.
119    pub precision_context: Option<PrecisionContext>,
120}
121
122/// Rounding modes for arbitrary precision arithmetic.
123#[derive(Debug, Clone, Copy, PartialEq, Eq)]
124pub enum RoundingMode {
125    /// Round to nearest, ties to even.
126    Nearest,
127    /// Round toward zero.
128    Zero,
129    /// Round toward positive infinity.
130    Up,
131    /// Round toward negative infinity.
132    Down,
133    /// Round away from zero.
134    Away,
135}
136
137impl Default for ArbitraryPrecisionContext {
138    fn default() -> Self {
139        Self {
140            floatprecision: get_defaultprecision(),
141            maxprecision: 4096,
142            rounding_mode: RoundingMode::Nearest,
143            trackprecision: false,
144            precision_context: None,
145        }
146    }
147}
148
149impl ArbitraryPrecisionContext {
150    /// Create a new precision context with specified precision (in bits).
151    pub fn withprecision(precision: u32) -> CoreResult<Self> {
152        check_positive(precision as f64, "precision")?;
153        Ok(Self {
154            floatprecision: precision,
155            ..Default::default()
156        })
157    }
158
159    /// Create a context with precision tracking enabled.
160    pub fn withprecision_tracking(precision: u32) -> CoreResult<Self> {
161        let mut ctx = Self::withprecision(precision)?;
162        ctx.trackprecision = true;
163        let mut precision_ctx = PrecisionContext::new();
164        precision_ctx.precision = precision as f64 / BITS_PER_DECIMAL_DIGIT;
165        ctx.precision_context = Some(precision_ctx);
166        Ok(ctx)
167    }
168
169    /// Set the rounding mode.
170    pub fn with_rounding(mut self, mode: RoundingMode) -> Self {
171        self.rounding_mode = mode;
172        self
173    }
174
175    /// Set the maximum precision.
176    pub fn with_maxprecision(mut self, maxprec: u32) -> Self {
177        self.maxprecision = maxprec;
178        self
179    }
180
181    /// Return the float precision expressed in decimal digits.
182    fn decimal_digits(&self) -> usize {
183        bits_to_decimal_digits(self.floatprecision)
184    }
185}
186
187// ---------------------------------------------------------------------------
188// ArbitraryInt — backed by oxinum_int::IBig
189// ---------------------------------------------------------------------------
190
191/// Arbitrary precision integer backed by `oxinum_int::IBig` (Pure Rust).
192#[derive(Clone, PartialEq, Eq)]
193pub struct ArbitraryInt {
194    value: IBig,
195}
196
197impl ArbitraryInt {
198    /// Create a new arbitrary precision integer (value 0).
199    pub fn new() -> Self {
200        Self {
201            value: IBig::from(0i32),
202        }
203    }
204
205    /// Create from a regular 64-bit signed integer.
206    pub fn from_i64(n: i64) -> Self {
207        Self {
208            value: IBig::from(n),
209        }
210    }
211
212    /// Create from a string in the given radix (2..=36).
213    pub fn from_str_radix(s: &str, radix: i32) -> CoreResult<Self> {
214        if !(2..=36).contains(&radix) {
215            return Err(CoreError::ValidationError(ErrorContext::new(format!(
216                "Invalid radix {radix}: must be 2..=36"
217            ))));
218        }
219        oxinum_int::ibig_from_radix(s, radix as u32)
220            .map(|value| Self { value })
221            .map_err(|e| {
222                CoreError::ValidationError(ErrorContext::new(format!(
223                    "Failed to parse integer from string '{s}': {e}"
224                )))
225            })
226    }
227
228    /// Convert to `num_bigint::BigInt`.
229    pub fn to_bigint(&self) -> BigInt {
230        BigInt::from_str(&self.value.to_string()).expect("Operation failed")
231    }
232
233    /// Check if the number is (probably) prime.
234    ///
235    /// Uses Miller-Rabin with `reps` witnesses.  If `reps` is 0, a
236    /// deterministic witness set is used (correct for all n < 3.3 × 10²⁴).
237    pub fn is_probably_prime(&self, reps: u32) -> bool {
238        // Negative or zero → not prime.
239        if self.value <= IBig::from(1i32) {
240            return false;
241        }
242        // Convert the positive IBig to a string and parse as UBig for the primality test.
243        let s = self.value.to_string();
244        match UBig::from_str(&s) {
245            Ok(u) => is_prime(&u, reps),
246            Err(_) => false,
247        }
248    }
249
250    /// Compute factorial n!.
251    pub fn factorial(n: u32) -> Self {
252        let u = oxinum_int::factorial(n);
253        Self {
254            value: IBig::from(u),
255        }
256    }
257
258    /// Compute binomial coefficient C(n, k).
259    pub fn binomial(n: u32, k: u32) -> Self {
260        if k > n {
261            return Self::new();
262        }
263        let u = oxinum_int::binomial(n, k);
264        Self {
265            value: IBig::from(u),
266        }
267    }
268
269    /// Compute greatest common divisor.
270    pub fn gcd(&self, other: &Self) -> Self {
271        use oxinum_int::Gcd;
272        // GCD on IBig returns UBig (always non-negative); wrap back into IBig.
273        let a = self.value.clone();
274        let b = other.value.clone();
275        let g: UBig = a.gcd(&b);
276        Self {
277            value: IBig::from(g),
278        }
279    }
280
281    /// Compute least common multiple.
282    pub fn lcm(&self, other: &Self) -> Self {
283        if self.value == IBig::from(0i32) || other.value == IBig::from(0i32) {
284            return Self::new();
285        }
286        let gcd = self.gcd(other);
287        let product = self.value.clone() * other.value.clone();
288        Self {
289            value: product / gcd.value,
290        }
291    }
292
293    /// Modular exponentiation: (self ^ exp) mod modulus.
294    pub fn mod_pow(&self, exp: &Self, modulus: &Self) -> CoreResult<Self> {
295        if modulus.value == IBig::from(0i32) {
296            return Err(CoreError::DomainError(ErrorContext::new(
297                "Modulus cannot be zero",
298            )));
299        }
300        // Convert to UBig (non-negative) for the oxinum mod_pow.
301        let base_str = self.value.to_string();
302        let exp_str = exp.value.to_string();
303        let mod_str = modulus.value.to_string();
304        let base_u = UBig::from_str(&base_str).map_err(|_| {
305            CoreError::DomainError(ErrorContext::new("base must be non-negative for mod_pow"))
306        })?;
307        let exp_u = UBig::from_str(&exp_str).map_err(|_| {
308            CoreError::DomainError(ErrorContext::new(
309                "exponent must be non-negative for mod_pow",
310            ))
311        })?;
312        let mod_u = UBig::from_str(&mod_str).map_err(|_| {
313            CoreError::DomainError(ErrorContext::new(
314                "modulus must be non-negative for mod_pow",
315            ))
316        })?;
317        let result = oxinum_int::mod_pow(&base_u, &exp_u, &mod_u)
318            .map_err(|e| CoreError::DomainError(ErrorContext::new(format!("{e}"))))?;
319        Ok(Self {
320            value: IBig::from(result),
321        })
322    }
323
324    /// Get the absolute value.
325    pub fn abs(&self) -> Self {
326        use oxinum_core::Abs;
327        Self {
328            value: self.value.clone().abs(),
329        }
330    }
331
332    /// Get the sign (-1, 0, or 1).
333    pub fn signum(&self) -> i32 {
334        use oxinum_core::Signed;
335        // `.sign()` (from dashu_base::Signed) returns a `Sign` enum.
336        // IBig(0) has sign Positive, so check for zero separately.
337        if self.value == IBig::from(0i32) {
338            return 0;
339        }
340        match self.value.sign() {
341            oxinum_core::Sign::Positive => 1,
342            oxinum_core::Sign::Negative => -1,
343        }
344    }
345}
346
347impl fmt::Display for ArbitraryInt {
348    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
349        write!(f, "{}", self.value)
350    }
351}
352
353impl fmt::Debug for ArbitraryInt {
354    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
355        write!(f, "ArbitraryInt({})", self.value)
356    }
357}
358
359impl Default for ArbitraryInt {
360    fn default() -> Self {
361        Self::new()
362    }
363}
364
365impl Add for ArbitraryInt {
366    type Output = Self;
367    fn add(self, rhs: Self) -> Self::Output {
368        Self {
369            value: self.value + rhs.value,
370        }
371    }
372}
373
374impl Sub for ArbitraryInt {
375    type Output = Self;
376    fn sub(self, rhs: Self) -> Self::Output {
377        Self {
378            value: self.value - rhs.value,
379        }
380    }
381}
382
383impl Mul for ArbitraryInt {
384    type Output = Self;
385    fn mul(self, rhs: Self) -> Self::Output {
386        Self {
387            value: self.value * rhs.value,
388        }
389    }
390}
391
392impl Div for ArbitraryInt {
393    type Output = Self;
394    fn div(self, rhs: Self) -> Self::Output {
395        Self {
396            value: self.value / rhs.value,
397        }
398    }
399}
400
401impl Neg for ArbitraryInt {
402    type Output = Self;
403    fn neg(self) -> Self::Output {
404        Self { value: -self.value }
405    }
406}
407
408// ---------------------------------------------------------------------------
409// ArbitraryFloat — backed by oxinum_float::DBig
410// ---------------------------------------------------------------------------
411
412/// Arbitrary precision floating-point number backed by `oxinum_float::DBig` (Pure Rust).
413///
414/// `DBig` is a base-10 decimal big-float.  The `ArbitraryPrecisionContext` stores
415/// precision in **bits** (for API stability), but all oxinum calls receive the
416/// equivalent decimal-digit count.
417#[derive(Clone)]
418pub struct ArbitraryFloat {
419    value: DBig,
420    context: ArbitraryPrecisionContext,
421}
422
423impl ArbitraryFloat {
424    /// Create a new zero-valued arbitrary precision float with the default precision.
425    pub fn new() -> Self {
426        let prec = get_defaultprecision();
427        let context = ArbitraryPrecisionContext::default();
428        let digits = bits_to_decimal_digits(prec);
429        let value = with_precision(&DBig::from(0u32), digits);
430        Self { value, context }
431    }
432
433    /// Create with a specific bit precision.
434    pub fn withprecision(prec: u32) -> CoreResult<Self> {
435        let context = ArbitraryPrecisionContext::withprecision(prec)?;
436        let digits = bits_to_decimal_digits(prec);
437        let value = with_precision(&DBig::from(0u32), digits);
438        Ok(Self { value, context })
439    }
440
441    /// Create with a specific precision context.
442    pub fn with_context(context: ArbitraryPrecisionContext) -> Self {
443        let digits = context.decimal_digits();
444        let value = with_precision(&DBig::from(0u32), digits);
445        Self { value, context }
446    }
447
448    /// Create from f64 with the default precision.
449    pub fn from_f64(v: f64) -> Self {
450        let prec = get_defaultprecision();
451        let context = ArbitraryPrecisionContext::default();
452        let digits = bits_to_decimal_digits(prec);
453        let raw = f64_to_dbig(v);
454        let value = with_precision(&raw, digits);
455        Self { value, context }
456    }
457
458    /// Create from f64 with a specific bit precision.
459    pub fn from_f64_withprecision(v: f64, prec: u32) -> CoreResult<Self> {
460        let context = ArbitraryPrecisionContext::withprecision(prec)?;
461        let digits = bits_to_decimal_digits(prec);
462        let raw = f64_to_dbig(v);
463        let value = with_precision(&raw, digits);
464        Ok(Self { value, context })
465    }
466
467    /// Parse a decimal string with a specific bit precision.
468    pub fn from_strprec(s: &str, prec: u32) -> CoreResult<Self> {
469        let context = ArbitraryPrecisionContext::withprecision(prec)?;
470        let digits = bits_to_decimal_digits(prec);
471        let parsed = DBig::from_str(s)
472            .map_err(|e| CoreError::ValidationError(ErrorContext::new(format!("{e}"))))?;
473        let value = with_precision(&parsed, digits);
474        Ok(Self { value, context })
475    }
476
477    /// Get the bit precision stored in the context.
478    pub fn precision(&self) -> u32 {
479        self.context.floatprecision
480    }
481
482    /// Get the precision in decimal digits.
483    pub fn decimalprecision(&self) -> u32 {
484        self.context.decimal_digits() as u32
485    }
486
487    /// Return a new value rebound to the given bit precision.
488    pub fn setprecision(&self, prec: u32) -> CoreResult<Self> {
489        let mut context = self.context.clone();
490        context.floatprecision = prec;
491        let digits = bits_to_decimal_digits(prec);
492        let value = with_precision(&self.value, digits);
493        Ok(Self { value, context })
494    }
495
496    /// Convert to f64 (may lose precision).
497    pub fn to_f64(&self) -> f64 {
498        dbig_to_f64(&self.value)
499    }
500
501    /// Check if the value is finite (DBig is always finite — no NaN/infinity).
502    pub fn is_finite(&self) -> bool {
503        true
504    }
505
506    /// Check if the value is infinite (DBig has no infinity).
507    pub fn is_infinite(&self) -> bool {
508        false
509    }
510
511    /// Check if the value is NaN (DBig has no NaN).
512    pub fn is_nan(&self) -> bool {
513        false
514    }
515
516    /// Check if the value is zero.
517    pub fn is_zero(&self) -> bool {
518        self.value == DBig::from(0u32)
519    }
520
521    /// Get the absolute value.
522    pub fn abs(&self) -> Self {
523        use oxinum_core::Abs;
524        Self {
525            value: self.value.clone().abs(),
526            context: self.context.clone(),
527        }
528    }
529
530    /// Square root.
531    pub fn sqrt(&self) -> CoreResult<Self> {
532        let zero = DBig::from(0u32);
533        if self.value < zero {
534            return Err(CoreError::DomainError(ErrorContext::new(
535                "Square root of negative number",
536            )));
537        }
538        let digits = self.context.decimal_digits();
539        let result = sqrt(&self.value, digits)
540            .map_err(|e| CoreError::DomainError(ErrorContext::new(format!("{e}"))))?;
541        Ok(Self {
542            value: result,
543            context: self.context.clone(),
544        })
545    }
546
547    /// Natural logarithm.
548    pub fn ln(&self) -> CoreResult<Self> {
549        let zero = DBig::from(0u32);
550        if self.value <= zero {
551            return Err(CoreError::DomainError(ErrorContext::new(
552                "Logarithm of non-positive number",
553            )));
554        }
555        let digits = self.context.decimal_digits();
556        let result = ln(&self.value, digits)
557            .map_err(|e| CoreError::DomainError(ErrorContext::new(format!("{e}"))))?;
558        Ok(Self {
559            value: result,
560            context: self.context.clone(),
561        })
562    }
563
564    /// Exponential function.
565    pub fn exp(&self) -> Self {
566        let digits = self.context.decimal_digits();
567        let result = exp(&self.value, digits).unwrap_or_else(|_| DBig::from(1u32));
568        Self {
569            value: result,
570            context: self.context.clone(),
571        }
572    }
573
574    /// Power function: self ^ exponent.
575    pub fn pow(&self, exponent: &Self) -> Self {
576        use oxinum_float::pow as oxinum_pow;
577        let digits = self.context.decimal_digits();
578        let result =
579            oxinum_pow(&self.value, &exponent.value, digits).unwrap_or_else(|_| DBig::from(1u32));
580        Self {
581            value: result,
582            context: self.context.clone(),
583        }
584    }
585
586    /// Sine.
587    pub fn sin(&self) -> Self {
588        let digits = self.context.decimal_digits();
589        let result = sin(&self.value, digits).unwrap_or_else(|_| DBig::from(0u32));
590        Self {
591            value: result,
592            context: self.context.clone(),
593        }
594    }
595
596    /// Cosine.
597    pub fn cos(&self) -> Self {
598        let digits = self.context.decimal_digits();
599        let result = cos(&self.value, digits).unwrap_or_else(|_| DBig::from(1u32));
600        Self {
601            value: result,
602            context: self.context.clone(),
603        }
604    }
605
606    /// Tangent.
607    pub fn tan(&self) -> Self {
608        let digits = self.context.decimal_digits();
609        let result = tan(&self.value, digits).unwrap_or_else(|_| DBig::from(0u32));
610        Self {
611            value: result,
612            context: self.context.clone(),
613        }
614    }
615
616    /// Arcsine.
617    pub fn asin(&self) -> CoreResult<Self> {
618        // |x| must be <= 1.  Check against 1.0.
619        let one = DBig::from(1u32);
620        let abs_val = {
621            use oxinum_core::Abs;
622            self.value.clone().abs()
623        };
624        if abs_val > one {
625            return Err(CoreError::DomainError(ErrorContext::new(
626                "Arcsine argument out of range [-1, 1]",
627            )));
628        }
629        // asin is not directly provided by oxinum-float; implement via atan:
630        //   asin(x) = atan(x / sqrt(1 - x²))
631        let digits = self.context.decimal_digits();
632        let one_minus_x2 = {
633            let x2 = self.value.clone() * self.value.clone();
634            DBig::from(1u32) - x2
635        };
636        let denom = sqrt(&one_minus_x2, digits + 4)
637            .map_err(|e| CoreError::DomainError(ErrorContext::new(format!("{e}"))))?;
638        let zero = DBig::from(0u32);
639        if denom == zero {
640            // x = ±1: asin(±1) = ±π/2
641            let pi = compute_pi(digits);
642            let two = DBig::from(2u32);
643            let half_pi = pi / two;
644            // DBig comparison: negative when self.value < 0
645            let result = if self.value < zero { -half_pi } else { half_pi };
646            return Ok(Self {
647                value: result,
648                context: self.context.clone(),
649            });
650        }
651        let ratio = self.value.clone() / denom;
652        use oxinum_float::atan as oxinum_atan;
653        let result = oxinum_atan(&ratio, digits)
654            .map_err(|e| CoreError::DomainError(ErrorContext::new(format!("{e}"))))?;
655        Ok(Self {
656            value: result,
657            context: self.context.clone(),
658        })
659    }
660
661    /// Arccosine.
662    pub fn acos(&self) -> CoreResult<Self> {
663        // acos(x) = π/2 - asin(x)
664        let one = DBig::from(1u32);
665        let abs_val = {
666            use oxinum_core::Abs;
667            self.value.clone().abs()
668        };
669        if abs_val > one {
670            return Err(CoreError::DomainError(ErrorContext::new(
671                "Arccosine argument out of range [-1, 1]",
672            )));
673        }
674        let digits = self.context.decimal_digits();
675        let pi = compute_pi(digits + 4);
676        let two = DBig::from(2u32);
677        let half_pi = pi / two;
678        let asin_val = self.asin()?;
679        Ok(Self {
680            value: half_pi - asin_val.value,
681            context: self.context.clone(),
682        })
683    }
684
685    /// Arctangent.
686    pub fn atan(&self) -> Self {
687        use oxinum_float::atan as oxinum_atan;
688        let digits = self.context.decimal_digits();
689        let result = oxinum_atan(&self.value, digits).unwrap_or_else(|_| DBig::from(0u32));
690        Self {
691            value: result,
692            context: self.context.clone(),
693        }
694    }
695
696    /// Two-argument arctangent: atan2(self, x).
697    pub fn atan2(&self, x: &Self) -> Self {
698        use oxinum_float::atan2 as oxinum_atan2;
699        let digits = self.context.decimal_digits();
700        let result =
701            oxinum_atan2(&self.value, &x.value, digits).unwrap_or_else(|_| DBig::from(0u32));
702        Self {
703            value: result,
704            context: self.context.clone(),
705        }
706    }
707
708    /// Hyperbolic sine.
709    pub fn sinh(&self) -> Self {
710        let digits = self.context.decimal_digits();
711        let result = sinh(&self.value, digits).unwrap_or_else(|_| DBig::from(0u32));
712        Self {
713            value: result,
714            context: self.context.clone(),
715        }
716    }
717
718    /// Hyperbolic cosine.
719    pub fn cosh(&self) -> Self {
720        let digits = self.context.decimal_digits();
721        let result = cosh(&self.value, digits).unwrap_or_else(|_| DBig::from(1u32));
722        Self {
723            value: result,
724            context: self.context.clone(),
725        }
726    }
727
728    /// Hyperbolic tangent.
729    pub fn tanh(&self) -> Self {
730        let digits = self.context.decimal_digits();
731        let result = tanh(&self.value, digits).unwrap_or_else(|_| DBig::from(0u32));
732        Self {
733            value: result,
734            context: self.context.clone(),
735        }
736    }
737
738    // -----------------------------------------------------------------------
739    // Mathematical constants
740    // -----------------------------------------------------------------------
741
742    /// Compute π to the given bit precision.
743    pub fn prec_2(prec: u32) -> CoreResult<Self> {
744        let context = ArbitraryPrecisionContext::withprecision(prec)?;
745        let digits = bits_to_decimal_digits(prec);
746        let value = with_precision(&compute_pi(digits), digits);
747        Ok(Self { value, context })
748    }
749
750    /// Compute e (Euler's number) to the given bit precision.
751    pub fn prec_3(prec: u32) -> CoreResult<Self> {
752        let context = ArbitraryPrecisionContext::withprecision(prec)?;
753        let digits = bits_to_decimal_digits(prec);
754        let value = with_precision(&compute_e(digits), digits);
755        Ok(Self { value, context })
756    }
757
758    /// Compute ln(2) to the given bit precision.
759    pub fn prec_4(prec: u32) -> CoreResult<Self> {
760        let context = ArbitraryPrecisionContext::withprecision(prec)?;
761        let digits = bits_to_decimal_digits(prec);
762        let value = with_precision(&compute_ln2(digits), digits);
763        Ok(Self { value, context })
764    }
765}
766
767impl fmt::Display for ArbitraryFloat {
768    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
769        write!(f, "{}", self.value)
770    }
771}
772
773impl fmt::Debug for ArbitraryFloat {
774    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
775        write!(
776            f,
777            "ArbitraryFloat({}, {} bits)",
778            self.value,
779            self.precision()
780        )
781    }
782}
783
784impl PartialEq for ArbitraryFloat {
785    fn eq(&self, other: &Self) -> bool {
786        self.value == other.value
787    }
788}
789
790impl PartialOrd for ArbitraryFloat {
791    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
792        self.value.partial_cmp(&other.value)
793    }
794}
795
796impl Default for ArbitraryFloat {
797    fn default() -> Self {
798        Self::new()
799    }
800}
801
802impl Add for ArbitraryFloat {
803    type Output = Self;
804    fn add(self, rhs: Self) -> Self::Output {
805        Self {
806            value: self.value + rhs.value,
807            context: self.context,
808        }
809    }
810}
811
812impl Sub for ArbitraryFloat {
813    type Output = Self;
814    fn sub(self, rhs: Self) -> Self::Output {
815        Self {
816            value: self.value - rhs.value,
817            context: self.context,
818        }
819    }
820}
821
822impl Mul for ArbitraryFloat {
823    type Output = Self;
824    fn mul(self, rhs: Self) -> Self::Output {
825        Self {
826            value: self.value * rhs.value,
827            context: self.context,
828        }
829    }
830}
831
832impl Div for ArbitraryFloat {
833    type Output = Self;
834    fn div(self, rhs: Self) -> Self::Output {
835        Self {
836            value: self.value / rhs.value,
837            context: self.context,
838        }
839    }
840}
841
842impl Neg for ArbitraryFloat {
843    type Output = Self;
844    fn neg(self) -> Self::Output {
845        Self {
846            value: -self.value,
847            context: self.context,
848        }
849    }
850}
851
852// ---------------------------------------------------------------------------
853// ArbitraryRational — backed by oxinum_rational::RBig
854// ---------------------------------------------------------------------------
855
856/// Arbitrary precision rational number backed by `oxinum_rational::RBig` (Pure Rust).
857#[derive(Clone, PartialEq, Eq)]
858pub struct ArbitraryRational {
859    value: RBig,
860}
861
862impl ArbitraryRational {
863    /// Create a new rational number (value 0/1).
864    pub fn new() -> Self {
865        Self {
866            value: RBig::from(0u32),
867        }
868    }
869
870    /// Create from numerator and denominator (i64).
871    pub fn num(num: i64, den: i64) -> CoreResult<Self> {
872        if den == 0 {
873            return Err(CoreError::DomainError(ErrorContext::new(
874                "Denominator cannot be zero",
875            )));
876        }
877        let n = RIBig::from(num);
878        let d = RUBig::from(den.unsigned_abs());
879        let signed_n = if den < 0 { -n } else { n };
880        Ok(Self {
881            value: RBig::from_parts(signed_n, d),
882        })
883    }
884
885    /// Create from arbitrary precision integers.
886    pub fn num_2(num: &ArbitraryInt, den: &ArbitraryInt) -> CoreResult<Self> {
887        if den.value == IBig::from(0i32) {
888            return Err(CoreError::DomainError(ErrorContext::new(
889                "Denominator cannot be zero",
890            )));
891        }
892        let n = RIBig::from_str(&num.value.to_string()).map_err(|_| {
893            CoreError::ValidationError(ErrorContext::new("numerator conversion failed"))
894        })?;
895        let d_ibig = IBig::from_str(&den.value.to_string()).map_err(|_| {
896            CoreError::ValidationError(ErrorContext::new("denominator conversion failed"))
897        })?;
898        // Take absolute value of denominator; fold sign into numerator.
899        use oxinum_core::Abs;
900        let (n_final, d_ubig) = if d_ibig < IBig::from(0i32) {
901            let neg_n = RIBig::from_str(&(-num.value.clone()).to_string())
902                .unwrap_or_else(|_| RIBig::from(0i32));
903            let d_abs = RUBig::from_str(&d_ibig.clone().abs().to_string()).unwrap_or(RUBig::ONE);
904            (neg_n, d_abs)
905        } else {
906            let d_abs = RUBig::from_str(&d_ibig.to_string()).unwrap_or(RUBig::ONE);
907            (n, d_abs)
908        };
909        Ok(Self {
910            value: RBig::from_parts(n_final, d_ubig),
911        })
912    }
913
914    /// Parse a rational from a string (e.g. "22/7").
915    #[deprecated(since = "0.1.0", note = "Use str::parse() instead")]
916    pub fn parse_rational(s: &str) -> CoreResult<Self> {
917        s.parse()
918    }
919
920    /// Convert to f64 (may lose precision).
921    pub fn to_f64(&self) -> f64 {
922        use oxinum_rational::to_f64 as rbig_to_f64;
923        rbig_to_f64(&self.value)
924    }
925
926    /// Convert to an arbitrary precision float at the given bit precision.
927    pub fn to_arbitrary_float(&self, prec: u32) -> CoreResult<ArbitraryFloat> {
928        let context = ArbitraryPrecisionContext::withprecision(prec)?;
929        let digits = bits_to_decimal_digits(prec);
930        // Compute numerator / denominator in high precision.
931        let num_str = self.value.numerator().to_string();
932        let den_str = self.value.denominator().to_string();
933        let n = DBig::from_str(&num_str)
934            .map_err(|e| CoreError::ValidationError(ErrorContext::new(format!("{e}"))))?;
935        let d = DBig::from_str(&den_str)
936            .map_err(|e| CoreError::ValidationError(ErrorContext::new(format!("{e}"))))?;
937        let n_prec = with_precision(&n, digits + 4);
938        let d_prec = with_precision(&d, digits + 4);
939        let value = with_precision(&(n_prec / d_prec), digits);
940        Ok(ArbitraryFloat { value, context })
941    }
942
943    /// Get numerator as an `ArbitraryInt`.
944    pub fn numerator(&self) -> ArbitraryInt {
945        let n_str = self.value.numerator().to_string();
946        let v = IBig::from_str(&n_str).unwrap_or_else(|_| IBig::from(0i32));
947        ArbitraryInt { value: v }
948    }
949
950    /// Get denominator as an `ArbitraryInt`.
951    pub fn denominator(&self) -> ArbitraryInt {
952        let d_str = self.value.denominator().to_string();
953        let v = IBig::from_str(&d_str).unwrap_or_else(|_| IBig::from(1i32));
954        ArbitraryInt { value: v }
955    }
956
957    /// Get the absolute value.
958    pub fn abs(&self) -> Self {
959        use oxinum_rational::rational_abs;
960        Self {
961            value: rational_abs(&self.value),
962        }
963    }
964
965    /// Get the reciprocal.
966    pub fn recip(&self) -> CoreResult<Self> {
967        if self.value == RBig::from(0u32) {
968            return Err(CoreError::DomainError(ErrorContext::new(
969                "Cannot take reciprocal of zero",
970            )));
971        }
972        use oxinum_rational::rational_reciprocal;
973        let recip = rational_reciprocal(&self.value)
974            .map_err(|e| CoreError::DomainError(ErrorContext::new(format!("{e}"))))?;
975        Ok(Self { value: recip })
976    }
977}
978
979impl fmt::Display for ArbitraryRational {
980    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
981        write!(f, "{}", self.value)
982    }
983}
984
985impl fmt::Debug for ArbitraryRational {
986    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
987        write!(f, "ArbitraryRational({})", self.value)
988    }
989}
990
991impl Default for ArbitraryRational {
992    fn default() -> Self {
993        Self::new()
994    }
995}
996
997impl FromStr for ArbitraryRational {
998    type Err = CoreError;
999
1000    fn from_str(s: &str) -> Result<Self, Self::Err> {
1001        // Try "numerator/denominator" format first.
1002        if let Some(slash) = s.find('/') {
1003            let num_s = &s[..slash];
1004            let den_s = &s[slash + 1..];
1005            let n = RIBig::from_str(num_s.trim()).map_err(|_| {
1006                CoreError::ValidationError(ErrorContext::new(format!(
1007                    "Failed to parse rational from string: {s}"
1008                )))
1009            })?;
1010            let d = RUBig::from_str(den_s.trim()).map_err(|_| {
1011                CoreError::ValidationError(ErrorContext::new(format!(
1012                    "Failed to parse rational from string: {s}"
1013                )))
1014            })?;
1015            return Ok(Self {
1016                value: RBig::from_parts(n, d),
1017            });
1018        }
1019        // Fall back to integer interpretation.
1020        let n = RIBig::from_str(s.trim()).map_err(|_| {
1021            CoreError::ValidationError(ErrorContext::new(format!(
1022                "Failed to parse rational from string: {s}"
1023            )))
1024        })?;
1025        Ok(Self {
1026            value: RBig::from_parts(n, RUBig::ONE),
1027        })
1028    }
1029}
1030
1031impl Add for ArbitraryRational {
1032    type Output = Self;
1033    fn add(self, rhs: Self) -> Self::Output {
1034        Self {
1035            value: self.value + rhs.value,
1036        }
1037    }
1038}
1039
1040impl Sub for ArbitraryRational {
1041    type Output = Self;
1042    fn sub(self, rhs: Self) -> Self::Output {
1043        Self {
1044            value: self.value - rhs.value,
1045        }
1046    }
1047}
1048
1049impl Mul for ArbitraryRational {
1050    type Output = Self;
1051    fn mul(self, rhs: Self) -> Self::Output {
1052        Self {
1053            value: self.value * rhs.value,
1054        }
1055    }
1056}
1057
1058impl Div for ArbitraryRational {
1059    type Output = Self;
1060    fn div(self, rhs: Self) -> Self::Output {
1061        Self {
1062            value: self.value / rhs.value,
1063        }
1064    }
1065}
1066
1067impl Neg for ArbitraryRational {
1068    type Output = Self;
1069    fn neg(self) -> Self::Output {
1070        Self { value: -self.value }
1071    }
1072}
1073
1074// ---------------------------------------------------------------------------
1075// ArbitraryComplex — backed by oxinum_complex::CBig (Pure Rust, GMP/MPC-free)
1076// ---------------------------------------------------------------------------
1077
1078/// Arbitrary precision complex number backed by `oxinum_complex::CBig` (Pure Rust).
1079///
1080/// `CBig` is a decimal arbitrary-precision complex number whose real and imaginary
1081/// parts are each a `DBig`.  This struct provides the same public API as the former
1082/// `rug::Complex`-backed implementation while being fully GMP/MPFR/MPC-free.
1083#[derive(Clone)]
1084pub struct ArbitraryComplex {
1085    value: CBig,
1086    context: ArbitraryPrecisionContext,
1087}
1088
1089impl ArbitraryComplex {
1090    /// Returns the decimal-digit precision derived from the bit precision stored
1091    /// in the context.  Used when calling CBig transcendental methods.
1092    fn prec_digits(&self) -> usize {
1093        bits_to_decimal_digits(self.context.floatprecision)
1094    }
1095
1096    /// Create a new complex number with default precision.
1097    pub fn new() -> Self {
1098        Self {
1099            value: CBig::zero(),
1100            context: ArbitraryPrecisionContext::default(),
1101        }
1102    }
1103
1104    /// Create with specific bit precision.
1105    pub fn prec(prec: u32) -> CoreResult<Self> {
1106        let context = ArbitraryPrecisionContext::withprecision(prec)?;
1107        Ok(Self {
1108            value: CBig::zero(),
1109            context,
1110        })
1111    }
1112
1113    /// Create from real and imaginary `ArbitraryFloat` parts.
1114    pub fn re(re: &ArbitraryFloat, im: &ArbitraryFloat) -> Self {
1115        let prec = re.precision().max(im.precision());
1116        let context = re.context.clone();
1117        let re_f = re.to_f64();
1118        let im_f = im.to_f64();
1119        // CBig::from_f64 rejects NaN/Inf; fall back to zero for non-finite inputs.
1120        let value = CBig::from_f64(re_f, im_f).unwrap_or_else(|_| CBig::zero());
1121        Self {
1122            value,
1123            context: ArbitraryPrecisionContext {
1124                floatprecision: prec,
1125                ..context
1126            },
1127        }
1128    }
1129
1130    /// Create from f64 real and imaginary parts.
1131    pub fn re_2(re: f64, im: f64) -> Self {
1132        let value = CBig::from_f64(re, im).unwrap_or_else(|_| CBig::zero());
1133        Self {
1134            value,
1135            context: ArbitraryPrecisionContext::default(),
1136        }
1137    }
1138
1139    /// Get the real part as an `ArbitraryFloat`.
1140    pub fn real(&self) -> ArbitraryFloat {
1141        let (re_f64, _) = self.value.to_f64_parts();
1142        ArbitraryFloat::from_f64(re_f64)
1143    }
1144
1145    /// Get the imaginary part as an `ArbitraryFloat`.
1146    pub fn imag(&self) -> ArbitraryFloat {
1147        let (_, im_f64) = self.value.to_f64_parts();
1148        ArbitraryFloat::from_f64(im_f64)
1149    }
1150
1151    /// Get the magnitude (absolute value) as an `ArbitraryFloat`.
1152    pub fn abs(&self) -> ArbitraryFloat {
1153        let digits = self.prec_digits();
1154        let mag_f64 = self
1155            .value
1156            .abs(digits)
1157            .map(|d| d.to_f64().value())
1158            .unwrap_or(0.0);
1159        ArbitraryFloat::from_f64(mag_f64)
1160    }
1161
1162    /// Get the phase (argument) as an `ArbitraryFloat`.
1163    pub fn arg(&self) -> ArbitraryFloat {
1164        let digits = self.prec_digits();
1165        let arg_f64 = self
1166            .value
1167            .arg(digits)
1168            .map(|d| d.to_f64().value())
1169            .unwrap_or(0.0);
1170        ArbitraryFloat::from_f64(arg_f64)
1171    }
1172
1173    /// Complex conjugate.
1174    pub fn conj(&self) -> Self {
1175        Self {
1176            value: self.value.conj(),
1177            context: self.context.clone(),
1178        }
1179    }
1180
1181    /// Natural logarithm.
1182    ///
1183    /// Returns the principal value `ln|z| + i·arg(z)`.
1184    /// If `z` is zero (ln undefined), returns a zero complex.
1185    pub fn ln(&self) -> Self {
1186        let digits = self.prec_digits();
1187        let value = self.value.ln(digits).unwrap_or_else(|_| CBig::zero());
1188        Self {
1189            value,
1190            context: self.context.clone(),
1191        }
1192    }
1193
1194    /// Exponential function.
1195    pub fn exp(&self) -> Self {
1196        let digits = self.prec_digits();
1197        let value = self.value.exp(digits).unwrap_or_else(|_| CBig::zero());
1198        Self {
1199            value,
1200            context: self.context.clone(),
1201        }
1202    }
1203
1204    /// Power function: self ^ exponent.
1205    ///
1206    /// Computed as `exp(exponent * ln(self))`.
1207    pub fn pow(&self, exp: &Self) -> Self {
1208        let digits = self.prec_digits();
1209        let value = self
1210            .value
1211            .pow(&exp.value, digits)
1212            .unwrap_or_else(|_| CBig::zero());
1213        Self {
1214            value,
1215            context: self.context.clone(),
1216        }
1217    }
1218
1219    /// Square root.
1220    pub fn sqrt(&self) -> Self {
1221        let digits = self.prec_digits();
1222        let value = self.value.sqrt(digits).unwrap_or_else(|_| CBig::zero());
1223        Self {
1224            value,
1225            context: self.context.clone(),
1226        }
1227    }
1228}
1229
1230impl fmt::Display for ArbitraryComplex {
1231    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1232        let (re, im) = self.value.to_f64_parts();
1233        if im >= 0.0 {
1234            write!(f, "{} + {}i", re, im)
1235        } else {
1236            write!(f, "{} - {}i", re, -im)
1237        }
1238    }
1239}
1240
1241impl fmt::Debug for ArbitraryComplex {
1242    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1243        write!(
1244            f,
1245            "ArbitraryComplex({}, {} bits)",
1246            self, self.context.floatprecision
1247        )
1248    }
1249}
1250
1251impl PartialEq for ArbitraryComplex {
1252    fn eq(&self, other: &Self) -> bool {
1253        self.value == other.value
1254    }
1255}
1256
1257impl Default for ArbitraryComplex {
1258    fn default() -> Self {
1259        Self::new()
1260    }
1261}
1262
1263impl Add for ArbitraryComplex {
1264    type Output = Self;
1265    fn add(self, rhs: Self) -> Self::Output {
1266        Self {
1267            value: self.value + rhs.value,
1268            context: self.context,
1269        }
1270    }
1271}
1272
1273impl Sub for ArbitraryComplex {
1274    type Output = Self;
1275    fn sub(self, rhs: Self) -> Self::Output {
1276        Self {
1277            value: self.value - rhs.value,
1278            context: self.context,
1279        }
1280    }
1281}
1282
1283impl Mul for ArbitraryComplex {
1284    type Output = Self;
1285    fn mul(self, rhs: Self) -> Self::Output {
1286        Self {
1287            value: self.value * rhs.value,
1288            context: self.context,
1289        }
1290    }
1291}
1292
1293impl Div for ArbitraryComplex {
1294    type Output = Self;
1295    fn div(self, rhs: Self) -> Self::Output {
1296        Self {
1297            value: self.value / rhs.value,
1298            context: self.context,
1299        }
1300    }
1301}
1302
1303impl Neg for ArbitraryComplex {
1304    type Output = Self;
1305    fn neg(self) -> Self::Output {
1306        Self {
1307            value: -self.value,
1308            context: self.context,
1309        }
1310    }
1311}
1312
1313// ---------------------------------------------------------------------------
1314// Conversion trait
1315// ---------------------------------------------------------------------------
1316
1317/// Conversion trait for arbitrary precision types.
1318pub trait ToArbitraryPrecision {
1319    /// The arbitrary precision output type.
1320    type ArbitraryType;
1321
1322    /// Convert to arbitrary precision with the default precision.
1323    fn to_arbitrary(&self) -> Self::ArbitraryType;
1324
1325    /// Convert to arbitrary precision with a specified bit precision.
1326    fn to_arbitraryprec(&self, prec: u32) -> CoreResult<Self::ArbitraryType>;
1327}
1328
1329impl ToArbitraryPrecision for i32 {
1330    type ArbitraryType = ArbitraryInt;
1331
1332    fn to_arbitrary(&self) -> Self::ArbitraryType {
1333        ArbitraryInt::from_i64(*self as i64)
1334    }
1335
1336    fn to_arbitraryprec(&self, _prec: u32) -> CoreResult<Self::ArbitraryType> {
1337        Ok(self.to_arbitrary())
1338    }
1339}
1340
1341impl ToArbitraryPrecision for i64 {
1342    type ArbitraryType = ArbitraryInt;
1343
1344    fn to_arbitrary(&self) -> Self::ArbitraryType {
1345        ArbitraryInt::from_i64(*self)
1346    }
1347
1348    fn to_arbitraryprec(&self, _prec: u32) -> CoreResult<Self::ArbitraryType> {
1349        Ok(self.to_arbitrary())
1350    }
1351}
1352
1353impl ToArbitraryPrecision for f32 {
1354    type ArbitraryType = ArbitraryFloat;
1355
1356    fn to_arbitrary(&self) -> Self::ArbitraryType {
1357        ArbitraryFloat::from_f64(*self as f64)
1358    }
1359
1360    fn to_arbitraryprec(&self, prec: u32) -> CoreResult<Self::ArbitraryType> {
1361        ArbitraryFloat::from_f64_withprecision(*self as f64, prec)
1362    }
1363}
1364
1365impl ToArbitraryPrecision for f64 {
1366    type ArbitraryType = ArbitraryFloat;
1367
1368    fn to_arbitrary(&self) -> Self::ArbitraryType {
1369        ArbitraryFloat::from_f64(*self)
1370    }
1371
1372    fn to_arbitraryprec(&self, prec: u32) -> CoreResult<Self::ArbitraryType> {
1373        ArbitraryFloat::from_f64_withprecision(*self, prec)
1374    }
1375}
1376
1377// ---------------------------------------------------------------------------
1378// Builder
1379// ---------------------------------------------------------------------------
1380
1381/// Builder for arbitrary precision calculations.
1382pub struct ArbitraryPrecisionBuilder {
1383    context: ArbitraryPrecisionContext,
1384}
1385
1386impl ArbitraryPrecisionBuilder {
1387    /// Create a new builder with default settings.
1388    pub fn new() -> Self {
1389        Self {
1390            context: ArbitraryPrecisionContext::default(),
1391        }
1392    }
1393
1394    /// Set the precision in bits.
1395    pub fn precision(mut self, prec: u32) -> Self {
1396        self.context.floatprecision = prec;
1397        self
1398    }
1399
1400    /// Set the precision in decimal digits (converted to bits internally).
1401    pub fn decimalprecision(mut self, digits: u32) -> Self {
1402        self.context.floatprecision = ((digits as f64) * BITS_PER_DECIMAL_DIGIT) as u32;
1403        self
1404    }
1405
1406    /// Set the rounding mode.
1407    pub fn rounding(mut self, mode: RoundingMode) -> Self {
1408        self.context.rounding_mode = mode;
1409        self
1410    }
1411
1412    /// Enable or disable precision tracking.
1413    pub fn trackprecision(mut self, track: bool) -> Self {
1414        self.context.trackprecision = track;
1415        if track && self.context.precision_context.is_none() {
1416            let mut precision_ctx = PrecisionContext::new();
1417            precision_ctx.precision = self.context.floatprecision as f64 / BITS_PER_DECIMAL_DIGIT;
1418            self.context.precision_context = Some(precision_ctx);
1419        }
1420        self
1421    }
1422
1423    /// Build an `ArbitraryFloat`.
1424    pub fn build_float(self) -> ArbitraryFloat {
1425        ArbitraryFloat::with_context(self.context)
1426    }
1427
1428    /// Build an `ArbitraryComplex`.
1429    pub fn build_complex(self) -> CoreResult<ArbitraryComplex> {
1430        ArbitraryComplex::prec(self.context.floatprecision)
1431    }
1432
1433    /// Execute a calculation with this precision context.
1434    pub fn calculate<F, R>(self, f: F) -> R
1435    where
1436        F: FnOnce(&ArbitraryPrecisionContext) -> R,
1437    {
1438        f(&self.context)
1439    }
1440}
1441
1442impl Default for ArbitraryPrecisionBuilder {
1443    fn default() -> Self {
1444        Self::new()
1445    }
1446}
1447
1448// ---------------------------------------------------------------------------
1449// Utility functions
1450// ---------------------------------------------------------------------------
1451
1452/// Utility functions for arbitrary precision arithmetic.
1453pub mod utils {
1454    use super::*;
1455
1456    /// Compute π to the given bit precision.
1457    pub fn pi(prec: u32) -> CoreResult<ArbitraryFloat> {
1458        ArbitraryFloat::prec_2(prec)
1459    }
1460
1461    /// Compute e to the given bit precision.
1462    pub fn e(prec: u32) -> CoreResult<ArbitraryFloat> {
1463        ArbitraryFloat::prec_3(prec)
1464    }
1465
1466    /// Compute ln(2) to the given bit precision.
1467    pub fn ln2(prec: u32) -> CoreResult<ArbitraryFloat> {
1468        ArbitraryFloat::prec_4(prec)
1469    }
1470
1471    /// Compute sqrt(2) to the given bit precision.
1472    pub fn sqrt2(prec: u32) -> CoreResult<ArbitraryFloat> {
1473        let two = ArbitraryFloat::from_f64_withprecision(2.0, prec)?;
1474        two.sqrt()
1475    }
1476
1477    /// Compute the golden ratio to the given bit precision.
1478    pub fn golden_ratio(prec: u32) -> CoreResult<ArbitraryFloat> {
1479        let one = ArbitraryFloat::from_f64_withprecision(1.0, prec)?;
1480        let five = ArbitraryFloat::from_f64_withprecision(5.0, prec)?;
1481        let sqrt5 = five.sqrt()?;
1482        let two = ArbitraryFloat::from_f64_withprecision(2.0, prec)?;
1483        Ok((one + sqrt5) / two)
1484    }
1485
1486    /// Compute n! using arbitrary precision integers.
1487    pub fn factorial(n: u32) -> ArbitraryInt {
1488        ArbitraryInt::factorial(n)
1489    }
1490
1491    /// Compute C(n, k) using arbitrary precision integers.
1492    pub fn binomial(n: u32, k: u32) -> ArbitraryInt {
1493        ArbitraryInt::binomial(n, k)
1494    }
1495
1496    /// Check if a large integer is probably prime.
1497    pub fn is_probably_prime(n: &ArbitraryInt, certainty: u32) -> bool {
1498        n.is_probably_prime(certainty)
1499    }
1500}
1501
1502// ---------------------------------------------------------------------------
1503// Tests
1504// ---------------------------------------------------------------------------
1505
1506#[cfg(test)]
1507mod tests {
1508    use super::*;
1509
1510    #[test]
1511    fn test_arbitrary_int_basic() {
1512        let a = ArbitraryInt::from_i64(123);
1513        let b = ArbitraryInt::from_i64(456);
1514        let sum = a.clone() + b.clone();
1515        assert_eq!(sum.to_string(), "579");
1516
1517        let product = a.clone() * b.clone();
1518        assert_eq!(product.to_string(), "56088");
1519
1520        let factorial = ArbitraryInt::factorial(20);
1521        assert_eq!(factorial.to_string(), "2432902008176640000");
1522    }
1523
1524    #[test]
1525    fn test_arbitrary_float_basic() {
1526        let a = ArbitraryFloat::from_f64_withprecision(1.0, 128).expect("Operation failed");
1527        let b = ArbitraryFloat::from_f64_withprecision(3.0, 128).expect("Operation failed");
1528        let c = a / b;
1529
1530        // Check that we get more precision than f64.
1531        let c_str = c.to_string();
1532        // Should start with 3.333... (decimal) or similar.
1533        assert!(
1534            c_str.starts_with("3.333333333333333") || c_str.starts_with("0.333333333333333"),
1535            "unexpected value: {c_str}"
1536        );
1537        assert!(c_str.len() > 10, "result too short: {c_str}");
1538    }
1539
1540    #[test]
1541    fn test_arbitrary_rational() {
1542        let r = ArbitraryRational::num(22, 7).expect("Operation failed");
1543        assert_eq!(r.to_string(), "22/7");
1544
1545        let a = ArbitraryRational::num(1, 3).expect("Operation failed");
1546        let b = ArbitraryRational::num(1, 6).expect("Operation failed");
1547        let sum = a + b;
1548        assert_eq!(sum.to_string(), "1/2");
1549    }
1550
1551    #[test]
1552    fn test_arbitrary_complex() {
1553        let z = ArbitraryComplex::re_2(3.0, 4.0);
1554        let mag = z.abs();
1555        assert!((mag.to_f64() - 5.0).abs() < 1e-10);
1556
1557        let conj = z.conj();
1558        assert_eq!(conj.real().to_f64(), 3.0);
1559        assert_eq!(conj.imag().to_f64(), -4.0);
1560    }
1561
1562    #[test]
1563    fn testprecision_builder() {
1564        let x = ArbitraryPrecisionBuilder::new()
1565            .decimalprecision(50)
1566            .rounding(RoundingMode::Nearest)
1567            .build_float();
1568
1569        assert!(x.decimalprecision() >= 49); // Allow for rounding in the conversion.
1570    }
1571
1572    #[test]
1573    fn test_constants() {
1574        let pi = utils::pi(256).expect("Operation failed");
1575        let pi_str = pi.to_string();
1576        assert!(pi_str.starts_with("3.14159265358979"), "pi = {pi_str}");
1577
1578        let e = utils::e(256).expect("Operation failed");
1579        let e_str = e.to_string();
1580        assert!(e_str.starts_with("2.71828182845904"), "e = {e_str}");
1581    }
1582
1583    #[test]
1584    fn test_prime_checking() {
1585        let prime = ArbitraryInt::from_i64(97);
1586        assert!(prime.is_probably_prime(20));
1587
1588        let composite = ArbitraryInt::from_i64(98);
1589        assert!(!composite.is_probably_prime(20));
1590    }
1591
1592    #[test]
1593    fn test_gcd_lcm() {
1594        let a = ArbitraryInt::from_i64(48);
1595        let b = ArbitraryInt::from_i64(18);
1596
1597        let gcd = a.gcd(&b);
1598        assert_eq!(gcd.to_string(), "6");
1599
1600        let lcm = a.lcm(&b);
1601        assert_eq!(lcm.to_string(), "144");
1602    }
1603
1604    #[test]
1605    fn test_transcendental_functions() {
1606        let x = ArbitraryFloat::from_f64_withprecision(0.5, 128).expect("Operation failed");
1607
1608        let sin_x = x.sin();
1609        let cos_x = x.cos();
1610        let identity = sin_x.clone() * sin_x + cos_x.clone() * cos_x;
1611
1612        // sin²(x) + cos²(x) = 1
1613        assert!(
1614            (identity.to_f64() - 1.0).abs() < 1e-10,
1615            "identity = {}",
1616            identity.to_f64()
1617        );
1618
1619        let ln_x = x.ln().expect("Operation failed");
1620        let exp_ln_x = ln_x.exp();
1621        assert!(
1622            (exp_ln_x.to_f64() - 0.5).abs() < 1e-10,
1623            "exp_ln = {}",
1624            exp_ln_x.to_f64()
1625        );
1626    }
1627
1628    #[test]
1629    fn testerror_handling() {
1630        // Division by zero (rational reciprocal).
1631        let zero = ArbitraryRational::new();
1632        assert!(zero.recip().is_err());
1633
1634        // Square root of negative.
1635        let neg = ArbitraryFloat::from_f64(-1.0);
1636        assert!(neg.sqrt().is_err());
1637
1638        // Logarithm of negative.
1639        assert!(neg.ln().is_err());
1640
1641        // Arcsine out of range.
1642        let out_of_range = ArbitraryFloat::from_f64(2.0);
1643        assert!(out_of_range.asin().is_err());
1644    }
1645}