qfall_math/rational/q/
properties.rs

1// Copyright © 2023 Niklas Siemer
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 includes functionality about properties of [`Q`] instances.
10
11use super::Q;
12use flint_sys::fmpq::{fmpq_abs, fmpq_inv, fmpq_is_one, fmpq_is_zero};
13
14impl Q {
15    /// Returns the given [`Q`] instance with its absolute value.
16    ///
17    /// # Examples
18    /// ```
19    /// use qfall_math::rational::Q;
20    /// let mut value = Q::from(-1);
21    ///
22    /// let value = value.abs();
23    ///
24    /// assert_eq!(Q::ONE, value);
25    /// ```
26    pub fn abs(mut self) -> Self {
27        unsafe {
28            fmpq_abs(&mut self.value, &self.value);
29        }
30        self
31    }
32
33    /// Returns the inverse of `self` as a fresh [`Q`] instance.
34    ///
35    /// As the inverse of `0` is undefined, it returns `None` in case `self == 0`.
36    ///
37    /// # Examples
38    /// ```
39    /// use qfall_math::rational::Q;
40    /// let value = Q::from(4);
41    ///
42    /// let inverse = value.inverse().unwrap();
43    ///
44    /// assert_eq!(Q::from((1, 4)), inverse);
45    /// ```
46    pub fn inverse(&self) -> Option<Q> {
47        if self == &Q::ZERO {
48            return None;
49        }
50
51        let mut out = Q::ZERO;
52        unsafe { fmpq_inv(&mut out.value, &self.value) };
53        Some(out)
54    }
55
56    /// Checks if a [`Q`] is `0`.
57    ///
58    /// Returns `true` if the value is `0`.
59    ///
60    /// # Examples
61    /// ```
62    /// use qfall_math::rational::Q;
63    ///
64    /// let value = Q::ZERO;
65    /// assert!(value.is_zero());
66    /// ```
67    pub fn is_zero(&self) -> bool {
68        1 == unsafe { fmpq_is_zero(&self.value) }
69    }
70
71    /// Checks if a [`Q`] is `1`.
72    ///
73    /// Returns `true` if the value is `1`.
74    ///
75    /// # Examples
76    /// ```
77    /// use qfall_math::rational::Q;
78    ///
79    /// let value = Q::ONE;
80    /// assert!(value.is_one());
81    /// ```
82    pub fn is_one(&self) -> bool {
83        1 == unsafe { fmpq_is_one(&self.value) }
84    }
85}
86
87#[cfg(test)]
88mod test_abs {
89    use super::Q;
90
91    /// Checks whether `abs` returns the positive value for small values correctly.
92    #[test]
93    fn small_values() {
94        let pos = Q::ONE;
95        let zero = Q::ZERO;
96        let neg = Q::from(-15);
97
98        assert_eq!(Q::ONE, pos.abs());
99        assert_eq!(Q::ZERO, zero.abs());
100        assert_eq!(Q::from(15), neg.abs());
101    }
102
103    /// Checks whether `abs` returns the positive value for large values correctly.
104    #[test]
105    fn large_values() {
106        let pos = Q::from(i64::MAX);
107        let neg = Q::from((1, i64::MIN));
108
109        assert_eq!(Q::from(i64::MAX), pos.abs());
110        assert_eq!(Q::from((-1, i64::MIN)), neg.abs());
111    }
112}
113
114#[cfg(test)]
115mod test_inv {
116    use super::Q;
117
118    /// Checks whether the inverse is correctly computed for small values.
119    #[test]
120    fn small_values() {
121        let val_0 = Q::from(4);
122        let val_1 = Q::from((2, -7));
123
124        let inv_0 = val_0.inverse().unwrap();
125        let inv_1 = val_1.inverse().unwrap();
126
127        assert_eq!(Q::from((1, 4)), inv_0);
128        assert_eq!(Q::from((-7, 2)), inv_1);
129    }
130
131    /// Checks whether the inverse is correctly computed for large values.
132    #[test]
133    fn large_values() {
134        let val_0 = Q::from((1, i64::MAX));
135        let val_1 = Q::from(i64::MIN);
136
137        let inv_0 = val_0.inverse().unwrap();
138        let inv_1 = val_1.inverse().unwrap();
139
140        assert_eq!(Q::from(i64::MAX), inv_0);
141        assert_eq!(Q::from((1, i64::MIN)), inv_1);
142    }
143
144    /// Checks whether the inverse of `0` returns `None`.
145    #[test]
146    fn inv_zero_none() {
147        let zero = Q::ZERO;
148
149        let inv_zero = zero.inverse();
150
151        assert!(inv_zero.is_none());
152    }
153}
154
155#[cfg(test)]
156mod test_is_zero {
157    use super::Q;
158    use std::str::FromStr;
159
160    /// Ensure that is_zero returns `true` for `0`.
161    #[test]
162    fn zero_detection() {
163        let zero = Q::ZERO;
164
165        assert!(zero.is_zero());
166    }
167
168    /// Ensure that is_zero returns `false` for non-zero values.
169    #[test]
170    fn zero_rejection() {
171        let small = Q::from(2);
172        let large = Q::from_str(&format!("{}", (u128::MAX - 1) / 2 + 1)).unwrap();
173
174        assert!(!small.is_zero());
175        assert!(!large.is_zero());
176    }
177}
178
179#[cfg(test)]
180mod test_is_one {
181    use super::Q;
182    use std::str::FromStr;
183
184    /// Ensure that is_one returns `true` for `1`.
185    #[test]
186    fn one_detection() {
187        let one = Q::ONE;
188
189        assert!(one.is_one());
190    }
191
192    /// Ensure that is_one returns `false` for other values.
193    #[test]
194    fn one_rejection() {
195        let small = Q::from(2);
196        let large = Q::from_str(&format!("1/{}", (u128::MAX - 1) / 2 + 2)).unwrap();
197
198        assert!(!small.is_one());
199        assert!(!large.is_one());
200    }
201}