From 601ab488d6c689aeb9616a6a7af8867773c3855f Mon Sep 17 00:00:00 2001 From: Ishaan Goel Date: Thu, 3 Oct 2024 17:00:45 +0530 Subject: [PATCH 1/7] feat: From Trait implementation for Strsplit --- zung_mini/src/strsplit/mod.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/zung_mini/src/strsplit/mod.rs b/zung_mini/src/strsplit/mod.rs index 5a72017..8e14aef 100644 --- a/zung_mini/src/strsplit/mod.rs +++ b/zung_mini/src/strsplit/mod.rs @@ -123,6 +123,7 @@ where /// occurrences of the delimiter. /// /// This type is constructed by the [`strsplit()`](StrsplitExt::strsplit()) method. +#[derive(Debug, Clone, Copy)] pub struct Strsplit<'a, N> { remainder: Option<&'a str>, needle: N, @@ -181,6 +182,15 @@ where } } +impl<'a, N> From> for Vec<&'a str> +where + N: 'a + AsRef, +{ + fn from(value: Strsplit<'a, N>) -> Self { + value.into_vec() + } +} + fn find_needle(needle: &str, haystack: &str) -> Option<(usize, usize)> { haystack .find(needle) @@ -293,4 +303,13 @@ mod tests { let result = text.till_needle("is"); assert_eq!(result, "th"); } + + #[test] + fn test_from_and_into() { + let strsplit = "a b c d e f".strsplit(" "); + let vec1 = Vec::from(strsplit); + let vec2: Vec<&str> = strsplit.into(); + + assert_eq!(vec1, vec2) + } } From bd6b97e3ff3618cde7fa3224f494b6cce120a1c1 Mon Sep 17 00:00:00 2001 From: Ishaan Goel Date: Thu, 3 Oct 2024 18:27:37 +0530 Subject: [PATCH 2/7] chore: added more tests --- zung_mini/src/strsplit/mod.rs | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/zung_mini/src/strsplit/mod.rs b/zung_mini/src/strsplit/mod.rs index 8e14aef..b75535c 100644 --- a/zung_mini/src/strsplit/mod.rs +++ b/zung_mini/src/strsplit/mod.rs @@ -255,6 +255,24 @@ mod tests { assert_eq!(a.strsplit(",").into_vec(), vec!["a b c", " d e f"]); } + #[test] + fn strsplit_from_and_into() { + let strsplit = "a b c d e f".strsplit(" "); + let vec1 = Vec::from(strsplit); + let vec2: Vec<&str> = strsplit.into(); + + assert_eq!(vec1, vec2) + } + + #[test] + fn strsplit_empty_haystack() { + let haystack = ""; + let needle = ","; + + let vec = haystack.strsplit(needle).into_vec(); + assert_eq!(vec, vec![""]); + } + #[test] fn till_needle_finds_substring() { let text = "hello world"; @@ -303,13 +321,4 @@ mod tests { let result = text.till_needle("is"); assert_eq!(result, "th"); } - - #[test] - fn test_from_and_into() { - let strsplit = "a b c d e f".strsplit(" "); - let vec1 = Vec::from(strsplit); - let vec2: Vec<&str> = strsplit.into(); - - assert_eq!(vec1, vec2) - } } From bbb6d686bac0de4f5a8b2876c3133c2869e21aaa Mon Sep 17 00:00:00 2001 From: Ishaan Goel Date: Fri, 4 Oct 2024 21:00:58 +0530 Subject: [PATCH 3/7] chore: API change `till_needle()` is now on strsplit and not directly on the implementors of the trait. This allows for Strsplit type to be used in other contexts also. Also this makes the API more explicit and does not hide the implementation details. --- zung_mini/src/lib.rs | 2 +- zung_mini/src/strsplit/mod.rs | 62 ++++++++++++++++------------------- 2 files changed, 30 insertions(+), 34 deletions(-) diff --git a/zung_mini/src/lib.rs b/zung_mini/src/lib.rs index 9a93828..140409b 100644 --- a/zung_mini/src/lib.rs +++ b/zung_mini/src/lib.rs @@ -128,7 +128,7 @@ impl MiniArgs { println!("{:?}", result); } StrsplitCommands::Until { needle, string } => { - let result = string.till_needle(needle); + let result = string.strsplit(needle).till_needle(); println!("{:?}", result); } }, diff --git a/zung_mini/src/strsplit/mod.rs b/zung_mini/src/strsplit/mod.rs index b75535c..37a0228 100644 --- a/zung_mini/src/strsplit/mod.rs +++ b/zung_mini/src/strsplit/mod.rs @@ -66,32 +66,6 @@ where fn strsplit

