From c127d65b056eb767464c8fd2828dbdaf55673d46 Mon Sep 17 00:00:00 2001 From: Paul Date: Fri, 17 Oct 2025 23:30:26 +0200 Subject: [PATCH 01/11] div_add --- src/lib.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 661b67b..0b5a4f5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -824,6 +824,25 @@ impl Div> for Complex { } } +// (a + i b) / (c + i d) + (e + i f) == [(a + i b) * (c - i d)] / (c*c + d*d) + (e + i f) +// == [(a + i b) * (c - i d) + (c*c + d*d) * (e + i f)] / n for n=(c*c + d*d) +// == [(a*c + b*d + n*e) + i (-a*d + b*c + n*f)] / n +// == [{(a*c + b*d) / n + e} + i {(-a*d + b*c) / n + f}] +impl + Neg> Complex { + #[inline] + fn div_add(self, other: Complex, add: Complex) -> Self { + let n = other.norm_sqr(); + let (a, b) = (self.re, self.im); + let (c, d) = (other.re, other.im); + let (e, f) = (add.re, add.im); + + let re = a.clone().mul_add(c.clone(), b.clone() * d.clone()) / n.clone() + e; + let im = a.mul_add(-d, b * c) / n + f; + + Self::new(re, im) + } +} + forward_all_binop!(impl Rem, rem); impl Complex { From 4b11856a0ff0568b7adf20ce00b81412ac18b7b3 Mon Sep 17 00:00:00 2001 From: Paul Date: Fri, 17 Oct 2025 23:30:43 +0200 Subject: [PATCH 02/11] div_add_ref --- src/lib.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 0b5a4f5..6bfe148 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -842,6 +842,12 @@ impl + Neg> Complex { Self::new(re, im) } } +impl + Neg> Complex { + #[inline] + fn div_add_ref(self, other: &Complex, add: &Complex) -> Self { + self.clone().div_add(other.clone(), add.clone()) + } +} forward_all_binop!(impl Rem, rem); From 13b7a0279951c1b05ad404353510859f2af74b1b Mon Sep 17 00:00:00 2001 From: Paul Date: Fri, 17 Oct 2025 23:41:46 +0200 Subject: [PATCH 03/11] overloaded div_add via DivAdd trait --- src/lib.rs | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 6bfe148..1a8b79b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -824,13 +824,20 @@ impl Div> for Complex { } } +trait DivAdd { + type Output; + fn div_add(self, rhs: Rhs, addend: Addend) -> Self::Output; +} + // (a + i b) / (c + i d) + (e + i f) == [(a + i b) * (c - i d)] / (c*c + d*d) + (e + i f) // == [(a + i b) * (c - i d) + (c*c + d*d) * (e + i f)] / n for n=(c*c + d*d) // == [(a*c + b*d + n*e) + i (-a*d + b*c + n*f)] / n // == [{(a*c + b*d) / n + e} + i {(-a*d + b*c) / n + f}] -impl + Neg> Complex { +impl + Neg> DivAdd> for Complex { + type Output = Self; + #[inline] - fn div_add(self, other: Complex, add: Complex) -> Self { + fn div_add(self, other: Complex, add: Complex) -> Self::Output { let n = other.norm_sqr(); let (a, b) = (self.re, self.im); let (c, d) = (other.re, other.im); @@ -842,9 +849,13 @@ impl + Neg> Complex { Self::new(re, im) } } -impl + Neg> Complex { +impl<'a, 'b, T: Clone + Num + MulAdd + Neg> DivAdd<&'b Complex> + for &'a Complex +{ + type Output = Complex; + #[inline] - fn div_add_ref(self, other: &Complex, add: &Complex) -> Self { + fn div_add(self, other: &Complex, add: &Complex) -> Self::Output { self.clone().div_add(other.clone(), add.clone()) } } From fba3b582488f9d006daa96e63a7d3977630ec3e1 Mon Sep 17 00:00:00 2001 From: Paul Date: Fri, 17 Oct 2025 23:42:18 +0200 Subject: [PATCH 04/11] elide explicit lifetimes --- src/lib.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 1a8b79b..c0be8ec 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -849,9 +849,7 @@ impl + Neg> DivAdd> f Self::new(re, im) } } -impl<'a, 'b, T: Clone + Num + MulAdd + Neg> DivAdd<&'b Complex> - for &'a Complex -{ +impl + Neg> DivAdd<&Complex> for &Complex { type Output = Complex; #[inline] From fc1a767571cb99c57606964cbf8ed63441e02b4b Mon Sep 17 00:00:00 2001 From: Paul Date: Sat, 18 Oct 2025 00:04:05 +0200 Subject: [PATCH 05/11] add tests --- src/lib.rs | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index c0be8ec..ebbe185 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1680,6 +1680,8 @@ pub(crate) mod test { use num_traits::{Num, One, Zero}; + use super::DivAdd; + pub const _0_0i: Complex64 = Complex::new(0.0, 0.0); pub const _1_0i: Complex64 = Complex::new(1.0, 0.0); pub const _1_1i: Complex64 = Complex::new(1.0, 1.0); @@ -1849,7 +1851,7 @@ pub(crate) mod test { close_to_tol(a, b, 1e-10) } - fn close_to_tol(a: Complex64, b: Complex64, tol: f64) -> bool { + pub(crate) fn close_to_tol(a: Complex64, b: Complex64, tol: f64) -> bool { // returns true if a and b are reasonably close let close = (a == b) || (a - b).norm() < tol; if !close { @@ -2536,6 +2538,9 @@ pub(crate) mod test { } mod complex_arithmetic { + use crate::test::float::close_to_tol; + + use super::DivAdd; use super::{_05_05i, _0_0i, _0_1i, _1_0i, _1_1i, _4_2i, _neg1_1i, all_consts}; use num_traits::{MulAdd, MulAddAssign, Zero}; @@ -2637,6 +2642,37 @@ pub(crate) mod test { } } + #[test] + fn test_div_add() { + use super::Complex; + const _0_0i: Complex = Complex { re: 0, im: 0 }; + const _1_0i: Complex = Complex { re: 1, im: 0 }; + const _2_0i: Complex = Complex { re: 2, im: 0 }; + const _1_1i: Complex = Complex { re: 1, im: 1 }; + const _0_1i: Complex = Complex { re: 0, im: 1 }; + const _neg1_1i: Complex = Complex { re: -1, im: 1 }; + const all_consts: [Complex; 6] = [_0_0i, _1_0i, _2_0i, _1_1i, _0_1i, _neg1_1i]; + const non_zero_consts: [Complex; 5] = [_1_0i, _2_0i, _1_1i, _0_1i, _neg1_1i]; + + assert_eq!(_1_0i.div_add(_1_0i, _0_0i), _1_0i); + assert_eq!(_0_1i.div_add(_0_1i, _0_1i), _1_1i); + assert_eq!(_1_0i.div_add(_1_0i, _1_0i), _2_0i); + + // a/b+c ~= 6.34 * e^(2i) + const _a: Complex = Complex { re: 1.23, im: -3.4 }; + const _b: Complex = Complex { re: -6.78, im: 9.0 }; + const _c: Complex = Complex { re: -2.34, im: 5.6 }; + assert!(close_to_tol(_a.div_add(_b, _c), _a / _b + _c, 1e-10)); + + for &a in &all_consts { + for &b in &non_zero_consts { + for &c in &all_consts { + assert_eq!(a.div_add(b, c), a / b + c); + } + } + } + } + #[test] fn test_div() { test_op!(_neg1_1i / _0_1i, _1_1i); From 49503f01f076b0612c528f8808da9be915b12658 Mon Sep 17 00:00:00 2001 From: Paul Date: Sat, 25 Oct 2025 13:45:53 +0200 Subject: [PATCH 06/11] simplify div_add impl: use Div, Add --- src/lib.rs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index ebbe185..1bd66bc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -830,9 +830,7 @@ trait DivAdd { } // (a + i b) / (c + i d) + (e + i f) == [(a + i b) * (c - i d)] / (c*c + d*d) + (e + i f) -// == [(a + i b) * (c - i d) + (c*c + d*d) * (e + i f)] / n for n=(c*c + d*d) -// == [(a*c + b*d + n*e) + i (-a*d + b*c + n*f)] / n -// == [{(a*c + b*d) / n + e} + i {(-a*d + b*c) / n + f}] +// == {(a*c + b*d) + i (-a*d + b*c)} / n + (e + i f) for n=(c*c + d*d) impl + Neg> DivAdd> for Complex { type Output = Self; @@ -841,12 +839,11 @@ impl + Neg> DivAdd> f let n = other.norm_sqr(); let (a, b) = (self.re, self.im); let (c, d) = (other.re, other.im); - let (e, f) = (add.re, add.im); + + let re = a.clone().mul_add(c.clone(), b.clone() * d.clone()); + let im = a.mul_add(-d, b * c); - let re = a.clone().mul_add(c.clone(), b.clone() * d.clone()) / n.clone() + e; - let im = a.mul_add(-d, b * c) / n + f; - - Self::new(re, im) + Self::new(re, im) / n + add } } impl + Neg> DivAdd<&Complex> for &Complex { From e8ce02045e4686b8ff5bcefc2220a678c27e1c04 Mon Sep 17 00:00:00 2001 From: Paul Date: Sat, 25 Oct 2025 13:46:11 +0200 Subject: [PATCH 07/11] test ref impl of div_add --- src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 1bd66bc..09952ff 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -839,7 +839,7 @@ impl + Neg> DivAdd> f let n = other.norm_sqr(); let (a, b) = (self.re, self.im); let (c, d) = (other.re, other.im); - + let re = a.clone().mul_add(c.clone(), b.clone() * d.clone()); let im = a.mul_add(-d, b * c); @@ -2665,6 +2665,7 @@ pub(crate) mod test { for &b in &non_zero_consts { for &c in &all_consts { assert_eq!(a.div_add(b, c), a / b + c); + assert_eq!((&a).div_add(&b, &c), a / b + c); } } } From 4376f8ed9eeae3c632a0184e0c8c2ceec1682ddf Mon Sep 17 00:00:00 2001 From: Paul Date: Sat, 25 Oct 2025 14:09:16 +0200 Subject: [PATCH 08/11] specify trait bound `Mul` --- src/lib.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 09952ff..66c2dea 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -831,7 +831,9 @@ trait DivAdd { // (a + i b) / (c + i d) + (e + i f) == [(a + i b) * (c - i d)] / (c*c + d*d) + (e + i f) // == {(a*c + b*d) + i (-a*d + b*c)} / n + (e + i f) for n=(c*c + d*d) -impl + Neg> DivAdd> for Complex { +impl + MulAdd + Neg> DivAdd> + for Complex +{ type Output = Self; #[inline] From ccec5722a177b400bbca4fa372a144bcaba12e85 Mon Sep 17 00:00:00 2001 From: Paul Date: Sat, 25 Oct 2025 14:09:39 +0200 Subject: [PATCH 09/11] DivAdd trait pub visibility --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 66c2dea..c3fbf55 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -824,7 +824,7 @@ impl Div> for Complex { } } -trait DivAdd { +pub trait DivAdd { type Output; fn div_add(self, rhs: Rhs, addend: Addend) -> Self::Output; } From 90da11d44e1dd87cd254d864c3ce9b5bb427408d Mon Sep 17 00:00:00 2001 From: Paul Date: Sat, 25 Oct 2025 14:16:18 +0200 Subject: [PATCH 10/11] cleanup usages in test module --- src/lib.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index c3fbf55..e46ecf9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1679,8 +1679,6 @@ pub(crate) mod test { use num_traits::{Num, One, Zero}; - use super::DivAdd; - pub const _0_0i: Complex64 = Complex::new(0.0, 0.0); pub const _1_0i: Complex64 = Complex::new(1.0, 0.0); pub const _1_1i: Complex64 = Complex::new(1.0, 1.0); @@ -2539,7 +2537,6 @@ pub(crate) mod test { mod complex_arithmetic { use crate::test::float::close_to_tol; - use super::DivAdd; use super::{_05_05i, _0_0i, _0_1i, _1_0i, _1_1i, _4_2i, _neg1_1i, all_consts}; use num_traits::{MulAdd, MulAddAssign, Zero}; @@ -2643,7 +2640,8 @@ pub(crate) mod test { #[test] fn test_div_add() { - use super::Complex; + use crate::{Complex, DivAdd}; + const _0_0i: Complex = Complex { re: 0, im: 0 }; const _1_0i: Complex = Complex { re: 1, im: 0 }; const _2_0i: Complex = Complex { re: 2, im: 0 }; From 1a0ab93fe51192f5099c7b5e183a0918ade420f5 Mon Sep 17 00:00:00 2001 From: Paul Date: Sat, 25 Oct 2025 14:24:46 +0200 Subject: [PATCH 11/11] move trait bounds into where clause --- src/lib.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index e46ecf9..829f286 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -831,8 +831,9 @@ pub trait DivAdd { // (a + i b) / (c + i d) + (e + i f) == [(a + i b) * (c - i d)] / (c*c + d*d) + (e + i f) // == {(a*c + b*d) + i (-a*d + b*c)} / n + (e + i f) for n=(c*c + d*d) -impl + MulAdd + Neg> DivAdd> - for Complex +impl DivAdd> for Complex +where + T: Clone + Num + Mul + MulAdd + Neg, { type Output = Self; @@ -848,7 +849,10 @@ impl + MulAdd + Neg> Di Self::new(re, im) / n + add } } -impl + Neg> DivAdd<&Complex> for &Complex { +impl DivAdd<&Complex> for &Complex +where + T: Clone + Num + Mul + MulAdd + Neg, +{ type Output = Complex; #[inline]