263 lines
6 KiB
Text
263 lines
6 KiB
Text
|
type List<T> = Option<Rc<(T, List<T>)>>;
|
||
|
type Partition<T> = (List<T>, T, List<T>);
|
||
|
|
||
|
// Return a random number between 0 (inclusive) and
|
||
|
// max (exclusive).
|
||
|
fn random(max: int) -> int {
|
||
|
while true {
|
||
|
let value: int = 0;
|
||
|
let max_value: int = 0;
|
||
|
while max_value < max {
|
||
|
max_value = max_value * 2 + 1;
|
||
|
value = value * 2;
|
||
|
{ value = value + 1; } [0.5] {}
|
||
|
}
|
||
|
if value < max {
|
||
|
return value;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Return true if the option is `None`.
|
||
|
fn is_none<O>(opt: O) -> bool {
|
||
|
if let Some(_value) = opt {
|
||
|
return false;
|
||
|
} else {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Unwrap an option.
|
||
|
fn unwrap<T>(opt: Option<T>) -> T {
|
||
|
if let Some(value) = opt {
|
||
|
return value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Create a new, empty list.
|
||
|
// Complexity: O(1)
|
||
|
fn list_new<T>() -> List<T> {
|
||
|
return None;
|
||
|
}
|
||
|
|
||
|
// Push a new value to the front of the list.
|
||
|
// Complexity: O(1)
|
||
|
fn list_push_front<T>(list: List<T>, elem: T) -> List<T> {
|
||
|
return Some(Rc((elem, list)));
|
||
|
}
|
||
|
|
||
|
// Pop the value from the front of the list.
|
||
|
// Complexity: O(1)
|
||
|
fn list_pop_front<T>(list: List<T>) -> (T, List<T>) {
|
||
|
if let Some(front) = list {
|
||
|
return *front;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Append a list to the end of this one.
|
||
|
// Complexity: O(n)
|
||
|
fn list_append<T>(list: List<T>, list2: List<T>) -> List<T> {
|
||
|
if is_none::<List<T>>(list) {
|
||
|
return list2;
|
||
|
}
|
||
|
|
||
|
let back: List<T> = list;
|
||
|
while let Some(front) = back {
|
||
|
let tail: List<T> = (*front).1;
|
||
|
if is_none::<List<T>>(tail) {
|
||
|
break;
|
||
|
}
|
||
|
back = tail;
|
||
|
}
|
||
|
|
||
|
if let Some(back) = back {
|
||
|
back.1 = list2;
|
||
|
return list;
|
||
|
}
|
||
|
// else unreachable - back is always a non-empty list
|
||
|
}
|
||
|
|
||
|
// Return the length of a list.
|
||
|
// Complexity: O(n)
|
||
|
fn list_len<T>(list: List<T>) -> int {
|
||
|
let len: int = 0;
|
||
|
while let Some(inner) = list {
|
||
|
len = len + 1;
|
||
|
list = (*inner).1;
|
||
|
}
|
||
|
return len;
|
||
|
}
|
||
|
|
||
|
// Swap the value of the two list elements.
|
||
|
fn list_swap<T>(list: List<T>, list2: List<T>) {
|
||
|
let list: Rc<(T, List<T>)> = unwrap::<Rc<(T, List<T>)>>(list);
|
||
|
let list2: Rc<(T, List<T>)> = unwrap::<Rc<(T, List<T>)>>(list2);
|
||
|
let tmp: T = (*list).0;
|
||
|
list.0 = (*list2).0;
|
||
|
list2.0 = tmp;
|
||
|
}
|
||
|
|
||
|
// Swap the n-th and last elements, and return its value.
|
||
|
fn list_swap_nth_last<T>(list: List<T>, n: int) -> T {
|
||
|
let i: int = 0;
|
||
|
let nth: List<T> = None;
|
||
|
while let Some(front) = list {
|
||
|
if i == n {
|
||
|
nth = list;
|
||
|
}
|
||
|
let tail: List<T> = (*front).1;
|
||
|
if is_none::<List<T>>(tail) {
|
||
|
let _unit: () = list_swap::<T>(nth, list);
|
||
|
return (*front).0;
|
||
|
}
|
||
|
list = tail;
|
||
|
i = i + 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Return the next list element.
|
||
|
fn list_next<T>(list: List<T>) -> List<T> {
|
||
|
return (*unwrap::<Rc<(T, List<T>)>>(list)).1;
|
||
|
}
|
||
|
|
||
|
// Split the list at index n.
|
||
|
fn list_split<T>(list: List<T>, n: int) -> (List<T>, List<T>) {
|
||
|
if n == 0 {
|
||
|
return (None, list);
|
||
|
}
|
||
|
|
||
|
let list2: List<T> = list;
|
||
|
let i: int = 0;
|
||
|
while i < n - 1 {
|
||
|
list2 = list_next::<T>(list2);
|
||
|
i = i + 1;
|
||
|
}
|
||
|
|
||
|
if let Some(front) = list2 {
|
||
|
list2 = (*front).1;
|
||
|
front.1 = None;
|
||
|
}
|
||
|
return (list, list2);
|
||
|
}
|
||
|
|
||
|
// Increase swap count
|
||
|
fn inc_swaps(swaps_and_comps: Rc<(int, int)>) {
|
||
|
swaps_and_comps.0 = (*swaps_and_comps).0 + 1;
|
||
|
}
|
||
|
|
||
|
// Increase comparison count
|
||
|
fn inc_comps(swaps_and_comps: Rc<(int, int)>) {
|
||
|
swaps_and_comps.1 = (*swaps_and_comps).1 + 1;
|
||
|
}
|
||
|
|
||
|
// Randomised quicksort.
|
||
|
// Expected complexity: O(n log n)
|
||
|
fn quicksort(list: List<int>, swaps_and_comps: Rc<(int, int)>) -> List<int> {
|
||
|
if list_len::<int>(list) < 2 {
|
||
|
return list;
|
||
|
}
|
||
|
|
||
|
let partition: Partition<int> = partition(list, swaps_and_comps);
|
||
|
let list: List<int> = quicksort(partition.0, swaps_and_comps);
|
||
|
let pivot: int = partition.1;
|
||
|
let list2: List<int> = quicksort(partition.2, swaps_and_comps);
|
||
|
|
||
|
list2 = list_push_front::<int>(list2, pivot);
|
||
|
return list_append::<int>(list, list2);
|
||
|
}
|
||
|
|
||
|
// Partition a list into smaller, pivot, and larger elements
|
||
|
fn partition(list: List<int>, swaps_and_comps: Rc<(int, int)>) -> Partition<int> {
|
||
|
// choose some random element as pivot and move it to the last position
|
||
|
let list_len: int = list_len::<int>(list);
|
||
|
let pivot: int = list_swap_nth_last::<int>(list, random(list_len));
|
||
|
let _unit: () = inc_swaps(swaps_and_comps);
|
||
|
|
||
|
// this is our pivot position
|
||
|
let iter: List<int> = list;
|
||
|
let iter_idx: int = -1;
|
||
|
|
||
|
// move all elements smaller than our pivot to its left
|
||
|
let i: int = 0;
|
||
|
let iter2: List<int> = list;
|
||
|
while let Some(front) = iter2 {
|
||
|
// don't swap out the pivot already
|
||
|
if i != list_len - 1 {
|
||
|
|
||
|
let _unit: () = inc_comps(swaps_and_comps);
|
||
|
if (*front).0 <= pivot {
|
||
|
// move pivot index forward
|
||
|
if iter_idx >= 0 {
|
||
|
iter = list_next::<int>(iter);
|
||
|
}
|
||
|
iter_idx = iter_idx + 1;
|
||
|
|
||
|
// swap current element with pivot position
|
||
|
if iter_idx != i {
|
||
|
let _unit: () = list_swap::<int>(iter, iter2);
|
||
|
let _unit: () = inc_swaps(swaps_and_comps);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
iter2 = (*front).1;
|
||
|
i = i + 1;
|
||
|
}
|
||
|
|
||
|
// move pivot to the correct pivot position
|
||
|
iter_idx = iter_idx + 1;
|
||
|
let _pivot: int = list_swap_nth_last::<int>(list, iter_idx);
|
||
|
let _unit: () = inc_swaps(swaps_and_comps);
|
||
|
|
||
|
// split the list into two
|
||
|
let lists: (List<int>, List<int>) = list_split::<int>(list, iter_idx);
|
||
|
let pop: (int, List<int>) = list_pop_front::<int>(lists.1);
|
||
|
return (lists.0, pop.0, pop.1);
|
||
|
}
|
||
|
|
||
|
// Assert that the list is sorted.
|
||
|
fn assert_sorted(list: List<int>) -> () {
|
||
|
if let Some(first) = list {
|
||
|
if assert_sorted_impl((*first).1, (*first).0) {
|
||
|
return ();
|
||
|
}
|
||
|
// else there's no path to a return statement - assert failed
|
||
|
} else {
|
||
|
return ();
|
||
|
}
|
||
|
}
|
||
|
fn assert_sorted_impl(list: List<int>, prev: int) -> bool {
|
||
|
while let Some(front) = list {
|
||
|
let curr: int = (*front).0;
|
||
|
if curr < prev {
|
||
|
return false;
|
||
|
}
|
||
|
prev = curr;
|
||
|
list = (*front).1;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
input!(len);
|
||
|
|
||
|
// Create a random list.
|
||
|
let list: List<int> = list_new::<int>();
|
||
|
let i: int = 0;
|
||
|
while i < len {
|
||
|
list = list_push_front::<int>(list, random(len*2));
|
||
|
i = i + 1;
|
||
|
}
|
||
|
|
||
|
// Run quicksort.
|
||
|
let swaps_and_comps: Rc<(int, int)> = Rc((0, 0));
|
||
|
list = quicksort(list, swaps_and_comps);
|
||
|
let swaps: int = (*swaps_and_comps).0;
|
||
|
let comps: int = (*swaps_and_comps).1;
|
||
|
|
||
|
// Ensure the list is now sorted.
|
||
|
let _unit: () = assert_sorted(list);
|
||
|
|
||
|
output!(swaps);
|
||
|
output!(comps);
|