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);
496 let first_term_denominator = n * adj_denominator;
497 let adjustment = 3.0 * (n - 1.0) * (n - 1.0) / adj_denominator;
498 let kurtosis = ((n - 1.0) * (n + 1.0) * fourth_power_sum)
499 .mul_add(1.0 / (first_term_denominator * variance_sq), -adjustment);
500
501 Some(kurtosis)
502}
503
504fn percentile_rank_on_sorted<T, V>(data: &[Partial<T>], value: &V) -> Option<f64>
505where
506 T: PartialOrd + ToPrimitive,
507 V: PartialOrd + ToPrimitive,
508{
509 let len = data.len();
510
511 if len == 0 {
512 core::hint::cold_path();
513 return None;
514 }
515
516 let value_f64 = value.to_f64()?;
517
518 let count_leq = data.binary_search_by(|x| {
521 x.0.to_f64()
522 .unwrap_or(f64::NAN)
523 .partial_cmp(&value_f64)
524 .unwrap_or(std::cmp::Ordering::Less)
525 });
526
527 let count = match count_leq {
528 Ok(idx) => {
529 let upper = data[idx + 1..].partition_point(|x| {
532 x.0.to_f64()
533 .is_some_and(|v| v.total_cmp(&value_f64).is_le())
534 });
535 idx + 1 + upper
536 }
537 Err(idx) => idx, };
539
540 Some((count as f64 / len as f64) * 100.0)
542}
543
544fn atkinson_on_sorted<T>(
545 data: &[Partial<T>],
546 epsilon: f64,
547 precalc_mean: Option<f64>,
548 precalc_geometric_sum: Option<f64>,
549) -> Option<f64>
550where
551 T: Sync + PartialOrd + ToPrimitive,
552{
553 let len = data.len();
554
555 if len == 0 {
557 core::hint::cold_path();
558 return None;
559 }
560
561 if len == 1 {
563 core::hint::cold_path();
564 return Some(0.0);
565 }
566
567 if epsilon < 0.0 {
569 core::hint::cold_path();
570 return None;
571 }
572
573 let epsilon_is_one = (epsilon - 1.0).abs() < 1e-10;
574
575 if epsilon_is_one && precalc_mean.is_none() && precalc_geometric_sum.is_none() {
578 let (sum, ln_sum, any_invalid) = if len < PARALLEL_THRESHOLD {
582 let mut s = 0.0f64;
583 let mut ls = 0.0f64;
584 let mut bad = false;
585 for x in data {
586 let v = unsafe { x.0.to_f64().unwrap_unchecked() };
588 if v.is_nan() || v <= 0.0 {
589 bad = true;
590 } else {
591 s += v;
592 ls += v.ln();
593 }
594 }
595 (s, ls, bad)
596 } else {
597 data.par_iter()
598 .fold(
599 || (0.0f64, 0.0f64, false),
600 |(s, ls, bad), x| {
601 let v = unsafe { x.0.to_f64().unwrap_unchecked() };
603 if v.is_nan() || v <= 0.0 {
604 (s, ls, true)
605 } else {
606 (s + v, ls + v.ln(), bad)
607 }
608 },
609 )
610 .reduce(
611 || (0.0, 0.0, false),
612 |a, b| (a.0 + b.0, a.1 + b.1, a.2 || b.2),
613 )
614 };
615 if any_invalid {
616 core::hint::cold_path();
617 return None;
618 }
619 let mean = sum / len as f64;
620 if mean == 0.0 {
621 core::hint::cold_path();
622 return None;
623 }
624 let geometric_mean = (ln_sum / len as f64).exp();
625 return Some(1.0 - geometric_mean / mean);
626 }
627
628 let mean = precalc_mean.unwrap_or_else(|| {
630 let sum: f64 = if len < PARALLEL_THRESHOLD {
631 data.iter()
633 .map(|x| unsafe { x.0.to_f64().unwrap_unchecked() })
635 .sum()
636 } else {
637 data.par_iter()
638 .map(|x| unsafe { x.0.to_f64().unwrap_unchecked() })
640 .sum()
641 };
642 sum / len as f64
643 });
644
645 if mean == 0.0 {
647 core::hint::cold_path();
648 return None;
649 }
650
651 if epsilon_is_one {
655 let geometric_sum: f64 = if let Some(precalc) = precalc_geometric_sum {
657 precalc
658 } else if len < PARALLEL_THRESHOLD {
659 let mut sum = 0.0;
660 for x in data {
661 let val = unsafe { x.0.to_f64().unwrap_unchecked() };
663 if val <= 0.0 {
664 return None;
666 }
667 sum += val.ln();
668 }
669 sum
670 } else {
671 data.par_iter()
672 .map(|x| {
673 let val = unsafe { x.0.to_f64().unwrap_unchecked() };
675 if val <= 0.0 {
676 return f64::NAN;
677 }
678 val.ln()
679 })
680 .sum()
681 };
682
683 if geometric_sum.is_nan() {
684 core::hint::cold_path();
685 return None;
686 }
687
688 let geometric_mean = (geometric_sum / len as f64).exp();
689 return Some(1.0 - geometric_mean / mean);
690 }
691
692 let exponent = 1.0 - epsilon;
695 let inv_mean = mean.recip();
697
698 let sum_powered: f64 = if len < PARALLEL_THRESHOLD {
699 let mut sum = 0.0;
700 for x in data {
701 let val = unsafe { x.0.to_f64().unwrap_unchecked() };
703 if val < 0.0 {
704 return None;
706 }
707 let ratio = val * inv_mean;
708 sum += ratio.powf(exponent);
709 }
710 sum
711 } else {
712 data.par_iter()
713 .map(|x| {
714 let val = unsafe { x.0.to_f64().unwrap_unchecked() };
716 if val < 0.0 {
717 return f64::NAN;
718 }
719 let ratio = val * inv_mean;
720 ratio.powf(exponent)
721 })
722 .sum()
723 };
724
725 if sum_powered.is_nan() || sum_powered <= 0.0 {
726 core::hint::cold_path();
727 return None;
728 }
729
730 let atkinson = 1.0 - (sum_powered / len as f64).powf(1.0 / exponent);
731 Some(atkinson)
732}
733
734#[cfg(test)]
737fn quickselect<T>(data: &mut [Partial<T>], k: usize) -> Option<&T>
738where
739 T: PartialOrd,
740{
741 if data.is_empty() || k >= data.len() {
742 core::hint::cold_path();
743 return None;
744 }
745
746 let mut left = 0;
747 let mut right = data.len() - 1;
748
749 loop {
750 if left == right {
751 return Some(&data[left].0);
752 }
753
754 let pivot_idx = median_of_three_pivot(data, left, right);
756 let pivot_idx = partition(data, left, right, pivot_idx);
757
758 match k.cmp(&pivot_idx) {
759 std::cmp::Ordering::Equal => return Some(&data[pivot_idx].0),
760 std::cmp::Ordering::Less => right = pivot_idx - 1,
761 std::cmp::Ordering::Greater => left = pivot_idx + 1,
762 }
763 }
764}
765
766#[cfg(test)]
768fn median_of_three_pivot<T>(data: &[Partial<T>], left: usize, right: usize) -> usize
769where
770 T: PartialOrd,
771{
772 let mid = left + (right - left) / 2;
773
774 if data[left] <= data[mid] {
775 if data[mid] <= data[right] {
776 mid
777 } else if data[left] <= data[right] {
778 right
779 } else {
780 left
781 }
782 } else if data[left] <= data[right] {
783 left
784 } else if data[mid] <= data[right] {
785 right
786 } else {
787 mid
788 }
789}
790
791#[cfg(test)]
793fn partition<T>(data: &mut [Partial<T>], left: usize, right: usize, pivot_idx: usize) -> usize
794where
795 T: PartialOrd,
796{
797 data.swap(pivot_idx, right);
799 let mut store_idx = left;
800
801 for i in left..right {
804 if unsafe { data.get_unchecked(i) <= data.get_unchecked(right) } {
807 data.swap(i, store_idx);
808 store_idx += 1;
809 }
810 }
811
812 data.swap(store_idx, right);
814 store_idx
815}
816
817fn quartiles_on_sorted<T>(data: &[Partial<T>]) -> Option<(f64, f64, f64)>
821where
822 T: PartialOrd + ToPrimitive,
823{
824 let len = data.len();
825
826 match len {
828 0..=2 => {
829 core::hint::cold_path();
830 return None;
831 }
832 3 => {
833 return Some(
834 unsafe {
836 (
837 data.get_unchecked(0).0.to_f64()?,
838 data.get_unchecked(1).0.to_f64()?,
839 data.get_unchecked(2).0.to_f64()?,
840 )
841 },
842 );
843 }
844 _ => {}
845 }
846
847 let k = len / 4;
849 let remainder = len % 4;
850
851 unsafe {
854 Some(match remainder {
855 0 => {
856 let q1 = f64::midpoint(
867 data.get_unchecked(k - 1).0.to_f64()?,
868 data.get_unchecked(k).0.to_f64()?,
869 );
870 let q2 = f64::midpoint(
871 data.get_unchecked(2 * k - 1).0.to_f64()?,
872 data.get_unchecked(2 * k).0.to_f64()?,
873 );
874 let q3 = f64::midpoint(
875 data.get_unchecked(3 * k - 1).0.to_f64()?,
876 data.get_unchecked(3 * k).0.to_f64()?,
877 );
878 (q1, q2, q3)
879 }
880 1 => {
881 let q1 = f64::midpoint(
892 data.get_unchecked(k - 1).0.to_f64()?,
893 data.get_unchecked(k).0.to_f64()?,
894 );
895 let q2 = data.get_unchecked(2 * k).0.to_f64()?;
896 let q3 = f64::midpoint(
897 data.get_unchecked(3 * k).0.to_f64()?,
898 data.get_unchecked(3 * k + 1).0.to_f64()?,
899 );
900 (q1, q2, q3)
901 }
902 2 => {
903 let q1 = data.get_unchecked(k).0.to_f64()?;
914 let q2 = f64::midpoint(
915 data.get_unchecked(2 * k).0.to_f64()?,
916 data.get_unchecked(2 * k + 1).0.to_f64()?,
917 );
918 let q3 = data.get_unchecked(3 * k + 1).0.to_f64()?;
919 (q1, q2, q3)
920 }
921 _ => {
922 let q1 = data.get_unchecked(k).0.to_f64()?;
933 let q2 = data.get_unchecked(2 * k + 1).0.to_f64()?;
934 let q3 = data.get_unchecked(3 * k + 2).0.to_f64()?;
935 (q1, q2, q3)
936 }
937 })
938 }
939}
940
941fn quartiles_with_zero_copy_selection<T>(data: &[Partial<T>]) -> Option<(f64, f64, f64)>
950where
951 T: PartialOrd + ToPrimitive,
952{
953 let len = data.len();
954
955 match len {
957 0..=2 => {
958 core::hint::cold_path();
959 return None;
960 }
961 3 => {
962 let mut indices: Vec<usize> = (0..3).collect();
963 let cmp = |a: &usize, b: &usize| {
964 data[*a]
965 .partial_cmp(&data[*b])
966 .unwrap_or(std::cmp::Ordering::Less)
967 };
968 indices.sort_unstable_by(cmp);
969 let min_val = data[indices[0]].0.to_f64()?;
970 let med_val = data[indices[1]].0.to_f64()?;
971 let max_val = data[indices[2]].0.to_f64()?;
972 return Some((min_val, med_val, max_val));
973 }
974 _ => {}
975 }
976
977 let k = len / 4;
978 let remainder = len % 4;
979
980 let mut indices: Vec<usize> = (0..len).collect();
981 let cmp = |a: &usize, b: &usize| {
982 data[*a]
983 .partial_cmp(&data[*b])
984 .unwrap_or(std::cmp::Ordering::Less)
985 };
986
987 let raw_positions: Vec<usize> = match remainder {
992 0 => vec![k - 1, k, 2 * k - 1, 2 * k, 3 * k - 1, 3 * k],
993 1 => vec![k - 1, k, 2 * k, 3 * k, 3 * k + 1],
994 2 => vec![k, 2 * k, 2 * k + 1, 3 * k + 1],
995 _ => vec![k, 2 * k + 1, 3 * k + 2],
996 };
997
998 let mut unique_positions = raw_positions.clone();
999 unique_positions.dedup();
1000
1001 let mut start = 0;
1003 for &pos in &unique_positions {
1004 indices[start..].select_nth_unstable_by(pos - start, &cmp);
1005 start = pos + 1;
1006 }
1007
1008 let values: Vec<f64> = raw_positions
1010 .iter()
1011 .map(|&pos| data[indices[pos]].0.to_f64())
1012 .collect::<Option<Vec<_>>>()?;
1013
1014 match remainder {
1015 0 => {
1016 let q1 = f64::midpoint(values[0], values[1]);
1017 let q2 = f64::midpoint(values[2], values[3]);
1018 let q3 = f64::midpoint(values[4], values[5]);
1019 Some((q1, q2, q3))
1020 }
1021 1 => {
1022 let q1 = f64::midpoint(values[0], values[1]);
1023 let q2 = values[2];
1024 let q3 = f64::midpoint(values[3], values[4]);
1025 Some((q1, q2, q3))
1026 }
1027 2 => {
1028 let q1 = values[0];
1029 let q2 = f64::midpoint(values[1], values[2]);
1030 let q3 = values[3];
1031 Some((q1, q2, q3))
1032 }
1033 _ => Some((values[0], values[1], values[2])),
1034 }
1035}
1036
1037fn mode_on_sorted<T, I>(it: I) -> Option<T>
1038where
1039 T: PartialOrd,
1040 I: Iterator<Item = T>,
1041{
1042 use std::cmp::Ordering;
1043
1044 let (mut mode, mut next) = (None, None);
1051 let (mut mode_count, mut next_count) = (0usize, 0usize);
1052 for x in it {
1053 if mode.as_ref() == Some(&x) {
1054 mode_count += 1;
1055 } else if next.as_ref() == Some(&x) {
1056 next_count += 1;
1057 } else {
1058 next = Some(x);
1059 next_count = 0;
1060 }
1061
1062 match next_count.cmp(&mode_count) {
1063 Ordering::Greater => {
1064 mode = next;
1065 mode_count = next_count;
1066 next = None;
1067 next_count = 0;
1068 }
1069 Ordering::Equal => {
1070 mode = None;
1071 mode_count = 0;
1072 }
1073 Ordering::Less => {}
1074 }
1075 }
1076 mode
1077}
1078
1079#[allow(clippy::type_complexity)]
1107#[inline]
1108fn modes_and_antimodes_on_sorted_slice<T>(
1109 data: &[Partial<T>],
1110) -> ((Vec<T>, usize, u32), (Vec<T>, usize, u32))
1111where
1112 T: PartialOrd + Clone,
1113{
1114 let size = data.len();
1115
1116 if size == 0 {
1118 core::hint::cold_path();
1119 return ((Vec::new(), 0, 0), (Vec::new(), 0, 0));
1120 }
1121
1122 let sqrt_size = size.isqrt();
1124 let mut runs: Vec<(&T, u32)> = Vec::with_capacity(sqrt_size.clamp(16, 1_000));
1125
1126 let mut current_value = &data[0].0;
1127 let mut current_count = 1;
1128 let mut highest_count = 1;
1129 let mut lowest_count = u32::MAX;
1130
1131 for x in data.iter().skip(1) {
1133 if x.0 == *current_value {
1134 current_count += 1;
1135 highest_count = highest_count.max(current_count);
1136 } else {
1137 runs.push((current_value, current_count));
1138 lowest_count = lowest_count.min(current_count);
1139 current_value = &x.0;
1140 current_count = 1;
1141 }
1142 }
1143 runs.push((current_value, current_count));
1144 lowest_count = lowest_count.min(current_count);
1145
1146 modes_antimodes_from_runs(runs, highest_count, lowest_count)
1147}
1148
1149#[allow(clippy::type_complexity)]
1172#[inline]
1173fn modes_antimodes_from_runs<T>(
1174 mut runs: Vec<(&T, u32)>,
1175 highest_count: u32,
1176 lowest_count: u32,
1177) -> ((Vec<T>, usize, u32), (Vec<T>, usize, u32))
1178where
1179 T: Clone,
1180{
1181 if runs.is_empty() {
1183 core::hint::cold_path();
1184 return ((Vec::new(), 0, 0), (Vec::new(), 0, 0));
1185 }
1186
1187 if runs.len() == 1 {
1189 let (val, count) = runs.pop().unwrap();
1190 return ((vec![val.clone()], 1, count), (Vec::new(), 0, 0));
1191 }
1192
1193 if highest_count == 1 {
1195 let antimodes_count = runs.len().min(10);
1196 let total_count = runs.len();
1197 let mut antimodes = Vec::with_capacity(antimodes_count);
1198 for (val, _) in runs.into_iter().take(antimodes_count) {
1199 antimodes.push(val.clone());
1200 }
1201 return ((Vec::new(), 0, 0), (antimodes, total_count, 1));
1203 }
1204
1205 let estimated_modes = (runs.len() / 10).clamp(1, 10);
1208 let estimated_antimodes = 10.min(runs.len());
1209
1210 let mut modes_result = Vec::with_capacity(estimated_modes);
1211 let mut antimodes_result = Vec::with_capacity(estimated_antimodes);
1212 let mut mode_count = 0;
1213 let mut antimodes_count = 0;
1214 let mut antimodes_collected = 0_u32;
1215
1216 for (val, count) in &runs {
1217 if *count == highest_count {
1218 modes_result.push((*val).clone());
1219 mode_count += 1;
1220 }
1221 if *count == lowest_count {
1222 antimodes_count += 1;
1223 if antimodes_collected < 10 {
1224 antimodes_result.push((*val).clone());
1225 antimodes_collected += 1;
1226 }
1227 }
1228 }
1229
1230 (
1231 (modes_result, mode_count, highest_count),
1232 (antimodes_result, antimodes_count, lowest_count),
1233 )
1234}
1235
1236#[allow(clippy::unsafe_derive_deserialize)]
1244#[derive(Clone, Serialize, Deserialize)]
1245pub struct Unsorted<T> {
1246 #[serde(skip)]
1249 sorted: bool,
1250 data: Vec<Partial<T>>,
1251}
1252
1253impl<T: PartialEq> PartialEq for Unsorted<T> {
1257 fn eq(&self, other: &Self) -> bool {
1258 self.data == other.data
1259 }
1260}
1261
1262impl<T: PartialEq> Eq for Unsorted<T> where Partial<T>: Eq {}
1263
1264impl<T: PartialOrd + Send> Unsorted<T> {
1265 #[inline]
1267 #[must_use]
1268 pub fn new() -> Unsorted<T> {
1269 Default::default()
1270 }
1271
1272 #[allow(clippy::inline_always)]
1274 #[inline(always)]
1275 pub fn add(&mut self, v: T) {
1276 self.sorted = false;
1277 self.data.push(Partial(v));
1278 }
1279
1280 #[inline]
1282 #[must_use]
1283 pub const fn len(&self) -> usize {
1284 self.data.len()
1285 }
1286
1287 #[inline]
1288 #[must_use]
1289 pub const fn is_empty(&self) -> bool {
1290 self.data.is_empty()
1291 }
1292
1293 #[inline]
1294 fn sort(&mut self) {
1295 if !self.sorted {
1296 if self.data.len() < PARALLEL_THRESHOLD {
1298 self.data.sort_unstable();
1299 } else {
1300 self.data.par_sort_unstable();
1301 }
1302 self.sorted = true;
1303 }
1304 }
1305
1306 #[inline]
1307 const fn already_sorted(&mut self) {
1308 self.sorted = true;
1309 }
1310
1311 #[inline]
1313 pub fn add_bulk(&mut self, values: Vec<T>) {
1314 self.sorted = false;
1315 self.data.reserve(values.len());
1316 self.data.extend(values.into_iter().map(Partial));
1317 }
1318
1319 #[inline]
1321 pub fn shrink_to_fit(&mut self) {
1322 self.data.shrink_to_fit();
1323 }
1324
1325 #[inline]
1327 #[must_use]
1328 pub fn with_capacity(capacity: usize) -> Self {
1329 Unsorted {
1330 sorted: true,
1331 data: Vec::with_capacity(capacity),
1332 }
1333 }
1334
1335 #[inline]
1337 pub fn push_ascending(&mut self, value: T) {
1338 if let Some(last) = self.data.last() {
1339 debug_assert!(last.0 <= value, "Value must be >= than last element");
1340 }
1341 self.data.push(Partial(value));
1342 }
1344}
1345
1346impl<T: PartialOrd + PartialEq + Clone + Send + Sync> Unsorted<T> {
1347 #[inline]
1348 pub fn cardinality(&mut self, sorted: bool, parallel_threshold: usize) -> u64 {
1356 const CHUNK_SIZE: usize = 2048; const DEFAULT_PARALLEL_THRESHOLD: usize = 10_240; let len = self.data.len();
1360 match len {
1361 0 => return 0,
1362 1 => return 1,
1363 _ => {}
1364 }
1365
1366 if sorted {
1367 self.already_sorted();
1368 } else {
1369 self.sort();
1370 }
1371
1372 let use_parallel = parallel_threshold != 0
1373 && (parallel_threshold == 1
1374 || len > parallel_threshold.max(DEFAULT_PARALLEL_THRESHOLD));
1375
1376 if use_parallel {
1377 self.data
1381 .par_chunks(CHUNK_SIZE)
1382 .map(|chunk| {
1383 let mut count = u64::from(!chunk.is_empty());
1385 for [a, b] in chunk.array_windows::<2>() {
1386 if a != b {
1387 count += 1;
1388 }
1389 }
1390 (count, chunk.first(), chunk.last())
1391 })
1392 .reduce(
1393 || (0u64, None, None),
1394 |(cl, fl, ll), (cr, fr, lr)| match (ll, fr) {
1395 (None, _) => (cl + cr, fr, lr),
1399 (_, None) => (cl + cr, fl, ll),
1400 (Some(l), Some(r)) => {
1401 let adj = u64::from(l == r);
1402 (cl + cr - adj, fl, lr)
1403 }
1404 },
1405 )
1406 .0
1407 } else {
1408 let mut count = u64::from(!self.data.is_empty());
1413
1414 for [a, b] in self.data.array_windows::<2>() {
1415 if a != b {
1416 count += 1;
1417 }
1418 }
1419 count
1420 }
1421 }
1422}
1423
1424impl<T: PartialOrd + Clone + Send> Unsorted<T> {
1425 #[inline]
1427 pub fn mode(&mut self) -> Option<T> {
1428 if self.data.is_empty() {
1429 return None;
1430 }
1431 self.sort();
1432 mode_on_sorted(self.data.iter().map(|p| &p.0)).cloned()
1433 }
1434
1435 #[inline]
1439 fn modes(&mut self) -> (Vec<T>, usize, u32) {
1440 if self.data.is_empty() {
1441 return (Vec::new(), 0, 0);
1442 }
1443 self.sort();
1444 modes_and_antimodes_on_sorted_slice(&self.data).0
1445 }
1446
1447 #[inline]
1450 fn antimodes(&mut self) -> (Vec<T>, usize, u32) {
1451 if self.data.is_empty() {
1452 return (Vec::new(), 0, 0);
1453 }
1454 self.sort();
1455 modes_and_antimodes_on_sorted_slice(&self.data).1
1456 }
1457
1458 #[allow(clippy::type_complexity)]
1461 #[inline]
1462 pub fn modes_antimodes(&mut self) -> ((Vec<T>, usize, u32), (Vec<T>, usize, u32)) {
1463 if self.data.is_empty() {
1464 return ((Vec::new(), 0, 0), (Vec::new(), 0, 0));
1465 }
1466 self.sort();
1467 modes_and_antimodes_on_sorted_slice(&self.data)
1468 }
1469}
1470
1471impl Unsorted<Vec<u8>> {
1472 #[allow(clippy::inline_always)]
1479 #[inline(always)]
1480 pub fn add_bytes(&mut self, v: &[u8]) {
1481 self.sorted = false;
1482 self.data.push(Partial(v.to_vec()));
1483 }
1484}
1485
1486impl<T: PartialOrd + ToPrimitive + Send> Unsorted<T> {
1487 #[inline]
1489 pub fn median(&mut self) -> Option<f64> {
1490 if self.data.is_empty() {
1491 return None;
1492 }
1493 self.sort();
1494 median_on_sorted(&self.data)
1495 }
1496}
1497
1498impl<T: PartialOrd + ToPrimitive + Send + Sync> Unsorted<T> {
1499 #[inline]
1501 pub fn mad(&mut self, existing_median: Option<f64>) -> Option<f64> {
1502 if self.data.is_empty() {
1503 return None;
1504 }
1505 if existing_median.is_none() {
1506 self.sort();
1507 }
1508 mad_on_sorted(&self.data, existing_median)
1509 }
1510}
1511
1512impl<T: PartialOrd + ToPrimitive + Send> Unsorted<T> {
1513 #[inline]
1518 pub fn quartiles(&mut self) -> Option<(f64, f64, f64)> {
1519 if self.data.is_empty() {
1520 return None;
1521 }
1522 self.sort();
1523 quartiles_on_sorted(&self.data)
1524 }
1525}
1526
1527impl<T: PartialOrd + ToPrimitive + Send + Sync> Unsorted<T> {
1528 #[inline]
1534 pub fn gini(&mut self, precalc_sum: Option<f64>) -> Option<f64> {
1535 if self.data.is_empty() {
1536 return None;
1537 }
1538 self.sort();
1539 gini_on_sorted(&self.data, precalc_sum)
1540 }
1541
1542 #[inline]
1549 pub fn kurtosis(
1550 &mut self,
1551 precalc_mean: Option<f64>,
1552 precalc_variance: Option<f64>,
1553 ) -> Option<f64> {
1554 if self.data.is_empty() {
1555 return None;
1556 }
1557 self.sort();
1558 kurtosis_on_sorted(&self.data, precalc_mean, precalc_variance)
1559 }
1560
1561 #[inline]
1568 #[allow(clippy::needless_pass_by_value)]
1569 pub fn percentile_rank<V>(&mut self, value: V) -> Option<f64>
1570 where
1571 V: PartialOrd + ToPrimitive,
1572 {
1573 if self.data.is_empty() {
1574 return None;
1575 }
1576 self.sort();
1577 percentile_rank_on_sorted(&self.data, &value)
1578 }
1579
1580 #[inline]
1597 pub fn atkinson(
1598 &mut self,
1599 epsilon: f64,
1600 precalc_mean: Option<f64>,
1601 precalc_geometric_sum: Option<f64>,
1602 ) -> Option<f64> {
1603 if self.data.is_empty() {
1604 return None;
1605 }
1606 self.sort();
1607 atkinson_on_sorted(&self.data, epsilon, precalc_mean, precalc_geometric_sum)
1608 }
1609}
1610
1611impl<T: PartialOrd + ToPrimitive + Clone + Send> Unsorted<T> {
1612 #[inline]
1624 pub fn quartiles_with_selection(&mut self) -> Option<(f64, f64, f64)> {
1625 if self.data.is_empty() {
1626 return None;
1627 }
1628 quartiles_with_zero_copy_selection(&self.data)
1630 }
1631}
1632
1633impl<T: PartialOrd + ToPrimitive + Send> Unsorted<T> {
1634 #[inline]
1640 #[must_use]
1641 pub fn quartiles_zero_copy(&self) -> Option<(f64, f64, f64)> {
1642 if self.data.is_empty() {
1643 return None;
1644 }
1645 quartiles_with_zero_copy_selection(&self.data)
1646 }
1647}
1648
1649impl<T: PartialOrd + Send> Commute for Unsorted<T> {
1650 #[inline]
1651 fn merge(&mut self, mut v: Unsorted<T>) {
1652 if v.is_empty() {
1653 return;
1654 }
1655
1656 self.sorted = false;
1657 self.data.extend(std::mem::take(&mut v.data));
1659 }
1660}
1661
1662impl<T: PartialOrd> Default for Unsorted<T> {
1663 #[inline]
1664 fn default() -> Unsorted<T> {
1665 Unsorted {
1666 data: Vec::with_capacity(16),
1667 sorted: true, }
1669 }
1670}
1671
1672impl<T: PartialOrd + Send> FromIterator<T> for Unsorted<T> {
1673 #[inline]
1674 fn from_iter<I: IntoIterator<Item = T>>(it: I) -> Unsorted<T> {
1675 let mut v = Unsorted::new();
1676 v.extend(it);
1677 v
1678 }
1679}
1680
1681impl<T: PartialOrd> Extend<T> for Unsorted<T> {
1682 #[inline]
1683 fn extend<I: IntoIterator<Item = T>>(&mut self, it: I) {
1684 self.sorted = false;
1685 self.data.extend(it.into_iter().map(Partial));
1686 }
1687}
1688
1689fn custom_percentiles_on_sorted<T>(data: &[Partial<T>], percentiles: &[u8]) -> Option<Vec<T>>
1690where
1691 T: PartialOrd + Clone,
1692{
1693 let len = data.len();
1694
1695 if len == 0 || percentiles.iter().any(|&p| p > 100) {
1697 return None;
1698 }
1699
1700 let unique_percentiles: Vec<u8> = if percentiles.len() <= 1 {
1702 percentiles.to_vec()
1704 } else {
1705 let is_sorted_unique = percentiles.array_windows::<2>().all(|[a, b]| a < b);
1707
1708 if is_sorted_unique {
1709 percentiles.to_vec()
1711 } else {
1712 let mut seen = [false; 101];
1714 let mut sorted_unique = Vec::with_capacity(percentiles.len().min(101));
1715 for &p in percentiles {
1716 if !seen[p as usize] {
1717 seen[p as usize] = true;
1718 sorted_unique.push(p);
1719 }
1720 }
1721 sorted_unique.sort_unstable();
1722 sorted_unique
1723 }
1724 };
1725
1726 let mut results = Vec::with_capacity(unique_percentiles.len());
1727
1728 unsafe {
1732 for &p in &unique_percentiles {
1733 #[allow(clippy::cast_sign_loss)]
1737 let rank = ((f64::from(p) / 100.0) * len as f64).ceil() as usize;
1738
1739 let idx = rank.saturating_sub(1);
1741
1742 results.push(data.get_unchecked(idx).0.clone());
1744 }
1745 }
1746
1747 Some(results)
1748}
1749
1750impl<T: PartialOrd + Clone + Send> Unsorted<T> {
1751 #[inline]
1773 pub fn custom_percentiles(&mut self, percentiles: &[u8]) -> Option<Vec<T>> {
1774 if self.data.is_empty() {
1775 return None;
1776 }
1777 self.sort();
1778 custom_percentiles_on_sorted(&self.data, percentiles)
1779 }
1780}
1781
1782#[cfg(test)]
1783mod test {
1784 use super::*;
1785
1786 #[test]
1787 fn test_cardinality_empty() {
1788 let mut unsorted: Unsorted<i32> = Unsorted::new();
1789 assert_eq!(unsorted.cardinality(false, 1), 0);
1790 }
1791
1792 #[test]
1793 fn test_cardinality_single_element() {
1794 let mut unsorted = Unsorted::new();
1795 unsorted.add(5);
1796 assert_eq!(unsorted.cardinality(false, 1), 1);
1797 }
1798
1799 #[test]
1800 fn test_cardinality_unique_elements() {
1801 let mut unsorted = Unsorted::new();
1802 unsorted.extend(vec![1, 2, 3, 4, 5]);
1803 assert_eq!(unsorted.cardinality(false, 1), 5);
1804 }
1805
1806 #[test]
1807 fn test_cardinality_duplicate_elements() {
1808 let mut unsorted = Unsorted::new();
1809 unsorted.extend(vec![1, 2, 2, 3, 3, 3, 4, 4, 4, 4]);
1810 assert_eq!(unsorted.cardinality(false, 1), 4);
1811 }
1812
1813 #[test]
1814 fn test_cardinality_all_same() {
1815 let mut unsorted = Unsorted::new();
1816 unsorted.extend(vec![1; 100]);
1817 assert_eq!(unsorted.cardinality(false, 1), 1);
1818 }
1819
1820 #[test]
1821 fn test_cardinality_large_range() {
1822 let mut unsorted = Unsorted::new();
1823 unsorted.extend(0..1_000_000);
1824 assert_eq!(unsorted.cardinality(false, 1), 1_000_000);
1825 }
1826
1827 #[test]
1828 fn test_cardinality_large_range_sequential() {
1829 let mut unsorted = Unsorted::new();
1830 unsorted.extend(0..1_000_000);
1831 assert_eq!(unsorted.cardinality(false, 2_000_000), 1_000_000);
1832 }
1833
1834 #[test]
1835 fn test_cardinality_presorted() {
1836 let mut unsorted = Unsorted::new();
1837 unsorted.extend(vec![1, 2, 3, 4, 5]);
1838 unsorted.sort();
1839 assert_eq!(unsorted.cardinality(true, 1), 5);
1840 }
1841
1842 #[test]
1843 fn test_cardinality_float() {
1844 let mut unsorted = Unsorted::new();
1845 unsorted.extend(vec![1.0, 1.0, 2.0, 3.0, 3.0, 4.0]);
1846 assert_eq!(unsorted.cardinality(false, 1), 4);
1847 }
1848
1849 #[test]
1850 fn test_cardinality_string() {
1851 let mut unsorted = Unsorted::new();
1852 unsorted.extend(vec!["a", "b", "b", "c", "c", "c"]);
1853 assert_eq!(unsorted.cardinality(false, 1), 3);
1854 }
1855
1856 #[test]
1857 fn test_quartiles_selection_vs_sorted() {
1858 let test_cases = vec![
1860 vec![3, 5, 7, 9],
1861 vec![3, 5, 7],
1862 vec![1, 2, 7, 11],
1863 vec![3, 5, 7, 9, 12],
1864 vec![2, 2, 3, 8, 10],
1865 vec![3, 5, 7, 9, 12, 20],
1866 vec![0, 2, 4, 8, 10, 11],
1867 vec![3, 5, 7, 9, 12, 20, 21],
1868 vec![1, 5, 6, 6, 7, 10, 19],
1869 ];
1870
1871 for test_case in test_cases {
1872 let mut unsorted1 = Unsorted::new();
1873 let mut unsorted2 = Unsorted::new();
1874 let mut unsorted3 = Unsorted::new();
1875 unsorted1.extend(test_case.clone());
1876 unsorted2.extend(test_case.clone());
1877 unsorted3.extend(test_case.clone());
1878
1879 let result_sorted = unsorted1.quartiles();
1880 let result_selection = unsorted2.quartiles_with_selection();
1881 let result_zero_copy = unsorted3.quartiles_zero_copy();
1882
1883 assert_eq!(
1884 result_sorted, result_selection,
1885 "Selection mismatch for test case: {:?}",
1886 test_case
1887 );
1888 assert_eq!(
1889 result_sorted, result_zero_copy,
1890 "Zero-copy mismatch for test case: {:?}",
1891 test_case
1892 );
1893 }
1894 }
1895
1896 #[test]
1897 fn test_quartiles_with_selection_small() {
1898 let mut unsorted: Unsorted<i32> = Unsorted::new();
1900 assert_eq!(unsorted.quartiles_with_selection(), None);
1901
1902 let mut unsorted = Unsorted::new();
1903 unsorted.extend(vec![1, 2]);
1904 assert_eq!(unsorted.quartiles_with_selection(), None);
1905
1906 let mut unsorted = Unsorted::new();
1907 unsorted.extend(vec![1, 2, 3]);
1908 assert_eq!(unsorted.quartiles_with_selection(), Some((1.0, 2.0, 3.0)));
1909 }
1910
1911 #[test]
1912 fn test_quickselect() {
1913 let data = vec![
1914 Partial(3),
1915 Partial(1),
1916 Partial(4),
1917 Partial(1),
1918 Partial(5),
1919 Partial(9),
1920 Partial(2),
1921 Partial(6),
1922 ];
1923
1924 assert_eq!(quickselect(&mut data.clone(), 0), Some(&1));
1926 assert_eq!(quickselect(&mut data.clone(), 3), Some(&3));
1927 assert_eq!(quickselect(&mut data.clone(), 7), Some(&9));
1928
1929 let mut empty: Vec<Partial<i32>> = vec![];
1931 assert_eq!(quickselect(&mut empty, 0), None);
1932
1933 let mut data = vec![Partial(3), Partial(1), Partial(4), Partial(1), Partial(5)];
1934 assert_eq!(quickselect(&mut data, 10), None); }
1936
1937 #[test]
1938 fn median_stream() {
1939 assert_eq!(median(vec![3usize, 5, 7, 9].into_iter()), Some(6.0));
1940 assert_eq!(median(vec![3usize, 5, 7].into_iter()), Some(5.0));
1941 }
1942
1943 #[test]
1944 fn mad_stream() {
1945 assert_eq!(mad(vec![3usize, 5, 7, 9].into_iter(), None), Some(2.0));
1946 assert_eq!(
1947 mad(
1948 vec![
1949 86usize, 60, 95, 39, 49, 12, 56, 82, 92, 24, 33, 28, 46, 34, 100, 39, 100, 38,
1950 50, 61, 39, 88, 5, 13, 64
1951 ]
1952 .into_iter(),
1953 None
1954 ),
1955 Some(16.0)
1956 );
1957 }
1958
1959 #[test]
1960 fn mad_stream_precalc_median() {
1961 let data = vec![3usize, 5, 7, 9].into_iter();
1962 let median1 = median(data.clone());
1963 assert_eq!(mad(data, median1), Some(2.0));
1964
1965 let data2 = vec![
1966 86usize, 60, 95, 39, 49, 12, 56, 82, 92, 24, 33, 28, 46, 34, 100, 39, 100, 38, 50, 61,
1967 39, 88, 5, 13, 64,
1968 ]
1969 .into_iter();
1970 let median2 = median(data2.clone());
1971 assert_eq!(mad(data2, median2), Some(16.0));
1972 }
1973
1974 #[test]
1975 fn mode_stream() {
1976 assert_eq!(mode(vec![3usize, 5, 7, 9].into_iter()), None);
1977 assert_eq!(mode(vec![3usize, 3, 3, 3].into_iter()), Some(3));
1978 assert_eq!(mode(vec![3usize, 3, 3, 4].into_iter()), Some(3));
1979 assert_eq!(mode(vec![4usize, 3, 3, 3].into_iter()), Some(3));
1980 assert_eq!(mode(vec![1usize, 1, 2, 3, 3].into_iter()), None);
1981 }
1982
1983 #[test]
1984 fn median_floats() {
1985 assert_eq!(median(vec![3.0f64, 5.0, 7.0, 9.0].into_iter()), Some(6.0));
1986 assert_eq!(median(vec![3.0f64, 5.0, 7.0].into_iter()), Some(5.0));
1987 }
1988
1989 #[test]
1990 fn mode_floats() {
1991 assert_eq!(mode(vec![3.0f64, 5.0, 7.0, 9.0].into_iter()), None);
1992 assert_eq!(mode(vec![3.0f64, 3.0, 3.0, 3.0].into_iter()), Some(3.0));
1993 assert_eq!(mode(vec![3.0f64, 3.0, 3.0, 4.0].into_iter()), Some(3.0));
1994 assert_eq!(mode(vec![4.0f64, 3.0, 3.0, 3.0].into_iter()), Some(3.0));
1995 assert_eq!(mode(vec![1.0f64, 1.0, 2.0, 3.0, 3.0].into_iter()), None);
1996 }
1997
1998 #[test]
1999 fn modes_stream() {
2000 assert_eq!(modes(vec![3usize, 5, 7, 9].into_iter()), (vec![], 0, 0));
2001 assert_eq!(modes(vec![3usize, 3, 3, 3].into_iter()), (vec![3], 1, 4));
2002 assert_eq!(modes(vec![3usize, 3, 4, 4].into_iter()), (vec![3, 4], 2, 2));
2003 assert_eq!(modes(vec![4usize, 3, 3, 3].into_iter()), (vec![3], 1, 3));
2004 assert_eq!(modes(vec![1usize, 1, 2, 2].into_iter()), (vec![1, 2], 2, 2));
2005 let vec: Vec<u32> = vec![];
2006 assert_eq!(modes(vec.into_iter()), (vec![], 0, 0));
2007 }
2008
2009 #[test]
2010 fn modes_floats() {
2011 assert_eq!(
2012 modes(vec![3_f64, 5.0, 7.0, 9.0].into_iter()),
2013 (vec![], 0, 0)
2014 );
2015 assert_eq!(
2016 modes(vec![3_f64, 3.0, 3.0, 3.0].into_iter()),
2017 (vec![3.0], 1, 4)
2018 );
2019 assert_eq!(
2020 modes(vec![3_f64, 3.0, 4.0, 4.0].into_iter()),
2021 (vec![3.0, 4.0], 2, 2)
2022 );
2023 assert_eq!(
2024 modes(vec![1_f64, 1.0, 2.0, 3.0, 3.0].into_iter()),
2025 (vec![1.0, 3.0], 2, 2)
2026 );
2027 }
2028
2029 #[test]
2030 fn antimodes_stream() {
2031 assert_eq!(
2032 antimodes(vec![3usize, 5, 7, 9].into_iter()),
2033 (vec![3, 5, 7, 9], 4, 1)
2034 );
2035 assert_eq!(
2036 antimodes(vec![1usize, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13].into_iter()),
2037 (vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 13, 1)
2038 );
2039 assert_eq!(
2040 antimodes(vec![1usize, 3, 3, 3].into_iter()),
2041 (vec![1], 1, 1)
2042 );
2043 assert_eq!(
2044 antimodes(vec![3usize, 3, 4, 4].into_iter()),
2045 (vec![3, 4], 2, 2)
2046 );
2047 assert_eq!(
2048 antimodes(
2049 vec![
2050 3usize, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13,
2051 14, 14, 15, 15
2052 ]
2053 .into_iter()
2054 ),
2055 (vec![3, 4, 5, 6, 7, 8, 9, 10, 11, 12], 13, 2)
2057 );
2058 assert_eq!(
2059 antimodes(
2060 vec![
2061 3usize, 3, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 4, 4, 5, 5, 6, 6, 7, 7, 13, 13,
2062 14, 14, 15, 15
2063 ]
2064 .into_iter()
2065 ),
2066 (vec![3, 4, 5, 6, 7, 8, 9, 10, 11, 12], 13, 2)
2067 );
2068 assert_eq!(
2069 antimodes(vec![3usize, 3, 3, 4].into_iter()),
2070 (vec![4], 1, 1)
2071 );
2072 assert_eq!(
2073 antimodes(vec![4usize, 3, 3, 3].into_iter()),
2074 (vec![4], 1, 1)
2075 );
2076 assert_eq!(
2077 antimodes(vec![1usize, 1, 2, 2].into_iter()),
2078 (vec![1, 2], 2, 2)
2079 );
2080 let vec: Vec<u32> = vec![];
2081 assert_eq!(antimodes(vec.into_iter()), (vec![], 0, 0));
2082 }
2083
2084 #[test]
2085 fn antimodes_floats() {
2086 assert_eq!(
2087 antimodes(vec![3_f64, 5.0, 7.0, 9.0].into_iter()),
2088 (vec![3.0, 5.0, 7.0, 9.0], 4, 1)
2089 );
2090 assert_eq!(
2091 antimodes(vec![3_f64, 3.0, 3.0, 3.0].into_iter()),
2092 (vec![], 0, 0)
2093 );
2094 assert_eq!(
2095 antimodes(vec![3_f64, 3.0, 4.0, 4.0].into_iter()),
2096 (vec![3.0, 4.0], 2, 2)
2097 );
2098 assert_eq!(
2099 antimodes(vec![1_f64, 1.0, 2.0, 3.0, 3.0].into_iter()),
2100 (vec![2.0], 1, 1)
2101 );
2102 }
2103
2104 #[test]
2105 fn test_custom_percentiles() {
2106 let mut unsorted: Unsorted<i32> = Unsorted::new();
2108 unsorted.extend(1..=11); let result = unsorted.custom_percentiles(&[25, 50, 75]).unwrap();
2111 assert_eq!(result, vec![3, 6, 9]);
2112
2113 let mut str_data = Unsorted::new();
2115 str_data.extend(vec!["a", "b", "c", "d", "e"]);
2116 let result = str_data.custom_percentiles(&[20, 40, 60, 80]).unwrap();
2117 assert_eq!(result, vec!["a", "b", "c", "d"]);
2118
2119 let mut char_data = Unsorted::new();
2121 char_data.extend('a'..='e');
2122 let result = char_data.custom_percentiles(&[25, 50, 75]).unwrap();
2123 assert_eq!(result, vec!['b', 'c', 'd']);
2124
2125 let mut float_data = Unsorted::new();
2127 float_data.extend(vec![1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9]);
2128 let result = float_data
2129 .custom_percentiles(&[10, 30, 50, 70, 90])
2130 .unwrap();
2131 assert_eq!(result, vec![1.1, 3.3, 5.5, 7.7, 9.9]);
2132
2133 let result = float_data.custom_percentiles(&[]).unwrap();
2135 assert_eq!(result, Vec::<f64>::new());
2136
2137 let result = float_data.custom_percentiles(&[50, 50, 50]).unwrap();
2139 assert_eq!(result, vec![5.5]);
2140
2141 let result = float_data.custom_percentiles(&[0, 100]).unwrap();
2143 assert_eq!(result, vec![1.1, 9.9]);
2144
2145 let result = float_data.custom_percentiles(&[75, 25, 50]).unwrap();
2147 assert_eq!(result, vec![3.3, 5.5, 7.7]); let mut single = Unsorted::new();
2151 single.add(42);
2152 let result = single.custom_percentiles(&[0, 50, 100]).unwrap();
2153 assert_eq!(result, vec![42, 42, 42]);
2154 }
2155
2156 #[test]
2157 fn quartiles_stream() {
2158 assert_eq!(
2159 quartiles(vec![3usize, 5, 7].into_iter()),
2160 Some((3., 5., 7.))
2161 );
2162 assert_eq!(
2163 quartiles(vec![3usize, 5, 7, 9].into_iter()),
2164 Some((4., 6., 8.))
2165 );
2166 assert_eq!(
2167 quartiles(vec![1usize, 2, 7, 11].into_iter()),
2168 Some((1.5, 4.5, 9.))
2169 );
2170 assert_eq!(
2171 quartiles(vec![3usize, 5, 7, 9, 12].into_iter()),
2172 Some((4., 7., 10.5))
2173 );
2174 assert_eq!(
2175 quartiles(vec![2usize, 2, 3, 8, 10].into_iter()),
2176 Some((2., 3., 9.))
2177 );
2178 assert_eq!(
2179 quartiles(vec![3usize, 5, 7, 9, 12, 20].into_iter()),
2180 Some((5., 8., 12.))
2181 );
2182 assert_eq!(
2183 quartiles(vec![0usize, 2, 4, 8, 10, 11].into_iter()),
2184 Some((2., 6., 10.))
2185 );
2186 assert_eq!(
2187 quartiles(vec![3usize, 5, 7, 9, 12, 20, 21].into_iter()),
2188 Some((5., 9., 20.))
2189 );
2190 assert_eq!(
2191 quartiles(vec![1usize, 5, 6, 6, 7, 10, 19].into_iter()),
2192 Some((5., 6., 10.))
2193 );
2194 }
2195
2196 #[test]
2197 fn quartiles_floats() {
2198 assert_eq!(
2199 quartiles(vec![3_f64, 5., 7.].into_iter()),
2200 Some((3., 5., 7.))
2201 );
2202 assert_eq!(
2203 quartiles(vec![3_f64, 5., 7., 9.].into_iter()),
2204 Some((4., 6., 8.))
2205 );
2206 assert_eq!(
2207 quartiles(vec![3_f64, 5., 7., 9., 12.].into_iter()),
2208 Some((4., 7., 10.5))
2209 );
2210 assert_eq!(
2211 quartiles(vec![3_f64, 5., 7., 9., 12., 20.].into_iter()),
2212 Some((5., 8., 12.))
2213 );
2214 assert_eq!(
2215 quartiles(vec![3_f64, 5., 7., 9., 12., 20., 21.].into_iter()),
2216 Some((5., 9., 20.))
2217 );
2218 }
2219
2220 #[test]
2221 fn test_quartiles_zero_copy_small() {
2222 let unsorted: Unsorted<i32> = Unsorted::new();
2224 assert_eq!(unsorted.quartiles_zero_copy(), None);
2225
2226 let mut unsorted = Unsorted::new();
2227 unsorted.extend(vec![1, 2]);
2228 assert_eq!(unsorted.quartiles_zero_copy(), None);
2229
2230 let mut unsorted = Unsorted::new();
2231 unsorted.extend(vec![1, 2, 3]);
2232 assert_eq!(unsorted.quartiles_zero_copy(), Some((1.0, 2.0, 3.0)));
2233
2234 let mut unsorted = Unsorted::new();
2236 unsorted.extend(vec![3, 5, 7, 9]);
2237 assert_eq!(unsorted.quartiles_zero_copy(), Some((4.0, 6.0, 8.0)));
2238 }
2239
2240 #[test]
2241 fn gini_empty() {
2242 let mut unsorted: Unsorted<i32> = Unsorted::new();
2243 assert_eq!(unsorted.gini(None), None);
2244 let empty_vec: Vec<i32> = vec![];
2245 assert_eq!(gini(empty_vec.into_iter(), None), None);
2246 }
2247
2248 #[test]
2249 fn gini_single_element() {
2250 let mut unsorted = Unsorted::new();
2251 unsorted.add(5);
2252 assert_eq!(unsorted.gini(None), Some(0.0));
2253 assert_eq!(gini(vec![5].into_iter(), None), Some(0.0));
2254 }
2255
2256 #[test]
2257 fn gini_perfect_equality() {
2258 let mut unsorted = Unsorted::new();
2260 unsorted.extend(vec![10, 10, 10, 10, 10]);
2261 let result = unsorted.gini(None).unwrap();
2262 assert!((result - 0.0).abs() < 1e-10, "Expected 0.0, got {}", result);
2263
2264 assert!((gini(vec![10, 10, 10, 10, 10].into_iter(), None).unwrap() - 0.0).abs() < 1e-10);
2265 }
2266
2267 #[test]
2268 fn gini_perfect_inequality() {
2269 let mut unsorted = Unsorted::new();
2272 unsorted.extend(vec![0, 0, 0, 0, 100]);
2273 let result = unsorted.gini(None).unwrap();
2274 assert!((result - 0.8).abs() < 1e-10, "Expected 0.8, got {}", result);
2277 }
2278
2279 #[test]
2280 fn gini_stream() {
2281 let result = gini(vec![1usize, 2, 3, 4, 5].into_iter(), None).unwrap();
2288 let expected = (2.0 * 55.0) / (5.0 * 15.0) - 6.0 / 5.0;
2289 assert!(
2290 (result - expected).abs() < 1e-10,
2291 "Expected {}, got {}",
2292 expected,
2293 result
2294 );
2295 }
2296
2297 #[test]
2298 fn gini_floats() {
2299 let mut unsorted = Unsorted::new();
2300 unsorted.extend(vec![1.0, 2.0, 3.0, 4.0, 5.0]);
2301 let result = unsorted.gini(None).unwrap();
2302 let expected = (2.0 * 55.0) / (5.0 * 15.0) - 6.0 / 5.0;
2303 assert!((result - expected).abs() < 1e-10);
2304
2305 assert!(
2306 (gini(vec![1.0f64, 2.0, 3.0, 4.0, 5.0].into_iter(), None).unwrap() - expected).abs()
2307 < 1e-10
2308 );
2309 }
2310
2311 #[test]
2312 fn gini_all_zeros() {
2313 let mut unsorted = Unsorted::new();
2315 unsorted.extend(vec![0, 0, 0, 0]);
2316 assert_eq!(unsorted.gini(None), None);
2317 assert_eq!(gini(vec![0, 0, 0, 0].into_iter(), None), None);
2318 }
2319
2320 #[test]
2321 fn gini_negative_values() {
2322 let mut unsorted = Unsorted::new();
2324 unsorted.extend(vec![-5, -3, -1, 1, 3, 5]);
2325 let result = unsorted.gini(None);
2326 assert_eq!(result, None);
2328
2329 let mut unsorted = Unsorted::new();
2331 unsorted.extend(vec![-2, -1, 0, 1, 2]);
2332 let result = unsorted.gini(None);
2333 assert_eq!(result, None);
2335
2336 let mut unsorted = Unsorted::new();
2339 unsorted.extend(vec![-1, 0, 1, 2, 3]);
2340 let result = unsorted.gini(None);
2341 assert_eq!(result, None);
2342 }
2343
2344 #[test]
2345 fn gini_known_cases() {
2346 let mut unsorted = Unsorted::new();
2348 unsorted.extend(vec![1, 1, 1, 1, 1]);
2349 let result = unsorted.gini(None).unwrap();
2350 assert!((result - 0.0).abs() < 1e-10);
2351
2352 let mut unsorted = Unsorted::new();
2354 unsorted.extend(vec![0, 0, 0, 0, 1]);
2355 let result = unsorted.gini(None).unwrap();
2356 assert!((result - 0.8).abs() < 1e-10);
2358
2359 let mut unsorted = Unsorted::new();
2361 unsorted.extend(vec![1, 2, 3]);
2362 let result = unsorted.gini(None).unwrap();
2363 let expected = (2.0 * 14.0) / (3.0 * 6.0) - 4.0 / 3.0;
2366 assert!((result - expected).abs() < 1e-10);
2367 }
2368
2369 #[test]
2370 fn gini_precalc_sum() {
2371 let mut unsorted = Unsorted::new();
2373 unsorted.extend(vec![1, 2, 3, 4, 5]);
2374 let precalc_sum = Some(15.0);
2375 let result = unsorted.gini(precalc_sum).unwrap();
2376 let expected = (2.0 * 55.0) / (5.0 * 15.0) - 6.0 / 5.0;
2377 assert!((result - expected).abs() < 1e-10);
2378
2379 let mut unsorted2 = Unsorted::new();
2381 unsorted2.extend(vec![1, 2, 3, 4, 5]);
2382 let result2 = unsorted2.gini(None).unwrap();
2383 assert!((result - result2).abs() < 1e-10);
2384 }
2385
2386 #[test]
2387 fn gini_large_dataset() {
2388 let data: Vec<i32> = (1..=1000).collect();
2390 let result = gini(data.iter().copied(), None);
2391 assert!(result.is_some());
2392 let gini_val = result.unwrap();
2393 assert!(gini_val > 0.0 && gini_val < 0.5);
2395 }
2396
2397 #[test]
2398 fn gini_unsorted_vs_sorted() {
2399 let mut unsorted1 = Unsorted::new();
2401 unsorted1.extend(vec![5, 2, 8, 1, 9, 3, 7, 4, 6]);
2402 let result1 = unsorted1.gini(None).unwrap();
2403
2404 let mut unsorted2 = Unsorted::new();
2405 unsorted2.extend(vec![1, 2, 3, 4, 5, 6, 7, 8, 9]);
2406 let result2 = unsorted2.gini(None).unwrap();
2407
2408 assert!((result1 - result2).abs() < 1e-10);
2409 }
2410
2411 #[test]
2412 fn gini_small_values() {
2413 let mut unsorted = Unsorted::new();
2415 unsorted.extend(vec![0.001, 0.002, 0.003, 0.004, 0.005]);
2416 let result = unsorted.gini(None);
2417 assert!(result.is_some());
2418 let expected = (2.0 * 55.0) / (5.0 * 15.0) - 6.0 / 5.0;
2420 assert!((result.unwrap() - expected).abs() < 1e-10);
2421 }
2422
2423 #[test]
2424 fn gini_large_values() {
2425 let mut unsorted = Unsorted::new();
2427 unsorted.extend(vec![1000, 2000, 3000, 4000, 5000]);
2428 let result = unsorted.gini(None);
2429 assert!(result.is_some());
2430 let expected = (2.0 * 55.0) / (5.0 * 15.0) - 6.0 / 5.0;
2432 assert!((result.unwrap() - expected).abs() < 1e-10);
2433 }
2434
2435 #[test]
2436 fn gini_two_elements() {
2437 let mut unsorted = Unsorted::new();
2439 unsorted.extend(vec![1, 2]);
2440 let result = unsorted.gini(None).unwrap();
2441 let expected = (2.0 * 5.0) / (2.0 * 3.0) - 3.0 / 2.0;
2444 assert!((result - expected).abs() < 1e-10);
2445 }
2446
2447 #[test]
2448 fn gini_precalc_sum_zero() {
2449 let mut unsorted = Unsorted::new();
2451 unsorted.extend(vec![1, 2, 3, 4, 5]);
2452 let result = unsorted.gini(Some(0.0));
2453 assert_eq!(result, None);
2454 }
2455
2456 #[test]
2457 fn gini_precalc_sum_negative() {
2458 let mut unsorted = Unsorted::new();
2460 unsorted.extend(vec![-5, -3, -1, 1, 3]);
2461 let result = unsorted.gini(None);
2462 assert_eq!(result, None);
2463
2464 let mut unsorted = Unsorted::new();
2466 unsorted.extend(vec![1, 2, 3]);
2467 let result = unsorted.gini(Some(-5.0));
2468 assert_eq!(result, None);
2469 }
2470
2471 #[test]
2472 fn gini_different_types() {
2473 let mut unsorted_u32 = Unsorted::new();
2475 unsorted_u32.extend(vec![1u32, 2, 3, 4, 5]);
2476 let result_u32 = unsorted_u32.gini(None).unwrap();
2477
2478 let mut unsorted_i64 = Unsorted::new();
2479 unsorted_i64.extend(vec![1i64, 2, 3, 4, 5]);
2480 let result_i64 = unsorted_i64.gini(None).unwrap();
2481
2482 let expected = (2.0 * 55.0) / (5.0 * 15.0) - 6.0 / 5.0;
2483 assert!((result_u32 - expected).abs() < 1e-10);
2484 assert!((result_i64 - expected).abs() < 1e-10);
2485 }
2486
2487 #[test]
2488 fn gini_extreme_inequality() {
2489 let mut unsorted = Unsorted::new();
2491 unsorted.extend(vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 1000]);
2492 let result = unsorted.gini(None).unwrap();
2493 assert!((result - 0.9).abs() < 1e-10);
2496 }
2497
2498 #[test]
2499 fn gini_duplicate_values() {
2500 let mut unsorted = Unsorted::new();
2502 unsorted.extend(vec![1, 1, 1, 5, 5, 5, 10, 10, 10]);
2503 let result = unsorted.gini(None);
2504 assert!(result.is_some());
2505 let gini_val = result.unwrap();
2507 assert!((0.0..=1.0).contains(&gini_val));
2508 }
2509
2510 #[test]
2511 fn kurtosis_empty() {
2512 let mut unsorted: Unsorted<i32> = Unsorted::new();
2513 assert_eq!(unsorted.kurtosis(None, None), None);
2514 let empty_vec: Vec<i32> = vec![];
2515 assert_eq!(kurtosis(empty_vec.into_iter(), None, None), None);
2516 }
2517
2518 #[test]
2519 fn kurtosis_small() {
2520 let mut unsorted = Unsorted::new();
2522 unsorted.extend(vec![1, 2]);
2523 assert_eq!(unsorted.kurtosis(None, None), None);
2524
2525 let mut unsorted = Unsorted::new();
2526 unsorted.extend(vec![1, 2, 3]);
2527 assert_eq!(unsorted.kurtosis(None, None), None);
2528 }
2529
2530 #[test]
2531 fn kurtosis_normal_distribution() {
2532 let mut unsorted = Unsorted::new();
2534 unsorted.extend(vec![1, 2, 3, 4, 5]);
2535 let result = unsorted.kurtosis(None, None);
2536 assert!(result.is_some());
2537 }
2539
2540 #[test]
2541 fn kurtosis_all_same() {
2542 let mut unsorted = Unsorted::new();
2544 unsorted.extend(vec![5, 5, 5, 5]);
2545 assert_eq!(unsorted.kurtosis(None, None), None);
2546 }
2547
2548 #[test]
2549 fn kurtosis_stream() {
2550 let result = kurtosis(vec![1usize, 2, 3, 4, 5].into_iter(), None, None);
2551 assert!(result.is_some());
2552 }
2553
2554 #[test]
2555 fn kurtosis_precalc_mean_variance() {
2556 let mut unsorted = Unsorted::new();
2558 unsorted.extend(vec![1, 2, 3, 4, 5]);
2559
2560 let mean = 3.0f64;
2562 let variance = ((1.0f64 - 3.0).powi(2)
2563 + (2.0f64 - 3.0).powi(2)
2564 + (3.0f64 - 3.0).powi(2)
2565 + (4.0f64 - 3.0).powi(2)
2566 + (5.0f64 - 3.0).powi(2))
2567 / 5.0;
2568
2569 let result = unsorted.kurtosis(Some(mean), Some(variance));
2570 assert!(result.is_some());
2571
2572 let mut unsorted2 = Unsorted::new();
2574 unsorted2.extend(vec![1, 2, 3, 4, 5]);
2575 let result2 = unsorted2.kurtosis(None, None);
2576 assert!((result.unwrap() - result2.unwrap()).abs() < 1e-10);
2577 }
2578
2579 #[test]
2580 fn kurtosis_precalc_mean_only() {
2581 let mut unsorted = Unsorted::new();
2583 unsorted.extend(vec![1, 2, 3, 4, 5]);
2584 let mean = 3.0f64;
2585
2586 let result = unsorted.kurtosis(Some(mean), None);
2587 assert!(result.is_some());
2588
2589 let mut unsorted2 = Unsorted::new();
2591 unsorted2.extend(vec![1, 2, 3, 4, 5]);
2592 let result2 = unsorted2.kurtosis(None, None);
2593 assert!((result.unwrap() - result2.unwrap()).abs() < 1e-10);
2594 }
2595
2596 #[test]
2597 fn kurtosis_precalc_variance_only() {
2598 let mut unsorted = Unsorted::new();
2600 unsorted.extend(vec![1, 2, 3, 4, 5]);
2601 let variance = ((1.0f64 - 3.0).powi(2)
2602 + (2.0f64 - 3.0).powi(2)
2603 + (3.0f64 - 3.0).powi(2)
2604 + (4.0f64 - 3.0).powi(2)
2605 + (5.0f64 - 3.0).powi(2))
2606 / 5.0;
2607
2608 let result = unsorted.kurtosis(None, Some(variance));
2609 assert!(result.is_some());
2610
2611 let mut unsorted2 = Unsorted::new();
2613 unsorted2.extend(vec![1, 2, 3, 4, 5]);
2614 let result2 = unsorted2.kurtosis(None, None);
2615 assert!((result.unwrap() - result2.unwrap()).abs() < 1e-10);
2616 }
2617
2618 #[test]
2619 fn kurtosis_exact_calculation() {
2620 let mut unsorted = Unsorted::new();
2627 unsorted.extend(vec![1, 2, 3, 4]);
2628 let result = unsorted.kurtosis(None, None).unwrap();
2629 assert!(
2630 (result - (-1.2)).abs() < 1e-4,
2631 "expected ~-1.2, got {result}"
2632 );
2633 }
2634
2635 #[test]
2636 fn kurtosis_uniform_distribution() {
2637 let mut unsorted = Unsorted::new();
2639 unsorted.extend(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
2640 let result = unsorted.kurtosis(None, None).unwrap();
2641 assert!(result.is_finite());
2644 }
2645
2646 #[test]
2647 fn kurtosis_uniform_is_negative_excess() {
2648 let data: Vec<f64> = (0..2000).map(|i| i as f64 * 100.0 / 1999.0).collect();
2650 let mut u = Unsorted::new();
2651 u.extend(data);
2652 let k = u.kurtosis(None, None).unwrap();
2653 assert!((k - (-1.2)).abs() < 0.05, "expected ~-1.2, got {k}");
2654 }
2655
2656 #[test]
2657 fn kurtosis_two_point_is_strongly_negative() {
2658 let mut u = Unsorted::new();
2660 u.extend(
2661 (0..2000)
2662 .map(|i| if i % 2 == 0 { 0.0 } else { 100.0 })
2663 .collect::<Vec<f64>>(),
2664 );
2665 let k = u.kurtosis(None, None).unwrap();
2666 assert!((k - (-2.0)).abs() < 0.05, "expected ~-2.0, got {k}");
2667 }
2668
2669 #[test]
2670 fn kurtosis_large_dataset() {
2671 let data: Vec<i32> = (1..=1000).collect();
2673 let result = kurtosis(data.iter().copied(), None, None);
2674 assert!(result.is_some());
2675 let kurt_val = result.unwrap();
2676 assert!(kurt_val.is_finite());
2677 }
2678
2679 #[test]
2680 fn kurtosis_unsorted_vs_sorted() {
2681 let mut unsorted1 = Unsorted::new();
2683 unsorted1.extend(vec![5, 2, 8, 1, 9, 3, 7, 4, 6]);
2684 let result1 = unsorted1.kurtosis(None, None).unwrap();
2685
2686 let mut unsorted2 = Unsorted::new();
2687 unsorted2.extend(vec![1, 2, 3, 4, 5, 6, 7, 8, 9]);
2688 let result2 = unsorted2.kurtosis(None, None).unwrap();
2689
2690 assert!((result1 - result2).abs() < 1e-10);
2691 }
2692
2693 #[test]
2694 fn kurtosis_minimum_size() {
2695 let mut unsorted = Unsorted::new();
2697 unsorted.extend(vec![1, 2, 3, 4]);
2698 let result = unsorted.kurtosis(None, None);
2699 assert!(result.is_some());
2700 assert!(result.unwrap().is_finite());
2701 }
2702
2703 #[test]
2704 fn kurtosis_heavy_tailed() {
2705 let mut unsorted = Unsorted::new();
2707 unsorted.extend(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 100]);
2708 let result = unsorted.kurtosis(None, None).unwrap();
2709 assert!(result.is_finite());
2711 assert!(result > -10.0); }
2714
2715 #[test]
2716 fn kurtosis_light_tailed() {
2717 let mut unsorted = Unsorted::new();
2719 unsorted.extend(vec![10, 11, 12, 13, 14, 15, 16, 17, 18, 19]);
2720 let result = unsorted.kurtosis(None, None).unwrap();
2721 assert!(result.is_finite());
2723 }
2724
2725 #[test]
2726 fn kurtosis_small_variance() {
2727 let mut unsorted = Unsorted::new();
2729 unsorted.extend(vec![10.0, 10.001, 10.002, 10.003, 10.004]);
2730 let result = unsorted.kurtosis(None, None);
2731 assert!(result.is_some());
2733 assert!(result.unwrap().is_finite());
2734 }
2735
2736 #[test]
2737 fn kurtosis_precalc_zero_variance() {
2738 let mut unsorted = Unsorted::new();
2740 unsorted.extend(vec![1, 2, 3, 4, 5]);
2741 let result = unsorted.kurtosis(None, Some(0.0));
2742 assert_eq!(result, None);
2743 }
2744
2745 #[test]
2746 fn kurtosis_precalc_negative_variance() {
2747 let mut unsorted = Unsorted::new();
2749 unsorted.extend(vec![1, 2, 3, 4, 5]);
2750 let result = unsorted.kurtosis(None, Some(-1.0));
2752 let _ = result;
2757 }
2758
2759 #[test]
2760 fn kurtosis_different_types() {
2761 let mut unsorted_u32 = Unsorted::new();
2763 unsorted_u32.extend(vec![1u32, 2, 3, 4, 5]);
2764 let result_u32 = unsorted_u32.kurtosis(None, None).unwrap();
2765
2766 let mut unsorted_i64 = Unsorted::new();
2767 unsorted_i64.extend(vec![1i64, 2, 3, 4, 5]);
2768 let result_i64 = unsorted_i64.kurtosis(None, None).unwrap();
2769
2770 assert!((result_u32 - result_i64).abs() < 1e-10);
2771 }
2772
2773 #[test]
2774 fn kurtosis_floating_point_precision() {
2775 let mut unsorted = Unsorted::new();
2777 unsorted.extend(vec![1.1, 2.2, 3.3, 4.4, 5.5]);
2778 let result = unsorted.kurtosis(None, None);
2779 assert!(result.is_some());
2780 assert!(result.unwrap().is_finite());
2781 }
2782
2783 #[test]
2784 fn kurtosis_negative_values() {
2785 let mut unsorted = Unsorted::new();
2787 unsorted.extend(vec![-5, -3, -1, 1, 3, 5]);
2788 let result = unsorted.kurtosis(None, None);
2789 assert!(result.is_some());
2790 assert!(result.unwrap().is_finite());
2791 }
2792
2793 #[test]
2794 fn kurtosis_mixed_positive_negative() {
2795 let mut unsorted = Unsorted::new();
2797 unsorted.extend(vec![-10, -5, 0, 5, 10]);
2798 let result = unsorted.kurtosis(None, None);
2799 assert!(result.is_some());
2800 assert!(result.unwrap().is_finite());
2801 }
2802
2803 #[test]
2804 fn kurtosis_duplicate_values() {
2805 let mut unsorted = Unsorted::new();
2807 unsorted.extend(vec![1, 1, 2, 2, 3, 3, 4, 4, 5, 5]);
2808 let result = unsorted.kurtosis(None, None);
2809 assert!(result.is_some());
2810 assert!(result.unwrap().is_finite());
2811 }
2812
2813 #[test]
2814 fn kurtosis_precalc_mean_wrong() {
2815 let mut unsorted1 = Unsorted::new();
2817 unsorted1.extend(vec![1, 2, 3, 4, 5]);
2818 let correct_result = unsorted1.kurtosis(None, None).unwrap();
2819
2820 let mut unsorted2 = Unsorted::new();
2821 unsorted2.extend(vec![1, 2, 3, 4, 5]);
2822 let wrong_mean = 10.0; let wrong_result = unsorted2.kurtosis(Some(wrong_mean), None).unwrap();
2824
2825 assert!((correct_result - wrong_result).abs() > 1e-5);
2827 }
2828
2829 #[test]
2830 fn percentile_rank_empty() {
2831 let mut unsorted: Unsorted<i32> = Unsorted::new();
2832 assert_eq!(unsorted.percentile_rank(5), None);
2833 let empty_vec: Vec<i32> = vec![];
2834 assert_eq!(percentile_rank(empty_vec.into_iter(), 5), None);
2835 }
2836
2837 #[test]
2838 fn percentile_rank_basic() {
2839 let mut unsorted = Unsorted::new();
2840 unsorted.extend(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
2841
2842 assert_eq!(unsorted.percentile_rank(0), Some(0.0));
2844
2845 assert_eq!(unsorted.percentile_rank(11), Some(100.0));
2847
2848 let rank = unsorted.percentile_rank(5).unwrap();
2850 assert!((rank - 50.0).abs() < 1.0);
2851
2852 let rank = unsorted.percentile_rank(1).unwrap();
2854 assert!((rank - 10.0).abs() < 1.0);
2855 }
2856
2857 #[test]
2858 fn percentile_rank_duplicates() {
2859 let mut unsorted = Unsorted::new();
2860 unsorted.extend(vec![1, 1, 2, 2, 3, 3, 4, 4, 5, 5]);
2861
2862 let rank = unsorted.percentile_rank(2).unwrap();
2864 assert!((rank - 40.0).abs() < 1.0);
2865 }
2866
2867 #[test]
2868 fn percentile_rank_stream() {
2869 let result = percentile_rank(vec![1usize, 2, 3, 4, 5].into_iter(), 3);
2870 assert_eq!(result, Some(60.0)); }
2872
2873 #[test]
2874 fn percentile_rank_many_ties() {
2875 let mut unsorted = Unsorted::new();
2877 for _ in 0..100 {
2878 unsorted.add(5u32);
2879 }
2880 for _ in 0..100 {
2881 unsorted.add(10u32);
2882 }
2883 let rank = unsorted.percentile_rank(5).unwrap();
2885 assert!((rank - 50.0).abs() < f64::EPSILON);
2886 let mut unsorted2 = Unsorted::new();
2888 for _ in 0..100 {
2889 unsorted2.add(5u32);
2890 }
2891 for _ in 0..100 {
2892 unsorted2.add(10u32);
2893 }
2894 let rank = unsorted2.percentile_rank(10).unwrap();
2895 assert!((rank - 100.0).abs() < f64::EPSILON);
2896 }
2897
2898 #[test]
2899 fn atkinson_empty() {
2900 let mut unsorted: Unsorted<i32> = Unsorted::new();
2901 assert_eq!(unsorted.atkinson(1.0, None, None), None);
2902 let empty_vec: Vec<i32> = vec![];
2903 assert_eq!(atkinson(empty_vec.into_iter(), 1.0, None, None), None);
2904 }
2905
2906 #[test]
2907 fn atkinson_single_element() {
2908 let mut unsorted = Unsorted::new();
2909 unsorted.add(5);
2910 assert_eq!(unsorted.atkinson(1.0, None, None), Some(0.0));
2911 assert_eq!(atkinson(vec![5].into_iter(), 1.0, None, None), Some(0.0));
2912 }
2913
2914 #[test]
2915 fn atkinson_perfect_equality() {
2916 let mut unsorted = Unsorted::new();
2918 unsorted.extend(vec![10, 10, 10, 10, 10]);
2919 let result = unsorted.atkinson(1.0, None, None).unwrap();
2920 assert!((result - 0.0).abs() < 1e-10);
2921 }
2922
2923 #[test]
2924 fn atkinson_epsilon_zero() {
2925 let mut unsorted = Unsorted::new();
2927 unsorted.extend(vec![1, 2, 3, 4, 5]);
2928 let result = unsorted.atkinson(0.0, None, None).unwrap();
2929 assert!((result - 0.0).abs() < 1e-10);
2930 }
2931
2932 #[test]
2933 fn atkinson_epsilon_one() {
2934 let mut unsorted = Unsorted::new();
2936 unsorted.extend(vec![1, 2, 3, 4, 5]);
2937 let result = unsorted.atkinson(1.0, None, None);
2938 assert!(result.is_some());
2939 }
2940
2941 #[test]
2942 fn atkinson_epsilon_one_rejects_nan() {
2943 let mut unsorted = Unsorted::new();
2946 unsorted.extend(vec![1.0_f64, 2.0, f64::NAN, 4.0, 5.0]);
2947 assert_eq!(unsorted.atkinson(1.0, None, None), None);
2948 }
2949
2950 #[test]
2951 fn atkinson_negative_epsilon() {
2952 let mut unsorted = Unsorted::new();
2953 unsorted.extend(vec![1, 2, 3, 4, 5]);
2954 assert_eq!(unsorted.atkinson(-1.0, None, None), None);
2955 }
2956
2957 #[test]
2958 fn atkinson_zero_mean() {
2959 let mut unsorted = Unsorted::new();
2961 unsorted.extend(vec![0, 0, 0, 0]);
2962 assert_eq!(unsorted.atkinson(1.0, None, None), None);
2963 }
2964
2965 #[test]
2966 fn atkinson_stream() {
2967 let result = atkinson(vec![1usize, 2, 3, 4, 5].into_iter(), 1.0, None, None);
2968 assert!(result.is_some());
2969 }
2970
2971 #[test]
2972 fn atkinson_precalc_mean_geometric_sum() {
2973 let mut unsorted = Unsorted::new();
2975 unsorted.extend(vec![1, 2, 3, 4, 5]);
2976
2977 let mean = 3.0f64;
2979 let geometric_sum = 1.0f64.ln() + 2.0f64.ln() + 3.0f64.ln() + 4.0f64.ln() + 5.0f64.ln();
2980
2981 let result = unsorted.atkinson(1.0, Some(mean), Some(geometric_sum));
2982 assert!(result.is_some());
2983
2984 let mut unsorted2 = Unsorted::new();
2986 unsorted2.extend(vec![1, 2, 3, 4, 5]);
2987 let result2 = unsorted2.atkinson(1.0, None, None);
2988 assert!((result.unwrap() - result2.unwrap()).abs() < 1e-10);
2989 }
2990
2991 #[test]
2992 fn atkinson_precalc_mean_only() {
2993 let mut unsorted = Unsorted::new();
2995 unsorted.extend(vec![1, 2, 3, 4, 5]);
2996 let mean = 3.0f64;
2997
2998 let result = unsorted.atkinson(1.0, Some(mean), None);
2999 assert!(result.is_some());
3000
3001 let mut unsorted2 = Unsorted::new();
3003 unsorted2.extend(vec![1, 2, 3, 4, 5]);
3004 let result2 = unsorted2.atkinson(1.0, None, None);
3005 assert!((result.unwrap() - result2.unwrap()).abs() < 1e-10);
3006 }
3007
3008 #[test]
3009 fn atkinson_precalc_geometric_sum_only() {
3010 let mut unsorted = Unsorted::new();
3012 unsorted.extend(vec![1, 2, 3, 4, 5]);
3013 let geometric_sum = 1.0f64.ln() + 2.0f64.ln() + 3.0f64.ln() + 4.0f64.ln() + 5.0f64.ln();
3014
3015 let result = unsorted.atkinson(1.0, None, Some(geometric_sum));
3016 assert!(result.is_some());
3017
3018 let mut unsorted2 = Unsorted::new();
3020 unsorted2.extend(vec![1, 2, 3, 4, 5]);
3021 let result2 = unsorted2.atkinson(1.0, None, None);
3022 assert!((result.unwrap() - result2.unwrap()).abs() < 1e-10);
3023 }
3024
3025 #[test]
3026 fn test_median_with_infinity() {
3027 let mut unsorted = Unsorted::new();
3028 unsorted.extend(vec![1.0f64, 2.0, f64::INFINITY]);
3029 assert_eq!(unsorted.median(), Some(2.0));
3030 }
3031
3032 #[test]
3033 fn test_median_with_neg_infinity() {
3034 let mut unsorted = Unsorted::new();
3035 unsorted.extend(vec![f64::NEG_INFINITY, 1.0f64, 2.0]);
3036 assert_eq!(unsorted.median(), Some(1.0));
3037 }
3038
3039 #[test]
3040 fn test_quartiles_with_infinity() {
3041 let mut unsorted = Unsorted::new();
3042 unsorted.extend(vec![f64::NEG_INFINITY, 1.0, 2.0, 3.0, f64::INFINITY]);
3043 let q = unsorted.quartiles();
3044 assert!(q.is_some());
3046 let (_, q2, _) = q.unwrap();
3047 assert_eq!(q2, 2.0);
3048 }
3049
3050 #[test]
3051 fn test_mode_with_nan() {
3052 let mut unsorted: Unsorted<f64> = Unsorted::new();
3056 unsorted.extend(vec![1.0, f64::NAN, 2.0, 2.0, 3.0]);
3057 let _result = unsorted.mode(); }
3059
3060 #[test]
3061 fn test_gini_with_infinity() {
3062 let mut unsorted = Unsorted::new();
3063 unsorted.extend(vec![1.0f64, 2.0, f64::INFINITY]);
3064 let g = unsorted.gini(None);
3065 assert!(g.unwrap().is_nan());
3069 }
3070
3071 #[test]
3072 fn test_cardinality_with_infinity() {
3073 let mut unsorted = Unsorted::new();
3074 unsorted.extend(vec![1.0f64, f64::INFINITY, f64::NEG_INFINITY, 1.0]);
3075 assert_eq!(unsorted.cardinality(false, 10_000), 3);
3076 }
3077}
3078
3079#[cfg(test)]
3080mod bench {
3081 use super::*;
3082 use std::time::Instant;
3083
3084 #[test]
3085 #[ignore] fn comprehensive_quartiles_benchmark() {
3087 let data_sizes = vec![
3089 1_000, 10_000, 100_000, 500_000, 1_000_000, 2_000_000, 5_000_000, 10_000_000,
3090 ];
3091
3092 println!("=== COMPREHENSIVE QUARTILES BENCHMARK ===\n");
3093
3094 for size in data_sizes {
3095 println!("--- Testing with {} elements ---", size);
3096
3097 let test_patterns = vec![
3099 ("Random", generate_random_data(size)),
3100 ("Reverse Sorted", {
3101 let mut v = Vec::with_capacity(size);
3102 for x in (0..size).rev() {
3103 v.push(x as i32);
3104 }
3105 v
3106 }),
3107 ("Already Sorted", {
3108 let mut v = Vec::with_capacity(size);
3109 for x in 0..size {
3110 v.push(x as i32);
3111 }
3112 v
3113 }),
3114 ("Many Duplicates", {
3115 let mut v = Vec::with_capacity(size);
3117 let chunk_size = size / 100;
3118 for i in 0..100 {
3119 v.extend(std::iter::repeat_n(i, chunk_size));
3120 }
3121 v.extend(std::iter::repeat_n(0, size - v.len()));
3123 v
3124 }),
3125 ];
3126
3127 for (pattern_name, test_data) in test_patterns {
3128 println!("\n Pattern: {}", pattern_name);
3129
3130 let mut unsorted1 = Unsorted::new();
3132 unsorted1.extend(test_data.clone());
3133
3134 let start = Instant::now();
3135 let result_sorted = unsorted1.quartiles();
3136 let sorted_time = start.elapsed();
3137
3138 let mut unsorted2 = Unsorted::new();
3140 unsorted2.extend(test_data.clone());
3141
3142 let start = Instant::now();
3143 let result_selection = unsorted2.quartiles_with_selection();
3144 let selection_time = start.elapsed();
3145
3146 let mut unsorted3 = Unsorted::new();
3148 unsorted3.extend(test_data);
3149
3150 let start = Instant::now();
3151 let result_zero_copy = unsorted3.quartiles_zero_copy();
3152 let zero_copy_time = start.elapsed();
3153
3154 assert_eq!(result_sorted, result_selection);
3156 assert_eq!(result_sorted, result_zero_copy);
3157
3158 let selection_speedup =
3159 sorted_time.as_nanos() as f64 / selection_time.as_nanos() as f64;
3160 let zero_copy_speedup =
3161 sorted_time.as_nanos() as f64 / zero_copy_time.as_nanos() as f64;
3162
3163 println!(" Sorting: {:>12?}", sorted_time);
3164 println!(
3165 " Selection: {:>12?} (speedup: {:.2}x)",
3166 selection_time, selection_speedup
3167 );
3168 println!(
3169 " Zero-copy: {:>12?} (speedup: {:.2}x)",
3170 zero_copy_time, zero_copy_speedup
3171 );
3172
3173 let best_algorithm =
3174 if zero_copy_speedup > 1.0 && zero_copy_speedup >= selection_speedup {
3175 "ZERO-COPY"
3176 } else if selection_speedup > 1.0 {
3177 "SELECTION"
3178 } else {
3179 "SORTING"
3180 };
3181 println!(" Best: {}", best_algorithm);
3182 }
3183
3184 println!(); }
3186 }
3187
3188 fn generate_random_data(size: usize) -> Vec<i32> {
3190 let mut rng = 1234567u64;
3192 let mut vec = Vec::with_capacity(size);
3193 for _ in 0..size {
3194 rng = rng.wrapping_mul(1103515245).wrapping_add(12345);
3195 vec.push((rng >> 16) as i32);
3196 }
3197 vec
3198 }
3199
3200 #[test]
3201 #[ignore] fn find_selection_threshold() {
3203 println!("=== FINDING SELECTION ALGORITHM THRESHOLD ===\n");
3204
3205 let mut found_threshold = None;
3207 let test_sizes = vec![
3208 1_000_000, 2_000_000, 3_000_000, 4_000_000, 5_000_000, 7_500_000, 10_000_000,
3209 15_000_000, 20_000_000, 25_000_000, 30_000_000,
3210 ];
3211
3212 for size in test_sizes {
3213 println!("Testing size: {}", size);
3214
3215 let test_data = generate_random_data(size);
3217
3218 let iterations = 3;
3220 let mut sorting_total = 0u128;
3221 let mut selection_total = 0u128;
3222 let mut zero_copy_total = 0u128;
3223
3224 for i in 0..iterations {
3225 println!(" Iteration {}/{}", i + 1, iterations);
3226
3227 let mut unsorted1 = Unsorted::new();
3229 unsorted1.extend(test_data.clone());
3230
3231 let start = Instant::now();
3232 let _result_sorted = unsorted1.quartiles();
3233 sorting_total += start.elapsed().as_nanos();
3234
3235 let mut unsorted2 = Unsorted::new();
3237 unsorted2.extend(test_data.clone());
3238
3239 let start = Instant::now();
3240 let _result_selection = unsorted2.quartiles_with_selection();
3241 selection_total += start.elapsed().as_nanos();
3242
3243 let mut unsorted3 = Unsorted::new();
3245 unsorted3.extend(test_data.clone());
3246
3247 let start = Instant::now();
3248 let _result_zero_copy = unsorted3.quartiles_zero_copy();
3249 zero_copy_total += start.elapsed().as_nanos();
3250 }
3251
3252 let avg_sorting = sorting_total / iterations as u128;
3253 let avg_selection = selection_total / iterations as u128;
3254 let avg_zero_copy = zero_copy_total / iterations as u128;
3255 let selection_speedup = avg_sorting as f64 / avg_selection as f64;
3256 let zero_copy_speedup = avg_sorting as f64 / avg_zero_copy as f64;
3257
3258 println!(
3259 " Average sorting: {:>12.2}ms",
3260 avg_sorting as f64 / 1_000_000.0
3261 );
3262 println!(
3263 " Average selection: {:>12.2}ms (speedup: {:.2}x)",
3264 avg_selection as f64 / 1_000_000.0,
3265 selection_speedup
3266 );
3267 println!(
3268 " Average zero-copy: {:>12.2}ms (speedup: {:.2}x)",
3269 avg_zero_copy as f64 / 1_000_000.0,
3270 zero_copy_speedup
3271 );
3272
3273 if (selection_speedup > 1.0 || zero_copy_speedup > 1.0) && found_threshold.is_none() {
3274 found_threshold = Some(size);
3275 let best_method = if zero_copy_speedup > selection_speedup {
3276 "Zero-copy"
3277 } else {
3278 "Selection"
3279 };
3280 println!(
3281 " *** THRESHOLD FOUND: {} becomes faster at {} elements ***",
3282 best_method, size
3283 );
3284 }
3285
3286 println!();
3287 }
3288
3289 match found_threshold {
3290 Some(threshold) => println!(
3291 "🎯 Selection algorithm becomes faster at approximately {} elements",
3292 threshold
3293 ),
3294 None => println!("❌ Selection algorithm did not become faster in the tested range"),
3295 }
3296 }
3297
3298 #[test]
3299 #[ignore] fn benchmark_different_data_types() {
3301 println!("=== BENCHMARKING DIFFERENT DATA TYPES ===\n");
3302
3303 let size = 5_000_000; println!("Testing with f64 data:");
3307 let float_data: Vec<f64> = generate_random_data(size)
3308 .into_iter()
3309 .map(|x| x as f64 / 1000.0)
3310 .collect();
3311
3312 let mut unsorted1 = Unsorted::new();
3313 unsorted1.extend(float_data.clone());
3314 let start = Instant::now();
3315 let _result = unsorted1.quartiles();
3316 let sorting_time = start.elapsed();
3317
3318 let mut unsorted2 = Unsorted::new();
3319 unsorted2.extend(float_data.clone());
3320 let start = Instant::now();
3321 let _result = unsorted2.quartiles_with_selection();
3322 let selection_time = start.elapsed();
3323
3324 let mut unsorted3 = Unsorted::new();
3325 unsorted3.extend(float_data);
3326 let start = Instant::now();
3327 let _result = unsorted3.quartiles_zero_copy();
3328 let zero_copy_time = start.elapsed();
3329
3330 println!(" Sorting: {:?}", sorting_time);
3331 println!(" Selection: {:?}", selection_time);
3332 println!(" Zero-copy: {:?}", zero_copy_time);
3333 println!(
3334 " Selection Speedup: {:.2}x",
3335 sorting_time.as_nanos() as f64 / selection_time.as_nanos() as f64
3336 );
3337 println!(
3338 " Zero-copy Speedup: {:.2}x\n",
3339 sorting_time.as_nanos() as f64 / zero_copy_time.as_nanos() as f64
3340 );
3341
3342 println!("Testing with i64 data:");
3344 let int64_data: Vec<i64> = generate_random_data(size)
3345 .into_iter()
3346 .map(|x| x as i64 * 1000)
3347 .collect();
3348
3349 let mut unsorted1 = Unsorted::new();
3350 unsorted1.extend(int64_data.clone());
3351 let start = Instant::now();
3352 let _result = unsorted1.quartiles();
3353 let sorting_time = start.elapsed();
3354
3355 let mut unsorted2 = Unsorted::new();
3356 unsorted2.extend(int64_data.clone());
3357 let start = Instant::now();
3358 let _result = unsorted2.quartiles_with_selection();
3359 let selection_time = start.elapsed();
3360
3361 let mut unsorted3 = Unsorted::new();
3362 unsorted3.extend(int64_data);
3363 let start = Instant::now();
3364 let _result = unsorted3.quartiles_zero_copy();
3365 let zero_copy_time = start.elapsed();
3366
3367 println!(" Sorting: {:?}", sorting_time);
3368 println!(" Selection: {:?}", selection_time);
3369 println!(" Zero-copy: {:?}", zero_copy_time);
3370 println!(
3371 " Selection Speedup: {:.2}x",
3372 sorting_time.as_nanos() as f64 / selection_time.as_nanos() as f64
3373 );
3374 println!(
3375 " Zero-copy Speedup: {:.2}x",
3376 sorting_time.as_nanos() as f64 / zero_copy_time.as_nanos() as f64
3377 );
3378 }
3379}