Skip to content

Commit

Permalink
Merge pull request #16 from KeplerOps/feature/quicksort-implementation
Browse files Browse the repository at this point in the history
Implement QuickSort Algorithm
  • Loading branch information
Brad-Edwards authored Jan 1, 2025
2 parents d81f0d0 + 2e477cb commit 2fa5c89
Show file tree
Hide file tree
Showing 3 changed files with 290 additions and 14 deletions.
5 changes: 5 additions & 0 deletions crates/cs/blocks-cs-sort/src/algorithms.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Sorting algorithms
pub mod quicksort;

// Re-export commonly used functions for convenience
pub use self::quicksort::sort as quicksort;
253 changes: 253 additions & 0 deletions crates/cs/blocks-cs-sort/src/algorithms/quicksort.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@
/// Sorts a mutable slice using the QuickSort algorithm.
///
/// # Algorithm Details
/// - Time Complexity: O(n log n) average case, O(n²) worst case
/// - Space Complexity: O(log n) average case for stack space
/// - Not stable: equal elements may be reordered
///
/// # Implementation Notes
/// - Uses median-of-three pivot selection to improve performance on partially sorted arrays
/// - Switches to insertion sort for small subarrays (length < 10) to improve performance
/// - Employs tail-call optimization to prevent stack overflow
///
/// # Examples
/// ```
/// use blocks_cs_sort::algorithms::quicksort::sort;
/// let mut numbers = vec![3, 1, 4, 1, 5, 9, 2, 6, 5, 3];
/// sort(&mut numbers);
/// assert_eq!(numbers, vec![1, 1, 2, 3, 3, 4, 5, 5, 6, 9]);
/// ```
pub fn sort<T: Ord>(arr: &mut [T]) {
// Threshold for switching to insertion sort
const INSERTION_SORT_THRESHOLD: usize = 10;

fn insertion_sort<T: Ord>(arr: &mut [T]) {
for i in 1..arr.len() {
let mut j = i;
while j > 0 && arr[j - 1] > arr[j] {
arr.swap(j - 1, j);
j -= 1;
}
}
}

fn quicksort_internal<T: Ord>(arr: &mut [T]) {
// Use iterative approach for tail-call optimization
let mut stack = Vec::with_capacity(32); // log₂(usize::MAX) ≈ 32 for 64-bit systems
stack.push((0, arr.len()));

while let Some((start, end)) = stack.pop() {
let len = end - start;

if len <= 1 {
continue;
}

if len < INSERTION_SORT_THRESHOLD {
insertion_sort(&mut arr[start..end]);
continue;
}

let pivot_idx = partition(&mut arr[start..end]) + start;

// Push larger partition first to maintain O(log n) stack space
if pivot_idx - start > end - (pivot_idx + 1) {
stack.push((start, pivot_idx));
stack.push((pivot_idx + 1, end));
} else {
stack.push((pivot_idx + 1, end));
stack.push((start, pivot_idx));
}
}
}

quicksort_internal(arr);
}

fn partition<T: Ord>(arr: &mut [T]) -> usize {
let len = arr.len();
if len <= 1 {
return 0;
}

// Median-of-three pivot selection
let mid = len / 2;
let last = len - 1;

// Sort first, middle, and last elements
if arr[0] > arr[mid] {
arr.swap(0, mid);
}
if arr[mid] > arr[last] {
arr.swap(mid, last);
}
if arr[0] > arr[mid] {
arr.swap(0, mid);
}

// Move pivot to end
arr.swap(mid, last - 1);
let pivot_idx = last - 1;

// Partition around pivot
let mut i = 0;
let mut j = pivot_idx;

while i < j {
while i < j && arr[i] <= arr[pivot_idx] {
i += 1;
}
while i < j && arr[j - 1] > arr[pivot_idx] {
j -= 1;
}
if i < j {
arr.swap(i, j - 1);
}
}

// Restore pivot
if arr[i] > arr[pivot_idx] {
arr.swap(i, pivot_idx);
i
} else {
pivot_idx
}
}

