1#![allow(clippy::approx_constant)]
8#![allow(clippy::excessive_precision)]
9
10#[cfg(feature = "simd")]
11use std::simd::{StdFloat, cmp::SimdPartialOrd, f32x4, f64x4, num::SimdFloat};
12use std::{
13 fmt::Debug,
14 ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign},
15};
16
17#[cfg(feature = "simd")]
19pub const SIMD_LANES: usize = 4;
20
21pub trait Sample:
30 Debug
31 + Copy
32 + Clone
33 + Send
34 + Sync
35 + Add<Output = Self>
36 + Sub<Output = Self>
37 + Mul<Output = Self>
38 + Div<Output = Self>
39 + Neg<Output = Self>
40 + AddAssign
41 + SubAssign
42 + MulAssign
43 + DivAssign
44 + PartialOrd
45 + PartialEq
46 + 'static
47{
48 const ZERO: Self;
50
51 const ONE: Self;
53
54 const EPSILON: Self;
57
58 const PI: Self;
60
61 const INV_PI: Self;
63
64 const FRAC_PI_2: Self;
66
67 const FRAC_PI_3: Self;
69
70 const FRAC_PI_4: Self;
72
73 const TAU: Self;
75
76 const INV_TAU: Self;
78
79 const PHI: Self;
81
82 const E: Self;
84
85 const SQRT_2: Self;
87
88 const INV_SQRT_2: Self;
90
91 fn from_f64(value: f64) -> Self;
93
94 fn to_f64(self) -> f64;
96
97 fn abs(self) -> Self;
99
100 #[cfg(feature = "simd")]
104 type Simd: SimdFloat<Scalar = Self>
105 + StdFloat
106 + SimdPartialOrd
107 + Copy
108 + Add<Output = Self::Simd>
109 + Sub<Output = Self::Simd>
110 + Mul<Output = Self::Simd>
111 + Div<Output = Self::Simd>
112 + std::ops::Rem<Output = Self::Simd>;
113
114 #[cfg(feature = "simd")]
116 fn simd_splat(value: Self) -> Self::Simd;
117
118 #[cfg(feature = "simd")]
120 fn simd_from_slice(slice: &[Self]) -> Self::Simd;
121
122 #[cfg(feature = "simd")]
124 fn simd_to_array(simd: Self::Simd) -> [Self; SIMD_LANES];
125
126 #[cfg(feature = "simd")]
129 fn simd_select_gt(a: Self::Simd, b: Self::Simd, if_true: Self::Simd, if_false: Self::Simd) -> Self::Simd;
130
131 #[cfg(feature = "simd")]
134 fn simd_select_lt(a: Self::Simd, b: Self::Simd, if_true: Self::Simd, if_false: Self::Simd) -> Self::Simd;
135
136 #[cfg(feature = "simd")]
138 fn simd_lane_offsets() -> Self::Simd;
139}
140
141impl Sample for f32 {
142 const ZERO: Self = 0.0;
143 const ONE: Self = 1.0;
144 const EPSILON: Self = 1.19209290e-07_f32;
145 const PI: Self = 3.14159265358979323846264338327950288_f32;
146 const INV_PI: Self = 0.318309886183790671537767526745028724_f32;
147 const FRAC_PI_2: Self = 1.57079632679489661923132169163975144_f32;
148 const FRAC_PI_3: Self = 1.04719755119659774615421446109316763_f32;
149 const FRAC_PI_4: Self = 0.785398163397448309615660845819875721_f32;
150 const TAU: Self = 6.28318530717958647692528676655900577_f32;
151 const INV_TAU: Self = 0.15915494309189533576882414343516084_f32;
152 const PHI: Self = 1.618033988749894848204586834365638118_f32;
153 const E: Self = 2.71828182845904523536028747135266250_f32;
154 const SQRT_2: Self = 1.41421356237309504880168872420969808_f32;
155 const INV_SQRT_2: Self = 0.707106781186547524400844362104849039_f32;
156
157 #[inline]
158 fn from_f64(value: f64) -> Self {
159 value as f32
160 }
161
162 #[inline]
163 fn to_f64(self) -> f64 {
164 self as f64
165 }
166
167 #[inline]
168 fn abs(self) -> Self {
169 self.abs()
170 }
171
172 #[cfg(feature = "simd")]
173 type Simd = f32x4;
174
175 #[cfg(feature = "simd")]
176 #[inline]
177 fn simd_splat(value: Self) -> Self::Simd {
178 f32x4::splat(value)
179 }
180
181 #[cfg(feature = "simd")]
182 #[inline]
183 fn simd_from_slice(slice: &[Self]) -> Self::Simd {
184 f32x4::from_slice(slice)
185 }
186
187 #[cfg(feature = "simd")]
188 #[inline]
189 fn simd_to_array(simd: Self::Simd) -> [Self; SIMD_LANES] {
190 simd.to_array()
191 }
192
193 #[cfg(feature = "simd")]
194 #[inline]
195 fn simd_select_gt(a: Self::Simd, b: Self::Simd, if_true: Self::Simd, if_false: Self::Simd) -> Self::Simd {
196 a.simd_gt(b).select(if_true, if_false)
197 }
198
199 #[cfg(feature = "simd")]
200 #[inline]
201 fn simd_select_lt(a: Self::Simd, b: Self::Simd, if_true: Self::Simd, if_false: Self::Simd) -> Self::Simd {
202 a.simd_lt(b).select(if_true, if_false)
203 }
204
205 #[cfg(feature = "simd")]
206 #[inline]
207 fn simd_lane_offsets() -> Self::Simd {
208 f32x4::from_array([0.0, 1.0, 2.0, 3.0])
209 }
210}
211
212impl Sample for f64 {
213 const ZERO: Self = 0.0;
214 const ONE: Self = 1.0;
215 const EPSILON: Self = 2.2204460492503131e-16_f64;
216 const PI: Self = 3.14159265358979323846264338327950288_f64;
217 const INV_PI: Self = 0.318309886183790671537767526745028724_f64;
218 const FRAC_PI_2: Self = 1.57079632679489661923132169163975144_f64;
219 const FRAC_PI_3: Self = 1.04719755119659774615421446109316763_f64;
220 const FRAC_PI_4: Self = 0.785398163397448309615660845819875721_f64;
221 const TAU: Self = 6.28318530717958647692528676655900577_f64;
222 const INV_TAU: Self = 0.15915494309189533576882414343516084_f64;
223 const PHI: Self = 1.618033988749894848204586834365638118_f64;
224 const E: Self = 2.71828182845904523536028747135266250_f64;
225 const SQRT_2: Self = 1.41421356237309504880168872420969808_f64;
226 const INV_SQRT_2: Self = 0.707106781186547524400844362104849039_f64;
227
228 #[inline]
229 fn from_f64(value: f64) -> Self {
230 value
231 }
232
233 #[inline]
234 fn to_f64(self) -> f64 {
235 self
236 }
237
238 #[inline]
239 fn abs(self) -> Self {
240 self.abs()
241 }
242
243 #[cfg(feature = "simd")]
244 type Simd = f64x4;
245
246 #[cfg(feature = "simd")]
247 #[inline]
248 fn simd_splat(value: Self) -> Self::Simd {
249 f64x4::splat(value)
250 }
251
252 #[cfg(feature = "simd")]
253 #[inline]
254 fn simd_from_slice(slice: &[Self]) -> Self::Simd {
255 f64x4::from_slice(slice)
256 }
257
258 #[cfg(feature = "simd")]
259 #[inline]
260 fn simd_to_array(simd: Self::Simd) -> [Self; SIMD_LANES] {
261 simd.to_array()
262 }
263
264 #[cfg(feature = "simd")]
265 #[inline]
266 fn simd_select_gt(a: Self::Simd, b: Self::Simd, if_true: Self::Simd, if_false: Self::Simd) -> Self::Simd {
267 a.simd_gt(b).select(if_true, if_false)
268 }
269
270 #[cfg(feature = "simd")]
271 #[inline]
272 fn simd_select_lt(a: Self::Simd, b: Self::Simd, if_true: Self::Simd, if_false: Self::Simd) -> Self::Simd {
273 a.simd_lt(b).select(if_true, if_false)
274 }
275
276 #[cfg(feature = "simd")]
277 #[inline]
278 fn simd_lane_offsets() -> Self::Simd {
279 f64x4::from_array([0.0, 1.0, 2.0, 3.0])
280 }
281}
282
283#[cfg(test)]
284mod tests {
285 use super::*;
286
287 fn approx_eq<S: Sample>(a: S, b: S, epsilon: f64) -> bool {
288 (a.to_f64() - b.to_f64()).abs() < epsilon
289 }
290
291 #[test]
292 fn test_f32_constants_accuracy() {
293 let epsilon = 1e-6;
294 assert!(approx_eq(f32::PI, std::f32::consts::PI, epsilon));
295 assert!(approx_eq(f32::TAU, std::f32::consts::TAU, epsilon));
296 assert!(approx_eq(f32::E, std::f32::consts::E, epsilon));
297 assert!(approx_eq(f32::SQRT_2, std::f32::consts::SQRT_2, epsilon));
298 assert!(approx_eq(f32::FRAC_PI_2, std::f32::consts::FRAC_PI_2, epsilon));
299 assert!(approx_eq(f32::FRAC_PI_3, std::f32::consts::FRAC_PI_3, epsilon));
300 assert!(approx_eq(f32::FRAC_PI_4, std::f32::consts::FRAC_PI_4, epsilon));
301 }
302
303 #[test]
304 fn test_f64_constants_accuracy() {
305 let epsilon = 1e-14;
306 assert!(approx_eq(f64::PI, std::f64::consts::PI, epsilon));
307 assert!(approx_eq(f64::TAU, std::f64::consts::TAU, epsilon));
308 assert!(approx_eq(f64::E, std::f64::consts::E, epsilon));
309 assert!(approx_eq(f64::SQRT_2, std::f64::consts::SQRT_2, epsilon));
310 assert!(approx_eq(f64::FRAC_PI_2, std::f64::consts::FRAC_PI_2, epsilon));
311 assert!(approx_eq(f64::FRAC_PI_3, std::f64::consts::FRAC_PI_3, epsilon));
312 assert!(approx_eq(f64::FRAC_PI_4, std::f64::consts::FRAC_PI_4, epsilon));
313 }
314
315 #[test]
316 fn test_derived_constants_f32() {
317 let epsilon = 1e-6;
318 assert!(approx_eq(f32::INV_PI, 1.0 / std::f32::consts::PI, epsilon));
319 assert!(approx_eq(f32::INV_TAU, 1.0 / std::f32::consts::TAU, epsilon));
320 assert!(approx_eq(f32::INV_SQRT_2, 1.0 / std::f32::consts::SQRT_2, epsilon));
321 }
322
323 #[test]
324 fn test_derived_constants_f64() {
325 let epsilon = 1e-14;
326 assert!(approx_eq(f64::INV_PI, 1.0 / std::f64::consts::PI, epsilon));
327 assert!(approx_eq(f64::INV_TAU, 1.0 / std::f64::consts::TAU, epsilon));
328 assert!(approx_eq(f64::INV_SQRT_2, 1.0 / std::f64::consts::SQRT_2, epsilon));
329 }
330
331 #[test]
332 fn test_zero_and_one() {
333 assert_eq!(f32::ZERO, 0.0f32);
334 assert_eq!(f32::ONE, 1.0f32);
335 assert_eq!(f64::ZERO, 0.0f64);
336 assert_eq!(f64::ONE, 1.0f64);
337 }
338
339 #[test]
340 fn test_epsilon_is_small_positive() {
341 assert!(f32::EPSILON > 0.0);
342 assert!(f32::EPSILON < 1e-5);
343 assert!(f64::EPSILON > 0.0);
344 assert!(f64::EPSILON < 1e-14);
345 }
346
347 #[test]
348 fn test_f32_to_f64_conversion() {
349 let values = [0.0f32, 1.0, -1.0, 0.5, -0.5, f32::EPSILON];
350 for v in values {
351 let converted = v.to_f64();
352 assert!((converted - v as f64).abs() < 1e-7);
353 }
354 }
355
356 #[test]
357 fn test_f64_to_f64_identity() {
358 let values = [0.0f64, 1.0, -1.0, 0.5, f64::EPSILON, std::f64::consts::PI];
359 for v in values {
360 assert_eq!(v.to_f64(), v);
361 }
362 }
363
364 #[test]
365 fn test_from_f64_f32() {
366 let values = [0.0f64, 1.0, -1.0, 0.5, -0.5];
367 for v in values {
368 let converted = f32::from_f64(v);
369 assert!((converted as f64 - v).abs() < 1e-6);
370 }
371 }
372
373 #[test]
374 fn test_from_f64_f64_identity() {
375 let values = [0.0f64, 1.0, -1.0, std::f64::consts::PI];
376 for v in values {
377 assert_eq!(f64::from_f64(v), v);
378 }
379 }
380
381 #[test]
382 fn test_roundtrip_f32() {
383 let values = [0.0f32, 1.0, -1.0, 0.5, 0.123456];
384 for v in values {
385 let roundtrip = f32::from_f64(v.to_f64());
386 assert!((roundtrip - v).abs() < f32::EPSILON * 2.0);
387 }
388 }
389
390 #[test]
391 fn test_infinity_handling_f32() {
392 let pos_inf = f32::INFINITY;
393 let neg_inf = f32::NEG_INFINITY;
394 assert!(pos_inf.to_f64().is_infinite());
395 assert!(neg_inf.to_f64().is_infinite());
396 assert!(pos_inf.to_f64() > 0.0);
397 assert!(neg_inf.to_f64() < 0.0);
398 }
399
400 #[test]
401 fn test_infinity_handling_f64() {
402 let pos_inf = f64::INFINITY;
403 let neg_inf = f64::NEG_INFINITY;
404 assert!(pos_inf.to_f64().is_infinite());
405 assert!(neg_inf.to_f64().is_infinite());
406 assert_eq!(f64::from_f64(pos_inf), f64::INFINITY);
407 assert_eq!(f64::from_f64(neg_inf), f64::NEG_INFINITY);
408 }
409
410 #[test]
411 fn test_nan_propagation_f32() {
412 let nan = f32::NAN;
413 assert!(nan.to_f64().is_nan());
414 assert!(f32::from_f64(f64::NAN).is_nan());
415 }
416
417 #[test]
418 fn test_nan_propagation_f64() {
419 let nan = f64::NAN;
420 assert!(nan.to_f64().is_nan());
421 assert!(f64::from_f64(f64::NAN).is_nan());
422 }
423
424 #[test]
425 fn test_denormal_conversion_f32() {
426 let denormal = 1e-40_f32;
427 assert!(denormal != 0.0);
428 let converted = denormal.to_f64();
429 assert!(converted != 0.0);
430 assert!((converted - denormal as f64).abs() < 1e-45);
431 }
432
433 #[test]
434 fn test_denormal_conversion_f64() {
435 let denormal = 1e-310_f64;
436 assert!(denormal != 0.0);
437 assert_eq!(denormal.to_f64(), denormal);
438 }
439
440 #[test]
441 fn test_arithmetic_operations_f32() {
442 let a: f32 = 2.0;
443 let b: f32 = 3.0;
444 assert!(approx_eq(a + b, 5.0f32, 1e-6));
445 assert!(approx_eq(a - b, -1.0f32, 1e-6));
446 assert!(approx_eq(a * b, 6.0f32, 1e-6));
447 assert!(approx_eq(a / b, 2.0 / 3.0, 1e-6));
448 assert!(approx_eq(-a, -2.0f32, 1e-6));
449 }
450
451 #[test]
452 fn test_arithmetic_operations_f64() {
453 let a: f64 = 2.0;
454 let b: f64 = 3.0;
455 assert!(approx_eq(a + b, 5.0f64, 1e-14));
456 assert!(approx_eq(a - b, -1.0f64, 1e-14));
457 assert!(approx_eq(a * b, 6.0f64, 1e-14));
458 assert!(approx_eq(a / b, 2.0 / 3.0, 1e-14));
459 assert!(approx_eq(-a, -2.0f64, 1e-14));
460 }
461
462 #[test]
463 fn test_assign_operations_f32() {
464 let mut v: f32 = 1.0;
465 v += 2.0;
466 assert!(approx_eq(v, 3.0f32, 1e-6));
467 v -= 1.0;
468 assert!(approx_eq(v, 2.0f32, 1e-6));
469 v *= 3.0;
470 assert!(approx_eq(v, 6.0f32, 1e-6));
471 v /= 2.0;
472 assert!(approx_eq(v, 3.0f32, 1e-6));
473 }
474
475 #[test]
476 fn test_assign_operations_f64() {
477 let mut v: f64 = 1.0;
478 v += 2.0;
479 assert!(approx_eq(v, 3.0f64, 1e-14));
480 v -= 1.0;
481 assert!(approx_eq(v, 2.0f64, 1e-14));
482 v *= 3.0;
483 assert!(approx_eq(v, 6.0f64, 1e-14));
484 v /= 2.0;
485 assert!(approx_eq(v, 3.0f64, 1e-14));
486 }
487
488 #[test]
489 fn test_comparison_operations() {
490 assert!(1.0f32 < 2.0f32);
491 assert!(2.0f32 > 1.0f32);
492 assert!(1.0f32 <= 1.0f32);
493 assert!(1.0f32 >= 1.0f32);
494 assert!(1.0f32 == 1.0f32);
495 assert!(1.0f32 != 2.0f32);
496
497 assert!(1.0f64 < 2.0f64);
498 assert!(2.0f64 > 1.0f64);
499 }
500
501 #[test]
502 fn test_phi_golden_ratio() {
503 let epsilon_f32 = 1e-6;
504 let epsilon_f64 = 1e-14;
505 let expected_phi = (1.0 + 5.0_f64.sqrt()) / 2.0;
506 assert!(approx_eq(f32::PHI, expected_phi as f32, epsilon_f32));
507 assert!(approx_eq(f64::PHI, expected_phi, epsilon_f64));
508 }
509
510 #[cfg(feature = "simd")]
511 mod simd_tests {
512 use super::*;
513
514 #[test]
515 fn test_simd_splat_f32() {
516 let vec = f32::simd_splat(2.5);
517 let arr = f32::simd_to_array(vec);
518 for v in arr {
519 assert!((v - 2.5).abs() < 1e-6);
520 }
521 }
522
523 #[test]
524 fn test_simd_splat_f64() {
525 let vec = f64::simd_splat(2.5);
526 let arr = f64::simd_to_array(vec);
527 for v in arr {
528 assert!((v - 2.5).abs() < 1e-14);
529 }
530 }
531
532 #[test]
533 fn test_simd_from_slice_f32() {
534 let data = [1.0f32, 2.0, 3.0, 4.0];
535 let vec = f32::simd_from_slice(&data);
536 let arr = f32::simd_to_array(vec);
537 for (a, b) in arr.iter().zip(data.iter()) {
538 assert!((a - b).abs() < 1e-6);
539 }
540 }
541
542 #[test]
543 fn test_simd_from_slice_f64() {
544 let data = [1.0f64, 2.0, 3.0, 4.0];
545 let vec = f64::simd_from_slice(&data);
546 let arr = f64::simd_to_array(vec);
547 for (a, b) in arr.iter().zip(data.iter()) {
548 assert!((a - b).abs() < 1e-14);
549 }
550 }
551
552 #[test]
553 fn test_simd_lane_offsets_f32() {
554 let offsets = f32::simd_lane_offsets();
555 let arr = f32::simd_to_array(offsets);
556 assert!((arr[0] - 0.0).abs() < 1e-6);
557 assert!((arr[1] - 1.0).abs() < 1e-6);
558 assert!((arr[2] - 2.0).abs() < 1e-6);
559 assert!((arr[3] - 3.0).abs() < 1e-6);
560 }
561
562 #[test]
563 fn test_simd_lane_offsets_f64() {
564 let offsets = f64::simd_lane_offsets();
565 let arr = f64::simd_to_array(offsets);
566 assert!((arr[0] - 0.0).abs() < 1e-14);
567 assert!((arr[1] - 1.0).abs() < 1e-14);
568 assert!((arr[2] - 2.0).abs() < 1e-14);
569 assert!((arr[3] - 3.0).abs() < 1e-14);
570 }
571
572 #[test]
573 fn test_simd_select_gt_f32() {
574 let a = f32::simd_from_slice(&[1.0, 3.0, 2.0, 5.0]);
575 let b = f32::simd_from_slice(&[2.0, 2.0, 2.0, 2.0]);
576 let if_true = f32::simd_splat(10.0);
577 let if_false = f32::simd_splat(0.0);
578 let result = f32::simd_select_gt(a, b, if_true, if_false);
579 let arr = f32::simd_to_array(result);
580 assert!((arr[0] - 0.0).abs() < 1e-6);
581 assert!((arr[1] - 10.0).abs() < 1e-6);
582 assert!((arr[2] - 0.0).abs() < 1e-6);
583 assert!((arr[3] - 10.0).abs() < 1e-6);
584 }
585
586 #[test]
587 fn test_simd_select_lt_f32() {
588 let a = f32::simd_from_slice(&[1.0, 3.0, 2.0, 5.0]);
589 let b = f32::simd_from_slice(&[2.0, 2.0, 2.0, 2.0]);
590 let if_true = f32::simd_splat(10.0);
591 let if_false = f32::simd_splat(0.0);
592 let result = f32::simd_select_lt(a, b, if_true, if_false);
593 let arr = f32::simd_to_array(result);
594 assert!((arr[0] - 10.0).abs() < 1e-6);
595 assert!((arr[1] - 0.0).abs() < 1e-6);
596 assert!((arr[2] - 0.0).abs() < 1e-6);
597 assert!((arr[3] - 0.0).abs() < 1e-6);
598 }
599
600 #[test]
601 fn test_simd_select_gt_f64() {
602 let a = f64::simd_from_slice(&[1.0, 3.0, 2.0, 5.0]);
603 let b = f64::simd_from_slice(&[2.0, 2.0, 2.0, 2.0]);
604 let if_true = f64::simd_splat(10.0);
605 let if_false = f64::simd_splat(0.0);
606 let result = f64::simd_select_gt(a, b, if_true, if_false);
607 let arr = f64::simd_to_array(result);
608 assert!((arr[0] - 0.0).abs() < 1e-14);
609 assert!((arr[1] - 10.0).abs() < 1e-14);
610 assert!((arr[2] - 0.0).abs() < 1e-14);
611 assert!((arr[3] - 10.0).abs() < 1e-14);
612 }
613
614 #[test]
615 fn test_simd_select_lt_f64() {
616 let a = f64::simd_from_slice(&[1.0, 3.0, 2.0, 5.0]);
617 let b = f64::simd_from_slice(&[2.0, 2.0, 2.0, 2.0]);
618 let if_true = f64::simd_splat(10.0);
619 let if_false = f64::simd_splat(0.0);
620 let result = f64::simd_select_lt(a, b, if_true, if_false);
621 let arr = f64::simd_to_array(result);
622 assert!((arr[0] - 10.0).abs() < 1e-14);
623 assert!((arr[1] - 0.0).abs() < 1e-14);
624 assert!((arr[2] - 0.0).abs() < 1e-14);
625 assert!((arr[3] - 0.0).abs() < 1e-14);
626 }
627 }
628}