malachite_q/arithmetic/
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::Rational;
10use core::ops::{Shl, ShlAssign, Shr, ShrAssign};
11use malachite_base::num::arithmetic::traits::UnsignedAbs;
12use malachite_base::num::basic::traits::Zero;
13use malachite_base::num::conversion::traits::ExactFrom;
14
15fn shl_unsigned_assign<T>(x: &mut Rational, bits: T)
16where
17    u64: TryFrom<T>,
18{
19    if *x == 0u32 {
20        return;
21    }
22    let denominator_zeros = x.denominator.trailing_zeros().unwrap();
23    let bits_64 = u64::exact_from(bits);
24    if denominator_zeros >= bits_64 {
25        x.denominator >>= bits_64;
26    } else {
27        x.denominator >>= denominator_zeros;
28        x.numerator <<= bits_64 - denominator_zeros;
29    }
30}
31
32fn shl_unsigned_ref<T>(x: &Rational, bits: T) -> Rational
33where
34    u64: TryFrom<T>,
35{
36    if *x == 0u32 {
37        return x.clone();
38    }
39    let denominator_zeros = x.denominator.trailing_zeros().unwrap();
40    let bits_64 = u64::exact_from(bits);
41    if denominator_zeros >= bits_64 {
42        Rational {
43            sign: x.sign,
44            numerator: x.numerator.clone(),
45            denominator: &x.denominator >> bits_64,
46        }
47    } else {
48        Rational {
49            sign: x.sign,
50            numerator: &x.numerator << (bits_64 - denominator_zeros),
51            denominator: &x.denominator >> denominator_zeros,
52        }
53    }
54}
55
56macro_rules! impl_shl_unsigned {
57    ($t:ident) => {
58        impl Shl<$t> for Rational {
59            type Output = Rational;
60
61            /// Left-shifts a [`Rational`] (multiplies it by a power of 2), taking it by value.
62            ///
63            /// $$
64            /// f(x, k) = x2^k.
65            /// $$
66            ///
67            /// # Worst-case complexity
68            /// $T(n, m) = O(n + m)$
69            ///
70            /// $M(n, m) = O(n + m)$
71            ///
72            /// where $T$ is time, $M$ is additional memory, $n$ is `self.significant_bits()`, and
73            /// $m$ is `bits`.
74            ///
75            /// # Examples
76            /// See [here](super::shl#shl).
77            #[inline]
78            fn shl(mut self, bits: $t) -> Rational {
79                self <<= bits;
80                self
81            }
82        }
83
84        impl Shl<$t> for &Rational {
85            type Output = Rational;
86
87            /// Left-shifts a [`Rational`] (multiplies it by a power of 2), taking it by value.
88            ///
89            /// $$
90            /// f(x, k) = x2^k.
91            /// $$
92            ///
93            /// # Worst-case complexity
94            /// $T(n, m) = O(n + m)$
95            ///
96            /// $M(n, m) = O(n + m)$
97            ///
98            /// where $T$ is time, $M$ is additional memory, $n$ is `self.significant_bits()`, and
99            /// $m$ is `bits`.
100            ///
101            /// # Examples
102            /// See [here](super::shl#shl).
103            #[inline]
104            fn shl(self, bits: $t) -> Rational {
105                shl_unsigned_ref(self, bits)
106            }
107        }
108
109        impl ShlAssign<$t> for Rational {
110            /// Left-shifts a [`Rational`] (multiplies it by a power of 2), in place.
111            ///
112            /// $$
113            /// x \gets x2^k.
114            /// $$
115            ///
116            /// # Worst-case complexity
117            /// $T(n, m) = O(n + m)$
118            ///
119            /// $M(n, m) = O(n + m)$
120            ///
121            /// where $T$ is time, $M$ is additional memory, $n$ is `self.significant_bits()`, and
122            /// $m$ is `bits`.
123            ///
124            /// # Examples
125            /// See [here](super::shl#shl_assign).
126            #[inline]
127            fn shl_assign(&mut self, bits: $t) {
128                shl_unsigned_assign(self, bits);
129            }
130        }
131    };
132}
133apply_to_unsigneds!(impl_shl_unsigned);
134
135fn shl_signed_ref<'a, U, S: Copy + Ord + UnsignedAbs<Output = U> + Zero>(
136    x: &'a Rational,
137    bits: S,
138) -> Rational
139where
140    &'a Rational: Shl<U, Output = Rational> + Shr<U, Output = Rational>,
141{
142    if bits >= S::ZERO {
143        x << bits.unsigned_abs()
144    } else {
145        x >> bits.unsigned_abs()
146    }
147}
148
149fn shl_assign_signed<U, S: Copy + Ord + UnsignedAbs<Output = U> + Zero>(x: &mut Rational, bits: S)
150where
151    Rational: ShlAssign<U> + ShrAssign<U>,
152{
153    if bits >= S::ZERO {
154        *x <<= bits.unsigned_abs();
155    } else {
156        *x >>= bits.unsigned_abs();
157    }
158}
159
160macro_rules! impl_shl_signed {
161    ($t:ident) => {
162        impl Shl<$t> for Rational {
163            type Output = Rational;
164
165            /// Left-shifts a [`Rational`] (multiplies it or divides it by a power of 2), taking it
166            /// by value.
167            ///
168            /// $$
169            /// f(x, k) = x2^k.
170            /// $$
171            ///
172            /// # Worst-case complexity
173            /// $T(n, m) = O(n + m)$
174            ///
175            /// $M(n, m) = O(n + m)$
176            ///
177            /// where $T$ is time, $M$ is additional memory, $n$ is `self.significant_bits()`, and
178            /// $m$ is `max(bits, 0)`.
179            ///
180            /// See [here](super::shl#shl).
181            #[inline]
182            fn shl(mut self, bits: $t) -> Rational {
183                self <<= bits;
184                self
185            }
186        }
187
188        impl Shl<$t> for &Rational {
189            type Output = Rational;
190
191            /// Left-shifts a [`Rational`] (multiplies or divides it by a power of 2), taking it by
192            /// reference.
193            ///
194            /// $$
195            /// f(x, k) = x2^k.
196            /// $$
197            ///
198            /// # Worst-case complexity
199            /// $T(n, m) = O(n + m)$
200            ///
201            /// $M(n, m) = O(n + m)$
202            ///
203            /// where $T$ is time, $M$ is additional memory, $n$ is `self.significant_bits()`, and
204            /// $m$ is `max(bits, 0)`.
205            ///
206            /// See [here](super::shl#shl).
207            #[inline]
208            fn shl(self, bits: $t) -> Rational {
209                shl_signed_ref(self, bits)
210            }
211        }
212
213        impl ShlAssign<$t> for Rational {
214            /// Left-shifts a [`Rational`] (multiplies or divides it by a power of 2), in place.
215            ///
216            /// $$
217            /// x \gets x2^k.
218            /// $$
219            ///
220            /// # Worst-case complexity
221            /// $T(n, m) = O(n + m)$
222            ///
223            /// $M(n, m) = O(n + m)$
224            ///
225            /// where $T$ is time, $M$ is additional memory, $n$ is `self.significant_bits()`, and
226            /// $m$ is `bits`.
227            ///
228            /// See [here](super::shl#shl_assign).
229            fn shl_assign(&mut self, bits: $t) {
230                shl_assign_signed(self, bits);
231            }
232        }
233    };
234}
235apply_to_signeds!(impl_shl_signed);