| // Licensed to the Apache Software Foundation (ASF) under one or more | |
| // contributor license agreements. See the NOTICE file distributed with | |
| // this work for additional information regarding copyright ownership. | |
| // The ASF licenses this file to You under the Apache License, Version 2.0 | |
| // (the "License"); you may not use this file except in compliance with | |
| // the License. You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| use std::ops::{Deref, DerefMut}; | |
| use unchecked_index::UncheckedIndex; | |
| pub trait KeyForRadixQueue { | |
| fn rdx(&self) -> usize; | |
| } | |
| /// An implementation of the radix tournament tree | |
| /// with time complexity of sorting all values: O(n + K) | |
| pub struct RadixQueue<T> { | |
| num_keys: usize, | |
| cur_rdx: usize, | |
| values: UncheckedIndex<Vec<T>>, | |
| entries: UncheckedIndex<Vec<usize>>, | |
| node_nexts: UncheckedIndex<Vec<usize>>, | |
| } | |
| #[allow(clippy::len_without_is_empty)] | |
| impl<T: KeyForRadixQueue> RadixQueue<T> { | |
| pub fn new(values: Vec<T>, num_keys: usize) -> Self { | |
| let num_keys = num_keys + 1; // avoid overflow | |
| let num_values = values.len(); | |
| let mut tree = unsafe { | |
| // safety: | |
| // this component is performance critical, use unchecked index | |
| // to avoid boundary checking. | |
| Self { | |
| num_keys, | |
| cur_rdx: 0, | |
| values: unchecked_index::unchecked_index(values), | |
| entries: unchecked_index::unchecked_index(vec![usize::MAX; num_keys]), | |
| node_nexts: unchecked_index::unchecked_index(vec![usize::MAX; num_values]), | |
| } | |
| }; | |
| tree.init_tree(); | |
| tree | |
| } | |
| pub fn values(&self) -> &[T] { | |
| &self.values | |
| } | |
| pub fn values_mut(&mut self) -> &mut [T] { | |
| &mut self.values | |
| } | |
| pub fn len(&self) -> usize { | |
| self.values.len() | |
| } | |
| pub fn peek(&self) -> &T { | |
| &self.values[self.entries.get(self.cur_rdx).cloned().unwrap_or_default()] | |
| } | |
| pub fn peek_mut(&mut self) -> RadixTournamentTreePeekMut<'_, T> { | |
| RadixTournamentTreePeekMut { | |
| tree: self, | |
| dirty: false, | |
| } | |
| } | |
| fn init_tree(&mut self) { | |
| let mut min_rdx = usize::MAX; | |
| for (i, v) in self.values.iter().enumerate() { | |
| let rdx = v.rdx(); | |
| if rdx < self.num_keys { | |
| self.node_nexts[i] = self.entries[rdx]; | |
| self.entries[rdx] = i; | |
| } | |
| min_rdx = min_rdx.min(rdx); | |
| } | |
| self.cur_rdx = min_rdx; | |
| } | |
| fn adjust_tree(&mut self) { | |
| let old_rdx = self.cur_rdx; | |
| if old_rdx < self.num_keys { | |
| let i = self.entries[old_rdx]; | |
| let new_rdx = self.values[i].rdx(); | |
| // move current node to the correct bucket | |
| if new_rdx > old_rdx { | |
| // unlink from old bucket | |
| self.entries[old_rdx] = self.node_nexts[i]; | |
| // link to new bucket | |
| if new_rdx < self.num_keys { | |
| self.node_nexts[i] = self.entries[new_rdx]; | |
| self.entries[new_rdx] = i; | |
| } | |
| // forward cur_rdx if current bucket is exhausted | |
| let mut next_rdx = old_rdx; | |
| while next_rdx < self.num_keys && self.entries[next_rdx] == usize::MAX { | |
| next_rdx += 1; | |
| } | |
| self.cur_rdx = next_rdx; | |
| } | |
| } | |
| } | |
| } | |
| /// A PeekMut structure to the loser tree, used to get smallest value and auto | |
| /// adjusting after dropped. | |
| pub struct RadixTournamentTreePeekMut<'a, T: KeyForRadixQueue> { | |
| tree: &'a mut RadixQueue<T>, | |
| dirty: bool, | |
| } | |
| impl<T: KeyForRadixQueue> Deref for RadixTournamentTreePeekMut<'_, T> { | |
| type Target = T; | |
| fn deref(&self) -> &Self::Target { | |
| self.tree.peek() | |
| } | |
| } | |
| impl<T: KeyForRadixQueue> DerefMut for RadixTournamentTreePeekMut<'_, T> { | |
| fn deref_mut(&mut self) -> &mut Self::Target { | |
| self.dirty = true; | |
| &mut self.tree.values[self | |
| .tree | |
| .entries | |
| .get(self.tree.cur_rdx) | |
| .cloned() | |
| .unwrap_or_default()] | |
| } | |
| } | |
| impl<T: KeyForRadixQueue> Drop for RadixTournamentTreePeekMut<'_, T> { | |
| fn drop(&mut self) { | |
| if self.dirty { | |
| self.tree.adjust_tree(); | |
| } | |
| } | |
| } | |
| #[cfg(test)] | |
| mod test { | |
| use itertools::Itertools; | |
| use rand::Rng; | |
| use crate::algorithm::rdx_queue::{KeyForRadixQueue, RadixQueue}; | |
| #[test] | |
| fn fuzztest() { | |
| for _ in 0..10 { | |
| let num_nodes = rand::rng().random_range(1..=999); | |
| let mut nodes = vec![]; | |
| for _ in 0..num_nodes { | |
| let node_len = rand::rng().random_range(1..=999); | |
| let mut node = vec![]; | |
| for _ in 0..node_len { | |
| node.push(rand::rng().random_range(1000..=9999)); | |
| } | |
| nodes.push(node); | |
| } | |
| // expected | |
| let expected = nodes | |
| .clone() | |
| .into_iter() | |
| .flatten() | |
| .sorted_unstable() | |
| .collect_vec(); | |
| // actual | |
| struct Cursor { | |
| row_idx: usize, | |
| values: Vec<u64>, | |
| } | |
| impl KeyForRadixQueue for Cursor { | |
| fn rdx(&self) -> usize { | |
| self.values.get(self.row_idx).cloned().unwrap_or(u64::MAX) as usize | |
| } | |
| } | |
| let mut loser_tree = RadixQueue::new( | |
| nodes | |
| .into_iter() | |
| .map(|node| Cursor { | |
| row_idx: 0, | |
| values: node.into_iter().sorted_unstable().collect_vec(), | |
| }) | |
| .collect_vec(), | |
| 10000, | |
| ); | |
| let mut actual = vec![]; | |
| loop { | |
| let mut min = loser_tree.peek_mut(); | |
| if let Some(v) = min.values.get(min.row_idx) { | |
| actual.push(*v); | |
| min.row_idx += 1; | |
| } else { | |
| break; | |
| } | |
| } | |
| for cursor in loser_tree.values() { | |
| assert_eq!(cursor.row_idx, cursor.values.len()); | |
| } | |
| assert_eq!(actual, expected); | |
| } | |
| } | |
| } |