qfall_math/integer/z/arithmetic/pow.rs
1// Copyright © 2023 Niklas Siemer
2//
3// This file is part of qFALL-math.
4//
5// qFALL-math is free software: you can redistribute it and/or modify it under
6// the terms of the Mozilla Public License Version 2.0 as published by the
7// Mozilla Foundation. See <https://mozilla.org/en-US/MPL/2.0/>.
8
9//! This module provides an implementation of the [`Pow`] trait for [`Z`].
10
11use crate::{error::MathError, integer::Z, traits::Pow};
12use flint_sys::fmpz::fmpz_pow_fmpz;
13
14impl<Integer: Into<Z>> Pow<Integer> for Z {
15 type Output = Z;
16
17 /// Raises the value of `self` to the power of an integer `exp`.
18 ///
19 /// Parameters:
20 /// - `exp`: specifies the exponent to which the value is raised
21 ///
22 /// Returns the value of `self` powered by `exp` as a new [`Z`] instance
23 /// or an error if the provided exponent is negative and the base value of `self` is not invertible.
24 ///
25 /// # Examples
26 /// ```
27 /// use qfall_math::integer::Z;
28 /// use qfall_math::traits::*;
29 ///
30 /// let base = Z::from(9);
31 ///
32 /// let powered_value = base.pow(3).unwrap();
33 ///
34 /// assert_eq!(Z::from(729), powered_value);
35 /// ```
36 ///
37 /// # Errors and Failures
38 /// - Returns a [`MathError`] of type [`InvalidExponent`](MathError::InvalidExponent)
39 /// if the provided exponent is negative and the base value of `self` is not invertible.
40 fn pow(&self, exp: Integer) -> Result<Self::Output, MathError> {
41 let exp = exp.into();
42 let mut out = Z::ZERO;
43 if exp < Z::ZERO {
44 return Err(MathError::InvalidExponent(format!(
45 "A negative exponent {exp} was used for the integer value {self}.
46 If you want to get the inverse as a rational object in return use `.inverse().pow({})`",
47 -1 * &exp
48 )));
49 }
50 unsafe { fmpz_pow_fmpz(&mut out.value, &self.value, &exp.value) };
51 Ok(out)
52 }
53}
54
55#[cfg(test)]
56mod test_pow {
57 use super::*;
58
59 /// Ensure that `pow` works for [`Z`] properly for small and zero values
60 #[test]
61 fn small() {
62 let base = Z::from(2);
63 let exp_pos = Z::from(4);
64 let zero = Z::ZERO;
65
66 let res_0 = base.pow(&exp_pos).unwrap();
67 let res_1 = base.pow(&zero).unwrap();
68 let res_2 = zero.pow(&zero).unwrap();
69 let res_3 = zero.pow(&exp_pos).unwrap();
70
71 assert_eq!(Z::from(16), res_0);
72 assert_eq!(Z::from(1), res_1);
73 assert_eq!(Z::ONE, res_2);
74 assert_eq!(Z::ZERO, res_3);
75 }
76
77 /// Ensure that `pow` works for [`Z`] properly for large values
78 #[test]
79 fn large() {
80 let base = Z::from(i64::MIN);
81 let exp_pos = Z::from(3);
82 let zero = Z::ZERO;
83 let cmp = &base * &base * &base;
84
85 let res_0 = base.pow(&exp_pos).unwrap();
86 let res_1 = base.pow(&zero).unwrap();
87
88 assert_eq!(cmp, res_0);
89 assert_eq!(Z::ONE, res_1);
90 }
91
92 /// Ensures that the `pow` trait is available for other types
93 #[test]
94 fn availability() {
95 let base = Z::from(i64::MAX);
96 let exp = Z::from(4);
97
98 let _ = base.pow(exp);
99 let _ = base.pow(2_i8);
100 let _ = base.pow(2_i16);
101 let _ = base.pow(2_i32);
102 let _ = base.pow(2_i64);
103 let _ = base.pow(2_u8);
104 let _ = base.pow(2_u16);
105 let _ = base.pow(2_u32);
106 let _ = base.pow(2_u64);
107 }
108
109 /// Ensures that `pow` returns an error if a non-invertible basis is
110 /// powered by a negative exponent
111 #[test]
112 fn non_invertible_detection() {
113 let base_0 = Z::from(4);
114 let base_1 = Z::from(u64::MAX);
115
116 assert!(base_0.pow(-1).is_err());
117 assert!(base_1.pow(-1).is_err());
118 }
119}