malachite_q/conversion/primitive_int_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 core::ops::Neg;
12use malachite_base::comparison::traits::{Max, Min};
13use malachite_base::named::Named;
14use malachite_base::num::arithmetic::traits::{DivRound, DivisibleByPowerOf2};
15use malachite_base::num::basic::integers::PrimitiveInt;
16use malachite_base::num::basic::traits::Zero;
17use malachite_base::num::conversion::traits::{ConvertibleFrom, RoundingFrom, WrappingFrom};
18use malachite_base::num::logic::traits::SignificantBits;
19use malachite_base::rounding_modes::RoundingMode::{self, *};
20use malachite_nz::integer::Integer;
21use malachite_nz::natural::Natural;
22
23#[derive(Clone, Copy, Debug, Eq, PartialEq)]
24pub struct UnsignedFromRationalError;
25
26fn try_from_unsigned<'a, T: TryFrom<&'a Natural>>(
27 x: &'a Rational,
28) -> Result<T, UnsignedFromRationalError> {
29 if x.sign && x.denominator == 1u32 {
30 T::try_from(&x.numerator).map_err(|_| UnsignedFromRationalError)
31 } else {
32 Err(UnsignedFromRationalError)
33 }
34}
35
36fn convertible_from_unsigned<T: for<'a> ConvertibleFrom<&'a Natural>>(x: &Rational) -> bool {
37 x.sign && x.denominator == 1u32 && T::convertible_from(&x.numerator)
38}
39
40#[allow(clippy::let_and_return)] // n doesn't live long enough for a direct return
41fn rounding_from_unsigned<'a, T: for<'b> TryFrom<&'b Natural> + Max + Named + Zero>(
42 x: &'a Rational,
43 rm: RoundingMode,
44) -> (T, Ordering) {
45 if x.sign {
46 let (n, o) = (&x.numerator).div_round(&x.denominator, rm);
47 let out = if let Ok(q) = T::try_from(&n) {
48 (q, o)
49 } else if rm == Down || rm == Floor || rm == Nearest {
50 (T::MAX, Less)
51 } else {
52 panic!(
53 "Rational is too large to round to {} using RoundingMode {}",
54 rm,
55 T::NAME
56 );
57 };
58 out
59 } else if rm == Down || rm == Ceiling || rm == Nearest {
60 (T::ZERO, Greater)
61 } else {
62 panic!(
63 "Cannot round negative Rational to {} using RoundingMode {}",
64 rm,
65 T::NAME
66 );
67 }
68}
69
70#[derive(Clone, Copy, Debug, Eq, PartialEq)]
71pub struct SignedFromRationalError;
72
73#[allow(clippy::trait_duplication_in_bounds)]
74fn try_from_signed<
75 'a,
76 U: WrappingFrom<&'a Natural>,
77 S: Neg<Output = S> + PrimitiveInt + WrappingFrom<U> + WrappingFrom<&'a Natural>,
78>(
79 x: &'a Rational,
80) -> Result<S, SignedFromRationalError> {
81 if x.denominator != 1u32 {
82 return Err(SignedFromRationalError);
83 }
84 match *x {
85 Rational {
86 sign: true,
87 ref numerator,
88 ..
89 } => {
90 if numerator.significant_bits() < S::WIDTH {
91 Ok(S::wrapping_from(numerator))
92 } else {
93 Err(SignedFromRationalError)
94 }
95 }
96 Rational {
97 sign: false,
98 ref numerator,
99 ..
100 } => {
101 let significant_bits = numerator.significant_bits();
102 if significant_bits < S::WIDTH
103 || significant_bits == S::WIDTH && numerator.divisible_by_power_of_2(S::WIDTH - 1)
104 {
105 Ok(S::wrapping_from(U::wrapping_from(numerator)).wrapping_neg())
106 } else {
107 Err(SignedFromRationalError)
108 }
109 }
110 }
111}
112
113fn convertible_from_signed<T: PrimitiveInt>(x: &Rational) -> bool {
114 if x.denominator != 1u32 {
115 return false;
116 }
117 match *x {
118 Rational {
119 sign: true,
120 ref numerator,
121 ..
122 } => numerator.significant_bits() < T::WIDTH,
123 Rational {
124 sign: false,
125 ref numerator,
126 ..
127 } => {
128 let significant_bits = numerator.significant_bits();
129 significant_bits < T::WIDTH
130 || significant_bits == T::WIDTH && numerator.divisible_by_power_of_2(T::WIDTH - 1)
131 }
132 }
133}
134
135fn rounding_from_signed<'a, T: Max + Min + Named + for<'b> WrappingFrom<&'b Integer>>(
136 x: &'a Rational,
137 rm: RoundingMode,
138) -> (T, Ordering)
139where
140 Integer: PartialOrd<T>,
141{
142 let (i, o) = Integer::rounding_from(x, rm);
143 if i > T::MAX {
144 if rm == Down || rm == Floor || rm == Nearest {
145 (T::MAX, Less)
146 } else {
147 panic!(
148 "Rational is too large to round to {} using RoundingMode {}",
149 rm,
150 T::NAME
151 );
152 }
153 } else if i < T::MIN {
154 if rm == Down || rm == Ceiling || rm == Nearest {
155 (T::MIN, Greater)
156 } else {
157 panic!(
158 "Rational is too small to round to {} using RoundingMode {}",
159 rm,
160 T::NAME
161 );
162 }
163 } else {
164 (T::wrapping_from(&i), o)
165 }
166}
167
168macro_rules! impl_from_unsigned {
169 ($u: ident) => {
170 impl<'a> TryFrom<&'a Rational> for $u {
171 type Error = UnsignedFromRationalError;
172
173 /// Converts a [`Rational`] to an unsigned primitive integer, returning an error if the
174 /// [`Rational`] cannot be represented.
175 ///
176 /// # Worst-case complexity
177 /// Constant time and additional memory.
178 ///
179 /// # Examples
180 /// See [here](super::primitive_int_from_rational#try_from).
181 #[inline]
182 fn try_from(value: &Rational) -> Result<$u, UnsignedFromRationalError> {
183 try_from_unsigned(value)
184 }
185 }
186
187 impl<'a> ConvertibleFrom<&'a Rational> for $u {
188 /// Determines whether a [`Rational`] can be converted to an unsigned primitive integer.
189 ///
190 /// # Worst-case complexity
191 /// Constant time and additional memory.
192 ///
193 /// # Examples
194 /// See [here](super::primitive_int_from_rational#convertible_from).
195 #[inline]
196 fn convertible_from(value: &Rational) -> bool {
197 convertible_from_unsigned::<$u>(value)
198 }
199 }
200
201 impl<'a> RoundingFrom<&'a Rational> for $u {
202 /// Converts a [`Rational`] to an unsigned integer, using a specified [`RoundingMode`].
203 ///
204 /// If the [`Rational`] is negative, then it will be rounded to zero when `rm` is
205 /// `Ceiling`, `Down`, or `Nearest`. Otherwise, this function will panic.
206 ///
207 /// If the [`Rational`] is larger than the maximum value of the unsigned type, then it
208 /// will be rounded to the maximum value when `rm` is `Floor`, `Down`, or `Nearest`.
209 /// Otherwise, this function will panic.
210 ///
211 /// # Worst-case complexity
212 /// $T(n) = O(n \log n \log\log n)$
213 ///
214 /// $M(n) = O(n \log n)$
215 ///
216 /// where $T$ is time, $M$ is additional memory, and $n$ is `value.significant_bits()`.
217 ///
218 /// # Panics
219 /// Panics if the [`Rational`] is not an integer and `rm` is `Exact`, if the
220 /// [`Rational`] is less than zero and `rm` is not `Down`, `Ceiling`, or `Nearest`, or
221 /// if the [`Rational`] is greater than `T::MAX` and `rm` is not `Down`, `Floor`, or
222 /// `Nearest`.
223 ///
224 /// # Examples
225 /// See [here](super::primitive_int_from_rational#rounding_from).
226 #[inline]
227 fn rounding_from(value: &Rational, rm: RoundingMode) -> ($u, Ordering) {
228 rounding_from_unsigned(value, rm)
229 }
230 }
231 };
232}
233apply_to_unsigneds!(impl_from_unsigned);
234
235macro_rules! impl_from_signed {
236 ($u: ident, $s: ident) => {
237 impl<'a> TryFrom<&'a Rational> for $s {
238 type Error = SignedFromRationalError;
239
240 /// Converts a [`Rational`] to a signed primitive integer, returning an error if the
241 /// [`Rational`] cannot be represented.
242 ///
243 /// # Worst-case complexity
244 /// Constant time and additional memory.
245 ///
246 /// # Examples
247 /// See [here](super::primitive_int_from_rational#try_from).
248 #[inline]
249 fn try_from(value: &Rational) -> Result<$s, SignedFromRationalError> {
250 try_from_signed::<$u, $s>(value)
251 }
252 }
253
254 impl<'a> ConvertibleFrom<&'a Rational> for $s {
255 /// Determines whether a [`Rational`] can be converted to a signed primitive integer.
256 ///
257 /// # Worst-case complexity
258 /// Constant time and additional memory.
259 ///
260 /// # Examples
261 /// See [here](super::primitive_int_from_rational#convertible_from).
262 #[inline]
263 fn convertible_from(value: &Rational) -> bool {
264 convertible_from_signed::<$s>(value)
265 }
266 }
267
268 impl<'a> RoundingFrom<&'a Rational> for $s {
269 /// Converts a [`Rational`] to a signed integer, using a specified [`RoundingMode`].
270 ///
271 /// If the [`Rational`] is smaller than the minimum value of the unsigned type, then it
272 /// will be rounded to the minimum value when `rm` is `Ceiling`, `Down`, or `Nearest`.
273 /// Otherwise, this function will panic.
274 ///
275 /// If the [`Rational`] is larger than the maximum value of the unsigned type, then it
276 /// will be rounded to the maximum value when `rm` is `Floor`, `Down`, or `Nearest`.
277 /// Otherwise, this function will panic.
278 ///
279 /// # Worst-case complexity
280 /// $T(n) = O(n \log n \log\log n)$
281 ///
282 /// $M(n) = O(n \log n)$
283 ///
284 /// where $T$ is time, $M$ is additional memory, and $n$ is `value.significant_bits()`.
285 ///
286 /// # Panics
287 /// Panics if the [`Rational`] is not an integer and `rm` is `Exact`, if the
288 /// [`Rational`] is less than `T::MIN` and `rm` is not `Down`, `Ceiling`, or `Nearest`,
289 /// or if the [`Rational`] is greater than `T::MAX` and `rm` is not `Down`, `Floor`, or
290 /// `Nearest`.
291 ///
292 /// # Examples
293 /// See [here](super::primitive_int_from_rational#rounding_from).
294 #[inline]
295 fn rounding_from(value: &Rational, rm: RoundingMode) -> ($s, Ordering) {
296 rounding_from_signed(value, rm)
297 }
298 }
299 };
300}
301apply_to_unsigned_signed_pairs!(impl_from_signed);