use super::UBigInt;
#[derive(Default, Clone, Copy, Eq, PartialEq, Debug, Hash)]
pub struct BigInt<const N: usize> {
pub(crate) digits: UBigInt<N>,
is_negative: bool,
}
impl<const N: usize> BigInt<N> {
pub const ZERO: Self = Self {
digits: UBigInt::ZERO,
is_negative: false,
};
pub const ONE: Self = Self {
digits: UBigInt::ONE,
is_negative: false,
};
pub const NEG_ONE: Self = Self {
digits: UBigInt::MAX,
is_negative: true,
};
pub const MAX: Self = Self {
digits: UBigInt::MAX,
is_negative: false,
};
pub const MIN: Self = Self {
digits: UBigInt::MIN,
is_negative: true,
};
#[allow(clippy::len_without_is_empty)]
pub const fn len(&self) -> usize {
self.digits.len()
}
pub fn neg(&self) -> Self {
Self::ZERO.sub(self)
}
pub fn neg_assign(&mut self) {
self.not_assign();
self.add_assign(&Self::ONE);
}
pub fn add_assign(&mut self, rhs: &Self) {
let overflowed = self.digits.overflowing_add_assign(&rhs.digits);
self.is_negative ^= overflowed ^ rhs.is_negative;
}
pub fn add(&self, rhs: &Self) -> Self {
let mut buf = *self;
buf.add_assign(rhs);
buf
}
pub fn sub_assign(&mut self, rhs: &Self) {
let overflowed = self.digits.overflowing_sub_assign(&rhs.digits);
self.is_negative ^= overflowed ^ rhs.is_negative;
}
pub fn sub(&self, rhs: &Self) -> Self {
let mut buf = *self;
buf.sub_assign(rhs);
buf
}
#[inline]
pub fn is_negative(&self) -> bool {
self.is_negative
}
#[inline]
pub fn is_positive(&self) -> bool {
!self.is_negative & (self.digits != UBigInt::ZERO)
}
pub fn not_assign(&mut self) {
self.digits.not_assign();
self.is_negative = !self.is_negative;
}
pub fn not(&self) -> Self {
let mut buf = *self;
buf.not_assign();
buf
}
pub fn xor_assign(&mut self, rhs: &Self) {
self.digits.xor_assign(&rhs.digits);
self.is_negative ^= rhs.is_negative;
}
pub fn xor(&self, rhs: &Self) -> Self {
let mut buf = *self;
buf.xor_assign(rhs);
buf
}
pub fn abs_assign(&mut self) {
let temp = *self;
self.neg_assign();
self.xor_assign(&temp);
}
pub fn abs(&self) -> Self {
self.xor(&self.neg())
}
pub fn resize<const O: usize>(self) -> BigInt<O> {
BigInt {
digits: self.digits.resize(),
is_negative: self.is_negative,
}
}
}
macro_rules! impl_big_int {
($n:literal) => {
impl BigInt<$n> {
pub fn div(&self, rhs: &Self) -> (Self, Self) {
let (quotient, remainder) = self.digits.div(&rhs.digits);
let quotient = Self {
digits: quotient,
is_negative: (self.is_negative() ^ rhs.is_negative())
& (self != &Self::ZERO)
& (rhs != &Self::ZERO),
};
let remainder = Self {
digits: remainder,
is_negative: self.is_negative,
};
(quotient, remainder)
}
pub fn div_assign(&mut self, rhs: &Self) {
*self = self.div(rhs).0;
}
pub fn widening_mul(&self, rhs: &Self) -> BigInt<{ $n * 2 }> {
let product = self.digits.widening_mul(&rhs.digits);
BigInt::<{ $n * 2 }> {
digits: product,
is_negative: (self.is_negative() ^ rhs.is_negative())
& (self != &Self::ZERO)
& (rhs != &Self::ZERO),
}
}
}
};
}
impl_big_int!(3);
impl_big_int!(4);
impl_big_int!(8);
impl<const N: usize> PartialOrd for BigInt<N> {
fn lt(&self, other: &Self) -> bool {
self.sub(other).is_negative
}
fn le(&self, other: &Self) -> bool {
!self.gt(other)
}
fn gt(&self, other: &Self) -> bool {
other.sub(self).is_negative
}
fn ge(&self, other: &Self) -> bool {
!self.lt(other)
}
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl<const N: usize> Ord for BigInt<N> {
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
let cmped = self.sub(other);
match cmped.is_negative {
true => core::cmp::Ordering::Greater,
false if self.digits == other.digits => core::cmp::Ordering::Equal,
false => core::cmp::Ordering::Less,
}
}
}
impl<const N: usize> From<UBigInt<N>> for BigInt<N> {
fn from(value: UBigInt<N>) -> Self {
Self {
digits: value,
is_negative: false,
}
}
}
#[cfg(test)]
mod tests {
use super::BigInt;
#[test]
fn cmp() {
assert_eq!(BigInt::<4>::ZERO, BigInt::<4>::ZERO);
assert_ne!(BigInt::<4>::ZERO, BigInt::ONE);
assert!(BigInt::<4>::ZERO > BigInt::NEG_ONE);
assert!(BigInt::<4>::NEG_ONE < BigInt::ZERO);
assert!(BigInt::<4>::ONE > BigInt::NEG_ONE);
}
#[test]
fn add() {
assert_eq!(BigInt::<4>::ONE.add(&BigInt::NEG_ONE), BigInt::ZERO);
assert_eq!(BigInt::<4>::MAX.add(&BigInt::ONE), BigInt::MIN);
assert_eq!(BigInt::<4>::ZERO.add(&BigInt::ONE), BigInt::ONE);
}
#[test]
fn sub() {
assert_eq!(BigInt::<4>::MIN.sub(&BigInt::ONE), BigInt::MAX);
assert_eq!(BigInt::<4>::MIN.sub(&BigInt::MAX), BigInt::ONE);
assert_eq!(BigInt::<4>::ZERO.sub(&BigInt::ONE), BigInt::NEG_ONE);
}
#[test]
fn neg() {
assert_eq!(BigInt::<4>::ONE.neg(), BigInt::NEG_ONE);
assert_eq!(BigInt::<4>::NEG_ONE.neg(), BigInt::ONE);
assert_eq!(BigInt::<4>::ZERO.neg(), BigInt::ZERO);
assert_eq!(BigInt::<4>::MIN.neg(), BigInt::MIN);
let mut one = BigInt::<4>::ONE;
one.neg_assign();
assert_eq!(one, BigInt::NEG_ONE);
}
#[test]
fn mul() {
assert_eq!(BigInt::<4>::ZERO.widening_mul(&BigInt::MIN), BigInt::ZERO);
assert_eq!(BigInt::<4>::ZERO.widening_mul(&BigInt::ZERO), BigInt::ZERO);
assert_eq!(
BigInt::<4>::ZERO.widening_mul(&BigInt::NEG_ONE),
BigInt::ZERO
);
assert_eq!(BigInt::<4>::ZERO.widening_mul(&BigInt::ONE), BigInt::ZERO);
assert_eq!(BigInt::<4>::ONE.widening_mul(&BigInt::ONE), BigInt::ONE);
}
#[test]
fn is_positive() {
assert!(BigInt::<4>::MAX.is_positive());
assert!(BigInt::<4>::ONE.is_positive());
assert!(!BigInt::<4>::ZERO.is_positive());
assert!(!BigInt::<4>::MIN.is_positive());
assert!(!BigInt::<4>::NEG_ONE.is_positive());
}
#[test]
fn is_negative() {
assert!(BigInt::<4>::MIN.is_negative());
assert!(BigInt::<4>::NEG_ONE.is_negative());
assert!(!BigInt::<4>::ZERO.is_negative());
assert!(!BigInt::<4>::MAX.is_negative());
assert!(!BigInt::<4>::ONE.is_negative());
}
}