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}