Skip to content

Commit

Permalink
WIP inline single-digit values
Browse files Browse the repository at this point in the history
  • Loading branch information
cuviper committed May 10, 2024
1 parent f511841 commit bfae1b0
Show file tree
Hide file tree
Showing 18 changed files with 839 additions and 320 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@nightly
- run: cargo doc --features std,serde,rand,quickcheck,arbitrary
- run: cargo doc --features std,inline,serde,rand,quickcheck,arbitrary
env:
RUSTDOCFLAGS: --cfg docsrs

Expand Down
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,15 @@ rust-version = "1.60"

[features]
default = ["std"]
inline = []
std = ["num-integer/std", "num-traits/std"]
arbitrary = ["dep:arbitrary"]
quickcheck = ["dep:quickcheck"]
rand = ["dep:rand"]
serde = ["dep:serde"]

[package.metadata.docs.rs]
features = ["std", "serde", "rand", "quickcheck", "arbitrary"]
features = ["std", "inline", "serde", "rand", "quickcheck", "arbitrary"]
rustdoc-args = ["--cfg", "docsrs"]

[[bench]]
Expand Down
4 changes: 2 additions & 2 deletions ci/test_full.sh
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ if ! check_version $MSRV ; then
exit 1
fi

STD_FEATURES=(arbitrary quickcheck rand serde)
NO_STD_FEATURES=(serde rand)
STD_FEATURES=(inline arbitrary quickcheck rand serde)
NO_STD_FEATURES=(inline serde rand)
echo "Testing supported features: ${STD_FEATURES[*]}"
if [ -n "${NO_STD_FEATURES[*]}" ]; then
echo " no_std supported features: ${NO_STD_FEATURES[*]}"
Expand Down
55 changes: 55 additions & 0 deletions src/big_digit.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
mod inline;
mod vec;

#[cfg(feature = "inline")]
pub(crate) use inline::BigDigits;

#[cfg(not(feature = "inline"))]
pub(crate) use vec::BigDigits;

// A [`BigDigit`] is a [`BigUint`]'s composing element.
cfg_digit!(
pub(crate) type BigDigit = u32;
pub(crate) type BigDigit = u64;
);

// A [`DoubleBigDigit`] is the internal type used to do the computations. Its
// size is the double of the size of [`BigDigit`].
cfg_digit!(
pub(crate) type DoubleBigDigit = u64;
pub(crate) type DoubleBigDigit = u128;
);

// A [`SignedDoubleBigDigit`] is the signed version of [`DoubleBigDigit`].
cfg_digit!(
pub(crate) type SignedDoubleBigDigit = i64;
pub(crate) type SignedDoubleBigDigit = i128;
);

pub(crate) const BITS: u8 = BigDigit::BITS as u8;
pub(crate) const HALF_BITS: u8 = BITS / 2;
pub(crate) const HALF: BigDigit = (1 << HALF_BITS) - 1;

pub(crate) const MAX: BigDigit = BigDigit::MAX;
const LO_MASK: DoubleBigDigit = MAX as DoubleBigDigit;

#[inline]
fn get_hi(n: DoubleBigDigit) -> BigDigit {
(n >> BITS) as BigDigit
}
#[inline]
fn get_lo(n: DoubleBigDigit) -> BigDigit {
(n & LO_MASK) as BigDigit
}

/// Split one [`DoubleBigDigit`] into two [`BigDigit`]s.
#[inline]
pub(crate) fn from_doublebigdigit(n: DoubleBigDigit) -> (BigDigit, BigDigit) {
(get_hi(n), get_lo(n))
}

/// Join two [`BigDigit`]s into one [`DoubleBigDigit`].
#[inline]
pub(crate) fn to_doublebigdigit(hi: BigDigit, lo: BigDigit) -> DoubleBigDigit {
DoubleBigDigit::from(lo) | (DoubleBigDigit::from(hi) << BITS)
}
282 changes: 282 additions & 0 deletions src/big_digit/inline.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,282 @@
#![cfg(feature = "inline")]

use super::BigDigit;
use alloc::vec::Vec;

// #[derive(Debug)]
pub(crate) enum BigDigits {
Inline(Option<BigDigit>),
Heap(Vec<BigDigit>),
}

