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