1use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
42
43use oxinum_core::OxiNumResult;
44use oxinum_float::native::{BigFloat, 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#[inline]
358fn add_scalar_bf(z: &BigComplex, d: &BigFloat) -> BigComplex {
359 BigComplex {
360 re: &z.re + d,
361 im: z.im.clone(),
362 }
363}
364
365#[inline]
367fn sub_scalar_bf(z: &BigComplex, d: &BigFloat) -> BigComplex {
368 BigComplex {
369 re: &z.re - d,
370 im: z.im.clone(),
371 }
372}
373
374#[inline]
376fn rsub_scalar_bf(d: &BigFloat, z: &BigComplex) -> BigComplex {
377 BigComplex {
378 re: d - &z.re,
379 im: -&z.im,
380 }
381}
382
383#[inline]
385fn mul_scalar_bf(z: &BigComplex, d: &BigFloat) -> BigComplex {
386 BigComplex {
387 re: &z.re * d,
388 im: &z.im * d,
389 }
390}
391
392#[inline]
394fn bf_from_i64_at(n: i64, prec: u32) -> BigFloat {
395 BigFloat::from_i64(n, prec, RoundingMode::HalfEven)
396}
397
398#[inline]
400fn add_scalar_i64(z: &BigComplex, n: i64) -> BigComplex {
401 add_scalar_bf(z, &bf_from_i64_at(n, z.re.precision()))
402}
403#[inline]
404fn sub_scalar_i64(z: &BigComplex, n: i64) -> BigComplex {
405 sub_scalar_bf(z, &bf_from_i64_at(n, z.re.precision()))
406}
407#[inline]
408fn rsub_scalar_i64(n: i64, z: &BigComplex) -> BigComplex {
409 rsub_scalar_bf(&bf_from_i64_at(n, z.re.precision()), z)
410}
411#[inline]
412fn mul_scalar_i64(z: &BigComplex, n: i64) -> BigComplex {
413 mul_scalar_bf(z, &bf_from_i64_at(n, z.re.precision()))
414}
415
416impl Add<BigFloat> for BigComplex {
421 type Output = BigComplex;
422 #[inline]
423 fn add(self, rhs: BigFloat) -> BigComplex {
424 add_scalar_bf(&self, &rhs)
425 }
426}
427
428impl Add<&BigFloat> for BigComplex {
429 type Output = BigComplex;
430 #[inline]
431 fn add(self, rhs: &BigFloat) -> BigComplex {
432 add_scalar_bf(&self, rhs)
433 }
434}
435
436impl Add<BigFloat> for &BigComplex {
437 type Output = BigComplex;
438 #[inline]
439 fn add(self, rhs: BigFloat) -> BigComplex {
440 add_scalar_bf(self, &rhs)
441 }
442}
443
444impl Add<&BigFloat> for &BigComplex {
445 type Output = BigComplex;
446 #[inline]
447 fn add(self, rhs: &BigFloat) -> BigComplex {
448 add_scalar_bf(self, rhs)
449 }
450}
451
452impl AddAssign<BigFloat> for BigComplex {
453 #[inline]
454 fn add_assign(&mut self, rhs: BigFloat) {
455 *self = add_scalar_bf(self, &rhs);
456 }
457}
458
459impl AddAssign<&BigFloat> for BigComplex {
460 #[inline]
461 fn add_assign(&mut self, rhs: &BigFloat) {
462 *self = add_scalar_bf(self, rhs);
463 }
464}
465
466impl Sub<BigFloat> for BigComplex {
471 type Output = BigComplex;
472 #[inline]
473 fn sub(self, rhs: BigFloat) -> BigComplex {
474 sub_scalar_bf(&self, &rhs)
475 }
476}
477
478impl Sub<&BigFloat> for BigComplex {
479 type Output = BigComplex;
480 #[inline]
481 fn sub(self, rhs: &BigFloat) -> BigComplex {
482 sub_scalar_bf(&self, rhs)
483 }
484}
485
486impl Sub<BigFloat> for &BigComplex {
487 type Output = BigComplex;
488 #[inline]
489 fn sub(self, rhs: BigFloat) -> BigComplex {
490 sub_scalar_bf(self, &rhs)
491 }
492}
493
494impl Sub<&BigFloat> for &BigComplex {
495 type Output = BigComplex;
496 #[inline]
497 fn sub(self, rhs: &BigFloat) -> BigComplex {
498 sub_scalar_bf(self, rhs)
499 }
500}
501
502impl SubAssign<BigFloat> for BigComplex {
503 #[inline]
504 fn sub_assign(&mut self, rhs: BigFloat) {
505 *self = sub_scalar_bf(self, &rhs);
506 }
507}
508
509impl SubAssign<&BigFloat> for BigComplex {
510 #[inline]
511 fn sub_assign(&mut self, rhs: &BigFloat) {
512 *self = sub_scalar_bf(self, rhs);
513 }
514}
515
516impl Sub<BigComplex> for BigFloat {
521 type Output = BigComplex;
522 #[inline]
523 fn sub(self, rhs: BigComplex) -> BigComplex {
524 rsub_scalar_bf(&self, &rhs)
525 }
526}
527
528impl Sub<&BigComplex> for BigFloat {
529 type Output = BigComplex;
530 #[inline]
531 fn sub(self, rhs: &BigComplex) -> BigComplex {
532 rsub_scalar_bf(&self, rhs)
533 }
534}
535
536impl Sub<BigComplex> for &BigFloat {
537 type Output = BigComplex;
538 #[inline]
539 fn sub(self, rhs: BigComplex) -> BigComplex {
540 rsub_scalar_bf(self, &rhs)
541 }
542}
543
544impl Sub<&BigComplex> for &BigFloat {
545 type Output = BigComplex;
546 #[inline]
547 fn sub(self, rhs: &BigComplex) -> BigComplex {
548 rsub_scalar_bf(self, rhs)
549 }
550}
551
552impl Mul<BigFloat> for BigComplex {
557 type Output = BigComplex;
558 #[inline]
559 fn mul(self, rhs: BigFloat) -> BigComplex {
560 mul_scalar_bf(&self, &rhs)
561 }
562}
563
564impl Mul<&BigFloat> for BigComplex {
565 type Output = BigComplex;
566 #[inline]
567 fn mul(self, rhs: &BigFloat) -> BigComplex {
568 mul_scalar_bf(&self, rhs)
569 }
570}
571
572impl Mul<BigFloat> for &BigComplex {
573 type Output = BigComplex;
574 #[inline]
575 fn mul(self, rhs: BigFloat) -> BigComplex {
576 mul_scalar_bf(self, &rhs)
577 }
578}
579
580impl Mul<&BigFloat> for &BigComplex {
581 type Output = BigComplex;
582 #[inline]
583 fn mul(self, rhs: &BigFloat) -> BigComplex {
584 mul_scalar_bf(self, rhs)
585 }
586}
587
588impl MulAssign<BigFloat> for BigComplex {
589 #[inline]
590 fn mul_assign(&mut self, rhs: BigFloat) {
591 *self = mul_scalar_bf(self, &rhs);
592 }
593}
594
595impl MulAssign<&BigFloat> for BigComplex {
596 #[inline]
597 fn mul_assign(&mut self, rhs: &BigFloat) {
598 *self = mul_scalar_bf(self, rhs);
599 }
600}
601
602impl Add<i64> for BigComplex {
607 type Output = BigComplex;
608 #[inline]
609 fn add(self, rhs: i64) -> BigComplex {
610 add_scalar_i64(&self, rhs)
611 }
612}
613
614impl Add<i64> for &BigComplex {
615 type Output = BigComplex;
616 #[inline]
617 fn add(self, rhs: i64) -> BigComplex {
618 add_scalar_i64(self, rhs)
619 }
620}
621
622impl AddAssign<i64> for BigComplex {
623 #[inline]
624 fn add_assign(&mut self, rhs: i64) {
625 *self = add_scalar_i64(self, rhs);
626 }
627}
628
629impl Sub<i64> for BigComplex {
634 type Output = BigComplex;
635 #[inline]
636 fn sub(self, rhs: i64) -> BigComplex {
637 sub_scalar_i64(&self, rhs)
638 }
639}
640
641impl Sub<i64> for &BigComplex {
642 type Output = BigComplex;
643 #[inline]
644 fn sub(self, rhs: i64) -> BigComplex {
645 sub_scalar_i64(self, rhs)
646 }
647}
648
649impl SubAssign<i64> for BigComplex {
650 #[inline]
651 fn sub_assign(&mut self, rhs: i64) {
652 *self = sub_scalar_i64(self, rhs);
653 }
654}
655
656impl Sub<BigComplex> for i64 {
661 type Output = BigComplex;
662 #[inline]
663 fn sub(self, rhs: BigComplex) -> BigComplex {
664 rsub_scalar_i64(self, &rhs)
665 }
666}
667
668impl Sub<&BigComplex> for i64 {
669 type Output = BigComplex;
670 #[inline]
671 fn sub(self, rhs: &BigComplex) -> BigComplex {
672 rsub_scalar_i64(self, rhs)
673 }
674}
675
676impl Mul<i64> for BigComplex {
681 type Output = BigComplex;
682 #[inline]
683 fn mul(self, rhs: i64) -> BigComplex {
684 mul_scalar_i64(&self, rhs)
685 }
686}
687
688impl Mul<i64> for &BigComplex {
689 type Output = BigComplex;
690 #[inline]
691 fn mul(self, rhs: i64) -> BigComplex {
692 mul_scalar_i64(self, rhs)
693 }
694}
695
696impl MulAssign<i64> for BigComplex {
697 #[inline]
698 fn mul_assign(&mut self, rhs: i64) {
699 *self = mul_scalar_i64(self, rhs);
700 }
701}
702
703#[cfg(test)]
708mod tests {
709 use super::*;
710
711 const PREC: u32 = 80;
712 const MODE: RoundingMode = RoundingMode::HalfEven;
713
714 fn c(re: f64, im: f64) -> BigComplex {
715 BigComplex::from_f64(re, im, PREC).expect("finite parts")
716 }
717
718 #[test]
719 fn add_sub_componentwise() {
720 let z = &c(1.0, 2.0) + &c(3.0, -4.0);
721 assert!((z.re().to_f64() - 4.0).abs() < 1e-12);
722 assert!((z.im().to_f64() + 2.0).abs() < 1e-12);
723
724 let w = &c(1.0, 2.0) - &c(3.0, -4.0);
725 assert!((w.re().to_f64() + 2.0).abs() < 1e-12);
726 assert!((w.im().to_f64() - 6.0).abs() < 1e-12);
727 }
728
729 #[test]
730 fn mul_basic() {
731 let z = &c(1.0, 2.0) * &c(3.0, 4.0);
733 assert!(
734 (z.re().to_f64() + 5.0).abs() < 1e-12,
735 "re = {}",
736 z.re().to_f64()
737 );
738 assert!(
739 (z.im().to_f64() - 10.0).abs() < 1e-12,
740 "im = {}",
741 z.im().to_f64()
742 );
743 }
744
745 #[test]
746 fn mul_i_squared() {
747 let one_plus_i = c(1.0, 1.0);
749 let z = &one_plus_i * &one_plus_i;
750 assert!(z.re().to_f64().abs() < 1e-12, "re = {}", z.re().to_f64());
751 assert!(
752 (z.im().to_f64() - 2.0).abs() < 1e-12,
753 "im = {}",
754 z.im().to_f64()
755 );
756 }
757
758 #[test]
759 fn checked_div_basic() {
760 let q = c(2.0, 0.0)
762 .checked_div(&c(1.0, 1.0), PREC, MODE)
763 .expect("non-zero divisor");
764 assert!(
765 (q.re().to_f64() - 1.0).abs() < 1e-12,
766 "re = {}",
767 q.re().to_f64()
768 );
769 assert!(
770 (q.im().to_f64() + 1.0).abs() < 1e-12,
771 "im = {}",
772 q.im().to_f64()
773 );
774 }
775
776 #[test]
777 fn checked_div_general() {
778 let q = c(1.0, 2.0)
780 .checked_div(&c(3.0, 4.0), PREC, MODE)
781 .expect("non-zero divisor");
782 assert!(
783 (q.re().to_f64() - 0.44).abs() < 1e-12,
784 "re = {}",
785 q.re().to_f64()
786 );
787 assert!(
788 (q.im().to_f64() - 0.08).abs() < 1e-12,
789 "im = {}",
790 q.im().to_f64()
791 );
792 }
793
794 #[test]
795 fn checked_div_by_zero_is_err() {
796 let q = c(1.0, 1.0).checked_div(&BigComplex::zero(PREC), PREC, MODE);
797 assert!(
798 matches!(q, Err(oxinum_core::OxiNumError::DivByZero)),
799 "expected DivByZero, got {q:?}"
800 );
801 }
802
803 #[test]
804 fn neg_and_assign_ops() {
805 let z = -&c(1.0, -2.0);
806 assert!((z.re().to_f64() + 1.0).abs() < 1e-12);
807 assert!((z.im().to_f64() - 2.0).abs() < 1e-12);
808
809 let mut a = c(1.0, 1.0);
810 a += &c(2.0, 3.0);
811 assert!((a.re().to_f64() - 3.0).abs() < 1e-12);
812 assert!((a.im().to_f64() - 4.0).abs() < 1e-12);
813
814 let mut b = c(5.0, 5.0);
815 b -= c(1.0, 2.0);
816 assert!((b.re().to_f64() - 4.0).abs() < 1e-12);
817 assert!((b.im().to_f64() - 3.0).abs() < 1e-12);
818
819 let mut m = c(1.0, 2.0);
820 m *= &c(3.0, 4.0);
821 assert!((m.re().to_f64() + 5.0).abs() < 1e-12);
822 assert!((m.im().to_f64() - 10.0).abs() < 1e-12);
823 }
824
825 #[test]
826 fn partial_eq_componentwise() {
827 assert_eq!(c(1.0, 2.0), c(1.0, 2.0));
828 assert_ne!(c(1.0, 2.0), c(1.0, 2.5));
829 }
830
831 #[test]
836 fn add_bigfloat_scalar() {
837 let z = BigComplex::from_f64(1.0, 2.0, PREC).expect("ok");
838 let d = BigFloat::from_i64(3, PREC, MODE);
839 let r = &z + &d;
840 assert!((r.re().to_f64() - 4.0).abs() < 1e-12);
841 assert!((r.im().to_f64() - 2.0).abs() < 1e-12);
842 }
843
844 #[test]
845 fn sub_bigfloat_scalar() {
846 let z = BigComplex::from_f64(5.0, 3.0, PREC).expect("ok");
847 let d = BigFloat::from_i64(2, PREC, MODE);
848 let r = &z - &d;
849 assert!((r.re().to_f64() - 3.0).abs() < 1e-12);
850 assert!((r.im().to_f64() - 3.0).abs() < 1e-12);
851 }
852
853 #[test]
854 fn mul_bigfloat_scalar() {
855 let z = BigComplex::from_f64(0.0, 1.0, PREC).expect("i");
856 let d = BigFloat::from_i64(3, PREC, MODE);
857 let r = z * d;
858 assert!(r.re().to_f64().abs() < 1e-12);
859 assert!((r.im().to_f64() - 3.0).abs() < 1e-12);
860 }
861
862 #[test]
863 fn sub_reversed_bigfloat_minus_z() {
864 let z = BigComplex::from_f64(3.0, 2.0, PREC).expect("ok");
866 let d = BigFloat::from_i64(5, PREC, MODE);
867 let r = d - &z;
868 assert!((r.re().to_f64() - 2.0).abs() < 1e-12);
869 assert!((r.im().to_f64() + 2.0).abs() < 1e-12);
870 }
871
872 #[test]
873 fn bigfloat_scalar_assign_ops() {
874 let mut z = BigComplex::from_f64(1.0, 2.0, PREC).expect("ok");
875 let d3 = BigFloat::from_i64(3, PREC, MODE);
876 z += &d3;
877 assert!((z.re().to_f64() - 4.0).abs() < 1e-12);
878 assert!((z.im().to_f64() - 2.0).abs() < 1e-12);
879
880 let d1 = BigFloat::from_i64(1, PREC, MODE);
881 z -= d1;
882 assert!((z.re().to_f64() - 3.0).abs() < 1e-12);
883
884 let d2 = BigFloat::from_i64(2, PREC, MODE);
885 z *= &d2;
886 assert!((z.re().to_f64() - 6.0).abs() < 1e-12);
887 assert!((z.im().to_f64() - 4.0).abs() < 1e-12);
888 }
889
890 #[test]
895 fn add_i64_scalar() {
896 let z = BigComplex::from_f64(1.0, 2.0, PREC).expect("ok");
897 let r = &z + 4i64;
898 assert!((r.re().to_f64() - 5.0).abs() < 1e-12);
899 assert!((r.im().to_f64() - 2.0).abs() < 1e-12);
900 }
901
902 #[test]
903 fn sub_i64_scalar() {
904 let z = BigComplex::from_f64(5.0, 3.0, PREC).expect("ok");
905 let r = z - 2i64;
906 assert!((r.re().to_f64() - 3.0).abs() < 1e-12);
907 assert!((r.im().to_f64() - 3.0).abs() < 1e-12);
908 }
909
910 #[test]
911 fn mul_i64_scalar_assign() {
912 let mut z = BigComplex::from_f64(1.0, 2.0, PREC).expect("ok");
913 z *= 3i64;
914 assert!((z.re().to_f64() - 3.0).abs() < 1e-12);
915 assert!((z.im().to_f64() - 6.0).abs() < 1e-12);
916 }
917
918 #[test]
919 fn sub_reversed_i64_minus_z() {
920 let z = BigComplex::from_f64(3.0, 2.0, PREC).expect("ok");
922 let r = 5i64 - &z;
923 assert!((r.re().to_f64() - 2.0).abs() < 1e-12);
924 assert!((r.im().to_f64() + 2.0).abs() < 1e-12);
925 }
926
927 #[test]
928 fn i64_scalar_ref_and_owned_consistent() {
929 let z = BigComplex::from_f64(2.0, 3.0, PREC).expect("ok");
930 let r1 = &z * 4i64;
931 let r2 = z.clone() * 4i64;
932 assert_eq!(r1, r2);
933 }
934}