qfall_math/integer/z/to_string.rs
1// Copyright © 2023 Marcel Luca Schmidt, 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//! This module contains all options to convert an integer of type
10//! [`Z`] into a [`String`].
11//!
12//! This includes the [`Display`](std::fmt::Display) trait.
13
14use super::Z;
15use crate::{error::MathError, macros::for_others::implement_for_owned};
16use core::fmt;
17use flint_sys::fmpz::{fmpz_bits, fmpz_get_str, fmpz_tstbit};
18use std::{ffi::CStr, ptr::null_mut, string::FromUtf8Error};
19
20impl From<&Z> for String {
21 /// Converts a [`Z`] into its [`String`] representation.
22 ///
23 /// Parameters:
24 /// - `value`: specifies the integer that will be represented as a [`String`]
25 ///
26 /// Returns a [`String`].
27 ///
28 /// # Examples
29 /// ```
30 /// use qfall_math::integer::Z;
31 /// use std::str::FromStr;
32 /// let z = Z::from_str("6").unwrap();
33 ///
34 /// let string: String = z.into();
35 /// ```
36 fn from(value: &Z) -> Self {
37 value.to_string()
38 }
39}
40
41implement_for_owned!(Z, String, From);
42
43impl fmt::Display for Z {
44 /// Allows to convert an integer of type [`Z`] into a [`String`].
45 ///
46 /// Returns the integer in form of a [`String`]. For integer `1`
47 /// the String looks like this `1`.
48 ///
49 /// # Examples
50 /// ```
51 /// use qfall_math::integer::Z;
52 /// use core::fmt;
53 ///
54 /// let integer = Z::from(42);
55 /// println!("{integer}");
56 /// ```
57 ///
58 /// ```
59 /// use qfall_math::integer::Z;
60 /// use core::fmt;
61 ///
62 /// let integer = Z::from(42);
63 /// let integer_string = integer.to_string();
64 /// ```
65 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
66 write!(f, "{}", self.to_string_b(10).unwrap())
67 }
68}
69
70impl Z {
71 /// Allows to convert an integer of type [`Z`] into a [`String`]
72 /// with a configurable base `b` between `2` and `62`.
73 ///
74 /// Parameters:
75 /// - `b`: specifies any base between `2` and `62` which specifies
76 /// the base of the returned [`String`].
77 ///
78 /// Returns the integer in form of a [`String`] with regards to the base `b`
79 /// or an error, if the base is out of bounds.
80 ///
81 /// # Examples
82 /// ```
83 /// use qfall_math::integer::Z;
84 /// use core::fmt;
85 ///
86 /// let integer = Z::from(42);
87 /// println!("{integer}");
88 /// ```
89 ///
90 /// ```
91 /// use qfall_math::integer::Z;
92 /// use core::fmt;
93 ///
94 /// let integer = Z::from(42);
95 /// let integer_string = integer.to_string();
96 /// ```
97 ///
98 /// # Errors and Failures
99 /// - Returns a [`MathError`] of type [`OutOfBounds`](MathError::OutOfBounds) if the
100 /// base is not between `2` and `62`.
101 pub fn to_string_b(&self, base: i32) -> Result<String, MathError> {
102 if !(2..=62).contains(&base) {
103 return Err(MathError::OutOfBounds(
104 "between 2 and 62".to_owned(),
105 base.to_string(),
106 ));
107 }
108
109 let c_str_ptr = unsafe { fmpz_get_str(null_mut(), base, &self.value) };
110
111 // we expect c_str_ptr to be reference a real value, hence get_str returns an
112 // actual value, hence a simple unwrap should be sufficient and we do not have
113 // to consider an exception
114 //
115 // c_string should not be null either, since we call this method on an
116 // instantiated object
117 let msg = "We expect the pointer to point to a real value and the c_string
118 not to be null. This error occurs if the provided string does not have UTF-8 format.";
119 let return_str = unsafe { CStr::from_ptr(c_str_ptr).to_str().expect(msg).to_owned() };
120
121 unsafe { libc::free(c_str_ptr as *mut libc::c_void) };
122
123 Ok(return_str)
124 }
125
126 /// Outputs the integer as a [`Vec`] of bytes.
127 /// The inverse function to [`Z::to_bytes`] is [`Z::from_bytes`] for positive numbers including `0`.
128 ///
129 /// **Warning**: The bits are returned as they are stored in the memory. For negative numbers,
130 /// this means that `-1` is output as `[255]`.
131 /// For these values, [`Z::from_bytes`] is not inverse to [`Z::to_bytes`],
132 /// as this function can only instantiate positive values.
133 ///
134 /// Returns a [`Vec<u8>`] of bytes representing the integer as it is stored in memory.
135 ///
136 /// # Examples
137 /// ```
138 /// use qfall_math::integer::Z;
139 ///
140 /// let integer = Z::from(257);
141 ///
142 /// let byte_string = integer.to_bytes();
143 ///
144 /// assert_eq!(vec![1, 1], byte_string);
145 /// ```
146 pub fn to_bytes(&self) -> Vec<u8> {
147 let num_bits = unsafe { fmpz_bits(&self.value) } as usize;
148 let num_bytes = num_bits.div_ceil(8);
149 let mut bytes = vec![0u8; num_bytes];
150
151 for (byte, item) in bytes.iter_mut().enumerate() {
152 for bit_of_byte in (0..8usize).rev() {
153 let bit_index = (byte * u8::BITS as usize + bit_of_byte) as u64;
154 let bit_at_index = unsafe { fmpz_tstbit(&self.value, bit_index) };
155
156 *item *= 2;
157 if bit_at_index == 1 {
158 *item += 1;
159 }
160 }
161 }
162
163 bytes
164 }
165
166 /// Enables conversion to a UTF8-Encoded [`String`] for [`Z`] values.
167 /// The inverse to this function is [`Z::from_utf8`] for valid UTF8-Encodings.
168 ///
169 /// **Warning**: Not every byte-sequence forms a valid UTF8-character.
170 /// If this is the case, a [`FromUtf8Error`] will be returned.
171 ///
172 /// Returns the corresponding UTF8-encoded [`String`] or a
173 /// [`FromUtf8Error`] if the byte sequence contains an invalid UTF8-character.
174 ///
175 /// # Examples
176 /// ```
177 /// use qfall_math::integer::Z;
178 /// let integer = Z::from(10);
179 ///
180 /// let text: String = integer.to_utf8().unwrap();
181 /// ```
182 ///
183 /// # Errors and Failures
184 /// - Returns a [`FromUtf8Error`] if the integer's byte sequence contains
185 /// invalid UTF8-characters.
186 pub fn to_utf8(&self) -> Result<String, FromUtf8Error> {
187 String::from_utf8(self.to_bytes())
188 }
189}
190
191#[cfg(test)]
192mod test_to_string {
193 use crate::integer::Z;
194 use std::str::FromStr;
195
196 /// Tests whether a large positive integer works in a roundtrip
197 #[test]
198 fn working_large_positive() {
199 let cmp = Z::from(u64::MAX);
200
201 assert_eq!(u64::MAX.to_string(), cmp.to_string());
202 }
203
204 /// Tests whether a large negative integer works in a roundtrip
205 #[test]
206 fn working_large_negative() {
207 let cmp = Z::from_str(&format!("-{}", u64::MAX)).unwrap();
208
209 assert_eq!(format!("-{}", u64::MAX), cmp.to_string());
210 }
211
212 /// Tests whether a positive integer works in a roundtrip
213 #[test]
214 fn working_positive() {
215 let cmp = Z::from(42);
216
217 assert_eq!("42", cmp.to_string());
218 }
219
220 /// Tests whether a negative integer works in a roundtrip
221 #[test]
222 fn working_negative() {
223 let cmp = Z::from(-42);
224
225 assert_eq!("-42", cmp.to_string());
226 }
227
228 /// Tests whether an integer that is created using a string, returns a
229 /// string that can be used to create a [`Z`]
230 #[test]
231 fn working_use_result_of_to_string_as_input() {
232 let cmp = Z::from(42);
233
234 let cmp_str_2 = cmp.to_string();
235
236 assert!(Z::from_str(&cmp_str_2).is_ok());
237 }
238
239 /// Ensures that the `Into<String>` trait works properly
240 #[test]
241 fn into_works_properly() {
242 let cmp = "6";
243 let integer = Z::from_str(cmp).unwrap();
244
245 let string: String = integer.clone().into();
246 let borrowed_string: String = (&integer).into();
247
248 assert_eq!(cmp, string);
249 assert_eq!(cmp, borrowed_string);
250 }
251}
252
253#[cfg(test)]
254mod test_to_string_b {
255 use crate::integer::Z;
256
257 /// Ensure that an error is returned, if an invalid base is provided
258 #[test]
259 fn out_of_bounds() {
260 let value = Z::from(42);
261
262 assert!(value.to_string_b(-1).is_err());
263 assert!(value.to_string_b(1).is_err());
264 assert!(value.to_string_b(63).is_err());
265 }
266
267 /// Ensure that binary representation works correctly
268 #[test]
269 fn binary() {
270 let value_1 = Z::from(u64::MAX);
271 let cmp_str_1 = "1".repeat(64);
272
273 let value_2 = Z::from(i64::MIN);
274 let cmp_str_2 = format!("-1{}", "0".repeat(63));
275
276 assert_eq!(cmp_str_1, value_1.to_string_b(2).unwrap());
277 assert_eq!(cmp_str_2, value_2.to_string_b(2).unwrap());
278 }
279}
280
281#[cfg(test)]
282mod test_to_bytes {
283 use super::Z;
284
285 /// Ensures that [`Z::to_bytes`] is inverse to [`Z::from_bytes`] for positive values.
286 #[test]
287 fn inverse_to_from_bytes() {
288 let bytes: Vec<u8> = vec![0, 255, 128, 77, 31, 52];
289
290 let integer = Z::from_bytes(&bytes);
291 let cmp_bytes = integer.to_bytes();
292
293 assert_eq!(bytes, cmp_bytes);
294 }
295
296 /// Ensures that [`Z::ZERO`] results in an empty vector of bytes.
297 #[test]
298 fn zero() {
299 let integer = Z::ZERO;
300 let cmp_bytes: Vec<u8> = vec![];
301
302 let bytes = integer.to_bytes();
303
304 assert_eq!(cmp_bytes, bytes);
305 }
306
307 /// Ensure that negative values are represented as they are stored in memory.
308 #[test]
309 fn negative() {
310 let integer = Z::MINUS_ONE;
311 let cmp_bytes: Vec<u8> = vec![255];
312
313 let bytes = integer.to_bytes();
314 let _integer = Z::from_bytes(&bytes);
315
316 assert_eq!(cmp_bytes, bytes);
317 }
318}
319
320#[cfg(test)]
321mod test_to_utf8 {
322 use super::Z;
323
324 /// Ensures that [`Z::to_utf8`] is inverse to [`Z::from_utf8`] for valid UTF8-Encodings.
325 #[test]
326 fn inverse_to_from_utf8() {
327 let cmp_text = "Some valid string formatted in UTF8!";
328
329 let integer = Z::from_utf8(cmp_text);
330 let text = integer.to_utf8().unwrap();
331
332 assert_eq!(cmp_text, text);
333 }
334
335 /// Ensures that [`Z::to_utf8`] outputs an error
336 /// if the integer contains an invalid UTF8-Encoding.
337 #[test]
338 fn invalid_encoding() {
339 let invalid_sequence = [128];
340
341 let integer = Z::from_bytes(&invalid_sequence);
342 let text = integer.to_utf8();
343
344 assert!(text.is_err());
345 }
346}