Auto merge of #132195 - clarfonthey:bigint-mul, r=scottmcm

Tidy up bigint multiplication methods

This tidies up the library version of the bigint multiplication methods after the addition of the intrinsics in #133663. It follows [this summary](https://github.com/rust-lang/rust/issues/85532#issuecomment-2403442775) of what's desired for these methods.

Note that, if `2H = N`, then `uH::MAX * uH::MAX + uH::MAX + uH::MAX` is `uN::MAX`, and that we can effectively add two "carry" values without overflowing.

For ease of terminology, the "low-order" or "least significant" or "wrapping" half of multiplication will be called the low part, and the "high-order" or "most significant" or "overflowing" half of multiplication will be called the high part. In all cases, the return convention is `(low, high)` and left unchanged by this PR, to be litigated later.

## API Changes

The original API:

```rust
impl uN {
    // computes self * rhs
    pub const fn widening_mul(self, rhs: uN) -> (uN, uN);

    // computes self * rhs + carry
    pub const fn carrying_mul(self, rhs: uN, carry: uN) -> (uN, uN);
}
```

The added API:

```rust
impl uN {
    // computes self * rhs + carry1 + carry2
    pub const fn carrying2_mul(self, rhs: uN, carry: uN, add: uN) -> (uN, uN);
}
impl iN {
    // note that the low part is unsigned
    pub const fn widening_mul(self, rhs: iN) -> (uN, iN);
    pub const fn carrying_mul(self, rhs: iN, carry: iN) -> (uN, iN);
    pub const fn carrying_mul_add(self, rhs: iN, carry: iN, add: iN) -> (uN, iN);
}
```

Additionally, a naive implementation has been added for `u128` and `i128` since there are no double-wide types for those. Eventually, an intrinsic will be added to make these more efficient, but rather than doing this all at once, the library changes are added first.

## Justifications for API

The unsigned parts are done to ensure consistency with overflowing addition: for a two's complement integer, you want to have unsigned overflow semantics for all parts of the integer except the highest one. This is because overflow for unsigned integers happens on the highest bit (from `MAX` to zero), whereas overflow for signed integers happens on the second highest bit (from `MAX` to `MIN`). Since the sign information only matters in the highest part, we use unsigned overflow for everything but that part.

There is still discussion on the merits of signed bigint *addition* methods, since getting the behaviour right is very subtle, but at least for signed bigint *multiplication*, the sign of the operands does make a difference. So, it feels appropriate that at least until we've nailed down the final API, there should be an option to do signed versions of these methods.

Additionally, while it's unclear whether we need all three versions of bigint multiplication (widening, carrying-1, and carrying-2), since it's possible to have up to two carries without overflow, there should at least be a method to allow that. We could potentially only offer the carry-2 method and expect that adding zero carries afterword will optimise correctly, but again, this can be litigated before stabilisation.

## Note on documentation

While a lot of care was put into the documentation for the `widening_mul` and `carrying_mul` methods on unsigned integers, I have not taken this same care for `carrying_mul_add` or the signed versions. While I have updated the doc tests to be more appropriate, there will likely be many documentation changes done before stabilisation.

## Note on tests

Alongside this change, I've added several tests to ensure that these methods work as expected. These are alongside the codegen tests for the intrinsics.
This commit is contained in:
bors
2024-12-31 18:49:36 +00:00
11 changed files with 388 additions and 123 deletions

View File

@@ -110,6 +110,7 @@
#![cfg_attr(bootstrap, feature(do_not_recommend))] #![cfg_attr(bootstrap, feature(do_not_recommend))]
#![feature(array_ptr_get)] #![feature(array_ptr_get)]
#![feature(asm_experimental_arch)] #![feature(asm_experimental_arch)]
#![feature(bigint_helper_methods)]
#![feature(const_carrying_mul_add)] #![feature(const_carrying_mul_add)]
#![feature(const_eval_select)] #![feature(const_eval_select)]
#![feature(core_intrinsics)] #![feature(core_intrinsics)]

View File

