mathhook_core/core/number/
integer_ops.rs

1//! Integer arithmetic operations
2//!
3//! Implements power operations with checked arithmetic for integer bases and exponents.
4//! Uses checked arithmetic to detect overflow and promotes to BigInt when needed.
5
6use super::types::Number;
7use crate::error::MathError;
8use num_bigint::BigInt;
9
10impl Number {
11    /// Power operation with overflow checking
12    ///
13    /// Computes self raised to the power of exponent. For integer bases and positive
14    /// integer exponents, uses checked arithmetic with promotion to BigInt on overflow.
15    /// For other cases, converts to float and checks for infinity/NaN.
16    ///
17    /// # Arguments
18    ///
19    /// * `exponent` - The exponent to raise self to
20    ///
21    /// # Examples
22    ///
23    /// ```rust
24    /// use mathhook_core::Number;
25    ///
26    /// let base = Number::integer(2);
27    /// let exp = Number::integer(3);
28    /// let result = base.pow(&exp).unwrap();
29    /// assert_eq!(result, Number::integer(8));
30    /// ```
31    ///
32    /// # Errors
33    ///
34    /// Returns `MathError::NumericOverflow` if:
35    /// - Float power results in infinity or NaN
36    /// - Exponentiation produces non-representable result
37    pub fn pow(&self, exponent: &Number) -> Result<Number, MathError> {
38        match (self, exponent) {
39            (Number::Integer(base), Number::Integer(exp)) if *exp >= 0 => {
40                if *exp > u32::MAX as i64 {
41                    return Err(MathError::NumericOverflow {
42                        operation: "exponent too large for integer power".to_owned(),
43                    });
44                }
45
46                let exp_u32 = *exp as u32;
47
48                if let Some(result) = Self::checked_pow_i64(*base, exp_u32) {
49                    Ok(Number::Integer(result))
50                } else {
51                    let base_bigint = BigInt::from(*base);
52                    Ok(Number::BigInteger(Box::new(num_traits::Pow::pow(
53                        base_bigint,
54                        exp_u32,
55                    ))))
56                }
57            }
58
59            (Number::BigInteger(base), Number::Integer(exp)) if *exp >= 0 => {
60                if *exp > u32::MAX as i64 {
61                    return Err(MathError::NumericOverflow {
62                        operation: "exponent too large for BigInteger power".to_owned(),
63                    });
64                }
65                Ok(Number::BigInteger(Box::new(num_traits::Pow::pow(
66                    base.as_ref().clone(),
67                    *exp as u32,
68                ))))
69            }
70
71            _ => {
72                let base_float = self.to_float()?;
73                let exp_float = exponent.to_float()?;
74                let result = base_float.powf(exp_float);
75
76                if result.is_infinite() || result.is_nan() {
77                    Err(MathError::NumericOverflow {
78                        operation: "float power".to_owned(),
79                    })
80                } else {
81                    Ok(Number::Float(result))
82                }
83            }
84        }
85    }
86
87    /// Helper function for checked integer power
88    fn checked_pow_i64(base: i64, exp: u32) -> Option<i64> {
89        if exp == 0 {
90            return Some(1);
91        }
92
93        let mut result = 1i64;
94        let mut base = base;
95        let mut exp = exp;
96
97        while exp > 0 {
98            if exp % 2 == 1 {
99                result = result.checked_mul(base)?;
100            }
101            base = base.checked_mul(base)?;
102            exp /= 2;
103        }
104
105        Some(result)
106    }
107}