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}