From 364316ba4fe56ad6ef3c2a7db5ee4f0ad5ca3d1a Mon Sep 17 00:00:00 2001 From: dzmitry-lahoda Date: Mon, 24 Jun 2024 23:55:23 +0100 Subject: [PATCH 01/14] borsh --- Cargo.toml | 3 +++ src/buint/mod.rs | 7 +++++++ 2 files changed, 10 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 02cc1bf..adea055 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,7 @@ default = [] nightly = [] serde = ["dep:serde", "serde-big-array"] numtraits = ["num-integer", "num-traits"] +borsh = ["dep:borsh"] [dependencies] num-integer = { version = "0.1", optional = true, default-features = false } @@ -34,6 +35,8 @@ quickcheck = { version = "1.0", 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 = ["rc", "std", "unstable__schema"]} + [dev-dependencies] quickcheck = "1.0" paste = "1.0" diff --git a/src/buint/mod.rs b/src/buint/mod.rs index 3d00417..7981cc1 100644 --- a/src/buint/mod.rs +++ b/src/buint/mod.rs @@ -11,6 +11,12 @@ use ::{ serde_big_array::BigArray, }; +#[cfg(feature = "borsh")] +use ::{ + alloc::string::ToString, + borsh::{BorshDeserialize, BorshSchema, BorshSerialize}, +}; + use core::default::Default; use core::iter::{Iterator, Product, Sum}; @@ -29,6 +35,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)] From 6f1237969ff2b3f3446db9dfa83eeca3d17f6a12 Mon Sep 17 00:00:00 2001 From: dzmitry-lahoda Date: Tue, 25 Jun 2024 00:00:09 +0100 Subject: [PATCH 02/14] int support too --- .github/workflows/test.yml | 3 ++- Cargo.toml | 1 - src/bint/mod.rs | 7 +++++++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 953e7e8..0f1a61f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -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: diff --git a/Cargo.toml b/Cargo.toml index adea055..2574724 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,7 +34,6 @@ 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 = ["rc", "std", "unstable__schema"]} [dev-dependencies] diff --git a/src/bint/mod.rs b/src/bint/mod.rs index e681875..f8a62e9 100644 --- a/src/bint/mod.rs +++ b/src/bint/mod.rs @@ -28,6 +28,12 @@ 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}; @@ -46,6 +52,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)] From 8c06221b8859f0586c60c24e09b12787eb731e18 Mon Sep 17 00:00:00 2001 From: Isaac Holt Date: Tue, 2 Jul 2024 18:15:40 -0700 Subject: [PATCH 03/14] add effects feature --- README.md | 2 +- src/int/ops.rs | 28 ---------------------------- src/lib.rs | 4 ++-- 3 files changed, 3 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index 8c6c6db..b4494f0 100644 --- a/README.md +++ b/README.md @@ -131,7 +131,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_ext`](https://github.com/rust-lang/rust/issues/91930). ## Testing diff --git a/src/int/ops.rs b/src/int/ops.rs index 799ff8c..7e3856a 100644 --- a/src/int/ops.rs +++ b/src/int/ops.rs @@ -200,37 +200,10 @@ macro_rules! all_shift_impls { i128 ); - #[cfg(feature = "usize_exptype")] - crate::int::ops::try_shift_impl!( - $Struct, $BUint, $BInt; - Shl, - shl, - ShlAssign, - shl_assign, - "attempt to shift left with overflow", - u32, - u64, - u128 - ); - - #[cfg(feature = "usize_exptype")] - crate::int::ops::try_shift_impl!( - $Struct, $BUint, $BInt; - Shr, - shr, - ShrAssign, - shr_assign, - "attempt to shift right with overflow", - u32, - u64, - u128 - ); - crate::int::ops::shift_impl!($Struct, Shl, shl, ShlAssign, shl_assign, u8, u16); crate::int::ops::shift_impl!($Struct, Shr, shr, ShrAssign, shr_assign, u8, u16); - #[cfg(not(feature = "usize_exptype"))] crate::int::ops::try_shift_impl!( $Struct, $BUint, $BInt; Shl, @@ -243,7 +216,6 @@ macro_rules! all_shift_impls { u128 ); - #[cfg(not(feature = "usize_exptype"))] crate::int::ops::try_shift_impl!( $Struct, $BUint, $BInt; Shr, diff --git a/src/lib.rs b/src/lib.rs index 3e48a9b..ece7ea5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,7 +4,8 @@ feature( generic_const_exprs, const_trait_impl, - const_option_ext + const_option_ext, + effects, ) )] #![cfg_attr( @@ -15,7 +16,6 @@ float_minimum_maximum, wrapping_next_power_of_two, float_next_up_down, - unchecked_math, unchecked_shifts, ) )] From 0c33e0d7df4bf905be677b868fa63536efd45d56 Mon Sep 17 00:00:00 2001 From: Quaternions Date: Thu, 29 Aug 2024 19:45:38 -0700 Subject: [PATCH 04/14] add set_bit to buint --- src/buint/mod.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/buint/mod.rs b/src/buint/mod.rs index 3d00417..c45289f 100644 --- a/src/buint/mod.rs +++ b/src/buint/mod.rs @@ -455,6 +455,15 @@ macro_rules! mod_impl { digit & (1 << (index & digit::$Digit::BITS_MINUS_1)) != 0 } + #[doc = doc::bit!(U 256)] + #[must_use] + #[inline] + pub fn set_bit(&mut self, index: ExpType, value: bool) { + let digit = &mut self.digits[index as usize >> digit::$Digit::BIT_SHIFT]; + let shift = index & digit::$Digit::BITS_MINUS_1; + *digit = *digit & (1 << shift) | ($Digit::from(value) << shift) + } + /// Returns an integer whose value is `2^power`. This is faster than using a shift left on `Self::ONE`. /// /// # Panics From 44b05f6314382550db4020a857e7b7f523a740fb Mon Sep 17 00:00:00 2001 From: Quaternions Date: Thu, 29 Aug 2024 19:47:11 -0700 Subject: [PATCH 05/14] add digits_mut to buint --- src/buint/mod.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/buint/mod.rs b/src/buint/mod.rs index 3d00417..cdfdecf 100644 --- a/src/buint/mod.rs +++ b/src/buint/mod.rs @@ -484,6 +484,13 @@ macro_rules! mod_impl { &self.digits } + /// Returns the digits stored in `self` as a mutable array. Digits are little endian (least significant digit first). + #[must_use] + #[inline(always)] + pub fn digits_mut(&mut self) -> &mut [$Digit; N] { + &mut self.digits + } + /// Creates a new unsigned integer from the given array of digits. Digits are stored as little endian (least significant digit first). #[must_use] #[inline(always)] From 054250f8d0f74b9339305a83fdb910be760d46b3 Mon Sep 17 00:00:00 2001 From: Quaternions Date: Wed, 4 Sep 2024 18:16:57 -0700 Subject: [PATCH 06/14] write doc for set_bit --- src/buint/mod.rs | 2 +- src/doc/mod.rs | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/buint/mod.rs b/src/buint/mod.rs index c45289f..7a6c7f1 100644 --- a/src/buint/mod.rs +++ b/src/buint/mod.rs @@ -455,7 +455,7 @@ macro_rules! mod_impl { digit & (1 << (index & digit::$Digit::BITS_MINUS_1)) != 0 } - #[doc = doc::bit!(U 256)] + #[doc = doc::set_bit!(U 256)] #[must_use] #[inline] pub fn set_bit(&mut self, index: ExpType, value: bool) { diff --git a/src/doc/mod.rs b/src/doc/mod.rs index 333869d..ba1b356 100644 --- a/src/doc/mod.rs +++ b/src/doc/mod.rs @@ -345,6 +345,22 @@ macro_rules! bit { pub(crate) use bit; +macro_rules! set_bit { + ($sign: ident $bits: literal) => { + doc::doc_comment! { + $sign $bits, + "Sets/unsets the bit in the given position (`1` if value is true). The least significant bit is at index `0`, the most significant bit is at index `Self::BITS - 1`", + + "let mut n = " doc::type_str!($sign $bits) "::from(0b001010100101010101u32);\n" + "assert!(n.bit(2));\n" + "n.set_bit(2,false);\n" + "assert!(!n.bit(2));\n" + } + }; +} + +pub(crate) use set_bit; + macro_rules! is_zero { ($sign: ident $bits: literal) => { doc::doc_comment! { From 9bb4d9f0d4f53d76c8ec7f53f4cc9f0f4d01e403 Mon Sep 17 00:00:00 2001 From: Isaac Holt Date: Wed, 18 Sep 2024 18:47:44 +0100 Subject: [PATCH 07/14] fix compilation errors, remove cfg warnings --- Cargo.toml | 5 ++++- changes/v0.12.0 | 3 +++ src/buint/as_float.rs | 6 +++--- src/cast.rs | 18 +++++++++++------- src/int/numtraits.rs | 3 --- src/lib.rs | 2 +- src/nightly.rs | 2 +- 7 files changed, 23 insertions(+), 16 deletions(-) create mode 100644 changes/v0.12.0 diff --git a/Cargo.toml b/Cargo.toml index 2574724..20655ad 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,4 +46,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 \ No newline at end of file +all-features = true + +[lints.rust] +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(test_int_bits, values("64", "128"))'] } \ No newline at end of file diff --git a/changes/v0.12.0 b/changes/v0.12.0 new file mode 100644 index 0000000..841b767 --- /dev/null +++ b/changes/v0.12.0 @@ -0,0 +1,3 @@ +- 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. \ No newline at end of file diff --git a/src/buint/as_float.rs b/src/buint/as_float.rs index 389886a..d81a4da 100644 --- a/src/buint/as_float.rs +++ b/src/buint/as_float.rs @@ -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; @@ -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; @@ -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; diff --git a/src/cast.rs b/src/cast.rs index c64b5ad..f4ace6c 100644 --- a/src/cast.rs +++ b/src/cast.rs @@ -2,12 +2,12 @@ /// Backend implementation trait for panic-free casting between numeric types. -#[cfg_attr(feature = "nightly", const_trait)] +// #[cfg_attr(feature = "nightly", const_trait)] pub trait CastFrom { fn cast_from(from: T) -> Self; } -#[cfg_attr(feature = "nightly", const_trait)] +// #[cfg_attr(feature = "nightly", const_trait)] pub(crate) trait CastTo { fn cast_to(self) -> U; } @@ -54,9 +54,11 @@ assert_eq!(b, f.as_()); #[cfg(feature = "nightly")] macro_rules! as_trait { () => { - impl const CastTo for T + // impl const CastTo for T + impl CastTo for T where - U: ~const CastFrom, + // U: ~const CastFrom, + U: CastFrom, { fn cast_to(self) -> U { U::cast_from(self) @@ -64,7 +66,7 @@ macro_rules! as_trait { } #[doc = as_trait_doc!()] - #[const_trait] + // #[const_trait] pub trait As { #[doc = as_method_doc!()] fn as_(self) -> T @@ -73,11 +75,13 @@ macro_rules! as_trait { Self: Sized; } - impl const As for U { + // impl const As for U { + impl As for U { #[inline] fn as_(self) -> T where - T: ~const CastFrom, + // T: ~const CastFrom, + T: CastFrom, Self: Sized, { T::cast_from(self) diff --git a/src/int/numtraits.rs b/src/int/numtraits.rs index 5ae28b3..0c2e78a 100644 --- a/src/int/numtraits.rs +++ b/src/int/numtraits.rs @@ -299,19 +299,16 @@ macro_rules! prim_int_methods { fn pow(self, exp: u32) -> Self; } - #[cfg(has_leading_trailing_ones)] #[inline] fn leading_ones(self) -> u32 { Self::leading_ones(self) } - #[cfg(has_leading_trailing_ones)] #[inline] fn trailing_ones(self) -> u32 { Self::trailing_ones(self) } - #[cfg(has_reverse_bits)] #[inline] fn reverse_bits(self) -> Self { Self::reverse_bits(self) diff --git a/src/lib.rs b/src/lib.rs index ece7ea5..80dc623 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,7 +5,7 @@ generic_const_exprs, const_trait_impl, const_option_ext, - effects, + // effects, ) )] #![cfg_attr( diff --git a/src/nightly.rs b/src/nightly.rs index 13b6673..da2d4fa 100644 --- a/src/nightly.rs +++ b/src/nightly.rs @@ -51,7 +51,7 @@ pub(crate) use impl_const; #[cfg(feature = "nightly")] macro_rules! const_impl { { impl $(<$(const $C: ident : $ty: ty), +>)? const $($tt: tt) + } => { - impl $(<$(const $C: $ty), +>)? const $($tt) + + impl $(<$(const $C: $ty), +>)? $($tt) + } } From 7c8fc002871f222276d833f10c53fa0318569053 Mon Sep 17 00:00:00 2001 From: Isaac Holt Date: Wed, 18 Sep 2024 20:33:26 +0100 Subject: [PATCH 08/14] fix set_bit, edit borsh features --- Cargo.toml | 5 ++--- README.md | 8 +++++--- src/buint/mod.rs | 7 +++++-- src/doc/mod.rs | 7 ++++--- src/lib.rs | 9 ++++++++- 5 files changed, 24 insertions(+), 12 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 20655ad..5d5987d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bnum" -version = "0.11.0" +version = "0.12.0" authors = ["isaac-holt "] edition = "2021" license = "MIT OR Apache-2.0" @@ -20,7 +20,6 @@ default = [] nightly = [] serde = ["dep:serde", "serde-big-array"] numtraits = ["num-integer", "num-traits"] -borsh = ["dep:borsh"] [dependencies] num-integer = { version = "0.1", optional = true, default-features = false } @@ -34,7 +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 = ["rc", "std", "unstable__schema"]} +borsh = { version = "^1.5", optional = true, default-features = false, features = ["unstable__schema"] } [dev-dependencies] quickcheck = "1.0" diff --git a/README.md b/README.md index b4494f0..599e4b8 100644 --- a/README.md +++ b/README.md @@ -32,13 +32,13 @@ This crate uses Rust's const generics to allow creation of integers of arbitrary 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 @@ -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` + ### `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). @@ -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), [`effects`](https://github.com/rust-lang/rust/issues/102090) 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 diff --git a/src/buint/mod.rs b/src/buint/mod.rs index 5b8714a..7f2b51f 100644 --- a/src/buint/mod.rs +++ b/src/buint/mod.rs @@ -463,12 +463,15 @@ macro_rules! mod_impl { } #[doc = doc::set_bit!(U 256)] - #[must_use] #[inline] pub fn set_bit(&mut self, index: ExpType, value: bool) { let digit = &mut self.digits[index as usize >> digit::$Digit::BIT_SHIFT]; let shift = index & digit::$Digit::BITS_MINUS_1; - *digit = *digit & (1 << shift) | ($Digit::from(value) << shift) + if value { + *digit |= (1 << shift); + } else { + *digit &= !(1 << shift); + } } /// Returns an integer whose value is `2^power`. This is faster than using a shift left on `Self::ONE`. diff --git a/src/doc/mod.rs b/src/doc/mod.rs index ba1b356..ccb51d2 100644 --- a/src/doc/mod.rs +++ b/src/doc/mod.rs @@ -333,7 +333,7 @@ macro_rules! bit { ($sign: ident $bits: literal) => { doc::doc_comment! { $sign $bits, - "Returns a boolean representing the bit in the given position (`true` if the bit is set). The least significant bit is at index `0`, the most significant bit is at index `Self::BITS - 1`", + "Returns a boolean representing the bit in the given position (`true` if the bit is set). The least significant bit is at index `0`, the most significant bit is at index `Self::BITS - 1`.", "let n = " doc::type_str!($sign $bits) "::from(0b001010100101010101u32);\n" "assert!(n.bit(0));\n" @@ -349,12 +349,13 @@ macro_rules! set_bit { ($sign: ident $bits: literal) => { doc::doc_comment! { $sign $bits, - "Sets/unsets the bit in the given position (`1` if value is true). The least significant bit is at index `0`, the most significant bit is at index `Self::BITS - 1`", + "Sets/unsets the bit in the given position (`1` if value is true). The least significant bit is at index `0`, the most significant bit is at index `Self::BITS - 1`.", "let mut n = " doc::type_str!($sign $bits) "::from(0b001010100101010101u32);\n" "assert!(n.bit(2));\n" - "n.set_bit(2,false);\n" + "n.set_bit(2, false);\n" "assert!(!n.bit(2));\n" + "assert_eq!(n, " doc::type_str!($sign $bits) "::from(0b001010100101010001u32));" } }; } diff --git a/src/lib.rs b/src/lib.rs index 80dc623..4bf924e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,7 +4,7 @@ feature( generic_const_exprs, const_trait_impl, - const_option_ext, + const_option, // effects, ) )] @@ -42,6 +42,13 @@ pub mod random; pub mod types; +#[test] +fn test_set_bit() { + let mut a = types::U128::MAX; + a.set_bit(6, false); + panic!("{:b}", a); +} + // #[cfg(feature = "nightly")] // mod float; From b881f2e531be412f80d80d6168d680f07a322443 Mon Sep 17 00:00:00 2001 From: Isaac Holt Date: Wed, 18 Sep 2024 20:35:42 +0100 Subject: [PATCH 09/14] remove temporary test --- src/lib.rs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 4bf924e..a735083 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -42,13 +42,6 @@ pub mod random; pub mod types; -#[test] -fn test_set_bit() { - let mut a = types::U128::MAX; - a.set_bit(6, false); - panic!("{:b}", a); -} - // #[cfg(feature = "nightly")] // mod float; From e202f0827114cd9eb06256d04cff42ce572c51c3 Mon Sep 17 00:00:00 2001 From: Isaac Holt Date: Wed, 18 Sep 2024 22:25:04 +0100 Subject: [PATCH 10/14] hopefully pass codecov --- src/int/numtraits.rs | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/src/int/numtraits.rs b/src/int/numtraits.rs index 0c2e78a..e6d885e 100644 --- a/src/int/numtraits.rs +++ b/src/int/numtraits.rs @@ -297,21 +297,9 @@ macro_rules! prim_int_methods { fn to_be(self) -> Self; fn to_le(self) -> Self; fn pow(self, exp: u32) -> Self; - } - - #[inline] - fn leading_ones(self) -> u32 { - Self::leading_ones(self) - } - - #[inline] - fn trailing_ones(self) -> u32 { - Self::trailing_ones(self) - } - - #[inline] - fn reverse_bits(self) -> Self { - Self::reverse_bits(self) + fn leading_ones(self) -> u32; + fn trailing_ones(self) -> u32; + fn reverse_bits(self) -> Self; } }; } From 52dcea2aaa43b3c9f7d763eda6c655592ec4650a Mon Sep 17 00:00:00 2001 From: Isaac Holt Date: Thu, 19 Sep 2024 18:33:56 +0100 Subject: [PATCH 11/14] remove warnings --- README.md | 2 +- src/buint/as_float.rs | 10 +++++----- src/buint/endian.rs | 4 ++-- src/cast.rs | 3 +++ 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 599e4b8..60fc410 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ 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 diff --git a/src/buint/as_float.rs b/src/buint/as_float.rs index d81a4da..3261564 100644 --- a/src/buint/as_float.rs +++ b/src/buint/as_float.rs @@ -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; } @@ -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 { diff --git a/src/buint/endian.rs b/src/buint/endian.rs index e484b7f..532f847 100644 --- a/src/buint/endian.rs +++ b/src/buint/endian.rs @@ -233,7 +233,7 @@ macro_rules! endian { #[inline] pub const fn from_be_bytes(bytes: [u8; N * digit::$Digit::BYTES as usize]) -> Self { let mut out = Self::ZERO; - let arr_ptr = bytes.as_ptr(); + // let arr_ptr = bytes.as_ptr(); let mut i = 0; while i < N { let mut digit_bytes = [0u8; digit::$Digit::BYTES as usize]; @@ -256,7 +256,7 @@ macro_rules! endian { #[inline] pub const fn from_le_bytes(bytes: [u8; N * digit::$Digit::BYTES as usize]) -> Self { let mut out = Self::ZERO; - let arr_ptr = bytes.as_ptr(); + // let arr_ptr = bytes.as_ptr(); let mut i = 0; while i < N { let mut digit_bytes = [0u8; digit::$Digit::BYTES as usize]; diff --git a/src/cast.rs b/src/cast.rs index f4ace6c..74b9a46 100644 --- a/src/cast.rs +++ b/src/cast.rs @@ -8,6 +8,7 @@ pub trait CastFrom { } // #[cfg_attr(feature = "nightly", const_trait)] +#[cfg(test)] pub(crate) trait CastTo { fn cast_to(self) -> U; } @@ -55,6 +56,7 @@ assert_eq!(b, f.as_()); macro_rules! as_trait { () => { // impl const CastTo for T + #[cfg(test)] impl CastTo for T where // U: ~const CastFrom, @@ -93,6 +95,7 @@ macro_rules! as_trait { #[cfg(not(feature = "nightly"))] macro_rules! as_trait { () => { + #[cfg(test)] impl CastTo for T where U: CastFrom, From 6d7c7a7eae6605f34ba2eb0633132ca9a91554d2 Mon Sep 17 00:00:00 2001 From: Isaac Holt Date: Fri, 20 Sep 2024 14:17:39 +0100 Subject: [PATCH 12/14] add strict methods, midpoint methods --- changes/v0.12.0 | 8 +- src/bint/bigint_helpers.rs | 20 ++++ src/bint/const_trait_fillers.rs | 2 +- src/bint/mod.rs | 34 ++++++- src/bint/strict.rs | 63 +++++++++++++ src/buint/bigint_helpers.rs | 45 +-------- src/buint/mod.rs | 27 ++++-- src/buint/strict.rs | 36 ++++++++ src/buint/unchecked.rs | 10 +- src/doc/bigint_helpers.rs | 8 ++ src/doc/mod.rs | 7 +- src/doc/strict.rs | 25 +++++ src/doc/unchecked.rs | 2 +- src/doc/wrapping.rs | 2 +- src/int/bigint_helpers.rs | 56 ++++++++++++ src/int/mod.rs | 5 + src/int/ops.rs | 25 +---- src/int/strict.rs | 157 ++++++++++++++++++++++++++++++++ src/lib.rs | 17 ++-- 19 files changed, 464 insertions(+), 85 deletions(-) create mode 100644 src/bint/bigint_helpers.rs create mode 100644 src/bint/strict.rs create mode 100644 src/buint/strict.rs create mode 100644 src/doc/strict.rs create mode 100644 src/int/bigint_helpers.rs create mode 100644 src/int/strict.rs diff --git a/changes/v0.12.0 b/changes/v0.12.0 index 841b767..0f046aa 100644 --- a/changes/v0.12.0 +++ b/changes/v0.12.0 @@ -1,3 +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. \ No newline at end of file +- `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 unsigned integers. +- `carrying_add` and `borrowing_sub` methods for signed integers. +- strict methods added. +- added `(bnum) ` prefix to error messages that were lacking it. \ No newline at end of file diff --git a/src/bint/bigint_helpers.rs b/src/bint/bigint_helpers.rs new file mode 100644 index 0000000..c535ce7 --- /dev/null +++ b/src/bint/bigint_helpers.rs @@ -0,0 +1,20 @@ +macro_rules! bigint_helpers { + ($BUint: ident, $BInt: ident, $Digit: ident) => { + #[doc = doc::bigint_helpers::impl_desc!()] + impl $BInt { + 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); diff --git a/src/bint/const_trait_fillers.rs b/src/bint/const_trait_fillers.rs index 1384a50..8654669 100644 --- a/src/bint/const_trait_fillers.rs +++ b/src/bint/const_trait_fillers.rs @@ -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() diff --git a/src/bint/mod.rs b/src/bint/mod.rs index f8a62e9..46b31ec 100644 --- a/src/bint/mod.rs +++ b/src/bint/mod.rs @@ -18,8 +18,8 @@ macro_rules! ilog { } } -#[cfg(debug_assertions)] -use crate::errors::option_expect; +// #[cfg(debug_assertions)] +// use crate::errors::option_expect; use crate::digit; use crate::ExpType; @@ -106,6 +106,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 { + self.to_bits() + } + #[doc = doc::rotate_left!(I 256, "i")] #[must_use = doc::must_use_op!()] #[inline] @@ -150,7 +157,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) @@ -177,7 +184,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() { @@ -230,6 +237,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); @@ -427,6 +448,9 @@ macro_rules! mod_impl { test_bignum! { function: ::is_negative(a: itest) } + test_bignum! { + function: ::cast_unsigned(a: itest) + } #[test] fn bit() { @@ -490,6 +514,7 @@ macro_rules! mod_impl { crate::macro_impl!(mod_impl); +mod bigint_helpers; pub mod cast; mod checked; mod cmp; @@ -504,5 +529,6 @@ mod ops; mod overflowing; mod radix; mod saturating; +mod strict; mod unchecked; mod wrapping; diff --git a/src/bint/strict.rs b/src/bint/strict.rs new file mode 100644 index 0000000..02420ea --- /dev/null +++ b/src/bint/strict.rs @@ -0,0 +1,63 @@ +macro_rules! strict { + ($BUint: ident, $BInt: ident, $Digit: ident) => { + #[doc = doc::strict::impl_desc!()] + impl $BInt { + 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) -> 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) -> 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: ::strict_abs(a: itest), + skip: a.checked_abs().is_none() + } + test_bignum! { + function: ::strict_add_unsigned(a: itest, b: utest), + skip: a.checked_add_unsigned(b).is_none() + } + test_bignum! { + function: ::strict_sub_unsigned(a: itest, b: utest), + skip: a.checked_sub_unsigned(b).is_none() + } + } + } + }; +} + +use crate::doc; + +crate::macro_impl!(strict); diff --git a/src/buint/bigint_helpers.rs b/src/buint/bigint_helpers.rs index 14fc9b8..54bf1bc 100644 --- a/src/buint/bigint_helpers.rs +++ b/src/buint/bigint_helpers.rs @@ -3,32 +3,9 @@ use crate::doc; macro_rules! bigint_helpers { ($BUint: ident, $BInt: ident, $Digit: ident) => { + #[doc = doc::bigint_helpers::impl_desc!()] impl $BUint { - #[doc = doc::bigint_helpers::carrying_add!(U)] - #[must_use = doc::must_use_op!()] - #[inline] - pub const fn carrying_add(self, rhs: Self, carry: bool) -> (Self, bool) { - let (s1, o1) = self.overflowing_add(rhs); - if carry { - let (s2, o2) = s1.overflowing_add(Self::ONE); - (s2, o1 || o2) - } else { - (s1, o1) - } - } - - #[doc = doc::bigint_helpers::borrowing_sub!(U)] - #[must_use = doc::must_use_op!()] - #[inline] - pub const fn borrowing_sub(self, rhs: Self, borrow: bool) -> (Self, bool) { - let (s1, o1) = self.overflowing_sub(rhs); - if borrow { - let (s2, o2) = s1.overflowing_sub(Self::ONE); - (s2, o1 || o2) - } else { - (s1, o1) - } - } + crate::int::bigint_helpers::impls!(U); #[doc = doc::bigint_helpers::widening_mul!(U)] #[must_use = doc::must_use_op!()] @@ -84,26 +61,12 @@ macro_rules! bigint_helpers { #[cfg(test)] paste::paste! { mod [<$Digit _digit_tests>] { - use crate::test::{test_bignum, types::*}; + // use crate::test::{test_bignum, types::*}; use crate::test::types::big_types::$Digit::*; type U64 = crate::$BUint::<{64 / $Digit::BITS as usize}>; - test_bignum! { - function: ::carrying_add(a: utest, rhs: utest, carry: bool), - cases: [ - (utest::MAX, 1u8, true), - (utest::MAX, 1u8, false) - ] - } - - test_bignum! { - function: ::borrowing_sub(a: utest, rhs: utest, carry: bool), - cases: [ - (0u8, 1u8, false), - (0u8, 1u8, true) - ] - } + crate::int::bigint_helpers::tests!(utest); test_bignum! { function: ::widening_mul(a: u64, b: u64), diff --git a/src/buint/mod.rs b/src/buint/mod.rs index 7f2b51f..ecec99a 100644 --- a/src/buint/mod.rs +++ b/src/buint/mod.rs @@ -142,6 +142,13 @@ macro_rules! mod_impl { ones } + #[doc = doc::cast_signed!(U)] + #[must_use = doc::must_use_op!()] + #[inline] + pub const fn cast_signed(self) -> $BInt { + $BInt::::from_bits(self) + } + #[inline] const unsafe fn rotate_digits_left(self, n: usize) -> Self { let mut out = Self::ZERO; @@ -238,10 +245,8 @@ 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) } @@ -298,6 +303,13 @@ macro_rules! mod_impl { self.wrapping_next_power_of_two() } + #[doc = doc::midpoint!(U)] + #[must_use = doc::must_use_op!()] + #[inline] + pub const fn midpoint(self, rhs: Self) -> Self { + (self.bitxor(rhs).shr(1)).add(self.bitand(rhs)) + } + #[doc = doc::ilog2!(U)] #[must_use = doc::must_use_op!()] #[inline] @@ -637,10 +649,12 @@ macro_rules! mod_impl { function: ::next_power_of_two(a: utest), skip: debug_skip!(a.checked_next_power_of_two().is_none()) } - test_bignum! { function: ::is_power_of_two(a: utest) } + test_bignum! { + function: ::cast_signed(a: utest) + } #[test] fn digits() { @@ -709,7 +723,7 @@ macro_rules! mod_impl { }; } -crate::main_impl!(mod_impl); +crate::macro_impl!(mod_impl); pub mod float_as; mod bigint_helpers; @@ -728,5 +742,6 @@ mod ops; mod overflowing; mod radix; mod saturating; +mod strict; mod unchecked; mod wrapping; diff --git a/src/buint/strict.rs b/src/buint/strict.rs new file mode 100644 index 0000000..395d920 --- /dev/null +++ b/src/buint/strict.rs @@ -0,0 +1,36 @@ +macro_rules! strict { + ($BUint: ident, $BInt: ident, $Digit: ident) => { + #[doc = doc::strict::impl_desc!()] + impl $BUint { + crate::int::strict::impls!(U); + + #[doc = doc::strict::strict_add_signed!(U)] + #[must_use = doc::must_use_op!()] + #[inline] + pub const fn strict_add_signed(self, rhs: $BInt) -> Self { + crate::errors::option_expect!( + self.checked_add_signed(rhs), + crate::errors::err_msg!("attempt to add with overflow") + ) + } + } + + #[cfg(test)] + paste::paste! { + mod [<$Digit _digit_tests>] { + use crate::test::types::big_types::$Digit::*; + + crate::int::strict::tests!(utest); + + test_bignum! { + function: ::strict_add_signed(a: utest, b: itest), + skip: a.checked_add_signed(b).is_none() + } + } + } + }; +} + +use crate::doc; + +crate::macro_impl!(strict); diff --git a/src/buint/unchecked.rs b/src/buint/unchecked.rs index 3a1d606..0601a54 100644 --- a/src/buint/unchecked.rs +++ b/src/buint/unchecked.rs @@ -1,6 +1,14 @@ macro_rules! unchecked { ($BUint: ident, $BInt: ident, $Digit: ident) => { - crate::int::unchecked::impls!($BUint, I); + crate::int::unchecked::impls!($BUint, U); + + #[cfg(test)] + paste::paste! { + mod [<$Digit _digit_tests>] { + use crate::test::types::big_types::$Digit::*; + crate::int::unchecked::tests!(utest); + } + } }; } diff --git a/src/doc/bigint_helpers.rs b/src/doc/bigint_helpers.rs index 33c40e8..c979f61 100644 --- a/src/doc/bigint_helpers.rs +++ b/src/doc/bigint_helpers.rs @@ -1,3 +1,11 @@ use crate::doc; +macro_rules! impl_desc { + () => { + "Bigint helper methods: common functions used to implement big integer arithmetic." + }; +} + +pub(crate) use impl_desc; + doc::link_doc_comment!(carrying_add, borrowing_sub, widening_mul, carrying_mul); diff --git a/src/doc/mod.rs b/src/doc/mod.rs index ccb51d2..9ab2334 100644 --- a/src/doc/mod.rs +++ b/src/doc/mod.rs @@ -6,8 +6,8 @@ pub mod endian; pub mod overflowing; pub mod radix; pub mod saturating; +pub mod strict; pub mod unchecked; - pub mod wrapping; macro_rules! arithmetic_doc { @@ -404,5 +404,8 @@ crate::doc::link_doc_comment! { abs, signum, is_positive, - is_negative + is_negative, + cast_signed, + cast_unsigned, + midpoint } diff --git a/src/doc/strict.rs b/src/doc/strict.rs new file mode 100644 index 0000000..c306961 --- /dev/null +++ b/src/doc/strict.rs @@ -0,0 +1,25 @@ +macro_rules! impl_desc { + () => { + doc::arithmetic_impl_desc!("Strict", "strict", "Each method will always panic if overflow/underflow occurs (i.e. when the checked equivalent would return `None`), regardless of whether overflow checks are enabled.") + }; +} + +pub(crate) use impl_desc; + +crate::doc::link_doc_comment!( + strict_abs, + strict_add, + strict_add_signed, + strict_add_unsigned, + strict_div, + strict_div_euclid, + strict_mul, + strict_neg, + strict_pow, + strict_rem, + strict_rem_euclid, + strict_shl, + strict_shr, + strict_sub, + strict_sub_unsigned +); diff --git a/src/doc/unchecked.rs b/src/doc/unchecked.rs index 0c10bd8..409cf1a 100644 --- a/src/doc/unchecked.rs +++ b/src/doc/unchecked.rs @@ -1,6 +1,6 @@ macro_rules! impl_desc { () => { - doc::arithmetic_impl_desc!("Unchecked", "unchecked", "Each method results in undefined behavior if overflow/underflow occurs, i.e. when the checked equivalent would return `None`.") + doc::arithmetic_impl_desc!("Unchecked", "unchecked", "Each method results in undefined behavior if overflow/underflow occurs (i.e. when the checked equivalent would return `None`).") }; } diff --git a/src/doc/wrapping.rs b/src/doc/wrapping.rs index a44c2a8..c8a80a5 100644 --- a/src/doc/wrapping.rs +++ b/src/doc/wrapping.rs @@ -1,6 +1,6 @@ macro_rules! impl_desc { () => { - doc::arithmetic_impl_desc!("Wrapping", "wrapping", "Each method returns of the calculation truncated to the number of bits of `self`, i.e. they each return the first item in the tuple returned by their overflowing equivalent.") + doc::arithmetic_impl_desc!("Wrapping", "wrapping", "Each method returns of the calculation truncated to the number of bits of `self` (i.e. they each return the first item in the tuple returned by their overflowing equivalent).") }; } diff --git a/src/int/bigint_helpers.rs b/src/int/bigint_helpers.rs new file mode 100644 index 0000000..6d76ca0 --- /dev/null +++ b/src/int/bigint_helpers.rs @@ -0,0 +1,56 @@ +macro_rules! impls { + ($sign: ident) => { + #[doc = doc::bigint_helpers::carrying_add!($sign)] + #[must_use = doc::must_use_op!()] + #[inline] + pub const fn carrying_add(self, rhs: Self, carry: bool) -> (Self, bool) { + let (s1, o1) = self.overflowing_add(rhs); + if carry { + let (s2, o2) = s1.overflowing_add(Self::ONE); + (s2, o1 || o2) + } else { + (s1, o1) + } + } + + #[doc = doc::bigint_helpers::borrowing_sub!($sign)] + #[must_use = doc::must_use_op!()] + #[inline] + pub const fn borrowing_sub(self, rhs: Self, borrow: bool) -> (Self, bool) { + let (s1, o1) = self.overflowing_sub(rhs); + if borrow { + let (s2, o2) = s1.overflowing_sub(Self::ONE); + (s2, o1 || o2) + } else { + (s1, o1) + } + } + }; +} + +pub(crate) use impls; + +#[cfg(test)] +macro_rules! tests { + ($int: ty) => { + use crate::test::{test_bignum, types::*}; + + test_bignum! { + function: <$int>::carrying_add(a: $int, b: $int, carry: bool), + cases: [ + (<$int>::MAX, 1u8, true), + (<$int>::MAX, 1u8, false) + ] + } + test_bignum! { + function: <$int>::borrowing_sub(a: $int, b: $int, borrow: bool), + cases: [ + (<$int>::MIN, 1u8, false), + (<$int>::MIN, 1u8, true) + ] + } + }; +} + +#[cfg(test)] +pub(crate) use tests; diff --git a/src/int/mod.rs b/src/int/mod.rs index 7756d71..d8c026e 100644 --- a/src/int/mod.rs +++ b/src/int/mod.rs @@ -1,3 +1,4 @@ +pub mod bigint_helpers; pub mod cast; pub mod checked; pub mod cmp; @@ -9,6 +10,7 @@ pub mod numtraits; pub mod ops; pub mod radix; +pub mod strict; pub mod unchecked; #[cfg(test)] @@ -59,6 +61,9 @@ macro_rules! tests { test_bignum! { function: <$int>::abs_diff(a: $int, b: $int) } + test_bignum! { + function: <$int>::midpoint(a: $int, b: $int) + } test_bignum! { function: <$int>::ilog(a: $int, base: $int), skip: a <= 0 || base <= 1 diff --git a/src/int/ops.rs b/src/int/ops.rs index 7e3856a..c660e3d 100644 --- a/src/int/ops.rs +++ b/src/int/ops.rs @@ -282,10 +282,7 @@ macro_rules! trait_fillers { #[inline] pub const fn add(self, rhs: Self) -> Self { #[cfg(debug_assertions)] - return crate::errors::option_expect!( - self.checked_add(rhs), - "attempt to add with overflow" - ); + return self.strict_add(rhs); #[cfg(not(debug_assertions))] self.wrapping_add(rhs) @@ -294,10 +291,7 @@ macro_rules! trait_fillers { #[inline] pub const fn mul(self, rhs: Self) -> Self { #[cfg(debug_assertions)] - return crate::errors::option_expect!( - self.checked_mul(rhs), - "attempt to multiply with overflow" - ); + return self.strict_mul(rhs); #[cfg(not(debug_assertions))] self.wrapping_mul(rhs) @@ -306,10 +300,7 @@ macro_rules! trait_fillers { #[inline] pub const fn shl(self, rhs: ExpType) -> Self { #[cfg(debug_assertions)] - return crate::errors::option_expect!( - self.checked_shl(rhs), - "attempt to shift left with overflow" - ); + return self.strict_shl(rhs); #[cfg(not(debug_assertions))] self.wrapping_shl(rhs) @@ -318,10 +309,7 @@ macro_rules! trait_fillers { #[inline] pub const fn shr(self, rhs: ExpType) -> Self { #[cfg(debug_assertions)] - return crate::errors::option_expect!( - self.checked_shr(rhs), - "attempt to shift left with overflow" - ); + return self.strict_shr(rhs); #[cfg(not(debug_assertions))] self.wrapping_shr(rhs) @@ -330,10 +318,7 @@ macro_rules! trait_fillers { #[inline] pub const fn sub(self, rhs: Self) -> Self { #[cfg(debug_assertions)] - return crate::errors::option_expect!( - self.checked_sub(rhs), - "attempt to subtract with overflow" - ); + return self.strict_sub(rhs); #[cfg(not(debug_assertions))] self.wrapping_sub(rhs) diff --git a/src/int/strict.rs b/src/int/strict.rs new file mode 100644 index 0000000..05b1b4a --- /dev/null +++ b/src/int/strict.rs @@ -0,0 +1,157 @@ +macro_rules! impls { + ($sign: ident) => { + #[doc = doc::strict::strict_add!($sign)] + #[must_use = doc::must_use_op!()] + #[inline] + pub const fn strict_add(self, rhs: Self) -> Self { + crate::errors::option_expect!( + self.checked_add(rhs), + crate::errors::err_msg!("attempt to add with overflow") + ) + } + + #[doc = doc::strict::strict_sub!($sign)] + #[must_use = doc::must_use_op!()] + #[inline] + pub const fn strict_sub(self, rhs: Self) -> Self { + crate::errors::option_expect!( + self.checked_sub(rhs), + crate::errors::err_msg!("attempt to subtract with overflow") + ) + } + + #[doc = doc::strict::strict_mul!($sign)] + #[must_use = doc::must_use_op!()] + #[inline] + pub const fn strict_mul(self, rhs: Self) -> Self { + crate::errors::option_expect!( + self.checked_mul(rhs), + crate::errors::err_msg!("attempt to multiply with overflow") + ) + } + + #[doc = doc::strict::strict_div!($sign)] + #[must_use = doc::must_use_op!()] + #[inline] + pub const fn strict_div(self, rhs: Self) -> Self { + self.div(rhs) + } + + #[doc = doc::strict::strict_div_euclid!($sign)] + #[must_use = doc::must_use_op!()] + #[inline] + pub const fn strict_div_euclid(self, rhs: Self) -> Self { + self.div_euclid(rhs) + } + + #[doc = doc::strict::strict_rem!($sign)] + #[must_use = doc::must_use_op!()] + #[inline] + pub const fn strict_rem(self, rhs: Self) -> Self { + self.rem(rhs) + } + + #[doc = doc::strict::strict_rem_euclid!($sign)] + #[must_use = doc::must_use_op!()] + #[inline] + pub const fn strict_rem_euclid(self, rhs: Self) -> Self { + self.rem_euclid(rhs) + } + + #[doc = doc::strict::strict_neg!($sign)] + #[must_use = doc::must_use_op!()] + #[inline] + pub const fn strict_neg(self) -> Self { + crate::errors::option_expect!( + self.checked_neg(), crate::errors::err_msg!("attempt to negate with overflow") + ) + } + + #[doc = doc::strict::strict_shl!($sign)] + #[must_use = doc::must_use_op!()] + #[inline] + pub const fn strict_shl(self, rhs: crate::ExpType) -> Self { + crate::errors::option_expect!( + self.checked_shl(rhs), + crate::errors::err_msg!("attempt to shift left with overflow") + ) + } + + #[doc = doc::strict::strict_shr!($sign)] + #[must_use = doc::must_use_op!()] + #[inline] + pub const fn strict_shr(self, rhs: crate::ExpType) -> Self { + crate::errors::option_expect!( + self.checked_shr(rhs), + crate::errors::err_msg!("attempt to shift right with overflow") + ) + } + + #[doc = doc::strict::strict_pow!($sign)] + #[must_use = doc::must_use_op!()] + #[inline] + pub const fn strict_pow(self, exp: crate::ExpType) -> Self { + crate::errors::option_expect!( + self.checked_pow(exp), + crate::errors::err_msg!("attempt to calculate power with overflow") + ) + } + } +} + +pub(crate) use impls; + +#[cfg(test)] +macro_rules! tests { + ($int: ty) => { + use crate::test::{test_bignum, types::*}; + + test_bignum! { + function: <$int>::strict_add(a: $int, b: $int), + skip: a.checked_add(b).is_none() + } + test_bignum! { + function: <$int>::strict_sub(a: $int, b: $int), + skip: a.checked_sub(b).is_none() + } + test_bignum! { + function: <$int>::strict_mul(a: $int, b: $int), + skip: a.checked_mul(b).is_none() + } + test_bignum! { + function: <$int>::strict_div(a: $int, b: $int), + skip: a.checked_div(b).is_none() + } + test_bignum! { + function: <$int>::strict_div_euclid(a: $int, b: $int), + skip: a.checked_div_euclid(b).is_none() + } + test_bignum! { + function: <$int>::strict_rem(a: $int, b: $int), + skip: a.checked_rem(b).is_none() + } + test_bignum! { + function: <$int>::strict_rem_euclid(a: $int, b: $int), + skip: a.checked_rem_euclid(b).is_none() + } + test_bignum! { + function: <$int>::strict_neg(a: $int), + skip: a.checked_neg().is_none() + } + test_bignum! { + function: <$int>::strict_shl(a: $int, b: u8), + skip: a.checked_shl(b as u32).is_none() + } + test_bignum! { + function: <$int>::strict_shr(a: $int, b: u8), + skip: a.checked_shr(b as u32).is_none() + } + test_bignum! { + function: <$int>::strict_pow(a: $int, b: u8), + skip: a.checked_pow(b as u32).is_none() + } + } +} + +#[cfg(test)] +pub(crate) use tests; diff --git a/src/lib.rs b/src/lib.rs index a735083..3204455 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,6 +17,9 @@ wrapping_next_power_of_two, float_next_up_down, unchecked_shifts, + integer_sign_cast, + num_midpoint, + strict_overflow_ops, ) )] #![doc = include_str!("../README.md")] @@ -56,6 +59,13 @@ use test::types::*; type ExpType = u32; +mod bigints { + pub use crate::bint::{BInt, BIntD16, BIntD32, BIntD8}; + pub use crate::buint::{BUint, BUintD16, BUintD32, BUintD8}; +} + +pub use bigints::*; + macro_rules! macro_impl { ($name: ident) => { use crate::bigints::*; @@ -90,13 +100,6 @@ bint_as_different_digit_bigint!(BUintD8, BIntD8, u8; (BInt, u64), (BIntD32, u32) pub(crate) use main_impl; -mod bigints { - pub use crate::bint::{BInt, BIntD16, BIntD32, BIntD8}; - pub use crate::buint::{BUint, BUintD16, BUintD32, BUintD8}; -} - -pub use bigints::*; - /// Trait for fallible conversions between `bnum` integer types. /// /// Unfortunately, [`TryFrom`] cannot currently be used for conversions between `bnum` integers, since [`TryFrom for T`](https://doc.rust-lang.org/std/convert/trait.TryFrom.html#impl-TryFrom%3CU%3E-for-T) is already implemented by the standard library (and so it is not possible to implement `TryFrom> for BUint`). When the [`generic_const_exprs`](https://github.com/rust-lang/rust/issues/76560) feature becomes stabilised, it may be possible to use [`TryFrom`] instead of `BTryFrom`. `BTryFrom` is designed to have the same behaviour as [`TryFrom`] for conversions between two primitive types, and conversions between a primitive type and a bnum type. `BTryFrom` is a workaround for the issue described above, and so you should not implement it yourself. It should only be used for conversions between `bnum` integers. From 7dd93708a7fcd54a0394f70babb5f1ac681b6f94 Mon Sep 17 00:00:00 2001 From: Isaac Holt Date: Fri, 20 Sep 2024 14:21:55 +0100 Subject: [PATCH 13/14] add borsh feature to readme --- README.md | 2 +- changes/v0.12.0 | 2 +- src/bint/mod.rs | 3 --- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 60fc410..bcfb131 100644 --- a/README.md +++ b/README.md @@ -113,7 +113,7 @@ 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` +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 diff --git a/changes/v0.12.0 b/changes/v0.12.0 index 0f046aa..4086aab 100644 --- a/changes/v0.12.0 +++ b/changes/v0.12.0 @@ -3,7 +3,7 @@ - `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 unsigned 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. \ No newline at end of file diff --git a/src/bint/mod.rs b/src/bint/mod.rs index 46b31ec..644af1d 100644 --- a/src/bint/mod.rs +++ b/src/bint/mod.rs @@ -18,9 +18,6 @@ macro_rules! ilog { } } -// #[cfg(debug_assertions)] -// use crate::errors::option_expect; - use crate::digit; use crate::ExpType; use crate::{doc, errors}; From 1eaa169bbdf3fd2facb345abbb607332b5a71d3b Mon Sep 17 00:00:00 2001 From: Isaac Holt Date: Fri, 20 Sep 2024 14:47:36 +0100 Subject: [PATCH 14/14] fix carrying_add and borrwing_sub --- src/int/bigint_helpers.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/int/bigint_helpers.rs b/src/int/bigint_helpers.rs index 6d76ca0..7e387da 100644 --- a/src/int/bigint_helpers.rs +++ b/src/int/bigint_helpers.rs @@ -7,7 +7,7 @@ macro_rules! impls { let (s1, o1) = self.overflowing_add(rhs); if carry { let (s2, o2) = s1.overflowing_add(Self::ONE); - (s2, o1 || o2) + (s2, o1 ^ o2) } else { (s1, o1) } @@ -20,7 +20,7 @@ macro_rules! impls { let (s1, o1) = self.overflowing_sub(rhs); if borrow { let (s2, o2) = s1.overflowing_sub(Self::ONE); - (s2, o1 || o2) + (s2, o1 ^ o2) } else { (s1, o1) }