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

v0.2.4 #13

Merged
merged 7 commits into from
Jan 31, 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
84 changes: 84 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# Changelog

## v0.2.4 (2024-01-30)

**Adds**

* Adds links to [benchmarks](https://nordzilla.github.io/count-digits/) to documentation, hosted via [GitHub Pages](https://pages.github.com/).
* Adds CHANGELOG.md (that's me) to the project.

**Changes**

* Removes unnecessary `checked_ilog` functions from the implementations of various `CountDigits` functions.
* Reworks testing macros to make the test output more granular.
* Reworks the benchmark test boundaries to test iterations for each hexadecimal digit added to an integer, from one to max, for each integer type.

## v0.2.3 (2024-01-26)

**Changes**

* Updates documentation.

## v0.2.2 (2024-01-25)

**Adds**

* Adds benchmarks using [Criterion](https://docs.rs/criterion/latest/criterion/).
* Adds unit tests for helper functions used throughout the test suite.

**Changes**

* Updates documentation.

## v0.2.1 (2024-01-24)

**Adds**

* Adds [CodeCov](https://about.codecov.io/) jobs to CI.
* Adds radix boundary tests for all integer types.

**Changes**

* Updates documentation.
* Refactors iteration tests.

**Fixes**

* Fixes an issue where the unsigned, non-zero integer iteration tests were calling the `CountDigits` functions for their corresponding primitive types, resulting in a gap of test coverage for these types.

## v0.2.0 (2024-01-24)

**Adds**

* Adds `CountDigits::count_bits()`.
* Adds `CountDigits::count_octal_digits()`.
* Adds `CountDigits::count_hex_digits()`.
* Adds `CountDigits::count_digits_radix()`.
* Implements the above functions for signed, primitive integer types.
* Implements the above functions for signed, non-zero integer types.
* Implements the above functions for unsigned, primitive integer types.
* Implements the above functions for unsigned, non-zero integer types.
* Bumps the minimum supported Rust version `(1.67.1 -> 1.71.1)` due to using `is_negative()` for non-zero integer types.

**Changes**

* Refactors min_and_max tests.
* Refactors iteration tests.

## v0.1.0 (2024-01-22)
> Initial Release

**Adds**

* Adds README.md.
* Adds Cargo.toml.
* Adds `CountDigits` trait.
* Adds `CountDigits::count_digits()` function.
* Implements `CountDigits` for signed, primitive integer types.
* Implements `CountDigits` for signed, non-zero integer types.
* Implements `CountDigits` for unsigned, primitive integer types.
* Implements `CountDigits` for unsigned, non-zero integer types.
* Adds tests for min and max values.
* Adds iterative tests for unsigned types.
* Adds iterative tests for signed types.
* Adds rust.yml to run tests in CI via GitHub Actions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "count-digits"
version = "0.2.3"
version = "0.2.4"
authors = ["Erik Nordin <[email protected]>"]
description = "A no-std trait to count the digits of integer types in various number bases."
homepage = "https://github.com/nordzilla/count-digits"
Expand Down
21 changes: 15 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,11 @@ assert_eq!(04, 0xF00D.count_digits_radix(16_u32));

---

> [!NOTE]
> The [count_digits()](https://docs.rs/count-digits/latest/count_digits/trait.CountDigits.html#tymethod.count_digits)
> [!NOTE]
> The base-10 functions
> [count_digits()](https://docs.rs/count-digits/latest/count_digits/trait.CountDigits.html#tymethod.count_digits)
> and [count_digits_radix(10)](https://docs.rs/count-digits/latest/count_digits/trait.CountDigits.html#tymethod.count_digits_radix)
> functions do not include the negative sign in their counts.
> do not include the negative sign in their counts.

```rust
assert_eq!(5, 12345_i32.wrapping_neg().count_digits());
Expand All @@ -39,13 +40,13 @@ assert_eq!(5, 12345_i32.wrapping_neg().count_digits_radix(10));

---

> [!NOTE]
> [!NOTE]
> Negative numbers counted in base-10 are counted differently than
> negative numbers counted in other number bases.

Since negative numbers represented in base-10 are displayed with a negative sign,
the base-10 digit count of a positive number will be equal to the base-10 digit count
of the number's negated value.
of the number's negated value, assuming no wrapping occurred.

```rust
assert_eq!(
Expand Down Expand Up @@ -98,6 +99,14 @@ assert_eq!(8, format!("{:b}", -1_i8).chars().count());
assert_eq!(3, format!("{:o}", -1_i8).chars().count());
assert_eq!(1, format!("{ }", -1_i8).strip_prefix('-').unwrap().chars().count());
assert_eq!(2, format!("{:x}", -1_i8).chars().count());
````
```

#### Benchmarks

* [table](https://nordzilla.github.io/count-digits)
* [count_bits()](https://nordzilla.github.io/count-digits/count_bits/report/index.html)
* [count_octal_digits()](https://nordzilla.github.io/count-digits/count_octal_digits/report/index.html)
* [count_digits()](https://nordzilla.github.io/count-digits/count_digits/report/index.html)
* [count_hex_digits()](https://nordzilla.github.io/count-digits/count_hex_digits/report/index.html)

License: MIT
116 changes: 82 additions & 34 deletions benches/benchmark.rs
Original file line number Diff line number Diff line change
@@ -1,68 +1,116 @@
use core::num::{NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8};
use core::num::{NonZeroU128, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8};
use count_digits::CountDigits;
use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion};

macro_rules! bench_function {
trait MaybeFromU128: Sized {
fn maybe_from_u128(u128: u128) -> Option<Self>;
}

macro_rules! impl_maybe_from_u128 {
($type:ty, $non_zero_type:ty) => {
impl MaybeFromU128 for $type {
fn maybe_from_u128(value: u128) -> Option<Self> {
if value <= (Self::MAX as u128) {
return Some(value as $type);
}
None
}
}
impl MaybeFromU128 for $non_zero_type {
fn maybe_from_u128(value: u128) -> Option<Self> {
if value <= (Self::MAX.get() as u128) {
return Self::new(value as $type);
}
None
}
}
};
}

impl_maybe_from_u128!(i8, NonZeroI8);
impl_maybe_from_u128!(i16, NonZeroI16);
impl_maybe_from_u128!(i32, NonZeroI32);
impl_maybe_from_u128!(i64, NonZeroI64);
impl_maybe_from_u128!(i128, NonZeroI128);
impl_maybe_from_u128!(u8, NonZeroU8);
impl_maybe_from_u128!(u16, NonZeroU16);
impl_maybe_from_u128!(u32, NonZeroU32);
impl_maybe_from_u128!(u64, NonZeroU64);
impl_maybe_from_u128!(u128, NonZeroU128);

macro_rules! radix_boundaries {
($type:ty, $radix:expr) => {
std::iter::successors(Some($radix as $type), move |n| n.checked_mul($radix))
.map(|n| n - 1)
};
}

macro_rules! comparison_bench_function {
($group:expr, $type:ty, $fn:ident, $input:expr) => {
$group.bench_with_input(
BenchmarkId::new(stringify!($type), $input),
$input,
|b, n| b.iter(|| CountDigits::$fn(black_box(*n as $type))),
);
if let Some(n) = <$type>::maybe_from_u128(*$input) {
$group.bench_with_input(
BenchmarkId::new(stringify!($type), $input),
$input,
|b, _| b.iter(|| CountDigits::$fn(black_box(n))),
);
}
};
}

macro_rules! bench_group {
($criterion:expr, $group_name:ident, $fn:ident, $input:expr, $($type:ty),+ $(,)?) => {{
paste::paste! {
let mut group = $criterion.benchmark_group(stringify!([<$group_name _ $fn>]));
for input in $input.iter() {
$(bench_function!(group, $type, $fn, input);)+
}
group.finish();
macro_rules! comparison_bench_group {
($criterion:expr, $fn:ident, $input:expr, $($type:ty),+ $(,)?) => {{
let mut group = $criterion.benchmark_group(stringify!($fn));
for input in &$input {
$(comparison_bench_function!(group, $type, $fn, input);)+
}
group.finish();
}};
}

macro_rules! create_bench {
macro_rules! create_comparison_bench {
($fn:ident) => {
fn $fn(c: &mut Criterion) {
bench_group!(
c,
signed,
fn $fn(criterion: &mut Criterion) {
comparison_bench_group!(
criterion,
$fn,
[i8::MIN, -64, 0, 64, i8::MAX],
radix_boundaries!(u128, 16).collect::<Vec<_>>(),
i8,
i16,
i32,
i64,
i128
);
bench_group!(
c,
unsigned,
$fn,
[u8::MIN, 64, 128, 192, u8::MAX],
i128,
u8,
u16,
u32,
u64,
u128
u128,
NonZeroI8,
NonZeroI16,
NonZeroI32,
NonZeroI64,
NonZeroI128,
NonZeroU8,
NonZeroU16,
NonZeroU32,
NonZeroU64,
NonZeroU128,
);
}
};
}

create_bench!(count_bits);
create_bench!(count_octal_digits);
create_bench!(count_digits);
create_bench!(count_hex_digits);
create_comparison_bench!(count_bits);
create_comparison_bench!(count_octal_digits);
create_comparison_bench!(count_digits);
create_comparison_bench!(count_hex_digits);

criterion_group!(
benches,
benchmarks,
count_bits,
count_octal_digits,
count_digits,
count_hex_digits
);

criterion_main!(benches);
criterion_main!(benchmarks);
Loading
Loading