#[cfg(test)]
mod tests {
use super::*;
use std::cmp::Ordering;

// Basic functionality tests
#[test]
fn test_empty_array() {
let mut arr: Vec<i32> = vec![];
sort(&mut arr);
assert_eq!(arr, vec![]);
}

#[test]
fn test_single_element() {
let mut arr = vec![1];
sort(&mut arr);
assert_eq!(arr, vec![1]);
}

#[test]
fn test_sorted_array() {
let mut arr = vec![1, 2, 3, 4, 5];
sort(&mut arr);
assert_eq!(arr, vec![1, 2, 3, 4, 5]);
}

#[test]
fn test_reverse_sorted_array() {
let mut arr = vec![5, 4, 3, 2, 1];
sort(&mut arr);
assert_eq!(arr, vec![1, 2, 3, 4, 5]);
}

// Edge cases
#[test]
fn test_all_equal_elements() {
let mut arr = vec![1, 1, 1, 1, 1];
sort(&mut arr);
assert_eq!(arr, vec![1, 1, 1, 1, 1]);
}

#[test]
fn test_two_unique_elements_repeated() {
let mut arr = vec![2, 1, 2, 1, 2, 1];
sort(&mut arr);
assert_eq!(arr, vec![1, 1, 1, 2, 2, 2]);
}

// Large array tests
#[test]
fn test_large_random_array() {
let mut arr: Vec<i32> = (0..1000).map(|i| (i * 17 + 11) % 1000).collect();
let mut expected = arr.clone();
expected.sort();
sort(&mut arr);
assert_eq!(arr, expected);
}

#[test]
fn test_large_mostly_sorted_array() {
let mut arr: Vec<i32> = (0..1000).collect();
// Introduce some disorder
for i in 0..50 {
arr.swap(i * 2, i * 2 + 1);
}
let mut expected = arr.clone();
expected.sort();
sort(&mut arr);
assert_eq!(arr, expected);
}

// Custom type tests
#[derive(Debug, Eq, PartialEq, Clone)]
struct Person {
name: String,
age: u32,
}

impl PartialOrd for Person {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}

impl Ord for Person {
fn cmp(&self, other: &Self) -> Ordering {
self.age.cmp(&other.age)
.then_with(|| self.name.cmp(&other.name))
}
}

#[test]
fn test_custom_type() {
let mut people = vec![
Person { name: "Alice".to_string(), age: 30 },
Person { name: "Bob".to_string(), age: 25 },
Person { name: "Charlie".to_string(), age: 35 },
Person { name: "David".to_string(), age: 25 },
];

sort(&mut people);

assert_eq!(people, vec![
Person { name: "Bob".to_string(), age: 25 },
Person { name: "David".to_string(), age: 25 },
Person { name: "Alice".to_string(), age: 30 },
Person { name: "Charlie".to_string(), age: 35 },
]);
}

// Performance edge cases
#[test]
fn test_array_with_many_duplicates() {
let mut arr = Vec::with_capacity(1000);
for i in 0..1000 {
arr.push(i % 4); // Only 4 unique values
}
let mut expected = arr.clone();
expected.sort();
sort(&mut arr);
assert_eq!(arr, expected);
}

#[test]
fn test_nearly_sorted_array() {
let mut arr: Vec<i32> = (0..1000).collect();
// Swap every 100th element
for i in 0..10 {
arr.swap(i * 100, i * 100 + 1);
}
let mut expected = arr.clone();
expected.sort();
sort(&mut arr);
assert_eq!(arr, expected);
}
}
46 changes: 32 additions & 14 deletions crates/cs/blocks-cs-sort/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,32 @@
pub fn add(left: u64, right: u64) -> u64 {
left + right
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn it_works() {
let result = add(2, 2);
assert_eq!(result, 4);
}
}
/*!
This crate provides a collection of sorting algorithms implemented in Rust.
Each algorithm is implemented with a focus on:
- Performance optimizations
- Memory efficiency
- Comprehensive testing
- Clear documentation
- Modern Rust idioms
# Available Algorithms
## Comparison Sorts
- [QuickSort](algorithms/quicksort/index.html): An efficient, in-place sorting algorithm with O(n log n) average case complexity
# Usage Example
```rust
use blocks_cs_sort::algorithms::quicksort;
let mut numbers = vec![3, 1, 4, 1, 5, 9, 2, 6, 5, 3];
quicksort(&mut numbers);
assert_eq!(numbers, vec![1, 1, 2, 3, 3, 4, 5, 5, 6, 9]);
```
# Features
- Generic implementations that work with any type implementing required traits
- Comprehensive test suites including edge cases and performance tests
- Detailed documentation with complexity analysis and usage examples
*/

pub mod algorithms;

0 comments on commit 2fa5c89

Please sign in to comment.