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}