use std::cmp::Ordering;
use crate::common::fixed_length_priority_queue::FixedLengthPriorityQueue;
use crate::common::types::PointOffsetType;
use rand::{Rng, RngExt};
use serde::{Deserialize, Serialize};
#[derive(Deserialize, Serialize, Clone, Debug, PartialEq)]
pub struct EntryPoint {
pub point_id: PointOffsetType,
pub level: usize,
}
impl Eq for EntryPoint {}
impl PartialOrd for EntryPoint {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for EntryPoint {
fn cmp(&self, other: &Self) -> Ordering {
self.level.cmp(&other.level)
}
}
#[derive(Deserialize, Serialize, Clone, Debug)]
pub struct EntryPoints {
entry_points: Vec<EntryPoint>,
extra_entry_points: FixedLengthPriorityQueue<EntryPoint>,
}
impl EntryPoints {
pub fn new(extra_entry_points: usize) -> Self {
EntryPoints {
entry_points: vec![],
extra_entry_points: FixedLengthPriorityQueue::new(extra_entry_points),
}
}
pub fn merge_from_other(&mut self, mut other: EntryPoints) {
self.entry_points.append(&mut other.entry_points);
}
pub fn new_point<F>(
&mut self,
new_point: PointOffsetType,
level: usize,
checker: F,
) -> Option<EntryPoint>
where
F: Fn(PointOffsetType) -> bool,
{
for i in 0..self.entry_points.len() {
let candidate = &self.entry_points[i];
if !checker(candidate.point_id) {
continue; }
return if candidate.level >= level {
self.extra_entry_points.push(EntryPoint {
point_id: new_point,
level,
});
Some(candidate.clone())
} else {
let entry = self.entry_points[i].clone();
self.entry_points[i] = EntryPoint {
point_id: new_point,
level,
};
self.extra_entry_points.push(entry.clone());
Some(entry)
};
}
let new_entry = EntryPoint {
point_id: new_point,
level,
};
self.entry_points.push(new_entry);
None
}
pub fn get_entry_point<F>(&self, checker: F) -> Option<EntryPoint>
where
F: Fn(PointOffsetType) -> bool,
{
self.entry_points
.iter()
.find(|entry| checker(entry.point_id))
.cloned()
.or_else(|| {
self.extra_entry_points
.iter_unsorted()
.filter(|entry| checker(entry.point_id))
.cloned()
.max_by_key(|ep| ep.level)
})
}
pub fn get_random_entry_point<F, R: Rng + ?Sized>(
&self,
rnd: &mut R,
checker: F,
) -> Option<EntryPoint>
where
F: Fn(PointOffsetType) -> bool,
{
let filtered_entry_points: Vec<_> = self
.entry_points
.iter()
.filter(|entry| checker(entry.point_id))
.cloned()
.collect();
if !filtered_entry_points.is_empty() {
let random_index = rnd.random_range(0..filtered_entry_points.len());
return Some(filtered_entry_points[random_index].clone());
}
let filtered_extra_entry_points: Vec<_> = self
.extra_entry_points
.iter_unsorted()
.filter(|entry| checker(entry.point_id))
.cloned()
.collect();
if !filtered_extra_entry_points.is_empty() {
let random_index = rnd.random_range(0..filtered_extra_entry_points.len());
return Some(filtered_extra_entry_points[random_index].clone());
}
None
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_entry_points() {
let mut points = EntryPoints::new(10);
let mut rnd = rand::rng();
for i in 0..1000 {
let level = rnd.random_range(0..10000);
points.new_point(i, level, |_x| true);
}
assert_eq!(points.entry_points.len(), 1);
assert_eq!(points.extra_entry_points.len(), 10);
assert!(points.entry_points[0].level > 1);
for i in 1000..2000 {
let level = rnd.random_range(0..10000);
points.new_point(i, level, |x| x % 5 == i % 5);
}
assert_eq!(points.entry_points.len(), 5);
assert_eq!(points.extra_entry_points.len(), 10);
}
}