1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
use crate::types::NativeType;

/// [`NativeType`] that supports a representation of 8 lanes
pub trait Simd8: NativeType {
    /// The 8 lane representation of `Self`
    type Simd: Simd8Lanes<Self>;
}

/// Trait declaring an 8-lane multi-data.
pub trait Simd8Lanes<T>: Copy {
    /// loads a complete chunk
    fn from_chunk(v: &[T]) -> Self;
    /// loads an incomplete chunk, filling the remaining items with `remaining`.
    fn from_incomplete_chunk(v: &[T], remaining: T) -> Self;
}

/// Trait implemented by implementors of [`Simd8Lanes`] whose [`Simd8`] implements [PartialEq].
pub trait Simd8PartialEq: Copy {
    /// Equal
    fn eq(self, other: Self) -> u8;
    /// Not equal
    fn neq(self, other: Self) -> u8;
}

/// Trait implemented by implementors of [`Simd8Lanes`] whose [`Simd8`] implements [PartialOrd].
pub trait Simd8PartialOrd: Copy {
    /// Less than or equal to
    fn lt_eq(self, other: Self) -> u8;
    /// Less than
    fn lt(self, other: Self) -> u8;
    /// Greater than
    fn gt(self, other: Self) -> u8;
    /// Greater than or equal to
    fn gt_eq(self, other: Self) -> u8;
}

#[inline]
pub(super) fn set<T: Copy, F: Fn(T, T) -> bool>(lhs: [T; 8], rhs: [T; 8], op: F) -> u8 {
    let mut byte = 0u8;
    lhs.iter()
        .zip(rhs.iter())
        .enumerate()
        .for_each(|(i, (lhs, rhs))| {
            byte |= if op(*lhs, *rhs) { 1 << i } else { 0 };
        });
    byte
}

/// Types that implement Simd8
macro_rules! simd8_native {
    ($type:ty) => {
        impl Simd8 for $type {
            type Simd = [$type; 8];
        }

        impl Simd8Lanes<$type> for [$type; 8] {
            #[inline]
            fn from_chunk(v: &[$type]) -> Self {
                v.try_into().unwrap()
            }

            #[inline]
            fn from_incomplete_chunk(v: &[$type], remaining: $type) -> Self {
                let mut a = [remaining; 8];
                a.iter_mut().zip(v.iter()).for_each(|(a, b)| *a = *b);
                a
            }
        }
    };
}

/// Types that implement PartialEq
macro_rules! simd8_native_partial_eq {
    ($type:ty) => {
        impl Simd8PartialEq for [$type; 8] {
            #[inline]
            fn eq(self, other: Self) -> u8 {
                set(self, other, |x, y| x == y)
            }

            #[inline]
            fn neq(self, other: Self) -> u8 {
                #[allow(clippy::float_cmp)]
                set(self, other, |x, y| x != y)
            }
        }
    };
}

/// Types that implement PartialOrd
macro_rules! simd8_native_partial_ord {
    ($type:ty) => {
        impl Simd8PartialOrd for [$type; 8] {
            #[inline]
            fn lt_eq(self, other: Self) -> u8 {
                set(self, other, |x, y| x <= y)
            }

            #[inline]
            fn lt(self, other: Self) -> u8 {
                set(self, other, |x, y| x < y)
            }

            #[inline]
            fn gt_eq(self, other: Self) -> u8 {
                set(self, other, |x, y| x >= y)
            }

            #[inline]
            fn gt(self, other: Self) -> u8 {
                set(self, other, |x, y| x > y)
            }
        }
    };
}

/// Types that implement simd8, PartialEq and PartialOrd
macro_rules! simd8_native_all {
    ($type:ty) => {
        simd8_native! {$type}
        simd8_native_partial_eq! {$type}
        simd8_native_partial_ord! {$type}
    };
}

#[cfg(not(feature = "simd"))]
mod native;
#[cfg(not(feature = "simd"))]
pub use native::*;
#[cfg(feature = "simd")]
mod packed;
#[cfg(feature = "simd")]
pub use packed::*;