impl BigDigits {
pub(crate) const ZERO: Self = BigDigits::Inline(None);
pub(crate) const ONE: Self = BigDigits::Inline(Some(1));

#[inline]
pub(crate) const fn from_digit(x: BigDigit) -> Self {
if x == 0 {
BigDigits::ZERO
} else {
BigDigits::Inline(Some(x))
}
}

#[inline]
pub(crate) fn from_slice(slice: &[BigDigit]) -> Self {
match slice {
&[] => BigDigits::ZERO,
&[x] => BigDigits::Inline(Some(x)),
xs => BigDigits::Heap(xs.to_vec()),
}
}

#[inline]
pub(crate) fn from_vec(xs: Vec<BigDigit>) -> Self {
BigDigits::Heap(xs)
}

#[inline]
pub(crate) fn clear(&mut self) {
match self {
BigDigits::Inline(x) => *x = None,
BigDigits::Heap(xs) => xs.clear(),
}
}

#[inline]
pub(crate) fn push(&mut self, y: BigDigit) {
match &mut *self {
BigDigits::Inline(x @ None) => *x = Some(y),
BigDigits::Inline(Some(x)) => *self = BigDigits::Heap([*x, y].to_vec()),
BigDigits::Heap(xs) => xs.push(y),
}
}

#[inline]
pub(crate) fn pop(&mut self) -> Option<BigDigit> {
match self {
BigDigits::Inline(x) => x.take(),
BigDigits::Heap(xs) => xs.pop(),
}
}

#[inline]
pub(crate) fn last(&self) -> Option<&BigDigit> {
match self {
BigDigits::Inline(x) => x.as_ref(),
BigDigits::Heap(xs) => xs.last(),
}
}

#[inline]
pub(crate) fn len(&self) -> usize {
match self {
BigDigits::Inline(None) => 0,
BigDigits::Inline(Some(_)) => 1,
BigDigits::Heap(xs) => xs.len(),
}
}

#[inline]
pub(crate) fn is_empty(&self) -> bool {
match self {
BigDigits::Inline(None) => true,
BigDigits::Inline(Some(_)) => false,
BigDigits::Heap(xs) => xs.is_empty(),
}
}

#[inline]
pub(crate) fn capacity(&self) -> usize {
match self {
BigDigits::Inline(_) => 1,
BigDigits::Heap(xs) => xs.capacity(),
}
}

#[inline]
pub(crate) fn shrink(&mut self) {
if let BigDigits::Heap(xs) = self {
if xs.len() < xs.capacity() / 2 {
match **xs {
[] => *self = BigDigits::ZERO,
[x] => *self = BigDigits::Inline(Some(x)),
_ => xs.shrink_to(xs.len() + 1),
}
}
}
}

/// Returns `true` if the most-significant digit (if any) is nonzero.
#[inline]
pub(crate) fn is_normal(&self) -> bool {
match self {
BigDigits::Inline(Some(0)) => false,
BigDigits::Inline(_) => true,
BigDigits::Heap(xs) => !matches!(**xs, [.., 0]),
}
}

/// Strips off trailing zero bigdigits - most algorithms require
/// the most significant digit in the number to be nonzero.
#[inline]
pub(crate) fn normalize(&mut self) {
match self {
BigDigits::Inline(x) => {
if let Some(0) = *x {
*x = None;
}
}
BigDigits::Heap(xs) => {
if let [.., 0] = **xs {
let len = xs.iter().rposition(|&d| d != 0).map_or(0, |i| i + 1);
xs.truncate(len);
}
if xs.len() < xs.capacity() / 2 {
match **xs {
[] => *self = BigDigits::ZERO,
[x] => *self = BigDigits::Inline(Some(x)),
_ => xs.shrink_to(xs.len() + 1),
}
}
}
}
}

#[inline]
pub(crate) fn truncate(&mut self, len: usize) {
match self {
BigDigits::Inline(x) => {
if len == 0 {
*x = None;
}
}
BigDigits::Heap(xs) => xs.truncate(len),
}
}

#[inline]
pub(crate) fn drain_front(&mut self, len: usize) {
match self {
BigDigits::Inline(x) => {
assert!(len <= 1);
if len == 1 {
*x = None;
}
}
BigDigits::Heap(xs) => {
xs.drain(..len);
}
}
}

pub(crate) fn resize(&mut self, len: usize, value: BigDigit) {
match &mut *self {
BigDigits::Inline(x) => match len {
0 => *x = None,
1 => {
if x.is_none() {
*x = Some(value);
}
}
_ => {
let mut xs = Vec::with_capacity(len);
if let Some(x) = *x {
xs.push(x);
}
xs.resize(len, value);
*self = BigDigits::Heap(xs);
}
},
BigDigits::Heap(xs) => xs.resize(len, value),
}
}

pub(crate) fn extend_from_slice(&mut self, ys: &[BigDigit]) {
match &mut *self {
BigDigits::Inline(None) => *self = BigDigits::from_slice(ys),
BigDigits::Inline(Some(x)) => {
let len = ys.len() + 1;
if len > 1 {
let mut xs = Vec::with_capacity(len);
xs.push(*x);
xs.extend_from_slice(ys);
*self = BigDigits::Heap(xs);
}
}
BigDigits::Heap(xs) => xs.extend_from_slice(ys),
}
}

pub(crate) fn extend<I>(&mut self, mut iter: I)
where
I: ExactSizeIterator<Item = BigDigit>,
{
match &mut *self {
BigDigits::Inline(x) => {
if x.is_none() {
match iter.next() {
Some(y) => *x = Some(y),
None => return,
}
}
if let Some(y) = iter.next() {
let len = iter.len().saturating_add(2);
let mut xs = Vec::with_capacity(len);
xs.push(x.unwrap());
xs.push(y);
xs.extend(iter);
*self = BigDigits::Heap(xs);
}
}
BigDigits::Heap(xs) => xs.extend(iter),
}
}
}

impl Clone for BigDigits {
#[inline]
fn clone(&self) -> Self {
match self {
BigDigits::Inline(x) => BigDigits::Inline(*x),
BigDigits::Heap(xs) => BigDigits::from_slice(xs),
}
}

#[inline]
fn clone_from(&mut self, source: &Self) {
match &mut *self {
BigDigits::Heap(xs) if xs.capacity() != 0 => {
xs.clear();
xs.extend_from_slice(source);
}
#[allow(clippy::assigning_clones)]
_ => *self = source.clone(),
}
}
}

impl core::ops::Deref for BigDigits {
type Target = [BigDigit];

#[inline]
fn deref(&self) -> &Self::Target {
match self {
BigDigits::Inline(None) => &[],
BigDigits::Inline(Some(x)) => core::slice::from_ref(x),
BigDigits::Heap(xs) => xs,
}
}
}

impl core::ops::DerefMut for BigDigits {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
match self {
BigDigits::Inline(None) => &mut [],
BigDigits::Inline(Some(x)) => core::slice::from_mut(x),
BigDigits::Heap(xs) => xs,
}
}
}
Loading

0 comments on commit bfae1b0

Please sign in to comment.