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

WIP inline single-digit values #307

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
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
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
49 changes: 49 additions & 0 deletions src/big_digit.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
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;
);

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