Skip to main content

rill_core/math/vector/
complex.rs

1// rill-core/src/math/vector/complex.rs
2//! Complex vector abstractions over the `Vector<T, 4>` eDSL.
3//!
4//! - `ComplexVector<T,V>` — 2 complex numbers in interleaved `[re0,im0,re1,im1]`
5//! - `ComplexSoa<V>` — 4 complex numbers, separate re/im arrays. For FFT/convolution.
6
7use core::marker::PhantomData;
8
9use crate::math::vector::traits::{Vector, VectorMask};
10use crate::Transcendental;
11
12/// Two complex numbers: `[re0, im0, re1, im1]` in one 4‑lane vector.
13#[derive(Copy, Clone, Debug, PartialEq)]
14pub struct ComplexVector<T: Transcendental, V: Vector<T, 4>> {
15    data: V,
16    _phantom: PhantomData<T>,
17}
18
19impl<T: Transcendental, V: Vector<T, 4>> ComplexVector<T, V> {
20    /// Wrap a raw `Vector<T,4>` in `[re0, im0, re1, im1]` interleaved layout.
21    pub fn from_raw(data: V) -> Self {
22        Self {
23            data,
24            _phantom: PhantomData,
25        }
26    }
27
28    /// Duplicate a single `(re, im)` pair into both lane pairs.
29    pub fn splat_pair(re: T, im: T) -> Self {
30        Self {
31            data: V::load(&[re, im, re, im]),
32            _phantom: PhantomData,
33        }
34    }
35
36    /// Create from a single `(re, im)` pair, duplicated to both lane pairs.
37    pub fn from_pair(c: (T, T)) -> Self {
38        Self::splat_pair(c.0, c.1)
39    }
40
41    /// Create from two possibly-different complex pairs.
42    /// Pairs go to lanes (0,1) and (2,3) respectively.
43    pub fn from_two(c0: (T, T), c1: (T, T)) -> Self {
44        Self {
45            data: V::load(&[c0.0, c0.1, c1.0, c1.1]),
46            _phantom: PhantomData,
47        }
48    }
49
50    /// Return a reference to the raw `Vector<T,4>`.
51    pub fn inner(&self) -> &V {
52        &self.data
53    }
54
55    /// Extract the `i`-th lane from the underlying vector.
56    pub fn extract(&self, i: usize) -> T {
57        self.data.extract(i)
58    }
59    /// Real part of the first complex element (lane 0).
60    pub fn re0(&self) -> T {
61        self.data.extract(0)
62    }
63    /// Imaginary part of the first complex element (lane 1).
64    pub fn im0(&self) -> T {
65        self.data.extract(1)
66    }
67
68    /// Complex conjugate.
69    pub fn conj(&self) -> Self {
70        let v = V::load(&[
71            self.data.extract(0),
72            -self.data.extract(1),
73            self.data.extract(2),
74            -self.data.extract(3),
75        ]);
76        Self {
77            data: v,
78            _phantom: PhantomData,
79        }
80    }
81
82    /// Complex multiplication: `self * other` (element-wise).
83    pub fn cmul(&self, other: &Self) -> Self {
84        let a_re = V::load(&[
85            self.data.extract(0),
86            self.data.extract(0),
87            self.data.extract(2),
88            self.data.extract(2),
89        ]);
90        let a_im = V::load(&[
91            self.data.extract(1),
92            self.data.extract(1),
93            self.data.extract(3),
94            self.data.extract(3),
95        ]);
96        let b_re = V::load(&[
97            other.data.extract(0),
98            other.data.extract(0),
99            other.data.extract(2),
100            other.data.extract(2),
101        ]);
102        let b_im = V::load(&[
103            other.data.extract(1),
104            other.data.extract(1),
105            other.data.extract(3),
106            other.data.extract(3),
107        ]);
108        let out_re = a_re * b_re - a_im * b_im;
109        let out_im = a_re * b_im + a_im * b_re;
110        Self {
111            data: V::load(&[
112                out_re.extract(0),
113                out_im.extract(0),
114                out_re.extract(2),
115                out_im.extract(2),
116            ]),
117            _phantom: PhantomData,
118        }
119    }
120
121    /// Complex addition: `self + other`.
122    pub fn cadd(&self, other: &Self) -> Self {
123        Self {
124            data: self.data + other.data,
125            _phantom: PhantomData,
126        }
127    }
128
129    /// Complex subtraction: `self - other`.
130    pub fn csub(&self, other: &Self) -> Self {
131        Self {
132            data: self.data - other.data,
133            _phantom: PhantomData,
134        }
135    }
136
137    /// Magnitude squared per complex element: `re² + im²` (lane‑pair summed).
138    pub fn norm_sqr(&self) -> (T, T) {
139        let r0 = self.data.extract(0);
140        let i0 = self.data.extract(1);
141        let r1 = self.data.extract(2);
142        let i1 = self.data.extract(3);
143        (r0 * r0 + i0 * i0, r1 * r1 + i1 * i1)
144    }
145
146    /// Multiply both real and imaginary parts by a real scalar.
147    pub fn scale_real(&self, scalar: T) -> Self {
148        Self {
149            data: self.data * V::splat(scalar),
150            _phantom: PhantomData,
151        }
152    }
153
154    /// Extract the first complex value as `(re, im)`.
155    pub fn to_complex0(&self) -> (T, T) {
156        (self.data.extract(0), self.data.extract(1))
157    }
158
159    /// Extract the second complex value as `(re, im)`.
160    pub fn to_complex1(&self) -> (T, T) {
161        (self.data.extract(2), self.data.extract(3))
162    }
163
164    /// Apply a closure to each of the two complex elements.
165    pub fn map_complex<F>(&self, f: F) -> Self
166    where
167        F: Fn((T, T)) -> (T, T),
168    {
169        let c0 = f(self.to_complex0());
170        let c1 = f(self.to_complex1());
171        Self::from_two(c0, c1)
172    }
173
174    /// Iterate over the two complex elements as `(re, im)` pairs.
175    pub fn iter_complex(&self) -> impl Iterator<Item = (T, T)> {
176        [self.to_complex0(), self.to_complex1()].into_iter()
177    }
178}
179
180// ============================================================================
181// Operator overloads — natural `a + b`, `a - b`, `a * b`, `-a` syntax
182// ============================================================================
183
184impl<T: Transcendental, V: Vector<T, 4>> core::ops::Add for ComplexVector<T, V> {
185    type Output = Self;
186    fn add(self, rhs: Self) -> Self {
187        self.cadd(&rhs)
188    }
189}
190
191impl<T: Transcendental, V: Vector<T, 4>> core::ops::Sub for ComplexVector<T, V> {
192    type Output = Self;
193    fn sub(self, rhs: Self) -> Self {
194        self.csub(&rhs)
195    }
196}
197
198impl<T: Transcendental, V: Vector<T, 4>> core::ops::Mul for ComplexVector<T, V> {
199    type Output = Self;
200    fn mul(self, rhs: Self) -> Self {
201        self.cmul(&rhs)
202    }
203}
204
205impl<T: Transcendental, V: Vector<T, 4>> core::ops::Neg for ComplexVector<T, V> {
206    type Output = Self;
207    fn neg(self) -> Self {
208        Self {
209            data: -self.data,
210            _phantom: PhantomData,
211        }
212    }
213}
214
215/// `cv * t` — scale both complex elements by a real scalar.
216impl<T: Transcendental, V: Vector<T, 4>> core::ops::Mul<T> for ComplexVector<T, V> {
217    type Output = Self;
218    fn mul(self, rhs: T) -> Self {
219        self.scale_real(rhs)
220    }
221}
222
223impl<T: Transcendental, V: Vector<T, 4>> core::ops::AddAssign for ComplexVector<T, V> {
224    fn add_assign(&mut self, rhs: Self) {
225        *self = *self + rhs;
226    }
227}
228
229impl<T: Transcendental, V: Vector<T, 4>> core::ops::SubAssign for ComplexVector<T, V> {
230    fn sub_assign(&mut self, rhs: Self) {
231        *self = *self - rhs;
232    }
233}
234
235impl<T: Transcendental, V: Vector<T, 4>> core::ops::MulAssign for ComplexVector<T, V> {
236    fn mul_assign(&mut self, rhs: Self) {
237        *self = *self * rhs;
238    }
239}
240
241impl<T: Transcendental, V: Vector<T, 4>> core::ops::DivAssign for ComplexVector<T, V> {
242    fn div_assign(&mut self, rhs: Self) {
243        *self = *self / rhs;
244    }
245}
246
247/// Complex division: `(a+bi)/(c+di) = (ac+bd)/(c²+d²) + i*(bc-ad)/(c²+d²)`.
248impl<T: Transcendental, V: Vector<T, 4>> core::ops::Div for ComplexVector<T, V> {
249    type Output = Self;
250    fn div(self, rhs: Self) -> Self {
251        let a_re = self.data.extract(0);
252        let a_im = self.data.extract(1);
253        let a2_re = self.data.extract(2);
254        let a2_im = self.data.extract(3);
255        let b_re = rhs.data.extract(0);
256        let b_im = rhs.data.extract(1);
257        let b2_re = rhs.data.extract(2);
258        let b2_im = rhs.data.extract(3);
259        let denom0 = b_re * b_re + b_im * b_im;
260        let denom1 = b2_re * b2_re + b2_im * b2_im;
261        Self {
262            data: V::load(&[
263                (a_re * b_re + a_im * b_im) / denom0,
264                (a_im * b_re - a_re * b_im) / denom0,
265                (a2_re * b2_re + a2_im * b2_im) / denom1,
266                (a2_im * b2_re - a2_re * b2_im) / denom1,
267            ]),
268            _phantom: PhantomData,
269        }
270    }
271}
272
273/// Four complex numbers, separate re/im arrays. For SIMD‑heavy operations.
274#[derive(Copy, Clone, Debug, PartialEq)]
275pub struct ComplexSoa<T: Transcendental, V: Vector<T, 4>> {
276    /// Real parts of four complex numbers (one per lane).
277    pub re: V,
278    /// Imaginary parts of four complex numbers (one per lane).
279    pub im: V,
280    _phantom: PhantomData<T>,
281}
282
283impl<T: Transcendental, V: Vector<T, 4> + VectorMask<T, 4>> ComplexSoa<T, V> {
284    /// Load four complex numbers from separate re/im slices.
285    pub fn load(re_slice: &[T], im_slice: &[T]) -> Self {
286        Self {
287            re: V::load(re_slice),
288            im: V::load(im_slice),
289            _phantom: PhantomData,
290        }
291    }
292
293    /// Create from four `(re, im)` pairs.
294    pub fn from_pairs(p: [(T, T); 4]) -> Self {
295        Self {
296            re: V::load(&[p[0].0, p[1].0, p[2].0, p[3].0]),
297            im: V::load(&[p[0].1, p[1].1, p[2].1, p[3].1]),
298            _phantom: PhantomData,
299        }
300    }
301
302    /// Store back to separate re/im slices.
303    pub fn store(&self, re_slice: &mut [T], im_slice: &mut [T]) {
304        self.re.store(re_slice);
305        self.im.store(im_slice);
306    }
307
308    /// Extract a single complex value at lane `i`: returns `(re, im)`.
309    pub fn extract_complex(&self, i: usize) -> (T, T) {
310        (self.re.extract(i), self.im.extract(i))
311    }
312
313    /// Extract all four complex values as an array of `(re, im)` pairs.
314    pub fn to_complexes(&self) -> [(T, T); 4] {
315        [
316            (self.re.extract(0), self.im.extract(0)),
317            (self.re.extract(1), self.im.extract(1)),
318            (self.re.extract(2), self.im.extract(2)),
319            (self.re.extract(3), self.im.extract(3)),
320        ]
321    }
322
323    /// Apply a closure to each of the four complex elements.
324    pub fn map_complex<F>(&self, f: F) -> Self
325    where
326        F: Fn((T, T)) -> (T, T),
327    {
328        let c = self.to_complexes();
329        Self::from_pairs([f(c[0]), f(c[1]), f(c[2]), f(c[3])])
330    }
331
332    /// Iterate over the four complex elements as `(re, im)` pairs.
333    pub fn iter_complex(&self) -> impl Iterator<Item = (T, T)> {
334        self.to_complexes().into_iter()
335    }
336
337    /// Complex multiplication: `self * other` on four numbers at once.
338    pub fn cmul(&self, other: &Self) -> Self {
339        Self {
340            re: self.re * other.re - self.im * other.im,
341            im: self.re * other.im + self.im * other.re,
342            _phantom: PhantomData,
343        }
344    }
345
346    /// Complex multiply-accumulate: `self += a * b`.
347    pub fn cmul_add(&mut self, a: &Self, b: &Self) {
348        self.re = self.re + (a.re * b.re - a.im * b.im);
349        self.im = self.im + (a.re * b.im + a.im * b.re);
350    }
351
352    /// Complex conjugate.
353    pub fn conj(&self) -> Self {
354        Self {
355            re: self.re,
356            im: -self.im,
357            _phantom: PhantomData,
358        }
359    }
360
361    /// Squared magnitude (re² + im²) per lane.
362    pub fn norm_sqr(&self) -> V {
363        self.re * self.re + self.im * self.im
364    }
365
366    /// True if all four norms are below `threshold_sq`.
367    pub fn all_norm_sqr_lt(&self, threshold_sq: T) -> bool {
368        let t = V::splat(threshold_sq);
369        V::all(&self.norm_sqr().lt(&t))
370    }
371
372    /// Complex addition: `self + other`.
373    pub fn cadd(&self, other: &Self) -> Self {
374        Self {
375            re: self.re + other.re,
376            im: self.im + other.im,
377            _phantom: PhantomData,
378        }
379    }
380
381    /// Complex subtraction: `self - other`.
382    pub fn csub(&self, other: &Self) -> Self {
383        Self {
384            re: self.re - other.re,
385            im: self.im - other.im,
386            _phantom: PhantomData,
387        }
388    }
389
390    /// Multiply both re and im by a real scalar vector (per-lane).
391    pub fn scale_real(&self, scalar: V) -> Self {
392        Self {
393            re: self.re * scalar,
394            im: self.im * scalar,
395            _phantom: PhantomData,
396        }
397    }
398}
399
400// ============================================================================
401// Operator overloads for ComplexSoa — natural formula syntax
402// ============================================================================
403
404impl<T: Transcendental, V: Vector<T, 4> + VectorMask<T, 4>> core::ops::Add for ComplexSoa<T, V> {
405    type Output = Self;
406    fn add(self, rhs: Self) -> Self {
407        self.cadd(&rhs)
408    }
409}
410
411impl<T: Transcendental, V: Vector<T, 4> + VectorMask<T, 4>> core::ops::Sub for ComplexSoa<T, V> {
412    type Output = Self;
413    fn sub(self, rhs: Self) -> Self {
414        self.csub(&rhs)
415    }
416}
417
418impl<T: Transcendental, V: Vector<T, 4> + VectorMask<T, 4>> core::ops::Mul for ComplexSoa<T, V> {
419    type Output = Self;
420    fn mul(self, rhs: Self) -> Self {
421        self.cmul(&rhs)
422    }
423}
424
425impl<T: Transcendental, V: Vector<T, 4> + VectorMask<T, 4>> core::ops::Neg for ComplexSoa<T, V> {
426    type Output = Self;
427    fn neg(self) -> Self {
428        Self {
429            re: -self.re,
430            im: -self.im,
431            _phantom: PhantomData,
432        }
433    }
434}
435
436/// `csoa * sv` — scale all four complex numbers by a real vector (per‑lane).
437impl<T: Transcendental, V: Vector<T, 4> + VectorMask<T, 4>> core::ops::Mul<V> for ComplexSoa<T, V> {
438    type Output = Self;
439    fn mul(self, rhs: V) -> Self {
440        self.scale_real(rhs)
441    }
442}
443
444impl<T: Transcendental, V: Vector<T, 4> + VectorMask<T, 4>> core::ops::AddAssign
445    for ComplexSoa<T, V>
446{
447    fn add_assign(&mut self, rhs: Self) {
448        *self = *self + rhs;
449    }
450}
451
452impl<T: Transcendental, V: Vector<T, 4> + VectorMask<T, 4>> core::ops::SubAssign
453    for ComplexSoa<T, V>
454{
455    fn sub_assign(&mut self, rhs: Self) {
456        *self = *self - rhs;
457    }
458}
459
460impl<T: Transcendental, V: Vector<T, 4> + VectorMask<T, 4>> core::ops::MulAssign
461    for ComplexSoa<T, V>
462{
463    fn mul_assign(&mut self, rhs: Self) {
464        *self = *self * rhs;
465    }
466}
467
468impl<T: Transcendental, V: Vector<T, 4> + VectorMask<T, 4>> core::ops::DivAssign
469    for ComplexSoa<T, V>
470{
471    fn div_assign(&mut self, rhs: Self) {
472        *self = *self / rhs;
473    }
474}
475
476/// Complex division (four at once): `(re+im·i)/(rre+rim·i)` per lane.
477///
478/// `denom = rre² + rim²`, then `re_out = (re·rre + im·rim)/denom`,
479/// `im_out = (im·rre − re·rim)/denom`.
480impl<T: Transcendental, V: Vector<T, 4> + VectorMask<T, 4>> core::ops::Div for ComplexSoa<T, V> {
481    type Output = Self;
482    fn div(self, rhs: Self) -> Self {
483        let denom = rhs.re * rhs.re + rhs.im * rhs.im;
484        Self {
485            re: (self.re * rhs.re + self.im * rhs.im) / denom,
486            im: (self.im * rhs.re - self.re * rhs.im) / denom,
487            _phantom: PhantomData,
488        }
489    }
490}
491
492#[cfg(test)]
493mod tests {
494    use super::*;
495    use crate::math::vector::scalar::ScalarVector4;
496
497    fn approx_eq(a: f32, b: f32) -> bool {
498        (a - b).abs() < 1e-4
499    }
500
501    #[test]
502    fn test_complex_vector_conj() {
503        type CV = ComplexVector<f32, ScalarVector4<f32>>;
504        let cv = CV::splat_pair(1.0, 2.0);
505        assert!(approx_eq(cv.re0(), 1.0));
506        assert!(approx_eq(cv.im0(), 2.0));
507        let conj = cv.conj();
508        assert!(approx_eq(conj.re0(), 1.0));
509        assert!(approx_eq(conj.im0(), -2.0));
510    }
511
512    #[test]
513    fn test_complex_vector_cmul() {
514        type CV = ComplexVector<f32, ScalarVector4<f32>>;
515        let a = CV::splat_pair(1.0, 2.0);
516        let b = CV::splat_pair(3.0, 4.0);
517        let prod = a.cmul(&b);
518        assert!(approx_eq(prod.re0(), -5.0));
519        assert!(approx_eq(prod.im0(), 10.0));
520    }
521
522    #[test]
523    fn test_complex_soa_cmul() {
524        type CS = ComplexSoa<f32, ScalarVector4<f32>>;
525        let a = CS::load(&[1.0, 0.0, 0.5, -0.5], &[0.0, 1.0, 0.5, 0.5]);
526        let b = CS::load(&[2.0, 0.0, 1.0, 1.0], &[3.0, 1.0, 1.0, -1.0]);
527        let prod = a.cmul(&b);
528        assert!(approx_eq(prod.re.extract(0), 2.0));
529        assert!(approx_eq(prod.im.extract(0), 3.0));
530        assert!(approx_eq(prod.re.extract(1), -1.0));
531        assert!(approx_eq(prod.im.extract(1), 0.0));
532    }
533
534    #[test]
535    fn test_complex_soa_conj_norm() {
536        type CS = ComplexSoa<f32, ScalarVector4<f32>>;
537        let a = CS::load(&[3.0, 1.0, 0.0, 2.0], &[4.0, 1.0, 1.0, 3.0]);
538        let conj = a.conj();
539        assert!(approx_eq(conj.im.extract(0), -4.0));
540        let mag2 = a.norm_sqr();
541        assert!(approx_eq(mag2.extract(0), 25.0));
542        assert!(approx_eq(mag2.extract(1), 2.0));
543        assert!(approx_eq(mag2.extract(2), 1.0));
544        assert!(approx_eq(mag2.extract(3), 13.0));
545    }
546
547    #[test]
548    fn test_complex_soa_cmul_add() {
549        type CS = ComplexSoa<f32, ScalarVector4<f32>>;
550        let a = CS::load(&[1.0, 2.0, 0.0, 4.0], &[0.0, 0.0, 1.0, 0.0]);
551        let b = CS::load(&[2.0, 3.0, 0.0, 0.0], &[3.0, 0.0, 1.0, 0.0]);
552        let mut acc = CS::load(&[0.0; 4], &[0.0; 4]);
553        acc.cmul_add(&a, &b);
554        assert!(approx_eq(acc.re.extract(0), 2.0));
555        assert!(approx_eq(acc.im.extract(0), 3.0));
556        assert!(approx_eq(acc.re.extract(1), 6.0));
557        assert!(approx_eq(acc.im.extract(1), 0.0));
558    }
559
560    #[test]
561    fn test_from_pair() {
562        type CV = ComplexVector<f32, ScalarVector4<f32>>;
563        let cv = CV::from_pair((1.0, 2.0));
564        assert!(approx_eq(cv.to_complex0().0, 1.0));
565        assert!(approx_eq(cv.to_complex0().1, 2.0));
566        assert!(approx_eq(cv.to_complex1().0, 1.0));
567        assert!(approx_eq(cv.to_complex1().1, 2.0));
568    }
569
570    #[test]
571    fn test_from_pairs() {
572        type CS = ComplexSoa<f32, ScalarVector4<f32>>;
573        let soa = CS::from_pairs([(1.0, 0.0), (0.0, 1.0), (-1.0, 0.0), (0.0, -1.0)]);
574        let c = soa.to_complexes();
575        assert!(approx_eq(c[0].0, 1.0));
576        assert!(approx_eq(c[0].1, 0.0));
577        assert!(approx_eq(c[1].0, 0.0));
578        assert!(approx_eq(c[1].1, 1.0));
579        assert!(approx_eq(c[2].0, -1.0));
580        assert!(approx_eq(c[2].1, 0.0));
581        assert!(approx_eq(c[3].0, 0.0));
582        assert!(approx_eq(c[3].1, -1.0));
583    }
584
585    #[test]
586    fn test_map_complex_soa() {
587        type CS = ComplexSoa<f32, ScalarVector4<f32>>;
588        let soa = CS::from_pairs([(1.0, 0.0), (2.0, 0.0), (3.0, 0.0), (4.0, 0.0)]);
589        // Scale each element by 2
590        let scaled = soa.map_complex(|(re, im)| (re * 2.0, im * 2.0));
591        let c = scaled.to_complexes();
592        assert!(approx_eq(c[0].0, 2.0));
593        assert!(approx_eq(c[1].0, 4.0));
594        assert!(approx_eq(c[2].0, 6.0));
595        assert!(approx_eq(c[3].0, 8.0));
596    }
597
598    #[test]
599    fn test_map_complex_vector() {
600        type CV = ComplexVector<f32, ScalarVector4<f32>>;
601        let cv = CV::from_two((1.0, 2.0), (3.0, 4.0));
602        // Negate both
603        let neg = cv.map_complex(|(re, im)| (-re, -im));
604        assert!(approx_eq(neg.to_complex0().0, -1.0));
605        assert!(approx_eq(neg.to_complex0().1, -2.0));
606        assert!(approx_eq(neg.to_complex1().0, -3.0));
607        assert!(approx_eq(neg.to_complex1().1, -4.0));
608    }
609
610    #[test]
611    fn test_complex_vector_csub() {
612        type CV = ComplexVector<f32, ScalarVector4<f32>>;
613        let a = CV::from_two((3.0, 5.0), (2.0, 1.0));
614        let b = CV::from_two((1.0, 2.0), (1.0, 0.0));
615        let diff = a.csub(&b);
616        assert!(approx_eq(diff.to_complex0().0, 2.0));
617        assert!(approx_eq(diff.to_complex0().1, 3.0));
618        assert!(approx_eq(diff.to_complex1().0, 1.0));
619        assert!(approx_eq(diff.to_complex1().1, 1.0));
620    }
621
622    #[test]
623    fn test_complex_vector_norm_sqr() {
624        type CV = ComplexVector<f32, ScalarVector4<f32>>;
625        let cv = CV::from_two((3.0, 4.0), (1.0, 1.0));
626        let (n0, n1) = cv.norm_sqr();
627        assert!(approx_eq(n0, 25.0)); // 3²+4²
628        assert!(approx_eq(n1, 2.0)); // 1²+1²
629    }
630
631    #[test]
632    fn test_operators_complex_vector() {
633        type CV = ComplexVector<f32, ScalarVector4<f32>>;
634        let a = CV::from_two((1.0, 2.0), (3.0, 4.0));
635        let b = CV::from_two((0.5, 1.0), (1.0, 0.0));
636        let sum = a + b;
637        assert!(approx_eq(sum.to_complex0().0, 1.5));
638        assert!(approx_eq(sum.to_complex0().1, 3.0));
639        let diff = a - b;
640        assert!(approx_eq(diff.to_complex0().0, 0.5));
641        assert!(approx_eq(diff.to_complex0().1, 1.0));
642        let neg = -a;
643        assert!(approx_eq(neg.to_complex0().0, -1.0));
644        assert!(approx_eq(neg.to_complex0().1, -2.0));
645    }
646
647    #[test]
648    fn test_operators_complex_soa() {
649        type CS = ComplexSoa<f32, ScalarVector4<f32>>;
650        let a = CS::load(&[1.0, 2.0, 3.0, 4.0], &[0.0; 4]);
651        let b = CS::load(&[5.0, 6.0, 7.0, 8.0], &[0.0; 4]);
652        let sum = a + b;
653        assert!(approx_eq(sum.re.extract(0), 6.0));
654        let neg = -a;
655        assert!(approx_eq(neg.re.extract(0), -1.0));
656    }
657}