Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Concat/split and multiply mixed size Uints #253

Merged
merged 5 commits into from
Jun 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 29 additions & 6 deletions src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,26 +187,49 @@ pub trait CheckedSub<Rhs = Self>: Sized {
fn checked_sub(&self, rhs: Rhs) -> CtOption<Self>;
}

/// 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<Rhs = Self> {
pub trait Concat: ConcatMixed<Self, MixedOutput = Self::Output> {
/// 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<Lo: ?Sized = Self> {
/// 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<Rhs = Self> {
pub trait Split: SplitMixed<Self::Output, Self::Output> {
/// 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<Hi, Lo> {
/// 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.
Expand Down
140 changes: 54 additions & 86 deletions src/uint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@
)]

#[macro_use]
mod concat;
#[macro_use]
mod split;
mod macros;

mod add;
mod add_mod;
Expand All @@ -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;
Expand All @@ -31,6 +30,7 @@ mod neg_mod;
mod resize;
mod shl;
mod shr;
mod split;
mod sqrt;
mod sub;
mod sub_mod;
Expand All @@ -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};

Expand Down Expand Up @@ -295,46 +295,6 @@ where
#[cfg(feature = "zeroize")]
impl<const LIMBS: usize> DefaultIsZeroes for Uint<LIMBS> {}

// 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"),
Expand All @@ -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"),
Expand All @@ -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")]
Expand Down
83 changes: 42 additions & 41 deletions src/uint/concat.rs
Original file line number Diff line number Diff line change
@@ -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<T> Concat for T
where
T: ConcatMixed<T>,
{
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<const L: usize, const H: usize, const O: usize>(
lo: &Uint<L>,
hi: &Uint<H>,
) -> Uint<O> {
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() {
Expand All @@ -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();
Expand Down
Loading