malachite_base/num/arithmetic/arithmetic_checked_shl.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::{ArithmeticCheckedShl, UnsignedAbs};
10use crate::num::basic::signeds::PrimitiveSigned;
11use crate::num::basic::unsigneds::PrimitiveUnsigned;
12use crate::num::conversion::traits::WrappingFrom;
13use core::ops::{Shl, Shr};
14
15fn arithmetic_checked_shl_unsigned_unsigned<
16 T: PrimitiveUnsigned + Shl<U, Output = T> + Shr<U, Output = T>,
17 U: Copy + Ord + WrappingFrom<u64>,
18>(
19 x: T,
20 bits: U,
21) -> Option<T> {
22 if x == T::ZERO {
23 Some(x)
24 } else if bits >= U::wrapping_from(T::WIDTH) {
25 None
26 } else {
27 let result = x << bits;
28 if result >> bits == x {
29 Some(result)
30 } else {
31 None
32 }
33 }
34}
35
36macro_rules! impl_arithmetic_checked_shl_unsigned_unsigned {
37 ($t:ident) => {
38 macro_rules! impl_arithmetic_checked_shl_unsigned_unsigned_inner {
39 ($u:ident) => {
40 impl ArithmeticCheckedShl<$u> for $t {
41 type Output = $t;
42
43 /// Left-shifts a number (multiplies it by a power of 2). If the result is too
44 /// large to be represented, `None` is returned.
45 ///
46 /// Zero may be shifted by any amount.
47 ///
48 /// $$
49 /// f(x, b) = \\begin{cases}
50 /// \operatorname{Some}(2^b x) & \text{if} \\quad 2^b x < 2^W, \\\\
51 /// \operatorname{None} & \text{if} \\quad 2^b x \geq 2^W,
52 /// \\end{cases}
53 /// $$
54 /// where $W$ is `Self::WIDTH`.
55 ///
56 /// # Worst-case complexity
57 /// Constant time and additional memory.
58 ///
59 /// # Examples
60 /// See [here](super::arithmetic_checked_shl#arithmetic_checked_shl).
61 #[inline]
62 fn arithmetic_checked_shl(self, bits: $u) -> Option<$t> {
63 arithmetic_checked_shl_unsigned_unsigned(self, bits)
64 }
65 }
66 };
67 }
68 apply_to_unsigneds!(impl_arithmetic_checked_shl_unsigned_unsigned_inner);
69 };
70}
71apply_to_unsigneds!(impl_arithmetic_checked_shl_unsigned_unsigned);
72
73fn arithmetic_checked_shl_unsigned_signed<
74 T: ArithmeticCheckedShl<U, Output = T> + PrimitiveUnsigned + Shr<U, Output = T>,
75 U: PrimitiveUnsigned,
76 S: PrimitiveSigned + UnsignedAbs<Output = U>,
77>(
78 x: T,
79 bits: S,
80) -> Option<T> {
81 if bits >= S::ZERO {
82 x.arithmetic_checked_shl(bits.unsigned_abs())
83 } else {
84 let abs_bits = bits.unsigned_abs();
85 Some(if abs_bits >= U::wrapping_from(T::WIDTH) {
86 T::ZERO
87 } else {
88 x >> abs_bits
89 })
90 }
91}
92
93macro_rules! impl_arithmetic_checked_shl_unsigned_signed {
94 ($t:ident) => {
95 macro_rules! impl_arithmetic_checked_shl_unsigned_signed_inner {
96 ($u:ident) => {
97 impl ArithmeticCheckedShl<$u> for $t {
98 type Output = $t;
99
100 /// Left-shifts a number (multiplies it by a power of 2). If the result is too
101 /// large to be represented, `None` is returned.
102 ///
103 /// Zero may be shifted by any amount, and any number may be shifted by any
104 /// negative amount; shifting by a negative amount with a high absolute value
105 /// returns `Some(0)`.
106 ///
107 /// $$
108 /// f(x, b) = \\begin{cases}
109 /// \operatorname{Some}(2^b x) &
110 /// \text{if} \\quad b \geq 0 \\ \mathrm{and}\\ 2^b x < 2^W, \\\\
111 /// \operatorname{None} &
112 /// \text{if} \\quad b \geq 0 \\ \mathrm{and} \\ 2^b x \geq 2^W, \\\\
113 /// \operatorname{Some}(\lfloor x/2^{-b} \rfloor) &
114 /// \text{if} \\quad b < 0,
115 /// \\end{cases}
116 /// $$
117 /// where $W$ is `Self::WIDTH`.
118 ///
119 /// # Worst-case complexity
120 /// Constant time and additional memory.
121 ///
122 /// # Examples
123 /// See [here](super::arithmetic_checked_shl#arithmetic_checked_shl).
124 #[inline]
125 fn arithmetic_checked_shl(self, bits: $u) -> Option<$t> {
126 arithmetic_checked_shl_unsigned_signed(self, bits)
127 }
128 }
129 };
130 }
131 apply_to_signeds!(impl_arithmetic_checked_shl_unsigned_signed_inner);
132 };
133}
134apply_to_unsigneds!(impl_arithmetic_checked_shl_unsigned_signed);
135
136fn arithmetic_checked_shl_signed_unsigned<
137 U: ArithmeticCheckedShl<B, Output = U> + PrimitiveUnsigned,
138 S: TryFrom<U> + PrimitiveSigned + UnsignedAbs<Output = U>,
139 B,
140>(
141 x: S,
142 bits: B,
143) -> Option<S> {
144 let abs = x.unsigned_abs();
145 if x >= S::ZERO {
146 abs.arithmetic_checked_shl(bits)
147 .and_then(|x| S::try_from(x).ok())
148 } else {
149 abs.arithmetic_checked_shl(bits).and_then(|x| {
150 if x == S::MIN.unsigned_abs() {
151 Some(S::MIN)
152 } else {
153 S::try_from(x).ok().map(|y| -y)
154 }
155 })
156 }
157}
158
159macro_rules! impl_arithmetic_checked_shl_signed_unsigned {
160 ($t:ident) => {
161 macro_rules! impl_arithmetic_checked_shl_signed_unsigned_inner {
162 ($u:ident) => {
163 impl ArithmeticCheckedShl<$u> for $t {
164 type Output = $t;
165
166 /// Left-shifts a number (multiplies it by a power of 2). If the result is too
167 /// large to be represented, `None` is returned.
168 ///
169 /// Zero may be shifted by any amount.
170 ///
171 /// $$
172 /// f(x, b) = \\begin{cases}
173 /// \operatorname{Some}(2^b x) &
174 /// \text{if} \\quad -2^{W-1} \leq 2^b x < 2^{W-1}, \\\\
175 /// \operatorname{None} &
176 /// \text{if} \\quad 2^b x < -2^{W-1} \\ \mathrm{or}
177 /// \\ 2^b x \geq 2^{W-1}, \\\\
178 /// \\end{cases}
179 /// $$
180 /// where $W$ is `Self::WIDTH`.
181 ///
182 /// # Worst-case complexity
183 /// Constant time and additional memory.
184 ///
185 /// # Examples
186 /// See [here](super::arithmetic_checked_shl#arithmetic_checked_shl).
187 #[inline]
188 fn arithmetic_checked_shl(self, bits: $u) -> Option<$t> {
189 arithmetic_checked_shl_signed_unsigned(self, bits)
190 }
191 }
192 };
193 }
194 apply_to_unsigneds!(impl_arithmetic_checked_shl_signed_unsigned_inner);
195 };
196}
197apply_to_signeds!(impl_arithmetic_checked_shl_signed_unsigned);
198
199fn arithmetic_checked_shl_signed_signed<
200 T: ArithmeticCheckedShl<U, Output = T> + PrimitiveSigned + Shr<U, Output = T>,
201 U: PrimitiveUnsigned,
202 S: PrimitiveSigned + UnsignedAbs<Output = U>,
203>(
204 x: T,
205 bits: S,
206) -> Option<T> {
207 if bits >= S::ZERO {
208 x.arithmetic_checked_shl(bits.unsigned_abs())
209 } else {
210 let width = U::wrapping_from(T::WIDTH);
211 let abs_bits = bits.unsigned_abs();
212 Some(if width != U::ZERO && abs_bits >= width {
213 -T::from(x < T::ZERO)
214 } else {
215 x >> abs_bits
216 })
217 }
218}
219
220macro_rules! impl_arithmetic_checked_shl_signed_signed {
221 ($t:ident) => {
222 macro_rules! impl_arithmetic_checked_shl_signed_signed_inner {
223 ($u:ident) => {
224 impl ArithmeticCheckedShl<$u> for $t {
225 type Output = $t;
226
227 /// Left-shifts a number (multiplies it by a power of 2). If the result is too
228 /// large to be represented, `None` is returned.
229 ///
230 /// Zero may be shifted by any amount, and any number may be shifted by any
231 /// negative amount; shifting by a negative amount with a high absolute value
232 /// returns `Some(0)` if `self` is positive, and `Some(-1)` if `self` is
233 /// negative.
234 ///
235 /// $$
236 /// f(x, b) = \\begin{cases}
237 /// \operatorname{Some}(2^b x) &
238 /// \text{if} \\quad b \geq 0 \\ \mathrm{and}
239 /// \\ -2^{W-1} \leq 2^b x < 2^{W-1}, \\\\
240 /// \operatorname{None} &
241 /// \text{if} \\quad b \geq 0 \\ \mathrm{and}
242 /// \\ (2^b x < -2^{W-1} \\ \mathrm{or} \\ 2^b x \geq 2^{W-1}), \\\\
243 /// \operatorname{Some}(\lfloor x/2^{-b} \rfloor) & \text{if} \\quad b < 0,
244 /// \\end{cases}
245 /// $$
246 /// where $W$ is `Self::WIDTH`.
247 ///
248 /// # Worst-case complexity
249 /// Constant time and additional memory.
250 ///
251 /// # Examples
252 /// See [here](super::arithmetic_checked_shl#arithmetic_checked_shl).
253 fn arithmetic_checked_shl(self, bits: $u) -> Option<$t> {
254 arithmetic_checked_shl_signed_signed(self, bits)
255 }
256 }
257 };
258 }
259 apply_to_signeds!(impl_arithmetic_checked_shl_signed_signed_inner);
260 };
261}
262apply_to_signeds!(impl_arithmetic_checked_shl_signed_signed);