1use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
35
36use oxinum_core::{OxiNumError, OxiNumResult};
37use oxinum_float::DBig;
38
39use crate::CBig;
40
41#[inline]
47fn add_core(a: &CBig, b: &CBig) -> CBig {
48 CBig {
49 re: &a.re + &b.re,
50 im: &a.im + &b.im,
51 }
52}
53
54#[inline]
56fn sub_core(a: &CBig, b: &CBig) -> CBig {
57 CBig {
58 re: &a.re - &b.re,
59 im: &a.im - &b.im,
60 }
61}
62
63#[inline]
65fn mul_core(a: &CBig, b: &CBig) -> CBig {
66 let re = (&a.re * &b.re) - (&a.im * &b.im);
67 let im = (&a.re * &b.im) + (&a.im * &b.re);
68 CBig { re, im }
69}
70
71#[inline]
79fn div_numerators(a: &CBig, b: &CBig) -> (crate::DBig, crate::DBig) {
80 let num_re = (&a.re * &b.re) + (&a.im * &b.im);
81 let num_im = (&a.im * &b.re) - (&a.re * &b.im);
82 (num_re, num_im)
83}
84
85fn div_core(a: &CBig, b: &CBig) -> OxiNumResult<CBig> {
91 let denom = b.norm_sqr();
92 if b.is_zero() {
93 return Err(OxiNumError::DivByZero);
94 }
95 let (num_re, num_im) = div_numerators(a, b);
96 Ok(CBig {
97 re: &num_re / &denom,
98 im: &num_im / &denom,
99 })
100}
101
102#[inline]
108fn div_op_core(a: &CBig, b: &CBig) -> CBig {
109 let denom = b.norm_sqr();
110 let (num_re, num_im) = div_numerators(a, b);
111 CBig {
112 re: &num_re / &denom,
113 im: &num_im / &denom,
114 }
115}
116
117#[inline]
127fn i64_to_exact_dbig(n: i64) -> DBig {
128 use oxinum_float::precision::with_precision;
129 with_precision(&DBig::from(n), 0)
130}
131
132#[inline]
134fn add_scalar_dbig(z: &CBig, d: &DBig) -> CBig {
135 CBig {
136 re: &z.re + d,
137 im: z.im.clone(),
138 }
139}
140
141#[inline]
143fn sub_scalar_dbig(z: &CBig, d: &DBig) -> CBig {
144 CBig {
145 re: &z.re - d,
146 im: z.im.clone(),
147 }
148}
149
150#[inline]
152fn rsub_scalar_dbig(d: &DBig, z: &CBig) -> CBig {
153 CBig {
154 re: d - &z.re,
155 im: -z.im.clone(),
156 }
157}
158
159#[inline]
161fn mul_scalar_dbig(z: &CBig, d: &DBig) -> CBig {
162 CBig {
163 re: &z.re * d,
164 im: &z.im * d,
165 }
166}
167
168#[inline]
170fn div_scalar_dbig(z: &CBig, d: &DBig) -> CBig {
171 CBig {
172 re: &z.re / d,
173 im: &z.im / d,
174 }
175}
176
177#[inline]
179fn add_scalar_i64(z: &CBig, n: i64) -> CBig {
180 add_scalar_dbig(z, &i64_to_exact_dbig(n))
181}
182#[inline]
183fn sub_scalar_i64(z: &CBig, n: i64) -> CBig {
184 sub_scalar_dbig(z, &i64_to_exact_dbig(n))
185}
186#[inline]
187fn rsub_scalar_i64(n: i64, z: &CBig) -> CBig {
188 rsub_scalar_dbig(&i64_to_exact_dbig(n), z)
189}
190#[inline]
191fn mul_scalar_i64(z: &CBig, n: i64) -> CBig {
192 mul_scalar_dbig(z, &i64_to_exact_dbig(n))
193}
194#[inline]
195fn div_scalar_i64(z: &CBig, n: i64) -> CBig {
196 div_scalar_dbig(z, &i64_to_exact_dbig(n))
197}
198
199impl Add<DBig> for CBig {
204 type Output = CBig;
205 #[inline]
206 fn add(self, rhs: DBig) -> CBig {
207 add_scalar_dbig(&self, &rhs)
208 }
209}
210
211impl Add<&DBig> for CBig {
212 type Output = CBig;
213 #[inline]
214 fn add(self, rhs: &DBig) -> CBig {
215 add_scalar_dbig(&self, rhs)
216 }
217}
218
219impl Add<DBig> for &CBig {
220 type Output = CBig;
221 #[inline]
222 fn add(self, rhs: DBig) -> CBig {
223 add_scalar_dbig(self, &rhs)
224 }
225}
226
227impl Add<&DBig> for &CBig {
228 type Output = CBig;
229 #[inline]
230 fn add(self, rhs: &DBig) -> CBig {
231 add_scalar_dbig(self, rhs)
232 }
233}
234
235impl AddAssign<DBig> for CBig {
236 #[inline]
237 fn add_assign(&mut self, rhs: DBig) {
238 *self = add_scalar_dbig(&*self, &rhs);
239 }
240}
241
242impl AddAssign<&DBig> for CBig {
243 #[inline]
244 fn add_assign(&mut self, rhs: &DBig) {
245 *self = add_scalar_dbig(&*self, rhs);
246 }
247}
248
249impl Sub<DBig> for CBig {
254 type Output = CBig;
255 #[inline]
256 fn sub(self, rhs: DBig) -> CBig {
257 sub_scalar_dbig(&self, &rhs)
258 }
259}
260
261impl Sub<&DBig> for CBig {
262 type Output = CBig;
263 #[inline]
264 fn sub(self, rhs: &DBig) -> CBig {
265 sub_scalar_dbig(&self, rhs)
266 }
267}
268
269impl Sub<DBig> for &CBig {
270 type Output = CBig;
271 #[inline]
272 fn sub(self, rhs: DBig) -> CBig {
273 sub_scalar_dbig(self, &rhs)
274 }
275}
276
277impl Sub<&DBig> for &CBig {
278 type Output = CBig;
279 #[inline]
280 fn sub(self, rhs: &DBig) -> CBig {
281 sub_scalar_dbig(self, rhs)
282 }
283}
284
285impl SubAssign<DBig> for CBig {
286 #[inline]
287 fn sub_assign(&mut self, rhs: DBig) {
288 *self = sub_scalar_dbig(&*self, &rhs);
289 }
290}
291
292impl SubAssign<&DBig> for CBig {
293 #[inline]
294 fn sub_assign(&mut self, rhs: &DBig) {
295 *self = sub_scalar_dbig(&*self, rhs);
296 }
297}
298
299impl Sub<CBig> for DBig {
304 type Output = CBig;
305 #[inline]
306 fn sub(self, rhs: CBig) -> CBig {
307 rsub_scalar_dbig(&self, &rhs)
308 }
309}
310
311impl Sub<&CBig> for DBig {
312 type Output = CBig;
313 #[inline]
314 fn sub(self, rhs: &CBig) -> CBig {
315 rsub_scalar_dbig(&self, rhs)
316 }
317}
318
319impl Sub<CBig> for &DBig {
320 type Output = CBig;
321 #[inline]
322 fn sub(self, rhs: CBig) -> CBig {
323 rsub_scalar_dbig(self, &rhs)
324 }
325}
326
327impl Sub<&CBig> for &DBig {
328 type Output = CBig;
329 #[inline]
330 fn sub(self, rhs: &CBig) -> CBig {
331 rsub_scalar_dbig(self, rhs)
332 }
333}
334
335impl Mul<DBig> for CBig {
340 type Output = CBig;
341 #[inline]
342 fn mul(self, rhs: DBig) -> CBig {
343 mul_scalar_dbig(&self, &rhs)
344 }
345}
346
347impl Mul<&DBig> for CBig {
348 type Output = CBig;
349 #[inline]
350 fn mul(self, rhs: &DBig) -> CBig {
351 mul_scalar_dbig(&self, rhs)
352 }
353}
354
355impl Mul<DBig> for &CBig {
356 type Output = CBig;
357 #[inline]
358 fn mul(self, rhs: DBig) -> CBig {
359 mul_scalar_dbig(self, &rhs)
360 }
361}
362
363impl Mul<&DBig> for &CBig {
364 type Output = CBig;
365 #[inline]
366 fn mul(self, rhs: &DBig) -> CBig {
367 mul_scalar_dbig(self, rhs)
368 }
369}
370
371impl MulAssign<DBig> for CBig {
372 #[inline]
373 fn mul_assign(&mut self, rhs: DBig) {
374 *self = mul_scalar_dbig(&*self, &rhs);
375 }
376}
377
378impl MulAssign<&DBig> for CBig {
379 #[inline]
380 fn mul_assign(&mut self, rhs: &DBig) {
381 *self = mul_scalar_dbig(&*self, rhs);
382 }
383}
384
385impl Div<DBig> for CBig {
390 type Output = CBig;
391 #[inline]
392 fn div(self, rhs: DBig) -> CBig {
393 div_scalar_dbig(&self, &rhs)
394 }
395}
396
397impl Div<&DBig> for CBig {
398 type Output = CBig;
399 #[inline]
400 fn div(self, rhs: &DBig) -> CBig {
401 div_scalar_dbig(&self, rhs)
402 }
403}
404
405impl Div<DBig> for &CBig {
406 type Output = CBig;
407 #[inline]
408 fn div(self, rhs: DBig) -> CBig {
409 div_scalar_dbig(self, &rhs)
410 }
411}
412
413impl Div<&DBig> for &CBig {
414 type Output = CBig;
415 #[inline]
416 fn div(self, rhs: &DBig) -> CBig {
417 div_scalar_dbig(self, rhs)
418 }
419}
420
421impl DivAssign<DBig> for CBig {
422 #[inline]
423 fn div_assign(&mut self, rhs: DBig) {
424 *self = div_scalar_dbig(&*self, &rhs);
425 }
426}
427
428impl DivAssign<&DBig> for CBig {
429 #[inline]
430 fn div_assign(&mut self, rhs: &DBig) {
431 *self = div_scalar_dbig(&*self, rhs);
432 }
433}
434
435impl Add<i64> for CBig {
440 type Output = CBig;
441 #[inline]
442 fn add(self, rhs: i64) -> CBig {
443 add_scalar_i64(&self, rhs)
444 }
445}
446
447impl Add<i64> for &CBig {
448 type Output = CBig;
449 #[inline]
450 fn add(self, rhs: i64) -> CBig {
451 add_scalar_i64(self, rhs)
452 }
453}
454
455impl AddAssign<i64> for CBig {
456 #[inline]
457 fn add_assign(&mut self, rhs: i64) {
458 *self = add_scalar_i64(&*self, rhs);
459 }
460}
461
462impl Sub<i64> for CBig {
467 type Output = CBig;
468 #[inline]
469 fn sub(self, rhs: i64) -> CBig {
470 sub_scalar_i64(&self, rhs)
471 }
472}
473
474impl Sub<i64> for &CBig {
475 type Output = CBig;
476 #[inline]
477 fn sub(self, rhs: i64) -> CBig {
478 sub_scalar_i64(self, rhs)
479 }
480}
481
482impl SubAssign<i64> for CBig {
483 #[inline]
484 fn sub_assign(&mut self, rhs: i64) {
485 *self = sub_scalar_i64(&*self, rhs);
486 }
487}
488
489impl Sub<CBig> for i64 {
494 type Output = CBig;
495 #[inline]
496 fn sub(self, rhs: CBig) -> CBig {
497 rsub_scalar_i64(self, &rhs)
498 }
499}
500
501impl Sub<&CBig> for i64 {
502 type Output = CBig;
503 #[inline]
504 fn sub(self, rhs: &CBig) -> CBig {
505 rsub_scalar_i64(self, rhs)
506 }
507}
508
509impl Mul<i64> for CBig {
514 type Output = CBig;
515 #[inline]
516 fn mul(self, rhs: i64) -> CBig {
517 mul_scalar_i64(&self, rhs)
518 }
519}
520
521impl Mul<i64> for &CBig {
522 type Output = CBig;
523 #[inline]
524 fn mul(self, rhs: i64) -> CBig {
525 mul_scalar_i64(self, rhs)
526 }
527}
528
529impl MulAssign<i64> for CBig {
530 #[inline]
531 fn mul_assign(&mut self, rhs: i64) {
532 *self = mul_scalar_i64(&*self, rhs);
533 }
534}
535
536impl Div<i64> for CBig {
541 type Output = CBig;
542 #[inline]
543 fn div(self, rhs: i64) -> CBig {
544 div_scalar_i64(&self, rhs)
545 }
546}
547
548impl Div<i64> for &CBig {
549 type Output = CBig;
550 #[inline]
551 fn div(self, rhs: i64) -> CBig {
552 div_scalar_i64(self, rhs)
553 }
554}
555
556impl DivAssign<i64> for CBig {
557 #[inline]
558 fn div_assign(&mut self, rhs: i64) {
559 *self = div_scalar_i64(&*self, rhs);
560 }
561}
562
563impl CBig {
568 pub fn checked_div(&self, rhs: &CBig) -> OxiNumResult<CBig> {
590 div_core(self, rhs)
591 }
592}
593
594macro_rules! impl_binop {
600 ($Trait:ident, $method:ident, $core:ident) => {
601 impl $Trait<&CBig> for &CBig {
602 type Output = CBig;
603 #[inline]
604 fn $method(self, rhs: &CBig) -> CBig {
605 $core(self, rhs)
606 }
607 }
608
609 impl $Trait<CBig> for CBig {
610 type Output = CBig;
611 #[inline]
612 fn $method(self, rhs: CBig) -> CBig {
613 $core(&self, &rhs)
614 }
615 }
616
617 impl $Trait<&CBig> for CBig {
618 type Output = CBig;
619 #[inline]
620 fn $method(self, rhs: &CBig) -> CBig {
621 $core(&self, rhs)
622 }
623 }
624
625 impl $Trait<CBig> for &CBig {
626 type Output = CBig;
627 #[inline]
628 fn $method(self, rhs: CBig) -> CBig {
629 $core(self, &rhs)
630 }
631 }
632 };
633}
634
635macro_rules! impl_assign {
636 ($Trait:ident, $method:ident, $core:ident) => {
637 impl $Trait<&CBig> for CBig {
638 #[inline]
639 fn $method(&mut self, rhs: &CBig) {
640 *self = $core(&*self, rhs);
641 }
642 }
643
644 impl $Trait<CBig> for CBig {
645 #[inline]
646 fn $method(&mut self, rhs: CBig) {
647 *self = $core(&*self, &rhs);
648 }
649 }
650 };
651}
652
653impl_binop!(Add, add, add_core);
654impl_binop!(Sub, sub, sub_core);
655impl_binop!(Mul, mul, mul_core);
656impl_binop!(Div, div, div_op_core);
657
658impl_assign!(AddAssign, add_assign, add_core);
659impl_assign!(SubAssign, sub_assign, sub_core);
660impl_assign!(MulAssign, mul_assign, mul_core);
661impl_assign!(DivAssign, div_assign, div_op_core);
662
663impl Neg for CBig {
668 type Output = CBig;
669 #[inline]
670 fn neg(self) -> CBig {
671 CBig {
672 re: -self.re,
673 im: -self.im,
674 }
675 }
676}
677
678impl Neg for &CBig {
679 type Output = CBig;
680 #[inline]
681 fn neg(self) -> CBig {
682 CBig {
683 re: -&self.re,
684 im: -&self.im,
685 }
686 }
687}
688
689impl PartialEq for CBig {
694 #[inline]
695 fn eq(&self, other: &Self) -> bool {
696 self.re == other.re && self.im == other.im
697 }
698}
699
700impl Default for CBig {
705 #[inline]
706 fn default() -> Self {
707 CBig::zero()
708 }
709}
710
711#[cfg(test)]
716mod tests {
717 use super::*;
718
719 fn c(re: f64, im: f64) -> CBig {
721 CBig::from_f64(re, im).expect("finite parts")
722 }
723
724 fn assert_parts(z: &CBig, re: &str, im: &str) {
726 assert_eq!(z.re().to_string(), re, "real part mismatch");
727 assert_eq!(z.im().to_string(), im, "imag part mismatch");
728 }
729
730 #[test]
731 fn add_component_wise() {
732 let sum = c(1.0, 2.0) + c(3.0, 4.0);
734 assert_parts(&sum, "4", "6");
735 }
736
737 #[test]
738 fn sub_component_wise() {
739 let diff = c(1.0, 2.0) - c(3.0, 4.0);
741 assert_parts(&diff, "-2", "-2");
742 }
743
744 #[test]
745 fn mul_hand_computed() {
746 let prod = c(1.0, 2.0) * c(3.0, 4.0);
748 assert_parts(&prod, "-5", "10");
749 }
750
751 #[test]
752 fn mul_i_squared_is_minus_one() {
753 let prod = CBig::i() * CBig::i();
755 assert_parts(&prod, "-1", "0");
756 }
757
758 #[test]
759 fn mul_one_plus_i_squared_is_two_i() {
760 let z = c(1.0, 1.0);
762 let sq = &z * &z;
763 assert_parts(&sq, "0", "2");
764 }
765
766 #[test]
767 fn div_self_is_one() {
768 let z = c(1.0, 1.0);
770 let q = &z / &z;
771 assert_parts(&q, "1", "0");
772 }
773
774 #[test]
775 fn div_general() {
776 let q = c(3.0, 4.0) / c(1.0, 2.0);
780 assert_parts(&q, "2.2", "-0.4");
781 }
782
783 #[test]
784 fn checked_div_self_is_one() {
785 let z = c(1.0, 1.0);
786 let q = z.checked_div(&z).expect("non-zero divisor");
787 assert_parts(&q, "1", "0");
788 }
789
790 #[test]
791 fn checked_div_by_zero_is_err() {
792 let z = c(1.0, 1.0);
793 assert!(matches!(
797 z.checked_div(&CBig::zero()),
798 Err(OxiNumError::DivByZero)
799 ));
800 }
801
802 #[test]
803 #[should_panic]
804 fn div_operator_by_zero_panics() {
805 let z = c(1.0, 1.0);
806 let _ = z / CBig::zero();
807 }
808
809 #[test]
810 fn norm_sqr_exact() {
811 assert_eq!(c(3.0, 4.0).norm_sqr().to_string(), "25");
813 }
814
815 #[test]
816 fn default_is_zero() {
817 let d = CBig::default();
818 assert!(d.is_zero());
819 assert!(d == CBig::zero());
822 }
823
824 #[test]
825 fn partial_eq_component_wise() {
826 assert!(c(1.5, -2.0) == c(1.5, -2.0));
827 assert!(c(1.5, -2.0) != c(1.5, 2.0));
828 assert!(c(1.5, -2.0) != c(-1.5, -2.0));
829 }
830
831 #[test]
832 fn neg_owned_and_borrowed() {
833 let z = c(2.0, -3.0);
834 let n_ref = -&z;
835 assert_parts(&n_ref, "-2", "3");
836 let n_owned = -z;
837 assert_parts(&n_owned, "-2", "3");
838 }
839
840 #[test]
841 fn add_assign_accumulates() {
842 let mut a = c(1.0, 2.0);
843 a += c(3.0, 4.0);
844 assert_parts(&a, "4", "6");
845 a += &c(-4.0, -6.0);
846 assert!(a.is_zero());
847 }
848
849 #[test]
850 fn mul_assign_updates_in_place() {
851 let mut a = c(1.0, 1.0);
853 a *= &c(1.0, 1.0);
854 assert_parts(&a, "0", "2");
855 }
856
857 #[test]
858 fn sub_and_div_assign() {
859 let mut a = c(5.0, 5.0);
860 a -= c(2.0, 1.0);
861 assert_parts(&a, "3", "4");
862 a /= c(3.0, 4.0);
864 assert_parts(&a, "1", "0");
865 }
866
867 #[test]
868 fn ownership_variants_consistent() {
869 let a = c(1.0, 2.0);
872 let b = c(3.0, 4.0);
873 let target = c(4.0, 6.0);
874 assert!(&a + &b == target);
875 assert!(a.clone() + b.clone() == target);
876 assert!(a.clone() + &b == target);
877 assert!(&a + b.clone() == target);
878 }
879
880 #[test]
885 fn add_dbig_scalar() {
886 let z = CBig::from_f64(1.0, 2.0).expect("ok");
887 let d = DBig::from(3u32);
888 let r = z + &d;
889 assert_eq!(r.re().to_string(), "4");
890 assert_eq!(r.im().to_string(), "2");
891 }
892
893 #[test]
894 fn mul_dbig_scalar() {
895 let z = CBig::i(); let d = DBig::from(3u32);
897 let r = &z * &d;
898 assert_eq!(r.re().to_string(), "0");
899 assert_eq!(r.im().to_string(), "3");
900 }
901
902 #[test]
903 fn div_dbig_scalar() {
904 let z = CBig::from_f64(6.0, 4.0).expect("ok");
905 let d = DBig::from(2u32);
906 let r = z / d;
907 assert_eq!(r.re().to_string(), "3");
908 assert_eq!(r.im().to_string(), "2");
909 }
910
911 #[test]
912 fn sub_reversed_dbig_minus_z() {
913 let z = CBig::from_f64(3.0, 2.0).expect("ok");
915 let d = DBig::from(5u32);
916 let r = d - &z;
917 assert_eq!(r.re().to_string(), "2");
918 assert_eq!(r.im().to_string(), "-2");
919 }
920
921 #[test]
922 fn dbig_scalar_assign_ops() {
923 let mut z = CBig::from_f64(1.0, 2.0).expect("ok");
925 z += DBig::from(3u32);
926 assert_eq!(z.re().to_string(), "4");
927 assert_eq!(z.im().to_string(), "2");
928
929 z -= &DBig::from(1u32);
931 assert_eq!(z.re().to_string(), "3");
932
933 z *= DBig::from(2u32);
935 assert_eq!(z.re().to_string(), "6");
936 assert_eq!(z.im().to_string(), "4");
937
938 z /= &DBig::from(2u32);
940 assert_eq!(z.re().to_string(), "3");
941 assert_eq!(z.im().to_string(), "2");
942 }
943
944 #[test]
949 fn mul_i64_scalar() {
950 let z = CBig::i(); let r = z * 5i64;
952 assert_eq!(r.re().to_string(), "0");
953 assert_eq!(r.im().to_string(), "5");
954 }
955
956 #[test]
957 fn add_i64_scalar_assign() {
958 let mut z = CBig::from_f64(1.0, 0.0).expect("ok");
959 z += 4i64;
960 assert_eq!(z.re().to_string(), "5");
961 assert_eq!(z.im().to_string(), "0");
962 }
963
964 #[test]
965 fn i64_scalar_product_is_exact() {
966 let z: CBig = (3i64, 4i64).into();
968 let r = z * 5i64;
969 assert_eq!(r.re().to_string(), "15");
970 assert_eq!(r.im().to_string(), "20");
971 }
972
973 #[test]
974 fn sub_i64_scalar() {
975 let z = CBig::from_f64(5.0, 3.0).expect("ok");
977 let r = z - 2i64;
978 assert_eq!(r.re().to_string(), "3");
979 assert_eq!(r.im().to_string(), "3");
980 }
981
982 #[test]
983 fn sub_reversed_i64_minus_z() {
984 let z = CBig::from_f64(3.0, 2.0).expect("ok");
986 let r = 5i64 - &z;
987 assert_eq!(r.re().to_string(), "2");
988 assert_eq!(r.im().to_string(), "-2");
989 }
990
991 #[test]
992 fn div_i64_scalar() {
993 let z = CBig::from_f64(6.0, 4.0).expect("ok");
995 let r = z / 2i64;
996 assert_eq!(r.re().to_string(), "3");
997 assert_eq!(r.im().to_string(), "2");
998 }
999
1000 #[test]
1001 fn i64_scalar_ref_ops_consistent() {
1002 let z = CBig::from_f64(2.0, 3.0).expect("ok");
1004 let r1 = &z * 4i64;
1005 let r2 = z.clone() * 4i64;
1006 assert!(r1 == r2);
1007 }
1008}