@@ -2512,6 +2512,114 @@ macro_rules! int_impl {
(a as Self, b) (a as Self, b)
} }
/// Calculates the complete product `self * rhs` without the possibility to overflow.
///
/// This returns the low-order (wrapping) bits and the high-order (overflow) bits
/// of the result as two separate values, in that order.
///
/// If you also need to add a carry to the wide result, then you want
/// [`Self::carrying_mul`] instead.
///
/// # Examples
///
/// Basic usage:
///
/// Please note that this example is shared between integer types.
/// Which explains why `i32` is used here.
///
/// ```
/// #![feature(bigint_helper_methods)]
/// assert_eq!(5i32.widening_mul(-2), (4294967286, -1));
/// assert_eq!(1_000_000_000i32.widening_mul(-10), (2884901888, -3));
/// ```
#[unstable(feature = "bigint_helper_methods", issue = "85532")]
#[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline]
pub const fn widening_mul(self, rhs: Self) -> ($UnsignedT, Self) {
Self::carrying_mul_add(self, rhs, 0, 0)
}
/// Calculates the "full multiplication" `self * rhs + carry`
/// without the possibility to overflow.
///
/// This returns the low-order (wrapping) bits and the high-order (overflow) bits
/// of the result as two separate values, in that order.
///
/// Performs "long multiplication" which takes in an extra amount to add, and may return an
/// additional amount of overflow. This allows for chaining together multiple
/// multiplications to create "big integers" which represent larger values.
///
/// If you don't need the `carry`, then you can use [`Self::widening_mul`] instead.
///
/// # Examples
///
/// Basic usage:
///
/// Please note that this example is shared between integer types.
/// Which explains why `i32` is used here.
///
/// ```
/// #![feature(bigint_helper_methods)]
/// assert_eq!(5i32.carrying_mul(-2, 0), (4294967286, -1));
/// assert_eq!(5i32.carrying_mul(-2, 10), (0, 0));
/// assert_eq!(1_000_000_000i32.carrying_mul(-10, 0), (2884901888, -3));
/// assert_eq!(1_000_000_000i32.carrying_mul(-10, 10), (2884901898, -3));
#[doc = concat!("assert_eq!(",
stringify!($SelfT), "::MAX.carrying_mul(", stringify!($SelfT), "::MAX, ", stringify!($SelfT), "::MAX), ",
"(", stringify!($SelfT), "::MAX.unsigned_abs() + 1, ", stringify!($SelfT), "::MAX / 2));"
)]
/// ```
#[unstable(feature = "bigint_helper_methods", issue = "85532")]
#[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline]
pub const fn carrying_mul(self, rhs: Self, carry: Self) -> ($UnsignedT, Self) {
Self::carrying_mul_add(self, rhs, carry, 0)
}
/// Calculates the "full multiplication" `self * rhs + carry1 + carry2`
/// without the possibility to overflow.
///
/// This returns the low-order (wrapping) bits and the high-order (overflow) bits
/// of the result as two separate values, in that order.
///
/// Performs "long multiplication" which takes in an extra amount to add, and may return an
/// additional amount of overflow. This allows for chaining together multiple
/// multiplications to create "big integers" which represent larger values.
///
/// If you don't need either `carry`, then you can use [`Self::widening_mul`] instead,
/// and if you only need one `carry`, then you can use [`Self::carrying_mul`] instead.
///
/// # Examples
///
/// Basic usage:
///
/// Please note that this example is shared between integer types.
/// Which explains why `i32` is used here.
///
/// ```
/// #![feature(bigint_helper_methods)]
/// assert_eq!(5i32.carrying_mul_add(-2, 0, 0), (4294967286, -1));
/// assert_eq!(5i32.carrying_mul_add(-2, 10, 10), (10, 0));
/// assert_eq!(1_000_000_000i32.carrying_mul_add(-10, 0, 0), (2884901888, -3));
/// assert_eq!(1_000_000_000i32.carrying_mul_add(-10, 10, 10), (2884901908, -3));
#[doc = concat!("assert_eq!(",
stringify!($SelfT), "::MAX.carrying_mul_add(", stringify!($SelfT), "::MAX, ", stringify!($SelfT), "::MAX, ", stringify!($SelfT), "::MAX), ",
"(", stringify!($UnsignedT), "::MAX, ", stringify!($SelfT), "::MAX / 2));"
)]
/// ```
#[unstable(feature = "bigint_helper_methods", issue = "85532")]
#[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline]
pub const fn carrying_mul_add(self, rhs: Self, carry: Self, add: Self) -> ($UnsignedT, Self) {
intrinsics::carrying_mul_add(self, rhs, carry, add)
}
/// Calculates the divisor when `self` is divided by `rhs`. /// Calculates the divisor when `self` is divided by `rhs`.
/// ///
/// Returns a tuple of the divisor along with a boolean indicating whether an arithmetic overflow would /// Returns a tuple of the divisor along with a boolean indicating whether an arithmetic overflow would

