Compare commits

..

No commits in common. "main" and "v0.2.0" have entirely different histories.

5 changed files with 44 additions and 32 deletions

View file

@ -1,34 +1,28 @@
[package]
name = "median-accumulator"
version = "0.4.0"
version = "0.2.0"
edition = "2021"
authors = ["tuxmain <tuxmain@zettascript.org>"]
license = "AGPL-3.0-only"
repository = "https://git.txmn.tk/tuxmain/median-accumulator"
documentation = "https://docs.rs/median-accumulator/"
description = "Simple, fast, space-efficient, generic accumulator for computing median"
description = "Simple, fast, space-efficient accumulator for computing median"
categories = ["algorithms", "data-structures", "no-std"]
keywords = ["median"]
[dependencies]
cc-traits = { version = "2.0.0", default_features = false }
cc-traits = "1.0.0"
smallvec = { version = "^1.6", optional = true }
[features]
std = ["cc-traits/alloc", "cc-traits/std"]
nostd = ["cc-traits/nostd"]
smallvec = ["dep:smallvec", "cc-traits/smallvec"]
default = ["std"]
[dev-dependencies]
criterion = { version = "0.5.1", features = ["html_reports"] }
medianheap = "0.4.1"
criterion = { version = "0.4.0", features = ["html_reports"] }
medianheap = "0.3.0"
rand = "0.8.5"
smallvec = "^1.6"
[[bench]]
name = "comparison"
harness = false
[package.metadata.docs.rs]
features = ["std"]

View file

@ -7,10 +7,11 @@ Simple, space-efficient algorithm to compute the median of an accumulation of el
* **Space-efficient**: `O(D)` space, D being the number of _different_ samples, not the _total_ number of samples
* **Time-efficient**: push is `O(log(N))`
* **Generic**: `T: Clone + Ord`
* **No unsafe**
* **Tested**
* **No unsafe**, no deps
* **no_std** (optional): supports generic collections
Faster than other implementations if lots of samples have the same value. If this is not your case, you should use another implementation.
Faster than other implementations if there are samples having the same value. If this is not your case, you should use another implementation.
## Use
@ -44,7 +45,7 @@ For other collections than `Vec` or `SmallVec`, you must implement [cc-traits](h
## License
CopyLeft 2022-2024 Pascal Engélibert [(why copyleft?)](https://txmn.tk/blog/why-copyleft/)
CopyLeft 2022-2023 Pascal Engélibert [(why copyleft?)](https://txmn.tk/blog/why-copyleft/)
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, version 3 of the License.

View file

@ -1,34 +1,54 @@
use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion};
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
use rand::Rng;
static ITERS: u32 = 10_000;
fn compare_crates(c: &mut Criterion) {
let mut rng = rand::thread_rng();
let mut group = c.benchmark_group("Comparison");
for redundancy in [1, 5, 10, 20, 40] {
let samples: Vec<u32> = (0..ITERS)
.map(|_| rng.gen_range(0..ITERS / redundancy))
.collect();
for len in [10, 50, 100, 500, 1000] {
let samples: Vec<u32> = (0..len).map(|_| rng.gen_range(0..len / 5)).collect();
group.bench_with_input(
BenchmarkId::new("median_accumulator", redundancy),
BenchmarkId::new("median_accumulator 1:5", len),
&samples,
|b, _i| {
b.iter(|| {
let mut median = median_accumulator::vec::MedianAcc::new();
samples.iter().for_each(|s| median.push(*s));
black_box(median.get_median());
median.get_median()
})
},
);
group.bench_with_input(
BenchmarkId::new("medianheap", redundancy),
BenchmarkId::new("medianheap 1:5", len),
&samples,
|b, _i| {
b.iter(|| {
let mut median = medianheap::MedianHeap::new();
samples.iter().for_each(|s| median.push(*s));
black_box(median.median());
median.median()
})
},
);
let samples: Vec<u32> = (0..len).map(|_| rng.gen_range(0..len)).collect();
group.bench_with_input(
BenchmarkId::new("median_accumulator 1:1", len),
&samples,
|b, _i| {
b.iter(|| {
let mut median = median_accumulator::vec::MedianAcc::new();
samples.iter().for_each(|s| median.push(*s));
median.get_median()
})
},
);
group.bench_with_input(
BenchmarkId::new("medianheap 1:1", len),
&samples,
|b, _i| {
b.iter(|| {
let mut median = medianheap::MedianHeap::new();
samples.iter().for_each(|s| median.push(*s));
median.median()
})
},
);

View file

@ -14,7 +14,7 @@
//!
//! In doc comments, _N_ represents the number of samples, _D_ represents the number of different values taken by the samples.
#![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(feature = "nostd", no_std)]
mod traits;
@ -34,7 +34,7 @@ pub struct MedianAcc<
_t: core::marker::PhantomData<T>,
}
#[cfg(feature = "std")]
#[cfg(not(feature = "nostd"))]
pub mod vec {
pub type MedianAcc<T> = crate::MedianAcc<T, Vec<(T, u32)>>;
}
@ -213,7 +213,6 @@ mod tests {
use rand::Rng;
#[cfg(feature = "std")]
fn naive_median<T: Clone + Ord>(samples: &mut [T]) -> Option<MedianResult<T>> {
if samples.is_empty() {
None
@ -233,7 +232,6 @@ mod tests {
}
}
#[cfg(feature = "std")]
#[test]
fn correctness() {
let mut rng = rand::thread_rng();
@ -251,7 +249,6 @@ mod tests {
}
}
#[cfg(feature = "smallvec")]
#[test]
fn correctness_smallvec() {
let mut rng = rand::thread_rng();

View file

@ -9,7 +9,7 @@ pub trait InsertIndex: cc_traits::Collection {
) -> Self::Output;
}
#[cfg(feature = "std")]
#[cfg(not(feature = "nostd"))]
impl<T> InsertIndex for Vec<T> {
type Output = ();