malachite_q/conversion/
integer_from_rational.rs

1// Copyright © 2025 Mikhail Hogrefe
2//
3// This file is part of Malachite.
4//
5// Malachite is free software: you can redistribute it and/or modify it under the terms of the GNU
6// Lesser General Public License (LGPL) as published by the Free Software Foundation; either version
7// 3 of the License, or (at your option) any later version. See <https://www.gnu.org/licenses/>.
8
9use crate::Rational;
10use core::cmp::Ordering;
11use malachite_base::num::arithmetic::traits::DivRound;
12use malachite_base::num::conversion::traits::{ConvertibleFrom, RoundingFrom};
13use malachite_base::rounding_modes::RoundingMode;
14use malachite_nz::integer::Integer;
15
16#[derive(Clone, Copy, Debug, Eq, PartialEq)]
17pub struct IntegerFromRationalError;
18
19impl TryFrom<Rational> for Integer {
20    type Error = IntegerFromRationalError;
21
22    /// Converts a [`Rational`] to an [`Integer`], taking the [`Rational`] by value. If the
23    /// [`Rational`] is not an integer, an error is returned.
24    ///
25    /// # Worst-case complexity
26    /// Constant time and additional memory.
27    ///
28    /// # Examples
29    /// ```
30    /// use malachite_nz::integer::Integer;
31    /// use malachite_q::conversion::integer_from_rational::IntegerFromRationalError;
32    /// use malachite_q::Rational;
33    ///
34    /// assert_eq!(Integer::try_from(Rational::from(123)).unwrap(), 123);
35    /// assert_eq!(Integer::try_from(Rational::from(-123)).unwrap(), -123);
36    /// assert_eq!(
37    ///     Integer::try_from(Rational::from_signeds(22, 7)),
38    ///     Err(IntegerFromRationalError)
39    /// );
40    /// ```
41    fn try_from(x: Rational) -> Result<Integer, Self::Error> {
42        if x.denominator == 1u32 {
43            Ok(Integer::from_sign_and_abs(x.sign, x.numerator))
44        } else {
45            Err(IntegerFromRationalError)
46        }
47    }
48}
49
50impl TryFrom<&Rational> for Integer {
51    type Error = IntegerFromRationalError;
52
53    /// Converts a [`Rational`] to an [`Integer`], taking the [`Rational`] by reference. If the
54    /// [`Rational`] is not an integer, an error is returned.
55    ///
56    /// # Worst-case complexity
57    /// $T(n) = O(n)$
58    ///
59    /// $M(n) = O(n)$
60    ///
61    /// where $T$ is time, $M$ is additional memory, and $n$ is `x.significant_bits()`.
62    ///
63    /// # Examples
64    /// ```
65    /// use malachite_nz::integer::Integer;
66    /// use malachite_q::conversion::integer_from_rational::IntegerFromRationalError;
67    /// use malachite_q::Rational;
68    ///
69    /// assert_eq!(Integer::try_from(&Rational::from(123)).unwrap(), 123);
70    /// assert_eq!(Integer::try_from(&Rational::from(-123)).unwrap(), -123);
71    /// assert_eq!(
72    ///     Integer::try_from(&Rational::from_signeds(22, 7)),
73    ///     Err(IntegerFromRationalError)
74    /// );
75    /// ```
76    fn try_from(x: &Rational) -> Result<Integer, Self::Error> {
77        if x.denominator == 1u32 {
78            Ok(Integer::from_sign_and_abs_ref(x.sign, &x.numerator))
79        } else {
80            Err(IntegerFromRationalError)
81        }
82    }
83}
84
85impl ConvertibleFrom<&Rational> for Integer {
86    /// Determines whether a [`Rational`] can be converted to an [`Integer`], taking the
87    /// [`Rational`] by reference.
88    ///
89    /// # Worst-case complexity
90    /// Constant time and additional memory.
91    ///
92    /// # Examples
93    /// ```
94    /// use malachite_base::num::conversion::traits::ConvertibleFrom;
95    /// use malachite_nz::integer::Integer;
96    /// use malachite_q::Rational;
97    ///
98    /// assert_eq!(Integer::convertible_from(&Rational::from(123)), true);
99    /// assert_eq!(Integer::convertible_from(&Rational::from(-123)), true);
100    /// assert_eq!(
101    ///     Integer::convertible_from(&Rational::from_signeds(22, 7)),
102    ///     false
103    /// );
104    /// ```
105    #[inline]
106    fn convertible_from(x: &Rational) -> bool {
107        x.denominator == 1u32
108    }
109}
110
111impl RoundingFrom<Rational> for Integer {
112    /// Converts a [`Rational`] to an [`Integer`], using a specified [`RoundingMode`] and taking the
113    /// [`Rational`] by value. An [`Ordering`] is also returned, indicating whether the returned
114    /// value is less than, equal to, or greater than the original value.
115    ///
116    /// # Worst-case complexity
117    /// $T(n) = O(n \log n \log\log n)$
118    ///
119    /// $M(n) = O(n \log n)$
120    ///
121    /// where $T$ is time, $M$ is additional memory, and $n$ is `x.significant_bits()`.
122    ///
123    /// # Panics
124    /// Panics if the [`Rational`] is not an integer and `rm` is `Exact`.
125    ///
126    /// # Examples
127    /// ```
128    /// use malachite_base::num::conversion::traits::RoundingFrom;
129    /// use malachite_base::rounding_modes::RoundingMode::*;
130    /// use malachite_base::strings::ToDebugString;
131    /// use malachite_nz::integer::Integer;
132    /// use malachite_q::Rational;
133    ///
134    /// assert_eq!(
135    ///     Integer::rounding_from(Rational::from(123), Exact).to_debug_string(),
136    ///     "(123, Equal)"
137    /// );
138    /// assert_eq!(
139    ///     Integer::rounding_from(Rational::from(-123), Exact).to_debug_string(),
140    ///     "(-123, Equal)"
141    /// );
142    ///
143    /// assert_eq!(
144    ///     Integer::rounding_from(Rational::from_signeds(22, 7), Floor).to_debug_string(),
145    ///     "(3, Less)"
146    /// );
147    /// assert_eq!(
148    ///     Integer::rounding_from(Rational::from_signeds(22, 7), Down).to_debug_string(),
149    ///     "(3, Less)"
150    /// );
151    /// assert_eq!(
152    ///     Integer::rounding_from(Rational::from_signeds(22, 7), Ceiling).to_debug_string(),
153    ///     "(4, Greater)"
154    /// );
155    /// assert_eq!(
156    ///     Integer::rounding_from(Rational::from_signeds(22, 7), Up).to_debug_string(),
157    ///     "(4, Greater)"
158    /// );
159    /// assert_eq!(
160    ///     Integer::rounding_from(Rational::from_signeds(22, 7), Nearest).to_debug_string(),
161    ///     "(3, Less)"
162    /// );
163    ///
164    /// assert_eq!(
165    ///     Integer::rounding_from(Rational::from_signeds(-22, 7), Floor).to_debug_string(),
166    ///     "(-4, Less)"
167    /// );
168    /// assert_eq!(
169    ///     Integer::rounding_from(Rational::from_signeds(-22, 7), Down).to_debug_string(),
170    ///     "(-3, Greater)"
171    /// );
172    /// assert_eq!(
173    ///     Integer::rounding_from(Rational::from_signeds(-22, 7), Ceiling).to_debug_string(),
174    ///     "(-3, Greater)"
175    /// );
176    /// assert_eq!(
177    ///     Integer::rounding_from(Rational::from_signeds(-22, 7), Up).to_debug_string(),
178    ///     "(-4, Less)"
179    /// );
180    /// assert_eq!(
181    ///     Integer::rounding_from(Rational::from_signeds(-22, 7), Nearest).to_debug_string(),
182    ///     "(-3, Greater)"
183    /// );
184    /// ```
185    fn rounding_from(x: Rational, rm: RoundingMode) -> (Integer, Ordering) {
186        let s = x.sign;
187        let (n, o) = x
188            .numerator
189            .div_round(x.denominator, if s { rm } else { -rm });
190        (
191            Integer::from_sign_and_abs(x.sign, n),
192            if s { o } else { o.reverse() },
193        )
194    }
195}
196
197impl RoundingFrom<&Rational> for Integer {
198    /// Converts a [`Rational`] to an [`Integer`], using a specified [`RoundingMode`] and taking the
199    /// [`Rational`] by reference. An [`Ordering`] is also returned, indicating whether the returned
200    /// value is less than, equal to, or greater than the original value.
201    ///
202    /// # Worst-case complexity
203    /// $T(n) = O(n \log n \log\log n)$
204    ///
205    /// $M(n) = O(n \log n)$
206    ///
207    /// where $T$ is time, $M$ is additional memory, and $n$ is `x.significant_bits()`.
208    ///
209    /// # Panics
210    /// Panics if the [`Rational`] is not an integer and `rm` is `Exact`.
211    ///
212    /// # Examples
213    /// ```
214    /// use malachite_base::num::conversion::traits::RoundingFrom;
215    /// use malachite_base::rounding_modes::RoundingMode::*;
216    /// use malachite_base::strings::ToDebugString;
217    /// use malachite_nz::integer::Integer;
218    /// use malachite_q::Rational;
219    ///
220    /// assert_eq!(
221    ///     Integer::rounding_from(&Rational::from(123), Exact).to_debug_string(),
222    ///     "(123, Equal)"
223    /// );
224    /// assert_eq!(
225    ///     Integer::rounding_from(&Rational::from(-123), Exact).to_debug_string(),
226    ///     "(-123, Equal)"
227    /// );
228    ///
229    /// assert_eq!(
230    ///     Integer::rounding_from(&Rational::from_signeds(22, 7), Floor).to_debug_string(),
231    ///     "(3, Less)"
232    /// );
233    /// assert_eq!(
234    ///     Integer::rounding_from(&Rational::from_signeds(22, 7), Down).to_debug_string(),
235    ///     "(3, Less)"
236    /// );
237    /// assert_eq!(
238    ///     Integer::rounding_from(&Rational::from_signeds(22, 7), Ceiling).to_debug_string(),
239    ///     "(4, Greater)"
240    /// );
241    /// assert_eq!(
242    ///     Integer::rounding_from(&Rational::from_signeds(22, 7), Up).to_debug_string(),
243    ///     "(4, Greater)"
244    /// );
245    /// assert_eq!(
246    ///     Integer::rounding_from(&Rational::from_signeds(22, 7), Nearest).to_debug_string(),
247    ///     "(3, Less)"
248    /// );
249    ///
250    /// assert_eq!(
251    ///     Integer::rounding_from(Rational::from_signeds(-22, 7), Floor).to_debug_string(),
252    ///     "(-4, Less)"
253    /// );
254    /// assert_eq!(
255    ///     Integer::rounding_from(Rational::from_signeds(-22, 7), Down).to_debug_string(),
256    ///     "(-3, Greater)"
257    /// );
258    /// assert_eq!(
259    ///     Integer::rounding_from(Rational::from_signeds(-22, 7), Ceiling).to_debug_string(),
260    ///     "(-3, Greater)"
261    /// );
262    /// assert_eq!(
263    ///     Integer::rounding_from(Rational::from_signeds(-22, 7), Up).to_debug_string(),
264    ///     "(-4, Less)"
265    /// );
266    /// assert_eq!(
267    ///     Integer::rounding_from(Rational::from_signeds(-22, 7), Nearest).to_debug_string(),
268    ///     "(-3, Greater)"
269    /// );
270    /// ```
271    fn rounding_from(x: &Rational, rm: RoundingMode) -> (Integer, Ordering) {
272        let (n, o) = (&x.numerator).div_round(&x.denominator, if x.sign { rm } else { -rm });
273        (
274            Integer::from_sign_and_abs(x.sign, n),
275            if x.sign { o } else { o.reverse() },
276        )
277    }
278}