View File

@@ -2530,6 +2530,162 @@ macro_rules! uint_impl {
(a as Self, b) (a as Self, b)
} }
/// Calculates the complete product `self * rhs` without the possibility to overflow.
///
/// This returns the low-order (wrapping) bits and the high-order (overflow) bits
/// of the result as two separate values, in that order.
///
/// If you also need to add a carry to the wide result, then you want
/// [`Self::carrying_mul`] instead.
///
/// # Examples
///
/// Basic usage:
///
/// Please note that this example is shared between integer types.
/// Which explains why `u32` is used here.
///
/// ```
/// #![feature(bigint_helper_methods)]
/// assert_eq!(5u32.widening_mul(2), (10, 0));
/// assert_eq!(1_000_000_000u32.widening_mul(10), (1410065408, 2));
/// ```
#[unstable(feature = "bigint_helper_methods", issue = "85532")]
#[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline]
pub const fn widening_mul(self, rhs: Self) -> (Self, Self) {
Self::carrying_mul_add(self, rhs, 0, 0)
}
/// Calculates the "full multiplication" `self * rhs + carry`
/// without the possibility to overflow.
///
/// This returns the low-order (wrapping) bits and the high-order (overflow) bits
/// of the result as two separate values, in that order.
///
/// Performs "long multiplication" which takes in an extra amount to add, and may return an
/// additional amount of overflow. This allows for chaining together multiple
/// multiplications to create "big integers" which represent larger values.
///
/// If you don't need the `carry`, then you can use [`Self::widening_mul`] instead.
///
/// # Examples
///
/// Basic usage:
///
/// Please note that this example is shared between integer types.
/// Which explains why `u32` is used here.
///
/// ```
/// #![feature(bigint_helper_methods)]
/// assert_eq!(5u32.carrying_mul(2, 0), (10, 0));
/// assert_eq!(5u32.carrying_mul(2, 10), (20, 0));
/// assert_eq!(1_000_000_000u32.carrying_mul(10, 0), (1410065408, 2));
/// assert_eq!(1_000_000_000u32.carrying_mul(10, 10), (1410065418, 2));
#[doc = concat!("assert_eq!(",
stringify!($SelfT), "::MAX.carrying_mul(", stringify!($SelfT), "::MAX, ", stringify!($SelfT), "::MAX), ",
"(0, ", stringify!($SelfT), "::MAX));"
)]
/// ```
///
/// This is the core operation needed for scalar multiplication when
/// implementing it for wider-than-native types.
///
/// ```
/// #![feature(bigint_helper_methods)]
/// fn scalar_mul_eq(little_endian_digits: &mut Vec<u16>, multiplicand: u16) {
/// let mut carry = 0;
/// for d in little_endian_digits.iter_mut() {
/// (*d, carry) = d.carrying_mul(multiplicand, carry);
/// }
/// if carry != 0 {
/// little_endian_digits.push(carry);
/// }
/// }
///
/// let mut v = vec![10, 20];
/// scalar_mul_eq(&mut v, 3);
/// assert_eq!(v, [30, 60]);
///
/// assert_eq!(0x87654321_u64 * 0xFEED, 0x86D3D159E38D);
/// let mut v = vec![0x4321, 0x8765];
/// scalar_mul_eq(&mut v, 0xFEED);
/// assert_eq!(v, [0xE38D, 0xD159, 0x86D3]);
/// ```
///
/// If `carry` is zero, this is similar to [`overflowing_mul`](Self::overflowing_mul),
/// except that it gives the value of the overflow instead of just whether one happened:
///
/// ```
/// #![feature(bigint_helper_methods)]
/// let r = u8::carrying_mul(7, 13, 0);
/// assert_eq!((r.0, r.1 != 0), u8::overflowing_mul(7, 13));
/// let r = u8::carrying_mul(13, 42, 0);
/// assert_eq!((r.0, r.1 != 0), u8::overflowing_mul(13, 42));
/// ```
///
/// The value of the first field in the returned tuple matches what you'd get
/// by combining the [`wrapping_mul`](Self::wrapping_mul) and
/// [`wrapping_add`](Self::wrapping_add) methods:
///
/// ```
/// #![feature(bigint_helper_methods)]
/// assert_eq!(
/// 789_u16.carrying_mul(456, 123).0,
/// 789_u16.wrapping_mul(456).wrapping_add(123),
/// );
/// ```
#[unstable(feature = "bigint_helper_methods", issue = "85532")]
#[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline]
pub const fn carrying_mul(self, rhs: Self, carry: Self) -> (Self, Self) {
Self::carrying_mul_add(self, rhs, carry, 0)
}
/// Calculates the "full multiplication" `self * rhs + carry1 + carry2`
/// without the possibility to overflow.
///
/// This returns the low-order (wrapping) bits and the high-order (overflow) bits
/// of the result as two separate values, in that order.
///
/// Performs "long multiplication" which takes in an extra amount to add, and may return an
/// additional amount of overflow. This allows for chaining together multiple
/// multiplications to create "big integers" which represent larger values.
///
/// If you don't need either `carry`, then you can use [`Self::widening_mul`] instead,
/// and if you only need one `carry`, then you can use [`Self::carrying_mul`] instead.
///
/// # Examples
///
/// Basic usage:
///
/// Please note that this example is shared between integer types.
/// Which explains why `u32` is used here.
///
/// ```
/// #![feature(bigint_helper_methods)]
/// assert_eq!(5u32.carrying_mul_add(2, 0, 0), (10, 0));
/// assert_eq!(5u32.carrying_mul_add(2, 10, 10), (30, 0));
/// assert_eq!(1_000_000_000u32.carrying_mul_add(10, 0, 0), (1410065408, 2));
/// assert_eq!(1_000_000_000u32.carrying_mul_add(10, 10, 10), (1410065428, 2));
#[doc = concat!("assert_eq!(",
stringify!($SelfT), "::MAX.carrying_mul_add(", stringify!($SelfT), "::MAX, ", stringify!($SelfT), "::MAX, ", stringify!($SelfT), "::MAX), ",
"(", stringify!($SelfT), "::MAX, ", stringify!($SelfT), "::MAX));"
)]
/// ```
#[unstable(feature = "bigint_helper_methods", issue = "85532")]
#[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline]
pub const fn carrying_mul_add(self, rhs: Self, carry: Self, add: Self) -> (Self, Self) {
intrinsics::carrying_mul_add(self, rhs, carry, add)
}
/// Calculates the divisor when `self` is divided by `rhs`. /// Calculates the divisor when `self` is divided by `rhs`.
/// ///
/// Returns a tuple of the divisor along with a boolean indicating /// Returns a tuple of the divisor along with a boolean indicating
@@ -3347,122 +3503,6 @@ macro_rules! uint_impl {
unsafe { mem::transmute(bytes) } unsafe { mem::transmute(bytes) }
} }
/// Calculates the complete product `self * rhs` without the possibility to overflow.
///
/// This returns the low-order (wrapping) bits and the high-order (overflow) bits
/// of the result as two separate values, in that order.
///
/// If you also need to add a carry to the wide result, then you want
/// [`Self::carrying_mul`] instead.
///
/// # Examples
///
/// Basic usage:
///
/// Please note that this example is shared between integer types.
/// Which explains why `u32` is used here.
///
/// ```
/// #![feature(bigint_helper_methods)]
/// assert_eq!(5u32.widening_mul(2), (10, 0));
/// assert_eq!(1_000_000_000u32.widening_mul(10), (1410065408, 2));
/// ```
#[unstable(feature = "bigint_helper_methods", issue = "85532")]
#[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline]
pub const fn widening_mul(self, rhs: Self) -> (Self, Self) {
Self::carrying_mul(self, rhs, 0)
}
/// Calculates the "full multiplication" `self * rhs + carry`
/// without the possibility to overflow.
///
/// This returns the low-order (wrapping) bits and the high-order (overflow) bits
/// of the result as two separate values, in that order.
///
/// Performs "long multiplication" which takes in an extra amount to add, and may return an
/// additional amount of overflow. This allows for chaining together multiple
/// multiplications to create "big integers" which represent larger values.
///
/// If you don't need the `carry`, then you can use [`Self::widening_mul`] instead.
///
/// # Examples
///
/// Basic usage:
///
/// Please note that this example is shared between integer types.
/// Which explains why `u32` is used here.
///
/// ```
/// #![feature(bigint_helper_methods)]
/// assert_eq!(5u32.carrying_mul(2, 0), (10, 0));
/// assert_eq!(5u32.carrying_mul(2, 10), (20, 0));
/// assert_eq!(1_000_000_000u32.carrying_mul(10, 0), (1410065408, 2));
/// assert_eq!(1_000_000_000u32.carrying_mul(10, 10), (1410065418, 2));
#[doc = concat!("assert_eq!(",
stringify!($SelfT), "::MAX.carrying_mul(", stringify!($SelfT), "::MAX, ", stringify!($SelfT), "::MAX), ",
"(0, ", stringify!($SelfT), "::MAX));"
)]
/// ```
///
/// This is the core operation needed for scalar multiplication when
/// implementing it for wider-than-native types.
///
/// ```
/// #![feature(bigint_helper_methods)]
/// fn scalar_mul_eq(little_endian_digits: &mut Vec<u16>, multiplicand: u16) {
/// let mut carry = 0;
/// for d in little_endian_digits.iter_mut() {
/// (*d, carry) = d.carrying_mul(multiplicand, carry);
/// }
/// if carry != 0 {
/// little_endian_digits.push(carry);
/// }
/// }
///
/// let mut v = vec![10, 20];
/// scalar_mul_eq(&mut v, 3);
/// assert_eq!(v, [30, 60]);
///
/// assert_eq!(0x87654321_u64 * 0xFEED, 0x86D3D159E38D);
/// let mut v = vec![0x4321, 0x8765];
/// scalar_mul_eq(&mut v, 0xFEED);
/// assert_eq!(v, [0xE38D, 0xD159, 0x86D3]);
/// ```
///
/// If `carry` is zero, this is similar to [`overflowing_mul`](Self::overflowing_mul),
/// except that it gives the value of the overflow instead of just whether one happened:
///
/// ```
/// #![feature(bigint_helper_methods)]
/// let r = u8::carrying_mul(7, 13, 0);
/// assert_eq!((r.0, r.1 != 0), u8::overflowing_mul(7, 13));
/// let r = u8::carrying_mul(13, 42, 0);
/// assert_eq!((r.0, r.1 != 0), u8::overflowing_mul(13, 42));
/// ```
///
/// The value of the first field in the returned tuple matches what you'd get
/// by combining the [`wrapping_mul`](Self::wrapping_mul) and
/// [`wrapping_add`](Self::wrapping_add) methods:
///
/// ```
/// #![feature(bigint_helper_methods)]
/// assert_eq!(
/// 789_u16.carrying_mul(456, 123).0,
/// 789_u16.wrapping_mul(456).wrapping_add(123),
/// );
/// ```
#[unstable(feature = "bigint_helper_methods", issue = "85532")]
#[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline]
pub const fn carrying_mul(self, rhs: Self, carry: Self) -> (Self, Self) {
intrinsics::carrying_mul_add(self, rhs, 0, carry)
}
/// New code should prefer to use /// New code should prefer to use
#[doc = concat!("[`", stringify!($SelfT), "::MIN", "`] instead.")] #[doc = concat!("[`", stringify!($SelfT), "::MIN", "`] instead.")]
/// ///

