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