use core::marker::PhantomData;
use std::path::Path;
use distances::Number;
use crate::{Cluster, Dataset, Instance, PartitionCriterion};
#[derive(Debug)]
pub struct Tree<I: Instance, U: Number, D: Dataset<I, U>, C: Cluster<U>> {
pub(crate) data: D,
pub(crate) root: C,
pub(crate) depth: usize,
_i: PhantomData<I>,
_u: PhantomData<U>,
}
impl<I: Instance, U: Number, D: Dataset<I, U>, C: Cluster<U>> Tree<I, U, D, C> {
pub fn new(data: D, seed: Option<u64>) -> Self {
let root = C::new_root(&data, seed);
let depth = root.max_leaf_depth();
Self {
data,
root,
depth,
_i: PhantomData,
_u: PhantomData,
}
}
#[must_use]
pub fn partition<P: PartitionCriterion<U>>(mut self, criteria: &P, seed: Option<u64>) -> Self {
self.root = self.root.partition(&mut self.data, criteria, seed);
self.depth = self.root.max_leaf_depth();
self
}
pub fn get_cluster(&self, offset: usize, cardinality: usize) -> Option<&C> {
self.root.descend_to(offset, cardinality)
}
pub const fn data(&self) -> &D {
&self.data
}
pub fn cardinality(&self) -> usize {
self.root.cardinality()
}
pub fn radius(&self) -> U {
self.root.radius()
}
pub const fn root(&self) -> &C {
&self.root
}
pub const fn depth(&self) -> usize {
self.depth
}
pub fn save(&self, path: &Path) -> Result<(), String> {
if !path.exists() {
return Err("Given path does not exist".to_string());
}
let dataset_path = path.join("dataset");
self.data.save(&dataset_path)?;
let cluster_path = path.join("clusters");
self.root.save(&cluster_path)?;
Ok(())
}
pub fn load(path: &Path, metric: fn(&I, &I) -> U, is_expensive: bool) -> Result<Self, String> {
if !path.exists() {
return Err("Given path does not exist".to_string());
}
let cluster_path = path.join("clusters");
let dataset_path = path.join("dataset");
if !(cluster_path.exists() && dataset_path.exists()) {
return Err("Saved tree is malformed".to_string());
}
let data = D::load(&dataset_path, metric, is_expensive)?;
let root = C::load(&cluster_path)?;
Ok(Self {
data,
depth: root.max_leaf_depth(),
root,
_i: PhantomData,
_u: PhantomData,
})
}
}