1use num_traits::ToPrimitive;
2use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator};
3use rayon::prelude::ParallelSlice;
4use rayon::slice::ParallelSliceMut;
5
6use serde::{Deserialize, Serialize};
7
8use {crate::Commute, crate::Partial};
9
10mod fp {
26 #[inline(always)]
27 pub fn add(a: f64, b: f64) -> f64 {
28 a.algebraic_add(b)
29 }
30
31 #[inline(always)]
32 pub fn sub(a: f64, b: f64) -> f64 {
33 a.algebraic_sub(b)
34 }
35
36 #[inline(always)]
37 pub fn mul(a: f64, b: f64) -> f64 {
38 a.algebraic_mul(b)
39 }
40
41 #[inline(always)]
43 pub fn mul_add(a: f64, b: f64, c: f64) -> f64 {
44 a.algebraic_mul(b).algebraic_add(c)
45 }
46}
47
48const PARALLEL_THRESHOLD: usize = 10_000;
52
53const REDUCTION_PARALLEL_THRESHOLD: usize = 1_000_000;
61
62#[inline]
66pub fn median<I>(it: I) -> Option<f64>
67where
68 I: Iterator,
69 <I as Iterator>::Item: PartialOrd + ToPrimitive + Send,
70{
71 it.collect::<Unsorted<_>>().median()
72}
73
74#[inline]
76pub fn mad<I>(it: I, precalc_median: Option<f64>) -> Option<f64>
77where
78 I: Iterator,
79 <I as Iterator>::Item: PartialOrd + ToPrimitive + Send + Sync,
80{
81 it.collect::<Unsorted<_>>().mad(precalc_median)
82}
83
84#[inline]
88pub fn quartiles<I>(it: I) -> Option<(f64, f64, f64)>
89where
90 I: Iterator,
91 <I as Iterator>::Item: PartialOrd + ToPrimitive + Send,
92{
93 it.collect::<Unsorted<_>>().quartiles()
94}
95
96#[inline]
102pub fn mode<T, I>(it: I) -> Option<T>
103where
104 T: PartialOrd + Clone + Send,
105 I: Iterator<Item = T>,
106{
107 it.collect::<Unsorted<T>>().mode()
108}
109
110#[inline]
128pub fn modes<T, I>(it: I) -> (Vec<T>, usize, u32)
129where
130 T: PartialOrd + Clone + Send,
131 I: Iterator<Item = T>,
132{
133 it.collect::<Unsorted<T>>().modes()
134}
135
136#[inline]
159pub fn antimodes<T, I>(it: I) -> (Vec<T>, usize, u32)
160where
161 T: PartialOrd + Clone + Send,
162 I: Iterator<Item = T>,
163{
164 let (antimodes_result, antimodes_count, antimodes_occurrences) =
165 it.collect::<Unsorted<T>>().antimodes();
166 (antimodes_result, antimodes_count, antimodes_occurrences)
167}
168
169#[inline]
176pub fn gini<I>(it: I, precalc_sum: Option<f64>) -> Option<f64>
177where
178 I: Iterator,
179 <I as Iterator>::Item: PartialOrd + ToPrimitive + Send + Sync,
180{
181 it.collect::<Unsorted<_>>().gini(precalc_sum)
182}
183
184#[inline]
192pub fn kurtosis<I>(it: I, precalc_mean: Option<f64>, precalc_variance: Option<f64>) -> Option<f64>
193where
194 I: Iterator,
195 <I as Iterator>::Item: PartialOrd + ToPrimitive + Send + Sync,
196{
197 it.collect::<Unsorted<_>>()
198 .kurtosis(precalc_mean, precalc_variance)
199}
200
201#[inline]
208pub fn percentile_rank<I, V>(it: I, value: V) -> Option<f64>
209where
210 I: Iterator,
211 <I as Iterator>::Item: PartialOrd + ToPrimitive + Send + Sync,
212 V: PartialOrd + ToPrimitive,
213{
214 it.collect::<Unsorted<_>>().percentile_rank(value)
215}
216
217#[inline]
225pub fn atkinson<I>(
226 it: I,
227 epsilon: f64,
228 precalc_mean: Option<f64>,
229 precalc_geometric_sum: Option<f64>,
230) -> Option<f64>
231where
232 I: Iterator,
233 <I as Iterator>::Item: PartialOrd + ToPrimitive + Send + Sync,
234{
235 it.collect::<Unsorted<_>>()
236 .atkinson(epsilon, precalc_mean, precalc_geometric_sum)
237}
238
239fn median_on_sorted<T>(data: &[T]) -> Option<f64>
240where
241 T: PartialOrd + ToPrimitive,
242{
243 Some(match data.len() {
244 0 => {
246 core::hint::cold_path();
247 return None;
248 }
249 1 => data.first()?.to_f64()?,
251 len if len.is_multiple_of(2) => {
253 let idx = len / 2;
254 let v1 = unsafe { data.get_unchecked(idx - 1) }.to_f64()?;
257 let v2 = unsafe { data.get_unchecked(idx) }.to_f64()?;
258 f64::midpoint(v1, v2)
259 }
260 len => unsafe { data.get_unchecked(len / 2) }.to_f64()?,
263 })
264}
265
266fn mad_on_sorted<T>(data: &[T], precalc_median: Option<f64>) -> Option<f64>
267where
268 T: Sync + PartialOrd + ToPrimitive,
269{
270 if data.is_empty() {
271 core::hint::cold_path();
272 return None;
273 }
274 let median_obs =
279 precalc_median.unwrap_or_else(|| unsafe { median_on_sorted(data).unwrap_unchecked() });
280
281 let mut abs_diff_vec: Vec<f64> = if data.len() < PARALLEL_THRESHOLD {
283 data.iter()
286 .map(|x| (median_obs - unsafe { x.to_f64().unwrap_unchecked() }).abs())
288 .collect()
289 } else {
290 data.par_iter()
292 .map(|x| (median_obs - unsafe { x.to_f64().unwrap_unchecked() }).abs())
294 .collect()
295 };
296
297 let len = abs_diff_vec.len();
299 let mid = len / 2;
300 let cmp = |a: &f64, b: &f64| a.total_cmp(b);
301
302 abs_diff_vec.select_nth_unstable_by(mid, cmp);
303
304 if len.is_multiple_of(2) {
305 let right = abs_diff_vec[mid];
307 let left = abs_diff_vec[..mid]
310 .iter()
311 .max_by(|a, b| cmp(a, b))
312 .copied()?;
313 Some(f64::midpoint(left, right))
314 } else {
315 Some(abs_diff_vec[mid])
316 }
317}
318
319fn gini_on_sorted<T>(data: &[Partial<T>], precalc_sum: Option<f64>) -> Option<f64>
320where
321 T: Sync + PartialOrd + ToPrimitive,
322{
323 let len = data.len();
324
325 if len == 0 {
327 core::hint::cold_path();
328 return None;
329 }
330
331 if len == 1 {
333 core::hint::cold_path();
334 return Some(0.0);
335 }
336
337 let first_val = unsafe { data.get_unchecked(0).0.to_f64().unwrap_unchecked() };
341 if first_val < 0.0 {
342 core::hint::cold_path();
343 return None;
344 }
345
346 let (sum, weighted_sum) = if let Some(precalc) = precalc_sum {
351 if precalc < 0.0 {
352 core::hint::cold_path();
353 return None;
354 }
355 let weighted_sum = if len < REDUCTION_PARALLEL_THRESHOLD {
357 let mut weighted_sum = 0.0;
358 for (i, x) in data.iter().enumerate() {
359 let val = unsafe { x.0.to_f64().unwrap_unchecked() };
361 weighted_sum = fp::mul_add((i + 1) as f64, val, weighted_sum);
362 }
363 weighted_sum
364 } else {
365 data.par_iter()
366 .enumerate()
367 .fold(
368 || 0.0_f64,
369 |acc, (i, x)| {
370 let val = unsafe { x.0.to_f64().unwrap_unchecked() };
372 fp::add(acc, fp::mul((i + 1) as f64, val))
373 },
374 )
375 .sum()
376 };
377 (precalc, weighted_sum)
378 } else if len < REDUCTION_PARALLEL_THRESHOLD {
379 let mut sum = 0.0;
381 let mut weighted_sum = 0.0;
382 for (i, x) in data.iter().enumerate() {
383 let val = unsafe { x.0.to_f64().unwrap_unchecked() };
385 sum = fp::add(sum, val);
386 weighted_sum = fp::mul_add((i + 1) as f64, val, weighted_sum);
387 }
388 (sum, weighted_sum)
389 } else {
390 data.par_iter()
392 .enumerate()
393 .fold(
394 || (0.0_f64, 0.0_f64),
395 |acc, (i, x)| {
396 let val = unsafe { x.0.to_f64().unwrap_unchecked() };
398 (fp::add(acc.0, val), fp::mul_add((i + 1) as f64, val, acc.1))
399 },
400 )
401 .reduce(|| (0.0, 0.0), |a, b| (a.0 + b.0, a.1 + b.1))
402 };
403
404 if sum == 0.0 {
406 core::hint::cold_path();
407 return None;
408 }
409
410 let n = len as f64;
414 let gini = 2.0f64.mul_add(weighted_sum / (n * sum), -(n + 1.0) / n);
415
416 Some(gini)
417}
418
419fn kurtosis_on_sorted<T>(
420 data: &[Partial<T>],
421 precalc_mean: Option<f64>,
422 precalc_variance: Option<f64>,
423) -> Option<f64>
424where
425 T: Sync + PartialOrd + ToPrimitive,
426{
427 let len = data.len();
428
429 if len < 4 {
431 core::hint::cold_path();
432 return None;
433 }
434
435 let mean = precalc_mean.unwrap_or_else(|| {
437 let sum: f64 = if len < REDUCTION_PARALLEL_THRESHOLD {
438 data.iter().fold(0.0_f64, |acc, x| {
441 fp::add(acc, unsafe { x.0.to_f64().unwrap_unchecked() })
443 })
444 } else {
445 data.par_iter()
446 .fold(
447 || 0.0_f64,
448 |acc, x| fp::add(acc, unsafe { x.0.to_f64().unwrap_unchecked() }),
450 )
451 .sum()
452 };
453 sum / len as f64
454 });
455
456 let (variance_sq, fourth_power_sum) = if let Some(variance) = precalc_variance {
460 if variance < 0.0 {
462 core::hint::cold_path();
463 return None;
464 }
465 let variance_sq = variance * variance;
467
468 let fourth_power_sum = if len < REDUCTION_PARALLEL_THRESHOLD {
470 let mut sum = 0.0;
471 for x in data {
472 let val = unsafe { x.0.to_f64().unwrap_unchecked() };
474 let diff = fp::sub(val, mean);
475 let diff_sq = fp::mul(diff, diff);
476 sum = fp::mul_add(diff_sq, diff_sq, sum);
477 }
478 sum
479 } else {
480 data.par_iter()
481 .fold(
482 || 0.0_f64,
483 |acc, x| {
484 let val = unsafe { x.0.to_f64().unwrap_unchecked() };
486 let diff = fp::sub(val, mean);
487 let diff_sq = fp::mul(diff, diff);
488 fp::add(acc, fp::mul(diff_sq, diff_sq))
489 },
490 )
491 .sum()
492 };
493
494 (variance_sq, fourth_power_sum)
495 } else {
496 let (variance_sum, fourth_power_sum) = if len < REDUCTION_PARALLEL_THRESHOLD {
498 let mut variance_sum = 0.0;
499 let mut fourth_power_sum = 0.0;
500
501 for x in data {
502 let val = unsafe { x.0.to_f64().unwrap_unchecked() };
504 let diff = fp::sub(val, mean);
505 let diff_sq = fp::mul(diff, diff);
506 variance_sum = fp::add(variance_sum, diff_sq);
507 fourth_power_sum = fp::mul_add(diff_sq, diff_sq, fourth_power_sum);
508 }
509
510 (variance_sum, fourth_power_sum)
511 } else {
512 data.par_iter()
514 .fold(
515 || (0.0_f64, 0.0_f64),
516 |acc, x| {
517 let val = unsafe { x.0.to_f64().unwrap_unchecked() };
519 let diff = fp::sub(val, mean);
520 let diff_sq = fp::mul(diff, diff);
521 (
522 fp::add(acc.0, diff_sq),
523 fp::mul_add(diff_sq, diff_sq, acc.1),
524 )
525 },
526 )
527 .reduce(|| (0.0, 0.0), |a, b| (a.0 + b.0, a.1 + b.1))
528 };
529
530 let variance = variance_sum / len as f64;
531
532 if variance == 0.0 {
534 core::hint::cold_path();
535 return None;
536 }
537
538 let variance_sq = variance * variance;
539 (variance_sq, fourth_power_sum)
540 };
541
542 if variance_sq == 0.0 {
544 core::hint::cold_path();
545 return None;
546 }
547
548 let n = len as f64;
549
550 let adj_denominator = (n - 2.0) * (n - 3.0);
556 let first_term_denominator = n * adj_denominator;
557 let adjustment = 3.0 * (n - 1.0) * (n - 1.0) / adj_denominator;
558 let kurtosis = ((n - 1.0) * (n + 1.0) * fourth_power_sum)
559 .mul_add(1.0 / (first_term_denominator * variance_sq), -adjustment);
560
561 Some(kurtosis)
562}
563
564fn percentile_rank_on_sorted<T, V>(data: &[Partial<T>], value: &V) -> Option<f64>
565where
566 T: PartialOrd + ToPrimitive,
567 V: PartialOrd + ToPrimitive,
568{
569 let len = data.len();
570
571 if len == 0 {
572 core::hint::cold_path();
573 return None;
574 }
575
576 let value_f64 = value.to_f64()?;
577
578 let count_leq = data.binary_search_by(|x| {
581 x.0.to_f64()
582 .unwrap_or(f64::NAN)
583 .partial_cmp(&value_f64)
584 .unwrap_or(std::cmp::Ordering::Less)
585 });
586
587 let count = match count_leq {
588 Ok(idx) => {
589 let upper = data[idx + 1..].partition_point(|x| {
592 x.0.to_f64()
593 .is_some_and(|v| v.total_cmp(&value_f64).is_le())
594 });
595 idx + 1 + upper
596 }
597 Err(idx) => idx, };
599
600 Some((count as f64 / len as f64) * 100.0)
602}
603
604fn atkinson_on_sorted<T>(
605 data: &[Partial<T>],
606 epsilon: f64,
607 precalc_mean: Option<f64>,
608 precalc_geometric_sum: Option<f64>,
609) -> Option<f64>
610where
611 T: Sync + PartialOrd + ToPrimitive,
612{
613 let len = data.len();
614
615 if len == 0 {
617 core::hint::cold_path();
618 return None;
619 }
620
621 if len == 1 {
623 core::hint::cold_path();
624 return Some(0.0);
625 }
626
627 if epsilon < 0.0 {
629 core::hint::cold_path();
630 return None;
631 }
632
633 let epsilon_is_one = (epsilon - 1.0).abs() < 1e-10;
634
635 if epsilon_is_one && precalc_mean.is_none() && precalc_geometric_sum.is_none() {
638 let (sum, ln_sum, any_invalid) = if len < PARALLEL_THRESHOLD {
642 let mut s = 0.0f64;
643 let mut ls = 0.0f64;
644 let mut bad = false;
645 for x in data {
646 let v = unsafe { x.0.to_f64().unwrap_unchecked() };
648 if v.is_nan() || v <= 0.0 {
649 bad = true;
650 } else {
651 s = fp::add(s, v);
652 ls = fp::add(ls, v.ln());
653 }
654 }
655 (s, ls, bad)
656 } else {
657 data.par_iter()
658 .fold(
659 || (0.0f64, 0.0f64, false),
660 |(s, ls, bad), x| {
661 let v = unsafe { x.0.to_f64().unwrap_unchecked() };
663 if v.is_nan() || v <= 0.0 {
664 (s, ls, true)
665 } else {
666 (fp::add(s, v), fp::add(ls, v.ln()), bad)
667 }
668 },
669 )
670 .reduce(
671 || (0.0, 0.0, false),
672 |a, b| (a.0 + b.0, a.1 + b.1, a.2 || b.2),
673 )
674 };
675 if any_invalid {
676 core::hint::cold_path();
677 return None;
678 }
679 let mean = sum / len as f64;
680 if mean == 0.0 {
681 core::hint::cold_path();
682 return None;
683 }
684 let geometric_mean = (ln_sum / len as f64).exp();
685 return Some(1.0 - geometric_mean / mean);
686 }
687
688 let mean = precalc_mean.unwrap_or_else(|| {
690 let sum: f64 = if len < REDUCTION_PARALLEL_THRESHOLD {
691 data.iter().fold(0.0_f64, |acc, x| {
694 fp::add(acc, unsafe { x.0.to_f64().unwrap_unchecked() })
696 })
697 } else {
698 data.par_iter()
699 .fold(
700 || 0.0_f64,
701 |acc, x| fp::add(acc, unsafe { x.0.to_f64().unwrap_unchecked() }),
703 )
704 .sum()
705 };
706 sum / len as f64
707 });
708
709 if mean == 0.0 {
711 core::hint::cold_path();
712 return None;
713 }
714
715 if epsilon_is_one {
719 let geometric_sum: f64 = if let Some(precalc) = precalc_geometric_sum {
721 precalc
722 } else if len < PARALLEL_THRESHOLD {
723 let mut sum = 0.0;
724 for x in data {
725 let val = unsafe { x.0.to_f64().unwrap_unchecked() };
727 if val <= 0.0 {
728 return None;
730 }
731 sum = fp::add(sum, val.ln());
732 }
733 sum
734 } else {
735 data.par_iter()
736 .fold(
737 || 0.0_f64,
738 |acc, x| {
739 let val = unsafe { x.0.to_f64().unwrap_unchecked() };
741 if val <= 0.0 {
742 return f64::NAN;
744 }
745 fp::add(acc, val.ln())
746 },
747 )
748 .sum()
749 };
750
751 if geometric_sum.is_nan() {
752 core::hint::cold_path();
753 return None;
754 }
755
756 let geometric_mean = (geometric_sum / len as f64).exp();
757 return Some(1.0 - geometric_mean / mean);
758 }
759
760 let exponent = 1.0 - epsilon;
763 let inv_mean = mean.recip();
765
766 let sum_powered: f64 = if len < PARALLEL_THRESHOLD {
767 let mut sum = 0.0;
768 for x in data {
769 let val = unsafe { x.0.to_f64().unwrap_unchecked() };
771 if val < 0.0 {
772 return None;
774 }
775 let ratio = val * inv_mean;
776 sum = fp::add(sum, ratio.powf(exponent));
777 }
778 sum
779 } else {
780 data.par_iter()
781 .fold(
782 || 0.0_f64,
783 |acc, x| {
784 let val = unsafe { x.0.to_f64().unwrap_unchecked() };
786 if val < 0.0 {
787 return f64::NAN;
789 }
790 let ratio = val * inv_mean;
791 fp::add(acc, ratio.powf(exponent))
792 },
793 )
794 .sum()
795 };
796
797 if sum_powered.is_nan() || sum_powered <= 0.0 {
798 core::hint::cold_path();
799 return None;
800 }
801
802 let atkinson = 1.0 - (sum_powered / len as f64).powf(1.0 / exponent);
803 Some(atkinson)
804}
805
806#[cfg(test)]
809fn quickselect<T>(data: &mut [Partial<T>], k: usize) -> Option<&T>
810where
811 T: PartialOrd,
812{
813 if data.is_empty() || k >= data.len() {
814 core::hint::cold_path();
815 return None;
816 }
817
818 let mut left = 0;
819 let mut right = data.len() - 1;
820
821 loop {
822 if left == right {
823 return Some(&data[left].0);
824 }
825
826 let pivot_idx = median_of_three_pivot(data, left, right);
828 let pivot_idx = partition(data, left, right, pivot_idx);
829
830 match k.cmp(&pivot_idx) {
831 std::cmp::Ordering::Equal => return Some(&data[pivot_idx].0),
832 std::cmp::Ordering::Less => right = pivot_idx - 1,
833 std::cmp::Ordering::Greater => left = pivot_idx + 1,
834 }
835 }
836}
837
838#[cfg(test)]
840fn median_of_three_pivot<T>(data: &[Partial<T>], left: usize, right: usize) -> usize
841where
842 T: PartialOrd,
843{
844 let mid = left + (right - left) / 2;
845
846 if data[left] <= data[mid] {
847 if data[mid] <= data[right] {
848 mid
849 } else if data[left] <= data[right] {
850 right
851 } else {
852 left
853 }
854 } else if data[left] <= data[right] {
855 left
856 } else if data[mid] <= data[right] {
857 right
858 } else {
859 mid
860 }
861}
862
863#[cfg(test)]
865fn partition<T>(data: &mut [Partial<T>], left: usize, right: usize, pivot_idx: usize) -> usize
866where
867 T: PartialOrd,
868{
869 data.swap(pivot_idx, right);
871 let mut store_idx = left;
872
873 for i in left..right {
876 if unsafe { data.get_unchecked(i) <= data.get_unchecked(right) } {
879 data.swap(i, store_idx);
880 store_idx += 1;
881 }
882 }
883
884 data.swap(store_idx, right);
886 store_idx
887}
888
889fn quartiles_on_sorted<T>(data: &[Partial<T>]) -> Option<(f64, f64, f64)>
893where
894 T: PartialOrd + ToPrimitive,
895{
896 let len = data.len();
897
898 match len {
900 0..=2 => {
901 core::hint::cold_path();
902 return None;
903 }
904 3 => {
905 return Some(
906 unsafe {
908 (
909 data.get_unchecked(0).0.to_f64()?,
910 data.get_unchecked(1).0.to_f64()?,
911 data.get_unchecked(2).0.to_f64()?,
912 )
913 },
914 );
915 }
916 _ => {}
917 }
918
919 let k = len / 4;
921 let remainder = len % 4;
922
923 unsafe {
926 Some(match remainder {
927 0 => {
928 let q1 = f64::midpoint(
939 data.get_unchecked(k - 1).0.to_f64()?,
940 data.get_unchecked(k).0.to_f64()?,
941 );
942 let q2 = f64::midpoint(
943 data.get_unchecked(2 * k - 1).0.to_f64()?,
944 data.get_unchecked(2 * k).0.to_f64()?,
945 );
946 let q3 = f64::midpoint(
947 data.get_unchecked(3 * k - 1).0.to_f64()?,
948 data.get_unchecked(3 * k).0.to_f64()?,
949 );
950 (q1, q2, q3)
951 }
952 1 => {
953 let q1 = f64::midpoint(
964 data.get_unchecked(k - 1).0.to_f64()?,
965 data.get_unchecked(k).0.to_f64()?,
966 );
967 let q2 = data.get_unchecked(2 * k).0.to_f64()?;
968 let q3 = f64::midpoint(
969 data.get_unchecked(3 * k).0.to_f64()?,
970 data.get_unchecked(3 * k + 1).0.to_f64()?,
971 );
972 (q1, q2, q3)
973 }
974 2 => {
975 let q1 = data.get_unchecked(k).0.to_f64()?;
986 let q2 = f64::midpoint(
987 data.get_unchecked(2 * k).0.to_f64()?,
988 data.get_unchecked(2 * k + 1).0.to_f64()?,
989 );
990 let q3 = data.get_unchecked(3 * k + 1).0.to_f64()?;
991 (q1, q2, q3)
992 }
993 _ => {
994 let q1 = data.get_unchecked(k).0.to_f64()?;
1005 let q2 = data.get_unchecked(2 * k + 1).0.to_f64()?;
1006 let q3 = data.get_unchecked(3 * k + 2).0.to_f64()?;
1007 (q1, q2, q3)
1008 }
1009 })
1010 }
1011}
1012
1013fn quartiles_with_zero_copy_selection<T>(data: &[Partial<T>]) -> Option<(f64, f64, f64)>
1022where
1023 T: PartialOrd + ToPrimitive,
1024{
1025 let len = data.len();
1026
1027 match len {
1029 0..=2 => {
1030 core::hint::cold_path();
1031 return None;
1032 }
1033 3 => {
1034 let mut indices: Vec<usize> = (0..3).collect();
1035 let cmp = |a: &usize, b: &usize| {
1036 data[*a]
1037 .partial_cmp(&data[*b])
1038 .unwrap_or(std::cmp::Ordering::Less)
1039 };
1040 indices.sort_unstable_by(cmp);
1041 let min_val = data[indices[0]].0.to_f64()?;
1042 let med_val = data[indices[1]].0.to_f64()?;
1043 let max_val = data[indices[2]].0.to_f64()?;
1044 return Some((min_val, med_val, max_val));
1045 }
1046 _ => {}
1047 }
1048
1049 let k = len / 4;
1050 let remainder = len % 4;
1051
1052 let mut indices: Vec<usize> = (0..len).collect();
1053 let cmp = |a: &usize, b: &usize| {
1054 data[*a]
1055 .partial_cmp(&data[*b])
1056 .unwrap_or(std::cmp::Ordering::Less)
1057 };
1058
1059 let raw_positions: Vec<usize> = match remainder {
1064 0 => vec![k - 1, k, 2 * k - 1, 2 * k, 3 * k - 1, 3 * k],
1065 1 => vec![k - 1, k, 2 * k, 3 * k, 3 * k + 1],
1066 2 => vec![k, 2 * k, 2 * k + 1, 3 * k + 1],
1067 _ => vec![k, 2 * k + 1, 3 * k + 2],
1068 };
1069
1070 let mut unique_positions = raw_positions.clone();
1071 unique_positions.dedup();
1072
1073 let mut start = 0;
1075 for &pos in &unique_positions {
1076 indices[start..].select_nth_unstable_by(pos - start, &cmp);
1077 start = pos + 1;
1078 }
1079
1080 let values: Vec<f64> = raw_positions
1082 .iter()
1083 .map(|&pos| data[indices[pos]].0.to_f64())
1084 .collect::<Option<Vec<_>>>()?;
1085
1086 match remainder {
1087 0 => {
1088 let q1 = f64::midpoint(values[0], values[1]);
1089 let q2 = f64::midpoint(values[2], values[3]);
1090 let q3 = f64::midpoint(values[4], values[5]);
1091 Some((q1, q2, q3))
1092 }
1093 1 => {
1094 let q1 = f64::midpoint(values[0], values[1]);
1095 let q2 = values[2];
1096 let q3 = f64::midpoint(values[3], values[4]);
1097 Some((q1, q2, q3))
1098 }
1099 2 => {
1100 let q1 = values[0];
1101 let q2 = f64::midpoint(values[1], values[2]);
1102 let q3 = values[3];
1103 Some((q1, q2, q3))
1104 }
1105 _ => Some((values[0], values[1], values[2])),
1106 }
1107}
1108
1109fn mode_on_sorted<T, I>(it: I) -> Option<T>
1110where
1111 T: PartialOrd,
1112 I: Iterator<Item = T>,
1113{
1114 use std::cmp::Ordering;
1115
1116 let (mut mode, mut next) = (None, None);
1123 let (mut mode_count, mut next_count) = (0usize, 0usize);
1124 for x in it {
1125 if mode.as_ref() == Some(&x) {
1126 mode_count += 1;
1127 } else if next.as_ref() == Some(&x) {
1128 next_count += 1;
1129 } else {
1130 next = Some(x);
1131 next_count = 0;
1132 }
1133
1134 match next_count.cmp(&mode_count) {
1135 Ordering::Greater => {
1136 mode = next;
1137 mode_count = next_count;
1138 next = None;
1139 next_count = 0;
1140 }
1141 Ordering::Equal => {
1142 mode = None;
1143 mode_count = 0;
1144 }
1145 Ordering::Less => {}
1146 }
1147 }
1148 mode
1149}
1150
1151#[allow(clippy::type_complexity)]
1179#[inline]
1180fn modes_and_antimodes_on_sorted_slice<T>(
1181 data: &[Partial<T>],
1182) -> ((Vec<T>, usize, u32), (Vec<T>, usize, u32))
1183where
1184 T: PartialOrd + Clone,
1185{
1186 let size = data.len();
1187
1188 if size == 0 {
1190 core::hint::cold_path();
1191 return ((Vec::new(), 0, 0), (Vec::new(), 0, 0));
1192 }
1193
1194 let sqrt_size = size.isqrt();
1196 let mut runs: Vec<(&T, u32)> = Vec::with_capacity(sqrt_size.clamp(16, 1_000));
1197
1198 let mut current_value = &data[0].0;
1199 let mut current_count = 1;
1200 let mut highest_count = 1;
1201 let mut lowest_count = u32::MAX;
1202
1203 for x in data.iter().skip(1) {
1205 if x.0 == *current_value {
1206 current_count += 1;
1207 highest_count = highest_count.max(current_count);
1208 } else {
1209 runs.push((current_value, current_count));
1210 lowest_count = lowest_count.min(current_count);
1211 current_value = &x.0;
1212 current_count = 1;
1213 }
1214 }
1215 runs.push((current_value, current_count));
1216 lowest_count = lowest_count.min(current_count);
1217
1218 modes_antimodes_from_runs(runs, highest_count, lowest_count)
1219}
1220
1221#[allow(clippy::type_complexity)]
1244#[inline]
1245fn modes_antimodes_from_runs<T>(
1246 mut runs: Vec<(&T, u32)>,
1247 highest_count: u32,
1248 lowest_count: u32,
1249) -> ((Vec<T>, usize, u32), (Vec<T>, usize, u32))
1250where
1251 T: Clone,
1252{
1253 if runs.is_empty() {
1255 core::hint::cold_path();
1256 return ((Vec::new(), 0, 0), (Vec::new(), 0, 0));
1257 }
1258
1259 if runs.len() == 1 {
1261 let (val, count) = runs.pop().unwrap();
1262 return ((vec![val.clone()], 1, count), (Vec::new(), 0, 0));
1263 }
1264
1265 if highest_count == 1 {
1267 let antimodes_count = runs.len().min(10);
1268 let total_count = runs.len();
1269 let mut antimodes = Vec::with_capacity(antimodes_count);
1270 for (val, _) in runs.into_iter().take(antimodes_count) {
1271 antimodes.push(val.clone());
1272 }
1273 return ((Vec::new(), 0, 0), (antimodes, total_count, 1));
1275 }
1276
1277 let estimated_modes = (runs.len() / 10).clamp(1, 10);
1280 let estimated_antimodes = 10.min(runs.len());
1281
1282 let mut modes_result = Vec::with_capacity(estimated_modes);
1283 let mut antimodes_result = Vec::with_capacity(estimated_antimodes);
1284 let mut mode_count = 0;
1285 let mut antimodes_count = 0;
1286 let mut antimodes_collected = 0_u32;
1287
1288 for (val, count) in &runs {
1289 if *count == highest_count {
1290 modes_result.push((*val).clone());
1291 mode_count += 1;
1292 }
1293 if *count == lowest_count {
1294 antimodes_count += 1;
1295 if antimodes_collected < 10 {
1296 antimodes_result.push((*val).clone());
1297 antimodes_collected += 1;
1298 }
1299 }
1300 }
1301
1302 (
1303 (modes_result, mode_count, highest_count),
1304 (antimodes_result, antimodes_count, lowest_count),
1305 )
1306}
1307
1308#[allow(clippy::unsafe_derive_deserialize)]
1316#[derive(Clone, Serialize, Deserialize)]
1317pub struct Unsorted<T> {
1318 #[serde(skip)]
1321 sorted: bool,
1322 data: Vec<Partial<T>>,
1323}
1324
1325impl<T: PartialEq> PartialEq for Unsorted<T> {
1329 fn eq(&self, other: &Self) -> bool {
1330 self.data == other.data
1331 }
1332}
1333
1334impl<T: PartialEq> Eq for Unsorted<T> where Partial<T>: Eq {}
1335
1336impl<T: PartialOrd + Send> Unsorted<T> {
1337 #[inline]
1339 #[must_use]
1340 pub fn new() -> Unsorted<T> {
1341 Default::default()
1342 }
1343
1344 #[allow(clippy::inline_always)]
1346 #[inline(always)]
1347 pub fn add(&mut self, v: T) {
1348 self.sorted = false;
1349 self.data.push(Partial(v));
1350 }
1351
1352 #[inline]
1354 #[must_use]
1355 pub const fn len(&self) -> usize {
1356 self.data.len()
1357 }
1358
1359 #[inline]
1360 #[must_use]
1361 pub const fn is_empty(&self) -> bool {
1362 self.data.is_empty()
1363 }
1364
1365 #[inline]
1366 fn sort(&mut self) {
1367 if !self.sorted {
1368 if self.data.len() < PARALLEL_THRESHOLD {
1370 self.data.sort_unstable();
1371 } else {
1372 self.data.par_sort_unstable();
1373 }
1374 self.sorted = true;
1375 }
1376 }
1377
1378 #[inline]
1379 const fn already_sorted(&mut self) {
1380 self.sorted = true;
1381 }
1382
1383 #[inline]
1385 pub fn add_bulk(&mut self, values: Vec<T>) {
1386 self.sorted = false;
1387 self.data.reserve(values.len());
1388 self.data.extend(values.into_iter().map(Partial));
1389 }
1390
1391 #[inline]
1393 pub fn shrink_to_fit(&mut self) {
1394 self.data.shrink_to_fit();
1395 }
1396
1397 #[inline]
1399 #[must_use]
1400 pub fn with_capacity(capacity: usize) -> Self {
1401 Unsorted {
1402 sorted: true,
1403 data: Vec::with_capacity(capacity),
1404 }
1405 }
1406
1407 #[inline]
1409 pub fn push_ascending(&mut self, value: T) {
1410 if let Some(last) = self.data.last() {
1411 debug_assert!(last.0 <= value, "Value must be >= than last element");
1412 }
1413 self.data.push(Partial(value));
1414 }
1416}
1417
1418impl<T: PartialOrd + PartialEq + Clone + Send + Sync> Unsorted<T> {
1419 #[inline]
1420 pub fn cardinality(&mut self, sorted: bool, parallel_threshold: usize) -> u64 {
1428 const CHUNK_SIZE: usize = 2048; const DEFAULT_PARALLEL_THRESHOLD: usize = 10_240; let len = self.data.len();
1432 match len {
1433 0 => return 0,
1434 1 => return 1,
1435 _ => {}
1436 }
1437
1438 if sorted {
1439 self.already_sorted();
1440 } else {
1441 self.sort();
1442 }
1443
1444 let use_parallel = parallel_threshold != 0
1445 && (parallel_threshold == 1
1446 || len > parallel_threshold.max(DEFAULT_PARALLEL_THRESHOLD));
1447
1448 if use_parallel {
1449 self.data
1453 .par_chunks(CHUNK_SIZE)
1454 .map(|chunk| {
1455 let mut count = u64::from(!chunk.is_empty());
1457 for [a, b] in chunk.array_windows::<2>() {
1458 if a != b {
1459 count += 1;
1460 }
1461 }
1462 (count, chunk.first(), chunk.last())
1463 })
1464 .reduce(
1465 || (0u64, None, None),
1466 |(cl, fl, ll), (cr, fr, lr)| match (ll, fr) {
1467 (None, _) => (cl + cr, fr, lr),
1471 (_, None) => (cl + cr, fl, ll),
1472 (Some(l), Some(r)) => {
1473 let adj = u64::from(l == r);
1474 (cl + cr - adj, fl, lr)
1475 }
1476 },
1477 )
1478 .0
1479 } else {
1480 let mut count = u64::from(!self.data.is_empty());
1485
1486 for [a, b] in self.data.array_windows::<2>() {
1487 if a != b {
1488 count += 1;
1489 }
1490 }
1491 count
1492 }
1493 }
1494}
1495
1496impl<T: PartialOrd + Clone + Send> Unsorted<T> {
1497 #[inline]
1499 pub fn mode(&mut self) -> Option<T> {
1500 if self.data.is_empty() {
1501 return None;
1502 }
1503 self.sort();
1504 mode_on_sorted(self.data.iter().map(|p| &p.0)).cloned()
1505 }
1506
1507 #[inline]
1511 fn modes(&mut self) -> (Vec<T>, usize, u32) {
1512 if self.data.is_empty() {
1513 return (Vec::new(), 0, 0);
1514 }
1515 self.sort();
1516 modes_and_antimodes_on_sorted_slice(&self.data).0
1517 }
1518
1519 #[inline]
1522 fn antimodes(&mut self) -> (Vec<T>, usize, u32) {
1523 if self.data.is_empty() {
1524 return (Vec::new(), 0, 0);
1525 }
1526 self.sort();
1527 modes_and_antimodes_on_sorted_slice(&self.data).1
1528 }
1529
1530 #[allow(clippy::type_complexity)]
1533 #[inline]
1534 pub fn modes_antimodes(&mut self) -> ((Vec<T>, usize, u32), (Vec<T>, usize, u32)) {
1535 if self.data.is_empty() {
1536 return ((Vec::new(), 0, 0), (Vec::new(), 0, 0));
1537 }
1538 self.sort();
1539 modes_and_antimodes_on_sorted_slice(&self.data)
1540 }
1541}
1542
1543impl Unsorted<Vec<u8>> {
1544 #[allow(clippy::inline_always)]
1551 #[inline(always)]
1552 pub fn add_bytes(&mut self, v: &[u8]) {
1553 self.sorted = false;
1554 self.data.push(Partial(v.to_vec()));
1555 }
1556}
1557
1558impl<T: PartialOrd + ToPrimitive + Send> Unsorted<T> {
1559 #[inline]
1561 pub fn median(&mut self) -> Option<f64> {
1562 if self.data.is_empty() {
1563 return None;
1564 }
1565 self.sort();
1566 median_on_sorted(&self.data)
1567 }
1568}
1569
1570impl<T: PartialOrd + ToPrimitive + Send + Sync> Unsorted<T> {
1571 #[inline]
1573 pub fn mad(&mut self, existing_median: Option<f64>) -> Option<f64> {
1574 if self.data.is_empty() {
1575 return None;
1576 }
1577 if existing_median.is_none() {
1578 self.sort();
1579 }
1580 mad_on_sorted(&self.data, existing_median)
1581 }
1582}
1583
1584impl<T: PartialOrd + ToPrimitive + Send> Unsorted<T> {
1585 #[inline]
1590 pub fn quartiles(&mut self) -> Option<(f64, f64, f64)> {
1591 if self.data.is_empty() {
1592 return None;
1593 }
1594 self.sort();
1595 quartiles_on_sorted(&self.data)
1596 }
1597}
1598
1599impl<T: PartialOrd + ToPrimitive + Send + Sync> Unsorted<T> {
1600 #[inline]
1606 pub fn gini(&mut self, precalc_sum: Option<f64>) -> Option<f64> {
1607 if self.data.is_empty() {
1608 return None;
1609 }
1610 self.sort();
1611 gini_on_sorted(&self.data, precalc_sum)
1612 }
1613
1614 #[inline]
1621 pub fn kurtosis(
1622 &mut self,
1623 precalc_mean: Option<f64>,
1624 precalc_variance: Option<f64>,
1625 ) -> Option<f64> {
1626 if self.data.is_empty() {
1627 return None;
1628 }
1629 self.sort();
1630 kurtosis_on_sorted(&self.data, precalc_mean, precalc_variance)
1631 }
1632
1633 #[inline]
1640 #[allow(clippy::needless_pass_by_value)]
1641 pub fn percentile_rank<V>(&mut self, value: V) -> Option<f64>
1642 where
1643 V: PartialOrd + ToPrimitive,
1644 {
1645 if self.data.is_empty() {
1646 return None;
1647 }
1648 self.sort();
1649 percentile_rank_on_sorted(&self.data, &value)
1650 }
1651
1652 #[inline]
1669 pub fn atkinson(
1670 &mut self,
1671 epsilon: f64,
1672 precalc_mean: Option<f64>,
1673 precalc_geometric_sum: Option<f64>,
1674 ) -> Option<f64> {
1675 if self.data.is_empty() {
1676 return None;
1677 }
1678 self.sort();
1679 atkinson_on_sorted(&self.data, epsilon, precalc_mean, precalc_geometric_sum)
1680 }
1681}
1682
1683impl<T: PartialOrd + ToPrimitive + Clone + Send> Unsorted<T> {
1684 #[inline]
1696 pub fn quartiles_with_selection(&mut self) -> Option<(f64, f64, f64)> {
1697 if self.data.is_empty() {
1698 return None;
1699 }
1700 quartiles_with_zero_copy_selection(&self.data)
1702 }
1703}
1704
1705impl<T: PartialOrd + ToPrimitive + Send> Unsorted<T> {
1706 #[inline]
1712 #[must_use]
1713 pub fn quartiles_zero_copy(&self) -> Option<(f64, f64, f64)> {
1714 if self.data.is_empty() {
1715 return None;
1716 }
1717 quartiles_with_zero_copy_selection(&self.data)
1718 }
1719}
1720
1721impl<T: PartialOrd + Send> Commute for Unsorted<T> {
1722 #[inline]
1723 fn merge(&mut self, mut v: Unsorted<T>) {
1724 if v.is_empty() {
1725 return;
1726 }
1727
1728 self.sorted = false;
1729 self.data.extend(std::mem::take(&mut v.data));
1731 }
1732}
1733
1734impl<T: PartialOrd> Default for Unsorted<T> {
1735 #[inline]
1736 fn default() -> Unsorted<T> {
1737 Unsorted {
1738 data: Vec::with_capacity(16),
1739 sorted: true, }
1741 }
1742}
1743
1744impl<T: PartialOrd + Send> FromIterator<T> for Unsorted<T> {
1745 #[inline]
1746 fn from_iter<I: IntoIterator<Item = T>>(it: I) -> Unsorted<T> {
1747 let mut v = Unsorted::new();
1748 v.extend(it);
1749 v
1750 }
1751}
1752
1753impl<T: PartialOrd> Extend<T> for Unsorted<T> {
1754 #[inline]
1755 fn extend<I: IntoIterator<Item = T>>(&mut self, it: I) {
1756 self.sorted = false;
1757 self.data.extend(it.into_iter().map(Partial));
1758 }
1759}
1760
1761fn custom_percentiles_on_sorted<T>(data: &[Partial<T>], percentiles: &[u8]) -> Option<Vec<T>>
1762where
1763 T: PartialOrd + Clone,
1764{
1765 let len = data.len();
1766
1767 if len == 0 || percentiles.iter().any(|&p| p > 100) {
1769 return None;
1770 }
1771
1772 let unique_percentiles: Vec<u8> = if percentiles.len() <= 1 {
1774 percentiles.to_vec()
1776 } else {
1777 let is_sorted_unique = percentiles.array_windows::<2>().all(|[a, b]| a < b);
1779
1780 if is_sorted_unique {
1781 percentiles.to_vec()
1783 } else {
1784 let mut seen = [false; 101];
1786 let mut sorted_unique = Vec::with_capacity(percentiles.len().min(101));
1787 for &p in percentiles {
1788 if !seen[p as usize] {
1789 seen[p as usize] = true;
1790 sorted_unique.push(p);
1791 }
1792 }
1793 sorted_unique.sort_unstable();
1794 sorted_unique
1795 }
1796 };
1797
1798 let mut results = Vec::with_capacity(unique_percentiles.len());
1799
1800 unsafe {
1804 for &p in &unique_percentiles {
1805 #[allow(clippy::cast_sign_loss)]
1809 let rank = ((f64::from(p) / 100.0) * len as f64).ceil() as usize;
1810
1811 let idx = rank.saturating_sub(1);
1813
1814 results.push(data.get_unchecked(idx).0.clone());
1816 }
1817 }
1818
1819 Some(results)
1820}
1821
1822impl<T: PartialOrd + Clone + Send> Unsorted<T> {
1823 #[inline]
1845 pub fn custom_percentiles(&mut self, percentiles: &[u8]) -> Option<Vec<T>> {
1846 if self.data.is_empty() {
1847 return None;
1848 }
1849 self.sort();
1850 custom_percentiles_on_sorted(&self.data, percentiles)
1851 }
1852}
1853
1854#[cfg(test)]
1855mod test {
1856 use super::*;
1857
1858 #[test]
1859 fn test_cardinality_empty() {
1860 let mut unsorted: Unsorted<i32> = Unsorted::new();
1861 assert_eq!(unsorted.cardinality(false, 1), 0);
1862 }
1863
1864 #[test]
1865 fn test_cardinality_single_element() {
1866 let mut unsorted = Unsorted::new();
1867 unsorted.add(5);
1868 assert_eq!(unsorted.cardinality(false, 1), 1);
1869 }
1870
1871 #[test]
1872 fn test_cardinality_unique_elements() {
1873 let mut unsorted = Unsorted::new();
1874 unsorted.extend(vec![1, 2, 3, 4, 5]);
1875 assert_eq!(unsorted.cardinality(false, 1), 5);
1876 }
1877
1878 #[test]
1879 fn test_cardinality_duplicate_elements() {
1880 let mut unsorted = Unsorted::new();
1881 unsorted.extend(vec![1, 2, 2, 3, 3, 3, 4, 4, 4, 4]);
1882 assert_eq!(unsorted.cardinality(false, 1), 4);
1883 }
1884
1885 #[test]
1886 fn test_cardinality_all_same() {
1887 let mut unsorted = Unsorted::new();
1888 unsorted.extend(vec![1; 100]);
1889 assert_eq!(unsorted.cardinality(false, 1), 1);
1890 }
1891
1892 #[test]
1893 fn test_cardinality_large_range() {
1894 let mut unsorted = Unsorted::new();
1895 unsorted.extend(0..1_000_000);
1896 assert_eq!(unsorted.cardinality(false, 1), 1_000_000);
1897 }
1898
1899 #[test]
1900 fn test_cardinality_large_range_sequential() {
1901 let mut unsorted = Unsorted::new();
1902 unsorted.extend(0..1_000_000);
1903 assert_eq!(unsorted.cardinality(false, 2_000_000), 1_000_000);
1904 }
1905
1906 #[test]
1907 fn test_cardinality_presorted() {
1908 let mut unsorted = Unsorted::new();
1909 unsorted.extend(vec![1, 2, 3, 4, 5]);
1910 unsorted.sort();
1911 assert_eq!(unsorted.cardinality(true, 1), 5);
1912 }
1913
1914 #[test]
1915 fn test_cardinality_float() {
1916 let mut unsorted = Unsorted::new();
1917 unsorted.extend(vec![1.0, 1.0, 2.0, 3.0, 3.0, 4.0]);
1918 assert_eq!(unsorted.cardinality(false, 1), 4);
1919 }
1920
1921 #[test]
1922 fn test_cardinality_string() {
1923 let mut unsorted = Unsorted::new();
1924 unsorted.extend(vec!["a", "b", "b", "c", "c", "c"]);
1925 assert_eq!(unsorted.cardinality(false, 1), 3);
1926 }
1927
1928 #[test]
1929 fn test_quartiles_selection_vs_sorted() {
1930 let test_cases = vec![
1932 vec![3, 5, 7, 9],
1933 vec![3, 5, 7],
1934 vec![1, 2, 7, 11],
1935 vec![3, 5, 7, 9, 12],
1936 vec![2, 2, 3, 8, 10],
1937 vec![3, 5, 7, 9, 12, 20],
1938 vec![0, 2, 4, 8, 10, 11],
1939 vec![3, 5, 7, 9, 12, 20, 21],
1940 vec![1, 5, 6, 6, 7, 10, 19],
1941 ];
1942
1943 for test_case in test_cases {
1944 let mut unsorted1 = Unsorted::new();
1945 let mut unsorted2 = Unsorted::new();
1946 let mut unsorted3 = Unsorted::new();
1947 unsorted1.extend(test_case.clone());
1948 unsorted2.extend(test_case.clone());
1949 unsorted3.extend(test_case.clone());
1950
1951 let result_sorted = unsorted1.quartiles();
1952 let result_selection = unsorted2.quartiles_with_selection();
1953 let result_zero_copy = unsorted3.quartiles_zero_copy();
1954
1955 assert_eq!(
1956 result_sorted, result_selection,
1957 "Selection mismatch for test case: {:?}",
1958 test_case
1959 );
1960 assert_eq!(
1961 result_sorted, result_zero_copy,
1962 "Zero-copy mismatch for test case: {:?}",
1963 test_case
1964 );
1965 }
1966 }
1967
1968 #[test]
1969 fn test_quartiles_with_selection_small() {
1970 let mut unsorted: Unsorted<i32> = Unsorted::new();
1972 assert_eq!(unsorted.quartiles_with_selection(), None);
1973
1974 let mut unsorted = Unsorted::new();
1975 unsorted.extend(vec![1, 2]);
1976 assert_eq!(unsorted.quartiles_with_selection(), None);
1977
1978 let mut unsorted = Unsorted::new();
1979 unsorted.extend(vec![1, 2, 3]);
1980 assert_eq!(unsorted.quartiles_with_selection(), Some((1.0, 2.0, 3.0)));
1981 }
1982
1983 #[test]
1984 fn test_quickselect() {
1985 let data = vec![
1986 Partial(3),
1987 Partial(1),
1988 Partial(4),
1989 Partial(1),
1990 Partial(5),
1991 Partial(9),
1992 Partial(2),
1993 Partial(6),
1994 ];
1995
1996 assert_eq!(quickselect(&mut data.clone(), 0), Some(&1));
1998 assert_eq!(quickselect(&mut data.clone(), 3), Some(&3));
1999 assert_eq!(quickselect(&mut data.clone(), 7), Some(&9));
2000
2001 let mut empty: Vec<Partial<i32>> = vec![];
2003 assert_eq!(quickselect(&mut empty, 0), None);
2004
2005 let mut data = vec![Partial(3), Partial(1), Partial(4), Partial(1), Partial(5)];
2006 assert_eq!(quickselect(&mut data, 10), None); }
2008
2009 #[test]
2010 fn median_stream() {
2011 assert_eq!(median(vec![3usize, 5, 7, 9].into_iter()), Some(6.0));
2012 assert_eq!(median(vec![3usize, 5, 7].into_iter()), Some(5.0));
2013 }
2014
2015 #[test]
2016 fn mad_stream() {
2017 assert_eq!(mad(vec![3usize, 5, 7, 9].into_iter(), None), Some(2.0));
2018 assert_eq!(
2019 mad(
2020 vec![
2021 86usize, 60, 95, 39, 49, 12, 56, 82, 92, 24, 33, 28, 46, 34, 100, 39, 100, 38,
2022 50, 61, 39, 88, 5, 13, 64
2023 ]
2024 .into_iter(),
2025 None
2026 ),
2027 Some(16.0)
2028 );
2029 }
2030
2031 #[test]
2032 fn mad_stream_precalc_median() {
2033 let data = vec![3usize, 5, 7, 9].into_iter();
2034 let median1 = median(data.clone());
2035 assert_eq!(mad(data, median1), Some(2.0));
2036
2037 let data2 = vec![
2038 86usize, 60, 95, 39, 49, 12, 56, 82, 92, 24, 33, 28, 46, 34, 100, 39, 100, 38, 50, 61,
2039 39, 88, 5, 13, 64,
2040 ]
2041 .into_iter();
2042 let median2 = median(data2.clone());
2043 assert_eq!(mad(data2, median2), Some(16.0));
2044 }
2045
2046 #[test]
2047 fn mode_stream() {
2048 assert_eq!(mode(vec![3usize, 5, 7, 9].into_iter()), None);
2049 assert_eq!(mode(vec![3usize, 3, 3, 3].into_iter()), Some(3));
2050 assert_eq!(mode(vec![3usize, 3, 3, 4].into_iter()), Some(3));
2051 assert_eq!(mode(vec![4usize, 3, 3, 3].into_iter()), Some(3));
2052 assert_eq!(mode(vec![1usize, 1, 2, 3, 3].into_iter()), None);
2053 }
2054
2055 #[test]
2056 fn median_floats() {
2057 assert_eq!(median(vec![3.0f64, 5.0, 7.0, 9.0].into_iter()), Some(6.0));
2058 assert_eq!(median(vec![3.0f64, 5.0, 7.0].into_iter()), Some(5.0));
2059 }
2060
2061 #[test]
2062 fn mode_floats() {
2063 assert_eq!(mode(vec![3.0f64, 5.0, 7.0, 9.0].into_iter()), None);
2064 assert_eq!(mode(vec![3.0f64, 3.0, 3.0, 3.0].into_iter()), Some(3.0));
2065 assert_eq!(mode(vec![3.0f64, 3.0, 3.0, 4.0].into_iter()), Some(3.0));
2066 assert_eq!(mode(vec![4.0f64, 3.0, 3.0, 3.0].into_iter()), Some(3.0));
2067 assert_eq!(mode(vec![1.0f64, 1.0, 2.0, 3.0, 3.0].into_iter()), None);
2068 }
2069
2070 #[test]
2071 fn modes_stream() {
2072 assert_eq!(modes(vec![3usize, 5, 7, 9].into_iter()), (vec![], 0, 0));
2073 assert_eq!(modes(vec![3usize, 3, 3, 3].into_iter()), (vec![3], 1, 4));
2074 assert_eq!(modes(vec![3usize, 3, 4, 4].into_iter()), (vec![3, 4], 2, 2));
2075 assert_eq!(modes(vec![4usize, 3, 3, 3].into_iter()), (vec![3], 1, 3));
2076 assert_eq!(modes(vec![1usize, 1, 2, 2].into_iter()), (vec![1, 2], 2, 2));
2077 let vec: Vec<u32> = vec![];
2078 assert_eq!(modes(vec.into_iter()), (vec![], 0, 0));
2079 }
2080
2081 #[test]
2082 fn modes_floats() {
2083 assert_eq!(
2084 modes(vec![3_f64, 5.0, 7.0, 9.0].into_iter()),
2085 (vec![], 0, 0)
2086 );
2087 assert_eq!(
2088 modes(vec![3_f64, 3.0, 3.0, 3.0].into_iter()),
2089 (vec![3.0], 1, 4)
2090 );
2091 assert_eq!(
2092 modes(vec![3_f64, 3.0, 4.0, 4.0].into_iter()),
2093 (vec![3.0, 4.0], 2, 2)
2094 );
2095 assert_eq!(
2096 modes(vec![1_f64, 1.0, 2.0, 3.0, 3.0].into_iter()),
2097 (vec![1.0, 3.0], 2, 2)
2098 );
2099 }
2100
2101 #[test]
2102 fn antimodes_stream() {
2103 assert_eq!(
2104 antimodes(vec![3usize, 5, 7, 9].into_iter()),
2105 (vec![3, 5, 7, 9], 4, 1)
2106 );
2107 assert_eq!(
2108 antimodes(vec![1usize, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13].into_iter()),
2109 (vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 13, 1)
2110 );
2111 assert_eq!(
2112 antimodes(vec![1usize, 3, 3, 3].into_iter()),
2113 (vec![1], 1, 1)
2114 );
2115 assert_eq!(
2116 antimodes(vec![3usize, 3, 4, 4].into_iter()),
2117 (vec![3, 4], 2, 2)
2118 );
2119 assert_eq!(
2120 antimodes(
2121 vec![
2122 3usize, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13,
2123 14, 14, 15, 15
2124 ]
2125 .into_iter()
2126 ),
2127 (vec![3, 4, 5, 6, 7, 8, 9, 10, 11, 12], 13, 2)
2129 );
2130 assert_eq!(
2131 antimodes(
2132 vec![
2133 3usize, 3, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 4, 4, 5, 5, 6, 6, 7, 7, 13, 13,
2134 14, 14, 15, 15
2135 ]
2136 .into_iter()
2137 ),
2138 (vec![3, 4, 5, 6, 7, 8, 9, 10, 11, 12], 13, 2)
2139 );
2140 assert_eq!(
2141 antimodes(vec![3usize, 3, 3, 4].into_iter()),
2142 (vec![4], 1, 1)
2143 );
2144 assert_eq!(
2145 antimodes(vec![4usize, 3, 3, 3].into_iter()),
2146 (vec![4], 1, 1)
2147 );
2148 assert_eq!(
2149 antimodes(vec![1usize, 1, 2, 2].into_iter()),
2150 (vec![1, 2], 2, 2)
2151 );
2152 let vec: Vec<u32> = vec![];
2153 assert_eq!(antimodes(vec.into_iter()), (vec![], 0, 0));
2154 }
2155
2156 #[test]
2157 fn antimodes_floats() {
2158 assert_eq!(
2159 antimodes(vec![3_f64, 5.0, 7.0, 9.0].into_iter()),
2160 (vec![3.0, 5.0, 7.0, 9.0], 4, 1)
2161 );
2162 assert_eq!(
2163 antimodes(vec![3_f64, 3.0, 3.0, 3.0].into_iter()),
2164 (vec![], 0, 0)
2165 );
2166 assert_eq!(
2167 antimodes(vec![3_f64, 3.0, 4.0, 4.0].into_iter()),
2168 (vec![3.0, 4.0], 2, 2)
2169 );
2170 assert_eq!(
2171 antimodes(vec![1_f64, 1.0, 2.0, 3.0, 3.0].into_iter()),
2172 (vec![2.0], 1, 1)
2173 );
2174 }
2175
2176 #[test]
2177 fn test_custom_percentiles() {
2178 let mut unsorted: Unsorted<i32> = Unsorted::new();
2180 unsorted.extend(1..=11); let result = unsorted.custom_percentiles(&[25, 50, 75]).unwrap();
2183 assert_eq!(result, vec![3, 6, 9]);
2184
2185 let mut str_data = Unsorted::new();
2187 str_data.extend(vec!["a", "b", "c", "d", "e"]);
2188 let result = str_data.custom_percentiles(&[20, 40, 60, 80]).unwrap();
2189 assert_eq!(result, vec!["a", "b", "c", "d"]);
2190
2191 let mut char_data = Unsorted::new();
2193 char_data.extend('a'..='e');
2194 let result = char_data.custom_percentiles(&[25, 50, 75]).unwrap();
2195 assert_eq!(result, vec!['b', 'c', 'd']);
2196
2197 let mut float_data = Unsorted::new();
2199 float_data.extend(vec![1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9]);
2200 let result = float_data
2201 .custom_percentiles(&[10, 30, 50, 70, 90])
2202 .unwrap();
2203 assert_eq!(result, vec![1.1, 3.3, 5.5, 7.7, 9.9]);
2204
2205 let result = float_data.custom_percentiles(&[]).unwrap();
2207 assert_eq!(result, Vec::<f64>::new());
2208
2209 let result = float_data.custom_percentiles(&[50, 50, 50]).unwrap();
2211 assert_eq!(result, vec![5.5]);
2212
2213 let result = float_data.custom_percentiles(&[0, 100]).unwrap();
2215 assert_eq!(result, vec![1.1, 9.9]);
2216
2217 let result = float_data.custom_percentiles(&[75, 25, 50]).unwrap();
2219 assert_eq!(result, vec![3.3, 5.5, 7.7]); let mut single = Unsorted::new();
2223 single.add(42);
2224 let result = single.custom_percentiles(&[0, 50, 100]).unwrap();
2225 assert_eq!(result, vec![42, 42, 42]);
2226 }
2227
2228 #[test]
2229 fn quartiles_stream() {
2230 assert_eq!(
2231 quartiles(vec![3usize, 5, 7].into_iter()),
2232 Some((3., 5., 7.))
2233 );
2234 assert_eq!(
2235 quartiles(vec![3usize, 5, 7, 9].into_iter()),
2236 Some((4., 6., 8.))
2237 );
2238 assert_eq!(
2239 quartiles(vec![1usize, 2, 7, 11].into_iter()),
2240 Some((1.5, 4.5, 9.))
2241 );
2242 assert_eq!(
2243 quartiles(vec![3usize, 5, 7, 9, 12].into_iter()),
2244 Some((4., 7., 10.5))
2245 );
2246 assert_eq!(
2247 quartiles(vec![2usize, 2, 3, 8, 10].into_iter()),
2248 Some((2., 3., 9.))
2249 );
2250 assert_eq!(
2251 quartiles(vec![3usize, 5, 7, 9, 12, 20].into_iter()),
2252 Some((5., 8., 12.))
2253 );
2254 assert_eq!(
2255 quartiles(vec![0usize, 2, 4, 8, 10, 11].into_iter()),
2256 Some((2., 6., 10.))
2257 );
2258 assert_eq!(
2259 quartiles(vec![3usize, 5, 7, 9, 12, 20, 21].into_iter()),
2260 Some((5., 9., 20.))
2261 );
2262 assert_eq!(
2263 quartiles(vec![1usize, 5, 6, 6, 7, 10, 19].into_iter()),
2264 Some((5., 6., 10.))
2265 );
2266 }
2267
2268 #[test]
2269 fn quartiles_floats() {
2270 assert_eq!(
2271 quartiles(vec![3_f64, 5., 7.].into_iter()),
2272 Some((3., 5., 7.))
2273 );
2274 assert_eq!(
2275 quartiles(vec![3_f64, 5., 7., 9.].into_iter()),
2276 Some((4., 6., 8.))
2277 );
2278 assert_eq!(
2279 quartiles(vec![3_f64, 5., 7., 9., 12.].into_iter()),
2280 Some((4., 7., 10.5))
2281 );
2282 assert_eq!(
2283 quartiles(vec![3_f64, 5., 7., 9., 12., 20.].into_iter()),
2284 Some((5., 8., 12.))
2285 );
2286 assert_eq!(
2287 quartiles(vec![3_f64, 5., 7., 9., 12., 20., 21.].into_iter()),
2288 Some((5., 9., 20.))
2289 );
2290 }
2291
2292 #[test]
2293 fn test_quartiles_zero_copy_small() {
2294 let unsorted: Unsorted<i32> = Unsorted::new();
2296 assert_eq!(unsorted.quartiles_zero_copy(), None);
2297
2298 let mut unsorted = Unsorted::new();
2299 unsorted.extend(vec![1, 2]);
2300 assert_eq!(unsorted.quartiles_zero_copy(), None);
2301
2302 let mut unsorted = Unsorted::new();
2303 unsorted.extend(vec![1, 2, 3]);
2304 assert_eq!(unsorted.quartiles_zero_copy(), Some((1.0, 2.0, 3.0)));
2305
2306 let mut unsorted = Unsorted::new();
2308 unsorted.extend(vec![3, 5, 7, 9]);
2309 assert_eq!(unsorted.quartiles_zero_copy(), Some((4.0, 6.0, 8.0)));
2310 }
2311
2312 #[test]
2313 fn gini_empty() {
2314 let mut unsorted: Unsorted<i32> = Unsorted::new();
2315 assert_eq!(unsorted.gini(None), None);
2316 let empty_vec: Vec<i32> = vec![];
2317 assert_eq!(gini(empty_vec.into_iter(), None), None);
2318 }
2319
2320 #[test]
2321 fn gini_single_element() {
2322 let mut unsorted = Unsorted::new();
2323 unsorted.add(5);
2324 assert_eq!(unsorted.gini(None), Some(0.0));
2325 assert_eq!(gini(vec![5].into_iter(), None), Some(0.0));
2326 }
2327
2328 #[test]
2329 fn gini_perfect_equality() {
2330 let mut unsorted = Unsorted::new();
2332 unsorted.extend(vec![10, 10, 10, 10, 10]);
2333 let result = unsorted.gini(None).unwrap();
2334 assert!((result - 0.0).abs() < 1e-10, "Expected 0.0, got {}", result);
2335
2336 assert!((gini(vec![10, 10, 10, 10, 10].into_iter(), None).unwrap() - 0.0).abs() < 1e-10);
2337 }
2338
2339 #[test]
2340 fn gini_perfect_inequality() {
2341 let mut unsorted = Unsorted::new();
2344 unsorted.extend(vec![0, 0, 0, 0, 100]);
2345 let result = unsorted.gini(None).unwrap();
2346 assert!((result - 0.8).abs() < 1e-10, "Expected 0.8, got {}", result);
2349 }
2350
2351 #[test]
2352 fn gini_stream() {
2353 let result = gini(vec![1usize, 2, 3, 4, 5].into_iter(), None).unwrap();
2360 let expected = (2.0 * 55.0) / (5.0 * 15.0) - 6.0 / 5.0;
2361 assert!(
2362 (result - expected).abs() < 1e-10,
2363 "Expected {}, got {}",
2364 expected,
2365 result
2366 );
2367 }
2368
2369 #[test]
2370 fn gini_floats() {
2371 let mut unsorted = Unsorted::new();
2372 unsorted.extend(vec![1.0, 2.0, 3.0, 4.0, 5.0]);
2373 let result = unsorted.gini(None).unwrap();
2374 let expected = (2.0 * 55.0) / (5.0 * 15.0) - 6.0 / 5.0;
2375 assert!((result - expected).abs() < 1e-10);
2376
2377 assert!(
2378 (gini(vec![1.0f64, 2.0, 3.0, 4.0, 5.0].into_iter(), None).unwrap() - expected).abs()
2379 < 1e-10
2380 );
2381 }
2382
2383 #[test]
2384 fn gini_all_zeros() {
2385 let mut unsorted = Unsorted::new();
2387 unsorted.extend(vec![0, 0, 0, 0]);
2388 assert_eq!(unsorted.gini(None), None);
2389 assert_eq!(gini(vec![0, 0, 0, 0].into_iter(), None), None);
2390 }
2391
2392 #[test]
2393 fn gini_negative_values() {
2394 let mut unsorted = Unsorted::new();
2396 unsorted.extend(vec![-5, -3, -1, 1, 3, 5]);
2397 let result = unsorted.gini(None);
2398 assert_eq!(result, None);
2400
2401 let mut unsorted = Unsorted::new();
2403 unsorted.extend(vec![-2, -1, 0, 1, 2]);
2404 let result = unsorted.gini(None);
2405 assert_eq!(result, None);
2407
2408 let mut unsorted = Unsorted::new();
2411 unsorted.extend(vec![-1, 0, 1, 2, 3]);
2412 let result = unsorted.gini(None);
2413 assert_eq!(result, None);
2414 }
2415
2416 #[test]
2417 fn gini_known_cases() {
2418 let mut unsorted = Unsorted::new();
2420 unsorted.extend(vec![1, 1, 1, 1, 1]);
2421 let result = unsorted.gini(None).unwrap();
2422 assert!((result - 0.0).abs() < 1e-10);
2423
2424 let mut unsorted = Unsorted::new();
2426 unsorted.extend(vec![0, 0, 0, 0, 1]);
2427 let result = unsorted.gini(None).unwrap();
2428 assert!((result - 0.8).abs() < 1e-10);
2430
2431 let mut unsorted = Unsorted::new();
2433 unsorted.extend(vec![1, 2, 3]);
2434 let result = unsorted.gini(None).unwrap();
2435 let expected = (2.0 * 14.0) / (3.0 * 6.0) - 4.0 / 3.0;
2438 assert!((result - expected).abs() < 1e-10);
2439 }
2440
2441 #[test]
2442 fn gini_precalc_sum() {
2443 let mut unsorted = Unsorted::new();
2445 unsorted.extend(vec![1, 2, 3, 4, 5]);
2446 let precalc_sum = Some(15.0);
2447 let result = unsorted.gini(precalc_sum).unwrap();
2448 let expected = (2.0 * 55.0) / (5.0 * 15.0) - 6.0 / 5.0;
2449 assert!((result - expected).abs() < 1e-10);
2450
2451 let mut unsorted2 = Unsorted::new();
2453 unsorted2.extend(vec![1, 2, 3, 4, 5]);
2454 let result2 = unsorted2.gini(None).unwrap();
2455 assert!((result - result2).abs() < 1e-10);
2456 }
2457
2458 #[test]
2459 fn gini_large_dataset() {
2460 let data: Vec<i32> = (1..=1000).collect();
2462 let result = gini(data.iter().copied(), None);
2463 assert!(result.is_some());
2464 let gini_val = result.unwrap();
2465 assert!(gini_val > 0.0 && gini_val < 0.5);
2467 }
2468
2469 #[test]
2470 fn gini_unsorted_vs_sorted() {
2471 let mut unsorted1 = Unsorted::new();
2473 unsorted1.extend(vec![5, 2, 8, 1, 9, 3, 7, 4, 6]);
2474 let result1 = unsorted1.gini(None).unwrap();
2475
2476 let mut unsorted2 = Unsorted::new();
2477 unsorted2.extend(vec![1, 2, 3, 4, 5, 6, 7, 8, 9]);
2478 let result2 = unsorted2.gini(None).unwrap();
2479
2480 assert!((result1 - result2).abs() < 1e-10);
2481 }
2482
2483 #[test]
2484 fn gini_small_values() {
2485 let mut unsorted = Unsorted::new();
2487 unsorted.extend(vec![0.001, 0.002, 0.003, 0.004, 0.005]);
2488 let result = unsorted.gini(None);
2489 assert!(result.is_some());
2490 let expected = (2.0 * 55.0) / (5.0 * 15.0) - 6.0 / 5.0;
2492 assert!((result.unwrap() - expected).abs() < 1e-10);
2493 }
2494
2495 #[test]
2496 fn gini_large_values() {
2497 let mut unsorted = Unsorted::new();
2499 unsorted.extend(vec![1000, 2000, 3000, 4000, 5000]);
2500 let result = unsorted.gini(None);
2501 assert!(result.is_some());
2502 let expected = (2.0 * 55.0) / (5.0 * 15.0) - 6.0 / 5.0;
2504 assert!((result.unwrap() - expected).abs() < 1e-10);
2505 }
2506
2507 #[test]
2508 fn gini_two_elements() {
2509 let mut unsorted = Unsorted::new();
2511 unsorted.extend(vec![1, 2]);
2512 let result = unsorted.gini(None).unwrap();
2513 let expected = (2.0 * 5.0) / (2.0 * 3.0) - 3.0 / 2.0;
2516 assert!((result - expected).abs() < 1e-10);
2517 }
2518
2519 #[test]
2520 fn gini_precalc_sum_zero() {
2521 let mut unsorted = Unsorted::new();
2523 unsorted.extend(vec![1, 2, 3, 4, 5]);
2524 let result = unsorted.gini(Some(0.0));
2525 assert_eq!(result, None);
2526 }
2527
2528 #[test]
2529 fn gini_precalc_sum_negative() {
2530 let mut unsorted = Unsorted::new();
2532 unsorted.extend(vec![-5, -3, -1, 1, 3]);
2533 let result = unsorted.gini(None);
2534 assert_eq!(result, None);
2535
2536 let mut unsorted = Unsorted::new();
2538 unsorted.extend(vec![1, 2, 3]);
2539 let result = unsorted.gini(Some(-5.0));
2540 assert_eq!(result, None);
2541 }
2542
2543 #[test]
2544 fn gini_different_types() {
2545 let mut unsorted_u32 = Unsorted::new();
2547 unsorted_u32.extend(vec![1u32, 2, 3, 4, 5]);
2548 let result_u32 = unsorted_u32.gini(None).unwrap();
2549
2550 let mut unsorted_i64 = Unsorted::new();
2551 unsorted_i64.extend(vec![1i64, 2, 3, 4, 5]);
2552 let result_i64 = unsorted_i64.gini(None).unwrap();
2553
2554 let expected = (2.0 * 55.0) / (5.0 * 15.0) - 6.0 / 5.0;
2555 assert!((result_u32 - expected).abs() < 1e-10);
2556 assert!((result_i64 - expected).abs() < 1e-10);
2557 }
2558
2559 #[test]
2560 fn gini_extreme_inequality() {
2561 let mut unsorted = Unsorted::new();
2563 unsorted.extend(vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 1000]);
2564 let result = unsorted.gini(None).unwrap();
2565 assert!((result - 0.9).abs() < 1e-10);
2568 }
2569
2570 #[test]
2571 fn gini_duplicate_values() {
2572 let mut unsorted = Unsorted::new();
2574 unsorted.extend(vec![1, 1, 1, 5, 5, 5, 10, 10, 10]);
2575 let result = unsorted.gini(None);
2576 assert!(result.is_some());
2577 let gini_val = result.unwrap();
2579 assert!((0.0..=1.0).contains(&gini_val));
2580 }
2581
2582 #[test]
2583 fn kurtosis_empty() {
2584 let mut unsorted: Unsorted<i32> = Unsorted::new();
2585 assert_eq!(unsorted.kurtosis(None, None), None);
2586 let empty_vec: Vec<i32> = vec![];
2587 assert_eq!(kurtosis(empty_vec.into_iter(), None, None), None);
2588 }
2589
2590 #[test]
2591 fn kurtosis_small() {
2592 let mut unsorted = Unsorted::new();
2594 unsorted.extend(vec![1, 2]);
2595 assert_eq!(unsorted.kurtosis(None, None), None);
2596
2597 let mut unsorted = Unsorted::new();
2598 unsorted.extend(vec![1, 2, 3]);
2599 assert_eq!(unsorted.kurtosis(None, None), None);
2600 }
2601
2602 #[test]
2603 fn kurtosis_normal_distribution() {
2604 let mut unsorted = Unsorted::new();
2606 unsorted.extend(vec![1, 2, 3, 4, 5]);
2607 let result = unsorted.kurtosis(None, None);
2608 assert!(result.is_some());
2609 }
2611
2612 #[test]
2613 fn kurtosis_all_same() {
2614 let mut unsorted = Unsorted::new();
2616 unsorted.extend(vec![5, 5, 5, 5]);
2617 assert_eq!(unsorted.kurtosis(None, None), None);
2618 }
2619
2620 #[test]
2621 fn kurtosis_stream() {
2622 let result = kurtosis(vec![1usize, 2, 3, 4, 5].into_iter(), None, None);
2623 assert!(result.is_some());
2624 }
2625
2626 #[test]
2627 fn kurtosis_precalc_mean_variance() {
2628 let mut unsorted = Unsorted::new();
2630 unsorted.extend(vec![1, 2, 3, 4, 5]);
2631
2632 let mean = 3.0f64;
2634 let variance = ((1.0f64 - 3.0).powi(2)
2635 + (2.0f64 - 3.0).powi(2)
2636 + (3.0f64 - 3.0).powi(2)
2637 + (4.0f64 - 3.0).powi(2)
2638 + (5.0f64 - 3.0).powi(2))
2639 / 5.0;
2640
2641 let result = unsorted.kurtosis(Some(mean), Some(variance));
2642 assert!(result.is_some());
2643
2644 let mut unsorted2 = Unsorted::new();
2646 unsorted2.extend(vec![1, 2, 3, 4, 5]);
2647 let result2 = unsorted2.kurtosis(None, None);
2648 assert!((result.unwrap() - result2.unwrap()).abs() < 1e-10);
2649 }
2650
2651 #[test]
2652 fn kurtosis_precalc_mean_only() {
2653 let mut unsorted = Unsorted::new();
2655 unsorted.extend(vec![1, 2, 3, 4, 5]);
2656 let mean = 3.0f64;
2657
2658 let result = unsorted.kurtosis(Some(mean), None);
2659 assert!(result.is_some());
2660
2661 let mut unsorted2 = Unsorted::new();
2663 unsorted2.extend(vec![1, 2, 3, 4, 5]);
2664 let result2 = unsorted2.kurtosis(None, None);
2665 assert!((result.unwrap() - result2.unwrap()).abs() < 1e-10);
2666 }
2667
2668 #[test]
2669 fn kurtosis_precalc_variance_only() {
2670 let mut unsorted = Unsorted::new();
2672 unsorted.extend(vec![1, 2, 3, 4, 5]);
2673 let variance = ((1.0f64 - 3.0).powi(2)
2674 + (2.0f64 - 3.0).powi(2)
2675 + (3.0f64 - 3.0).powi(2)
2676 + (4.0f64 - 3.0).powi(2)
2677 + (5.0f64 - 3.0).powi(2))
2678 / 5.0;
2679
2680 let result = unsorted.kurtosis(None, Some(variance));
2681 assert!(result.is_some());
2682
2683 let mut unsorted2 = Unsorted::new();
2685 unsorted2.extend(vec![1, 2, 3, 4, 5]);
2686 let result2 = unsorted2.kurtosis(None, None);
2687 assert!((result.unwrap() - result2.unwrap()).abs() < 1e-10);
2688 }
2689
2690 #[test]
2691 fn kurtosis_exact_calculation() {
2692 let mut unsorted = Unsorted::new();
2699 unsorted.extend(vec![1, 2, 3, 4]);
2700 let result = unsorted.kurtosis(None, None).unwrap();
2701 assert!(
2702 (result - (-1.2)).abs() < 1e-4,
2703 "expected ~-1.2, got {result}"
2704 );
2705 }
2706
2707 #[test]
2708 fn kurtosis_uniform_distribution() {
2709 let mut unsorted = Unsorted::new();
2711 unsorted.extend(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
2712 let result = unsorted.kurtosis(None, None).unwrap();
2713 assert!(result.is_finite());
2716 }
2717
2718 #[test]
2719 fn kurtosis_uniform_is_negative_excess() {
2720 let data: Vec<f64> = (0..2000).map(|i| i as f64 * 100.0 / 1999.0).collect();
2722 let mut u = Unsorted::new();
2723 u.extend(data);
2724 let k = u.kurtosis(None, None).unwrap();
2725 assert!((k - (-1.2)).abs() < 0.05, "expected ~-1.2, got {k}");
2726 }
2727
2728 #[test]
2729 fn kurtosis_two_point_is_strongly_negative() {
2730 let mut u = Unsorted::new();
2732 u.extend(
2733 (0..2000)
2734 .map(|i| if i % 2 == 0 { 0.0 } else { 100.0 })
2735 .collect::<Vec<f64>>(),
2736 );
2737 let k = u.kurtosis(None, None).unwrap();
2738 assert!((k - (-2.0)).abs() < 0.05, "expected ~-2.0, got {k}");
2739 }
2740
2741 #[test]
2742 fn kurtosis_large_dataset() {
2743 let data: Vec<i32> = (1..=1000).collect();
2745 let result = kurtosis(data.iter().copied(), None, None);
2746 assert!(result.is_some());
2747 let kurt_val = result.unwrap();
2748 assert!(kurt_val.is_finite());
2749 }
2750
2751 #[test]
2752 fn kurtosis_unsorted_vs_sorted() {
2753 let mut unsorted1 = Unsorted::new();
2755 unsorted1.extend(vec![5, 2, 8, 1, 9, 3, 7, 4, 6]);
2756 let result1 = unsorted1.kurtosis(None, None).unwrap();
2757
2758 let mut unsorted2 = Unsorted::new();
2759 unsorted2.extend(vec![1, 2, 3, 4, 5, 6, 7, 8, 9]);
2760 let result2 = unsorted2.kurtosis(None, None).unwrap();
2761
2762 assert!((result1 - result2).abs() < 1e-10);
2763 }
2764
2765 #[test]
2766 fn kurtosis_minimum_size() {
2767 let mut unsorted = Unsorted::new();
2769 unsorted.extend(vec![1, 2, 3, 4]);
2770 let result = unsorted.kurtosis(None, None);
2771 assert!(result.is_some());
2772 assert!(result.unwrap().is_finite());
2773 }
2774
2775 #[test]
2776 fn kurtosis_heavy_tailed() {
2777 let mut unsorted = Unsorted::new();
2779 unsorted.extend(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 100]);
2780 let result = unsorted.kurtosis(None, None).unwrap();
2781 assert!(result.is_finite());
2783 assert!(result > -10.0); }
2786
2787 #[test]
2788 fn kurtosis_light_tailed() {
2789 let mut unsorted = Unsorted::new();
2791 unsorted.extend(vec![10, 11, 12, 13, 14, 15, 16, 17, 18, 19]);
2792 let result = unsorted.kurtosis(None, None).unwrap();
2793 assert!(result.is_finite());
2795 }
2796
2797 #[test]
2798 fn kurtosis_small_variance() {
2799 let mut unsorted = Unsorted::new();
2801 unsorted.extend(vec![10.0, 10.001, 10.002, 10.003, 10.004]);
2802 let result = unsorted.kurtosis(None, None);
2803 assert!(result.is_some());
2805 assert!(result.unwrap().is_finite());
2806 }
2807
2808 #[test]
2809 fn kurtosis_precalc_zero_variance() {
2810 let mut unsorted = Unsorted::new();
2812 unsorted.extend(vec![1, 2, 3, 4, 5]);
2813 let result = unsorted.kurtosis(None, Some(0.0));
2814 assert_eq!(result, None);
2815 }
2816
2817 #[test]
2818 fn kurtosis_precalc_negative_variance() {
2819 let mut unsorted = Unsorted::new();
2821 unsorted.extend(vec![1, 2, 3, 4, 5]);
2822 let result = unsorted.kurtosis(None, Some(-1.0));
2824 let _ = result;
2829 }
2830
2831 #[test]
2832 fn kurtosis_different_types() {
2833 let mut unsorted_u32 = Unsorted::new();
2835 unsorted_u32.extend(vec![1u32, 2, 3, 4, 5]);
2836 let result_u32 = unsorted_u32.kurtosis(None, None).unwrap();
2837
2838 let mut unsorted_i64 = Unsorted::new();
2839 unsorted_i64.extend(vec![1i64, 2, 3, 4, 5]);
2840 let result_i64 = unsorted_i64.kurtosis(None, None).unwrap();
2841
2842 assert!((result_u32 - result_i64).abs() < 1e-10);
2843 }
2844
2845 #[test]
2846 fn kurtosis_floating_point_precision() {
2847 let mut unsorted = Unsorted::new();
2849 unsorted.extend(vec![1.1, 2.2, 3.3, 4.4, 5.5]);
2850 let result = unsorted.kurtosis(None, None);
2851 assert!(result.is_some());
2852 assert!(result.unwrap().is_finite());
2853 }
2854
2855 #[test]
2856 fn kurtosis_negative_values() {
2857 let mut unsorted = Unsorted::new();
2859 unsorted.extend(vec![-5, -3, -1, 1, 3, 5]);
2860 let result = unsorted.kurtosis(None, None);
2861 assert!(result.is_some());
2862 assert!(result.unwrap().is_finite());
2863 }
2864
2865 #[test]
2866 fn kurtosis_mixed_positive_negative() {
2867 let mut unsorted = Unsorted::new();
2869 unsorted.extend(vec![-10, -5, 0, 5, 10]);
2870 let result = unsorted.kurtosis(None, None);
2871 assert!(result.is_some());
2872 assert!(result.unwrap().is_finite());
2873 }
2874
2875 #[test]
2876 fn kurtosis_duplicate_values() {
2877 let mut unsorted = Unsorted::new();
2879 unsorted.extend(vec![1, 1, 2, 2, 3, 3, 4, 4, 5, 5]);
2880 let result = unsorted.kurtosis(None, None);
2881 assert!(result.is_some());
2882 assert!(result.unwrap().is_finite());
2883 }
2884
2885 #[test]
2886 fn kurtosis_precalc_mean_wrong() {
2887 let mut unsorted1 = Unsorted::new();
2889 unsorted1.extend(vec![1, 2, 3, 4, 5]);
2890 let correct_result = unsorted1.kurtosis(None, None).unwrap();
2891
2892 let mut unsorted2 = Unsorted::new();
2893 unsorted2.extend(vec![1, 2, 3, 4, 5]);
2894 let wrong_mean = 10.0; let wrong_result = unsorted2.kurtosis(Some(wrong_mean), None).unwrap();
2896
2897 assert!((correct_result - wrong_result).abs() > 1e-5);
2899 }
2900
2901 #[test]
2902 fn percentile_rank_empty() {
2903 let mut unsorted: Unsorted<i32> = Unsorted::new();
2904 assert_eq!(unsorted.percentile_rank(5), None);
2905 let empty_vec: Vec<i32> = vec![];
2906 assert_eq!(percentile_rank(empty_vec.into_iter(), 5), None);
2907 }
2908
2909 #[test]
2910 fn percentile_rank_basic() {
2911 let mut unsorted = Unsorted::new();
2912 unsorted.extend(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
2913
2914 assert_eq!(unsorted.percentile_rank(0), Some(0.0));
2916
2917 assert_eq!(unsorted.percentile_rank(11), Some(100.0));
2919
2920 let rank = unsorted.percentile_rank(5).unwrap();
2922 assert!((rank - 50.0).abs() < 1.0);
2923
2924 let rank = unsorted.percentile_rank(1).unwrap();
2926 assert!((rank - 10.0).abs() < 1.0);
2927 }
2928
2929 #[test]
2930 fn percentile_rank_duplicates() {
2931 let mut unsorted = Unsorted::new();
2932 unsorted.extend(vec![1, 1, 2, 2, 3, 3, 4, 4, 5, 5]);
2933
2934 let rank = unsorted.percentile_rank(2).unwrap();
2936 assert!((rank - 40.0).abs() < 1.0);
2937 }
2938
2939 #[test]
2940 fn percentile_rank_stream() {
2941 let result = percentile_rank(vec![1usize, 2, 3, 4, 5].into_iter(), 3);
2942 assert_eq!(result, Some(60.0)); }
2944
2945 #[test]
2946 fn percentile_rank_many_ties() {
2947 let mut unsorted = Unsorted::new();
2949 for _ in 0..100 {
2950 unsorted.add(5u32);
2951 }
2952 for _ in 0..100 {
2953 unsorted.add(10u32);
2954 }
2955 let rank = unsorted.percentile_rank(5).unwrap();
2957 assert!((rank - 50.0).abs() < f64::EPSILON);
2958 let mut unsorted2 = Unsorted::new();
2960 for _ in 0..100 {
2961 unsorted2.add(5u32);
2962 }
2963 for _ in 0..100 {
2964 unsorted2.add(10u32);
2965 }
2966 let rank = unsorted2.percentile_rank(10).unwrap();
2967 assert!((rank - 100.0).abs() < f64::EPSILON);
2968 }
2969
2970 #[test]
2971 fn atkinson_empty() {
2972 let mut unsorted: Unsorted<i32> = Unsorted::new();
2973 assert_eq!(unsorted.atkinson(1.0, None, None), None);
2974 let empty_vec: Vec<i32> = vec![];
2975 assert_eq!(atkinson(empty_vec.into_iter(), 1.0, None, None), None);
2976 }
2977
2978 #[test]
2979 fn atkinson_single_element() {
2980 let mut unsorted = Unsorted::new();
2981 unsorted.add(5);
2982 assert_eq!(unsorted.atkinson(1.0, None, None), Some(0.0));
2983 assert_eq!(atkinson(vec![5].into_iter(), 1.0, None, None), Some(0.0));
2984 }
2985
2986 #[test]
2987 fn atkinson_perfect_equality() {
2988 let mut unsorted = Unsorted::new();
2990 unsorted.extend(vec![10, 10, 10, 10, 10]);
2991 let result = unsorted.atkinson(1.0, None, None).unwrap();
2992 assert!((result - 0.0).abs() < 1e-10);
2993 }
2994
2995 #[test]
2996 fn atkinson_epsilon_zero() {
2997 let mut unsorted = Unsorted::new();
2999 unsorted.extend(vec![1, 2, 3, 4, 5]);
3000 let result = unsorted.atkinson(0.0, None, None).unwrap();
3001 assert!((result - 0.0).abs() < 1e-10);
3002 }
3003
3004 #[test]
3005 fn atkinson_epsilon_one() {
3006 let mut unsorted = Unsorted::new();
3008 unsorted.extend(vec![1, 2, 3, 4, 5]);
3009 let result = unsorted.atkinson(1.0, None, None);
3010 assert!(result.is_some());
3011 }
3012
3013 #[test]
3014 fn atkinson_epsilon_one_rejects_nan() {
3015 let mut unsorted = Unsorted::new();
3018 unsorted.extend(vec![1.0_f64, 2.0, f64::NAN, 4.0, 5.0]);
3019 assert_eq!(unsorted.atkinson(1.0, None, None), None);
3020 }
3021
3022 #[test]
3023 fn atkinson_negative_epsilon() {
3024 let mut unsorted = Unsorted::new();
3025 unsorted.extend(vec![1, 2, 3, 4, 5]);
3026 assert_eq!(unsorted.atkinson(-1.0, None, None), None);
3027 }
3028
3029 #[test]
3030 fn atkinson_zero_mean() {
3031 let mut unsorted = Unsorted::new();
3033 unsorted.extend(vec![0, 0, 0, 0]);
3034 assert_eq!(unsorted.atkinson(1.0, None, None), None);
3035 }
3036
3037 #[test]
3038 fn atkinson_stream() {
3039 let result = atkinson(vec![1usize, 2, 3, 4, 5].into_iter(), 1.0, None, None);
3040 assert!(result.is_some());
3041 }
3042
3043 #[test]
3044 fn atkinson_precalc_mean_geometric_sum() {
3045 let mut unsorted = Unsorted::new();
3047 unsorted.extend(vec![1, 2, 3, 4, 5]);
3048
3049 let mean = 3.0f64;
3051 let geometric_sum = 1.0f64.ln() + 2.0f64.ln() + 3.0f64.ln() + 4.0f64.ln() + 5.0f64.ln();
3052
3053 let result = unsorted.atkinson(1.0, Some(mean), Some(geometric_sum));
3054 assert!(result.is_some());
3055
3056 let mut unsorted2 = Unsorted::new();
3058 unsorted2.extend(vec![1, 2, 3, 4, 5]);
3059 let result2 = unsorted2.atkinson(1.0, None, None);
3060 assert!((result.unwrap() - result2.unwrap()).abs() < 1e-10);
3061 }
3062
3063 #[test]
3064 fn atkinson_precalc_mean_only() {
3065 let mut unsorted = Unsorted::new();
3067 unsorted.extend(vec![1, 2, 3, 4, 5]);
3068 let mean = 3.0f64;
3069
3070 let result = unsorted.atkinson(1.0, Some(mean), None);
3071 assert!(result.is_some());
3072
3073 let mut unsorted2 = Unsorted::new();
3075 unsorted2.extend(vec![1, 2, 3, 4, 5]);
3076 let result2 = unsorted2.atkinson(1.0, None, None);
3077 assert!((result.unwrap() - result2.unwrap()).abs() < 1e-10);
3078 }
3079
3080 #[test]
3081 fn atkinson_precalc_geometric_sum_only() {
3082 let mut unsorted = Unsorted::new();
3084 unsorted.extend(vec![1, 2, 3, 4, 5]);
3085 let geometric_sum = 1.0f64.ln() + 2.0f64.ln() + 3.0f64.ln() + 4.0f64.ln() + 5.0f64.ln();
3086
3087 let result = unsorted.atkinson(1.0, None, Some(geometric_sum));
3088 assert!(result.is_some());
3089
3090 let mut unsorted2 = Unsorted::new();
3092 unsorted2.extend(vec![1, 2, 3, 4, 5]);
3093 let result2 = unsorted2.atkinson(1.0, None, None);
3094 assert!((result.unwrap() - result2.unwrap()).abs() < 1e-10);
3095 }
3096
3097 #[test]
3098 fn test_median_with_infinity() {
3099 let mut unsorted = Unsorted::new();
3100 unsorted.extend(vec![1.0f64, 2.0, f64::INFINITY]);
3101 assert_eq!(unsorted.median(), Some(2.0));
3102 }
3103
3104 #[test]
3105 fn test_median_with_neg_infinity() {
3106 let mut unsorted = Unsorted::new();
3107 unsorted.extend(vec![f64::NEG_INFINITY, 1.0f64, 2.0]);
3108 assert_eq!(unsorted.median(), Some(1.0));
3109 }
3110
3111 #[test]
3112 fn test_quartiles_with_infinity() {
3113 let mut unsorted = Unsorted::new();
3114 unsorted.extend(vec![f64::NEG_INFINITY, 1.0, 2.0, 3.0, f64::INFINITY]);
3115 let q = unsorted.quartiles();
3116 assert!(q.is_some());
3118 let (_, q2, _) = q.unwrap();
3119 assert_eq!(q2, 2.0);
3120 }
3121
3122 #[test]
3123 fn test_mode_with_nan() {
3124 let mut unsorted: Unsorted<f64> = Unsorted::new();
3128 unsorted.extend(vec![1.0, f64::NAN, 2.0, 2.0, 3.0]);
3129 let _result = unsorted.mode(); }
3131
3132 #[test]
3133 fn test_gini_with_infinity() {
3134 let mut unsorted = Unsorted::new();
3135 unsorted.extend(vec![1.0f64, 2.0, f64::INFINITY]);
3136 let g = unsorted.gini(None);
3137 assert!(g.unwrap().is_nan());
3141 }
3142
3143 #[test]
3144 fn test_cardinality_with_infinity() {
3145 let mut unsorted = Unsorted::new();
3146 unsorted.extend(vec![1.0f64, f64::INFINITY, f64::NEG_INFINITY, 1.0]);
3147 assert_eq!(unsorted.cardinality(false, 10_000), 3);
3148 }
3149}
3150
3151#[cfg(test)]
3152mod bench {
3153 use super::*;
3154 use std::time::Instant;
3155
3156 #[test]
3157 #[ignore] fn comprehensive_quartiles_benchmark() {
3159 let data_sizes = vec![
3161 1_000, 10_000, 100_000, 500_000, 1_000_000, 2_000_000, 5_000_000, 10_000_000,
3162 ];
3163
3164 println!("=== COMPREHENSIVE QUARTILES BENCHMARK ===\n");
3165
3166 for size in data_sizes {
3167 println!("--- Testing with {} elements ---", size);
3168
3169 let test_patterns = vec![
3171 ("Random", generate_random_data(size)),
3172 ("Reverse Sorted", {
3173 let mut v = Vec::with_capacity(size);
3174 for x in (0..size).rev() {
3175 v.push(x as i32);
3176 }
3177 v
3178 }),
3179 ("Already Sorted", {
3180 let mut v = Vec::with_capacity(size);
3181 for x in 0..size {
3182 v.push(x as i32);
3183 }
3184 v
3185 }),
3186 ("Many Duplicates", {
3187 let mut v = Vec::with_capacity(size);
3189 let chunk_size = size / 100;
3190 for i in 0..100 {
3191 v.extend(std::iter::repeat_n(i, chunk_size));
3192 }
3193 v.extend(std::iter::repeat_n(0, size - v.len()));
3195 v
3196 }),
3197 ];
3198
3199 for (pattern_name, test_data) in test_patterns {
3200 println!("\n Pattern: {}", pattern_name);
3201
3202 let mut unsorted1 = Unsorted::new();
3204 unsorted1.extend(test_data.clone());
3205
3206 let start = Instant::now();
3207 let result_sorted = unsorted1.quartiles();
3208 let sorted_time = start.elapsed();
3209
3210 let mut unsorted2 = Unsorted::new();
3212 unsorted2.extend(test_data.clone());
3213
3214 let start = Instant::now();
3215 let result_selection = unsorted2.quartiles_with_selection();
3216 let selection_time = start.elapsed();
3217
3218 let mut unsorted3 = Unsorted::new();
3220 unsorted3.extend(test_data);
3221
3222 let start = Instant::now();
3223 let result_zero_copy = unsorted3.quartiles_zero_copy();
3224 let zero_copy_time = start.elapsed();
3225
3226 assert_eq!(result_sorted, result_selection);
3228 assert_eq!(result_sorted, result_zero_copy);
3229
3230 let selection_speedup =
3231 sorted_time.as_nanos() as f64 / selection_time.as_nanos() as f64;
3232 let zero_copy_speedup =
3233 sorted_time.as_nanos() as f64 / zero_copy_time.as_nanos() as f64;
3234
3235 println!(" Sorting: {:>12?}", sorted_time);
3236 println!(
3237 " Selection: {:>12?} (speedup: {:.2}x)",
3238 selection_time, selection_speedup
3239 );
3240 println!(
3241 " Zero-copy: {:>12?} (speedup: {:.2}x)",
3242 zero_copy_time, zero_copy_speedup
3243 );
3244
3245 let best_algorithm =
3246 if zero_copy_speedup > 1.0 && zero_copy_speedup >= selection_speedup {
3247 "ZERO-COPY"
3248 } else if selection_speedup > 1.0 {
3249 "SELECTION"
3250 } else {
3251 "SORTING"
3252 };
3253 println!(" Best: {}", best_algorithm);
3254 }
3255
3256 println!(); }
3258 }
3259
3260 fn generate_random_data(size: usize) -> Vec<i32> {
3262 let mut rng = 1234567u64;
3264 let mut vec = Vec::with_capacity(size);
3265 for _ in 0..size {
3266 rng = rng.wrapping_mul(1103515245).wrapping_add(12345);
3267 vec.push((rng >> 16) as i32);
3268 }
3269 vec
3270 }
3271
3272 #[test]
3273 #[ignore] fn find_selection_threshold() {
3275 println!("=== FINDING SELECTION ALGORITHM THRESHOLD ===\n");
3276
3277 let mut found_threshold = None;
3279 let test_sizes = vec![
3280 1_000_000, 2_000_000, 3_000_000, 4_000_000, 5_000_000, 7_500_000, 10_000_000,
3281 15_000_000, 20_000_000, 25_000_000, 30_000_000,
3282 ];
3283
3284 for size in test_sizes {
3285 println!("Testing size: {}", size);
3286
3287 let test_data = generate_random_data(size);
3289
3290 let iterations = 3;
3292 let mut sorting_total = 0u128;
3293 let mut selection_total = 0u128;
3294 let mut zero_copy_total = 0u128;
3295
3296 for i in 0..iterations {
3297 println!(" Iteration {}/{}", i + 1, iterations);
3298
3299 let mut unsorted1 = Unsorted::new();
3301 unsorted1.extend(test_data.clone());
3302
3303 let start = Instant::now();
3304 let _result_sorted = unsorted1.quartiles();
3305 sorting_total += start.elapsed().as_nanos();
3306
3307 let mut unsorted2 = Unsorted::new();
3309 unsorted2.extend(test_data.clone());
3310
3311 let start = Instant::now();
3312 let _result_selection = unsorted2.quartiles_with_selection();
3313 selection_total += start.elapsed().as_nanos();
3314
3315 let mut unsorted3 = Unsorted::new();
3317 unsorted3.extend(test_data.clone());
3318
3319 let start = Instant::now();
3320 let _result_zero_copy = unsorted3.quartiles_zero_copy();
3321 zero_copy_total += start.elapsed().as_nanos();
3322 }
3323
3324 let avg_sorting = sorting_total / iterations as u128;
3325 let avg_selection = selection_total / iterations as u128;
3326 let avg_zero_copy = zero_copy_total / iterations as u128;
3327 let selection_speedup = avg_sorting as f64 / avg_selection as f64;
3328 let zero_copy_speedup = avg_sorting as f64 / avg_zero_copy as f64;
3329
3330 println!(
3331 " Average sorting: {:>12.2}ms",
3332 avg_sorting as f64 / 1_000_000.0
3333 );
3334 println!(
3335 " Average selection: {:>12.2}ms (speedup: {:.2}x)",
3336 avg_selection as f64 / 1_000_000.0,
3337 selection_speedup
3338 );
3339 println!(
3340 " Average zero-copy: {:>12.2}ms (speedup: {:.2}x)",
3341 avg_zero_copy as f64 / 1_000_000.0,
3342 zero_copy_speedup
3343 );
3344
3345 if (selection_speedup > 1.0 || zero_copy_speedup > 1.0) && found_threshold.is_none() {
3346 found_threshold = Some(size);
3347 let best_method = if zero_copy_speedup > selection_speedup {
3348 "Zero-copy"
3349 } else {
3350 "Selection"
3351 };
3352 println!(
3353 " *** THRESHOLD FOUND: {} becomes faster at {} elements ***",
3354 best_method, size
3355 );
3356 }
3357
3358 println!();
3359 }
3360
3361 match found_threshold {
3362 Some(threshold) => println!(
3363 "🎯 Selection algorithm becomes faster at approximately {} elements",
3364 threshold
3365 ),
3366 None => println!("❌ Selection algorithm did not become faster in the tested range"),
3367 }
3368 }
3369
3370 #[test]
3371 #[ignore] fn benchmark_different_data_types() {
3373 println!("=== BENCHMARKING DIFFERENT DATA TYPES ===\n");
3374
3375 let size = 5_000_000; println!("Testing with f64 data:");
3379 let float_data: Vec<f64> = generate_random_data(size)
3380 .into_iter()
3381 .map(|x| x as f64 / 1000.0)
3382 .collect();
3383
3384 let mut unsorted1 = Unsorted::new();
3385 unsorted1.extend(float_data.clone());
3386 let start = Instant::now();
3387 let _result = unsorted1.quartiles();
3388 let sorting_time = start.elapsed();
3389
3390 let mut unsorted2 = Unsorted::new();
3391 unsorted2.extend(float_data.clone());
3392 let start = Instant::now();
3393 let _result = unsorted2.quartiles_with_selection();
3394 let selection_time = start.elapsed();
3395
3396 let mut unsorted3 = Unsorted::new();
3397 unsorted3.extend(float_data);
3398 let start = Instant::now();
3399 let _result = unsorted3.quartiles_zero_copy();
3400 let zero_copy_time = start.elapsed();
3401
3402 println!(" Sorting: {:?}", sorting_time);
3403 println!(" Selection: {:?}", selection_time);
3404 println!(" Zero-copy: {:?}", zero_copy_time);
3405 println!(
3406 " Selection Speedup: {:.2}x",
3407 sorting_time.as_nanos() as f64 / selection_time.as_nanos() as f64
3408 );
3409 println!(
3410 " Zero-copy Speedup: {:.2}x\n",
3411 sorting_time.as_nanos() as f64 / zero_copy_time.as_nanos() as f64
3412 );
3413
3414 println!("Testing with i64 data:");
3416 let int64_data: Vec<i64> = generate_random_data(size)
3417 .into_iter()
3418 .map(|x| x as i64 * 1000)
3419 .collect();
3420
3421 let mut unsorted1 = Unsorted::new();
3422 unsorted1.extend(int64_data.clone());
3423 let start = Instant::now();
3424 let _result = unsorted1.quartiles();
3425 let sorting_time = start.elapsed();
3426
3427 let mut unsorted2 = Unsorted::new();
3428 unsorted2.extend(int64_data.clone());
3429 let start = Instant::now();
3430 let _result = unsorted2.quartiles_with_selection();
3431 let selection_time = start.elapsed();
3432
3433 let mut unsorted3 = Unsorted::new();
3434 unsorted3.extend(int64_data);
3435 let start = Instant::now();
3436 let _result = unsorted3.quartiles_zero_copy();
3437 let zero_copy_time = start.elapsed();
3438
3439 println!(" Sorting: {:?}", sorting_time);
3440 println!(" Selection: {:?}", selection_time);
3441 println!(" Zero-copy: {:?}", zero_copy_time);
3442 println!(
3443 " Selection Speedup: {:.2}x",
3444 sorting_time.as_nanos() as f64 / selection_time.as_nanos() as f64
3445 );
3446 println!(
3447 " Zero-copy Speedup: {:.2}x",
3448 sorting_time.as_nanos() as f64 / zero_copy_time.as_nanos() as f64
3449 );
3450 }
3451}