1use super::*;
2
3pick! {
4 if #[cfg(target_feature="avx2")] {
5 #[derive(Default, Clone, Copy, PartialEq, Eq)]
12 #[repr(C, align(32))]
13 pub struct i64x4 { pub(crate) avx2: m256i }
14 } else {
15 #[derive(Default, Clone, Copy, PartialEq, Eq)]
22 #[repr(C, align(32))]
23 pub struct i64x4 { pub(crate) a : i64x2, pub(crate) b : i64x2 }
24 }
25}
26
27impl_simd_int! {
28 unsafe {
29 T = i64,
30 N = 4,
31 Simd = i64x4,
32 UintSimd = u64x4,
33 T_BITS = 64,
34 T_BITS_MUL_2 = 128,
35 BitmaskType = u32,
36 [0, 1, 2, 3],
37 optional_type_x86_inner { X86Inner = __m256i },
38 optional_type_arm_inner {},
39 optional_type_wasm_inner {},
40 }
41
42 #[inline]
43 fn simd_lt(self, rhs: Self) -> Self::Output {
44 pick! {
45 if #[cfg(target_feature="avx2")] {
46 Self { avx2: !(cmp_gt_mask_i64_m256i(self.avx2, rhs.avx2) ^ cmp_eq_mask_i64_m256i(self.avx2, rhs.avx2)) }
47 } else {
48 Self {
49 a : self.a.simd_lt(rhs.a),
50 b : self.b.simd_lt(rhs.b),
51 }
52 }
53 }
54 }
55
56 #[inline]
57 fn simd_gt(self, rhs: Self) -> Self::Output {
58 pick! {
59 if #[cfg(target_feature="avx2")] {
60 Self { avx2: cmp_gt_mask_i64_m256i(self.avx2, rhs.avx2) }
61 } else {
62 Self {
63 a : self.a.simd_gt(rhs.a),
64 b : self.b.simd_gt(rhs.b),
65 }
66 }
67 }
68 }
69
70 #[inline]
71 fn simd_le(self, rhs: Self) -> Self::Output {
72 pick! {
73 if #[cfg(target_feature="avx2")] {
74 !self.simd_gt(rhs)
75 } else {
76 Self {
77 a : self.a.simd_le(rhs.a),
78 b : self.b.simd_le(rhs.b),
79 }
80 }
81 }
82 }
83
84 #[inline]
85 fn simd_ge(self, rhs: Self) -> Self::Output {
86 pick! {
87 if #[cfg(target_feature="avx2")] {
88 !self.simd_lt(rhs)
89 } else {
90 Self {
91 a : self.a.simd_ge(rhs.a),
92 b : self.b.simd_ge(rhs.b),
93 }
94 }
95 }
96 }
97
98 #[inline]
99 fn shr(self, rhs: u64x4) -> Self::Output {
100 pick! {
101 if #[cfg(target_feature="avx2")] {
102 let arr: [i64; 4] = cast(self);
103 let rhs: [u64; 4] = cast(rhs);
104 cast([
105 arr[0].wrapping_shr(rhs[0] as u32),
106 arr[1].wrapping_shr(rhs[1] as u32),
107 arr[2].wrapping_shr(rhs[2] as u32),
108 arr[3].wrapping_shr(rhs[3] as u32),
109 ])
110 } else {
111 Self {
112 a : self.a.shr(rhs.a),
113 b : self.b.shr(rhs.b),
114 }
115 }
116 }
117 }
118
119 #[inline]
120 fn shr(self, rhs: u32) -> Self::Output {
121 let [a,b] : [i64x2; 2] = cast(self);
123 cast([a.shr(rhs), b.shr(rhs)])
124 }
125
126 #[inline]
127 pub fn max(self, rhs: Self) -> Self {
128 self.simd_gt(rhs).select(self, rhs)
129 }
130
131 #[inline]
132 pub fn min(self, rhs: Self) -> Self {
133 self.simd_lt(rhs).select(self, rhs)
134 }
135
136 #[inline]
137 pub fn reduce_max(self) -> i64 {
138 let array: [i64; 4] = cast(self);
139 array[0].max(array[1]).max(array[2]).max(array[3])
140 }
141
142 #[inline]
143 pub fn reduce_min(self) -> i64 {
144 let array: [i64; 4] = cast(self);
145 array[0].min(array[1]).min(array[2]).min(array[3])
146 }
147
148 #[inline]
149 pub fn unbounded_shr(self, rhs: u64x4) -> Self {
150 let [self_a, self_b] = cast::<i64x4, [i64x2; 2]>(self);
151 let [rhs_a, rhs_b] = cast::<u64x4, [u64x2; 2]>(rhs);
152
153 cast([self_a.unbounded_shr(rhs_a), self_b.unbounded_shr(rhs_b)])
154 }
155
156 #[inline]
157 pub fn unbounded_shr_scalar(self, rhs: u32) -> Self {
158 let [self_a, self_b] = cast::<i64x4, [i64x2; 2]>(self);
160 cast([self_a.unbounded_shr_scalar(rhs), self_b.unbounded_shr_scalar(rhs)])
161 }
162
163 #[inline]
164 pub fn saturating_add(self, rhs: Self) -> Self {
165 pick! {
166 if #[cfg(target_feature="avx2")] {
167 let result = self + rhs;
168 let overflow = (!(self ^ rhs) & (self ^ result)).is_negative();
169 let negative = self.is_negative();
170
171 overflow.select(Self::MAX ^ negative, result)
173 } else {
174 Self {
175 a: self.a.saturating_add(rhs.a),
176 b: self.b.saturating_add(rhs.b),
177 }
178 }
179 }
180 }
181
182 #[inline]
183 pub fn saturating_sub(self, rhs: Self) -> Self {
184 pick! {
185 if #[cfg(target_feature="avx2")] {
186 let result = self - rhs;
187 let overflow = ((self ^ rhs) & (self ^ result)).is_negative();
188 let negative = self.is_negative();
189
190 overflow.select(Self::MAX ^ negative, result)
192 } else {
193 Self {
194 a: self.a.saturating_sub(rhs.a),
195 b: self.b.saturating_sub(rhs.b),
196 }
197 }
198 }
199 }
200
201 #[inline]
202 pub fn overflowing_mul(self, rhs: Self) -> (Self, Self) {
203 let self_array = self.to_array();
208 let rhs_array = rhs.to_array();
209
210 let result = [
211 self_array[0].overflowing_mul(rhs_array[0]),
212 self_array[1].overflowing_mul(rhs_array[1]),
213 self_array[2].overflowing_mul(rhs_array[2]),
214 self_array[3].overflowing_mul(rhs_array[3]),
215 ];
216 (
217 Self::new([result[0].0, result[1].0, result[2].0, result[3].0]),
218 Self::new([
219 -(result[0].1 as i64),
220 -(result[1].1 as i64),
221 -(result[2].1 as i64),
222 -(result[3].1 as i64),
223 ]),
224 )
225 }
226
227 optional_fn_widening_mul {
228 }
230
231 #[inline]
232 pub fn mul_keep_low_high(self, rhs: Self) -> (u64x4, i64x4) {
233 let self_array = self.to_array();
237 let rhs_array = rhs.to_array();
238
239 let widening_mul = [
240 (self_array[0] as i128).wrapping_mul(rhs_array[0] as i128),
241 (self_array[1] as i128).wrapping_mul(rhs_array[1] as i128),
242 (self_array[2] as i128).wrapping_mul(rhs_array[2] as i128),
243 (self_array[3] as i128).wrapping_mul(rhs_array[3] as i128),
244 ];
245
246 (
247 u64x4::new([
248 widening_mul[0] as u64,
249 widening_mul[1] as u64,
250 widening_mul[2] as u64,
251 widening_mul[3] as u64,
252 ]),
253 i64x4::new([
254 (widening_mul[0] >> 64) as i64,
255 (widening_mul[1] >> 64) as i64,
256 (widening_mul[2] >> 64) as i64,
257 (widening_mul[3] >> 64) as i64,
258 ]),
259 )
260 }
261
262 #[inline]
263 pub fn mul_keep_high(self, rhs: Self) -> Self {
264 let self_array = self.to_array();
265 let rhs_array = rhs.to_array();
266
267 Self::new([
268 ((self_array[0] as i128).wrapping_mul(rhs_array[0] as i128) >> 64) as i64,
269 ((self_array[1] as i128).wrapping_mul(rhs_array[1] as i128) >> 64) as i64,
270 ((self_array[2] as i128).wrapping_mul(rhs_array[2] as i128) >> 64) as i64,
271 ((self_array[3] as i128).wrapping_mul(rhs_array[3] as i128) >> 64) as i64,
272 ])
273 }
274
275 #[inline]
276 pub fn abs(self) -> Self {
277 pick! {
278 if #[cfg(target_feature="avx2")] {
279 let arr: [i64; 4] = cast(self);
281 cast(
282 [
283 arr[0].wrapping_abs(),
284 arr[1].wrapping_abs(),
285 arr[2].wrapping_abs(),
286 arr[3].wrapping_abs(),
287 ])
288 } else {
289 Self {
290 a : self.a.abs(),
291 b : self.b.abs(),
292 }
293 }
294 }
295 }
296
297 #[inline]
298 pub fn is_positive(self) -> Self {
299 pick! {
300 if #[cfg(all(target_feature="neon", target_arch="aarch64"))] {
301 Self {
303 a: self.a.is_positive(),
304 b: self.b.is_positive(),
305 }
306 } else {
307 self.simd_gt(Self::ZERO)
308 }
309 }
310 }
311
312 #[inline]
313 pub fn is_negative(self) -> Self {
314 pick! {
315 if #[cfg(all(target_feature="neon", target_arch="aarch64"))] {
316 Self {
318 a: self.a.is_negative(),
319 b: self.b.is_negative(),
320 }
321 } else {
322 self.simd_lt(Self::ZERO)
323 }
324 }
325 }
326
327 optional_fn_deserialize {}
328}
329
330impl i64x4 {
333 #[inline]
335 #[must_use]
336 pub fn round_float(self) -> f64x4 {
337 let arr: [i64; 4] = cast(self);
338 cast([arr[0] as f64, arr[1] as f64, arr[2] as f64, arr[3] as f64])
339 }
340
341 #[must_use]
343 #[inline]
344 #[allow(dead_code)]
345 pub(crate) fn unpack_lo(self, b: Self) -> Self {
346 pick! {
347 if #[cfg(target_feature="avx2")] {
348 let [aa, _]: [i64x2; 2] = cast(self);
349 let [ba, _]: [i64x2; 2] = cast(b);
350 cast([aa.unpack_lo(ba), aa.unpack_hi(ba)])
351 } else {
352 Self { a: self.a.unpack_lo(b.a), b: self.a.unpack_hi(b.a) }
353 }
354 }
355 }
356
357 #[must_use]
359 #[inline]
360 #[allow(dead_code)]
361 pub(crate) fn unpack_hi(self, b: Self) -> Self {
362 pick! {
363 if #[cfg(target_feature="avx2")] {
364 let [_, ab]: [i64x2; 2] = cast(self);
365 let [_, bb]: [i64x2; 2] = cast(b);
366 cast([ab.unpack_lo(bb), ab.unpack_hi(bb)])
367 } else {
368 Self { a: self.b.unpack_lo(b.b), b: self.b.unpack_hi(b.b) }
369 }
370 }
371 }
372}