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