From e61d8861ce4161501d1a915674e7dd4e7ad597e1 Mon Sep 17 00:00:00 2001 From: Anson Biggs Date: Thu, 31 Mar 2022 11:02:52 -0700 Subject: [PATCH] updated code borrowed from internet --- .gitignore | 3 + rust/Cargo.toml | 13 ++++ rust/src/main.rs | 156 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 172 insertions(+) create mode 100644 .gitignore create mode 100644 rust/Cargo.toml create mode 100644 rust/src/main.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2280feb --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +target/ +debug/ +rust/Cargo.lock \ No newline at end of file diff --git a/rust/Cargo.toml b/rust/Cargo.toml new file mode 100644 index 0000000..37355f6 --- /dev/null +++ b/rust/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "rust" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +rayon = "1.5.1" +rand = "0.8.5" + +[dev-dependencies] +quickcheck = "0.8.0" \ No newline at end of file diff --git a/rust/src/main.rs b/rust/src/main.rs new file mode 100644 index 0000000..6eafc20 --- /dev/null +++ b/rust/src/main.rs @@ -0,0 +1,156 @@ +use rayon::join; + +fn partition(d: &mut [T], is_less: &F) -> usize +where + F: Fn(&T, &T) -> bool, +{ + d.swap(0, d.len() / 2); + let mut mid = 0; + for i in 1..d.len() { + if is_less(&d[i], &d[0]) { + mid += 1; + d.swap(i, mid); + } + } + d.swap(0, mid); + mid +} + +fn insert_sort(d: &mut [T], is_less: &F) +where + F: Fn(&T, &T) -> bool, +{ + for i in 1..d.len() { + let mut n = i; + while n > 0 && is_less(&d[n], &d[n - 1]) { + d.swap(n, n - 1); + n -= 1; + } + } +} + +fn quick_sort(d: &mut [T], is_less: &F) +where + F: Fn(&T, &T) -> bool, +{ + if d.len() > 30 { + let mut mid = partition(d, is_less); + if mid < d.len() / 2 { + mid += 1; + } + let (left, right) = d.split_at_mut(mid); + quick_sort(left, is_less); + quick_sort(right, is_less); + } else { + insert_sort(d, is_less); + } +} + +fn par_quick_sort(d: &mut [T], is_less: &F) +where + F: Fn(&T, &T) -> bool + Send + Sync, + T: Send, +{ + if d.len() > 30 { + let mut mid = partition(d, is_less); + if mid < d.len() / 2 { + mid += 1; + } + let (left, right) = d.split_at_mut(mid); + + if right.len() > 100_000 { + join( + || par_quick_sort(left, is_less), + || par_quick_sort(right, is_less), + ); + } else { + quick_sort(left, is_less); + quick_sort(right, is_less); + } + } else { + insert_sort(d, is_less); + } +} + +pub trait QSort { + fn qsort(&mut self); + + fn is_sorted(&self) -> bool; +} + +impl QSort for [T] +where + T: Ord, +{ + fn qsort(&mut self) { + quick_sort(self, &|a: &T, b: &T| a.lt(b)); + } + + fn is_sorted(&self) -> bool { + self.windows(2).all(|w| w[0] <= w[1]) + } +} + +pub trait ParQSort { + fn par_qsort(&mut self); +} + +impl ParQSort for [T] +where + T: Ord + Send, +{ + fn par_qsort(&mut self) { + par_quick_sort(self, &|a: &T, b: &T| a.lt(b)); + } +} + +use rand::distributions::{Distribution, Uniform}; +use rand::thread_rng; +use rayon::prelude::*; +use std::time::Instant; + +fn main() { + let len = 50_000_000; + let orig: Vec = Uniform::from(0..1_000_000_000) + .sample_iter(&mut thread_rng()) + .take(len) + .collect(); + + macro_rules! test_sort { + ($name:expr, $sort:ident) => { + println!( + "Sorting {} million numbers with {} in Rust ...", + len / 1_000_000, + $name + ); + let mut data = orig.clone(); + let start = Instant::now(); + data.$sort(); + println!("Time: {:.2?}", start.elapsed()); + assert!(data.is_sorted()); + } + } + + test_sort!("naive quicksort", qsort); + test_sort!("stdlib quicksort", sort_unstable); + test_sort!("naive parallel quicksort", par_qsort); + test_sort!("Rayon quicksort", par_sort_unstable); +} + +#[cfg(test)] +mod test { + use super::*; + use quickcheck::quickcheck; + + quickcheck! { + fn test_all(d: Vec) -> bool { + let mut d = d; + + let mut expected = d.clone(); + expected.sort_unstable(); + d.par_qsort(); + + expected == d + } + } +} \ No newline at end of file