diff --git a/crates/functions/src/stat/descriptive.rs b/crates/functions/src/stat/descriptive.rs index 08938d039..3c8f78f69 100644 --- a/crates/functions/src/stat/descriptive.rs +++ b/crates/functions/src/stat/descriptive.rs @@ -39,9 +39,10 @@ pub(super) fn sorted(mut v: Vec) -> Vec { } /// Sorts `v` if it is already monotone in `total_cmp` order (a descending slice is reversed) and -/// says whether it did. One cheap pass that stops at the first out-of-order pair, so unordered -/// data costs next to nothing. Needed because the std sort recognises ascending and descending -/// runs in O(n), which a selection cannot beat. +/// says whether it did. At most two passes, each stopping at the first out-of-order pair, so +/// unordered data costs next to nothing. Needed because the std sort recognises ascending and +/// descending runs in O(n) with a small constant; selection is O(n) too but measured 1.2-1.8x +/// slower on such input. fn sorted_if_monotone(v: &mut [f64]) -> bool { if v.is_sorted_by(|a, b| a.total_cmp(b).is_le()) { return true; diff --git a/crates/functions/src/stat/tests.rs b/crates/functions/src/stat/tests.rs index f11629369..023b36815 100644 --- a/crates/functions/src/stat/tests.rs +++ b/crates/functions/src/stat/tests.rs @@ -269,7 +269,7 @@ fn selection_matches_full_sort_bit_for_bit() { // Few distinct values: many ties. (0, r) => (r % 3) as f64, // Specials, including both zeros and NaNs. - (1, r) => specials[(next() as usize + r as usize) % specials.len()], + (1, r) => specials[(next() as usize).wrapping_add(r as usize) % specials.len()], // Wide random range. (2, _) => (next() as i64 as f64) / 1e3, // Integers with some specials mixed in.