Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

A sorting algorithm library #25

Merged
merged 7 commits into from
Oct 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .github/codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,12 @@ coverage:
# Avoid false negatives
threshold: 1%

# Test files aren't important for coverage
# These files aren't important for coverage
ignore:
- "tests"
- "**/main.rs"
- "**/lib.rs"
- "**/benchmark.rs"

# Make comments less noisy
comment:
Expand Down
4 changes: 4 additions & 0 deletions zung_mini/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
15 changes: 12 additions & 3 deletions zung_mini/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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.

---

Expand Down
8 changes: 7 additions & 1 deletion zung_mini/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
//!
//! Mini rust projects that target specific features of rust

pub mod orst;
pub mod progbar;
pub mod strsplit;

Expand Down Expand Up @@ -32,6 +33,9 @@ enum MiniCommands {
#[command(subcommand)]
command: StrsplitCommands,
},

/// Run custom sorting algorithms.
Orst,
}

#[derive(Clone, Subcommand, Debug)]
Expand Down Expand Up @@ -128,10 +132,12 @@ impl MiniArgs {
println!("{:?}", result);
}
StrsplitCommands::Until { needle, string } => {
let result = string.till_needle(needle);
let result = string.strsplit(needle).till_needle();
println!("{:?}", result);
}
},

MiniCommands::Orst => orst::benchmark::run_orst(),
}
}
}
234 changes: 234 additions & 0 deletions zung_mini/src/orst/benchmark.rs
Original file line number Diff line number Diff line change
@@ -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<T> {
// 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<Cell<usize>>,
}

impl<T> SortEvaluator<T> {
fn new(elem: T, comparison_counter: Rc<Cell<usize>>) -> 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<T: Eq> Eq for SortEvaluator<T> {}

// 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<T: PartialEq> PartialEq for SortEvaluator<T> {
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<T: PartialOrd> PartialOrd for SortEvaluator<T> {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
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<T: Ord> Ord for SortEvaluator<T> {
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<T, S>(
sorter: S,
values: &mut [SortEvaluator<T>],
comparisons: Rc<Cell<usize>>,
) -> usize
where
T: Ord + Eq + Clone,
S: Sorter<SortEvaluator<T>>,
{
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::<i32>(), 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!();
}
}
37 changes: 37 additions & 0 deletions zung_mini/src/orst/mod.rs
Original file line number Diff line number Diff line change
@@ -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<T>
where
T: Ord,
{
fn sort(&self, slice: &mut [T]);
}

pub trait Sort<T, S>
where
S: Sorter<T>,
T: Ord,
{
fn orst(&mut self);
}
Loading
Loading