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 = Rational;
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(
105        mut self,
106        pow: i64,
107        rm: RoundingMode,
108    ) -> (Rational, Ordering) {
109        let o = self.round_to_multiple_of_power_of_2_assign(pow, rm);
110        (self, o)
111    }
112}
113
114impl RoundToMultipleOfPowerOf2<i64> for &Rational {
115    type Output = Rational;
116
117    /// Rounds a [`Rational`] to an integer multiple of $2^k$ according to a specified rounding
118    /// mode. The [`Rational`] is taken by reference. An [`Ordering`] is also returned, indicating
119    /// whether the returned value is less than, equal to, or greater than the original value.
120    ///
121    /// Let $q = \frac{x}{2^k}$:
122    ///
123    /// $f(x, k, \mathrm{Down}) = 2^k \operatorname{sgn}(q) \lfloor |q| \rfloor.$
124    ///
125    /// $f(x, k, \mathrm{Up}) = 2^k \operatorname{sgn}(q) \lceil |q| \rceil.$
126    ///
127    /// $f(x, k, \mathrm{Floor}) = 2^k \lfloor q \rfloor.$
128    ///
129    /// $f(x, k, \mathrm{Ceiling}) = 2^k \lceil q \rceil.$
130    ///
131    /// $$
132    /// f(x, k, \mathrm{Nearest}) = \begin{cases}
133    ///     2^k \lfloor q \rfloor & \text{if} \\quad
134    ///     q - \lfloor q \rfloor < \frac{1}{2} \\\\
135    ///     2^k \lceil q \rceil & \text{if} \\quad q - \lfloor q \rfloor > \frac{1}{2} \\\\
136    ///     2^k \lfloor q \rfloor &
137    ///     \text{if} \\quad q - \lfloor q \rfloor =
138    ///         \frac{1}{2} \\ \text{and} \\ \lfloor q \rfloor
139    ///     \\ \text{is even} \\\\
140    ///     2^k \lceil q \rceil &
141    ///     \text{if} \\quad q - \lfloor q \rfloor =
142    ///         \frac{1}{2} \\ \text{and} \\ \lfloor q \rfloor \\ \text{is odd.}
143    /// \end{cases}
144    /// $$
145    ///
146    /// $f(x, k, \mathrm{Exact}) = 2^k q$, but panics if $q \notin \Z$.
147    ///
148    /// # Worst-case complexity
149    /// $T(n) = O(n)$
150    ///
151    /// $M(n) = O(n)$
152    ///
153    /// where $T$ is time, $M$ is additional memory, and $n$ is `max(self.significant_bits(), pow /
154    /// Limb::WIDTH)`.
155    ///
156    /// # Panics
157    /// Panics if `rm` is `Exact`, but `self` is not a multiple of the power of 2.
158    ///
159    /// # Examples
160    /// ```
161    /// use malachite_base::num::arithmetic::traits::RoundToMultipleOfPowerOf2;
162    /// use malachite_base::num::conversion::traits::ExactFrom;
163    /// use malachite_base::rounding_modes::RoundingMode::*;
164    /// use malachite_base::strings::ToDebugString;
165    /// use malachite_q::Rational;
166    ///
167    /// let q = Rational::exact_from(std::f64::consts::PI);
168    /// assert_eq!(
169    ///     (&q).round_to_multiple_of_power_of_2(-3, Floor)
170    ///         .to_debug_string(),
171    ///     "(25/8, Less)"
172    /// );
173    /// assert_eq!(
174    ///     (&q).round_to_multiple_of_power_of_2(-3, Down)
175    ///         .to_debug_string(),
176    ///     "(25/8, Less)"
177    /// );
178    /// assert_eq!(
179    ///     (&q).round_to_multiple_of_power_of_2(-3, Ceiling)
180    ///         .to_debug_string(),
181    ///     "(13/4, Greater)"
182    /// );
183    /// assert_eq!(
184    ///     (&q).round_to_multiple_of_power_of_2(-3, Up)
185    ///         .to_debug_string(),
186    ///     "(13/4, Greater)"
187    /// );
188    /// assert_eq!(
189    ///     (&q).round_to_multiple_of_power_of_2(-3, Nearest)
190    ///         .to_debug_string(),
191    ///     "(25/8, Less)"
192    /// );
193    /// ```
194    fn round_to_multiple_of_power_of_2(self, pow: i64, rm: RoundingMode) -> (Rational, Ordering) {
195        let (s, o) = Integer::rounding_from(self >> pow, rm);
196        (Rational::from(s) << pow, o)
197    }
198}
199
200impl RoundToMultipleOfPowerOf2Assign<i64> for Rational {
201    /// Rounds a [`Rational`] to a multiple of $2^k$ in place, according to a specified rounding
202    /// mode. An [`Ordering`] is returned, indicating whether the returned value is less than, equal
203    /// to, or greater than the original value.
204    ///
205    /// See the [`RoundToMultipleOfPowerOf2`] documentation for details.
206    ///
207    /// but the latter should be used as it is clearer and more efficient.
208    ///
209    /// # Worst-case complexity
210    /// $T(n) = O(n)$
211    ///
212    /// $M(n) = O(n)$
213    ///
214    /// where $T$ is time, $M$ is additional memory, and $n$ is `max(self.significant_bits(), pow /
215    /// Limb::WIDTH)`.
216    ///
217    /// # Panics
218    /// Panics if `rm` is `Exact`, but `self` is not a multiple of the power of 2.
219    ///
220    /// # Examples
221    /// ```
222    /// use malachite_base::num::arithmetic::traits::RoundToMultipleOfPowerOf2Assign;
223    /// use malachite_base::num::conversion::traits::ExactFrom;
224    /// use malachite_base::rounding_modes::RoundingMode::*;
225    /// use malachite_q::Rational;
226    /// use std::cmp::Ordering::*;
227    ///
228    /// let q = Rational::exact_from(std::f64::consts::PI);
229    ///
230    /// let mut x = q.clone();
231    /// assert_eq!(x.round_to_multiple_of_power_of_2_assign(-3, Floor), Less);
232    /// assert_eq!(x.to_string(), "25/8");
233    ///
234    /// let mut x = q.clone();
235    /// assert_eq!(x.round_to_multiple_of_power_of_2_assign(-3, Down), Less);
236    /// assert_eq!(x.to_string(), "25/8");
237    ///
238    /// let mut x = q.clone();
239    /// assert_eq!(
240    ///     x.round_to_multiple_of_power_of_2_assign(-3, Ceiling),
241    ///     Greater
242    /// );
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, Up), Greater);
247    /// assert_eq!(x.to_string(), "13/4");
248    ///
249    /// let mut x = q.clone();
250    /// assert_eq!(x.round_to_multiple_of_power_of_2_assign(-3, Nearest), Less);
251    /// assert_eq!(x.to_string(), "25/8");
252    /// ```
253    fn round_to_multiple_of_power_of_2_assign(&mut self, pow: i64, rm: RoundingMode) -> Ordering {
254        *self >>= pow;
255        let (s, o) = Integer::rounding_from(&*self, rm);
256        *self = Rational::from(s) << pow;
257        o
258    }
259}