Skip to main content

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