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}