use crate::kd_tree::ConstructionError;
use crate::traits::leaf_strategy::ConstructibleLeafStrategy;
use crate::{Axis, Content, KdTree, StemStrategy};
use super::construction::{validate_auto_generated_items, DefaultConstruction, SerialConstruction};
#[cfg(feature = "multi-threaded")]
use super::construction::{ParallelConstruction, DEFAULT_PARALLEL_CONSTRUCTION_THRESHOLD};
#[must_use = "a construction builder does nothing until a build method is called"]
pub struct KdTreeBuilder<A, T, SS, LS, const K: usize, const B: usize, P = DefaultConstruction> {
#[cfg_attr(not(feature = "multi-threaded"), allow(dead_code))]
pub(in crate::kd_tree) policy: P,
pub(in crate::kd_tree) _phantom: std::marker::PhantomData<(A, T, SS, LS)>,
}
impl<A, T, SS, LS, const K: usize, const B: usize, P> KdTreeBuilder<A, T, SS, LS, K, B, P> {
pub fn with_serial_construction(self) -> KdTreeBuilder<A, T, SS, LS, K, B, SerialConstruction> {
KdTreeBuilder {
policy: SerialConstruction,
_phantom: std::marker::PhantomData,
}
}
#[cfg(feature = "multi-threaded")]
pub fn with_parallel_construction(
self,
) -> KdTreeBuilder<A, T, SS, LS, K, B, ParallelConstruction> {
KdTreeBuilder {
policy: ParallelConstruction::with_threshold(1),
_phantom: std::marker::PhantomData,
}
}
#[cfg(feature = "multi-threaded")]
pub fn with_parallel_construction_threshold(
self,
item_count: usize,
) -> KdTreeBuilder<A, T, SS, LS, K, B, ParallelConstruction> {
KdTreeBuilder {
policy: ParallelConstruction::with_threshold(item_count),
_phantom: std::marker::PhantomData,
}
}
}
impl<A, T, SS, LS, const K: usize, const B: usize> Default
for KdTreeBuilder<A, T, SS, LS, K, B, SerialConstruction>
{
fn default() -> Self {
Self {
policy: SerialConstruction,
_phantom: std::marker::PhantomData,
}
}
}
impl<A, T, SS, LS, const K: usize, const B: usize>
KdTreeBuilder<A, T, SS, LS, K, B, SerialConstruction>
where
A: Axis<Coord = A>,
T: Content,
SS: StemStrategy,
LS: ConstructibleLeafStrategy<A, T, SS, K, B>,
{
pub fn build_from_slice(
self,
source: &[[A; K]],
) -> Result<KdTree<A, T, SS, LS, K, B>, ConstructionError>
where
T: TryFrom<usize>,
{
validate_auto_generated_items::<T>(source.len())?;
KdTree::new_from_source_with(
source,
|point: &[A; K], dim| point[dim],
|src_idx: usize, _point: &[A; K]| {
T::try_from(src_idx).map_err(|_| {
ConstructionError::AutoGeneratedItemIndexOverflow {
item_count: source.len(),
item_type: core::any::type_name::<T>(),
}
})
},
)
}
pub fn build_from_source<X, FA, FI>(
self,
source: &[X],
axis_at: FA,
item_at: FI,
) -> Result<KdTree<A, T, SS, LS, K, B>, ConstructionError>
where
FA: Fn(&X, usize) -> A,
FI: Fn(usize, &X) -> T,
{
KdTree::new_from_source_with(source, axis_at, |src_idx, src| Ok(item_at(src_idx, src)))
}
pub fn build_from_entries(
self,
source: &[(T, [A; K])],
) -> Result<KdTree<A, T, SS, LS, K, B>, ConstructionError> {
self.build_from_source(
source,
|entry: &(T, [A; K]), dim| entry.1[dim],
|_src_idx, entry: &(T, [A; K])| entry.0,
)
}
}
impl<A, SS, LS, const K: usize, const B: usize>
KdTreeBuilder<A, (), SS, LS, K, B, SerialConstruction>
where
A: Axis<Coord = A>,
SS: StemStrategy,
LS: ConstructibleLeafStrategy<A, (), SS, K, B>,
{
pub fn build_from_slice_no_items(
self,
source: &[[A; K]],
) -> Result<KdTree<A, (), SS, LS, K, B>, ConstructionError> {
self.build_from_source(
source,
|point: &[A; K], dim| point[dim],
|_src_idx, _point| (),
)
}
}
#[cfg(feature = "multi-threaded")]
impl<A, T, SS, LS, const K: usize, const B: usize> Default
for KdTreeBuilder<A, T, SS, LS, K, B, ParallelConstruction>
{
fn default() -> Self {
Self {
policy: ParallelConstruction::with_threshold(DEFAULT_PARALLEL_CONSTRUCTION_THRESHOLD),
_phantom: std::marker::PhantomData,
}
}
}
#[cfg(feature = "multi-threaded")]
impl<A, T, SS, LS, const K: usize, const B: usize>
KdTreeBuilder<A, T, SS, LS, K, B, ParallelConstruction>
where
A: Axis<Coord = A> + Send + Sync,
T: Content,
SS: StemStrategy,
LS: ConstructibleLeafStrategy<A, T, SS, K, B>,
{
pub fn build_from_slice(
self,
source: &[[A; K]],
) -> Result<KdTree<A, T, SS, LS, K, B>, ConstructionError>
where
T: TryFrom<usize>,
{
validate_auto_generated_items::<T>(source.len())?;
KdTree::new_from_source_with_parallel_policy(
source,
|point: &[A; K], dim| point[dim],
|src_idx: usize, _point: &[A; K]| {
T::try_from(src_idx).map_err(|_| {
ConstructionError::AutoGeneratedItemIndexOverflow {
item_count: source.len(),
item_type: core::any::type_name::<T>(),
}
})
},
self.policy,
)
}
pub fn build_from_source<X, FA, FI>(
self,
source: &[X],
axis_at: FA,
item_at: FI,
) -> Result<KdTree<A, T, SS, LS, K, B>, ConstructionError>
where
X: Sync,
FA: Fn(&X, usize) -> A + Sync,
FI: Fn(usize, &X) -> T,
{
KdTree::new_from_source_with_parallel_policy(
source,
axis_at,
|src_idx, src| Ok(item_at(src_idx, src)),
self.policy,
)
}
pub fn build_from_entries(
self,
source: &[(T, [A; K])],
) -> Result<KdTree<A, T, SS, LS, K, B>, ConstructionError>
where
T: Sync,
{
self.build_from_source(
source,
|entry: &(T, [A; K]), dim| entry.1[dim],
|_src_idx, entry: &(T, [A; K])| entry.0,
)
}
}
#[cfg(feature = "multi-threaded")]
impl<A, SS, LS, const K: usize, const B: usize>
KdTreeBuilder<A, (), SS, LS, K, B, ParallelConstruction>
where
A: Axis<Coord = A> + Send + Sync,
SS: StemStrategy,
LS: ConstructibleLeafStrategy<A, (), SS, K, B>,
{
pub fn build_from_slice_no_items(
self,
source: &[[A; K]],
) -> Result<KdTree<A, (), SS, LS, K, B>, ConstructionError> {
self.build_from_source(
source,
|point: &[A; K], dim| point[dim],
|_src_idx, _point| (),
)
}
}