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}