qfall_math/integer_mod_q/modulus/
from.rs

1// Copyright © 2023 Marvin Beckmann, Sven Moog
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//! Implementations to create a [`Modulus`] value from other types.
10//!
11//! The explicit functions contain the documentation.
12
13use super::Modulus;
14use crate::{
15    error::MathError, integer::Z, macros::for_others::implement_empty_trait_owned_ref, traits::*,
16};
17use flint_sys::{
18    fmpz::{fmpz, fmpz_clear, fmpz_cmp_si},
19    fmpz_mod::fmpz_mod_ctx_init,
20};
21use std::{mem::MaybeUninit, rc::Rc, str::FromStr};
22
23impl Modulus {
24    /// Initialize a [`Modulus`] from an fmpz reference.
25    ///
26    /// Parameters:
27    /// - `value`: the initial value the modulus should have.
28    ///   It must be larger than one.
29    ///
30    /// Returns a [`Modulus`] or an error if the
31    /// provided value is smaller than `2`.
32    ///
33    /// # Safety
34    /// Since the parameter is a reference, it still has to be
35    /// properly cleared outside this function.
36    /// For example, by the drop trait of [`Z`].
37    ///
38    /// # Examples
39    /// ```compile_fail
40    /// use qfall_math::integer_mod_q::Modulus;
41    /// use qfall_math::integer::Z;
42    ///
43    /// let z = Z::from(42);
44    /// let modulus = Modulus::from_fmpz_ref(&z.value);
45    /// ```
46    ///
47    /// # Errors and Failures
48    /// - Returns a [`MathError`] of type [`InvalidModulus`](MathError::InvalidModulus)
49    ///   if the provided value is smaller than `2`.
50    pub(crate) fn from_fmpz_ref(value: &fmpz) -> Result<Self, MathError> {
51        if (unsafe { fmpz_cmp_si(value, 1) } <= 0) {
52            let z = Z::from(value);
53            return Err(MathError::InvalidModulus(z.to_string()));
54        }
55
56        let mut ctx = MaybeUninit::uninit();
57        unsafe {
58            fmpz_mod_ctx_init(ctx.as_mut_ptr(), value);
59            Ok(Self {
60                modulus: Rc::new(ctx.assume_init()),
61            })
62        }
63    }
64}
65
66/// A trait that filters for which types the `From for Modulus` should be implemented.
67/// It is used as a workaround to implement the [`From`] trait without colliding
68/// with the default implementation for [`Modulus`] and also to filter out
69/// [`Zq`](crate::integer_mod_q::Zq) and &[`Modulus`].
70trait IntoModulus {}
71implement_empty_trait_owned_ref!(IntoModulus for Z fmpz u8 u16 u32 u64 i8 i16 i32 i64);
72
73impl<Integer: AsInteger + IntoModulus> From<Integer> for Modulus {
74    /// Creates a [`Modulus`] from a positive integer.
75    ///
76    /// Parameters:
77    /// - `value`: the initial value the modulus should have.
78    ///   It must be larger than one.
79    ///
80    /// Returns a [`Modulus`] or an panics, if the
81    /// provided value is smaller than `2`.
82    ///
83    /// # Examples
84    /// ```
85    /// use qfall_math::integer_mod_q::Modulus;
86    /// use qfall_math::integer::Z;
87    ///
88    /// let _ = Modulus::from(10);
89    /// let _ = Modulus::from(u64::MAX);
90    /// let _ = Modulus::from(Z::from(42));
91    /// ```
92    ///
93    /// # Panics ...
94    /// - if the provided value is smaller than `2`.
95    fn from(value: Integer) -> Self {
96        match value.get_fmpz_ref() {
97            Some(val) => Modulus::from_fmpz_ref(val).unwrap(),
98            None => unsafe {
99                let mut value = value.into_fmpz();
100                let out = Modulus::from_fmpz_ref(&value);
101                fmpz_clear(&mut value);
102                out.unwrap()
103            },
104        }
105    }
106}
107
108impl From<&Modulus> for Modulus {
109    // This is more efficient than the generic implementation above
110    // since only the smart pointer is increased here.
111
112    /// Alias for [`Modulus::clone`].
113    fn from(value: &Modulus) -> Self {
114        value.clone()
115    }
116}
117
118impl FromStr for Modulus {
119    type Err = MathError;
120
121    /// Creates a [`Modulus`] from a [`String`].
122    ///
123    /// Parameters:
124    /// - `s`: the modulus of form: `"[0-9]+"` and not all zeros.
125    ///
126    /// Returns a [`Modulus`] or an [`MathError`], if the provided string is not
127    /// a valid modulus.
128    ///
129    /// # Examples
130    /// ```
131    /// use qfall_math::integer_mod_q::Modulus;
132    /// use std::str::FromStr;
133    ///
134    /// let modulus = Modulus::from_str("42").unwrap();
135    /// ```
136    ///
137    /// # Errors and Failures
138    /// - Returns a [`MathError`] of type
139    ///   [`StringConversionError`](MathError::StringConversionError)
140    ///   if the provided string was not formatted correctly, e.g. not a correctly
141    ///   formatted [`Z`].
142    /// - Returns a [`MathError`] of type
143    ///   [`InvalidModulus`](MathError::InvalidModulus)
144    ///   if the provided value is smaller than `2`.
145    fn from_str(s: &str) -> Result<Self, Self::Err> {
146        let z = Z::from_str(s)?;
147
148        Modulus::from_fmpz_ref(&z.value)
149    }
150}
151
152#[cfg(test)]
153mod test_from {
154    use super::*;
155
156    /// Showcase the different ways to initialize a [`Modulus`].
157    #[test]
158    #[allow(clippy::useless_conversion)]
159    fn available() {
160        // signed rust integer
161        let _ = Modulus::from(i8::MAX);
162        let _ = Modulus::from(i16::MAX);
163        let _ = Modulus::from(i32::MAX);
164        let _ = Modulus::from(i64::MAX);
165        let _ = Modulus::from(&i8::MAX);
166        let _ = Modulus::from(&i16::MAX);
167        let _ = Modulus::from(&i32::MAX);
168        let _ = Modulus::from(&i64::MAX);
169
170        // unsigned rust integer
171        let _ = Modulus::from(u8::MAX);
172        let _ = Modulus::from(u16::MAX);
173        let _ = Modulus::from(u32::MAX);
174        let _ = Modulus::from(u64::MAX);
175        let _ = Modulus::from(&u8::MAX);
176        let _ = Modulus::from(&u16::MAX);
177        let _ = Modulus::from(&u32::MAX);
178        let _ = Modulus::from(&u64::MAX);
179
180        // from Z
181        let _ = Modulus::from(Z::from(10));
182        let _ = Modulus::from(&Z::from(10));
183
184        // from fmpz
185        let z = Z::from(42);
186        let _ = Modulus::from(&z.value);
187        let modulus = Modulus::from(z.value);
188
189        // from Modulus
190        let _ = Modulus::from(&modulus);
191        let _ = Modulus::from(modulus);
192    }
193
194    /// Ensure that a modulus of one panics.
195    #[test]
196    #[should_panic]
197    fn invalid_modulus() {
198        let _ = Modulus::from(1);
199    }
200
201    /// Ensure that a large modulus is initialized correctly.
202    #[test]
203    fn large() {
204        let modulus = Modulus::from(i64::MAX);
205
206        assert_eq!(Z::from(modulus), Z::from(i64::MAX));
207    }
208
209    /// Ensure that a small modulus is initialized correctly.
210    #[test]
211    fn small() {
212        let modulus = Modulus::from(2);
213
214        assert_eq!(Z::from(modulus), Z::from(2));
215    }
216}
217
218#[cfg(test)]
219mod test_from_fmpz_ref {
220    use super::*;
221    use crate::integer::Z;
222
223    /// Tests whether a small modulus ist instantiated correctly.
224    #[test]
225    fn working_example() {
226        let z = Z::from(100);
227        assert!(Modulus::from_fmpz_ref(&z.value).is_ok());
228    }
229
230    /// Tests whether a large modulus (> 64 bits) is instantiated correctly.
231    #[test]
232    fn large_modulus() {
233        let z = &Z::from(u64::MAX);
234        assert!(Modulus::from_fmpz_ref(&z.value).is_ok());
235    }
236
237    /// Tests whether a negative input value returns an error.
238    #[test]
239    fn negative_modulus() {
240        let z = &Z::from(-42);
241        assert!(Modulus::from_fmpz_ref(&z.value).is_err());
242    }
243
244    /// Tests whether a zero as input value returns an error.
245    #[test]
246    fn zero_modulus() {
247        let z = &Z::ZERO;
248        assert!(Modulus::from_fmpz_ref(&z.value).is_err());
249    }
250}
251
252#[cfg(test)]
253mod test_from_str {
254    use super::Modulus;
255    use std::str::FromStr;
256
257    /// Tests whether a correctly formatted string outputs an instantiation of a
258    /// Modulus, i.e. does not return an error.
259    #[test]
260    fn working_example() {
261        assert!(Modulus::from_str("42").is_ok());
262    }
263
264    /// Tests whether a large value (> 64 bits) is instantiated correctly.
265    #[test]
266    fn large_value() {
267        assert!(Modulus::from_str(&"1".repeat(65)).is_ok());
268    }
269
270    /// Tests whether a falsely formatted string (wrong whitespaces) returns an
271    /// error.
272    #[test]
273    fn false_format_whitespaces() {
274        assert!(Modulus::from_str("4 2").is_err());
275    }
276
277    /// Tests whether a falsely formatted string (wrong symbols) returns an error.
278    #[test]
279    fn false_format_symbols() {
280        assert!(Modulus::from_str("b a").is_err());
281    }
282
283    /// Tests whether a false string (negative) returns an error.
284    #[test]
285    fn false_sign() {
286        assert!(Modulus::from_str("-42").is_err());
287    }
288}