qfall_math/integer/poly_over_z/from.rs
1// Copyright © 2023 Marvin Beckmann
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 [`PolyOverZ`] value from other types.
10//!
11//! The explicit functions contain the documentation.
12
13use super::PolyOverZ;
14use crate::{
15 error::{MathError, StringConversionError},
16 integer::Z,
17 traits::AsInteger,
18};
19use flint_sys::fmpz_poly::{fmpz_poly_set_fmpz, fmpz_poly_set_str};
20use std::{ffi::CString, str::FromStr};
21
22impl FromStr for PolyOverZ {
23 type Err = MathError;
24
25 /// Creates a polynomial with arbitrarily many coefficients of type [`Z`]
26 /// from a [`String`].
27 ///
28 /// **Warning**: If the input string starts with a correctly formatted [`PolyOverZ`] object,
29 /// the rest of the string is ignored. This means that the input string
30 /// `"4 0 1 2 3"` is the same as `"4 0 1 2 3 4 5 6 7"`.
31 ///
32 /// Parameters:
33 /// - `s`: the polynomial of form: `"[#number of coefficients]⌴⌴[0th coefficient]⌴[1st coefficient]⌴..."`.
34 ///
35 /// Note that the `[#number of coefficients]` and `[0th coefficient]`
36 /// are divided by two spaces and the input string is trimmed, i.e. all whitespaces
37 /// before and after are ignored.
38 ///
39 /// Returns a [`PolyOverZ`] or an error if the provided string was not formatted
40 /// correctly, the number of coefficients was smaller than the number provided
41 /// at the start of the provided string, or the provided string contains a `Null` Byte.
42 ///
43 /// # Examples
44 /// ```
45 /// use qfall_math::integer::PolyOverZ;
46 /// use std::str::FromStr;
47 ///
48 /// let poly = PolyOverZ::from_str("4 0 1 2 3").unwrap();
49 /// ```
50 ///
51 /// # Errors and Failures
52 /// - Returns a [`MathError`] of type [`MathError::StringConversionError`]
53 /// - if the provided string was not formatted correctly,
54 /// - if the number of coefficients was smaller than the number provided
55 /// at the start of the provided string,
56 /// - if the provided value did not contain two whitespaces, or
57 /// - if the provided string contains a `Null` Byte.
58 fn from_str(s: &str) -> Result<Self, Self::Err> {
59 // remove whitespaces at the start and at the end
60 let s_trimmed = s.trim();
61
62 if s_trimmed == "0" {
63 return Ok(Self::default());
64 }
65
66 // fmpz_poly_set_str just skips the two symbols after the first space
67 // behind the number of coefficients (even if not a space), hence
68 // it has to be checked here to Ensure that no number is lost.
69 // We only have to check it once, because for every other position it checks
70 // whether there is only one space.
71 if !s_trimmed.contains(" ") {
72 return Err(StringConversionError::InvalidStringToPolyMissingWhitespace(
73 s.to_owned(),
74 ))?;
75 };
76
77 let mut res = Self::default();
78
79 let c_string = CString::new(s_trimmed)?;
80
81 match unsafe { fmpz_poly_set_str(&mut res.poly, c_string.as_ptr()) } {
82 0 => Ok(res),
83 _ => Err(StringConversionError::InvalidStringToPolyInput(
84 s.to_owned(),
85 ))?,
86 }
87 }
88}
89
90impl<Integer: AsInteger + Into<Z>> From<Integer> for PolyOverZ {
91 /// Creates a constant [`PolyOverZ`] with a specified integer constant.
92 ///
93 /// Parameters:
94 /// `value`: an integer like [`Z`], rust Integers or a reference to these values.
95 ///
96 /// Returns a new constant polynomial with the specified value.
97 ///
98 /// # Examples
99 /// ```
100 /// use qfall_math::{integer::*, traits::*};
101 ///
102 /// let one = PolyOverZ::from(1);
103 ///
104 /// assert_eq!(one.get_coeff(0).unwrap(), Z::ONE);
105 /// assert_eq!(one.get_degree(), 0);
106 /// ```
107 fn from(value: Integer) -> Self {
108 let mut ret = PolyOverZ::default();
109 unsafe {
110 match value.get_fmpz_ref() {
111 Some(fmpz_ref) => fmpz_poly_set_fmpz(&mut ret.poly, fmpz_ref),
112 None => {
113 // Does not include a fmpz in the original data Type.
114 // We convert the value into Z to also handle the memory management.
115 let z_value: Z = value.into();
116 fmpz_poly_set_fmpz(&mut ret.poly, &z_value.value)
117 }
118 }
119 }
120
121 ret
122 }
123}
124
125impl From<&PolyOverZ> for PolyOverZ {
126 /// Alias for [`PolyOverZ::clone`].
127 fn from(value: &PolyOverZ) -> Self {
128 value.clone()
129 }
130}
131
132#[cfg(test)]
133mod test_from_str {
134 use super::PolyOverZ;
135 use std::str::FromStr;
136
137 /// Ensure that zero-coefficients are reduced
138 #[test]
139 fn reduce_zero_coeff() {
140 let one_1 = PolyOverZ::from_str("2 24 1").unwrap();
141 let one_2 = PolyOverZ::from_str("3 24 1 0").unwrap();
142
143 assert_eq!(one_1, one_2);
144 }
145
146 /// Tests whether the same string yields the same polynomial
147 #[test]
148 fn same_string() {
149 let str_1 = format!("3 1 {} {}", u64::MAX, i64::MIN);
150
151 let poly_1 = PolyOverZ::from_str(&str_1).unwrap();
152 let poly_2 = PolyOverZ::from_str(&str_1).unwrap();
153
154 assert_eq!(poly_1, poly_2);
155 }
156
157 /// Tests whether a correctly formatted string outputs an instantiation of a
158 /// polynomial, i.e. does not return an error
159 #[test]
160 fn working_example() {
161 assert!(PolyOverZ::from_str("3 1 2 -3").is_ok());
162 }
163
164 /// Tests whether a falsely formatted string (missing double-space) returns
165 /// an error
166 #[test]
167 fn missing_whitespace() {
168 assert!(PolyOverZ::from_str("3 12 2 -3").is_err());
169 assert!(PolyOverZ::from_str("2 17 42").is_err());
170 assert!(PolyOverZ::from_str("2 17 42").is_err());
171 assert!(PolyOverZ::from_str("2 17 42 ").is_err());
172 assert!(PolyOverZ::from_str(" 2 17 42").is_err());
173 }
174
175 /// Tests whether a falsely formatted string (too many whitespaces) returns
176 /// an error
177 #[test]
178 fn too_many_whitespaces() {
179 assert!(PolyOverZ::from_str("3 1 2 -3").is_err());
180 }
181
182 /// Tests whether a falsely formatted string (wrong number of total
183 /// coefficients) returns an error
184 #[test]
185 fn false_number_of_coefficient() {
186 assert!(PolyOverZ::from_str("4 1 2 -3").is_err());
187 }
188
189 /// Ensure that the input works with strings that have to be trimmed
190 #[test]
191 fn trim_input() {
192 let poly = PolyOverZ::from_str(" 4 1 2 3 -4 ");
193 assert!(poly.is_ok());
194 assert_eq!(PolyOverZ::from_str("4 1 2 3 -4").unwrap(), poly.unwrap());
195 }
196}
197
198#[cfg(test)]
199mod test_from_integer {
200 use super::*;
201 use crate::traits::GetCoefficient;
202
203 /// Ensure that the [`From`] trait works for large
204 /// borrowed and owned [`Z`] and [`u64`] instances.
205 #[test]
206 fn large() {
207 let value = Z::from(u64::MAX);
208
209 let poly = PolyOverZ::from(&value);
210 let poly_2 = PolyOverZ::from(value.clone());
211 let poly_3 = PolyOverZ::from(u64::MAX);
212 let poly_4 = PolyOverZ::from(&u64::MAX);
213
214 assert_eq!(value, poly.get_coeff(0).unwrap());
215 assert_eq!(poly.get_degree(), 0);
216 assert_eq!(poly, poly_2);
217 assert_eq!(poly, poly_3);
218 assert_eq!(poly, poly_4);
219 }
220
221 /// Ensure that the [`From`] trait works for small
222 /// borrowed and owned [`Z`] and rust integer instances.
223 #[test]
224 fn small() {
225 let value = Z::ONE;
226
227 let poly = PolyOverZ::from(&value);
228 let poly_2 = PolyOverZ::from(value.clone());
229
230 let poly_3 = PolyOverZ::from(1u64);
231 let poly_4 = PolyOverZ::from(&1u64);
232 let poly_5 = PolyOverZ::from(1i64);
233 let poly_6 = PolyOverZ::from(&1i64);
234 // Assume that it also works for the other rust integers.
235
236 assert_eq!(value, poly.get_coeff(0).unwrap());
237 assert_eq!(poly.get_degree(), 0);
238 assert_eq!(poly, poly_2);
239 assert_eq!(poly, poly_3);
240 assert_eq!(poly, poly_4);
241 assert_eq!(poly, poly_5);
242 assert_eq!(poly, poly_6);
243 }
244
245 /// Ensure that the [`From`] trait works for large negative
246 /// borrowed and owned [`Z`] and rust integer instances.
247 #[test]
248 fn negative() {
249 let value = Z::from(i64::MIN);
250
251 let poly = PolyOverZ::from(&value);
252 let poly_2 = PolyOverZ::from(value.clone());
253
254 let poly_3 = PolyOverZ::from(i64::MIN);
255 let poly_4 = PolyOverZ::from(&i64::MIN);
256 // Assume that it also works for the other rust integers.
257
258 assert_eq!(value, poly.get_coeff(0).unwrap());
259 assert_eq!(poly.get_degree(), 0);
260 assert_eq!(poly, poly_2);
261 assert_eq!(poly, poly_3);
262 assert_eq!(poly, poly_4);
263 }
264}