Rollup merge of #152132 - folkertdev:carryless-mul, r=Mark-Simulacrum
implement `carryless_mul` tracking issue: https://github.com/rust-lang/rust/issues/152080 ACP: https://github.com/rust-lang/libs-team/issues/738 This defers to LLVM's `llvm.clmul` when available, and otherwise falls back to a method from the `polyval` crate ([link](https://github.com/RustCrypto/universal-hashes/blob/master/polyval/src/field_element/soft/soft64.rs)). Some things are missing, which I think we can defer: - the ACP has some discussion about additional methods, but I'm not sure exactly what is wanted or how to implement it efficiently - the SIMD intrinsic is not yet `const` (I think I ran into a bootstrapping issue). That is fine for now, I think in `stdarch` we can't really use this intrinsic at the moment, we'd only want the scalar version to replace some riscv intrinsics. - the SIMD intrinsic is not implemented for the gcc and cranelift backends. That should be reasonably straightforward once we have a const eval implementation though.
This commit is contained in:
commit
f065c9dab1
15 changed files with 569 additions and 5 deletions
|
|
@ -218,3 +218,38 @@ macro_rules! impl_funnel_shifts {
|
|||
impl_funnel_shifts! {
|
||||
u8, u16, u32, u64, u128, usize
|
||||
}
|
||||
|
||||
#[rustc_const_unstable(feature = "core_intrinsics_fallbacks", issue = "none")]
|
||||
pub const trait CarrylessMul: Copy + 'static {
|
||||
/// See [`super::carryless_mul`]; we just need the trait indirection to handle
|
||||
/// different types since calling intrinsics with generics doesn't work.
|
||||
fn carryless_mul(self, rhs: Self) -> Self;
|
||||
}
|
||||
|
||||
macro_rules! impl_carryless_mul{
|
||||
($($type:ident),*) => {$(
|
||||
#[rustc_const_unstable(feature = "core_intrinsics_fallbacks", issue = "none")]
|
||||
impl const CarrylessMul for $type {
|
||||
#[inline]
|
||||
fn carryless_mul(self, rhs: Self) -> Self {
|
||||
let mut result = 0;
|
||||
let mut i = 0;
|
||||
|
||||
while i < $type::BITS {
|
||||
// If the i-th bit in rhs is set.
|
||||
if (rhs >> i) & 1 != 0 {
|
||||
// Then xor the result with `self` shifted to the left by i positions.
|
||||
result ^= self << i;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
)*};
|
||||
}
|
||||
|
||||
impl_carryless_mul! {
|
||||
u8, u16, u32, u64, u128, usize
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2178,6 +2178,19 @@ pub const unsafe fn unchecked_funnel_shr<T: [const] fallback::FunnelShift>(
|
|||
unsafe { a.unchecked_funnel_shr(b, shift) }
|
||||
}
|
||||
|
||||
/// Carryless multiply.
|
||||
///
|
||||
/// Safe versions of this intrinsic are available on the integer primitives
|
||||
/// via the `carryless_mul` method. For example, [`u32::carryless_mul`].
|
||||
#[rustc_intrinsic]
|
||||
#[rustc_nounwind]
|
||||
#[rustc_const_unstable(feature = "uint_carryless_mul", issue = "152080")]
|
||||
#[unstable(feature = "uint_carryless_mul", issue = "152080")]
|
||||
#[miri::intrinsic_fallback_is_spec]
|
||||
pub const fn carryless_mul<T: [const] fallback::CarrylessMul>(a: T, b: T) -> T {
|
||||
a.carryless_mul(b)
|
||||
}
|
||||
|
||||
/// This is an implementation detail of [`crate::ptr::read`] and should
|
||||
/// not be used anywhere else. See its comments for why this exists.
|
||||
///
|
||||
|
|
|
|||
|
|
@ -162,6 +162,18 @@ pub const unsafe fn simd_funnel_shl<T>(a: T, b: T, shift: T) -> T;
|
|||
#[rustc_nounwind]
|
||||
pub const unsafe fn simd_funnel_shr<T>(a: T, b: T, shift: T) -> T;
|
||||
|
||||
/// Compute the carry-less product.
|
||||
///
|
||||
/// This is similar to long multiplication except that the carry is discarded.
|
||||
///
|
||||
/// This operation can be used to model multiplication in `GF(2)[X]`, the polynomial
|
||||
/// ring over `GF(2)`.
|
||||
///
|
||||
/// `T` must be a vector of integers.
|
||||
#[rustc_intrinsic]
|
||||
#[rustc_nounwind]
|
||||
pub unsafe fn simd_carryless_mul<T>(a: T, b: T) -> T;
|
||||
|
||||
/// "And"s vectors elementwise.
|
||||
///
|
||||
/// `T` must be a vector of integers.
|
||||
|
|
|
|||
|
|
@ -170,6 +170,7 @@
|
|||
#![feature(trait_alias)]
|
||||
#![feature(transparent_unions)]
|
||||
#![feature(try_blocks)]
|
||||
#![feature(uint_carryless_mul)]
|
||||
#![feature(unboxed_closures)]
|
||||
#![feature(unsized_fn_params)]
|
||||
#![feature(with_negative_coherence)]
|
||||
|
|
|
|||
|
|
@ -244,6 +244,104 @@ macro_rules! midpoint_impl {
|
|||
};
|
||||
}
|
||||
|
||||
macro_rules! widening_carryless_mul_impl {
|
||||
($SelfT:ty, $WideT:ty) => {
|
||||
/// Performs a widening carry-less multiplication.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(uint_carryless_mul)]
|
||||
///
|
||||
#[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.widening_carryless_mul(",
|
||||
stringify!($SelfT), "::MAX), ", stringify!($WideT), "::MAX / 3);")]
|
||||
/// ```
|
||||
#[rustc_const_unstable(feature = "uint_carryless_mul", issue = "152080")]
|
||||
#[doc(alias = "clmul")]
|
||||
#[unstable(feature = "uint_carryless_mul", issue = "152080")]
|
||||
#[must_use = "this returns the result of the operation, \
|
||||
without modifying the original"]
|
||||
#[inline]
|
||||
pub const fn widening_carryless_mul(self, rhs: $SelfT) -> $WideT {
|
||||
(self as $WideT).carryless_mul(rhs as $WideT)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! carrying_carryless_mul_impl {
|
||||
(u128, u256) => {
|
||||
carrying_carryless_mul_impl! { @internal u128 =>
|
||||
pub const fn carrying_carryless_mul(self, rhs: Self, carry: Self) -> (Self, Self) {
|
||||
let x0 = self as u64;
|
||||
let x1 = (self >> 64) as u64;
|
||||
let y0 = rhs as u64;
|
||||
let y1 = (rhs >> 64) as u64;
|
||||
|
||||
let z0 = u64::widening_carryless_mul(x0, y0);
|
||||
let z2 = u64::widening_carryless_mul(x1, y1);
|
||||
|
||||
// The grade school algorithm would compute:
|
||||
// z1 = x0y1 ^ x1y0
|
||||
|
||||
// Instead, Karatsuba first computes:
|
||||
let z3 = u64::widening_carryless_mul(x0 ^ x1, y0 ^ y1);
|
||||
// Since it distributes over XOR,
|
||||
// z3 == x0y0 ^ x0y1 ^ x1y0 ^ x1y1
|
||||
// |--| |---------| |--|
|
||||
// == z0 ^ z1 ^ z2
|
||||
// so we can compute z1 as
|
||||
let z1 = z3 ^ z0 ^ z2;
|
||||
|
||||
let lo = z0 ^ (z1 << 64);
|
||||
let hi = z2 ^ (z1 >> 64);
|
||||
|
||||
(lo ^ carry, hi)
|
||||
}
|
||||
}
|
||||
};
|
||||
($SelfT:ty, $WideT:ty) => {
|
||||
carrying_carryless_mul_impl! { @internal $SelfT =>
|
||||
pub const fn carrying_carryless_mul(self, rhs: Self, carry: Self) -> (Self, Self) {
|
||||
// Can't use widening_carryless_mul because it's not implemented for usize.
|
||||
let p = (self as $WideT).carryless_mul(rhs as $WideT);
|
||||
|
||||
let lo = (p as $SelfT);
|
||||
let hi = (p >> Self::BITS) as $SelfT;
|
||||
|
||||
(lo ^ carry, hi)
|
||||
}
|
||||
}
|
||||
};
|
||||
(@internal $SelfT:ty => $($fn:tt)*) => {
|
||||
/// Calculates the "full carryless multiplication" 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.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Please note that this example is shared among integer types, which is why `u8` is used.
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(uint_carryless_mul)]
|
||||
///
|
||||
/// assert_eq!(0b1000_0000u8.carrying_carryless_mul(0b1000_0000, 0b0000), (0, 0b0100_0000));
|
||||
/// assert_eq!(0b1000_0000u8.carrying_carryless_mul(0b1000_0000, 0b1111), (0b1111, 0b0100_0000));
|
||||
#[doc = concat!("assert_eq!(",
|
||||
stringify!($SelfT), "::MAX.carrying_carryless_mul(", stringify!($SelfT), "::MAX, ", stringify!($SelfT), "::MAX), ",
|
||||
"(!(", stringify!($SelfT), "::MAX / 3), ", stringify!($SelfT), "::MAX / 3));"
|
||||
)]
|
||||
/// ```
|
||||
#[rustc_const_unstable(feature = "uint_carryless_mul", issue = "152080")]
|
||||
#[doc(alias = "clmul")]
|
||||
#[unstable(feature = "uint_carryless_mul", issue = "152080")]
|
||||
#[must_use = "this returns the result of the operation, \
|
||||
without modifying the original"]
|
||||
#[inline]
|
||||
$($fn)*
|
||||
}
|
||||
}
|
||||
|
||||
impl i8 {
|
||||
int_impl! {
|
||||
Self = i8,
|
||||
|
|
@ -458,6 +556,9 @@ impl u8 {
|
|||
fsh_op = "0x36",
|
||||
fshl_result = "0x8",
|
||||
fshr_result = "0x8d",
|
||||
clmul_lhs = "0x12",
|
||||
clmul_rhs = "0x34",
|
||||
clmul_result = "0x28",
|
||||
swap_op = "0x12",
|
||||
swapped = "0x12",
|
||||
reversed = "0x48",
|
||||
|
|
@ -468,6 +569,8 @@ impl u8 {
|
|||
bound_condition = "",
|
||||
}
|
||||
midpoint_impl! { u8, u16, unsigned }
|
||||
widening_carryless_mul_impl! { u8, u16 }
|
||||
carrying_carryless_mul_impl! { u8, u16 }
|
||||
|
||||
/// Checks if the value is within the ASCII range.
|
||||
///
|
||||
|
|
@ -1095,6 +1198,9 @@ impl u16 {
|
|||
fsh_op = "0x2de",
|
||||
fshl_result = "0x30",
|
||||
fshr_result = "0x302d",
|
||||
clmul_lhs = "0x9012",
|
||||
clmul_rhs = "0xcd34",
|
||||
clmul_result = "0x928",
|
||||
swap_op = "0x1234",
|
||||
swapped = "0x3412",
|
||||
reversed = "0x2c48",
|
||||
|
|
@ -1105,6 +1211,8 @@ impl u16 {
|
|||
bound_condition = "",
|
||||
}
|
||||
midpoint_impl! { u16, u32, unsigned }
|
||||
widening_carryless_mul_impl! { u16, u32 }
|
||||
carrying_carryless_mul_impl! { u16, u32 }
|
||||
|
||||
/// Checks if the value is a Unicode surrogate code point, which are disallowed values for [`char`].
|
||||
///
|
||||
|
|
@ -1145,6 +1253,9 @@ impl u32 {
|
|||
fsh_op = "0x2fe78e45",
|
||||
fshl_result = "0xb32f",
|
||||
fshr_result = "0xb32fe78e",
|
||||
clmul_lhs = "0x56789012",
|
||||
clmul_rhs = "0xf52ecd34",
|
||||
clmul_result = "0x9b980928",
|
||||
swap_op = "0x12345678",
|
||||
swapped = "0x78563412",
|
||||
reversed = "0x1e6a2c48",
|
||||
|
|
@ -1155,6 +1266,8 @@ impl u32 {
|
|||
bound_condition = "",
|
||||
}
|
||||
midpoint_impl! { u32, u64, unsigned }
|
||||
widening_carryless_mul_impl! { u32, u64 }
|
||||
carrying_carryless_mul_impl! { u32, u64 }
|
||||
}
|
||||
|
||||
impl u64 {
|
||||
|
|
@ -1171,6 +1284,9 @@ impl u64 {
|
|||
fsh_op = "0x2fe78e45983acd98",
|
||||
fshl_result = "0x6e12fe",
|
||||
fshr_result = "0x6e12fe78e45983ac",
|
||||
clmul_lhs = "0x7890123456789012",
|
||||
clmul_rhs = "0xdd358416f52ecd34",
|
||||
clmul_result = "0xa6299579b980928",
|
||||
swap_op = "0x1234567890123456",
|
||||
swapped = "0x5634129078563412",
|
||||
reversed = "0x6a2c48091e6a2c48",
|
||||
|
|
@ -1181,6 +1297,8 @@ impl u64 {
|
|||
bound_condition = "",
|
||||
}
|
||||
midpoint_impl! { u64, u128, unsigned }
|
||||
widening_carryless_mul_impl! { u64, u128 }
|
||||
carrying_carryless_mul_impl! { u64, u128 }
|
||||
}
|
||||
|
||||
impl u128 {
|
||||
|
|
@ -1197,6 +1315,9 @@ impl u128 {
|
|||
fsh_op = "0x2fe78e45983acd98039000008736273",
|
||||
fshl_result = "0x4f7602fe",
|
||||
fshr_result = "0x4f7602fe78e45983acd9803900000873",
|
||||
clmul_lhs = "0x12345678901234567890123456789012",
|
||||
clmul_rhs = "0x4317e40ab4ddcf05dd358416f52ecd34",
|
||||
clmul_result = "0xb9cf660de35d0c170a6299579b980928",
|
||||
swap_op = "0x12345678901234567890123456789012",
|
||||
swapped = "0x12907856341290785634129078563412",
|
||||
reversed = "0x48091e6a2c48091e6a2c48091e6a2c48",
|
||||
|
|
@ -1209,6 +1330,7 @@ impl u128 {
|
|||
bound_condition = "",
|
||||
}
|
||||
midpoint_impl! { u128, unsigned }
|
||||
carrying_carryless_mul_impl! { u128, u256 }
|
||||
}
|
||||
|
||||
#[cfg(target_pointer_width = "16")]
|
||||
|
|
@ -1223,9 +1345,12 @@ impl usize {
|
|||
rot = 4,
|
||||
rot_op = "0xa003",
|
||||
rot_result = "0x3a",
|
||||
fsh_op = "0x2fe78e45983acd98039000008736273",
|
||||
fshl_result = "0x4f7602fe",
|
||||
fshr_result = "0x4f7602fe78e45983acd9803900000873",
|
||||
fsh_op = "0x2de",
|
||||
fshl_result = "0x30",
|
||||
fshr_result = "0x302d",
|
||||
clmul_lhs = "0x9012",
|
||||
clmul_rhs = "0xcd34",
|
||||
clmul_result = "0x928",
|
||||
swap_op = "0x1234",
|
||||
swapped = "0x3412",
|
||||
reversed = "0x2c48",
|
||||
|
|
@ -1236,6 +1361,7 @@ impl usize {
|
|||
bound_condition = " on 16-bit targets",
|
||||
}
|
||||
midpoint_impl! { usize, u32, unsigned }
|
||||
carrying_carryless_mul_impl! { usize, u32 }
|
||||
}
|
||||
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
|
|
@ -1253,6 +1379,9 @@ impl usize {
|
|||
fsh_op = "0x2fe78e45",
|
||||
fshl_result = "0xb32f",
|
||||
fshr_result = "0xb32fe78e",
|
||||
clmul_lhs = "0x56789012",
|
||||
clmul_rhs = "0xf52ecd34",
|
||||
clmul_result = "0x9b980928",
|
||||
swap_op = "0x12345678",
|
||||
swapped = "0x78563412",
|
||||
reversed = "0x1e6a2c48",
|
||||
|
|
@ -1263,6 +1392,7 @@ impl usize {
|
|||
bound_condition = " on 32-bit targets",
|
||||
}
|
||||
midpoint_impl! { usize, u64, unsigned }
|
||||
carrying_carryless_mul_impl! { usize, u64 }
|
||||
}
|
||||
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
|
|
@ -1280,6 +1410,9 @@ impl usize {
|
|||
fsh_op = "0x2fe78e45983acd98",
|
||||
fshl_result = "0x6e12fe",
|
||||
fshr_result = "0x6e12fe78e45983ac",
|
||||
clmul_lhs = "0x7890123456789012",
|
||||
clmul_rhs = "0xdd358416f52ecd34",
|
||||
clmul_result = "0xa6299579b980928",
|
||||
swap_op = "0x1234567890123456",
|
||||
swapped = "0x5634129078563412",
|
||||
reversed = "0x6a2c48091e6a2c48",
|
||||
|
|
@ -1290,6 +1423,7 @@ impl usize {
|
|||
bound_condition = " on 64-bit targets",
|
||||
}
|
||||
midpoint_impl! { usize, u128, unsigned }
|
||||
carrying_carryless_mul_impl! { usize, u128 }
|
||||
}
|
||||
|
||||
impl usize {
|
||||
|
|
|
|||
|
|
@ -17,6 +17,9 @@ macro_rules! uint_impl {
|
|||
fsh_op = $fsh_op:literal,
|
||||
fshl_result = $fshl_result:literal,
|
||||
fshr_result = $fshr_result:literal,
|
||||
clmul_lhs = $clmul_rhs:literal,
|
||||
clmul_rhs = $clmul_lhs:literal,
|
||||
clmul_result = $clmul_result:literal,
|
||||
swap_op = $swap_op:literal,
|
||||
swapped = $swapped:literal,
|
||||
reversed = $reversed:literal,
|
||||
|
|
@ -482,6 +485,62 @@ macro_rules! uint_impl {
|
|||
unsafe { intrinsics::unchecked_funnel_shr(self, rhs, n) }
|
||||
}
|
||||
|
||||
/// Performs a carry-less multiplication, returning the lower bits.
|
||||
///
|
||||
/// This operation is similar to long multiplication, except that exclusive or is used
|
||||
/// instead of addition. The implementation is equivalent to:
|
||||
///
|
||||
/// ```no_run
|
||||
#[doc = concat!("pub fn carryless_mul(lhs: ", stringify!($SelfT), ", rhs: ", stringify!($SelfT), ") -> ", stringify!($SelfT), "{")]
|
||||
/// let mut retval = 0;
|
||||
#[doc = concat!(" for i in 0..", stringify!($SelfT), "::BITS {")]
|
||||
/// if (rhs >> i) & 1 != 0 {
|
||||
/// // long multiplication would use +=
|
||||
/// retval ^= lhs << i;
|
||||
/// }
|
||||
/// }
|
||||
/// retval
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// The actual implementation is more efficient, and on some platforms lowers directly to a
|
||||
/// dedicated instruction.
|
||||
///
|
||||
/// # Uses
|
||||
///
|
||||
/// Carryless multiplication can be used to turn a bitmask of quote characters into a
|
||||
/// bit mask of characters surrounded by quotes:
|
||||
///
|
||||
/// ```no_run
|
||||
/// r#"abc xxx "foobar" zzz "a"!"#; // input string
|
||||
/// 0b0000000010000001000001010; // quote_mask
|
||||
/// 0b0000000001111110000000100; // quote_mask.carryless_mul(!0) & !quote_mask
|
||||
/// ```
|
||||
///
|
||||
/// Another use is in cryptography, where carryless multiplication allows for efficient
|
||||
/// implementations of polynomial multiplication in `GF(2)[X]`, the polynomial ring
|
||||
/// over `GF(2)`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(uint_carryless_mul)]
|
||||
///
|
||||
#[doc = concat!("let a = ", $clmul_lhs, stringify!($SelfT), ";")]
|
||||
#[doc = concat!("let b = ", $clmul_rhs, stringify!($SelfT), ";")]
|
||||
///
|
||||
#[doc = concat!("assert_eq!(a.carryless_mul(b), ", $clmul_result, ");")]
|
||||
/// ```
|
||||
#[rustc_const_unstable(feature = "uint_carryless_mul", issue = "152080")]
|
||||
#[doc(alias = "clmul")]
|
||||
#[unstable(feature = "uint_carryless_mul", issue = "152080")]
|
||||
#[must_use = "this returns the result of the operation, \
|
||||
without modifying the original"]
|
||||
#[inline(always)]
|
||||
pub const fn carryless_mul(self, rhs: Self) -> Self {
|
||||
intrinsics::carryless_mul(self, rhs)
|
||||
}
|
||||
|
||||
/// Reverses the byte order of the integer.
|
||||
///
|
||||
/// # Examples
|
||||
|
|
|
|||
|
|
@ -116,6 +116,7 @@
|
|||
#![feature(try_trait_v2)]
|
||||
#![feature(type_info)]
|
||||
#![feature(uint_bit_width)]
|
||||
#![feature(uint_carryless_mul)]
|
||||
#![feature(uint_gather_scatter_bits)]
|
||||
#![feature(unsize)]
|
||||
#![feature(unwrap_infallible)]
|
||||
|
|
|
|||
254
library/coretests/tests/num/carryless_mul.rs
Normal file
254
library/coretests/tests/num/carryless_mul.rs
Normal file
|
|
@ -0,0 +1,254 @@
|
|||
//! Tests the `Unsigned::{carryless_mul, widening_carryless_mul, carrying_carryless_mul}` methods.
|
||||
|
||||
#[test]
|
||||
fn carryless_mul_u128() {
|
||||
assert_eq_const_safe!(u128: <u128>::carryless_mul(0, 0), 0);
|
||||
assert_eq_const_safe!(u128: <u128>::carryless_mul(1, 1), 1);
|
||||
|
||||
assert_eq_const_safe!(
|
||||
u128: <u128>::carryless_mul(
|
||||
0x0123456789ABCDEF_FEDCBA9876543210,
|
||||
1u128 << 64,
|
||||
),
|
||||
0xFEDCBA9876543210_0000000000000000
|
||||
);
|
||||
|
||||
assert_eq_const_safe!(
|
||||
u128: <u128>::carryless_mul(
|
||||
0x0123456789ABCDEF_FEDCBA9876543210,
|
||||
(1u128 << 64) | 1,
|
||||
),
|
||||
0xFFFFFFFFFFFFFFFF_FEDCBA9876543210
|
||||
);
|
||||
|
||||
assert_eq_const_safe!(
|
||||
u128: <u128>::carryless_mul(
|
||||
0x0123456789ABCDEF_FEDCBA9876543211,
|
||||
1u128 << 127,
|
||||
),
|
||||
0x8000000000000000_0000000000000000
|
||||
);
|
||||
|
||||
assert_eq_const_safe!(
|
||||
u128: <u128>::carryless_mul(
|
||||
0xAAAAAAAAAAAAAAAA_AAAAAAAAAAAAAAAA,
|
||||
0x5555555555555555_5555555555555555,
|
||||
),
|
||||
0x2222222222222222_2222222222222222
|
||||
);
|
||||
|
||||
assert_eq_const_safe!(
|
||||
u128: <u128>::carryless_mul(
|
||||
(1 << 127) | (1 << 64) | 1,
|
||||
(1 << 63) | 1
|
||||
),
|
||||
(1 << 64) | (1 << 63) | 1
|
||||
);
|
||||
|
||||
assert_eq_const_safe!(
|
||||
u128: <u128>::carryless_mul(
|
||||
0x8000000000000000_0000000000000001,
|
||||
0x7FFFFFFFFFFFFFFF_FFFFFFFFFFFFFFFF,
|
||||
),
|
||||
0xFFFFFFFFFFFFFFFF_FFFFFFFFFFFFFFFF
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn carryless_mul_u64() {
|
||||
assert_eq_const_safe!(u64: <u64>::carryless_mul(0, 0), 0);
|
||||
assert_eq_const_safe!(u64: <u64>::carryless_mul(1, 1), 1);
|
||||
|
||||
assert_eq_const_safe!(
|
||||
u64: <u64>::carryless_mul(
|
||||
0x0123_4567_89AB_CDEF,
|
||||
1u64 << 32,
|
||||
),
|
||||
0x89AB_CDEF_0000_0000
|
||||
);
|
||||
|
||||
assert_eq_const_safe!(
|
||||
u64: <u64>::carryless_mul(
|
||||
0x0123_4567_89AB_CDEF,
|
||||
(1u64 << 32) | 1,
|
||||
),
|
||||
0x8888_8888_89AB_CDEF
|
||||
);
|
||||
|
||||
assert_eq_const_safe!(
|
||||
u64: <u64>::carryless_mul(
|
||||
0x0123_4567_89AB_CDEF,
|
||||
1u64 << 63,
|
||||
),
|
||||
0x8000_0000_0000_0000
|
||||
);
|
||||
|
||||
assert_eq_const_safe!(
|
||||
u64: <u64>::carryless_mul(
|
||||
0xAAAA_AAAA_AAAA_AAAA,
|
||||
0x5555_5555_5555_5555,
|
||||
),
|
||||
0x2222_2222_2222_2222
|
||||
);
|
||||
|
||||
assert_eq_const_safe!(
|
||||
u64: <u64>::carryless_mul(
|
||||
(1u64 << 63) | (1u64 << 32) | 1,
|
||||
(1u64 << 31) | 1,
|
||||
),
|
||||
(1u64 << 32) | (1u64 << 31) | 1
|
||||
);
|
||||
|
||||
assert_eq_const_safe!(
|
||||
u64: <u64>::carryless_mul(
|
||||
0x8000_0000_0000_0001,
|
||||
0x7FFF_FFFF_FFFF_FFFF,
|
||||
),
|
||||
0xFFFF_FFFF_FFFF_FFFF
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn carryless_mul_u32() {
|
||||
assert_eq_const_safe!(
|
||||
u32: <u32>::carryless_mul(0x0123_4567, 1u32 << 16),
|
||||
0x4567_0000
|
||||
);
|
||||
|
||||
assert_eq_const_safe!(
|
||||
u32: <u32>::carryless_mul(0xAAAA_AAAA, 0x5555_5555),
|
||||
0x2222_2222
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn carryless_mul_u16() {
|
||||
assert_eq_const_safe!(
|
||||
u16: <u16>::carryless_mul(0x0123, 1u16 << 8),
|
||||
0x2300
|
||||
);
|
||||
|
||||
assert_eq_const_safe!(
|
||||
u16: <u16>::carryless_mul(0xAAAA, 0x5555),
|
||||
0x2222
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn carryless_mul_u8() {
|
||||
assert_eq_const_safe!(
|
||||
u8: <u8>::carryless_mul(0x01, 1u8 << 4),
|
||||
0x10
|
||||
);
|
||||
|
||||
assert_eq_const_safe!(
|
||||
u8: <u8>::carryless_mul(0xAA, 0x55),
|
||||
0x22
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn widening_carryless_mul() {
|
||||
assert_eq_const_safe!(
|
||||
u16: <u8>::widening_carryless_mul(0xEFu8, 1u8 << 7),
|
||||
0x7780u16
|
||||
);
|
||||
assert_eq_const_safe!(
|
||||
u16: <u8>::widening_carryless_mul(0xEFu8, (1u8 << 7) | 1),
|
||||
0x776Fu16
|
||||
);
|
||||
|
||||
assert_eq_const_safe!(
|
||||
u32: <u16>::widening_carryless_mul(0xBEEFu16, 1u16 << 15),
|
||||
0x5F77_8000u32
|
||||
);
|
||||
assert_eq_const_safe!(
|
||||
u32: <u16>::widening_carryless_mul(0xBEEFu16, (1u16 << 15) | 1),
|
||||
0x5F77_3EEFu32
|
||||
);
|
||||
|
||||
assert_eq_const_safe!(
|
||||
u64: <u32>::widening_carryless_mul(0xDEAD_BEEFu32, 1u32 << 31),
|
||||
0x6F56_DF77_8000_0000u64
|
||||
);
|
||||
assert_eq_const_safe!(
|
||||
u64: <u32>::widening_carryless_mul(0xDEAD_BEEFu32, (1u32 << 31) | 1),
|
||||
0x6F56_DF77_5EAD_BEEFu64
|
||||
);
|
||||
|
||||
assert_eq_const_safe!(
|
||||
u128: <u64>::widening_carryless_mul(0xDEAD_BEEF_FACE_FEEDu64, 1u64 << 63),
|
||||
147995377545877439359040026616086396928
|
||||
|
||||
);
|
||||
assert_eq_const_safe!(
|
||||
u128: <u64>::widening_carryless_mul(0xDEAD_BEEF_FACE_FEEDu64, (1u64 << 63) | 1),
|
||||
147995377545877439356638973527682121453
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn carrying_carryless_mul() {
|
||||
assert_eq_const_safe!(
|
||||
(u8, u8): <u8>::carrying_carryless_mul(0xEFu8, 1u8 << 7, 0),
|
||||
(0x80u8, 0x77u8)
|
||||
);
|
||||
assert_eq_const_safe!(
|
||||
(u8, u8): <u8>::carrying_carryless_mul(0xEFu8, (1u8 << 7) | 1, 0xEF),
|
||||
(0x80u8, 0x77u8)
|
||||
);
|
||||
|
||||
assert_eq_const_safe!(
|
||||
(u16, u16): <u16>::carrying_carryless_mul(0xBEEFu16, 1u16 << 15, 0),
|
||||
(0x8000u16, 0x5F77u16)
|
||||
);
|
||||
assert_eq_const_safe!(
|
||||
(u16, u16): <u16>::carrying_carryless_mul(0xBEEFu16, (1u16 << 15) | 1, 0xBEEF),
|
||||
(0x8000u16, 0x5F77u16)
|
||||
);
|
||||
|
||||
assert_eq_const_safe!(
|
||||
(u32, u32): <u32>::carrying_carryless_mul(0xDEAD_BEEFu32, 1u32 << 31, 0),
|
||||
(0x8000_0000u32, 0x6F56_DF77u32)
|
||||
);
|
||||
assert_eq_const_safe!(
|
||||
(u32, u32): <u32>::carrying_carryless_mul(0xDEAD_BEEFu32, (1u32 << 31) | 1, 0xDEAD_BEEF),
|
||||
(0x8000_0000u32, 0x6F56_DF77u32)
|
||||
);
|
||||
|
||||
assert_eq_const_safe!(
|
||||
(u64, u64): <u64>::carrying_carryless_mul(0xDEAD_BEEF_FACE_FEEDu64, 1u64 << 63, 0),
|
||||
(9223372036854775808, 8022845492652638070)
|
||||
);
|
||||
assert_eq_const_safe!(
|
||||
(u64, u64): <u64>::carrying_carryless_mul(
|
||||
0xDEAD_BEEF_FACE_FEEDu64,
|
||||
(1u64 << 63) | 1,
|
||||
0xDEAD_BEEF_FACE_FEED,
|
||||
),
|
||||
(9223372036854775808, 8022845492652638070)
|
||||
);
|
||||
|
||||
assert_eq_const_safe!(
|
||||
(u128, u128): <u128>::carrying_carryless_mul(
|
||||
0xDEAD_BEEF_FACE_FEED_0123_4567_89AB_CDEFu128,
|
||||
1u128 << 127,
|
||||
0,
|
||||
),
|
||||
(
|
||||
0x8000_0000_0000_0000_0000_0000_0000_0000u128,
|
||||
147995377545877439359081019380694640375,
|
||||
)
|
||||
);
|
||||
assert_eq_const_safe!(
|
||||
(u128, u128): <u128>::carrying_carryless_mul(
|
||||
0xDEAD_BEEF_FACE_FEED_0123_4567_89AB_CDEFu128,
|
||||
(1u128 << 127) | 1,
|
||||
0xDEAD_BEEF_FACE_FEED_0123_4567_89AB_CDEF,
|
||||
),
|
||||
(
|
||||
0x8000_0000_0000_0000_0000_0000_0000_0000u128,
|
||||
147995377545877439359081019380694640375,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
@ -22,6 +22,7 @@ mod u64;
|
|||
mod u8;
|
||||
|
||||
mod bignum;
|
||||
mod carryless_mul;
|
||||
mod const_from;
|
||||
mod dec2flt;
|
||||
mod float_iter_sum_identity;
|
||||
|
|
|
|||
|
|
@ -117,6 +117,13 @@ macro_rules! uint_module {
|
|||
assert_eq_const_safe!($T: <$T>::funnel_shr(_1, _1, 4), <$T>::rotate_right(_1, 4));
|
||||
}
|
||||
|
||||
fn test_carryless_mul() {
|
||||
assert_eq_const_safe!($T: <$T>::carryless_mul(0, 0), 0);
|
||||
assert_eq_const_safe!($T: <$T>::carryless_mul(1, 1), 1);
|
||||
|
||||
assert_eq_const_safe!($T: <$T>::carryless_mul(0b0100, 2), 0b1000);
|
||||
}
|
||||
|
||||
fn test_swap_bytes() {
|
||||
assert_eq_const_safe!($T: A.swap_bytes().swap_bytes(), A);
|
||||
assert_eq_const_safe!($T: B.swap_bytes().swap_bytes(), B);
|
||||
|
|
|
|||
|
|
@ -315,6 +315,7 @@
|
|||
#![feature(try_blocks)]
|
||||
#![feature(try_trait_v2)]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
#![feature(uint_carryless_mul)]
|
||||
// tidy-alphabetical-end
|
||||
//
|
||||
// Library features (core):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue