qfall_math/rational/poly_over_q/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 [`PolyOverQ`] value from other types.
10//!
11//! The explicit functions contain the documentation.
12
13use super::PolyOverQ;
14use crate::{
15 error::{MathError, StringConversionError},
16 integer::PolyOverZ,
17 macros::for_others::implement_for_owned,
18 rational::Q,
19};
20use flint_sys::fmpq_poly::{
21 fmpq_poly_canonicalise, fmpq_poly_set_fmpq, fmpq_poly_set_fmpz_poly, fmpq_poly_set_str,
22};
23use std::{ffi::CString, str::FromStr};
24
25impl FromStr for PolyOverQ {
26 type Err = MathError;
27
28 /// Creates a polynomial with arbitrarily many coefficients of type [`Q`].
29 ///
30 /// Parameters:
31 /// - `s`: the polynomial of form: "`[#number of coefficients]⌴⌴[0th coefficient]⌴[1st coefficient]⌴...`"
32 ///
33 /// Note that the `[#number of coefficients]` and `[0th coefficient]`
34 /// are divided by two spaces and the input string is trimmed, i.e. all whitespaces
35 /// before and after are ignored.
36 ///
37 /// Returns a [`PolyOverQ`] or an error if the provided string was not formatted
38 /// correctly, the number of coefficients was smaller than the number provided at the
39 /// start of the provided string, or the provided string contains a `Null` Byte.
40 ///
41 /// # Examples
42 /// ```
43 /// use qfall_math::rational::PolyOverQ;
44 /// use std::str::FromStr;
45 ///
46 /// let poly = PolyOverQ::from_str("5 0 1/3 2/10 -3/2 1").unwrap();
47 /// ```
48 /// # Errors and Failures
49 /// - Returns a [`MathError`] of type
50 /// [`StringConversionError`](MathError::StringConversionError)
51 /// - if the provided string was not formatted correctly,
52 /// - if the number of coefficients was smaller than the number provided
53 /// at the start of the provided string,
54 /// - if the provided value did not contain two whitespaces, or
55 /// - if the provided string contains a `Null` Byte.
56 fn from_str(s: &str) -> Result<Self, Self::Err> {
57 let mut res = Self::default();
58
59 let c_string = CString::new(s.trim())?;
60
61 // `0` is returned if the string is a valid input
62 // additionally if it was not successfully, test if the provided value 's' actually
63 // contains two whitespaces, since this might be a common error
64 match unsafe { fmpq_poly_set_str(&mut res.poly, c_string.as_ptr()) } {
65 0 => unsafe {
66 // set_str assumes that all coefficients are reduced as far as possible,
67 // hence we have to reduce manually
68 fmpq_poly_canonicalise(&mut res.poly);
69 Ok(res)
70 },
71 _ if !s.contains(" ") => Err(
72 StringConversionError::InvalidStringToPolyMissingWhitespace(s.to_owned()),
73 )?,
74 _ => Err(StringConversionError::InvalidStringToPolyInput(
75 s.to_owned(),
76 ))?,
77 }
78 }
79}
80
81impl From<&PolyOverZ> for PolyOverQ {
82 /// Creates a [`PolyOverQ`] from a [`PolyOverZ`].
83 ///
84 /// Parameters:
85 /// - `poly`: the polynomial from which the coefficients are copied
86 ///
87 /// # Examples
88 /// ```
89 /// use qfall_math::integer::PolyOverZ;
90 /// use qfall_math::rational::PolyOverQ;
91 /// use std::str::FromStr;
92 ///
93 /// let poly = PolyOverZ::from_str("4 0 1 102 3").unwrap();
94 ///
95 /// let poly_q = PolyOverQ::from(&poly);
96 ///
97 /// # let cmp_poly = PolyOverQ::from_str("4 0 1 102 3").unwrap();
98 /// # assert_eq!(cmp_poly, poly_q);
99 /// ```
100 fn from(poly: &PolyOverZ) -> Self {
101 let mut out = Self::default();
102 unsafe { fmpq_poly_set_fmpz_poly(&mut out.poly, &poly.poly) };
103 out
104 }
105}
106
107implement_for_owned!(PolyOverZ, PolyOverQ, From);
108
109impl<Rational: Into<Q>> From<Rational> for PolyOverQ {
110 /// Creates a constant [`PolyOverQ`] with a specified rational constant.
111 ///
112 /// Parameters:
113 /// - `value`: the constant value the polynomial will have. It has to be a rational
114 /// number like [`Q`], an integer or a tuple of integers `(numerator, denominator)`.
115 ///
116 /// Returns a new constant polynomial with the specified value.
117 ///
118 /// # Examples
119 /// ```
120 /// use qfall_math::{rational::*, traits::GetCoefficient};
121 ///
122 /// let one = PolyOverQ::from(1);
123 /// let three_quarter = PolyOverQ::from(Q::from((3, 4)));
124 /// let one_half = PolyOverQ::from((1, 2));
125 ///
126 /// assert_eq!(one_half.get_coeff(0).unwrap(), Q::from((1, 2)));
127 /// assert_eq!(one_half.get_degree(), 0);
128 /// ```
129 ///
130 /// # Panics ...
131 /// - if the provided value can not be converted into a [`Q`].
132 /// For example, because of a division by zero.
133 fn from(value: Rational) -> Self {
134 let mut out = PolyOverQ::default();
135 let value: Q = value.into();
136
137 unsafe {
138 fmpq_poly_set_fmpq(&mut out.poly, &value.value);
139 }
140 out
141 }
142}
143
144impl From<&PolyOverQ> for PolyOverQ {
145 /// Alias for [`PolyOverQ::clone`].
146 fn from(value: &PolyOverQ) -> Self {
147 value.clone()
148 }
149}
150
151#[cfg(test)]
152mod test_from_str {
153 use super::PolyOverQ;
154 use std::str::FromStr;
155
156 /// Ensure that zero-coefficients are reduced
157 #[test]
158 fn reduce_zero_coeff() {
159 let one_1 = PolyOverQ::from_str("2 24/42 1").unwrap();
160 let one_2 = PolyOverQ::from_str("3 24/42 1 0").unwrap();
161
162 assert_eq!(one_1, one_2);
163 }
164
165 /// Ensure that coefficients in the string are reduced
166 #[test]
167 fn reduce_coeff() {
168 assert_eq!(
169 PolyOverQ::from_str("3 4/77 4/14 -28/21").unwrap(),
170 PolyOverQ::from_str("3 4/77 2/7 -28/21").unwrap()
171 );
172 }
173
174 /// Tests whether the same string yields the same polynomial
175 #[test]
176 fn same_string() {
177 let str_1 = format!("3 1 2/3 {}/{}", u64::MAX, i64::MIN);
178
179 let poly_1 = PolyOverQ::from_str(&str_1).unwrap();
180 let poly_2 = PolyOverQ::from_str(&str_1).unwrap();
181
182 assert_eq!(poly_1, poly_2);
183 }
184
185 /// Tests whether a correctly formatted string outputs an instantiation of a
186 /// polynomial, i.e. does not return an error
187 #[test]
188 fn working_example() {
189 assert!(PolyOverQ::from_str("3 1 2/5 -3/2").is_ok());
190 }
191
192 /// Tests whether a falsely formatted string (missing double-space) returns
193 /// an error
194 #[test]
195 fn missing_whitespace() {
196 assert!(PolyOverQ::from_str("3 1 2/5 -3/2").is_err());
197 assert!(PolyOverQ::from_str("3 12/5 2 -3").is_err());
198 assert!(PolyOverQ::from_str("2 17 42/4").is_err());
199 assert!(PolyOverQ::from_str("2 17 42").is_err());
200 assert!(PolyOverQ::from_str("2 17/1 42").is_err());
201 assert!(PolyOverQ::from_str("2 17/13 42 ").is_err());
202 assert!(PolyOverQ::from_str(" 2 17/5 42").is_err());
203 }
204
205 /// Tests whether a falsely formatted string (too many whitespaces) returns
206 /// an error
207 #[test]
208 fn too_many_whitespaces() {
209 assert!(PolyOverQ::from_str("3 1 2/5 -3/2").is_err());
210 }
211
212 /// Tests whether a falsely formatted string (wrong number of total
213 /// coefficients) returns an error
214 #[test]
215 fn false_number_of_coefficient() {
216 assert!(PolyOverQ::from_str("4 1 2/5 -3/2").is_err());
217 }
218
219 /// Tests whether a falsely formatted string (too many divisors) returns
220 /// an error
221 #[test]
222 fn too_many_divisors() {
223 assert!(PolyOverQ::from_str("3 1 2/5 -3/2/3").is_err());
224 }
225
226 /// Ensure that the input works with strings that have to be trimmed
227 #[test]
228 fn trim_input() {
229 let poly = PolyOverQ::from_str(" 4 1/2 2/3 3/4 -4 ");
230 assert!(poly.is_ok());
231 assert_eq!(
232 PolyOverQ::from_str("4 1/2 2/3 3/4 -4").unwrap(),
233 poly.unwrap()
234 );
235 }
236}
237
238#[cfg(test)]
239mod test_from_poly_over_z {
240 use crate::{integer::PolyOverZ, rational::PolyOverQ};
241 use std::str::FromStr;
242
243 /// Ensure that the conversion works with negative entries
244 #[test]
245 fn small_negative() {
246 let poly = PolyOverZ::from_str("4 0 1 -102 -3").unwrap();
247
248 let poly_q = PolyOverQ::from(&poly);
249
250 let cmp_poly = PolyOverQ::from_str("4 0 1 -102 -3").unwrap();
251 assert_eq!(cmp_poly, poly_q);
252 }
253
254 /// Ensure that the conversion works with negative large entries
255 #[test]
256 fn large_negative() {
257 let poly = PolyOverZ::from_str(&format!("4 0 1 -102 -{}", u64::MAX)).unwrap();
258
259 let poly_q = PolyOverQ::from(&poly);
260
261 let cmp_poly = PolyOverQ::from_str(&format!("4 0 1 -102 -{}", u64::MAX)).unwrap();
262 assert_eq!(cmp_poly, poly_q);
263 }
264
265 /// Ensure that the conversion works with positive large entries
266 #[test]
267 fn large_positive() {
268 let poly = PolyOverZ::from_str(&format!("4 0 1 102 {}", u64::MAX)).unwrap();
269
270 let poly_q = PolyOverQ::from(&poly);
271
272 let cmp_poly = PolyOverQ::from_str(&format!("4 0 1 102 {}", u64::MAX)).unwrap();
273 assert_eq!(cmp_poly, poly_q);
274 }
275
276 /// Ensure that the conversion works for owned values
277 #[test]
278 fn availability() {
279 let poly = PolyOverZ::from_str("4 0 1 -102 -3").unwrap();
280
281 let _ = PolyOverQ::from(poly);
282 }
283}
284
285#[cfg(test)]
286mod test_from_rational {
287 use super::*;
288 use crate::{integer::Z, traits::GetCoefficient};
289
290 /// Ensure that the [`From`] trait works for large
291 /// borrowed and owned [`Q`],[`Z`] and [`u64`] instances.
292 #[test]
293 fn large() {
294 let value = Q::from(u64::MAX);
295
296 let poly = PolyOverQ::from(&value);
297 let poly_2 = PolyOverQ::from(value.clone());
298 let poly_3 = PolyOverQ::from(u64::MAX);
299 let poly_4 = PolyOverQ::from(&u64::MAX);
300 let poly_5 = PolyOverQ::from(Z::from(u64::MAX));
301 let poly_6 = PolyOverQ::from(&Z::from(u64::MAX));
302
303 assert_eq!(poly.get_coeff(0).unwrap(), value);
304 assert_eq!(poly.get_degree(), 0);
305 assert_eq!(poly, poly_2);
306 assert_eq!(poly, poly_3);
307 assert_eq!(poly, poly_4);
308 assert_eq!(poly, poly_5);
309 assert_eq!(poly, poly_6);
310 }
311
312 /// Ensure that the [`From`] trait works for small
313 /// borrowed and owned [`Q`] and integer tuples instances.
314 #[test]
315 fn small() {
316 let value = Q::from((1, 2));
317
318 let poly = PolyOverQ::from(&value);
319 let poly_2 = PolyOverQ::from(value.clone());
320 let poly_3 = PolyOverQ::from((1, 2));
321 let poly_4 = PolyOverQ::from((&1, &2));
322
323 assert_eq!(poly.get_coeff(0).unwrap(), value);
324 assert_eq!(poly.get_degree(), 0);
325 assert_eq!(poly, poly_2);
326 assert_eq!(poly, poly_3);
327 assert_eq!(poly, poly_4);
328 }
329
330 /// Ensure that a division by zero panics.
331 #[test]
332 #[should_panic]
333 fn divide_by_zero() {
334 let _ = PolyOverQ::from((1, 0));
335 }
336}