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
10const PARALLEL_THRESHOLD: usize = 10_000;
14
15#[inline]
19pub fn median<I>(it: I) -> Option<f64>
20where
21 I: Iterator,
22 <I as Iterator>::Item: PartialOrd + ToPrimitive + Send,
23{
24 it.collect::<Unsorted<_>>().median()
25}
26
27#[inline]
29pub fn mad<I>(it: I, precalc_median: Option<f64>) -> Option<f64>
30where
31 I: Iterator,
32 <I as Iterator>::Item: PartialOrd + ToPrimitive + Send + Sync,
33{
34 it.collect::<Unsorted<_>>().mad(precalc_median)
35}
36
37#[inline]
41pub fn quartiles<I>(it: I) -> Option<(f64, f64, f64)>
42where
43 I: Iterator,
44 <I as Iterator>::Item: PartialOrd + ToPrimitive + Send,
45{
46 it.collect::<Unsorted<_>>().quartiles()
47}
48
49#[inline]
55pub fn mode<T, I>(it: I) -> Option<T>
56where
57 T: PartialOrd + Clone + Send,
58 I: Iterator<Item = T>,
59{
60 it.collect::<Unsorted<T>>().mode()
61}
62
63#[inline]
81pub fn modes<T, I>(it: I) -> (Vec<T>, usize, u32)
82where
83 T: PartialOrd + Clone + Send,
84 I: Iterator<Item = T>,
85{
86 it.collect::<Unsorted<T>>().modes()
87}
88
89#[inline]
112pub fn antimodes<T, I>(it: I) -> (Vec<T>, usize, u32)
113where
114 T: PartialOrd + Clone + Send,
115 I: Iterator<Item = T>,
116{
117 let (antimodes_result, antimodes_count, antimodes_occurrences) =
118 it.collect::<Unsorted<T>>().antimodes();
119 (antimodes_result, antimodes_count, antimodes_occurrences)
120}
121
122#[inline]
129pub fn gini<I>(it: I, precalc_sum: Option<f64>) -> Option<f64>
130where
131 I: Iterator,
132 <I as Iterator>::Item: PartialOrd + ToPrimitive + Send + Sync,
133{
134 it.collect::<Unsorted<_>>().gini(precalc_sum)
135}
136
137#[inline]
145pub fn kurtosis<I>(it: I, precalc_mean: Option<f64>, precalc_variance: Option<f64>) -> Option<f64>
146where
147 I: Iterator,
148 <I as Iterator>::Item: PartialOrd + ToPrimitive + Send + Sync,
149{
150 it.collect::<Unsorted<_>>()
151 .kurtosis(precalc_mean, precalc_variance)
152}
153
154#[inline]
161pub fn percentile_rank<I, V>(it: I, value: V) -> Option<f64>
162where
163 I: Iterator,
164 <I as Iterator>::Item: PartialOrd + ToPrimitive + Send + Sync,
165 V: PartialOrd + ToPrimitive,
166{
167 it.collect::<Unsorted<_>>().percentile_rank(value)
168}
169
170#[inline]
178pub fn atkinson<I>(
179 it: I,
180 epsilon: f64,
181 precalc_mean: Option<f64>,
182 precalc_geometric_sum: Option<f64>,
183) -> Option<f64>
184where
185 I: Iterator,
186 <I as Iterator>::Item: PartialOrd + ToPrimitive + Send + Sync,
187{
188 it.collect::<Unsorted<_>>()
189 .atkinson(epsilon, precalc_mean, precalc_geometric_sum)
190}
191
192fn median_on_sorted<T>(data: &[T]) -> Option<f64>
193where
194 T: PartialOrd + ToPrimitive,
195{
196 Some(match data.len() {
197 0 => {
199 core::hint::cold_path();
200 return None;
201 }
202 1 => data.first()?.to_f64()?,
204 len if len.is_multiple_of(2) => {
206 let idx = len / 2;
207 let v1 = unsafe { data.get_unchecked(idx - 1) }.to_f64()?;
210 let v2 = unsafe { data.get_unchecked(idx) }.to_f64()?;
211 f64::midpoint(v1, v2)
212 }
213 len => unsafe { data.get_unchecked(len / 2) }.to_f64()?,
216 })
217}
218
219fn mad_on_sorted<T>(data: &[T], precalc_median: Option<f64>) -> Option<f64>
220where
221 T: Sync + PartialOrd + ToPrimitive,
222{
223 if data.is_empty() {
224 core::hint::cold_path();
225 return None;
226 }
227 let median_obs =
232 precalc_median.unwrap_or_else(|| unsafe { median_on_sorted(data).unwrap_unchecked() });
233
234 let mut abs_diff_vec: Vec<f64> = if data.len() < PARALLEL_THRESHOLD {
236 data.iter()
239 .map(|x| (median_obs - unsafe { x.to_f64().unwrap_unchecked() }).abs())
241 .collect()
242 } else {
243 data.par_iter()
245 .map(|x| (median_obs - unsafe { x.to_f64().unwrap_unchecked() }).abs())
247 .collect()
248 };
249
250 let len = abs_diff_vec.len();
252 let mid = len / 2;
253 let cmp = |a: &f64, b: &f64| a.total_cmp(b);
254
255 abs_diff_vec.select_nth_unstable_by(mid, cmp);
256
257 if len.is_multiple_of(2) {
258 let right = abs_diff_vec[mid];
260 let left = abs_diff_vec[..mid]
263 .iter()
264 .max_by(|a, b| cmp(a, b))
265 .copied()?;
266 Some(f64::midpoint(left, right))
267 } else {
268 Some(abs_diff_vec[mid])
269 }
270}
271
272fn gini_on_sorted<T>(data: &[Partial<T>], precalc_sum: Option<f64>) -> Option<f64>
273where
274 T: Sync + PartialOrd + ToPrimitive,
275{
276 let len = data.len();
277
278 if len == 0 {
280 core::hint::cold_path();
281 return None;
282 }
283
284 if len == 1 {
286 core::hint::cold_path();
287 return Some(0.0);
288 }
289
290 let first_val = unsafe { data.get_unchecked(0).0.to_f64().unwrap_unchecked() };
294 if first_val < 0.0 {
295 core::hint::cold_path();
296 return None;
297 }
298
299 let (sum, weighted_sum) = if let Some(precalc) = precalc_sum {
304 if precalc < 0.0 {
305 core::hint::cold_path();
306 return None;
307 }
308 let weighted_sum = if len < PARALLEL_THRESHOLD {
310 let mut weighted_sum = 0.0;
311 for (i, x) in data.iter().enumerate() {
312 let val = unsafe { x.0.to_f64().unwrap_unchecked() };
314 weighted_sum = ((i + 1) as f64).mul_add(val, weighted_sum);
315 }
316 weighted_sum
317 } else {
318 data.par_iter()
319 .enumerate()
320 .map(|(i, x)| {
321 let val = unsafe { x.0.to_f64().unwrap_unchecked() };
323 (i + 1) as f64 * val
324 })
325 .sum()
326 };
327 (precalc, weighted_sum)
328 } else if len < PARALLEL_THRESHOLD {
329 let mut sum = 0.0;
331 let mut weighted_sum = 0.0;
332 for (i, x) in data.iter().enumerate() {
333 let val = unsafe { x.0.to_f64().unwrap_unchecked() };
335 sum += val;
336 weighted_sum = ((i + 1) as f64).mul_add(val, weighted_sum);
337 }
338 (sum, weighted_sum)
339 } else {
340 data.par_iter()
342 .enumerate()
343 .fold(
344 || (0.0_f64, 0.0_f64),
345 |acc, (i, x)| {
346 let val = unsafe { x.0.to_f64().unwrap_unchecked() };
348 (acc.0 + val, ((i + 1) as f64).mul_add(val, acc.1))
349 },
350 )
351 .reduce(|| (0.0, 0.0), |a, b| (a.0 + b.0, a.1 + b.1))
352 };
353
354 if sum == 0.0 {
356 core::hint::cold_path();
357 return None;
358 }
359
360 let n = len as f64;
364 let gini = 2.0f64.mul_add(weighted_sum / (n * sum), -(n + 1.0) / n);
365
366 Some(gini)
367}
368
369fn kurtosis_on_sorted<T>(
370 data: &[Partial<T>],
371 precalc_mean: Option<f64>,
372 precalc_variance: Option<f64>,
373) -> Option<f64>
374where
375 T: Sync + PartialOrd + ToPrimitive,
376{
377 let len = data.len();
378
379 if len < 4 {
381 core::hint::cold_path();
382 return None;
383 }
384
385 let mean = precalc_mean.unwrap_or_else(|| {
387 let sum: f64 = if len < PARALLEL_THRESHOLD {
388 data.iter()
390 .map(|x| unsafe { x.0.to_f64().unwrap_unchecked() })
392 .sum()
393 } else {
394 data.par_iter()
395 .map(|x| unsafe { x.0.to_f64().unwrap_unchecked() })
397 .sum()
398 };
399 sum / len as f64
400 });
401
402 let (variance_sq, fourth_power_sum) = if let Some(variance) = precalc_variance {
406 if variance < 0.0 {
408 core::hint::cold_path();
409 return None;
410 }
411 let variance_sq = variance * variance;
413
414 let fourth_power_sum = if len < PARALLEL_THRESHOLD {
416 let mut sum = 0.0;
417 for x in data {
418 let val = unsafe { x.0.to_f64().unwrap_unchecked() };
420 let diff = val - mean;
421 let diff_sq = diff * diff;
422 sum = diff_sq.mul_add(diff_sq, sum);
423 }
424 sum
425 } else {
426 data.par_iter()
427 .map(|x| {
428 let val = unsafe { x.0.to_f64().unwrap_unchecked() };
430 let diff = val - mean;
431 let diff_sq = diff * diff;
432 diff_sq * diff_sq
433 })
434 .sum()
435 };
436
437 (variance_sq, fourth_power_sum)
438 } else {
439 let (variance_sum, fourth_power_sum) = if len < PARALLEL_THRESHOLD {
441 let mut variance_sum = 0.0;
442 let mut fourth_power_sum = 0.0;
443
444 for x in data {
445 let val = unsafe { x.0.to_f64().unwrap_unchecked() };
447 let diff = val - mean;
448 let diff_sq = diff * diff;
449 variance_sum += diff_sq;
450 fourth_power_sum = diff_sq.mul_add(diff_sq, fourth_power_sum);
451 }
452
453 (variance_sum, fourth_power_sum)
454 } else {
455 data.par_iter()
457 .fold(
458 || (0.0_f64, 0.0_f64),
459 |acc, x| {
460 let val = unsafe { x.0.to_f64().unwrap_unchecked() };
462 let diff = val - mean;
463 let diff_sq = diff * diff;
464 (acc.0 + diff_sq, diff_sq.mul_add(diff_sq, acc.1))
465 },
466 )
467 .reduce(|| (0.0, 0.0), |a, b| (a.0 + b.0, a.1 + b.1))
468 };
469
470 let variance = variance_sum / len as f64;
471
472 if variance == 0.0 {
474 core::hint::cold_path();
475 return None;
476 }
477
478 let variance_sq = variance * variance;
479 (variance_sq, fourth_power_sum)
480 };
481
482 if variance_sq == 0.0 {
484 core::hint::cold_path();
485 return None;
486 }
487
488 let n = len as f64;
489
490 let adj_denominator = (n - 2.0) * (n - 3.0);
493 let denominator = (n - 1.0) * adj_denominator;
494 let adjustment = 3.0 * (n - 1.0) * (n - 1.0) / adj_denominator;
495 let kurtosis =
496 (n * (n + 1.0) * fourth_power_sum).mul_add(1.0 / (denominator * variance_sq), -adjustment);
497
498 Some(kurtosis)
499}
500
501fn percentile_rank_on_sorted<T, V>(data: &[Partial<T>], value: &V) -> Option<f64>
502where
503 T: PartialOrd + ToPrimitive,
504 V: PartialOrd + ToPrimitive,
505{
506 let len = data.len();
507
508 if len == 0 {
509 core::hint::cold_path();
510 return None;
511 }
512
513 let value_f64 = value.to_f64()?;
514
515 let count_leq = data.binary_search_by(|x| {
518 x.0.to_f64()
519 .unwrap_or(f64::NAN)
520 .partial_cmp(&value_f64)
521 .unwrap_or(std::cmp::Ordering::Less)
522 });
523
524 let count = match count_leq {
525 Ok(idx) => {
526 let upper = data[idx + 1..].partition_point(|x| {
529 x.0.to_f64()
530 .is_some_and(|v| v.total_cmp(&value_f64).is_le())
531 });
532 idx + 1 + upper
533 }
534 Err(idx) => idx, };
536
537 Some((count as f64 / len as f64) * 100.0)
539}
540
541fn atkinson_on_sorted<T>(
542 data: &[Partial<T>],
543 epsilon: f64,
544 precalc_mean: Option<f64>,
545 precalc_geometric_sum: Option<f64>,
546) -> Option<f64>
547where
548 T: Sync + PartialOrd + ToPrimitive,
549{
550 let len = data.len();
551
552 if len == 0 {
554 core::hint::cold_path();
555 return None;
556 }
557
558 if len == 1 {
560 core::hint::cold_path();
561 return Some(0.0);
562 }
563
564 if epsilon < 0.0 {
566 core::hint::cold_path();
567 return None;
568 }
569
570 let epsilon_is_one = (epsilon - 1.0).abs() < 1e-10;
571
572 if epsilon_is_one && precalc_mean.is_none() && precalc_geometric_sum.is_none() {
575 let (sum, ln_sum, any_invalid) = if len < PARALLEL_THRESHOLD {
579 let mut s = 0.0f64;
580 let mut ls = 0.0f64;
581 let mut bad = false;
582 for x in data {
583 let v = unsafe { x.0.to_f64().unwrap_unchecked() };
585 if v.is_nan() || v <= 0.0 {
586 bad = true;
587 } else {
588 s += v;
589 ls += v.ln();
590 }
591 }
592 (s, ls, bad)
593 } else {
594 data.par_iter()
595 .fold(
596 || (0.0f64, 0.0f64, false),
597 |(s, ls, bad), x| {
598 let v = unsafe { x.0.to_f64().unwrap_unchecked() };
600 if v.is_nan() || v <= 0.0 {
601 (s, ls, true)
602 } else {
603 (s + v, ls + v.ln(), bad)
604 }
605 },
606 )
607 .reduce(
608 || (0.0, 0.0, false),
609 |a, b| (a.0 + b.0, a.1 + b.1, a.2 || b.2),
610 )
611 };
612 if any_invalid {
613 core::hint::cold_path();
614 return None;
615 }
616 let mean = sum / len as f64;
617 if mean == 0.0 {
618 core::hint::cold_path();
619 return None;
620 }
621 let geometric_mean = (ln_sum / len as f64).exp();
622 return Some(1.0 - geometric_mean / mean);
623 }
624
625 let mean = precalc_mean.unwrap_or_else(|| {
627 let sum: f64 = if len < PARALLEL_THRESHOLD {
628 data.iter()
630 .map(|x| unsafe { x.0.to_f64().unwrap_unchecked() })
632 .sum()
633 } else {
634 data.par_iter()
635 .map(|x| unsafe { x.0.to_f64().unwrap_unchecked() })
637 .sum()
638 };
639 sum / len as f64
640 });
641
642 if mean == 0.0 {
644 core::hint::cold_path();
645 return None;
646 }
647
648 if epsilon_is_one {
652 let geometric_sum: f64 = if let Some(precalc) = precalc_geometric_sum {
654 precalc
655 } else if len < PARALLEL_THRESHOLD {
656 let mut sum = 0.0;
657 for x in data {
658 let val = unsafe { x.0.to_f64().unwrap_unchecked() };
660 if val <= 0.0 {
661 return None;
663 }
664 sum += val.ln();
665 }
666 sum
667 } else {
668 data.par_iter()
669 .map(|x| {
670 let val = unsafe { x.0.to_f64().unwrap_unchecked() };
672 if val <= 0.0 {
673 return f64::NAN;
674 }
675 val.ln()
676 })
677 .sum()
678 };
679
680 if geometric_sum.is_nan() {
681 core::hint::cold_path();
682 return None;
683 }
684
685 let geometric_mean = (geometric_sum / len as f64).exp();
686 return Some(1.0 - geometric_mean / mean);
687 }
688
689 let exponent = 1.0 - epsilon;
692 let inv_mean = mean.recip();
694
695 let sum_powered: f64 = if len < PARALLEL_THRESHOLD {
696 let mut sum = 0.0;
697 for x in data {
698 let val = unsafe { x.0.to_f64().unwrap_unchecked() };
700 if val < 0.0 {
701 return None;
703 }
704 let ratio = val * inv_mean;
705 sum += ratio.powf(exponent);
706 }
707 sum
708 } else {
709 data.par_iter()
710 .map(|x| {
711 let val = unsafe { x.0.to_f64().unwrap_unchecked() };
713 if val < 0.0 {
714 return f64::NAN;
715 }
716 let ratio = val * inv_mean;
717 ratio.powf(exponent)
718 })
719 .sum()
720 };
721
722 if sum_powered.is_nan() || sum_powered <= 0.0 {
723 core::hint::cold_path();
724 return None;
725 }
726
727 let atkinson = 1.0 - (sum_powered / len as f64).powf(1.0 / exponent);
728 Some(atkinson)
729}
730
731#[cfg(test)]
734fn quickselect<T>(data: &mut [Partial<T>], k: usize) -> Option<&T>
735where
736 T: PartialOrd,
737{
738 if data.is_empty() || k >= data.len() {
739 core::hint::cold_path();
740 return None;
741 }
742
743 let mut left = 0;
744 let mut right = data.len() - 1;
745
746 loop {
747 if left == right {
748 return Some(&data[left].0);
749 }
750
751 let pivot_idx = median_of_three_pivot(data, left, right);
753 let pivot_idx = partition(data, left, right, pivot_idx);
754
755 match k.cmp(&pivot_idx) {
756 std::cmp::Ordering::Equal => return Some(&data[pivot_idx].0),
757 std::cmp::Ordering::Less => right = pivot_idx - 1,
758 std::cmp::Ordering::Greater => left = pivot_idx + 1,
759 }
760 }
761}
762
763#[cfg(test)]
765fn median_of_three_pivot<T>(data: &[Partial<T>], left: usize, right: usize) -> usize
766where
767 T: PartialOrd,
768{
769 let mid = left + (right - left) / 2;
770
771 if data[left] <= data[mid] {
772 if data[mid] <= data[right] {
773 mid
774 } else if data[left] <= data[right] {
775 right
776 } else {
777 left
778 }
779 } else if data[left] <= data[right] {
780 left
781 } else if data[mid] <= data[right] {
782 right
783 } else {
784 mid
785 }
786}
787
788#[cfg(test)]
790fn partition<T>(data: &mut [Partial<T>], left: usize, right: usize, pivot_idx: usize) -> usize
791where
792 T: PartialOrd,
793{
794 data.swap(pivot_idx, right);
796 let mut store_idx = left;
797
798 for i in left..right {
801 if unsafe { data.get_unchecked(i) <= data.get_unchecked(right) } {
804 data.swap(i, store_idx);
805 store_idx += 1;
806 }
807 }
808
809 data.swap(store_idx, right);
811 store_idx
812}
813
814fn quartiles_on_sorted<T>(data: &[Partial<T>]) -> Option<(f64, f64, f64)>
818where
819 T: PartialOrd + ToPrimitive,
820{
821 let len = data.len();
822
823 match len {
825 0..=2 => {
826 core::hint::cold_path();
827 return None;
828 }
829 3 => {
830 return Some(
831 unsafe {
833 (
834 data.get_unchecked(0).0.to_f64()?,
835 data.get_unchecked(1).0.to_f64()?,
836 data.get_unchecked(2).0.to_f64()?,
837 )
838 },
839 );
840 }
841 _ => {}
842 }
843
844 let k = len / 4;
846 let remainder = len % 4;
847
848 unsafe {
851 Some(match remainder {
852 0 => {
853 let q1 = f64::midpoint(
864 data.get_unchecked(k - 1).0.to_f64()?,
865 data.get_unchecked(k).0.to_f64()?,
866 );
867 let q2 = f64::midpoint(
868 data.get_unchecked(2 * k - 1).0.to_f64()?,
869 data.get_unchecked(2 * k).0.to_f64()?,
870 );
871 let q3 = f64::midpoint(
872 data.get_unchecked(3 * k - 1).0.to_f64()?,
873 data.get_unchecked(3 * k).0.to_f64()?,
874 );
875 (q1, q2, q3)
876 }
877 1 => {
878 let q1 = f64::midpoint(
889 data.get_unchecked(k - 1).0.to_f64()?,
890 data.get_unchecked(k).0.to_f64()?,
891 );
892 let q2 = data.get_unchecked(2 * k).0.to_f64()?;
893 let q3 = f64::midpoint(
894 data.get_unchecked(3 * k).0.to_f64()?,
895 data.get_unchecked(3 * k + 1).0.to_f64()?,
896 );
897 (q1, q2, q3)
898 }
899 2 => {
900 let q1 = data.get_unchecked(k).0.to_f64()?;
911 let q2 = f64::midpoint(
912 data.get_unchecked(2 * k).0.to_f64()?,
913 data.get_unchecked(2 * k + 1).0.to_f64()?,
914 );
915 let q3 = data.get_unchecked(3 * k + 1).0.to_f64()?;
916 (q1, q2, q3)
917 }
918 _ => {
919 let q1 = data.get_unchecked(k).0.to_f64()?;
930 let q2 = data.get_unchecked(2 * k + 1).0.to_f64()?;
931 let q3 = data.get_unchecked(3 * k + 2).0.to_f64()?;
932 (q1, q2, q3)
933 }
934 })
935 }
936}
937
938fn quartiles_with_zero_copy_selection<T>(data: &[Partial<T>]) -> Option<(f64, f64, f64)>
947where
948 T: PartialOrd + ToPrimitive,
949{
950 let len = data.len();
951
952 match len {
954 0..=2 => {
955 core::hint::cold_path();
956 return None;
957 }
958 3 => {
959 let mut indices: Vec<usize> = (0..3).collect();
960 let cmp = |a: &usize, b: &usize| {
961 data[*a]
962 .partial_cmp(&data[*b])
963 .unwrap_or(std::cmp::Ordering::Less)
964 };
965 indices.sort_unstable_by(cmp);
966 let min_val = data[indices[0]].0.to_f64()?;
967 let med_val = data[indices[1]].0.to_f64()?;
968 let max_val = data[indices[2]].0.to_f64()?;
969 return Some((min_val, med_val, max_val));
970 }
971 _ => {}
972 }
973
974 let k = len / 4;
975 let remainder = len % 4;
976
977 let mut indices: Vec<usize> = (0..len).collect();
978 let cmp = |a: &usize, b: &usize| {
979 data[*a]
980 .partial_cmp(&data[*b])
981 .unwrap_or(std::cmp::Ordering::Less)
982 };
983
984 let raw_positions: Vec<usize> = match remainder {
989 0 => vec![k - 1, k, 2 * k - 1, 2 * k, 3 * k - 1, 3 * k],
990 1 => vec![k - 1, k, 2 * k, 3 * k, 3 * k + 1],
991 2 => vec![k, 2 * k, 2 * k + 1, 3 * k + 1],
992 _ => vec![k, 2 * k + 1, 3 * k + 2],
993 };
994
995 let mut unique_positions = raw_positions.clone();
996 unique_positions.dedup();
997
998 let mut start = 0;
1000 for &pos in &unique_positions {
1001 indices[start..].select_nth_unstable_by(pos - start, &cmp);
1002 start = pos + 1;
1003 }
1004
1005 let values: Vec<f64> = raw_positions
1007 .iter()
1008 .map(|&pos| data[indices[pos]].0.to_f64())
1009 .collect::<Option<Vec<_>>>()?;
1010
1011 match remainder {
1012 0 => {
1013 let q1 = f64::midpoint(values[0], values[1]);
1014 let q2 = f64::midpoint(values[2], values[3]);
1015 let q3 = f64::midpoint(values[4], values[5]);
1016 Some((q1, q2, q3))
1017 }
1018 1 => {
1019 let q1 = f64::midpoint(values[0], values[1]);
1020 let q2 = values[2];
1021 let q3 = f64::midpoint(values[3], values[4]);
1022 Some((q1, q2, q3))
1023 }
1024 2 => {
1025 let q1 = values[0];
1026 let q2 = f64::midpoint(values[1], values[2]);
1027 let q3 = values[3];
1028 Some((q1, q2, q3))
1029 }
1030 _ => Some((values[0], values[1], values[2])),
1031 }
1032}
1033
1034fn mode_on_sorted<T, I>(it: I) -> Option<T>
1035where
1036 T: PartialOrd,
1037 I: Iterator<Item = T>,
1038{
1039 use std::cmp::Ordering;
1040
1041 let (mut mode, mut next) = (None, None);
1048 let (mut mode_count, mut next_count) = (0usize, 0usize);
1049 for x in it {
1050 if mode.as_ref() == Some(&x) {
1051 mode_count += 1;
1052 } else if next.as_ref() == Some(&x) {
1053 next_count += 1;
1054 } else {
1055 next = Some(x);
1056 next_count = 0;
1057 }
1058
1059 match next_count.cmp(&mode_count) {
1060 Ordering::Greater => {
1061 mode = next;
1062 mode_count = next_count;
1063 next = None;
1064 next_count = 0;
1065 }
1066 Ordering::Equal => {
1067 mode = None;
1068 mode_count = 0;
1069 }
1070 Ordering::Less => {}
1071 }
1072 }
1073 mode
1074}
1075
1076#[allow(clippy::type_complexity)]
1104#[inline]
1105fn modes_and_antimodes_on_sorted_slice<T>(
1106 data: &[Partial<T>],
1107) -> ((Vec<T>, usize, u32), (Vec<T>, usize, u32))
1108where
1109 T: PartialOrd + Clone,
1110{
1111 let size = data.len();
1112
1113 if size == 0 {
1115 core::hint::cold_path();
1116 return ((Vec::new(), 0, 0), (Vec::new(), 0, 0));
1117 }
1118
1119 let sqrt_size = size.isqrt();
1121 let mut runs: Vec<(&T, u32)> = Vec::with_capacity(sqrt_size.clamp(16, 1_000));
1122
1123 let mut current_value = &data[0].0;
1124 let mut current_count = 1;
1125 let mut highest_count = 1;
1126 let mut lowest_count = u32::MAX;
1127
1128 for x in data.iter().skip(1) {
1130 if x.0 == *current_value {
1131 current_count += 1;
1132 highest_count = highest_count.max(current_count);
1133 } else {
1134 runs.push((current_value, current_count));
1135 lowest_count = lowest_count.min(current_count);
1136 current_value = &x.0;
1137 current_count = 1;
1138 }
1139 }
1140 runs.push((current_value, current_count));
1141 lowest_count = lowest_count.min(current_count);
1142
1143 modes_antimodes_from_runs(runs, highest_count, lowest_count)
1144}
1145
1146#[allow(clippy::type_complexity)]
1169#[inline]
1170fn modes_antimodes_from_runs<T>(
1171 mut runs: Vec<(&T, u32)>,
1172 highest_count: u32,
1173 lowest_count: u32,
1174) -> ((Vec<T>, usize, u32), (Vec<T>, usize, u32))
1175where
1176 T: Clone,
1177{
1178 if runs.is_empty() {
1180 core::hint::cold_path();
1181 return ((Vec::new(), 0, 0), (Vec::new(), 0, 0));
1182 }
1183
1184 if runs.len() == 1 {
1186 let (val, count) = runs.pop().unwrap();
1187 return ((vec![val.clone()], 1, count), (Vec::new(), 0, 0));
1188 }
1189
1190 if highest_count == 1 {
1192 let antimodes_count = runs.len().min(10);
1193 let total_count = runs.len();
1194 let mut antimodes = Vec::with_capacity(antimodes_count);
1195 for (val, _) in runs.into_iter().take(antimodes_count) {
1196 antimodes.push(val.clone());
1197 }
1198 return ((Vec::new(), 0, 0), (antimodes, total_count, 1));
1200 }
1201
1202 let estimated_modes = (runs.len() / 10).clamp(1, 10);
1205 let estimated_antimodes = 10.min(runs.len());
1206
1207 let mut modes_result = Vec::with_capacity(estimated_modes);
1208 let mut antimodes_result = Vec::with_capacity(estimated_antimodes);
1209 let mut mode_count = 0;
1210 let mut antimodes_count = 0;
1211 let mut antimodes_collected = 0_u32;
1212
1213 for (val, count) in &runs {
1214 if *count == highest_count {
1215 modes_result.push((*val).clone());
1216 mode_count += 1;
1217 }
1218 if *count == lowest_count {
1219 antimodes_count += 1;
1220 if antimodes_collected < 10 {
1221 antimodes_result.push((*val).clone());
1222 antimodes_collected += 1;
1223 }
1224 }
1225 }
1226
1227 (
1228 (modes_result, mode_count, highest_count),
1229 (antimodes_result, antimodes_count, lowest_count),
1230 )
1231}
1232
1233#[allow(clippy::unsafe_derive_deserialize)]
1241#[derive(Clone, Serialize, Deserialize)]
1242pub struct Unsorted<T> {
1243 #[serde(skip)]
1246 sorted: bool,
1247 data: Vec<Partial<T>>,
1248}
1249
1250impl<T: PartialEq> PartialEq for Unsorted<T> {
1254 fn eq(&self, other: &Self) -> bool {
1255 self.data == other.data
1256 }
1257}
1258
1259impl<T: PartialEq> Eq for Unsorted<T> where Partial<T>: Eq {}
1260
1261impl<T: PartialOrd + Send> Unsorted<T> {
1262 #[inline]
1264 #[must_use]
1265 pub fn new() -> Unsorted<T> {
1266 Default::default()
1267 }
1268
1269 #[allow(clippy::inline_always)]
1271 #[inline(always)]
1272 pub fn add(&mut self, v: T) {
1273 self.sorted = false;
1274 self.data.push(Partial(v));
1275 }
1276
1277 #[inline]
1279 #[must_use]
1280 pub const fn len(&self) -> usize {
1281 self.data.len()
1282 }
1283
1284 #[inline]
1285 #[must_use]
1286 pub const fn is_empty(&self) -> bool {
1287 self.data.is_empty()
1288 }
1289
1290 #[inline]
1291 fn sort(&mut self) {
1292 if !self.sorted {
1293 if self.data.len() < PARALLEL_THRESHOLD {
1295 self.data.sort_unstable();
1296 } else {
1297 self.data.par_sort_unstable();
1298 }
1299 self.sorted = true;
1300 }
1301 }
1302
1303 #[inline]
1304 const fn already_sorted(&mut self) {
1305 self.sorted = true;
1306 }
1307
1308 #[inline]
1310 pub fn add_bulk(&mut self, values: Vec<T>) {
1311 self.sorted = false;
1312 self.data.reserve(values.len());
1313 self.data.extend(values.into_iter().map(Partial));
1314 }
1315
1316 #[inline]
1318 pub fn shrink_to_fit(&mut self) {
1319 self.data.shrink_to_fit();
1320 }
1321
1322 #[inline]
1324 #[must_use]
1325 pub fn with_capacity(capacity: usize) -> Self {
1326 Unsorted {
1327 sorted: true,
1328 data: Vec::with_capacity(capacity),
1329 }
1330 }
1331
1332 #[inline]
1334 pub fn push_ascending(&mut self, value: T) {
1335 if let Some(last) = self.data.last() {
1336 debug_assert!(last.0 <= value, "Value must be >= than last element");
1337 }
1338 self.data.push(Partial(value));
1339 }
1341}
1342
1343impl<T: PartialOrd + PartialEq + Clone + Send + Sync> Unsorted<T> {
1344 #[inline]
1345 pub fn cardinality(&mut self, sorted: bool, parallel_threshold: usize) -> u64 {
1353 const CHUNK_SIZE: usize = 2048; const DEFAULT_PARALLEL_THRESHOLD: usize = 10_240; let len = self.data.len();
1357 match len {
1358 0 => return 0,
1359 1 => return 1,
1360 _ => {}
1361 }
1362
1363 if sorted {
1364 self.already_sorted();
1365 } else {
1366 self.sort();
1367 }
1368
1369 let use_parallel = parallel_threshold != 0
1370 && (parallel_threshold == 1
1371 || len > parallel_threshold.max(DEFAULT_PARALLEL_THRESHOLD));
1372
1373 if use_parallel {
1374 self.data
1378 .par_chunks(CHUNK_SIZE)
1379 .map(|chunk| {
1380 let mut count = u64::from(!chunk.is_empty());
1382 for [a, b] in chunk.array_windows::<2>() {
1383 if a != b {
1384 count += 1;
1385 }
1386 }
1387 (count, chunk.first(), chunk.last())
1388 })
1389 .reduce(
1390 || (0u64, None, None),
1391 |(cl, fl, ll), (cr, fr, lr)| match (ll, fr) {
1392 (None, _) => (cl + cr, fr, lr),
1396 (_, None) => (cl + cr, fl, ll),
1397 (Some(l), Some(r)) => {
1398 let adj = u64::from(l == r);
1399 (cl + cr - adj, fl, lr)
1400 }
1401 },
1402 )
1403 .0
1404 } else {
1405 let mut count = u64::from(!self.data.is_empty());
1410
1411 for [a, b] in self.data.array_windows::<2>() {
1412 if a != b {
1413 count += 1;
1414 }
1415 }
1416 count
1417 }
1418 }
1419}
1420
1421impl<T: PartialOrd + Clone + Send> Unsorted<T> {
1422 #[inline]
1424 pub fn mode(&mut self) -> Option<T> {
1425 if self.data.is_empty() {
1426 return None;
1427 }
1428 self.sort();
1429 mode_on_sorted(self.data.iter().map(|p| &p.0)).cloned()
1430 }
1431
1432 #[inline]
1436 fn modes(&mut self) -> (Vec<T>, usize, u32) {
1437 if self.data.is_empty() {
1438 return (Vec::new(), 0, 0);
1439 }
1440 self.sort();
1441 modes_and_antimodes_on_sorted_slice(&self.data).0
1442 }
1443
1444 #[inline]
1447 fn antimodes(&mut self) -> (Vec<T>, usize, u32) {
1448 if self.data.is_empty() {
1449 return (Vec::new(), 0, 0);
1450 }
1451 self.sort();
1452 modes_and_antimodes_on_sorted_slice(&self.data).1
1453 }
1454
1455 #[allow(clippy::type_complexity)]
1458 #[inline]
1459 pub fn modes_antimodes(&mut self) -> ((Vec<T>, usize, u32), (Vec<T>, usize, u32)) {
1460 if self.data.is_empty() {
1461 return ((Vec::new(), 0, 0), (Vec::new(), 0, 0));
1462 }
1463 self.sort();
1464 modes_and_antimodes_on_sorted_slice(&self.data)
1465 }
1466}
1467
1468impl Unsorted<Vec<u8>> {
1469 #[allow(clippy::inline_always)]
1476 #[inline(always)]
1477 pub fn add_bytes(&mut self, v: &[u8]) {
1478 self.sorted = false;
1479 self.data.push(Partial(v.to_vec()));
1480 }
1481}
1482
1483impl<T: PartialOrd + ToPrimitive + Send> Unsorted<T> {
1484 #[inline]
1486 pub fn median(&mut self) -> Option<f64> {
1487 if self.data.is_empty() {
1488 return None;
1489 }
1490 self.sort();
1491 median_on_sorted(&self.data)
1492 }
1493}
1494
1495impl<T: PartialOrd + ToPrimitive + Send + Sync> Unsorted<T> {
1496 #[inline]
1498 pub fn mad(&mut self, existing_median: Option<f64>) -> Option<f64> {
1499 if self.data.is_empty() {
1500 return None;
1501 }
1502 if existing_median.is_none() {
1503 self.sort();
1504 }
1505 mad_on_sorted(&self.data, existing_median)
1506 }
1507}
1508
1509impl<T: PartialOrd + ToPrimitive + Send> Unsorted<T> {
1510 #[inline]
1515 pub fn quartiles(&mut self) -> Option<(f64, f64, f64)> {
1516 if self.data.is_empty() {
1517 return None;
1518 }
1519 self.sort();
1520 quartiles_on_sorted(&self.data)
1521 }
1522}
1523
1524impl<T: PartialOrd + ToPrimitive + Send + Sync> Unsorted<T> {
1525 #[inline]
1531 pub fn gini(&mut self, precalc_sum: Option<f64>) -> Option<f64> {
1532 if self.data.is_empty() {
1533 return None;
1534 }
1535 self.sort();
1536 gini_on_sorted(&self.data, precalc_sum)
1537 }
1538
1539 #[inline]
1546 pub fn kurtosis(
1547 &mut self,
1548 precalc_mean: Option<f64>,
1549 precalc_variance: Option<f64>,
1550 ) -> Option<f64> {
1551 if self.data.is_empty() {
1552 return None;
1553 }
1554 self.sort();
1555 kurtosis_on_sorted(&self.data, precalc_mean, precalc_variance)
1556 }
1557
1558 #[inline]
1565 #[allow(clippy::needless_pass_by_value)]
1566 pub fn percentile_rank<V>(&mut self, value: V) -> Option<f64>
1567 where
1568 V: PartialOrd + ToPrimitive,
1569 {
1570 if self.data.is_empty() {
1571 return None;
1572 }
1573 self.sort();
1574 percentile_rank_on_sorted(&self.data, &value)
1575 }
1576
1577 #[inline]
1594 pub fn atkinson(
1595 &mut self,
1596 epsilon: f64,
1597 precalc_mean: Option<f64>,
1598 precalc_geometric_sum: Option<f64>,
1599 ) -> Option<f64> {
1600 if self.data.is_empty() {
1601 return None;
1602 }
1603 self.sort();
1604 atkinson_on_sorted(&self.data, epsilon, precalc_mean, precalc_geometric_sum)
1605 }
1606}
1607
1608impl<T: PartialOrd + ToPrimitive + Clone + Send> Unsorted<T> {
1609 #[inline]
1621 pub fn quartiles_with_selection(&mut self) -> Option<(f64, f64, f64)> {
1622 if self.data.is_empty() {
1623 return None;
1624 }
1625 quartiles_with_zero_copy_selection(&self.data)
1627 }
1628}
1629
1630impl<T: PartialOrd + ToPrimitive + Send> Unsorted<T> {
1631 #[inline]
1637 #[must_use]
1638 pub fn quartiles_zero_copy(&self) -> Option<(f64, f64, f64)> {
1639 if self.data.is_empty() {
1640 return None;
1641 }
1642 quartiles_with_zero_copy_selection(&self.data)
1643 }
1644}
1645
1646impl<T: PartialOrd + Send> Commute for Unsorted<T> {
1647 #[inline]
1648 fn merge(&mut self, mut v: Unsorted<T>) {
1649 if v.is_empty() {
1650 return;
1651 }
1652
1653 self.sorted = false;
1654 self.data.extend(std::mem::take(&mut v.data));
1656 }
1657}
1658
1659impl<T: PartialOrd> Default for Unsorted<T> {
1660 #[inline]
1661 fn default() -> Unsorted<T> {
1662 Unsorted {
1663 data: Vec::with_capacity(16),
1664 sorted: true, }
1666 }
1667}
1668
1669impl<T: PartialOrd + Send> FromIterator<T> for Unsorted<T> {
1670 #[inline]
1671 fn from_iter<I: IntoIterator<Item = T>>(it: I) -> Unsorted<T> {
1672 let mut v = Unsorted::new();
1673 v.extend(it);
1674 v
1675 }
1676}
1677
1678impl<T: PartialOrd> Extend<T> for Unsorted<T> {
1679 #[inline]
1680 fn extend<I: IntoIterator<Item = T>>(&mut self, it: I) {
1681 self.sorted = false;
1682 self.data.extend(it.into_iter().map(Partial));
1683 }
1684}
1685
1686fn custom_percentiles_on_sorted<T>(data: &[Partial<T>], percentiles: &[u8]) -> Option<Vec<T>>
1687where
1688 T: PartialOrd + Clone,
1689{
1690 let len = data.len();
1691
1692 if len == 0 || percentiles.iter().any(|&p| p > 100) {
1694 return None;
1695 }
1696
1697 let unique_percentiles: Vec<u8> = if percentiles.len() <= 1 {
1699 percentiles.to_vec()
1701 } else {
1702 let is_sorted_unique = percentiles.array_windows::<2>().all(|[a, b]| a < b);
1704
1705 if is_sorted_unique {
1706 percentiles.to_vec()
1708 } else {
1709 let mut seen = [false; 101];
1711 let mut sorted_unique = Vec::with_capacity(percentiles.len().min(101));
1712 for &p in percentiles {
1713 if !seen[p as usize] {
1714 seen[p as usize] = true;
1715 sorted_unique.push(p);
1716 }
1717 }
1718 sorted_unique.sort_unstable();
1719 sorted_unique
1720 }
1721 };
1722
1723 let mut results = Vec::with_capacity(unique_percentiles.len());
1724
1725 unsafe {
1729 for &p in &unique_percentiles {
1730 #[allow(clippy::cast_sign_loss)]
1734 let rank = ((f64::from(p) / 100.0) * len as f64).ceil() as usize;
1735
1736 let idx = rank.saturating_sub(1);
1738
1739 results.push(data.get_unchecked(idx).0.clone());
1741 }
1742 }
1743
1744 Some(results)
1745}
1746
1747impl<T: PartialOrd + Clone + Send> Unsorted<T> {
1748 #[inline]
1770 pub fn custom_percentiles(&mut self, percentiles: &[u8]) -> Option<Vec<T>> {
1771 if self.data.is_empty() {
1772 return None;
1773 }
1774 self.sort();
1775 custom_percentiles_on_sorted(&self.data, percentiles)
1776 }
1777}
1778
1779#[cfg(test)]
1780mod test {
1781 use super::*;
1782
1783 #[test]
1784 fn test_cardinality_empty() {
1785 let mut unsorted: Unsorted<i32> = Unsorted::new();
1786 assert_eq!(unsorted.cardinality(false, 1), 0);
1787 }
1788
1789 #[test]
1790 fn test_cardinality_single_element() {
1791 let mut unsorted = Unsorted::new();
1792 unsorted.add(5);
1793 assert_eq!(unsorted.cardinality(false, 1), 1);
1794 }
1795
1796 #[test]
1797 fn test_cardinality_unique_elements() {
1798 let mut unsorted = Unsorted::new();
1799 unsorted.extend(vec![1, 2, 3, 4, 5]);
1800 assert_eq!(unsorted.cardinality(false, 1), 5);
1801 }
1802
1803 #[test]
1804 fn test_cardinality_duplicate_elements() {
1805 let mut unsorted = Unsorted::new();
1806 unsorted.extend(vec![1, 2, 2, 3, 3, 3, 4, 4, 4, 4]);
1807 assert_eq!(unsorted.cardinality(false, 1), 4);
1808 }
1809
1810 #[test]
1811 fn test_cardinality_all_same() {
1812 let mut unsorted = Unsorted::new();
1813 unsorted.extend(vec![1; 100]);
1814 assert_eq!(unsorted.cardinality(false, 1), 1);
1815 }
1816
1817 #[test]
1818 fn test_cardinality_large_range() {
1819 let mut unsorted = Unsorted::new();
1820 unsorted.extend(0..1_000_000);
1821 assert_eq!(unsorted.cardinality(false, 1), 1_000_000);
1822 }
1823
1824 #[test]
1825 fn test_cardinality_large_range_sequential() {
1826 let mut unsorted = Unsorted::new();
1827 unsorted.extend(0..1_000_000);
1828 assert_eq!(unsorted.cardinality(false, 2_000_000), 1_000_000);
1829 }
1830
1831 #[test]
1832 fn test_cardinality_presorted() {
1833 let mut unsorted = Unsorted::new();
1834 unsorted.extend(vec![1, 2, 3, 4, 5]);
1835 unsorted.sort();
1836 assert_eq!(unsorted.cardinality(true, 1), 5);
1837 }
1838
1839 #[test]
1840 fn test_cardinality_float() {
1841 let mut unsorted = Unsorted::new();
1842 unsorted.extend(vec![1.0, 1.0, 2.0, 3.0, 3.0, 4.0]);
1843 assert_eq!(unsorted.cardinality(false, 1), 4);
1844 }
1845
1846 #[test]
1847 fn test_cardinality_string() {
1848 let mut unsorted = Unsorted::new();
1849 unsorted.extend(vec!["a", "b", "b", "c", "c", "c"]);
1850 assert_eq!(unsorted.cardinality(false, 1), 3);
1851 }
1852
1853 #[test]
1854 fn test_quartiles_selection_vs_sorted() {
1855 let test_cases = vec![
1857 vec![3, 5, 7, 9],
1858 vec![3, 5, 7],
1859 vec![1, 2, 7, 11],
1860 vec![3, 5, 7, 9, 12],
1861 vec![2, 2, 3, 8, 10],
1862 vec![3, 5, 7, 9, 12, 20],
1863 vec![0, 2, 4, 8, 10, 11],
1864 vec![3, 5, 7, 9, 12, 20, 21],
1865 vec![1, 5, 6, 6, 7, 10, 19],
1866 ];
1867
1868 for test_case in test_cases {
1869 let mut unsorted1 = Unsorted::new();
1870 let mut unsorted2 = Unsorted::new();
1871 let mut unsorted3 = Unsorted::new();
1872 unsorted1.extend(test_case.clone());
1873 unsorted2.extend(test_case.clone());
1874 unsorted3.extend(test_case.clone());
1875
1876 let result_sorted = unsorted1.quartiles();
1877 let result_selection = unsorted2.quartiles_with_selection();
1878 let result_zero_copy = unsorted3.quartiles_zero_copy();
1879
1880 assert_eq!(
1881 result_sorted, result_selection,
1882 "Selection mismatch for test case: {:?}",
1883 test_case
1884 );
1885 assert_eq!(
1886 result_sorted, result_zero_copy,
1887 "Zero-copy mismatch for test case: {:?}",
1888 test_case
1889 );
1890 }
1891 }
1892
1893 #[test]
1894 fn test_quartiles_with_selection_small() {
1895 let mut unsorted: Unsorted<i32> = Unsorted::new();
1897 assert_eq!(unsorted.quartiles_with_selection(), None);
1898
1899 let mut unsorted = Unsorted::new();
1900 unsorted.extend(vec![1, 2]);
1901 assert_eq!(unsorted.quartiles_with_selection(), None);
1902
1903 let mut unsorted = Unsorted::new();
1904 unsorted.extend(vec![1, 2, 3]);
1905 assert_eq!(unsorted.quartiles_with_selection(), Some((1.0, 2.0, 3.0)));
1906 }
1907
1908 #[test]
1909 fn test_quickselect() {
1910 let data = vec![
1911 Partial(3),
1912 Partial(1),
1913 Partial(4),
1914 Partial(1),
1915 Partial(5),
1916 Partial(9),
1917 Partial(2),
1918 Partial(6),
1919 ];
1920
1921 assert_eq!(quickselect(&mut data.clone(), 0), Some(&1));
1923 assert_eq!(quickselect(&mut data.clone(), 3), Some(&3));
1924 assert_eq!(quickselect(&mut data.clone(), 7), Some(&9));
1925
1926 let mut empty: Vec<Partial<i32>> = vec![];
1928 assert_eq!(quickselect(&mut empty, 0), None);
1929
1930 let mut data = vec![Partial(3), Partial(1), Partial(4), Partial(1), Partial(5)];
1931 assert_eq!(quickselect(&mut data, 10), None); }
1933
1934 #[test]
1935 fn median_stream() {
1936 assert_eq!(median(vec![3usize, 5, 7, 9].into_iter()), Some(6.0));
1937 assert_eq!(median(vec![3usize, 5, 7].into_iter()), Some(5.0));
1938 }
1939
1940 #[test]
1941 fn mad_stream() {
1942 assert_eq!(mad(vec![3usize, 5, 7, 9].into_iter(), None), Some(2.0));
1943 assert_eq!(
1944 mad(
1945 vec![
1946 86usize, 60, 95, 39, 49, 12, 56, 82, 92, 24, 33, 28, 46, 34, 100, 39, 100, 38,
1947 50, 61, 39, 88, 5, 13, 64
1948 ]
1949 .into_iter(),
1950 None
1951 ),
1952 Some(16.0)
1953 );
1954 }
1955
1956 #[test]
1957 fn mad_stream_precalc_median() {
1958 let data = vec![3usize, 5, 7, 9].into_iter();
1959 let median1 = median(data.clone());
1960 assert_eq!(mad(data, median1), Some(2.0));
1961
1962 let data2 = vec![
1963 86usize, 60, 95, 39, 49, 12, 56, 82, 92, 24, 33, 28, 46, 34, 100, 39, 100, 38, 50, 61,
1964 39, 88, 5, 13, 64,
1965 ]
1966 .into_iter();
1967 let median2 = median(data2.clone());
1968 assert_eq!(mad(data2, median2), Some(16.0));
1969 }
1970
1971 #[test]
1972 fn mode_stream() {
1973 assert_eq!(mode(vec![3usize, 5, 7, 9].into_iter()), None);
1974 assert_eq!(mode(vec![3usize, 3, 3, 3].into_iter()), Some(3));
1975 assert_eq!(mode(vec![3usize, 3, 3, 4].into_iter()), Some(3));
1976 assert_eq!(mode(vec![4usize, 3, 3, 3].into_iter()), Some(3));
1977 assert_eq!(mode(vec![1usize, 1, 2, 3, 3].into_iter()), None);
1978 }
1979
1980 #[test]
1981 fn median_floats() {
1982 assert_eq!(median(vec![3.0f64, 5.0, 7.0, 9.0].into_iter()), Some(6.0));
1983 assert_eq!(median(vec![3.0f64, 5.0, 7.0].into_iter()), Some(5.0));
1984 }
1985
1986 #[test]
1987 fn mode_floats() {
1988 assert_eq!(mode(vec![3.0f64, 5.0, 7.0, 9.0].into_iter()), None);
1989 assert_eq!(mode(vec![3.0f64, 3.0, 3.0, 3.0].into_iter()), Some(3.0));
1990 assert_eq!(mode(vec![3.0f64, 3.0, 3.0, 4.0].into_iter()), Some(3.0));
1991 assert_eq!(mode(vec![4.0f64, 3.0, 3.0, 3.0].into_iter()), Some(3.0));
1992 assert_eq!(mode(vec![1.0f64, 1.0, 2.0, 3.0, 3.0].into_iter()), None);
1993 }
1994
1995 #[test]
1996 fn modes_stream() {
1997 assert_eq!(modes(vec![3usize, 5, 7, 9].into_iter()), (vec![], 0, 0));
1998 assert_eq!(modes(vec![3usize, 3, 3, 3].into_iter()), (vec![3], 1, 4));
1999 assert_eq!(modes(vec![3usize, 3, 4, 4].into_iter()), (vec![3, 4], 2, 2));
2000 assert_eq!(modes(vec![4usize, 3, 3, 3].into_iter()), (vec![3], 1, 3));
2001 assert_eq!(modes(vec![1usize, 1, 2, 2].into_iter()), (vec![1, 2], 2, 2));
2002 let vec: Vec<u32> = vec![];
2003 assert_eq!(modes(vec.into_iter()), (vec![], 0, 0));
2004 }
2005
2006 #[test]
2007 fn modes_floats() {
2008 assert_eq!(
2009 modes(vec![3_f64, 5.0, 7.0, 9.0].into_iter()),
2010 (vec![], 0, 0)
2011 );
2012 assert_eq!(
2013 modes(vec![3_f64, 3.0, 3.0, 3.0].into_iter()),
2014 (vec![3.0], 1, 4)
2015 );
2016 assert_eq!(
2017 modes(vec![3_f64, 3.0, 4.0, 4.0].into_iter()),
2018 (vec![3.0, 4.0], 2, 2)
2019 );
2020 assert_eq!(
2021 modes(vec![1_f64, 1.0, 2.0, 3.0, 3.0].into_iter()),
2022 (vec![1.0, 3.0], 2, 2)
2023 );
2024 }
2025
2026 #[test]
2027 fn antimodes_stream() {
2028 assert_eq!(
2029 antimodes(vec![3usize, 5, 7, 9].into_iter()),
2030 (vec![3, 5, 7, 9], 4, 1)
2031 );
2032 assert_eq!(
2033 antimodes(vec![1usize, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13].into_iter()),
2034 (vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 13, 1)
2035 );
2036 assert_eq!(
2037 antimodes(vec![1usize, 3, 3, 3].into_iter()),
2038 (vec![1], 1, 1)
2039 );
2040 assert_eq!(
2041 antimodes(vec![3usize, 3, 4, 4].into_iter()),
2042 (vec![3, 4], 2, 2)
2043 );
2044 assert_eq!(
2045 antimodes(
2046 vec![
2047 3usize, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13,
2048 14, 14, 15, 15
2049 ]
2050 .into_iter()
2051 ),
2052 (vec![3, 4, 5, 6, 7, 8, 9, 10, 11, 12], 13, 2)
2054 );
2055 assert_eq!(
2056 antimodes(
2057 vec![
2058 3usize, 3, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 4, 4, 5, 5, 6, 6, 7, 7, 13, 13,
2059 14, 14, 15, 15
2060 ]
2061 .into_iter()
2062 ),
2063 (vec![3, 4, 5, 6, 7, 8, 9, 10, 11, 12], 13, 2)
2064 );
2065 assert_eq!(
2066 antimodes(vec![3usize, 3, 3, 4].into_iter()),
2067 (vec![4], 1, 1)
2068 );
2069 assert_eq!(
2070 antimodes(vec![4usize, 3, 3, 3].into_iter()),
2071 (vec![4], 1, 1)
2072 );
2073 assert_eq!(
2074 antimodes(vec![1usize, 1, 2, 2].into_iter()),
2075 (vec![1, 2], 2, 2)
2076 );
2077 let vec: Vec<u32> = vec![];
2078 assert_eq!(antimodes(vec.into_iter()), (vec![], 0, 0));
2079 }
2080
2081 #[test]
2082 fn antimodes_floats() {
2083 assert_eq!(
2084 antimodes(vec![3_f64, 5.0, 7.0, 9.0].into_iter()),
2085 (vec![3.0, 5.0, 7.0, 9.0], 4, 1)
2086 );
2087 assert_eq!(
2088 antimodes(vec![3_f64, 3.0, 3.0, 3.0].into_iter()),
2089 (vec![], 0, 0)
2090 );
2091 assert_eq!(
2092 antimodes(vec![3_f64, 3.0, 4.0, 4.0].into_iter()),
2093 (vec![3.0, 4.0], 2, 2)
2094 );
2095 assert_eq!(
2096 antimodes(vec![1_f64, 1.0, 2.0, 3.0, 3.0].into_iter()),
2097 (vec![2.0], 1, 1)
2098 );
2099 }
2100
2101 #[test]
2102 fn test_custom_percentiles() {
2103 let mut unsorted: Unsorted<i32> = Unsorted::new();
2105 unsorted.extend(1..=11); let result = unsorted.custom_percentiles(&[25, 50, 75]).unwrap();
2108 assert_eq!(result, vec![3, 6, 9]);
2109
2110 let mut str_data = Unsorted::new();
2112 str_data.extend(vec!["a", "b", "c", "d", "e"]);
2113 let result = str_data.custom_percentiles(&[20, 40, 60, 80]).unwrap();
2114 assert_eq!(result, vec!["a", "b", "c", "d"]);
2115
2116 let mut char_data = Unsorted::new();
2118 char_data.extend('a'..='e');
2119 let result = char_data.custom_percentiles(&[25, 50, 75]).unwrap();
2120 assert_eq!(result, vec!['b', 'c', 'd']);
2121
2122 let mut float_data = Unsorted::new();
2124 float_data.extend(vec![1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9]);
2125 let result = float_data
2126 .custom_percentiles(&[10, 30, 50, 70, 90])
2127 .unwrap();
2128 assert_eq!(result, vec![1.1, 3.3, 5.5, 7.7, 9.9]);
2129
2130 let result = float_data.custom_percentiles(&[]).unwrap();
2132 assert_eq!(result, Vec::<f64>::new());
2133
2134 let result = float_data.custom_percentiles(&[50, 50, 50]).unwrap();
2136 assert_eq!(result, vec![5.5]);
2137
2138 let result = float_data.custom_percentiles(&[0, 100]).unwrap();
2140 assert_eq!(result, vec![1.1, 9.9]);
2141
2142 let result = float_data.custom_percentiles(&[75, 25, 50]).unwrap();
2144 assert_eq!(result, vec![3.3, 5.5, 7.7]); let mut single = Unsorted::new();
2148 single.add(42);
2149 let result = single.custom_percentiles(&[0, 50, 100]).unwrap();
2150 assert_eq!(result, vec![42, 42, 42]);
2151 }
2152
2153 #[test]
2154 fn quartiles_stream() {
2155 assert_eq!(
2156 quartiles(vec![3usize, 5, 7].into_iter()),
2157 Some((3., 5., 7.))
2158 );
2159 assert_eq!(
2160 quartiles(vec![3usize, 5, 7, 9].into_iter()),
2161 Some((4., 6., 8.))
2162 );
2163 assert_eq!(
2164 quartiles(vec![1usize, 2, 7, 11].into_iter()),
2165 Some((1.5, 4.5, 9.))
2166 );
2167 assert_eq!(
2168 quartiles(vec![3usize, 5, 7, 9, 12].into_iter()),
2169 Some((4., 7., 10.5))
2170 );
2171 assert_eq!(
2172 quartiles(vec![2usize, 2, 3, 8, 10].into_iter()),
2173 Some((2., 3., 9.))
2174 );
2175 assert_eq!(
2176 quartiles(vec![3usize, 5, 7, 9, 12, 20].into_iter()),
2177 Some((5., 8., 12.))
2178 );
2179 assert_eq!(
2180 quartiles(vec![0usize, 2, 4, 8, 10, 11].into_iter()),
2181 Some((2., 6., 10.))
2182 );
2183 assert_eq!(
2184 quartiles(vec![3usize, 5, 7, 9, 12, 20, 21].into_iter()),
2185 Some((5., 9., 20.))
2186 );
2187 assert_eq!(
2188 quartiles(vec![1usize, 5, 6, 6, 7, 10, 19].into_iter()),
2189 Some((5., 6., 10.))
2190 );
2191 }
2192
2193 #[test]
2194 fn quartiles_floats() {
2195 assert_eq!(
2196 quartiles(vec![3_f64, 5., 7.].into_iter()),
2197 Some((3., 5., 7.))
2198 );
2199 assert_eq!(
2200 quartiles(vec![3_f64, 5., 7., 9.].into_iter()),
2201 Some((4., 6., 8.))
2202 );
2203 assert_eq!(
2204 quartiles(vec![3_f64, 5., 7., 9., 12.].into_iter()),
2205 Some((4., 7., 10.5))
2206 );
2207 assert_eq!(
2208 quartiles(vec![3_f64, 5., 7., 9., 12., 20.].into_iter()),
2209 Some((5., 8., 12.))
2210 );
2211 assert_eq!(
2212 quartiles(vec![3_f64, 5., 7., 9., 12., 20., 21.].into_iter()),
2213 Some((5., 9., 20.))
2214 );
2215 }
2216
2217 #[test]
2218 fn test_quartiles_zero_copy_small() {
2219 let unsorted: Unsorted<i32> = Unsorted::new();
2221 assert_eq!(unsorted.quartiles_zero_copy(), None);
2222
2223 let mut unsorted = Unsorted::new();
2224 unsorted.extend(vec![1, 2]);
2225 assert_eq!(unsorted.quartiles_zero_copy(), None);
2226
2227 let mut unsorted = Unsorted::new();
2228 unsorted.extend(vec![1, 2, 3]);
2229 assert_eq!(unsorted.quartiles_zero_copy(), Some((1.0, 2.0, 3.0)));
2230
2231 let mut unsorted = Unsorted::new();
2233 unsorted.extend(vec![3, 5, 7, 9]);
2234 assert_eq!(unsorted.quartiles_zero_copy(), Some((4.0, 6.0, 8.0)));
2235 }
2236
2237 #[test]
2238 fn gini_empty() {
2239 let mut unsorted: Unsorted<i32> = Unsorted::new();
2240 assert_eq!(unsorted.gini(None), None);
2241 let empty_vec: Vec<i32> = vec![];
2242 assert_eq!(gini(empty_vec.into_iter(), None), None);
2243 }
2244
2245 #[test]
2246 fn gini_single_element() {
2247 let mut unsorted = Unsorted::new();
2248 unsorted.add(5);
2249 assert_eq!(unsorted.gini(None), Some(0.0));
2250 assert_eq!(gini(vec![5].into_iter(), None), Some(0.0));
2251 }
2252
2253 #[test]
2254 fn gini_perfect_equality() {
2255 let mut unsorted = Unsorted::new();
2257 unsorted.extend(vec![10, 10, 10, 10, 10]);
2258 let result = unsorted.gini(None).unwrap();
2259 assert!((result - 0.0).abs() < 1e-10, "Expected 0.0, got {}", result);
2260
2261 assert!((gini(vec![10, 10, 10, 10, 10].into_iter(), None).unwrap() - 0.0).abs() < 1e-10);
2262 }
2263
2264 #[test]
2265 fn gini_perfect_inequality() {
2266 let mut unsorted = Unsorted::new();
2269 unsorted.extend(vec![0, 0, 0, 0, 100]);
2270 let result = unsorted.gini(None).unwrap();
2271 assert!((result - 0.8).abs() < 1e-10, "Expected 0.8, got {}", result);
2274 }
2275
2276 #[test]
2277 fn gini_stream() {
2278 let result = gini(vec![1usize, 2, 3, 4, 5].into_iter(), None).unwrap();
2285 let expected = (2.0 * 55.0) / (5.0 * 15.0) - 6.0 / 5.0;
2286 assert!(
2287 (result - expected).abs() < 1e-10,
2288 "Expected {}, got {}",
2289 expected,
2290 result
2291 );
2292 }
2293
2294 #[test]
2295 fn gini_floats() {
2296 let mut unsorted = Unsorted::new();
2297 unsorted.extend(vec![1.0, 2.0, 3.0, 4.0, 5.0]);
2298 let result = unsorted.gini(None).unwrap();
2299 let expected = (2.0 * 55.0) / (5.0 * 15.0) - 6.0 / 5.0;
2300 assert!((result - expected).abs() < 1e-10);
2301
2302 assert!(
2303 (gini(vec![1.0f64, 2.0, 3.0, 4.0, 5.0].into_iter(), None).unwrap() - expected).abs()
2304 < 1e-10
2305 );
2306 }
2307
2308 #[test]
2309 fn gini_all_zeros() {
2310 let mut unsorted = Unsorted::new();
2312 unsorted.extend(vec![0, 0, 0, 0]);
2313 assert_eq!(unsorted.gini(None), None);
2314 assert_eq!(gini(vec![0, 0, 0, 0].into_iter(), None), None);
2315 }
2316
2317 #[test]
2318 fn gini_negative_values() {
2319 let mut unsorted = Unsorted::new();
2321 unsorted.extend(vec![-5, -3, -1, 1, 3, 5]);
2322 let result = unsorted.gini(None);
2323 assert_eq!(result, None);
2325
2326 let mut unsorted = Unsorted::new();
2328 unsorted.extend(vec![-2, -1, 0, 1, 2]);
2329 let result = unsorted.gini(None);
2330 assert_eq!(result, None);
2332
2333 let mut unsorted = Unsorted::new();
2336 unsorted.extend(vec![-1, 0, 1, 2, 3]);
2337 let result = unsorted.gini(None);
2338 assert_eq!(result, None);
2339 }
2340
2341 #[test]
2342 fn gini_known_cases() {
2343 let mut unsorted = Unsorted::new();
2345 unsorted.extend(vec![1, 1, 1, 1, 1]);
2346 let result = unsorted.gini(None).unwrap();
2347 assert!((result - 0.0).abs() < 1e-10);
2348
2349 let mut unsorted = Unsorted::new();
2351 unsorted.extend(vec![0, 0, 0, 0, 1]);
2352 let result = unsorted.gini(None).unwrap();
2353 assert!((result - 0.8).abs() < 1e-10);
2355
2356 let mut unsorted = Unsorted::new();
2358 unsorted.extend(vec![1, 2, 3]);
2359 let result = unsorted.gini(None).unwrap();
2360 let expected = (2.0 * 14.0) / (3.0 * 6.0) - 4.0 / 3.0;
2363 assert!((result - expected).abs() < 1e-10);
2364 }
2365
2366 #[test]
2367 fn gini_precalc_sum() {
2368 let mut unsorted = Unsorted::new();
2370 unsorted.extend(vec![1, 2, 3, 4, 5]);
2371 let precalc_sum = Some(15.0);
2372 let result = unsorted.gini(precalc_sum).unwrap();
2373 let expected = (2.0 * 55.0) / (5.0 * 15.0) - 6.0 / 5.0;
2374 assert!((result - expected).abs() < 1e-10);
2375
2376 let mut unsorted2 = Unsorted::new();
2378 unsorted2.extend(vec![1, 2, 3, 4, 5]);
2379 let result2 = unsorted2.gini(None).unwrap();
2380 assert!((result - result2).abs() < 1e-10);
2381 }
2382
2383 #[test]
2384 fn gini_large_dataset() {
2385 let data: Vec<i32> = (1..=1000).collect();
2387 let result = gini(data.iter().copied(), None);
2388 assert!(result.is_some());
2389 let gini_val = result.unwrap();
2390 assert!(gini_val > 0.0 && gini_val < 0.5);
2392 }
2393
2394 #[test]
2395 fn gini_unsorted_vs_sorted() {
2396 let mut unsorted1 = Unsorted::new();
2398 unsorted1.extend(vec![5, 2, 8, 1, 9, 3, 7, 4, 6]);
2399 let result1 = unsorted1.gini(None).unwrap();
2400
2401 let mut unsorted2 = Unsorted::new();
2402 unsorted2.extend(vec![1, 2, 3, 4, 5, 6, 7, 8, 9]);
2403 let result2 = unsorted2.gini(None).unwrap();
2404
2405 assert!((result1 - result2).abs() < 1e-10);
2406 }
2407
2408 #[test]
2409 fn gini_small_values() {
2410 let mut unsorted = Unsorted::new();
2412 unsorted.extend(vec![0.001, 0.002, 0.003, 0.004, 0.005]);
2413 let result = unsorted.gini(None);
2414 assert!(result.is_some());
2415 let expected = (2.0 * 55.0) / (5.0 * 15.0) - 6.0 / 5.0;
2417 assert!((result.unwrap() - expected).abs() < 1e-10);
2418 }
2419
2420 #[test]
2421 fn gini_large_values() {
2422 let mut unsorted = Unsorted::new();
2424 unsorted.extend(vec![1000, 2000, 3000, 4000, 5000]);
2425 let result = unsorted.gini(None);
2426 assert!(result.is_some());
2427 let expected = (2.0 * 55.0) / (5.0 * 15.0) - 6.0 / 5.0;
2429 assert!((result.unwrap() - expected).abs() < 1e-10);
2430 }
2431
2432 #[test]
2433 fn gini_two_elements() {
2434 let mut unsorted = Unsorted::new();
2436 unsorted.extend(vec![1, 2]);
2437 let result = unsorted.gini(None).unwrap();
2438 let expected = (2.0 * 5.0) / (2.0 * 3.0) - 3.0 / 2.0;
2441 assert!((result - expected).abs() < 1e-10);
2442 }
2443
2444 #[test]
2445 fn gini_precalc_sum_zero() {
2446 let mut unsorted = Unsorted::new();
2448 unsorted.extend(vec![1, 2, 3, 4, 5]);
2449 let result = unsorted.gini(Some(0.0));
2450 assert_eq!(result, None);
2451 }
2452
2453 #[test]
2454 fn gini_precalc_sum_negative() {
2455 let mut unsorted = Unsorted::new();
2457 unsorted.extend(vec![-5, -3, -1, 1, 3]);
2458 let result = unsorted.gini(None);
2459 assert_eq!(result, None);
2460
2461 let mut unsorted = Unsorted::new();
2463 unsorted.extend(vec![1, 2, 3]);
2464 let result = unsorted.gini(Some(-5.0));
2465 assert_eq!(result, None);
2466 }
2467
2468 #[test]
2469 fn gini_different_types() {
2470 let mut unsorted_u32 = Unsorted::new();
2472 unsorted_u32.extend(vec![1u32, 2, 3, 4, 5]);
2473 let result_u32 = unsorted_u32.gini(None).unwrap();
2474
2475 let mut unsorted_i64 = Unsorted::new();
2476 unsorted_i64.extend(vec![1i64, 2, 3, 4, 5]);
2477 let result_i64 = unsorted_i64.gini(None).unwrap();
2478
2479 let expected = (2.0 * 55.0) / (5.0 * 15.0) - 6.0 / 5.0;
2480 assert!((result_u32 - expected).abs() < 1e-10);
2481 assert!((result_i64 - expected).abs() < 1e-10);
2482 }
2483
2484 #[test]
2485 fn gini_extreme_inequality() {
2486 let mut unsorted = Unsorted::new();
2488 unsorted.extend(vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 1000]);
2489 let result = unsorted.gini(None).unwrap();
2490 assert!((result - 0.9).abs() < 1e-10);
2493 }
2494
2495 #[test]
2496 fn gini_duplicate_values() {
2497 let mut unsorted = Unsorted::new();
2499 unsorted.extend(vec![1, 1, 1, 5, 5, 5, 10, 10, 10]);
2500 let result = unsorted.gini(None);
2501 assert!(result.is_some());
2502 let gini_val = result.unwrap();
2504 assert!((0.0..=1.0).contains(&gini_val));
2505 }
2506
2507 #[test]
2508 fn kurtosis_empty() {
2509 let mut unsorted: Unsorted<i32> = Unsorted::new();
2510 assert_eq!(unsorted.kurtosis(None, None), None);
2511 let empty_vec: Vec<i32> = vec![];
2512 assert_eq!(kurtosis(empty_vec.into_iter(), None, None), None);
2513 }
2514
2515 #[test]
2516 fn kurtosis_small() {
2517 let mut unsorted = Unsorted::new();
2519 unsorted.extend(vec![1, 2]);
2520 assert_eq!(unsorted.kurtosis(None, None), None);
2521
2522 let mut unsorted = Unsorted::new();
2523 unsorted.extend(vec![1, 2, 3]);
2524 assert_eq!(unsorted.kurtosis(None, None), None);
2525 }
2526
2527 #[test]
2528 fn kurtosis_normal_distribution() {
2529 let mut unsorted = Unsorted::new();
2531 unsorted.extend(vec![1, 2, 3, 4, 5]);
2532 let result = unsorted.kurtosis(None, None);
2533 assert!(result.is_some());
2534 }
2536
2537 #[test]
2538 fn kurtosis_all_same() {
2539 let mut unsorted = Unsorted::new();
2541 unsorted.extend(vec![5, 5, 5, 5]);
2542 assert_eq!(unsorted.kurtosis(None, None), None);
2543 }
2544
2545 #[test]
2546 fn kurtosis_stream() {
2547 let result = kurtosis(vec![1usize, 2, 3, 4, 5].into_iter(), None, None);
2548 assert!(result.is_some());
2549 }
2550
2551 #[test]
2552 fn kurtosis_precalc_mean_variance() {
2553 let mut unsorted = Unsorted::new();
2555 unsorted.extend(vec![1, 2, 3, 4, 5]);
2556
2557 let mean = 3.0f64;
2559 let variance = ((1.0f64 - 3.0).powi(2)
2560 + (2.0f64 - 3.0).powi(2)
2561 + (3.0f64 - 3.0).powi(2)
2562 + (4.0f64 - 3.0).powi(2)
2563 + (5.0f64 - 3.0).powi(2))
2564 / 5.0;
2565
2566 let result = unsorted.kurtosis(Some(mean), Some(variance));
2567 assert!(result.is_some());
2568
2569 let mut unsorted2 = Unsorted::new();
2571 unsorted2.extend(vec![1, 2, 3, 4, 5]);
2572 let result2 = unsorted2.kurtosis(None, None);
2573 assert!((result.unwrap() - result2.unwrap()).abs() < 1e-10);
2574 }
2575
2576 #[test]
2577 fn kurtosis_precalc_mean_only() {
2578 let mut unsorted = Unsorted::new();
2580 unsorted.extend(vec![1, 2, 3, 4, 5]);
2581 let mean = 3.0f64;
2582
2583 let result = unsorted.kurtosis(Some(mean), None);
2584 assert!(result.is_some());
2585
2586 let mut unsorted2 = Unsorted::new();
2588 unsorted2.extend(vec![1, 2, 3, 4, 5]);
2589 let result2 = unsorted2.kurtosis(None, None);
2590 assert!((result.unwrap() - result2.unwrap()).abs() < 1e-10);
2591 }
2592
2593 #[test]
2594 fn kurtosis_precalc_variance_only() {
2595 let mut unsorted = Unsorted::new();
2597 unsorted.extend(vec![1, 2, 3, 4, 5]);
2598 let variance = ((1.0f64 - 3.0).powi(2)
2599 + (2.0f64 - 3.0).powi(2)
2600 + (3.0f64 - 3.0).powi(2)
2601 + (4.0f64 - 3.0).powi(2)
2602 + (5.0f64 - 3.0).powi(2))
2603 / 5.0;
2604
2605 let result = unsorted.kurtosis(None, Some(variance));
2606 assert!(result.is_some());
2607
2608 let mut unsorted2 = Unsorted::new();
2610 unsorted2.extend(vec![1, 2, 3, 4, 5]);
2611 let result2 = unsorted2.kurtosis(None, None);
2612 assert!((result.unwrap() - result2.unwrap()).abs() < 1e-10);
2613 }
2614
2615 #[test]
2616 fn kurtosis_exact_calculation() {
2617 let mut unsorted = Unsorted::new();
2625 unsorted.extend(vec![1, 2, 3, 4]);
2626 let result = unsorted.kurtosis(None, None).unwrap();
2627 assert!(
2628 (result - 8.366_67).abs() < 1e-4,
2629 "expected ~8.36667, got {result}"
2630 );
2631 }
2632
2633 #[test]
2634 fn kurtosis_uniform_distribution() {
2635 let mut unsorted = Unsorted::new();
2637 unsorted.extend(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
2638 let result = unsorted.kurtosis(None, None).unwrap();
2639 assert!(result.is_finite());
2642 }
2643
2644 #[test]
2645 fn kurtosis_uniform_is_negative_excess() {
2646 let data: Vec<f64> = (0..2000).map(|i| i as f64 * 100.0 / 1999.0).collect();
2648 let mut u = Unsorted::new();
2649 u.extend(data);
2650 let k = u.kurtosis(None, None).unwrap();
2651 assert!((k - (-1.2)).abs() < 0.05, "expected ~-1.2, got {k}");
2652 }
2653
2654 #[test]
2655 fn kurtosis_two_point_is_strongly_negative() {
2656 let mut u = Unsorted::new();
2658 u.extend(
2659 (0..2000)
2660 .map(|i| if i % 2 == 0 { 0.0 } else { 100.0 })
2661 .collect::<Vec<f64>>(),
2662 );
2663 let k = u.kurtosis(None, None).unwrap();
2664 assert!((k - (-2.0)).abs() < 0.05, "expected ~-2.0, got {k}");
2665 }
2666
2667 #[test]
2668 fn kurtosis_large_dataset() {
2669 let data: Vec<i32> = (1..=1000).collect();
2671 let result = kurtosis(data.iter().copied(), None, None);
2672 assert!(result.is_some());
2673 let kurt_val = result.unwrap();
2674 assert!(kurt_val.is_finite());
2675 }
2676
2677 #[test]
2678 fn kurtosis_unsorted_vs_sorted() {
2679 let mut unsorted1 = Unsorted::new();
2681 unsorted1.extend(vec![5, 2, 8, 1, 9, 3, 7, 4, 6]);
2682 let result1 = unsorted1.kurtosis(None, None).unwrap();
2683
2684 let mut unsorted2 = Unsorted::new();
2685 unsorted2.extend(vec![1, 2, 3, 4, 5, 6, 7, 8, 9]);
2686 let result2 = unsorted2.kurtosis(None, None).unwrap();
2687
2688 assert!((result1 - result2).abs() < 1e-10);
2689 }
2690
2691 #[test]
2692 fn kurtosis_minimum_size() {
2693 let mut unsorted = Unsorted::new();
2695 unsorted.extend(vec![1, 2, 3, 4]);
2696 let result = unsorted.kurtosis(None, None);
2697 assert!(result.is_some());
2698 assert!(result.unwrap().is_finite());
2699 }
2700
2701 #[test]
2702 fn kurtosis_heavy_tailed() {
2703 let mut unsorted = Unsorted::new();
2705 unsorted.extend(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 100]);
2706 let result = unsorted.kurtosis(None, None).unwrap();
2707 assert!(result.is_finite());
2709 assert!(result > -10.0); }
2712
2713 #[test]
2714 fn kurtosis_light_tailed() {
2715 let mut unsorted = Unsorted::new();
2717 unsorted.extend(vec![10, 11, 12, 13, 14, 15, 16, 17, 18, 19]);
2718 let result = unsorted.kurtosis(None, None).unwrap();
2719 assert!(result.is_finite());
2721 }
2722
2723 #[test]
2724 fn kurtosis_small_variance() {
2725 let mut unsorted = Unsorted::new();
2727 unsorted.extend(vec![10.0, 10.001, 10.002, 10.003, 10.004]);
2728 let result = unsorted.kurtosis(None, None);
2729 assert!(result.is_some());
2731 assert!(result.unwrap().is_finite());
2732 }
2733
2734 #[test]
2735 fn kurtosis_precalc_zero_variance() {
2736 let mut unsorted = Unsorted::new();
2738 unsorted.extend(vec![1, 2, 3, 4, 5]);
2739 let result = unsorted.kurtosis(None, Some(0.0));
2740 assert_eq!(result, None);
2741 }
2742
2743 #[test]
2744 fn kurtosis_precalc_negative_variance() {
2745 let mut unsorted = Unsorted::new();
2747 unsorted.extend(vec![1, 2, 3, 4, 5]);
2748 let result = unsorted.kurtosis(None, Some(-1.0));
2750 let _ = result;
2755 }
2756
2757 #[test]
2758 fn kurtosis_different_types() {
2759 let mut unsorted_u32 = Unsorted::new();
2761 unsorted_u32.extend(vec![1u32, 2, 3, 4, 5]);
2762 let result_u32 = unsorted_u32.kurtosis(None, None).unwrap();
2763
2764 let mut unsorted_i64 = Unsorted::new();
2765 unsorted_i64.extend(vec![1i64, 2, 3, 4, 5]);
2766 let result_i64 = unsorted_i64.kurtosis(None, None).unwrap();
2767
2768 assert!((result_u32 - result_i64).abs() < 1e-10);
2769 }
2770
2771 #[test]
2772 fn kurtosis_floating_point_precision() {
2773 let mut unsorted = Unsorted::new();
2775 unsorted.extend(vec![1.1, 2.2, 3.3, 4.4, 5.5]);
2776 let result = unsorted.kurtosis(None, None);
2777 assert!(result.is_some());
2778 assert!(result.unwrap().is_finite());
2779 }
2780
2781 #[test]
2782 fn kurtosis_negative_values() {
2783 let mut unsorted = Unsorted::new();
2785 unsorted.extend(vec![-5, -3, -1, 1, 3, 5]);
2786 let result = unsorted.kurtosis(None, None);
2787 assert!(result.is_some());
2788 assert!(result.unwrap().is_finite());
2789 }
2790
2791 #[test]
2792 fn kurtosis_mixed_positive_negative() {
2793 let mut unsorted = Unsorted::new();
2795 unsorted.extend(vec![-10, -5, 0, 5, 10]);
2796 let result = unsorted.kurtosis(None, None);
2797 assert!(result.is_some());
2798 assert!(result.unwrap().is_finite());
2799 }
2800
2801 #[test]
2802 fn kurtosis_duplicate_values() {
2803 let mut unsorted = Unsorted::new();
2805 unsorted.extend(vec![1, 1, 2, 2, 3, 3, 4, 4, 5, 5]);
2806 let result = unsorted.kurtosis(None, None);
2807 assert!(result.is_some());
2808 assert!(result.unwrap().is_finite());
2809 }
2810
2811 #[test]
2812 fn kurtosis_precalc_mean_wrong() {
2813 let mut unsorted1 = Unsorted::new();
2815 unsorted1.extend(vec![1, 2, 3, 4, 5]);
2816 let correct_result = unsorted1.kurtosis(None, None).unwrap();
2817
2818 let mut unsorted2 = Unsorted::new();
2819 unsorted2.extend(vec![1, 2, 3, 4, 5]);
2820 let wrong_mean = 10.0; let wrong_result = unsorted2.kurtosis(Some(wrong_mean), None).unwrap();
2822
2823 assert!((correct_result - wrong_result).abs() > 1e-5);
2825 }
2826
2827 #[test]
2828 fn percentile_rank_empty() {
2829 let mut unsorted: Unsorted<i32> = Unsorted::new();
2830 assert_eq!(unsorted.percentile_rank(5), None);
2831 let empty_vec: Vec<i32> = vec![];
2832 assert_eq!(percentile_rank(empty_vec.into_iter(), 5), None);
2833 }
2834
2835 #[test]
2836 fn percentile_rank_basic() {
2837 let mut unsorted = Unsorted::new();
2838 unsorted.extend(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
2839
2840 assert_eq!(unsorted.percentile_rank(0), Some(0.0));
2842
2843 assert_eq!(unsorted.percentile_rank(11), Some(100.0));
2845
2846 let rank = unsorted.percentile_rank(5).unwrap();
2848 assert!((rank - 50.0).abs() < 1.0);
2849
2850 let rank = unsorted.percentile_rank(1).unwrap();
2852 assert!((rank - 10.0).abs() < 1.0);
2853 }
2854
2855 #[test]
2856 fn percentile_rank_duplicates() {
2857 let mut unsorted = Unsorted::new();
2858 unsorted.extend(vec![1, 1, 2, 2, 3, 3, 4, 4, 5, 5]);
2859
2860 let rank = unsorted.percentile_rank(2).unwrap();
2862 assert!((rank - 40.0).abs() < 1.0);
2863 }
2864
2865 #[test]
2866 fn percentile_rank_stream() {
2867 let result = percentile_rank(vec![1usize, 2, 3, 4, 5].into_iter(), 3);
2868 assert_eq!(result, Some(60.0)); }
2870
2871 #[test]
2872 fn percentile_rank_many_ties() {
2873 let mut unsorted = Unsorted::new();
2875 for _ in 0..100 {
2876 unsorted.add(5u32);
2877 }
2878 for _ in 0..100 {
2879 unsorted.add(10u32);
2880 }
2881 let rank = unsorted.percentile_rank(5).unwrap();
2883 assert!((rank - 50.0).abs() < f64::EPSILON);
2884 let mut unsorted2 = Unsorted::new();
2886 for _ in 0..100 {
2887 unsorted2.add(5u32);
2888 }
2889 for _ in 0..100 {
2890 unsorted2.add(10u32);
2891 }
2892 let rank = unsorted2.percentile_rank(10).unwrap();
2893 assert!((rank - 100.0).abs() < f64::EPSILON);
2894 }
2895
2896 #[test]
2897 fn atkinson_empty() {
2898 let mut unsorted: Unsorted<i32> = Unsorted::new();
2899 assert_eq!(unsorted.atkinson(1.0, None, None), None);
2900 let empty_vec: Vec<i32> = vec![];
2901 assert_eq!(atkinson(empty_vec.into_iter(), 1.0, None, None), None);
2902 }
2903
2904 #[test]
2905 fn atkinson_single_element() {
2906 let mut unsorted = Unsorted::new();
2907 unsorted.add(5);
2908 assert_eq!(unsorted.atkinson(1.0, None, None), Some(0.0));
2909 assert_eq!(atkinson(vec![5].into_iter(), 1.0, None, None), Some(0.0));
2910 }
2911
2912 #[test]
2913 fn atkinson_perfect_equality() {
2914 let mut unsorted = Unsorted::new();
2916 unsorted.extend(vec![10, 10, 10, 10, 10]);
2917 let result = unsorted.atkinson(1.0, None, None).unwrap();
2918 assert!((result - 0.0).abs() < 1e-10);
2919 }
2920
2921 #[test]
2922 fn atkinson_epsilon_zero() {
2923 let mut unsorted = Unsorted::new();
2925 unsorted.extend(vec![1, 2, 3, 4, 5]);
2926 let result = unsorted.atkinson(0.0, None, None).unwrap();
2927 assert!((result - 0.0).abs() < 1e-10);
2928 }
2929
2930 #[test]
2931 fn atkinson_epsilon_one() {
2932 let mut unsorted = Unsorted::new();
2934 unsorted.extend(vec![1, 2, 3, 4, 5]);
2935 let result = unsorted.atkinson(1.0, None, None);
2936 assert!(result.is_some());
2937 }
2938
2939 #[test]
2940 fn atkinson_epsilon_one_rejects_nan() {
2941 let mut unsorted = Unsorted::new();
2944 unsorted.extend(vec![1.0_f64, 2.0, f64::NAN, 4.0, 5.0]);
2945 assert_eq!(unsorted.atkinson(1.0, None, None), None);
2946 }
2947
2948 #[test]
2949 fn atkinson_negative_epsilon() {
2950 let mut unsorted = Unsorted::new();
2951 unsorted.extend(vec![1, 2, 3, 4, 5]);
2952 assert_eq!(unsorted.atkinson(-1.0, None, None), None);
2953 }
2954
2955 #[test]
2956 fn atkinson_zero_mean() {
2957 let mut unsorted = Unsorted::new();
2959 unsorted.extend(vec![0, 0, 0, 0]);
2960 assert_eq!(unsorted.atkinson(1.0, None, None), None);
2961 }
2962
2963 #[test]
2964 fn atkinson_stream() {
2965 let result = atkinson(vec![1usize, 2, 3, 4, 5].into_iter(), 1.0, None, None);
2966 assert!(result.is_some());
2967 }
2968
2969 #[test]
2970 fn atkinson_precalc_mean_geometric_sum() {
2971 let mut unsorted = Unsorted::new();
2973 unsorted.extend(vec![1, 2, 3, 4, 5]);
2974
2975 let mean = 3.0f64;
2977 let geometric_sum = 1.0f64.ln() + 2.0f64.ln() + 3.0f64.ln() + 4.0f64.ln() + 5.0f64.ln();
2978
2979 let result = unsorted.atkinson(1.0, Some(mean), Some(geometric_sum));
2980 assert!(result.is_some());
2981
2982 let mut unsorted2 = Unsorted::new();
2984 unsorted2.extend(vec![1, 2, 3, 4, 5]);
2985 let result2 = unsorted2.atkinson(1.0, None, None);
2986 assert!((result.unwrap() - result2.unwrap()).abs() < 1e-10);
2987 }
2988
2989 #[test]
2990 fn atkinson_precalc_mean_only() {
2991 let mut unsorted = Unsorted::new();
2993 unsorted.extend(vec![1, 2, 3, 4, 5]);
2994 let mean = 3.0f64;
2995
2996 let result = unsorted.atkinson(1.0, Some(mean), None);
2997 assert!(result.is_some());
2998
2999 let mut unsorted2 = Unsorted::new();
3001 unsorted2.extend(vec![1, 2, 3, 4, 5]);
3002 let result2 = unsorted2.atkinson(1.0, None, None);
3003 assert!((result.unwrap() - result2.unwrap()).abs() < 1e-10);
3004 }
3005
3006 #[test]
3007 fn atkinson_precalc_geometric_sum_only() {
3008 let mut unsorted = Unsorted::new();
3010 unsorted.extend(vec![1, 2, 3, 4, 5]);
3011 let geometric_sum = 1.0f64.ln() + 2.0f64.ln() + 3.0f64.ln() + 4.0f64.ln() + 5.0f64.ln();
3012
3013 let result = unsorted.atkinson(1.0, None, Some(geometric_sum));
3014 assert!(result.is_some());
3015
3016 let mut unsorted2 = Unsorted::new();
3018 unsorted2.extend(vec![1, 2, 3, 4, 5]);
3019 let result2 = unsorted2.atkinson(1.0, None, None);
3020 assert!((result.unwrap() - result2.unwrap()).abs() < 1e-10);
3021 }
3022
3023 #[test]
3024 fn test_median_with_infinity() {
3025 let mut unsorted = Unsorted::new();
3026 unsorted.extend(vec![1.0f64, 2.0, f64::INFINITY]);
3027 assert_eq!(unsorted.median(), Some(2.0));
3028 }
3029
3030 #[test]
3031 fn test_median_with_neg_infinity() {
3032 let mut unsorted = Unsorted::new();
3033 unsorted.extend(vec![f64::NEG_INFINITY, 1.0f64, 2.0]);
3034 assert_eq!(unsorted.median(), Some(1.0));
3035 }
3036
3037 #[test]
3038 fn test_quartiles_with_infinity() {
3039 let mut unsorted = Unsorted::new();
3040 unsorted.extend(vec![f64::NEG_INFINITY, 1.0, 2.0, 3.0, f64::INFINITY]);
3041 let q = unsorted.quartiles();
3042 assert!(q.is_some());
3044 let (_, q2, _) = q.unwrap();
3045 assert_eq!(q2, 2.0);
3046 }
3047
3048 #[test]
3049 fn test_mode_with_nan() {
3050 let mut unsorted: Unsorted<f64> = Unsorted::new();
3054 unsorted.extend(vec![1.0, f64::NAN, 2.0, 2.0, 3.0]);
3055 let _result = unsorted.mode(); }
3057
3058 #[test]
3059 fn test_gini_with_infinity() {
3060 let mut unsorted = Unsorted::new();
3061 unsorted.extend(vec![1.0f64, 2.0, f64::INFINITY]);
3062 let g = unsorted.gini(None);
3063 assert!(g.unwrap().is_nan());
3067 }
3068
3069 #[test]
3070 fn test_cardinality_with_infinity() {
3071 let mut unsorted = Unsorted::new();
3072 unsorted.extend(vec![1.0f64, f64::INFINITY, f64::NEG_INFINITY, 1.0]);
3073 assert_eq!(unsorted.cardinality(false, 10_000), 3);
3074 }
3075}
3076
3077#[cfg(test)]
3078mod bench {
3079 use super::*;
3080 use std::time::Instant;
3081
3082 #[test]
3083 #[ignore] fn comprehensive_quartiles_benchmark() {
3085 let data_sizes = vec![
3087 1_000, 10_000, 100_000, 500_000, 1_000_000, 2_000_000, 5_000_000, 10_000_000,
3088 ];
3089
3090 println!("=== COMPREHENSIVE QUARTILES BENCHMARK ===\n");
3091
3092 for size in data_sizes {
3093 println!("--- Testing with {} elements ---", size);
3094
3095 let test_patterns = vec![
3097 ("Random", generate_random_data(size)),
3098 ("Reverse Sorted", {
3099 let mut v = Vec::with_capacity(size);
3100 for x in (0..size).rev() {
3101 v.push(x as i32);
3102 }
3103 v
3104 }),
3105 ("Already Sorted", {
3106 let mut v = Vec::with_capacity(size);
3107 for x in 0..size {
3108 v.push(x as i32);
3109 }
3110 v
3111 }),
3112 ("Many Duplicates", {
3113 let mut v = Vec::with_capacity(size);
3115 let chunk_size = size / 100;
3116 for i in 0..100 {
3117 v.extend(std::iter::repeat_n(i, chunk_size));
3118 }
3119 v.extend(std::iter::repeat_n(0, size - v.len()));
3121 v
3122 }),
3123 ];
3124
3125 for (pattern_name, test_data) in test_patterns {
3126 println!("\n Pattern: {}", pattern_name);
3127
3128 let mut unsorted1 = Unsorted::new();
3130 unsorted1.extend(test_data.clone());
3131
3132 let start = Instant::now();
3133 let result_sorted = unsorted1.quartiles();
3134 let sorted_time = start.elapsed();
3135
3136 let mut unsorted2 = Unsorted::new();
3138 unsorted2.extend(test_data.clone());
3139
3140 let start = Instant::now();
3141 let result_selection = unsorted2.quartiles_with_selection();
3142 let selection_time = start.elapsed();
3143
3144 let mut unsorted3 = Unsorted::new();
3146 unsorted3.extend(test_data);
3147
3148 let start = Instant::now();
3149 let result_zero_copy = unsorted3.quartiles_zero_copy();
3150 let zero_copy_time = start.elapsed();
3151
3152 assert_eq!(result_sorted, result_selection);
3154 assert_eq!(result_sorted, result_zero_copy);
3155
3156 let selection_speedup =
3157 sorted_time.as_nanos() as f64 / selection_time.as_nanos() as f64;
3158 let zero_copy_speedup =
3159 sorted_time.as_nanos() as f64 / zero_copy_time.as_nanos() as f64;
3160
3161 println!(" Sorting: {:>12?}", sorted_time);
3162 println!(
3163 " Selection: {:>12?} (speedup: {:.2}x)",
3164 selection_time, selection_speedup
3165 );
3166 println!(
3167 " Zero-copy: {:>12?} (speedup: {:.2}x)",
3168 zero_copy_time, zero_copy_speedup
3169 );
3170
3171 let best_algorithm =
3172 if zero_copy_speedup > 1.0 && zero_copy_speedup >= selection_speedup {
3173 "ZERO-COPY"
3174 } else if selection_speedup > 1.0 {
3175 "SELECTION"
3176 } else {
3177 "SORTING"
3178 };
3179 println!(" Best: {}", best_algorithm);
3180 }
3181
3182 println!(); }
3184 }
3185
3186 fn generate_random_data(size: usize) -> Vec<i32> {
3188 let mut rng = 1234567u64;
3190 let mut vec = Vec::with_capacity(size);
3191 for _ in 0..size {
3192 rng = rng.wrapping_mul(1103515245).wrapping_add(12345);
3193 vec.push((rng >> 16) as i32);
3194 }
3195 vec
3196 }
3197
3198 #[test]
3199 #[ignore] fn find_selection_threshold() {
3201 println!("=== FINDING SELECTION ALGORITHM THRESHOLD ===\n");
3202
3203 let mut found_threshold = None;
3205 let test_sizes = vec![
3206 1_000_000, 2_000_000, 3_000_000, 4_000_000, 5_000_000, 7_500_000, 10_000_000,
3207 15_000_000, 20_000_000, 25_000_000, 30_000_000,
3208 ];
3209
3210 for size in test_sizes {
3211 println!("Testing size: {}", size);
3212
3213 let test_data = generate_random_data(size);
3215
3216 let iterations = 3;
3218 let mut sorting_total = 0u128;
3219 let mut selection_total = 0u128;
3220 let mut zero_copy_total = 0u128;
3221
3222 for i in 0..iterations {
3223 println!(" Iteration {}/{}", i + 1, iterations);
3224
3225 let mut unsorted1 = Unsorted::new();
3227 unsorted1.extend(test_data.clone());
3228
3229 let start = Instant::now();
3230 let _result_sorted = unsorted1.quartiles();
3231 sorting_total += start.elapsed().as_nanos();
3232
3233 let mut unsorted2 = Unsorted::new();
3235 unsorted2.extend(test_data.clone());
3236
3237 let start = Instant::now();
3238 let _result_selection = unsorted2.quartiles_with_selection();
3239 selection_total += start.elapsed().as_nanos();
3240
3241 let mut unsorted3 = Unsorted::new();
3243 unsorted3.extend(test_data.clone());
3244
3245 let start = Instant::now();
3246 let _result_zero_copy = unsorted3.quartiles_zero_copy();
3247 zero_copy_total += start.elapsed().as_nanos();
3248 }
3249
3250 let avg_sorting = sorting_total / iterations as u128;
3251 let avg_selection = selection_total / iterations as u128;
3252 let avg_zero_copy = zero_copy_total / iterations as u128;
3253 let selection_speedup = avg_sorting as f64 / avg_selection as f64;
3254 let zero_copy_speedup = avg_sorting as f64 / avg_zero_copy as f64;
3255
3256 println!(
3257 " Average sorting: {:>12.2}ms",
3258 avg_sorting as f64 / 1_000_000.0
3259 );
3260 println!(
3261 " Average selection: {:>12.2}ms (speedup: {:.2}x)",
3262 avg_selection as f64 / 1_000_000.0,
3263 selection_speedup
3264 );
3265 println!(
3266 " Average zero-copy: {:>12.2}ms (speedup: {:.2}x)",
3267 avg_zero_copy as f64 / 1_000_000.0,
3268 zero_copy_speedup
3269 );
3270
3271 if (selection_speedup > 1.0 || zero_copy_speedup > 1.0) && found_threshold.is_none() {
3272 found_threshold = Some(size);
3273 let best_method = if zero_copy_speedup > selection_speedup {
3274 "Zero-copy"
3275 } else {
3276 "Selection"
3277 };
3278 println!(
3279 " *** THRESHOLD FOUND: {} becomes faster at {} elements ***",
3280 best_method, size
3281 );
3282 }
3283
3284 println!();
3285 }
3286
3287 match found_threshold {
3288 Some(threshold) => println!(
3289 "🎯 Selection algorithm becomes faster at approximately {} elements",
3290 threshold
3291 ),
3292 None => println!("❌ Selection algorithm did not become faster in the tested range"),
3293 }
3294 }
3295
3296 #[test]
3297 #[ignore] fn benchmark_different_data_types() {
3299 println!("=== BENCHMARKING DIFFERENT DATA TYPES ===\n");
3300
3301 let size = 5_000_000; println!("Testing with f64 data:");
3305 let float_data: Vec<f64> = generate_random_data(size)
3306 .into_iter()
3307 .map(|x| x as f64 / 1000.0)
3308 .collect();
3309
3310 let mut unsorted1 = Unsorted::new();
3311 unsorted1.extend(float_data.clone());
3312 let start = Instant::now();
3313 let _result = unsorted1.quartiles();
3314 let sorting_time = start.elapsed();
3315
3316 let mut unsorted2 = Unsorted::new();
3317 unsorted2.extend(float_data.clone());
3318 let start = Instant::now();
3319 let _result = unsorted2.quartiles_with_selection();
3320 let selection_time = start.elapsed();
3321
3322 let mut unsorted3 = Unsorted::new();
3323 unsorted3.extend(float_data);
3324 let start = Instant::now();
3325 let _result = unsorted3.quartiles_zero_copy();
3326 let zero_copy_time = start.elapsed();
3327
3328 println!(" Sorting: {:?}", sorting_time);
3329 println!(" Selection: {:?}", selection_time);
3330 println!(" Zero-copy: {:?}", zero_copy_time);
3331 println!(
3332 " Selection Speedup: {:.2}x",
3333 sorting_time.as_nanos() as f64 / selection_time.as_nanos() as f64
3334 );
3335 println!(
3336 " Zero-copy Speedup: {:.2}x\n",
3337 sorting_time.as_nanos() as f64 / zero_copy_time.as_nanos() as f64
3338 );
3339
3340 println!("Testing with i64 data:");
3342 let int64_data: Vec<i64> = generate_random_data(size)
3343 .into_iter()
3344 .map(|x| x as i64 * 1000)
3345 .collect();
3346
3347 let mut unsorted1 = Unsorted::new();
3348 unsorted1.extend(int64_data.clone());
3349 let start = Instant::now();
3350 let _result = unsorted1.quartiles();
3351 let sorting_time = start.elapsed();
3352
3353 let mut unsorted2 = Unsorted::new();
3354 unsorted2.extend(int64_data.clone());
3355 let start = Instant::now();
3356 let _result = unsorted2.quartiles_with_selection();
3357 let selection_time = start.elapsed();
3358
3359 let mut unsorted3 = Unsorted::new();
3360 unsorted3.extend(int64_data);
3361 let start = Instant::now();
3362 let _result = unsorted3.quartiles_zero_copy();
3363 let zero_copy_time = start.elapsed();
3364
3365 println!(" Sorting: {:?}", sorting_time);
3366 println!(" Selection: {:?}", selection_time);
3367 println!(" Zero-copy: {:?}", zero_copy_time);
3368 println!(
3369 " Selection Speedup: {:.2}x",
3370 sorting_time.as_nanos() as f64 / selection_time.as_nanos() as f64
3371 );
3372 println!(
3373 " Zero-copy Speedup: {:.2}x",
3374 sorting_time.as_nanos() as f64 / zero_copy_time.as_nanos() as f64
3375 );
3376 }
3377}