pixelator_core/common/
utils.rs

1use std::cmp::Ordering;
2use std::collections::VecDeque;
3use std::fmt;
4
5use itertools::Itertools;
6use rand::prelude::*;
7
8use crate::common::types::NodeIdx;
9
10/// Returns the mode (most common value) of a mutable slice of usize, breaking ties randomly.
11///
12/// The function sorts the input slice in-place, then finds the value that appears most frequently.
13/// If there are multiple values with the same highest frequency, one is chosen at random.
14/// This is efficient for small slices and avoids heap allocation for counting.
15///
16pub fn mode_via_sort(numbers: &mut [usize]) -> usize {
17    if numbers.is_empty() {
18        panic!("No numbers to choose from, `numbers` is empty");
19    }
20
21    numbers.sort_unstable();
22
23    let mut rng = rand::rng();
24    let ((_, best_n), _) = numbers.iter().dedup_with_count().fold(
25        ((0, 0), 0),
26        |((best_count, best_n), ties), (c, &n)| match c.cmp(&best_count) {
27            Ordering::Less => ((best_count, best_n), ties),
28            Ordering::Greater => ((c, n), 1),
29            Ordering::Equal => {
30                let ties = ties + 1;
31                if rng.random_bool(1. / ties as f64) {
32                    ((c, n), ties)
33                } else {
34                    ((best_count, best_n), ties)
35                }
36            }
37        },
38    );
39    best_n
40}
41
42pub struct NodeQueue {
43    queue: VecDeque<NodeIdx>,
44    present: Vec<bool>,
45}
46
47impl fmt::Debug for NodeQueue {
48    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49        if self.queue.len() > 20 {
50            write!(
51                f,
52                "NodeQueue(len: {}, front: {:?}, ... , back: {:?})",
53                self.queue.len(),
54                self.queue.iter().take(3).collect::<Vec<&NodeIdx>>(),
55                self.queue.iter().rev().take(3).collect::<Vec<&NodeIdx>>()
56            )
57        } else {
58            write!(
59                f,
60                "NodeQueue(len: {}, elements: {:?})",
61                self.queue.len(),
62                self.queue
63            )
64        }
65    }
66}
67/// The node queue can be used to iterate nodes in order. It has a number of specific requirements:
68/// - It assumes all nodes are consecutive integers starting from 0
69/// - It assumes all nodes are present in the queue at the time of initialization.
70/// - No new nodes will show up during processing, i.e. all indexes 0..n must be known at
71///   initialization.
72impl NodeQueue {
73    pub fn from(vec: Vec<NodeIdx>) -> Self {
74        let present = vec![true; vec.len()];
75
76        NodeQueue {
77            queue: VecDeque::from(vec),
78            present,
79        }
80    }
81
82    pub fn pop_front(&mut self) -> Option<NodeIdx> {
83        let node = self.queue.pop_front();
84        if let Some(n) = node {
85            self.present[n] = false;
86        }
87        node
88    }
89
90    pub fn push_back(&mut self, node: NodeIdx) {
91        if !self.present[node] {
92            self.queue.push_back(node);
93            self.present[node] = true;
94        }
95    }
96
97    pub fn is_empty(&self) -> bool {
98        self.queue.is_empty()
99    }
100
101    pub fn len(&self) -> usize {
102        self.queue.len()
103    }
104
105    pub fn contains(&self, node: NodeIdx) -> bool {
106        self.present[node]
107    }
108}
109
110#[cfg(test)]
111mod tests {
112    use super::*;
113    use rustc_hash::FxHashSet as HashSet;
114
115    #[test]
116    fn test_mode_via_sort_basic() {
117        let mut data = vec![1, 2, 2, 3];
118        let mode = mode_via_sort(&mut data);
119        assert_eq!(mode, 2);
120    }
121
122    #[test]
123    fn test_mode_via_sort_tie() {
124        // Both 1 and 2 appear twice, so either is valid
125        let mut data = vec![1, 2, 1, 2, 3];
126        let mode = mode_via_sort(&mut data);
127        assert!(mode == 1 || mode == 2);
128    }
129
130    #[test]
131    fn test_mode_via_sort_single_element() {
132        let mut data = vec![42];
133        let mode = mode_via_sort(&mut data);
134        assert_eq!(mode, 42);
135    }
136
137    #[test]
138    #[should_panic]
139    fn test_mode_via_sort_empty_panics() {
140        let mut data: Vec<usize> = vec![];
141        let _ = mode_via_sort(&mut data);
142    }
143
144    #[test]
145    fn test_nodequeue_basic_operations() {
146        let mut nq = NodeQueue::from(vec![0, 1, 2]);
147        assert_eq!(nq.len(), 3);
148        assert!(!nq.is_empty());
149        assert!(nq.contains(1));
150
151        assert_eq!(nq.pop_front(), Some(0));
152        assert!(!nq.contains(0));
153        assert_eq!(nq.len(), 2);
154
155        // Already present in queue: should not duplicate.
156        nq.push_back(2);
157        assert_eq!(nq.len(), 2);
158
159        // Re-enqueueing a popped node should append it.
160        nq.push_back(0);
161        assert_eq!(nq.len(), 3);
162        assert_eq!(nq.pop_front(), Some(1));
163        assert_eq!(nq.pop_front(), Some(2));
164        assert_eq!(nq.pop_front(), Some(0));
165        assert!(nq.is_empty());
166    }
167
168    #[test]
169    fn test_nodequeue_uniqueness() {
170        let mut nq = NodeQueue::from(vec![0, 1]);
171        nq.push_back(1); // should not add again
172        nq.push_back(0); // should not add again
173        assert_eq!(nq.len(), 2);
174        let mut seen = HashSet::default();
175        while let Some(n) = nq.pop_front() {
176            assert!(!seen.contains(&n));
177            seen.insert(n);
178        }
179        assert!(nq.is_empty());
180    }
181
182    #[test]
183    fn test_nodequeue_from_preserves_order() {
184        let mut nq = NodeQueue::from(vec![0, 1, 2, 3]);
185        assert_eq!(nq.pop_front(), Some(0));
186        assert_eq!(nq.pop_front(), Some(1));
187        assert_eq!(nq.pop_front(), Some(2));
188        assert_eq!(nq.pop_front(), Some(3));
189        assert_eq!(nq.pop_front(), None);
190    }
191
192    #[test]
193    fn test_nodequeue_reenqueue_after_pop() {
194        let mut nq = NodeQueue::from(vec![0, 1]);
195        assert_eq!(nq.pop_front(), Some(0));
196        assert!(!nq.contains(0));
197
198        nq.push_back(0);
199        assert!(nq.contains(0));
200        assert_eq!(nq.pop_front(), Some(1));
201        assert_eq!(nq.pop_front(), Some(0));
202    }
203
204    #[test]
205    #[should_panic]
206    fn test_nodequeue_contains_out_of_range_panics() {
207        let nq = NodeQueue::from(vec![0, 1, 2]);
208        let _ = nq.contains(3);
209    }
210
211    #[test]
212    fn test_nodequeue_empty_invariants() {
213        let mut nq = NodeQueue::from(vec![]);
214        assert!(nq.is_empty());
215        assert_eq!(nq.len(), 0);
216        assert_eq!(nq.pop_front(), None);
217    }
218
219    #[test]
220    #[should_panic]
221    fn test_nodequeue_push_back_out_of_range_panics() {
222        let mut nq = NodeQueue::from(vec![0, 1, 2]);
223        nq.push_back(3);
224    }
225
226    #[test]
227    fn test_nodequeue_all_nodes_present_at_initialization() {
228        let nq = NodeQueue::from(vec![0, 1, 2, 3]);
229        assert!(nq.contains(0));
230        assert!(nq.contains(1));
231        assert!(nq.contains(2));
232        assert!(nq.contains(3));
233    }
234}