mathhook_core/core/number/
arithmetic.rs

1//! Basic arithmetic operations with overflow checking
2//!
3//! Implements Add, Sub, Mul, Div, and Neg traits for Number type.
4//! Uses checked arithmetic to detect overflow and promotes to BigInt or Rational when needed.
5//! All float operations check for infinity and NaN.
6
7use super::types::Number;
8use crate::error::MathError;
9use num_bigint::BigInt;
10use num_rational::BigRational;
11use num_traits::ToPrimitive;
12use std::ops::{Add, Div, Mul, Neg, Sub};
13
14/// Addition with overflow checking and promotion to BigInt
15///
16/// # Examples
17///
18/// ```rust
19/// use mathhook_core::Number;
20///
21/// let a = Number::integer(5);
22/// let b = Number::integer(3);
23/// let result = (a + b).unwrap();
24/// assert_eq!(result, Number::integer(8));
25/// ```
26impl Add for Number {
27    type Output = Result<Number, MathError>;
28
29    fn add(self, other: Number) -> Result<Number, MathError> {
30        match (self, other) {
31            (Number::Integer(a), Number::Integer(b)) => match a.checked_add(b) {
32                Some(result) => Ok(Number::Integer(result)),
33                None => Ok(Number::BigInteger(Box::new(
34                    BigInt::from(a) + BigInt::from(b),
35                ))),
36            },
37
38            (Number::BigInteger(a), Number::BigInteger(b)) => {
39                Ok(Number::BigInteger(Box::new(*a + *b)))
40            }
41
42            (Number::Integer(i), Number::BigInteger(bi))
43            | (Number::BigInteger(bi), Number::Integer(i)) => {
44                Ok(Number::BigInteger(Box::new(*bi + BigInt::from(i))))
45            }
46
47            (Number::Rational(a), Number::Rational(b)) => Ok(Number::Rational(Box::new(*a + *b))),
48
49            (Number::Integer(i), Number::Rational(r))
50            | (Number::Rational(r), Number::Integer(i)) => {
51                let i_rational = BigRational::from(BigInt::from(i));
52                Ok(Number::Rational(Box::new(i_rational + *r)))
53            }
54
55            (Number::BigInteger(bi), Number::Rational(r))
56            | (Number::Rational(r), Number::BigInteger(bi)) => {
57                let bi_rational = BigRational::from(*bi);
58                Ok(Number::Rational(Box::new(bi_rational + *r)))
59            }
60
61            (Number::Float(a), Number::Float(b)) => {
62                let result = a + b;
63                if result.is_infinite() || result.is_nan() {
64                    Err(MathError::NumericOverflow {
65                        operation: "float addition".to_owned(),
66                    })
67                } else {
68                    Ok(Number::Float(result))
69                }
70            }
71
72            (Number::Integer(i), Number::Float(f)) | (Number::Float(f), Number::Integer(i)) => {
73                let result = i as f64 + f;
74                if result.is_infinite() || result.is_nan() {
75                    Err(MathError::NumericOverflow {
76                        operation: "integer-float addition".to_owned(),
77                    })
78                } else {
79                    Ok(Number::Float(result))
80                }
81            }
82
83            (Number::BigInteger(bi), Number::Float(f))
84            | (Number::Float(f), Number::BigInteger(bi)) => {
85                let bi_float = bi.to_f64().ok_or_else(|| MathError::NumericOverflow {
86                    operation: "BigInteger to float conversion".to_owned(),
87                })?;
88                let result = bi_float + f;
89                if result.is_infinite() || result.is_nan() {
90                    Err(MathError::NumericOverflow {
91                        operation: "BigInteger-float addition".to_owned(),
92                    })
93                } else {
94                    Ok(Number::Float(result))
95                }
96            }
97
98            (Number::Rational(r), Number::Float(f)) | (Number::Float(f), Number::Rational(r)) => {
99                let numer_float = r
100                    .numer()
101                    .to_f64()
102                    .ok_or_else(|| MathError::NumericOverflow {
103                        operation: "Rational numerator to float conversion".to_owned(),
104                    })?;
105                let denom_float = r
106                    .denom()
107                    .to_f64()
108                    .ok_or_else(|| MathError::NumericOverflow {
109                        operation: "Rational denominator to float conversion".to_owned(),
110                    })?;
111                let r_float = numer_float / denom_float;
112                let result = r_float + f;
113                if result.is_infinite() || result.is_nan() {
114                    Err(MathError::NumericOverflow {
115                        operation: "Rational-float addition".to_owned(),
116                    })
117                } else {
118                    Ok(Number::Float(result))
119                }
120            }
121        }
122    }
123}
124
125/// Subtraction with overflow checking and promotion to BigInt
126///
127/// # Examples
128///
129/// ```rust
130/// use mathhook_core::Number;
131///
132/// let a = Number::integer(10);
133/// let b = Number::integer(3);
134/// let result = (a - b).unwrap();
135/// assert_eq!(result, Number::integer(7));
136/// ```
137impl Sub for Number {
138    type Output = Result<Number, MathError>;
139
140    fn sub(self, other: Number) -> Result<Number, MathError> {
141        match (self, other) {
142            (Number::Integer(a), Number::Integer(b)) => match a.checked_sub(b) {
143                Some(result) => Ok(Number::Integer(result)),
144                None => Ok(Number::BigInteger(Box::new(
145                    BigInt::from(a) - BigInt::from(b),
146                ))),
147            },
148
149            (Number::BigInteger(a), Number::BigInteger(b)) => {
150                Ok(Number::BigInteger(Box::new(*a - *b)))
151            }
152
153            (Number::Integer(i), Number::BigInteger(bi)) => {
154                Ok(Number::BigInteger(Box::new(BigInt::from(i) - *bi)))
155            }
156
157            (Number::BigInteger(bi), Number::Integer(i)) => {
158                Ok(Number::BigInteger(Box::new(*bi - BigInt::from(i))))
159            }
160
161            (Number::Rational(a), Number::Rational(b)) => Ok(Number::Rational(Box::new(*a - *b))),
162
163            (Number::Integer(i), Number::Rational(r)) => {
164                let i_rational = BigRational::from(BigInt::from(i));
165                Ok(Number::Rational(Box::new(i_rational - *r)))
166            }
167
168            (Number::Rational(r), Number::Integer(i)) => {
169                let i_rational = BigRational::from(BigInt::from(i));
170                Ok(Number::Rational(Box::new(*r - i_rational)))
171            }
172
173            (Number::BigInteger(bi), Number::Rational(r)) => {
174                let bi_rational = BigRational::from(*bi);
175                Ok(Number::Rational(Box::new(bi_rational - *r)))
176            }
177
178            (Number::Rational(r), Number::BigInteger(bi)) => {
179                let bi_rational = BigRational::from(*bi);
180                Ok(Number::Rational(Box::new(*r - bi_rational)))
181            }
182
183            (Number::Float(a), Number::Float(b)) => {
184                let result = a - b;
185                if result.is_infinite() || result.is_nan() {
186                    Err(MathError::NumericOverflow {
187                        operation: "float subtraction".to_owned(),
188                    })
189                } else {
190                    Ok(Number::Float(result))
191                }
192            }
193
194            (Number::Integer(i), Number::Float(f)) => {
195                let result = i as f64 - f;
196                if result.is_infinite() || result.is_nan() {
197                    Err(MathError::NumericOverflow {
198                        operation: "integer-float subtraction".to_owned(),
199                    })
200                } else {
201                    Ok(Number::Float(result))
202                }
203            }
204
205            (Number::Float(f), Number::Integer(i)) => {
206                let result = f - i as f64;
207                if result.is_infinite() || result.is_nan() {
208                    Err(MathError::NumericOverflow {
209                        operation: "float-integer subtraction".to_owned(),
210                    })
211                } else {
212                    Ok(Number::Float(result))
213                }
214            }
215
216            (Number::BigInteger(bi), Number::Float(f)) => {
217                let bi_float = bi.to_f64().ok_or_else(|| MathError::NumericOverflow {
218                    operation: "BigInteger to float conversion".to_owned(),
219                })?;
220                let result = bi_float - f;
221                if result.is_infinite() || result.is_nan() {
222                    Err(MathError::NumericOverflow {
223                        operation: "BigInteger-float subtraction".to_owned(),
224                    })
225                } else {
226                    Ok(Number::Float(result))
227                }
228            }
229
230            (Number::Float(f), Number::BigInteger(bi)) => {
231                let bi_float = bi.to_f64().ok_or_else(|| MathError::NumericOverflow {
232                    operation: "BigInteger to float conversion".to_owned(),
233                })?;
234                let result = f - bi_float;
235                if result.is_infinite() || result.is_nan() {
236                    Err(MathError::NumericOverflow {
237                        operation: "float-BigInteger subtraction".to_owned(),
238                    })
239                } else {
240                    Ok(Number::Float(result))
241                }
242            }
243
244            (Number::Rational(r), Number::Float(f)) => {
245                let numer_float = r
246                    .numer()
247                    .to_f64()
248                    .ok_or_else(|| MathError::NumericOverflow {
249                        operation: "Rational numerator to float conversion".to_owned(),
250                    })?;
251                let denom_float = r
252                    .denom()
253                    .to_f64()
254                    .ok_or_else(|| MathError::NumericOverflow {
255                        operation: "Rational denominator to float conversion".to_owned(),
256                    })?;
257                let r_float = numer_float / denom_float;
258                let result = r_float - f;
259                if result.is_infinite() || result.is_nan() {
260                    Err(MathError::NumericOverflow {
261                        operation: "Rational-float subtraction".to_owned(),
262                    })
263                } else {
264                    Ok(Number::Float(result))
265                }
266            }
267
268            (Number::Float(f), Number::Rational(r)) => {
269                let numer_float = r
270                    .numer()
271                    .to_f64()
272                    .ok_or_else(|| MathError::NumericOverflow {
273                        operation: "Rational numerator to float conversion".to_owned(),
274                    })?;
275                let denom_float = r
276                    .denom()
277                    .to_f64()
278                    .ok_or_else(|| MathError::NumericOverflow {
279                        operation: "Rational denominator to float conversion".to_owned(),
280                    })?;
281                let r_float = numer_float / denom_float;
282                let result = f - r_float;
283                if result.is_infinite() || result.is_nan() {
284                    Err(MathError::NumericOverflow {
285                        operation: "float-Rational subtraction".to_owned(),
286                    })
287                } else {
288                    Ok(Number::Float(result))
289                }
290            }
291        }
292    }
293}
294
295/// Multiplication with overflow checking and promotion to BigInt
296///
297/// # Examples
298///
299/// ```rust
300/// use mathhook_core::Number;
301///
302/// let a = Number::integer(6);
303/// let b = Number::integer(7);
304/// let result = (a * b).unwrap();
305/// assert_eq!(result, Number::integer(42));
306/// ```
307impl Mul for Number {
308    type Output = Result<Number, MathError>;
309
310    fn mul(self, other: Number) -> Result<Number, MathError> {
311        match (self, other) {
312            (Number::Integer(a), Number::Integer(b)) => match a.checked_mul(b) {
313                Some(result) => Ok(Number::Integer(result)),
314                None => Ok(Number::BigInteger(Box::new(
315                    BigInt::from(a) * BigInt::from(b),
316                ))),
317            },
318
319            (Number::BigInteger(a), Number::BigInteger(b)) => {
320                Ok(Number::BigInteger(Box::new(*a * *b)))
321            }
322
323            (Number::Integer(i), Number::BigInteger(bi))
324            | (Number::BigInteger(bi), Number::Integer(i)) => {
325                Ok(Number::BigInteger(Box::new(*bi * BigInt::from(i))))
326            }
327
328            (Number::Rational(a), Number::Rational(b)) => Ok(Number::Rational(Box::new(*a * *b))),
329
330            (Number::Integer(i), Number::Rational(r))
331            | (Number::Rational(r), Number::Integer(i)) => {
332                let i_rational = BigRational::from(BigInt::from(i));
333                Ok(Number::Rational(Box::new(i_rational * *r)))
334            }
335
336            (Number::BigInteger(bi), Number::Rational(r))
337            | (Number::Rational(r), Number::BigInteger(bi)) => {
338                let bi_rational = BigRational::from(*bi);
339                Ok(Number::Rational(Box::new(bi_rational * *r)))
340            }
341
342            (Number::Float(a), Number::Float(b)) => {
343                let result = a * b;
344                if result.is_infinite() || result.is_nan() {
345                    Err(MathError::NumericOverflow {
346                        operation: "float multiplication".to_owned(),
347                    })
348                } else {
349                    Ok(Number::Float(result))
350                }
351            }
352
353            (Number::Integer(i), Number::Float(f)) | (Number::Float(f), Number::Integer(i)) => {
354                let result = i as f64 * f;
355                if result.is_infinite() || result.is_nan() {
356                    Err(MathError::NumericOverflow {
357                        operation: "integer-float multiplication".to_owned(),
358                    })
359                } else {
360                    Ok(Number::Float(result))
361                }
362            }
363
364            (Number::BigInteger(bi), Number::Float(f))
365            | (Number::Float(f), Number::BigInteger(bi)) => {
366                let bi_float = bi.to_f64().ok_or_else(|| MathError::NumericOverflow {
367                    operation: "BigInteger to float conversion".to_owned(),
368                })?;
369                let result = bi_float * f;
370                if result.is_infinite() || result.is_nan() {
371                    Err(MathError::NumericOverflow {
372                        operation: "BigInteger-float multiplication".to_owned(),
373                    })
374                } else {
375                    Ok(Number::Float(result))
376                }
377            }
378
379            (Number::Rational(r), Number::Float(f)) | (Number::Float(f), Number::Rational(r)) => {
380                let numer_float = r
381                    .numer()
382                    .to_f64()
383                    .ok_or_else(|| MathError::NumericOverflow {
384                        operation: "Rational numerator to float conversion".to_owned(),
385                    })?;
386                let denom_float = r
387                    .denom()
388                    .to_f64()
389                    .ok_or_else(|| MathError::NumericOverflow {
390                        operation: "Rational denominator to float conversion".to_owned(),
391                    })?;
392                let r_float = numer_float / denom_float;
393                let result = r_float * f;
394                if result.is_infinite() || result.is_nan() {
395                    Err(MathError::NumericOverflow {
396                        operation: "Rational-float multiplication".to_owned(),
397                    })
398                } else {
399                    Ok(Number::Float(result))
400                }
401            }
402        }
403    }
404}
405
406/// Division with division by zero check and automatic promotion to rational
407///
408/// # Examples
409///
410/// ```rust
411/// use mathhook_core::Number;
412///
413/// let a = Number::integer(10);
414/// let b = Number::integer(2);
415/// let result = (a / b).unwrap();
416/// assert_eq!(result, Number::integer(5));
417/// ```
418impl Div for Number {
419    type Output = Result<Number, MathError>;
420
421    fn div(self, other: Number) -> Result<Number, MathError> {
422        if other.is_zero() {
423            return Err(MathError::DivisionByZero);
424        }
425
426        match (self, other) {
427            (Number::Integer(a), Number::Integer(b)) => {
428                if a % b == 0 {
429                    Ok(Number::Integer(a / b))
430                } else {
431                    Ok(Number::Rational(Box::new(BigRational::new(
432                        BigInt::from(a),
433                        BigInt::from(b),
434                    ))))
435                }
436            }
437
438            (Number::BigInteger(a), Number::BigInteger(b)) => {
439                if (*a).clone() % (*b).clone() == BigInt::from(0) {
440                    Ok(Number::BigInteger(Box::new(*a / *b)))
441                } else {
442                    Ok(Number::Rational(Box::new(BigRational::new(*a, *b))))
443                }
444            }
445
446            (Number::Integer(i), Number::BigInteger(bi)) => Ok(Number::Rational(Box::new(
447                BigRational::new(BigInt::from(i), *bi),
448            ))),
449
450            (Number::BigInteger(bi), Number::Integer(i)) => Ok(Number::Rational(Box::new(
451                BigRational::new(*bi, BigInt::from(i)),
452            ))),
453
454            (Number::Rational(a), Number::Rational(b)) => Ok(Number::Rational(Box::new(*a / *b))),
455
456            (Number::Integer(i), Number::Rational(r)) => {
457                let i_rational = BigRational::from(BigInt::from(i));
458                Ok(Number::Rational(Box::new(i_rational / *r)))
459            }
460
461            (Number::Rational(r), Number::Integer(i)) => {
462                let i_rational = BigRational::from(BigInt::from(i));
463                Ok(Number::Rational(Box::new(*r / i_rational)))
464            }
465
466            (Number::BigInteger(bi), Number::Rational(r)) => {
467                let bi_rational = BigRational::from(*bi);
468                Ok(Number::Rational(Box::new(bi_rational / *r)))
469            }
470
471            (Number::Rational(r), Number::BigInteger(bi)) => {
472                let bi_rational = BigRational::from(*bi);
473                Ok(Number::Rational(Box::new(*r / bi_rational)))
474            }
475
476            (Number::Float(a), Number::Float(b)) => {
477                let result = a / b;
478                if result.is_infinite() || result.is_nan() {
479                    Err(MathError::NumericOverflow {
480                        operation: "float division".to_owned(),
481                    })
482                } else {
483                    Ok(Number::Float(result))
484                }
485            }
486
487            (Number::Integer(i), Number::Float(f)) => {
488                let result = i as f64 / f;
489                if result.is_infinite() || result.is_nan() {
490                    Err(MathError::NumericOverflow {
491                        operation: "integer-float division".to_owned(),
492                    })
493                } else {
494                    Ok(Number::Float(result))
495                }
496            }
497
498            (Number::Float(f), Number::Integer(i)) => {
499                let result = f / i as f64;
500                if result.is_infinite() || result.is_nan() {
501                    Err(MathError::NumericOverflow {
502                        operation: "float-integer division".to_owned(),
503                    })
504                } else {
505                    Ok(Number::Float(result))
506                }
507            }
508
509            (Number::BigInteger(bi), Number::Float(f)) => {
510                let bi_float = bi.to_f64().ok_or_else(|| MathError::NumericOverflow {
511                    operation: "BigInteger to float conversion".to_owned(),
512                })?;
513                let result = bi_float / f;
514                if result.is_infinite() || result.is_nan() {
515                    Err(MathError::NumericOverflow {
516                        operation: "BigInteger-float division".to_owned(),
517                    })
518                } else {
519                    Ok(Number::Float(result))
520                }
521            }
522
523            (Number::Float(f), Number::BigInteger(bi)) => {
524                let bi_float = bi.to_f64().ok_or_else(|| MathError::NumericOverflow {
525                    operation: "BigInteger to float conversion".to_owned(),
526                })?;
527                let result = f / bi_float;
528                if result.is_infinite() || result.is_nan() {
529                    Err(MathError::NumericOverflow {
530                        operation: "float-BigInteger division".to_owned(),
531                    })
532                } else {
533                    Ok(Number::Float(result))
534                }
535            }
536
537            (Number::Rational(r), Number::Float(f)) => {
538                let numer_float = r
539                    .numer()
540                    .to_f64()
541                    .ok_or_else(|| MathError::NumericOverflow {
542                        operation: "Rational numerator to float conversion".to_owned(),
543                    })?;
544                let denom_float = r
545                    .denom()
546                    .to_f64()
547                    .ok_or_else(|| MathError::NumericOverflow {
548                        operation: "Rational denominator to float conversion".to_owned(),
549                    })?;
550                let r_float = numer_float / denom_float;
551                let result = r_float / f;
552                if result.is_infinite() || result.is_nan() {
553                    Err(MathError::NumericOverflow {
554                        operation: "Rational-float division".to_owned(),
555                    })
556                } else {
557                    Ok(Number::Float(result))
558                }
559            }
560
561            (Number::Float(f), Number::Rational(r)) => {
562                let numer_float = r
563                    .numer()
564                    .to_f64()
565                    .ok_or_else(|| MathError::NumericOverflow {
566                        operation: "Rational numerator to float conversion".to_owned(),
567                    })?;
568                let denom_float = r
569                    .denom()
570                    .to_f64()
571                    .ok_or_else(|| MathError::NumericOverflow {
572                        operation: "Rational denominator to float conversion".to_owned(),
573                    })?;
574                let r_float = numer_float / denom_float;
575                let result = f / r_float;
576                if result.is_infinite() || result.is_nan() {
577                    Err(MathError::NumericOverflow {
578                        operation: "float-Rational division".to_owned(),
579                    })
580                } else {
581                    Ok(Number::Float(result))
582                }
583            }
584        }
585    }
586}
587
588/// Negation with overflow checking
589///
590/// # Examples
591///
592/// ```rust
593/// use mathhook_core::Number;
594///
595/// let a = Number::integer(5);
596/// let result = (-a).unwrap();
597/// assert_eq!(result, Number::integer(-5));
598/// ```
599impl Neg for Number {
600    type Output = Result<Number, MathError>;
601
602    fn neg(self) -> Result<Number, MathError> {
603        match self {
604            Number::Integer(i) => match i.checked_neg() {
605                Some(result) => Ok(Number::Integer(result)),
606                None => Ok(Number::BigInteger(Box::new(-BigInt::from(i)))),
607            },
608
609            Number::BigInteger(bi) => Ok(Number::BigInteger(Box::new(-*bi))),
610
611            Number::Float(f) => Ok(Number::Float(-f)),
612
613            Number::Rational(r) => Ok(Number::Rational(Box::new(-*r))),
614        }
615    }
616}