Skip to main content

zenith_float_num/
complex.rs

1//! Rectangular complex numbers over [`ExactNum`].
2
3use crate::defs::DEFAULT_P;
4use crate::Consts;
5use crate::ExactNum;
6use crate::Exponent;
7use crate::RoundingMode;
8use crate::WORD_BIT_SIZE;
9
10/// Complex value `re + i·im` with software-limb real and imaginary parts.
11#[derive(Debug, Clone)]
12pub struct ExactComplex {
13    re: ExactNum,
14    im: ExactNum,
15}
16
17impl ExactComplex {
18    /// Constructs `re + i·im`.
19    pub fn new(re: ExactNum, im: ExactNum) -> Self {
20        Self { re, im }
21    }
22
23    /// Real part.
24    pub fn re(&self) -> &ExactNum {
25        &self.re
26    }
27
28    /// Imaginary part.
29    pub fn im(&self) -> &ExactNum {
30        &self.im
31    }
32
33    /// `0 + 0i` at precision `p`.
34    pub fn zero(p: usize) -> Self {
35        Self::new(ExactNum::new(p), ExactNum::new(p))
36    }
37
38    /// `1 + 0i` at precision `p`.
39    pub fn one(p: usize) -> Self {
40        Self::new(ExactNum::from_u8(1, p), ExactNum::new(p))
41    }
42
43    /// `0 + 1i` at precision `p`.
44    pub fn i(p: usize) -> Self {
45        Self::new(ExactNum::new(p), ExactNum::from_u8(1, p))
46    }
47
48    /// True if either part is inexact.
49    pub fn inexact(&self) -> bool {
50        self.re.inexact() || self.im.inexact()
51    }
52
53    /// Sets the inexact flag on both parts.
54    pub fn set_inexact(&mut self, inexact: bool) {
55        self.re.set_inexact(inexact);
56        self.im.set_inexact(inexact);
57    }
58
59    /// Rounds both parts to precision `p`.
60    pub fn set_precision(&mut self, p: usize, rm: RoundingMode) -> Result<(), crate::Error> {
61        self.re.set_precision(p, rm)?;
62        self.im.set_precision(p, rm)
63    }
64
65    /// Real `x` as `x + 0i`. Imaginary zero uses precision `p`.
66    pub fn from_real(re: ExactNum, p: usize) -> Self {
67        Self::new(re, ExactNum::new(p))
68    }
69
70    /// `1 / self`.
71    pub fn reciprocal(&self, p: usize, rm: RoundingMode) -> Self {
72        Self::one(p).div(self, p, rm)
73    }
74
75    /// True if either part is NaN.
76    pub fn is_nan(&self) -> bool {
77        self.re.is_nan() || self.im.is_nan()
78    }
79
80    /// Complex conjugate.
81    pub fn conj(&self) -> Self {
82        Self::new(self.re.clone(), self.im.neg())
83    }
84
85    /// Modulus `|z| = hypot(re, im)` at precision `p`.
86    pub fn abs(&self, p: usize, rm: RoundingMode) -> ExactNum {
87        self.re.hypot(&self.im, p, rm)
88    }
89
90    /// Argument `atan2(im, re)` at precision `p`.
91    ///
92    /// Branch: same as real `atan2`; values lie in (−π, π]. The cut of `ln` / `sqrt` /
93    /// `pow` is the non-positive real axis, approached from above as +π and from below as −π.
94    pub fn arg(&self, p: usize, rm: RoundingMode, cc: &mut Consts) -> ExactNum {
95        self.im.atan2(&self.re, p, rm, cc)
96    }
97
98    /// `self + rhs` at precision `p`.
99    pub fn add(&self, rhs: &Self, p: usize, rm: RoundingMode) -> Self {
100        Self::new(self.re.add(&rhs.re, p, rm), self.im.add(&rhs.im, p, rm))
101    }
102
103    /// `self - rhs` at precision `p`.
104    pub fn sub(&self, rhs: &Self, p: usize, rm: RoundingMode) -> Self {
105        Self::new(self.re.sub(&rhs.re, p, rm), self.im.sub(&rhs.im, p, rm))
106    }
107
108    /// `self * rhs` at precision `p`.
109    ///
110    /// Each of `ac`, `bd`, `ad`, `bc` is rounded at `(p, rm)`, then `ac−bd` and
111    /// `ad+bc` are rounded at `(p, rm)`. That matches the MPFR componentwise gold.
112    /// Callers that pass [`RoundingMode::None`] still keep full products (series paths).
113    pub fn mul(&self, rhs: &Self, p: usize, rm: RoundingMode) -> Self {
114        let ac = self.re.mul(&rhs.re, p, rm);
115        let bd = self.im.mul(&rhs.im, p, rm);
116        let ad = self.re.mul(&rhs.im, p, rm);
117        let bc = self.im.mul(&rhs.re, p, rm);
118        Self::new(ac.sub(&bd, p, rm), ad.add(&bc, p, rm))
119    }
120
121    /// `self / rhs` at precision `p`.
122    pub fn div(&self, rhs: &Self, p: usize, rm: RoundingMode) -> Self {
123        let ac = self.re.mul(&rhs.re, p, RoundingMode::None);
124        let bd = self.im.mul(&rhs.im, p, RoundingMode::None);
125        let bc = self.im.mul(&rhs.re, p, RoundingMode::None);
126        let ad = self.re.mul(&rhs.im, p, RoundingMode::None);
127        let den = rhs.re.mul(&rhs.re, p, RoundingMode::None).add(
128            &rhs.im.mul(&rhs.im, p, RoundingMode::None),
129            p,
130            RoundingMode::None,
131        );
132        Self::new(
133            ac.add(&bd, p, rm).div(&den, p, rm),
134            bc.sub(&ad, p, rm).div(&den, p, rm),
135        )
136    }
137
138    /// `e^self` using `exp(re) (cos(im) + i sin(im))`.
139    pub fn exp(&self, p: usize, rm: RoundingMode, cc: &mut Consts) -> Self {
140        let er = self.re.exp(p, RoundingMode::None, cc);
141        let (s, c) = self.im.sin_cos(p, RoundingMode::None, cc);
142        Self::new(er.mul(&c, p, rm), er.mul(&s, p, rm))
143    }
144
145    /// Principal logarithm `ln|z| + i Arg(z)`.
146    ///
147    /// Branch cut: (−∞, 0] on the real axis. `ln(−1)` is `iπ` (argument +π).
148    pub fn ln(&self, p: usize, rm: RoundingMode, cc: &mut Consts) -> Self {
149        let mag = self.abs(p, RoundingMode::None);
150        Self::new(mag.ln(p, rm, cc), self.arg(p, rm, cc))
151    }
152
153    /// `sin(self)` via `sin(re)cosh(im) + i cos(re)sinh(im)`.
154    ///
155    /// The complex value is **not** passed to `rem_pi`. Only the real (resp. imaginary)
156    /// *component* uses real `sin_cos` / `sinh_cosh`.
157    pub fn sin(&self, p: usize, rm: RoundingMode, cc: &mut Consts) -> Self {
158        let (sn, cs) = self.re.sin_cos(p, RoundingMode::None, cc);
159        let (sh, ch) = self.im.sinh_cosh(p, RoundingMode::None, cc);
160        Self::new(sn.mul(&ch, p, rm), cs.mul(&sh, p, rm))
161    }
162
163    /// `cos(self)` via `cos(re)cosh(im) - i sin(re)sinh(im)`.
164    pub fn cos(&self, p: usize, rm: RoundingMode, cc: &mut Consts) -> Self {
165        let (sn, cs) = self.re.sin_cos(p, RoundingMode::None, cc);
166        let (sh, ch) = self.im.sinh_cosh(p, RoundingMode::None, cc);
167        Self::new(cs.mul(&ch, p, rm), sn.mul(&sh, p, rm).neg())
168    }
169
170    fn finish(self, p: usize, rm: RoundingMode) -> Self {
171        let mut re = self.re;
172        let mut im = self.im;
173        if let Err(e) = re.set_precision(p, rm) {
174            return Self::new(ExactNum::nan(Some(e)), ExactNum::nan(Some(e)));
175        }
176        if let Err(e) = im.set_precision(p, rm) {
177            return Self::new(ExactNum::nan(Some(e)), ExactNum::nan(Some(e)));
178        }
179        Self::new(re, im)
180    }
181
182    fn work_p(p: usize) -> usize {
183        p.saturating_add(WORD_BIT_SIZE)
184    }
185
186    /// `tan(self) = sin(self) / cos(self)`.
187    pub fn tan(&self, p: usize, rm: RoundingMode, cc: &mut Consts) -> Self {
188        let p_x = Self::work_p(p);
189        self.sin(p_x, RoundingMode::None, cc)
190            .div(
191                &self.cos(p_x, RoundingMode::None, cc),
192                p_x,
193                RoundingMode::None,
194            )
195            .finish(p, rm)
196    }
197
198    /// `sinh(self)` via `sinh(re)cos(im) + i cosh(re)sin(im)`.
199    pub fn sinh(&self, p: usize, rm: RoundingMode, cc: &mut Consts) -> Self {
200        let p_x = Self::work_p(p);
201        let (sh, ch) = self.re.sinh_cosh(p_x, RoundingMode::None, cc);
202        let (sn, cs) = self.im.sin_cos(p_x, RoundingMode::None, cc);
203        Self::new(
204            sh.mul(&cs, p_x, RoundingMode::None),
205            ch.mul(&sn, p_x, RoundingMode::None),
206        )
207        .finish(p, rm)
208    }
209
210    /// `cosh(self)` via `cosh(re)cos(im) + i sinh(re)sin(im)`.
211    pub fn cosh(&self, p: usize, rm: RoundingMode, cc: &mut Consts) -> Self {
212        let p_x = Self::work_p(p);
213        let (sh, ch) = self.re.sinh_cosh(p_x, RoundingMode::None, cc);
214        let (sn, cs) = self.im.sin_cos(p_x, RoundingMode::None, cc);
215        Self::new(
216            ch.mul(&cs, p_x, RoundingMode::None),
217            sh.mul(&sn, p_x, RoundingMode::None),
218        )
219        .finish(p, rm)
220    }
221
222    /// `tanh(self) = sinh(self) / cosh(self)`.
223    pub fn tanh(&self, p: usize, rm: RoundingMode, cc: &mut Consts) -> Self {
224        let p_x = Self::work_p(p);
225        self.sinh(p_x, RoundingMode::None, cc)
226            .div(
227                &self.cosh(p_x, RoundingMode::None, cc),
228                p_x,
229                RoundingMode::None,
230            )
231            .finish(p, rm)
232    }
233
234    /// Principal square root: `√r (cos(θ/2) + i sin(θ/2))`.
235    ///
236    /// Branch cut: (−∞, 0]. Real part of the result is ≥ 0. `sqrt(−1)` is `+i`.
237    pub fn sqrt(&self, p: usize, rm: RoundingMode, cc: &mut Consts) -> Self {
238        let p_x = Self::work_p(p);
239        let r = self.abs(p_x, RoundingMode::None);
240        let th = self.arg(p_x, RoundingMode::None, cc);
241        let sr = r.sqrt(p_x, RoundingMode::None);
242        let half_th = th.ldexp(-1, p_x, RoundingMode::None);
243        let (s, c) = half_th.sin_cos(p_x, RoundingMode::None, cc);
244        Self::new(
245            sr.mul(&c, p_x, RoundingMode::None),
246            sr.mul(&s, p_x, RoundingMode::None),
247        )
248        .finish(p, rm)
249    }
250
251    /// `log2(self) = ln(self) / ln 2` (principal branch).
252    pub fn log2(&self, p: usize, rm: RoundingMode, cc: &mut Consts) -> Self {
253        let p_x = Self::work_p(p);
254        let ln = self.ln(p_x, RoundingMode::None, cc);
255        let base = Self::from_real(cc.ln_2(p_x, RoundingMode::None), p_x);
256        ln.div(&base, p_x, RoundingMode::None).finish(p, rm)
257    }
258
259    /// `log10(self) = ln(self) / ln 10` (principal branch).
260    pub fn log10(&self, p: usize, rm: RoundingMode, cc: &mut Consts) -> Self {
261        let p_x = Self::work_p(p);
262        let ln = self.ln(p_x, RoundingMode::None, cc);
263        let base = Self::from_real(cc.ln_10(p_x, RoundingMode::None), p_x);
264        ln.div(&base, p_x, RoundingMode::None).finish(p, rm)
265    }
266
267    /// `log_base(self) = ln(self) / ln(base)` (principal branch).
268    pub fn log(&self, base: &Self, p: usize, rm: RoundingMode, cc: &mut Consts) -> Self {
269        let p_x = Self::work_p(p);
270        let ln = self.ln(p_x, RoundingMode::None, cc);
271        let lnb = base.ln(p_x, RoundingMode::None, cc);
272        ln.div(&lnb, p_x, RoundingMode::None).finish(p, rm)
273    }
274
275    /// `ln(1 + self)` (principal branch).
276    pub fn log1p(&self, p: usize, rm: RoundingMode, cc: &mut Consts) -> Self {
277        let p_x = Self::work_p(p);
278        Self::one(p_x)
279            .add(self, p_x, RoundingMode::None)
280            .ln(p_x, RoundingMode::None, cc)
281            .finish(p, rm)
282    }
283
284    /// `exp2(self) = exp(self · ln 2)`.
285    pub fn exp2(&self, p: usize, rm: RoundingMode, cc: &mut Consts) -> Self {
286        let p_x = Self::work_p(p);
287        let ln2 = Self::from_real(cc.ln_2(p_x, RoundingMode::None), p_x);
288        self.mul(&ln2, p_x, RoundingMode::None)
289            .exp(p_x, RoundingMode::None, cc)
290            .finish(p, rm)
291    }
292
293    /// `exp10(self) = exp(self · ln 10)`.
294    pub fn exp10(&self, p: usize, rm: RoundingMode, cc: &mut Consts) -> Self {
295        let p_x = Self::work_p(p);
296        let ln10 = Self::from_real(cc.ln_10(p_x, RoundingMode::None), p_x);
297        self.mul(&ln10, p_x, RoundingMode::None)
298            .exp(p_x, RoundingMode::None, cc)
299            .finish(p, rm)
300    }
301
302    /// `exp(self) − 1`.
303    pub fn expm1(&self, p: usize, rm: RoundingMode, cc: &mut Consts) -> Self {
304        let p_x = Self::work_p(p);
305        self.exp(p_x, RoundingMode::None, cc)
306            .sub(&Self::one(p_x), p_x, RoundingMode::None)
307            .finish(p, rm)
308    }
309
310    /// Scale both parts by `2^n` (`ldexp` on re and im).
311    pub fn ldexp(&self, n: Exponent, p: usize, rm: RoundingMode) -> Self {
312        self.ldexp_parts(n, p, rm)
313    }
314
315    /// Same as [`ldexp`](Self::ldexp).
316    pub fn scalb(&self, n: Exponent, p: usize, rm: RoundingMode) -> Self {
317        self.ldexp(n, p, rm)
318    }
319
320    /// `logb(|z|)` as a real (`x + 0i`).
321    pub fn logb(&self, p: usize, rm: RoundingMode) -> ExactNum {
322        self.abs(p, rm).logb(p, rm)
323    }
324
325    /// Principal `n`-th root via `exp(ln(z) / n)`. Inherits the `ln` branch cut.
326    pub fn nth_root(&self, n: usize, p: usize, rm: RoundingMode, cc: &mut Consts) -> Self {
327        if n == 0 {
328            return Self::new(
329                ExactNum::nan(Some(crate::Error::InvalidArgument)),
330                ExactNum::nan(Some(crate::Error::InvalidArgument)),
331            );
332        }
333        if n == 1 {
334            let mut z = self.clone();
335            if let Err(e) = z.set_precision(p, rm) {
336                return Self::new(ExactNum::nan(Some(e)), ExactNum::nan(Some(e)));
337            }
338            return z;
339        }
340        if n == 2 {
341            return self.sqrt(p, rm, cc);
342        }
343        let p_x = Self::work_p(p);
344        let ln = self.ln(p_x, RoundingMode::None, cc);
345        let ninv = Self::from_real(
346            ExactNum::from_u32(1, p_x).div(
347                &ExactNum::from_u32(n as u32, p_x),
348                p_x,
349                RoundingMode::None,
350            ),
351            p_x,
352        );
353        ln.mul(&ninv, p_x, RoundingMode::None)
354            .exp(p_x, RoundingMode::None, cc)
355            .finish(p, rm)
356    }
357
358    /// Principal cube root. Same branch as [`nth_root`](Self::nth_root) with `n = 3`.
359    pub fn cbrt(&self, p: usize, rm: RoundingMode, cc: &mut Consts) -> Self {
360        self.nth_root(3, p, rm, cc)
361    }
362
363    /// Principal `sqrt(self² + other²)` (analytic continuation of real `hypot`).
364    pub fn hypot(&self, other: &Self, p: usize, rm: RoundingMode, cc: &mut Consts) -> Self {
365        let p_x = Self::work_p(p);
366        self.mul(self, p_x, RoundingMode::None)
367            .add(
368                &other.mul(other, p_x, RoundingMode::None),
369                p_x,
370                RoundingMode::None,
371            )
372            .sqrt(p_x, RoundingMode::None, cc)
373            .finish(p, rm)
374    }
375
376    /// `self * b + c` at extra working precision, then one round (not a fused complex hardware op).
377    pub fn fma(&self, b: &Self, c: &Self, p: usize, rm: RoundingMode) -> Self {
378        let p_x = Self::work_p(p);
379        self.mul(b, p_x, RoundingMode::None)
380            .add(c, p_x, RoundingMode::None)
381            .finish(p, rm)
382    }
383
384    /// Alias of [`fma`](Self::fma).
385    pub fn mul_add(&self, b: &Self, c: &Self, p: usize, rm: RoundingMode) -> Self {
386        self.fma(b, c, p, rm)
387    }
388
389    /// `self^rhs` as `exp(rhs * ln(self))` (principal branch).
390    ///
391    /// Inherits the `ln` cut on `self`: non-positive real base uses Arg = ±π.
392    pub fn pow(&self, rhs: &Self, p: usize, rm: RoundingMode, cc: &mut Consts) -> Self {
393        let p_x = Self::work_p(p);
394        let ln = self.ln(p_x, RoundingMode::None, cc);
395        rhs.mul(&ln, p_x, RoundingMode::None)
396            .exp(p_x, RoundingMode::None, cc)
397            .finish(p, rm)
398    }
399
400    /// Principal `asin`: `-i ln(i z + √(1 − z²))`.
401    pub fn asin(&self, p: usize, rm: RoundingMode, cc: &mut Consts) -> Self {
402        let p_x = Self::work_p(p);
403        let z2 = self.mul(self, p_x, RoundingMode::None);
404        let one = Self::one(p_x);
405        let rad = one
406            .sub(&z2, p_x, RoundingMode::None)
407            .sqrt(p_x, RoundingMode::None, cc);
408        let iz = Self::i(p_x).mul(self, p_x, RoundingMode::None);
409        let ln = iz
410            .add(&rad, p_x, RoundingMode::None)
411            .ln(p_x, RoundingMode::None, cc);
412        Self::i(p_x)
413            .mul(&ln, p_x, RoundingMode::None)
414            .neg()
415            .finish(p, rm)
416    }
417
418    /// Principal `acos`: `π/2 − asin(z)`.
419    pub fn acos(&self, p: usize, rm: RoundingMode, cc: &mut Consts) -> Self {
420        let p_x = Self::work_p(p);
421        let half_pi = cc
422            .pi(p_x, RoundingMode::None)
423            .ldexp(-1, p_x, RoundingMode::None);
424        let asinv = self.asin(p_x, RoundingMode::None, cc);
425        Self::new(half_pi, ExactNum::new(p_x))
426            .sub(&asinv, p_x, RoundingMode::None)
427            .finish(p, rm)
428    }
429
430    /// Principal `atan`: `(i/2) ln((i+z)/(i−z))`.
431    pub fn atan(&self, p: usize, rm: RoundingMode, cc: &mut Consts) -> Self {
432        let p_x = Self::work_p(p);
433        let i = Self::i(p_x);
434        let num = i.add(self, p_x, RoundingMode::None);
435        let den = i.sub(self, p_x, RoundingMode::None);
436        let ln = num
437            .div(&den, p_x, RoundingMode::None)
438            .ln(p_x, RoundingMode::None, cc);
439        let half_i = i.ldexp_parts(-1, p_x, RoundingMode::None);
440        half_i.mul(&ln, p_x, RoundingMode::None).finish(p, rm)
441    }
442
443    /// Principal `asinh`: `ln(z + √(z² + 1))`.
444    pub fn asinh(&self, p: usize, rm: RoundingMode, cc: &mut Consts) -> Self {
445        let p_x = Self::work_p(p);
446        let z2 = self.mul(self, p_x, RoundingMode::None);
447        let rad =
448            z2.add(&Self::one(p_x), p_x, RoundingMode::None)
449                .sqrt(p_x, RoundingMode::None, cc);
450        self.add(&rad, p_x, RoundingMode::None)
451            .ln(p_x, RoundingMode::None, cc)
452            .finish(p, rm)
453    }
454
455    /// Principal `acosh`: `ln(z + √(z−1)√(z+1))`.
456    pub fn acosh(&self, p: usize, rm: RoundingMode, cc: &mut Consts) -> Self {
457        let p_x = Self::work_p(p);
458        let one = Self::one(p_x);
459        let zm = self
460            .sub(&one, p_x, RoundingMode::None)
461            .sqrt(p_x, RoundingMode::None, cc);
462        let zp = self
463            .add(&one, p_x, RoundingMode::None)
464            .sqrt(p_x, RoundingMode::None, cc);
465        self.add(
466            &zm.mul(&zp, p_x, RoundingMode::None),
467            p_x,
468            RoundingMode::None,
469        )
470        .ln(p_x, RoundingMode::None, cc)
471        .finish(p, rm)
472    }
473
474    /// Principal `atanh`: `(1/2) ln((1+z)/(1−z))`.
475    pub fn atanh(&self, p: usize, rm: RoundingMode, cc: &mut Consts) -> Self {
476        let p_x = Self::work_p(p);
477        let one = Self::one(p_x);
478        let num = one.add(self, p_x, RoundingMode::None);
479        let den = one.sub(self, p_x, RoundingMode::None);
480        num.div(&den, p_x, RoundingMode::None)
481            .ln(p_x, RoundingMode::None, cc)
482            .ldexp_parts(-1, p_x, RoundingMode::None)
483            .finish(p, rm)
484    }
485
486    fn ldexp_parts(&self, n: crate::Exponent, p: usize, rm: RoundingMode) -> Self {
487        Self::new(self.re.ldexp(n, p, rm), self.im.ldexp(n, p, rm))
488    }
489
490    fn neg(&self) -> Self {
491        Self::new(self.re.neg(), self.im.neg())
492    }
493}
494
495macro_rules! impl_cplx_binop {
496    ($trait:ident, $method:ident) => {
497        impl core::ops::$trait<&ExactComplex> for &ExactComplex {
498            type Output = ExactComplex;
499            fn $method(self, rhs: &ExactComplex) -> ExactComplex {
500                ExactComplex::$method(self, rhs, DEFAULT_P, RoundingMode::ToEven)
501            }
502        }
503        impl core::ops::$trait<ExactComplex> for &ExactComplex {
504            type Output = ExactComplex;
505            fn $method(self, rhs: ExactComplex) -> ExactComplex {
506                ExactComplex::$method(self, &rhs, DEFAULT_P, RoundingMode::ToEven)
507            }
508        }
509        impl core::ops::$trait<&ExactComplex> for ExactComplex {
510            type Output = ExactComplex;
511            fn $method(self, rhs: &ExactComplex) -> ExactComplex {
512                ExactComplex::$method(&self, rhs, DEFAULT_P, RoundingMode::ToEven)
513            }
514        }
515        impl core::ops::$trait<ExactComplex> for ExactComplex {
516            type Output = ExactComplex;
517            fn $method(self, rhs: ExactComplex) -> ExactComplex {
518                ExactComplex::$method(&self, &rhs, DEFAULT_P, RoundingMode::ToEven)
519            }
520        }
521    };
522}
523
524impl_cplx_binop!(Add, add);
525impl_cplx_binop!(Sub, sub);
526impl_cplx_binop!(Mul, mul);
527impl_cplx_binop!(Div, div);
528
529impl crate::FromExt<ExactComplex> for ExactComplex {
530    fn from_ext(mut v: ExactComplex, p: usize, rm: RoundingMode, _cc: &mut Consts) -> Self {
531        if let Err(e) = v.set_precision(p, rm) {
532            return Self::new(ExactNum::nan(Some(e)), ExactNum::nan(Some(e)));
533        }
534        v.set_inexact(false);
535        v
536    }
537}
538
539impl crate::FromExt<&ExactComplex> for ExactComplex {
540    fn from_ext(v: &ExactComplex, p: usize, rm: RoundingMode, cc: &mut Consts) -> Self {
541        <Self as crate::FromExt<ExactComplex>>::from_ext(v.clone(), p, rm, cc)
542    }
543}
544
545impl<T> crate::FromExt<T> for ExactComplex
546where
547    ExactNum: crate::FromExt<T>,
548{
549    fn from_ext(v: T, p: usize, rm: RoundingMode, cc: &mut Consts) -> Self {
550        Self::from_real(<ExactNum as crate::FromExt<T>>::from_ext(v, p, rm, cc), p)
551    }
552}
553
554#[cfg(test)]
555mod tests {
556    use super::*;
557    use crate::NAN;
558
559    #[test]
560    fn test_complex_arith() {
561        let p = 256;
562        let rm = RoundingMode::ToEven;
563        let mut cc = Consts::new().unwrap();
564
565        let a = ExactComplex::new(ExactNum::from_u8(3, p), ExactNum::from_u8(4, p));
566        let mag = a.abs(p, rm);
567        let five = ExactNum::from_u8(5, p);
568        assert_eq!(mag.cmp(&five), Some(0));
569
570        let i = ExactComplex::i(p);
571        let i2 = i.mul(&i, p, rm);
572        assert_eq!(i2.re().cmp(&ExactNum::from_i8(-1, p)), Some(0));
573        assert!(i2.im().is_zero());
574
575        let z = ExactComplex::one(p);
576        let e = z.exp(p, rm, &mut cc);
577        let back = e.ln(p, rm, &mut cc);
578        let d = back.re().sub(z.re(), p, RoundingMode::None).abs();
579        assert!(d.is_zero() || d.exponent().unwrap_or(0) < -((p as i32) / 4));
580
581        let z0 = ExactComplex::zero(p);
582        let t = z0.tan(p, rm, &mut cc);
583        assert!(t.re().is_zero() || t.re().exponent().unwrap_or(0) < -((p as i32) / 4));
584        assert!(t.im().is_zero() || t.im().exponent().unwrap_or(0) < -((p as i32) / 4));
585        let sh = z0.sinh(p, rm, &mut cc);
586        assert!(sh.re().is_zero() || sh.re().exponent().unwrap_or(0) < -((p as i32) / 4));
587
588        let two = ExactComplex::from_real(ExactNum::from_u8(2, p), p);
589        let four = ExactComplex::from_real(ExactNum::from_u8(4, p), p);
590        let lg = four.log2(p, rm, &mut cc);
591        let d = lg.re().sub(two.re(), p, RoundingMode::None).abs();
592        assert!(d.is_zero() || d.exponent().unwrap_or(0) < -((p as i32) / 8));
593        assert!(lg.im().is_zero() || lg.im().exponent().unwrap_or(0) < -((p as i32) / 4));
594        let e2 = two.exp2(p, rm, &mut cc);
595        let d2 = e2.re().sub(four.re(), p, RoundingMode::None).abs();
596        assert!(d2.is_zero() || d2.exponent().unwrap_or(0) < -((p as i32) / 8));
597    }
598
599    #[test]
600    fn test_complex_branch_cuts() {
601        let p = 256;
602        let rm = RoundingMode::ToEven;
603        let mut cc = Consts::new().unwrap();
604        let m1 = ExactComplex::from_real(ExactNum::from_i8(-1, p), p);
605
606        let s = m1.sqrt(p, rm, &mut cc);
607        assert!(s.re().is_zero() || s.re().exponent().unwrap_or(0) < -((p as i32) / 4));
608        assert_eq!(s.im().cmp(&ExactNum::from_u8(1, p)), Some(0));
609
610        let l = m1.ln(p, rm, &mut cc);
611        assert!(l.re().is_zero() || l.re().exponent().unwrap_or(0) < -((p as i32) / 4));
612        let pi = cc.pi(p, rm);
613        let d = l.im().abs().sub(&pi, p, RoundingMode::None).abs();
614        assert!(d.is_zero() || d.exponent().unwrap_or(0) < -((p as i32) / 8));
615        assert!(l.im().is_positive());
616
617        let below = ExactComplex::new(ExactNum::from_i8(-1, p), ExactNum::new(p).neg());
618        let lb = below.ln(p, rm, &mut cc);
619        let db = lb.im().abs().sub(&pi, p, RoundingMode::None).abs();
620        assert!(db.is_zero() || db.exponent().unwrap_or(0) < -((p as i32) / 8));
621        assert!(lb.im().is_negative());
622    }
623
624    #[test]
625    fn test_complex_nan() {
626        let n = ExactComplex::new(NAN.clone(), ExactNum::new(64));
627        assert!(n.is_nan());
628    }
629}