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