1use num_bigint::{BigInt, BigUint};
2use num_traits::Zero;
3
4use crate::result::{illegal_operation, IonError};
5use crate::types::UInt;
6use std::convert::TryFrom;
7use std::fmt::{Display, Formatter};
8use std::ops::{MulAssign, Neg};
9
10#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
13pub enum Sign {
14 Negative,
15 Positive,
16}
17
18#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq)]
23pub struct Coefficient {
24 pub(crate) sign: Sign,
25 pub(crate) magnitude: UInt,
26}
27
28impl Coefficient {
29 pub(crate) fn new<I: Into<UInt>>(sign: Sign, magnitude: I) -> Self {
30 let magnitude = magnitude.into();
31 Coefficient { sign, magnitude }
32 }
33
34 pub(crate) fn sign(&self) -> Sign {
35 self.sign
36 }
37
38 pub(crate) fn magnitude(&self) -> &UInt {
39 &self.magnitude
40 }
41
42 pub(crate) fn number_of_decimal_digits(&self) -> u64 {
44 self.magnitude.number_of_decimal_digits()
45 }
46
47 pub(crate) fn negative_zero() -> Self {
49 Coefficient {
50 sign: Sign::Negative,
51 magnitude: UInt::U64(0),
52 }
53 }
54
55 pub(crate) fn is_negative_zero(&self) -> bool {
57 match (self.sign, &self.magnitude) {
58 (Sign::Negative, UInt::U64(0)) => true,
59 (Sign::Negative, UInt::BigUInt(b)) if b.is_zero() => true,
60 _ => false,
61 }
62 }
63
64 pub(crate) fn is_positive_zero(&self) -> bool {
66 match (self.sign, &self.magnitude) {
67 (Sign::Positive, UInt::U64(0)) => true,
68 (Sign::Positive, UInt::BigUInt(b)) if b.is_zero() => true,
69 _ => false,
70 }
71 }
72
73 pub(crate) fn is_zero(&self) -> bool {
75 match (self.sign, &self.magnitude) {
76 (_, UInt::U64(0)) => true,
77 (_, UInt::BigUInt(b)) if b.is_zero() => true,
78 _ => false,
79 }
80 }
81
82 pub(crate) fn as_i64(&self) -> Option<i64> {
85 match self.magnitude {
86 UInt::U64(unsigned) => match i64::try_from(unsigned) {
87 Ok(signed) => match self.sign {
88 Sign::Negative => Some(signed.neg()), Sign::Positive => Some(signed),
90 },
91 Err(_) => None,
92 },
93 UInt::BigUInt(_) => None,
94 }
95 }
96}
97
98macro_rules! impl_coefficient_from_unsigned_int_types {
100 ($($t:ty),*) => ($(
101 impl From<$t> for Coefficient {
102 fn from(value: $t) -> Coefficient {
103 Coefficient::new(Sign::Positive, value)
104 }
105 }
106 )*)
107}
108impl_coefficient_from_unsigned_int_types!(u8, u16, u32, u64, u128, usize, BigUint);
109
110macro_rules! impl_coefficient_from_signed_int_types {
112 ($($t:ty),*) => ($(
113 impl From<$t> for Coefficient {
114 fn from(value: $t) -> Coefficient {
115 let sign = if value < <$t>::zero() { Sign::Negative } else { Sign::Positive };
116 Coefficient::new(sign, value)
117 }
118 }
119 )*)
120}
121impl_coefficient_from_signed_int_types!(i8, i16, i32, i64, i128, isize);
122
123impl TryFrom<Coefficient> for BigInt {
125 type Error = IonError;
126
127 fn try_from(value: Coefficient) -> Result<Self, Self::Error> {
130 if value.is_negative_zero() {
131 illegal_operation("Cannot convert negative zero Decimal to BigInt")?;
132 }
133 let mut big_int: BigInt = match value.magnitude {
134 UInt::U64(m) => m.into(),
135 UInt::BigUInt(m) => m.into(),
136 };
137 if value.sign == Sign::Negative {
138 big_int.mul_assign(-1);
139 }
140 Ok(big_int)
141 }
142}
143
144impl TryFrom<BigInt> for Coefficient {
145 type Error = IonError;
146
147 fn try_from(value: BigInt) -> Result<Self, Self::Error> {
148 let (sign, magnitude) = value.into_parts();
149 let sign = match sign {
150 num_bigint::Sign::Minus => Sign::Negative,
151 num_bigint::Sign::Plus => Sign::Positive,
152 num_bigint::Sign::NoSign => {
153 if magnitude.is_zero() {
154 Sign::Positive
155 } else {
156 return illegal_operation(
157 "Cannot convert sign-less non-zero BigInt to Decimal.",
158 );
159 }
160 }
161 };
162 Ok(Coefficient::new(sign, magnitude))
163 }
164}
165
166impl Display for Coefficient {
167 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
168 match self.sign {
169 Sign::Positive => {}
170 Sign::Negative => write!(f, "-")?,
171 };
172 match &self.magnitude {
173 UInt::U64(m) => write!(f, "{}", *m),
174 UInt::BigUInt(m) => write!(f, "{m}"),
175 }
176 }
177}
178
179#[cfg(test)]
180mod coefficient_tests {
181 use crate::ion_data::IonEq;
182 use num_bigint::BigUint;
183
184 use crate::types::{Coefficient, Decimal};
185
186 fn eq_test<I1, I2>(c1: I1, c2: I2)
187 where
188 I1: Into<Coefficient>,
189 I2: Into<Coefficient>,
190 {
191 let c1 = c1.into();
192 let c2 = c2.into();
193 assert_eq!(c1, c2);
194 }
195
196 #[test]
197 fn test_coefficient_eq() {
198 eq_test(0u64, 0u64);
199 eq_test(0u64, BigUint::from(0u64));
200 eq_test(BigUint::from(0u64), 0u64);
201 eq_test(BigUint::from(0u64), BigUint::from(0u64));
202
203 eq_test(u64::MAX, u64::MAX);
204 eq_test(u64::MAX, BigUint::from(u64::MAX));
205 eq_test(BigUint::from(u64::MAX), u64::MAX);
206 eq_test(BigUint::from(u64::MAX), BigUint::from(u64::MAX));
207
208 eq_test(BigUint::from(u128::MAX), BigUint::from(u128::MAX));
209 }
210
211 #[test]
212 fn test_negative_zero_eq() {
213 let neg_zero = Decimal::new(Coefficient::negative_zero(), 0);
214 let pos_zero = Decimal::new(0, 0);
215 assert_eq!(neg_zero, neg_zero);
216 assert!(neg_zero.ion_eq(&neg_zero));
217
218 assert_eq!(neg_zero, pos_zero);
219 assert!(!neg_zero.ion_eq(&pos_zero));
220
221 assert_eq!(pos_zero, pos_zero);
222 assert!(pos_zero.ion_eq(&pos_zero));
223 }
224}