use super::uint::BigUint;
use core::cmp::Ordering;
use oxinum_core::Sign;
use std::fmt;
#[derive(Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct BigInt {
#[cfg_attr(feature = "serde", serde(with = "sign_serde"))]
pub(super) sign: Sign,
pub(super) mag: BigUint,
}
#[cfg(feature = "serde")]
mod sign_serde {
use oxinum_core::Sign;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
pub(super) fn serialize<S: Serializer>(s: &Sign, ser: S) -> Result<S::Ok, S::Error> {
bool::from(*s).serialize(ser)
}
pub(super) fn deserialize<'de, D: Deserializer<'de>>(de: D) -> Result<Sign, D::Error> {
bool::deserialize(de).map(Sign::from)
}
}
impl Default for BigInt {
#[inline]
fn default() -> Self {
Self::ZERO
}
}
impl BigInt {
pub const ZERO: BigInt = BigInt {
sign: Sign::Positive,
mag: BigUint::ZERO,
};
#[inline]
pub fn zero() -> Self {
Self::ZERO
}
#[inline]
pub fn one() -> Self {
Self {
sign: Sign::Positive,
mag: BigUint::one(),
}
}
pub fn from_parts(sign: Sign, mag: BigUint) -> Self {
let mut out = Self { sign, mag };
out.canonicalize();
out
}
#[inline]
pub fn into_parts(self) -> (Sign, BigUint) {
(self.sign, self.mag)
}
#[inline]
pub fn sign(&self) -> Sign {
self.sign
}
#[inline]
pub fn signum(&self) -> Sign {
self.sign
}
#[inline]
pub fn magnitude(&self) -> &BigUint {
&self.mag
}
pub fn abs(&self) -> Self {
Self {
sign: Sign::Positive,
mag: self.mag.clone(),
}
}
#[inline]
pub fn is_zero(&self) -> bool {
self.mag.is_zero()
}
#[inline]
pub fn is_one(&self) -> bool {
self.sign == Sign::Positive && self.mag.is_one()
}
#[inline]
pub fn is_negative(&self) -> bool {
self.sign == Sign::Negative && !self.mag.is_zero()
}
#[inline]
pub fn is_positive(&self) -> bool {
self.sign == Sign::Positive && !self.mag.is_zero()
}
#[inline]
pub(crate) fn canonicalize(&mut self) {
if self.mag.is_zero() {
self.sign = Sign::Positive;
}
}
}
impl PartialEq for BigInt {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.sign == other.sign && self.mag == other.mag
}
}
impl Eq for BigInt {}
impl std::hash::Hash for BigInt {
#[inline]
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.sign.hash(state);
self.mag.hash(state);
}
}
impl Ord for BigInt {
fn cmp(&self, other: &Self) -> Ordering {
match (self.sign, other.sign) {
(Sign::Positive, Sign::Positive) => self.mag.cmp(&other.mag),
(Sign::Negative, Sign::Negative) => other.mag.cmp(&self.mag),
(Sign::Positive, Sign::Negative) => {
if other.mag.is_zero() {
Ordering::Equal
} else {
Ordering::Greater
}
}
(Sign::Negative, Sign::Positive) => {
if self.mag.is_zero() {
Ordering::Equal
} else {
Ordering::Less
}
}
}
}
}
impl PartialOrd for BigInt {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl fmt::Display for BigInt {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.sign == Sign::Negative && !self.mag.is_zero() {
f.write_str("-")?;
}
fmt::Display::fmt(&self.mag, f)
}
}
impl fmt::Debug for BigInt {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let sign_str = if self.sign == Sign::Negative && !self.mag.is_zero() {
"-"
} else {
""
};
write!(f, "BigInt({sign_str}{})", self.mag)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn zero_is_canonical_under_from_parts() {
let pz = BigInt::from_parts(Sign::Positive, BigUint::ZERO);
let nz = BigInt::from_parts(Sign::Negative, BigUint::ZERO);
assert_eq!(pz, nz);
assert_eq!(pz.sign(), Sign::Positive);
assert_eq!(nz.sign(), Sign::Positive);
}
#[test]
fn is_zero_one_negative_positive() {
assert!(BigInt::zero().is_zero());
assert!(BigInt::one().is_one());
assert!(!BigInt::zero().is_negative());
assert!(!BigInt::zero().is_positive());
let neg = BigInt::from_parts(Sign::Negative, BigUint::from_u64(7));
assert!(neg.is_negative());
assert!(!neg.is_positive());
}
#[test]
fn ord_negative_less_than_positive() {
let n = BigInt::from_parts(Sign::Negative, BigUint::from_u64(100));
let p = BigInt::from_parts(Sign::Positive, BigUint::from_u64(1));
assert!(n < p);
}
#[test]
fn ord_two_negatives_larger_mag_is_smaller() {
let a = BigInt::from_parts(Sign::Negative, BigUint::from_u64(100));
let b = BigInt::from_parts(Sign::Negative, BigUint::from_u64(1));
assert!(a < b);
}
}