1use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
42
43use oxinum_core::OxiNumResult;
44use oxinum_float::native::RoundingMode;
45
46use super::BigComplex;
47
48impl BigComplex {
49 pub(crate) fn mul_core(&self, rhs: &BigComplex) -> BigComplex {
56 let a = &self.re;
57 let b = &self.im;
58 let c = &rhs.re;
59 let d = &rhs.im;
60
61 let ac = a * c;
63 let bd = b * d;
64 let re = &ac - &bd;
65
66 let ad = a * d;
68 let bc = b * c;
69 let im = &ad + &bc;
70
71 BigComplex { re, im }
72 }
73
74 pub(crate) fn div_core(
82 &self,
83 rhs: &BigComplex,
84 prec: u32,
85 mode: RoundingMode,
86 ) -> OxiNumResult<BigComplex> {
87 if rhs.is_zero() {
91 return Err(oxinum_core::OxiNumError::DivByZero);
92 }
93
94 let guard = prec.saturating_add(10);
95
96 let a = &self.re;
97 let b = &self.im;
98 let c = &rhs.re;
99 let d = &rhs.im;
100
101 let denom = rhs.norm_sqr();
103
104 let ac = a * c;
106 let bd = b * d;
107 let num_re = &ac + &bd;
108
109 let bc = b * c;
111 let ad = a * d;
112 let num_im = &bc - &ad;
113
114 let _ = guard;
118 let re = num_re
119 .div_ref_with_mode(&denom, mode)?
120 .with_precision(prec, mode);
121 let im = num_im
122 .div_ref_with_mode(&denom, mode)?
123 .with_precision(prec, mode);
124
125 Ok(BigComplex { re, im })
126 }
127
128 pub fn checked_div(
153 &self,
154 rhs: &BigComplex,
155 prec: u32,
156 mode: RoundingMode,
157 ) -> OxiNumResult<BigComplex> {
158 self.div_core(rhs, prec, mode)
159 }
160}
161
162impl Add<&BigComplex> for &BigComplex {
167 type Output = BigComplex;
168 #[inline]
169 fn add(self, rhs: &BigComplex) -> BigComplex {
170 BigComplex {
171 re: &self.re + &rhs.re,
172 im: &self.im + &rhs.im,
173 }
174 }
175}
176
177impl Add<BigComplex> for BigComplex {
178 type Output = BigComplex;
179 #[inline]
180 fn add(self, rhs: BigComplex) -> BigComplex {
181 (&self).add(&rhs)
182 }
183}
184
185impl Add<&BigComplex> for BigComplex {
186 type Output = BigComplex;
187 #[inline]
188 fn add(self, rhs: &BigComplex) -> BigComplex {
189 (&self).add(rhs)
190 }
191}
192
193impl Add<BigComplex> for &BigComplex {
194 type Output = BigComplex;
195 #[inline]
196 fn add(self, rhs: BigComplex) -> BigComplex {
197 self.add(&rhs)
198 }
199}
200
201impl AddAssign<&BigComplex> for BigComplex {
202 #[inline]
203 fn add_assign(&mut self, rhs: &BigComplex) {
204 *self = (&*self).add(rhs);
205 }
206}
207
208impl AddAssign<BigComplex> for BigComplex {
209 #[inline]
210 fn add_assign(&mut self, rhs: BigComplex) {
211 *self = (&*self).add(&rhs);
212 }
213}
214
215impl Sub<&BigComplex> for &BigComplex {
220 type Output = BigComplex;
221 #[inline]
222 fn sub(self, rhs: &BigComplex) -> BigComplex {
223 BigComplex {
224 re: &self.re - &rhs.re,
225 im: &self.im - &rhs.im,
226 }
227 }
228}
229
230impl Sub<BigComplex> for BigComplex {
231 type Output = BigComplex;
232 #[inline]
233 fn sub(self, rhs: BigComplex) -> BigComplex {
234 (&self).sub(&rhs)
235 }
236}
237
238impl Sub<&BigComplex> for BigComplex {
239 type Output = BigComplex;
240 #[inline]
241 fn sub(self, rhs: &BigComplex) -> BigComplex {
242 (&self).sub(rhs)
243 }
244}
245
246impl Sub<BigComplex> for &BigComplex {
247 type Output = BigComplex;
248 #[inline]
249 fn sub(self, rhs: BigComplex) -> BigComplex {
250 self.sub(&rhs)
251 }
252}
253
254impl SubAssign<&BigComplex> for BigComplex {
255 #[inline]
256 fn sub_assign(&mut self, rhs: &BigComplex) {
257 *self = (&*self).sub(rhs);
258 }
259}
260
261impl SubAssign<BigComplex> for BigComplex {
262 #[inline]
263 fn sub_assign(&mut self, rhs: BigComplex) {
264 *self = (&*self).sub(&rhs);
265 }
266}
267
268impl Mul<&BigComplex> for &BigComplex {
273 type Output = BigComplex;
274 #[inline]
275 fn mul(self, rhs: &BigComplex) -> BigComplex {
276 self.mul_core(rhs)
277 }
278}
279
280impl Mul<BigComplex> for BigComplex {
281 type Output = BigComplex;
282 #[inline]
283 fn mul(self, rhs: BigComplex) -> BigComplex {
284 self.mul_core(&rhs)
285 }
286}
287
288impl Mul<&BigComplex> for BigComplex {
289 type Output = BigComplex;
290 #[inline]
291 fn mul(self, rhs: &BigComplex) -> BigComplex {
292 self.mul_core(rhs)
293 }
294}
295
296impl Mul<BigComplex> for &BigComplex {
297 type Output = BigComplex;
298 #[inline]
299 fn mul(self, rhs: BigComplex) -> BigComplex {
300 self.mul_core(&rhs)
301 }
302}
303
304impl MulAssign<&BigComplex> for BigComplex {
305 #[inline]
306 fn mul_assign(&mut self, rhs: &BigComplex) {
307 *self = self.mul_core(rhs);
308 }
309}
310
311impl MulAssign<BigComplex> for BigComplex {
312 #[inline]
313 fn mul_assign(&mut self, rhs: BigComplex) {
314 *self = self.mul_core(&rhs);
315 }
316}
317
318impl Neg for &BigComplex {
323 type Output = BigComplex;
324 #[inline]
325 fn neg(self) -> BigComplex {
326 BigComplex {
327 re: -&self.re,
328 im: -&self.im,
329 }
330 }
331}
332
333impl Neg for BigComplex {
334 type Output = BigComplex;
335 #[inline]
336 fn neg(self) -> BigComplex {
337 (&self).neg()
338 }
339}
340
341impl PartialEq for BigComplex {
346 #[inline]
347 fn eq(&self, other: &Self) -> bool {
348 self.re == other.re && self.im == other.im
349 }
350}
351
352#[cfg(test)]
357mod tests {
358 use super::*;
359
360 const PREC: u32 = 80;
361 const MODE: RoundingMode = RoundingMode::HalfEven;
362
363 fn c(re: f64, im: f64) -> BigComplex {
364 BigComplex::from_f64(re, im, PREC).expect("finite parts")
365 }
366
367 #[test]
368 fn add_sub_componentwise() {
369 let z = &c(1.0, 2.0) + &c(3.0, -4.0);
370 assert!((z.re().to_f64() - 4.0).abs() < 1e-12);
371 assert!((z.im().to_f64() + 2.0).abs() < 1e-12);
372
373 let w = &c(1.0, 2.0) - &c(3.0, -4.0);
374 assert!((w.re().to_f64() + 2.0).abs() < 1e-12);
375 assert!((w.im().to_f64() - 6.0).abs() < 1e-12);
376 }
377
378 #[test]
379 fn mul_basic() {
380 let z = &c(1.0, 2.0) * &c(3.0, 4.0);
382 assert!(
383 (z.re().to_f64() + 5.0).abs() < 1e-12,
384 "re = {}",
385 z.re().to_f64()
386 );
387 assert!(
388 (z.im().to_f64() - 10.0).abs() < 1e-12,
389 "im = {}",
390 z.im().to_f64()
391 );
392 }
393
394 #[test]
395 fn mul_i_squared() {
396 let one_plus_i = c(1.0, 1.0);
398 let z = &one_plus_i * &one_plus_i;
399 assert!(z.re().to_f64().abs() < 1e-12, "re = {}", z.re().to_f64());
400 assert!(
401 (z.im().to_f64() - 2.0).abs() < 1e-12,
402 "im = {}",
403 z.im().to_f64()
404 );
405 }
406
407 #[test]
408 fn checked_div_basic() {
409 let q = c(2.0, 0.0)
411 .checked_div(&c(1.0, 1.0), PREC, MODE)
412 .expect("non-zero divisor");
413 assert!(
414 (q.re().to_f64() - 1.0).abs() < 1e-12,
415 "re = {}",
416 q.re().to_f64()
417 );
418 assert!(
419 (q.im().to_f64() + 1.0).abs() < 1e-12,
420 "im = {}",
421 q.im().to_f64()
422 );
423 }
424
425 #[test]
426 fn checked_div_general() {
427 let q = c(1.0, 2.0)
429 .checked_div(&c(3.0, 4.0), PREC, MODE)
430 .expect("non-zero divisor");
431 assert!(
432 (q.re().to_f64() - 0.44).abs() < 1e-12,
433 "re = {}",
434 q.re().to_f64()
435 );
436 assert!(
437 (q.im().to_f64() - 0.08).abs() < 1e-12,
438 "im = {}",
439 q.im().to_f64()
440 );
441 }
442
443 #[test]
444 fn checked_div_by_zero_is_err() {
445 let q = c(1.0, 1.0).checked_div(&BigComplex::zero(PREC), PREC, MODE);
446 assert!(
447 matches!(q, Err(oxinum_core::OxiNumError::DivByZero)),
448 "expected DivByZero, got {q:?}"
449 );
450 }
451
452 #[test]
453 fn neg_and_assign_ops() {
454 let z = -&c(1.0, -2.0);
455 assert!((z.re().to_f64() + 1.0).abs() < 1e-12);
456 assert!((z.im().to_f64() - 2.0).abs() < 1e-12);
457
458 let mut a = c(1.0, 1.0);
459 a += &c(2.0, 3.0);
460 assert!((a.re().to_f64() - 3.0).abs() < 1e-12);
461 assert!((a.im().to_f64() - 4.0).abs() < 1e-12);
462
463 let mut b = c(5.0, 5.0);
464 b -= c(1.0, 2.0);
465 assert!((b.re().to_f64() - 4.0).abs() < 1e-12);
466 assert!((b.im().to_f64() - 3.0).abs() < 1e-12);
467
468 let mut m = c(1.0, 2.0);
469 m *= &c(3.0, 4.0);
470 assert!((m.re().to_f64() + 5.0).abs() < 1e-12);
471 assert!((m.im().to_f64() - 10.0).abs() < 1e-12);
472 }
473
474 #[test]
475 fn partial_eq_componentwise() {
476 assert_eq!(c(1.0, 2.0), c(1.0, 2.0));
477 assert_ne!(c(1.0, 2.0), c(1.0, 2.5));
478 }
479}