-
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #52 from orxfun/sort-methods
sort methods are implemented
- Loading branch information
Showing
5 changed files
with
299 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,240 @@ | ||
use crate::Fragment; | ||
use std::cmp::Ordering::{self, *}; | ||
|
||
pub fn in_place_sort_by<T, F>(fragments: &mut [Fragment<T>], mut compare: F) | ||
where | ||
F: FnMut(&T, &T) -> Ordering, | ||
{ | ||
if fragments.len() == 0 { | ||
return; | ||
} | ||
|
||
for fragment in fragments.iter_mut() { | ||
fragment.sort_by(&mut compare); | ||
} | ||
|
||
let num_fragments = fragments.len(); | ||
|
||
let mut r = 0; | ||
let mut c = 0; | ||
|
||
while r < num_fragments - 1 { | ||
let row_to_swap = get_row_to_swap(fragments, &mut compare, r, c); | ||
if let Some(target_row) = row_to_swap { | ||
let pa = std::ptr::addr_of_mut!(fragments[r][c]); | ||
let pb = std::ptr::addr_of_mut!(fragments[target_row][0]); | ||
// SAFETY: `pa` and `pb` have been created from safe mutable references and refer | ||
// to elements in the slice and therefore are guaranteed to be valid and aligned. | ||
// Note that accessing the elements behind `a` and `b` is checked and will | ||
// panic when out of bounds. | ||
unsafe { std::ptr::swap(pa, pb) }; | ||
|
||
let fragment_right = &fragments[target_row][1..]; | ||
let value = &fragments[target_row][0]; | ||
let position_to_insert = find_position_to_insert(fragment_right, &mut compare, value); | ||
if let Some(p) = position_to_insert { | ||
for i in 0..p { | ||
fragments[target_row].swap(i, i + 1); | ||
} | ||
} | ||
} | ||
|
||
match c == fragments[r].len() - 1 { | ||
true => (r, c) = (r + 1, 0), | ||
false => c += 1, | ||
} | ||
} | ||
} | ||
|
||
fn get_row_to_swap<T, F>( | ||
fragments: &[Fragment<T>], | ||
compare: &mut F, | ||
r: usize, | ||
c: usize, | ||
) -> Option<usize> | ||
where | ||
F: FnMut(&T, &T) -> Ordering, | ||
{ | ||
let mut r_best = r + 1; | ||
match r_best == fragments.len() { | ||
true => None, | ||
false => { | ||
let mut best = &fragments[r_best][0]; | ||
for q in (r_best + 1)..fragments.len() { | ||
if let Less = compare(&fragments[q][0], best) { | ||
(best, r_best) = (&fragments[q][0], q); | ||
} | ||
} | ||
|
||
match compare(best, &fragments[r][c]) { | ||
Less => Some(r_best), | ||
_ => None, | ||
} | ||
} | ||
} | ||
} | ||
|
||
fn find_position_to_insert<T, F>(fragment: &[T], compare: &mut F, value: &T) -> Option<usize> | ||
where | ||
F: FnMut(&T, &T) -> Ordering, | ||
{ | ||
match fragment.len() { | ||
0 => None, | ||
_ => match compare(unsafe { fragment.get_unchecked(0) }, value) { | ||
Equal | Greater => None, | ||
Less => { | ||
let mut target = 0; | ||
let mut size = fragment.len(); | ||
let mut left = 0; | ||
let mut right = size; | ||
while left < right { | ||
let mid = left + size / 2; | ||
|
||
match compare(unsafe { fragment.get_unchecked(mid) }, value) { | ||
// Equal => return Some(mid), | ||
Greater | Equal => right = mid, | ||
Less => { | ||
target = mid + 1; | ||
left = mid; | ||
} | ||
} | ||
if size == 1 { | ||
return Some(target); | ||
} | ||
size = right - left; | ||
} | ||
|
||
Some(target) | ||
} | ||
}, | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
use crate::{Doubling, Growth, Linear, Recursive}; | ||
use test_case::test_case; | ||
|
||
#[test] | ||
fn insertion_position() { | ||
let fragment: Fragment<u32> = vec![4, 7, 9, 13, 16, 17, 23].into(); | ||
|
||
let mut c = |a: &u32, b: &u32| a.cmp(b); | ||
let mut pos = |val: &u32| find_position_to_insert(&fragment, &mut c, val); | ||
|
||
assert_eq!(pos(&0), None); | ||
assert_eq!(pos(&3), None); | ||
assert_eq!(pos(&4), None); | ||
|
||
assert_eq!(pos(&5), Some(1)); | ||
assert_eq!(pos(&6), Some(1)); | ||
assert_eq!(pos(&7), Some(1)); | ||
|
||
assert_eq!(pos(&8), Some(2)); | ||
assert_eq!(pos(&9), Some(2)); | ||
|
||
assert_eq!(pos(&10), Some(3)); | ||
assert_eq!(pos(&11), Some(3)); | ||
assert_eq!(pos(&12), Some(3)); | ||
assert_eq!(pos(&13), Some(3)); | ||
|
||
assert_eq!(pos(&14), Some(4)); | ||
assert_eq!(pos(&15), Some(4)); | ||
assert_eq!(pos(&16), Some(4)); | ||
|
||
assert_eq!(pos(&17), Some(5)); | ||
|
||
assert_eq!(pos(&18), Some(6)); | ||
assert_eq!(pos(&19), Some(6)); | ||
assert_eq!(pos(&20), Some(6)); | ||
assert_eq!(pos(&21), Some(6)); | ||
assert_eq!(pos(&22), Some(6)); | ||
assert_eq!(pos(&23), Some(6)); | ||
|
||
assert_eq!(pos(&24), Some(7)); | ||
assert_eq!(pos(&25), Some(7)); | ||
assert_eq!(pos(&26), Some(7)); | ||
assert_eq!(pos(&100), Some(7)); | ||
} | ||
|
||
#[test] | ||
fn insertion_position_with_ties() { | ||
let mut c = |a: &u32, b: &u32| a.cmp(b); | ||
|
||
let fragment: Fragment<u32> = vec![4, 7, 13, 13, 13, 17, 23].into(); | ||
let mut pos = |val: &u32| find_position_to_insert(&fragment, &mut c, val); | ||
assert_eq!(pos(&13), Some(2)); | ||
|
||
let fragment: Fragment<u32> = vec![4, 7, 13, 13, 23, 23, 23].into(); | ||
let mut pos = |val: &u32| find_position_to_insert(&fragment, &mut c, val); | ||
assert_eq!(pos(&23), Some(4)); | ||
|
||
let fragment: Fragment<u32> = vec![4, 4, 13, 13, 23, 23, 23].into(); | ||
let mut pos = |val: &u32| find_position_to_insert(&fragment, &mut c, val); | ||
assert_eq!(pos(&4), None); | ||
} | ||
|
||
#[test] | ||
fn sort_simple() { | ||
let mut c = |a: &u32, b: &u32| a.cmp(b); | ||
|
||
let mut fragments: Vec<Fragment<u32>> = | ||
vec![vec![2, 4].into(), vec![0, 5, 6].into(), vec![1, 3].into()]; | ||
|
||
in_place_sort_by(&mut fragments, &mut c); | ||
|
||
assert_is_sorted(fragments); | ||
} | ||
|
||
#[test_case(Doubling)] | ||
#[test_case(Recursive)] | ||
#[test_case(Linear::new(10))] | ||
fn sort_growth(growth: impl Growth) { | ||
let mut c = |a: &i32, b: &i32| a.cmp(b); | ||
|
||
let num_fragments = 10; | ||
let mut fragments: Vec<Fragment<_>> = vec![]; | ||
|
||
let mut len = 0; | ||
for _ in 0..num_fragments { | ||
let fragment_capacities: Vec<_> = fragments.iter().map(|x| x.capacity()).collect(); | ||
let mut fragment = | ||
Fragment::new(growth.new_fragment_capacity_from(fragment_capacities.into_iter())); | ||
for i in 0..fragment.capacity() { | ||
let i = len + i; | ||
let value = match i % 3 { | ||
0 => i as i32, | ||
1 => 42, | ||
_ => -(i as i32), | ||
}; | ||
fragment.push(value); | ||
} | ||
|
||
assert_eq!(fragment.len(), fragment.capacity()); | ||
len += fragment.len(); | ||
fragments.push(fragment); | ||
} | ||
|
||
assert_eq!(fragments.len(), num_fragments); | ||
|
||
in_place_sort_by(&mut fragments, &mut c); | ||
|
||
assert_is_sorted(fragments); | ||
} | ||
|
||
fn assert_is_sorted<T: Ord>(fragments: Vec<Fragment<T>>) { | ||
let flattened: Vec<T> = fragments.into_iter().flat_map(|x| Vec::from(x)).collect(); | ||
|
||
if flattened.is_empty() { | ||
return; | ||
} | ||
|
||
let mut curr = &flattened[0]; | ||
for i in 1..flattened.len() { | ||
let cmp = curr.cmp(&flattened[i]); | ||
assert!(cmp != Greater); | ||
curr = &flattened[i]; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
pub mod binary_search; | ||
pub mod in_place_sort; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
use orx_split_vec::*; | ||
use std::cmp::Ordering; | ||
|
||
#[test] | ||
fn sort() { | ||
let mut vec: SplitVec<_> = [3, 4, 1, 2, 3, 5, 8, 10, 9].into_iter().collect(); | ||
vec.sort(); | ||
assert_eq!(&vec, &[1, 2, 3, 3, 4, 5, 8, 9, 10]); | ||
} | ||
|
||
#[test] | ||
fn sort_by() { | ||
let compare = |a: &i32, b: &i32| match a.cmp(b) { | ||
Ordering::Less => Ordering::Greater, | ||
Ordering::Greater => Ordering::Less, | ||
Ordering::Equal => Ordering::Equal, | ||
}; | ||
let mut vec: SplitVec<_> = [3, 4, 1, 2, 3, 5, 8, 10, 9].into_iter().collect(); | ||
vec.sort_by(compare); | ||
assert_eq!(&vec, &[10, 9, 8, 5, 4, 3, 3, 2, 1]); | ||
} | ||
|
||
#[test] | ||
fn sort_by_key() { | ||
let key = |a: &i32| match a % 2 { | ||
0 => *a, | ||
_ => -a, | ||
}; | ||
|
||
let mut vec: SplitVec<_> = [3, 4, 1, 2, 3, 5, 8, 10, 9].into_iter().collect(); | ||
vec.sort_by_key(key); | ||
assert_eq!(&vec, &[9, 5, 3, 3, 1, 2, 4, 8, 10]); | ||
} |