Skip to main content

rill_core/math/vector/simd/
wide.rs

1//! Cross-platform SIMD implementations via the `wide` crate
2//!
3//! This module provides vector types using the `wide` library,
4//! which provides portable SIMD operations with fallback to scalar implementations.
5//!
6//! Types:
7//! - `F32x4`, `F32x8` for `f32`
8//! - `F64x2`, `F64x4` for `f64`
9
10use crate::Transcendental;
11use std::ops::{
12    Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, RemAssign, Sub, SubAssign,
13};
14use wide::{f32x4, f32x8, f64x2, f64x4, CmpEq, CmpGe, CmpGt, CmpLe, CmpLt, CmpNe};
15
16use crate::math::vector::traits::{Vector, VectorMask, VectorTranscendental};
17
18// -----------------------------------------------------------------------------
19// Wrappers around wide types for implementing the Vector trait
20// -----------------------------------------------------------------------------
21
22/// SIMD vector of 4 `f32` elements
23#[derive(Copy, Clone, Debug, PartialEq)]
24pub struct F32x4(f32x4);
25
26/// SIMD vector of 8 `f32` elements
27#[derive(Copy, Clone, Debug, PartialEq)]
28pub struct F32x8(f32x8);
29
30/// SIMD vector of 2 `f64` elements
31#[derive(Copy, Clone, Debug, PartialEq)]
32pub struct F64x2(f64x2);
33
34/// SIMD vector of 4 `f64` elements
35#[derive(Copy, Clone, Debug, PartialEq)]
36pub struct F64x4(f64x4);
37
38// -----------------------------------------------------------------------------
39// Default implementations
40// -----------------------------------------------------------------------------
41
42impl Default for F32x4 {
43    fn default() -> Self {
44        Self(f32x4::splat(0.0))
45    }
46}
47
48impl Default for F32x8 {
49    fn default() -> Self {
50        Self(f32x8::splat(0.0))
51    }
52}
53
54impl Default for F64x2 {
55    fn default() -> Self {
56        Self(f64x2::splat(0.0))
57    }
58}
59
60impl Default for F64x4 {
61    fn default() -> Self {
62        Self(f64x4::splat(0.0))
63    }
64}
65
66// -----------------------------------------------------------------------------
67// Vector implementation for F32x4
68// -----------------------------------------------------------------------------
69
70impl Vector<f32, 4> for F32x4 {
71    fn splat(value: f32) -> Self {
72        F32x4(f32x4::splat(value))
73    }
74
75    fn load(slice: &[f32]) -> Self {
76        let mut arr = [0.0f32; 4];
77        arr.copy_from_slice(&slice[0..4]);
78        F32x4(f32x4::from(arr))
79    }
80
81    fn store(&self, slice: &mut [f32]) {
82        let arr: [f32; 4] = self.0.into();
83        slice[0..4].copy_from_slice(&arr);
84    }
85
86    fn extract(&self, index: usize) -> f32 {
87        let arr: [f32; 4] = self.0.into();
88        arr[index]
89    }
90
91    fn insert(&self, index: usize, value: f32) -> Self {
92        let mut arr: [f32; 4] = self.0.into();
93        arr[index] = value;
94        F32x4(f32x4::from(arr))
95    }
96
97    fn add(&self, other: &Self) -> Self {
98        F32x4(self.0 + other.0)
99    }
100
101    fn sub(&self, other: &Self) -> Self {
102        F32x4(self.0 - other.0)
103    }
104
105    fn mul(&self, other: &Self) -> Self {
106        F32x4(self.0 * other.0)
107    }
108
109    fn div(&self, other: &Self) -> Self {
110        F32x4(self.0 / other.0)
111    }
112
113    fn rem(&self, other: &Self) -> Self {
114        // wide does not provide a remainder operation, implement component-wise
115        let a: [f32; 4] = self.0.into();
116        let b: [f32; 4] = other.0.into();
117        let mut arr = [0.0f32; 4];
118        for i in 0..4 {
119            arr[i] = a[i] % b[i];
120        }
121        F32x4(f32x4::from(arr))
122    }
123
124    fn neg(&self) -> Self {
125        F32x4(-self.0)
126    }
127
128    fn abs(&self) -> Self {
129        F32x4(self.0.abs())
130    }
131
132    fn min(&self, other: &Self) -> Self {
133        F32x4(self.0.min(other.0))
134    }
135
136    fn max(&self, other: &Self) -> Self {
137        F32x4(self.0.max(other.0))
138    }
139
140    fn clamp(&self, min: &Self, max: &Self) -> Self {
141        // clamp = self.max(min).min(max)
142        F32x4(self.0.max(min.0).min(max.0))
143    }
144}
145
146impl VectorTranscendental<f32, 4> for F32x4 {
147    fn sqrt(&self) -> Self {
148        F32x4(self.0.sqrt())
149    }
150    fn exp(&self) -> Self {
151        F32x4(self.0.exp())
152    }
153    fn ln(&self) -> Self {
154        F32x4(self.0.ln())
155    }
156    fn sin(&self) -> Self {
157        F32x4(self.0.sin())
158    }
159    fn cos(&self) -> Self {
160        F32x4(self.0.cos())
161    }
162    fn tan(&self) -> Self {
163        F32x4(self.0.tan())
164    }
165}
166
167// -----------------------------------------------------------------------------
168// Vector implementation for F32x8
169// -----------------------------------------------------------------------------
170
171impl Vector<f32, 8> for F32x8 {
172    fn splat(value: f32) -> Self {
173        F32x8(f32x8::splat(value))
174    }
175
176    fn load(slice: &[f32]) -> Self {
177        let mut arr = [0.0f32; 8];
178        arr.copy_from_slice(&slice[0..8]);
179        F32x8(f32x8::from(arr))
180    }
181
182    fn store(&self, slice: &mut [f32]) {
183        let arr: [f32; 8] = self.0.into();
184        slice[0..8].copy_from_slice(&arr);
185    }
186
187    fn extract(&self, index: usize) -> f32 {
188        let arr: [f32; 8] = self.0.into();
189        arr[index]
190    }
191
192    fn insert(&self, index: usize, value: f32) -> Self {
193        let mut arr: [f32; 8] = self.0.into();
194        arr[index] = value;
195        F32x8(f32x8::from(arr))
196    }
197
198    fn add(&self, other: &Self) -> Self {
199        F32x8(self.0 + other.0)
200    }
201
202    fn sub(&self, other: &Self) -> Self {
203        F32x8(self.0 - other.0)
204    }
205
206    fn mul(&self, other: &Self) -> Self {
207        F32x8(self.0 * other.0)
208    }
209
210    fn div(&self, other: &Self) -> Self {
211        F32x8(self.0 / other.0)
212    }
213
214    fn rem(&self, other: &Self) -> Self {
215        let a: [f32; 8] = self.0.into();
216        let b: [f32; 8] = other.0.into();
217        let mut arr = [0.0f32; 8];
218        for i in 0..8 {
219            arr[i] = a[i] % b[i];
220        }
221        F32x8(f32x8::from(arr))
222    }
223
224    fn neg(&self) -> Self {
225        F32x8(-self.0)
226    }
227
228    fn abs(&self) -> Self {
229        F32x8(self.0.abs())
230    }
231
232    fn min(&self, other: &Self) -> Self {
233        F32x8(self.0.min(other.0))
234    }
235
236    fn max(&self, other: &Self) -> Self {
237        F32x8(self.0.max(other.0))
238    }
239
240    fn clamp(&self, min: &Self, max: &Self) -> Self {
241        F32x8(self.0.max(min.0).min(max.0))
242    }
243}
244
245impl VectorTranscendental<f32, 8> for F32x8 {
246    fn sqrt(&self) -> Self {
247        F32x8(self.0.sqrt())
248    }
249    fn exp(&self) -> Self {
250        F32x8(self.0.exp())
251    }
252    fn ln(&self) -> Self {
253        F32x8(self.0.ln())
254    }
255    fn sin(&self) -> Self {
256        F32x8(self.0.sin())
257    }
258    fn cos(&self) -> Self {
259        F32x8(self.0.cos())
260    }
261    fn tan(&self) -> Self {
262        F32x8(self.0.tan())
263    }
264}
265
266// -----------------------------------------------------------------------------
267// Vector implementation for F64x2
268// -----------------------------------------------------------------------------
269
270impl Vector<f64, 2> for F64x2 {
271    fn splat(value: f64) -> Self {
272        F64x2(f64x2::splat(value))
273    }
274
275    fn load(slice: &[f64]) -> Self {
276        let mut arr = [0.0f64; 2];
277        arr.copy_from_slice(&slice[0..2]);
278        F64x2(f64x2::from(arr))
279    }
280
281    fn store(&self, slice: &mut [f64]) {
282        let arr: [f64; 2] = self.0.into();
283        slice[0..2].copy_from_slice(&arr);
284    }
285
286    fn extract(&self, index: usize) -> f64 {
287        let arr: [f64; 2] = self.0.into();
288        arr[index]
289    }
290
291    fn insert(&self, index: usize, value: f64) -> Self {
292        let mut arr: [f64; 2] = self.0.into();
293        arr[index] = value;
294        F64x2(f64x2::from(arr))
295    }
296
297    fn add(&self, other: &Self) -> Self {
298        F64x2(self.0 + other.0)
299    }
300
301    fn sub(&self, other: &Self) -> Self {
302        F64x2(self.0 - other.0)
303    }
304
305    fn mul(&self, other: &Self) -> Self {
306        F64x2(self.0 * other.0)
307    }
308
309    fn div(&self, other: &Self) -> Self {
310        F64x2(self.0 / other.0)
311    }
312
313    fn rem(&self, other: &Self) -> Self {
314        let a: [f64; 2] = self.0.into();
315        let b: [f64; 2] = other.0.into();
316        let mut arr = [0.0f64; 2];
317        for i in 0..2 {
318            arr[i] = a[i] % b[i];
319        }
320        F64x2(f64x2::from(arr))
321    }
322
323    fn neg(&self) -> Self {
324        F64x2(-self.0)
325    }
326
327    fn abs(&self) -> Self {
328        F64x2(self.0.abs())
329    }
330
331    fn min(&self, other: &Self) -> Self {
332        F64x2(self.0.min(other.0))
333    }
334
335    fn max(&self, other: &Self) -> Self {
336        F64x2(self.0.max(other.0))
337    }
338
339    fn clamp(&self, min: &Self, max: &Self) -> Self {
340        F64x2(self.0.max(min.0).min(max.0))
341    }
342}
343
344impl VectorTranscendental<f64, 2> for F64x2 {
345    fn sqrt(&self) -> Self {
346        F64x2(self.0.sqrt())
347    }
348    fn exp(&self) -> Self {
349        F64x2(self.0.exp())
350    }
351    fn ln(&self) -> Self {
352        F64x2(self.0.ln())
353    }
354    fn sin(&self) -> Self {
355        F64x2(self.0.sin())
356    }
357    fn cos(&self) -> Self {
358        F64x2(self.0.cos())
359    }
360    fn tan(&self) -> Self {
361        F64x2(self.0.tan())
362    }
363}
364
365// -----------------------------------------------------------------------------
366// Vector implementation for F64x4
367// -----------------------------------------------------------------------------
368
369impl Vector<f64, 4> for F64x4 {
370    fn splat(value: f64) -> Self {
371        F64x4(f64x4::splat(value))
372    }
373
374    fn load(slice: &[f64]) -> Self {
375        let mut arr = [0.0f64; 4];
376        arr.copy_from_slice(&slice[0..4]);
377        F64x4(f64x4::from(arr))
378    }
379
380    fn store(&self, slice: &mut [f64]) {
381        let arr: [f64; 4] = self.0.into();
382        slice[0..4].copy_from_slice(&arr);
383    }
384
385    fn extract(&self, index: usize) -> f64 {
386        let arr: [f64; 4] = self.0.into();
387        arr[index]
388    }
389
390    fn insert(&self, index: usize, value: f64) -> Self {
391        let mut arr: [f64; 4] = self.0.into();
392        arr[index] = value;
393        F64x4(f64x4::from(arr))
394    }
395
396    fn add(&self, other: &Self) -> Self {
397        F64x4(self.0 + other.0)
398    }
399
400    fn sub(&self, other: &Self) -> Self {
401        F64x4(self.0 - other.0)
402    }
403
404    fn mul(&self, other: &Self) -> Self {
405        F64x4(self.0 * other.0)
406    }
407
408    fn div(&self, other: &Self) -> Self {
409        F64x4(self.0 / other.0)
410    }
411
412    fn rem(&self, other: &Self) -> Self {
413        let a: [f64; 4] = self.0.into();
414        let b: [f64; 4] = other.0.into();
415        let mut arr = [0.0f64; 4];
416        for i in 0..4 {
417            arr[i] = a[i] % b[i];
418        }
419        F64x4(f64x4::from(arr))
420    }
421
422    fn neg(&self) -> Self {
423        F64x4(-self.0)
424    }
425
426    fn abs(&self) -> Self {
427        F64x4(self.0.abs())
428    }
429
430    fn min(&self, other: &Self) -> Self {
431        F64x4(self.0.min(other.0))
432    }
433
434    fn max(&self, other: &Self) -> Self {
435        F64x4(self.0.max(other.0))
436    }
437
438    fn clamp(&self, min: &Self, max: &Self) -> Self {
439        F64x4(self.0.max(min.0).min(max.0))
440    }
441}
442
443impl VectorTranscendental<f64, 4> for F64x4 {
444    fn sqrt(&self) -> Self {
445        F64x4(self.0.sqrt())
446    }
447    fn exp(&self) -> Self {
448        F64x4(self.0.exp())
449    }
450    fn ln(&self) -> Self {
451        F64x4(self.0.ln())
452    }
453    fn sin(&self) -> Self {
454        F64x4(self.0.sin())
455    }
456    fn cos(&self) -> Self {
457        F64x4(self.0.cos())
458    }
459    fn tan(&self) -> Self {
460        F64x4(self.0.tan())
461    }
462}
463
464// -----------------------------------------------------------------------------
465// VectorMask implementation for F64x4
466// -----------------------------------------------------------------------------
467
468impl VectorMask<f64, 4> for F64x4 {
469    type Mask = F64x4;
470
471    fn eq(&self, other: &Self) -> F64x4 {
472        F64x4(self.0.cmp_eq(other.0))
473    }
474    fn ne(&self, other: &Self) -> F64x4 {
475        F64x4(self.0.cmp_ne(other.0))
476    }
477    fn gt(&self, other: &Self) -> F64x4 {
478        F64x4(self.0.cmp_gt(other.0))
479    }
480    fn ge(&self, other: &Self) -> F64x4 {
481        F64x4(self.0.cmp_ge(other.0))
482    }
483    fn lt(&self, other: &Self) -> F64x4 {
484        F64x4(self.0.cmp_lt(other.0))
485    }
486    fn le(&self, other: &Self) -> F64x4 {
487        F64x4(self.0.cmp_le(other.0))
488    }
489    fn select(&self, other: &Self, mask: F64x4) -> Self {
490        F64x4(mask.0.blend(self.0, other.0))
491    }
492    fn all(mask: &F64x4) -> bool {
493        mask.0.move_mask() == 0b1111
494    }
495}
496
497// -----------------------------------------------------------------------------
498// VectorMask implementation for F64x2
499// -----------------------------------------------------------------------------
500
501impl VectorMask<f64, 2> for F64x2 {
502    type Mask = F64x2;
503
504    fn eq(&self, other: &Self) -> F64x2 {
505        F64x2(self.0.cmp_eq(other.0))
506    }
507    fn ne(&self, other: &Self) -> F64x2 {
508        F64x2(self.0.cmp_ne(other.0))
509    }
510    fn gt(&self, other: &Self) -> F64x2 {
511        F64x2(self.0.cmp_gt(other.0))
512    }
513    fn ge(&self, other: &Self) -> F64x2 {
514        F64x2(self.0.cmp_ge(other.0))
515    }
516    fn lt(&self, other: &Self) -> F64x2 {
517        F64x2(self.0.cmp_lt(other.0))
518    }
519    fn le(&self, other: &Self) -> F64x2 {
520        F64x2(self.0.cmp_le(other.0))
521    }
522    fn select(&self, other: &Self, mask: F64x2) -> Self {
523        F64x2(mask.0.blend(self.0, other.0))
524    }
525    fn all(mask: &F64x2) -> bool {
526        mask.0.move_mask() == 0b11
527    }
528}
529
530// -----------------------------------------------------------------------------
531// VectorMask implementation for F32x4
532// -----------------------------------------------------------------------------
533
534impl VectorMask<f32, 4> for F32x4 {
535    type Mask = F32x4;
536
537    fn eq(&self, other: &Self) -> F32x4 {
538        F32x4(self.0.cmp_eq(other.0))
539    }
540    fn ne(&self, other: &Self) -> F32x4 {
541        F32x4(self.0.cmp_ne(other.0))
542    }
543    fn gt(&self, other: &Self) -> F32x4 {
544        F32x4(self.0.cmp_gt(other.0))
545    }
546    fn ge(&self, other: &Self) -> F32x4 {
547        F32x4(self.0.cmp_ge(other.0))
548    }
549    fn lt(&self, other: &Self) -> F32x4 {
550        F32x4(self.0.cmp_lt(other.0))
551    }
552    fn le(&self, other: &Self) -> F32x4 {
553        F32x4(self.0.cmp_le(other.0))
554    }
555    fn select(&self, other: &Self, mask: F32x4) -> Self {
556        F32x4(mask.0.blend(self.0, other.0))
557    }
558    fn all(mask: &F32x4) -> bool {
559        mask.0.move_mask() == 0b1111
560    }
561}
562
563// -----------------------------------------------------------------------------
564// VectorMask implementation for F32x8
565// -----------------------------------------------------------------------------
566
567impl VectorMask<f32, 8> for F32x8 {
568    type Mask = F32x8;
569
570    fn eq(&self, other: &Self) -> F32x8 {
571        F32x8(self.0.cmp_eq(other.0))
572    }
573    fn ne(&self, other: &Self) -> F32x8 {
574        F32x8(self.0.cmp_ne(other.0))
575    }
576    fn gt(&self, other: &Self) -> F32x8 {
577        F32x8(self.0.cmp_gt(other.0))
578    }
579    fn ge(&self, other: &Self) -> F32x8 {
580        F32x8(self.0.cmp_ge(other.0))
581    }
582    fn lt(&self, other: &Self) -> F32x8 {
583        F32x8(self.0.cmp_lt(other.0))
584    }
585    fn le(&self, other: &Self) -> F32x8 {
586        F32x8(self.0.cmp_le(other.0))
587    }
588    fn select(&self, other: &Self, mask: F32x8) -> Self {
589        F32x8(mask.0.blend(self.0, other.0))
590    }
591    fn all(mask: &F32x8) -> bool {
592        mask.0.move_mask() == 0b1111_1111
593    }
594}
595
596// -----------------------------------------------------------------------------
597// Operator implementations (Add, Sub, Mul, Div, Rem, Neg)
598// -----------------------------------------------------------------------------
599
600impl Add for F32x4 {
601    type Output = Self;
602    fn add(self, rhs: Self) -> Self {
603        Self(self.0 + rhs.0)
604    }
605}
606
607impl Sub for F32x4 {
608    type Output = Self;
609    fn sub(self, rhs: Self) -> Self {
610        Self(self.0 - rhs.0)
611    }
612}
613
614impl Mul for F32x4 {
615    type Output = Self;
616    fn mul(self, rhs: Self) -> Self {
617        Self(self.0 * rhs.0)
618    }
619}
620
621impl Div for F32x4 {
622    type Output = Self;
623    fn div(self, rhs: Self) -> Self {
624        Self(self.0 / rhs.0)
625    }
626}
627
628impl Rem for F32x4 {
629    type Output = Self;
630    fn rem(self, rhs: Self) -> Self {
631        let a: [f32; 4] = self.0.into();
632        let b: [f32; 4] = rhs.0.into();
633        let mut arr = [0.0f32; 4];
634        for i in 0..4 {
635            arr[i] = a[i] % b[i];
636        }
637        Self(f32x4::from(arr))
638    }
639}
640
641impl Neg for F32x4 {
642    type Output = Self;
643    fn neg(self) -> Self {
644        Self(-self.0)
645    }
646}
647
648impl AddAssign for F32x4 {
649    fn add_assign(&mut self, rhs: Self) {
650        self.0 += rhs.0;
651    }
652}
653impl SubAssign for F32x4 {
654    fn sub_assign(&mut self, rhs: Self) {
655        self.0 -= rhs.0;
656    }
657}
658impl MulAssign for F32x4 {
659    fn mul_assign(&mut self, rhs: Self) {
660        self.0 *= rhs.0;
661    }
662}
663impl DivAssign for F32x4 {
664    fn div_assign(&mut self, rhs: Self) {
665        self.0 /= rhs.0;
666    }
667}
668impl RemAssign for F32x4 {
669    fn rem_assign(&mut self, rhs: Self) {
670        let a: [f32; 4] = self.0.into();
671        let b: [f32; 4] = rhs.0.into();
672        let mut arr = [0.0f32; 4];
673        for i in 0..4 {
674            arr[i] = a[i] % b[i];
675        }
676        self.0 = f32x4::from(arr);
677    }
678}
679
680// Similarly for F32x8, F64x2, F64x4
681
682impl Add for F32x8 {
683    type Output = Self;
684    fn add(self, rhs: Self) -> Self {
685        Self(self.0 + rhs.0)
686    }
687}
688
689impl Sub for F32x8 {
690    type Output = Self;
691    fn sub(self, rhs: Self) -> Self {
692        Self(self.0 - rhs.0)
693    }
694}
695
696impl Mul for F32x8 {
697    type Output = Self;
698    fn mul(self, rhs: Self) -> Self {
699        Self(self.0 * rhs.0)
700    }
701}
702
703impl Div for F32x8 {
704    type Output = Self;
705    fn div(self, rhs: Self) -> Self {
706        Self(self.0 / rhs.0)
707    }
708}
709
710impl Rem for F32x8 {
711    type Output = Self;
712    fn rem(self, rhs: Self) -> Self {
713        let a: [f32; 8] = self.0.into();
714        let b: [f32; 8] = rhs.0.into();
715        let mut arr = [0.0f32; 8];
716        for i in 0..8 {
717            arr[i] = a[i] % b[i];
718        }
719        Self(f32x8::from(arr))
720    }
721}
722
723impl Neg for F32x8 {
724    type Output = Self;
725    fn neg(self) -> Self {
726        Self(-self.0)
727    }
728}
729
730impl AddAssign for F32x8 {
731    fn add_assign(&mut self, rhs: Self) {
732        self.0 += rhs.0;
733    }
734}
735impl SubAssign for F32x8 {
736    fn sub_assign(&mut self, rhs: Self) {
737        self.0 -= rhs.0;
738    }
739}
740impl MulAssign for F32x8 {
741    fn mul_assign(&mut self, rhs: Self) {
742        self.0 *= rhs.0;
743    }
744}
745impl DivAssign for F32x8 {
746    fn div_assign(&mut self, rhs: Self) {
747        self.0 /= rhs.0;
748    }
749}
750impl RemAssign for F32x8 {
751    fn rem_assign(&mut self, rhs: Self) {
752        let a: [f32; 8] = self.0.into();
753        let b: [f32; 8] = rhs.0.into();
754        let mut arr = [0.0f32; 8];
755        for i in 0..8 {
756            arr[i] = a[i] % b[i];
757        }
758        self.0 = f32x8::from(arr);
759    }
760}
761
762impl Add for F64x2 {
763    type Output = Self;
764    fn add(self, rhs: Self) -> Self {
765        Self(self.0 + rhs.0)
766    }
767}
768
769impl Sub for F64x2 {
770    type Output = Self;
771    fn sub(self, rhs: Self) -> Self {
772        Self(self.0 - rhs.0)
773    }
774}
775
776impl Mul for F64x2 {
777    type Output = Self;
778    fn mul(self, rhs: Self) -> Self {
779        Self(self.0 * rhs.0)
780    }
781}
782
783impl Div for F64x2 {
784    type Output = Self;
785    fn div(self, rhs: Self) -> Self {
786        Self(self.0 / rhs.0)
787    }
788}
789
790impl Rem for F64x2 {
791    type Output = Self;
792    fn rem(self, rhs: Self) -> Self {
793        let a: [f64; 2] = self.0.into();
794        let b: [f64; 2] = rhs.0.into();
795        let mut arr = [0.0f64; 2];
796        for i in 0..2 {
797            arr[i] = a[i] % b[i];
798        }
799        Self(f64x2::from(arr))
800    }
801}
802
803impl Neg for F64x2 {
804    type Output = Self;
805    fn neg(self) -> Self {
806        Self(-self.0)
807    }
808}
809
810impl AddAssign for F64x2 {
811    fn add_assign(&mut self, rhs: Self) {
812        self.0 += rhs.0;
813    }
814}
815impl SubAssign for F64x2 {
816    fn sub_assign(&mut self, rhs: Self) {
817        self.0 -= rhs.0;
818    }
819}
820impl MulAssign for F64x2 {
821    fn mul_assign(&mut self, rhs: Self) {
822        self.0 *= rhs.0;
823    }
824}
825impl DivAssign for F64x2 {
826    fn div_assign(&mut self, rhs: Self) {
827        self.0 /= rhs.0;
828    }
829}
830impl RemAssign for F64x2 {
831    fn rem_assign(&mut self, rhs: Self) {
832        let a: [f64; 2] = self.0.into();
833        let b: [f64; 2] = rhs.0.into();
834        let mut arr = [0.0f64; 2];
835        for i in 0..2 {
836            arr[i] = a[i] % b[i];
837        }
838        self.0 = f64x2::from(arr);
839    }
840}
841
842impl Add for F64x4 {
843    type Output = Self;
844    fn add(self, rhs: Self) -> Self {
845        Self(self.0 + rhs.0)
846    }
847}
848
849impl Sub for F64x4 {
850    type Output = Self;
851    fn sub(self, rhs: Self) -> Self {
852        Self(self.0 - rhs.0)
853    }
854}
855
856impl Mul for F64x4 {
857    type Output = Self;
858    fn mul(self, rhs: Self) -> Self {
859        Self(self.0 * rhs.0)
860    }
861}
862
863impl Div for F64x4 {
864    type Output = Self;
865    fn div(self, rhs: Self) -> Self {
866        Self(self.0 / rhs.0)
867    }
868}
869
870impl Rem for F64x4 {
871    type Output = Self;
872    fn rem(self, rhs: Self) -> Self {
873        let a: [f64; 4] = self.0.into();
874        let b: [f64; 4] = rhs.0.into();
875        let mut arr = [0.0f64; 4];
876        for i in 0..4 {
877            arr[i] = a[i] % b[i];
878        }
879        Self(f64x4::from(arr))
880    }
881}
882
883impl Neg for F64x4 {
884    type Output = Self;
885    fn neg(self) -> Self {
886        Self(-self.0)
887    }
888}
889
890impl AddAssign for F64x4 {
891    fn add_assign(&mut self, rhs: Self) {
892        self.0 += rhs.0;
893    }
894}
895impl SubAssign for F64x4 {
896    fn sub_assign(&mut self, rhs: Self) {
897        self.0 -= rhs.0;
898    }
899}
900impl MulAssign for F64x4 {
901    fn mul_assign(&mut self, rhs: Self) {
902        self.0 *= rhs.0;
903    }
904}
905impl DivAssign for F64x4 {
906    fn div_assign(&mut self, rhs: Self) {
907        self.0 /= rhs.0;
908    }
909}
910impl RemAssign for F64x4 {
911    fn rem_assign(&mut self, rhs: Self) {
912        let a: [f64; 4] = self.0.into();
913        let b: [f64; 4] = rhs.0.into();
914        let mut arr = [0.0f64; 4];
915        for i in 0..4 {
916            arr[i] = a[i] % b[i];
917        }
918        self.0 = f64x4::from(arr);
919    }
920}
921
922// -----------------------------------------------------------------------------
923// Unit tests
924// -----------------------------------------------------------------------------
925
926#[cfg(test)]
927mod tests {
928    use super::*;
929    use crate::math::vector::traits::VectorMask;
930
931    #[test]
932    fn test_f32x4_basic() {
933        let a = F32x4::load(&[1.0, 2.0, 3.0, 4.0]);
934        let b = F32x4::load(&[5.0, 6.0, 7.0, 8.0]);
935
936        let c = a + b;
937        let mut arr = [0.0f32; 4];
938        c.store(&mut arr);
939        assert_eq!(arr, [6.0, 8.0, 10.0, 12.0]);
940
941        let c = a * b;
942        c.store(&mut arr);
943        assert_eq!(arr, [5.0, 12.0, 21.0, 32.0]);
944    }
945
946    #[test]
947    fn test_f32x4_math() {
948        let a = F32x4::load(&[0.0, 0.5, 1.0, 2.0]);
949        let sin_a = a.sin();
950        let mut arr = [0.0f32; 4];
951        sin_a.store(&mut arr);
952        let expected = [0.0f32.sin(), 0.5f32.sin(), 1.0f32.sin(), 2.0f32.sin()];
953        for i in 0..4 {
954            assert!((arr[i] - expected[i]).abs() < 1e-5);
955        }
956    }
957
958    #[test]
959    fn test_f64x2_basic() {
960        let a = F64x2::load(&[1.0, 2.0]);
961        let b = F64x2::load(&[3.0, 4.0]);
962
963        let c = a + b;
964        let mut arr = [0.0f64; 2];
965        c.store(&mut arr);
966        assert_eq!(arr, [4.0, 6.0]);
967    }
968
969    #[test]
970    fn test_f64x4_basic() {
971        let a = F64x4::load(&[1.0, 2.0, 3.0, 4.0]);
972        let b = F64x4::load(&[5.0, 6.0, 7.0, 8.0]);
973
974        let c = a + b;
975        let mut arr = [0.0f64; 4];
976        c.store(&mut arr);
977        assert_eq!(arr, [6.0, 8.0, 10.0, 12.0]);
978
979        let c = a * b;
980        c.store(&mut arr);
981        assert_eq!(arr, [5.0, 12.0, 21.0, 32.0]);
982    }
983
984    #[test]
985    fn test_f64x4_math() {
986        let a = F64x4::load(&[0.0, 0.5, 1.0, 2.0]);
987        let sqrt_a = a.sqrt();
988        let mut arr = [0.0f64; 4];
989        sqrt_a.store(&mut arr);
990        let expected = [0.0f64.sqrt(), 0.5f64.sqrt(), 1.0f64.sqrt(), 2.0f64.sqrt()];
991        for i in 0..4 {
992            assert!((arr[i] - expected[i]).abs() < 1e-12);
993        }
994
995        let exp_a = a.exp();
996        exp_a.store(&mut arr);
997        let expected = [0.0f64.exp(), 0.5f64.exp(), 1.0f64.exp(), 2.0f64.exp()];
998        for i in 0..4 {
999            assert!((arr[i] - expected[i]).abs() < 1e-12);
1000        }
1001    }
1002
1003    #[test]
1004    fn test_f64x4_vector_mask_lt() {
1005        // wide 0.7 returns mask with from_bits(u64::MAX) = NaN for true, 0.0 for false
1006        // Use move_mask to check bits
1007        let a = F64x4::load(&[1.0, 2.0, 3.0, 4.0]);
1008        let b = F64x4::load(&[3.0, 3.0, 3.0, 3.0]);
1009        let mask = <F64x4 as VectorMask<f64, 4>>::lt(&a, &b);
1010        // move_mask extracts sign bit of each lane
1011        assert_eq!(mask.0.move_mask() & 0b1111, 0b0011); // lanes 0,1 true
1012    }
1013
1014    #[test]
1015    fn test_f64x4_vector_mask_gt() {
1016        let a = F64x4::load(&[1.0, 2.0, 3.0, 4.0]);
1017        let b = F64x4::load(&[2.0, 2.0, 2.0, 2.0]);
1018        let mask = <F64x4 as VectorMask<f64, 4>>::gt(&a, &b);
1019        assert_eq!(mask.0.move_mask() & 0b1111, 0b1100); // lanes 2,3 true
1020    }
1021
1022    #[test]
1023    fn test_f64x4_vector_mask_eq() {
1024        let a = F64x4::load(&[1.0, 2.0, 3.0, 4.0]);
1025        let b = F64x4::load(&[1.0, 0.0, 3.0, 5.0]);
1026        let mask = <F64x4 as VectorMask<f64, 4>>::eq(&a, &b);
1027        assert_eq!(mask.0.move_mask() & 0b1111, 0b0101); // lanes 0,2 true
1028    }
1029
1030    #[test]
1031    fn test_f64x4_vector_mask_all() {
1032        let all_true = <F64x4 as VectorMask<f64, 4>>::lt(&F64x4::splat(1.0), &F64x4::splat(2.0));
1033        assert!(<F64x4 as VectorMask<f64, 4>>::all(&all_true));
1034
1035        let partial_true = <F64x4 as VectorMask<f64, 4>>::lt(
1036            &F64x4::load(&[1.0, 2.0, 3.0, 4.0]),
1037            &F64x4::splat(3.0),
1038        );
1039        assert!(!<F64x4 as VectorMask<f64, 4>>::all(&partial_true));
1040    }
1041
1042    #[test]
1043    fn test_f64x4_vector_mask_select() {
1044        let true_vals = F64x4::load(&[10.0, 20.0, 30.0, 40.0]);
1045        let false_vals = F64x4::load(&[1.0, 2.0, 3.0, 4.0]);
1046        // mask: true where true_vals < 25
1047        let threshold = F64x4::load(&[5.0, 25.0, 25.0, 25.0]);
1048        let mask = <F64x4 as VectorMask<f64, 4>>::lt(&true_vals, &threshold);
1049        let selected = <F64x4 as VectorMask<f64, 4>>::select(&true_vals, &false_vals, mask);
1050        // lanes 0 true (10 < 5? No — 10 < 5 false, so lane 0 is false)
1051
1052        // Actually: a = [10, 20, 30, 40], threshold = [5, 25, 25, 25]
1053        // a < threshold: [false, true, false, false]
1054        assert_eq!(mask.0.move_mask() & 0b1111, 0b0010);
1055        // select: only lane 1 takes from true_vals (20)
1056        let mut arr = [0.0; 4];
1057        selected.store(&mut arr);
1058        assert!((arr[0] - 1.0).abs() < 1e-15);
1059        assert!((arr[1] - 20.0).abs() < 1e-15);
1060        assert!((arr[2] - 3.0).abs() < 1e-15);
1061        assert!((arr[3] - 4.0).abs() < 1e-15);
1062    }
1063}