use self::Repr::*;
use crate::{
arch::{ntt, word::Word},
buffer::Buffer,
math,
primitive::WORD_BITS_USIZE,
};
use core::slice;
#[derive(Debug, Eq, Hash, PartialEq)]
pub(crate) enum Repr {
Small(Word),
Large(Buffer),
}
#[derive(Eq, Hash, PartialEq)]
pub struct UBig(Repr);
impl UBig {
pub(crate) fn from_word(word: Word) -> UBig {
UBig(Small(word))
}
pub(crate) fn repr(&self) -> &Repr {
&self.0
}
pub(crate) fn into_repr(self) -> Repr {
self.0
}
pub(crate) fn len(&self) -> usize {
match self.repr() {
Small(_) => 1,
Large(buffer) => buffer.len(),
}
}
pub(crate) fn as_words(&self) -> &[Word] {
match self.repr() {
Small(0) => &[],
Small(word) => slice::from_ref(word),
Large(buffer) => &buffer,
}
}
pub(crate) const MAX_LEN: usize = math::min_usize(
usize::MAX / WORD_BITS_USIZE,
match 1usize.checked_shl(ntt::MAX_ORDER) {
Some(ntt_len) => ntt_len,
None => usize::MAX,
},
);
pub const MAX_BIT_LEN: usize = UBig::MAX_LEN * WORD_BITS_USIZE;
pub(crate) fn panic_number_too_large() -> ! {
panic!("number too large, maximum is {} bits", UBig::MAX_BIT_LEN)
}
}
impl Clone for UBig {
fn clone(&self) -> UBig {
match self.repr() {
Small(x) => UBig(Small(*x)),
Large(buffer) => UBig(Large(buffer.clone())),
}
}
fn clone_from(&mut self, source: &UBig) {
if let Large(buffer) = &mut self.0 {
if let Large(source_buffer) = source.repr() {
buffer.resizing_clone_from(source_buffer);
return;
}
}
*self = source.clone();
}
}
impl From<Buffer> for UBig {
fn from(mut buffer: Buffer) -> UBig {
buffer.pop_leading_zeros();
match buffer.len() {
0 => UBig::from_word(0),
1 => UBig::from_word(buffer[0]),
_ if buffer.len() > UBig::MAX_LEN => UBig::panic_number_too_large(),
_ => {
buffer.shrink();
UBig(Large(buffer))
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn capacity(x: &UBig) -> usize {
match x.repr() {
Small(_) => 1,
Large(large) => large.capacity(),
}
}
#[test]
fn test_buffer_to_ubig() {
let buf = Buffer::allocate(5);
let num: UBig = buf.into();
assert_eq!(num, UBig::from_word(0));
let mut buf = Buffer::allocate(5);
buf.push(7);
let num: UBig = buf.into();
assert_eq!(num, UBig::from_word(7));
let mut buf = Buffer::allocate(100);
buf.push(7);
buf.push(0);
buf.push(0);
let num: UBig = buf.into();
assert_eq!(num, UBig::from_word(7));
let mut buf = Buffer::allocate(5);
buf.push(1);
buf.push(2);
buf.push(3);
buf.push(4);
let num: UBig = buf.into();
assert_eq!(capacity(&num), 7);
let mut buf = Buffer::allocate(100);
buf.push(1);
buf.push(2);
buf.push(3);
buf.push(4);
let num: UBig = buf.into();
assert_eq!(capacity(&num), 6);
}
#[test]
fn test_clone() {
let a = UBig::from_word(5);
assert_eq!(a.clone(), a);
let a = gen_ubig(10);
let b = a.clone();
assert_eq!(a, b);
assert_eq!(capacity(&a), capacity(&b));
}
#[test]
fn test_clone_from() {
let num: UBig = gen_ubig(10);
let mut a = UBig::from_word(3);
a.clone_from(&num);
assert_eq!(a, num);
let b = UBig::from_word(7);
a.clone_from(&b);
assert_eq!(a, b);
a.clone_from(&b);
assert_eq!(a, b);
let mut a = gen_ubig(9);
let prev_cap = capacity(&a);
a.clone_from(&num);
assert_eq!(capacity(&a), prev_cap);
assert_ne!(capacity(&a), capacity(&num));
let mut a = gen_ubig(2);
let prev_cap = capacity(&a);
a.clone_from(&num);
assert_ne!(capacity(&a), prev_cap);
assert_eq!(capacity(&a), capacity(&num));
let mut a = gen_ubig(100);
let prev_cap = capacity(&a);
a.clone_from(&num);
assert_ne!(capacity(&a), prev_cap);
assert_eq!(capacity(&a), capacity(&num));
}
fn gen_ubig(num_words: u16) -> UBig {
let mut buf = Buffer::allocate(num_words.into());
for i in 0..num_words {
buf.push(i.into());
}
buf.into()
}
}