Skip to main content

dashu_cmplx/
fmt.rs

1//! [`Display`] / [`Debug`] for [`CBig`] in the algebraic `a+bi` notation.
2//!
3//! This diverges from MPC's parenthesized `"(re im)"` form: `dashu-cmplx` uses the human-readable
4//! algebraic notation (the `num-complex` idiom). The parenthesized form is **not** accepted on input.
5
6use crate::cbig::CBig;
7use core::fmt::{self, Debug, Display, Formatter, Write};
8use dashu_float::round::Round;
9use dashu_float::{FBig, Repr};
10use dashu_int::{IBig, Word};
11
12/// A part is a unit (value exactly `±1`) iff its normalized significand is `±1` at exponent `0`.
13fn is_unit<const B: Word>(repr: &Repr<B>) -> bool {
14    repr.exponent() == 0 && {
15        let s = repr.significand();
16        *s == IBig::ONE || *s == IBig::NEG_ONE
17    }
18}
19
20impl<R: Round, const B: Word> Display for CBig<R, B> {
21    /// Format in algebraic `a+bi` form: `"1+2i"`, `"-3-4i"`, `"5"` (pure real), `"-7i"` (pure
22    /// imaginary), `"i"` (`0+1i`), `"-i"` (`0-1i`). The imaginary term always carries an explicit
23    /// sign, a unit coefficient is elided, and a zero imaginary is omitted. Each coefficient uses
24    /// [`FBig`]'s native `Display` (specials render as `inf` / `-inf`).
25    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
26        let fctx = self.context.float();
27        let re_zero = self.re.is_pos_zero() || self.re.is_neg_zero();
28        let im_zero = self.im.is_pos_zero() || self.im.is_neg_zero();
29        let im_neg = self.im.sign() == dashu_base::Sign::Negative;
30        let im_unit = is_unit(&self.im);
31
32        if im_zero {
33            // pure real (incl. 0+0i → "0")
34            let re = FBig::from_repr(self.re.clone(), fctx);
35            return Display::fmt(&re, f);
36        }
37
38        // imaginary part is nonzero
39        if !re_zero {
40            let re = FBig::from_repr(self.re.clone(), fctx);
41            Display::fmt(&re, f)?;
42            f.write_char(if im_neg { '-' } else { '+' })?;
43        } else if im_neg {
44            f.write_char('-')?;
45        }
46        if !im_unit {
47            let im_abs_repr = if im_neg {
48                -self.im.clone()
49            } else {
50                self.im.clone()
51            };
52            let im_abs = FBig::from_repr(im_abs_repr, fctx);
53            Display::fmt(&im_abs, f)?;
54        }
55        f.write_char('i')
56    }
57}
58
59impl<R: Round, const B: Word> Debug for CBig<R, B> {
60    /// Structured form `"re:<re> im:<im> (prec: <p>)"` — e.g. `"re:1.5 im:-2.0 (prec: 53)"` — for
61    /// quick inspection (mirrors `FBig`'s `Debug` style). The alternate `#` form exposes the raw
62    /// significands and exponent scaling.
63    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
64        let fctx = self.context.float();
65        let re = FBig::from_repr(self.re.clone(), fctx);
66        let im = FBig::from_repr(self.im.clone(), fctx);
67        if f.alternate() {
68            f.debug_struct("CBig")
69                .field("re", &re)
70                .field("im", &im)
71                .field("precision", &self.context.precision())
72                .finish()
73        } else {
74            f.write_str("re:")?;
75            Display::fmt(&re, f)?;
76            f.write_str(" im:")?;
77            Display::fmt(&im, f)?;
78            f.write_fmt(format_args!(" (prec: {})", self.context.precision()))
79        }
80    }
81}
82
83#[cfg(test)]
84mod tests {
85    use super::*;
86    use alloc::format;
87    use dashu_float::round::mode;
88
89    type C = CBig<mode::HalfAway, 10>;
90
91    fn c(re: i32, im: i32) -> C {
92        C::from_parts(re.into(), im.into())
93    }
94
95    #[test]
96    fn display_algebraic() {
97        assert_eq!(format!("{}", c(0, 0)), "0");
98        assert_eq!(format!("{}", c(5, 0)), "5");
99        assert_eq!(format!("{}", c(0, 1)), "i");
100        assert_eq!(format!("{}", c(0, -1)), "-i");
101        assert_eq!(format!("{}", c(0, 4)), "4i");
102        assert_eq!(format!("{}", c(0, -4)), "-4i");
103        assert_eq!(format!("{}", c(1, 2)), "1+2i");
104        assert_eq!(format!("{}", c(-3, -4)), "-3-4i");
105        assert_eq!(format!("{}", c(1, 1)), "1+i");
106        assert_eq!(format!("{}", c(2, -1)), "2-i");
107    }
108
109    #[test]
110    fn display_constants() {
111        assert_eq!(format!("{}", C::I), "i");
112        assert_eq!(format!("{}", C::ONE), "1");
113        assert_eq!(format!("{}", C::ZERO), "0");
114    }
115
116    #[test]
117    fn debug_structured() {
118        let z = C::from_parts(FBig::from(3), FBig::from(4));
119        let s = format!("{:?}", z);
120        assert!(s.starts_with("re:3 im:4 (prec:"));
121    }
122}