Skip to main content

malachite_nz/integer/arithmetic/
mul_shr_round.rs

1// Copyright © 2026 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::integer::Integer;
10use crate::natural::arithmetic::mul_shr_round::mul_shr_round_ref_ref;
11use core::cmp::Ordering::{self, *};
12use malachite_base::num::arithmetic::traits::{MulShrRound, MulShrRoundAssign};
13use malachite_base::num::basic::traits::Zero;
14use malachite_base::rounding_modes::RoundingMode::{self, *};
15
16// Rounding a negative value with `rm` is rounding its magnitude with `-rm`, and the magnitude's
17// `Ordering` flips on the way back. Exactness is checked here rather than left to the `Natural`
18// core so that the panic message shows the signed operands.
19fn mul_shr_round_integers(
20    x: &Integer,
21    y: &Integer,
22    bits: u64,
23    rm: RoundingMode,
24) -> (Integer, Ordering) {
25    if *x == 0 || *y == 0 {
26        return (Integer::ZERO, Equal);
27    }
28    if rm == Exact {
29        let exact = x.unsigned_abs_ref().trailing_zeros().unwrap()
30            + y.unsigned_abs_ref().trailing_zeros().unwrap()
31            >= bits;
32        assert!(
33            exact,
34            "Product right shift is not exact: {x} * {y} >> {bits}"
35        );
36    }
37    let negative = (*x < 0) != (*y < 0);
38    let (mag, o) = mul_shr_round_ref_ref(
39        x.unsigned_abs_ref(),
40        y.unsigned_abs_ref(),
41        bits,
42        if negative { -rm } else { rm },
43    );
44    if negative {
45        (Integer::from_sign_and_abs(false, mag), o.reverse())
46    } else {
47        (Integer::from(mag), o)
48    }
49}
50
51impl MulShrRound<Self, u64> for Integer {
52    type Output = Self;
53
54    /// Multiplies two [`Integer`]s and right-shifts the product (divides it by a power of 2),
55    /// rounding according to a specified rounding mode, taking both [`Integer`]s by value. An
56    /// [`Ordering`] is also returned, indicating whether the returned value is less than, equal to,
57    /// or greater than the exact value.
58    ///
59    /// When most of the product is discarded, the product's low portion is never computed: a short
60    /// product determines the surviving bits at roughly half the cost of a full multiplication.
61    /// `Floor` rounds toward negative infinity and `Down` toward zero, so they differ when the
62    /// product is negative.
63    ///
64    /// # Worst-case complexity
65    /// $T(n) = O(n \log n \log\log n)$
66    ///
67    /// $M(n) = O(n \log n)$
68    ///
69    /// where $T$ is time, $M$ is additional memory, and $n$ is `max(self.significant_bits(),
70    /// y.significant_bits())`.
71    ///
72    /// # Panics
73    /// Panics if `rm` is `Exact` but the shift is not exact.
74    ///
75    /// # Examples
76    /// ```
77    /// use malachite_base::num::arithmetic::traits::MulShrRound;
78    /// use malachite_base::rounding_modes::RoundingMode::*;
79    /// use malachite_nz::integer::Integer;
80    /// use std::cmp::Ordering::*;
81    ///
82    /// assert_eq!(
83    ///     Integer::from(-100).mul_shr_round(Integer::from(200), 8, Floor),
84    ///     (Integer::from(-79), Less)
85    /// );
86    /// assert_eq!(
87    ///     Integer::from(-100).mul_shr_round(Integer::from(200), 8, Down),
88    ///     (Integer::from(-78), Greater)
89    /// );
90    /// ```
91    #[inline]
92    fn mul_shr_round(self, y: Self, bits: u64, rm: RoundingMode) -> (Self, Ordering) {
93        mul_shr_round_integers(&self, &y, bits, rm)
94    }
95}
96
97impl MulShrRound<&Self, u64> for Integer {
98    type Output = Self;
99
100    /// Multiplies two [`Integer`]s and right-shifts the product (divides it by a power of 2),
101    /// rounding according to a specified rounding mode, taking the first [`Integer`] by value and
102    /// the second by reference. An [`Ordering`] is also returned, indicating whether the returned
103    /// value is less than, equal to, or greater than the exact value.
104    ///
105    /// See the by-value documentation for details.
106    ///
107    /// # Worst-case complexity
108    /// $T(n) = O(n \log n \log\log n)$
109    ///
110    /// $M(n) = O(n \log n)$
111    ///
112    /// where $T$ is time, $M$ is additional memory, and $n$ is `max(self.significant_bits(),
113    /// y.significant_bits())`.
114    ///
115    /// # Panics
116    /// Panics if `rm` is `Exact` but the shift is not exact.
117    ///
118    /// # Examples
119    /// ```
120    /// use malachite_base::num::arithmetic::traits::MulShrRound;
121    /// use malachite_base::rounding_modes::RoundingMode::*;
122    /// use malachite_nz::integer::Integer;
123    /// use std::cmp::Ordering::*;
124    ///
125    /// assert_eq!(
126    ///     Integer::from(-100).mul_shr_round(&Integer::from(200), 8, Nearest),
127    ///     (Integer::from(-78), Greater)
128    /// );
129    /// ```
130    #[inline]
131    fn mul_shr_round(self, y: &Self, bits: u64, rm: RoundingMode) -> (Self, Ordering) {
132        mul_shr_round_integers(&self, y, bits, rm)
133    }
134}
135
136impl MulShrRound<Integer, u64> for &Integer {
137    type Output = Integer;
138
139    /// Multiplies two [`Integer`]s and right-shifts the product (divides it by a power of 2),
140    /// rounding according to a specified rounding mode, taking the first [`Integer`] by reference
141    /// and the second by value. An [`Ordering`] is also returned, indicating whether the returned
142    /// value is less than, equal to, or greater than the exact value.
143    ///
144    /// See the by-value documentation for details.
145    ///
146    /// # Worst-case complexity
147    /// $T(n) = O(n \log n \log\log n)$
148    ///
149    /// $M(n) = O(n \log n)$
150    ///
151    /// where $T$ is time, $M$ is additional memory, and $n$ is `max(self.significant_bits(),
152    /// y.significant_bits())`.
153    ///
154    /// # Panics
155    /// Panics if `rm` is `Exact` but the shift is not exact.
156    ///
157    /// # Examples
158    /// ```
159    /// use malachite_base::num::arithmetic::traits::MulShrRound;
160    /// use malachite_base::rounding_modes::RoundingMode::*;
161    /// use malachite_nz::integer::Integer;
162    /// use std::cmp::Ordering::*;
163    ///
164    /// assert_eq!(
165    ///     (&Integer::from(-96)).mul_shr_round(Integer::from(8), 8, Exact),
166    ///     (Integer::from(-3), Equal)
167    /// );
168    /// ```
169    #[inline]
170    fn mul_shr_round(self, y: Integer, bits: u64, rm: RoundingMode) -> (Integer, Ordering) {
171        mul_shr_round_integers(self, &y, bits, rm)
172    }
173}
174
175impl MulShrRound<&Integer, u64> for &Integer {
176    type Output = Integer;
177
178    /// Multiplies two [`Integer`]s and right-shifts the product (divides it by a power of 2),
179    /// rounding according to a specified rounding mode, taking both [`Integer`]s by reference. An
180    /// [`Ordering`] is also returned, indicating whether the returned value is less than, equal to,
181    /// or greater than the exact value.
182    ///
183    /// See the by-value documentation for details.
184    ///
185    /// # Worst-case complexity
186    /// $T(n) = O(n \log n \log\log n)$
187    ///
188    /// $M(n) = O(n \log n)$
189    ///
190    /// where $T$ is time, $M$ is additional memory, and $n$ is `max(self.significant_bits(),
191    /// y.significant_bits())`.
192    ///
193    /// # Panics
194    /// Panics if `rm` is `Exact` but the shift is not exact.
195    ///
196    /// # Examples
197    /// ```
198    /// use malachite_base::num::arithmetic::traits::MulShrRound;
199    /// use malachite_base::rounding_modes::RoundingMode::*;
200    /// use malachite_nz::integer::Integer;
201    /// use std::cmp::Ordering::*;
202    ///
203    /// assert_eq!(
204    ///     (&Integer::from(100)).mul_shr_round(&Integer::from(-200), 8, Ceiling),
205    ///     (Integer::from(-78), Greater)
206    /// );
207    /// ```
208    #[inline]
209    fn mul_shr_round(self, y: &Integer, bits: u64, rm: RoundingMode) -> (Integer, Ordering) {
210        mul_shr_round_integers(self, y, bits, rm)
211    }
212}
213
214impl MulShrRoundAssign<Self, u64> for Integer {
215    /// Multiplies two [`Integer`]s and right-shifts the product (divides it by a power of 2) in
216    /// place, rounding according to a specified rounding mode, taking the [`Integer`] on the
217    /// right-hand side by value. An [`Ordering`] is returned, indicating whether the assigned value
218    /// is less than, equal to, or greater than the exact value.
219    ///
220    /// See the [`MulShrRound`] documentation for details.
221    ///
222    /// # Worst-case complexity
223    /// $T(n) = O(n \log n \log\log n)$
224    ///
225    /// $M(n) = O(n \log n)$
226    ///
227    /// where $T$ is time, $M$ is additional memory, and $n$ is `max(self.significant_bits(),
228    /// y.significant_bits())`.
229    ///
230    /// # Panics
231    /// Panics if `rm` is `Exact` but the shift is not exact.
232    ///
233    /// # Examples
234    /// ```
235    /// use malachite_base::num::arithmetic::traits::MulShrRoundAssign;
236    /// use malachite_base::rounding_modes::RoundingMode::*;
237    /// use malachite_nz::integer::Integer;
238    /// use std::cmp::Ordering::*;
239    ///
240    /// let mut x = Integer::from(-100);
241    /// assert_eq!(x.mul_shr_round_assign(Integer::from(200), 8, Floor), Less);
242    /// assert_eq!(x, -79);
243    /// ```
244    #[inline]
245    fn mul_shr_round_assign(&mut self, y: Self, bits: u64, rm: RoundingMode) -> Ordering {
246        let o;
247        (*self, o) = mul_shr_round_integers(self, &y, bits, rm);
248        o
249    }
250}
251
252impl MulShrRoundAssign<&Self, u64> for Integer {
253    /// Multiplies two [`Integer`]s and right-shifts the product (divides it by a power of 2) in
254    /// place, rounding according to a specified rounding mode, taking the [`Integer`] on the
255    /// right-hand side by reference. An [`Ordering`] is returned, indicating whether the assigned
256    /// value is less than, equal to, or greater than the exact value.
257    ///
258    /// See the [`MulShrRound`] documentation for details.
259    ///
260    /// # Worst-case complexity
261    /// $T(n) = O(n \log n \log\log n)$
262    ///
263    /// $M(n) = O(n \log n)$
264    ///
265    /// where $T$ is time, $M$ is additional memory, and $n$ is `max(self.significant_bits(),
266    /// y.significant_bits())`.
267    ///
268    /// # Panics
269    /// Panics if `rm` is `Exact` but the shift is not exact.
270    ///
271    /// # Examples
272    /// ```
273    /// use malachite_base::num::arithmetic::traits::MulShrRoundAssign;
274    /// use malachite_base::rounding_modes::RoundingMode::*;
275    /// use malachite_nz::integer::Integer;
276    /// use std::cmp::Ordering::*;
277    ///
278    /// let mut x = Integer::from(-100);
279    /// assert_eq!(
280    ///     x.mul_shr_round_assign(&Integer::from(200), 8, Ceiling),
281    ///     Greater
282    /// );
283    /// assert_eq!(x, -78);
284    /// ```
285    #[inline]
286    fn mul_shr_round_assign(&mut self, y: &Self, bits: u64, rm: RoundingMode) -> Ordering {
287        let o;
288        (*self, o) = mul_shr_round_integers(self, y, bits, rm);
289        o
290    }
291}