Skip to content

Commit 23802ee

Browse files
committed
WIP inline single-digit values
1 parent 71b78bc commit 23802ee

File tree

18 files changed

+833
-314
lines changed

18 files changed

+833
-314
lines changed

.github/workflows/ci.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ jobs:
6969
steps:
7070
- uses: actions/checkout@v4
7171
- uses: dtolnay/rust-toolchain@nightly
72-
- run: cargo doc --features std,serde,rand,quickcheck,arbitrary
72+
- run: cargo doc --features std,inline,serde,rand,quickcheck,arbitrary
7373
env:
7474
RUSTDOCFLAGS: --cfg docsrs
7575

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,15 @@ rust-version = "1.60"
1616

1717
[features]
1818
default = ["std"]
19+
inline = []
1920
std = ["num-integer/std", "num-traits/std"]
2021
arbitrary = ["dep:arbitrary"]
2122
quickcheck = ["dep:quickcheck"]
2223
rand = ["dep:rand"]
2324
serde = ["dep:serde"]
2425

2526
[package.metadata.docs.rs]
26-
features = ["std", "serde", "rand", "quickcheck", "arbitrary"]
27+
features = ["std", "inline", "serde", "rand", "quickcheck", "arbitrary"]
2728
rustdoc-args = ["--cfg", "docsrs"]
2829

2930
[[bench]]

ci/test_full.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ if ! check_version $MSRV ; then
2727
exit 1
2828
fi
2929

30-
STD_FEATURES=(arbitrary quickcheck rand serde)
31-
NO_STD_FEATURES=(serde rand)
30+
STD_FEATURES=(inline arbitrary quickcheck rand serde)
31+
NO_STD_FEATURES=(inline serde rand)
3232
echo "Testing supported features: ${STD_FEATURES[*]}"
3333
if [ -n "${NO_STD_FEATURES[*]}" ]; then
3434
echo " no_std supported features: ${NO_STD_FEATURES[*]}"

src/big_digit.rs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
mod inline;
2+
mod vec;
3+
4+
#[cfg(feature = "inline")]
5+
pub(crate) use inline::BigDigits;
6+
7+
#[cfg(not(feature = "inline"))]
8+
pub(crate) use vec::BigDigits;
9+
10+
// A [`BigDigit`] is a [`BigUint`]'s composing element.
11+
cfg_digit!(
12+
pub(crate) type BigDigit = u32;
13+
pub(crate) type BigDigit = u64;
14+
);
15+
16+
// A [`DoubleBigDigit`] is the internal type used to do the computations. Its
17+
// size is the double of the size of [`BigDigit`].
18+
cfg_digit!(
19+
pub(crate) type DoubleBigDigit = u64;
20+
pub(crate) type DoubleBigDigit = u128;
21+
);
22+
23+
pub(crate) const BITS: u8 = BigDigit::BITS as u8;
24+
pub(crate) const HALF_BITS: u8 = BITS / 2;
25+
pub(crate) const HALF: BigDigit = (1 << HALF_BITS) - 1;
26+
27+
pub(crate) const MAX: BigDigit = BigDigit::MAX;
28+
const LO_MASK: DoubleBigDigit = MAX as DoubleBigDigit;
29+
30+
#[inline]
31+
fn get_hi(n: DoubleBigDigit) -> BigDigit {
32+
(n >> BITS) as BigDigit
33+
}
34+
#[inline]
35+
fn get_lo(n: DoubleBigDigit) -> BigDigit {
36+
(n & LO_MASK) as BigDigit
37+
}
38+
39+
/// Split one [`DoubleBigDigit`] into two [`BigDigit`]s.
40+
#[inline]
41+
pub(crate) fn from_doublebigdigit(n: DoubleBigDigit) -> (BigDigit, BigDigit) {
42+
(get_hi(n), get_lo(n))
43+
}
44+
45+
/// Join two [`BigDigit`]s into one [`DoubleBigDigit`].
46+
#[inline]
47+
pub(crate) fn to_doublebigdigit(hi: BigDigit, lo: BigDigit) -> DoubleBigDigit {
48+
DoubleBigDigit::from(lo) | (DoubleBigDigit::from(hi) << BITS)
49+
}

src/big_digit/inline.rs

