Skip to main content

malachite_base/iterators/
bit_distributor.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::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 const fn normal(weight: usize) -> Self {
37        assert!(weight != 0);
38        Self {
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() -> Self {
52        Self {
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]) -> Self {
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        Self {
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]) -> Self {
143        let mut distributor = Self::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    #[inline]
178    pub fn bit_map_as_slice(&self) -> &[usize] {
179        self.bit_map.as_ref()
180    }
181
182    // The two bookkeeping `Vec`s stay separate allocations: both shrink via `remove` as output
183    // types reach their bit limits, and slices of a shared allocation cannot do that.
184    #[cfg_attr(dylint_lib = "malachite_lints", allow(adjacent_vec_allocations))]
185    fn update_bit_map(&mut self) {
186        let (mut normal_output_type_indices, mut tiny_output_type_indices): (
187            Vec<usize>,
188            Vec<usize>,
189        ) = (0..self.output_types.len()).partition(|&i| self.output_types[i].weight != 0);
190        let mut normal_output_types_bits_used = vec![0; normal_output_type_indices.len()];
191        let mut tiny_output_types_bits_used = vec![0; tiny_output_type_indices.len()];
192        let mut ni = normal_output_type_indices.len() - 1;
193        let mut ti = tiny_output_type_indices.len().saturating_sub(1);
194        let mut weight_counter = self.output_types[normal_output_type_indices[ni]].weight;
195        for i in 0..COUNTER_WIDTH {
196            let use_normal_output_type = !normal_output_type_indices.is_empty()
197                && (tiny_output_type_indices.is_empty() || !usize::is_power_of_two(i + 1));
198            if use_normal_output_type {
199                self.bit_map[i] = normal_output_type_indices[ni];
200                let output_type = self.output_types[normal_output_type_indices[ni]];
201                normal_output_types_bits_used[ni] += 1;
202                weight_counter -= 1;
203                if output_type.max_bits == Some(normal_output_types_bits_used[ni]) {
204                    normal_output_type_indices.remove(ni);
205                    normal_output_types_bits_used.remove(ni);
206                    if normal_output_type_indices.is_empty() {
207                        continue;
208                    }
209                    weight_counter = 0;
210                }
211                if weight_counter == 0 {
212                    if ni == 0 {
213                        ni = normal_output_type_indices.len() - 1;
214                    } else {
215                        ni -= 1;
216                    }
217                    weight_counter = self.output_types[normal_output_type_indices[ni]].weight;
218                }
219            } else {
220                if tiny_output_type_indices.is_empty() {
221                    self.bit_map[i] = usize::MAX;
222                    continue;
223                }
224                self.bit_map[i] = tiny_output_type_indices[ti];
225                let output_type = self.output_types[tiny_output_type_indices[ti]];
226                tiny_output_types_bits_used[ti] += 1;
227                if output_type.max_bits == Some(tiny_output_types_bits_used[ti]) {
228                    tiny_output_type_indices.remove(ti);
229                    tiny_output_types_bits_used.remove(ti);
230                    if tiny_output_type_indices.is_empty() {
231                        continue;
232                    }
233                }
234                if ti == 0 {
235                    ti = tiny_output_type_indices.len() - 1;
236                } else {
237                    ti -= 1;
238                }
239            }
240        }
241    }
242
243    /// Sets the maximum bits for several outputs.
244    ///
245    /// Given slice of output indices, sets the maximum bits for each of the outputs and rebuilds
246    /// the bit map.
247    ///
248    /// # Worst-case complexity
249    /// $T(n) = O(n)$
250    ///
251    /// $M(n) = O(1)$
252    ///
253    /// where $T$ is time, $M$ is additional memory, and $n$ is `output_type_indices.len()`.
254    ///
255    /// # Panics
256    /// Panics if `max_bits` is 0 or if any index is greater than or equal to
257    /// `self.output_types.len()`.
258    ///
259    /// # Examples
260    /// ```
261    /// use malachite_base::iterators::bit_distributor::{
262    ///     BitDistributor, BitDistributorOutputType,
263    /// };
264    ///
265    /// let mut bd = BitDistributor::new(&[BitDistributorOutputType::normal(2); 3]);
266    /// assert_eq!(
267    ///     bd.bit_map_as_slice(),
268    ///     &[
269    ///         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,
270    ///         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,
271    ///         1, 1, 0, 0, 2, 2, 1, 1
272    ///     ][..]
273    /// );
274    ///
275    /// bd.set_max_bits(&[0, 2], 5);
276    /// assert_eq!(
277    ///     bd.bit_map_as_slice(),
278    ///     &[
279    ///         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,
280    ///         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,
281    ///         1, 1, 1, 1, 1, 1, 1, 1
282    ///     ][..]
283    /// );
284    /// ```
285    pub fn set_max_bits(&mut self, output_type_indices: &[usize], max_bits: usize) {
286        assert_ne!(max_bits, 0);
287        for &index in output_type_indices {
288            self.output_types[index].max_bits = Some(max_bits);
289        }
290        self.update_bit_map();
291    }
292
293    /// Increments the counter in preparation for a new set of outputs.
294    ///
295    /// If the counter is incremented $2^{64}$ times, it rolls back to 0.
296    ///
297    /// # Worst-case complexity
298    /// Constant time and additional memory.
299    ///
300    /// # Examples
301    /// ```
302    /// use malachite_base::iterators::bit_distributor::{
303    ///     BitDistributor, BitDistributorOutputType,
304    /// };
305    ///
306    /// let mut bd = BitDistributor::new(&[BitDistributorOutputType::normal(1)]);
307    /// let mut outputs = Vec::new();
308    /// for _ in 0..20 {
309    ///     outputs.push(bd.get_output(0));
310    ///     bd.increment_counter();
311    /// }
312    /// assert_eq!(
313    ///     outputs,
314    ///     &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
315    /// );
316    /// ```
317    pub fn increment_counter(&mut self) {
318        for b in &mut self.counter {
319            b.not_assign();
320            if *b {
321                break;
322            }
323        }
324    }
325
326    /// Gets the output at a specified index.
327    ///
328    /// # Worst-case complexity
329    /// Constant time and additional memory.
330    ///
331    /// # Panics
332    /// Panics if `index` is greater than or equal to `self.output_types.len()`.
333    ///
334    /// # Examples
335    /// ```
336    /// use itertools::Itertools;
337    /// use malachite_base::iterators::bit_distributor::{
338    ///     BitDistributor, BitDistributorOutputType,
339    /// };
340    ///
341    /// let mut bd = BitDistributor::new(&[BitDistributorOutputType::normal(1); 2]);
342    /// let mut outputs = Vec::new();
343    /// for _ in 0..10 {
344    ///     outputs.push((0..2).map(|i| bd.get_output(i)).collect_vec());
345    ///     bd.increment_counter();
346    /// }
347    /// let expected_outputs: &[&[usize]] = &[
348    ///     &[0, 0],
349    ///     &[0, 1],
350    ///     &[1, 0],
351    ///     &[1, 1],
352    ///     &[0, 2],
353    ///     &[0, 3],
354    ///     &[1, 2],
355    ///     &[1, 3],
356    ///     &[2, 0],
357    ///     &[2, 1],
358    /// ];
359    /// assert_eq!(outputs, expected_outputs);
360    /// ```
361    pub fn get_output(&self, index: usize) -> usize {
362        assert!(index < self.output_types.len());
363        usize::from_bits_asc(
364            self.bit_map
365                .iter()
366                .zip(self.counter.iter())
367                .filter_map(|(&m, &c)| if m == index { Some(c) } else { None }),
368        )
369    }
370}