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);