malachite_base/iterators/bit_distributor.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::basic::integers::PrimitiveInt;
10use crate::num::logic::traits::{BitConvertible, NotAssign};
11use alloc::vec::Vec;
12use core::fmt::Debug;
13
14const COUNTER_WIDTH: usize = u64::WIDTH as usize;
15
16/// This struct is used to configure [`BitDistributor`]s.
17///
18/// See the [`BitDistributor`] documentation for more.
19#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
20pub struct BitDistributorOutputType {
21 weight: usize, // 0 means a tiny output_type
22 max_bits: Option<usize>,
23}
24
25impl BitDistributorOutputType {
26 /// Creates a normal output with a specified weight.
27 ///
28 /// # Worst-case complexity
29 /// Constant time and additional memory.
30 ///
31 /// # Panics
32 /// Panics if `weight` is zero.
33 ///
34 /// The corresponding element grows as a power of $i$. See the [`BitDistributor`] documentation
35 /// for more.
36 pub fn normal(weight: usize) -> BitDistributorOutputType {
37 assert_ne!(weight, 0);
38 BitDistributorOutputType {
39 weight,
40 max_bits: None,
41 }
42 }
43
44 /// Creates a tiny output.
45 ///
46 /// # Worst-case complexity
47 /// Constant time and additional memory.
48 ///
49 /// The corresponding element grows logarithmically. See the [`BitDistributor`] documentation
50 /// for more.
51 pub const fn tiny() -> BitDistributorOutputType {
52 BitDistributorOutputType {
53 weight: 0,
54 max_bits: None,
55 }
56 }
57}
58
59/// Helps generate tuples exhaustively.
60///
61/// Think of `counter` as the bits of an integer. It's initialized to zero (all `false`s), and as
62/// it's repeatedly incremented, it eventually takes on every 64-bit value.
63///
64/// `output_types` is a list of $n$ configuration structs that, together, specify how to generate an
65/// n-element tuple of unsigned integers. Calling `get_output` repeatedly, passing in 0 through $n -
66/// 1$ as `index`, distributes the bits of `counter` into a tuple.
67///
68/// This is best shown with an example. If `output_types` is set to
69/// `[BitDistributorOutputType::normal(1); 2]`, the distributor will generate all pairs of unsigned
70/// integers. A pair may be extracted by calling `get_output(0)` and `get_output(1)`; then `counter`
71/// may be incremented to create the next pair. In this case, the pairs will be $(0, 0), (0, 1), (1,
72/// 0), (1, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 0), (2, 1), \ldots$.
73///
74/// If you think of these pairs as coordinates in the $xy$-plane, they are traversed along a
75/// [Z-order curve](https://en.wikipedia.org/wiki/Z-order_curve). Every pair of unsigned integers
76/// will be generated exactly once.
77///
78/// In general, setting `output_types` to `[BitDistributorOutputType::normal(1); n]` will generate
79/// $n$-tuples. The elements of the tuples will be very roughly the same size, in the sense that
80/// each element will grow as $O(\sqrt\[n\]{i})$, where $i$ is the counter. Sometimes we want the
81/// elements to grow at different rates. To accomplish this, we can change the weights of the output
82/// types. For example, if we set `output_types` to `[BitDistributorOutputType::normal(1),
83/// BitDistributorOutputType::normal(2)]`, the first element of the generated pairs will grow as
84/// $O(\sqrt\[3\]{i})$ and the second as $O(i^{2/3})$. In general, if the weights are $w_0, w_1,
85/// \\ldots, w_{n-1}$, then the $k$th element of the output tuples will grow as
86/// $O(i^{w_i/\sum_{j=0}^{n-1}w_j})$.
87///
88/// Apart from creating _normal_ output types with different weights, we can create _tiny_ output
89/// types, which indicate that the corresponding tuple element should grow especially slowly. If
90/// `output_types` contains $m$ tiny output types, each tiny tuple element grows as
91/// $O(\sqrt\[m\]{\log i})$. The growth of the other elements is unaffected. Having only tiny types
92/// in `output_types` is disallowed.
93///
94/// The above discussion of growth rates assumes that `max_bits` is not specified for any output
95/// type. But if `max_bits` is set to $b$, then the corresponding element will start growing just as
96/// if `max_bits` wasn't specified, but will stop growing once it reaches $2^b-1$.
97#[derive(Clone, Debug, Eq, PartialEq, Hash)]
98pub struct BitDistributor {
99 #[cfg(feature = "test_build")]
100 pub output_types: Vec<BitDistributorOutputType>,
101 #[cfg(not(feature = "test_build"))]
102 output_types: Vec<BitDistributorOutputType>,
103 bit_map: [usize; COUNTER_WIDTH],
104 counter: [bool; COUNTER_WIDTH],
105}
106
107impl BitDistributor {
108 fn new_without_init(output_types: &[BitDistributorOutputType]) -> BitDistributor {
109 if output_types
110 .iter()
111 .all(|output_type| output_type.weight == 0)
112 {
113 panic!("All output_types cannot be tiny");
114 }
115 BitDistributor {
116 output_types: output_types.to_vec(),
117 bit_map: [0; COUNTER_WIDTH],
118 counter: [false; COUNTER_WIDTH],
119 }
120 }
121
122 /// Creates a new [`BitDistributor`].
123 ///
124 /// # Worst-case complexity
125 /// $T(n) = O(n)$
126 ///
127 /// $M(n) = O(n)$
128 ///
129 /// where $T$ is time, $M$ is additional memory, and $n$ is `output_types.len()`.
130 ///
131 /// # Examples
132 /// ```
133 /// use malachite_base::iterators::bit_distributor::{
134 /// BitDistributor, BitDistributorOutputType,
135 /// };
136 ///
137 /// BitDistributor::new(&[
138 /// BitDistributorOutputType::normal(2),
139 /// BitDistributorOutputType::tiny(),
140 /// ]);
141 /// ```
142 pub fn new(output_types: &[BitDistributorOutputType]) -> BitDistributor {
143 let mut distributor = BitDistributor::new_without_init(output_types);
144 distributor.update_bit_map();
145 distributor
146 }
147
148 /// Returns a reference to the internal bit map as a slice.
149 ///
150 /// The bit map determines which output gets each bit of the counter. For example, if the bit
151 /// map is $[0, 1, 0, 1, 0, 1, \ldots]$, then the first element of the output pair gets the bits
152 /// with indices $0, 2, 4, \ldots$ and the second element gets the bits with indices $1, 3, 5,
153 /// \ldots$.
154 ///
155 /// # Worst-case complexity
156 /// Constant time and additional memory.
157 ///
158 /// # Examples
159 /// ```
160 /// use malachite_base::iterators::bit_distributor::{
161 /// BitDistributor, BitDistributorOutputType,
162 /// };
163 ///
164 /// let bd = BitDistributor::new(&[
165 /// BitDistributorOutputType::normal(2),
166 /// BitDistributorOutputType::tiny(),
167 /// ]);
168 /// assert_eq!(
169 /// bd.bit_map_as_slice(),
170 /// &[
171 /// 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
172 /// 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
173 /// 0, 0, 0, 0, 0, 0, 0, 1
174 /// ][..]
175 /// );
176 /// ```
177 pub fn bit_map_as_slice(&self) -> &[usize] {
178 self.bit_map.as_ref()
179 }
180
181 fn update_bit_map(&mut self) {
182 let (mut normal_output_type_indices, mut tiny_output_type_indices): (
183 Vec<usize>,
184 Vec<usize>,
185 ) = (0..self.output_types.len()).partition(|&i| self.output_types[i].weight != 0);
186 let mut normal_output_types_bits_used = vec![0; normal_output_type_indices.len()];
187 let mut tiny_output_types_bits_used = vec![0; tiny_output_type_indices.len()];
188 let mut ni = normal_output_type_indices.len() - 1;
189 let mut ti = tiny_output_type_indices.len().saturating_sub(1);
190 let mut weight_counter = self.output_types[normal_output_type_indices[ni]].weight;
191 for i in 0..COUNTER_WIDTH {
192 let use_normal_output_type = !normal_output_type_indices.is_empty()
193 && (tiny_output_type_indices.is_empty() || !usize::is_power_of_two(i + 1));
194 if use_normal_output_type {
195 self.bit_map[i] = normal_output_type_indices[ni];
196 let output_type = self.output_types[normal_output_type_indices[ni]];
197 normal_output_types_bits_used[ni] += 1;
198 weight_counter -= 1;
199 if output_type.max_bits == Some(normal_output_types_bits_used[ni]) {
200 normal_output_type_indices.remove(ni);
201 normal_output_types_bits_used.remove(ni);
202 if normal_output_type_indices.is_empty() {
203 continue;
204 }
205 weight_counter = 0;
206 }
207 if weight_counter == 0 {
208 if ni == 0 {
209 ni = normal_output_type_indices.len() - 1;
210 } else {
211 ni -= 1;
212 }
213 weight_counter = self.output_types[normal_output_type_indices[ni]].weight;
214 }
215 } else {
216 if tiny_output_type_indices.is_empty() {
217 self.bit_map[i] = usize::MAX;
218 continue;
219 }
220 self.bit_map[i] = tiny_output_type_indices[ti];
221 let output_type = self.output_types[tiny_output_type_indices[ti]];
222 tiny_output_types_bits_used[ti] += 1;
223 if output_type.max_bits == Some(tiny_output_types_bits_used[ti]) {
224 tiny_output_type_indices.remove(ti);
225 tiny_output_types_bits_used.remove(ti);
226 if tiny_output_type_indices.is_empty() {
227 continue;
228 }
229 }
230 if ti == 0 {
231 ti = tiny_output_type_indices.len() - 1;
232 } else {
233 ti -= 1;
234 }
235 }
236 }
237 }
238
239 /// Sets the maximum bits for several outputs.
240 ///
241 /// Given slice of output indices, sets the maximum bits for each of the outputs and rebuilds
242 /// the bit map.
243 ///
244 /// # Worst-case complexity
245 /// $T(n) = O(n)$
246 ///
247 /// $M(n) = O(1)$
248 ///
249 /// where $T$ is time, $M$ is additional memory, and $n$ is `output_type_indices.len()`.
250 ///
251 /// # Panics
252 /// Panics if `max_bits` is 0 or if any index is greater than or equal to
253 /// `self.output_types.len()`.
254 ///
255 /// # Examples
256 /// ```
257 /// use malachite_base::iterators::bit_distributor::{
258 /// BitDistributor, BitDistributorOutputType,
259 /// };
260 ///
261 /// let mut bd = BitDistributor::new(&[BitDistributorOutputType::normal(2); 3]);
262 /// assert_eq!(
263 /// bd.bit_map_as_slice(),
264 /// &[
265 /// 2, 2, 1, 1, 0, 0, 2, 2, 1, 1, 0, 0, 2, 2, 1, 1, 0, 0, 2, 2, 1, 1, 0, 0, 2, 2, 1, 1,
266 /// 0, 0, 2, 2, 1, 1, 0, 0, 2, 2, 1, 1, 0, 0, 2, 2, 1, 1, 0, 0, 2, 2, 1, 1, 0, 0, 2, 2,
267 /// 1, 1, 0, 0, 2, 2, 1, 1
268 /// ][..]
269 /// );
270 ///
271 /// bd.set_max_bits(&[0, 2], 5);
272 /// assert_eq!(
273 /// bd.bit_map_as_slice(),
274 /// &[
275 /// 2, 2, 1, 1, 0, 0, 2, 2, 1, 1, 0, 0, 2, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
276 /// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
277 /// 1, 1, 1, 1, 1, 1, 1, 1
278 /// ][..]
279 /// );
280 /// ```
281 pub fn set_max_bits(&mut self, output_type_indices: &[usize], max_bits: usize) {
282 assert_ne!(max_bits, 0);
283 for &index in output_type_indices {
284 self.output_types[index].max_bits = Some(max_bits);
285 }
286 self.update_bit_map();
287 }
288
289 /// Increments the counter in preparation for a new set of outputs.
290 ///
291 /// If the counter is incremented $2^{64}$ times, it rolls back to 0.
292 ///
293 /// # Worst-case complexity
294 /// Constant time and additional memory.
295 ///
296 /// # Examples
297 /// ```
298 /// use malachite_base::iterators::bit_distributor::{
299 /// BitDistributor, BitDistributorOutputType,
300 /// };
301 ///
302 /// let mut bd = BitDistributor::new(&[BitDistributorOutputType::normal(1)]);
303 /// let mut outputs = Vec::new();
304 /// for _ in 0..20 {
305 /// outputs.push(bd.get_output(0));
306 /// bd.increment_counter();
307 /// }
308 /// assert_eq!(
309 /// outputs,
310 /// &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
311 /// );
312 /// ```
313 pub fn increment_counter(&mut self) {
314 for b in &mut self.counter {
315 b.not_assign();
316 if *b {
317 break;
318 }
319 }
320 }
321
322 /// Gets the output at a specified index.
323 ///
324 /// # Worst-case complexity
325 /// Constant time and additional memory.
326 ///
327 /// # Panics
328 /// Panics if `index` is greater than or equal to `self.output_types.len()`.
329 ///
330 /// # Examples
331 /// ```
332 /// use itertools::Itertools;
333 /// use malachite_base::iterators::bit_distributor::{
334 /// BitDistributor, BitDistributorOutputType,
335 /// };
336 ///
337 /// let mut bd = BitDistributor::new(&[BitDistributorOutputType::normal(1); 2]);
338 /// let mut outputs = Vec::new();
339 /// for _ in 0..10 {
340 /// outputs.push((0..2).map(|i| bd.get_output(i)).collect_vec());
341 /// bd.increment_counter();
342 /// }
343 /// let expected_outputs: &[&[usize]] = &[
344 /// &[0, 0],
345 /// &[0, 1],
346 /// &[1, 0],
347 /// &[1, 1],
348 /// &[0, 2],
349 /// &[0, 3],
350 /// &[1, 2],
351 /// &[1, 3],
352 /// &[2, 0],
353 /// &[2, 1],
354 /// ];
355 /// assert_eq!(outputs, expected_outputs);
356 /// ```
357 pub fn get_output(&self, index: usize) -> usize {
358 assert!(index < self.output_types.len());
359 usize::from_bits_asc(
360 self.bit_map
361 .iter()
362 .zip(self.counter.iter())
363 .filter_map(|(&m, &c)| if m == index { Some(c) } else { None }),
364 )
365 }
366}