Skip to content

Commit

Permalink
Use helper functions for min/max_idx
Browse files Browse the repository at this point in the history
  • Loading branch information
Sp00ph committed May 24, 2023
1 parent 3d11b65 commit fd5fa01
Showing 1 changed file with 28 additions and 18 deletions.
46 changes: 28 additions & 18 deletions library/core/src/slice/select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,26 @@ fn partition_at_index_loop<'a, T, F>(
}
}

/// Helper function that returns the index of the minimum element in the slice using the given
/// comparator function
fn min_index<T, F: FnMut(&T, &T) -> bool>(slice: &[T], is_less: &mut F) -> Option<usize> {
slice
.iter()
.enumerate()
.reduce(|acc, t| if is_less(t.1, acc.1) { t } else { acc })
.map(|(i, _)| i)
}

/// Helper function that returns the index of the maximum element in the slice using the given
/// comparator function
fn max_index<T, F: FnMut(&T, &T) -> bool>(slice: &[T], is_less: &mut F) -> Option<usize> {
slice
.iter()
.enumerate()
.reduce(|acc, t| if is_less(acc.1, t.1) { t } else { acc })
.map(|(i, _)| i)
}

/// Reorder the slice such that the element at `index` is at its final sorted position.
pub fn partition_at_index<T, F>(
v: &mut [T],
Expand All @@ -120,13 +140,13 @@ where
} else if index == v.len() - 1 {
// Find max element and place it in the last position of the array. We're free to use
// `unwrap()` here because we know v must not be empty.
let (max_index, _) = v.iter().enumerate().max_by(from_is_less(&mut is_less)).unwrap();
v.swap(max_index, index);
let max_idx = max_index(v, &mut is_less).unwrap();
v.swap(max_idx, index);
} else if index == 0 {
// Find min element and place it in the first position of the array. We're free to use
// `unwrap()` here because we know v must not be empty.
let (min_index, _) = v.iter().enumerate().min_by(from_is_less(&mut is_less)).unwrap();
v.swap(min_index, index);
let min_idx = min_index(v, &mut is_less).unwrap();
v.swap(min_idx, index);
} else {
partition_at_index_loop(v, index, &mut is_less, None);
}
Expand All @@ -137,16 +157,6 @@ where
(left, pivot, right)
}

/// helper function used to find the index of the min/max element
/// using e.g. `slice.iter().enumerate().min_by(from_is_less(&mut is_less)).unwrap()`
fn from_is_less<T>(
is_less: &mut impl FnMut(&T, &T) -> bool,
) -> impl FnMut(&(usize, &T), &(usize, &T)) -> cmp::Ordering + '_ {
|&(_, x), &(_, y)| {
if is_less(x, y) { cmp::Ordering::Less } else { cmp::Ordering::Greater }
}
}

/// Selection algorithm to select the k-th element from the slice in guaranteed O(n) time.
/// This is essentially a quickselect that uses Tukey's Ninther for pivot selection
fn median_of_medians<T, F: FnMut(&T, &T) -> bool>(mut v: &mut [T], is_less: &mut F, mut k: usize) {
Expand All @@ -170,14 +180,14 @@ fn median_of_medians<T, F: FnMut(&T, &T) -> bool>(mut v: &mut [T], is_less: &mut
if k == v.len() - 1 {
// Find max element and place it in the last position of the array. We're free to use
// `unwrap()` here because we know v must not be empty.
let (max_index, _) = v.iter().enumerate().max_by(from_is_less(is_less)).unwrap();
v.swap(max_index, k);
let max_idx = max_index(v, is_less).unwrap();
v.swap(max_idx, k);
return;
} else if k == 0 {
// Find min element and place it in the first position of the array. We're free to use
// `unwrap()` here because we know v must not be empty.
let (min_index, _) = v.iter().enumerate().min_by(from_is_less(is_less)).unwrap();
v.swap(min_index, k);
let min_idx = min_index(v, is_less).unwrap();
v.swap(min_idx, k);
return;
}

Expand Down

0 comments on commit fd5fa01

Please sign in to comment.