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);