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

Latest #46

Merged
merged 17 commits into from
Sep 20, 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
3 changes: 2 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ jobs:
- name: Install latest stable Rust
run: rustup install stable
- name: Check crate builds on stable
run: cargo build --features serde,numtraits,rand,arbitrary && cargo build
# NOTE: consider using https://github.com/frewsxcv/cargo-all-features, because all features != their arbitrary combinations
run: cargo build --features serde,numtraits,rand,arbitrary,borsh && cargo build
test_nightly:
runs-on: ubuntu-latest
steps:
Expand Down
8 changes: 6 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "bnum"
version = "0.11.0"
version = "0.12.0"
authors = ["isaac-holt <isaac_holt@icloud.com>"]
edition = "2021"
license = "MIT OR Apache-2.0"
Expand Down Expand Up @@ -33,6 +33,7 @@ quickcheck = { version = "1.0", optional = true, default-features = false }
# proptest = { version = "1.2", optional = true, default-features = false }
valuable = { version = "0.1", optional = true, features = ["derive"], default-features = false }
# lit-parser = { path = "./lit-parser/", optional = true }
borsh = { version = "^1.5", optional = true, default-features = false, features = ["unstable__schema"] }

[dev-dependencies]
quickcheck = "1.0"
Expand All @@ -44,4 +45,7 @@ lto = true # enable link-time optimisation for faster runtime, but slower compil
opt-level = 3 # maximum optimisation level for faster runtime, but slower compile time

[package.metadata.docs.rs]
all-features = true
all-features = true

[lints.rust]
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(test_int_bits, values("64", "128"))'] }
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,21 +24,21 @@ This crate uses Rust's const generics to allow creation of integers of arbitrary

