qfall_math/rational/q/arithmetic/
pow.rs1use crate::{error::MathError, integer::Z, rational::Q, traits::Pow};
12use flint_sys::fmpq::fmpq_pow_fmpz;
13
14impl<Integer: Into<Z>> Pow<Integer> for Q {
15 type Output = Q;
16
17 fn pow(&self, exp: Integer) -> Result<Self::Output, MathError> {
41 let exp = exp.into();
42 if self == &Q::ZERO && exp < Z::ZERO {
43 return Err(MathError::InvalidExponent(format!(
44 "A negative exponent {exp} was used for a zero value. There's no inverse for zero values."
45 )));
46 }
47
48 let mut out = Q::ZERO;
49 unsafe { fmpq_pow_fmpz(&mut out.value, &self.value, &exp.value) };
50 Ok(out)
51 }
52}
53
54#[cfg(test)]
55mod test_pow {
56 use super::*;
57
58 #[test]
60 fn zero() {
61 let zero = Q::ZERO;
62
63 assert_eq!(Q::ONE, zero.pow(0).unwrap());
64 assert_eq!(Q::ZERO, zero.pow(1).unwrap());
65 assert!(zero.pow(-1).is_err());
66 }
67
68 #[test]
70 fn one() {
71 let base_pos = Q::ONE;
72 let base_neg = Q::MINUS_ONE;
73
74 assert_eq!(Q::ONE, base_pos.pow(0).unwrap());
75 assert_eq!(Q::ONE, base_pos.pow(1).unwrap());
76 assert_eq!(Q::ONE, base_pos.pow(-2).unwrap());
77 assert_eq!(Q::ONE, base_pos.pow(5).unwrap());
78 assert_eq!(Q::ONE, base_neg.pow(0).unwrap());
79 assert_eq!(Q::MINUS_ONE, base_neg.pow(1).unwrap());
80 assert_eq!(Q::ONE, base_neg.pow(-2).unwrap());
81 assert_eq!(Q::MINUS_ONE, base_neg.pow(5).unwrap());
82 }
83
84 #[test]
86 fn small() {
87 let base_0 = Q::from(2);
88 let base_1 = Q::from((1, 2));
89 let exp_pos = Z::from(4);
90
91 let res_0 = base_0.pow(&exp_pos).unwrap();
92 let res_1 = base_0.pow(0).unwrap();
93 let res_2 = base_1.pow(&exp_pos).unwrap();
94 let res_3 = base_1.pow(0).unwrap();
95
96 assert_eq!(Q::from(16), res_0);
97 assert_eq!(Q::ONE, res_1);
98 assert_eq!(Q::from((1, 16)), res_2);
99 assert_eq!(Q::ONE, res_3);
100 }
101
102 #[test]
104 fn large() {
105 let base_0 = Q::from(i64::MIN);
106 let base_1 = Q::from((1, i64::MIN));
107 let exp_pos = Z::from(3);
108 let cmp_0 = &base_0 * &base_0 * &base_0;
109 let cmp_1 = &base_1 * &base_1 * &base_1;
110
111 let res_0 = base_0.pow(&exp_pos).unwrap();
112 let res_1 = base_0.pow(0).unwrap();
113 let res_2 = base_0.pow(-1).unwrap();
114 let res_3 = base_1.pow(&exp_pos).unwrap();
115 let res_4 = base_1.pow(0).unwrap();
116 let res_5 = base_1.pow(-1).unwrap();
117
118 assert_eq!(cmp_0, res_0);
119 assert_eq!(Q::ONE, res_1);
120 assert_eq!(Q::from((1, i64::MIN)), res_2);
121 assert_eq!(cmp_1, res_3);
122 assert_eq!(Q::ONE, res_4);
123 assert_eq!(Q::from(i64::MIN), res_5);
124 }
125
126 #[test]
128 fn availability() {
129 let base = Q::from(i64::MAX);
130 let exp = Z::from(4);
131
132 let _ = base.pow(exp);
133 let _ = base.pow(2_i8);
134 let _ = base.pow(2_i16);
135 let _ = base.pow(2_i32);
136 let _ = base.pow(2_i64);
137 let _ = base.pow(2_u8);
138 let _ = base.pow(2_u16);
139 let _ = base.pow(2_u32);
140 let _ = base.pow(2_u64);
141 }
142
143 #[test]
146 fn non_invertible_detection() {
147 let base = Q::ZERO;
148
149 assert!(base.pow(-1).is_err());
150 }
151}