1 // Copyright © 2016–2020 University of Malta
2
3 // This program is free software: you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public License
5 // as published by the Free Software Foundation, either version 3 of
6 // the License, or (at your option) any later version.
7 //
8 // This program is distributed in the hope that it will be useful, but
9 // WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 // General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public
14 // License and a copy of the GNU General Public License along with
15 // this program. If not, see <https://www.gnu.org/licenses/>.
16
17 use crate::{
18 ext::{xmpq, xmpz},
19 rational::{big, ParseRationalError, TryFromFloatError},
20 Assign, Integer, Rational,
21 };
22 use az::CheckedCast;
23 use core::{
24 convert::TryFrom,
25 fmt::{Binary, Debug, Display, Formatter, LowerHex, Octal, Result as FmtResult, UpperHex},
26 hash::{Hash, Hasher},
27 mem::{self, MaybeUninit},
28 str::FromStr,
29 };
30 use gmp_mpfr_sys::gmp::{self, mpq_t};
31 use std::error::Error;
32
33 impl Default for Rational {
34 #[inline]
default() -> Rational35 fn default() -> Rational {
36 Rational::new()
37 }
38 }
39
40 impl Clone for Rational {
41 #[inline]
clone(&self) -> Rational42 fn clone(&self) -> Rational {
43 unsafe {
44 let mut dst = MaybeUninit::uninit();
45 let inner_ptr = cast_ptr_mut!(dst.as_mut_ptr(), mpq_t);
46 let num = cast_ptr_mut!(gmp::mpq_numref(inner_ptr), Integer);
47 xmpz::init_set(num, self.numer());
48 let den = cast_ptr_mut!(gmp::mpq_denref(inner_ptr), Integer);
49 xmpz::init_set(den, self.denom());
50 dst.assume_init()
51 }
52 }
53
54 #[inline]
clone_from(&mut self, src: &Rational)55 fn clone_from(&mut self, src: &Rational) {
56 self.assign(src);
57 }
58 }
59
60 impl Drop for Rational {
61 #[inline]
drop(&mut self)62 fn drop(&mut self) {
63 unsafe {
64 xmpq::clear(self);
65 }
66 }
67 }
68
69 impl Hash for Rational {
70 #[inline]
hash<H: Hasher>(&self, state: &mut H)71 fn hash<H: Hasher>(&self, state: &mut H) {
72 self.numer().hash(state);
73 self.denom().hash(state);
74 }
75 }
76
77 impl FromStr for Rational {
78 type Err = ParseRationalError;
79 #[inline]
from_str(src: &str) -> Result<Rational, ParseRationalError>80 fn from_str(src: &str) -> Result<Rational, ParseRationalError> {
81 Ok(Rational::from(Rational::parse(src)?))
82 }
83 }
84
85 impl Display for Rational {
fmt(&self, f: &mut Formatter<'_>) -> FmtResult86 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
87 fmt_radix(self, f, 10, false, "")
88 }
89 }
90
91 impl Debug for Rational {
fmt(&self, f: &mut Formatter<'_>) -> FmtResult92 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
93 fmt_radix(self, f, 10, false, "")
94 }
95 }
96
97 impl Binary for Rational {
fmt(&self, f: &mut Formatter<'_>) -> FmtResult98 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
99 fmt_radix(self, f, 2, false, "0b")
100 }
101 }
102
103 impl Octal for Rational {
fmt(&self, f: &mut Formatter<'_>) -> FmtResult104 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
105 fmt_radix(self, f, 8, false, "0o")
106 }
107 }
108
109 impl LowerHex for Rational {
fmt(&self, f: &mut Formatter<'_>) -> FmtResult110 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
111 fmt_radix(self, f, 16, false, "0x")
112 }
113 }
114
115 impl UpperHex for Rational {
fmt(&self, f: &mut Formatter<'_>) -> FmtResult116 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
117 fmt_radix(self, f, 16, true, "0x")
118 }
119 }
120
121 impl Assign for Rational {
122 #[inline]
assign(&mut self, src: Rational)123 fn assign(&mut self, src: Rational) {
124 drop(mem::replace(self, src));
125 }
126 }
127
128 impl Assign<&Rational> for Rational {
129 #[inline]
assign(&mut self, src: &Rational)130 fn assign(&mut self, src: &Rational) {
131 xmpq::set(self, src);
132 }
133 }
134
135 impl From<&Rational> for Rational {
136 #[inline]
from(src: &Rational) -> Self137 fn from(src: &Rational) -> Self {
138 unsafe {
139 let mut dst = MaybeUninit::uninit();
140 xmpq::init_set(dst.as_mut_ptr(), src);
141 dst.assume_init()
142 }
143 }
144 }
145
146 impl<Num> Assign<Num> for Rational
147 where
148 Integer: Assign<Num>,
149 {
150 #[inline]
assign(&mut self, src: Num)151 fn assign(&mut self, src: Num) {
152 // Safety: no need to canonicalize, as denominator will be 1.
153 let (num, den) = unsafe { self.as_mut_numer_denom_no_canonicalization() };
154 num.assign(src);
155 xmpz::set_1(den);
156 }
157 }
158
159 impl<Num> From<Num> for Rational
160 where
161 Integer: From<Num>,
162 {
163 #[inline]
from(src: Num) -> Self164 fn from(src: Num) -> Self {
165 // Safety: no need to canonicalize, as denominator will be 1.
166 unsafe {
167 let mut dst = MaybeUninit::uninit();
168 let inner_ptr = cast_ptr_mut!(dst.as_mut_ptr(), mpq_t);
169 let num = cast_ptr_mut!(gmp::mpq_numref(inner_ptr), Integer);
170 num.write(Integer::from(src));
171 let den = cast_ptr_mut!(gmp::mpq_denref(inner_ptr), Integer);
172 xmpz::init_set_u32(den, 1);
173 dst.assume_init()
174 }
175 }
176 }
177
178 impl<Num, Den> Assign<(Num, Den)> for Rational
179 where
180 Integer: Assign<Num> + Assign<Den>,
181 {
182 #[inline]
assign(&mut self, src: (Num, Den))183 fn assign(&mut self, src: (Num, Den)) {
184 self.mutate_numer_denom(move |num, den| {
185 num.assign(src.0);
186 den.assign(src.1);
187 })
188 }
189 }
190
191 impl<Num, Den> From<(Num, Den)> for Rational
192 where
193 Integer: From<Num> + From<Den>,
194 {
195 #[inline]
from((num, den): (Num, Den)) -> Self196 fn from((num, den): (Num, Den)) -> Self {
197 let (num, den) = (Integer::from(num), Integer::from(den));
198 let mut dst = MaybeUninit::uninit();
199 xmpq::write_num_den_canonicalize(&mut dst, num, den);
200 // Safety: write_num_den_canonicalize initializes and canonicalizes dst.
201 unsafe { dst.assume_init() }
202 }
203 }
204
205 impl<'a, Num, Den> Assign<&'a (Num, Den)> for Rational
206 where
207 Integer: Assign<&'a Num> + Assign<&'a Den>,
208 {
209 #[inline]
assign(&mut self, src: &'a (Num, Den))210 fn assign(&mut self, src: &'a (Num, Den)) {
211 self.mutate_numer_denom(|num, den| {
212 num.assign(&src.0);
213 den.assign(&src.1);
214 });
215 }
216 }
217
218 impl<'a, Num, Den> From<&'a (Num, Den)> for Rational
219 where
220 Integer: From<&'a Num> + From<&'a Den>,
221 {
222 #[inline]
from(src: &'a (Num, Den)) -> Self223 fn from(src: &'a (Num, Den)) -> Self {
224 let (num, den) = (Integer::from(&src.0), Integer::from(&src.1));
225 let mut dst = MaybeUninit::uninit();
226 xmpq::write_num_den_canonicalize(&mut dst, num, den);
227 // Safety: write_num_den_canonicalize initializes and canonicalizes dst.
228 unsafe { dst.assume_init() }
229 }
230 }
231
232 impl TryFrom<f32> for Rational {
233 type Error = TryFromFloatError;
234 #[inline]
try_from(value: f32) -> Result<Self, TryFromFloatError>235 fn try_from(value: f32) -> Result<Self, TryFromFloatError> {
236 value
237 .checked_cast()
238 .ok_or(TryFromFloatError { _unused: () })
239 }
240 }
241
242 impl TryFrom<f64> for Rational {
243 type Error = TryFromFloatError;
244 #[inline]
try_from(value: f64) -> Result<Self, TryFromFloatError>245 fn try_from(value: f64) -> Result<Self, TryFromFloatError> {
246 value
247 .checked_cast()
248 .ok_or(TryFromFloatError { _unused: () })
249 }
250 }
251
fmt_radix( r: &Rational, f: &mut Formatter<'_>, radix: i32, to_upper: bool, prefix: &str, ) -> FmtResult252 fn fmt_radix(
253 r: &Rational,
254 f: &mut Formatter<'_>,
255 radix: i32,
256 to_upper: bool,
257 prefix: &str,
258 ) -> FmtResult {
259 let mut s = String::new();
260 big::append_to_string(&mut s, r, radix, to_upper);
261 let neg = s.starts_with('-');
262 let buf = if neg { &s[1..] } else { &s[..] };
263 f.pad_integral(!neg, prefix, buf)
264 }
265
266 impl TryFromFloatError {
desc(&self) -> &str267 fn desc(&self) -> &str {
268 "conversion of infinite or NaN value attempted"
269 }
270 }
271
272 impl Error for TryFromFloatError {
description(&self) -> &str273 fn description(&self) -> &str {
274 self.desc()
275 }
276 }
277
278 impl Display for TryFromFloatError {
fmt(&self, f: &mut Formatter) -> FmtResult279 fn fmt(&self, f: &mut Formatter) -> FmtResult {
280 Display::fmt(self.desc(), f)
281 }
282 }
283
284 // Safety: mpq_t is thread safe as guaranteed by the GMP library.
285 unsafe impl Send for Rational {}
286 unsafe impl Sync for Rational {}
287
288 #[cfg(test)]
289 #[allow(clippy::float_cmp)]
290 mod tests {
291 use crate::{Assign, Rational};
292 use core::convert::TryFrom;
293
294 #[test]
check_assign()295 fn check_assign() {
296 let mut r = Rational::from((1, 2));
297 assert_eq!(r, (1, 2));
298 let other = Rational::from((-2, 3));
299 r.assign(&other);
300 assert_eq!(r, (-2, 3));
301 r.assign(-other);
302 assert_eq!(r, (2, 3));
303 let another = Rational::from(&r);
304 assert_eq!(another, r);
305 }
306
307 #[test]
check_fallible_conversions()308 fn check_fallible_conversions() {
309 use crate::tests::{F32, F64};
310 for &f in F32 {
311 let r = Rational::try_from(f);
312 assert_eq!(r.is_ok(), f.is_finite());
313 #[cfg(feature = "float")]
314 {
315 if let Ok(r) = r {
316 assert_eq!(r, f);
317 }
318 }
319 }
320 for &f in F64 {
321 let r = Rational::try_from(f);
322 assert_eq!(r.is_ok(), f.is_finite());
323 #[cfg(feature = "float")]
324 {
325 if let Ok(r) = r {
326 assert_eq!(r, f);
327 }
328 }
329 }
330 }
331 }
332