1use std::convert::TryFrom;
4use std::fmt::{Display, Formatter};
5
6use crate::result::{IonError, IonFailure};
7use crate::types::CountDecimalDigits;
8use crate::IonResult;
9use crate::{Int, UInt};
10
11#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
14pub enum Sign {
15 Negative = -1,
16 Positive = 1,
17}
18
19#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq)]
29pub struct Coefficient {
30 sign: Sign,
34 magnitude: Int,
35}
36
37impl Coefficient {
38 pub const ZERO: Coefficient = Coefficient {
39 sign: Sign::Positive,
40 magnitude: Int::ZERO,
41 };
42
43 pub const NEGATIVE_ZERO: Coefficient = Coefficient {
44 sign: Sign::Negative,
45 magnitude: Int::ZERO,
46 };
47
48 pub(crate) fn new<I: Into<Int>>(value: I) -> Self {
49 let value: Int = value.into();
50 let sign = if value.is_negative() {
51 Sign::Negative
52 } else {
53 Sign::Positive
54 };
55 Coefficient {
56 sign,
57 magnitude: value,
58 }
59 }
60
61 pub(crate) fn from_sign_and_value(sign: Sign, magnitude: impl Into<Int>) -> Self {
62 Self {
63 sign,
64 magnitude: magnitude.into(),
65 }
66 }
67
68 pub fn sign(&self) -> Sign {
69 self.sign
70 }
71
72 pub fn magnitude(&self) -> UInt {
73 self.magnitude.unsigned_abs()
74 }
75
76 pub fn is_negative(&self) -> bool {
77 self.sign == Sign::Negative
78 }
79
80 pub(crate) fn number_of_decimal_digits(&self) -> u32 {
82 self.magnitude.clone().count_decimal_digits()
83 }
84
85 pub(crate) fn negative_zero() -> Self {
87 Coefficient {
88 sign: Sign::Negative,
89 magnitude: 0u64.into(),
90 }
91 }
92
93 pub fn is_negative_zero(&self) -> bool {
95 self.is_zero_with_sign(Sign::Negative)
96 }
97
98 pub fn is_positive_zero(&self) -> bool {
100 self.is_zero_with_sign(Sign::Positive)
101 }
102
103 pub(crate) fn is_zero_with_sign(&self, test_sign: Sign) -> bool {
104 self.sign == test_sign && self.magnitude.is_zero()
105 }
106
107 pub fn is_zero(&self) -> bool {
109 self.magnitude().is_zero()
110 }
111
112 pub(crate) fn as_int(&self) -> Option<Int> {
115 if self.is_negative_zero() {
116 return None;
118 }
119 Some(self.magnitude.clone())
120 }
121}
122
123macro_rules! impl_coefficient_from_unsigned_int_types {
125 ($($t:ty),*) => ($(
126 impl From<$t> for Coefficient {
127 fn from(value: $t) -> Coefficient {
128 Coefficient::new(value)
129 }
130 }
131 )*)
132}
133impl_coefficient_from_unsigned_int_types!(u8, u16, u32, u64, u128, usize, UInt);
134
135macro_rules! impl_coefficient_from_signed_int_types {
137 ($($t:ty),*) => ($(
138 impl From<$t> for Coefficient {
139 fn from(value: $t) -> Coefficient {
140 Coefficient::new(value)
141 }
142 }
143 )*)
144}
145impl_coefficient_from_signed_int_types!(i8, i16, i32, i64, i128, isize, Int);
146
147impl TryFrom<Coefficient> for Int {
148 type Error = IonError;
149
150 fn try_from(value: Coefficient) -> Result<Self, Self::Error> {
151 if value.is_negative_zero() {
152 return IonResult::illegal_operation("cannot convert negative zero Coefficient to Int");
153 }
154 Ok(value.magnitude)
155 }
156}
157
158impl TryFrom<&Coefficient> for Int {
159 type Error = IonError;
160
161 fn try_from(value: &Coefficient) -> Result<Self, Self::Error> {
162 value.clone().try_into()
163 }
164}
165
166impl TryFrom<Coefficient> for UInt {
167 type Error = IonError;
168
169 fn try_from(value: Coefficient) -> Result<Self, Self::Error> {
170 if value.is_negative() {
171 return IonResult::illegal_operation("cannot convert a negative Coefficient to a UInt");
172 }
173 Ok(value.magnitude.unsigned_abs())
174 }
175}
176
177impl TryFrom<&Coefficient> for UInt {
178 type Error = IonError;
179
180 fn try_from(value: &Coefficient) -> Result<Self, Self::Error> {
181 value.clone().try_into()
182 }
183}
184
185impl Display for Coefficient {
186 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
187 match self.sign {
188 Sign::Positive => {}
189 Sign::Negative => write!(f, "-")?,
190 };
191 write!(f, "{}", self.magnitude)
192 }
193}
194
195#[cfg(test)]
196mod coefficient_tests {
197 use crate::ion_data::IonEq;
198 use crate::Int;
199 use crate::{Decimal, UInt};
200
201 use super::*;
202
203 fn eq_test<I1, I2>(c1: I1, c2: I2)
204 where
205 I1: Into<Coefficient>,
206 I2: Into<Coefficient>,
207 {
208 let c1 = c1.into();
209 let c2 = c2.into();
210 assert_eq!(c1, c2);
211 }
212
213 #[test]
214 fn test_coefficient_eq() {
215 eq_test(0u64, 0u64);
216 eq_test(0u64, 0i64);
217 eq_test(0i128, 0u64);
218 eq_test(0i128, 0i64);
219
220 eq_test(u64::MAX, u64::MAX);
221 eq_test(u64::MAX, i128::from(u64::MAX));
222 eq_test(i128::from(u64::MAX), u64::MAX);
223 eq_test(i128::from(u64::MAX), i128::from(u64::MAX));
224
225 eq_test(i128::MAX, i128::MAX);
226 }
227
228 #[test]
229 fn test_negative_zero_eq() {
230 let neg_zero = Decimal::new(Coefficient::negative_zero(), 0);
231 let pos_zero = Decimal::new(0, 0);
232 assert_eq!(neg_zero, neg_zero);
233 assert!(neg_zero.ion_eq(&neg_zero));
234
235 assert_eq!(neg_zero, pos_zero);
236 assert!(!neg_zero.ion_eq(&pos_zero));
237
238 assert_eq!(pos_zero, pos_zero);
239 assert!(pos_zero.ion_eq(&pos_zero));
240 }
241
242 #[test]
243 fn is_negative_zero() {
244 assert!(Coefficient::negative_zero().is_negative_zero());
245 assert!(!Coefficient::new(0).is_negative_zero());
246 assert!(!Coefficient::new(5).is_negative_zero());
247 }
248
249 #[test]
250 fn is_positive_zero() {
251 assert!(Coefficient::new(0).is_positive_zero());
252 assert!(!Coefficient::new(5).is_positive_zero());
253 assert!(!Coefficient::negative_zero().is_positive_zero());
254 }
255
256 #[test]
257 fn is_negative() {
258 assert!(Coefficient::negative_zero().is_negative());
259 assert!(Coefficient::new(-5).is_negative());
260 assert!(!Coefficient::new(5).is_negative());
261 }
262
263 #[test]
264 fn sign() {
265 assert_eq!(Coefficient::negative_zero().sign(), Sign::Negative);
266 assert_eq!(Coefficient::new(0).sign(), Sign::Positive);
267 assert_eq!(Coefficient::new(-5).sign(), Sign::Negative);
268 assert_eq!(Coefficient::new(5).sign(), Sign::Positive);
269 }
270
271 #[test]
272 fn magnitude() {
273 assert_eq!(Coefficient::negative_zero().magnitude(), UInt::from(0u32));
274 assert_eq!(Coefficient::new(0).magnitude(), UInt::from(0u32));
275 assert_eq!(Coefficient::new(-5).magnitude(), UInt::from(5u32));
276 assert_eq!(Coefficient::new(5).magnitude(), UInt::from(5u32));
277 }
278
279 #[test]
280 fn convert_to_int() {
281 assert_eq!(Int::try_from(Coefficient::new(5)), Ok(Int::from(5)));
283 assert_eq!(Int::try_from(Coefficient::new(-5)), Ok(Int::from(-5)));
284
285 let enormous_int = Int::from(12345678901234567890123456789u128);
286 assert_eq!(
287 Int::try_from(Coefficient::new(enormous_int.clone())),
288 Ok(enormous_int.clone())
289 );
290 assert_eq!(
291 Int::try_from(Coefficient::new(enormous_int.clone().neg())),
292 Ok(enormous_int.neg())
293 );
294
295 assert_eq!(Int::try_from(Coefficient::new(0)), Ok(Int::from(0)));
297 assert!(Int::try_from(Coefficient::negative_zero()).is_err());
298 }
299
300 #[test]
301 fn test_casting_sign() {
302 assert_eq!(-1, Sign::Negative as i8);
303 assert_eq!(1, Sign::Positive as i8);
304 }
305}