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