Lines changed: 282 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,282 @@
1+
#![cfg(feature = "inline")]
2+
3+
use super::BigDigit;
4+
use alloc::vec::Vec;
5+
6+
// #[derive(Debug)]
7+
pub(crate) enum BigDigits {
8+
Inline(Option<BigDigit>),
9+
Heap(Vec<BigDigit>),
10+
}
11+
12+
impl BigDigits {
13+
pub(crate) const ZERO: Self = BigDigits::Inline(None);
14+
pub(crate) const ONE: Self = BigDigits::Inline(Some(1));
15+
16+
#[inline]
17+
pub(crate) const fn from_digit(x: BigDigit) -> Self {
18+
if x == 0 {
19+
BigDigits::ZERO
20+
} else {
21+
BigDigits::Inline(Some(x))
22+
}
23+
}
24+
25+
#[inline]
26+
pub(crate) fn from_slice(slice: &[BigDigit]) -> Self {
27+
match slice {
28+
&[] => BigDigits::ZERO,
29+
&[x] => BigDigits::Inline(Some(x)),
30+
xs => BigDigits::Heap(xs.to_vec()),
31+
}
32+
}
33+
34+
#[inline]
35+
pub(crate) fn from_vec(xs: Vec<BigDigit>) -> Self {
36+
BigDigits::Heap(xs)
37+
}
38+
39+
#[inline]
40+
pub(crate) fn clear(&mut self) {
41+
match self {
42+
BigDigits::Inline(x) => *x = None,
43+
BigDigits::Heap(xs) => xs.clear(),
44+
}
45+
}
46+
47+
#[inline]
48+
pub(crate) fn push(&mut self, y: BigDigit) {
49+
match &mut *self {
50+
BigDigits::Inline(x @ None) => *x = Some(y),
51+
BigDigits::Inline(Some(x)) => *self = BigDigits::Heap([*x, y].to_vec()),
52+
BigDigits::Heap(xs) => xs.push(y),
53+
}
54+
}
55+
56+
#[inline]
57+
pub(crate) fn pop(&mut self) -> Option<BigDigit> {
58+
match self {
59+
BigDigits::Inline(x) => x.take(),
60+
BigDigits::Heap(xs) => xs.pop(),
61+
}
62+
}
63+
64+
#[inline]
65+
pub(crate) fn last(&self) -> Option<&BigDigit> {
66+
match self {
67+
BigDigits::Inline(x) => x.as_ref(),
68+
BigDigits::Heap(xs) => xs.last(),
69+
}
70+
}
71+
72+
#[inline]
73+
pub(crate) fn len(&self) -> usize {
74+
match self {
75+
BigDigits::Inline(None) => 0,
76+
BigDigits::Inline(Some(_)) => 1,
77+
BigDigits::Heap(xs) => xs.len(),
78+
}
79+
}
80+
81+
#[inline]
82+
pub(crate) fn is_empty(&self) -> bool {
83+
match self {
84+
BigDigits::Inline(None) => true,
85+
BigDigits::Inline(Some(_)) => false,
86+
BigDigits::Heap(xs) => xs.is_empty(),
87+
}
88+
}
89+
90+
#[inline]
91+
pub(crate) fn capacity(&self) -> usize {
92+
match self {
93+
BigDigits::Inline(_) => 1,
94+
BigDigits::Heap(xs) => xs.capacity(),
95+
}
96+
}
97+
98+
#[inline]
99+
pub(crate) fn shrink(&mut self) {
100+
if let BigDigits::Heap(xs) = self {
101+
if xs.len() < xs.capacity() / 2 {
102+
match **xs {
103+
[] => *self = BigDigits::ZERO,
104+
[x] => *self = BigDigits::Inline(Some(x)),
105+
_ => xs.shrink_to(xs.len() + 1),
106+
}
107+
}
108+
}
109+
}
110+
111+
/// Returns `true` if the most-significant digit (if any) is nonzero.
112+
#[inline]
113+
pub(crate) fn is_normal(&self) -> bool {
114+
match self {
115+
BigDigits::Inline(Some(0)) => false,
116+
BigDigits::Inline(_) => true,
117+
BigDigits::Heap(xs) => !matches!(**xs, [.., 0]),
118+
}
119+
}
120+
121+
/// Strips off trailing zero bigdigits - most algorithms require
122+
/// the most significant digit in the number to be nonzero.
123+
#[inline]
124+
pub(crate) fn normalize(&mut self) {
125+
match self {
126+
BigDigits::Inline(x) => {
127+
if let Some(0) = *x {
128+
*x = None;
129+
}
130+
}
131+
BigDigits::Heap(xs) => {
132+
if let [.., 0] = **xs {
133+
let len = xs.iter().rposition(|&d| d != 0).map_or(0, |i| i + 1);
134+
xs.truncate(len);
135+
}
136+
if xs.len() < xs.capacity() / 2 {
137+
match **xs {
138+
[] => *self = BigDigits::ZERO,
139+
[x] => *self = BigDigits::Inline(Some(x)),
140+
_ => xs.shrink_to(xs.len() + 1),
141+
}
142+
}
143+
}
144+
}
145+
}
146+
147+
#[inline]
148+
pub(crate) fn truncate(&mut self, len: usize) {
149+
match self {
150+
BigDigits::Inline(x) => {
151+
if len == 0 {
152+
*x = None;
153+
}
154+
}
155+
BigDigits::Heap(xs) => xs.truncate(len),
156+
}
157+
}
158+
159+
#[inline]
160+
pub(crate) fn drain_front(&mut self, len: usize) {
161+
match self {
162+
BigDigits::Inline(x) => {
163+
assert!(len <= 1);
164+
if len == 1 {
165+
*x = None;
166+
}
167+
}
168+
BigDigits::Heap(xs) => {
169+
xs.drain(..len);
170+
}
171+
}
172+
}
173+
174+
pub(crate) fn resize(&mut self, len: usize, value: BigDigit) {
175+
match &mut *self {
176+
BigDigits::Inline(x) => match len {
177+
0 => *x = None,
178+
1 => {
179+
if x.is_none() {
180+
*x = Some(value);
181+
}
182+
}
183+
_ => {
184+
let mut xs = Vec::with_capacity(len);
185+
if let Some(x) = *x {
186+
xs.push(x);
187+
}
188+
xs.resize(len, value);
189+
*self = BigDigits::Heap(xs);
190+
}
191+
},
192+
BigDigits::Heap(xs) => xs.resize(len, value),
193+
}
194+
}
195+
196+
pub(crate) fn extend_from_slice(&mut self, ys: &[BigDigit]) {
197+
match &mut *self {
198+
BigDigits::Inline(None) => *self = BigDigits::from_slice(ys),
199+
BigDigits::Inline(Some(x)) => {
200+
let len = ys.len() + 1;
201+
if len > 1 {
202+
let mut xs = Vec::with_capacity(len);
203+
xs.push(*x);
204+
xs.extend_from_slice(ys);
205+
*self = BigDigits::Heap(xs);
206+
}
207+
}
208+
BigDigits::Heap(xs) => xs.extend_from_slice(ys),
209+
}
210+
}
211+
212+
pub(crate) fn extend<I>(&mut self, mut iter: I)
213+
where
214+
I: ExactSizeIterator<Item = BigDigit>,
215+
{
216+
match &mut *self {
217+
BigDigits::Inline(x) => {
218+
if x.is_none() {
219+
match iter.next() {
220+
Some(y) => *x = Some(y),
221+
None => return,
222+
}
223+
}
224+
if let Some(y) = iter.next() {
225+
let len = iter.len().saturating_add(2);
226+
let mut xs = Vec::with_capacity(len);
227+
xs.push(x.unwrap());
228+
xs.push(y);
229+
xs.extend(iter);
230+
*self = BigDigits::Heap(xs);
231+
}
232+
}
233+
BigDigits::Heap(xs) => xs.extend(iter),
234+
}
235+
}
236+
}
237+
238+
impl Clone for BigDigits {
239+
#[inline]
240+
fn clone(&self) -> Self {
241+
match self {
242+
BigDigits::Inline(x) => BigDigits::Inline(*x),
243+
BigDigits::Heap(xs) => BigDigits::from_slice(xs),
244+
}
245+
}
246+
247+
#[inline]
248+
fn clone_from(&mut self, source: &Self) {
249+
match &mut *self {
250+
BigDigits::Heap(xs) if xs.capacity() != 0 => {
251+
xs.clear();
252+
xs.extend_from_slice(source);
253+
}
254+
#[allow(clippy::assigning_clones)]
255+
_ => *self = source.clone(),
256+
}
257+
}
258+
}
259+
260+
impl core::ops::Deref for BigDigits {
261+
type Target = [BigDigit];
262+
263+
#[inline]
264+
fn deref(&self) -> &Self::Target {
265+
match self {
266+
BigDigits::Inline(None) => &[],
267+
BigDigits::Inline(Some(x)) => core::slice::from_ref(x),
268+
BigDigits::Heap(xs) => xs,
269+
}
270+
}
271+
}
272+
273+
impl core::ops::DerefMut for BigDigits {
274+
#[inline]
275+
fn deref_mut(&mut self) -> &mut Self::Target {
276+
match self {
277+
BigDigits::Inline(None) => &mut [],
278+
BigDigits::Inline(Some(x)) => core::slice::from_mut(x),
279+
BigDigits::Heap(xs) => xs,
280+
}
281+
}
282+
}

0 commit comments

Comments
 (0)