View File

@@ -98,10 +98,13 @@
/// Version of `assert_matches` that ignores fancy runtime printing in const context and uses structural equality. /// Version of `assert_matches` that ignores fancy runtime printing in const context and uses structural equality.
macro_rules! assert_eq_const_safe { macro_rules! assert_eq_const_safe {
($left:expr, $right:expr) => {
assert_eq_const_safe!($left, $right, concat!(stringify!($left), " == ", stringify!($right)));
};
($left:expr, $right:expr$(, $($arg:tt)+)?) => { ($left:expr, $right:expr$(, $($arg:tt)+)?) => {
{ {
fn runtime() { fn runtime() {
assert_eq!($left, $right, $($arg)*); assert_eq!($left, $right, $($($arg)*),*);
} }
const fn compiletime() { const fn compiletime() {
assert!(matches!($left, const { $right })); assert!(matches!($left, const { $right }));

View File

@@ -1 +1 @@
int_module!(i128); int_module!(i128, u128);

View File

@@ -1 +1 @@
int_module!(i16); int_module!(i16, u16);

View File

@@ -1,4 +1,4 @@
int_module!(i32); int_module!(i32, u32);
#[test] #[test]
fn test_arith_operation() { fn test_arith_operation() {

View File

@@ -1 +1 @@
int_module!(i64); int_module!(i64, u64);

View File

@@ -1 +1 @@
int_module!(i8); int_module!(i8, u8);

View File

@@ -1,8 +1,10 @@
macro_rules! int_module { macro_rules! int_module {
($T:ident) => { ($T:ident, $U:ident) => {
use core::ops::{BitAnd, BitOr, BitXor, Not, Shl, Shr}; use core::ops::{BitAnd, BitOr, BitXor, Not, Shl, Shr};
use core::$T::*; use core::$T::*;
const UMAX: $U = $U::MAX;
use crate::num; use crate::num;
#[test] #[test]
@@ -355,6 +357,102 @@ macro_rules! int_module {
assert_eq_const_safe!((0 as $T).borrowing_sub(MIN, true), (MAX, false)); assert_eq_const_safe!((0 as $T).borrowing_sub(MIN, true), (MAX, false));
} }
fn test_widening_mul() {
assert_eq_const_safe!(MAX.widening_mul(MAX), (1, MAX / 2));
assert_eq_const_safe!(MIN.widening_mul(MAX), (MIN as $U, MIN / 2));
assert_eq_const_safe!(MIN.widening_mul(MIN), (0, MAX / 2 + 1));
}
fn test_carrying_mul() {
assert_eq_const_safe!(MAX.carrying_mul(MAX, 0), (1, MAX / 2));
assert_eq_const_safe!(
MAX.carrying_mul(MAX, MAX),
(UMAX / 2 + 1, MAX / 2)
);
assert_eq_const_safe!(
MAX.carrying_mul(MAX, MIN),
(UMAX / 2 + 2, MAX / 2 - 1)
);
assert_eq_const_safe!(MIN.carrying_mul(MAX, 0), (MIN as $U, MIN / 2));
assert_eq_const_safe!(MIN.carrying_mul(MAX, MAX), (UMAX, MIN / 2));
assert_eq_const_safe!(MIN.carrying_mul(MAX, MIN), (0, MIN / 2));
assert_eq_const_safe!(MIN.carrying_mul(MIN, 0), (0, MAX / 2 + 1));
assert_eq_const_safe!(
MIN.carrying_mul(MIN, MAX),
(UMAX / 2, MAX / 2 + 1)
);
assert_eq_const_safe!(
MIN.carrying_mul(MIN, MIN),
(UMAX / 2 + 1, MAX / 2)
);
}
fn test_carrying_mul_add() {
assert_eq_const_safe!(MAX.carrying_mul_add(MAX, 0, 0), (1, MAX / 2));
assert_eq_const_safe!(
MAX.carrying_mul_add(MAX, MAX, 0),
(UMAX / 2 + 1, MAX / 2)
);
assert_eq_const_safe!(
MAX.carrying_mul_add(MAX, MIN, 0),
(UMAX / 2 + 2, MAX / 2 - 1)
);
assert_eq_const_safe!(
MAX.carrying_mul_add(MAX, MAX, MAX),
(UMAX, MAX / 2)
);
assert_eq_const_safe!(
MAX.carrying_mul_add(MAX, MAX, MIN),
(0, MAX / 2)
);
assert_eq_const_safe!(
MAX.carrying_mul_add(MAX, MIN, MIN),
(1, MAX / 2 - 1)
);
assert_eq_const_safe!(
MIN.carrying_mul_add(MAX, 0, 0),
(MIN as $U, MIN / 2)
);
assert_eq_const_safe!(
MIN.carrying_mul_add(MAX, MAX, 0),
(UMAX, MIN / 2)
);
assert_eq_const_safe!(MIN.carrying_mul_add(MAX, MIN, 0), (0, MIN / 2));
assert_eq_const_safe!(
MIN.carrying_mul_add(MAX, MAX, MAX),
(UMAX / 2 - 1, MIN / 2 + 1)
);
assert_eq_const_safe!(
MIN.carrying_mul_add(MAX, MAX, MIN),
(UMAX / 2, MIN / 2)
);
assert_eq_const_safe!(
MIN.carrying_mul_add(MAX, MIN, MIN),
(UMAX / 2 + 1, MIN / 2 - 1)
);
assert_eq_const_safe!(MIN.carrying_mul_add(MIN, 0, 0), (0, MAX / 2 + 1));
assert_eq_const_safe!(
MIN.carrying_mul_add(MIN, MAX, 0),
(UMAX / 2, MAX / 2 + 1)
);
assert_eq_const_safe!(
MIN.carrying_mul_add(MIN, MIN, 0),
(UMAX / 2 + 1, MAX / 2)
);
assert_eq_const_safe!(
MIN.carrying_mul_add(MIN, MAX, MAX),
(UMAX - 1, MAX / 2 + 1)
);
assert_eq_const_safe!(
MIN.carrying_mul_add(MIN, MAX, MIN),
(UMAX, MAX / 2)
);
assert_eq_const_safe!(
MIN.carrying_mul_add(MIN, MIN, MIN),
(0, MAX / 2)
);
}
fn test_midpoint() { fn test_midpoint() {
assert_eq_const_safe!(<$T>::midpoint(1, 3), 2); assert_eq_const_safe!(<$T>::midpoint(1, 3), 2);
assert_eq_const_safe!(<$T>::midpoint(3, 1), 2); assert_eq_const_safe!(<$T>::midpoint(3, 1), 2);

View File

@@ -277,6 +277,21 @@ macro_rules! uint_module {
assert_eq_const_safe!($T::MAX.borrowing_sub($T::MAX, true), ($T::MAX, true)); assert_eq_const_safe!($T::MAX.borrowing_sub($T::MAX, true), ($T::MAX, true));
} }
fn test_widening_mul() {
assert_eq_const_safe!($T::MAX.widening_mul($T::MAX), (1, $T::MAX - 1));
}
fn test_carrying_mul() {
assert_eq_const_safe!($T::MAX.carrying_mul($T::MAX, 0), (1, $T::MAX - 1));
assert_eq_const_safe!($T::MAX.carrying_mul($T::MAX, $T::MAX), (0, $T::MAX));
}
fn test_carrying_mul_add() {
assert_eq_const_safe!($T::MAX.carrying_mul_add($T::MAX, 0, 0), (1, $T::MAX - 1));
assert_eq_const_safe!($T::MAX.carrying_mul_add($T::MAX, $T::MAX, 0), (0, $T::MAX));
assert_eq_const_safe!($T::MAX.carrying_mul_add($T::MAX, $T::MAX, $T::MAX), ($T::MAX, $T::MAX));
}
fn test_midpoint() { fn test_midpoint() {
assert_eq_const_safe!(<$T>::midpoint(1, 3), 2); assert_eq_const_safe!(<$T>::midpoint(1, 3), 2);
assert_eq_const_safe!(<$T>::midpoint(3, 1), 2); assert_eq_const_safe!(<$T>::midpoint(3, 1), 2);