1use super::*;
2
3pick! {
4 if #[cfg(target_feature="avx512f")] {
5 #[derive(Default, Clone, Copy, PartialEq)]
6 #[repr(C, align(64))]
7 pub struct f64x8 { pub(crate) avx512: m512d }
8 } else {
9 #[derive(Default, Clone, Copy, PartialEq)]
10 #[repr(C, align(64))]
11 pub struct f64x8 { pub(crate) a : f64x4, pub(crate) b : f64x4 }
12 }
13}
14
15macro_rules! const_f64_as_f64x8 {
16 ($i:ident, $f:expr) => {
17 #[allow(non_upper_case_globals)]
18 pub const $i: f64x8 = f64x8::new([$f; 8]);
19 };
20}
21
22unsafe impl Zeroable for f64x8 {}
23unsafe impl Pod for f64x8 {}
24
25impl AlignTo for f64x8 {
26 type Elem = f64;
27}
28
29impl Add for f64x8 {
30 type Output = Self;
31 #[inline]
32 fn add(self, rhs: Self) -> Self::Output {
33 pick! {
34 if #[cfg(target_feature="avx512f")] {
35 Self { avx512: add_m512d(self.avx512, rhs.avx512) }
36 } else {
37 Self {
38 a : self.a.add(rhs.a),
39 b : self.b.add(rhs.b),
40 }
41 }
42 }
43 }
44}
45
46impl Sub for f64x8 {
47 type Output = Self;
48 #[inline]
49 fn sub(self, rhs: Self) -> Self::Output {
50 pick! {
51 if #[cfg(target_feature="avx512f")] {
52 Self { avx512: sub_m512d(self.avx512, rhs.avx512) }
53 } else {
54 Self {
55 a : self.a.sub(rhs.a),
56 b : self.b.sub(rhs.b),
57 }
58 }
59 }
60 }
61}
62
63impl Mul for f64x8 {
64 type Output = Self;
65 #[inline]
66 fn mul(self, rhs: Self) -> Self::Output {
67 pick! {
68 if #[cfg(target_feature="avx512f")] {
69 Self { avx512: mul_m512d(self.avx512, rhs.avx512) }
70 } else {
71 Self { a: self.a.mul(rhs.a), b: self.b.mul(rhs.b) }
72 }
73 }
74 }
75}
76
77impl Div for f64x8 {
78 type Output = Self;
79 #[inline]
80 fn div(self, rhs: Self) -> Self::Output {
81 pick! {
82 if #[cfg(target_feature="avx512f")] {
83 Self { avx512: div_m512d(self.avx512, rhs.avx512) }
84 } else {
85 Self { a: self.a.div(rhs.a), b: self.b.div(rhs.b) }
86 }
87 }
88 }
89}
90
91impl Rem for f64x8 {
92 type Output = Self;
93 #[inline]
94 fn rem(self, rhs: Self) -> Self::Output {
95 Self::new([
96 self.to_array()[0] % rhs.to_array()[0],
97 self.to_array()[1] % rhs.to_array()[1],
98 self.to_array()[2] % rhs.to_array()[2],
99 self.to_array()[3] % rhs.to_array()[3],
100 self.to_array()[4] % rhs.to_array()[4],
101 self.to_array()[5] % rhs.to_array()[5],
102 self.to_array()[6] % rhs.to_array()[6],
103 self.to_array()[7] % rhs.to_array()[7],
104 ])
105 }
106}
107
108impl Neg for f64x8 {
109 type Output = Self;
110 #[inline]
111 fn neg(self) -> Self::Output {
112 pick! {
113 if #[cfg(target_feature="avx512f")] {
114 Self { avx512: bitxor_m512d(self.avx512, Self::splat(-0.0).avx512) }
115 } else {
116 Self {
117 a : self.a.neg(),
118 b : self.b.neg(),
119 }
120 }
121 }
122 }
123}
124
125impl Add<f64> for f64x8 {
126 type Output = Self;
127 #[inline]
128 fn add(self, rhs: f64) -> Self::Output {
129 self.add(Self::splat(rhs))
130 }
131}
132
133impl Sub<f64> for f64x8 {
134 type Output = Self;
135 #[inline]
136 fn sub(self, rhs: f64) -> Self::Output {
137 self.sub(Self::splat(rhs))
138 }
139}
140
141impl Mul<f64> for f64x8 {
142 type Output = Self;
143 #[inline]
144 fn mul(self, rhs: f64) -> Self::Output {
145 self.mul(Self::splat(rhs))
146 }
147}
148
149impl Div<f64> for f64x8 {
150 type Output = Self;
151 #[inline]
152 fn div(self, rhs: f64) -> Self::Output {
153 self.div(Self::splat(rhs))
154 }
155}
156
157impl Rem<f64> for f64x8 {
158 type Output = Self;
159 #[inline]
160 fn rem(self, rhs: f64) -> Self::Output {
161 self.rem(Self::splat(rhs))
162 }
163}
164
165impl Add<f64x8> for f64 {
166 type Output = f64x8;
167 #[inline]
168 fn add(self, rhs: f64x8) -> Self::Output {
169 f64x8::splat(self).add(rhs)
170 }
171}
172
173impl Sub<f64x8> for f64 {
174 type Output = f64x8;
175 #[inline]
176 fn sub(self, rhs: f64x8) -> Self::Output {
177 f64x8::splat(self).sub(rhs)
178 }
179}
180
181impl Mul<f64x8> for f64 {
182 type Output = f64x8;
183 #[inline]
184 fn mul(self, rhs: f64x8) -> Self::Output {
185 f64x8::splat(self).mul(rhs)
186 }
187}
188
189impl Div<f64x8> for f64 {
190 type Output = f64x8;
191 #[inline]
192 fn div(self, rhs: f64x8) -> Self::Output {
193 f64x8::splat(self).div(rhs)
194 }
195}
196
197impl Rem<f64x8> for f64 {
198 type Output = f64x8;
199 #[inline]
200 fn rem(self, rhs: f64x8) -> Self::Output {
201 f64x8::splat(self).rem(rhs)
202 }
203}
204
205impl BitAnd for f64x8 {
206 type Output = Self;
207 #[inline]
208 fn bitand(self, rhs: Self) -> Self::Output {
209 pick! {
210 if #[cfg(target_feature="avx512f")] {
211 Self { avx512: bitand_m512d(self.avx512, rhs.avx512) }
212 } else {
213 Self {
214 a : self.a.bitand(rhs.a),
215 b : self.b.bitand(rhs.b),
216 }
217 }
218 }
219 }
220}
221
222impl BitOr for f64x8 {
223 type Output = Self;
224 #[inline]
225 fn bitor(self, rhs: Self) -> Self::Output {
226 pick! {
227 if #[cfg(target_feature="avx512f")] {
228 Self { avx512: bitor_m512d(self.avx512, rhs.avx512) }
229 } else {
230 Self {
231 a : self.a.bitor(rhs.a),
232 b : self.b.bitor(rhs.b),
233 }
234 }
235 }
236 }
237}
238
239impl BitXor for f64x8 {
240 type Output = Self;
241 #[inline]
242 fn bitxor(self, rhs: Self) -> Self::Output {
243 pick! {
244 if #[cfg(target_feature="avx512f")] {
245 Self { avx512: bitxor_m512d(self.avx512, rhs.avx512) }
246 } else {
247 Self {
248 a : self.a.bitxor(rhs.a),
249 b : self.b.bitxor(rhs.b),
250 }
251 }
252 }
253 }
254}
255
256#[expect(deprecated)]
257impl CmpEq for f64x8 {
258 type Output = Self;
259 #[inline]
260 fn simd_eq(self, rhs: Self) -> Self::Output {
261 pick! {
262 if #[cfg(target_feature="avx512f")] {
263 Self { avx512: cmp_op_mask_m512d::<{cmp_op!(EqualOrdered)}>(self.avx512, rhs.avx512) }
264 } else {
265 Self {
266 a : self.a.simd_eq(rhs.a),
267 b : self.b.simd_eq(rhs.b),
268 }
269 }
270 }
271 }
272}
273
274#[expect(deprecated)]
275impl CmpGt for f64x8 {
276 type Output = Self;
277 #[inline]
278 fn simd_gt(self, rhs: Self) -> Self::Output {
279 pick! {
280 if #[cfg(target_feature="avx512f")] {
281 Self { avx512: cmp_op_mask_m512d::<{cmp_op!(GreaterThanOrdered)}>(self.avx512, rhs.avx512) }
282 } else {
283 Self {
284 a : self.a.simd_gt(rhs.a),
285 b : self.b.simd_gt(rhs.b),
286 }
287 }
288 }
289 }
290}
291
292#[expect(deprecated)]
293impl CmpGe for f64x8 {
294 type Output = Self;
295 #[inline]
296 fn simd_ge(self, rhs: Self) -> Self::Output {
297 pick! {
298 if #[cfg(target_feature="avx512f")] {
299 Self { avx512: cmp_op_mask_m512d::<{cmp_op!(GreaterEqualOrdered)}>(self.avx512, rhs.avx512) }
300 } else {
301 Self {
302 a : self.a.simd_ge(rhs.a),
303 b : self.b.simd_ge(rhs.b),
304 }
305 }
306 }
307 }
308}
309
310#[expect(deprecated)]
311impl CmpLt for f64x8 {
312 type Output = Self;
313 #[inline]
314 fn simd_lt(self, rhs: Self) -> Self::Output {
315 pick! {
316 if #[cfg(target_feature="avx512f")] {
317 Self { avx512: cmp_op_mask_m512d::<{cmp_op!(LessThanOrdered)}>(self.avx512, rhs.avx512) }
318 } else {
319 Self {
320 a : self.a.simd_lt(rhs.a),
321 b : self.b.simd_lt(rhs.b),
322 }
323 }
324 }
325 }
326}
327
328#[expect(deprecated)]
329impl CmpLe for f64x8 {
330 type Output = Self;
331 #[inline]
332 fn simd_le(self, rhs: Self) -> Self::Output {
333 pick! {
334 if #[cfg(target_feature="avx512f")] {
335 Self { avx512: cmp_op_mask_m512d::<{cmp_op!(LessEqualOrdered)}>(self.avx512, rhs.avx512) }
336 } else {
337 Self {
338 a : self.a.simd_le(rhs.a),
339 b : self.b.simd_le(rhs.b),
340 }
341 }
342 }
343 }
344}
345
346#[expect(deprecated)]
347impl CmpNe for f64x8 {
348 type Output = Self;
349 #[inline]
350 fn simd_ne(self, rhs: Self) -> Self::Output {
351 pick! {
352 if #[cfg(target_feature="avx512f")] {
353 Self { avx512: cmp_op_mask_m512d::<{cmp_op!(NotEqualUnordered)}>(self.avx512, rhs.avx512) }
354 } else {
355 Self {
356 a : self.a.simd_ne(rhs.a),
357 b : self.b.simd_ne(rhs.b),
358 }
359 }
360 }
361 }
362}
363
364impl f64x8 {
365 const_f64_as_f64x8!(ONE, 1.0);
366 const_f64_as_f64x8!(ZERO, 0.0);
367 const_f64_as_f64x8!(HALF, 0.5);
368 const_f64_as_f64x8!(EPSILON, f64::EPSILON);
369 const_f64_as_f64x8!(MIN, f64::MIN);
370 const_f64_as_f64x8!(MIN_POSITIVE, f64::MIN_POSITIVE);
371 const_f64_as_f64x8!(MAX, f64::MAX);
372 const_f64_as_f64x8!(NAN, f64::NAN);
373 const_f64_as_f64x8!(INFINITY, f64::INFINITY);
374 const_f64_as_f64x8!(NEG_INFINITY, f64::NEG_INFINITY);
375 const_f64_as_f64x8!(E, core::f64::consts::E);
376 const_f64_as_f64x8!(FRAC_1_PI, core::f64::consts::FRAC_1_PI);
377 const_f64_as_f64x8!(FRAC_2_PI, core::f64::consts::FRAC_2_PI);
378 const_f64_as_f64x8!(FRAC_2_SQRT_PI, core::f64::consts::FRAC_2_SQRT_PI);
379 const_f64_as_f64x8!(FRAC_1_SQRT_2, core::f64::consts::FRAC_1_SQRT_2);
380 const_f64_as_f64x8!(FRAC_PI_2, core::f64::consts::FRAC_PI_2);
381 const_f64_as_f64x8!(FRAC_PI_3, core::f64::consts::FRAC_PI_3);
382 const_f64_as_f64x8!(FRAC_PI_4, core::f64::consts::FRAC_PI_4);
383 const_f64_as_f64x8!(FRAC_PI_6, core::f64::consts::FRAC_PI_6);
384 const_f64_as_f64x8!(FRAC_PI_8, core::f64::consts::FRAC_PI_8);
385 const_f64_as_f64x8!(LN_2, core::f64::consts::LN_2);
386 const_f64_as_f64x8!(LN_10, core::f64::consts::LN_10);
387 const_f64_as_f64x8!(LOG2_E, core::f64::consts::LOG2_E);
388 const_f64_as_f64x8!(LOG10_E, core::f64::consts::LOG10_E);
389 const_f64_as_f64x8!(LOG10_2, core::f64::consts::LOG10_2);
390 const_f64_as_f64x8!(LOG2_10, core::f64::consts::LOG2_10);
391 const_f64_as_f64x8!(PI, core::f64::consts::PI);
392 const_f64_as_f64x8!(SQRT_2, core::f64::consts::SQRT_2);
393 const_f64_as_f64x8!(TAU, core::f64::consts::TAU);
394
395 #[inline]
396 #[must_use]
397 pub const fn new(array: [f64; 8]) -> Self {
398 unsafe { core::mem::transmute(array) }
399 }
400
401 simd_comparison_fns!();
402
403 #[inline]
404 #[must_use]
405 pub fn blend(self, t: Self, f: Self) -> Self {
406 pick! {
407 if #[cfg(target_feature="avx512f")] {
408 Self { avx512: blend_varying_m512d(f.avx512, t.avx512, movepi64_mask_m512d(self.avx512)) }
409 } else {
410 Self {
411 a : self.a.blend(t.a, f.a),
412 b : self.b.blend(t.b, f.b),
413 }
414 }
415 }
416 }
417
418 #[inline]
419 #[must_use]
420 pub fn abs(self) -> Self {
421 pick! {
422 if #[cfg(target_feature="avx512f")] {
423 let non_sign_bits = f64x8::from(f64::from_bits(i64::MAX as u64));
424 self & non_sign_bits
425 } else {
426 Self {
427 a: self.a.abs(),
428 b: self.b.abs(),
429 }
430 }
431 }
432 }
433
434 #[inline]
435 #[must_use]
436 pub fn signum(self) -> Self {
437 let result = Self::ONE | self & -Self::ZERO;
438
439 self.is_nan().blend(self, result)
440 }
441
442 #[inline]
443 #[must_use]
444 pub fn floor(self) -> Self {
445 pick! {
446 if #[cfg(target_feature="avx512f")] {
447 Self { avx512: round_m512d::<{round_op!(NegInf)}>(self.avx512) }
448 } else {
449 Self {
450 a : self.a.floor(),
451 b : self.b.floor(),
452 }
453 }
454 }
455 }
456 #[inline]
457 #[must_use]
458 pub fn ceil(self) -> Self {
459 pick! {
460 if #[cfg(target_feature="avx512f")] {
461 Self { avx512: round_m512d::<{round_op!(PosInf)}>(self.avx512) }
462 } else {
463 Self {
464 a : self.a.ceil(),
465 b : self.b.ceil(),
466 }
467 }
468 }
469 }
470
471 #[inline]
472 #[must_use]
473 pub fn fast_max(self, rhs: Self) -> Self {
474 pick! {
475 if #[cfg(target_feature="avx512f")] {
476 Self { avx512: max_m512d(self.avx512, rhs.avx512) }
477 } else {
478 Self {
479 a : self.a.fast_max(rhs.a),
480 b : self.b.fast_max(rhs.b),
481 }
482 }
483 }
484 }
485
486 #[inline]
487 #[must_use]
488 pub fn max(self, rhs: Self) -> Self {
489 pick! {
490 if #[cfg(target_feature="avx512f")] {
491 rhs.is_nan().blend(self, Self { avx512: max_m512d(self.avx512, rhs.avx512) })
492 } else {
493 Self {
494 a: self.a.max(rhs.a),
495 b: self.b.max(rhs.b),
496 }
497 }
498 }
499 }
500
501 #[inline]
502 #[must_use]
503 pub fn fast_min(self, rhs: Self) -> Self {
504 pick! {
505 if #[cfg(target_feature="avx512f")] {
506 Self { avx512: min_m512d(self.avx512, rhs.avx512) }
507 } else {
508 Self {
509 a : self.a.fast_min(rhs.a),
510 b : self.b.fast_min(rhs.b),
511 }
512 }
513 }
514 }
515
516 #[inline]
517 #[must_use]
518 pub fn min(self, rhs: Self) -> Self {
519 pick! {
520 if #[cfg(target_feature="avx512f")] {
521 rhs.is_nan().blend(self, Self { avx512: min_m512d(self.avx512, rhs.avx512) })
522 } else {
523 Self {
524 a: self.a.min(rhs.a),
525 b: self.b.min(rhs.b),
526 }
527 }
528 }
529 }
530
531 #[inline]
536 #[must_use]
537 pub fn clamp(self, min: Self, max: Self) -> Self {
538 let is_nan = self.is_nan() | min.is_nan() | max.is_nan();
539 let clamped = self.fast_min(max).fast_max(min);
540 is_nan.blend(Self::splat(f64::NAN), clamped)
541 }
542
543 #[inline]
550 #[must_use]
551 pub fn fast_clamp(self, min: Self, max: Self) -> Self {
552 pick! {
553 if #[cfg(target_feature="avx512f")] {
554 Self { avx512: min_m512d(max.avx512, max_m512d(min.avx512, self.avx512)) }
557 } else {
558 Self {
559 a: self.a.fast_clamp(min.a, max.a),
560 b: self.b.fast_clamp(min.b, max.b),
561 }
562 }
563 }
564 }
565
566 #[inline]
567 #[must_use]
568 pub fn midpoint(self, other: Self) -> Self {
569 (self + other) * 0.5
570 }
571
572 #[inline]
573 #[must_use]
574 pub fn is_nan(self) -> Self {
575 pick! {
576 if #[cfg(target_feature="avx512f")] {
577 Self { avx512: cmp_op_mask_m512d::<{cmp_op!(Unordered)}>(self.avx512, self.avx512) }
578 } else {
579 Self {
580 a: self.a.is_nan(),
581 b: self.b.is_nan(),
582 }
583 }
584 }
585 }
586
587 #[inline]
588 #[must_use]
589 pub fn is_finite(self) -> Self {
590 let shifted_exp_mask = u64x8::splat(0xFFE0000000000000);
591 let u: u64x8 = cast(self);
592 let shift_u = u << 1_u64;
593 let out = !(shift_u & shifted_exp_mask).simd_eq(shifted_exp_mask);
594 cast(out)
595 }
596
597 #[inline]
598 #[must_use]
599 pub fn is_inf(self) -> Self {
600 let shifted_inf = u64x8::from(0xFFE0000000000000);
601 let u: u64x8 = cast(self);
602 let shift_u = u << 1_u64;
603 let out = (shift_u).simd_eq(shifted_inf);
604 cast(out)
605 }
606
607 #[inline]
608 #[must_use]
609 pub fn round(self) -> Self {
610 pick! {
611 if #[cfg(target_feature="avx512f")] {
612 Self { avx512: round_m512d::<{round_op!(Nearest)}>(self.avx512) }
613 } else {
614 Self {
615 a: self.a.round(),
616 b: self.b.round(),
617 }
618 }
619 }
620 }
621
622 #[inline]
623 #[must_use]
624 pub fn fast_round_int(self) -> i64x8 {
625 pick! {
626 if #[cfg(target_feature="avx512dq")] {
627 #[cfg(target_arch = "x86")]
628 use core::arch::x86::_mm512_cvtpd_epi64;
629 #[cfg(target_arch = "x86_64")]
630 use core::arch::x86_64::_mm512_cvtpd_epi64;
631
632 cast(m512i(unsafe { _mm512_cvtpd_epi64(self.avx512.0) }))
634 } else {
635 cast([
636 self.a.fast_round_int(),
637 self.b.fast_round_int(),
638 ])
639 }
640 }
641 }
642
643 #[inline]
644 #[must_use]
645 pub fn round_int(self) -> i64x8 {
646 pick! {
647 if #[cfg(target_feature="avx512dq")] {
648 #[cfg(target_arch = "x86")]
649 use core::arch::x86::_mm512_cvtpd_epi64;
650 #[cfg(target_arch = "x86_64")]
651 use core::arch::x86_64::_mm512_cvtpd_epi64;
652
653 let non_nan_mask = self.simd_eq(self);
655 let non_nan = self & non_nan_mask;
656 let flip_to_max: i64x8 = cast(self.simd_ge(Self::splat(9223372036854775808.0)));
657
658 let cast: i64x8 = cast(m512i(unsafe { _mm512_cvtpd_epi64(non_nan.avx512.0) }));
660 flip_to_max ^ cast
661 } else {
662 cast([
663 self.a.round_int(),
664 self.b.round_int(),
665 ])
666 }
667 }
668 }
669
670 #[inline]
671 #[must_use]
672 pub fn trunc(self) -> Self {
673 pick! {
674 if #[cfg(target_feature="avx512f")] {
675 Self { avx512: round_m512d::<{round_op!(Zero)}>(self.avx512) }
676 } else {
677 Self {
678 a: self.a.trunc(),
679 b: self.b.trunc(),
680 }
681 }
682 }
683 }
684
685 #[inline]
689 #[must_use]
690 pub fn fast_trunc_int(self) -> i64x8 {
691 pick! {
692 if #[cfg(target_feature="avx512dq")] {
693 #[cfg(target_arch = "x86")]
694 use core::arch::x86::_mm512_cvttpd_epi64;
695 #[cfg(target_arch = "x86_64")]
696 use core::arch::x86_64::_mm512_cvttpd_epi64;
697
698 cast(m512i(unsafe { _mm512_cvttpd_epi64(self.avx512.0) }))
700 } else {
701 cast([
702 self.a.fast_trunc_int(),
703 self.b.fast_trunc_int(),
704 ])
705 }
706 }
707 }
708
709 #[inline]
713 #[must_use]
714 pub fn trunc_int(self) -> i64x8 {
715 pick! {
716 if #[cfg(target_feature="avx512dq")] {
717 #[cfg(target_arch = "x86")]
718 use core::arch::x86::_mm512_cvttpd_epi64;
719 #[cfg(target_arch = "x86_64")]
720 use core::arch::x86_64::_mm512_cvttpd_epi64;
721
722 let non_nan_mask = self.simd_eq(self);
724 let non_nan = self & non_nan_mask;
725 let flip_to_max: i64x8 = cast(self.simd_ge(Self::splat(9223372036854775808.0)));
726
727 let cast: i64x8 = cast(m512i(unsafe { _mm512_cvttpd_epi64(non_nan.avx512.0) }));
729 flip_to_max ^ cast
730 } else {
731 cast([
732 self.a.trunc_int(),
733 self.b.trunc_int(),
734 ])
735 }
736 }
737 }
738
739 #[inline]
740 #[must_use]
741 pub fn fract(self) -> Self {
742 self - self.trunc()
743 }
744
745 #[inline]
771 #[must_use]
772 pub fn mul_add(self, m: Self, a: Self) -> Self {
773 pick! {
774 if #[cfg(all(target_feature="avx512f",target_feature="fma"))] {
775 Self { avx512: fused_mul_add_m512d(self.avx512, m.avx512, a.avx512) }
776 } else if #[cfg(target_feature="avx512f")] {
777 (self * m) + a
779 } else {
780 Self {
781 a : self.a.mul_add(m.a, a.a),
782 b : self.b.mul_add(m.b, a.b),
783 }
784 }
785 }
786 }
787
788 #[inline]
814 #[must_use]
815 pub fn mul_sub(self, m: Self, s: Self) -> Self {
816 pick! {
817 if #[cfg(all(target_feature="avx512f",target_feature="fma"))] {
818 Self { avx512: fused_mul_sub_m512d(self.avx512, m.avx512, s.avx512) }
819 } else if #[cfg(target_feature="avx512f")] {
820 (self * m) - s
822 } else {
823 Self {
824 a : self.a.mul_sub(m.a, s.a),
825 b : self.b.mul_sub(m.b, s.b),
826 }
827 }
828 }
829 }
830
831 #[inline]
857 #[must_use]
858 pub fn mul_neg_add(self, m: Self, a: Self) -> Self {
859 pick! {
860 if #[cfg(all(target_feature="avx512f",target_feature="fma"))] {
861 Self { avx512: fused_mul_neg_add_m512d(self.avx512, m.avx512, a.avx512) }
862 } else if #[cfg(target_feature="avx512f")] {
863 a - (self * m)
865 } else {
866 Self {
867 a : self.a.mul_neg_add(m.a, a.a),
868 b : self.b.mul_neg_add(m.b, a.b),
869 }
870 }
871 }
872 }
873
874 #[inline]
900 #[must_use]
901 pub fn mul_neg_sub(self, m: Self, s: Self) -> Self {
902 pick! {
903 if #[cfg(all(target_feature="avx512f",target_feature="fma"))] {
904 Self { avx512: fused_mul_neg_sub_m512d(self.avx512, m.avx512, s.avx512) }
905 } else if #[cfg(target_feature="avx512f")] {
906 -(self * m) - s
908 } else {
909 Self {
910 a : self.a.mul_neg_sub(m.a, s.a),
911 b : self.b.mul_neg_sub(m.b, s.b),
912 }
913 }
914 }
915 }
916
917 #[inline]
918 #[must_use]
919 pub fn div_euclid(self, rhs: Self) -> Self {
920 let q = (self / rhs).trunc();
921 (self % rhs)
922 .simd_lt(Self::ZERO)
923 .blend(rhs.simd_gt(Self::ZERO).blend(q - Self::ONE, q + Self::ONE), q)
924 }
925
926 #[inline]
927 #[must_use]
928 pub fn rem_euclid(self, rhs: Self) -> Self {
929 let r = self % rhs;
930 r.simd_lt(Self::ZERO).blend(r + rhs.abs(), r)
931 }
932
933 #[inline]
934 #[must_use]
935 pub fn flip_signs(self, signs: Self) -> Self {
936 self ^ (signs & Self::from(-0.0))
937 }
938
939 #[inline]
940 #[must_use]
941 pub fn copysign(self, sign: Self) -> Self {
942 let magnitude_mask = Self::from(f64::from_bits(u64::MAX >> 1));
943 (self & magnitude_mask) | (sign & Self::from(-0.0))
944 }
945
946 #[inline]
947 pub fn asin_acos(self) -> (Self, Self) {
948 const_f64_as_f64x8!(R4asin, 2.967721961301243206100E-3);
951 const_f64_as_f64x8!(R3asin, -5.634242780008963776856E-1);
952 const_f64_as_f64x8!(R2asin, 6.968710824104713396794E0);
953 const_f64_as_f64x8!(R1asin, -2.556901049652824852289E1);
954 const_f64_as_f64x8!(R0asin, 2.853665548261061424989E1);
955
956 const_f64_as_f64x8!(S3asin, -2.194779531642920639778E1);
957 const_f64_as_f64x8!(S2asin, 1.470656354026814941758E2);
958 const_f64_as_f64x8!(S1asin, -3.838770957603691357202E2);
959 const_f64_as_f64x8!(S0asin, 3.424398657913078477438E2);
960
961 const_f64_as_f64x8!(P5asin, 4.253011369004428248960E-3);
962 const_f64_as_f64x8!(P4asin, -6.019598008014123785661E-1);
963 const_f64_as_f64x8!(P3asin, 5.444622390564711410273E0);
964 const_f64_as_f64x8!(P2asin, -1.626247967210700244449E1);
965 const_f64_as_f64x8!(P1asin, 1.956261983317594739197E1);
966 const_f64_as_f64x8!(P0asin, -8.198089802484824371615E0);
967
968 const_f64_as_f64x8!(Q4asin, -1.474091372988853791896E1);
969 const_f64_as_f64x8!(Q3asin, 7.049610280856842141659E1);
970 const_f64_as_f64x8!(Q2asin, -1.471791292232726029859E2);
971 const_f64_as_f64x8!(Q1asin, 1.395105614657485689735E2);
972 const_f64_as_f64x8!(Q0asin, -4.918853881490881290097E1);
973
974 let xa = self.abs();
975
976 let big = xa.simd_ge(f64x8::splat(0.625));
977
978 let x1 = big.blend(f64x8::splat(1.0) - xa, xa * xa);
979
980 let x2 = x1 * x1;
981 let x3 = x2 * x1;
982 let x4 = x2 * x2;
983 let x5 = x4 * x1;
984
985 let do_big = big.any();
986 let do_small = !big.all();
987
988 let mut rx = f64x8::default();
989 let mut sx = f64x8::default();
990 let mut px = f64x8::default();
991 let mut qx = f64x8::default();
992
993 if do_big {
994 rx = x3.mul_add(R3asin, x2 * R2asin)
995 + x4.mul_add(R4asin, x1.mul_add(R1asin, R0asin));
996 sx =
997 x3.mul_add(S3asin, x4) + x2.mul_add(S2asin, x1.mul_add(S1asin, S0asin));
998 }
999
1000 if do_small {
1001 px = x3.mul_add(P3asin, P0asin)
1002 + x4.mul_add(P4asin, x1 * P1asin)
1003 + x5.mul_add(P5asin, x2 * P2asin);
1004 qx = x4.mul_add(Q4asin, x5)
1005 + x3.mul_add(Q3asin, x1 * Q1asin)
1006 + x2.mul_add(Q2asin, Q0asin);
1007 };
1008
1009 let vx = big.blend(rx, px);
1010 let wx = big.blend(sx, qx);
1011
1012 let y1 = vx / wx * x1;
1013
1014 let mut z1 = f64x8::default();
1015 let mut z2 = f64x8::default();
1016 if do_big {
1017 let xb = (x1 + x1).sqrt();
1018 z1 = xb.mul_add(y1, xb);
1019 }
1020
1021 if do_small {
1022 z2 = xa.mul_add(y1, xa);
1023 }
1024
1025 let z3 = f64x8::FRAC_PI_2 - z1;
1027 let asin = big.blend(z3, z2);
1028 let asin = asin.flip_signs(self);
1029
1030 let z3 = self.simd_lt(f64x8::ZERO).blend(f64x8::PI - z1, z1);
1032 let z4 = f64x8::FRAC_PI_2 - z2.flip_signs(self);
1033 let acos = big.blend(z3, z4);
1034
1035 (asin, acos)
1036 }
1037
1038 #[inline]
1039 pub fn acos(self) -> Self {
1040 const_f64_as_f64x8!(R4asin, 2.967721961301243206100E-3);
1043 const_f64_as_f64x8!(R3asin, -5.634242780008963776856E-1);
1044 const_f64_as_f64x8!(R2asin, 6.968710824104713396794E0);
1045 const_f64_as_f64x8!(R1asin, -2.556901049652824852289E1);
1046 const_f64_as_f64x8!(R0asin, 2.853665548261061424989E1);
1047
1048 const_f64_as_f64x8!(S3asin, -2.194779531642920639778E1);
1049 const_f64_as_f64x8!(S2asin, 1.470656354026814941758E2);
1050 const_f64_as_f64x8!(S1asin, -3.838770957603691357202E2);
1051 const_f64_as_f64x8!(S0asin, 3.424398657913078477438E2);
1052
1053 const_f64_as_f64x8!(P5asin, 4.253011369004428248960E-3);
1054 const_f64_as_f64x8!(P4asin, -6.019598008014123785661E-1);
1055 const_f64_as_f64x8!(P3asin, 5.444622390564711410273E0);
1056 const_f64_as_f64x8!(P2asin, -1.626247967210700244449E1);
1057 const_f64_as_f64x8!(P1asin, 1.956261983317594739197E1);
1058 const_f64_as_f64x8!(P0asin, -8.198089802484824371615E0);
1059
1060 const_f64_as_f64x8!(Q4asin, -1.474091372988853791896E1);
1061 const_f64_as_f64x8!(Q3asin, 7.049610280856842141659E1);
1062 const_f64_as_f64x8!(Q2asin, -1.471791292232726029859E2);
1063 const_f64_as_f64x8!(Q1asin, 1.395105614657485689735E2);
1064 const_f64_as_f64x8!(Q0asin, -4.918853881490881290097E1);
1065
1066 let xa = self.abs();
1067
1068 let big = xa.simd_ge(f64x8::splat(0.625));
1069
1070 let x1 = big.blend(f64x8::splat(1.0) - xa, xa * xa);
1071
1072 let x2 = x1 * x1;
1073 let x3 = x2 * x1;
1074 let x4 = x2 * x2;
1075 let x5 = x4 * x1;
1076
1077 let do_big = big.any();
1078 let do_small = !big.all();
1079
1080 let mut rx = f64x8::default();
1081 let mut sx = f64x8::default();
1082 let mut px = f64x8::default();
1083 let mut qx = f64x8::default();
1084
1085 if do_big {
1086 rx = x3.mul_add(R3asin, x2 * R2asin)
1087 + x4.mul_add(R4asin, x1.mul_add(R1asin, R0asin));
1088 sx =
1089 x3.mul_add(S3asin, x4) + x2.mul_add(S2asin, x1.mul_add(S1asin, S0asin));
1090 }
1091 if do_small {
1092 px = x3.mul_add(P3asin, P0asin)
1093 + x4.mul_add(P4asin, x1 * P1asin)
1094 + x5.mul_add(P5asin, x2 * P2asin);
1095 qx = x4.mul_add(Q4asin, x5)
1096 + x3.mul_add(Q3asin, x1 * Q1asin)
1097 + x2.mul_add(Q2asin, Q0asin);
1098 };
1099
1100 let vx = big.blend(rx, px);
1101 let wx = big.blend(sx, qx);
1102
1103 let y1 = vx / wx * x1;
1104
1105 let mut z1 = f64x8::default();
1106 let mut z2 = f64x8::default();
1107 if do_big {
1108 let xb = (x1 + x1).sqrt();
1109 z1 = xb.mul_add(y1, xb);
1110 }
1111
1112 if do_small {
1113 z2 = xa.mul_add(y1, xa);
1114 }
1115
1116 let z3 = self.simd_lt(f64x8::ZERO).blend(f64x8::PI - z1, z1);
1118 let z4 = f64x8::FRAC_PI_2 - z2.flip_signs(self);
1119 let acos = big.blend(z3, z4);
1120
1121 acos
1122 }
1123 #[inline]
1124 #[must_use]
1125 pub fn asin(self) -> Self {
1126 const_f64_as_f64x8!(R4asin, 2.967721961301243206100E-3);
1129 const_f64_as_f64x8!(R3asin, -5.634242780008963776856E-1);
1130 const_f64_as_f64x8!(R2asin, 6.968710824104713396794E0);
1131 const_f64_as_f64x8!(R1asin, -2.556901049652824852289E1);
1132 const_f64_as_f64x8!(R0asin, 2.853665548261061424989E1);
1133
1134 const_f64_as_f64x8!(S3asin, -2.194779531642920639778E1);
1135 const_f64_as_f64x8!(S2asin, 1.470656354026814941758E2);
1136 const_f64_as_f64x8!(S1asin, -3.838770957603691357202E2);
1137 const_f64_as_f64x8!(S0asin, 3.424398657913078477438E2);
1138
1139 const_f64_as_f64x8!(P5asin, 4.253011369004428248960E-3);
1140 const_f64_as_f64x8!(P4asin, -6.019598008014123785661E-1);
1141 const_f64_as_f64x8!(P3asin, 5.444622390564711410273E0);
1142 const_f64_as_f64x8!(P2asin, -1.626247967210700244449E1);
1143 const_f64_as_f64x8!(P1asin, 1.956261983317594739197E1);
1144 const_f64_as_f64x8!(P0asin, -8.198089802484824371615E0);
1145
1146 const_f64_as_f64x8!(Q4asin, -1.474091372988853791896E1);
1147 const_f64_as_f64x8!(Q3asin, 7.049610280856842141659E1);
1148 const_f64_as_f64x8!(Q2asin, -1.471791292232726029859E2);
1149 const_f64_as_f64x8!(Q1asin, 1.395105614657485689735E2);
1150 const_f64_as_f64x8!(Q0asin, -4.918853881490881290097E1);
1151
1152 let xa = self.abs();
1153
1154 let big = xa.simd_ge(f64x8::splat(0.625));
1155
1156 let x1 = big.blend(f64x8::splat(1.0) - xa, xa * xa);
1157
1158 let x2 = x1 * x1;
1159 let x3 = x2 * x1;
1160 let x4 = x2 * x2;
1161 let x5 = x4 * x1;
1162
1163 let do_big = big.any();
1164 let do_small = !big.all();
1165
1166 let mut rx = f64x8::default();
1167 let mut sx = f64x8::default();
1168 let mut px = f64x8::default();
1169 let mut qx = f64x8::default();
1170
1171 if do_big {
1172 rx = x3.mul_add(R3asin, x2 * R2asin)
1173 + x4.mul_add(R4asin, x1.mul_add(R1asin, R0asin));
1174 sx =
1175 x3.mul_add(S3asin, x4) + x2.mul_add(S2asin, x1.mul_add(S1asin, S0asin));
1176 }
1177 if do_small {
1178 px = x3.mul_add(P3asin, P0asin)
1179 + x4.mul_add(P4asin, x1 * P1asin)
1180 + x5.mul_add(P5asin, x2 * P2asin);
1181 qx = x4.mul_add(Q4asin, x5)
1182 + x3.mul_add(Q3asin, x1 * Q1asin)
1183 + x2.mul_add(Q2asin, Q0asin);
1184 };
1185
1186 let vx = big.blend(rx, px);
1187 let wx = big.blend(sx, qx);
1188
1189 let y1 = vx / wx * x1;
1190
1191 let mut z1 = f64x8::default();
1192 let mut z2 = f64x8::default();
1193 if do_big {
1194 let xb = (x1 + x1).sqrt();
1195 z1 = xb.mul_add(y1, xb);
1196 }
1197
1198 if do_small {
1199 z2 = xa.mul_add(y1, xa);
1200 }
1201
1202 let z3 = f64x8::FRAC_PI_2 - z1;
1204 let asin = big.blend(z3, z2);
1205 let asin = asin.flip_signs(self);
1206
1207 asin
1208 }
1209
1210 #[inline]
1211 pub fn atan(self) -> Self {
1212 const_f64_as_f64x8!(MORE_BITS, 6.123233995736765886130E-17);
1215 const_f64_as_f64x8!(MORE_BITS_O2, 6.123233995736765886130E-17 * 0.5);
1216 const_f64_as_f64x8!(T3PO8, core::f64::consts::SQRT_2 + 1.0);
1217
1218 const_f64_as_f64x8!(P4atan, -8.750608600031904122785E-1);
1219 const_f64_as_f64x8!(P3atan, -1.615753718733365076637E1);
1220 const_f64_as_f64x8!(P2atan, -7.500855792314704667340E1);
1221 const_f64_as_f64x8!(P1atan, -1.228866684490136173410E2);
1222 const_f64_as_f64x8!(P0atan, -6.485021904942025371773E1);
1223
1224 const_f64_as_f64x8!(Q4atan, 2.485846490142306297962E1);
1225 const_f64_as_f64x8!(Q3atan, 1.650270098316988542046E2);
1226 const_f64_as_f64x8!(Q2atan, 4.328810604912902668951E2);
1227 const_f64_as_f64x8!(Q1atan, 4.853903996359136964868E2);
1228 const_f64_as_f64x8!(Q0atan, 1.945506571482613964425E2);
1229
1230 let t = self.abs();
1231
1232 let notbig = t.simd_le(T3PO8);
1236 let notsmal = t.simd_ge(Self::splat(0.66));
1237
1238 let mut s = notbig.blend(Self::FRAC_PI_4, Self::FRAC_PI_2);
1239 s = notsmal & s;
1240 let mut fac = notbig.blend(MORE_BITS_O2, MORE_BITS);
1241 fac = notsmal & fac;
1242
1243 let mut a = notbig & t;
1247 a = notsmal.blend(a - Self::ONE, a);
1248 let mut b = notbig & Self::ONE;
1249 b = notsmal.blend(b + t, b);
1250 let z = a / b;
1251
1252 let zz = z * z;
1253
1254 let px = polynomial_4!(zz, P0atan, P1atan, P2atan, P3atan, P4atan);
1255 let qx = polynomial_5n!(zz, Q0atan, Q1atan, Q2atan, Q3atan, Q4atan);
1256
1257 let mut re = (px / qx).mul_add(z * zz, z);
1258 re += s + fac;
1259
1260 re = (self.is_sign_negative()).blend(-re, re);
1262
1263 re
1264 }
1265
1266 #[inline]
1267 pub fn atan2(self, x: Self) -> Self {
1268 const_f64_as_f64x8!(MORE_BITS, 6.123233995736765886130E-17);
1271 const_f64_as_f64x8!(MORE_BITS_O2, 6.123233995736765886130E-17 * 0.5);
1272 const_f64_as_f64x8!(T3PO8, core::f64::consts::SQRT_2 + 1.0);
1273
1274 const_f64_as_f64x8!(P4atan, -8.750608600031904122785E-1);
1275 const_f64_as_f64x8!(P3atan, -1.615753718733365076637E1);
1276 const_f64_as_f64x8!(P2atan, -7.500855792314704667340E1);
1277 const_f64_as_f64x8!(P1atan, -1.228866684490136173410E2);
1278 const_f64_as_f64x8!(P0atan, -6.485021904942025371773E1);
1279
1280 const_f64_as_f64x8!(Q4atan, 2.485846490142306297962E1);
1281 const_f64_as_f64x8!(Q3atan, 1.650270098316988542046E2);
1282 const_f64_as_f64x8!(Q2atan, 4.328810604912902668951E2);
1283 const_f64_as_f64x8!(Q1atan, 4.853903996359136964868E2);
1284 const_f64_as_f64x8!(Q0atan, 1.945506571482613964425E2);
1285
1286 let y = self;
1287
1288 let x1 = x.abs();
1290 let y1 = y.abs();
1291 let swapxy = y1.simd_gt(x1);
1292 let mut x2 = swapxy.blend(y1, x1);
1294 let mut y2 = swapxy.blend(x1, y1);
1295
1296 let both_infinite = x.is_inf() & y.is_inf();
1298 if both_infinite.any() {
1299 let minus_one = -Self::ONE;
1300 x2 = both_infinite.blend(x2 & minus_one, x2);
1301 y2 = both_infinite.blend(y2 & minus_one, y2);
1302 }
1303
1304 let t = y2 / x2;
1306
1307 let notbig = t.simd_le(T3PO8);
1311 let notsmal = t.simd_ge(Self::splat(0.66));
1312
1313 let mut s = notbig.blend(Self::FRAC_PI_4, Self::FRAC_PI_2);
1314 s = notsmal & s;
1315 let mut fac = notbig.blend(MORE_BITS_O2, MORE_BITS);
1316 fac = notsmal & fac;
1317
1318 let mut a = notbig & t;
1322 a = notsmal.blend(a - Self::ONE, a);
1323 let mut b = notbig & Self::ONE;
1324 b = notsmal.blend(b + t, b);
1325 let z = a / b;
1326
1327 let zz = z * z;
1328
1329 let px = polynomial_4!(zz, P0atan, P1atan, P2atan, P3atan, P4atan);
1330 let qx = polynomial_5n!(zz, Q0atan, Q1atan, Q2atan, Q3atan, Q4atan);
1331
1332 let mut re = (px / qx).mul_add(z * zz, z);
1333 re += s + fac;
1334
1335 re = swapxy.blend(Self::FRAC_PI_2 - re, re);
1337 re = ((x | y).simd_eq(Self::ZERO)).blend(Self::ZERO, re);
1338 re = (x.is_sign_negative()).blend(Self::PI - re, re);
1339
1340 re = (y.is_sign_negative()).blend(-re, re);
1342
1343 re
1344 }
1345
1346 #[inline]
1347 #[must_use]
1348 pub fn sin_cos(self) -> (Self, Self) {
1349 const_f64_as_f64x8!(P0sin, -1.66666666666666307295E-1);
1353 const_f64_as_f64x8!(P1sin, 8.33333333332211858878E-3);
1354 const_f64_as_f64x8!(P2sin, -1.98412698295895385996E-4);
1355 const_f64_as_f64x8!(P3sin, 2.75573136213857245213E-6);
1356 const_f64_as_f64x8!(P4sin, -2.50507477628578072866E-8);
1357 const_f64_as_f64x8!(P5sin, 1.58962301576546568060E-10);
1358
1359 const_f64_as_f64x8!(P0cos, 4.16666666666665929218E-2);
1360 const_f64_as_f64x8!(P1cos, -1.38888888888730564116E-3);
1361 const_f64_as_f64x8!(P2cos, 2.48015872888517045348E-5);
1362 const_f64_as_f64x8!(P3cos, -2.75573141792967388112E-7);
1363 const_f64_as_f64x8!(P4cos, 2.08757008419747316778E-9);
1364 const_f64_as_f64x8!(P5cos, -1.13585365213876817300E-11);
1365
1366 const_f64_as_f64x8!(DP1, 7.853981554508209228515625E-1 * 2.);
1367 const_f64_as_f64x8!(DP2, 7.94662735614792836714E-9 * 2.);
1368 const_f64_as_f64x8!(DP3, 3.06161699786838294307E-17 * 2.);
1369
1370 const_f64_as_f64x8!(TWO_OVER_PI, 2.0 / core::f64::consts::PI);
1371
1372 let xa = self.abs();
1373
1374 let y = (xa * TWO_OVER_PI).round();
1375 let q = y.round_int();
1376
1377 let x = y.mul_neg_add(DP3, y.mul_neg_add(DP2, y.mul_neg_add(DP1, xa)));
1378
1379 let x2 = x * x;
1380 let mut s = polynomial_5!(x2, P0sin, P1sin, P2sin, P3sin, P4sin, P5sin);
1381 let mut c = polynomial_5!(x2, P0cos, P1cos, P2cos, P3cos, P4cos, P5cos);
1382 s = (x * x2).mul_add(s, x);
1383 c =
1384 (x2 * x2).mul_add(c, x2.mul_neg_add(f64x8::from(0.5), f64x8::from(1.0)));
1385
1386 let swap = !((q & i64x8::from(1)).simd_eq(i64x8::from(0)));
1387
1388 let mut overflow: f64x8 = cast(q.simd_gt(i64x8::from(0x80000000000000)));
1389 overflow &= xa.is_finite();
1390 s = overflow.blend(f64x8::from(0.0), s);
1391 c = overflow.blend(f64x8::from(1.0), c);
1392
1393 let mut sin1 = cast::<_, f64x8>(swap).blend(c, s);
1395 let sign_sin: i64x8 = (q << 62) ^ cast::<_, i64x8>(self);
1396 sin1 = sin1.flip_signs(cast(sign_sin));
1397
1398 let mut cos1 = cast::<_, f64x8>(swap).blend(s, c);
1400 let sign_cos: i64x8 = ((q + i64x8::from(1)) & i64x8::from(2)) << 62;
1401 cos1 ^= cast::<_, f64x8>(sign_cos);
1402
1403 let finite = self.is_finite();
1405 let nan = Self::splat(f64::NAN);
1406 let sin_final = finite.blend(sin1, nan);
1407 let cos_final = finite.blend(cos1, nan);
1408
1409 (sin_final, cos_final)
1410 }
1411 #[inline]
1412 #[must_use]
1413 pub fn sin(self) -> Self {
1414 let (s, _) = self.sin_cos();
1415 s
1416 }
1417 #[inline]
1418 #[must_use]
1419 pub fn cos(self) -> Self {
1420 let (_, c) = self.sin_cos();
1421 c
1422 }
1423 #[inline]
1424 #[must_use]
1425 pub fn tan(self) -> Self {
1426 let (s, c) = self.sin_cos();
1427 s / c
1428 }
1429
1430 #[inline]
1432 #[must_use]
1433 pub fn sinh(self) -> Self {
1434 const_f64_as_f64x8!(P0, 1.0);
1435 const_f64_as_f64x8!(P1, 1.0 / 6.0);
1436 const_f64_as_f64x8!(P2, 1.0 / 120.0);
1437 const_f64_as_f64x8!(P3, 1.0 / 5040.0);
1438 const_f64_as_f64x8!(P4, 1.0 / 362880.0);
1439 const_f64_as_f64x8!(P5, 1.0 / 39916800.0);
1440 const_f64_as_f64x8!(P6, 1.0 / 6227020800.0);
1441 let a = self.abs();
1442 let small = a.simd_lt(f64x8::from(0.5));
1444 let t = a * a;
1445 let poly = a * polynomial_6!(t, P0, P1, P2, P3, P4, P5, P6);
1446 let exp_based = {
1447 let e = a.exp();
1448 (e - Self::ONE / e) * Self::HALF
1449 };
1450 let result = small.blend(poly, exp_based);
1451 result.flip_signs(self)
1452 }
1453
1454 #[inline]
1456 #[must_use]
1457 pub fn cosh(self) -> Self {
1458 const_f64_as_f64x8!(P0, 1.0);
1459 const_f64_as_f64x8!(P1, 1.0 / 2.0);
1460 const_f64_as_f64x8!(P2, 1.0 / 24.0);
1461 const_f64_as_f64x8!(P3, 1.0 / 720.0);
1462 const_f64_as_f64x8!(P4, 1.0 / 40320.0);
1463 const_f64_as_f64x8!(P5, 1.0 / 3628800.0);
1464 const_f64_as_f64x8!(P6, 1.0 / 479001600.0);
1465 const_f64_as_f64x8!(P7, 1.0 / 87178291200.0);
1466 let a = self.abs();
1467 let small = a.simd_lt(f64x8::from(0.5));
1469 let t = a * a;
1470 let poly = polynomial_7!(t, P0, P1, P2, P3, P4, P5, P6, P7);
1471 let exp_based = {
1472 let e = a.exp();
1473 (e + Self::ONE / e) * Self::HALF
1474 };
1475 small.blend(poly, exp_based)
1476 }
1477
1478 #[inline]
1480 #[must_use]
1481 pub fn tanh(self) -> Self {
1482 let a = self.abs();
1486 let large = a.simd_gt(f64x8::from(19.062));
1487 if large.all() {
1488 return Self::ONE.flip_signs(self);
1489 }
1490 let small = a.simd_lt(f64x8::from(5e-8));
1491 let exp_based = {
1492 let t = (Self::from(-2.0) * a).exp_m1();
1493 let pos = -t / (t + Self::from(2.0));
1494 pos.flip_signs(self)
1495 };
1496 let result = small.blend(self, exp_based);
1497 large.blend(Self::ONE.flip_signs(self), result)
1498 }
1499
1500 #[inline]
1502 #[must_use]
1503 pub fn cbrt(self) -> Self {
1504 let a = self.abs();
1505 let zero = a.simd_eq(Self::ZERO);
1506 if zero.all() {
1507 return self; }
1509 let inf = a.is_inf();
1510 let nan = self.is_nan();
1511
1512 const SUBN_SCALE: f64 = 1.8014398509481984e16;
1513 const SUBN_CBRT: f64 = 262144.0;
1514 let tiny = a.simd_lt(Self::from(f64::MIN_POSITIVE));
1515 let a = tiny.blend(a * Self::from(SUBN_SCALE), a);
1516
1517 let e = Self::exponent(a) + Self::ONE;
1518 let d = Self::fraction_2(a);
1519
1520 const_f64_as_f64x8!(C0, 2.2307275302496609725722);
1525 const_f64_as_f64x8!(C1, -3.85841935510444988821632);
1526 const_f64_as_f64x8!(C2, 6.03990368989458747961407);
1527 const_f64_as_f64x8!(C3, -5.73353060922947843636166);
1528 const_f64_as_f64x8!(C4, 2.96155103020039511818595);
1529 const_f64_as_f64x8!(C5, -0.640245898480692909870982);
1530 let mut x = polynomial_5!(d, C0, C1, C2, C3, C4, C5);
1531
1532 let x2 = x * x;
1534 let x4 = x2 * x2;
1535 x = x - d.mul_add(x4, -x) * Self::from(1.0 / 3.0);
1536
1537 let mut y = (d * x) * x;
1539 let yx = y * x;
1540 let t = Self::from(2.0 / 3.0);
1541 y = y - t * y * (yx - Self::ONE);
1542
1543 let three = Self::from(3.0);
1545 let two = Self::from(2.0);
1546 let neg = e.simd_lt(Self::ZERO);
1547 let e_adj = neg.blend(e - two, e);
1548 let k = (e_adj / three).trunc();
1549 let r = e - three * k;
1550 const_f64_as_f64x8!(CBRT2, 1.2599210498948732);
1551 const_f64_as_f64x8!(CBRT4, 1.5874010519681994);
1552 y = r.simd_eq(Self::ONE).blend(y * CBRT2, y);
1553 y = r.simd_eq(two).blend(y * CBRT4, y);
1554 y *= Self::vm_pow2n(k);
1555 y = tiny.blend(y / Self::from(SUBN_CBRT), y);
1556
1557 let result = y.flip_signs(self);
1558 let result = nan.blend(self, result);
1559 let result = zero.blend(self, result);
1560 let result = inf.blend(self, result);
1561 result
1562 }
1563
1564 #[inline]
1565 #[must_use]
1566 pub fn to_degrees(self) -> Self {
1567 const_f64_as_f64x8!(RAD_TO_DEG_RATIO, 180.0_f64 / core::f64::consts::PI);
1568 self * RAD_TO_DEG_RATIO
1569 }
1570 #[inline]
1571 #[must_use]
1572 pub fn to_radians(self) -> Self {
1573 const_f64_as_f64x8!(DEG_TO_RAD_RATIO, core::f64::consts::PI / 180.0_f64);
1574 self * DEG_TO_RAD_RATIO
1575 }
1576
1577 #[inline]
1578 #[must_use]
1579 pub fn recip(self) -> Self {
1580 Self::ONE / self
1583 }
1584
1585 #[inline]
1586 #[must_use]
1587 pub fn recip_sqrt(self) -> Self {
1588 Self::ONE / self.sqrt()
1591 }
1592
1593 #[inline]
1594 #[must_use]
1595 pub fn sqrt(self) -> Self {
1596 pick! {
1597 if #[cfg(target_feature="avx512f")] {
1598 Self { avx512: sqrt_m512d(self.avx512) }
1599 } else {
1600 Self {
1601 a : self.a.sqrt(),
1602 b : self.b.sqrt(),
1603 }
1604 }
1605 }
1606 }
1607 #[inline]
1608 #[must_use]
1609 #[doc(alias("movemask", "move_mask"))]
1610 pub fn to_bitmask(self) -> u32 {
1611 pick! {
1612 if #[cfg(target_feature="avx512f")] {
1613 movepi64_mask_m512d(self.avx512) as u32
1614 } else {
1615 (self.b.to_bitmask() << 4) | self.a.to_bitmask()
1616 }
1617 }
1618 }
1619 #[inline]
1620 #[must_use]
1621 pub fn any(self) -> bool {
1622 pick! {
1623 if #[cfg(target_feature="avx512f")] {
1624 movepi64_mask_m512d(self.avx512) != 0
1625 } else {
1626 self.a.any() || self.b.any()
1627 }
1628 }
1629 }
1630 #[inline]
1631 #[must_use]
1632 pub fn all(self) -> bool {
1633 pick! {
1634 if #[cfg(target_feature="avx512f")] {
1635 movepi64_mask_m512d(self.avx512) == 0b11111111
1636 } else {
1637 self.a.all() && self.b.all()
1638 }
1639 }
1640 }
1641
1642 #[inline]
1643 #[must_use]
1644 pub fn none(self) -> bool {
1645 !self.any()
1646 }
1647
1648 #[inline]
1649 fn vm_pow2n(self) -> Self {
1650 const_f64_as_f64x8!(pow2_52, 4503599627370496.0);
1651 const_f64_as_f64x8!(bias, 1023.0);
1652 let a = self + (bias + pow2_52);
1653 let c = cast::<_, i64x8>(a) << 52;
1654 let std_result = cast::<_, f64x8>(c);
1655
1656 let min_exp = f64x8::from(-1022.0);
1657 let is_sub = self.simd_lt(min_exp);
1658 if is_sub.any() {
1659 let valid = self.simd_ge(f64x8::from(-1074.0));
1660 let shift_f = self + f64x8::from(1074.0);
1661 let mut shift_i = shift_f.trunc_int();
1662 shift_i = cast::<_, i64x8>(valid).blend(shift_i, i64x8::ZERO);
1663 let mantissa = i64x8::ONE << shift_i;
1664 let sub_result = cast::<_, f64x8>(mantissa);
1665 let sub_result = valid.blend(sub_result, f64x8::ZERO);
1666 is_sub.blend(sub_result, std_result)
1667 } else {
1668 std_result
1669 }
1670 }
1671
1672 #[inline]
1674 #[must_use]
1675 pub fn exp(self) -> Self {
1676 const_f64_as_f64x8!(P2, 1.0 / 2.0);
1677 const_f64_as_f64x8!(P3, 1.0 / 6.0);
1678 const_f64_as_f64x8!(P4, 1.0 / 24.0);
1679 const_f64_as_f64x8!(P5, 1.0 / 120.0);
1680 const_f64_as_f64x8!(P6, 1.0 / 720.0);
1681 const_f64_as_f64x8!(P7, 1.0 / 5040.0);
1682 const_f64_as_f64x8!(P8, 1.0 / 40320.0);
1683 const_f64_as_f64x8!(P9, 1.0 / 362880.0);
1684 const_f64_as_f64x8!(P10, 1.0 / 3628800.0);
1685 const_f64_as_f64x8!(P11, 1.0 / 39916800.0);
1686 const_f64_as_f64x8!(P12, 1.0 / 479001600.0);
1687 const_f64_as_f64x8!(P13, 1.0 / 6227020800.0);
1688 const_f64_as_f64x8!(LN2D_HI, 0.693145751953125);
1692 const_f64_as_f64x8!(LN2D_LO, 1.42860682030941723212E-6);
1693 let max_x = f64x8::from(709.783);
1694 let min_x = f64x8::from(-744.79);
1695 let finite = self.is_finite();
1696 let neg_underflow = self.simd_lt(min_x) & finite;
1698 if neg_underflow.all() {
1699 return Self::ZERO;
1700 }
1701 let max_r = f64x8::from(1023.0);
1702 let r = (self * Self::LOG2_E).round();
1703 let big = r.simd_gt(max_r);
1704 let r_safe = big.blend(max_r, r);
1705 let excess = r - max_r;
1706 let excess = big.blend(excess, Self::ZERO);
1707 let scale = Self::vm_pow2n(excess);
1708 let x = r.mul_neg_add(LN2D_HI, self);
1709 let x = r.mul_neg_add(LN2D_LO, x);
1710 let z =
1711 polynomial_13!(x, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13);
1712 let n2 = Self::vm_pow2n(r_safe);
1713 let z = (z + Self::ONE) * scale * n2;
1714 let nan_mask = self.is_nan();
1715 let mut result = nan_mask.blend(Self::nan_pow(), z);
1716 let pos_overflow = self.simd_gt(max_x) & finite;
1717 result = pos_overflow.blend(Self::infinity(), result);
1718 result = neg_underflow.blend(Self::ZERO, result);
1719 let pos_inf = !finite & !self.is_sign_negative() & !nan_mask;
1720 result = pos_inf.blend(Self::infinity(), result);
1721 let neg_inf = !finite & self.is_sign_negative() & !nan_mask;
1722 result = neg_inf.blend(Self::ZERO, result);
1723 result
1724 }
1725
1726 #[inline]
1729 #[must_use]
1730 pub fn exp_m1(self) -> Self {
1731 const_f64_as_f64x8!(P2, 1.0 / 2.0);
1732 const_f64_as_f64x8!(P3, 1.0 / 6.0);
1733 const_f64_as_f64x8!(P4, 1.0 / 24.0);
1734 const_f64_as_f64x8!(P5, 1.0 / 120.0);
1735 const_f64_as_f64x8!(P6, 1.0 / 720.0);
1736 const_f64_as_f64x8!(P7, 1.0 / 5040.0);
1737 const_f64_as_f64x8!(P8, 1.0 / 40320.0);
1738 const_f64_as_f64x8!(P9, 1.0 / 362880.0);
1739 const_f64_as_f64x8!(P10, 1.0 / 3628800.0);
1740 const_f64_as_f64x8!(P11, 1.0 / 39916800.0);
1741 const_f64_as_f64x8!(P12, 1.0 / 479001600.0);
1742 const_f64_as_f64x8!(P13, 1.0 / 6227020800.0);
1743 const_f64_as_f64x8!(LN2D_HI, 0.693145751953125);
1746 const_f64_as_f64x8!(LN2D_LO, 1.42860682030941723212E-6);
1747 if self.simd_lt(f64x8::from(-37.429)).all() {
1751 return f64x8::from(-1.0);
1752 }
1753 let max_x = f64x8::from(709.783);
1757 let min_x = f64x8::from(-744.79);
1758 let max_r = f64x8::from(1023.0);
1759 let r = (self * Self::LOG2_E).round();
1760 let big = r.simd_gt(max_r);
1761 let r_safe = big.blend(max_r, r);
1762 let excess = r - max_r;
1763 let excess = big.blend(excess, Self::ZERO);
1764 let scale = Self::vm_pow2n(excess);
1765 let x = r.mul_neg_add(LN2D_HI, self);
1766 let x = r.mul_neg_add(LN2D_LO, x);
1767 let z =
1768 polynomial_13!(x, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13);
1769 let n2 = Self::vm_pow2n(r_safe);
1770 let exp_val = (z + Self::ONE) * scale * n2;
1771 let r_is_zero = r.simd_eq(Self::ZERO);
1775 let z = r_is_zero.blend(z, exp_val - Self::ONE);
1776 let nan_mask = self.is_nan();
1777 let finite = self.is_finite();
1778 let mut result = nan_mask.blend(Self::nan_pow(), z);
1779 let pos_overflow = self.simd_gt(max_x) & finite;
1780 result = pos_overflow.blend(Self::infinity(), result);
1781 let neg_underflow = self.simd_lt(min_x) & finite;
1782 result = neg_underflow.blend(-Self::ONE, result);
1783 let pos_inf = !finite & !self.is_sign_negative() & !nan_mask;
1784 result = pos_inf.blend(Self::infinity(), result);
1785 let neg_inf = !finite & self.is_sign_negative() & !nan_mask;
1786 result = neg_inf.blend(-Self::ONE, result);
1787 let is_zero = self.simd_eq(Self::ZERO);
1788 result = is_zero.blend(self, result);
1789 result
1790 }
1791
1792 #[inline]
1794 #[must_use]
1795 pub fn exp2(self) -> Self {
1796 const_f64_as_f64x8!(P2, 1.0 / 2.0);
1797 const_f64_as_f64x8!(P3, 1.0 / 6.0);
1798 const_f64_as_f64x8!(P4, 1.0 / 24.0);
1799 const_f64_as_f64x8!(P5, 1.0 / 120.0);
1800 const_f64_as_f64x8!(P6, 1.0 / 720.0);
1801 const_f64_as_f64x8!(P7, 1.0 / 5040.0);
1802 const_f64_as_f64x8!(P8, 1.0 / 40320.0);
1803 const_f64_as_f64x8!(P9, 1.0 / 362880.0);
1804 const_f64_as_f64x8!(P10, 1.0 / 3628800.0);
1805
1806 let max_x = f64x8::from(1023.9999999999999);
1809 let min_x = f64x8::from(-1074.5);
1810 let finite = self.is_finite();
1811 let neg_underflow = self.simd_lt(min_x) & finite;
1812 if neg_underflow.all() {
1813 return Self::ZERO;
1814 }
1815
1816 let round = self.round();
1817 let max_r = f64x8::from(1023.0);
1818 let big = round.simd_gt(max_r);
1819 let r_safe = big.blend(max_r, round);
1820 let excess = round - max_r;
1821 let excess = big.blend(excess, Self::ZERO);
1822 let scale = Self::vm_pow2n(excess);
1823
1824 let fract = (self - round) * Self::LN_2;
1825 let fract_partial_exp2 =
1826 polynomial_8!(fract, P2, P3, P4, P5, P6, P7, P8, P9, P10);
1827 let fract2 = fract * fract;
1828 let fract_exp2 = fract_partial_exp2.mul_add(fract2, fract) + Self::ONE;
1829
1830 let n2 = Self::vm_pow2n(r_safe);
1831 let result = fract_exp2 * scale * n2;
1832
1833 let nan_mask = self.is_nan();
1834 let mut result = nan_mask.blend(Self::nan_pow(), result);
1835 let pos_overflow = self.simd_gt(max_x) & finite;
1836 result = pos_overflow.blend(Self::infinity(), result);
1837 result = neg_underflow.blend(Self::ZERO, result);
1838 let pos_inf = !finite & !self.is_sign_negative() & !nan_mask;
1839 result = pos_inf.blend(Self::infinity(), result);
1840 let neg_inf = !finite & self.is_sign_negative() & !nan_mask;
1841 result = neg_inf.blend(Self::ZERO, result);
1842 result
1843 }
1844
1845 #[inline]
1846 fn exponent(self) -> f64x8 {
1847 const_f64_as_f64x8!(pow2_52, 4503599627370496.0);
1848 const_f64_as_f64x8!(bias, 1023.0);
1849 let a = cast::<_, u64x8>(self);
1850 let b = a >> 52;
1851 let c = b | cast::<_, u64x8>(pow2_52);
1852 let d = cast::<_, f64x8>(c);
1853 let e = d - (pow2_52 + bias);
1854 e
1855 }
1856
1857 #[inline]
1858 fn fraction_2(self) -> Self {
1859 let t1 = cast::<_, u64x8>(self);
1860 let t2 = cast::<_, u64x8>(
1861 (t1 & u64x8::from(0x000FFFFFFFFFFFFF)) | u64x8::from(0x3FE0000000000000),
1862 );
1863 cast::<_, f64x8>(t2)
1864 }
1865 #[inline]
1866 fn is_zero_or_subnormal(self) -> Self {
1867 let t = cast::<_, i64x8>(self);
1868 let t = t & i64x8::splat(0x7FF0000000000000);
1869 let mask = t.simd_eq(i64x8::splat(0));
1870 cast::<_, f64x8>(mask)
1871 }
1872 #[inline]
1873 fn infinity() -> Self {
1874 cast::<_, f64x8>(i64x8::splat(0x7FF0000000000000))
1875 }
1876 #[inline]
1877 fn nan_log() -> Self {
1878 cast::<_, f64x8>(i64x8::splat(0x7FF8000000000000 | 0x101 << 29))
1879 }
1880 #[inline]
1881 fn nan_pow() -> Self {
1882 cast::<_, f64x8>(i64x8::splat(0x7FF8000000000000 | 0x101 << 29))
1883 }
1884
1885 #[inline]
1888 #[must_use]
1889 pub fn is_sign_positive(self) -> Self {
1890 const SIGN_MASK: u64x8 = u64x8::splat((-0.0_f64).to_bits());
1891
1892 let bits = cast::<f64x8, u64x8>(self);
1893 let sign = bits & SIGN_MASK;
1894 let result = sign.simd_eq(u64x8::ZERO);
1895 cast::<u64x8, f64x8>(result)
1896 }
1897
1898 #[inline]
1901 #[must_use]
1902 pub fn is_sign_negative(self) -> Self {
1903 const SIGN_MASK: u64x8 = u64x8::splat((-0.0_f64).to_bits());
1904
1905 let bits = cast::<f64x8, u64x8>(self);
1906 let sign = bits & SIGN_MASK;
1907 let result = sign.simd_eq(SIGN_MASK);
1908 cast::<u64x8, f64x8>(result)
1909 }
1910
1911 #[inline]
1912 pub fn reduce_add(self) -> f64 {
1913 pick! {
1914 if #[cfg(target_feature="avx512f")] {
1915 let lo = cast_to_m256d_from_m512d(self.avx512);
1917 let hi = extract_m256d_from_m512d::<1>(self.avx512);
1918 let v = add_m256d(lo, hi); let t = add_horizontal_m256d(v, v); let lo = cast_to_m128d_from_m256d(t); let hi = extract_m128d_from_m256d::<1>(t); let s = add_m128d(lo, hi); get_f64_from_m128d_s(s)
1924 } else {
1925 self.a.reduce_add() + self.b.reduce_add()
1926 }
1927 }
1928 }
1929
1930 #[inline]
1931 #[must_use]
1932 pub fn reduce_mul(self) -> f64 {
1933 pick! {
1934 if #[cfg(target_feature="avx512f")] {
1935 let lo = cast_to_m256d_from_m512d(self.avx512);
1937 let hi = extract_m256d_from_m512d::<1>(self.avx512);
1938 let v = mul_m256d(lo, hi);
1939 let lo = cast_to_m128d_from_m256d(v);
1940 let hi = extract_m128d_from_m256d::<1>(v);
1941 let lo = mul_m128d(lo,hi);
1942 let hi64 = unpack_high_m128d(lo,lo);
1943 let product = mul_m128d_s(lo,hi64);
1944 get_f64_from_m128d_s(product)
1945 } else {
1946 self.a.reduce_mul() * self.b.reduce_mul()
1947 }
1948 }
1949 }
1950
1951 #[inline]
1953 #[must_use]
1954 pub fn ln(self) -> Self {
1955 const_f64_as_f64x8!(HALF, 0.5);
1956 const_f64_as_f64x8!(P0, 7.70838733755885391666E0);
1957 const_f64_as_f64x8!(P1, 1.79368678507819816313E1);
1958 const_f64_as_f64x8!(P2, 1.44989225341610930846E1);
1959 const_f64_as_f64x8!(P3, 4.70579119878881725854E0);
1960 const_f64_as_f64x8!(P4, 4.97494994976747001425E-1);
1961 const_f64_as_f64x8!(P5, 1.01875663804580931796E-4);
1962
1963 const_f64_as_f64x8!(Q0, 2.31251620126765340583E1);
1964 const_f64_as_f64x8!(Q1, 7.11544750618563894466E1);
1965 const_f64_as_f64x8!(Q2, 8.29875266912776603211E1);
1966 const_f64_as_f64x8!(Q3, 4.52279145837532221105E1);
1967 const_f64_as_f64x8!(Q4, 1.12873587189167450590E1);
1968 const_f64_as_f64x8!(LN2F_HI, f64::from_bits(0x3FE62E42FEE00000));
1974 const_f64_as_f64x8!(LN2F_LO, f64::from_bits(0x3DEA39EF35793C76));
1975 const_f64_as_f64x8!(VM_SQRT2, 1.414213562373095048801);
1976 const_f64_as_f64x8!(VM_SMALLEST_NORMAL, 2.2250738585072014E-308);
1977
1978 let x1 = self;
1979 let x = Self::fraction_2(x1);
1980 let e = Self::exponent(x1);
1981 let mask = x.simd_gt(VM_SQRT2 * HALF);
1982 let x = (!mask).blend(x + x, x);
1983 let fe = mask.blend(e + Self::ONE, e);
1984 let x = x - Self::ONE;
1985 let px = polynomial_5!(x, P0, P1, P2, P3, P4, P5);
1986 let x2 = x * x;
1987 let px = x2 * x * px;
1988 let qx = polynomial_5n!(x, Q0, Q1, Q2, Q3, Q4);
1989 let res = px / qx;
1990 let res = fe.mul_add(LN2F_LO, res);
1991 let res = res + x2.mul_neg_add(HALF, x);
1992 let res = fe.mul_add(LN2F_HI, res);
1993 let overflow = !self.is_finite();
1994 let underflow = x1.simd_lt(VM_SMALLEST_NORMAL);
1995 let mask = overflow | underflow;
1996 if !mask.any() {
1997 res
1998 } else {
1999 let is_zero = self.is_zero_or_subnormal();
2000 let res = underflow.blend(Self::nan_log(), res);
2001 let res = is_zero.blend(-Self::infinity(), res);
2006 let res = overflow.blend(self, res);
2007 let res = (!self.is_finite() & self.is_sign_negative())
2009 .blend(Self::nan_log(), res);
2010 res
2011 }
2012 }
2013
2014 #[inline]
2017 #[must_use]
2018 pub fn ln_1p(self) -> Self {
2019 let u = self + Self::ONE;
2027 let eq = u.simd_eq(Self::ONE);
2028 let ln_u = Self::ln(u);
2029 let correction = self * (ln_u / (u - Self::ONE));
2030 let result = eq.blend(self, correction);
2031 let over = u.is_inf();
2032 over.blend(ln_u, result)
2033 }
2034
2035 #[inline]
2036 #[must_use]
2037 pub fn log2(self) -> Self {
2038 Self::ln(self) * Self::LOG2_E
2039 }
2040 #[inline]
2041 #[must_use]
2042 pub fn log10(self) -> Self {
2043 Self::ln(self) * Self::LOG10_E
2044 }
2045
2046 #[inline]
2047 #[must_use]
2048 pub fn pow_f64x8(self, y: Self) -> Self {
2049 const_f64_as_f64x8!(ln2d_hi, 0.693145751953125);
2050 const_f64_as_f64x8!(ln2d_lo, 1.42860682030941723212E-6);
2051 const_f64_as_f64x8!(P0log, 2.0039553499201281259648E1);
2052 const_f64_as_f64x8!(P1log, 5.7112963590585538103336E1);
2053 const_f64_as_f64x8!(P2log, 6.0949667980987787057556E1);
2054 const_f64_as_f64x8!(P3log, 2.9911919328553073277375E1);
2055 const_f64_as_f64x8!(P4log, 6.5787325942061044846969E0);
2056 const_f64_as_f64x8!(P5log, 4.9854102823193375972212E-1);
2057 const_f64_as_f64x8!(P6log, 4.5270000862445199635215E-5);
2058 const_f64_as_f64x8!(Q0log, 6.0118660497603843919306E1);
2059 const_f64_as_f64x8!(Q1log, 2.1642788614495947685003E2);
2060 const_f64_as_f64x8!(Q2log, 3.0909872225312059774938E2);
2061 const_f64_as_f64x8!(Q3log, 2.2176239823732856465394E2);
2062 const_f64_as_f64x8!(Q4log, 8.3047565967967209469434E1);
2063 const_f64_as_f64x8!(Q5log, 1.5062909083469192043167E1);
2064
2065 const_f64_as_f64x8!(p2, 1.0 / 2.0); const_f64_as_f64x8!(p3, 1.0 / 6.0);
2068 const_f64_as_f64x8!(p4, 1.0 / 24.0);
2069 const_f64_as_f64x8!(p5, 1.0 / 120.0);
2070 const_f64_as_f64x8!(p6, 1.0 / 720.0);
2071 const_f64_as_f64x8!(p7, 1.0 / 5040.0);
2072 const_f64_as_f64x8!(p8, 1.0 / 40320.0);
2073 const_f64_as_f64x8!(p9, 1.0 / 362880.0);
2074 const_f64_as_f64x8!(p10, 1.0 / 3628800.0);
2075 const_f64_as_f64x8!(p11, 1.0 / 39916800.0);
2076 const_f64_as_f64x8!(p12, 1.0 / 479001600.0);
2077 const_f64_as_f64x8!(p13, 1.0 / 6227020800.0);
2078
2079 let x1 = self.abs();
2080 let x = x1.fraction_2();
2081 let mask = x.simd_gt(f64x8::SQRT_2 * f64x8::HALF);
2082 let x = (!mask).blend(x + x, x);
2083 let x = x - f64x8::ONE;
2084 let x2 = x * x;
2085 let px = polynomial_6!(x, P0log, P1log, P2log, P3log, P4log, P5log, P6log);
2086 let px = px * x * x2;
2087 let qx = polynomial_6n!(x, Q0log, Q1log, Q2log, Q3log, Q4log, Q5log);
2088 let lg1 = px / qx;
2089
2090 let ef = x1.exponent();
2091 let ef = mask.blend(ef + f64x8::ONE, ef);
2092 let e1 = (ef * y).round();
2093 let yr = ef.mul_sub(y, e1);
2094
2095 let lg = f64x8::HALF.mul_neg_add(x2, x) + lg1;
2096 let x2err = (f64x8::HALF * x).mul_sub(x, f64x8::HALF * x2);
2097 let lg_err = f64x8::HALF.mul_add(x2, lg - x) - lg1;
2098
2099 let e2 = (lg * y * f64x8::LOG2_E).round();
2100 let v = lg.mul_sub(y, e2 * ln2d_hi);
2101 let v = e2.mul_neg_add(ln2d_lo, v);
2102 let v = v - (lg_err + x2err).mul_sub(y, yr * f64x8::LN_2);
2103
2104 let x = v;
2105 let e3 = (x * f64x8::LOG2_E).round();
2106 let x = e3.mul_neg_add(f64x8::LN_2, x);
2107 let z =
2108 polynomial_13!(x, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13)
2109 + f64x8::ONE;
2110 let ee = e1 + e2 + e3;
2111 let ei = cast::<_, i64x8>(ee.round_int());
2112 let ej = cast::<_, i64x8>(ei + (cast::<_, i64x8>(z) >> 52));
2113
2114 let overflow = cast::<_, f64x8>(!ej.simd_lt(i64x8::splat(0x07FF)))
2115 | ee.simd_gt(f64x8::splat(3000.0));
2116 let underflow = cast::<_, f64x8>(!ej.simd_gt(i64x8::splat(0x000)))
2117 | ee.simd_lt(f64x8::splat(-3000.0));
2118
2119 let z = cast::<_, f64x8>(cast::<_, i64x8>(z) + (ei << 52));
2121
2122 let z = if (overflow | underflow).any() {
2124 let z = underflow.blend(f64x8::ZERO, z);
2125 overflow.blend(Self::infinity(), z)
2126 } else {
2127 z
2128 };
2129
2130 let x_zero = self.is_zero_or_subnormal();
2132 let z = x_zero.blend(
2133 y.simd_lt(f64x8::ZERO).blend(
2134 Self::infinity(),
2135 y.simd_eq(f64x8::ZERO).blend(f64x8::ONE, f64x8::ZERO),
2136 ),
2137 z,
2138 );
2139
2140 let x_sign = self.is_sign_negative();
2141
2142 let z = if x_sign.any() {
2143 let yi = y.simd_eq(y.round());
2145 let y_odd = cast::<_, i64x8>(y.round_int() << 63).round_float();
2147 let z1 =
2148 yi.blend(z | y_odd, self.simd_eq(Self::ZERO).blend(z, Self::nan_pow()));
2149 x_sign.blend(z1, z)
2150 } else {
2151 z
2152 };
2153
2154 let x_finite = self.is_finite();
2155 let y_finite = y.is_finite();
2156 let e_finite = ee.is_finite();
2157
2158 if (x_finite & y_finite & (e_finite | x_zero)).all() {
2159 return z;
2160 }
2161
2162 (self.is_nan() | y.is_nan()).blend(self + y, z)
2163 }
2164
2165 #[inline]
2166 pub fn powf(self, y: f64) -> Self {
2167 Self::pow_f64x8(self, f64x8::splat(y))
2168 }
2169
2170 #[must_use]
2172 #[inline]
2173 pub fn transpose(data: [f64x8; 8]) -> [f64x8; 8] {
2174 #[inline(always)]
2177 fn transpose_column(data: &[f64x8; 8], index: usize) -> f64x8 {
2178 f64x8::new([
2179 data[0].as_array()[index],
2180 data[1].as_array()[index],
2181 data[2].as_array()[index],
2182 data[3].as_array()[index],
2183 data[4].as_array()[index],
2184 data[5].as_array()[index],
2185 data[6].as_array()[index],
2186 data[7].as_array()[index],
2187 ])
2188 }
2189
2190 [
2191 transpose_column(&data, 0),
2192 transpose_column(&data, 1),
2193 transpose_column(&data, 2),
2194 transpose_column(&data, 3),
2195 transpose_column(&data, 4),
2196 transpose_column(&data, 5),
2197 transpose_column(&data, 6),
2198 transpose_column(&data, 7),
2199 ]
2200 }
2201
2202 #[inline]
2203 #[must_use]
2204 pub fn to_array(self) -> [f64; 8] {
2205 cast(self)
2206 }
2207
2208 #[inline]
2209 #[must_use]
2210 pub fn as_array(&self) -> &[f64; 8] {
2211 cast_ref(self)
2212 }
2213
2214 #[inline]
2215 #[must_use]
2216 pub fn as_mut_array(&mut self) -> &mut [f64; 8] {
2217 cast_mut(self)
2218 }
2219
2220 #[inline]
2221 pub fn from_i32x8(v: i32x8) -> Self {
2222 pick! {
2223 if #[cfg(target_feature="avx512f")] {
2224 Self { avx512: convert_to_m512d_from_i32_m256i(v.avx2) }
2225 } else {
2226 Self::new([
2227 v.as_array()[0] as f64,
2228 v.as_array()[1] as f64,
2229 v.as_array()[2] as f64,
2230 v.as_array()[3] as f64,
2231 v.as_array()[4] as f64,
2232 v.as_array()[5] as f64,
2233 v.as_array()[6] as f64,
2234 v.as_array()[7] as f64,
2235 ])
2236 }
2237 }
2238 }
2239}
2240
2241impl From<i32x8> for f64x8 {
2242 #[inline]
2243 fn from(v: i32x8) -> Self {
2244 Self::from_i32x8(v)
2245 }
2246}
2247
2248impl Not for f64x8 {
2249 type Output = Self;
2250 #[inline]
2251 fn not(self) -> Self::Output {
2252 pick! {
2253 if #[cfg(target_feature="avx512f")] {
2254 Self { avx512: bitxor_m512d(self.avx512, set_splat_m512d(f64::from_bits(u64::MAX))) }
2255 } else {
2256 Self {
2257 a : self.a.not(),
2258 b : self.b.not(),
2259 }
2260 }
2261 }
2262 }
2263}