use std::cmp::Ordering;
use std::hint::select_unpredictable;
use std::ops::Add;
use std::marker::Sized;
use num_traits::Bounded;
#[cfg(test)]
mod test;
mod debug;
#[doc(hidden)]
pub struct Owned<T>(T);
pub trait MetricSpace<UserImplementationType = ()> {
type UserData;
type Distance: Copy + PartialOrd + Bounded + Add<Output = Self::Distance>;
fn distance(&self, other: &Self, user_data: &Self::UserData) -> Self::Distance;
}
pub trait BestCandidate<Item: MetricSpace<Impl> + Clone, Impl> where Self: Sized {
type Output;
fn consider(&mut self, item: &Item, distance: Item::Distance, candidate_index: usize, user_data: &Item::UserData);
fn distance(&self) -> Item::Distance;
fn result(self, user_data: &Item::UserData) -> Self::Output;
}
impl<Item: MetricSpace<Impl> + Clone, Impl> BestCandidate<Item, Impl> for ReturnByIndex<Item, Impl> {
type Output = (usize, Item::Distance);
#[inline]
fn consider(&mut self, _: &Item, distance: Item::Distance, candidate_index: usize, _: &Item::UserData) {
if distance < self.distance {
self.distance = distance;
self.idx = candidate_index;
}
}
#[inline]
fn distance(&self) -> Item::Distance {
self.distance
}
fn result(self, _: &Item::UserData) -> (usize, Item::Distance) {
(self.idx, self.distance)
}
}
const NO_NODE: u32 = u32::MAX;
struct Node<Item: MetricSpace<Impl> + Clone, Impl> {
near: u32,
far: u32,
vantage_point: Item, radius: Item::Distance, idx: u32, }
pub struct Tree<Item: MetricSpace<Impl> + Clone, Impl=(), Ownership=Owned<()>> {
nodes: Vec<Node<Item, Impl>>,
root: u32,
user_data: Ownership,
}
struct Tmp<Item: MetricSpace<Impl>, Impl> {
distance: Item::Distance,
idx: u32,
}
struct ReturnByIndex<Item: MetricSpace<Impl>, Impl> {
distance: Item::Distance,
idx: usize,
}
impl<Item: MetricSpace<Impl>, Impl> ReturnByIndex<Item, Impl> {
fn new() -> Self {
Self {
distance: <Item::Distance as Bounded>::max_value(),
idx: 0,
}
}
}
impl<Item: MetricSpace<Impl, UserData = ()> + Clone, Impl> Tree<Item, Impl, Owned<()>> {
pub fn new(items: &[Item]) -> Self {
Self::new_with_user_data_owned(items, ())
}
}
impl<U, Impl, Item: MetricSpace<Impl, UserData = U> + Clone> Tree<Item, Impl, Owned<U>> {
#[inline]
pub fn find_nearest(&self, needle: &Item) -> (usize, Item::Distance) {
self.find_nearest_with_user_data(needle, &self.user_data.0)
}
}
impl<Item: MetricSpace<Impl> + Clone, Ownership, Impl> Tree<Item, Impl, Ownership> {
fn sort_indexes_by_distance(vantage_point: &Item, indexes: &mut [Tmp<Item, Impl>], items: &[Item], user_data: &Item::UserData) {
for i in indexes.iter_mut() {
i.distance = vantage_point.distance(&items[i.idx as usize], user_data);
}
indexes.sort_unstable_by(|a, b| if a.distance < b.distance {Ordering::Less} else {Ordering::Greater});
}
fn create_node(indexes: &mut [Tmp<Item, Impl>], nodes: &mut Vec<Node<Item, Impl>>, items: &[Item], user_data: &Item::UserData) -> u32 {
if indexes.is_empty() {
return NO_NODE;
}
if indexes.len() == 1 {
let node_idx = nodes.len();
nodes.push(Node{
near: NO_NODE, far: NO_NODE,
vantage_point: items[indexes[0].idx as usize].clone(),
idx: indexes[0].idx,
radius: <Item::Distance as Bounded>::max_value(),
});
return node_idx as u32;
}
let last = indexes.len()-1;
let ref_idx = indexes[last].idx;
let rest = &mut indexes[..last];
Self::sort_indexes_by_distance(&items[ref_idx as usize], rest, items, user_data);
let half_idx = rest.len()/2;
let (near_indexes, far_indexes) = rest.split_at_mut(half_idx);
let vantage_point = items[ref_idx as usize].clone();
let radius = far_indexes[0].distance;
let node_idx = nodes.len();
nodes.push(Node{
vantage_point,
idx: ref_idx,
radius,
near: NO_NODE,
far: NO_NODE,
});
let near = Self::create_node(near_indexes, nodes, items, user_data);
let far = Self::create_node(far_indexes, nodes, items, user_data);
nodes[node_idx].near = near;
nodes[node_idx].far = far;
node_idx as u32
}
}
impl<Item: MetricSpace<Impl> + Clone, Impl> Tree<Item, Impl, Owned<Item::UserData>> {
pub fn new_with_user_data_owned(items: &[Item], user_data: Item::UserData) -> Self {
let mut nodes = Vec::with_capacity(items.len());
let root = Self::create_root_node(items, &mut nodes, &user_data);
Self {
root,
nodes,
user_data: Owned(user_data),
}
}
}
impl<Item: MetricSpace<Impl> + Clone, Impl> Tree<Item, Impl, ()> {
pub fn new_with_user_data_ref(items: &[Item], user_data: &Item::UserData) -> Self {
let mut nodes = Vec::with_capacity(items.len());
let root = Self::create_root_node(items, &mut nodes, user_data);
Self {
root,
nodes,
user_data: (),
}
}
#[inline]
pub fn find_nearest(&self, needle: &Item, user_data: &Item::UserData) -> (usize, Item::Distance) {
self.find_nearest_with_user_data(needle, user_data)
}
}
impl<Item: MetricSpace<Impl> + Clone, Ownership, Impl> Tree<Item, Impl, Ownership> {
fn create_root_node(items: &[Item], nodes: &mut Vec<Node<Item, Impl>>, user_data: &Item::UserData) -> u32 {
assert!(items.len() < (u32::MAX/2) as usize);
let mut indexes: Vec<_> = (0..items.len() as u32).map(|i| Tmp{
idx: i, distance: <Item::Distance as Bounded>::max_value(),
}).collect();
Self::create_node(&mut indexes[..], nodes, items, user_data)
}
fn search_node<B: BestCandidate<Item, Impl>>(node: &Node<Item, Impl>, nodes: &[Node<Item, Impl>], needle: &Item, best_candidate: &mut B, user_data: &Item::UserData) {
let distance = needle.distance(&node.vantage_point, user_data);
best_candidate.consider(&node.vantage_point, distance, node.idx as usize, user_data);
if select_unpredictable(distance < node.radius, true, false) {
if let Some(near) = nodes.get(node.near as usize) {
Self::search_node(near, nodes, needle, best_candidate, user_data);
}
if let Some(far) = nodes.get(node.far as usize)
&& distance + best_candidate.distance() >= node.radius {
Self::search_node(far, nodes, needle, best_candidate, user_data);
}
} else {
if let Some(far) = nodes.get(node.far as usize) {
Self::search_node(far, nodes, needle, best_candidate, user_data);
}
if let Some(near) = nodes.get(node.near as usize)
&& distance <= node.radius + best_candidate.distance() {
Self::search_node(near, nodes, needle, best_candidate, user_data);
}
}
}
#[inline]
fn find_nearest_with_user_data(&self, needle: &Item, user_data: &Item::UserData) -> (usize, Item::Distance) {
self.find_nearest_custom(needle, user_data, ReturnByIndex::new())
}
#[inline]
pub fn find_nearest_custom<ReturnBy: BestCandidate<Item, Impl>>(&self, needle: &Item, user_data: &Item::UserData, mut best_candidate: ReturnBy) -> ReturnBy::Output {
if let Some(root) = self.nodes.get(self.root as usize) {
Self::search_node(root, &self.nodes, needle, &mut best_candidate, user_data);
}
best_candidate.result(user_data)
}
}