- **Zero dependencies by default**: `bnum` does not depend on any other crates by default. Support for crates such as [`rand`](https://docs.rs/rand/latest/rand/) and [`serde`](https://docs.rs/serde/latest/serde/) can be enabled with crate [features](#features).
- **`no-std` compatible**: `bnum` can be used in `no_std` environments, provided that the [`arbitrary`](#fuzzing) and [`quickcheck`](#quickcheck) features are not enabled.
- **Compile-time integer parsing**: the `from_str_radix` and `parse_str_radix` methods on `bnum` integers are `const`, which allows parsing of integers from string slices at compile time. Note that this is more powerful than compile-time parsing of integer literals. This is because it allows parsing of strings in all radices from `2` to `36` inclusive instead of just `2`, `8`, `10` and `16`. Additionally, the string to be parsed does not have to be a literal: it could, for example, be obtained via [`include_str!`](https://doc.rust-lang.org/core/macro.include_str.html), or [`env!`](https://doc.rust-lang.org/core/macro.env.html)`.
- **Compile-time integer parsing**: the `from_str_radix` and `parse_str_radix` methods on `bnum` integers are `const`, which allows parsing of integers from string slices at compile time. Note that this is more powerful than compile-time parsing of integer literals. This is because it allows parsing of strings in all radices from `2` to `36` inclusive instead of just `2`, `8`, `10` and `16`. Additionally, the string to be parsed does not have to be a literal: it could, for example, be obtained via [`include_str!`](https://doc.rust-lang.org/core/macro.include_str.html), or [`env!`](https://doc.rust-lang.org/core/macro.env.html).
- **`const` evaluation**: nearly all methods defined on `bnum` integers are `const`, which allows complex compile-time calculations.

## Installation

To install and use `bnum`, simply add the following line to your `Cargo.toml` file in the `[dependencies]` section:

```toml
bnum = "0.11.0"
bnum = "0.12.0"
```

Or, to enable various `bnum` features as well, add for example this line instead:

```toml
bnum = { version = "0.11.0", features = ["rand"] } # enables the "rand" feature
bnum = { version = "0.12.0", features = ["rand"] } # enables the "rand" feature
```

## Example Usage
Expand Down Expand Up @@ -113,6 +113,8 @@ The `rand` feature allows creation of random `bnum` integers via the [`rand`](ht

The `serde` feature enables serialization and deserialization of `bnum` integers via the [`serde`](https://docs.rs/serde/latest/serde/) and [`serde_big_array`](https://docs.rs/serde-big-array/latest/serde_big_array/) crates.

The `borsh` feature enables serialization and deserialization of `bnum` integers via the [`borsh`](https://docs.rs/borsh/latest/borsh/) crate.

### `num_traits` and `num_integer` trait implementations

The `numtraits` feature includes implementations of traits from the [`num_traits`](https://docs.rs/num-traits/latest/num_traits/) and [`num_integer`](https://docs.rs/num-integer/latest/num_integer/) crates, e.g. [`AsPrimitive`](https://docs.rs/num-traits/latest/num_traits/cast/trait.AsPrimitive.html), [`Signed`](https://docs.rs/num-traits/latest/num_traits/sign/trait.Signed.html), [`Integer`](https://docs.rs/num-integer/latest/num_integer/trait.Integer.html) and [`Roots`](https://docs.rs/num-integer/latest/num_integer/trait.Roots.html).
Expand All @@ -131,7 +133,7 @@ The `valuable` feature enables the [`Valuable`](https://docs.rs/valuable/latest/

### Nightly features

Activating the `nightly` feature will enable the `from_be_bytes`, `from_le_bytes`, `from_ne_bytes`, `to_be_bytes`, `to_le_bytes` and `to_ne_bytes` methods on `bnum`'s unsigned and signed integers and will make the `unchecked_...` methods `const`. This comes at the cost of only being able to compile on nightly. The nightly features that this uses are [`generic_const_exprs`](https://github.com/rust-lang/rust/issues/76560), [`const_trait_impl`](https://github.com/rust-lang/rust/issues/67792) and [`const_option_ext`](https://github.com/rust-lang/rust/issues/91930).
Activating the `nightly` feature will enable the `from_be_bytes`, `from_le_bytes`, `from_ne_bytes`, `to_be_bytes`, `to_le_bytes` and `to_ne_bytes` methods on `bnum`'s unsigned and signed integers and will make the `unchecked_...` methods `const`. This comes at the cost of only being able to compile on nightly. The nightly features that this uses are [`generic_const_exprs`](https://github.com/rust-lang/rust/issues/76560), [`const_trait_impl`](https://github.com/rust-lang/rust/issues/67792), [`effects`](https://github.com/rust-lang/rust/issues/102090) and [`const_option`](https://github.com/rust-lang/rust/issues/67441).

## Testing

Expand Down
9 changes: 9 additions & 0 deletions changes/v0.12.0
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
- Add optional borsh support
- Add `digits_mut` method to unsigned integers.
- `As` and `CastFrom` traits no longer const, as const trait support removed from latest nightly.
- `cast_signed` method for unsigned integers.
- `cast_unsigned` method for signed integers.
- `midpoint` method for integers.
- `carrying_add` and `borrowing_sub` methods for signed integers.
- strict methods added.
- added `(bnum) ` prefix to error messages that were lacking it.
20 changes: 20 additions & 0 deletions src/bint/bigint_helpers.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
macro_rules! bigint_helpers {
($BUint: ident, $BInt: ident, $Digit: ident) => {
#[doc = doc::bigint_helpers::impl_desc!()]
impl<const N: usize> $BInt<N> {
crate::int::bigint_helpers::impls!(I);
}

#[cfg(test)]
paste::paste! {
mod [<$Digit _digit_tests>] {
use crate::test::types::big_types::$Digit::*;
crate::int::bigint_helpers::tests!(itest);
}
}
};
}

use crate::doc;

crate::macro_impl!(bigint_helpers);
2 changes: 1 addition & 1 deletion src/bint/const_trait_fillers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ macro_rules! const_trait_fillers {
#[inline]
pub const fn neg(self) -> Self {
#[cfg(debug_assertions)]
return crate::errors::option_expect!(self.checked_neg(), crate::errors::err_msg!("attempt to negate with overflow"));
return self.strict_neg();

#[cfg(not(debug_assertions))]
self.wrapping_neg()
Expand Down
40 changes: 35 additions & 5 deletions src/bint/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,19 @@ macro_rules! ilog {
}
}

#[cfg(debug_assertions)]
use crate::errors::option_expect;

use crate::digit;
use crate::ExpType;
use crate::{doc, errors};

#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};

#[cfg(feature = "borsh")]
use ::{
alloc::string::ToString,
borsh::{BorshDeserialize, BorshSchema, BorshSerialize},
};

use core::default::Default;

use core::iter::{Iterator, Product, Sum};
Expand All @@ -46,6 +49,7 @@ macro_rules! mod_impl {

#[derive(Clone, Copy, Hash, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize, BorshSchema))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[cfg_attr(feature = "valuable", derive(valuable::Valuable))]
#[repr(transparent)]
Expand Down Expand Up @@ -99,6 +103,13 @@ macro_rules! mod_impl {
self.bits.trailing_ones()
}

#[doc = doc::cast_unsigned!(I)]
#[must_use = doc::must_use_op!()]
#[inline]
pub const fn cast_unsigned(self) -> $BUint<N> {
self.to_bits()
}

#[doc = doc::rotate_left!(I 256, "i")]
#[must_use = doc::must_use_op!()]
#[inline]
Expand Down Expand Up @@ -143,7 +154,7 @@ macro_rules! mod_impl {
#[inline]
pub const fn pow(self, exp: ExpType) -> Self {
#[cfg(debug_assertions)]
return option_expect!(self.checked_pow(exp), errors::err_msg!("attempt to calculate power with overflow"));
return self.strict_pow(exp);

#[cfg(not(debug_assertions))]
self.wrapping_pow(exp)
Expand All @@ -170,7 +181,7 @@ macro_rules! mod_impl {
#[inline]
pub const fn abs(self) -> Self {
#[cfg(debug_assertions)]
return option_expect!(self.checked_abs(), errors::err_msg!("attempt to negate with overflow"));
return self.strict_abs();

#[cfg(not(debug_assertions))]
match self.checked_abs() {
Expand Down Expand Up @@ -223,6 +234,20 @@ macro_rules! mod_impl {
!self.is_negative() &&self.bits.is_power_of_two()
}

#[doc = doc::midpoint!(I)]
#[must_use = doc::must_use_op!()]
#[inline]
pub const fn midpoint(self, rhs: Self) -> Self {
let m = Self::from_bits(self.to_bits().midpoint(rhs.to_bits()));
if self.is_negative() == rhs.is_negative() {
// signs agree. in the positive case, we can just compute as if they were unsigned. in the negative case, we compute as if unsigned, and the result is 2^(type bits) too large, but this is 0 (modulo 2^(type bits)) so does not affect the bits
m
} else {
// result is 2^(type bits - 1) too large, so subtract 2^(type bits - 1) by applying xor
m.bitxor(Self::MIN)
}
}

ilog!(ilog, base: Self);
ilog!(ilog2);
ilog!(ilog10);
Expand Down Expand Up @@ -420,6 +445,9 @@ macro_rules! mod_impl {
test_bignum! {
function: <itest>::is_negative(a: itest)
}
test_bignum! {
function: <itest>::cast_unsigned(a: itest)
}

#[test]
fn bit() {
Expand Down Expand Up @@ -483,6 +511,7 @@ macro_rules! mod_impl {

crate::macro_impl!(mod_impl);

mod bigint_helpers;
pub mod cast;
mod checked;
mod cmp;
Expand All @@ -497,5 +526,6 @@ mod ops;
mod overflowing;
mod radix;
mod saturating;
mod strict;
mod unchecked;
mod wrapping;
63 changes: 63 additions & 0 deletions src/bint/strict.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
macro_rules! strict {
($BUint: ident, $BInt: ident, $Digit: ident) => {
#[doc = doc::strict::impl_desc!()]
impl<const N: usize> $BInt<N> {
crate::int::strict::impls!(I);

#[doc = doc::strict::strict_abs!(I)]
#[must_use = doc::must_use_op!()]
#[inline]
pub const fn strict_abs(self) -> Self {
crate::errors::option_expect!(
self.checked_abs(),
crate::errors::err_msg!("attempt to negate with overflow")
)
}

#[doc = doc::strict::strict_add_unsigned!(I)]
#[must_use = doc::must_use_op!()]
#[inline]
pub const fn strict_add_unsigned(self, rhs: $BUint<N>) -> Self {
crate::errors::option_expect!(
self.checked_add_unsigned(rhs),
crate::errors::err_msg!("attempt to add with overflow")
)
}

#[doc = doc::strict::strict_sub_unsigned!(I)]
#[must_use = doc::must_use_op!()]
#[inline]
pub const fn strict_sub_unsigned(self, rhs: $BUint<N>) -> Self {
crate::errors::option_expect!(
self.checked_sub_unsigned(rhs),
crate::errors::err_msg!("attempt to subtract with overflow")
)
}
}

#[cfg(test)]
paste::paste! {
mod [<$Digit _digit_tests>] {
use crate::test::types::big_types::$Digit::*;
crate::int::strict::tests!(itest);

test_bignum! {
function: <itest>::strict_abs(a: itest),
skip: a.checked_abs().is_none()
}
test_bignum! {
function: <itest>::strict_add_unsigned(a: itest, b: utest),
skip: a.checked_add_unsigned(b).is_none()
}
test_bignum! {
function: <itest>::strict_sub_unsigned(a: itest, b: utest),
skip: a.checked_sub_unsigned(b).is_none()
}
}
}
};
}

use crate::doc;

crate::macro_impl!(strict);
16 changes: 8 additions & 8 deletions src/buint/as_float.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::ExpType;
use crate::cast::CastFrom;

#[cfg_attr(feature = "nightly", const_trait)]
// #[cfg_attr(feature = "nightly", const_trait)]
pub trait CastToFloatHelper: Copy {
const ZERO: Self;
const BITS: ExpType;
Expand Down Expand Up @@ -93,7 +93,7 @@ pub(crate) use impl_helper_buint;

crate::macro_impl!(impl_helper_buint);

#[cfg_attr(feature = "nightly", const_trait)]
// #[cfg_attr(feature = "nightly", const_trait)]
pub trait CastToFloatConsts {
type M: Mantissa;

Expand Down Expand Up @@ -131,7 +131,7 @@ macro_rules! cast_to_float_consts {

cast_to_float_consts!(f32; u32, f64; u64);

#[cfg_attr(feature = "nightly", const_trait)]
// #[cfg_attr(feature = "nightly", const_trait)]
pub trait Mantissa {
const ONE: Self;
const TWO: Self;
Expand All @@ -144,7 +144,7 @@ pub trait Mantissa {
fn add(self, rhs: Self) -> Self;
fn sub(self, rhs: Self) -> Self;
fn leading_zeros(self) -> ExpType;
fn bitand(self, rhs: Self) -> Self;
// fn bitand(self, rhs: Self) -> Self;
fn gt(&self, rhs: &Self) -> bool;
}

Expand Down Expand Up @@ -188,10 +188,10 @@ macro_rules! impl_mantissa_for_uint {
Self::leading_zeros(self) as ExpType
}

#[inline]
fn bitand(self, rhs: Self) -> Self {
self & rhs
}
// #[inline]
// fn bitand(self, rhs: Self) -> Self {
// self & rhs
// }

#[inline]
fn gt(&self, rhs: &Self) -> bool {
Expand Down
Loading
Loading