use dashu_int::ops::BitTest;
use dashu_int::{IBig, Sign as DashuSign, UBig};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[allow(clippy::enum_variant_names)]
pub enum Sign {
Minus,
NoSign,
Plus,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct BigInt(IBig);
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct BigUint(UBig);
pub trait ToPrimitive {
fn to_i64(&self) -> Option<i64>;
fn to_i128(&self) -> Option<i128>;
fn to_u32(&self) -> Option<u32>;
fn to_u64(&self) -> Option<u64>;
fn to_u128(&self) -> Option<u128>;
fn to_f64(&self) -> Option<f64>;
}
pub trait Zero {
fn zero() -> Self;
fn is_zero(&self) -> bool;
}
pub trait Signed {
fn abs(&self) -> Self;
}
impl ::core::fmt::Display for BigInt {
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
::core::fmt::Display::fmt(&self.0, f)
}
}
impl ::core::fmt::Display for BigUint {
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
::core::fmt::Display::fmt(&self.0, f)
}
}
fn to_dashu_sign(sign: Sign) -> DashuSign {
match sign {
Sign::Minus => DashuSign::Negative,
Sign::NoSign | Sign::Plus => DashuSign::Positive,
}
}
impl BigInt {
pub fn from_bytes_be(sign: Sign, bytes: &[u8]) -> BigInt {
BigInt(IBig::from_parts(
to_dashu_sign(sign),
UBig::from_be_bytes(bytes),
))
}
pub fn to_bytes_be(&self) -> (Sign, Vec<u8>) {
let (sign, mag) = self.0.clone().into_parts();
(
from_dashu_sign(sign, mag.is_zero()),
mag.to_be_bytes().into_vec(),
)
}
pub fn sign(&self) -> Sign {
from_dashu_sign(self.0.sign(), self.0.is_zero())
}
pub fn magnitude(&self) -> BigUint {
BigUint(self.0.clone().into_parts().1)
}
pub fn to_signed_bytes_le(&self) -> Vec<u8> {
let (sign, mag) = self.0.clone().into_parts();
if mag.is_zero() {
return vec![0];
}
let mut bytes = mag.to_le_bytes().into_vec();
if bytes.last().unwrap() & 0x80 != 0 {
bytes.push(0);
}
if sign == DashuSign::Negative {
twos_complement_le(&mut bytes);
}
bytes
}
pub fn from_signed_bytes_le(bytes: &[u8]) -> BigInt {
match bytes.last() {
None => BigInt::zero(),
Some(&last) if last & 0x80 == 0 => BigInt(IBig::from(UBig::from_le_bytes(bytes))),
Some(_) => {
let mut buf = bytes.to_vec();
twos_complement_le(&mut buf);
BigInt(IBig::from_parts(
DashuSign::Negative,
UBig::from_le_bytes(&buf),
))
}
}
}
pub fn bits(&self) -> u64 {
self.magnitude().bits()
}
}
impl BigUint {
pub fn from_bytes_le(bytes: &[u8]) -> BigUint {
BigUint(UBig::from_le_bytes(bytes))
}
pub fn bits(&self) -> u64 {
self.0.bit_len() as u64
}
pub fn pow(&self, exp: u32) -> BigUint {
BigUint(self.0.pow(exp as usize))
}
}
fn twos_complement_le(bytes: &mut [u8]) {
let mut carry = true;
for b in bytes.iter_mut() {
let (sum, overflow) = (!*b).overflowing_add(carry as u8);
*b = sum;
carry = overflow;
}
}
fn from_dashu_sign(sign: DashuSign, is_zero: bool) -> Sign {
if is_zero {
Sign::NoSign
} else {
match sign {
DashuSign::Positive => Sign::Plus,
DashuSign::Negative => Sign::Minus,
}
}
}
impl Zero for BigInt {
fn zero() -> Self {
BigInt(IBig::ZERO)
}
fn is_zero(&self) -> bool {
self.0.is_zero()
}
}
impl Zero for BigUint {
fn zero() -> Self {
BigUint(UBig::ZERO)
}
fn is_zero(&self) -> bool {
self.0.is_zero()
}
}
impl Signed for BigInt {
fn abs(&self) -> Self {
BigInt(IBig::from(self.0.clone().into_parts().1))
}
}
macro_rules! impl_to_primitive {
($wrapper:ident) => {
impl ToPrimitive for $wrapper {
fn to_i64(&self) -> Option<i64> {
i64::try_from(&self.0).ok()
}
fn to_i128(&self) -> Option<i128> {
i128::try_from(&self.0).ok()
}
fn to_u32(&self) -> Option<u32> {
u32::try_from(&self.0).ok()
}
fn to_u64(&self) -> Option<u64> {
u64::try_from(&self.0).ok()
}
fn to_u128(&self) -> Option<u128> {
u128::try_from(&self.0).ok()
}
fn to_f64(&self) -> Option<f64> {
Some(self.0.to_f64().value())
}
}
};
}
impl_to_primitive!(BigInt);
impl_to_primitive!(BigUint);
macro_rules! impl_from_int {
($wrapper:ident, $inner:ident, $($t:ty),*) => {
$(
impl From<$t> for $wrapper {
fn from(v: $t) -> Self {
$wrapper($inner::from(v))
}
}
)*
};
}
impl_from_int!(
BigInt, IBig, i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize
);
impl_from_int!(BigUint, UBig, u8, u16, u32, u64, u128);
impl From<BigUint> for BigInt {
fn from(v: BigUint) -> Self {
BigInt(IBig::from(v.0))
}
}
macro_rules! impl_try_into_native {
($($t:ty),*) => {
$(
impl TryFrom<&BigInt> for $t {
type Error = ();
fn try_from(v: &BigInt) -> Result<$t, ()> {
<$t>::try_from(&v.0).map_err(|_| ())
}
}
)*
};
}
impl_try_into_native!(
i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize
);
macro_rules! impl_try_from_owned_bigint {
($($t:ty),*) => {
$(
impl TryFrom<BigInt> for $t {
type Error = ();
fn try_from(v: BigInt) -> Result<$t, ()> {
<$t>::try_from(&v)
}
}
)*
};
}
impl_try_from_owned_bigint!(
i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize
);
macro_rules! impl_try_into_native_uint {
($($t:ty),*) => {
$(
impl TryFrom<BigUint> for $t {
type Error = ();
fn try_from(v: BigUint) -> Result<$t, ()> {
<$t>::try_from(v.0).map_err(|_| ())
}
}
impl TryFrom<&BigUint> for $t {
type Error = ();
fn try_from(v: &BigUint) -> Result<$t, ()> {
<$t>::try_from(&v.0).map_err(|_| ())
}
}
)*
};
}
impl_try_into_native_uint!(u8, u16, u32, u64, u128);
macro_rules! impl_binop {
($wrapper:ident, $trait:ident, $method:ident) => {
impl ::core::ops::$trait for $wrapper {
type Output = $wrapper;
fn $method(self, rhs: $wrapper) -> $wrapper {
$wrapper(::core::ops::$trait::$method(self.0, rhs.0))
}
}
impl ::core::ops::$trait<&$wrapper> for $wrapper {
type Output = $wrapper;
fn $method(self, rhs: &$wrapper) -> $wrapper {
$wrapper(::core::ops::$trait::$method(self.0, rhs.0.clone()))
}
}
impl ::core::ops::$trait<$wrapper> for &$wrapper {
type Output = $wrapper;
fn $method(self, rhs: $wrapper) -> $wrapper {
$wrapper(::core::ops::$trait::$method(self.0.clone(), rhs.0))
}
}
impl ::core::ops::$trait<&$wrapper> for &$wrapper {
type Output = $wrapper;
fn $method(self, rhs: &$wrapper) -> $wrapper {
$wrapper(::core::ops::$trait::$method(self.0.clone(), rhs.0.clone()))
}
}
};
}
impl_binop!(BigInt, Add, add);
impl_binop!(BigInt, Sub, sub);
impl_binop!(BigInt, Mul, mul);
impl_binop!(BigUint, Add, add);
impl_binop!(BigUint, Sub, sub);
impl_binop!(BigUint, Mul, mul);
impl_binop!(BigUint, Div, div);
impl_binop!(BigUint, Rem, rem);
impl ::core::ops::AddAssign<i32> for BigInt {
fn add_assign(&mut self, rhs: i32) {
*self = &*self + rhs;
}
}
impl ::core::ops::AddAssign for BigUint {
fn add_assign(&mut self, rhs: BigUint) {
*self = &*self + rhs;
}
}
impl ::core::ops::SubAssign for BigUint {
fn sub_assign(&mut self, rhs: BigUint) {
*self = &*self - rhs;
}
}
impl ::core::ops::MulAssign for BigUint {
fn mul_assign(&mut self, rhs: BigUint) {
*self = &*self * rhs;
}
}
impl ::core::ops::DivAssign<&BigUint> for BigUint {
fn div_assign(&mut self, rhs: &BigUint) {
*self = &*self / rhs;
}
}
macro_rules! impl_binop_i32 {
($trait:ident, $method:ident) => {
impl ::core::ops::$trait<i32> for BigInt {
type Output = BigInt;
fn $method(self, rhs: i32) -> BigInt {
BigInt(::core::ops::$trait::$method(self.0, IBig::from(rhs)))
}
}
impl ::core::ops::$trait<i32> for &BigInt {
type Output = BigInt;
fn $method(self, rhs: i32) -> BigInt {
BigInt(::core::ops::$trait::$method(
self.0.clone(),
IBig::from(rhs),
))
}
}
};
}
impl_binop_i32!(Add, add);
impl_binop_i32!(Sub, sub);
impl_binop_i32!(Div, div);
impl ::core::ops::Neg for BigInt {
type Output = BigInt;
fn neg(self) -> BigInt {
BigInt(-self.0)
}
}
impl ::core::ops::Neg for &BigInt {
type Output = BigInt;
fn neg(self) -> BigInt {
BigInt(-self.0.clone())
}
}
macro_rules! impl_shift {
($wrapper:ident, $trait:ident, $method:ident, $rhs:ty) => {
impl ::core::ops::$trait<$rhs> for $wrapper {
type Output = $wrapper;
fn $method(self, rhs: $rhs) -> $wrapper {
$wrapper(::core::ops::$trait::$method(self.0, rhs as usize))
}
}
impl ::core::ops::$trait<$rhs> for &$wrapper {
type Output = $wrapper;
fn $method(self, rhs: $rhs) -> $wrapper {
$wrapper(::core::ops::$trait::$method(self.0.clone(), rhs as usize))
}
}
};
}
impl_shift!(BigInt, Shr, shr, usize);
impl_shift!(BigUint, Shr, shr, u32);
impl_shift!(BigUint, Shl, shl, usize);
#[cfg(test)]
#[path = "../../tests/embedded/native/bignum_tests.rs"]
mod tests;