Skip to main content

qfall_math/rational/q/
from.rs

1// Copyright 2023 Marcel Luca Schmidt, 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 [`Q`] value from other types.
10//!
11//! The explicit functions contain the documentation.
12
13use super::Q;
14use crate::{
15    error::{MathError, StringConversionError},
16    integer::Z,
17    macros::from::{from_trait, from_type},
18    traits::{AsInteger, Pow},
19};
20use flint3_sys::{
21    fmpq, {fmpq_canonicalise, fmpq_clear, fmpq_get_d, fmpq_set_str}, {fmpz_init_set, fmpz_is_zero},
22};
23use std::{ffi::CString, str::FromStr};
24
25impl Q {
26    /// Creates a rational number of type [`Q`] from a [`f64`].
27    /// This function works with the exact float it received as input.
28    /// Many numbers like `0.1` are not exactly representable as floats and
29    /// will therefore not be instantiated as `1/10`.
30    ///
31    /// Input parameters:
32    /// - `value`: the value the rational number will have, provided as a [`f64`]
33    ///
34    /// Returns a [`Q`].
35    ///
36    /// # Examples
37    /// ```rust
38    /// use qfall_math::rational::Q;
39    ///
40    /// let a: Q = Q::from_f64(0.3);
41    /// let a: Q = Q::from_f64(-123.4567);
42    /// ```
43    pub fn from_f64(value: f64) -> Self {
44        let bits: u64 = value.to_bits();
45        let sign = if bits >> 63 == 0 {
46            Q::ONE
47        } else {
48            Q::MINUS_ONE
49        };
50        let mut exponent: i16 = ((bits >> 52) & 0x7ff) as i16;
51        let mantissa = if exponent == 0 {
52            (bits & 0xfffffffffffff) << 1
53        } else {
54            // prepend one bit to the mantissa because the most significant bit is implicit
55            (bits & 0xfffffffffffff) | 0x10000000000000
56        };
57
58        // -1023 because of the offset representation of the exponent
59        // -52 because the mantissa is 52 bit long
60        exponent -= 1023 + 52;
61        let shift = match exponent {
62            // This could be optimized with `fmpz_lshift_mpn` once it is part of flint3_sys.
63            e if e >= 1 => Q::from(2).pow(e).unwrap(),
64            e => Q::from((1, 2)).pow(e.abs()).unwrap(),
65        };
66
67        sign * Z::from(mantissa) * shift
68    }
69
70    from_type!(f32, f64, Q, Q::from_f64);
71}
72
73impl<IntegerNumerator: AsInteger, IntegerDenominator: AsInteger>
74    From<(IntegerNumerator, IntegerDenominator)> for Q
75{
76    /// Creates a [`Q`] from two integers.
77    ///
78    /// Parameters:
79    /// - `num`: the value of the numerator.
80    /// - `den`: the value of the denominator.
81    ///
82    /// Returns a [`Q`].
83    ///
84    /// # Examples
85    /// ```rust
86    /// use qfall_math::rational::Q;
87    /// use qfall_math::integer::Z;
88    ///
89    /// let a = Q::from((42, &2));
90    /// let b = Q::from((Z::from(84), 4));
91    ///
92    /// assert_eq!(a, b);
93    /// ```
94    ///
95    /// # Panics ...
96    /// - if the denominator is zero.
97    fn from((num, den): (IntegerNumerator, IntegerDenominator)) -> Self {
98        unsafe {
99            let num = num.into_fmpz();
100            let den = den.into_fmpz();
101
102            assert_ne!(den, 0, "The denominator can not be zero");
103
104            let mut value = fmpq { num, den };
105            fmpq_canonicalise(&mut value);
106
107            Q { value }
108        }
109    }
110}
111
112impl<Integer: Into<Z>> From<Integer> for Q {
113    /// Creates a [`Q`] from a value that implements [`Into<Z>`].
114    ///
115    /// Parameters:
116    /// - `value`: the initial value the [`Q`] should have.
117    ///
118    /// Returns a [`Q`].
119    ///
120    /// # Examples
121    /// ```
122    /// use qfall_math::rational::Q;
123    /// use qfall_math::integer::Z;
124    ///
125    /// let a: Q = Q::from(17);
126    /// let b: Q = Q::from(Z::from(17));
127    /// ```
128    fn from(value: Integer) -> Self {
129        let value = value.into();
130        // this efficient implementation depends on Q::default instantiating 1 as denominator
131        let mut out = Q::default();
132        unsafe { fmpz_init_set(&mut out.value.num, &value.value) }
133        out
134    }
135}
136
137impl From<f64> for Q {
138    /// Create a new rational number of type [`Q`] from a [`f64`].
139    /// This function works with the bit representation of the float it received as input.
140    /// Floats like `0.1` that are not completely representable,
141    /// will not be instantiated as `1/10`.
142    ///
143    /// Input parameters:
144    /// - `value`: the value the rational number will have, provided as a [`f64`]
145    ///
146    /// Returns a [`Q`].
147    ///
148    /// # Examples
149    /// ```rust
150    /// use qfall_math::rational::Q;
151    ///
152    /// let a: Q = Q::from(0.3);
153    /// let a: Q = Q::from(-123.4567);
154    /// ```
155    fn from(value: f64) -> Self {
156        Q::from_f64(value)
157    }
158}
159
160from_trait!(f32, Q, Q::from_f32);
161
162impl From<&Q> for f64 {
163    /// Convert a rational [`Q`] into an [`f64`].
164    /// The value is rounded to the closest [`f64`] representation.
165    ///
166    /// **WARNING:** The return is system dependent if `self` is
167    /// is too large or too small to fit in an [`f64`], i.e. the value should be within
168    /// [`f64::MIN`] and [`f64::MAX`]. It the entry can't be represented exactly, it will
169    /// be rounded towards zero.
170    ///
171    /// **WARNING:** Please be aware that the deviation of the representation of `self` as a [`f64`]
172    /// will scale with the size of `self`, e.g. a value within the size of `2^{64}`
173    /// might deviate from the original value by a distance of `1_000`.
174    ///
175    /// # Examples
176    /// ```
177    /// use qfall_math::rational::Q;
178    ///
179    /// let one_half = Q::from((1, 2));
180    /// let float = f64::from(&one_half);
181    ///
182    /// assert_eq!(0.5, float);
183    /// ```
184    fn from(value: &Q) -> Self {
185        unsafe { fmpq_get_d(&value.value) }
186    }
187}
188
189impl From<&Q> for Q {
190    /// An alias for [`Q::clone`].
191    /// It makes the use of generic [`Into<Q>`] types easier.
192    fn from(value: &Q) -> Self {
193        value.clone()
194    }
195}
196
197impl FromStr for Q {
198    type Err = MathError;
199
200    /// Creates a [`Q`] rational from a [`String`]
201    /// In the string should be two decimal numbers separated by `/`.
202    /// Optionally, before one or both of them can be a `-`.
203    /// The format of that string looks like this `-12/53`.
204    ///
205    /// If the number is an integer, the string can be in the format of one.
206    /// The format of that string looks like this `-12`.
207    /// It is automatically transformed to `-12/1`.
208    ///
209    /// Parameters:
210    /// - `s`: the rational value
211    ///
212    /// Returns a [`Q`] or an error if the provided string was not formatted
213    /// correctly, contained a `Null` byte, or the denominator was `0`.
214    ///
215    /// # Examples
216    /// ```
217    /// use std::str::FromStr;
218    /// use qfall_math::rational::Q;
219    ///  
220    /// let a: Q = "100/3".parse().unwrap();
221    /// let b: Q = Q::from_str("100/3").unwrap();
222    /// ```
223    ///
224    /// ```
225    /// use std::str::FromStr;
226    /// use qfall_math::rational::Q;
227    ///  
228    /// let q: Q = Q::from_str("-10/3").unwrap();
229    /// let b: Q = Q::from_str("10/-3").unwrap();
230    /// ```
231    ///
232    /// ```
233    /// use std::str::FromStr;
234    /// use qfall_math::rational::Q;
235    ///  
236    /// let q: Q = Q::from_str("-10").unwrap();
237    /// let b: Q = Q::from_str("10").unwrap();
238    /// ```
239    ///
240    /// # Errors and Failures
241    /// - Returns a [`MathError`] of type
242    ///   [`StringConversionError`](MathError::StringConversionError)
243    ///     - if the provided string contains a `Null` byte, or
244    ///     - if the provided string was not formatted correctly.
245    /// - Returns a [`MathError`] of type
246    ///   [`DivisionByZeroError`](MathError::DivisionByZeroError)
247    ///   if the provided string has `0` as the denominator.
248    fn from_str(s: &str) -> Result<Self, MathError> {
249        if s.contains(char::is_whitespace) {
250            Err(StringConversionError::InvalidStringToQInput(s.to_owned()))?;
251        }
252
253        // `default()` returns the value `0/0`
254        let mut value = fmpq::default();
255
256        let c_string = CString::new(s)?;
257
258        // -1 is returned if the string is an invalid input.
259        //
260        // Given the documentation `c_string.as_ptr()` is freed once c_string is deallocated
261        // 'The pointer will be valid for as long as `self` is'
262        // For reading more look at the documentation of `.as_ptr()`.
263        //
264        // since value is set to `0`, if an error occurs, we do not need to free
265        // the allocated space manually
266        if -1 == unsafe { fmpq_set_str(&mut value, c_string.as_ptr(), 10) } {
267            Err(StringConversionError::InvalidStringToQInput(s.to_owned()))?;
268        };
269
270        // canonical form is expected by other functions
271        unsafe { fmpq_canonicalise(&mut value) };
272
273        // if `value.den` is set to `0`, `value.num` is not necessarily `0` as well.
274        // hence we do need to free the allocated space of the numerator
275        // manually by using `fmpq_clear`
276        match unsafe { fmpz_is_zero(&value.den) } {
277            0 => Ok(Q { value }),
278            _ => {
279                unsafe {
280                    fmpq_clear(&mut value);
281                }
282                Err(MathError::DivisionByZeroError(s.to_owned()))
283            }
284        }
285    }
286}
287
288#[cfg(test)]
289mod tests_from_str {
290    use crate::rational::Q;
291    use std::str::FromStr;
292
293    /// Ensure that initialization with large numerators and denominators works.
294    #[test]
295    fn max_int_positive() {
296        let mut s_1 = (i64::MAX).to_string();
297        s_1.push('/');
298        s_1.push_str(&(i64::MAX).to_string());
299
300        let mut s_2 = ("1/").to_string();
301        s_2.push_str(&(i64::MAX).to_string());
302
303        assert!(Q::from_str(&(i64::MAX).to_string()).is_ok());
304        assert!(Q::from_str(&s_1).is_ok());
305        assert!(Q::from_str(&s_2).is_ok());
306    }
307
308    /// Ensure that initialization with large numerators and denominators
309    /// (larger than i64) works.
310    #[test]
311    fn large_positive() {
312        let mut s_1 = "1".repeat(65);
313        s_1.push('/');
314        s_1.push_str(&"1".repeat(65));
315
316        let mut s_2 = ("1/").to_string();
317        s_2.push_str(&"1".repeat(65));
318
319        assert!(Q::from_str(&"1".repeat(65)).is_ok());
320        assert!(Q::from_str(&s_1).is_ok());
321        assert!(Q::from_str(&s_2).is_ok());
322    }
323
324    /// Ensure that initialization with large negative numerators and
325    /// denominators works.
326    #[test]
327    fn max_int_negative() {
328        let mut s_1 = (i64::MIN).to_string();
329        s_1.push('/');
330        s_1.push_str(&(i64::MIN).to_string());
331
332        let mut s_2 = ("1/").to_string();
333        s_2.push_str(&(i64::MIN).to_string());
334
335        assert!(Q::from_str(&(i64::MIN).to_string()).is_ok());
336        assert!(Q::from_str(&s_1).is_ok());
337        assert!(Q::from_str(&s_2).is_ok());
338    }
339
340    /// Ensure that initialization with large negative numerators and
341    /// denominators (larger than [`i64`]) works.
342    #[test]
343    fn large_negative() {
344        let mut s_1 = "-".to_string();
345        s_1.push_str(&"1".repeat(65));
346        s_1.push('/');
347        s_1.push_str(&"1".repeat(65));
348
349        let mut s_2 = ("-1/").to_string();
350        s_2.push_str(&"1".repeat(65));
351
352        assert!(Q::from_str(&"1".repeat(65)).is_ok());
353        assert!(Q::from_str(&s_1).is_ok());
354        assert!(Q::from_str(&s_2).is_ok());
355    }
356
357    /// Ensure that an initialization with two minus works.
358    #[test]
359    fn no_error_both_minus() {
360        assert!(Q::from_str("-3/-2").is_ok());
361    }
362
363    /// Ensure that wrong initialization yields an Error.
364    #[test]
365    fn error_wrong_letters() {
366        assert!(Q::from_str("hbrkt35itu3gg").is_err());
367    }
368
369    /// Ensure that wrong initialization yields an Error.
370    #[test]
371    fn error_wrong_order() {
372        assert!(Q::from_str("3/2-").is_err());
373    }
374
375    /// Ensure that wrong initialization yields an Error.
376    #[test]
377    fn error_two_divisions() {
378        assert!(Q::from_str("3/2/4").is_err());
379    }
380
381    /// Ensure that wrong initialization yields an Error.
382    #[test]
383    fn error_wrong_minus() {
384        assert!(Q::from_str("-3-4/2").is_err());
385    }
386
387    /// Ensure that wrong initialization yields an Error.
388    #[test]
389    fn error_whitespace_mid() {
390        assert!(Q::from_str("876/ 543").is_err());
391    }
392
393    /// Ensure that wrong initialization yields an Error.
394    #[test]
395    fn error_whitespace_start() {
396        assert!(Q::from_str(" 876543").is_err());
397    }
398
399    /// Ensure that wrong initialization yields an Error.
400    #[test]
401    fn error_whitespace_end() {
402        assert!(Q::from_str("876543 ").is_err());
403    }
404
405    /// Ensure that wrong initialization yields an Error.
406    #[test]
407    fn error_whitespace_minus() {
408        assert!(Q::from_str("- 876543").is_err());
409    }
410
411    /// Ensure that values returned by [`Q::from_str()`] are canonical.
412    #[test]
413    fn canonical_result() {
414        let one_1 = Q::from_str("1/1").unwrap();
415        let one_2 = Q::from_str("2/2").unwrap();
416        let one_3 = Q::from_str("-42/-42").unwrap();
417
418        let zero_1 = Q::from_str("0/1").unwrap();
419        let zero_2 = Q::from_str("0/42").unwrap();
420
421        assert_eq!(one_1, one_2);
422        assert_eq!(one_1, one_3);
423        assert_eq!(zero_1, zero_2);
424    }
425}
426
427#[cfg(test)]
428mod test_from_int_int {
429    use crate::integer::Z;
430    use crate::integer_mod_q::Zq;
431    use crate::rational::Q;
432
433    /// Test that the different combinations of rust integers, [`Z`], and [`Zq`]
434    /// in their owned and borrowed form can be used to create a [`Q`].
435    #[test]
436    fn different_types() {
437        let int_8: i8 = 10;
438        let int_16: i16 = 10;
439        let int_32: i32 = 10;
440        let int_64: i64 = 10;
441        let uint_8: u8 = 10;
442        let uint_16: u16 = 10;
443        let uint_32: u32 = 10;
444        let uint_64: u64 = 10;
445        let z = Z::from(10);
446        let zq = Zq::from((10, 20));
447
448        // owned, owned the same type in numerator and denominator
449        let _ = Q::from((int_8, int_8));
450        let _ = Q::from((int_16, int_16));
451        let _ = Q::from((int_32, int_32));
452        let _ = Q::from((int_64, int_64));
453        let _ = Q::from((uint_8, uint_8));
454        let _ = Q::from((uint_16, uint_16));
455        let _ = Q::from((uint_32, uint_32));
456        let _ = Q::from((uint_64, uint_64));
457        let _ = Q::from((z.clone(), z.clone()));
458        let _ = Q::from((zq.clone(), zq.clone()));
459
460        // borrowed, borrowed the same type in numerator and denominator
461        let _ = Q::from((&int_8, &int_8));
462        let _ = Q::from((&int_16, &int_16));
463        let _ = Q::from((&int_32, &int_32));
464        let _ = Q::from((&int_64, &int_64));
465        let _ = Q::from((&uint_8, &uint_8));
466        let _ = Q::from((&uint_16, &uint_16));
467        let _ = Q::from((&uint_32, &uint_32));
468        let _ = Q::from((&uint_64, &uint_64));
469        let _ = Q::from((&z, &z));
470        let _ = Q::from((&zq, &zq));
471
472        // From now on assume that i/u8, i/u16, i/u32 and i/u64 behave the same.
473        // This assumption is reasonable, since their implementation is the same.
474
475        // owned, owned mixed types
476        let _ = Q::from((int_8, z.clone()));
477        let _ = Q::from((zq.clone(), z.clone()));
478        let _ = Q::from((z.clone(), int_8));
479        let _ = Q::from((z.clone(), zq.clone()));
480        let _ = Q::from((int_8, zq.clone()));
481        let _ = Q::from((zq.clone(), int_8));
482
483        // owned, borrowed mixed types
484        let _ = Q::from((int_8, &z));
485        let _ = Q::from((zq.clone(), &z));
486        let _ = Q::from((z.clone(), &int_8));
487        let _ = Q::from((z.clone(), &zq));
488        let _ = Q::from((int_8, &zq));
489        let _ = Q::from((zq.clone(), &int_8));
490
491        // borrowed, owned mixed types
492        let _ = Q::from((&int_8, z.clone()));
493        let _ = Q::from((&zq, z.clone()));
494        let _ = Q::from((&z, int_8));
495        let _ = Q::from((&z, zq.clone()));
496        let _ = Q::from((&int_8, zq.clone()));
497        let _ = Q::from((&zq, int_8));
498
499        // borrowed, borrowed mixed types
500        let _ = Q::from((&int_8, &z));
501        let _ = Q::from((&zq, &z));
502        let _ = Q::from((&z, &int_8));
503        let _ = Q::from((&z, &zq));
504        let _ = Q::from((&int_8, &zq));
505        let _ = Q::from((&zq, &int_8));
506    }
507
508    /// Ensure that large parameters work (FLINT uses pointer representation).
509    #[test]
510    fn working_large() {
511        let numerator = u64::MAX;
512        let denominator = u64::MAX - 1;
513        let numerator_z = Z::from(numerator);
514        let denominator_z = Z::from(denominator);
515
516        let q_1 = Q::from((numerator, denominator));
517        let q_2 = Q::from((numerator_z, denominator_z));
518
519        assert_eq!(q_1, q_2);
520    }
521
522    /// Test with `0` denominator (not valid -> should lead to an error)
523    #[test]
524    #[should_panic]
525    fn divide_by_zero() {
526        let _ = Q::from((10, 0));
527    }
528
529    /// Test with either negative denominator or numerator
530    #[test]
531    fn negative_small() {
532        let numerator = 10;
533        let denominator = -1;
534
535        let q_1 = Q::from((numerator, denominator));
536        let q_2 = Q::from((-numerator, -denominator));
537
538        assert_eq!(q_1, q_2);
539    }
540
541    /// Ensure that the result is canonical for small parameters.
542    #[test]
543    fn canonical_small() {
544        let numerator = 10;
545        let denominator = 1;
546
547        let q_1 = Q::from((numerator, denominator));
548        let q_2 = Q::from((-numerator, -denominator));
549        let q_3 = Q::from((numerator * 2, denominator * 2));
550
551        let q_4_negative = Q::from((-numerator, denominator));
552        let q_5_negative = Q::from((numerator, -denominator));
553
554        assert_eq!(q_1, q_2);
555        assert_eq!(q_1, q_3);
556
557        assert_eq!(q_4_negative, q_5_negative);
558    }
559
560    /// Ensure that the result is canonical for large parameters.
561    #[test]
562    fn canonical_large() {
563        let numerator = i64::MAX;
564        let denominator = i64::MAX - 1;
565
566        let numerator_z = Z::from(numerator);
567        let denominator_z = Z::from(denominator);
568
569        let q_1 = Q::from((numerator, denominator));
570        let q_2 = Q::from((-numerator, -denominator));
571        let q_3 = Q::from((&numerator_z, &denominator_z));
572        let q_4 = Q::from((&numerator_z * 2, &denominator_z * 2));
573        let q_5_negative = Q::from((-1 * &numerator_z, &denominator_z));
574        let q_6_negative = Q::from((&numerator_z, -1 * &denominator_z));
575
576        assert_eq!(q_1, q_2);
577        assert_eq!(q_1, q_3);
578        assert_eq!(q_1, q_4);
579        assert_eq!(q_5_negative, q_6_negative);
580    }
581}
582
583#[cfg(test)]
584mod test_try_from_int_int {
585    use crate::integer::Z;
586    use crate::rational::Q;
587
588    /// Test the different borrowed parameter types with small numerator and denominator.
589    #[test]
590    fn test_types_borrowed_small() {
591        let numerator = 10;
592        let denominator = 15;
593
594        let q_1 = Q::from((&(numerator as u8), &(denominator as i8)));
595        let q_2 = Q::from((&(numerator as u16), &(denominator as i16)));
596        let q_3 = Q::from((&(numerator as u32), &(denominator)));
597        let q_4 = Q::from((&(numerator as u64), &(denominator as i64)));
598        let q_5 = Q::from((&Z::from(numerator), &Z::from(denominator)));
599
600        let q_6 = Q::from((&(numerator as i16), &(denominator as u16)));
601        let q_7 = Q::from((&(numerator as i16), &(denominator as i16)));
602
603        assert_eq!(q_1, q_2);
604        assert_eq!(q_1, q_3);
605        assert_eq!(q_1, q_4);
606        assert_eq!(q_1, q_5);
607        assert_eq!(q_1, q_6);
608        assert_eq!(q_1, q_7);
609    }
610
611    /// Ensure that large parameters work (FLINT uses pointer representation).
612    #[test]
613    fn working_large() {
614        let numerator = u64::MAX;
615        let denominator = u64::MAX - 1;
616        let numerator_z = Z::from(numerator);
617        let denominator_z = Z::from(denominator);
618
619        let q_1 = Q::from((&numerator, &denominator));
620        let q_2 = Q::from((&numerator_z, &denominator_z));
621
622        assert_eq!(q_1, q_2);
623    }
624
625    /// Test with `0` denominator (not valid -> should lead to an error)
626    #[test]
627    #[should_panic]
628    fn divide_by_zero() {
629        let numerator = 10;
630        let denominator = 0;
631
632        let _ = Q::from((&numerator, &denominator));
633    }
634
635    /// Test with either negative denominator or numerator
636    #[test]
637    fn negative_small() {
638        let numerator = 10;
639        let denominator = -1;
640
641        let q_1 = Q::from((&numerator, &denominator));
642        let q_2 = Q::from((&-numerator, &-denominator));
643
644        assert_eq!(q_1, q_2);
645    }
646
647    /// Ensure that the result is canonical for small parameters.
648    #[test]
649    fn canonical_small() {
650        let numerator = 10;
651        let denominator = 1;
652
653        let q_1 = Q::from((&numerator, &denominator));
654        let q_2 = Q::from((&-numerator, &-denominator));
655        let q_3 = Q::from((&(numerator * 2), &(denominator * 2)));
656
657        let q_4_negative = Q::from((&-numerator, &denominator));
658        let q_5_negative = Q::from((&numerator, &-denominator));
659
660        assert_eq!(q_1, q_2);
661        assert_eq!(q_1, q_3);
662
663        assert_eq!(q_4_negative, q_5_negative);
664    }
665
666    /// Ensure that the result is canonical for large parameters.
667    #[test]
668    fn canonical_large() {
669        let numerator = i64::MAX;
670        let denominator = i64::MAX - 1;
671
672        let numerator_z = Z::from(numerator);
673        let denominator_z = Z::from(denominator);
674
675        let q_1 = Q::from((&numerator, &denominator));
676        let q_2 = Q::from((&-numerator, &-denominator));
677        let q_3 = Q::from((&numerator_z, &denominator_z));
678        let q_4 = Q::from((&(&numerator_z * Z::from(2)), &(&denominator_z * Z::from(2))));
679        let q_5_negative = Q::from((&(&numerator_z * Z::from(-1)), &denominator_z));
680        let q_6_negative = Q::from((&numerator_z, &(&denominator_z * Z::from(-1))));
681
682        assert_eq!(q_1, q_2);
683        assert_eq!(q_1, q_3);
684        assert_eq!(q_1, q_4);
685        assert_eq!(q_5_negative, q_6_negative);
686    }
687}
688
689#[cfg(test)]
690mod test_from_z {
691    use super::Q;
692    use crate::integer::Z;
693
694    /// Ensure that the [`From`] trait is available and works correctly for
695    /// small and large instances of [`Z`].
696    #[test]
697    fn from_trait() {
698        let z_1 = Z::from(u64::MAX);
699        let z_2 = Z::from(17);
700
701        assert_eq!(Q::from(u64::MAX), Q::from(z_1));
702        assert_eq!(Q::from(17), Q::from(z_2));
703    }
704
705    /// Ensure that all types that can be turned into an [`Z`]
706    /// can be used to instantiate a [`Q`]
707    #[test]
708    fn from_into_z() {
709        let _ = Q::from(u8::MAX);
710        let _ = Q::from(u16::MAX);
711        let _ = Q::from(u32::MAX);
712        let _ = Q::from(u64::MAX);
713
714        let _ = Q::from(i8::MIN);
715        let _ = Q::from(i16::MIN);
716        let _ = Q::from(i32::MIN);
717        let _ = Q::from(i64::MIN);
718    }
719}
720
721#[cfg(test)]
722mod test_from_float {
723    use super::Q;
724    use std::{
725        f64::consts::{E, LN_2, LN_10},
726        str::FromStr,
727    };
728
729    /// Test that a large number is correctly converted from float.
730    #[test]
731    fn large_value() {
732        // This is the exact value stored when creating a float with the value 1e+100
733        let a: f64 = 10000000000000000159028911097599180468360808563945281389781327557747838772170381060813469985856815104.0;
734
735        let q = Q::from(a);
736
737        let cmp = Q::from_str("10000000000000000159028911097599180468360808563945281389781327557747838772170381060813469985856815104")
738                    .unwrap();
739        assert_eq!(q, cmp);
740    }
741
742    // Test that a small number is correctly converted from float.
743    #[test]
744    #[allow(clippy::excessive_precision)]
745    fn small_value() {
746        // This is the exact value stored when creating a float with the value 0.1
747        let a: f64 = 0.1000000000000000055511151231257827021181583404541015625;
748
749        let q = Q::from(a);
750
751        let cmp = Q::from_str("1000000000000000055511151231257827021181583404541015625/10000000000000000000000000000000000000000000000000000000")
752            .unwrap();
753        assert_eq!(q, cmp);
754    }
755
756    /// Enure that the from works correctly for positive values
757    #[test]
758    fn positive() {
759        let numerator = 150001;
760        let denominator = 16;
761
762        let value = Q::from(numerator as f64 / denominator as f64);
763
764        let cmp = Q::from((numerator, denominator));
765        assert_eq!(cmp, value);
766    }
767
768    /// Enure that the from works correctly for positive values
769    #[test]
770    fn negative() {
771        let numerator = 150001;
772        let denominator = -8;
773
774        let value = Q::from(numerator as f64 / denominator as f64);
775
776        let cmp = Q::from((numerator, denominator));
777        assert_eq!(cmp, value);
778    }
779
780    /// Ensure that the [`From`] trait is available for [`f64`] constants
781    #[test]
782    fn from_trait() {
783        let _ = Q::from(E);
784        let _ = Q::from(LN_10);
785        let _ = Q::from(LN_2);
786    }
787
788    /// test availability for [`f32`]
789    #[test]
790    fn from_f32_available() {
791        let f: f32 = 42.17;
792
793        let _ = Q::from(f);
794        let _ = Q::from_f32(f);
795    }
796}
797
798#[cfg(test)]
799mod test_into_float {
800    use super::*;
801
802    /// Ensure that `1/2` is correctly converted to `0.5`.
803    /// Special about `0.5` is that it can be exactly represented as a float.
804    #[test]
805    fn one_half() {
806        let one_half = Q::from((1, 2));
807        let float = f64::from(&one_half);
808
809        assert_eq!(0.5, float);
810    }
811
812    /// Ensure that a roundtrip [`f64`] -> [`Q`] -> [`f64`]
813    /// does not change the value.
814    #[test]
815    fn round_trip() {
816        let start_values = vec![
817            0.0,
818            0.1,
819            f64::MAX,
820            f64::MIN,
821            f64::MIN_POSITIVE,
822            f64::EPSILON,
823        ];
824
825        for start in start_values {
826            let end = f64::from(&Q::from(start));
827
828            assert_eq!(start, end);
829        }
830    }
831}
832
833#[cfg(test)]
834mod test_from_q_ref {
835    use crate::rational::Q;
836
837    /// Ensure that [`Q`] can be created from [`&Q`].
838    #[test]
839    fn availability() {
840        let q = Q::from(u64::MAX);
841
842        let _ = Q::from(&q);
843    }
844}