malachite_nz/natural/logic/
bit_block_access.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::natural::InnerNatural::{Large, Small};
10use crate::natural::Natural;
11use crate::natural::arithmetic::mod_power_of_2::limbs_vec_mod_power_of_2_in_place;
12use crate::natural::arithmetic::shl::limbs_slice_shl_in_place;
13use crate::natural::arithmetic::shr::limbs_slice_shr_in_place;
14use crate::natural::logic::not::limbs_not_in_place;
15use crate::platform::Limb;
16use alloc::vec::Vec;
17use malachite_base::num::arithmetic::traits::{ModPowerOf2, ShrRound};
18use malachite_base::num::basic::integers::PrimitiveInt;
19use malachite_base::num::conversion::traits::ExactFrom;
20use malachite_base::num::logic::traits::{BitBlockAccess, LeadingZeros};
21use malachite_base::rounding_modes::RoundingMode::*;
22use malachite_base::slices::slice_set_zero;
23use malachite_base::vecs::vec_delete_left;
24
25// Interpreting a slice of `Limb`s as the limbs (in ascending order) of a `Natural`, returns the
26// limbs obtained by taking a slice of bits beginning at index `start` of the input slice and ending
27// at index `end - 1`. `start` must be less than or equal to `end`, but apart from that there are no
28// restrictions on the index values. If they index beyond the physical size of the input limbs, the
29// function interprets them as pointing to `false` bits.
30//
31// # Worst-case complexity
32// $T(n) = O(n)$
33//
34// $M(n) = O(n)$
35//
36// where $T$ is time, $M$ is additional memory, and $n$ is `xs.len()`.
37//
38// # Panics
39// Panics if `start > end`.
40pub_crate_test! {limbs_slice_get_bits(xs: &[Limb], start: u64, end: u64) -> Vec<Limb> {
41    assert!(start <= end);
42    let small_start = usize::exact_from(start >> Limb::LOG_WIDTH);
43    let len = xs.len();
44    if small_start >= len {
45        return Vec::new();
46    }
47    let small_end = usize::exact_from(end >> Limb::LOG_WIDTH) + 1;
48    let mut out = (if small_end >= len {
49        &xs[small_start..]
50    } else {
51        &xs[small_start..small_end]
52    })
53    .to_vec();
54    let offset = start & Limb::WIDTH_MASK;
55    if offset != 0 {
56        limbs_slice_shr_in_place(&mut out, offset);
57    }
58    limbs_vec_mod_power_of_2_in_place(&mut out, end - start);
59    out
60}}
61
62// Interpreting a `Vec` of `Limb`s as the limbs (in ascending order) of a `Natural`, returns the
63// limbs obtained by taking a slice of bits beginning at index `start` of the input slice and ending
64// at index `end - 1`. `start` must be less than or equal to `end`, but apart from that there are no
65// restrictions on the index values. If they index beyond the physical size of the input limbs, the
66// function interprets them as pointing to `false` bits.
67//
68// # Worst-case complexity
69// $T(n) = O(n)$
70//
71// $M(n) = O(1)$
72//
73// where $T$ is time, $M$ is additional memory, and $n$ is `xs.len()`.
74//
75// # Panics
76// Panics if `start > end`.
77pub_test! {limbs_vec_get_bits(mut xs: Vec<Limb>, start: u64, end: u64) -> Vec<Limb> {
78    assert!(start <= end);
79    let small_start = usize::exact_from(start >> Limb::LOG_WIDTH);
80    if small_start >= xs.len() {
81        return Vec::new();
82    }
83    limbs_vec_mod_power_of_2_in_place(&mut xs, end);
84    vec_delete_left(&mut xs, small_start);
85    let offset = start & Limb::WIDTH_MASK;
86    if offset != 0 {
87        limbs_slice_shr_in_place(&mut xs, offset);
88    }
89    xs
90}}
91
92// Copy values from `ys` into `xs`.
93//
94// - If `ys` has the same length as `xs`, the usual copy is performed.
95// - If `ys` is longer than `xs`, the first `xs.len()` limbs of `ys` are copied.
96// - If `ys` is shorter than `xs`, `ys` is copied and the remaining bits of `xs` are filled with
97//   zeros.
98//
99// # Worst-case complexity
100// $T(n) = O(n)$
101//
102// $M(n) = O(1)$
103//
104// where $T$ is time, $M$ is additional memory, and $n$ is `xs.len()`.
105fn copy_from_diff_len_slice(xs: &mut [Limb], ys: &[Limb]) {
106    let xs_len = xs.len();
107    let ys_len = ys.len();
108    if xs_len <= ys_len {
109        xs.copy_from_slice(&ys[..xs_len]);
110    } else {
111        let (xs_lo, xs_hi) = xs.split_at_mut(ys_len);
112        xs_lo.copy_from_slice(ys);
113        slice_set_zero(xs_hi);
114    }
115}
116
117pub(crate) fn limbs_assign_bits_helper(
118    xs: &mut Vec<Limb>,
119    start: u64,
120    end: u64,
121    mut bits: &[Limb],
122    invert: bool,
123) {
124    let small_start = usize::exact_from(start >> Limb::LOG_WIDTH);
125    let small_end = usize::exact_from((end - 1) >> Limb::LOG_WIDTH) + 1;
126    let width = usize::exact_from((end - start).shr_round(Limb::LOG_WIDTH, Ceiling).0);
127    if width < bits.len() {
128        bits = &bits[..width];
129    }
130    let start_remainder = start & Limb::WIDTH_MASK;
131    let end_remainder = end & Limb::WIDTH_MASK;
132    if small_end > xs.len() {
133        // Possible inefficiency here: we might write many zeros only to delete them later.
134        xs.resize(small_end, 0);
135    }
136    let out = &mut xs[small_start..small_end];
137    assert!(!out.is_empty());
138    let original_first = out[0];
139    let original_last = *out.last().unwrap();
140    copy_from_diff_len_slice(out, bits);
141    if invert {
142        limbs_not_in_place(out);
143    }
144    if start_remainder != 0 {
145        limbs_slice_shl_in_place(out, start_remainder);
146        out[0] |= original_first.mod_power_of_2(start_remainder);
147    }
148    if end_remainder != 0 {
149        out.last_mut().unwrap().assign_bits(
150            end_remainder,
151            Limb::WIDTH,
152            &(original_last >> end_remainder),
153        );
154    }
155}
156
157// Writes the limbs of `bits` into the limbs of `xs`, starting at bit `start` of `xs` (inclusive)
158// and ending at bit `end` of `xs` (exclusive). The bit indices do not need to be aligned with any
159// limb boundaries. If `bits` has more than `end` - `start` bits, only the first `end` - `start`
160// bits are written. If `bits` has fewer than `end` - `start` bits, the remaining written bits are
161// zero. `xs` may be extended to accommodate the new bits. `start` must be smaller than `end`.
162//
163// # Worst-case complexity
164// $T(n) = O(n)$
165//
166// $M(n) = O(n)$
167//
168// where $T$ is time, $M$ is additional memory, and $n$ is `end`.
169//
170// # Panics
171// Panics if `start >= end`.
172pub_test! {limbs_assign_bits(xs: &mut Vec<Limb>, start: u64, end: u64, bits: &[Limb]) {
173    assert!(start < end);
174    limbs_assign_bits_helper(xs, start, end, bits, false);
175}}
176
177impl BitBlockAccess for Natural {
178    type Bits = Natural;
179
180    /// Extracts a block of adjacent bits from a [`Natural`], taking the [`Natural`] by reference.
181    ///
182    /// The first index is `start` and last index is `end - 1`.
183    ///
184    /// Let $n$ be `self`, and let $p$ and $q$ be `start` and `end`, respectively.
185    ///
186    /// Let
187    /// $$
188    /// n = \sum_{i=0}^\infty 2^{b_i},
189    /// $$
190    /// where for all $i$, $b_i\in \\{0, 1\\}$; so finitely many of the bits are 1, and the rest are
191    /// 0. Then
192    /// $$
193    /// f(n, p, q) = \sum_{i=p}^{q-1} 2^{b_{i-p}}.
194    /// $$
195    ///
196    /// # Worst-case complexity
197    /// $T(n) = O(n)$
198    ///
199    /// $M(n) = O(n)$
200    ///
201    /// where $T$ is time, $M$ is additional memory, and $n$ is `self.significant_bits()`.
202    ///
203    /// # Panics
204    /// Panics if `start > end`.
205    ///
206    /// # Examples
207    /// ```
208    /// use malachite_base::num::logic::traits::BitBlockAccess;
209    /// use malachite_nz::natural::Natural;
210    ///
211    /// assert_eq!(
212    ///     Natural::from(0xabcdef0112345678u64).get_bits(16, 48),
213    ///     0xef011234u32
214    /// );
215    /// assert_eq!(
216    ///     Natural::from(0xabcdef0112345678u64).get_bits(4, 16),
217    ///     0x567u32
218    /// );
219    /// assert_eq!(
220    ///     Natural::from(0xabcdef0112345678u64).get_bits(0, 100),
221    ///     0xabcdef0112345678u64
222    /// );
223    /// assert_eq!(Natural::from(0xabcdef0112345678u64).get_bits(10, 10), 0);
224    /// ```
225    fn get_bits(&self, start: u64, end: u64) -> Natural {
226        match self {
227            Natural(Small(small)) => Natural(Small(small.get_bits(start, end))),
228            Natural(Large(limbs)) => {
229                Natural::from_owned_limbs_asc(limbs_slice_get_bits(limbs, start, end))
230            }
231        }
232    }
233
234    /// Extracts a block of adjacent bits from a [`Natural`], taking the [`Natural`] by value.
235    ///
236    /// The first index is `start` and last index is `end - 1`.
237    ///
238    /// Let $n$ be `self`, and let $p$ and $q$ be `start` and `end`, respectively.
239    ///
240    /// Let
241    /// $$
242    /// n = \sum_{i=0}^\infty 2^{b_i},
243    /// $$
244    /// where for all $i$, $b_i\in \\{0, 1\\}$; so finitely many of the bits are 1, and the rest are
245    /// 0. Then
246    /// $$
247    /// f(n, p, q) = \sum_{i=p}^{q-1} 2^{b_{i-p}}.
248    /// $$
249    ///
250    /// # Worst-case complexity
251    /// $T(n) = O(n)$
252    ///
253    /// $M(n) = O(1)$
254    ///
255    /// where $T$ is time, $M$ is additional memory, and $n$ is `self.significant_bits()`.
256    ///
257    /// # Panics
258    /// Panics if `start > end`.
259    ///
260    /// # Examples
261    /// ```
262    /// use malachite_base::num::logic::traits::BitBlockAccess;
263    /// use malachite_nz::natural::Natural;
264    ///
265    /// assert_eq!(
266    ///     Natural::from(0xabcdef0112345678u64).get_bits_owned(16, 48),
267    ///     0xef011234u32
268    /// );
269    /// assert_eq!(
270    ///     Natural::from(0xabcdef0112345678u64).get_bits_owned(4, 16),
271    ///     0x567u32
272    /// );
273    /// assert_eq!(
274    ///     Natural::from(0xabcdef0112345678u64).get_bits_owned(0, 100),
275    ///     0xabcdef0112345678u64
276    /// );
277    /// assert_eq!(
278    ///     Natural::from(0xabcdef0112345678u64).get_bits_owned(10, 10),
279    ///     0
280    /// );
281    /// ```
282    fn get_bits_owned(self, start: u64, end: u64) -> Natural {
283        match self {
284            Natural(Small(small)) => Natural(Small(small.get_bits(start, end))),
285            Natural(Large(limbs)) => {
286                Natural::from_owned_limbs_asc(limbs_vec_get_bits(limbs, start, end))
287            }
288        }
289    }
290
291    /// Replaces a block of adjacent bits in a [`Natural`] with other bits.
292    ///
293    /// The least-significant `end - start` bits of `bits` are assigned to bits `start` through `end
294    /// - 1`, inclusive, of `self`.
295    ///
296    /// Let $n$ be `self` and let $m$ be `bits`, and let $p$ and $q$ be `start` and `end`,
297    /// respectively.
298    ///
299    /// If `bits` has fewer bits than `end - start`, the high bits are interpreted as 0. Let
300    /// $$
301    /// n = \sum_{i=0}^\infty 2^{b_i},
302    /// $$
303    /// where for all $i$, $b_i\in \\{0, 1\\}$; so finitely many of the bits are 1, and the rest are
304    /// 0. Let
305    /// $$
306    /// m = \sum_{i=0}^k 2^{d_i},
307    /// $$
308    /// where for all $i$, $d_i\in \\{0, 1\\}$. Also, let $p, q \in \mathbb{N}$, and let $W$ be
309    /// `max(self.significant_bits(), end + 1)`.
310    ///
311    /// Then
312    /// $$
313    /// n \gets \sum_{i=0}^{W-1} 2^{c_i},
314    /// $$
315    /// where
316    /// $$
317    /// \\{c_0, c_1, c_2, \ldots, c_ {W-1}\\} =
318    /// \\{b_0, b_1, b_2, \ldots, b_{p-1}, d_0, d_1, \ldots, d_{p-q-1}, b_q, \ldots,
319    /// b_ {W-1}\\}.
320    /// $$
321    ///
322    /// # Worst-case complexity
323    /// $T(n) = O(n)$
324    ///
325    /// $M(n) = O(n)$
326    ///
327    /// where $T$ is time, $M$ is additional memory, and $n$ is `end`.
328    ///
329    /// # Panics
330    /// Panics if `start > end`.
331    ///
332    /// # Examples
333    /// ```
334    /// use malachite_base::num::logic::traits::BitBlockAccess;
335    /// use malachite_nz::natural::Natural;
336    ///
337    /// let mut n = Natural::from(123u32);
338    /// n.assign_bits(5, 7, &Natural::from(456u32));
339    /// assert_eq!(n, 27);
340    ///
341    /// let mut n = Natural::from(123u32);
342    /// n.assign_bits(64, 128, &Natural::from(456u32));
343    /// assert_eq!(n.to_string(), "8411715297611555537019");
344    ///
345    /// let mut n = Natural::from(123u32);
346    /// n.assign_bits(80, 100, &Natural::from(456u32));
347    /// assert_eq!(n.to_string(), "551270173744270903666016379");
348    /// ```
349    fn assign_bits(&mut self, start: u64, end: u64, bits: &Natural) {
350        if start == end {
351            return;
352        }
353        if let Natural(Small(small_self)) = self {
354            if let Natural(Small(small_bits)) = bits {
355                let bits_width = end - start;
356                let small_bits = small_bits.mod_power_of_2(bits_width);
357                if small_bits == 0 || LeadingZeros::leading_zeros(small_bits) >= start {
358                    small_self.assign_bits(start, end, &small_bits);
359                    return;
360                }
361            }
362        }
363        let limbs = self.promote_in_place();
364        match bits {
365            Natural(Small(small_bits)) => limbs_assign_bits(limbs, start, end, &[*small_bits]),
366            Natural(Large(bits_limbs)) => limbs_assign_bits(limbs, start, end, bits_limbs),
367        }
368        self.trim();
369    }
370}