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}