(&'a self, needle: P) -> Strsplit<'a, P> where P: 'b + AsRef; - - /// Returns the substring before the first occurrence of the given `needle` - /// without scanning the entire string. - /// - /// This function splits the string using the provided `needle` and immediately - /// returns the portion of the string before the first occurrence of the `needle`. - /// It stops searching once the `needle` is found, making it efficient as it - /// avoids iterating over the entire string unnecessarily. If the `needle` is not found, - /// the function returns the entire string. - /// - /// # Example - /// - /// ```rust - /// use zung_mini::strsplit::StrsplitExt; - /// let text = "hello world"; - /// - /// let result = text.till_needle(" "); - /// assert_eq!(result, "hello"); - /// ``` - fn till_needle

(&'a self, needle: P) -> &str - where - P: 'b + AsRef + Clone, - { - let mut splitter = self.strsplit(needle.clone()); - splitter.next().unwrap() - } } impl<'a, 'b> StrsplitExt<'a, 'b> for String @@ -161,6 +135,28 @@ where pub fn into_vec(self) -> Vec<&'a str> { self.collect() } + + /// Returns the substring before the first occurrence of the given `needle` + /// without scanning the entire string. + /// + /// This function splits the string using the provided `needle` and immediately + /// returns the portion of the string before the first occurrence of the `needle`. + /// It stops searching once the `needle` is found, making it efficient as it + /// avoids iterating over the entire string unnecessarily. If the `needle` is not found, + /// the function returns the entire string. + /// + /// # Example + /// + /// ```rust + /// use zung_mini::strsplit::StrsplitExt; + /// let text = "hello world"; + /// + /// let result = text.strsplit(" ").till_needle(); + /// assert_eq!(result, "hello"); + /// ``` + pub fn till_needle(&mut self) -> &'a str { + self.next().unwrap() + } } impl<'a, N> Iterator for Strsplit<'a, N> @@ -276,28 +272,28 @@ mod tests { #[test] fn till_needle_finds_substring() { let text = "hello world"; - let result = text.till_needle(" "); + let result = text.strsplit(" ").till_needle(); assert_eq!(result, "hello"); } #[test] fn till_needle_returns_entire_string_if_needle_not_found() { let text = "hello"; - let result = text.till_needle(","); + let result = text.strsplit(" ").till_needle(); assert_eq!(result, "hello"); } #[test] fn till_needle_with_multiple_occurrences() { let text = "apple,banana,orange"; - let result = text.till_needle(","); + let result = text.strsplit(",").till_needle(); assert_eq!(result, "apple"); // Stops at first occurrence } #[test] fn till_needle_returns_none_for_empty_string() { let text = ""; - let result = text.till_needle(","); + let result = text.strsplit(" ").till_needle(); assert_eq!(result, ""); } @@ -305,20 +301,20 @@ mod tests { #[should_panic(expected = "Empty needle is not allowed")] fn till_needle_empty_needle_panics() { let text = "example"; - let _ = text.till_needle(""); // Should panic due to empty needle + let _ = text.strsplit("").till_needle(); // Should panic due to empty needle } #[test] fn till_needle_handles_special_characters() { let text = "foo@bar.com"; - let result = text.till_needle("@"); + let result = text.strsplit("@").till_needle(); assert_eq!(result, "foo"); } #[test] fn till_needle_works_with_longer_needle() { let text = "this is a test string"; - let result = text.till_needle("is"); + let result = text.strsplit("is").till_needle(); assert_eq!(result, "th"); } } From 4eb6ac2e59c0fa99af3885afea5e9f9bdf6a959a Mon Sep 17 00:00:00 2001 From: Ishaan Goel Date: Fri, 4 Oct 2024 22:29:16 +0530 Subject: [PATCH 4/7] feat: Introducing a sorting algorithm library --- zung_mini/Cargo.toml | 4 ++++ zung_mini/README.md | 15 ++++++++++++--- zung_mini/src/lib.rs | 6 ++++++ 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/zung_mini/Cargo.toml b/zung_mini/Cargo.toml index e892d16..92e0ce2 100644 --- a/zung_mini/Cargo.toml +++ b/zung_mini/Cargo.toml @@ -11,3 +11,7 @@ keywords = ["projects", "learning", "mini"] [dependencies] clap = { version = "4.5.18", features = ["derive"] } +indicatif = "0.17" +colored = "2.1.0" +rand = "0.8" +prettytable = "0.10.0" diff --git a/zung_mini/README.md b/zung_mini/README.md index 2fab891..4b25a2b 100644 --- a/zung_mini/README.md +++ b/zung_mini/README.md @@ -10,6 +10,10 @@ _This library is intended for **learning purposes only**. While I will do my bes - [Mini Project 1](#mini-project-1---progbar) - [Features](#features) +- [Mini Project 2](#mini-project-2---strsplit) + - [Features](#features) +- [Mini Project 3](#mini-project-3---orst) + - [Features](#features) - [Usage](#usage) # Mini Project 1 - ProgBar @@ -31,11 +35,16 @@ The [`ProgBar`](https://docs.rs/zung_mini/latest/zung_mini/progbar/index.html) m The [`Strsplit`](https://docs.rs/zung_mini/latest/zung_mini/strsplit/index.html) module provides an efficient, iterator-based string splitting utility for Rust. It extends both `String` and `&str` types with a `strsplit` method, allowing users to split strings based on a specified delimiter and iterate over the resulting substrings lazily or collect them all at once. This is particularly useful when you need efficient and flexible string splitting behavior. +# Mini Project 3 - Orst + +**\_ _Implementation of custom sorting algorithms along with a benchmark following [Crust of Rust: Sorting Algorithms](https://www.youtube.com/watch?v=h4RkCyJyXmM)_** + +The [`Orst`](https://docs.rs/zung_mini/latest/zung_mini/orst/index.html) module provides an custom implementations of sorting algorithms along with a simple algorithm. + ## Features -- **Lazy Evaluation**: The splitting is performed lazily as the iterator progresses, avoiding unnecessary allocation or processing. -- **Supports Bounded Splits**: You can split on any valid substring and return the result immediately upon the first match. -- **Panics on Empty Needle**: Ensures correct usage by panicking if an empty delimiter is passed. +- It sorts stuff. +- Easy to use. --- diff --git a/zung_mini/src/lib.rs b/zung_mini/src/lib.rs index 140409b..176cb05 100644 --- a/zung_mini/src/lib.rs +++ b/zung_mini/src/lib.rs @@ -2,6 +2,7 @@ //! //! Mini rust projects that target specific features of rust +pub mod orst; pub mod progbar; pub mod strsplit; @@ -32,6 +33,9 @@ enum MiniCommands { #[command(subcommand)] command: StrsplitCommands, }, + + /// Run custom sorting algorithms. + Orst, } #[derive(Clone, Subcommand, Debug)] @@ -132,6 +136,8 @@ impl MiniArgs { println!("{:?}", result); } }, + + MiniCommands::Orst => orst::benchmark::run_orst(), } } } From ae2bfc5f0b61c342e9227fa4ba5518c309911531 Mon Sep 17 00:00:00 2001 From: Ishaan Goel Date: Fri, 4 Oct 2024 22:29:22 +0530 Subject: [PATCH 5/7] feat: Introducing a sorting algorithm library --- zung_mini/src/orst/benchmark.rs | 234 ++++++++++++++++++ zung_mini/src/orst/mod.rs | 37 +++ zung_mini/src/orst/sorters/bubble_sorter.rs | 120 +++++++++ .../src/orst/sorters/insertion_sorter.rs | 153 ++++++++++++ zung_mini/src/orst/sorters/mod.rs | 7 + zung_mini/src/orst/sorters/quick_sorter.rs | 155 ++++++++++++ .../src/orst/sorters/selection_sorter.rs | 160 ++++++++++++ 7 files changed, 866 insertions(+) create mode 100644 zung_mini/src/orst/benchmark.rs create mode 100644 zung_mini/src/orst/mod.rs create mode 100644 zung_mini/src/orst/sorters/bubble_sorter.rs create mode 100644 zung_mini/src/orst/sorters/insertion_sorter.rs create mode 100644 zung_mini/src/orst/sorters/mod.rs create mode 100644 zung_mini/src/orst/sorters/quick_sorter.rs create mode 100644 zung_mini/src/orst/sorters/selection_sorter.rs diff --git a/zung_mini/src/orst/benchmark.rs b/zung_mini/src/orst/benchmark.rs new file mode 100644 index 0000000..d368a1c --- /dev/null +++ b/zung_mini/src/orst/benchmark.rs @@ -0,0 +1,234 @@ +use colored::Colorize; +use rand::{self, Rng}; +use std::{cell::Cell, rc::Rc, time::Instant}; + +use prettytable::{row, Table}; + +use super::{BubbleSorter, InsertionSorter, QuickSorter, SelectionSorter, Sorter}; + +const ZERO: usize = 0; +const ONE: usize = 1; +const HUNDRED: usize = 100; +const TEN_THOUSAND: usize = 10_000; +const HUNDRED_THOUSAND: usize = 100_000; +const MILLION: usize = 1_000_000; +const HUNDRED_MILLION: usize = 100_000_000; + +// In this the `elem` will be compared and the `comparison_counter` will be ignored. +#[derive(Clone)] +struct SortEvaluator { + // For making the comparisons + elem: T, + // This counter will update every time the `elem` is compared. + // Therefore, obviously this has to be a mutable value. + // Therefore, it is rapped in reference counter and a cell + comparison_counter: Rc>, +} + +impl SortEvaluator { + fn new(elem: T, comparison_counter: Rc>) -> Self { + Self { + elem, + comparison_counter, + } + } +} + +// Trait for equality comparisons which are equivalence relations. +// +// This means, that in addition to a == b and a != b being strict inverses, the equality +// must be (for all a, b and c): +// +// reflexive: a == a; +// symmetric: a == b implies b == a; and +// transitive: a == b and b == c implies a == c. +// +// This property cannot be checked by the compiler, and therefore Eq implies PartialEq, +// and has no extra methods. +impl Eq for SortEvaluator {} + +// This trait allows for partial equality, +// for types that do not have a full equivalence relation. +// For example, in floating point numbers NaN != NaN, +// so floating point types implement PartialEq but not Eq. +// Formally speaking, when Rhs == Self, +// this trait corresponds to a partial equivalence relation. +impl PartialEq for SortEvaluator { + fn eq(&self, other: &Self) -> bool { + self.comparison_counter + .set(self.comparison_counter.get() + 1); + self.elem == other.elem + } +} + +// Trait for types that form a partial order. +// +// The lt, le, gt, and ge methods of this trait can be called using the <, <=, >, and >= operators, respectively. +// +// The methods of this trait must be consistent with each other and with those of PartialEq. The following conditions must hold: +// +// 1. a == b if and only if partial_cmp(a, b) == Some(Equal). +// 2. a < b if and only if partial_cmp(a, b) == Some(Less) +// 3. a > b if and only if partial_cmp(a, b) == Some(Greater) +// 4. a <= b if and only if a < b || a == b +// 5. a >= b if and only if a > b || a == b +// 6. a != b if and only if !(a == b). +// +impl PartialOrd for SortEvaluator { + fn partial_cmp(&self, other: &Self) -> Option { + self.comparison_counter + .set(self.comparison_counter.get() + 1); + self.elem.partial_cmp(&other.elem) + } +} + +// Trait for types that form a total order. +// +// Implementations must be consistent with the PartialOrd implementation, +// and ensure max, min, and clamp are consistent with cmp: +// +// partial_cmp(a, b) == Some(cmp(a, b)). +// max(a, b) == max_by(a, b, cmp) (ensured by the default implementation). +// min(a, b) == min_by(a, b, cmp) (ensured by the default implementation). +// For a.clamp(min, max), see the method docs (ensured by the default implementation). +// +// It’s easy to accidentally make cmp and partial_cmp disagree by deriving +// some of the traits and manually implementing others. +impl Ord for SortEvaluator { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.comparison_counter + .set(self.comparison_counter.get() + 1); + self.elem.cmp(&other.elem) + } +} + +fn run_bench( + sorter: S, + values: &mut [SortEvaluator], + comparisons: Rc>, +) -> usize +where + T: Ord + Eq + Clone, + S: Sorter>, +{ + comparisons.set(0); + sorter.sort(values); + + comparisons.get() +} + +pub fn run_orst() { + let mut random = rand::thread_rng(); + let counter = Rc::new(Cell::new(0)); + for &n in &[ + ZERO, + ONE, + HUNDRED, + TEN_THOUSAND, + HUNDRED_THOUSAND, + MILLION, + HUNDRED_MILLION, + ] { + let mut values = Vec::with_capacity(n); + for _ in 0..n { + values.push(SortEvaluator::new(random.gen::(), counter.clone())); + } + + println!( + "{} {}", + "List Size -> ".bold().underline().blue(), + n.to_string().bold() + ); + + let mut table = Table::new(); + table.add_row(row![ + "Sorter".bold(), + "Comparisons Made".bold(), + "Time Taken".bold() + ]); + + if n <= HUNDRED_THOUSAND { + let now = Instant::now(); + let took = run_bench(BubbleSorter, &mut values, counter.clone()); + table.add_row(row![ + "Bubble Sort", + took.to_string(), + format!("{:?}", now.elapsed()) + ]); + } else { + table.add_row(row!["Bubble Sort", "Not Doing It".red(), "It is Stupid"]); + } + + if n <= HUNDRED_THOUSAND { + let now = Instant::now(); + let took = run_bench( + InsertionSorter { smart: true }, + &mut values, + counter.clone(), + ); + + table.add_row(row![ + "Insertion Sort", + took.to_string(), + format!("{:?}", now.elapsed()) + ]); + + let now = Instant::now(); + let took = run_bench( + InsertionSorter { smart: false }, + &mut values, + counter.clone(), + ); + + table.add_row(row![ + "Insertion Sort (not smart)", + took.to_string(), + format!("{:?}", now.elapsed()) + ]); + } else { + table.add_row(row!["Insertion Sort", "Not Doing It".red(), "It is Stupid"]); + } + + if n <= HUNDRED_THOUSAND { + let now = Instant::now(); + let took = run_bench(SelectionSorter, &mut values, counter.clone()); + table.add_row(row![ + "Selection Sort", + took.to_string(), + format!("{:?}", now.elapsed()) + ]); + } else { + table.add_row(row!["Selection Sort", "Not Doing It".red(), "It is Stupid"]); + } + + let now = Instant::now(); + let took = run_bench(QuickSorter, &mut values, counter.clone()); + + table.add_row(row![ + "Quick Sort", + took.to_string(), + format!("{:?}", now.elapsed()) + ]); + + // TODO: Implement this. + // + // let now = Instant::now(); + // let took = run_bench(StdSorter { stable: true }, &mut values, counter.clone()); + // table.add_row(row![ + // "Standard Library Sort Stable", + // took.to_string(), + // format!("{:?}", now.elapsed()) + // ]); + // + // let now = Instant::now(); + // let took = run_bench(StdSorter { stable: false }, &mut values, counter.clone()); + // table.add_row(row![ + // "Standart Library Sort Unstable", + // took.to_string(), + // format!("{:?}", now.elapsed()) + // ]); + + table.printstd(); + println!(); + } +} diff --git a/zung_mini/src/orst/mod.rs b/zung_mini/src/orst/mod.rs new file mode 100644 index 0000000..fe8ae3c --- /dev/null +++ b/zung_mini/src/orst/mod.rs @@ -0,0 +1,37 @@ +//! Implementation of sorting algorithms from [Crust of Rust: Sorting +//! Algorithms](https://www.youtube.com/watch?v=h4RkCyJyXmM) +//! +//! # Example +//! +//! ``` +//! use zung_mini::orst::BubbleSorter; +//! use zung_mini::orst::Sorter; +//! +//! let mut slice = vec![1, 3, 2, 5, 4]; +//! BubbleSorter.sort(&mut slice); +//! assert_eq!(vec![1, 2, 3, 4, 5], slice); +//! ``` + +pub mod benchmark; +mod sorters; + +pub use sorters::bubble_sorter::BubbleSorter; +pub use sorters::insertion_sorter::InsertionSorter; +pub use sorters::quick_sorter::QuickSorter; +pub use sorters::selection_sorter::SelectionSorter; + +/// The sorting algorithm must implement the trait `Sorter`. +pub trait Sorter +where + T: Ord, +{ + fn sort(&self, slice: &mut [T]); +} + +pub trait Sort +where + S: Sorter, + T: Ord, +{ + fn orst(&mut self); +} diff --git a/zung_mini/src/orst/sorters/bubble_sorter.rs b/zung_mini/src/orst/sorters/bubble_sorter.rs new file mode 100644 index 0000000..9e4858e --- /dev/null +++ b/zung_mini/src/orst/sorters/bubble_sorter.rs @@ -0,0 +1,120 @@ +use crate::orst::Sorter; +use indicatif::{ProgressBar, ProgressStyle}; + +/// An implementation of [Bubble Sort](https://en.wikipedia.org/wiki/Bubble_sort) +/// +/// # Usage +///``` +/// use zung_mini::orst::{BubbleSorter, Sorter}; +/// +/// let mut slice = [1, 5, 4, 2, 3]; +/// BubbleSorter.sort(&mut slice); +/// assert_eq!(slice, [1, 2, 3, 4, 5]); +///``` +/// # Explanation +/// +/// Bubble sort, sometimes referred to as sinking sort, +/// is a simple sorting algorithm that repeatedly steps +/// through the list, compares adjacent elements and swaps +/// them if they are in the wrong order. The pass through +/// the list is repeated until the list is sorted. The +/// algorithm, which is a comparison sort, is named for the +/// way smaller or larger elements "bubble" to the top of the list. +/// +/// +/// # Algorithm +/// +/// ``` +/// let mut slice = vec![1, 3, 2, 5, 4]; +/// +/// let mut swapped = true; +/// +/// while swapped { +/// swapped = false; +/// for i in 0..(slice.len() - 1) { +/// // swap the elements at index if the current element is +/// // bigger that the next element. +/// if slice[i] > slice[i + 1] { +/// slice.swap(i, i + 1); +/// swapped = true; +/// } +/// } +/// } +/// ``` +#[derive(Default)] +pub struct BubbleSorter; + +impl Sorter for BubbleSorter +where + T: Ord, +{ + #[inline] + fn sort(&self, slice: &mut [T]) { + let pb = ProgressBar::new(slice.len() as u64); + pb.set_style( + ProgressStyle::with_template( + "Bubble Sort -> {spinner:.green} [{elapsed_precise}] [{bar:50.cyan/blue}] On Slice: ({pos}/{len}, ETA: {eta})", + ) + .unwrap(), + ); + + let mut swapped = true; + + while swapped { + swapped = false; + for i in 1..slice.len() { + if slice[i - 1] > slice[i] { + slice.swap(i - 1, i); + swapped = true; + } + } + pb.inc(1); + } + } +} + +#[cfg(test)] +mod tests { + + use super::*; + + #[test] + fn arbitrary_array() { + let mut slice = [1, 5, 4, 2, 3]; + BubbleSorter.sort(&mut slice); + assert_eq!(slice, [1, 2, 3, 4, 5]); + } + + #[test] + fn sorted_array() { + let mut slice = (1..10).collect::>(); + BubbleSorter.sort(&mut slice); + assert_eq!(slice, (1..10).collect::>()); + } + + #[test] + fn very_unsorted() { + let mut slice = (1..1000).rev().collect::>(); + BubbleSorter.sort(&mut slice); + assert_eq!(slice, (1..1000).collect::>()); + } + + #[test] + fn simple_edge_cases() { + let mut one = vec![1]; + BubbleSorter.sort(&mut one); + assert_eq!(one, vec![1]); + + let mut two = vec![1, 2]; + BubbleSorter.sort(&mut two); + assert_eq!(two, vec![1, 2]); + + let mut two = vec![2, 1]; + BubbleSorter.sort(&mut two); + assert_eq!(two, vec![1, 2]); + + let mut three = vec![3, 1, 2]; + BubbleSorter.sort(&mut three); + assert_eq!(three, vec![1, 2, 3]); + } +} diff --git a/zung_mini/src/orst/sorters/insertion_sorter.rs b/zung_mini/src/orst/sorters/insertion_sorter.rs new file mode 100644 index 0000000..ab82e45 --- /dev/null +++ b/zung_mini/src/orst/sorters/insertion_sorter.rs @@ -0,0 +1,153 @@ +use indicatif::{ProgressBar, ProgressStyle}; + +use crate::orst::Sorter; + +/// An implementation of [Insertion Sort](https://en.wikipedia.org/wiki/Insertion_sort) +/// +/// # Explanation +/// +/// Insertion sort is a simple sorting algorithm that builds the final sorted array (or list) one +/// item at a time +/// +/// Insertion sort iterates, consuming one input element each repetition, and grows a sorted output +/// list. At each iteration, insertion sort removes one element from the input data, finds the +/// location it belongs within the sorted list, and inserts it there. It repeats until no input +/// elements remain. +/// +/// Sorting is typically done in-place, by iterating up the array, growing the sorted list behind +/// it. At each array-position, it checks the value there against the largest value in the sorted +/// list (which happens to be next to it, in the previous array- position checked). If larger, it +/// leaves the element in place and moves to the next. If smaller, it finds the correct position +/// within the sorted list, shifts all the larger values up to make a space, and inserts into that +/// correct position. +/// +/// # Usage +///``` +/// use zung_mini::orst::{InsertionSorter, Sorter}; +/// +/// let mut slice = [1, 5, 4, 2, 3]; +/// InsertionSorter{ smart: true }.sort(&mut slice); +/// assert_eq!(slice, [1, 2, 3, 4, 5]); +///``` +pub struct InsertionSorter { + pub smart: bool, +} + +impl Sorter for InsertionSorter +where + T: Ord, +{ + #[inline] + fn sort(&self, slice: &mut [T]) { + let pb = ProgressBar::new(slice.len() as u64); + pb.set_style( + ProgressStyle::with_template( + "Insertion Sort -> {spinner:.green} [{elapsed_precise}] [{bar:50.cyan/blue}] On Slice: ({pos}/{len}, ETA: {eta})", + ) + .unwrap(), + ); + + for unsorted in 1..slice.len() { + if !self.smart { + let mut i = unsorted; + while i > 0 && slice[i - 1] > slice[i] { + slice.swap(i - 1, i); + i -= 1; + } + } else { + let i = match slice[..unsorted].binary_search(&slice[unsorted]) { + Ok(i) => i, + Err(i) => i, + }; + slice[i..=unsorted].rotate_right(1); + } + pb.inc(1); + } + } +} + +#[cfg(test)] +mod tests { + + use super::*; + + #[test] + fn arbitrary_array_smart() { + let mut slice = [1, 5, 4, 2, 3]; + InsertionSorter { smart: true }.sort(&mut slice); + assert_eq!(slice, [1, 2, 3, 4, 5]); + } + + #[test] + fn arbitrary_array_lame() { + let mut slice = [1, 5, 4, 2, 3]; + InsertionSorter { smart: false }.sort(&mut slice); + assert_eq!(slice, [1, 2, 3, 4, 5]); + } + + #[test] + fn sorted_array_smart() { + let mut slice = (1..10).collect::>(); + InsertionSorter { smart: true }.sort(&mut slice); + assert_eq!(slice, (1..10).collect::>()); + } + + #[test] + fn sorted_array_lame() { + let mut slice = (1..10).collect::>(); + InsertionSorter { smart: false }.sort(&mut slice); + assert_eq!(slice, (1..10).collect::>()); + } + + #[test] + fn very_unsorted_smart() { + let mut slice = (1..1000).rev().collect::>(); + InsertionSorter { smart: true }.sort(&mut slice); + assert_eq!(slice, (1..1000).collect::>()); + } + + #[test] + fn very_unsorted_lame() { + let mut slice = (1..1000).rev().collect::>(); + InsertionSorter { smart: false }.sort(&mut slice); + assert_eq!(slice, (1..1000).collect::>()); + } + + #[test] + fn simple_edge_cases_smart() { + let mut one = vec![1]; + InsertionSorter { smart: true }.sort(&mut one); + assert_eq!(one, vec![1]); + + let mut two = vec![1, 2]; + InsertionSorter { smart: true }.sort(&mut two); + assert_eq!(two, vec![1, 2]); + + let mut two = vec![2, 1]; + InsertionSorter { smart: true }.sort(&mut two); + assert_eq!(two, vec![1, 2]); + + let mut three = vec![3, 1, 2]; + InsertionSorter { smart: true }.sort(&mut three); + assert_eq!(three, vec![1, 2, 3]); + } + + #[test] + fn simple_edge_cases_lame() { + let mut one = vec![1]; + InsertionSorter { smart: false }.sort(&mut one); + assert_eq!(one, vec![1]); + + let mut two = vec![1, 2]; + InsertionSorter { smart: false }.sort(&mut two); + assert_eq!(two, vec![1, 2]); + + let mut two = vec![2, 1]; + InsertionSorter { smart: false }.sort(&mut two); + assert_eq!(two, vec![1, 2]); + + let mut three = vec![3, 1, 2]; + InsertionSorter { smart: false }.sort(&mut three); + assert_eq!(three, vec![1, 2, 3]); + } +} diff --git a/zung_mini/src/orst/sorters/mod.rs b/zung_mini/src/orst/sorters/mod.rs new file mode 100644 index 0000000..3fe2b80 --- /dev/null +++ b/zung_mini/src/orst/sorters/mod.rs @@ -0,0 +1,7 @@ +pub(crate) mod bubble_sorter; + +pub(crate) mod insertion_sorter; + +pub(crate) mod selection_sorter; + +pub(crate) mod quick_sorter; diff --git a/zung_mini/src/orst/sorters/quick_sorter.rs b/zung_mini/src/orst/sorters/quick_sorter.rs new file mode 100644 index 0000000..89950bb --- /dev/null +++ b/zung_mini/src/orst/sorters/quick_sorter.rs @@ -0,0 +1,155 @@ +use indicatif::{ProgressBar, ProgressStyle}; +use rand::Rng; + +use crate::orst::Sorter; + +/// An implementation of [Quick Sort](https://en.wikipedia.org/wiki/Quicksort) +/// +/// # Usage +///``` +/// use zung_mini::orst::{QuickSorter, Sorter}; +/// +/// let mut slice = [1, 5, 4, 2, 3]; +/// QuickSorter.sort(&mut slice); +/// assert_eq!(slice, [1, 2, 3, 4, 5]); +///``` +/// +/// # Explanation +/// +/// Quicksort is an in-place sorting algorithm. Developed +/// by British computer scientist Tony Hoare in 1959 and published +/// in 1961 it is still a commonly used algorithm for +/// sorting. When implemented well, it can be somewhat +/// faster than merge sort and about two or three times +/// faster than heapsort. +/// +/// # Algorithm +/// +/// Quicksort is a divide-and-conquer algorithm. +/// It works by selecting a 'pivot' element from +/// the array and partitioning the other elements into two sub +/// -arrays, according to whether they are less than +/// or greater than the pivot. For this reason, +/// it is sometimes called partition-exchange sort. +/// The sub-arrays are then sorted recursively. +/// This can be done in-place, requiring small +/// additional amounts of memory to perform the sorting. +pub struct QuickSorter; + +fn quicksort(slice: &mut [T]) { + const INSERTION_THRESHOLD: usize = 10; + + let pb = ProgressBar::new(slice.len() as u64); + pb.set_style( + ProgressStyle::with_template( + "Quick Sort -> {spinner:.green} [{elapsed_precise}] {bar:50.cyan/blue} On Slice: {pos}/{len}, ETA: {eta}", + ) + .unwrap(), + ); + + // Define a closure to encapsulate the counter + let quicksort_with_pb = |slice: &mut [T]| { + fn inner_quicksort(slice: &mut [T], pb: &ProgressBar) { + if slice.len() <= INSERTION_THRESHOLD { + slice.sort(); + return; + } + + let pivot_index = rand::thread_rng().gen_range(0..slice.len()); + slice.swap(0, pivot_index); + + let (pivot, rest) = slice.split_first_mut().expect("Unexpected empty slice"); + let mut left = 0; + let mut right = rest.len() - 1; + + while left <= right { + if &rest[left] <= pivot { + left += 1; + } else if &rest[right] > pivot { + if right == 0 { + break; + } + right -= 1; + } else { + rest.swap(left, right); + left += 1; + if right == 0 { + break; + } + right -= 1; + } + } + + let left = left + 1; + slice.swap(0, left - 1); + + let (left_part, right_part) = slice.split_at_mut(left - 1); + assert!(left_part.last() <= right_part.first()); + + pb.inc(1); + inner_quicksort(left_part, pb); + inner_quicksort(&mut right_part[1..], pb); + } + + // Call the inner recursive function + inner_quicksort(slice, &pb); + }; + + quicksort_with_pb(slice); +} + +impl Sorter for QuickSorter +where + T: Ord, +{ + #[inline] + fn sort(&self, slice: &mut [T]) { + quicksort(slice) + } +} + +#[cfg(test)] +mod tests { + + use super::*; + + #[test] + fn arbitrary_array() { + let mut slice = [1, 5, 4, 2, 3]; + QuickSorter.sort(&mut slice); + assert_eq!(slice, [1, 2, 3, 4, 5]); + } + + #[test] + fn sorted_array() { + let mut slice = (1..10).collect::>(); + QuickSorter.sort(&mut slice); + assert_eq!(slice, (1..10).collect::>()); + } + + #[test] + fn very_unsorted() { + let mut slice = (1..1000).rev().collect::>(); + QuickSorter.sort(&mut slice); + assert_eq!(slice, (1..1000).collect::>()); + } + + #[test] + fn simple_edge_cases() { + let mut one = vec![1]; + QuickSorter.sort(&mut one); + assert_eq!(one, vec![1]); + + let mut two = vec![1, 2]; + QuickSorter.sort(&mut two); + assert_eq!(two, vec![1, 2]); + + let mut two = vec![2, 1]; + QuickSorter.sort(&mut two); + assert_eq!(two, vec![1, 2]); + + let mut three = vec![3, 1, 2]; + QuickSorter.sort(&mut three); + assert_eq!(three, vec![1, 2, 3]); + } +} diff --git a/zung_mini/src/orst/sorters/selection_sorter.rs b/zung_mini/src/orst/sorters/selection_sorter.rs new file mode 100644 index 0000000..4d81e75 --- /dev/null +++ b/zung_mini/src/orst/sorters/selection_sorter.rs @@ -0,0 +1,160 @@ +use indicatif::{ProgressBar, ProgressStyle}; + +use crate::orst::Sorter; + +/// An implementation of [Selection Sort](https://en.wikipedia.org/wiki/Selection_sort) +/// +/// # Usage +///``` +/// use zung_mini::orst::{SelectionSorter, Sorter}; +/// +/// let mut slice = [1, 5, 4, 2, 3]; +/// SelectionSorter.sort(&mut slice); +/// assert_eq!(slice, [1, 2, 3, 4, 5]); +///``` +/// # Explanation +/// +/// Selection sort is an in-place comparison sorting +/// algorithm. It has an O(n2) time complexity, which +/// makes it inefficient on large lists, and generally +/// performs worse than the similar insertion sort. Selection sort is noted for its +/// simplicity and has performance advantages over more complicated algorithms +/// in certain situations, particularly where auxiliary memory is +/// limited. +/// +/// # Algorithm +/// +/// The algorithm divides the input list into two parts: +/// a sorted sublist of items which is built +/// up from left to right at the front ( +/// left) of the list and a sublist of +/// the remaining unsorted items that occupy the rest of +/// the list. Initially, the sorted sublist is +/// empty and the unsorted sublist is the entire input +/// list. The algorithm proceeds by finding the smallest +/// (or largest, depending on sorting order) +/// element in the unsorted sublist, exchanging (swapping +/// ) it with the leftmost unsorted element (putting +/// it in sorted order), and moving the sublist +/// boundaries one element to the right. +pub struct SelectionSorter; + +impl Sorter for SelectionSorter +where + T: Ord, +{ + fn sort(&self, slice: &mut [T]) { + let pb = ProgressBar::new(slice.len() as u64); + pb.set_style( + ProgressStyle::with_template( + "Selection Sort -> {spinner:.green} [{elapsed_precise}] [{bar:50.cyan/blue}] On Slice: ({pos}/{len}, ETA: {eta})", + ) + .unwrap(), + ); + for unsorted in 0..slice.len() { + let mut smallest_in_rest = unsorted; + for i in (unsorted + 1)..slice.len() { + if slice[i] < slice[smallest_in_rest] { + smallest_in_rest = i; + } + } + if unsorted != smallest_in_rest { + slice.swap(unsorted, smallest_in_rest); + } + pb.inc(1); + } + } +} + +#[test] +fn works() { + let mut things = vec![4, 2, 3, 5, 1]; + SelectionSorter.sort(&mut things); + assert_eq!(things, &[1, 2, 3, 4, 5]) +} + +#[cfg(test)] +mod tests { + + use super::*; + + #[test] + fn arbitrary_array_smart() { + let mut slice = [1, 5, 4, 2, 3]; + SelectionSorter.sort(&mut slice); + assert_eq!(slice, [1, 2, 3, 4, 5]); + } + + #[test] + fn arbitrary_array_lame() { + let mut slice = [1, 5, 4, 2, 3]; + SelectionSorter.sort(&mut slice); + assert_eq!(slice, [1, 2, 3, 4, 5]); + } + + #[test] + fn sorted_array_smart() { + let mut slice = (1..10).collect::>(); + SelectionSorter.sort(&mut slice); + assert_eq!(slice, (1..10).collect::>()); + } + + #[test] + fn sorted_array_lame() { + let mut slice = (1..10).collect::>(); + SelectionSorter.sort(&mut slice); + assert_eq!(slice, (1..10).collect::>()); + } + + #[test] + fn very_unsorted_smart() { + let mut slice = (1..1000).rev().collect::>(); + SelectionSorter.sort(&mut slice); + assert_eq!(slice, (1..1000).collect::>()); + } + + #[test] + fn very_unsorted_lame() { + let mut slice = (1..1000).rev().collect::>(); + SelectionSorter.sort(&mut slice); + assert_eq!(slice, (1..1000).collect::>()); + } + + #[test] + fn simple_edge_cases_smart() { + let mut one = vec![1]; + SelectionSorter.sort(&mut one); + assert_eq!(one, vec![1]); + + let mut two = vec![1, 2]; + SelectionSorter.sort(&mut two); + assert_eq!(two, vec![1, 2]); + + let mut two = vec![2, 1]; + SelectionSorter.sort(&mut two); + assert_eq!(two, vec![1, 2]); + + let mut three = vec![3, 1, 2]; + SelectionSorter.sort(&mut three); + assert_eq!(three, vec![1, 2, 3]); + } + + #[test] + fn simple_edge_cases_lame() { + let mut one = vec![1]; + SelectionSorter.sort(&mut one); + assert_eq!(one, vec![1]); + + let mut two = vec![1, 2]; + SelectionSorter.sort(&mut two); + assert_eq!(two, vec![1, 2]); + + let mut two = vec![2, 1]; + SelectionSorter.sort(&mut two); + assert_eq!(two, vec![1, 2]); + + let mut three = vec![3, 1, 2]; + SelectionSorter.sort(&mut three); + assert_eq!(three, vec![1, 2, 3]); + } +} From a95d845e56e0384a5ab72299b422df160b93320f Mon Sep 17 00:00:00 2001 From: Ishaan Goel Date: Fri, 4 Oct 2024 22:37:05 +0530 Subject: [PATCH 6/7] codecov ignore files --- .github/codecov.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/codecov.yml b/.github/codecov.yml index ad15589..9074d0c 100644 --- a/.github/codecov.yml +++ b/.github/codecov.yml @@ -11,9 +11,11 @@ coverage: # Avoid false negatives threshold: 1% -# Test files aren't important for coverage +# These files aren't important for coverage ignore: - "tests" + - "main.rs" + - "benchmark.rs" # Make comments less noisy comment: From 98d53e5795987f5d34884ab4d617dbb276032c12 Mon Sep 17 00:00:00 2001 From: Ishaan Goel Date: Fri, 4 Oct 2024 22:39:51 +0530 Subject: [PATCH 7/7] ingnore fix --- .github/codecov.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/codecov.yml b/.github/codecov.yml index 9074d0c..8693315 100644 --- a/.github/codecov.yml +++ b/.github/codecov.yml @@ -14,8 +14,9 @@ coverage: # These files aren't important for coverage ignore: - "tests" - - "main.rs" - - "benchmark.rs" + - "**/main.rs" + - "**/lib.rs" + - "**/benchmark.rs" # Make comments less noisy comment: