Skip to main content

oxinum_float/
mp_float.rs

1//! `MpFloat` — a `rug::Float`-compatible adapter over `dashu-float`'s `DBig`.
2//!
3//! This module provides [`MpFloat`] and [`MpComplex`] as drop-in replacements
4//! for the `rug::Float` and `rug::Complex` types used in scirs2-special's
5//! `arbitrary_precision` module.  The API surface matches what that module
6//! actually calls; it is **not** a complete re-implementation of the rug API.
7//!
8//! ## Design
9//!
10//! `MpFloat` wraps `dashu-float`'s `DBig` (decimal arbitrary-precision
11//! big-float) and delegates all arithmetic to it.  Special functions
12//! (gamma, erf, etc.) delegate to [`crate::special`].
13//!
14//! ## Precision
15//!
16//! `rug::Float` precision is measured in **binary bits**.  `DBig` precision
17//! is in **decimal digits**.  The conversion used here is:
18//!   `decimal_digits = ceil(bits * log10(2))` ≈ `bits * 0.30103`
19//! plus a 5-digit guard margin, with a minimum of 10 decimal digits.
20
21use crate::{
22    special::{bessel_j0, catalan, digamma, erf, erfc, euler_gamma, gamma, ln_gamma},
23    DBig,
24};
25use std::{
26    fmt,
27    ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign},
28    str::FromStr,
29};
30
31// ---------------------------------------------------------------------------
32// Rounding mode (replaces rug::float::Round)
33// ---------------------------------------------------------------------------
34
35/// Rounding modes for arbitrary-precision operations.
36///
37/// This enum mirrors `rug::float::Round` for API compatibility.
38#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
39pub enum Round {
40    /// Round to nearest, ties away from zero (default).
41    #[default]
42    Nearest,
43    /// Round toward positive infinity.
44    Up,
45    /// Round toward negative infinity.
46    Down,
47    /// Round toward zero.
48    Zero,
49}
50
51// ---------------------------------------------------------------------------
52// MpFloat
53// ---------------------------------------------------------------------------
54
55/// Arbitrary-precision floating-point number.
56///
57/// Wraps [`DBig`] with a precision (in **binary bits**) stored alongside so
58/// that `prec()` returns the correct bit width.  All arithmetic is carried
59/// out at the stored precision.
60#[derive(Clone, Debug)]
61pub struct MpFloat {
62    value: DBig,
63    bits: u32,
64}
65
66impl MpFloat {
67    // -----------------------------------------------------------------------
68    // Constructors
69    // -----------------------------------------------------------------------
70
71    /// Create a new `MpFloat` from an `f64` at the given **bit precision**.
72    ///
73    /// # Examples
74    ///
75    /// ```
76    /// use oxinum_float::mp_float::MpFloat;
77    /// let x = MpFloat::with_val(256, 3.14f64);
78    /// assert!((x.to_f64() - 3.14).abs() < 1e-10);
79    /// ```
80    pub fn with_val(bits: u32, v: f64) -> Self {
81        let prec = bits_to_decimal_prec(bits);
82        let s = format!("{:.prec$e}", v, prec = prec + 5);
83        let d = DBig::from_str(&s)
84            .unwrap_or_else(|_| DBig::from_str(&format!("{v}")).expect("f64 to DBig: fallback"))
85            .with_precision(prec)
86            .value();
87        Self { value: d, bits }
88    }
89
90    /// Create a new `MpFloat` from another `MpFloat` value at the given bit
91    /// precision (analogous to `rug::Float::with_val(bits, &other)`).
92    pub fn with_val_from(bits: u32, other: &MpFloat) -> Self {
93        let prec = bits_to_decimal_prec(bits);
94        Self {
95            value: other.value.clone().with_precision(prec).value(),
96            bits,
97        }
98    }
99
100    /// Create from a `DBig` reference, using the existing precision.
101    pub fn from_dbig(d: &DBig, bits: u32) -> Self {
102        let prec = bits_to_decimal_prec(bits);
103        Self {
104            value: d.clone().with_precision(prec).value(),
105            bits,
106        }
107    }
108
109    // -----------------------------------------------------------------------
110    // Accessors
111    // -----------------------------------------------------------------------
112
113    /// Return the precision in **binary bits**.
114    pub fn prec(&self) -> u32 {
115        self.bits
116    }
117
118    /// Return the decimal precision used internally.
119    pub fn decimal_prec(&self) -> usize {
120        bits_to_decimal_prec(self.bits)
121    }
122
123    /// Convert to `f64` (may lose precision).
124    pub fn to_f64(&self) -> f64 {
125        self.value.to_string().parse::<f64>().unwrap_or(0.0)
126    }
127
128    /// Borrow the inner `DBig`.
129    pub fn as_dbig(&self) -> &DBig {
130        &self.value
131    }
132
133    // -----------------------------------------------------------------------
134    // Numeric predicates
135    // -----------------------------------------------------------------------
136
137    /// True if the value is exactly zero.
138    pub fn is_zero(&self) -> bool {
139        is_zero_dbig(&self.value)
140    }
141
142    /// True if the value is finite (always true for `DBig`).
143    pub fn is_finite(&self) -> bool {
144        // dashu-float DBig is always finite (no Inf/NaN)
145        true
146    }
147
148    /// True if the value is an integer (fractional part is zero).
149    pub fn is_integer(&self) -> bool {
150        is_integer_dbig(&self.value)
151    }
152
153    /// True if the value is strictly positive.
154    pub fn is_sign_positive(&self) -> bool {
155        !self.value.to_string().starts_with('-') && !self.is_zero()
156    }
157
158    /// True if the value is strictly negative.
159    pub fn is_sign_negative(&self) -> bool {
160        self.value.to_string().starts_with('-') && !self.is_zero()
161    }
162
163    // -----------------------------------------------------------------------
164    // Math operations
165    // -----------------------------------------------------------------------
166
167    /// Absolute value.
168    pub fn abs(self) -> Self {
169        let s = self.value.to_string();
170        let positive = s.trim_start_matches('-');
171        let d = DBig::from_str(positive)
172            .unwrap_or_else(|_| self.value.clone())
173            .with_precision(self.decimal_prec())
174            .value();
175        Self {
176            value: d,
177            bits: self.bits,
178        }
179    }
180
181    /// Square root.  Returns 0 for negative inputs (mirrors MPFR NaN→0 fallback).
182    pub fn sqrt(self) -> Self {
183        if self.is_sign_negative() {
184            return Self::with_val(self.bits, 0.0);
185        }
186        let prec = self.decimal_prec();
187        match crate::elementary::sqrt(&self.value, prec) {
188            Ok(v) => Self {
189                value: v,
190                bits: self.bits,
191            },
192            Err(_) => Self::with_val(self.bits, 0.0),
193        }
194    }
195
196    /// Exponential e^x.
197    pub fn exp(self) -> Self {
198        let prec = self.decimal_prec();
199        match crate::elementary::exp(&self.value, prec) {
200            Ok(v) => Self {
201                value: v,
202                bits: self.bits,
203            },
204            Err(_) => Self::with_val(self.bits, 0.0),
205        }
206    }
207
208    /// Natural logarithm.  Returns 0 for x ≤ 0 (fallback).
209    pub fn ln(self) -> Self {
210        let prec = self.decimal_prec();
211        match crate::elementary::ln(&self.value, prec) {
212            Ok(v) => Self {
213                value: v,
214                bits: self.bits,
215            },
216            Err(_) => Self::with_val(self.bits, 0.0),
217        }
218    }
219
220    /// Sine.
221    pub fn sin(self) -> Self {
222        let prec = self.decimal_prec();
223        match crate::trig::sin(&self.value, prec) {
224            Ok(v) => Self {
225                value: v,
226                bits: self.bits,
227            },
228            Err(_) => Self::with_val(self.bits, 0.0),
229        }
230    }
231
232    /// Cosine.
233    pub fn cos(self) -> Self {
234        let prec = self.decimal_prec();
235        match crate::trig::cos(&self.value, prec) {
236            Ok(v) => Self {
237                value: v,
238                bits: self.bits,
239            },
240            Err(_) => Self::with_val(self.bits, 0.0),
241        }
242    }
243
244    /// Gamma function Γ(self).
245    pub fn gamma(self) -> Self {
246        let prec = self.decimal_prec();
247        match gamma(&self.value, prec) {
248            Ok(v) => Self {
249                value: v,
250                bits: self.bits,
251            },
252            Err(_) => Self::with_val(self.bits, f64::NAN),
253        }
254    }
255
256    /// Apply gamma function in-place (mirrors `rug::Float::gamma_mut()`).
257    pub fn gamma_mut(&mut self) {
258        let prec = self.decimal_prec();
259        if let Ok(v) = gamma(&self.value, prec) {
260            self.value = v;
261        }
262    }
263
264    /// Log-gamma function ln(Γ(self)).
265    pub fn ln_gamma(self) -> Self {
266        let prec = self.decimal_prec();
267        match ln_gamma(&self.value, prec) {
268            Ok(v) => Self {
269                value: v,
270                bits: self.bits,
271            },
272            Err(_) => Self::with_val(self.bits, f64::NAN),
273        }
274    }
275
276    /// Apply log-gamma in-place (mirrors `rug::Float::ln_gamma_mut()`).
277    pub fn ln_gamma_mut(&mut self) {
278        let prec = self.decimal_prec();
279        if let Ok(v) = ln_gamma(&self.value, prec) {
280            self.value = v;
281        }
282    }
283
284    /// Digamma function ψ(self).
285    pub fn digamma(self) -> Self {
286        let prec = self.decimal_prec();
287        match digamma(&self.value, prec) {
288            Ok(v) => Self {
289                value: v,
290                bits: self.bits,
291            },
292            Err(_) => Self::with_val(self.bits, f64::NAN),
293        }
294    }
295
296    /// Apply digamma in-place (mirrors `rug::Float::digamma_mut()`).
297    pub fn digamma_mut(&mut self) {
298        let prec = self.decimal_prec();
299        if let Ok(v) = digamma(&self.value, prec) {
300            self.value = v;
301        }
302    }
303
304    /// Error function erf(self).
305    pub fn erf(self) -> Self {
306        let prec = self.decimal_prec();
307        match erf(&self.value, prec) {
308            Ok(v) => Self {
309                value: v,
310                bits: self.bits,
311            },
312            Err(_) => Self::with_val(self.bits, 0.0),
313        }
314    }
315
316    /// Apply erf in-place (mirrors `rug::Float::erf_mut()`).
317    pub fn erf_mut(&mut self) {
318        let prec = self.decimal_prec();
319        if let Ok(v) = erf(&self.value, prec) {
320            self.value = v;
321        }
322    }
323
324    /// Complementary error function erfc(self).
325    pub fn erfc(self) -> Self {
326        let prec = self.decimal_prec();
327        match erfc(&self.value, prec) {
328            Ok(v) => Self {
329                value: v,
330                bits: self.bits,
331            },
332            Err(_) => Self::with_val(self.bits, 0.0),
333        }
334    }
335
336    /// Apply erfc in-place (mirrors `rug::Float::erfc_mut()`).
337    pub fn erfc_mut(&mut self) {
338        let prec = self.decimal_prec();
339        if let Ok(v) = erfc(&self.value, prec) {
340            self.value = v;
341        }
342    }
343
344    /// Bessel function J₀(self).
345    pub fn j0(self) -> Self {
346        let prec = self.decimal_prec();
347        match bessel_j0(&self.value, prec) {
348            Ok(v) => Self {
349                value: v,
350                bits: self.bits,
351            },
352            Err(_) => Self::with_val(self.bits, 0.0),
353        }
354    }
355
356    /// Apply j0 in-place (mirrors `rug::Float::j0_mut()`).
357    pub fn j0_mut(&mut self) {
358        let prec = self.decimal_prec();
359        if let Ok(v) = bessel_j0(&self.value, prec) {
360            self.value = v;
361        }
362    }
363
364    /// Raise to the power of another `MpFloat`.
365    pub fn pow_float(self, exp: &MpFloat) -> Self {
366        let prec = self.decimal_prec();
367        match crate::elementary::pow(&self.value, &exp.value, prec) {
368            Ok(v) => Self {
369                value: v,
370                bits: self.bits,
371            },
372            Err(_) => Self::with_val(self.bits, 0.0),
373        }
374    }
375
376    /// Raise to an integer power.
377    pub fn pow_i32(self, exp: i32) -> Self {
378        let prec = self.decimal_prec();
379        let exp_d = dbig_f64(exp as f64, prec);
380        match crate::elementary::pow(&self.value, &exp_d, prec) {
381            Ok(v) => Self {
382                value: v,
383                bits: self.bits,
384            },
385            Err(_) => Self::with_val(self.bits, 0.0),
386        }
387    }
388
389    /// Raise to an f64 power (convenience).
390    pub fn pow_f64(self, exp: f64) -> Self {
391        let prec = self.decimal_prec();
392        let exp_d = dbig_f64(exp, prec);
393        match crate::elementary::pow(&self.value, &exp_d, prec) {
394            Ok(v) => Self {
395                value: v,
396                bits: self.bits,
397            },
398            Err(_) => Self::with_val(self.bits, 0.0),
399        }
400    }
401}
402
403// ---------------------------------------------------------------------------
404// Display and scientific notation formatting
405// ---------------------------------------------------------------------------
406
407impl fmt::Display for MpFloat {
408    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
409        // If a precision was requested, use f64 for the format (sufficient for
410        // typical display purposes).  Without requested precision, show full DBig.
411        if let Some(prec) = f.precision() {
412            write!(f, "{:.prec$}", self.to_f64(), prec = prec)
413        } else {
414            write!(f, "{}", self.value)
415        }
416    }
417}
418
419impl fmt::LowerExp for MpFloat {
420    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
421        if let Some(prec) = f.precision() {
422            write!(f, "{:.prec$e}", self.to_f64(), prec = prec)
423        } else {
424            write!(f, "{:e}", self.to_f64())
425        }
426    }
427}
428
429impl fmt::UpperExp for MpFloat {
430    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
431        if let Some(prec) = f.precision() {
432            write!(f, "{:.prec$E}", self.to_f64(), prec = prec)
433        } else {
434            write!(f, "{:E}", self.to_f64())
435        }
436    }
437}
438
439// ---------------------------------------------------------------------------
440// Comparison
441// ---------------------------------------------------------------------------
442
443impl PartialEq for MpFloat {
444    fn eq(&self, other: &Self) -> bool {
445        self.value == other.value
446    }
447}
448
449impl PartialOrd for MpFloat {
450    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
451        self.value.partial_cmp(&other.value)
452    }
453}
454
455impl PartialEq<f64> for MpFloat {
456    fn eq(&self, other: &f64) -> bool {
457        (self.to_f64() - other).abs() < 1e-300
458    }
459}
460
461impl PartialOrd<f64> for MpFloat {
462    fn partial_cmp(&self, other: &f64) -> Option<std::cmp::Ordering> {
463        self.to_f64().partial_cmp(other)
464    }
465}
466
467// ---------------------------------------------------------------------------
468// Arithmetic ops (owned × owned)
469// ---------------------------------------------------------------------------
470
471impl Add for MpFloat {
472    type Output = MpFloat;
473    fn add(self, rhs: MpFloat) -> MpFloat {
474        let prec = self.bits.max(rhs.bits);
475        let d = (&self.value + &rhs.value)
476            .with_precision(bits_to_decimal_prec(prec))
477            .value();
478        MpFloat {
479            value: d,
480            bits: prec,
481        }
482    }
483}
484
485impl Sub for MpFloat {
486    type Output = MpFloat;
487    fn sub(self, rhs: MpFloat) -> MpFloat {
488        let prec = self.bits.max(rhs.bits);
489        let d = (&self.value - &rhs.value)
490            .with_precision(bits_to_decimal_prec(prec))
491            .value();
492        MpFloat {
493            value: d,
494            bits: prec,
495        }
496    }
497}
498
499impl Mul for MpFloat {
500    type Output = MpFloat;
501    fn mul(self, rhs: MpFloat) -> MpFloat {
502        let prec = self.bits.max(rhs.bits);
503        let d = (&self.value * &rhs.value)
504            .with_precision(bits_to_decimal_prec(prec))
505            .value();
506        MpFloat {
507            value: d,
508            bits: prec,
509        }
510    }
511}
512
513impl Div for MpFloat {
514    type Output = MpFloat;
515    fn div(self, rhs: MpFloat) -> MpFloat {
516        let prec = self.bits.max(rhs.bits);
517        let d = (&self.value / &rhs.value)
518            .with_precision(bits_to_decimal_prec(prec))
519            .value();
520        MpFloat {
521            value: d,
522            bits: prec,
523        }
524    }
525}
526
527// ---------------------------------------------------------------------------
528// Arithmetic ops (owned × &ref)
529// ---------------------------------------------------------------------------
530
531impl Add<&MpFloat> for MpFloat {
532    type Output = MpFloat;
533    fn add(self, rhs: &MpFloat) -> MpFloat {
534        let prec = self.bits.max(rhs.bits);
535        let d = (&self.value + &rhs.value)
536            .with_precision(bits_to_decimal_prec(prec))
537            .value();
538        MpFloat {
539            value: d,
540            bits: prec,
541        }
542    }
543}
544
545impl Sub<&MpFloat> for MpFloat {
546    type Output = MpFloat;
547    fn sub(self, rhs: &MpFloat) -> MpFloat {
548        let prec = self.bits.max(rhs.bits);
549        let d = (&self.value - &rhs.value)
550            .with_precision(bits_to_decimal_prec(prec))
551            .value();
552        MpFloat {
553            value: d,
554            bits: prec,
555        }
556    }
557}
558
559impl Mul<&MpFloat> for MpFloat {
560    type Output = MpFloat;
561    fn mul(self, rhs: &MpFloat) -> MpFloat {
562        let prec = self.bits.max(rhs.bits);
563        let d = (&self.value * &rhs.value)
564            .with_precision(bits_to_decimal_prec(prec))
565            .value();
566        MpFloat {
567            value: d,
568            bits: prec,
569        }
570    }
571}
572
573impl Div<&MpFloat> for MpFloat {
574    type Output = MpFloat;
575    fn div(self, rhs: &MpFloat) -> MpFloat {
576        let prec = self.bits.max(rhs.bits);
577        let d = (&self.value / &rhs.value)
578            .with_precision(bits_to_decimal_prec(prec))
579            .value();
580        MpFloat {
581            value: d,
582            bits: prec,
583        }
584    }
585}
586
587// ---------------------------------------------------------------------------
588// Arithmetic ops (&ref × &ref)
589// ---------------------------------------------------------------------------
590
591impl Add<&MpFloat> for &MpFloat {
592    type Output = MpFloat;
593    fn add(self, rhs: &MpFloat) -> MpFloat {
594        let prec = self.bits.max(rhs.bits);
595        let d = (&self.value + &rhs.value)
596            .with_precision(bits_to_decimal_prec(prec))
597            .value();
598        MpFloat {
599            value: d,
600            bits: prec,
601        }
602    }
603}
604
605impl Sub<&MpFloat> for &MpFloat {
606    type Output = MpFloat;
607    fn sub(self, rhs: &MpFloat) -> MpFloat {
608        let prec = self.bits.max(rhs.bits);
609        let d = (&self.value - &rhs.value)
610            .with_precision(bits_to_decimal_prec(prec))
611            .value();
612        MpFloat {
613            value: d,
614            bits: prec,
615        }
616    }
617}
618
619impl Mul<&MpFloat> for &MpFloat {
620    type Output = MpFloat;
621    fn mul(self, rhs: &MpFloat) -> MpFloat {
622        let prec = self.bits.max(rhs.bits);
623        let d = (&self.value * &rhs.value)
624            .with_precision(bits_to_decimal_prec(prec))
625            .value();
626        MpFloat {
627            value: d,
628            bits: prec,
629        }
630    }
631}
632
633impl Div<&MpFloat> for &MpFloat {
634    type Output = MpFloat;
635    fn div(self, rhs: &MpFloat) -> MpFloat {
636        let prec = self.bits.max(rhs.bits);
637        let d = (&self.value / &rhs.value)
638            .with_precision(bits_to_decimal_prec(prec))
639            .value();
640        MpFloat {
641            value: d,
642            bits: prec,
643        }
644    }
645}
646
647// ---------------------------------------------------------------------------
648// f64 arithmetic
649// ---------------------------------------------------------------------------
650
651impl Add<f64> for MpFloat {
652    type Output = MpFloat;
653    fn add(self, rhs: f64) -> MpFloat {
654        let rhs_mp = MpFloat::with_val(self.bits, rhs);
655        self + rhs_mp
656    }
657}
658
659impl Sub<f64> for MpFloat {
660    type Output = MpFloat;
661    fn sub(self, rhs: f64) -> MpFloat {
662        let rhs_mp = MpFloat::with_val(self.bits, rhs);
663        self - rhs_mp
664    }
665}
666
667impl Mul<f64> for MpFloat {
668    type Output = MpFloat;
669    fn mul(self, rhs: f64) -> MpFloat {
670        let rhs_mp = MpFloat::with_val(self.bits, rhs);
671        self * rhs_mp
672    }
673}
674
675impl Div<f64> for MpFloat {
676    type Output = MpFloat;
677    fn div(self, rhs: f64) -> MpFloat {
678        let rhs_mp = MpFloat::with_val(self.bits, rhs);
679        self / rhs_mp
680    }
681}
682
683impl Add<f64> for &MpFloat {
684    type Output = MpFloat;
685    fn add(self, rhs: f64) -> MpFloat {
686        let rhs_mp = MpFloat::with_val(self.bits, rhs);
687        self + &rhs_mp
688    }
689}
690
691impl Sub<f64> for &MpFloat {
692    type Output = MpFloat;
693    fn sub(self, rhs: f64) -> MpFloat {
694        let rhs_mp = MpFloat::with_val(self.bits, rhs);
695        self - &rhs_mp
696    }
697}
698
699impl Mul<f64> for &MpFloat {
700    type Output = MpFloat;
701    fn mul(self, rhs: f64) -> MpFloat {
702        let rhs_mp = MpFloat::with_val(self.bits, rhs);
703        self * &rhs_mp
704    }
705}
706
707impl Div<f64> for &MpFloat {
708    type Output = MpFloat;
709    fn div(self, rhs: f64) -> MpFloat {
710        let rhs_mp = MpFloat::with_val(self.bits, rhs);
711        self / &rhs_mp
712    }
713}
714
715// ---------------------------------------------------------------------------
716// Negation
717// ---------------------------------------------------------------------------
718
719impl Neg for MpFloat {
720    type Output = MpFloat;
721    fn neg(self) -> MpFloat {
722        let bits = self.bits;
723        let s = self.value.to_string();
724        let neg_s = if let Some(stripped) = s.strip_prefix('-') {
725            stripped.to_string()
726        } else if s == "0" || s == "0.0" {
727            s
728        } else {
729            format!("-{s}")
730        };
731        let d = DBig::from_str(&neg_s)
732            .unwrap_or_else(|_| DBig::from_str("0.0").expect("zero"))
733            .with_precision(bits_to_decimal_prec(bits))
734            .value();
735        MpFloat { value: d, bits }
736    }
737}
738
739impl Neg for &MpFloat {
740    type Output = MpFloat;
741    fn neg(self) -> MpFloat {
742        self.clone().neg()
743    }
744}
745
746// ---------------------------------------------------------------------------
747// Assign ops
748// ---------------------------------------------------------------------------
749
750impl AddAssign<&MpFloat> for MpFloat {
751    fn add_assign(&mut self, rhs: &MpFloat) {
752        let new = &*self + rhs;
753        *self = new;
754    }
755}
756
757impl AddAssign<MpFloat> for MpFloat {
758    fn add_assign(&mut self, rhs: MpFloat) {
759        let new = &*self + &rhs;
760        *self = new;
761    }
762}
763
764impl AddAssign<f64> for MpFloat {
765    fn add_assign(&mut self, rhs: f64) {
766        let new = self.clone() + rhs;
767        *self = new;
768    }
769}
770
771impl SubAssign<&MpFloat> for MpFloat {
772    fn sub_assign(&mut self, rhs: &MpFloat) {
773        let new = &*self - rhs;
774        *self = new;
775    }
776}
777
778impl SubAssign<MpFloat> for MpFloat {
779    fn sub_assign(&mut self, rhs: MpFloat) {
780        let new = &*self - &rhs;
781        *self = new;
782    }
783}
784
785impl SubAssign<f64> for MpFloat {
786    fn sub_assign(&mut self, rhs: f64) {
787        let new = self.clone() - rhs;
788        *self = new;
789    }
790}
791
792impl MulAssign<&MpFloat> for MpFloat {
793    fn mul_assign(&mut self, rhs: &MpFloat) {
794        let new = &*self * rhs;
795        *self = new;
796    }
797}
798
799impl MulAssign<MpFloat> for MpFloat {
800    fn mul_assign(&mut self, rhs: MpFloat) {
801        let new = &*self * &rhs;
802        *self = new;
803    }
804}
805
806impl MulAssign<f64> for MpFloat {
807    fn mul_assign(&mut self, rhs: f64) {
808        let new = self.clone() * rhs;
809        *self = new;
810    }
811}
812
813impl DivAssign<f64> for MpFloat {
814    fn div_assign(&mut self, rhs: f64) {
815        let new = self.clone() / rhs;
816        *self = new;
817    }
818}
819
820impl DivAssign<MpFloat> for MpFloat {
821    fn div_assign(&mut self, rhs: MpFloat) {
822        let new = self.clone() / rhs;
823        *self = new;
824    }
825}
826
827impl DivAssign<&MpFloat> for MpFloat {
828    fn div_assign(&mut self, rhs: &MpFloat) {
829        let new = self.clone() / rhs;
830        *self = new;
831    }
832}
833
834// ---------------------------------------------------------------------------
835// Pow trait (mirrors rug::ops::Pow)
836// ---------------------------------------------------------------------------
837
838/// Pow implementation for `MpFloat` — `base.pow(exp)`.
839pub trait MpPow<T> {
840    fn pow(self, exp: T) -> MpFloat;
841}
842
843impl MpPow<&MpFloat> for MpFloat {
844    fn pow(self, exp: &MpFloat) -> MpFloat {
845        self.pow_float(exp)
846    }
847}
848
849impl MpPow<MpFloat> for MpFloat {
850    fn pow(self, exp: MpFloat) -> MpFloat {
851        self.pow_float(&exp)
852    }
853}
854
855impl MpPow<f64> for MpFloat {
856    fn pow(self, exp: f64) -> MpFloat {
857        self.pow_f64(exp)
858    }
859}
860
861impl MpPow<i32> for MpFloat {
862    fn pow(self, exp: i32) -> MpFloat {
863        self.pow_i32(exp)
864    }
865}
866
867// ---------------------------------------------------------------------------
868// MpComplex
869// ---------------------------------------------------------------------------
870
871/// Arbitrary-precision complex number.
872///
873/// Stores real and imaginary parts as [`MpFloat`] at a shared bit precision.
874/// This mirrors enough of `rug::Complex` to replace its usage in
875/// `scirs2-special::arbitrary_precision`.
876#[derive(Clone, Debug)]
877pub struct MpComplex {
878    re: MpFloat,
879    im: MpFloat,
880    bits: u32,
881}
882
883impl MpComplex {
884    /// Create a complex number from `(real, imag)` f64 pair.
885    ///
886    /// # Examples
887    ///
888    /// ```
889    /// use oxinum_float::mp_float::MpComplex;
890    /// let z = MpComplex::with_val(256, (1.0f64, 2.0f64));
891    /// assert!((z.real().to_f64() - 1.0).abs() < 1e-10);
892    /// assert!((z.imag().to_f64() - 2.0).abs() < 1e-10);
893    /// ```
894    pub fn with_val(bits: u32, (re, im): (f64, f64)) -> Self {
895        Self {
896            re: MpFloat::with_val(bits, re),
897            im: MpFloat::with_val(bits, im),
898            bits,
899        }
900    }
901
902    /// Borrow the real part.
903    pub fn real(&self) -> &MpFloat {
904        &self.re
905    }
906
907    /// Borrow the imaginary part.
908    pub fn imag(&self) -> &MpFloat {
909        &self.im
910    }
911
912    /// Decompose into `(real, imag)` `MpFloat` pair (mirrors
913    /// `rug::Complex::into_real_imag()`).
914    pub fn into_real_imag(self) -> (MpFloat, MpFloat) {
915        (self.re, self.im)
916    }
917
918    /// Precision in bits.
919    pub fn prec(&self) -> u32 {
920        self.bits
921    }
922}
923
924// ---------------------------------------------------------------------------
925// PrecisionContext helper: convert bits → decimal prec
926// ---------------------------------------------------------------------------
927
928/// Convert a **bit** precision to an approximate **decimal digit** count.
929///
930/// Formula: `ceil(bits * log10(2))` + 5 guard digits, minimum 10.
931pub fn bits_to_decimal_prec(bits: u32) -> usize {
932    let digits = (bits as f64 * std::f64::consts::LOG2_10.recip()).ceil() as usize;
933    digits.max(10) + 5
934}
935
936/// Build a `DBig` constant for the Euler-Mascheroni constant at the given bit
937/// precision. Convenience wrapper so PrecisionContext doesn't need to import
938/// special module.
939pub fn euler_gamma_at_bits(bits: u32) -> MpFloat {
940    let prec = bits_to_decimal_prec(bits);
941    let d = euler_gamma(prec);
942    MpFloat { value: d, bits }
943}
944
945/// Build Catalan's constant at the given bit precision.
946pub fn catalan_at_bits(bits: u32) -> MpFloat {
947    let prec = bits_to_decimal_prec(bits);
948    let d = catalan(prec);
949    MpFloat { value: d, bits }
950}
951
952// ---------------------------------------------------------------------------
953// Internal helpers
954// ---------------------------------------------------------------------------
955
956fn is_zero_dbig(v: &DBig) -> bool {
957    let s = v.to_string().trim_start_matches('-').to_string();
958    s == "0" || s == "0.0" || {
959        if let Some(dot) = s.find('.') {
960            let int = &s[..dot];
961            let frac = &s[dot + 1..];
962            int == "0" && frac.chars().all(|c| c == '0')
963        } else {
964            s.chars().all(|c| c == '0')
965        }
966    }
967}
968
969fn is_integer_dbig(v: &DBig) -> bool {
970    let s = v.to_string().trim_start_matches('-').to_string();
971    if let Some(dot) = s.find('.') {
972        let frac = &s[dot + 1..];
973        frac.chars().all(|c| c == '0')
974    } else {
975        true
976    }
977}
978
979fn dbig_f64(v: f64, precision: usize) -> DBig {
980    let s = format!("{:.prec$e}", v, prec = precision + 5);
981    DBig::from_str(&s)
982        .unwrap_or_else(|_| DBig::from_str(&format!("{v}")).expect("f64 to DBig fallback"))
983        .with_precision(precision)
984        .value()
985}
986
987// ---------------------------------------------------------------------------
988// Tests
989// ---------------------------------------------------------------------------
990
991#[cfg(test)]
992mod tests {
993    use super::*;
994
995    #[test]
996    fn with_val_pi() {
997        let x = MpFloat::with_val(256, std::f64::consts::PI);
998        let v = x.to_f64();
999        assert!((v - std::f64::consts::PI).abs() < 1e-10, "pi = {v}");
1000    }
1001
1002    #[test]
1003    fn arithmetic_ops() {
1004        let a = MpFloat::with_val(128, 3.0);
1005        let b = MpFloat::with_val(128, 2.0);
1006        assert!(((&a + &b).to_f64() - 5.0).abs() < 1e-10);
1007        assert!(((&a - &b).to_f64() - 1.0).abs() < 1e-10);
1008        assert!(((&a * &b).to_f64() - 6.0).abs() < 1e-10);
1009        assert!(((&a / &b).to_f64() - 1.5).abs() < 1e-10);
1010    }
1011
1012    #[test]
1013    fn sqrt_exp_ln() {
1014        let x = MpFloat::with_val(128, 4.0);
1015        let s = x.sqrt();
1016        assert!((s.to_f64() - 2.0).abs() < 1e-8, "sqrt(4) = {}", s.to_f64());
1017
1018        let e = MpFloat::with_val(128, 1.0).exp();
1019        assert!((e.to_f64() - std::f64::consts::E).abs() < 1e-8);
1020
1021        let l = MpFloat::with_val(128, std::f64::consts::E).ln();
1022        assert!((l.to_f64() - 1.0).abs() < 1e-8);
1023    }
1024
1025    #[test]
1026    fn is_zero_predicate() {
1027        let z = MpFloat::with_val(128, 0.0);
1028        let nz = MpFloat::with_val(128, 1.0);
1029        assert!(z.is_zero());
1030        assert!(!nz.is_zero());
1031    }
1032
1033    #[test]
1034    fn is_integer_predicate() {
1035        let i = MpFloat::with_val(128, 5.0);
1036        let f = MpFloat::with_val(128, 5.5);
1037        assert!(i.is_integer());
1038        assert!(!f.is_integer());
1039    }
1040
1041    #[test]
1042    fn gamma_mut_test() {
1043        let mut x = MpFloat::with_val(128, 5.0);
1044        x.gamma_mut();
1045        // Γ(5) = 4! = 24
1046        assert!(
1047            (x.to_f64() - 24.0).abs() < 1e-5,
1048            "gamma(5) = {}",
1049            x.to_f64()
1050        );
1051    }
1052
1053    #[test]
1054    fn erf_mut_test() {
1055        let mut x = MpFloat::with_val(128, 0.0);
1056        x.erf_mut();
1057        assert!(x.is_zero());
1058    }
1059
1060    #[test]
1061    fn neg_operator() {
1062        let x = MpFloat::with_val(128, 3.0);
1063        let neg_x = -x;
1064        assert!(neg_x.is_sign_negative());
1065        assert!((neg_x.to_f64() + 3.0).abs() < 1e-10);
1066    }
1067
1068    #[test]
1069    fn complex_into_parts() {
1070        let z = MpComplex::with_val(128, (1.5_f64, 2.5_f64));
1071        let (re, im) = z.into_real_imag();
1072        assert!((re.to_f64() - 1.5).abs() < 1e-10);
1073        assert!((im.to_f64() - 2.5).abs() < 1e-10);
1074    }
1075
1076    #[test]
1077    fn euler_gamma_const() {
1078        let g = euler_gamma_at_bits(256);
1079        let v = g.to_f64();
1080        assert!((v - 0.5772156649_f64).abs() < 1e-8, "euler_gamma = {v}");
1081    }
1082
1083    #[test]
1084    fn catalan_const() {
1085        let g = catalan_at_bits(256);
1086        let v = g.to_f64();
1087        assert!((v - 0.9159655942_f64).abs() < 1e-8, "catalan = {v}");
1088    }
1089}