use std::convert::TryFrom;
use std::fmt::{Display, Formatter};
use crate::result::{IonError, IonFailure};
use crate::types::CountDecimalDigits;
use crate::IonResult;
use crate::{Int, UInt};
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub enum Sign {
Negative = -1,
Positive = 1,
}
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub struct Coefficient {
sign: Sign,
magnitude: Int,
}
impl Coefficient {
pub const ZERO: Coefficient = Coefficient {
sign: Sign::Positive,
magnitude: Int::ZERO,
};
pub const NEGATIVE_ZERO: Coefficient = Coefficient {
sign: Sign::Negative,
magnitude: Int::ZERO,
};
pub(crate) fn new<I: Into<Int>>(value: I) -> Self {
let value: Int = value.into();
let sign = if value.is_negative() {
Sign::Negative
} else {
Sign::Positive
};
Coefficient {
sign,
magnitude: value,
}
}
pub(crate) fn from_sign_and_value(sign: Sign, magnitude: impl Into<Int>) -> Self {
Self {
sign,
magnitude: magnitude.into(),
}
}
pub fn sign(&self) -> Sign {
self.sign
}
pub fn magnitude(&self) -> UInt {
self.magnitude.unsigned_abs()
}
pub fn is_negative(&self) -> bool {
self.sign == Sign::Negative
}
pub(crate) fn number_of_decimal_digits(&self) -> u32 {
self.magnitude.clone().count_decimal_digits()
}
pub(crate) fn negative_zero() -> Self {
Coefficient {
sign: Sign::Negative,
magnitude: 0u64.into(),
}
}
pub fn is_negative_zero(&self) -> bool {
self.is_zero_with_sign(Sign::Negative)
}
pub fn is_positive_zero(&self) -> bool {
self.is_zero_with_sign(Sign::Positive)
}
pub(crate) fn is_zero_with_sign(&self, test_sign: Sign) -> bool {
self.sign == test_sign && self.magnitude.is_zero()
}
pub fn is_zero(&self) -> bool {
self.magnitude().is_zero()
}
pub(crate) fn as_int(&self) -> Option<Int> {
if self.is_negative_zero() {
return None;
}
Some(self.magnitude.clone())
}
}
macro_rules! impl_coefficient_from_unsigned_int_types {
($($t:ty),*) => ($(
impl From<$t> for Coefficient {
fn from(value: $t) -> Coefficient {
Coefficient::new(value)
}
}
)*)
}
impl_coefficient_from_unsigned_int_types!(u8, u16, u32, u64, u128, usize, UInt);
macro_rules! impl_coefficient_from_signed_int_types {
($($t:ty),*) => ($(
impl From<$t> for Coefficient {
fn from(value: $t) -> Coefficient {
Coefficient::new(value)
}
}
)*)
}
impl_coefficient_from_signed_int_types!(i8, i16, i32, i64, i128, isize, Int);
impl TryFrom<Coefficient> for Int {
type Error = IonError;
fn try_from(value: Coefficient) -> Result<Self, Self::Error> {
if value.is_negative_zero() {
return IonResult::illegal_operation("cannot convert negative zero Coefficient to Int");
}
Ok(value.magnitude)
}
}
impl TryFrom<&Coefficient> for Int {
type Error = IonError;
fn try_from(value: &Coefficient) -> Result<Self, Self::Error> {
value.clone().try_into()
}
}
impl TryFrom<Coefficient> for UInt {
type Error = IonError;
fn try_from(value: Coefficient) -> Result<Self, Self::Error> {
if value.is_negative() {
return IonResult::illegal_operation("cannot convert a negative Coefficient to a UInt");
}
Ok(value.magnitude.unsigned_abs())
}
}
impl TryFrom<&Coefficient> for UInt {
type Error = IonError;
fn try_from(value: &Coefficient) -> Result<Self, Self::Error> {
value.clone().try_into()
}
}
impl Display for Coefficient {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self.sign {
Sign::Positive => {}
Sign::Negative => write!(f, "-")?,
};
write!(f, "{}", self.magnitude)
}
}
#[cfg(test)]
mod coefficient_tests {
use crate::ion_data::IonEq;
use crate::Int;
use crate::{Decimal, UInt};
use super::*;
fn eq_test<I1, I2>(c1: I1, c2: I2)
where
I1: Into<Coefficient>,
I2: Into<Coefficient>,
{
let c1 = c1.into();
let c2 = c2.into();
assert_eq!(c1, c2);
}
#[test]
fn test_coefficient_eq() {
eq_test(0u64, 0u64);
eq_test(0u64, 0i64);
eq_test(0i128, 0u64);
eq_test(0i128, 0i64);
eq_test(u64::MAX, u64::MAX);
eq_test(u64::MAX, i128::from(u64::MAX));
eq_test(i128::from(u64::MAX), u64::MAX);
eq_test(i128::from(u64::MAX), i128::from(u64::MAX));
eq_test(i128::MAX, i128::MAX);
}
#[test]
fn test_negative_zero_eq() {
let neg_zero = Decimal::new(Coefficient::negative_zero(), 0);
let pos_zero = Decimal::new(0, 0);
assert_eq!(neg_zero, neg_zero);
assert!(neg_zero.ion_eq(&neg_zero));
assert_eq!(neg_zero, pos_zero);
assert!(!neg_zero.ion_eq(&pos_zero));
assert_eq!(pos_zero, pos_zero);
assert!(pos_zero.ion_eq(&pos_zero));
}
#[test]
fn is_negative_zero() {
assert!(Coefficient::negative_zero().is_negative_zero());
assert!(!Coefficient::new(0).is_negative_zero());
assert!(!Coefficient::new(5).is_negative_zero());
}
#[test]
fn is_positive_zero() {
assert!(Coefficient::new(0).is_positive_zero());
assert!(!Coefficient::new(5).is_positive_zero());
assert!(!Coefficient::negative_zero().is_positive_zero());
}
#[test]
fn is_negative() {
assert!(Coefficient::negative_zero().is_negative());
assert!(Coefficient::new(-5).is_negative());
assert!(!Coefficient::new(5).is_negative());
}
#[test]
fn sign() {
assert_eq!(Coefficient::negative_zero().sign(), Sign::Negative);
assert_eq!(Coefficient::new(0).sign(), Sign::Positive);
assert_eq!(Coefficient::new(-5).sign(), Sign::Negative);
assert_eq!(Coefficient::new(5).sign(), Sign::Positive);
}
#[test]
fn magnitude() {
assert_eq!(Coefficient::negative_zero().magnitude(), UInt::from(0u32));
assert_eq!(Coefficient::new(0).magnitude(), UInt::from(0u32));
assert_eq!(Coefficient::new(-5).magnitude(), UInt::from(5u32));
assert_eq!(Coefficient::new(5).magnitude(), UInt::from(5u32));
}
#[test]
fn convert_to_int() {
assert_eq!(Int::try_from(Coefficient::new(5)), Ok(Int::from(5)));
assert_eq!(Int::try_from(Coefficient::new(-5)), Ok(Int::from(-5)));
let enormous_int = Int::from(12345678901234567890123456789u128);
assert_eq!(
Int::try_from(Coefficient::new(enormous_int.clone())),
Ok(enormous_int.clone())
);
assert_eq!(
Int::try_from(Coefficient::new(enormous_int.clone().neg())),
Ok(enormous_int.neg())
);
assert_eq!(Int::try_from(Coefficient::new(0)), Ok(Int::from(0)));
assert!(Int::try_from(Coefficient::negative_zero()).is_err());
}
#[test]
fn test_casting_sign() {
assert_eq!(-1, Sign::Negative as i8);
assert_eq!(1, Sign::Positive as i8);
}
}