Skip to content

Commit

Permalink
Implement core::fmt::Step for UInt (#30)
Browse files Browse the repository at this point in the history
* implement core::fmt::Step behind a feature

* more tests

* github actions workflow with this feature enabled

* make range inclusive to test boundary

* update changelog (1.2.7)

* step_backward test is wrong! (it produced empty ranges)

* add suggested tests, test steps_between
  • Loading branch information
pickx authored Jul 14, 2023
1 parent dcff722 commit 6aefcc8
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 0 deletions.
19 changes: 19 additions & 0 deletions .github/workflows/test-step.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
name: test const
run-name: ${{ github.actor }}'s patch
on: [push]
jobs:
build-and-test:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '14'
- uses: actions-rs/toolchain@v1
with:
toolchain: nightly
override: true
- uses: actions-rs/cargo@v1
with:
command: test
args: --no-default-features --features step_trait
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## arbitrary-int 1.2.7

### Added

- Support `Step` so that arbitrary-int can be used in a range expression, e.g. `for n in u3::MIN..=u3::MAX { println!("{n}") }`. Note this trait is currently unstable, and so is only usable in nightly. Enable this feature with `step_trait`.

## arbitrary-int 1.2.6

### Added
Expand Down
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,8 @@ std = []
# (2023-04-20 is broken, 2022-11-23 works. The exact day is somewhere inbetween)
const_convert_and_const_trait_impl = []

# core::fmt::Step is currently unstable and is available on nightly behind a feature gate
step_trait = []

[dependencies]
num-traits = { version = "0.2.15", default-features = false, optional = true }
34 changes: 34 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@
feature = "const_convert_and_const_trait_impl",
feature(const_convert, const_trait_impl)
)]
#![cfg_attr(feature = "step_trait", feature(step_trait))]

use core::fmt::{Binary, Debug, Display, Formatter, LowerHex, Octal, UpperHex};
use core::hash::{Hash, Hasher};
#[cfg(feature = "step_trait")]
use core::iter::Step;
#[cfg(feature = "num-traits")]
use core::num::Wrapping;
use core::ops::{
Expand Down Expand Up @@ -718,6 +721,37 @@ where
}
}

#[cfg(feature = "step_trait")]
impl<T, const BITS: usize> Step for UInt<T, BITS>
where
Self: Number<UnderlyingType = T>,
T: Copy + Step,
{
#[inline]
fn steps_between(start: &Self, end: &Self) -> Option<usize> {
Step::steps_between(&start.value(), &end.value())
}

#[inline]
fn forward_checked(start: Self, count: usize) -> Option<Self> {
if let Some(res) = Step::forward_checked(start.value(), count) {
Self::try_new(res).ok()
} else {
None
}
}

#[inline]
fn backward_checked(start: Self, count: usize) -> Option<Self> {
if let Some(res) = Step::backward_checked(start.value(), count) {
Self::try_new(res).ok()
} else {
None
}
}
}


#[cfg(feature = "num-traits")]
impl<T, const NUM_BITS: usize> num_traits::WrappingAdd for UInt<T, NUM_BITS>
where
Expand Down
70 changes: 70 additions & 0 deletions tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ extern crate core;

use arbitrary_int::*;
use std::collections::HashMap;
#[cfg(feature = "step_trait")]
use std::iter::Step;

#[test]
fn constants() {
Expand Down Expand Up @@ -1301,3 +1303,71 @@ fn rotate_right() {

assert_eq!(u24::new(0xEC0FFE), u24::new(0xC0FFEE).rotate_right(4));
}

#[cfg(feature = "step_trait")]
#[test]
fn range_agrees_with_underlying() {
compare_range(u19::MIN, u19::MAX);
compare_range(u37::new(95_993), u37::new(1_994_910));
compare_range(u68::new(58_858_348), u68::new(58_860_000));
compare_range(u122::new(111_222_333_444), u122::new(111_222_444_555));
compare_range(u5::MIN, u5::MAX);
compare_range(u23::MIN, u23::MAX);
compare_range(u48::new(999_444), u48::new(1_005_000));
compare_range(u99::new(12345), u99::new(54321));

fn compare_range<T, const BITS: usize>(arb_start: UInt<T, BITS>, arb_end: UInt<T, BITS>)
where
T: Copy + Step,
UInt<T, BITS>: Step,
{
let arbint_range = (arb_start..=arb_end).map(UInt::value);
let underlying_range = arb_start.value()..=arb_end.value();

assert!(arbint_range.eq(underlying_range));
}
}

#[cfg(feature = "step_trait")]
#[test]
fn forward_checked() {
// In range
assert_eq!(Some(u7::new(121)), Step::forward_checked(u7::new(120), 1));
assert_eq!(Some(u7::new(127)), Step::forward_checked(u7::new(120), 7));

// Out of range
assert_eq!(None, Step::forward_checked(u7::new(120), 8));

// Out of range for the underlying type
assert_eq!(None, Step::forward_checked(u7::new(120), 140));
}

#[cfg(feature = "step_trait")]
#[test]
fn backward_checked() {
// In range
assert_eq!(Some(u7::new(1)), Step::backward_checked(u7::new(10), 9));
assert_eq!(Some(u7::new(0)), Step::backward_checked(u7::new(10), 10));

// Out of range (for both the arbitrary int and and the underlying type)
assert_eq!(None, Step::backward_checked(u7::new(10), 11));
}

#[cfg(feature = "step_trait")]
#[test]
fn steps_between() {
assert_eq!(Some(0), Step::steps_between(&u50::new(50), &u50::new(50)));

assert_eq!(Some(4), Step::steps_between(&u24::new(5), &u24::new(9)));
assert_eq!(None, Step::steps_between(&u24::new(9), &u24::new(5)));

// this assumes usize is <= 64 bits. a test like this one exists in `core::iter::step`.
assert_eq!(
Some(usize::MAX),
Step::steps_between(&u125::new(0x7), &u125::new(0x1_0000_0000_0000_0006))
);
assert_eq!(
None,
Step::steps_between(&u125::new(0x7), &u125::new(0x1_0000_0000_0000_0007))
);
}

0 comments on commit 6aefcc8

Please sign in to comment.