oxinum_complex/native/complex.rs
1//! Native `BigComplex` — a binary-base arbitrary-precision complex number
2//! built as the ordered pair `(re, im)` of [`BigFloat`]s.
3//!
4//! The value of a `BigComplex` is `re + im·i`, where both components are
5//! native binary [`BigFloat`]s. Each component carries its own precision
6//! and (via [`BigFloat`]) full IEEE-754-style non-finite support; arithmetic
7//! and transcendental routines added by sibling modules reconcile precision
8//! and rounding as required.
9//!
10//! # Why `Hash`, `Eq`, and `Ord` are not implemented
11//!
12//! The complex field has no order compatible with its ring structure, so no
13//! `Ord` / `PartialOrd` is provided. `BigFloat` itself implements neither
14//! `Eq` (NaN breaks reflexivity) nor `Hash`, so `BigComplex` inherits the
15//! same constraints; component-wise `PartialEq` is available where useful.
16
17use oxinum_core::OxiNumResult;
18use oxinum_float::native::{BigFloat, RoundingMode};
19
20/// Native arbitrary-precision binary complex number `re + im·i`.
21///
22/// Both components are [`BigFloat`] values. See the module-level
23/// documentation for the rationale behind the absence of `Hash`, `Eq`, and
24/// `Ord`.
25///
26/// # Examples
27///
28/// ```
29/// use oxinum_complex::native::{BigComplex, RoundingMode};
30///
31/// let z = BigComplex::from_f64(3.0, 4.0, 53).expect("finite parts");
32/// // |3 + 4i|^2 = 25.
33/// assert_eq!(z.norm_sqr().to_f64(), 25.0);
34/// ```
35#[derive(Clone)]
36pub struct BigComplex {
37 pub(crate) re: BigFloat,
38 pub(crate) im: BigFloat,
39}
40
41impl BigComplex {
42 /// Construct a complex number from its real and imaginary parts.
43 pub fn from_parts(re: BigFloat, im: BigFloat) -> Self {
44 Self { re, im }
45 }
46
47 /// Construct a complex number from its real and imaginary parts.
48 ///
49 /// Alias of [`BigComplex::from_parts`].
50 pub fn new(re: BigFloat, im: BigFloat) -> Self {
51 Self::from_parts(re, im)
52 }
53
54 /// Construct a purely real complex number (`im = 0` at `re`'s precision).
55 pub fn from_real(re: BigFloat) -> Self {
56 let prec = re.precision();
57 Self {
58 re,
59 im: BigFloat::zero(prec),
60 }
61 }
62
63 /// Construct a purely imaginary complex number (`re = 0` at `im`'s precision).
64 pub fn from_imag(im: BigFloat) -> Self {
65 let prec = im.precision();
66 Self {
67 re: BigFloat::zero(prec),
68 im,
69 }
70 }
71
72 /// The additive identity `0 + 0·i` at `prec` bits of precision.
73 pub fn zero(prec: u32) -> Self {
74 Self {
75 re: BigFloat::zero(prec),
76 im: BigFloat::zero(prec),
77 }
78 }
79
80 /// The multiplicative identity `1 + 0·i` at `prec` bits of precision.
81 pub fn one(prec: u32, mode: RoundingMode) -> Self {
82 Self {
83 re: BigFloat::from_i64(1, prec, mode),
84 im: BigFloat::zero(prec),
85 }
86 }
87
88 /// The imaginary unit `0 + 1·i` at `prec` bits of precision.
89 pub fn i(prec: u32, mode: RoundingMode) -> Self {
90 Self {
91 re: BigFloat::zero(prec),
92 im: BigFloat::from_i64(1, prec, mode),
93 }
94 }
95
96 /// Construct a complex number from a pair of `f64` values at `prec` bits.
97 ///
98 /// Delegates to [`BigFloat::from_f64`] for each component.
99 ///
100 /// # Errors
101 ///
102 /// Propagates the [`OxiNumError`](oxinum_core::OxiNumError) returned by
103 /// [`BigFloat::from_f64`] when a component is `NaN` or infinite — the
104 /// native `BigFloat` rejects those inputs.
105 ///
106 /// # Examples
107 ///
108 /// ```
109 /// use oxinum_complex::native::BigComplex;
110 /// let z = BigComplex::from_f64(1.5, -2.25, 53).expect("finite parts");
111 /// assert_eq!(z.re().to_f64(), 1.5);
112 /// assert_eq!(z.im().to_f64(), -2.25);
113 /// ```
114 pub fn from_f64(re: f64, im: f64, prec: u32) -> OxiNumResult<Self> {
115 Ok(Self {
116 re: BigFloat::from_f64(re, prec)?,
117 im: BigFloat::from_f64(im, prec)?,
118 })
119 }
120
121 /// A shared reference to the real part.
122 pub fn re(&self) -> &BigFloat {
123 &self.re
124 }
125
126 /// A shared reference to the imaginary part.
127 pub fn im(&self) -> &BigFloat {
128 &self.im
129 }
130
131 /// A clone of the real part.
132 pub fn real(&self) -> BigFloat {
133 self.re.clone()
134 }
135
136 /// A clone of the imaginary part.
137 pub fn imag(&self) -> BigFloat {
138 self.im.clone()
139 }
140
141 /// Decompose into the owned `(re, im)` pair.
142 pub fn into_parts(self) -> (BigFloat, BigFloat) {
143 (self.re, self.im)
144 }
145
146 /// The complex conjugate `re − im·i`.
147 ///
148 /// # Examples
149 ///
150 /// ```
151 /// use oxinum_complex::native::BigComplex;
152 /// let z = BigComplex::from_f64(2.0, 3.0, 53).expect("finite parts");
153 /// let c = z.conj();
154 /// assert_eq!(c.re().to_f64(), 2.0);
155 /// assert_eq!(c.im().to_f64(), -3.0);
156 /// ```
157 pub fn conj(&self) -> Self {
158 Self {
159 re: self.re.clone(),
160 im: -&self.im,
161 }
162 }
163
164 /// The squared magnitude `re² + im²` (always real, non-negative).
165 ///
166 /// # Examples
167 ///
168 /// ```
169 /// use oxinum_complex::native::BigComplex;
170 /// let z = BigComplex::from_f64(3.0, 4.0, 53).expect("finite parts");
171 /// assert_eq!(z.norm_sqr().to_f64(), 25.0);
172 /// ```
173 pub fn norm_sqr(&self) -> BigFloat {
174 &(&self.re * &self.re) + &(&self.im * &self.im)
175 }
176
177 /// Returns `true` if both components are the canonical zero.
178 pub fn is_zero(&self) -> bool {
179 self.re.is_zero() && self.im.is_zero()
180 }
181}
182
183// ---------------------------------------------------------------------------
184// Tests
185// ---------------------------------------------------------------------------
186
187#[cfg(test)]
188mod tests {
189 use super::*;
190
191 #[test]
192 fn zero_one_i_constructors() {
193 let z = BigComplex::zero(53);
194 assert!(z.is_zero());
195
196 let one = BigComplex::one(53, RoundingMode::HalfEven);
197 assert_eq!(one.re().to_f64(), 1.0);
198 assert_eq!(one.im().to_f64(), 0.0);
199
200 let imag = BigComplex::i(53, RoundingMode::HalfEven);
201 assert_eq!(imag.re().to_f64(), 0.0);
202 assert_eq!(imag.im().to_f64(), 1.0);
203 }
204
205 #[test]
206 fn from_parts_round_trip() {
207 let z = BigComplex::from_f64(2.5, -7.0, 53).expect("finite parts");
208 let (re, im) = z.clone().into_parts();
209 assert_eq!(re.to_f64(), 2.5);
210 assert_eq!(im.to_f64(), -7.0);
211 assert_eq!(z.real().to_f64(), 2.5);
212 assert_eq!(z.imag().to_f64(), -7.0);
213 }
214
215 #[test]
216 fn conj_and_norm_sqr() {
217 let z = BigComplex::from_f64(3.0, 4.0, 53).expect("finite parts");
218 let c = z.conj();
219 assert_eq!(c.re().to_f64(), 3.0);
220 assert_eq!(c.im().to_f64(), -4.0);
221 assert_eq!(z.norm_sqr().to_f64(), 25.0);
222 }
223}