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}