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