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}