use ndarray::prelude::*;
use strsim::generic_levenshtein;
pub fn nn_bound(error: f64, nlabels: usize) -> f64 {
let nl = nlabels as f64;
let rg = (nl-1.)/nl;
match error {
e if e < rg => rg * (1. - (1. - nl/(nl-1.)*error).sqrt()),
_ => rg,
}
}
pub fn euclidean_distance(v1: &ArrayView1<f64>, v2: &ArrayView1<f64>) -> f64 {
v1.iter()
.zip(v2.iter())
.map(|(x,y)| (x - y).powi(2))
.sum::<f64>()
.sqrt()
}
pub fn levenshtein_distance(v1: &ArrayView1<f64>, v2: &ArrayView1<f64>) -> f64 {
generic_levenshtein(v1, v2) as f64
}
#[derive(Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum KNNStrategy {
Ln,
Log10,
#[serde(skip)]
NN,
#[serde(skip)]
FixedK(usize),
#[serde(skip)]
Custom(Box<dyn Fn(usize) -> usize>),
}
pub fn knn_strategy(strategy: KNNStrategy) -> Box<dyn Fn(usize) -> usize> {
match strategy {
KNNStrategy::NN => Box::new(move |_| 1),
KNNStrategy::FixedK(k) => Box::new(move |_| k),
KNNStrategy::Ln => Box::new(move |n|
next_odd(if n != 0 {
(n as f64).ln().ceil() as usize
} else { 1 })),
KNNStrategy::Log10 => Box::new(move |n|
next_odd(if n != 0 {
(n as f64).log10().ceil() as usize
} else { 1 })),
KNNStrategy::Custom(custom) => custom,
}
}
fn next_odd(n: usize) -> usize {
match n % 2 {
0 => n + 1,
_ => n,
}
}