#[cfg(feature = "gxhash")]
type HashMap<K, V> = std::collections::HashMap<K, V, gxhash::GxBuildHasher>;
#[cfg(feature = "ahash")]
use ahash::HashMap;
#[cfg(feature = "rustc-hash")]
use rustc_hash::FxHashMap as HashMap;
#[cfg(all(
not(feature = "ahash"),
not(feature = "gxhash"),
not(feature = "rustc-hash")
))]
use std::collections::HashMap;
use std::hash::Hash;
#[derive(Debug, Default)]
pub struct SearchTopScores<'a, K: Hash + Ord> {
pub(crate) top: HashMap<&'a K, usize>,
pub(crate) bottom: Option<(&'a K, usize)>,
pub(crate) capacity: usize,
}
impl<'a, K: Hash + Ord> crate::simple::internal::SearchTopScores<'a, K> {
#[allow(clippy::default_trait_access)]
pub(crate) fn with_capacity(capacity: usize) -> Self {
Self {
top: HashMap::with_capacity_and_hasher(
capacity,
std::default::Default::default()
),
bottom: None,
capacity,
}
}
pub(crate) fn insert(&mut self, key: &'a K, score: usize) {
let below_threshold = self.top.len() >= self.capacity && self
.bottom
.as_ref()
.is_some_and(|(_, bottom_score)| score <= *bottom_score);
if !below_threshold {
if self.top.len() >= self.capacity {
self.remove_bottom();
}
self.top.insert(key, score);
let is_new_bottom = self
.bottom
.as_ref()
.is_none_or(|(_, bottom_score)| score < *bottom_score);
if is_new_bottom {
self.bottom = Some((key, score));
}
}
}
fn remove_bottom(&mut self) {
if let Some((bottom_key, _)) = &self.bottom {
self.top.remove(bottom_key);
}
self.bottom = self
.top
.iter()
.min_by(|(_, a_score), (_, b_score)| a_score.cmp(b_score))
.map(|(key, score)| (*key, *score));
}
pub(crate) fn results(self) -> impl Iterator<Item = (&'a K, usize)> {
let mut vec: Vec<_> = self.top.into_iter().collect();
vec.sort_unstable_by(|(a_key, a_score), (b_key, b_score)| {
b_score.cmp(a_score).then_with(|| a_key.cmp(b_key))
});
vec.into_iter()
}
}