Skip to main content

oxiz_math/
mpfr.rs

1//! Arbitrary Precision Floating-Point Arithmetic (MPFR-like)
2//!
3//! This module provides an arbitrary precision floating-point type with configurable
4//! precision and rounding modes. It is designed for applications requiring extreme
5//! precision beyond what IEEE 754 double precision provides.
6//!
7//! # Features
8//!
9//! - Configurable precision (number of bits in mantissa)
10//! - Multiple rounding modes (RoundNearest, RoundTowardZero, RoundUp, RoundDown)
11//! - Basic arithmetic operations (add, sub, mul, div, sqrt)
12//! - Comparison operations
13//! - Conversion to/from f64
14//!
15//! # Example
16//!
17//! ```
18//! use oxiz_math::mpfr::{ArbitraryFloat, RoundingMode, Precision};
19//!
20//! // Create with 128-bit precision
21//! let precision = Precision::new(128);
22//! let a = ArbitraryFloat::from_f64(3.14159265358979323846, precision);
23//! let b = ArbitraryFloat::from_f64(2.71828182845904523536, precision);
24//!
25//! // Perform addition with rounding toward nearest
26//! let sum = a.add(&b, RoundingMode::RoundNearest);
27//! ```
28//!
29//! # Implementation Notes
30//!
31//! This implementation uses `num-bigint` for arbitrary precision integer arithmetic.
32//! The floating-point representation uses:
33//! - Sign bit (boolean)
34//! - Mantissa (BigInt with specified precision bits)
35//! - Exponent (i64 for the binary exponent)
36//!
37//! This is a simplified MPFR-like implementation suitable for SMT solving scenarios
38//! that require extended precision arithmetic.
39
40#[allow(unused_imports)]
41use crate::prelude::*;
42use core::cmp::Ordering;
43use core::fmt;
44use core::ops::{Add, Div, Mul, Neg, Sub};
45use num_bigint::BigUint;
46use num_integer::Integer;
47use num_traits::{One, ToPrimitive, Zero};
48
49/// Precision specification for arbitrary precision floats.
50///
51/// Specifies the number of bits in the mantissa (significand).
52/// Higher precision means more accurate results but slower computation.
53#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
54pub struct Precision {
55    /// Number of bits in the mantissa.
56    bits: u32,
57}
58
59impl Precision {
60    /// Create a new precision specification.
61    ///
62    /// # Arguments
63    /// * `bits` - Number of bits for the mantissa (minimum 1)
64    ///
65    /// # Example
66    /// ```
67    /// use oxiz_math::mpfr::Precision;
68    /// let p = Precision::new(128); // 128-bit precision
69    /// ```
70    pub fn new(bits: u32) -> Self {
71        assert!(bits >= 1, "Precision must be at least 1 bit");
72        Self { bits }
73    }
74
75    /// Get the number of bits.
76    pub fn bits(&self) -> u32 {
77        self.bits
78    }
79
80    /// Double precision (53 bits, same as f64).
81    pub const DOUBLE: Precision = Precision { bits: 53 };
82
83    /// Extended precision (64 bits, x86 extended precision).
84    pub const EXTENDED: Precision = Precision { bits: 64 };
85
86    /// Quadruple precision (113 bits, IEEE 754 quad).
87    pub const QUAD: Precision = Precision { bits: 113 };
88
89    /// High precision for SMT solving (256 bits).
90    pub const HIGH: Precision = Precision { bits: 256 };
91}
92
93impl Default for Precision {
94    fn default() -> Self {
95        Self::DOUBLE
96    }
97}
98
99/// Rounding modes for arbitrary precision operations.
100///
101/// These correspond to IEEE 754 rounding modes.
102#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
103pub enum RoundingMode {
104    /// Round to nearest, ties to even (default).
105    #[default]
106    RoundNearest,
107    /// Round toward zero (truncation).
108    RoundTowardZero,
109    /// Round toward positive infinity.
110    RoundUp,
111    /// Round toward negative infinity.
112    RoundDown,
113}
114
115/// Special values for arbitrary precision floats.
116#[derive(Debug, Clone, Copy, PartialEq, Eq)]
117enum SpecialValue {
118    /// Not a special value (regular number).
119    None,
120    /// Positive infinity.
121    PosInfinity,
122    /// Negative infinity.
123    NegInfinity,
124    /// Not a Number.
125    NaN,
126}
127
128/// Arbitrary precision floating-point number.
129///
130/// Represents a floating-point number with configurable precision.
131/// The internal representation uses a sign, mantissa (BigInt), and exponent.
132///
133/// Value = (-1)^sign * mantissa * 2^(exponent - precision + 1)
134#[derive(Clone)]
135pub struct ArbitraryFloat {
136    /// Sign: true for negative, false for positive.
137    sign: bool,
138    /// Mantissa (significand) as a big integer.
139    mantissa: BigUint,
140    /// Binary exponent.
141    exponent: i64,
142    /// Precision (number of bits in mantissa).
143    precision: Precision,
144    /// Special value indicator.
145    special: SpecialValue,
146}
147
148impl ArbitraryFloat {
149    /// Create a new arbitrary precision float from components.
150    ///
151    /// # Arguments
152    /// * `sign` - True for negative, false for positive
153    /// * `mantissa` - The significand
154    /// * `exponent` - The binary exponent
155    /// * `precision` - The precision specification
156    fn new(sign: bool, mantissa: BigUint, exponent: i64, precision: Precision) -> Self {
157        Self {
158            sign,
159            mantissa,
160            exponent,
161            precision,
162            special: SpecialValue::None,
163        }
164    }
165
166    /// Create a zero value with the given precision.
167    pub fn zero(precision: Precision) -> Self {
168        Self::new(false, BigUint::zero(), 0, precision)
169    }
170
171    /// Create a one value with the given precision.
172    pub fn one(precision: Precision) -> Self {
173        let mantissa = BigUint::one() << (precision.bits() - 1);
174        Self::new(false, mantissa, 0, precision)
175    }
176
177    /// Create positive infinity.
178    pub fn pos_infinity(precision: Precision) -> Self {
179        let mut f = Self::zero(precision);
180        f.special = SpecialValue::PosInfinity;
181        f
182    }
183
184    /// Create negative infinity.
185    pub fn neg_infinity(precision: Precision) -> Self {
186        let mut f = Self::zero(precision);
187        f.special = SpecialValue::NegInfinity;
188        f
189    }
190
191    /// Create NaN (Not a Number).
192    pub fn nan(precision: Precision) -> Self {
193        let mut f = Self::zero(precision);
194        f.special = SpecialValue::NaN;
195        f
196    }
197
198    /// Check if this value is zero.
199    pub fn is_zero(&self) -> bool {
200        self.special == SpecialValue::None && self.mantissa.is_zero()
201    }
202
203    /// Check if this value is positive infinity.
204    pub fn is_pos_infinity(&self) -> bool {
205        self.special == SpecialValue::PosInfinity
206    }
207
208    /// Check if this value is negative infinity.
209    pub fn is_neg_infinity(&self) -> bool {
210        self.special == SpecialValue::NegInfinity
211    }
212
213    /// Check if this value is any infinity.
214    pub fn is_infinity(&self) -> bool {
215        self.is_pos_infinity() || self.is_neg_infinity()
216    }
217
218    /// Check if this value is NaN.
219    pub fn is_nan(&self) -> bool {
220        self.special == SpecialValue::NaN
221    }
222
223    /// Check if this value is finite (not infinity or NaN).
224    pub fn is_finite(&self) -> bool {
225        self.special == SpecialValue::None
226    }
227
228    /// Check if this value is negative.
229    pub fn is_negative(&self) -> bool {
230        self.sign && !self.is_zero() && !self.is_nan()
231    }
232
233    /// Check if this value is positive.
234    pub fn is_positive(&self) -> bool {
235        !self.sign && !self.is_zero() && !self.is_nan()
236    }
237
238    /// Get the precision of this value.
239    pub fn precision(&self) -> Precision {
240        self.precision
241    }
242
243    /// Create from an f64 value.
244    ///
245    /// # Arguments
246    /// * `value` - The f64 value to convert
247    /// * `precision` - Target precision
248    ///
249    /// # Example
250    /// ```
251    /// use oxiz_math::mpfr::{ArbitraryFloat, Precision};
252    /// let f = ArbitraryFloat::from_f64(3.14159, Precision::new(128));
253    /// ```
254    pub fn from_f64(value: f64, precision: Precision) -> Self {
255        // Handle special values
256        if value.is_nan() {
257            return Self::nan(precision);
258        }
259        if value.is_infinite() {
260            return if value > 0.0 {
261                Self::pos_infinity(precision)
262            } else {
263                Self::neg_infinity(precision)
264            };
265        }
266        if value == 0.0 {
267            // Preserve sign of zero
268            let mut z = Self::zero(precision);
269            z.sign = value.is_sign_negative();
270            return z;
271        }
272
273        // Extract IEEE 754 components
274        let bits = value.to_bits();
275        let sign = (bits >> 63) != 0;
276        let exp_bits = ((bits >> 52) & 0x7FF) as i64;
277        let mantissa_bits = bits & 0x000F_FFFF_FFFF_FFFF;
278
279        // Compute actual exponent (removing bias of 1023)
280        let exponent = if exp_bits == 0 {
281            // Subnormal
282            1 - 1023 - 52
283        } else {
284            // Normal
285            exp_bits - 1023 - 52
286        };
287
288        // Build mantissa (implicit leading 1 for normalized numbers)
289        let mantissa = if exp_bits == 0 {
290            // Subnormal: no implicit leading 1
291            BigUint::from(mantissa_bits)
292        } else {
293            // Normal: add implicit leading 1
294            BigUint::from(mantissa_bits | (1u64 << 52))
295        };
296
297        // Scale mantissa to target precision
298        let mut result = Self::new(sign, mantissa, exponent, precision);
299        result.normalize(RoundingMode::RoundNearest);
300        result
301    }
302
303    /// Convert to f64.
304    ///
305    /// May lose precision if the ArbitraryFloat has higher precision than f64.
306    ///
307    /// # Arguments
308    /// * `rounding` - Rounding mode for the conversion
309    ///
310    /// # Example
311    /// ```
312    /// use oxiz_math::mpfr::{ArbitraryFloat, Precision, RoundingMode};
313    /// let f = ArbitraryFloat::from_f64(3.14159, Precision::new(128));
314    /// let back = f.to_f64(RoundingMode::RoundNearest);
315    /// ```
316    pub fn to_f64(&self, _rounding: RoundingMode) -> f64 {
317        // Handle special values
318        if self.is_nan() {
319            return f64::NAN;
320        }
321        if self.is_pos_infinity() {
322            return f64::INFINITY;
323        }
324        if self.is_neg_infinity() {
325            return f64::NEG_INFINITY;
326        }
327        if self.is_zero() {
328            return if self.sign { -0.0 } else { 0.0 };
329        }
330
331        // For f64 conversion, we need mantissa in [2^52, 2^53) and adjust exponent
332        // Current: value = mantissa * 2^exponent (mantissa has self.precision bits)
333        // Target: value = f64_mantissa * 2^f64_exp (f64_mantissa has 53 bits)
334
335        let precision_bits = self.precision.bits();
336        if precision_bits >= 53 {
337            // Shift mantissa to 53 bits
338            let shift = (precision_bits - 53) as usize;
339            let f64_mantissa = (&self.mantissa >> shift).to_f64().unwrap_or(0.0);
340            let f64_exponent = self.exponent + shift as i64;
341
342            let result = f64_mantissa * 2.0f64.powi(f64_exponent as i32);
343            if self.sign { -result } else { result }
344        } else {
345            // Mantissa is smaller than 53 bits
346            let mantissa_f64 = self.mantissa.to_f64().unwrap_or(0.0);
347            let result = mantissa_f64 * 2.0f64.powi(self.exponent as i32);
348            if self.sign { -result } else { result }
349        }
350    }
351
352    /// Normalize the mantissa to have the leading 1 in the correct position.
353    fn normalize(&mut self, rounding: RoundingMode) {
354        if self.mantissa.is_zero() || !self.is_finite() {
355            return;
356        }
357
358        let target_bits = self.precision.bits() as u64;
359        let current_bits = self.mantissa.bits();
360
361        if current_bits > target_bits {
362            // Need to round down
363            let shift = current_bits - target_bits;
364            let (quotient, remainder) = self.mantissa.div_rem(&(BigUint::one() << shift as usize));
365
366            self.mantissa = quotient;
367            self.exponent += shift as i64;
368
369            // Apply rounding
370            let half = BigUint::one() << (shift as usize - 1);
371            let round_up = match rounding {
372                RoundingMode::RoundNearest => {
373                    // Round to nearest, ties to even
374                    if remainder > half {
375                        true
376                    } else if remainder == half {
377                        // Tie: round to even
378                        self.mantissa.bit(0)
379                    } else {
380                        false
381                    }
382                }
383                RoundingMode::RoundUp => !self.sign && !remainder.is_zero(),
384                RoundingMode::RoundDown => self.sign && !remainder.is_zero(),
385                RoundingMode::RoundTowardZero => false,
386            };
387
388            if round_up {
389                self.mantissa += 1u32;
390                // Check for overflow after rounding
391                if self.mantissa.bits() > target_bits {
392                    self.mantissa >>= 1;
393                    self.exponent += 1;
394                }
395            }
396        } else if current_bits < target_bits {
397            // Shift left to fill precision
398            let shift = target_bits - current_bits;
399            self.mantissa <<= shift as usize;
400            self.exponent -= shift as i64;
401        }
402    }
403
404    /// Add two arbitrary precision floats.
405    ///
406    /// # Arguments
407    /// * `other` - The other value to add
408    /// * `rounding` - Rounding mode for the result
409    ///
410    /// # Returns
411    /// The sum with the higher precision of the two operands.
412    pub fn add(&self, other: &Self, rounding: RoundingMode) -> Self {
413        // Handle special values
414        if self.is_nan() || other.is_nan() {
415            return Self::nan(self.precision.max(other.precision));
416        }
417
418        // inf + inf = inf (same sign) or NaN (opposite sign)
419        if self.is_infinity() || other.is_infinity() {
420            if self.is_pos_infinity() {
421                if other.is_neg_infinity() {
422                    return Self::nan(self.precision);
423                }
424                return Self::pos_infinity(self.precision);
425            }
426            if self.is_neg_infinity() {
427                if other.is_pos_infinity() {
428                    return Self::nan(self.precision);
429                }
430                return Self::neg_infinity(self.precision);
431            }
432            if other.is_pos_infinity() {
433                return Self::pos_infinity(other.precision);
434            }
435            return Self::neg_infinity(other.precision);
436        }
437
438        // Handle zeros
439        if self.is_zero() {
440            return other.clone();
441        }
442        if other.is_zero() {
443            return self.clone();
444        }
445
446        // Use higher precision
447        let result_precision = if self.precision.bits() >= other.precision.bits() {
448            self.precision
449        } else {
450            other.precision
451        };
452
453        // Align exponents
454        let (m1, m2, exp, s1, s2) = self.align_with(other);
455
456        // Perform addition/subtraction based on signs
457        let (result_mantissa, result_sign) = if s1 == s2 {
458            // Same sign: add mantissas
459            (m1 + m2, s1)
460        } else {
461            // Different signs: subtract mantissas
462            match m1.cmp(&m2) {
463                Ordering::Greater => (m1 - m2, s1),
464                Ordering::Less => (m2 - m1, s2),
465                Ordering::Equal => return Self::zero(result_precision),
466            }
467        };
468
469        let mut result = Self::new(result_sign, result_mantissa, exp, result_precision);
470        result.normalize(rounding);
471        result
472    }
473
474    /// Subtract two arbitrary precision floats.
475    pub fn sub(&self, other: &Self, rounding: RoundingMode) -> Self {
476        let negated = other.neg();
477        self.add(&negated, rounding)
478    }
479
480    /// Multiply two arbitrary precision floats.
481    pub fn mul(&self, other: &Self, rounding: RoundingMode) -> Self {
482        let result_precision = if self.precision.bits() >= other.precision.bits() {
483            self.precision
484        } else {
485            other.precision
486        };
487
488        // Handle special values
489        if self.is_nan() || other.is_nan() {
490            return Self::nan(result_precision);
491        }
492
493        // 0 * inf = NaN
494        if (self.is_zero() && other.is_infinity()) || (self.is_infinity() && other.is_zero()) {
495            return Self::nan(result_precision);
496        }
497
498        // Handle infinities
499        let result_sign = self.sign != other.sign;
500        if self.is_infinity() || other.is_infinity() {
501            return if result_sign {
502                Self::neg_infinity(result_precision)
503            } else {
504                Self::pos_infinity(result_precision)
505            };
506        }
507
508        // Handle zeros
509        if self.is_zero() || other.is_zero() {
510            let mut z = Self::zero(result_precision);
511            z.sign = result_sign;
512            return z;
513        }
514
515        // Multiply mantissas and add exponents
516        // value = (m1 * m2) * 2^(e1 + e2)
517        let result_mantissa = &self.mantissa * &other.mantissa;
518        let result_exponent = self.exponent + other.exponent;
519
520        let mut result = Self::new(
521            result_sign,
522            result_mantissa,
523            result_exponent,
524            result_precision,
525        );
526        result.normalize(rounding);
527        result
528    }
529
530    /// Divide two arbitrary precision floats.
531    pub fn div(&self, other: &Self, rounding: RoundingMode) -> Self {
532        let result_precision = if self.precision.bits() >= other.precision.bits() {
533            self.precision
534        } else {
535            other.precision
536        };
537
538        // Handle special values
539        if self.is_nan() || other.is_nan() {
540            return Self::nan(result_precision);
541        }
542
543        let result_sign = self.sign != other.sign;
544
545        // 0/0 = NaN, inf/inf = NaN
546        if (self.is_zero() && other.is_zero()) || (self.is_infinity() && other.is_infinity()) {
547            return Self::nan(result_precision);
548        }
549
550        // x/0 = inf
551        if other.is_zero() {
552            return if result_sign {
553                Self::neg_infinity(result_precision)
554            } else {
555                Self::pos_infinity(result_precision)
556            };
557        }
558
559        // 0/x = 0
560        if self.is_zero() {
561            let mut z = Self::zero(result_precision);
562            z.sign = result_sign;
563            return z;
564        }
565
566        // x/inf = 0
567        if other.is_infinity() {
568            let mut z = Self::zero(result_precision);
569            z.sign = result_sign;
570            return z;
571        }
572
573        // inf/x = inf
574        if self.is_infinity() {
575            return if result_sign {
576                Self::neg_infinity(result_precision)
577            } else {
578                Self::pos_infinity(result_precision)
579            };
580        }
581
582        // Division: shift dividend left for precision, then divide
583        let extra_bits = result_precision.bits() as usize + 10; // Extra bits for rounding
584        let shifted_dividend = &self.mantissa << extra_bits;
585        let result_mantissa = &shifted_dividend / &other.mantissa;
586        // value = (dividend << extra_bits) / divisor * 2^exponent
587        //       = (m1 / m2) * 2^extra_bits * 2^exponent
588        // We want: (m1 / m2) * 2^(e1 - e2)
589        // So: exponent = e1 - e2 - extra_bits
590        let result_exponent = self.exponent - other.exponent - (extra_bits as i64);
591
592        let mut result = Self::new(
593            result_sign,
594            result_mantissa,
595            result_exponent,
596            result_precision,
597        );
598        result.normalize(rounding);
599        result
600    }
601
602    /// Compute the square root.
603    ///
604    /// Uses Newton-Raphson iteration for arbitrary precision square root.
605    pub fn sqrt(&self, rounding: RoundingMode) -> Self {
606        // Handle special values
607        if self.is_nan() || self.is_neg_infinity() || (self.is_negative() && !self.is_zero()) {
608            return Self::nan(self.precision);
609        }
610        if self.is_pos_infinity() {
611            return Self::pos_infinity(self.precision);
612        }
613        if self.is_zero() {
614            return Self::zero(self.precision);
615        }
616
617        // Use Newton-Raphson: x_{n+1} = (x_n + S/x_n) / 2
618        // Start with a reasonable initial guess using f64
619        let initial_guess = self.to_f64(RoundingMode::RoundNearest).sqrt();
620        let mut x = Self::from_f64(initial_guess, self.precision);
621
622        // Iterate until convergence
623        let two = Self::from_f64(2.0, self.precision);
624        let tolerance_bits = self.precision.bits() + 10;
625
626        for _ in 0..100 {
627            // x_new = (x + self/x) / 2
628            let s_div_x = Self::div(self, &x, rounding);
629            let sum = Self::add(&x, &s_div_x, rounding);
630            let x_new = Self::div(&sum, &two, rounding);
631
632            // Check convergence
633            let diff = Self::sub(&x_new, &x, rounding);
634            if diff.is_zero()
635                || (diff.mantissa.bits() as i64 + diff.exponent
636                    < x_new.exponent - tolerance_bits as i64)
637            {
638                return x_new;
639            }
640
641            x = x_new;
642        }
643
644        x
645    }
646
647    /// Negate the value.
648    pub fn neg(&self) -> Self {
649        if self.is_nan() {
650            return Self::nan(self.precision);
651        }
652        if self.is_pos_infinity() {
653            return Self::neg_infinity(self.precision);
654        }
655        if self.is_neg_infinity() {
656            return Self::pos_infinity(self.precision);
657        }
658
659        let mut result = self.clone();
660        result.sign = !result.sign;
661        result
662    }
663
664    /// Absolute value.
665    pub fn abs(&self) -> Self {
666        if self.is_nan() {
667            return Self::nan(self.precision);
668        }
669        if self.is_neg_infinity() {
670            return Self::pos_infinity(self.precision);
671        }
672
673        let mut result = self.clone();
674        result.sign = false;
675        result
676    }
677
678    /// Align two floats to the same exponent for addition.
679    /// Returns (m1, m2, common_exp, sign1, sign2).
680    fn align_with(&self, other: &Self) -> (BigUint, BigUint, i64, bool, bool) {
681        let exp_diff = self.exponent - other.exponent;
682
683        if exp_diff >= 0 {
684            // self has larger exponent - shift other's mantissa right
685            let shifted_other = &other.mantissa >> (exp_diff as usize);
686            (
687                self.mantissa.clone(),
688                shifted_other,
689                self.exponent,
690                self.sign,
691                other.sign,
692            )
693        } else {
694            // other has larger exponent - shift self's mantissa right
695            let shift = (-exp_diff) as usize;
696            let shifted_self = &self.mantissa >> shift;
697            (
698                shifted_self,
699                other.mantissa.clone(),
700                other.exponent,
701                self.sign,
702                other.sign,
703            )
704        }
705    }
706
707    /// Compare two arbitrary precision floats.
708    ///
709    /// Returns None if either value is NaN.
710    pub fn partial_compare(&self, other: &Self) -> Option<Ordering> {
711        // NaN comparisons
712        if self.is_nan() || other.is_nan() {
713            return None;
714        }
715
716        // Handle infinities
717        if self.is_pos_infinity() {
718            return if other.is_pos_infinity() {
719                Some(Ordering::Equal)
720            } else {
721                Some(Ordering::Greater)
722            };
723        }
724        if self.is_neg_infinity() {
725            return if other.is_neg_infinity() {
726                Some(Ordering::Equal)
727            } else {
728                Some(Ordering::Less)
729            };
730        }
731        if other.is_pos_infinity() {
732            return Some(Ordering::Less);
733        }
734        if other.is_neg_infinity() {
735            return Some(Ordering::Greater);
736        }
737
738        // Handle zeros
739        let self_zero = self.is_zero();
740        let other_zero = other.is_zero();
741        if self_zero && other_zero {
742            return Some(Ordering::Equal);
743        }
744        if self_zero {
745            return if other.sign {
746                Some(Ordering::Greater)
747            } else {
748                Some(Ordering::Less)
749            };
750        }
751        if other_zero {
752            return if self.sign {
753                Some(Ordering::Less)
754            } else {
755                Some(Ordering::Greater)
756            };
757        }
758
759        // Compare signs
760        if self.sign != other.sign {
761            return if self.sign {
762                Some(Ordering::Less)
763            } else {
764                Some(Ordering::Greater)
765            };
766        }
767
768        // Same sign: compare magnitudes
769        let (m1, m2, _, _, _) = self.align_with(other);
770        let mag_cmp = m1.cmp(&m2);
771
772        // If negative, reverse the comparison
773        if self.sign {
774            Some(mag_cmp.reverse())
775        } else {
776            Some(mag_cmp)
777        }
778    }
779
780    /// Create from a string representation.
781    ///
782    /// Supports decimal notation (e.g., "3.14159") and scientific notation (e.g., "3.14e-10").
783    pub fn from_str(s: &str, precision: Precision) -> Option<Self> {
784        let s = s.trim();
785
786        // Handle special values
787        if s.eq_ignore_ascii_case("nan") {
788            return Some(Self::nan(precision));
789        }
790        if s.eq_ignore_ascii_case("inf") || s.eq_ignore_ascii_case("+inf") {
791            return Some(Self::pos_infinity(precision));
792        }
793        if s.eq_ignore_ascii_case("-inf") {
794            return Some(Self::neg_infinity(precision));
795        }
796
797        // Try parsing as f64 first (simple approach)
798        if let Ok(f) = s.parse::<f64>() {
799            return Some(Self::from_f64(f, precision));
800        }
801
802        None
803    }
804}
805
806impl fmt::Debug for ArbitraryFloat {
807    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
808        if self.is_nan() {
809            write!(f, "NaN")
810        } else if self.is_pos_infinity() {
811            write!(f, "+Inf")
812        } else if self.is_neg_infinity() {
813            write!(f, "-Inf")
814        } else {
815            write!(
816                f,
817                "ArbitraryFloat {{ sign: {}, mantissa: {}, exp: {}, prec: {} }}",
818                self.sign,
819                self.mantissa,
820                self.exponent,
821                self.precision.bits()
822            )
823        }
824    }
825}
826
827impl fmt::Display for ArbitraryFloat {
828    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
829        if self.is_nan() {
830            write!(f, "NaN")
831        } else if self.is_pos_infinity() {
832            write!(f, "+Inf")
833        } else if self.is_neg_infinity() {
834            write!(f, "-Inf")
835        } else {
836            // Convert to f64 for display (may lose precision)
837            let value = self.to_f64(RoundingMode::RoundNearest);
838            if self.sign && !value.is_sign_negative() {
839                write!(f, "-{}", value.abs())
840            } else {
841                write!(f, "{}", value)
842            }
843        }
844    }
845}
846
847impl PartialEq for ArbitraryFloat {
848    fn eq(&self, other: &Self) -> bool {
849        self.partial_compare(other) == Some(Ordering::Equal)
850    }
851}
852
853impl PartialOrd for ArbitraryFloat {
854    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
855        self.partial_compare(other)
856    }
857}
858
859impl Add for ArbitraryFloat {
860    type Output = Self;
861
862    fn add(self, rhs: Self) -> Self::Output {
863        ArbitraryFloat::add(&self, &rhs, RoundingMode::RoundNearest)
864    }
865}
866
867impl Add for &ArbitraryFloat {
868    type Output = ArbitraryFloat;
869
870    fn add(self, rhs: Self) -> Self::Output {
871        ArbitraryFloat::add(self, rhs, RoundingMode::RoundNearest)
872    }
873}
874
875impl Sub for ArbitraryFloat {
876    type Output = Self;
877
878    fn sub(self, rhs: Self) -> Self::Output {
879        ArbitraryFloat::sub(&self, &rhs, RoundingMode::RoundNearest)
880    }
881}
882
883impl Sub for &ArbitraryFloat {
884    type Output = ArbitraryFloat;
885
886    fn sub(self, rhs: Self) -> Self::Output {
887        ArbitraryFloat::sub(self, rhs, RoundingMode::RoundNearest)
888    }
889}
890
891impl Mul for ArbitraryFloat {
892    type Output = Self;
893
894    fn mul(self, rhs: Self) -> Self::Output {
895        ArbitraryFloat::mul(&self, &rhs, RoundingMode::RoundNearest)
896    }
897}
898
899impl Mul for &ArbitraryFloat {
900    type Output = ArbitraryFloat;
901
902    fn mul(self, rhs: Self) -> Self::Output {
903        ArbitraryFloat::mul(self, rhs, RoundingMode::RoundNearest)
904    }
905}
906
907impl Div for ArbitraryFloat {
908    type Output = Self;
909
910    fn div(self, rhs: Self) -> Self::Output {
911        ArbitraryFloat::div(&self, &rhs, RoundingMode::RoundNearest)
912    }
913}
914
915impl Div for &ArbitraryFloat {
916    type Output = ArbitraryFloat;
917
918    fn div(self, rhs: Self) -> Self::Output {
919        ArbitraryFloat::div(self, rhs, RoundingMode::RoundNearest)
920    }
921}
922
923impl Neg for ArbitraryFloat {
924    type Output = Self;
925
926    fn neg(self) -> Self::Output {
927        ArbitraryFloat::neg(&self)
928    }
929}
930
931impl Neg for &ArbitraryFloat {
932    type Output = ArbitraryFloat;
933
934    fn neg(self) -> Self::Output {
935        ArbitraryFloat::neg(self)
936    }
937}
938
939// Helper trait implementations for max on Precision
940impl Precision {
941    /// Return the maximum of two precisions.
942    fn max(self, other: Self) -> Self {
943        if self.bits >= other.bits { self } else { other }
944    }
945}
946
947/// Context for arbitrary precision operations.
948///
949/// Provides a convenient way to perform multiple operations with the same
950/// precision and rounding mode.
951#[derive(Debug, Clone)]
952pub struct ArbitraryFloatContext {
953    /// Default precision for operations.
954    pub precision: Precision,
955    /// Default rounding mode.
956    pub rounding: RoundingMode,
957}
958
959impl ArbitraryFloatContext {
960    /// Create a new context with the given precision and rounding mode.
961    pub fn new(precision: Precision, rounding: RoundingMode) -> Self {
962        Self {
963            precision,
964            rounding,
965        }
966    }
967
968    /// Create a zero value.
969    pub fn zero(&self) -> ArbitraryFloat {
970        ArbitraryFloat::zero(self.precision)
971    }
972
973    /// Create a one value.
974    pub fn one(&self) -> ArbitraryFloat {
975        ArbitraryFloat::one(self.precision)
976    }
977
978    /// Create from f64.
979    pub fn from_f64(&self, value: f64) -> ArbitraryFloat {
980        ArbitraryFloat::from_f64(value, self.precision)
981    }
982
983    /// Add two values.
984    pub fn add(&self, a: &ArbitraryFloat, b: &ArbitraryFloat) -> ArbitraryFloat {
985        a.add(b, self.rounding)
986    }
987
988    /// Subtract two values.
989    pub fn sub(&self, a: &ArbitraryFloat, b: &ArbitraryFloat) -> ArbitraryFloat {
990        a.sub(b, self.rounding)
991    }
992
993    /// Multiply two values.
994    pub fn mul(&self, a: &ArbitraryFloat, b: &ArbitraryFloat) -> ArbitraryFloat {
995        a.mul(b, self.rounding)
996    }
997
998    /// Divide two values.
999    pub fn div(&self, a: &ArbitraryFloat, b: &ArbitraryFloat) -> ArbitraryFloat {
1000        a.div(b, self.rounding)
1001    }
1002
1003    /// Compute square root.
1004    pub fn sqrt(&self, a: &ArbitraryFloat) -> ArbitraryFloat {
1005        a.sqrt(self.rounding)
1006    }
1007}
1008
1009impl Default for ArbitraryFloatContext {
1010    fn default() -> Self {
1011        Self::new(Precision::DOUBLE, RoundingMode::RoundNearest)
1012    }
1013}
1014
1015#[cfg(test)]
1016mod tests {
1017    use super::*;
1018
1019    const EPSILON: f64 = 1e-10;
1020
1021    fn approx_eq(a: f64, b: f64) -> bool {
1022        (a - b).abs() < EPSILON || (a.is_nan() && b.is_nan())
1023    }
1024
1025    #[test]
1026    fn test_precision_constants() {
1027        assert_eq!(Precision::DOUBLE.bits(), 53);
1028        assert_eq!(Precision::EXTENDED.bits(), 64);
1029        assert_eq!(Precision::QUAD.bits(), 113);
1030        assert_eq!(Precision::HIGH.bits(), 256);
1031    }
1032
1033    #[test]
1034    fn test_from_f64_basic() {
1035        let prec = Precision::new(64);
1036        let f = ArbitraryFloat::from_f64(core::f64::consts::PI, prec);
1037        let back = f.to_f64(RoundingMode::RoundNearest);
1038        assert!(approx_eq(back, core::f64::consts::PI));
1039    }
1040
1041    #[test]
1042    fn test_from_f64_special_values() {
1043        let prec = Precision::new(64);
1044
1045        let nan = ArbitraryFloat::from_f64(f64::NAN, prec);
1046        assert!(nan.is_nan());
1047
1048        let pos_inf = ArbitraryFloat::from_f64(f64::INFINITY, prec);
1049        assert!(pos_inf.is_pos_infinity());
1050
1051        let neg_inf = ArbitraryFloat::from_f64(f64::NEG_INFINITY, prec);
1052        assert!(neg_inf.is_neg_infinity());
1053
1054        let zero = ArbitraryFloat::from_f64(0.0, prec);
1055        assert!(zero.is_zero());
1056    }
1057
1058    #[test]
1059    fn test_addition() {
1060        let prec = Precision::new(64);
1061        let a = ArbitraryFloat::from_f64(1.5, prec);
1062        let b = ArbitraryFloat::from_f64(2.5, prec);
1063        let sum = ArbitraryFloat::add(&a, &b, RoundingMode::RoundNearest);
1064        assert!(approx_eq(sum.to_f64(RoundingMode::RoundNearest), 4.0));
1065    }
1066
1067    #[test]
1068    fn test_subtraction() {
1069        let prec = Precision::new(64);
1070        let a = ArbitraryFloat::from_f64(5.0, prec);
1071        let b = ArbitraryFloat::from_f64(3.0, prec);
1072        let diff = ArbitraryFloat::sub(&a, &b, RoundingMode::RoundNearest);
1073        assert!(approx_eq(diff.to_f64(RoundingMode::RoundNearest), 2.0));
1074    }
1075
1076    #[test]
1077    fn test_multiplication() {
1078        let prec = Precision::new(64);
1079        let a = ArbitraryFloat::from_f64(3.0, prec);
1080        let b = ArbitraryFloat::from_f64(4.0, prec);
1081        let prod = ArbitraryFloat::mul(&a, &b, RoundingMode::RoundNearest);
1082        assert!(approx_eq(prod.to_f64(RoundingMode::RoundNearest), 12.0));
1083    }
1084
1085    #[test]
1086    fn test_division() {
1087        let prec = Precision::new(64);
1088        let a = ArbitraryFloat::from_f64(10.0, prec);
1089        let b = ArbitraryFloat::from_f64(4.0, prec);
1090        let quot = ArbitraryFloat::div(&a, &b, RoundingMode::RoundNearest);
1091        assert!(approx_eq(quot.to_f64(RoundingMode::RoundNearest), 2.5));
1092    }
1093
1094    #[test]
1095    fn test_sqrt() {
1096        let prec = Precision::new(64);
1097        let a = ArbitraryFloat::from_f64(4.0, prec);
1098        let root = a.sqrt(RoundingMode::RoundNearest);
1099        assert!(approx_eq(root.to_f64(RoundingMode::RoundNearest), 2.0));
1100    }
1101
1102    #[test]
1103    fn test_sqrt_2() {
1104        let prec = Precision::new(128);
1105        let a = ArbitraryFloat::from_f64(2.0, prec);
1106        let root = a.sqrt(RoundingMode::RoundNearest);
1107        let expected = 2.0f64.sqrt();
1108        let result = root.to_f64(RoundingMode::RoundNearest);
1109        assert!(
1110            (result - expected).abs() < 1e-14,
1111            "sqrt(2) = {}, expected {}",
1112            result,
1113            expected
1114        );
1115    }
1116
1117    #[test]
1118    fn test_negation() {
1119        let prec = Precision::new(64);
1120        let a = ArbitraryFloat::from_f64(5.0, prec);
1121        let neg_a = a.neg();
1122        assert!(approx_eq(neg_a.to_f64(RoundingMode::RoundNearest), -5.0));
1123    }
1124
1125    #[test]
1126    fn test_abs() {
1127        let prec = Precision::new(64);
1128        let a = ArbitraryFloat::from_f64(-5.0, prec);
1129        let abs_a = a.abs();
1130        assert!(approx_eq(abs_a.to_f64(RoundingMode::RoundNearest), 5.0));
1131    }
1132
1133    #[test]
1134    fn test_comparison() {
1135        let prec = Precision::new(64);
1136        let a = ArbitraryFloat::from_f64(3.0, prec);
1137        let b = ArbitraryFloat::from_f64(5.0, prec);
1138        let c = ArbitraryFloat::from_f64(3.0, prec);
1139
1140        assert!(a < b);
1141        assert!(b > a);
1142        assert!(a == c);
1143        assert!(a <= c);
1144        assert!(a >= c);
1145    }
1146
1147    #[test]
1148    fn test_comparison_with_nan() {
1149        let prec = Precision::new(64);
1150        let a = ArbitraryFloat::from_f64(3.0, prec);
1151        let nan = ArbitraryFloat::nan(prec);
1152
1153        assert!(a.partial_compare(&nan).is_none());
1154        assert!(nan.partial_compare(&a).is_none());
1155        assert!(nan.partial_compare(&nan).is_none());
1156    }
1157
1158    #[test]
1159    fn test_infinity_operations() {
1160        let prec = Precision::new(64);
1161        let pos_inf = ArbitraryFloat::pos_infinity(prec);
1162        let neg_inf = ArbitraryFloat::neg_infinity(prec);
1163        let one = ArbitraryFloat::from_f64(1.0, prec);
1164
1165        // inf + 1 = inf
1166        let sum = ArbitraryFloat::add(&pos_inf, &one, RoundingMode::RoundNearest);
1167        assert!(sum.is_pos_infinity());
1168
1169        // inf + (-inf) = NaN
1170        let sum2 = ArbitraryFloat::add(&pos_inf, &neg_inf, RoundingMode::RoundNearest);
1171        assert!(sum2.is_nan());
1172
1173        // inf * 2 = inf
1174        let two = ArbitraryFloat::from_f64(2.0, prec);
1175        let prod = ArbitraryFloat::mul(&pos_inf, &two, RoundingMode::RoundNearest);
1176        assert!(prod.is_pos_infinity());
1177
1178        // inf * 0 = NaN
1179        let zero = ArbitraryFloat::zero(prec);
1180        let prod2 = ArbitraryFloat::mul(&pos_inf, &zero, RoundingMode::RoundNearest);
1181        assert!(prod2.is_nan());
1182    }
1183
1184    #[test]
1185    fn test_zero_operations() {
1186        let prec = Precision::new(64);
1187        let zero = ArbitraryFloat::zero(prec);
1188        let one = ArbitraryFloat::from_f64(1.0, prec);
1189
1190        // 0 + 1 = 1
1191        let sum = ArbitraryFloat::add(&zero, &one, RoundingMode::RoundNearest);
1192        assert!(approx_eq(sum.to_f64(RoundingMode::RoundNearest), 1.0));
1193
1194        // 0 * 1 = 0
1195        let prod = ArbitraryFloat::mul(&zero, &one, RoundingMode::RoundNearest);
1196        assert!(prod.is_zero());
1197
1198        // 1 / 0 = inf
1199        let quot = ArbitraryFloat::div(&one, &zero, RoundingMode::RoundNearest);
1200        assert!(quot.is_pos_infinity());
1201
1202        // 0 / 0 = NaN
1203        let quot2 = ArbitraryFloat::div(&zero, &zero, RoundingMode::RoundNearest);
1204        assert!(quot2.is_nan());
1205    }
1206
1207    #[test]
1208    fn test_operator_overloads() {
1209        let prec = Precision::new(64);
1210        let a = ArbitraryFloat::from_f64(10.0, prec);
1211        let b = ArbitraryFloat::from_f64(3.0, prec);
1212
1213        let sum = a.clone() + b.clone();
1214        assert!(approx_eq(sum.to_f64(RoundingMode::RoundNearest), 13.0));
1215
1216        let diff = a.clone() - b.clone();
1217        assert!(approx_eq(diff.to_f64(RoundingMode::RoundNearest), 7.0));
1218
1219        let prod = a.clone() * b.clone();
1220        assert!(approx_eq(prod.to_f64(RoundingMode::RoundNearest), 30.0));
1221
1222        let quot = a.clone() / b.clone();
1223        let result = quot.to_f64(RoundingMode::RoundNearest);
1224        assert!(
1225            (result - 10.0 / 3.0).abs() < 1e-10,
1226            "10/3 = {}, expected {}",
1227            result,
1228            10.0 / 3.0
1229        );
1230
1231        let neg = -a;
1232        assert!(approx_eq(neg.to_f64(RoundingMode::RoundNearest), -10.0));
1233    }
1234
1235    #[test]
1236    fn test_context() {
1237        let ctx = ArbitraryFloatContext::new(Precision::new(128), RoundingMode::RoundNearest);
1238
1239        let a = ctx.from_f64(3.5);
1240        let b = ctx.from_f64(2.25);
1241
1242        let sum = ctx.add(&a, &b);
1243        assert!(approx_eq(
1244            sum.to_f64(RoundingMode::RoundNearest),
1245            3.5 + 2.25
1246        ));
1247
1248        let sqrt_2 = ctx.sqrt(&ctx.from_f64(2.0));
1249        assert!((sqrt_2.to_f64(RoundingMode::RoundNearest) - 2.0f64.sqrt()).abs() < 1e-14);
1250    }
1251
1252    #[test]
1253    fn test_from_str() {
1254        let prec = Precision::new(64);
1255
1256        let nan = ArbitraryFloat::from_str("NaN", prec).expect("serialization failed");
1257        assert!(nan.is_nan());
1258
1259        let inf = ArbitraryFloat::from_str("inf", prec).expect("serialization failed");
1260        assert!(inf.is_pos_infinity());
1261
1262        let neg_inf = ArbitraryFloat::from_str("-inf", prec).expect("serialization failed");
1263        assert!(neg_inf.is_neg_infinity());
1264
1265        let val = ArbitraryFloat::from_str("3.5", prec).expect("serialization failed");
1266        assert!(approx_eq(val.to_f64(RoundingMode::RoundNearest), 3.5));
1267    }
1268
1269    #[test]
1270    fn test_high_precision() {
1271        // Test that higher precision gives more accurate results
1272        let low_prec = Precision::new(53);
1273        let high_prec = Precision::new(256);
1274
1275        // Compute 1/3 * 3 at different precisions
1276        let one_low = ArbitraryFloat::from_f64(1.0, low_prec);
1277        let three_low = ArbitraryFloat::from_f64(3.0, low_prec);
1278        let div_low = ArbitraryFloat::div(&one_low, &three_low, RoundingMode::RoundNearest);
1279        let result_low = ArbitraryFloat::mul(&div_low, &three_low, RoundingMode::RoundNearest);
1280
1281        let one_high = ArbitraryFloat::from_f64(1.0, high_prec);
1282        let three_high = ArbitraryFloat::from_f64(3.0, high_prec);
1283        let div_high = ArbitraryFloat::div(&one_high, &three_high, RoundingMode::RoundNearest);
1284        let result_high = ArbitraryFloat::mul(&div_high, &three_high, RoundingMode::RoundNearest);
1285
1286        // Both should be close to 1, but high precision should be closer
1287        let error_low = (result_low.to_f64(RoundingMode::RoundNearest) - 1.0).abs();
1288        let error_high = (result_high.to_f64(RoundingMode::RoundNearest) - 1.0).abs();
1289
1290        assert!(
1291            error_high <= error_low + 1e-15,
1292            "High precision error {} should be <= low precision error {}",
1293            error_high,
1294            error_low
1295        );
1296    }
1297
1298    #[test]
1299    fn test_display() {
1300        let prec = Precision::new(64);
1301
1302        let nan = ArbitraryFloat::nan(prec);
1303        assert_eq!(format!("{}", nan), "NaN");
1304
1305        let pos_inf = ArbitraryFloat::pos_infinity(prec);
1306        assert_eq!(format!("{}", pos_inf), "+Inf");
1307
1308        let neg_inf = ArbitraryFloat::neg_infinity(prec);
1309        assert_eq!(format!("{}", neg_inf), "-Inf");
1310    }
1311}