vrl/
vec8f.rs

1use std::{
2    fmt::Debug,
3    mem::MaybeUninit,
4    ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign},
5};
6
7use crate::{
8    common::SIMDVector,
9    macros::{vec_impl_sum_prod, vec_overload_operator},
10    Vec4f,
11};
12
13#[cfg(avx)]
14use crate::intrinsics::*;
15
16#[cfg(no_avx)]
17use derive_more::{Add, Div, Mul, Sub};
18
19/// Represents a packed vector of 8 single-precision floating-point values.
20///
21/// On platforms with AVX support [`Vec8f`] is a [`__m256`] wrapper. Otherwise it is a pair of
22/// [`Vec4f`] values.
23#[derive(Clone, Copy)]
24#[cfg_attr(no_avx, derive(Add, Sub, Mul, Div), mul(forward), div(forward))]
25#[cfg_attr(avx, repr(transparent))]
26#[cfg_attr(no_avx, repr(C))] // [repr(C)] guarantees fields ordering and paddinglessness.
27pub struct Vec8f {
28    #[cfg(avx)]
29    ymm: __m256,
30
31    #[cfg(no_avx)]
32    _low: Vec4f,
33    #[cfg(no_avx)]
34    _high: Vec4f,
35}
36
37impl Vec8f {
38    /// Initializes elements of returned vector with given values.
39    ///
40    /// # Example
41    /// ```
42    /// # use vrl::Vec8f;
43    /// assert_eq!(
44    ///     Vec8f::new(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0),
45    ///     [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0].into()
46    /// );
47    /// ```
48    #[inline(always)]
49    #[allow(clippy::too_many_arguments)]
50    pub fn new(v0: f32, v1: f32, v2: f32, v3: f32, v4: f32, v5: f32, v6: f32, v7: f32) -> Self {
51        #[cfg(avx)]
52        {
53            unsafe { _mm256_setr_ps(v0, v1, v2, v3, v4, v5, v6, v7) }.into()
54        }
55
56        #[cfg(no_avx)]
57        {
58            (Vec4f::new(v0, v1, v2, v3), Vec4f::new(v4, v5, v6, v7)).into()
59        }
60    }
61
62    /// Joins two [`Vec4f`] into a single [`Vec8f`]. The first four elements of returned vector are
63    /// elements of `a` and the last four elements are elements of `b`.
64    ///
65    /// See also [`split`](Self::split).
66    ///
67    /// # Exmaple
68    /// ```
69    /// # use vrl::{Vec4f, Vec8f};
70    /// let a = Vec4f::new(1.0, 2.0, 3.0, 4.0);
71    /// let b = Vec4f::new(5.0, 6.0, 7.0, 8.0);
72    /// let joined = Vec8f::join(a, b);
73    /// assert_eq!(a, joined.low());
74    /// assert_eq!(b, joined.high());
75    /// assert_eq!(joined.split(), (a, b));
76    /// ```
77    #[inline(always)]
78    pub fn join(a: Vec4f, b: Vec4f) -> Self {
79        #[cfg(avx)]
80        {
81            unsafe { _mm256_set_m128(b.into(), a.into()) }.into()
82        }
83
84        #[cfg(no_avx)]
85        {
86            Self { _low: a, _high: b }
87        }
88    }
89
90    /// Loads vector from array pointer by `addr`.
91    /// `addr` is not required to be aligned.
92    ///
93    /// # Safety
94    /// `addr` must be a valid pointer.
95    ///
96    /// # Example
97    /// ```
98    /// # use vrl::Vec8f;
99    /// let array = [42.0; 8];
100    /// let vec = unsafe { Vec8f::load_ptr(&array) };
101    /// ```
102    #[inline(always)]
103    pub unsafe fn load_ptr(addr: *const [f32; 8]) -> Self {
104        #[cfg(avx)]
105        {
106            _mm256_loadu_ps(addr as *const f32).into()
107        }
108
109        #[cfg(no_avx)]
110        {
111            let addr = addr as *const [f32; 4];
112            (Vec4f::load_ptr(addr), Vec4f::load_ptr(addr.add(1))).into()
113        }
114    }
115
116    /// Loads vector from aligned array pointed by `addr`.
117    ///
118    /// # Safety
119    /// Like [`load_ptr`], requires `addr` to be valid.
120    /// Unlike [`load_ptr`], requires `addr` to be divisible by `32`, i.e. to be a `32`-bytes aligned address.
121    ///
122    /// [`load_ptr`]: Self::load_ptr
123    ///
124    /// # Examples
125    /// ```
126    /// # use vrl::Vec8f;
127    /// #[repr(align(32))]
128    /// struct AlignedArray([f32; 8]);
129    ///
130    /// let array = AlignedArray([42.0; 8]);
131    /// let vec = unsafe { Vec8f::load_ptr_aligned(&array.0) };
132    /// assert_eq!(vec, Vec8f::broadcast(42.0));
133    /// ```
134    /// In the following example `zeros` is aligned as `u16`, i.e. 2-bytes aligned.
135    /// Therefore `zeros.as_ptr().byte_add(1)` is an odd address and hence not divisible by `32`.
136    /// ```should_panic
137    /// # use vrl::Vec8f;
138    /// let zeros = unsafe { std::mem::zeroed::<[u16; 20]>() };
139    /// unsafe { Vec8f::load_ptr_aligned(zeros.as_ptr().byte_add(1) as *const [f32; 8]) };
140    /// ```
141    #[inline(always)]
142    pub unsafe fn load_ptr_aligned(addr: *const [f32; 8]) -> Self {
143        #[cfg(avx)]
144        {
145            _mm256_load_ps(addr as *const f32).into()
146        }
147
148        #[cfg(no_avx)]
149        {
150            let addr = addr as *const [f32; 4];
151            (
152                Vec4f::load_ptr_aligned(addr),
153                Vec4f::load_ptr_aligned(addr.add(1)),
154            )
155                .into()
156        }
157    }
158
159    /// Loads values of returned vector from given data.
160    ///
161    /// # Exmaple
162    /// ```
163    /// # use vrl::Vec8f;
164    /// assert_eq!(
165    ///     Vec8f::new(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0),
166    ///     Vec8f::load(&[1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0])
167    /// );
168    /// ```
169    #[inline(always)]
170    pub fn load(data: &[f32; 8]) -> Self {
171        unsafe { Self::load_ptr(data) }
172    }
173
174    /// Checks that data contains exactly eight elements and loads them into vector.
175    ///
176    /// # Panics
177    /// Panics if `data.len()` isn't `8`.
178    ///
179    /// # Examples
180    /// ```
181    /// # use vrl::Vec8f;
182    /// assert_eq!(
183    ///     Vec8f::load_checked(&[1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0]),
184    ///     Vec8f::new(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0)
185    /// );
186    /// ```
187    /// ```should_panic
188    /// # use vrl::Vec8f;
189    /// Vec8f::load_checked(&[1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0]);
190    /// ```
191    /// ```should_panic
192    /// # use vrl::Vec8f;
193    /// Vec8f::load_checked(&[1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]);
194    /// ```
195    #[inline(always)]
196    pub fn load_checked(data: &[f32]) -> Self {
197        Self::load(
198            data.try_into()
199                .expect("data must contain exactly 8 elements"),
200        )
201    }
202
203    /// Loads the first eight element of `data` into vector.
204    ///
205    /// # Panics
206    /// Panics if `data` contains less than eight elements.
207    ///
208    /// # Exmaples
209    /// ```
210    /// # use vrl::Vec8f;
211    /// assert_eq!(
212    ///     Vec8f::load_prefix(&[1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]),
213    ///     Vec8f::new(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0)
214    /// );
215    /// ```
216    ///
217    /// ```should_panic
218    /// # use vrl::Vec8f;
219    /// Vec8f::load_prefix(&[1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0]);
220    /// ```
221    #[inline(always)]
222    pub fn load_prefix(data: &[f32]) -> Self {
223        if data.len() < 8 {
224            panic!("data must contain at least 8 elements");
225        }
226        unsafe { Self::load_ptr(data.as_ptr() as *const [f32; 8]) }
227    }
228
229    /// Loads first 8 elements of `data` if available otherwise initializes first elements of
230    /// returned vector with values of `data` and rest elements with zeros.
231    ///
232    /// # Exmaple
233    /// ```
234    /// # use vrl::Vec8f;
235    /// let values = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0];
236    /// assert_eq!(
237    ///     Vec8f::load_partial(&values),
238    ///     Vec8f::from(&values[..8].try_into().unwrap())
239    /// );
240    /// assert_eq!(
241    ///     Vec8f::load_partial(&values[..5]),
242    ///     Vec8f::new(1.0, 2.0, 3.0, 4.0, 5.0, 0.0, 0.0, 0.0)  // note zeros here
243    /// );
244    /// ```
245    #[inline]
246    pub fn load_partial(data: &[f32]) -> Self {
247        match data.len() {
248            8.. => unsafe { Self::load_ptr(data.as_ptr() as *const [f32; 8]) },
249            4.. => Self::join(
250                unsafe { Vec4f::load_ptr(data.as_ptr() as *const [f32; 4]) },
251                Vec4f::load_partial(data.split_at(4).1),
252            ),
253            0.. => Self::join(Vec4f::load_partial(data), Vec4f::default()),
254        }
255    }
256
257    /// Returns vector with all its elements initialized with a given `value`, i.e. broadcasts
258    /// `value` to all elements of returned vector.
259    ///
260    /// # Example
261    /// ```
262    /// # use vrl::Vec8f;
263    /// assert_eq!(
264    ///     Vec8f::broadcast(42.0),
265    ///     [42.0; 8].into()
266    /// );
267    /// ```
268    #[inline(always)]
269    pub fn broadcast(value: f32) -> Self {
270        #[cfg(avx)]
271        {
272            unsafe { _mm256_set1_ps(value) }.into()
273        }
274
275        #[cfg(no_avx)]
276        {
277            let half = Vec4f::broadcast(value);
278            (half, half).into()
279        }
280    }
281
282    /// Stores vector into array at given address.
283    ///
284    /// # Safety
285    /// `addr` must be a valid pointer.
286    #[inline(always)]
287    pub unsafe fn store_ptr(&self, addr: *mut [f32; 8]) {
288        #[cfg(avx)]
289        {
290            _mm256_storeu_ps(addr as *mut f32, self.ymm);
291        }
292
293        #[cfg(no_avx)]
294        {
295            let addr = addr as *mut [f32; 4];
296            self.low().store_ptr(addr);
297            self.high().store_ptr(addr.add(1));
298        }
299    }
300
301    /// Stores vector into aligned array at given address.
302    ///
303    /// # Safety
304    /// Like [`store_ptr`], requires `addr` to be valid.
305    /// Unlike [`store_ptr`], requires `addr` to be divisible by `32`, i.e. to be a 32-bytes aligned address.
306    ///
307    /// [`store_ptr`]: Self::store_ptr
308    #[inline(always)]
309    pub unsafe fn store_ptr_aligned(&self, addr: *mut [f32; 8]) {
310        #[cfg(avx)]
311        {
312            _mm256_store_ps(addr as *mut f32, self.ymm);
313        }
314
315        #[cfg(no_avx)]
316        {
317            let addr = addr as *mut [f32; 4];
318            self.low().store_ptr_aligned(addr);
319            self.high().store_ptr_aligned(addr.add(1));
320        }
321    }
322
323    /// Stores vector into aligned array at given address in uncached memory (non-temporal store).
324    /// This may be more efficient than [`store_ptr_aligned`] if it is unlikely that stored data will
325    /// stay in cache until it is read again, for instance, when storing large blocks of memory.
326    ///
327    /// # Safety
328    /// Has same requirements as [`store_ptr_aligned`]: `addr` must be valid and
329    /// divisible by `32`, i.e. to be a 32-bytes aligned address.
330    ///
331    /// [`store_ptr_aligned`]: Self::store_ptr_aligned
332    #[inline(always)]
333    pub unsafe fn store_ptr_non_temporal(&self, addr: *mut [f32; 8]) {
334        #[cfg(avx)]
335        {
336            _mm256_stream_ps(addr as *mut f32, self.ymm)
337        }
338
339        #[cfg(no_avx)]
340        {
341            let addr = addr as *mut [f32; 4];
342            self.low().store_ptr_non_temporal(addr);
343            self.high().store_ptr_non_temporal(addr.add(1));
344        }
345    }
346
347    /// Stores vector into given `array`.
348    #[inline(always)]
349    pub fn store(&self, array: &mut [f32; 8]) {
350        unsafe { self.store_ptr(array) }
351    }
352
353    /// Checkes that `slice` contains exactly eight elements and store elements of vector there.
354    ///
355    /// # Panics
356    /// Panics if `slice.len()` isn't `8`.
357    ///
358    /// # Examples
359    /// ```
360    /// # use vrl::Vec8f;
361    /// let mut data = [-1.0; 8];
362    /// Vec8f::default().store_checked(&mut data);
363    /// assert_eq!(data, [0.0; 8]);
364    /// ```
365    /// ```should_panic
366    /// # use vrl::Vec8f;
367    /// let mut data = [-1.0; 7];
368    /// Vec8f::default().store_checked(&mut data);
369    /// ```
370    /// ```should_panic
371    /// # use vrl::Vec8f;
372    /// let mut data = [-1.0; 9];
373    /// Vec8f::default().store_checked(&mut data);
374    /// ```
375    #[inline]
376    pub fn store_checked(&self, slice: &mut [f32]) {
377        self.store(
378            slice
379                .try_into()
380                .expect("slice must contain at least 8 elements"),
381        )
382    }
383
384    /// Stores elements of vector into the first eight elements of `slice`.
385    ///
386    /// # Panics
387    /// Panics if `slice` contains less then eight elements.
388    ///
389    /// # Exmaples
390    /// ```
391    /// # use vrl::Vec8f;
392    /// let mut data = [-1.0; 9];
393    /// Vec8f::broadcast(2.0).store_prefix(&mut data);
394    /// assert_eq!(data, [2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, -1.0]);
395    /// ```
396    /// ```should_panic
397    /// # use vrl::Vec8f;
398    /// let mut data = [-1.0; 7];
399    /// Vec8f::default().store_prefix(&mut data);
400    /// ```
401    #[inline(always)]
402    pub fn store_prefix(&self, slice: &mut [f32]) {
403        if slice.len() < 8 {
404            panic!("slice.len() must at least 8");
405        }
406        unsafe { self.store_ptr(slice.as_ptr() as *mut [f32; 8]) };
407    }
408
409    /// Stores `min(8, slice.len())` elements of vector into prefix of `slice`.
410    ///
411    /// # Examples
412    /// ```
413    /// # use vrl::Vec8f;
414    /// let mut data = [0.0; 7];
415    /// Vec8f::broadcast(1.0).store_partial(&mut data);
416    /// assert_eq!(data, [1.0; 7]);
417    /// ```
418    /// ```
419    /// # use vrl::Vec8f;
420    /// let mut data = [0.0; 9];
421    /// Vec8f::broadcast(1.0).store_partial(&mut data);
422    /// assert_eq!(data, [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0]);  // note last zero
423    /// ```
424    #[inline]
425    pub fn store_partial(&self, slice: &mut [f32]) {
426        match slice.len() {
427            8.. => unsafe { self.store_ptr(slice.as_mut_ptr() as *mut [f32; 8]) },
428            4.. => {
429                unsafe { self.low().store_ptr(slice.as_mut_ptr() as *mut [f32; 4]) };
430                self.high().store_partial(slice.split_at_mut(4).1)
431            }
432            0.. => self.low().store_partial(slice),
433        }
434    }
435
436    /// Calculates the sum of all elements of vector.
437    ///
438    /// # Exmaple
439    /// ```
440    /// # use vrl::Vec8f;
441    /// let vec = Vec8f::new(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0);
442    /// assert_eq!(vec.sum(), 36.0);
443    /// ```
444    #[inline(always)]
445    pub fn sum(self) -> f32 {
446        (self.low() + self.high()).sum()
447    }
448
449    /// Returns the first four elements of vector.
450    ///
451    /// # Exmaple
452    /// ```
453    /// # use vrl::{Vec4f, Vec8f};
454    /// let vec8 = Vec8f::new(1.0, 1.0, 1.0, 1.0, 2.0, 2.0, 2.0, 2.0);
455    /// assert_eq!(vec8.low(), Vec4f::broadcast(1.0));
456    /// ```
457    #[inline(always)]
458    pub fn low(self) -> Vec4f {
459        #[cfg(avx)]
460        {
461            unsafe { _mm256_castps256_ps128(self.ymm) }.into()
462        }
463
464        #[cfg(no_avx)]
465        {
466            self._low
467        }
468    }
469
470    /// Returns the last four elements of vector.
471    ///
472    /// # Exmaple
473    /// ```
474    /// # use vrl::{Vec4f, Vec8f};
475    /// let vec8 = Vec8f::new(1.0, 1.0, 1.0, 1.0, 2.0, 2.0, 2.0, 2.0);
476    /// assert_eq!(vec8.high(), Vec4f::broadcast(2.0));
477    /// ```
478    #[inline(always)]
479    pub fn high(self) -> Vec4f {
480        #[cfg(avx)]
481        {
482            unsafe { _mm256_extractf128_ps(self.ymm, 1) }.into()
483        }
484
485        #[cfg(no_avx)]
486        {
487            self._high
488        }
489    }
490
491    /// Splits vector into low and high halfs.
492    ///
493    /// See also [`join`](Self::join).
494    ///
495    /// # Example
496    /// ```
497    /// # use::vrl::{Vec4f, Vec8f};
498    /// let vec = Vec8f::new(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0);
499    /// let (low, high) = vec.split();
500    /// assert_eq!(low, vec.low());
501    /// assert_eq!(high, vec.high());
502    /// assert_eq!(Vec8f::join(low, high), vec);
503    /// ```
504    #[inline(always)]
505    pub fn split(self) -> (Vec4f, Vec4f) {
506        (self.low(), self.high())
507    }
508}
509
510impl SIMDVector for Vec8f {
511    #[cfg(avx)]
512    type Underlying = __m256;
513    #[cfg(no_avx)]
514    type Underlying = (Vec4f, Vec4f);
515
516    type Element = f32;
517    const ELEMENTS: usize = 8;
518}
519
520impl Default for Vec8f {
521    /// Initializes all elements of returned vector with zero.
522    ///
523    /// # Example
524    /// ```
525    /// # use vrl::Vec8f;
526    /// assert_eq!(Vec8f::default(), Vec8f::broadcast(0.0));
527    /// ```
528    #[inline(always)]
529    fn default() -> Self {
530        #[cfg(avx)]
531        {
532            unsafe { _mm256_setzero_ps() }.into()
533        }
534
535        #[cfg(no_avx)]
536        {
537            (Vec4f::default(), Vec4f::default()).into()
538        }
539    }
540}
541
542impl Neg for Vec8f {
543    type Output = Self;
544
545    /// Flips sign bit of each element including non-finite ones.
546    #[inline(always)]
547    fn neg(self) -> Self::Output {
548        #[cfg(avx)]
549        {
550            unsafe { _mm256_xor_ps(self.ymm, _mm256_set1_ps(-0f32)) }.into()
551        }
552
553        #[cfg(no_avx)]
554        {
555            (-self.low(), -self.high()).into()
556        }
557    }
558}
559
560vec_overload_operator!(Vec8f, Add, add, _mm256_add_ps, avx);
561vec_overload_operator!(Vec8f, Sub, sub, _mm256_sub_ps, avx);
562vec_overload_operator!(Vec8f, Mul, mul, _mm256_mul_ps, avx);
563vec_overload_operator!(Vec8f, Div, div, _mm256_div_ps, avx);
564vec_impl_sum_prod!(Vec8f);
565
566#[cfg(avx)]
567impl From<__m256> for Vec8f {
568    /// Wraps given `value` into [`Vec8f`].
569    #[inline(always)]
570    fn from(value: __m256) -> Self {
571        Self { ymm: value }
572    }
573}
574
575#[cfg(avx)]
576impl From<Vec8f> for __m256 {
577    /// Unwraps given vector into raw [`__m256`] value.
578    #[inline(always)]
579    fn from(value: Vec8f) -> Self {
580        value.ymm
581    }
582}
583
584impl From<&[f32; 8]> for Vec8f {
585    /// Does same as [`load`](Self::load).
586    #[inline(always)]
587    fn from(value: &[f32; 8]) -> Self {
588        Self::load(value)
589    }
590}
591
592impl From<[f32; 8]> for Vec8f {
593    #[inline(always)]
594    fn from(value: [f32; 8]) -> Self {
595        (&value).into()
596    }
597}
598
599impl From<Vec8f> for [f32; 8] {
600    #[inline(always)]
601    fn from(value: Vec8f) -> Self {
602        let mut result = MaybeUninit::<Self>::uninit();
603        unsafe {
604            value.store_ptr(result.as_mut_ptr());
605            result.assume_init()
606        }
607    }
608}
609
610impl From<&Vec8f> for [f32; 8] {
611    #[inline(always)]
612    fn from(value: &Vec8f) -> Self {
613        unsafe { *(value as *const Vec8f as *const [f32; 8]) }
614    }
615}
616
617impl From<(Vec4f, Vec4f)> for Vec8f {
618    /// Does same as [`join`](Self::join).
619    #[inline(always)]
620    fn from((low, high): (Vec4f, Vec4f)) -> Self {
621        Self::join(low, high)
622    }
623}
624
625impl From<Vec8f> for (Vec4f, Vec4f) {
626    /// Does same as [`split`](Vec8f::split).
627    #[inline(always)]
628    fn from(vec: Vec8f) -> (Vec4f, Vec4f) {
629        vec.split()
630    }
631}
632
633impl PartialEq for Vec8f {
634    /// Checks whether all elements of vectors are equal.
635    ///
636    /// __Note__: when [`NaN`](`f32::NAN`) is an element of one of the operands the result is always `false`.
637    ///
638    /// # Examples
639    /// ```
640    /// # use vrl::Vec8f;
641    /// let a = Vec8f::new(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0);
642    /// assert_eq!(a, a);
643    /// ```
644    ///
645    /// ```
646    /// # use vrl::Vec8f;
647    /// let a = Vec8f::broadcast(f32::NAN);
648    /// assert_ne!(a, a);
649    /// ```
650    #[inline(always)]
651    fn eq(&self, other: &Self) -> bool {
652        #[cfg(avx)]
653        {
654            unsafe {
655                let cmp_result = _mm256_cmp_ps::<0>(self.ymm, other.ymm);
656                _mm256_testz_ps(cmp_result, cmp_result) == 0
657            }
658        }
659
660        #[cfg(no_avx)]
661        {
662            self.split() == other.split()
663        }
664    }
665}
666
667impl Debug for Vec8f {
668    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
669        let mut debug_tuple = f.debug_tuple("Vec8f");
670        for value in <[f32; 8]>::from(self) {
671            debug_tuple.field(&value);
672        }
673        debug_tuple.finish()
674    }
675}
676
677#[cfg(test)]
678mod tests {
679    use crate::Vec8f;
680
681    #[test]
682    #[inline(never)] // in order to find the function in disassembled binary
683    fn it_works() {
684        let a = Vec8f::broadcast(1.0);
685        assert_eq!(<[f32; 8]>::from(a), [1.0; 8]);
686        assert_eq!(a, [1.0; 8].into());
687
688        let b = 2.0 * a;
689        assert_ne!(a, b);
690
691        let mut c = b / 2.0;
692        assert_eq!(a, c);
693
694        c += Vec8f::from(&[1.0, 0.0, 2.0, 0.0, 3.0, 0.0, 4.0, 0.0]);
695        let d = -c;
696
697        const EXPECTED_D: [f32; 8] = [-2.0, -1.0, -3.0, -1.0, -4.0, -1.0, -5.0, -1.0];
698        assert_eq!(d, EXPECTED_D.into());
699        assert_eq!(<[f32; 8]>::from(d), EXPECTED_D);
700    }
701
702    #[test]
703    fn test_load_partial() {
704        const VALUES: &[f32] = &[1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0];
705        for i in 0..8 {
706            let vec_values = <[f32; 8]>::from(Vec8f::load_partial(&VALUES[..i]));
707            assert_eq!(vec_values[..i], VALUES[..i]);
708            assert!(vec_values[i..].iter().all(|x| *x == 0.0));
709        }
710        assert_eq!(
711            Vec8f::load_partial(VALUES),
712            Vec8f::from(&VALUES[..8].try_into().unwrap())
713        );
714    }
715
716    #[cfg(avx)]
717    #[test]
718    fn test_m256_conv() {
719        use crate::{intrinsics::__m256, Vec4f};
720        let vec = Vec8f::join(Vec4f::broadcast(1.0), Vec4f::broadcast(2.0));
721        assert_eq!(vec, __m256::from(vec).into());
722    }
723}