1use core::cmp::Ordering;
16use core::ops::{Add, Mul, Neg, Sub};
17
18use dashu_base::Sign;
19
20use crate::error::FpError;
21use crate::repr::{Repr, Word};
22use crate::utils::shl_digits;
23
24impl<const B: Word> PartialEq for Repr<B> {
25 #[inline]
28 fn eq(&self, other: &Self) -> bool {
29 if self.significand.is_zero() && other.significand.is_zero() {
30 let (self_inf, other_inf) = (self.is_infinite(), other.is_infinite());
31 match (self_inf, other_inf) {
32 (true, true) => self.sign() == other.sign(),
33 (false, false) => true, _ => false, }
36 } else {
37 self.significand == other.significand && self.exponent == other.exponent
38 }
39 }
40}
41
42impl<const B: Word> Eq for Repr<B> {}
43
44impl<const B: Word> Neg for Repr<B> {
45 type Output = Self;
46 #[inline]
47 fn neg(self) -> Self::Output {
48 Repr::neg(self)
49 }
50}
51
52impl<const B: Word> Add<&Repr<B>> for &Repr<B> {
53 type Output = Repr<B>;
54
55 #[inline]
56 fn add(self, rhs: &Repr<B>) -> Repr<B> {
57 debug_assert!(self.is_finite());
58 debug_assert!(rhs.is_finite());
59
60 if self.significand.is_zero() {
65 return rhs.clone();
66 }
67 if rhs.significand.is_zero() {
68 return self.clone();
69 }
70
71 match self.exponent.cmp(&rhs.exponent) {
74 Ordering::Equal => Repr::new(&self.significand + &rhs.significand, self.exponent),
75 Ordering::Greater => Repr::new(
76 shl_digits::<B>(&self.significand, (self.exponent - rhs.exponent) as usize)
77 + &rhs.significand,
78 rhs.exponent,
79 ),
80 Ordering::Less => Repr::new(
81 &self.significand
82 + shl_digits::<B>(&rhs.significand, (rhs.exponent - self.exponent) as usize),
83 self.exponent,
84 ),
85 }
86 }
87}
88
89impl<const B: Word> Add<&Repr<B>> for Repr<B> {
90 type Output = Repr<B>;
91 #[inline]
92 fn add(self, rhs: &Repr<B>) -> Repr<B> {
93 (&self) + rhs
94 }
95}
96impl<const B: Word> Add<Repr<B>> for &Repr<B> {
97 type Output = Repr<B>;
98 #[inline]
99 fn add(self, rhs: Repr<B>) -> Repr<B> {
100 self + &rhs
101 }
102}
103impl<const B: Word> Add<Repr<B>> for Repr<B> {
104 type Output = Repr<B>;
105 #[inline]
106 fn add(self, rhs: Repr<B>) -> Repr<B> {
107 (&self) + &rhs
108 }
109}
110
111impl<const B: Word> Sub<&Repr<B>> for &Repr<B> {
112 type Output = Repr<B>;
113
114 #[inline]
115 fn sub(self, rhs: &Repr<B>) -> Repr<B> {
116 debug_assert!(self.is_finite());
117 debug_assert!(rhs.is_finite());
118
119 if rhs.significand.is_zero() {
122 return self.clone();
123 }
124 if self.significand.is_zero() {
125 return rhs.clone().neg();
126 }
127
128 match self.exponent.cmp(&rhs.exponent) {
130 Ordering::Equal => Repr::new(&self.significand - &rhs.significand, self.exponent),
131 Ordering::Greater => Repr::new(
132 shl_digits::<B>(&self.significand, (self.exponent - rhs.exponent) as usize)
133 - &rhs.significand,
134 rhs.exponent,
135 ),
136 Ordering::Less => Repr::new(
137 &self.significand
138 - shl_digits::<B>(&rhs.significand, (rhs.exponent - self.exponent) as usize),
139 self.exponent,
140 ),
141 }
142 }
143}
144
145impl<const B: Word> Sub<&Repr<B>> for Repr<B> {
146 type Output = Repr<B>;
147 #[inline]
148 fn sub(self, rhs: &Repr<B>) -> Repr<B> {
149 (&self) - rhs
150 }
151}
152impl<const B: Word> Sub<Repr<B>> for &Repr<B> {
153 type Output = Repr<B>;
154 #[inline]
155 fn sub(self, rhs: Repr<B>) -> Repr<B> {
156 self - &rhs
157 }
158}
159impl<const B: Word> Sub<Repr<B>> for Repr<B> {
160 type Output = Repr<B>;
161 #[inline]
162 fn sub(self, rhs: Repr<B>) -> Repr<B> {
163 (&self) - &rhs
164 }
165}
166
167impl<const B: Word> Mul<&Repr<B>> for &Repr<B> {
168 type Output = Repr<B>;
169
170 #[inline]
171 fn mul(self, rhs: &Repr<B>) -> Repr<B> {
172 debug_assert!(self.is_finite());
173 debug_assert!(rhs.is_finite());
174
175 let significand = &self.significand * &rhs.significand;
176 if significand.is_zero() {
177 return if self.sign() != rhs.sign() {
179 Repr::neg_zero()
180 } else {
181 Repr::zero()
182 };
183 }
184 let sign = if self.sign() != rhs.sign() {
185 Sign::Negative
186 } else {
187 Sign::Positive
188 };
189 let exponent = match self.exponent.checked_add(rhs.exponent) {
193 Some(e) => e,
194 None => {
195 debug_assert!(
196 self.exponent.is_positive() == rhs.exponent.is_positive(),
197 "checked_add overflow with mixed-sign exponents is impossible"
198 );
199 return if self.exponent > 0 {
200 Repr::infinity_with_sign(sign)
201 } else {
202 Repr::zero_with_sign(sign)
203 };
204 }
205 };
206 match Repr::new(significand, exponent).check_finite_exponent() {
207 Ok(r) => r,
208 Err(FpError::Overflow(s)) => Repr::infinity_with_sign(s),
209 Err(FpError::Underflow(s)) => Repr::zero_with_sign(s),
210 Err(_) => unreachable!(),
211 }
212 }
213}
214
215impl<const B: Word> Mul<&Repr<B>> for Repr<B> {
216 type Output = Repr<B>;
217 #[inline]
218 fn mul(self, rhs: &Repr<B>) -> Repr<B> {
219 (&self) * rhs
220 }
221}
222impl<const B: Word> Mul<Repr<B>> for &Repr<B> {
223 type Output = Repr<B>;
224 #[inline]
225 fn mul(self, rhs: Repr<B>) -> Repr<B> {
226 self * &rhs
227 }
228}
229impl<const B: Word> Mul<Repr<B>> for Repr<B> {
230 type Output = Repr<B>;
231 #[inline]
232 fn mul(self, rhs: Repr<B>) -> Repr<B> {
233 (&self) * &rhs
234 }
235}
236
237#[cfg(test)]
238mod tests {
239 use super::*;
240 use dashu_int::IBig;
241
242 fn r<const B: Word>(sig: i128, exp: isize) -> Repr<B> {
243 Repr::new(IBig::from(sig), exp)
244 }
245
246 #[test]
247 fn add_same_exponent() {
248 assert_eq!(&r::<10>(3, 0) + &r::<10>(4, 0), r::<10>(7, 0));
249 }
250
251 #[test]
252 fn add_aligns_exponents() {
253 assert_eq!(&r::<10>(3, 0) + &r::<10>(4, -1), r::<10>(34, -1));
255 assert_eq!(&r::<10>(1, 0) + &r::<10>(1, -5), r::<10>(100001, -5));
257 }
258
259 #[test]
260 fn add_neg_zero_is_identity() {
261 let nz = Repr::<10>::neg_zero();
262 let x = r::<10>(5, -2);
263 assert_eq!(&nz + &x, x);
264 assert_eq!(&x + &nz, x);
265 assert_eq!(&nz + &nz, Repr::<10>::neg_zero());
267 }
268
269 #[test]
270 fn add_cancellation_is_positive_zero() {
271 let z = &r::<10>(1, 0) + &r::<10>(-1, 0);
272 assert_eq!(z, Repr::<10>::zero());
273 assert!(z.is_pos_zero());
274 }
275
276 #[test]
277 fn sub_basic() {
278 assert_eq!(&r::<10>(5, 0) - &r::<10>(3, 0), r::<10>(2, 0));
279 assert_eq!(&r::<10>(3, 0) - &r::<10>(5, 0), r::<10>(-2, 0));
280 assert_eq!(&r::<10>(5, 0) - &Repr::<10>::zero(), r::<10>(5, 0));
282 }
283
284 #[test]
285 fn mul_basic() {
286 assert_eq!(&r::<10>(3, 0) * &r::<10>(4, 0), r::<10>(12, 0));
287 assert_eq!(&r::<10>(3, 2) * &r::<10>(2, -1), r::<10>(6, 1));
289 assert_eq!(&r::<2>(3, 0) * &r::<2>(3, 0), r::<2>(9, 0));
290 }
291
292 #[test]
293 fn mul_zero_product_sign() {
294 assert_eq!(&Repr::<10>::zero() * &r::<10>(5, 0), Repr::<10>::zero());
296 let prod = &Repr::<10>::zero() * &r::<10>(-5, 0);
298 assert!(prod.is_neg_zero());
299 }
300
301 #[test]
302 fn ref_val_combos() {
303 let a = r::<10>(2, 0);
304 let b = r::<10>(3, 0);
305 assert_eq!(&a + &b, r::<10>(5, 0));
306 assert_eq!(a.clone() + &b, r::<10>(5, 0));
307 assert_eq!(&a + b.clone(), r::<10>(5, 0));
308 assert_eq!(a.clone() + b.clone(), r::<10>(5, 0));
309
310 let c = r::<10>(7, 0);
311 let d = r::<10>(4, 0);
312 assert_eq!(&c - &d, r::<10>(3, 0));
313 assert_eq!(c.clone() - &d, r::<10>(3, 0));
314 assert_eq!(&c - d.clone(), r::<10>(3, 0));
315 assert_eq!(c.clone() - d.clone(), r::<10>(3, 0));
316
317 let e = r::<10>(6, 0);
318 let f = r::<10>(7, 0);
319 assert_eq!(&e * &f, r::<10>(42, 0));
320 assert_eq!(e.clone() * &f, r::<10>(42, 0));
321 assert_eq!(&e * f.clone(), r::<10>(42, 0));
322 assert_eq!(e.clone() * f.clone(), r::<10>(42, 0));
323 }
324}