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}