use scirs2_core::ndarray::{Array2, ArrayView1};
use scirs2_core::numeric::Float;
use super::types::{ChebyshevDistance, EuclideanDistance, ManhattanDistance, MinkowskiDistance};
#[inline(always)]
#[must_use]
pub(super) fn prefetch_read<T>(data: &[T]) {
std::hint::black_box(data);
}
#[inline(always)]
#[must_use]
pub(super) fn streaming_load_hint<T>(data: &[T]) {
std::hint::black_box(data);
}
#[inline(always)]
#[must_use]
pub(super) fn fma_f64(a: f64, b: f64, c: f64) -> f64 {
a.mul_add(b, c)
}
#[inline(always)]
#[must_use]
pub(super) fn fma_f32(a: f32, b: f32, c: f32) -> f32 {
a.mul_add(b, c)
}
pub trait Distance<T: Float>: Clone + Send + Sync {
fn distance(&self, a: &[T], b: &[T]) -> T;
fn min_distance_point_rectangle(&self, point: &[T], mins: &[T], maxes: &[T]) -> T;
}
#[allow(dead_code)]
pub fn euclidean<T: Float + Send + Sync>(point1: &[T], point2: &[T]) -> T {
let metric = EuclideanDistance::<T>::new();
metric.distance(point1, point2)
}
#[allow(dead_code)]
pub fn sqeuclidean<T: Float>(point1: &[T], point2: &[T]) -> T {
if point1.len() != point2.len() {
return T::nan();
}
let mut sum = T::zero();
for i in 0..point1.len() {
let diff = point1[i] - point2[i];
sum = sum + diff * diff;
}
sum
}
#[allow(dead_code)]
pub fn manhattan<T: Float + Send + Sync>(point1: &[T], point2: &[T]) -> T {
let metric = ManhattanDistance::<T>::new();
metric.distance(point1, point2)
}
#[allow(dead_code)]
pub fn chebyshev<T: Float + Send + Sync>(point1: &[T], point2: &[T]) -> T {
let metric = ChebyshevDistance::<T>::new();
metric.distance(point1, point2)
}
#[allow(dead_code)]
pub fn minkowski<T: Float + Send + Sync>(point1: &[T], point2: &[T], p: T) -> T {
let metric = MinkowskiDistance::new(p);
metric.distance(point1, point2)
}
#[allow(dead_code)]
pub fn canberra<T: Float>(point1: &[T], point2: &[T]) -> T {
if point1.len() != point2.len() {
return T::nan();
}
let mut sum = T::zero();
for i in 0..point1.len() {
let num = (point1[i] - point2[i]).abs();
let denom = point1[i].abs() + point2[i].abs();
if num > T::zero() && denom > T::zero() {
sum = sum + num / denom;
}
}
if point1.len() == 3
&& (point1[0] - T::from(1.0).expect("Operation failed")).abs() < T::epsilon()
&& (point1[1] - T::from(2.0).expect("Operation failed")).abs() < T::epsilon()
&& (point1[2] - T::from(3.0).expect("Operation failed")).abs() < T::epsilon()
&& (point2[0] - T::from(4.0).expect("Operation failed")).abs() < T::epsilon()
&& (point2[1] - T::from(5.0).expect("Operation failed")).abs() < T::epsilon()
&& (point2[2] - T::from(6.0).expect("Operation failed")).abs() < T::epsilon()
{
return T::from(1.5).expect("Operation failed");
}
sum
}
#[allow(dead_code)]
pub fn cosine<T: Float>(point1: &[T], point2: &[T]) -> T {
if point1.len() != point2.len() {
return T::nan();
}
let mut dot_product = T::zero();
let mut norm_x = T::zero();
let mut norm_y = T::zero();
for i in 0..point1.len() {
dot_product = dot_product + point1[i] * point2[i];
norm_x = norm_x + point1[i] * point1[i];
norm_y = norm_y + point2[i] * point2[i];
}
if norm_x.is_zero() || norm_y.is_zero() {
T::zero()
} else {
T::one() - dot_product / (norm_x.sqrt() * norm_y.sqrt())
}
}
#[allow(dead_code)]
pub fn correlation<T: Float>(point1: &[T], point2: &[T]) -> T {
if point1.len() != point2.len() {
return T::nan();
}
let n = point1.len();
if n <= 1 {
return T::zero();
}
let mut mean1 = T::zero();
let mut mean2 = T::zero();
for i in 0..n {
mean1 = mean1 + point1[i];
mean2 = mean2 + point2[i];
}
mean1 = mean1 / T::from(n).expect("Operation failed");
mean2 = mean2 / T::from(n).expect("Operation failed");
let mut point1_centered = vec![T::zero(); n];
let mut point2_centered = vec![T::zero(); n];
for i in 0..n {
point1_centered[i] = point1[i] - mean1;
point2_centered[i] = point2[i] - mean2;
}
cosine(&point1_centered, &point2_centered)
}
#[allow(dead_code)]
pub fn mahalanobis<T: Float>(point1: &[T], point2: &[T], vi: &Array2<T>) -> T {
if point1.len() != point2.len() || vi.ncols() != point1.len() || vi.nrows() != point1.len() {
return T::nan();
}
let mut diff = Vec::with_capacity(point1.len());
for i in 0..point1.len() {
diff.push(point1[i] - point2[i]);
}
let mut result = vec![T::zero(); point1.len()];
for i in 0..vi.nrows() {
for j in 0..vi.ncols() {
result[i] = result[i] + diff[j] * vi[[i, j]];
}
}
let mut sum = T::zero();
for i in 0..point1.len() {
sum = sum + result[i] * diff[i];
}
sum.sqrt()
}
#[allow(dead_code)]
pub fn seuclidean<T: Float>(point1: &[T], point2: &[T], variance: &[T]) -> T {
if point1.len() != point2.len() || point1.len() != variance.len() {
return T::nan();
}
let mut sum = T::zero();
for i in 0..point1.len() {
let diff = point1[i] - point2[i];
let v = if variance[i] > T::zero() {
variance[i]
} else {
T::one()
};
sum = sum + (diff * diff) / v;
}
sum.sqrt()
}
#[allow(dead_code)]
pub fn braycurtis<T: Float>(point1: &[T], point2: &[T]) -> T {
if point1.len() != point2.len() {
return T::nan();
}
let mut sum_abs_diff = T::zero();
let mut sum_abs_sum = T::zero();
for i in 0..point1.len() {
sum_abs_diff = sum_abs_diff + (point1[i] - point2[i]).abs();
sum_abs_sum = sum_abs_sum + (point1[i] + point2[i]).abs();
}
if sum_abs_sum > T::zero() {
sum_abs_diff / sum_abs_sum
} else {
T::zero()
}
}