1include!(concat!(env!("OUT_DIR"), "/simd_lanes.rs"));
17
18#[cfg(not(feature = "fast_hash"))]
19use std::collections::HashMap;
20use std::collections::HashSet;
21use std::hash::Hash;
22use std::ops::{Mul, Sub};
23#[cfg(feature = "simd")]
24use std::simd::{
25 Mask, Simd, SimdElement,
26 cmp::SimdOrd,
27 num::{SimdFloat, SimdInt, SimdUint},
28};
29
30#[cfg(feature = "fast_hash")]
31use ahash::AHashMap;
32use minarrow::{Bitmask, Vec64};
33use num_traits::{Float, NumCast, One, ToPrimitive, Zero};
34
35use crate::kernels::sort::{sort_float, total_cmp_f};
36use crate::traits::dense_iter::collect_valid;
37use crate::traits::to_bits::ToBits;
38use crate::utils::has_nulls;
39use minarrow::utils::is_simd_aligned;
40
41#[cfg(feature = "simd")]
42use crate::utils::bitmask_to_simd_mask;
43
44macro_rules! impl_stat_moments_float {
49 ($name:ident, $ty:ty, $LANES:expr) => {
50 #[doc = concat!("Computes sum, sum-of-squares, and count for a `", stringify!($ty), "` slice.")]
51 #[doc = "Skips nulls if present. Uses SIMD if available."]
52 #[inline(always)]
53 pub fn $name(
54 data: &[$ty],
55 mask: Option<&Bitmask>,
56 null_count: Option<usize>,
57 ) -> (f64, f64, usize) {
58 let has_nulls = has_nulls(null_count, mask);
59
60 #[cfg(feature = "simd")]
61 {
62 if is_simd_aligned(data) {
64 type V<const L: usize> = Simd<$ty, L>;
65 type M<const L: usize> = <$ty as SimdElement>::Mask;
67
68 const N: usize = $LANES;
69 let len = data.len();
70 let mut i = 0;
71 let (mut sum, mut sum2) = (0.0_f64, 0.0_f64);
72 let mut cnt = 0_usize;
73
74 if !has_nulls {
75 while i + N <= len {
77 let v = V::<N>::from_slice(&data[i..i + N]);
78 let dv = v.cast::<f64>();
79 cnt += N;
80 sum += dv.reduce_sum();
81 sum2 += (dv * dv).reduce_sum();
82 i += N;
83 }
84 for &v in &data[i..] {
86 let x = v as f64;
87 sum += x;
88 sum2 += x * x;
89 cnt += 1;
90 }
91 } else {
92 let mb = mask.expect("Mask must be Some if nulls are present");
94 let mask_bytes = mb.as_bytes();
95
96 while i + N <= len {
97 let v = V::<N>::from_slice(&data[i..i + N]);
98 let lane_mask: Mask<M<N>, N> =
100 bitmask_to_simd_mask::<N, M<N>>(mask_bytes, i, len);
101 let zeros = V::<N>::splat(0.0 as $ty);
103 let vv = lane_mask.select(v, zeros);
104
105 cnt += lane_mask.to_bitmask().count_ones() as usize;
107
108 let dv = vv.cast::<f64>();
109 sum += dv.reduce_sum();
110 sum2 += (dv * dv).reduce_sum();
111
112 i += N;
113 }
114 for j in i..len {
116 if unsafe { mb.get_unchecked(j) } {
117 let x = data[j] as f64;
118 sum += x;
119 sum2 += x * x;
120 cnt += 1;
121 }
122 }
123 }
124 return (sum, sum2, cnt);
125 }
126 }
128
129 let (mut sum, mut sum2, mut cnt) = (0.0_f64, 0.0_f64, 0_usize);
131 if !has_nulls {
132 for &v in data {
133 let x = v as f64;
134 sum += x;
135 sum2 += x * x;
136 cnt += 1;
137 }
138 } else {
139 let mb = mask.expect("Mask must be Some if nulls are present");
140 for (i, &v) in data.iter().enumerate() {
141 if unsafe { mb.get_unchecked(i) } {
142 let x = v as f64;
143 sum += x;
144 sum2 += x * x;
145 cnt += 1;
146 }
147 }
148 }
149 (sum, sum2, cnt)
150 }
151 };
152}
153
154macro_rules! impl_stat_moments_int {
157 ($name:ident, $ty:ty, $acc:ty, $LANES:expr, $mask_ty:ty) => {
158 #[doc = concat!("Computes sum, sum-of-squares, and count for `", stringify!($ty), "` integers.")]
159 #[doc = concat!(" Uses `", stringify!($acc), "` for accumulation. Skips nulls if present.")]
160 #[inline(always)]
161 pub fn $name(
162 data: &[$ty],
163 mask: Option<&Bitmask>,
164 null_count: Option<usize>
165 ) -> (f64, f64, usize) {
166 let has_nulls = has_nulls(null_count, mask);
167
168 #[cfg(feature = "simd")]
169 {
170 if is_simd_aligned(data) {
172 const N: usize = $LANES;
173 let mut i = 0;
174 let mut sum: $acc = 0;
175 let mut sum2: $acc = 0;
176 let mut cnt = 0usize;
177
178 if !has_nulls {
179 while i + N <= data.len() {
180 let v = Simd::<$ty, N>::from_slice(&data[i..i + N]);
181 sum += v.reduce_sum() as $acc;
182 sum2 += (v * v).reduce_sum() as $acc;
183 cnt += N;
184 i += N;
185 }
186 for &x in &data[i..] {
187 let x = x as $acc;
188 sum += x;
189 sum2 += x * x;
190 cnt += 1;
191 }
192 } else {
193 let mb = mask.expect("Mask must be Some if nulls are present or mask was supplied");
194 let mask_bytes = &mb.bits;
195 while i + N <= data.len() {
196 let v = Simd::<$ty, N>::from_slice(&data[i..i + N]);
197 let lane_mask = bitmask_to_simd_mask::<N, $mask_ty>(mask_bytes, i, data.len());
199 let vv = lane_mask.select(v, Simd::<$ty, N>::splat(0 as $ty));
200 sum += vv.reduce_sum() as $acc;
201 sum2 += (vv * vv).reduce_sum() as $acc;
202 cnt += lane_mask.to_bitmask().count_ones() as usize;
203 i += N;
204 }
205 for j in i..data.len() {
206 if unsafe { mb.get_unchecked(j) } {
207 let x = data[j] as $acc;
208 sum += x;
209 sum2 += x * x;
210 cnt += 1;
211 }
212 }
213 }
214 return (sum as f64, sum2 as f64, cnt);
215 }
216 }
218
219 let mut sum: $acc = 0;
221 let mut sum2: $acc = 0;
222 let mut cnt = 0usize;
223 if !has_nulls {
224 for &x in data.iter() {
225 let xi = x as $acc;
226 sum += xi;
227 sum2 += xi * xi;
228 cnt += 1;
229 }
230 } else {
231 let mb = mask.expect("Mask must be Some if nulls are present or mask was supplied");
232 for (i, &x) in data.iter().enumerate() {
233 if unsafe { mb.get_unchecked(i) } {
234 let xi = x as $acc;
235 sum += xi;
236 sum2 += xi * xi;
237 cnt += 1;
238 }
239 }
240 }
241 (sum as f64, sum2 as f64, cnt)
242 }
243 };
244}
245
246macro_rules! impl_reduce_min_max {
249 ($name:ident, $ty:ty, $LANES:expr, $minval:expr, $maxval:expr) => {
250 #[inline(always)]
265 pub fn $name(
266 data: &[$ty],
267 mask: Option<&Bitmask>,
268 null_count: Option<usize>,
269 ) -> Option<($ty, $ty)> {
270 let has_nulls = has_nulls(null_count, mask);
271 let len = data.len();
272 if len == 0 {
273 return None;
274 }
275
276 #[cfg(feature = "simd")]
277 {
278 if is_simd_aligned(data) {
280 type V<const N: usize> = Simd<$ty, N>;
281 type M<const N: usize> = <$ty as SimdElement>::Mask;
282 const N: usize = $LANES;
283
284 let mut first_valid = None;
285 if !has_nulls {
286 if len == 0 {
288 return None;
289 }
290 first_valid = Some((0, data[0]));
291 } else {
292 let mb = mask.expect("Mask must be Some if nulls are present");
293 for (idx, &x) in data.iter().enumerate() {
294 if unsafe { mb.get_unchecked(idx) } {
295 first_valid = Some((idx, x));
296 break;
297 }
298 }
299 }
300 let (mut i, x0) = match first_valid {
301 Some(v) => v,
302 None => return None,
303 };
304 let mut min_s = x0;
305 let mut max_s = x0;
306 i += 1;
307
308 while i < len && (i % N != 0) {
310 let x = data[i];
311 if !has_nulls
312 || (has_nulls && {
313 let mb = mask.unwrap();
314 unsafe { mb.get_unchecked(i) }
315 })
316 {
317 min_s = min_s.min(x);
318 max_s = max_s.max(x);
319 }
320 i += 1;
321 }
322
323 let mut min_v = V::<N>::splat(min_s);
324 let mut max_v = V::<N>::splat(max_s);
325
326 if !has_nulls {
327 while i + N <= len {
328 let v = V::<N>::from_slice(&data[i..i + N]);
329 min_v = min_v.simd_min(v);
330 max_v = max_v.simd_max(v);
331 i += N;
332 }
333 } else {
334 let mb = mask.unwrap();
335 let mask_bytes = mb.as_bytes();
336 while i + N <= len {
337 let v = V::<N>::from_slice(&data[i..i + N]);
338 let lane_m: Mask<M<N>, N> =
339 bitmask_to_simd_mask::<N, M<N>>(mask_bytes, i, len);
340 let v_min = lane_m.select(v, V::<N>::splat(min_s));
342 let v_max = lane_m.select(v, V::<N>::splat(max_s));
343 min_v = min_v.simd_min(v_min);
344 max_v = max_v.simd_max(v_max);
345 i += N;
346 }
347 }
348
349 for idx in i..len {
350 let x = data[idx];
351 if !has_nulls
352 || (has_nulls && {
353 let mb = mask.unwrap();
354 unsafe { mb.get_unchecked(idx) }
355 })
356 {
357 min_s = min_s.min(x);
358 max_s = max_s.max(x);
359 }
360 }
361
362 min_s = min_s.min(min_v.reduce_min());
363 max_s = max_s.max(max_v.reduce_max());
364 return Some((min_s, max_s));
365 }
366 }
368
369 let mut min = None;
371 let mut max = None;
372 if !has_nulls {
373 for &x in data {
374 match min {
375 None => {
376 min = Some(x);
377 max = Some(x);
378 }
379 Some(m) => {
380 min = Some(m.min(x));
381 max = Some(max.unwrap().max(x));
382 }
383 }
384 }
385 } else {
386 let mb = mask.expect("Mask must be Some if nulls are present");
387 for (i, &x) in data.iter().enumerate() {
388 if unsafe { mb.get_unchecked(i) } {
389 match min {
390 None => {
391 min = Some(x);
392 max = Some(x);
393 }
394 Some(m) => {
395 min = Some(m.min(x));
396 max = Some(max.unwrap().max(x));
397 }
398 }
399 }
400 }
401 }
402 min.zip(max)
403 }
404 };
405}
406
407macro_rules! impl_reduce_min_max_float {
410 ($fn_name:ident, $ty:ty, $LANES:ident, $SIMD:ident, $MASK:ident) => {
411 #[inline(always)]
426 pub fn $fn_name(
427 data: &[$ty],
428 mask: Option<&Bitmask>,
429 null_count: Option<usize>,
430 ) -> Option<($ty, $ty)> {
431 let has_nulls = has_nulls(null_count, mask);
432 let len = data.len();
433 if len == 0 {
434 return None;
435 }
436
437 #[cfg(feature = "simd")]
438 {
439 if is_simd_aligned(data) {
441 const LANES: usize = $LANES;
442 type V = $SIMD<$ty, LANES>;
443 type M = <$ty as SimdElement>::Mask;
444
445 let mut first_valid = None;
447 if !has_nulls {
448 for (idx, &x) in data.iter().enumerate() {
449 if !x.is_nan() {
450 first_valid = Some((idx, x));
451 break;
452 }
453 }
454 } else {
455 let mb = mask.expect("Bitmask must be Some if nulls are present");
456 for (idx, &x) in data.iter().enumerate() {
457 if unsafe { mb.get_unchecked(idx) } && !x.is_nan() {
458 first_valid = Some((idx, x));
459 break;
460 }
461 }
462 }
463 let (mut i, x0) = match first_valid {
464 Some(v) => v,
465 None => return None,
466 };
467 let mut min_s = x0;
468 let mut max_s = x0;
469 i += 1;
470
471 while i < len && (i % LANES != 0) {
473 let x = data[i];
474 if (!has_nulls && !x.is_nan())
475 || (has_nulls && {
476 let mb = mask.unwrap();
477 unsafe { mb.get_unchecked(i) && !x.is_nan() }
478 })
479 {
480 min_s = min_s.min(x);
481 max_s = max_s.max(x);
482 }
483 i += 1;
484 }
485
486 let mut min_v = V::splat(min_s);
487 let mut max_v = V::splat(max_s);
488
489 if !has_nulls {
490 while i + LANES <= len {
491 let v = V::from_slice(&data[i..i + LANES]);
492 let valid = !v.is_nan();
493 let v_min = valid.select(v, V::splat(<$ty>::INFINITY));
494 let v_max = valid.select(v, V::splat(<$ty>::NEG_INFINITY));
495 min_v = min_v.simd_min(v_min);
496 max_v = max_v.simd_max(v_max);
497 i += LANES;
498 }
499 } else {
500 let mb = mask.unwrap();
501 let mask_bytes = mb.as_bytes();
502 while i + LANES <= len {
503 let v = V::from_slice(&data[i..i + LANES]);
504 let valid_mask =
505 bitmask_to_simd_mask::<LANES, M>(mask_bytes, i, len) & !v.is_nan();
506 let v_min = valid_mask.select(v, V::splat(<$ty>::INFINITY));
507 let v_max = valid_mask.select(v, V::splat(<$ty>::NEG_INFINITY));
508 min_v = min_v.simd_min(v_min);
509 max_v = max_v.simd_max(v_max);
510 i += LANES;
511 }
512 }
513
514 for idx in i..len {
515 let x = data[idx];
516 if (!has_nulls && !x.is_nan())
517 || (has_nulls && {
518 let mb = mask.unwrap();
519 unsafe { mb.get_unchecked(idx) && !x.is_nan() }
520 })
521 {
522 min_s = min_s.min(x);
523 max_s = max_s.max(x);
524 }
525 }
526
527 min_s = min_s.min(min_v.reduce_min());
528 max_s = max_s.max(max_v.reduce_max());
529 return Some((min_s, max_s));
530 }
531 }
533
534 let mut min = None;
536 let mut max = None;
537 if !has_nulls {
538 for &x in data {
539 if !x.is_nan() {
540 match min {
541 None => {
542 min = Some(x);
543 max = Some(x);
544 }
545 Some(m) => {
546 min = Some(m.min(x));
547 max = Some(max.unwrap().max(x));
548 }
549 }
550 }
551 }
552 } else {
553 let mb = mask.expect("Bitmask must be Some if nulls are present");
554 for (i, &x) in data.iter().enumerate() {
555 if unsafe { mb.get_unchecked(i) } && !x.is_nan() {
556 match min {
557 None => {
558 min = Some(x);
559 max = Some(x);
560 }
561 Some(m) => {
562 min = Some(m.min(x));
563 max = Some(max.unwrap().max(x));
564 }
565 }
566 }
567 }
568 }
569 min.zip(max)
570 }
571 };
572}
573
574macro_rules! stat_moments {
576 (f64 => $d:expr, $m:expr, $hn:expr) => {
577 stat_moments_f64($d, $m, $hn)
578 };
579 (f32 => $d:expr, $m:expr, $hn:expr) => {
580 stat_moments_f32($d, $m, $hn)
581 };
582 (i64 => $d:expr, $m:expr, $hn:expr) => {
583 stat_moments_i64($d, $m, $hn)
584 };
585 (u64 => $d:expr, $m:expr, $hn:expr) => {
586 stat_moments_u64($d, $m, $hn)
587 };
588 (i32 => $d:expr, $m:expr, $hn:expr) => {
589 stat_moments_i32($d, $m, $hn)
590 };
591 (u32 => $d:expr, $m:expr, $hn:expr) => {
592 stat_moments_u32($d, $m, $hn)
593 };
594}
595
596macro_rules! impl_variance {
599 ($ty:ident, $fn_var:ident) => {
600 #[doc = concat!("Returns the variance for `", stringify!($ty), "`; if `sample` is true, uses Bessel's correction (n-1).")]
601 #[inline(always)]
602 pub fn $fn_var(
603 data: &[$ty],
604 mask: Option<&Bitmask>,
605 null_count: Option<usize>,
606 sample: bool,
607 ) -> Option<f64> {
608 let (s, s2, n) = stat_moments!($ty => data, mask, null_count);
609 if n == 0 || (sample && n < 2) {
610 None
611 } else if sample {
612 Some((s2 - s * s / n as f64) / (n as f64 - 1.0))
613 } else {
614 Some((s2 - s * s / n as f64) / n as f64)
615 }
616 }
617 };
618}
619
620macro_rules! impl_min_max_range {
622 ($ty:ty, $reduce_fn:ident, $min_fn:ident, $max_fn:ident, $range_fn:ident) => {
623 #[doc = concat!("Returns the minimum of a `", stringify!($ty), "` slice.")]
624 #[inline(always)]
625 pub fn $min_fn(
626 data: &[$ty],
627 mask: Option<&Bitmask>,
628 null_count: Option<usize>,
629 ) -> Option<$ty> {
630 $reduce_fn(data, mask, null_count).map(|(min, _)| min)
631 }
632
633 #[doc = concat!("Returns the maximum of a `", stringify!($ty), "` slice.")]
634 #[inline(always)]
635 pub fn $max_fn(
636 data: &[$ty],
637 mask: Option<&Bitmask>,
638 null_count: Option<usize>,
639 ) -> Option<$ty> {
640 $reduce_fn(data, mask, null_count).map(|(_, max)| max)
641 }
642
643 #[doc = concat!("Returns the (min, max) of a `", stringify!($ty), "` slice.")]
644 #[inline(always)]
645 pub fn $range_fn(
646 data: &[$ty],
647 mask: Option<&Bitmask>,
648 null_count: Option<usize>,
649 ) -> Option<($ty, $ty)> {
650 $reduce_fn(data, mask, null_count)
651 }
652 };
653}
654
655macro_rules! impl_sum_float {
659 ($name:ident, $ty:ty, $LANES:expr) => {
660 #[doc = concat!("Returns (sum, count) for non-null `", stringify!($ty), "` values.")]
661 #[doc = " Uses SIMD where available."]
662 #[inline(always)]
663 pub fn $name(
664 data: &[$ty],
665 mask: Option<&Bitmask>,
666 null_count: Option<usize>,
667 ) -> (f64, usize) {
668 let has_nulls = has_nulls(null_count, mask);
669
670 #[cfg(feature = "simd")]
671 {
672 if is_simd_aligned(data) {
674 const N: usize = $LANES;
675 type V<const L: usize> = Simd<$ty, L>;
676 type M<const L: usize> = <$ty as SimdElement>::Mask;
677
678 let len = data.len();
679 let mut i = 0;
680 let mut sum = 0.0f64;
681 let mut cnt = 0usize;
682
683 if !has_nulls {
684 while i + N <= len {
686 let v = V::<N>::from_slice(&data[i..i + N]);
687 sum += v.cast::<f64>().reduce_sum();
688 cnt += N;
689 i += N;
690 }
691 for &x in &data[i..] {
692 sum += x as f64;
693 cnt += 1;
694 }
695 } else {
696 let mb = mask.expect(
698 "Bitmask must be Some if nulls are present or mask is supplied",
699 );
700 let bytes = mb.as_bytes();
701
702 while i + N <= len {
703 let v = V::<N>::from_slice(&data[i..i + N]);
704 let lane_m: Mask<M<N>, N> =
706 bitmask_to_simd_mask::<N, M<N>>(bytes, i, len);
707 let vv = lane_m.select(v, V::<N>::splat(0.0 as $ty));
709 sum += vv.cast::<f64>().reduce_sum();
710 cnt += lane_m.to_bitmask().count_ones() as usize;
712 i += N;
713 }
714 for idx in i..len {
715 if unsafe { mb.get_unchecked(idx) } {
716 sum += data[idx] as f64;
717 cnt += 1;
718 }
719 }
720 }
721
722 return (sum, cnt);
723 }
724 }
726
727 let mut sum = 0.0f64;
729 let mut cnt = 0usize;
730 let len = data.len();
731
732 if !has_nulls {
733 for &x in data {
734 sum += x as f64;
735 cnt += 1;
736 }
737 } else {
738 let mb =
739 mask.expect("Bitmask must be Some if nulls are present or mask is supplied");
740 for i in 0..len {
741 if unsafe { mb.get_unchecked(i) } {
742 sum += data[i] as f64;
743 cnt += 1;
744 }
745 }
746 }
747
748 (sum, cnt)
749 }
750 };
751}
752
753macro_rules! impl_sum_int {
755 ($name:ident, $ty:ty, $acc:ty, $LANES:expr) => {
756 #[doc = concat!("Returns (sum, count) for non-null `", stringify!($ty), "` values.")]
757 #[doc = concat!(" Accumulates using `", stringify!($acc), "`. Uses SIMD if available.")]
758 #[inline(always)]
759 pub fn $name(
760 data: &[$ty],
761 mask: Option<&Bitmask>,
762 null_count: Option<usize>,
763 ) -> ($acc, usize) {
764 let has_nulls = has_nulls(null_count, mask);
765
766 #[cfg(feature = "simd")]
767 {
768 if is_simd_aligned(data) {
770 const N: usize = $LANES;
771 type V<const L: usize> = Simd<$ty, L>;
772 type M<const L: usize> = <$ty as SimdElement>::Mask;
773
774 let len = data.len();
775 let mut i = 0;
776 let mut sum: $acc = 0;
777 let mut cnt = 0_usize;
778
779 if !has_nulls {
780 while i + N <= len {
782 let v = V::<N>::from_slice(&data[i..i + N]);
783 sum += v.reduce_sum() as $acc;
784 cnt += N;
785 i += N;
786 }
787 for &x in &data[i..] {
789 sum += x as $acc;
790 cnt += 1;
791 }
792 } else {
793 let mb = mask.expect(
795 "Bitmask must be Some if nulls are present or mask is supplied",
796 );
797 let bytes = mb.as_bytes();
798
799 while i + N <= len {
800 let v = V::<N>::from_slice(&data[i..i + N]);
801 let lane_m: Mask<M<N>, N> =
803 bitmask_to_simd_mask::<N, M<N>>(bytes, i, len);
804 let vv = lane_m.select(v, V::<N>::splat(0 as $ty));
806 sum += vv.reduce_sum() as $acc;
807 cnt += lane_m.to_bitmask().count_ones() as usize;
809 i += N;
810 }
811 for idx in i..len {
813 if unsafe { mb.get_unchecked(idx) } {
814 sum += data[idx] as $acc;
815 cnt += 1;
816 }
817 }
818 }
819
820 return (sum, cnt);
821 }
822 }
824
825 let mut sum: $acc = 0;
827 let mut cnt = 0_usize;
828 let len = data.len();
829
830 if !has_nulls {
831 for &x in data {
832 sum += x as $acc;
833 cnt += 1;
834 }
835 } else {
836 let mb =
837 mask.expect("Bitmask must be Some if nulls are present or mask is supplied");
838 for i in 0..len {
839 if unsafe { mb.get_unchecked(i) } {
840 sum += data[i] as $acc;
841 cnt += 1;
842 }
843 }
844 }
845
846 (sum, cnt)
847 }
848 };
849}
850
851macro_rules! impl_sum2_float {
854 ($fn_name:ident, $ty:ty, $LANES:expr) => {
855 #[doc = concat!("Computes sum-of-squares for `", stringify!($ty), "` (Σx²).")]
856 #[doc = " Skips nulls if present. SIMD‐enabled."]
857 #[inline(always)]
858 pub fn $fn_name(data: &[$ty], mask: Option<&Bitmask>, null_count: Option<usize>) -> f64 {
859 let has_nulls = has_nulls(null_count, mask);
860
861 #[cfg(feature = "simd")]
862 {
863 if is_simd_aligned(data) {
865 type V<const N: usize> = Simd<$ty, N>;
867 type M<const N: usize> = <$ty as SimdElement>::Mask;
869
870 const N: usize = $LANES;
871 let len = data.len();
872 let mut i = 0;
873 let mut s2 = 0.0f64;
874
875 if !has_nulls {
876 while i + N <= len {
878 let dv = V::<N>::from_slice(&data[i..i + N]).cast::<f64>();
880 s2 += (dv * dv).reduce_sum();
881 i += N;
882 }
883 for &x in &data[i..] {
885 let y = x as f64;
886 s2 += y * y;
887 }
888 } else {
889 let mb = mask.expect("mask must be Some if nulls present");
891 let bytes = mb.as_bytes();
892 while i + N <= len {
893 let v = V::<N>::from_slice(&data[i..i + N]);
895 let lane_m: Mask<M<N>, N> =
897 bitmask_to_simd_mask::<N, M<N>>(bytes, i, len);
898 let vv = lane_m.select(v, V::<N>::splat(0 as $ty));
900 let dv = vv.cast::<f64>();
902 s2 += (dv * dv).reduce_sum();
903 i += N;
904 }
905 for idx in i..len {
907 if unsafe { mb.get_unchecked(idx) } {
908 let y = data[idx] as f64;
909 s2 += y * y;
910 }
911 }
912 }
913 return s2;
914 }
915 }
917
918 let mut s2 = 0.0f64;
920 if !has_nulls {
921 for &x in data {
922 let y = x as f64;
923 s2 += y * y;
924 }
925 } else {
926 let mb = mask.expect("mask must be Some if nulls present");
927 for (i, &x) in data.iter().enumerate() {
928 if unsafe { mb.get_unchecked(i) } {
929 let y = x as f64;
930 s2 += y * y;
931 }
932 }
933 }
934 s2
935 }
936 };
937}
938
939macro_rules! impl_count {
941 ($fn_name:ident) => {
942 #[doc = "Returns the number of valid (non-null) elements."]
943 #[inline(always)]
944 pub fn $fn_name(len: usize, mask: Option<&Bitmask>, null_count: Option<usize>) -> usize {
945 match null_count {
946 Some(n) => len - n,
947 None => match mask {
948 Some(m) => m.count_ones(),
949 None => len,
950 },
951 }
952 }
953 };
954}
955
956macro_rules! impl_sum_avg {
959 (float $ty:ident, $sum_fn:ident, $avg_fn:ident, $raw:ident) => {
961 #[doc = concat!("Returns the sum of `", stringify!($ty), "` values, or None if all-null.")]
962 #[inline(always)]
963 pub fn $sum_fn(d: &[$ty], m: Option<&Bitmask>, null_count: Option<usize>) -> Option<f64> {
964 let (s, n) = $raw(d, m, null_count);
965 if n == 0 { None } else { Some(s) }
966 }
967
968 #[doc = concat!("Returns the average of `", stringify!($ty), "` values, or None if all-null.")]
969 #[inline(always)]
970 pub fn $avg_fn(d: &[$ty], m: Option<&Bitmask>, null_count: Option<usize>) -> Option<f64> {
971 let (s, n) = $raw(d, m, null_count);
972 if n == 0 { None } else { Some(s / n as f64) }
973 }
974 };
975
976 (int $ty:ident, $sum_fn:ident, $avg_fn:ident, $acc:ty, $raw:ident) => {
978 #[doc = concat!("Returns the sum of `", stringify!($ty), "` values, or None if all-null.")]
979 #[inline(always)]
980 pub fn $sum_fn(d: &[$ty], m: Option<&Bitmask>, null_count: Option<usize>) -> Option<$acc> {
981 let (s, n) = $raw(d, m, null_count);
982 if n == 0 { None } else { Some(s) }
983 }
984
985 #[doc = concat!("Returns the average of `", stringify!($ty), "` values, or None if all-null.")]
986 #[inline(always)]
987 pub fn $avg_fn(d: &[$ty], m: Option<&Bitmask>, null_count: Option<usize>) -> Option<f64> {
988 let (s, n) = $raw(d, m, null_count);
989 if n == 0 { None } else { Some(s as f64 / n as f64) }
990 }
991 };
992}
993
994macro_rules! impl_stat_moments4_float {
995 ($fn_name:ident, $ty:ty, $LANES:expr) => {
996 #[doc = concat!(
997 "SIMD-accelerated (∑x … ∑x⁴) for `", stringify!($ty), "`.\n",
998 "Returns `(s1,s2,s3,s4,n)` as `(f64,f64,f64,f64,usize)`."
999 )]
1000 #[inline(always)]
1001 pub fn $fn_name(
1002 data: &[$ty],
1003 mask: Option<&Bitmask>,
1004 null_count: Option<usize>,
1005 ) -> (f64, f64, f64, f64, usize) {
1006 let has_nulls = has_nulls(null_count, mask);
1007
1008 #[cfg(feature = "simd")]
1009 {
1010 if is_simd_aligned(data) {
1012 type V<const L: usize> = Simd<$ty, L>;
1013 type M<const L: usize> = <$ty as SimdElement>::Mask;
1014
1015 const N: usize = $LANES;
1016 let len = data.len();
1017 let mut i = 0usize;
1018
1019 let mut s1 = 0.0;
1020 let mut s2 = 0.0;
1021 let mut s3 = 0.0;
1022 let mut s4 = 0.0;
1023 let mut n = 0usize;
1024
1025 if !has_nulls {
1026 while i + N <= len {
1028 let v = V::<N>::from_slice(&data[i..i + N]).cast::<f64>();
1029 let v2 = v * v;
1030 s1 += v.reduce_sum();
1031 s2 += v2.reduce_sum();
1032 s3 += (v2 * v).reduce_sum();
1033 s4 += (v2 * v2).reduce_sum();
1034 n += N;
1035 i += N;
1036 }
1037 for &x in &data[i..] {
1038 let xf = x as f64;
1039 s1 += xf;
1040 let x2 = xf * xf;
1041 s2 += x2;
1042 s3 += x2 * xf;
1043 s4 += x2 * x2;
1044 n += 1;
1045 }
1046 } else {
1047 let mb = mask.expect("mask must be Some when nulls present");
1049 let mask_bytes = mb.as_bytes();
1050 while i + N <= len {
1051 let v_raw = V::<N>::from_slice(&data[i..i + N]);
1052 let lane_m: Mask<M<N>, N> =
1053 bitmask_to_simd_mask::<N, M<N>>(mask_bytes, i, len);
1054 if !lane_m.any() {
1055 i += N;
1056 continue;
1057 }
1058 let v = lane_m.select(v_raw, V::<N>::splat(0 as $ty)).cast::<f64>();
1060 let v2 = v * v;
1061 s1 += v.reduce_sum();
1062 s2 += v2.reduce_sum();
1063 s3 += (v2 * v).reduce_sum();
1064 s4 += (v2 * v2).reduce_sum();
1065 n += lane_m.to_bitmask().count_ones() as usize;
1066 i += N;
1067 }
1068 for j in i..len {
1069 if unsafe { mb.get_unchecked(j) } {
1070 let xf = data[j] as f64;
1071 s1 += xf;
1072 let x2 = xf * xf;
1073 s2 += x2;
1074 s3 += x2 * xf;
1075 s4 += x2 * x2;
1076 n += 1;
1077 }
1078 }
1079 }
1080 return (s1, s2, s3, s4, n);
1081 }
1082 }
1084
1085 let mut s1 = 0.0;
1087 let mut s2 = 0.0;
1088 let mut s3 = 0.0;
1089 let mut s4 = 0.0;
1090 let mut n = 0usize;
1091
1092 if !has_nulls {
1093 for &x in data {
1094 let xf = x as f64;
1095 s1 += xf;
1096 let x2 = xf * xf;
1097 s2 += x2;
1098 s3 += x2 * xf;
1099 s4 += x2 * x2;
1100 n += 1;
1101 }
1102 } else {
1103 let mb = mask.expect("mask must be Some when nulls present");
1104 for (idx, &x) in data.iter().enumerate() {
1105 if unsafe { mb.get_unchecked(idx) } {
1106 let xf = x as f64;
1107 s1 += xf;
1108 let x2 = xf * xf;
1109 s2 += x2;
1110 s3 += x2 * xf;
1111 s4 += x2 * x2;
1112 n += 1;
1113 }
1114 }
1115 }
1116 (s1, s2, s3, s4, n)
1117 }
1118 };
1119}
1120
1121macro_rules! impl_stat_moments4_int {
1122 ($fn_name:ident, $ty:ty, $acc:ty, $LANES:expr) => {
1123 #[doc = concat!(
1124 "SIMD-accelerated (∑x … ∑x⁴) for `", stringify!($ty), "` (promoted to f64).\n",
1125 "Accumulator type for scalar paths: `", stringify!($acc), "`."
1126 )]
1127 #[inline(always)]
1128 pub fn $fn_name(
1129 data: &[$ty],
1130 mask: Option<&Bitmask>,
1131 null_count: Option<usize>
1132 ) -> (f64, f64, f64, f64, usize) {
1133 let has_nulls = has_nulls(null_count, mask);
1134
1135 #[cfg(feature = "simd")]
1136 {
1137 if is_simd_aligned(data) {
1139 type V<const L: usize> = Simd<$ty, L>;
1140 type M<const L: usize> = <$ty as SimdElement>::Mask;
1141
1142 const N: usize = $LANES;
1143 let len = data.len();
1144 let mut i = 0usize;
1145
1146 let mut s1 = 0.0;
1147 let mut s2 = 0.0;
1148 let mut s3 = 0.0;
1149 let mut s4 = 0.0;
1150 let mut n = 0usize;
1151
1152 if !has_nulls {
1153 while i + N <= len {
1154 let v_i = V::<N>::from_slice(&data[i..i + N]);
1155 let v_f = v_i.cast::<f64>();
1156 let v2 = v_f * v_f;
1157 s1 += v_f.reduce_sum();
1158 s2 += v2.reduce_sum();
1159 s3 += (v2 * v_f).reduce_sum();
1160 s4 += (v2 * v2).reduce_sum();
1161 n += N;
1162 i += N;
1163 }
1164 for &x in &data[i..] {
1165 let xf = x as f64;
1166 s1 += xf;
1167 let x2 = xf * xf;
1168 s2 += x2;
1169 s3 += x2 * xf;
1170 s4 += x2 * x2;
1171 n += 1;
1172 }
1173 } else {
1174 let mb = mask.expect("mask must be Some when nulls present");
1175 let mask_bytes = mb.as_bytes();
1176 while i + N <= len {
1177 let v_i = V::<N>::from_slice(&data[i..i + N]);
1178 let lane_m: Mask<M<N>, N> =
1179 bitmask_to_simd_mask::<N, M<N>>(mask_bytes, i, len);
1180 if !lane_m.any() {
1181 i += N;
1182 continue;
1183 }
1184 let v_masked = lane_m.select(v_i, V::<N>::splat(0));
1185 let v_f = v_masked.cast::<f64>();
1186 let v2 = v_f * v_f;
1187 s1 += v_f.reduce_sum();
1188 s2 += v2.reduce_sum();
1189 s3 += (v2 * v_f).reduce_sum();
1190 s4 += (v2 * v2).reduce_sum();
1191 n += lane_m.to_bitmask().count_ones() as usize;
1192 i += N;
1193 }
1194 for j in i..len {
1195 if unsafe { mb.get_unchecked(j) } {
1196 let xf = data[j] as f64;
1197 let x2 = xf * xf;
1198 s1 += xf;
1199 s2 += x2;
1200 s3 += x2 * xf;
1201 s4 += x2 * x2;
1202 n += 1;
1203 }
1204 }
1205 }
1206 return (s1, s2, s3, s4, n);
1207 }
1208 }
1210
1211 let mut s1: $acc = 0;
1214 let mut s2: $acc = 0;
1215 let mut s3: $acc = 0;
1216 let mut s4: $acc = 0;
1217 let mut n = 0usize;
1218
1219 if !has_nulls {
1220 for &x in data {
1221 let xi: $acc = x as $acc;
1222 let x2 = xi * xi;
1223 s1 += xi;
1224 s2 += x2;
1225 s3 += x2 * xi;
1226 s4 += x2 * x2;
1227 n += 1;
1228 }
1229 } else {
1230 let mb = mask.expect("mask must be Some when nulls present");
1231 for (idx, &x) in data.iter().enumerate() {
1232 if unsafe { mb.get_unchecked(idx) } {
1233 let xi: $acc = x as $acc;
1234 let x2 = xi * xi;
1235 s1 += xi;
1236 s2 += x2;
1237 s3 += x2 * xi;
1238 s4 += x2 * x2;
1239 n += 1;
1240 }
1241 }
1242 }
1243 (s1 as f64, s2 as f64, s3 as f64, s4 as f64, n)
1244 }
1245 };
1246}
1247
1248macro_rules! impl_weighted_sum2_float {
1250 ($fn:ident, $ty:ty, $LANES:expr) => {
1251 #[doc = concat!(
1252 "Returns `(∑w·x, ∑w, ∑w·x², rows)` for `", stringify!($ty),
1253 "` slices `vals`, `wts` with optional null mask."
1254 )]
1255 #[inline(always)]
1256 pub fn $fn(
1257 vals: &[$ty],
1258 wts: &[$ty],
1259 mask: Option<&Bitmask>,
1260 null_count: Option<usize>,
1261 ) -> (f64, f64, f64, usize) {
1262 debug_assert_eq!(vals.len(), wts.len(), "weighted_sum2x: len mismatch");
1263 let has_nulls = has_nulls(null_count, mask);
1264
1265 #[cfg(feature = "simd")]
1266 {
1267 if is_simd_aligned(vals) && is_simd_aligned(wts) {
1269 type V<const L: usize> = Simd<$ty, L>;
1270 type M<const L: usize> = <$ty as SimdElement>::Mask;
1271 const N: usize = $LANES;
1272
1273 let len = vals.len();
1274 let mut i = 0usize;
1275 let mut swx = 0.0;
1276 let mut sw = 0.0;
1277 let mut swx2 = 0.0;
1278 let mut n = 0usize;
1279
1280 if !has_nulls {
1281 while i + N <= len {
1282 let v = V::<N>::from_slice(&vals[i..i + N]).cast::<f64>();
1283 let w = V::<N>::from_slice(&wts[i..i + N]).cast::<f64>();
1284 swx += (v * w).reduce_sum();
1285 sw += w.reduce_sum();
1286 swx2 += (w * v * v).reduce_sum();
1287 n += N;
1288 i += N;
1289 }
1290 for j in i..len {
1291 let vf = vals[j] as f64;
1292 let wf = wts[j] as f64;
1293 swx += wf * vf;
1294 sw += wf;
1295 swx2 += wf * vf * vf;
1296 n += 1;
1297 }
1298 } else {
1299 let mb = mask.expect("mask = Some when nulls present");
1300 let mask_bytes = mb.as_bytes();
1301 while i + N <= len {
1302 let v_raw = V::<N>::from_slice(&vals[i..i + N]);
1303 let w_raw = V::<N>::from_slice(&wts[i..i + N]);
1304 let lane_m: Mask<M<N>, N> =
1305 bitmask_to_simd_mask::<N, M<N>>(mask_bytes, i, len);
1306 if !lane_m.any() {
1307 i += N;
1308 continue;
1309 }
1310 let v = lane_m.select(v_raw, V::<N>::splat(0.0)).cast::<f64>();
1311 let w = lane_m.select(w_raw, V::<N>::splat(0.0)).cast::<f64>();
1312 swx += (v * w).reduce_sum();
1313 sw += w.reduce_sum();
1314 swx2 += (w * v * v).reduce_sum();
1315 n += lane_m.to_bitmask().count_ones() as usize;
1316 i += N;
1317 }
1318 for j in i..len {
1319 if unsafe { mb.get_unchecked(j) } {
1320 let vf = vals[j] as f64;
1321 let wf = wts[j] as f64;
1322 swx += wf * vf;
1323 sw += wf;
1324 swx2 += wf * vf * vf;
1325 n += 1;
1326 }
1327 }
1328 }
1329 return (swx, sw, swx2, n);
1330 }
1331 }
1333
1334 let mut swx = 0.0;
1336 let mut sw = 0.0;
1337 let mut swx2 = 0.0;
1338 let mut n = 0usize;
1339
1340 if !has_nulls {
1341 for i in 0..vals.len() {
1342 let vf = vals[i] as f64;
1343 let wf = wts[i] as f64;
1344 swx += wf * vf;
1345 sw += wf;
1346 swx2 += wf * vf * vf;
1347 n += 1;
1348 }
1349 } else {
1350 let mb = mask.expect("mask = Some when nulls present");
1351 for i in 0..vals.len() {
1352 if unsafe { mb.get_unchecked(i) } {
1353 let vf = vals[i] as f64;
1354 let wf = wts[i] as f64;
1355 swx += wf * vf;
1356 sw += wf;
1357 swx2 += wf * vf * vf;
1358 n += 1;
1359 }
1360 }
1361 }
1362 (swx, sw, swx2, n)
1363 }
1364 };
1365}
1366
1367#[derive(Default)]
1388pub struct PairStats {
1389 pub n: usize,
1391 pub sx: f64,
1393 pub sy: f64,
1395 pub sxy: f64,
1397 pub sx2: f64,
1399 pub sy2: f64,
1401}
1402
1403macro_rules! impl_pair_stats_float {
1405 ($fn:ident, $ty:ty, $LANES:expr) => {
1406 #[inline(always)]
1408 pub fn $fn(
1409 xs: &[$ty],
1410 ys: &[$ty],
1411 mask: Option<&Bitmask>,
1412 null_count: Option<usize>,
1413 ) -> PairStats {
1414 debug_assert_eq!(xs.len(), ys.len(), "pair_stats: len mismatch ");
1415 let has_nulls = has_nulls(null_count, mask);
1416
1417 #[cfg(feature = "simd")]
1418 {
1419 if is_simd_aligned(xs) && is_simd_aligned(ys) {
1421 type V<const L: usize> = Simd<$ty, L>;
1422 type M<const L: usize> = <$ty as SimdElement>::Mask;
1423 const N: usize = $LANES;
1424 let len = xs.len();
1425
1426 let mut i = 0usize;
1427 let mut sx = 0.0;
1428 let mut sy = 0.0;
1429 let mut sxy = 0.0;
1430 let mut sx2 = 0.0;
1431 let mut sy2 = 0.0;
1432 let mut n = 0usize;
1433
1434 if !has_nulls {
1435 while i + N <= len {
1436 let vx = V::<N>::from_slice(&xs[i..i + N]).cast::<f64>();
1437 let vy = V::<N>::from_slice(&ys[i..i + N]).cast::<f64>();
1438
1439 sx += vx.reduce_sum();
1440 sy += vy.reduce_sum();
1441 sxy += (vx * vy).reduce_sum();
1442 sx2 += (vx * vx).reduce_sum();
1443 sy2 += (vy * vy).reduce_sum();
1444 n += N;
1445 i += N;
1446 }
1447 for j in i..len {
1448 let xf = xs[j] as f64;
1449 let yf = ys[j] as f64;
1450 sx += xf;
1451 sy += yf;
1452 sxy += xf * yf;
1453 sx2 += xf * xf;
1454 sy2 += yf * yf;
1455 n += 1;
1456 }
1457 } else {
1458 let mb = mask.expect("mask Some when nulls present");
1459 let mask_bytes = mb.as_bytes();
1460
1461 while i + N <= len {
1462 let vx_raw = V::<N>::from_slice(&xs[i..i + N]);
1463 let vy_raw = V::<N>::from_slice(&ys[i..i + N]);
1464 let lane_m: Mask<M<N>, N> =
1465 bitmask_to_simd_mask::<N, M<N>>(mask_bytes, i, len);
1466 if !lane_m.any() {
1467 i += N;
1468 continue;
1469 }
1470
1471 let vx = lane_m.select(vx_raw, V::<N>::splat(0.0)).cast::<f64>();
1472 let vy = lane_m.select(vy_raw, V::<N>::splat(0.0)).cast::<f64>();
1473
1474 sx += vx.reduce_sum();
1475 sy += vy.reduce_sum();
1476 sxy += (vx * vy).reduce_sum();
1477 sx2 += (vx * vx).reduce_sum();
1478 sy2 += (vy * vy).reduce_sum();
1479 n += lane_m.to_bitmask().count_ones() as usize;
1480 i += N;
1481 }
1482 for j in i..len {
1483 if unsafe { mb.get_unchecked(j) } {
1484 let xf = xs[j] as f64;
1485 let yf = ys[j] as f64;
1486 sx += xf;
1487 sy += yf;
1488 sxy += xf * yf;
1489 sx2 += xf * xf;
1490 sy2 += yf * yf;
1491 n += 1;
1492 }
1493 }
1494 }
1495 return PairStats {
1496 n,
1497 sx,
1498 sy,
1499 sxy,
1500 sx2,
1501 sy2,
1502 };
1503 }
1504 }
1506
1507 let mut sx = 0.0;
1509 let mut sy = 0.0;
1510 let mut sxy = 0.0;
1511 let mut sx2 = 0.0;
1512 let mut sy2 = 0.0;
1513 let mut n = 0usize;
1514
1515 if !has_nulls {
1516 for i in 0..xs.len() {
1517 let xf = xs[i] as f64;
1518 let yf = ys[i] as f64;
1519 sx += xf;
1520 sy += yf;
1521 sxy += xf * yf;
1522 sx2 += xf * xf;
1523 sy2 += yf * yf;
1524 n += 1;
1525 }
1526 } else {
1527 let mb = mask.expect("mask Some when nulls present");
1528 for i in 0..xs.len() {
1529 if unsafe { mb.get_unchecked(i) } {
1530 let xf = xs[i] as f64;
1531 let yf = ys[i] as f64;
1532 sx += xf;
1533 sy += yf;
1534 sxy += xf * yf;
1535 sx2 += xf * xf;
1536 sy2 += yf * yf;
1537 n += 1;
1538 }
1539 }
1540 }
1541 PairStats {
1542 n,
1543 sx,
1544 sy,
1545 sxy,
1546 sx2,
1547 sy2,
1548 }
1549 }
1550 };
1551}
1552
1553#[inline(always)]
1555fn moments4_scalar<I>(iter: I) -> (usize, f64, f64, f64, f64)
1556where
1557 I: IntoIterator<Item = f64>,
1558{
1559 let mut n = 0usize;
1560 let mut m1 = 0.0; let mut m2 = 0.0; let mut m3 = 0.0; let mut m4 = 0.0; for x in iter {
1566 n += 1;
1567 let n_f = n as f64;
1568 let delta = x - m1;
1569 let delta_n = delta / n_f;
1570 let delta_n2 = delta_n * delta_n;
1571 let term1 = delta * delta_n * (n_f - 1.0);
1572
1573 m4 += term1 * delta_n2 * (n_f * n_f - 3.0 * n_f + 3.0) + 6.0 * delta_n2 * m2
1574 - 4.0 * delta_n * m3;
1575
1576 m3 += term1 * delta_n * (n_f - 2.0) - 3.0 * delta_n * m2;
1577 m2 += term1;
1578 m1 += delta_n;
1579 }
1580 (n, m1, m2, m3, m4)
1581}
1582
1583macro_rules! impl_skew_kurt_float {
1586 ($ty:ident, $skew_fn:ident, $kurt_fn:ident) => {
1587 #[inline(always)]
1604 pub fn $skew_fn(
1605 d: &[$ty],
1606 m: Option<&Bitmask>,
1607 null_count: Option<usize>,
1608 adjust_sample_bias: bool,
1609 ) -> Option<f64> {
1610 let vals: Vec<f64> = if has_nulls(null_count, m) {
1611 collect_valid(d, m).into_iter().map(|x| x as f64).collect()
1612 } else {
1613 d.iter().map(|&x| x as f64).collect()
1614 };
1615
1616 let (n, _m1, m2, m3, _) = moments4_scalar(vals);
1617 if n < 3 || m2 == 0.0 {
1618 return None;
1619 }
1620 let n_f = n as f64;
1621
1622 let pop = n_f.sqrt() * m3 / m2.powf(1.5);
1624 if !adjust_sample_bias {
1625 return Some(pop);
1626 }
1627
1628 Some(n_f * (n_f - 1.0).sqrt() * m3 / ((n_f - 2.0) * m2.powf(1.5)))
1630 }
1631
1632 #[inline(always)]
1649 pub fn $kurt_fn(
1650 d: &[$ty],
1651 m: Option<&Bitmask>,
1652 null_count: Option<usize>,
1653 adjust_sample_bias: bool,
1654 ) -> Option<f64> {
1655 let vals: Vec<f64> = if has_nulls(null_count, m) {
1656 collect_valid(d, m).into_iter().map(|x| x as f64).collect()
1657 } else {
1658 d.iter().map(|&x| x as f64).collect()
1659 };
1660
1661 let (n, _m1, m2, _m3, m4) = moments4_scalar(vals);
1662 if n < 2 || m2 == 0.0 {
1663 return None;
1664 }
1665 let n_f = n as f64;
1666 let pop_excess = n_f * m4 / (m2 * m2) - 3.0;
1667 if !adjust_sample_bias {
1668 return Some(pop_excess);
1669 }
1670
1671 if n < 4 {
1673 return Some(pop_excess);
1674 }
1675
1676 let numerator = (n_f + 1.0) * n_f * (m4 / (m2 * m2)) - 3.0 * (n_f - 1.0);
1678 let denominator = (n_f - 2.0) * (n_f - 3.0);
1679 Some(numerator * (n_f - 1.0) / denominator)
1680 }
1681 };
1682}
1683
1684macro_rules! impl_skew_kurt_int {
1687 ($ty:ident, $skew_fn:ident, $kurt_fn:ident) => {
1688 #[inline(always)]
1704 pub fn $skew_fn(
1705 d: &[$ty],
1706 m: Option<&Bitmask>,
1707 null_count: Option<usize>,
1708 adjust_sample_bias: bool,
1709 ) -> Option<f64> {
1710 let vals: Vec<f64> = if has_nulls(null_count, m) {
1711 collect_valid(d, m).into_iter().map(|x| x as f64).collect()
1712 } else {
1713 d.iter().map(|&x| x as f64).collect()
1714 };
1715
1716 let (n, _m1, m2, m3, _) = moments4_scalar(vals);
1717 if n < 3 || m2 == 0.0 {
1718 return None;
1719 }
1720 let n_f = n as f64;
1721
1722 let pop = n_f.sqrt() * m3 / m2.powf(1.5);
1723 if !adjust_sample_bias {
1724 return Some(pop);
1725 }
1726
1727 Some(n_f * (n_f - 1.0).sqrt() * m3 / ((n_f - 2.0) * m2.powf(1.5)))
1728 }
1729
1730 #[inline(always)]
1746 pub fn $kurt_fn(
1747 d: &[$ty],
1748 m: Option<&Bitmask>,
1749 null_count: Option<usize>,
1750 adjust_sample_bias: bool,
1751 ) -> Option<f64> {
1752 let vals: Vec<f64> = if has_nulls(null_count, m) {
1753 collect_valid(d, m).into_iter().map(|x| x as f64).collect()
1754 } else {
1755 d.iter().map(|&x| x as f64).collect()
1756 };
1757
1758 let (n, _m1, m2, _m3, m4) = moments4_scalar(vals);
1759 if n < 2 || m2 == 0.0 {
1760 return None;
1761 }
1762 let n_f = n as f64;
1763
1764 let pop_excess = n_f * m4 / (m2 * m2) - 3.0;
1765 if !adjust_sample_bias {
1766 return Some(pop_excess);
1767 }
1768 if n < 4 {
1769 return Some(pop_excess);
1770 }
1771
1772 let numerator = (n_f + 1.0) * n_f * (m4 / (m2 * m2)) - 3.0 * (n_f - 1.0);
1773 let denominator = (n_f - 2.0) * (n_f - 3.0);
1774 Some(numerator * (n_f - 1.0) / denominator)
1775 }
1776 };
1777}
1778
1779#[inline(always)]
1782pub fn sum_squares(v: &[f64], mask: Option<&Bitmask>, null_count: Option<usize>) -> f64 {
1783 let has_nulls = has_nulls(null_count, mask);
1784
1785 #[cfg(feature = "simd")]
1786 if is_simd_aligned(v) {
1787 type V<const L: usize> = Simd<f64, L>;
1788 type M<const L: usize> = <f64 as SimdElement>::Mask;
1789 const N: usize = W64;
1790 let len = v.len();
1791 let mut i = 0;
1792 let mut acc = 0.0f64;
1793 if !has_nulls {
1794 while i + N <= len {
1795 let simd_v = V::<N>::from_slice(&v[i..i + N]);
1796 acc += (simd_v * simd_v).reduce_sum();
1797 i += N;
1798 }
1799 for &x in &v[i..] {
1800 acc += x * x;
1801 }
1802 } else {
1803 let mb = mask.expect("sum_squares: mask must be Some when nulls are present");
1804 let mask_bytes = mb.as_bytes();
1805 while i + N <= len {
1806 let simd_v = V::<N>::from_slice(&v[i..i + N]);
1807 let lane_m: Mask<M<N>, N> = bitmask_to_simd_mask::<N, M<N>>(mask_bytes, i, len);
1808 let simd_v_masked = lane_m.select(simd_v, V::<N>::splat(0.0));
1809 acc += (simd_v_masked * simd_v_masked).reduce_sum();
1810 i += N;
1811 }
1812 for j in i..len {
1813 if unsafe { mb.get_unchecked(j) } {
1814 acc += v[j] * v[j];
1815 }
1816 }
1817 }
1818 return acc;
1819 }
1820
1821 let mut acc = 0.0f64;
1823 if !has_nulls {
1824 for &x in v {
1825 acc += x * x;
1826 }
1827 } else {
1828 let mb = mask.expect("sum_squares: mask must be Some when nulls are present");
1829 for (j, &x) in v.iter().enumerate() {
1830 if unsafe { mb.get_unchecked(j) } {
1831 acc += x * x;
1832 }
1833 }
1834 }
1835 acc
1836}
1837
1838#[inline(always)]
1841pub fn sum_cubes(v: &[f64], mask: Option<&Bitmask>, null_count: Option<usize>) -> f64 {
1842 let has_nulls = has_nulls(null_count, mask);
1843
1844 #[cfg(feature = "simd")]
1845 if is_simd_aligned(v) {
1846 type V<const L: usize> = Simd<f64, L>;
1847 type M<const L: usize> = <f64 as SimdElement>::Mask;
1848 const N: usize = W64;
1849 let len = v.len();
1850 let mut i = 0;
1851 let mut acc = 0.0f64;
1852 let mut found_infinite = false;
1853
1854 if !has_nulls {
1855 while i + N <= len {
1856 let simd_v = V::<N>::from_slice(&v[i..i + N]);
1857 let simd_v2 = simd_v * simd_v;
1858 let simd_cubes = simd_v2 * simd_v;
1859 let infinite_mask = simd_cubes.is_infinite();
1861 if infinite_mask.any() {
1862 found_infinite = true;
1863 }
1864 acc += simd_cubes.reduce_sum();
1865 i += N;
1866 }
1867 for &x in &v[i..] {
1868 let cube = x * x * x;
1869 if cube.is_infinite() {
1870 found_infinite = true;
1871 }
1872 acc += cube;
1873 }
1874 } else {
1875 let mb = mask.expect("sum_cubes: mask must be Some when nulls are present");
1876 let mask_bytes = mb.as_bytes();
1877 while i + N <= len {
1878 let simd_v = V::<N>::from_slice(&v[i..i + N]);
1879 let lane_m: Mask<M<N>, N> = bitmask_to_simd_mask::<N, M<N>>(mask_bytes, i, len);
1880 let simd_v_masked = lane_m.select(simd_v, V::<N>::splat(0.0));
1881 let simd_v2 = simd_v_masked * simd_v_masked;
1882 let simd_cubes = simd_v2 * simd_v_masked;
1883 let infinite_mask = simd_cubes.is_infinite();
1884 if infinite_mask.any() {
1885 found_infinite = true;
1886 }
1887 acc += simd_cubes.reduce_sum();
1888 i += N;
1889 }
1890 for j in i..len {
1891 if unsafe { mb.get_unchecked(j) } {
1892 let cube = v[j] * v[j] * v[j];
1893 if cube.is_infinite() {
1894 found_infinite = true;
1895 }
1896 acc += cube;
1897 }
1898 }
1899 }
1900 return if acc.is_nan() && found_infinite {
1901 f64::INFINITY
1902 } else {
1903 acc
1904 };
1905 }
1906
1907 let mut acc = 0.0f64;
1909 let mut found_infinite = false;
1910 if !has_nulls {
1911 for &x in v {
1912 let cube = x * x * x;
1913 if cube.is_infinite() {
1914 found_infinite = true;
1915 }
1916 acc += cube;
1917 }
1918 } else {
1919 let mb = mask.expect("sum_cubes: mask must be Some when nulls are present");
1920 for (j, &x) in v.iter().enumerate() {
1921 if unsafe { mb.get_unchecked(j) } {
1922 let cube = x * x * x;
1923 if cube.is_infinite() {
1924 found_infinite = true;
1925 }
1926 acc += cube;
1927 }
1928 }
1929 }
1930 if acc.is_nan() && found_infinite {
1931 f64::INFINITY
1932 } else {
1933 acc
1934 }
1935}
1936
1937#[inline(always)]
1940pub fn sum_quartics(v: &[f64], mask: Option<&Bitmask>, null_count: Option<usize>) -> f64 {
1941 let has_nulls = has_nulls(null_count, mask);
1942
1943 #[cfg(feature = "simd")]
1944 if is_simd_aligned(v) {
1945 type V<const L: usize> = Simd<f64, L>;
1946 type M<const L: usize> = <f64 as SimdElement>::Mask;
1947 const N: usize = W64;
1948 let len = v.len();
1949 let mut i = 0;
1950 let mut acc = 0.0f64;
1951 if !has_nulls {
1952 while i + N <= len {
1953 let simd_v = V::<N>::from_slice(&v[i..i + N]);
1954 let simd_v2 = simd_v * simd_v;
1955 acc += (simd_v2 * simd_v2).reduce_sum();
1956 i += N;
1957 }
1958 for &x in &v[i..] {
1959 let x2 = x * x;
1960 acc += x2 * x2;
1961 }
1962 } else {
1963 let mb = mask.expect("sum_quartics: mask must be Some when nulls are present");
1964 let mask_bytes = mb.as_bytes();
1965 while i + N <= len {
1966 let simd_v = V::<N>::from_slice(&v[i..i + N]);
1967 let lane_m: Mask<M<N>, N> = bitmask_to_simd_mask::<N, M<N>>(mask_bytes, i, len);
1968 let simd_v_masked = lane_m.select(simd_v, V::<N>::splat(0.0));
1969 let simd_v2 = simd_v_masked * simd_v_masked;
1970 acc += (simd_v2 * simd_v2).reduce_sum();
1971 i += N;
1972 }
1973 for j in i..len {
1974 if unsafe { mb.get_unchecked(j) } {
1975 let x2 = v[j] * v[j];
1976 acc += x2 * x2;
1977 }
1978 }
1979 }
1980 return acc;
1981 }
1982
1983 let mut acc = 0.0f64;
1985 if !has_nulls {
1986 for &x in v {
1987 let x2 = x * x;
1988 acc += x2 * x2;
1989 }
1990 } else {
1991 let mb = mask.expect("sum_quartics: mask must be Some when nulls are present");
1992 for (j, &x) in v.iter().enumerate() {
1993 if unsafe { mb.get_unchecked(j) } {
1994 let x2 = x * x;
1995 acc += x2 * x2;
1996 }
1997 }
1998 }
1999 acc
2000}
2001
2002impl_weighted_sum2_float!(weighted_sum2_f64, f64, W64);
2005impl_weighted_sum2_float!(weighted_sum2_f32, f32, W32);
2006
2007impl_pair_stats_float!(pair_stats_f64, f64, W64);
2008impl_pair_stats_float!(pair_stats_f32, f32, W32);
2009
2010impl_stat_moments4_float!(stat_moments4_f64, f64, W64);
2011impl_stat_moments4_float!(stat_moments4_f32, f32, W32);
2012
2013impl_stat_moments4_int!(stat_moments4_i64, i64, i128, W64);
2014impl_stat_moments4_int!(stat_moments4_u64, u64, u128, W64);
2015impl_stat_moments4_int!(stat_moments4_i32, i32, i128, W32);
2016impl_stat_moments4_int!(stat_moments4_u32, u32, u128, W32);
2017
2018impl_stat_moments_float!(stat_moments_f64, f64, W64);
2019impl_stat_moments_float!(stat_moments_f32, f32, W32);
2020impl_stat_moments_int!(stat_moments_i64, i64, i64, W64, i64);
2021impl_stat_moments_int!(stat_moments_u64, u64, u64, W64, i64);
2022impl_stat_moments_int!(stat_moments_i32, i32, i64, W32, i32);
2023impl_stat_moments_int!(stat_moments_u32, u32, u64, W32, i32);
2024impl_reduce_min_max!(reduce_min_max_i64, i64, W64, i64::MAX, i64::MIN);
2025impl_reduce_min_max!(reduce_min_max_u64, u64, W64, u64::MAX, u64::MIN);
2026impl_reduce_min_max!(reduce_min_max_i32, i32, W32, i32::MAX, i32::MIN);
2027impl_reduce_min_max!(reduce_min_max_u32, u32, W32, u32::MAX, u32::MIN);
2028impl_reduce_min_max_float!(reduce_min_max_f64, f64, W64, Simd, Mask);
2029impl_reduce_min_max_float!(reduce_min_max_f32, f32, W32, Simd, Mask);
2030
2031impl_min_max_range!(f64, reduce_min_max_f64, min_f64, max_f64, range_f64);
2032impl_min_max_range!(f32, reduce_min_max_f32, min_f32, max_f32, range_f32);
2033impl_min_max_range!(i64, reduce_min_max_i64, min_i64, max_i64, range_i64);
2034impl_min_max_range!(u64, reduce_min_max_u64, min_u64, max_u64, range_u64);
2035impl_min_max_range!(i32, reduce_min_max_i32, min_i32, max_i32, range_i32);
2036impl_min_max_range!(u32, reduce_min_max_u32, min_u32, max_u32, range_u32);
2037
2038impl_sum_avg!(float f64, sum_f64, average_f64, sum_f64_raw);
2039impl_sum_avg!(float f32, sum_f32, average_f32, sum_f32_raw);
2040
2041impl_sum_avg!(int i64, sum_i64, average_i64, i64, sum_i64_raw);
2042impl_sum_avg!(int u64, sum_u64, average_u64, u64, sum_u64_raw);
2043impl_sum_avg!(int i32, sum_i32, average_i32, i64 , sum_i32_raw);
2044impl_sum_avg!(int u32, sum_u32, average_u32, u64 , sum_u32_raw);
2045
2046impl_variance!(f64, variance_f64);
2047impl_variance!(f32, variance_f32);
2048impl_variance!(i64, variance_i64);
2049impl_variance!(u64, variance_u64);
2050impl_variance!(i32, variance_i32);
2051impl_variance!(u32, variance_u32);
2052
2053impl_sum_float!(sum_f64_raw, f64, W64);
2054impl_sum_float!(sum_f32_raw, f32, W32);
2055impl_sum_int!(sum_i64_raw, i64, i64, W64);
2056impl_sum_int!(sum_u64_raw, u64, u64, W64);
2057impl_sum_int!(sum_i32_raw, i32, i64, W32);
2058impl_sum_int!(sum_u32_raw, u32, u64, W32);
2059
2060impl_sum2_float!(sum2_f64_raw, f64, W64);
2061impl_sum2_float!(sum2_f32_raw, f32, W32);
2062
2063impl_count!(count_generic_raw);
2064
2065impl_skew_kurt_float!(f64, skewness_f64, kurtosis_f64);
2066impl_skew_kurt_float!(f32, skewness_f32, kurtosis_f32);
2067
2068impl_skew_kurt_int!(i64, skewness_i64, kurtosis_i64);
2069impl_skew_kurt_int!(u64, skewness_u64, kurtosis_u64);
2070impl_skew_kurt_int!(i32, skewness_i32, kurtosis_i32);
2071impl_skew_kurt_int!(u32, skewness_u32, kurtosis_u32);
2072
2073#[inline(always)]
2075pub fn median<T: Ord + Copy>(
2076 d: &[T],
2077 m: Option<&Bitmask>,
2078 null_count: Option<usize>,
2079 sort: bool,
2080) -> Option<T> {
2081 let has_nulls = match null_count {
2082 Some(n) => n > 0,
2083 None => m.is_some(),
2084 };
2085
2086 let mut v = if !has_nulls {
2088 Vec64::from_slice(d)
2089 } else {
2090 collect_valid(d, m)
2091 };
2092
2093 if v.is_empty() {
2094 return None;
2095 }
2096
2097 let len = v.len();
2098 let mid = len / 2;
2099
2100 if len % 2 == 1 {
2101 if sort {
2103 let (_, nth, _) = v.select_nth_unstable(mid);
2104 Some(*nth)
2105 } else {
2106 Some(v[mid])
2107 }
2108 } else {
2109 if sort {
2112 let (_, nth, _) = v.select_nth_unstable(mid - 1);
2113 Some(*nth)
2114 } else {
2115 Some(v[mid])
2116 }
2117 }
2118}
2119
2120#[inline(always)]
2122pub fn median_f<T: Float + Copy>(
2123 d: &[T],
2124 m: Option<&Bitmask>,
2125 null_count: Option<usize>,
2126 sort: bool,
2127) -> Option<T> {
2128 let has_nulls = match null_count {
2129 Some(n) => n > 0,
2130 None => m.is_some(),
2131 };
2132
2133 let mut v = if !has_nulls {
2134 Vec64::from_slice(d)
2135 } else {
2136 collect_valid(d, m)
2137 };
2138
2139 if v.is_empty() {
2140 return None;
2141 }
2142
2143 let len = v.len();
2144 let mid = len / 2;
2145
2146 if !sort {
2147 return Some(if len & 1 == 0 {
2148 (v[mid - 1] + v[mid]) / T::from(2.0).unwrap()
2149 } else {
2150 v[mid]
2151 });
2152 }
2153
2154 if len & 1 == 1 {
2155 let (_, nth, _) = v.select_nth_unstable_by(mid, total_cmp_f);
2156 Some(*nth)
2157 } else {
2158 let (_, nth_hi, _) = v.select_nth_unstable_by(mid, total_cmp_f);
2159 let hi = *nth_hi;
2160 let (_, nth_lo, _) = v.select_nth_unstable_by(mid - 1, total_cmp_f);
2161 let lo = *nth_lo;
2162 Some((lo + hi) / T::from(2.0).unwrap())
2163 }
2164}
2165#[inline(always)]
2167pub fn percentile_ord<T: Ord + Copy>(
2168 d: &[T],
2169 m: Option<&Bitmask>,
2170 null_count: Option<usize>,
2171 p: f64,
2172 sort: bool,
2173) -> Option<T> {
2174 let has_nulls = match null_count {
2175 Some(n) => n > 0,
2176 None => m.is_some(),
2177 };
2178
2179 let mut v = if !has_nulls {
2180 Vec64::from_slice(d)
2181 } else {
2182 collect_valid(d, m)
2183 };
2184
2185 if v.is_empty() {
2186 return None;
2187 }
2188
2189 let idx = ((p / 100.0) * (v.len() as f64 - 1.0)).round() as usize;
2190
2191 if !sort {
2192 return v.get(idx).copied();
2193 }
2194
2195 let (_, nth, _) = v.select_nth_unstable(idx);
2196 Some(*nth)
2197}
2198
2199#[inline(always)]
2201pub fn percentile_f<T: Float + Copy>(
2202 d: &[T],
2203 m: Option<&Bitmask>,
2204 null_count: Option<usize>,
2205 p: f64,
2206 sort: bool,
2207) -> Option<T> {
2208 let has_nulls = match null_count {
2209 Some(n) => n > 0,
2210 None => m.is_some(),
2211 };
2212
2213 let mut v = if !has_nulls {
2214 Vec64::from_slice(d)
2215 } else {
2216 collect_valid(d, m)
2217 };
2218
2219 if v.is_empty() {
2220 return None;
2221 }
2222
2223 let idx = (p.clamp(0.0, 1.0) * (v.len() - 1) as f64).round() as usize;
2224
2225 if !sort {
2226 return v.get(idx).copied();
2227 }
2228
2229 let (_, nth, _) = v.select_nth_unstable_by(idx, total_cmp_f);
2230 Some(*nth)
2231}
2232
2233#[inline(always)]
2235pub fn quantile<T: Ord + Copy>(
2236 d: &[T],
2237 m: Option<&Bitmask>,
2238 null_count: Option<usize>,
2239 q: usize,
2240 sort: bool,
2241) -> Option<Vec64<T>> {
2242 if q < 2 {
2243 return None;
2244 }
2245
2246 let has_nulls = match null_count {
2247 Some(n) => n > 0,
2248 None => m.is_some(),
2249 };
2250
2251 let mut v = if !has_nulls {
2252 Vec64::from_slice(d)
2253 } else {
2254 collect_valid(d, m)
2255 };
2256
2257 let n = v.len();
2258 if n == 0 {
2259 return None;
2260 }
2261
2262 if sort {
2263 v.sort_unstable();
2264 }
2265
2266 let mut out = Vec64::with_capacity(q - 1);
2267 unsafe {
2268 out.set_len(q - 1);
2269 }
2270
2271 for k in 1..q {
2272 let p = k as f64 / q as f64;
2273 let h = 1.0 + (n as f64 - 1.0) * p;
2274 let idx = h.floor() as usize;
2275 let idx = idx.saturating_sub(1).min(n - 1);
2276 unsafe {
2277 *out.get_unchecked_mut(k - 1) = v[idx];
2278 }
2279 }
2280
2281 Some(out)
2282}
2283
2284#[inline(always)]
2286pub fn quantile_f<T: Float + Copy>(
2287 d: &[T],
2288 m: Option<&Bitmask>,
2289 null_count: Option<usize>,
2290 q: usize,
2291 sort: bool,
2292) -> Option<Vec64<T>> {
2293 if q < 2 {
2294 return None;
2295 }
2296
2297 let has_nulls = match null_count {
2298 Some(n) => n > 0,
2299 None => m.is_some(),
2300 };
2301
2302 let mut v = if !has_nulls {
2303 Vec64::from_slice(d)
2304 } else {
2305 collect_valid(d, m)
2306 };
2307
2308 let n = v.len();
2309 if n == 0 {
2310 return None;
2311 }
2312
2313 if sort {
2314 sort_float(&mut v);
2315 }
2316
2317 let mut out = Vec64::with_capacity(q - 1);
2318 unsafe {
2319 out.set_len(q - 1);
2320 }
2321
2322 for k in 1..q {
2323 let p = k as f64 / q as f64;
2324 let h = 1.0 + (n as f64 - 1.0) * p;
2325 let hf = h.floor();
2326 let hc = h.ceil();
2327
2328 let idx_lo = (hf as usize).saturating_sub(1).min(n - 1);
2329 let idx_hi = (hc as usize).saturating_sub(1).min(n - 1);
2330
2331 let value = if idx_lo == idx_hi {
2332 v[idx_lo]
2333 } else {
2334 let weight = T::from(h - hf).unwrap();
2335 let v_lo = v[idx_lo];
2336 let v_hi = v[idx_hi];
2337 v_lo + (v_hi - v_lo) * weight
2338 };
2339
2340 unsafe {
2341 *out.get_unchecked_mut(k - 1) = value;
2342 }
2343 }
2344
2345 Some(out)
2346}
2347
2348#[inline(always)]
2350pub fn iqr<T>(d: &[T], m: Option<&Bitmask>, null_count: Option<usize>, s: bool) -> Option<T>
2351where
2352 T: Ord + Copy + Sub<Output = T>,
2353{
2354 Some(percentile_ord(d, m, null_count, 75.0, s)? - percentile_ord(d, m, null_count, 25.0, s)?)
2355}
2356
2357#[inline(always)]
2359pub fn iqr_f<T: Float + Copy>(
2360 d: &[T],
2361 m: Option<&Bitmask>,
2362 null_count: Option<usize>,
2363 s: bool,
2364) -> Option<T> {
2365 Some(percentile_f(d, m, null_count, 0.75, s)? - percentile_f(d, m, null_count, 0.25, s)?)
2366}
2367
2368#[inline(always)]
2370pub fn mode<T: Eq + Hash + Copy>(
2371 d: &[T],
2372 m: Option<&Bitmask>,
2373 null_count: Option<usize>,
2374) -> Option<T> {
2375 let has_nulls = match null_count {
2376 Some(n) => n > 0,
2377 None => m.is_some(),
2378 };
2379
2380 #[cfg(feature = "fast_hash")]
2381 let mut f = AHashMap::with_capacity(d.len());
2382 #[cfg(not(feature = "fast_hash"))]
2383 let mut f = HashMap::with_capacity(d.len());
2384
2385 if !has_nulls {
2386 for &v in d {
2387 *f.entry(v).or_insert(0usize) += 1;
2388 }
2389 } else {
2390 let mb = m.expect("mode: nulls present but mask is None");
2391 for (i, &v) in d.iter().enumerate() {
2392 if unsafe { mb.get_unchecked(i) } {
2393 *f.entry(v).or_insert(0usize) += 1;
2394 }
2395 }
2396 }
2397
2398 f.into_iter().max_by_key(|e| e.1).map(|e| e.0)
2399}
2400
2401#[inline(always)]
2403pub fn mode_f<T: ToBits + Copy>(
2404 d: &[T],
2405 m: Option<&Bitmask>,
2406 null_count: Option<usize>,
2407) -> Option<T> {
2408 let has_nulls = match null_count {
2409 Some(n) => n > 0,
2410 None => m.is_some(),
2411 };
2412
2413 #[cfg(feature = "fast_hash")]
2414 let mut f: AHashMap<T::Bits, (usize, T)> = AHashMap::with_capacity(d.len());
2415 #[cfg(not(feature = "fast_hash"))]
2416 let mut f: HashMap<T::Bits, (usize, T)> = HashMap::with_capacity(d.len());
2417
2418 if !has_nulls {
2419 for &v in d {
2420 let e = f.entry(v.to_bits()).or_insert((0, v));
2421 e.0 += 1;
2422 }
2423 } else {
2424 let mb = m.expect("mode_f: nulls present but mask is None");
2425 for (i, &v) in d.iter().enumerate() {
2426 if unsafe { mb.get_unchecked(i) } {
2427 let e = f.entry(v.to_bits()).or_insert((0, v));
2428 e.0 += 1;
2429 }
2430 }
2431 }
2432
2433 f.into_values().max_by_key(|x| x.0).map(|x| x.1)
2434}
2435
2436#[inline(always)]
2438pub fn count_distinct<T: Eq + Hash + Copy>(
2439 d: &[T],
2440 m: Option<&Bitmask>,
2441 null_count: Option<usize>,
2442) -> usize {
2443 let has_nulls = match null_count {
2444 Some(n) => n > 0,
2445 None => m.is_some(),
2446 };
2447
2448 if !has_nulls {
2449 d.iter().collect::<HashSet<_>>().len()
2450 } else {
2451 let mb = m.expect("count_distinct: nulls present but mask is None");
2452 d.iter()
2453 .enumerate()
2454 .filter(|(i, _)| unsafe { mb.get_unchecked(*i) })
2455 .map(|(_, v)| v)
2456 .collect::<HashSet<_>>()
2457 .len()
2458 }
2459}
2460
2461#[inline(always)]
2463pub fn count_distinct_f<T: Float + ToBits + Copy>(
2464 d: &[T],
2465 m: Option<&Bitmask>,
2466 null_count: Option<usize>,
2467) -> usize {
2468 let has_nulls = match null_count {
2469 Some(n) => n > 0,
2470 None => m.is_some(),
2471 };
2472
2473 if !has_nulls {
2474 d.iter().map(|v| v.to_bits()).collect::<HashSet<_>>().len()
2475 } else {
2476 let mb = m.expect("count_distinct_f: nulls present but mask is None");
2477 d.iter()
2478 .enumerate()
2479 .filter(|(i, _)| unsafe { mb.get_unchecked(*i) })
2480 .map(|(_, v)| v.to_bits())
2481 .collect::<HashSet<_>>()
2482 .len()
2483 }
2484}
2485
2486#[inline(always)]
2488pub fn harmonic_mean_int<T: NumCast + Copy + PartialOrd>(
2489 d: &[T],
2490 m: Option<&Bitmask>,
2491 null_count: Option<usize>,
2492) -> f64 {
2493 let has_nulls = match null_count {
2494 Some(n) => n > 0,
2495 None => m.is_some(),
2496 };
2497
2498 let v: Vec<f64> = if !has_nulls {
2499 d.iter().map(|&x| NumCast::from(x).unwrap()).collect()
2500 } else {
2501 collect_valid(d, m)
2502 .into_iter()
2503 .map(|x| NumCast::from(x).unwrap())
2504 .collect()
2505 };
2506
2507 if v.is_empty() {
2508 panic!("harmonic_mean_int: input data is empty");
2509 }
2510 if v.iter().any(|&x| x <= 0.0) {
2511 panic!("harmonic_mean_int: non-positive values are invalid");
2512 }
2513
2514 let n = v.len() as f64;
2515 let denom: f64 = v.iter().map(|&x| 1.0 / x).sum();
2516
2517 if denom == 0.0 {
2518 panic!("harmonic_mean_int: denominator is zero");
2519 }
2520
2521 n / denom
2522}
2523
2524#[inline(always)]
2526pub fn harmonic_mean_uint<T: NumCast + Copy + PartialOrd>(
2527 d: &[T],
2528 m: Option<&Bitmask>,
2529 null_count: Option<usize>,
2530) -> f64 {
2531 harmonic_mean_int(d, m, null_count)
2532}
2533
2534#[inline(always)]
2536pub fn harmonic_mean_f<T: Float + Into<f64> + Copy>(
2537 d: &[T],
2538 m: Option<&Bitmask>,
2539 null_count: Option<usize>,
2540) -> f64 {
2541 let has_nulls = match null_count {
2542 Some(n) => n > 0,
2543 None => m.is_some(),
2544 };
2545
2546 let (mut s, mut n) = (0.0, 0usize);
2547
2548 if !has_nulls {
2549 for &v in d {
2550 let x = v.into();
2551 if x <= 0.0 {
2552 panic!("harmonic_mean_f: non-positive values are invalid");
2553 }
2554 s += 1.0 / x;
2555 n += 1;
2556 }
2557 } else {
2558 let mb = m.expect("harmonic_mean_f: nulls present but mask is None");
2559 for (i, &v) in d.iter().enumerate() {
2560 if unsafe { mb.get_unchecked(i) } {
2561 let x = v.into();
2562 if x <= 0.0 {
2563 panic!("harmonic_mean_f: non-positive values are invalid");
2564 }
2565 s += 1.0 / x;
2566 n += 1;
2567 }
2568 }
2569 }
2570
2571 if n == 0 {
2572 panic!("harmonic_mean_f: input data is empty");
2573 }
2574 if s == 0.0 {
2575 panic!("harmonic_mean_f: denominator is zero");
2576 }
2577
2578 n as f64 / s
2579}
2580
2581#[inline(always)]
2583pub fn geometric_mean_int<T: ToPrimitive + Copy>(
2584 d: &[T],
2585 m: Option<&Bitmask>,
2586 null_count: Option<usize>,
2587) -> f64 {
2588 let has_nulls = match null_count {
2589 Some(n) => n > 0,
2590 None => m.is_some(),
2591 };
2592
2593 let (mut s, mut n) = (0.0, 0usize);
2594 if !has_nulls {
2595 for &v in d.iter() {
2596 let val = v.to_f64().unwrap();
2597 if val <= 0.0 {
2598 panic!("geometric_mean_int: non-positive values are invalid");
2599 }
2600 s += val.ln();
2601 n += 1;
2602 }
2603 } else {
2604 let mb = m.expect("geometric_mean_int: nulls present but mask is None");
2605 for (i, &v) in d.iter().enumerate() {
2606 if unsafe { mb.get_unchecked(i) } {
2607 let val = v.to_f64().unwrap();
2608 if val <= 0.0 {
2609 panic!("geometric_mean_int: non-positive values are invalid");
2610 }
2611 s += val.ln();
2612 n += 1;
2613 }
2614 }
2615 }
2616
2617 if n == 0 {
2618 panic!("geometric_mean_int: input data is empty");
2619 }
2620
2621 (s / n as f64).exp()
2622}
2623
2624#[inline(always)]
2626pub fn geometric_mean_uint<T: ToPrimitive + Copy>(
2627 d: &[T],
2628 m: Option<&Bitmask>,
2629 null_count: Option<usize>,
2630) -> f64 {
2631 geometric_mean_int(d, m, null_count)
2632}
2633
2634#[inline(always)]
2636pub fn geometric_mean_f<T: Float + Into<f64> + Copy>(
2637 d: &[T],
2638 m: Option<&Bitmask>,
2639 null_count: Option<usize>,
2640) -> f64 {
2641 let has_nulls = match null_count {
2642 Some(n) => n > 0,
2643 None => m.is_some(),
2644 };
2645
2646 let (mut s, mut n) = (0.0, 0usize);
2647 if !has_nulls {
2648 for &v in d.iter() {
2649 let val = v.into();
2650 if val <= 0.0 {
2651 panic!("geometric_mean_f: non-positive values are invalid");
2652 }
2653 s += val.ln();
2654 n += 1;
2655 }
2656 } else {
2657 let mb = m.expect("geometric_mean_f: nulls present but mask is None");
2658 for (i, &v) in d.iter().enumerate() {
2659 if unsafe { mb.get_unchecked(i) } {
2660 let val = v.into();
2661 if val <= 0.0 {
2662 panic!("geometric_mean_f: non-positive values are invalid");
2663 }
2664 s += val.ln();
2665 n += 1;
2666 }
2667 }
2668 }
2669
2670 if n == 0 {
2671 panic!("geometric_mean_f: input data is empty");
2672 }
2673
2674 (s / n as f64).exp()
2675}
2676
2677#[inline(always)]
2679pub fn agg_product<T: Copy + One + Mul<Output = T> + Zero + PartialEq + 'static>(
2680 data: &[T],
2681 mask: Option<&Bitmask>,
2682 offset: usize,
2683 len: usize,
2684) -> T {
2685 let mut prod = T::one();
2686 match mask {
2687 Some(mask) => {
2688 let mask = mask.slice_clone(offset, len);
2689 for (i, &x) in data[offset..offset + len].iter().enumerate() {
2690 if unsafe { mask.get_unchecked(i) } {
2691 prod = prod * x;
2692 if prod == T::zero() {
2693 break;
2694 }
2695 }
2696 }
2697 }
2698 None => {
2699 for &x in &data[offset..offset + len] {
2700 prod = prod * x;
2701 if prod == T::zero() {
2702 break;
2703 }
2704 }
2705 }
2706 }
2707 prod
2708}
2709
2710#[cfg(test)]
2711mod tests {
2712 use minarrow::{Vec64, vec64};
2713 use num_traits::Float;
2714
2715 use super::*;
2716
2717 fn mask_from_bools(bits: &[bool]) -> Bitmask {
2719 Bitmask::from_bools(bits)
2720 }
2721
2722 fn approx_eq(a: f64, b: f64, eps: f64) -> bool {
2724 (a - b).abs() <= eps
2725 }
2726
2727 #[test]
2728 fn test_stat_moments_f64_dense() {
2729 let data = vec64![1.0, 2.0, 3.0, 4.0];
2730 let (sum, sum2, count) = stat_moments_f64(&data, None, None);
2731 assert_eq!(sum, 10.0);
2732 assert_eq!(sum2, 30.0);
2733 assert_eq!(count, 4);
2734 }
2735
2736 #[test]
2737 fn test_stat_moments_f64_masked() {
2738 let data = vec64![1.0, 2.0, 3.0, 4.0];
2739 let mask = mask_from_bools(&[true, false, true, false]);
2740 let (sum, sum2, count) = stat_moments_f64(&data, Some(&mask), Some(2));
2741 assert_eq!(sum, 4.0);
2742 assert_eq!(sum2, 10.0);
2743 assert_eq!(count, 2);
2744 }
2745
2746 #[test]
2747 fn test_stat_moments_i32_dense() {
2748 let data = vec64![1i32, 2, 3, 4];
2749 let (sum, sum2, count) = stat_moments_i32(&data, None, None);
2750 assert_eq!(sum, 10.0);
2751 assert_eq!(sum2, 30.0);
2752 assert_eq!(count, 4);
2753 }
2754
2755 #[test]
2756 fn test_stat_moments_i32_masked() {
2757 let data = vec64![1i32, 2, 3, 4];
2758 let mask = mask_from_bools(&[true, false, false, true]);
2759 let (sum, sum2, count) = stat_moments_i32(&data, Some(&mask), Some(2));
2760 assert_eq!(sum, 5.0);
2761 assert_eq!(sum2, 17.0);
2762 assert_eq!(count, 2);
2763 }
2764
2765 #[test]
2766 fn test_min_max_range_f64_dense() {
2767 let data = vec64![5.0, 3.0, 1.0, 7.0];
2768 assert_eq!(min_f64(&data, None, None), Some(1.0));
2769 assert_eq!(max_f64(&data, None, None), Some(7.0));
2770 assert_eq!(range_f64(&data, None, None), Some((1.0, 7.0)));
2771 }
2772
2773 #[test]
2774 fn test_min_max_range_f64_masked() {
2775 let data = vec64![5.0, 3.0, 1.0, 7.0];
2776 let mask = mask_from_bools(&[false, true, true, false]);
2777 assert_eq!(min_f64(&data, Some(&mask), Some(2)), Some(1.0));
2778 assert_eq!(max_f64(&data, Some(&mask), Some(2)), Some(3.0));
2779 assert_eq!(range_f64(&data, Some(&mask), Some(2)), Some((1.0, 3.0)));
2780 }
2781
2782 #[test]
2783 fn test_sum_and_avg_i32() {
2784 let data = vec64![1i32, 2, 3, 4];
2785 assert_eq!(sum_i32(&data, None, None), Some(10));
2786 assert_eq!(average_i32(&data, None, None), Some(2.5));
2787 let mask = mask_from_bools(&[true, false, true, true]);
2788 assert_eq!(sum_i32(&data, Some(&mask), Some(1)), Some(8));
2789 assert_eq!(average_i32(&data, Some(&mask), Some(1)), Some(8.0 / 3.0));
2790 }
2791
2792 #[test]
2793 fn test_sum_and_avg_f32() {
2794 let data = vec64![1.0f32, 2.0, 3.0, 4.0];
2795 assert_eq!(sum_f32(&data, None, None), Some(10.0));
2796 assert_eq!(average_f32(&data, None, None), Some(2.5));
2797 let mask = mask_from_bools(&[false, true, true, false]);
2798 assert_eq!(sum_f32(&data, Some(&mask), Some(2)), Some(5.0));
2799 assert_eq!(average_f32(&data, Some(&mask), Some(2)), Some(2.5));
2800 }
2801
2802 #[test]
2803 fn test_variance_f64() {
2804 let data = vec64![1.0, 2.0, 3.0, 4.0];
2805 let v = variance_f64(&data, None, None, false);
2806 assert!(approx_eq(v.unwrap(), 1.25, 1e-9));
2807 let mask = mask_from_bools(&[true, true, false, false]);
2808 let v2 = variance_f64(&data, Some(&mask), Some(2), false);
2809 assert!(approx_eq(v2.unwrap(), 0.25, 1e-9));
2810 }
2811
2812 #[test]
2813 fn test_count_generic_raw() {
2814 let n = 100;
2815 let mask = mask_from_bools(&[true; 100]);
2816 assert_eq!(count_generic_raw(n, None, None), 100);
2817 assert_eq!(count_generic_raw(n, Some(&mask), Some(0)), 100);
2818
2819 let mask = mask_from_bools(&(0..n).map(|i| i % 2 == 0).collect::<Vec<_>>());
2820 assert_eq!(count_generic_raw(n, Some(&mask), Some(50)), 50);
2821 }
2822
2823 #[test]
2824 fn test_median_ord_sorted() {
2825 let data = vec64![1i32, 4, 2, 3];
2826 assert_eq!(median(&data, None, None, true), Some(2));
2827 }
2828
2829 #[test]
2830 fn test_median_ord_unsorted() {
2831 let data = vec64![1i32, 4, 2, 3];
2832 assert_eq!(median(&data, None, None, false), Some(2));
2834 }
2835
2836 #[test]
2837 fn test_median_ord_masked() {
2838 let data = vec64![1i32, 4, 2, 3];
2839 let mask = mask_from_bools(&[true, true, false, false]);
2840 assert_eq!(median(&data, Some(&mask), Some(2), true), Some(1));
2841 }
2842
2843 #[test]
2844 fn test_median_f_sorted() {
2845 let dataf = vec64![1.0f64, 2.0, 3.0, 4.0];
2846 assert_eq!(median_f(&dataf, None, None, true), Some(2.5));
2847 }
2848
2849 #[test]
2850 fn test_median_f_masked() {
2851 let dataf = vec64![1.0f64, 2.0, 3.0, 4.0];
2852 let mask = mask_from_bools(&[true, false, true, false]);
2853 assert_eq!(median_f(&dataf, Some(&mask), Some(2), true), Some(2.0));
2854 }
2855
2856 #[test]
2857 fn test_percentile_ord_and_f() {
2858 let data = vec64![10i32, 20, 30, 40, 50];
2859 assert_eq!(percentile_ord(&data, None, None, 0.0, true), Some(10));
2860 assert_eq!(percentile_ord(&data, None, None, 100.0, true), Some(50));
2861 assert_eq!(percentile_ord(&data, None, None, 50.0, true), Some(30));
2862 let mask = mask_from_bools(&[false, true, true, false, true]);
2863 assert_eq!(
2864 percentile_ord(&data, Some(&mask), Some(2), 50.0, true),
2865 Some(30)
2866 );
2867
2868 let dataf = vec64![1.0f64, 2.0, 3.0, 4.0, 5.0];
2869 assert_eq!(percentile_f(&dataf, None, None, 0.0, true), Some(1.0));
2870 assert_eq!(percentile_f(&dataf, None, None, 1.0, true), Some(5.0));
2871 assert_eq!(percentile_f(&dataf, None, None, 0.5, true), Some(3.0));
2872 }
2873
2874 #[test]
2875 fn test_quantile_ord_and_f() {
2876 let data = vec64![1i32, 2, 3, 4, 5];
2877 let out = quantile(&data, None, None, 4, true).unwrap();
2878 assert_eq!(out.len(), 3);
2879 assert_eq!(out, Vec64::from_slice(&[2, 3, 4]));
2880
2881 let mask = mask_from_bools(&[true, false, true, true, false]);
2882 let out2 = quantile(&data, Some(&mask), Some(2), 3, true).unwrap();
2883 assert_eq!(out2, Vec64::from_slice(&[1, 3]));
2884
2885 let dataf = vec64![10.0f64, 20.0, 30.0, 40.0, 50.0];
2886 let out = quantile_f(&dataf, None, None, 4, true).unwrap();
2887 assert_eq!(out.len(), 3);
2888 assert!(approx_eq(out[1], 30.0, 1e-9));
2889 }
2890
2891 #[test]
2892 fn test_iqr_ord() {
2893 let data = vec64![1i32, 2, 3, 4, 5];
2894 assert_eq!(iqr(&data, None, None, true), Some(2));
2895 }
2896
2897 #[test]
2898 fn test_iqr_f64() {
2899 let dataf = vec64![10.0f64, 20.0, 30.0, 40.0, 50.0];
2900 assert_eq!(iqr_f(&dataf, None, None, true), Some(20.0));
2902 }
2903 #[test]
2904 fn test_mode_ord_and_f() {
2905 let data = vec64![1i32, 2, 3, 2, 2, 4, 5, 1];
2906 assert_eq!(mode(&data, None, None), Some(2));
2907 let dataf = vec64![1.0f32, 2.0, 2.0, 3.0, 3.0, 3.0];
2908 assert_eq!(mode_f(&dataf, None, None), Some(3.0));
2909 let data = vec64![1i32, 2, 3, 3, 2, 1];
2910 let m = mode(&data, None, None);
2911 assert!(m == Some(1) || m == Some(2) || m == Some(3));
2912 }
2913
2914 #[test]
2915 fn test_count_distinct_and_f() {
2916 let data = vec64![1i32, 2, 2, 3, 3, 4];
2917 assert_eq!(count_distinct(&data, None, None), 4);
2918 let dataf = vec64![1.0f32, 2.0, 2.0, 3.0, 3.0, 4.0];
2919 assert_eq!(count_distinct_f(&dataf, None, None), 4);
2920 }
2921
2922 #[test]
2923 fn test_harmonic_and_geometric_mean() {
2924 let data = vec64![1.0f64, 2.0, 4.0];
2925 let hm = harmonic_mean_f(&data, None, None);
2926 assert!(approx_eq(hm, 1.714285714, 1e-6));
2927
2928 let gm = geometric_mean_f(&data, None, None);
2929 assert!(approx_eq(gm, 2.0, 1e-6));
2930
2931 let data = vec64![1i32, 2, 4];
2932 let hm = harmonic_mean_int(&data, None, None);
2933 assert!(approx_eq(hm, 1.714285714, 1e-6));
2934
2935 let gm = geometric_mean_int(&data, None, None);
2936 assert!(approx_eq(gm, 2.0, 1e-6));
2937 }
2938
2939 #[test]
2940 fn test_empty_arrays() {
2941 assert_eq!(stat_moments_f64(&[], None, None), (0.0, 0.0, 0));
2942 assert_eq!(stat_moments_i32(&[], None, None), (0.0, 0.0, 0));
2943 assert_eq!(sum_f64(&[], None, None), None);
2944 assert_eq!(average_i32(&[], None, None), None);
2945 assert_eq!(variance_f64(&[], None, None, false), None);
2946 assert_eq!(median::<i32>(&[], None, None, true), None);
2947 assert_eq!(median_f::<f64>(&[], None, None, true), None);
2948 assert_eq!(mode::<i32>(&[], None, None), None);
2949 assert_eq!(mode_f::<f32>(&[], None, None), None);
2950 assert_eq!(quantile::<i32>(&[], None, None, 3, true), None);
2951 assert_eq!(quantile_f::<f64>(&[], None, None, 3, true), None);
2952 assert_eq!(iqr::<i32>(&[], None, None, true), None);
2953 assert_eq!(iqr_f::<f64>(&[], None, None, true), None);
2954 assert_eq!(count_distinct::<i32>(&[], None, None), 0);
2955 assert_eq!(count_distinct_f::<f32>(&[], None, None), 0);
2956 }
2957
2958 #[test]
2960 fn test_stat_moments_all_types() {
2961 let i64_data = vec64![-8i64, 2, 8, -2, 3];
2962 let mask = mask_from_bools(&[true, false, true, true, true]);
2963 let (sum, sum2, count) = stat_moments_i64(&i64_data, Some(&mask), Some(1));
2964 assert_eq!(sum, (-8.0) + 8.0 + (-2.0) + 3.0);
2965 assert_eq!(sum2, 64.0 + 64.0 + 4.0 + 9.0);
2966 assert_eq!(count, 4);
2967
2968 let u64_data = vec64![1u64, 2, 3, 4, 5];
2969 let mask = mask_from_bools(&[false, true, true, true, false]);
2970 let (sum, sum2, count) = stat_moments_u64(&u64_data, Some(&mask), Some(2));
2971 assert_eq!(sum, 2.0 + 3.0 + 4.0);
2972 assert_eq!(sum2, 4.0 + 9.0 + 16.0);
2973 assert_eq!(count, 3);
2974
2975 let f32_data = vec64![0.5f32, 2.0, 4.5, -2.0, 3.0];
2976 let mask = mask_from_bools(&[true, true, false, false, true]);
2977 let (sum, sum2, count) = stat_moments_f32(&f32_data, Some(&mask), Some(2));
2978 assert!(approx_eq(sum, 0.5 + 2.0 + 3.0, 1e-6));
2979 assert!(approx_eq(sum2, 0.25 + 4.0 + 9.0, 1e-6));
2980 assert_eq!(count, 3);
2981
2982 let u32_data = vec64![100u32, 200, 300, 400, 500];
2983 let mask = mask_from_bools(&[true, false, false, true, true]);
2984 let (sum, sum2, count) = stat_moments_u32(&u32_data, Some(&mask), Some(2));
2985 assert_eq!(sum, 100.0 + 400.0 + 500.0);
2986 assert_eq!(sum2, 10000.0 + 160000.0 + 250000.0);
2987 assert_eq!(count, 3);
2988 }
2989
2990 #[test]
2991 fn test_reduce_min_max_for_all_types() {
2992 let i64_data = vec64![-8i64, 2, 8, -2, 3];
2993 assert_eq!(reduce_min_max_i64(&i64_data, None, None), Some((-8, 8)));
2994 let mask = mask_from_bools(&[true, false, false, false, true]);
2995 assert_eq!(
2996 reduce_min_max_i64(&i64_data, Some(&mask), Some(3)),
2997 Some((-8, 3))
2998 );
2999
3000 let u64_data = vec64![1u64, 2, 3, 4, 5];
3001 assert_eq!(reduce_min_max_u64(&u64_data, None, None), Some((1, 5)));
3002 let mask = mask_from_bools(&[false, false, true, true, false]);
3003 assert_eq!(
3004 reduce_min_max_u64(&u64_data, Some(&mask), Some(3)),
3005 Some((3, 4))
3006 );
3007
3008 let i32_data = vec64![9i32, -2, 5, -2];
3009 assert_eq!(reduce_min_max_i32(&i32_data, None, None), Some((-2, 9)));
3010 let mask = mask_from_bools(&[false, false, true, false]);
3011 assert_eq!(
3012 reduce_min_max_i32(&i32_data, Some(&mask), Some(3)),
3013 Some((5, 5))
3014 );
3015
3016 let u32_data = vec64![7u32, 2, 5, 11];
3017 assert_eq!(reduce_min_max_u32(&u32_data, None, None), Some((2, 11)));
3018 let mask = mask_from_bools(&[true, false, false, true]);
3019 assert_eq!(
3020 reduce_min_max_u32(&u32_data, Some(&mask), Some(2)),
3021 Some((7, 11))
3022 );
3023
3024 let f64_data = vec64![f64::NAN, 3.0, 1.0, 7.0];
3025 assert_eq!(reduce_min_max_f64(&f64_data, None, None), Some((1.0, 7.0))); }
3027
3028 #[test]
3029 fn test_empty_and_all_null_min_max() {
3030 let empty: [i32; 0] = [];
3031 assert_eq!(reduce_min_max_i32(&empty, None, None), None);
3032
3033 let all_nulls = vec64![5i32, 10, 15];
3034 let mask = mask_from_bools(&[false, false, false]);
3035 assert_eq!(reduce_min_max_i32(&all_nulls, Some(&mask), Some(3)), None);
3036 }
3037
3038 #[test]
3040 fn test_sum_avg_and_variance_all_types() {
3041 let data = vec64![100u32, 200, 0, 400];
3043 assert_eq!(sum_u32(&data, None, None), Some(700));
3044 assert_eq!(average_u32(&data, None, None), Some(175.0));
3045 let mask = mask_from_bools(&[false, true, false, true]);
3046 assert_eq!(sum_u32(&data, Some(&mask), Some(2)), Some(600));
3047 assert_eq!(average_u32(&data, Some(&mask), Some(2)), Some(300.0));
3048 assert_eq!(
3049 variance_u32(&data, None, None, false).unwrap(),
3050 ((100f64.powi(2) + 200f64.powi(2) + 400f64.powi(2)) / 4.0 - (700.0 / 4.0).powi(2))
3051 );
3052
3053 let data = vec64![5i64, -10, 25, 5];
3055 assert_eq!(sum_i64(&data, None, None), Some(25));
3056 assert_eq!(average_i64(&data, None, None), Some(6.25));
3057 let mask = mask_from_bools(&[true, false, false, false]);
3058 assert_eq!(sum_i64(&data, Some(&mask), Some(3)), Some(5));
3059 assert_eq!(average_i64(&data, Some(&mask), Some(3)), Some(5.0));
3060 assert!(variance_i64(&data, None, None, false).unwrap() > 0.0);
3061
3062 let data = vec64![10u64, 0, 10, 10];
3064 assert_eq!(sum_u64(&data, None, None), Some(30));
3065 assert_eq!(average_u64(&data, None, None), Some(7.5));
3066 let mask = mask_from_bools(&[false, false, false, false]);
3067 assert_eq!(sum_u64(&data, Some(&mask), Some(4)), None);
3068 assert_eq!(average_u64(&data, Some(&mask), Some(4)), None);
3069
3070 let data = vec64![2.0f32, 4.0, 6.0];
3072 assert_eq!(sum_f32(&data, None, None), Some(12.0));
3073 assert_eq!(average_f32(&data, None, None), Some(4.0));
3074 let mask = mask_from_bools(&[true, false, true]);
3075 assert_eq!(sum_f32(&data, Some(&mask), Some(1)), Some(8.0));
3076 assert_eq!(average_f32(&data, Some(&mask), Some(1)), Some(4.0));
3077 assert!(variance_f32(&data, None, None, false).unwrap() > 0.0);
3078
3079 let data = vec64![1.0f64, f64::INFINITY, f64::NAN];
3081 assert!(
3082 sum_f64(&data, None, None).unwrap().is_nan()
3083 || sum_f64(&data, None, None).unwrap().is_infinite()
3084 );
3085 }
3086
3087 #[test]
3089 fn test_min_max_range_i32_singleton() {
3090 let d = vec64![1i32];
3091 assert_eq!(min_i32(&d, None, None), Some(1));
3092 assert_eq!(max_i32(&d, None, None), Some(1));
3093 assert_eq!(range_i32(&d, None, None), Some((1, 1)));
3094 }
3095
3096 #[test]
3097 fn test_min_f64_nan() {
3098 let d = vec64![5.0f64, f64::NAN];
3099 assert_eq!(min_f64(&d, None, None), Some(5.0));
3101 }
3102
3103 #[test]
3104 fn test_max_f64_nan() {
3105 let d = vec64![5.0f64, f64::NAN];
3106 assert_eq!(max_f64(&d, None, None), Some(5.0));
3108 }
3109
3110 #[test]
3111 fn test_min_f32_neg_inf() {
3112 let d = vec64![f32::NEG_INFINITY, 0.0f32, 5.0f32];
3113 assert_eq!(min_f32(&d, None, None), Some(f32::NEG_INFINITY));
3114 }
3115
3116 #[test]
3117 fn test_max_f32_regular() {
3118 let d = vec64![f32::NEG_INFINITY, 0.0f32, 5.0f32];
3119 assert_eq!(max_f32(&d, None, None), Some(5.0));
3120 }
3121
3122 #[test]
3123 fn test_median_ord_even() {
3124 let d = vec64![2i64, 4, 6, 8];
3125 assert_eq!(median(&d, None, None, true), Some(4));
3129 }
3130
3131 #[test]
3132 fn test_median_ord_odd() {
3133 let d = vec64![7i32, 3, 5];
3134 assert_eq!(median(&d, None, None, true), Some(5));
3135 }
3136
3137 #[test]
3138 fn test_median_ord_with_mask() {
3139 let d = vec64![7i32, 3, 5];
3140 let mask = mask_from_bools(&[false, true, false]);
3141 assert_eq!(median(&d, Some(&mask), Some(2), true), Some(3));
3142 }
3143
3144 #[test]
3145 fn test_quantile_ord() {
3146 let d = vec64![10u32, 20, 30, 40, 50, 60];
3147 let q = quantile(&d, None, None, 3, true).unwrap();
3148 assert_eq!(q.len(), 2);
3149 assert!(q[0] >= 20 && q[1] >= 40);
3150 }
3151
3152 #[test]
3153 fn test_median_f64() {
3154 let df = vec64![1.0f64, 5.0, 3.0, 2.0];
3155 assert!(approx_eq(
3156 median_f(&df, None, None, true).unwrap(),
3157 2.5,
3158 1e-9
3159 ));
3160 }
3161
3162 #[test]
3163 fn test_quantile_f64() {
3164 let df = vec64![1.0f64, 5.0, 3.0, 2.0];
3165 let qf = quantile_f(&df, None, None, 3, true).unwrap();
3166 assert_eq!(qf.len(), 2);
3168 assert!(qf.iter().all(|&v| v >= 1.0 && v <= 5.0));
3169 }
3170
3171 #[test]
3172 fn test_iqr_i32() {
3173 let d = vec64![1i32, 3, 2, 8, 4, 5];
3174 assert!(iqr(&d, None, None, true).unwrap() >= 2);
3175 }
3176
3177 #[test]
3178 fn test_iqr_f32() {
3179 let d = vec64![10.0f32, 100.0, 30.0, 50.0];
3180 assert!(iqr_f(&d, None, None, true).unwrap() > 0.0);
3181 }
3182
3183 #[test]
3185 fn test_mode_and_count_distinct_exhaustive() {
3186 let d = vec64![7u64, 5, 3, 1];
3188 assert!(d.contains(&mode(&d, None, None).unwrap()));
3189
3190 let d = vec64![2i64; 6];
3192 assert_eq!(mode(&d, None, None), Some(2));
3193
3194 let d = vec64![3i32, 5, 3, 5];
3196 let m = mode(&d, None, None);
3197 assert!(m == Some(3) || m == Some(5));
3198
3199 let d = vec64![f32::NAN, f32::INFINITY, 5.0, 5.0, f32::NAN];
3201 let m = mode_f(&d, None, None);
3202 assert!(m == Some(5.0) || m.unwrap().is_nan() || m.unwrap().is_infinite());
3203
3204 let d = vec64![1u64, 1, 1, 1];
3206 assert_eq!(count_distinct(&d, None, None), 1);
3207
3208 let d = vec64![1i32, 2, 3, 4, 5];
3209 assert_eq!(count_distinct(&d, None, None), 5);
3210
3211 let mask = mask_from_bools(&[true, false, false, true, true]);
3212 assert_eq!(count_distinct(&d, Some(&mask), Some(2)), 3);
3213
3214 let d = vec64![f64::NAN, f64::INFINITY, 0.0];
3215 assert_eq!(count_distinct_f(&d, None, None), 3);
3216 }
3217
3218 #[test]
3220 fn test_harmonic_mean_f64() {
3221 let data = vec64![0.5f64, 2.0, 1.0];
3222 let hm = harmonic_mean_f(&data, None, None);
3224 assert!(approx_eq(hm, 0.857142857, 1e-2));
3225 }
3226
3227 #[test]
3228 fn test_geometric_mean_f64() {
3229 let data = vec64![0.5f64, 2.0, 1.0];
3230 let gm = geometric_mean_f(&data, None, None);
3231 assert!(approx_eq(gm, 1.0, 1e-9));
3232 }
3233
3234 #[test]
3235 #[should_panic(expected = "harmonic_mean_int: non-positive values are invalid")]
3236 fn test_harmonic_mean_int_all_zeros() {
3237 let d = vec64![0i32, 0, 0];
3238 let _ = harmonic_mean_int(&d, None, None);
3240 }
3241
3242 #[test]
3243 fn test_geometric_mean_uint() {
3244 let data = vec64![2u64, 8, 4];
3245 let gm = geometric_mean_uint(&data, None, None);
3246 assert!(approx_eq(gm, 4.0, 1e-9));
3247 }
3248
3249 #[test]
3250 #[should_panic(expected = "geometric_mean_int: non-positive values are invalid")]
3251 fn test_geometric_mean_int_negative() {
3252 let d = vec64![-2i64, -4, -8];
3254 let _ = geometric_mean_int(&d, None, None);
3255 }
3256
3257 #[test]
3259 fn test_percentile_ord_and_f_all_cases() {
3260 let d = vec64![1i32, 4, 6, 8, 10];
3261 assert_eq!(percentile_ord(&d, None, None, 0.0, true), Some(1));
3262 assert_eq!(percentile_ord(&d, None, None, 100.0, true), Some(10));
3263 let mask = mask_from_bools(&[false, false, false, false, false]);
3264 assert_eq!(percentile_ord(&d, Some(&mask), Some(5), 50.0, true), None);
3265
3266 let df = vec64![1.0f64, 2.0, 3.0, 4.0, 5.0];
3267 assert_eq!(percentile_f(&df, None, None, 0.0, true), Some(1.0));
3268 assert_eq!(percentile_f(&df, None, None, 1.0, true), Some(5.0));
3269 let mask = mask_from_bools(&[false, false, false, false, false]);
3270 assert_eq!(percentile_f(&df, Some(&mask), Some(5), 0.5, true), None);
3271
3272 assert_eq!(percentile_f(&df, None, None, 10.0, true), Some(5.0));
3274 assert_eq!(percentile_f(&df, None, None, -5.0, true), Some(1.0));
3275 }
3276
3277 #[test]
3279 fn test_quantile_f_and_ord_special_cases() {
3280 let d = vec64![2.0f32, 4.0, 6.0, 8.0];
3281 assert_eq!(quantile_f(&d, None, None, 1, true), None);
3283 let mask = mask_from_bools(&[false, false, false, false]);
3285 assert_eq!(quantile_f(&d, Some(&mask), Some(4), 2, true), None);
3286
3287 let mask = mask_from_bools(&[false, false, true, false]);
3289 let out = quantile_f(&d, Some(&mask), Some(3), 2, true).unwrap();
3290 assert_eq!(out.len(), 1);
3291 assert!(out.iter().all(|&x| x == 6.0));
3292 }
3293
3294 #[test]
3296 fn test_all_apis_singleton_and_nulls() {
3297 let d = vec64![7u32];
3298 assert_eq!(sum_u32(&d, None, None), Some(7));
3299 assert_eq!(average_u32(&d, None, None), Some(7.0));
3300 assert_eq!(variance_u32(&d, None, None, false).unwrap(), 0.0);
3301 assert_eq!(mode(&d, None, None), Some(7));
3302 assert_eq!(median(&d, None, None, true), Some(7));
3303 assert_eq!(
3304 quantile(&d, None, None, 3, true).unwrap(),
3305 Vec64::from_slice(&[7, 7])
3306 );
3307 assert_eq!(iqr(&d, None, None, true), Some(0));
3308
3309 let mask = mask_from_bools(&[false]);
3310 assert_eq!(sum_u32(&d, Some(&mask), Some(1)), None);
3311 assert_eq!(median(&d, Some(&mask), Some(1), true), None);
3312 assert_eq!(quantile(&d, Some(&mask), Some(1), 3, true), None);
3313 assert_eq!(mode(&d, Some(&mask), Some(1)), None);
3314 assert_eq!(iqr(&d, Some(&mask), Some(1), true), None);
3315 }
3316
3317 #[test]
3318 fn test_skewness_kurtosis_dense() {
3319 let d_f64 = vec64![1.0f64, 2.0, 3.0, 4.0, 5.0];
3323 assert!(approx_eq(
3325 skewness_f64(&d_f64, None, None, false).unwrap(),
3326 0.0,
3327 1e-12
3328 ));
3329 assert!(approx_eq(
3330 kurtosis_f64(&d_f64, None, None, false).unwrap(),
3331 -1.3,
3332 1e-12
3333 ));
3334 assert!(approx_eq(
3336 skewness_f64(&d_f64, None, None, true).unwrap(),
3337 0.0,
3338 1e-12
3339 ));
3340 assert!(approx_eq(
3341 kurtosis_f64(&d_f64, None, None, true).unwrap(),
3342 -1.2,
3343 1e-12
3344 ));
3345
3346 let d_f32 = vec64![1.0f32, 2.0, 3.0, 4.0, 5.0];
3347 assert!(approx_eq(
3349 skewness_f32(&d_f32, None, None, false).unwrap(),
3350 0.0,
3351 1e-6
3352 ));
3353 assert!(approx_eq(
3354 kurtosis_f32(&d_f32, None, None, false).unwrap(),
3355 -1.3,
3356 1e-6
3357 ));
3358 assert!(approx_eq(
3360 skewness_f32(&d_f32, None, None, true).unwrap(),
3361 0.0,
3362 1e-6
3363 ));
3364 assert!(approx_eq(
3365 kurtosis_f32(&d_f32, None, None, true).unwrap(),
3366 -1.2,
3367 1e-6
3368 ));
3369
3370 let d_i32 = vec64![1i32, 2, 3, 4, 5];
3371 assert!(approx_eq(
3372 skewness_i32(&d_i32, None, None, false).unwrap(),
3373 0.0,
3374 1e-12
3375 ));
3376 assert!(approx_eq(
3377 kurtosis_i32(&d_i32, None, None, false).unwrap(),
3378 -1.3,
3379 1e-12
3380 ));
3381 assert!(approx_eq(
3382 skewness_i32(&d_i32, None, None, true).unwrap(),
3383 0.0,
3384 1e-12
3385 ));
3386 assert!(approx_eq(
3387 kurtosis_i32(&d_i32, None, None, true).unwrap(),
3388 -1.2,
3389 1e-12
3390 ));
3391 }
3392
3393 #[test]
3394 fn test_skewness_kurtosis_masked() {
3395 let data = vec64![1i32, 2, 3, 4, 5];
3398 let mask = mask_from_bools(&[false, true, true, true, false]);
3399 assert!(approx_eq(
3401 skewness_i32(&data, Some(&mask), Some(2), false).unwrap(),
3402 0.0,
3403 1e-12
3404 ));
3405 assert!(approx_eq(
3406 kurtosis_i32(&data, Some(&mask), Some(2), false).unwrap(),
3407 -1.5,
3408 1e-12
3409 ));
3410 assert!(approx_eq(
3412 skewness_i32(&data, Some(&mask), Some(2), true).unwrap(),
3413 0.0,
3414 1e-12
3415 ));
3416 assert!(approx_eq(
3417 kurtosis_i32(&data, Some(&mask), Some(2), true).unwrap(),
3418 -1.5,
3419 1e-12
3420 ));
3421 }
3422
3423 #[test]
3424 fn test_skewness_kurtosis_constant_none() {
3425 let d = vec64![7u32; 5];
3427 assert_eq!(skewness_u32(&d, None, None, false), None);
3428 assert_eq!(kurtosis_u32(&d, None, None, false), None);
3429 assert_eq!(skewness_u32(&d, None, None, true), None);
3430 assert_eq!(kurtosis_u32(&d, None, None, true), None);
3431 }
3432
3433 #[test]
3434 fn test_kurtosis_skewness_large_f32() {
3435 let data = vec64![-2.0, -1.0, -1.0, 0.0, 0.0, 1.0, 2.0, 2.0, 2.0, 3.0, 3.0];
3439 assert!(approx_eq(
3441 skewness_f32(&data, None, None, false).unwrap(),
3442 -0.20081265422848577_f64,
3443 1e-12
3444 ));
3445 assert!(approx_eq(
3446 kurtosis_f32(&data, None, None, false).unwrap(),
3447 -1.2841469381610144_f64,
3448 1e-12
3449 ));
3450 assert!(approx_eq(
3452 skewness_f32(&data, None, None, true).unwrap(),
3453 -0.23401565397707677_f64,
3454 1e-12
3455 ));
3456 assert!(approx_eq(
3457 kurtosis_f32(&data, None, None, true).unwrap(),
3458 -1.3069115636016906_f64,
3459 1e-12
3460 ));
3461 }
3462
3463 fn bitmask_from_bools(bs: &[bool]) -> Bitmask {
3464 let mut mask = Bitmask::with_capacity(bs.len());
3465 for (i, &b) in bs.iter().enumerate() {
3466 mask.set(i, b);
3467 }
3468 mask
3469 }
3470
3471 #[test]
3472 fn test_kurtosis_skewness_large_masked_i32() {
3473 let data = vec64![1, 2, 3, 4, 5, 6, 7, 100, 1000, 10000];
3477 let mask = mask_from_bools(&[true, true, true, false, true, true, true, true, true, true]);
3478 assert!(approx_eq(
3480 skewness_i32(&data, Some(&mask), None, false).unwrap(),
3481 2.4306799843134046_f64,
3482 1e-12
3483 ));
3484 assert!(approx_eq(
3485 kurtosis_i32(&data, Some(&mask), None, false).unwrap(),
3486 3.991203437112363_f64,
3487 1e-12
3488 ));
3489 assert!(approx_eq(
3491 skewness_i32(&data, Some(&mask), None, true).unwrap(),
3492 2.946429085375575_f64,
3493 1e-12
3494 ));
3495 assert!(approx_eq(
3496 kurtosis_i32(&data, Some(&mask), None, true).unwrap(),
3497 8.745149404023547_f64,
3498 1e-12
3499 ));
3500 }
3501
3502 #[test]
3503 fn test_kurtosis_skewness_no_valids() {
3504 let data: [f64; 3] = [0.0, 0.0, 0.0];
3505 let mask = bitmask_from_bools(&[false, false, false]);
3506 assert_eq!(kurtosis_f64(&data, Some(&mask), None, false), None);
3507 assert_eq!(skewness_f64(&data, Some(&mask), None, false), None);
3508 assert_eq!(kurtosis_f64(&data, Some(&mask), None, true), None);
3509 assert_eq!(skewness_f64(&data, Some(&mask), None, true), None);
3510 }
3511
3512 #[test]
3513 fn test_kurtosis_skewness_single_value() {
3514 let data = vec64![1u32];
3515 assert_eq!(kurtosis_u32(&data, None, None, false), None);
3516 assert_eq!(skewness_u32(&data, None, None, false), None);
3517 assert_eq!(kurtosis_u32(&data, None, None, true), None);
3518 assert_eq!(skewness_u32(&data, None, None, true), None);
3519 }
3520
3521 #[test]
3522 fn test_kurtosis_skewness_all_equal() {
3523 let data = vec64![7.0, 7.0, 7.0, 7.0];
3524 assert_eq!(kurtosis_f64(&data, None, None, false), None);
3525 assert_eq!(skewness_f64(&data, None, None, false), None);
3526 assert_eq!(kurtosis_f64(&data, None, None, true), None);
3527 assert_eq!(skewness_f64(&data, None, None, true), None);
3528 }
3529
3530 #[test]
3531 fn test_kurtosis_skewness_mixed_nulls() {
3532 let data = vec64![2.0, 5.0, 7.0, 3.0, 10.0];
3536 let mask = mask_from_bools(&[true, false, true, false, true]);
3537 assert!(approx_eq(
3539 skewness_f64(&data, Some(&mask), None, false).unwrap(),
3540 -0.29479962014482897_f64,
3541 1e-12
3542 ));
3543 assert!(approx_eq(
3544 kurtosis_f64(&data, Some(&mask), None, false).unwrap(),
3545 -1.5_f64,
3546 1e-12
3547 ));
3548 assert!(approx_eq(
3550 skewness_f64(&data, Some(&mask), None, true).unwrap(),
3551 -0.7221086457211346_f64,
3552 1e-12
3553 ));
3554 assert!(approx_eq(
3555 kurtosis_f64(&data, Some(&mask), None, true).unwrap(),
3556 -1.5_f64,
3557 1e-12
3558 ));
3559 }
3560
3561 #[test]
3562 fn test_kurtosis_skewness_integers_large() {
3563 let data = vec64![1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9];
3564 assert!(kurtosis_i32(&data, None, None, false).unwrap().abs() < 1.5);
3565 assert!(skewness_i32(&data, None, None, false).unwrap().abs() < 1e-6);
3566 assert!(kurtosis_i32(&data, None, None, true).unwrap().abs() < 2.0);
3568 assert!(skewness_i32(&data, None, None, true).unwrap().abs() < 1e-6);
3569 }
3570
3571 #[test]
3572 fn test_agg_product_dense() {
3573 let data = vec64![2, 3, 4, 5];
3575 let prod = agg_product(&data, None, 0, data.len());
3576 assert_eq!(prod, 120);
3577
3578 let prod = agg_product(&data, None, 1, 2);
3580 assert_eq!(prod, 12); }
3582
3583 #[test]
3584 fn test_agg_product_with_mask_all_valid() {
3585 let data = vec64![2, 3, 4, 5];
3586 let mask = Bitmask::new_set_all(4, true);
3587 let prod = agg_product(&data, Some(&mask), 0, 4);
3588 assert_eq!(prod, 120);
3589 }
3590
3591 #[test]
3592 fn test_agg_product_with_mask_some_nulls() {
3593 let data = vec64![2, 3, 4, 5];
3594 let mut mask = Bitmask::new_set_all(4, false);
3595 mask.set(0, true);
3596 mask.set(2, true);
3597 let prod = agg_product(&data, Some(&mask), 0, 4);
3599 assert_eq!(prod, 8);
3600 }
3601
3602 #[test]
3603 fn test_agg_product_zero_short_circuit() {
3604 let data = vec64![2, 0, 4, 5];
3605 let prod = agg_product(&data, None, 0, 4);
3606 assert_eq!(prod, 0);
3607 }
3608
3609 #[test]
3610 fn test_agg_product_empty() {
3611 let data: [i32; 0] = [];
3612 let prod = agg_product(&data, None, 0, 0);
3613 assert_eq!(prod, 1); }
3615
3616 #[test]
3617 fn test_agg_product_mask_all_null() {
3618 let data = vec64![2, 3, 4, 5];
3619 let mask = Bitmask::new_set_all(4, false);
3620 let prod = agg_product(&data, Some(&mask), 0, 4);
3621 assert_eq!(prod, 1); }
3623
3624 #[test]
3625 fn test_agg_product_offset_and_mask() {
3626 let data = vec64![2, 3, 4, 5, 6];
3627 let mut mask = Bitmask::new_set_all(5, false);
3628 mask.set(1, true);
3629 mask.set(3, true);
3630 let prod = agg_product(&data, Some(&mask), 1, 3);
3631 assert_eq!(prod, 15);
3632 }
3633
3634 #[test]
3635 fn test_sum_squares_empty() {
3636 let v: [f64; 0] = [];
3637 assert_eq!(sum_squares(&v, None, None), 0.0);
3638 }
3639
3640 #[test]
3641 fn test_sum_squares_basic() {
3642 let v = vec64![1.0, 2.0, 3.0];
3643 assert_eq!(sum_squares(&v, None, None), 1.0 + 4.0 + 9.0);
3644 }
3645
3646 #[test]
3647 fn test_sum_squares_neg() {
3648 let v = vec64![-1.0, -2.0, -3.0];
3649 assert_eq!(sum_squares(&v, None, None), 1.0 + 4.0 + 9.0);
3650 }
3651
3652 #[test]
3653 fn test_sum_squares_large() {
3654 let v: Vec<f64> = (0..1000).map(|x| x as f64).collect();
3655 let expect: f64 = v.iter().map(|x| x * x).sum();
3656 assert!((sum_squares(&v, None, None) - expect).abs() < 1e-9);
3657 }
3658
3659 #[test]
3660 fn test_sum_squares_masked() {
3661 let v = vec64![1.0, 2.0, 3.0, 4.0];
3662 let mask = Bitmask::from_bools(&[true, false, true, false]);
3663 assert_eq!(sum_squares(&v, Some(&mask), Some(2)), 1.0 + 9.0);
3664 }
3665
3666 #[test]
3667 fn test_sum_cubes_empty() {
3668 let v: [f64; 0] = [];
3669 assert_eq!(sum_cubes(&v, None, None), 0.0);
3670 }
3671
3672 #[test]
3673 fn test_sum_cubes_basic() {
3674 let v = vec64![1.0, 2.0, 3.0];
3675 assert_eq!(sum_cubes(&v, None, None), 1.0 + 8.0 + 27.0);
3676 }
3677
3678 #[test]
3679 fn test_sum_cubes_neg() {
3680 let v = vec64![-1.0, -2.0, -3.0];
3681 assert_eq!(sum_cubes(&v, None, None), -1.0 - 8.0 - 27.0);
3682 }
3683
3684 #[test]
3685 fn test_sum_cubes_large() {
3686 let v: Vec<f64> = (0..1000).map(|x| x as f64).collect();
3687 let expect: f64 = v.iter().map(|x| x * x * x).sum();
3688 assert!((sum_cubes(&v, None, None) - expect).abs() < 1e-6);
3689 }
3690
3691 #[test]
3692 fn test_sum_cubes_masked() {
3693 let v = vec64![1.0, 2.0, 3.0, 4.0];
3694 let mask = Bitmask::from_bools(&[true, true, false, true]);
3695 assert_eq!(sum_cubes(&v, Some(&mask), Some(1)), 1.0 + 8.0 + 64.0);
3696 }
3697
3698 #[test]
3699 fn test_sum_quartics_empty() {
3700 let v: [f64; 0] = [];
3701 assert_eq!(sum_quartics(&v, None, None), 0.0);
3702 }
3703
3704 #[test]
3705 fn test_sum_quartics_basic() {
3706 let v = vec64![1.0, 2.0, 3.0];
3707 assert_eq!(sum_quartics(&v, None, None), 1.0 + 16.0 + 81.0);
3708 }
3709
3710 #[test]
3711 fn test_sum_quartics_neg() {
3712 let v = vec64![-1.0, -2.0, -3.0];
3713 assert_eq!(sum_quartics(&v, None, None), 1.0 + 16.0 + 81.0);
3714 }
3715
3716 #[test]
3717 fn test_sum_quartics_large() {
3718 let v: Vec<f64> = (0..1000).map(|x| x as f64).collect();
3719 let expect: f64 = v
3720 .iter()
3721 .map(|x| {
3722 let x2 = x * x;
3723 x2 * x2
3724 })
3725 .sum();
3726 assert!((sum_quartics(&v, None, None) - expect).abs() < 1e-3);
3727 }
3728
3729 #[test]
3730 fn test_sum_quartics_masked() {
3731 let v = vec64![1.0, 2.0, 3.0, 4.0];
3732 let mask = Bitmask::from_bools(&[false, true, false, true]);
3733 assert_eq!(sum_quartics(&v, Some(&mask), Some(2)), 16.0 + 256.0);
3734 }
3735
3736 #[test]
3737 fn test_sum_powers_singleton() {
3738 let v = vec64![4.2];
3739 assert!((sum_squares(&v, None, None) - 4.2 * 4.2).abs() < 1e-12);
3740 assert!((sum_cubes(&v, None, None) - 4.2 * 4.2 * 4.2).abs() < 1e-12);
3741 assert!((sum_quartics(&v, None, None) - (4.2 * 4.2 * 4.2 * 4.2)).abs() < 1e-12);
3742 }
3743
3744 #[test]
3745 fn test_sum_powers_f64_extremes() {
3746 let v = vec64![f64::MAX, -f64::MAX, 0.0, f64::MIN_POSITIVE];
3747 let ss = sum_squares(&v, None, None).is_infinite();
3748 let sc = sum_cubes(&v, None, None).is_infinite();
3749 let sq = sum_quartics(&v, None, None).is_infinite();
3750 println!("{:?}", ss);
3751 println!("{:?}", sc);
3752 println!("{:?}", sq);
3753 assert!(sum_squares(&v, None, None).is_infinite());
3755 assert!(sum_cubes(&v, None, None).is_infinite());
3756 assert!(sum_quartics(&v, None, None).is_infinite());
3757 }
3758}