diff --git a/src/traits.rs b/src/traits.rs index da9b9b64..b5000014 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -187,26 +187,49 @@ pub trait CheckedSub: Sized { fn checked_sub(&self, rhs: Rhs) -> CtOption; } -/// Concatenate two numbers into a "wide" twice-width value, using the `rhs` +/// Concatenate two numbers into a "wide" double-width value, using the `lo` /// value as the least significant value. -pub trait Concat { +pub trait Concat: ConcatMixed { /// Concatenated output: twice the width of `Self`. type Output; - /// Concatenate the two values, with `self` as most significant and `rhs` + /// Concatenate the two halves, with `self` as most significant and `lo` /// as the least significant. - fn concat(&self, rhs: &Self) -> Self::Output; + fn concat(&self, lo: &Self) -> Self::Output { + self.concat_mixed(lo) + } +} + +/// Concatenate two numbers into a "wide" combined-width value, using the `lo` +/// value as the least significant value. +pub trait ConcatMixed { + /// Concatenated output: combination of `Lo` and `Self`. + type MixedOutput; + + /// Concatenate the two values, with `self` as most significant and `lo` + /// as the least significant. + fn concat_mixed(&self, lo: &Lo) -> Self::MixedOutput; } /// Split a number in half, returning the most significant half followed by /// the least significant. -pub trait Split { +pub trait Split: SplitMixed { /// Split output: high/low components of the value. type Output; /// Split this number in half, returning its high and low components /// respectively. - fn split(&self) -> (Self::Output, Self::Output); + fn split(&self) -> (Self::Output, Self::Output) { + self.split_mixed() + } +} + +/// Split a number into parts, returning the most significant part followed by +/// the least significant. +pub trait SplitMixed { + /// Split this number into parts, returning its high and low components + /// respectively. + fn split_mixed(&self) -> (Hi, Lo); } /// Integers whose representation takes a bounded amount of space. diff --git a/src/uint.rs b/src/uint.rs index b1ed8766..db4bb935 100644 --- a/src/uint.rs +++ b/src/uint.rs @@ -7,9 +7,7 @@ )] #[macro_use] -mod concat; -#[macro_use] -mod split; +mod macros; mod add; mod add_mod; @@ -19,6 +17,7 @@ mod bit_or; mod bit_xor; mod bits; mod cmp; +mod concat; mod div; pub(crate) mod div_limb; mod encoding; @@ -31,6 +30,7 @@ mod neg_mod; mod resize; mod shl; mod shr; +mod split; mod sqrt; mod sub; mod sub_mod; @@ -44,7 +44,7 @@ mod array; #[cfg(feature = "rand_core")] mod rand; -use crate::{Bounded, Concat, Encoding, Integer, Limb, Split, Word, Zero}; +use crate::{Bounded, Encoding, Integer, Limb, Word, Zero}; use core::fmt; use subtle::{Choice, ConditionallySelectable}; @@ -295,46 +295,6 @@ where #[cfg(feature = "zeroize")] impl DefaultIsZeroes for Uint {} -// TODO(tarcieri): use `const_evaluatable_checked` when stable to make generic around bits. -macro_rules! impl_uint_aliases { - ($(($name:ident, $bits:expr, $doc:expr)),+) => { - $( - #[doc = $doc] - #[doc="unsigned big integer."] - pub type $name = Uint<{nlimbs!($bits)}>; - - impl Encoding for $name { - - type Repr = [u8; $bits / 8]; - - #[inline] - fn from_be_bytes(bytes: Self::Repr) -> Self { - Self::from_be_slice(&bytes) - } - - #[inline] - fn from_le_bytes(bytes: Self::Repr) -> Self { - Self::from_le_slice(&bytes) - } - - #[inline] - fn to_be_bytes(&self) -> Self::Repr { - let mut result = [0u8; $bits / 8]; - self.write_be_bytes(&mut result); - result - } - - #[inline] - fn to_le_bytes(&self) -> Self::Repr { - let mut result = [0u8; $bits / 8]; - self.write_le_bytes(&mut result); - result - } - } - )+ - }; -} - // TODO(tarcieri): use `const_evaluatable_checked` when stable to make generic around bits. impl_uint_aliases! { (U64, 64, "64-bit"), @@ -347,8 +307,11 @@ impl_uint_aliases! { (U512, 512, "512-bit"), (U576, 576, "576-bit"), (U640, 640, "640-bit"), + (U704, 704, "704-bit"), (U768, 768, "768-bit"), + (U832, 832, "832-bit"), (U896, 896, "896-bit"), + (U960, 960, "960-bit"), (U1024, 1024, "1024-bit"), (U1280, 1280, "1280-bit"), (U1536, 1536, "1536-bit"), @@ -369,50 +332,55 @@ impl_uint_aliases! { (U544, 544, "544-bit") // For NIST P-521 } -// TODO(tarcieri): use `const_evaluatable_checked` when stable to make generic around bits. -impl_concat! { - (U64, 64), - (U128, 128), - (U192, 192), - (U256, 256), - (U320, 320), - (U384, 384), - (U448, 448), - (U512, 512), - (U640, 640), - (U768, 768), - (U896, 896), - (U1024, 1024), - (U1536, 1536), - (U1792, 1792), - (U2048, 2048), - (U3072, 3072), - (U4096, 4096), - (U4224, 4224), - (U4352, 4352) +#[cfg(target_pointer_width = "32")] +impl_uint_concat_split_even! { + U64, } -// TODO(tarcieri): use `const_evaluatable_checked` when stable to make generic around bits. -impl_split! { - (U128, 128), - (U256, 256), - (U384, 384), - (U512, 512), - (U640, 640), - (U768, 768), - (U896, 896), - (U1024, 1024), - (U1280, 1280), - (U1536, 1536), - (U1792, 1792), - (U2048, 2048), - (U3072, 3072), - (U3584, 3584), - (U4096, 4096), - (U4224, 4224), - (U4352, 4352), - (U6144, 6144), - (U8192, 8192) +// Implement concat and split for double-width Uint sizes: these should be +// multiples of 128 bits. +impl_uint_concat_split_even! { + U128, + U256, + U384, + U512, + U640, + U768, + U896, + U1024, + U1280, + U1536, + U1792, + U2048, + U3072, + U3584, + U4096, + U4224, + U4352, + U6144, + U8192, +} + +// Implement mixed concat and split for combinations not implemented by +// impl_uint_concat_split_even. The numbers represent the size of each +// component Uint in multiple of 64 bits. For example, +// (U256, [1, 3]) will allow splitting U256 into (U64, U192) as well as +// (U192, U64), while the (U128, U128) combination is already covered. +impl_uint_concat_split_mixed! { + (U192, [1, 2]), + (U256, [1, 3]), + (U320, [1, 2, 3, 4]), + (U384, [1, 2, 4, 5]), + (U448, [1, 2, 3, 4, 5, 6]), + (U512, [1, 2, 3, 5, 6, 7]), + (U576, [1, 2, 3, 4, 5, 6, 7, 8]), + (U640, [1, 2, 3, 4, 6, 7, 8, 9]), + (U704, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]), + (U768, [1, 2, 3, 4, 5, 7, 8, 9, 10, 11]), + (U832, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]), + (U896, [1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13]), + (U960, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]), + (U1024, [1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15]), } #[cfg(feature = "extra-sizes")] diff --git a/src/uint/concat.rs b/src/uint/concat.rs index 8b53401d..dde5242a 100644 --- a/src/uint/concat.rs +++ b/src/uint/concat.rs @@ -1,52 +1,39 @@ -// TODO(tarcieri): use `const_evaluatable_checked` when stable to make generic around bits. -macro_rules! impl_concat { - ($(($name:ident, $bits:expr)),+) => { - $( - impl $name { - /// Concatenate the two values, with `self` as most significant and `rhs` - /// as the least significant. - pub const fn concat(&self, rhs: &Self) -> Uint<{nlimbs!($bits) * 2}> { - let mut limbs = [Limb::ZERO; nlimbs!($bits) * 2]; - let mut i = 0; - let mut j = 0; +use crate::{Concat, ConcatMixed, Limb, Uint}; - while j < nlimbs!($bits) { - limbs[i] = rhs.limbs[j]; - i += 1; - j += 1; - } - - j = 0; - while j < nlimbs!($bits) { - limbs[i] = self.limbs[j]; - i += 1; - j += 1; - } - - Uint { limbs } - } - } +impl Concat for T +where + T: ConcatMixed, +{ + type Output = Self::MixedOutput; +} - impl Concat for $name { - type Output = Uint<{nlimbs!($bits) * 2}>; +/// Concatenate the two values, with `lo` as least significant and `hi` +/// as the most significant. +#[inline] +pub(crate) const fn concat_mixed( + lo: &Uint, + hi: &Uint, +) -> Uint { + let top = L + H; + let top = if top < O { top } else { O }; + let mut limbs = [Limb::ZERO; O]; + let mut i = 0; - fn concat(&self, rhs: &Self) -> Self::Output { - self.concat(rhs) - } - } + while i < top { + if i < L { + limbs[i] = lo.limbs[i]; + } else { + limbs[i] = hi.limbs[i - L]; + } + i += 1; + } - impl From<($name, $name)> for Uint<{nlimbs!($bits) * 2}> { - fn from(nums: ($name, $name)) -> Uint<{nlimbs!($bits) * 2}> { - nums.1.concat(&nums.0) - } - } - )+ - }; + Uint { limbs } } #[cfg(test)] mod tests { - use crate::{U128, U64}; + use crate::{ConcatMixed, U128, U192, U64}; #[test] fn concat() { @@ -58,6 +45,20 @@ mod tests { ); } + #[test] + fn concat_mixed() { + let a = U64::from_u64(0x0011223344556677); + let b = U128::from_u128(0x8899aabbccddeeff_8899aabbccddeeff); + assert_eq!( + a.concat_mixed(&b), + U192::from_be_hex("00112233445566778899aabbccddeeff8899aabbccddeeff") + ); + assert_eq!( + b.concat_mixed(&a), + U192::from_be_hex("8899aabbccddeeff8899aabbccddeeff0011223344556677") + ); + } + #[test] fn convert() { let res: U128 = U64::ONE.mul_wide(&U64::ONE).into(); diff --git a/src/uint/extra_sizes.rs b/src/uint/extra_sizes.rs index 0d5d00fd..fb639c71 100644 --- a/src/uint/extra_sizes.rs +++ b/src/uint/extra_sizes.rs @@ -8,9 +8,6 @@ use super::*; impl_uint_aliases! { - (U704, 704, "704-bit"), - (U832, 832, "832-bit"), - (U960, 960, "960-bit"), (U1088, 1088, "1088-bit"), (U1152, 1152, "1152-bit"), (U1216, 1216, "1216-bit"), @@ -114,162 +111,50 @@ impl_uint_aliases! { (U8128, 8128, "8128-bit") } -impl_concat! { - (U576, 576), - (U704, 704), - (U832, 832), - (U960, 960), - (U1088, 1088), - (U1152, 1152), - (U1216, 1216), - (U1280, 1280), - (U1344, 1344), - (U1408, 1408), - (U1472, 1472), - (U1600, 1600), - (U1664, 1664), - (U1728, 1728), - (U1856, 1856), - (U1920, 1920), - (U1984, 1984), - (U2112, 2112), - (U2176, 2176), - (U2240, 2240), - (U2304, 2304), - (U2368, 2368), - (U2432, 2432), - (U2496, 2496), - (U2560, 2560), - (U2624, 2624), - (U2688, 2688), - (U2752, 2752), - (U2816, 2816), - (U2880, 2880), - (U2944, 2944), - (U3008, 3008), - (U3136, 3136), - (U3200, 3200), - (U3264, 3264), - (U3328, 3328), - (U3392, 3392), - (U3456, 3456), - (U3520, 3520), - (U3584, 3584), - (U3648, 3648), - (U3712, 3712), - (U3776, 3776), - (U3840, 3840), - (U3904, 3904), - (U3968, 3968), - (U4032, 4032), - (U4160, 4160), - (U4288, 4288), - (U4416, 4416), - (U4480, 4480), - (U4544, 4544), - (U4608, 4608), - (U4672, 4672), - (U4736, 4736), - (U4800, 4800), - (U4864, 4864), - (U4928, 4928), - (U4992, 4992), - (U5056, 5056), - (U5120, 5120), - (U5184, 5184), - (U5248, 5248), - (U5312, 5312), - (U5376, 5376), - (U5440, 5440), - (U5504, 5504), - (U5568, 5568), - (U5632, 5632), - (U5696, 5696), - (U5760, 5760), - (U5824, 5824), - (U5888, 5888), - (U5952, 5952), - (U6016, 6016), - (U6080, 6080), - (U6144, 6144), - (U6208, 6208), - (U6272, 6272), - (U6336, 6336), - (U6400, 6400), - (U6464, 6464), - (U6528, 6528), - (U6592, 6592), - (U6656, 6656), - (U6720, 6720), - (U6784, 6784), - (U6848, 6848), - (U6912, 6912), - (U6976, 6976), - (U7040, 7040), - (U7104, 7104), - (U7168, 7168), - (U7232, 7232), - (U7296, 7296), - (U7360, 7360), - (U7424, 7424), - (U7488, 7488), - (U7552, 7552), - (U7616, 7616), - (U7680, 7680), - (U7744, 7744), - (U7808, 7808), - (U7872, 7872), - (U7936, 7936), - (U8000, 8000), - (U8064, 8064), - (U8128, 8128), - (U8192, 8192) -} - -impl_split! { - (U1152, 1152), - (U1408, 1408), - (U1664, 1664), - (U1920, 1920), - (U2176, 2176), - (U2304, 2304), - (U2432, 2432), - (U2560, 2560), - (U2688, 2688), - (U2816, 2816), - (U2944, 2944), - (U3200, 3200), - (U3328, 3328), - (U3456, 3456), - (U3712, 3712), - (U3840, 3840), - (U3968, 3968), - (U4480, 4480), - (U4608, 4608), - (U4736, 4736), - (U4864, 4864), - (U4992, 4992), - (U5120, 5120), - (U5248, 5248), - (U5376, 5376), - (U5504, 5504), - (U5632, 5632), - (U5760, 5760), - (U5888, 5888), - (U6016, 6016), - (U6272, 6272), - (U6400, 6400), - (U6528, 6528), - (U6656, 6656), - (U6784, 6784), - (U6912, 6912), - (U7040, 7040), - (U7168, 7168), - (U7296, 7296), - (U7424, 7424), - (U7552, 7552), - (U7680, 7680), - (U7808, 7808), - (U7936, 7936), - (U8064, 8064) +impl_uint_concat_split_even! { + U1152, + U1408, + U1664, + U1920, + U2176, + U2304, + U2432, + U2560, + U2688, + U2816, + U2944, + U3200, + U3328, + U3456, + U3712, + U3840, + U3968, + U4480, + U4608, + U4736, + U4864, + U4992, + U5120, + U5248, + U5376, + U5504, + U5632, + U5760, + U5888, + U6016, + U6272, + U6400, + U6528, + U6656, + U6784, + U6912, + U7040, + U7168, + U7296, + U7424, + U7552, + U7680, + U7808, + U7936, + U8064, } diff --git a/src/uint/from.rs b/src/uint/from.rs index ac02f2ef..0f1bfbca 100644 --- a/src/uint/from.rs +++ b/src/uint/from.rs @@ -1,6 +1,6 @@ //! `From`-like conversions for [`Uint`]. -use crate::{Limb, Uint, WideWord, Word, U128, U64}; +use crate::{ConcatMixed, Limb, Uint, WideWord, Word, U128, U64}; impl Uint { /// Create a [`Uint`] from a `u8` (const-friendly) @@ -156,8 +156,13 @@ impl From for u64 { impl From for u128 { fn from(n: U128) -> u128 { - let (hi, lo) = n.split(); - (u64::from(hi) as u128) << 64 | (u64::from(lo) as u128) + let mut i = U128::LIMBS - 1; + let mut res = n.limbs[i].0 as u128; + while i > 0 { + i -= 1; + res = (res << Limb::BITS) | (n.limbs[i].0 as u128); + } + res } } @@ -191,6 +196,36 @@ impl From for Uint { } } +impl From<(Uint, Uint)> for Uint +where + Uint: ConcatMixed, MixedOutput = Uint>, +{ + fn from(nums: (Uint, Uint)) -> Uint { + nums.1.concat_mixed(&nums.0) + } +} + +impl From<&(Uint, Uint)> for Uint +where + Uint: ConcatMixed, MixedOutput = Uint>, +{ + fn from(nums: &(Uint, Uint)) -> Uint { + nums.1.concat_mixed(&nums.0) + } +} + +impl From> for (Uint, Uint) { + fn from(num: Uint) -> (Uint, Uint) { + crate::uint::split::split_mixed(&num) + } +} + +impl From<&Uint> for Uint { + fn from(num: &Uint) -> Uint { + num.resize() + } +} + #[cfg(test)] mod tests { use crate::{Limb, Word, U128}; diff --git a/src/uint/macros.rs b/src/uint/macros.rs new file mode 100644 index 00000000..fd253d87 --- /dev/null +++ b/src/uint/macros.rs @@ -0,0 +1,115 @@ +// TODO(tarcieri): use `const_evaluatable_checked` when stable to make generic around bits. +macro_rules! impl_uint_aliases { + ($(($name:ident, $bits:expr, $doc:expr)),+) => { + $( + #[doc = $doc] + #[doc="unsigned big integer."] + pub type $name = Uint<{nlimbs!($bits)}>; + + impl Encoding for $name { + + type Repr = [u8; $bits / 8]; + + #[inline] + fn from_be_bytes(bytes: Self::Repr) -> Self { + Self::from_be_slice(&bytes) + } + + #[inline] + fn from_le_bytes(bytes: Self::Repr) -> Self { + Self::from_le_slice(&bytes) + } + + #[inline] + fn to_be_bytes(&self) -> Self::Repr { + let mut result = [0u8; $bits / 8]; + self.write_be_bytes(&mut result); + result + } + + #[inline] + fn to_le_bytes(&self) -> Self::Repr { + let mut result = [0u8; $bits / 8]; + self.write_le_bytes(&mut result); + result + } + } + )+ + }; +} + +macro_rules! impl_uint_concat_split_mixed { + ($name:ident, $size:literal) => { + impl $crate::traits::ConcatMixed> for Uint<{ <$name>::LIMBS - U64::LIMBS * $size }> + { + type MixedOutput = $name; + + fn concat_mixed(&self, lo: &Uint<{ U64::LIMBS * $size }>) -> Self::MixedOutput { + $crate::uint::concat::concat_mixed(lo, self) + } + } + + impl $crate::traits::SplitMixed, Uint<{ <$name>::LIMBS - U64::LIMBS * $size }>> for $name + { + fn split_mixed(&self) -> (Uint<{ U64::LIMBS * $size }>, Uint<{ <$name>::LIMBS - U64::LIMBS * $size }>) { + $crate::uint::split::split_mixed(self) + } + } + }; + ($name:ident, [ $($size:literal),+ ]) => { + $( + impl_uint_concat_split_mixed!($name, $size); + )+ + }; + ($( ($name:ident, $sizes:tt), )+) => { + $( + impl_uint_concat_split_mixed!($name, $sizes); + )+ + }; +} + +macro_rules! impl_uint_concat_split_even { + ($name:ident) => { + impl $crate::traits::ConcatMixed::LIMBS / 2 }>> for Uint<{ <$name>::LIMBS / 2 }> + { + type MixedOutput = $name; + + fn concat_mixed(&self, lo: &Uint<{ <$name>::LIMBS / 2 }>) -> Self::MixedOutput { + $crate::uint::concat::concat_mixed(lo, self) + } + } + + impl Uint<{ <$name>::LIMBS / 2 }> { + /// Concatenate the two values, with `self` as most significant and `rhs` + /// as the least significant. + pub const fn concat(&self, lo: &Uint<{ <$name>::LIMBS / 2 }>) -> $name { + $crate::uint::concat::concat_mixed(lo, self) + } + } + + impl $crate::traits::SplitMixed::LIMBS / 2 }>, Uint<{ <$name>::LIMBS / 2 }>> for $name + { + fn split_mixed(&self) -> (Uint<{ <$name>::LIMBS / 2 }>, Uint<{ <$name>::LIMBS / 2 }>) { + $crate::uint::split::split_mixed(self) + } + } + + impl $crate::traits::Split for $name + { + type Output = Uint<{ <$name>::LIMBS / 2 }>; + } + + impl $name { + /// Split this number in half, returning its high and low components + /// respectively. + pub const fn split(&self) -> (Uint<{ <$name>::LIMBS / 2 }>, Uint<{ <$name>::LIMBS / 2 }>) { + $crate::uint::split::split_mixed(self) + } + } + }; + ($($name:ident,)+) => { + $( + impl_uint_concat_split_even!($name); + )+ + } +} diff --git a/src/uint/modular/constant_mod/macros.rs b/src/uint/modular/constant_mod/macros.rs index 70dc4379..5e8ede8a 100644 --- a/src/uint/modular/constant_mod/macros.rs +++ b/src/uint/modular/constant_mod/macros.rs @@ -8,14 +8,12 @@ macro_rules! impl_modulus { #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] pub struct $name {} impl - $crate::modular::constant_mod::ResidueParams<{ $crate::nlimbs!(<$uint_type>::BITS) }> - for $name + $crate::modular::constant_mod::ResidueParams<{ <$uint_type>::LIMBS }> for $name where - $crate::Uint<{ $crate::nlimbs!(<$uint_type>::BITS) }>: - $crate::Concat>, + $uint_type: $crate::ConcatMixed>, { - const LIMBS: usize = { $crate::nlimbs!(<$uint_type>::BITS) }; - const MODULUS: $crate::Uint<{ $crate::nlimbs!(<$uint_type>::BITS) }> = { + const LIMBS: usize = <$uint_type>::LIMBS; + const MODULUS: $uint_type = { let res = <$uint_type>::from_be_hex($value); // Check that the modulus is odd @@ -25,11 +23,11 @@ macro_rules! impl_modulus { res }; - const R: $crate::Uint<{ $crate::nlimbs!(<$uint_type>::BITS) }> = $crate::Uint::MAX + const R: $uint_type = $crate::Uint::MAX .const_rem(&Self::MODULUS) .0 .wrapping_add(&$crate::Uint::ONE); - const R2: $crate::Uint<{ $crate::nlimbs!(<$uint_type>::BITS) }> = + const R2: $uint_type = $crate::Uint::const_rem_wide(Self::R.square_wide(), &Self::MODULUS).0; const MOD_NEG_INV: $crate::Limb = $crate::Limb( $crate::Word::MIN.wrapping_sub( @@ -39,12 +37,11 @@ macro_rules! impl_modulus { .0, ), ); - const R3: $crate::Uint<{ $crate::nlimbs!(<$uint_type>::BITS) }> = - $crate::modular::montgomery_reduction( - &Self::R2.square_wide(), - &Self::MODULUS, - Self::MOD_NEG_INV, - ); + const R3: $uint_type = $crate::modular::montgomery_reduction( + &Self::R2.square_wide(), + &Self::MODULUS, + Self::MOD_NEG_INV, + ); } }; } diff --git a/src/uint/mul.rs b/src/uint/mul.rs index fcfc756a..cb29332c 100644 --- a/src/uint/mul.rs +++ b/src/uint/mul.rs @@ -1,10 +1,22 @@ //! [`Uint`] addition operations. -use crate::{Checked, CheckedMul, Concat, Limb, Uint, WideWord, Word, Wrapping, Zero}; +use crate::{Checked, CheckedMul, Concat, ConcatMixed, Limb, Uint, WideWord, Word, Wrapping, Zero}; use core::ops::{Mul, MulAssign}; use subtle::CtOption; impl Uint { + /// Multiply `self` by `rhs`, returning a concatenated "wide" result. + pub fn mul( + &self, + rhs: &Uint, + ) -> as ConcatMixed>::MixedOutput + where + Uint: ConcatMixed, + { + let (lo, hi) = self.mul_wide(rhs); + hi.concat_mixed(&lo) + } + /// Compute "wide" multiplication, with a product twice the size of the input. /// /// Returns a tuple containing the `(lo, hi)` components of the product. @@ -16,11 +28,10 @@ impl Uint { /// the APIs in this crate. /// /// For more info see: - // TODO(tarcieri): use `concat` to construct a wide output - pub const fn mul_wide(&self, rhs: &Self) -> (Self, Self) { + pub const fn mul_wide(&self, rhs: &Uint) -> (Self, Uint) { let mut i = 0; let mut lo = Self::ZERO; - let mut hi = Self::ZERO; + let mut hi = Uint::::ZERO; // Schoolbook multiplication. // TODO(tarcieri): use Karatsuba for better performance? @@ -28,7 +39,7 @@ impl Uint { let mut j = 0; let mut carry = Limb::ZERO; - while j < LIMBS { + while j < HLIMBS { let k = i + j; if k >= LIMBS { @@ -44,7 +55,11 @@ impl Uint { j += 1; } - hi.limbs[i + j - LIMBS] = carry; + if i + j >= LIMBS { + hi.limbs[i + j - LIMBS] = carry; + } else { + lo.limbs[i + j] = carry; + } i += 1; } @@ -52,26 +67,13 @@ impl Uint { } /// Perform saturating multiplication, returning `MAX` on overflow. - pub const fn saturating_mul(&self, rhs: &Self) -> Self { + pub const fn saturating_mul(&self, rhs: &Uint) -> Self { let (res, overflow) = self.mul_wide(rhs); - - let mut i = 0; - let mut accumulator = 0; - - while i < LIMBS { - accumulator |= overflow.limbs[i].0; - i += 1; - } - - if accumulator == 0 { - res - } else { - Self::MAX - } + Self::ct_select(&res, &Self::MAX, overflow.ct_is_nonzero()) } /// Perform wrapping multiplication, discarding overflow. - pub const fn wrapping_mul(&self, rhs: &Self) -> Self { + pub const fn wrapping_mul(&self, rhs: &Uint) -> Self { self.mul_wide(rhs).0 } @@ -159,106 +161,168 @@ impl Uint { } } -impl CheckedMul<&Uint> for Uint { +impl CheckedMul<&Uint> for Uint { type Output = Self; - fn checked_mul(&self, rhs: &Self) -> CtOption { + fn checked_mul(&self, rhs: &Uint) -> CtOption { let (lo, hi) = self.mul_wide(rhs); CtOption::new(lo, hi.is_zero()) } } -impl Mul for Wrapping> { +impl Mul>> + for Wrapping> +{ type Output = Self; - fn mul(self, rhs: Self) -> Wrapping> { + fn mul(self, rhs: Wrapping>) -> Wrapping> { Wrapping(self.0.wrapping_mul(&rhs.0)) } } -impl Mul<&Wrapping>> for Wrapping> { - type Output = Wrapping>; +impl Mul<&Wrapping>> + for Wrapping> +{ + type Output = Self; - fn mul(self, rhs: &Wrapping>) -> Wrapping> { + fn mul(self, rhs: &Wrapping>) -> Wrapping> { Wrapping(self.0.wrapping_mul(&rhs.0)) } } -impl Mul>> for &Wrapping> { +impl Mul>> + for &Wrapping> +{ type Output = Wrapping>; - fn mul(self, rhs: Wrapping>) -> Wrapping> { + fn mul(self, rhs: Wrapping>) -> Wrapping> { Wrapping(self.0.wrapping_mul(&rhs.0)) } } -impl Mul<&Wrapping>> for &Wrapping> { +impl Mul<&Wrapping>> + for &Wrapping> +{ type Output = Wrapping>; - fn mul(self, rhs: &Wrapping>) -> Wrapping> { + fn mul(self, rhs: &Wrapping>) -> Wrapping> { Wrapping(self.0.wrapping_mul(&rhs.0)) } } -impl MulAssign for Wrapping> { - fn mul_assign(&mut self, other: Self) { +impl MulAssign>> + for Wrapping> +{ + fn mul_assign(&mut self, other: Wrapping>) { *self = *self * other; } } -impl MulAssign<&Wrapping>> for Wrapping> { - fn mul_assign(&mut self, other: &Self) { +impl MulAssign<&Wrapping>> + for Wrapping> +{ + fn mul_assign(&mut self, other: &Wrapping>) { *self = *self * other; } } -impl Mul for Checked> { +impl Mul>> for Checked> { type Output = Self; - fn mul(self, rhs: Self) -> Checked> { + fn mul(self, rhs: Checked>) -> Checked> { Checked(self.0.and_then(|a| rhs.0.and_then(|b| a.checked_mul(&b)))) } } -impl Mul<&Checked>> for Checked> { +impl Mul<&Checked>> for Checked> { type Output = Checked>; - fn mul(self, rhs: &Checked>) -> Checked> { + fn mul(self, rhs: &Checked>) -> Checked> { Checked(self.0.and_then(|a| rhs.0.and_then(|b| a.checked_mul(&b)))) } } -impl Mul>> for &Checked> { +impl Mul>> for &Checked> { type Output = Checked>; - fn mul(self, rhs: Checked>) -> Checked> { + fn mul(self, rhs: Checked>) -> Checked> { Checked(self.0.and_then(|a| rhs.0.and_then(|b| a.checked_mul(&b)))) } } -impl Mul<&Checked>> for &Checked> { +impl Mul<&Checked>> + for &Checked> +{ type Output = Checked>; - fn mul(self, rhs: &Checked>) -> Checked> { + fn mul(self, rhs: &Checked>) -> Checked> { Checked(self.0.and_then(|a| rhs.0.and_then(|b| a.checked_mul(&b)))) } } -impl MulAssign for Checked> { - fn mul_assign(&mut self, other: Self) { +impl MulAssign>> + for Checked> +{ + fn mul_assign(&mut self, other: Checked>) { *self = *self * other; } } -impl MulAssign<&Checked>> for Checked> { - fn mul_assign(&mut self, other: &Self) { +impl MulAssign<&Checked>> + for Checked> +{ + fn mul_assign(&mut self, other: &Checked>) { *self = *self * other; } } +impl Mul> for Uint +where + Uint: ConcatMixed>, +{ + type Output = as ConcatMixed>::MixedOutput; + + fn mul(self, other: Uint) -> Self::Output { + Uint::mul(&self, &other) + } +} + +impl Mul<&Uint> for Uint +where + Uint: ConcatMixed>, +{ + type Output = as ConcatMixed>::MixedOutput; + + fn mul(self, other: &Uint) -> Self::Output { + Uint::mul(&self, other) + } +} + +impl Mul> for &Uint +where + Uint: ConcatMixed>, +{ + type Output = as ConcatMixed>>::MixedOutput; + + fn mul(self, other: Uint) -> Self::Output { + Uint::mul(self, &other) + } +} + +impl Mul<&Uint> for &Uint +where + Uint: ConcatMixed>, +{ + type Output = as ConcatMixed>>::MixedOutput; + + fn mul(self, other: &Uint) -> Self::Output { + Uint::mul(self, other) + } +} + #[cfg(test)] mod tests { - use crate::{CheckedMul, Zero, U256, U64}; + use crate::{CheckedMul, Zero, U128, U192, U256, U64}; #[test] fn mul_wide_zero_and_one() { @@ -282,6 +346,28 @@ mod tests { } } + #[test] + fn mul_concat_even() { + assert_eq!(U64::ZERO * U64::MAX, U128::ZERO); + assert_eq!(U64::MAX * U64::ZERO, U128::ZERO); + assert_eq!( + U64::MAX * U64::MAX, + U128::from_u128(0xfffffffffffffffe_0000000000000001) + ); + assert_eq!( + U64::ONE * U64::MAX, + U128::from_u128(0x0000000000000000_ffffffffffffffff) + ); + } + + #[test] + fn mul_concat_mixed() { + let a = U64::from_u64(0x0011223344556677); + let b = U128::from_u128(0x8899aabbccddeeff_8899aabbccddeeff); + assert_eq!(a * b, U192::from(&a).saturating_mul(&b)); + assert_eq!(b * a, U192::from(&b).saturating_mul(&a)); + } + #[test] fn checked_mul_ok() { let n = U64::from_u32(0xffff_ffff); diff --git a/src/uint/split.rs b/src/uint/split.rs index b681a615..e6909743 100644 --- a/src/uint/split.rs +++ b/src/uint/split.rs @@ -1,48 +1,27 @@ -// TODO(tarcieri): use `const_evaluatable_checked` when stable to make generic around bits. -macro_rules! impl_split { - ($(($name:ident, $bits:expr)),+) => { - $( - impl $name { - /// Split this number in half, returning its high and low components - /// respectively. - pub const fn split(&self) -> (Uint<{nlimbs!($bits) / 2}>, Uint<{nlimbs!($bits) / 2}>) { - let mut lo = [Limb::ZERO; nlimbs!($bits) / 2]; - let mut hi = [Limb::ZERO; nlimbs!($bits) / 2]; - let mut i = 0; - let mut j = 0; - - while j < (nlimbs!($bits) / 2) { - lo[j] = self.limbs[i]; - i += 1; - j += 1; - } - - j = 0; - while j < (nlimbs!($bits) / 2) { - hi[j] = self.limbs[i]; - i += 1; - j += 1; - } - - (Uint { limbs: hi }, Uint { limbs: lo }) - } - } - - impl Split for $name { - type Output = Uint<{nlimbs!($bits) / 2}>; - - fn split(&self) -> (Self::Output, Self::Output) { - self.split() - } - } +use crate::{Limb, Uint}; + +/// Split this number in half, returning its high and low components +/// respectively. +#[inline] +pub(crate) const fn split_mixed( + n: &Uint, +) -> (Uint, Uint) { + let top = L + H; + let top = if top < O { top } else { O }; + let mut lo = [Limb::ZERO; L]; + let mut hi = [Limb::ZERO; H]; + let mut i = 0; + + while i < top { + if i < L { + lo[i] = n.limbs[i]; + } else { + hi[i - L] = n.limbs[i]; + } + i += 1; + } - impl From<$name> for (Uint<{nlimbs!($bits) / 2}>, Uint<{nlimbs!($bits) / 2}>) { - fn from(num: $name) -> (Uint<{nlimbs!($bits) / 2}>, Uint<{nlimbs!($bits) / 2}>) { - num.split() - } - } - )+ - }; + (Uint { limbs: hi }, Uint { limbs: lo }) } #[cfg(test)]