malachite_q/arithmetic/
round_to_multiple_of_power_of_2.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::{
12    RoundToMultipleOfPowerOf2, RoundToMultipleOfPowerOf2Assign,
13};
14use malachite_base::num::conversion::traits::RoundingFrom;
15use malachite_base::rounding_modes::RoundingMode;
16use malachite_nz::integer::Integer;
17
18impl RoundToMultipleOfPowerOf2<i64> for Rational {
19    type Output = Self;
20
21    /// Rounds a [`Rational`] to an integer multiple of $2^k$ according to a specified rounding
22    /// mode. The [`Rational`] is taken by value. An [`Ordering`] is also returned, indicating
23    /// whether the returned value is less than, equal to, or greater than the original value.
24    ///
25    /// Let $q = \frac{x}{2^k}$:
26    ///
27    /// $f(x, k, \mathrm{Down}) = 2^k \operatorname{sgn}(q) \lfloor |q| \rfloor.$
28    ///
29    /// $f(x, k, \mathrm{Up}) = 2^k \operatorname{sgn}(q) \lceil |q| \rceil.$
30    ///
31    /// $f(x, k, \mathrm{Floor}) = 2^k \lfloor q \rfloor.$
32    ///
33    /// $f(x, k, \mathrm{Ceiling}) = 2^k \lceil q \rceil.$
34    ///
35    /// $$
36    /// f(x, k, \mathrm{Nearest}) = \begin{cases}
37    ///     2^k \lfloor q \rfloor & \text{if} \\quad
38    ///     q - \lfloor q \rfloor < \frac{1}{2} \\\\
39    ///     2^k \lceil q \rceil & \text{if} \\quad q - \lfloor q \rfloor > \frac{1}{2} \\\\
40    ///     2^k \lfloor q \rfloor &
41    ///     \text{if} \\quad q - \lfloor q \rfloor =
42    ///         \frac{1}{2} \\ \text{and} \\ \lfloor q \rfloor
43    ///     \\ \text{is even} \\\\
44    ///     2^k \lceil q \rceil &
45    ///     \text{if} \\quad q - \lfloor q \rfloor =
46    ///         \frac{1}{2} \\ \text{and} \\ \lfloor q \rfloor \\ \text{is odd.}
47    /// \end{cases}
48    /// $$
49    ///
50    /// $f(x, k, \mathrm{Exact}) = 2^k q$, but panics if $q \notin \Z$.
51    ///
52    /// # Worst-case complexity
53    /// $T(n) = O(n)$
54    ///
55    /// $M(n) = O(n)$
56    ///
57    /// where $T$ is time, $M$ is additional memory, and $n$ is `max(self.significant_bits(), pow /
58    /// Limb::WIDTH)`.
59    ///
60    /// # Panics
61    /// Panics if `rm` is `Exact`, but `self` is not a multiple of the power of 2.
62    ///
63    /// # Examples
64    /// ```
65    /// use malachite_base::num::arithmetic::traits::RoundToMultipleOfPowerOf2;
66    /// use malachite_base::num::conversion::traits::ExactFrom;
67    /// use malachite_base::rounding_modes::RoundingMode::*;
68    /// use malachite_base::strings::ToDebugString;
69    /// use malachite_q::Rational;
70    ///
71    /// let q = Rational::exact_from(std::f64::consts::PI);
72    /// assert_eq!(
73    ///     q.clone()
74    ///         .round_to_multiple_of_power_of_2(-3, Floor)
75    ///         .to_debug_string(),
76    ///     "(25/8, Less)"
77    /// );
78    /// assert_eq!(
79    ///     q.clone()
80    ///         .round_to_multiple_of_power_of_2(-3, Down)
81    ///         .to_debug_string(),
82    ///     "(25/8, Less)"
83    /// );
84    /// assert_eq!(
85    ///     q.clone()
86    ///         .round_to_multiple_of_power_of_2(-3, Ceiling)
87    ///         .to_debug_string(),
88    ///     "(13/4, Greater)"
89    /// );
90    /// assert_eq!(
91    ///     q.clone()
92    ///         .round_to_multiple_of_power_of_2(-3, Up)
93    ///         .to_debug_string(),
94    ///     "(13/4, Greater)"
95    /// );
96    /// assert_eq!(
97    ///     q.clone()
98    ///         .round_to_multiple_of_power_of_2(-3, Nearest)
99    ///         .to_debug_string(),
100    ///     "(25/8, Less)"
101    /// );
102    /// ```
103    #[inline]
104    fn round_to_multiple_of_power_of_2(mut self, pow: i64, rm: RoundingMode) -> (Self, Ordering) {
105        let o = self.round_to_multiple_of_power_of_2_assign(pow, rm);
106        (self, o)
107    }
108}
109
110impl RoundToMultipleOfPowerOf2<i64> for &Rational {
111    type Output = Rational;
112
113    /// Rounds a [`Rational`] to an integer multiple of $2^k$ according to a specified rounding
114    /// mode. The [`Rational`] is taken by reference. An [`Ordering`] is also returned, indicating
115    /// whether the returned value is less than, equal to, or greater than the original value.
116    ///
117    /// Let $q = \frac{x}{2^k}$:
118    ///
119    /// $f(x, k, \mathrm{Down}) = 2^k \operatorname{sgn}(q) \lfloor |q| \rfloor.$
120    ///
121    /// $f(x, k, \mathrm{Up}) = 2^k \operatorname{sgn}(q) \lceil |q| \rceil.$
122    ///
123    /// $f(x, k, \mathrm{Floor}) = 2^k \lfloor q \rfloor.$
124    ///
125    /// $f(x, k, \mathrm{Ceiling}) = 2^k \lceil q \rceil.$
126    ///
127    /// $$
128    /// f(x, k, \mathrm{Nearest}) = \begin{cases}
129    ///     2^k \lfloor q \rfloor & \text{if} \\quad
130    ///     q - \lfloor q \rfloor < \frac{1}{2} \\\\
131    ///     2^k \lceil q \rceil & \text{if} \\quad q - \lfloor q \rfloor > \frac{1}{2} \\\\
132    ///     2^k \lfloor q \rfloor &
133    ///     \text{if} \\quad q - \lfloor q \rfloor =
134    ///         \frac{1}{2} \\ \text{and} \\ \lfloor q \rfloor
135    ///     \\ \text{is even} \\\\
136    ///     2^k \lceil q \rceil &
137    ///     \text{if} \\quad q - \lfloor q \rfloor =
138    ///         \frac{1}{2} \\ \text{and} \\ \lfloor q \rfloor \\ \text{is odd.}
139    /// \end{cases}
140    /// $$
141    ///
142    /// $f(x, k, \mathrm{Exact}) = 2^k q$, but panics if $q \notin \Z$.
143    ///
144    /// # Worst-case complexity
145    /// $T(n) = O(n)$
146    ///
147    /// $M(n) = O(n)$
148    ///
149    /// where $T$ is time, $M$ is additional memory, and $n$ is `max(self.significant_bits(), pow /
150    /// Limb::WIDTH)`.
151    ///
152    /// # Panics
153    /// Panics if `rm` is `Exact`, but `self` is not a multiple of the power of 2.
154    ///
155    /// # Examples
156    /// ```
157    /// use malachite_base::num::arithmetic::traits::RoundToMultipleOfPowerOf2;
158    /// use malachite_base::num::conversion::traits::ExactFrom;
159    /// use malachite_base::rounding_modes::RoundingMode::*;
160    /// use malachite_base::strings::ToDebugString;
161    /// use malachite_q::Rational;
162    ///
163    /// let q = Rational::exact_from(std::f64::consts::PI);
164    /// assert_eq!(
165    ///     (&q).round_to_multiple_of_power_of_2(-3, Floor)
166    ///         .to_debug_string(),
167    ///     "(25/8, Less)"
168    /// );
169    /// assert_eq!(
170    ///     (&q).round_to_multiple_of_power_of_2(-3, Down)
171    ///         .to_debug_string(),
172    ///     "(25/8, Less)"
173    /// );
174    /// assert_eq!(
175    ///     (&q).round_to_multiple_of_power_of_2(-3, Ceiling)
176    ///         .to_debug_string(),
177    ///     "(13/4, Greater)"
178    /// );
179    /// assert_eq!(
180    ///     (&q).round_to_multiple_of_power_of_2(-3, Up)
181    ///         .to_debug_string(),
182    ///     "(13/4, Greater)"
183    /// );
184    /// assert_eq!(
185    ///     (&q).round_to_multiple_of_power_of_2(-3, Nearest)
186    ///         .to_debug_string(),
187    ///     "(25/8, Less)"
188    /// );
189    /// ```
190    fn round_to_multiple_of_power_of_2(self, pow: i64, rm: RoundingMode) -> (Rational, Ordering) {
191        let (s, o) = Integer::rounding_from(self >> pow, rm);
192        (Rational::from(s) << pow, o)
193    }
194}
195
196impl RoundToMultipleOfPowerOf2Assign<i64> for Rational {
197    /// Rounds a [`Rational`] to a multiple of $2^k$ in place, according to a specified rounding
198    /// mode. An [`Ordering`] is returned, indicating whether the returned value is less than, equal
199    /// to, or greater than the original value.
200    ///
201    /// See the [`RoundToMultipleOfPowerOf2`] documentation for details.
202    ///
203    /// but the latter should be used as it is clearer and more efficient.
204    ///
205    /// # Worst-case complexity
206    /// $T(n) = O(n)$
207    ///
208    /// $M(n) = O(n)$
209    ///
210    /// where $T$ is time, $M$ is additional memory, and $n$ is `max(self.significant_bits(), pow /
211    /// Limb::WIDTH)`.
212    ///
213    /// # Panics
214    /// Panics if `rm` is `Exact`, but `self` is not a multiple of the power of 2.
215    ///
216    /// # Examples
217    /// ```
218    /// use malachite_base::num::arithmetic::traits::RoundToMultipleOfPowerOf2Assign;
219    /// use malachite_base::num::conversion::traits::ExactFrom;
220    /// use malachite_base::rounding_modes::RoundingMode::*;
221    /// use malachite_q::Rational;
222    /// use std::cmp::Ordering::*;
223    ///
224    /// let q = Rational::exact_from(std::f64::consts::PI);
225    ///
226    /// let mut x = q.clone();
227    /// assert_eq!(x.round_to_multiple_of_power_of_2_assign(-3, Floor), Less);
228    /// assert_eq!(x.to_string(), "25/8");
229    ///
230    /// let mut x = q.clone();
231    /// assert_eq!(x.round_to_multiple_of_power_of_2_assign(-3, Down), Less);
232    /// assert_eq!(x.to_string(), "25/8");
233    ///
234    /// let mut x = q.clone();
235    /// assert_eq!(
236    ///     x.round_to_multiple_of_power_of_2_assign(-3, Ceiling),
237    ///     Greater
238    /// );
239    /// assert_eq!(x.to_string(), "13/4");
240    ///
241    /// let mut x = q.clone();
242    /// assert_eq!(x.round_to_multiple_of_power_of_2_assign(-3, Up), Greater);
243    /// assert_eq!(x.to_string(), "13/4");
244    ///
245    /// let mut x = q.clone();
246    /// assert_eq!(x.round_to_multiple_of_power_of_2_assign(-3, Nearest), Less);
247    /// assert_eq!(x.to_string(), "25/8");
248    /// ```
249    fn round_to_multiple_of_power_of_2_assign(&mut self, pow: i64, rm: RoundingMode) -> Ordering {
250        *self >>= pow;
251        let (s, o) = Integer::rounding_from(&*self, rm);
252        *self = Self::from(s) << pow;
253        o
254    }
255}