Skip to main content

malachite_base/unsigned_vector/arithmetic/
shr.rs

1// Copyright © 2026 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::basic::unsigneds::PrimitiveUnsigned;
10use crate::unsigned_vector::UnsignedVector;
11use alloc::vec;
12use core::ops::{Shr, ShrAssign};
13
14// The shift amount as a `u64`, or `None` if it is at least `T::WIDTH`, in which case every element
15// becomes zero. `T::WIDTH` is at most 128, so it fits in every unsigned type.
16fn shift_below_width<T: PrimitiveUnsigned, U: PrimitiveUnsigned>(bits: U) -> Option<u64> {
17    if bits >= U::exact_from(T::WIDTH) {
18        None
19    } else {
20        Some(bits.exact_into())
21    }
22}
23
24// Shifts every element right by `bits`, taking the floor.
25fn shr_ref<T: PrimitiveUnsigned, U: PrimitiveUnsigned>(
26    v: &UnsignedVector<T>,
27    bits: U,
28) -> UnsignedVector<T> {
29    let Some(bits) = shift_below_width::<T, U>(bits) else {
30        return UnsignedVector {
31            elements: vec![T::ZERO; v.elements.len()],
32        };
33    };
34    UnsignedVector {
35        elements: v.elements.iter().map(|&x| x >> bits).collect(),
36    }
37}
38
39// Shifts every element right by `bits`, taking the floor, in place.
40fn shr_assign<T: PrimitiveUnsigned, U: PrimitiveUnsigned>(v: &mut UnsignedVector<T>, bits: U) {
41    let Some(bits) = shift_below_width::<T, U>(bits) else {
42        v.elements.fill(T::ZERO);
43        return;
44    };
45    if bits != 0 {
46        for x in &mut v.elements {
47            *x >>= bits;
48        }
49    }
50}
51
52macro_rules! impl_shr_unsigned {
53    ($t:ident) => {
54        impl<T: PrimitiveUnsigned> Shr<$t> for UnsignedVector<T> {
55            type Output = UnsignedVector<T>;
56
57            /// Right-shifts an [`UnsignedVector`] (divides it by a power of 2 and takes the floor),
58            /// taking it by value. Every element is shifted, and the dimension is unchanged; if
59            /// `bits` is at least `T::WIDTH`, every element is zero.
60            ///
61            /// $f(v, k) = \lfloor v/2^k \rfloor$. The floor is taken entrywise; to round
62            /// differently, use
63            /// [`entrywise_shr_round`](crate::num::arithmetic::traits::EntrywiseShrRound).
64            ///
65            /// There is no corresponding non-modular left shift, since it could overflow; use
66            /// [`mod_power_of_2_shl`](crate::num::arithmetic::traits::ModPowerOf2Shl) or
67            /// [`mod_shl`](crate::num::arithmetic::traits::ModShl) instead.
68            ///
69            /// # Worst-case complexity
70            /// $T(n) = O(n)$
71            ///
72            /// $M(n) = O(1)$
73            ///
74            /// where $T$ is time, $M$ is additional memory, and $n$ is `self.dimension()`.
75            ///
76            /// # Examples
77            /// See [here](super::shr#shr).
78            #[inline]
79            fn shr(mut self, bits: $t) -> UnsignedVector<T> {
80                self >>= bits;
81                self
82            }
83        }
84
85        impl<T: PrimitiveUnsigned> Shr<$t> for &UnsignedVector<T> {
86            type Output = UnsignedVector<T>;
87
88            /// Right-shifts an [`UnsignedVector`] (divides it by a power of 2 and takes the floor),
89            /// taking it by reference. Every element is shifted, and the dimension is unchanged; if
90            /// `bits` is at least `T::WIDTH`, every element is zero.
91            ///
92            /// $f(v, k) = \lfloor v/2^k \rfloor$. The floor is taken entrywise; to round
93            /// differently, use
94            /// [`entrywise_shr_round`](crate::num::arithmetic::traits::EntrywiseShrRound).
95            ///
96            /// # Worst-case complexity
97            /// $T(n) = O(n)$
98            ///
99            /// $M(n) = O(n)$
100            ///
101            /// where $T$ is time, $M$ is additional memory, and $n$ is `self.dimension()`.
102            ///
103            /// # Examples
104            /// See [here](super::shr#shr).
105            #[inline]
106            fn shr(self, bits: $t) -> UnsignedVector<T> {
107                shr_ref(self, bits)
108            }
109        }
110
111        impl<T: PrimitiveUnsigned> ShrAssign<$t> for UnsignedVector<T> {
112            /// Right-shifts an [`UnsignedVector`] (divides it by a power of 2 and takes the floor),
113            /// in place. Every element is shifted, and the dimension is unchanged; if `bits` is at
114            /// least `T::WIDTH`, every element becomes zero.
115            ///
116            /// $v \gets \lfloor v/2^k \rfloor$. The floor is taken entrywise; to round differently,
117            /// use [`entrywise_shr_round`](crate::num::arithmetic::traits::EntrywiseShrRound).
118            ///
119            /// # Worst-case complexity
120            /// $T(n) = O(n)$
121            ///
122            /// $M(n) = O(1)$
123            ///
124            /// where $T$ is time, $M$ is additional memory, and $n$ is `self.dimension()`.
125            ///
126            /// # Examples
127            /// See [here](super::shr#shr_assign).
128            #[inline]
129            fn shr_assign(&mut self, bits: $t) {
130                shr_assign(self, bits);
131            }
132        }
133    };
134}
135apply_to_unsigneds!(impl_shr_unsigned);