qfall_math/integer_mod_q/z_q/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 [`Zq`].
10
11use crate::{error::MathError, integer::Z, integer_mod_q::Zq, traits::Pow};
12use flint_sys::fmpz_mod::fmpz_mod_pow_fmpz;
13
14impl<Integer: Into<Z>> Pow<Integer> for Zq {
15 type Output = Zq;
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 `Output` instance
23 /// or an error if the 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::integer_mod_q::Zq;
29 /// use qfall_math::traits::*;
30 ///
31 /// let base = Zq::from((2, 9));
32 ///
33 /// let powered_value = base.pow(4).unwrap();
34 ///
35 /// let cmp = Zq::from((7, 9));
36 /// assert_eq!(cmp, powered_value);
37 /// ```
38 ///
39 /// # Errors and Failures
40 /// - Returns a [`MathError`] of type [`InvalidExponent`](MathError::InvalidExponent)
41 /// if the provided exponent is negative and the base value of `self` is not invertible.
42 fn pow(&self, exp: Integer) -> Result<Self::Output, MathError> {
43 let exp = exp.into();
44 let mut out = self.clone();
45 if 0 == unsafe {
46 fmpz_mod_pow_fmpz(
47 &mut out.value.value,
48 &self.value.value,
49 &exp.value,
50 self.modulus.get_fmpz_mod_ctx_struct(),
51 )
52 } {
53 return Err(MathError::InvalidExponent(format!(
54 "The negative exponent {exp} was used for a non-invertible base {self}"
55 )));
56 }
57 Ok(out)
58 }
59}
60
61#[cfg(test)]
62mod test_pow {
63 use super::*;
64
65 /// Ensure that `pow` works for [`Zq`] properly for small values
66 #[test]
67 fn small() {
68 let base = Zq::from((2, 9));
69 let exp_0 = Z::from(4);
70 let exp_1 = Z::ZERO;
71 let exp_2 = Z::from(-2);
72
73 let res_0 = base.pow(&exp_0).unwrap();
74 let res_1 = base.pow(&exp_1).unwrap();
75 let res_2 = base.pow(&exp_2).unwrap();
76
77 assert_eq!(Zq::from((7, 9)), res_0);
78 assert_eq!(Zq::from((1, 9)), res_1);
79 assert_eq!(Zq::from((7, 9)), res_2);
80 }
81
82 /// Ensure that `pow` works for [`Zq`] properly for large values
83 #[test]
84 fn large() {
85 let base = Zq::from((i64::MAX, u64::MAX));
86 let exp_0 = Z::from(4);
87 let exp_1 = Z::ZERO;
88 let exp_2 = Z::from(-1);
89 let cmp_0 = Zq::from((1152921504606846976_i64, u64::MAX));
90 let cmp_1 = Zq::from((1, u64::MAX));
91 let cmp_2 = Zq::from((18446744073709551613_u64, u64::MAX));
92
93 let res_0 = base.pow(&exp_0).unwrap();
94 let res_1 = base.pow(&exp_1).unwrap();
95 let res_2 = base.pow(&exp_2).unwrap();
96
97 assert_eq!(cmp_0, res_0);
98 assert_eq!(cmp_1, res_1);
99 assert_eq!(cmp_2, res_2);
100 }
101
102 /// Ensures that the `pow` trait is available for other types
103 #[test]
104 fn availability() {
105 let base = Zq::from((i64::MAX, u64::MAX));
106 let exp = Z::from(4);
107
108 let _ = base.pow(exp);
109 let _ = base.pow(2_i8);
110 let _ = base.pow(2_i16);
111 let _ = base.pow(2_i32);
112 let _ = base.pow(2_i64);
113 let _ = base.pow(2_u8);
114 let _ = base.pow(2_u16);
115 let _ = base.pow(2_u32);
116 let _ = base.pow(2_u64);
117 }
118
119 /// Ensures that `pow` returns an error if a non-invertible basis is
120 /// powered by a negative exponent
121 #[test]
122 fn non_invertible_detection() {
123 let base = Zq::from((2, 4));
124
125 assert!(base.pow(-1).is_err());
126 }
127}