malachite_base/num/arithmetic/shl_round.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::num::arithmetic::traits::{
10 ShlRound, ShlRoundAssign, ShrRound, ShrRoundAssign, UnsignedAbs,
11};
12use crate::num::basic::integers::PrimitiveInt;
13use crate::num::basic::signeds::PrimitiveSigned;
14use crate::rounding_modes::RoundingMode;
15use core::cmp::Ordering::{self, *};
16use core::ops::{Shl, ShlAssign};
17
18fn shl_round<
19 T: PrimitiveInt + Shl<U, Output = T> + ShrRound<U, Output = T>,
20 U,
21 S: PrimitiveSigned + UnsignedAbs<Output = U>,
22>(
23 x: T,
24 bits: S,
25 rm: RoundingMode,
26) -> (T, Ordering) {
27 if bits >= S::ZERO {
28 let width = S::wrapping_from(T::WIDTH);
29 (
30 if width >= S::ZERO && bits >= width {
31 T::ZERO
32 } else {
33 x << bits.unsigned_abs()
34 },
35 Equal,
36 )
37 } else {
38 x.shr_round(bits.unsigned_abs(), rm)
39 }
40}
41
42fn shl_round_assign<
43 T: PrimitiveInt + ShlAssign<U> + ShrRoundAssign<U>,
44 U,
45 S: PrimitiveSigned + UnsignedAbs<Output = U>,
46>(
47 x: &mut T,
48 bits: S,
49 rm: RoundingMode,
50) -> Ordering {
51 if bits >= S::ZERO {
52 let width = S::wrapping_from(T::WIDTH);
53 if width >= S::ZERO && bits >= width {
54 *x = T::ZERO;
55 } else {
56 *x <<= bits.unsigned_abs();
57 }
58 Equal
59 } else {
60 x.shr_round_assign(bits.unsigned_abs(), rm)
61 }
62}
63
64macro_rules! impl_shl_round {
65 ($t:ident) => {
66 macro_rules! impl_shl_round_inner {
67 ($u:ident) => {
68 impl ShlRound<$u> for $t {
69 type Output = $t;
70
71 /// Left-shifts a number (multiplies it by a power of 2 or divides it by a power
72 /// of 2 and takes the floor) and rounds according to the specified rounding
73 /// mode. An [`Ordering`] is also returned, indicating whether the returned
74 /// value is less than, equal to, or greater than the exact value. If `bits` is
75 /// non-negative, then the returned [`Ordering`] is always `Equal`, even if the
76 /// higher bits of the result are lost.
77 ///
78 /// Passing `Floor` or `Down` is equivalent to using `>>`. To test whether
79 /// `Exact` can be passed, use `bits > 0 || self.divisible_by_power_of_2(bits)`.
80 /// Rounding might only be necessary if `bits` is negative.
81 ///
82 /// Let $q = x2^k$, and let $g$ be the function that just returns the first
83 /// element of the pair, without the [`Ordering`]:
84 ///
85 /// $g(x, k, \mathrm{Down}) = g(x, y, \mathrm{Floor}) = \lfloor q \rfloor.$
86 ///
87 /// $g(x, k, \mathrm{Up}) = g(x, y, \mathrm{Ceiling}) = \lceil q \rceil.$
88 ///
89 /// $$
90 /// g(x, k, \mathrm{Nearest}) = \begin{cases}
91 /// \lfloor q \rfloor & \text{if}
92 /// \\quad q - \lfloor q \rfloor < \frac{1}{2}, \\\\
93 /// \lceil q \rceil & \text{if}
94 /// \\quad q - \lfloor q \rfloor > \frac{1}{2}, \\\\
95 /// \lfloor q \rfloor & \text{if} \\quad q - \lfloor q \rfloor =
96 /// \frac{1}{2} \\ \text{and} \\ \lfloor q \rfloor
97 /// \\ \text{is even}, \\\\
98 /// \lceil q \rceil &
99 /// \text{if} \\quad q - \lfloor q \rfloor = \frac{1}{2} \\ \text{and}
100 /// \\ \lfloor q \rfloor \\ \text{is odd}.
101 /// \end{cases}
102 /// $$
103 ///
104 /// $g(x, k, \mathrm{Exact}) = q$, but panics if $q \notin \N$.
105 ///
106 /// Then
107 ///
108 /// $f(x, k, r) = (g(x, k, r), \operatorname{cmp}(g(x, k, r), q))$.
109 ///
110 /// # Worst-case complexity
111 /// Constant time and additional memory.
112 ///
113 /// # Panics
114 /// Panics if `bits` is positive and `rm` is `Exact` but `self` is not divisible
115 /// by $2^b$.
116 ///
117 /// # Examples
118 /// See [here](super::shl_round#shl_round).
119 #[inline]
120 fn shl_round(self, bits: $u, rm: RoundingMode) -> ($t, Ordering) {
121 shl_round(self, bits, rm)
122 }
123 }
124
125 impl ShlRoundAssign<$u> for $t {
126 /// Left-shifts a number (multiplies it by a power of 2 or divides it by a power
127 /// of 2 and takes the floor) and rounds according to the specified rounding
128 /// mode, in place. An [`Ordering`] is returned, indicating whether the assigned
129 /// value is less than, equal to, or greater than the exact value. If `bits` is
130 /// non-negative, then the returned [`Ordering`] is always `Equal`, even if the
131 /// higher bits of the result are lost.
132 ///
133 /// Passing `Floor` or `Down` is equivalent to using `>>`. To test whether
134 /// `Exact` can be passed, use `bits > 0 || self.divisible_by_power_of_2(bits)`.
135 /// Rounding might only be necessary if `bits` is negative.
136 ///
137 /// See the [`ShlRound`] documentation for details.
138 ///
139 /// # Worst-case complexity
140 /// Constant time and additional memory.
141 ///
142 /// # Panics
143 /// Panics if `bits` is positive and `rm` is `Exact` but `self` is not divisible
144 /// by $2^b$.
145 ///
146 /// # Examples
147 /// See [here](super::shl_round#shl_round_assign).
148 #[inline]
149 fn shl_round_assign(&mut self, bits: $u, rm: RoundingMode) -> Ordering {
150 shl_round_assign(self, bits, rm)
151 }
152 }
153 };
154 }
155 apply_to_signeds!(impl_shl_round_inner);
156 };
157}
158apply_to_primitive_ints!(impl_shl_round);