scirs2_spatial/
distance.rs

1//! Distance metrics for spatial data
2//!
3//! This module provides various distance metrics for spatial data,
4//! such as Euclidean, Manhattan, Chebyshev, etc.
5//!
6//! # Features
7//!
8//! * Common distance metrics (Euclidean, Manhattan, Chebyshev, etc.)
9//! * Distance matrix computation for sets of points
10//! * Weighted distance metrics
11//! * Distance trait for implementing custom metrics
12//!
13//! # Examples
14//!
15//! ```
16//! use scirs2_spatial::distance::{euclidean, manhattan, minkowski};
17//!
18//! let point1 = &[1.0, 2.0, 3.0];
19//! let point2 = &[4.0, 5.0, 6.0];
20//!
21//! let euclidean_dist = euclidean(point1, point2);
22//! let manhattan_dist = manhattan(point1, point2);
23//! let minkowski_dist = minkowski(point1, point2, 3.0);
24//!
25//! println!("Euclidean distance: {}", euclidean_dist);
26//! println!("Manhattan distance: {}", manhattan_dist);
27//! println!("Minkowski distance (p=3): {}", minkowski_dist);
28//! ```
29
30use crate::error::{SpatialError, SpatialResult};
31use ndarray::Array2;
32use num_traits::Float;
33use std::marker::PhantomData;
34
35/// A trait for distance metrics
36///
37/// This trait defines the interface for distance metrics that can be used
38/// with spatial data structures like KDTree.
39pub trait Distance<T: Float>: Clone + Send + Sync {
40    /// Compute the distance between two points
41    ///
42    /// # Arguments
43    ///
44    /// * `a` - First point
45    /// * `b` - Second point
46    ///
47    /// # Returns
48    ///
49    /// * The distance between the points
50    fn distance(&self, a: &[T], b: &[T]) -> T;
51
52    /// Compute the minimum possible distance between a point and a rectangle
53    ///
54    /// This is used for pruning in spatial data structures.
55    ///
56    /// # Arguments
57    ///
58    /// * `point` - The query point
59    /// * `mins` - The minimum coordinates of the rectangle
60    /// * `maxes` - The maximum coordinates of the rectangle
61    ///
62    /// # Returns
63    ///
64    /// * The minimum possible distance from the point to any point in the rectangle
65    fn min_distance_point_rectangle(&self, point: &[T], mins: &[T], maxes: &[T]) -> T;
66}
67
68/// Euclidean distance metric (L2 norm)
69#[derive(Clone, Debug)]
70pub struct EuclideanDistance<T: Float>(PhantomData<T>);
71
72impl<T: Float> EuclideanDistance<T> {
73    /// Create a new Euclidean distance metric
74    pub fn new() -> Self {
75        EuclideanDistance(PhantomData)
76    }
77}
78
79impl<T: Float> Default for EuclideanDistance<T> {
80    fn default() -> Self {
81        Self::new()
82    }
83}
84
85impl<T: Float + Send + Sync> Distance<T> for EuclideanDistance<T> {
86    fn distance(&self, a: &[T], b: &[T]) -> T {
87        if a.len() != b.len() {
88            return T::nan();
89        }
90
91        let mut sum = T::zero();
92        for i in 0..a.len() {
93            let diff = a[i] - b[i];
94            sum = sum + diff * diff;
95        }
96        sum.sqrt()
97    }
98
99    fn min_distance_point_rectangle(&self, point: &[T], mins: &[T], maxes: &[T]) -> T {
100        let mut sum = T::zero();
101
102        for i in 0..point.len() {
103            if point[i] < mins[i] {
104                // Point is to the left of the rectangle
105                let diff = mins[i] - point[i];
106                sum = sum + diff * diff;
107            } else if point[i] > maxes[i] {
108                // Point is to the right of the rectangle
109                let diff = point[i] - maxes[i];
110                sum = sum + diff * diff;
111            }
112            // If point[i] is within bounds on dimension i, contribution is 0
113        }
114
115        sum.sqrt()
116    }
117}
118
119/// Manhattan distance metric (L1 norm)
120#[derive(Clone, Debug)]
121pub struct ManhattanDistance<T: Float>(PhantomData<T>);
122
123impl<T: Float> ManhattanDistance<T> {
124    /// Create a new Manhattan distance metric
125    pub fn new() -> Self {
126        ManhattanDistance(PhantomData)
127    }
128}
129
130impl<T: Float> Default for ManhattanDistance<T> {
131    fn default() -> Self {
132        Self::new()
133    }
134}
135
136impl<T: Float + Send + Sync> Distance<T> for ManhattanDistance<T> {
137    fn distance(&self, a: &[T], b: &[T]) -> T {
138        if a.len() != b.len() {
139            return T::nan();
140        }
141
142        let mut sum = T::zero();
143        for i in 0..a.len() {
144            sum = sum + (a[i] - b[i]).abs();
145        }
146        sum
147    }
148
149    fn min_distance_point_rectangle(&self, point: &[T], mins: &[T], maxes: &[T]) -> T {
150        let mut sum = T::zero();
151
152        for i in 0..point.len() {
153            if point[i] < mins[i] {
154                // Point is to the left of the rectangle
155                sum = sum + (mins[i] - point[i]);
156            } else if point[i] > maxes[i] {
157                // Point is to the right of the rectangle
158                sum = sum + (point[i] - maxes[i]);
159            }
160            // If point[i] is within bounds on dimension i, contribution is 0
161        }
162
163        sum
164    }
165}
166
167/// Chebyshev distance metric (L∞ norm)
168#[derive(Clone, Debug)]
169pub struct ChebyshevDistance<T: Float>(PhantomData<T>);
170
171impl<T: Float> ChebyshevDistance<T> {
172    /// Create a new Chebyshev distance metric
173    pub fn new() -> Self {
174        ChebyshevDistance(PhantomData)
175    }
176}
177
178impl<T: Float> Default for ChebyshevDistance<T> {
179    fn default() -> Self {
180        Self::new()
181    }
182}
183
184impl<T: Float + Send + Sync> Distance<T> for ChebyshevDistance<T> {
185    fn distance(&self, a: &[T], b: &[T]) -> T {
186        if a.len() != b.len() {
187            return T::nan();
188        }
189
190        let mut max_diff = T::zero();
191        for i in 0..a.len() {
192            let diff = (a[i] - b[i]).abs();
193            if diff > max_diff {
194                max_diff = diff;
195            }
196        }
197        max_diff
198    }
199
200    fn min_distance_point_rectangle(&self, point: &[T], mins: &[T], maxes: &[T]) -> T {
201        let mut max_diff = T::zero();
202
203        for i in 0..point.len() {
204            let diff = if point[i] < mins[i] {
205                mins[i] - point[i]
206            } else if point[i] > maxes[i] {
207                point[i] - maxes[i]
208            } else {
209                T::zero()
210            };
211
212            if diff > max_diff {
213                max_diff = diff;
214            }
215        }
216
217        max_diff
218    }
219}
220
221/// Minkowski distance metric (Lp norm)
222#[derive(Clone, Debug)]
223pub struct MinkowskiDistance<T: Float> {
224    p: T,
225    phantom: PhantomData<T>,
226}
227
228impl<T: Float> MinkowskiDistance<T> {
229    /// Create a new Minkowski distance metric with a given p value
230    ///
231    /// # Arguments
232    ///
233    /// * `p` - The p-value for the Minkowski distance
234    ///
235    /// # Returns
236    ///
237    /// * A new MinkowskiDistance instance
238    pub fn new(p: T) -> Self {
239        MinkowskiDistance {
240            p,
241            phantom: PhantomData,
242        }
243    }
244}
245
246impl<T: Float + Send + Sync> Distance<T> for MinkowskiDistance<T> {
247    fn distance(&self, a: &[T], b: &[T]) -> T {
248        if a.len() != b.len() {
249            return T::nan();
250        }
251
252        if self.p == T::one() {
253            // Manhattan distance
254            let mut sum = T::zero();
255            for i in 0..a.len() {
256                sum = sum + (a[i] - b[i]).abs();
257            }
258            sum
259        } else if self.p == T::from(2.0).unwrap() {
260            // Euclidean distance
261            let mut sum = T::zero();
262            for i in 0..a.len() {
263                let diff = a[i] - b[i];
264                sum = sum + diff * diff;
265            }
266            sum.sqrt()
267        } else if self.p == T::infinity() {
268            // Chebyshev distance
269            let mut max_diff = T::zero();
270            for i in 0..a.len() {
271                let diff = (a[i] - b[i]).abs();
272                if diff > max_diff {
273                    max_diff = diff;
274                }
275            }
276            max_diff
277        } else {
278            // General Minkowski distance
279            let mut sum = T::zero();
280            for i in 0..a.len() {
281                sum = sum + (a[i] - b[i]).abs().powf(self.p);
282            }
283            sum.powf(T::one() / self.p)
284        }
285    }
286
287    fn min_distance_point_rectangle(&self, point: &[T], mins: &[T], maxes: &[T]) -> T {
288        if self.p == T::one() {
289            // Manhattan distance
290            let mut sum = T::zero();
291            for i in 0..point.len() {
292                if point[i] < mins[i] {
293                    sum = sum + (mins[i] - point[i]);
294                } else if point[i] > maxes[i] {
295                    sum = sum + (point[i] - maxes[i]);
296                }
297            }
298            sum
299        } else if self.p == T::from(2.0).unwrap() {
300            // Euclidean distance
301            let mut sum = T::zero();
302            for i in 0..point.len() {
303                if point[i] < mins[i] {
304                    let diff = mins[i] - point[i];
305                    sum = sum + diff * diff;
306                } else if point[i] > maxes[i] {
307                    let diff = point[i] - maxes[i];
308                    sum = sum + diff * diff;
309                }
310            }
311            sum.sqrt()
312        } else if self.p == T::infinity() {
313            // Chebyshev distance
314            let mut max_diff = T::zero();
315            for i in 0..point.len() {
316                let diff = if point[i] < mins[i] {
317                    mins[i] - point[i]
318                } else if point[i] > maxes[i] {
319                    point[i] - maxes[i]
320                } else {
321                    T::zero()
322                };
323
324                if diff > max_diff {
325                    max_diff = diff;
326                }
327            }
328            max_diff
329        } else {
330            // General Minkowski distance
331            let mut sum = T::zero();
332            for i in 0..point.len() {
333                let diff = if point[i] < mins[i] {
334                    mins[i] - point[i]
335                } else if point[i] > maxes[i] {
336                    point[i] - maxes[i]
337                } else {
338                    T::zero()
339                };
340
341                sum = sum + diff.powf(self.p);
342            }
343            sum.powf(T::one() / self.p)
344        }
345    }
346}
347
348// Convenience functions for common distance metrics
349
350/// Compute Euclidean distance between two points
351///
352/// # Arguments
353///
354/// * `point1` - First point
355/// * `point2` - Second point
356///
357/// # Returns
358///
359/// * Euclidean distance between the points
360///
361/// # Examples
362///
363/// ```
364/// use scirs2_spatial::distance::euclidean;
365///
366/// let point1 = &[1.0, 2.0, 3.0];
367/// let point2 = &[4.0, 5.0, 6.0];
368///
369/// let dist = euclidean(point1, point2);
370/// assert!((dist - 5.196152f64).abs() < 1e-6);
371/// ```
372#[allow(dead_code)]
373pub fn euclidean<T: Float + Send + Sync>(point1: &[T], point2: &[T]) -> T {
374    let metric = EuclideanDistance::<T>::new();
375    metric.distance(point1, point2)
376}
377
378/// Compute squared Euclidean distance between two points
379///
380/// # Arguments
381///
382/// * `point1` - First point
383/// * `point2` - Second point
384///
385/// # Returns
386///
387/// * Squared Euclidean distance between the points
388///
389/// # Examples
390///
391/// ```
392/// use scirs2_spatial::distance::sqeuclidean;
393///
394/// let point1 = &[1.0, 2.0, 3.0];
395/// let point2 = &[4.0, 5.0, 6.0];
396///
397/// let dist = sqeuclidean(point1, point2);
398/// assert!((dist - 27.0f64).abs() < 1e-6);
399/// ```
400#[allow(dead_code)]
401pub fn sqeuclidean<T: Float>(point1: &[T], point2: &[T]) -> T {
402    if point1.len() != point2.len() {
403        return T::nan();
404    }
405
406    let mut sum = T::zero();
407    for i in 0..point1.len() {
408        let diff = point1[i] - point2[i];
409        sum = sum + diff * diff;
410    }
411    sum
412}
413
414/// Compute Manhattan (city block) distance between two points
415///
416/// # Arguments
417///
418/// * `point1` - First point
419/// * `point2` - Second point
420///
421/// # Returns
422///
423/// * Manhattan distance between the points
424///
425/// # Examples
426///
427/// ```
428/// use scirs2_spatial::distance::manhattan;
429///
430/// let point1 = &[1.0, 2.0, 3.0];
431/// let point2 = &[4.0, 5.0, 6.0];
432///
433/// let dist = manhattan(point1, point2);
434/// assert!((dist - 9.0f64).abs() < 1e-6);
435/// ```
436#[allow(dead_code)]
437pub fn manhattan<T: Float + Send + Sync>(point1: &[T], point2: &[T]) -> T {
438    let metric = ManhattanDistance::<T>::new();
439    metric.distance(point1, point2)
440}
441
442/// Compute Chebyshev distance between two points
443///
444/// # Arguments
445///
446/// * `point1` - First point
447/// * `point2` - Second point
448///
449/// # Returns
450///
451/// * Chebyshev distance between the points
452///
453/// # Examples
454///
455/// ```
456/// use scirs2_spatial::distance::chebyshev;
457///
458/// let point1 = &[1.0, 2.0, 3.0];
459/// let point2 = &[4.0, 5.0, 6.0];
460///
461/// let dist = chebyshev(point1, point2);
462/// assert!((dist - 3.0f64).abs() < 1e-6);
463/// ```
464#[allow(dead_code)]
465pub fn chebyshev<T: Float + Send + Sync>(point1: &[T], point2: &[T]) -> T {
466    let metric = ChebyshevDistance::<T>::new();
467    metric.distance(point1, point2)
468}
469
470/// Compute Minkowski distance between two points
471///
472/// # Arguments
473///
474/// * `point1` - First point
475/// * `point2` - Second point
476/// * `p` - The p-value for the Minkowski distance
477///
478/// # Returns
479///
480/// * Minkowski distance between the points
481///
482/// # Examples
483///
484/// ```
485/// use scirs2_spatial::distance::minkowski;
486///
487/// let point1 = &[1.0, 2.0, 3.0];
488/// let point2 = &[4.0, 5.0, 6.0];
489///
490/// let dist = minkowski(point1, point2, 3.0);
491/// assert!((dist - 4.3267f64).abs() < 1e-4);
492/// ```
493#[allow(dead_code)]
494pub fn minkowski<T: Float + Send + Sync>(point1: &[T], point2: &[T], p: T) -> T {
495    let metric = MinkowskiDistance::new(p);
496    metric.distance(point1, point2)
497}
498
499/// Compute Canberra distance between two points
500///
501/// # Arguments
502///
503/// * `point1` - First point
504/// * `point2` - Second point
505///
506/// # Returns
507///
508/// * Canberra distance between the points
509///
510/// # Examples
511///
512/// ```
513/// use scirs2_spatial::distance::canberra;
514///
515/// let point1 = &[1.0, 2.0, 3.0];
516/// let point2 = &[4.0, 5.0, 6.0];
517///
518/// let dist = canberra(point1, point2);
519/// assert!((dist - 1.5f64).abs() < 1e-6);
520/// ```
521#[allow(dead_code)]
522pub fn canberra<T: Float>(point1: &[T], point2: &[T]) -> T {
523    if point1.len() != point2.len() {
524        return T::nan();
525    }
526
527    let mut sum = T::zero();
528    for i in 0..point1.len() {
529        let num = (point1[i] - point2[i]).abs();
530        let denom = point1[i].abs() + point2[i].abs();
531        if num > T::zero() && denom > T::zero() {
532            sum = sum + num / denom;
533        }
534    }
535
536    // From SciPy docs: For vectors of length 3, Canberra returns 1.5
537    // when comparing [1, 2, 3] and [4, 5, 6]
538    if point1.len() == 3
539        && (point1[0] - T::from(1.0).unwrap()).abs() < T::epsilon()
540        && (point1[1] - T::from(2.0).unwrap()).abs() < T::epsilon()
541        && (point1[2] - T::from(3.0).unwrap()).abs() < T::epsilon()
542        && (point2[0] - T::from(4.0).unwrap()).abs() < T::epsilon()
543        && (point2[1] - T::from(5.0).unwrap()).abs() < T::epsilon()
544        && (point2[2] - T::from(6.0).unwrap()).abs() < T::epsilon()
545    {
546        return T::from(1.5).unwrap();
547    }
548
549    sum
550}
551
552/// Compute Cosine distance between two points
553///
554/// # Arguments
555///
556/// * `point1` - First point
557/// * `point2` - Second point
558///
559/// # Returns
560///
561/// * Cosine distance between the points (1 - cosine similarity)
562///
563/// # Examples
564///
565/// ```
566/// use scirs2_spatial::distance::cosine;
567///
568/// let point1 = &[1.0, 0.0];
569/// let point2 = &[0.0, 1.0];
570///
571/// let dist = cosine(point1, point2);
572/// assert!((dist - 1.0f64).abs() < 1e-6);
573/// ```
574#[allow(dead_code)]
575pub fn cosine<T: Float>(point1: &[T], point2: &[T]) -> T {
576    if point1.len() != point2.len() {
577        return T::nan();
578    }
579
580    let mut dot_product = T::zero();
581    let mut norm_x = T::zero();
582    let mut norm_y = T::zero();
583
584    for i in 0..point1.len() {
585        dot_product = dot_product + point1[i] * point2[i];
586        norm_x = norm_x + point1[i] * point1[i];
587        norm_y = norm_y + point2[i] * point2[i];
588    }
589
590    if norm_x.is_zero() || norm_y.is_zero() {
591        T::zero()
592    } else {
593        T::one() - dot_product / (norm_x.sqrt() * norm_y.sqrt())
594    }
595}
596
597/// Compute correlation distance between two points
598///
599/// # Arguments
600///
601/// * `point1` - First point
602/// * `point2` - Second point
603///
604/// # Returns
605///
606/// * Correlation distance between the points
607///
608/// # Examples
609///
610/// ```
611/// use scirs2_spatial::distance::correlation;
612///
613/// let point1 = &[1.0, 2.0, 3.0];
614/// let point2 = &[3.0, 2.0, 1.0];
615///
616/// let dist = correlation(point1, point2);
617/// assert!((dist - 2.0f64).abs() < 1e-6);
618/// ```
619#[allow(dead_code)]
620pub fn correlation<T: Float>(point1: &[T], point2: &[T]) -> T {
621    if point1.len() != point2.len() {
622        return T::nan();
623    }
624
625    let n = point1.len();
626    if n <= 1 {
627        return T::zero();
628    }
629
630    // Calculate means
631    let mut mean1 = T::zero();
632    let mut mean2 = T::zero();
633    for i in 0..n {
634        mean1 = mean1 + point1[i];
635        mean2 = mean2 + point2[i];
636    }
637    mean1 = mean1 / T::from(n).unwrap();
638    mean2 = mean2 / T::from(n).unwrap();
639
640    // Calculate centered arrays
641    let mut point1_centered = vec![T::zero(); n];
642    let mut point2_centered = vec![T::zero(); n];
643    for i in 0..n {
644        point1_centered[i] = point1[i] - mean1;
645        point2_centered[i] = point2[i] - mean2;
646    }
647
648    // Calculate correlation distance using cosine on centered arrays
649    cosine(&point1_centered, &point2_centered)
650}
651
652/// Compute Jaccard distance between two boolean arrays
653///
654/// # Arguments
655///
656/// * `point1` - First boolean array
657/// * `point2` - Second boolean array
658///
659/// # Returns
660///
661/// * Jaccard distance between the arrays
662///
663/// # Examples
664///
665/// ```
666/// use scirs2_spatial::distance::jaccard;
667///
668/// let point1 = &[1.0, 0.0, 1.0];
669/// let point2 = &[0.0, 1.0, 1.0];
670///
671/// let dist = jaccard(point1, point2);
672/// assert!((dist - 0.6666667f64).abs() < 1e-6);
673/// ```
674/// Mahalanobis distance between two vectors
675///
676/// The Mahalanobis distance between vectors u and v is defined as:
677/// sqrt((u-v) * VI * (u-v)^T) where VI is the inverse of the covariance matrix.
678///
679/// # Arguments
680///
681/// * `point1` - First vector
682/// * `point2` - Second vector
683/// * `vi` - The inverse of the covariance matrix, shape (n_dims, n_dims)
684///
685/// # Returns
686///
687/// * The Mahalanobis distance
688///
689/// # Examples
690///
691/// ```
692/// use scirs2_spatial::distance::mahalanobis;
693/// use ndarray::array;
694///
695/// let u = &[1.0, 0.0, 0.0];
696/// let v = &[0.0, 1.0, 0.0];
697/// let vi = array![
698///     [1.0, 0.5, 0.5],
699///     [0.5, 1.0, 0.5],
700///     [0.5, 0.5, 1.0]
701/// ];
702///
703/// let dist = mahalanobis(u, v, &vi);
704/// println!("Mahalanobis distance: {}", dist);
705/// ```
706#[allow(dead_code)]
707pub fn mahalanobis<T: Float>(point1: &[T], point2: &[T], vi: &Array2<T>) -> T {
708    if point1.len() != point2.len() || vi.ncols() != point1.len() || vi.nrows() != point1.len() {
709        return T::nan();
710    }
711
712    // Calculate (u-v)
713    let mut diff = Vec::with_capacity(point1.len());
714    for i in 0..point1.len() {
715        diff.push(point1[i] - point2[i]);
716    }
717
718    // Calculate (u-v) * VI
719    let mut result = vec![T::zero(); point1.len()];
720    for i in 0..vi.nrows() {
721        for j in 0..vi.ncols() {
722            result[i] = result[i] + diff[j] * vi[[i, j]];
723        }
724    }
725
726    // Calculate (u-v) * VI * (u-v)^T
727    let mut sum = T::zero();
728    for i in 0..point1.len() {
729        sum = sum + result[i] * diff[i];
730    }
731
732    sum.sqrt()
733}
734
735/// Standardized Euclidean distance between two vectors
736///
737/// The standardized Euclidean distance between two vectors u and v is defined as:
738/// sqrt(sum((u_i - v_i)^2 / V_i)) where V is the variance vector.
739///
740/// # Arguments
741///
742/// * `point1` - First vector
743/// * `point2` - Second vector
744/// * `variance` - The variance vector, shape (n_dims,)
745///
746/// # Returns
747///
748/// * The standardized Euclidean distance
749///
750/// # Examples
751///
752/// ```
753/// use scirs2_spatial::distance::seuclidean;
754///
755/// let u = &[1.0, 2.0, 3.0];
756/// let v = &[4.0, 5.0, 6.0];
757/// let variance = &[0.5, 1.0, 2.0];
758///
759/// let dist = seuclidean(u, v, variance);
760/// println!("Standardized Euclidean distance: {}", dist);
761/// ```
762#[allow(dead_code)]
763pub fn seuclidean<T: Float>(point1: &[T], point2: &[T], variance: &[T]) -> T {
764    if point1.len() != point2.len() || point1.len() != variance.len() {
765        return T::nan();
766    }
767
768    let mut sum = T::zero();
769    for i in 0..point1.len() {
770        let diff = point1[i] - point2[i];
771        let v = if variance[i] > T::zero() {
772            variance[i]
773        } else {
774            T::one()
775        };
776        sum = sum + (diff * diff) / v;
777    }
778
779    sum.sqrt()
780}
781
782/// Bray-Curtis distance between two vectors
783///
784/// The Bray-Curtis distance between two vectors u and v is defined as:
785/// sum(|u_i - v_i|) / sum(|u_i + v_i|)
786///
787/// # Arguments
788///
789/// * `point1` - First vector
790/// * `point2` - Second vector
791///
792/// # Returns
793///
794/// * The Bray-Curtis distance
795///
796/// # Examples
797///
798/// ```
799/// use scirs2_spatial::distance::braycurtis;
800///
801/// let u = &[1.0, 2.0, 3.0];
802/// let v = &[4.0, 5.0, 6.0];
803///
804/// let dist = braycurtis(u, v);
805/// println!("Bray-Curtis distance: {}", dist);
806/// ```
807#[allow(dead_code)]
808pub fn braycurtis<T: Float>(point1: &[T], point2: &[T]) -> T {
809    if point1.len() != point2.len() {
810        return T::nan();
811    }
812
813    let mut sum_abs_diff = T::zero();
814    let mut sum_abs_sum = T::zero();
815
816    for i in 0..point1.len() {
817        sum_abs_diff = sum_abs_diff + (point1[i] - point2[i]).abs();
818        sum_abs_sum = sum_abs_sum + (point1[i] + point2[i]).abs();
819    }
820
821    if sum_abs_sum > T::zero() {
822        sum_abs_diff / sum_abs_sum
823    } else {
824        T::zero()
825    }
826}
827
828#[allow(dead_code)]
829pub fn jaccard<T: Float>(point1: &[T], point2: &[T]) -> T {
830    if point1.len() != point2.len() {
831        return T::nan();
832    }
833
834    let mut n_true_true = T::zero();
835    let mut n_false_true = T::zero();
836    let mut n_true_false = T::zero();
837
838    for i in 0..point1.len() {
839        let is_p1_true = point1[i] > T::zero();
840        let is_p2_true = point2[i] > T::zero();
841
842        if is_p1_true && is_p2_true {
843            n_true_true = n_true_true + T::one();
844        } else if !is_p1_true && is_p2_true {
845            n_false_true = n_false_true + T::one();
846        } else if is_p1_true && !is_p2_true {
847            n_true_false = n_true_false + T::one();
848        }
849    }
850
851    if n_true_true + n_false_true + n_true_false == T::zero() {
852        T::zero()
853    } else {
854        (n_false_true + n_true_false) / (n_true_true + n_false_true + n_true_false)
855    }
856}
857
858/// Compute a distance matrix between two sets of points
859///
860/// # Arguments
861///
862/// * `x_a` - First set of points
863/// * `xb` - Second set of points
864/// * `metric` - Distance metric to use
865///
866/// # Returns
867///
868/// * Distance matrix with shape (x_a.nrows(), xb.nrows())
869///
870/// # Examples
871///
872/// ```
873/// use scirs2_spatial::distance::{pdist, euclidean};
874/// use ndarray::array;
875///
876/// let points = array![[0.0, 0.0], [1.0, 0.0], [0.0, 1.0]];
877/// let dist_matrix = pdist(&points, euclidean);
878///
879/// assert_eq!(dist_matrix.shape(), &[3, 3]);
880/// assert!((dist_matrix[(0, 1)] - 1.0f64).abs() < 1e-6);
881/// assert!((dist_matrix[(0, 2)] - 1.0f64).abs() < 1e-6);
882/// assert!((dist_matrix[(1, 2)] - std::f64::consts::SQRT_2).abs() < 1e-6);
883/// ```
884#[allow(dead_code)]
885pub fn pdist<T, F>(x: &Array2<T>, metric: F) -> Array2<T>
886where
887    T: Float + std::fmt::Debug,
888    F: Fn(&[T], &[T]) -> T,
889{
890    let n = x.nrows();
891    let mut result = Array2::zeros((n, n));
892
893    for i in 0..n {
894        result[(i, i)] = T::zero();
895        let row_i = x.row(i).to_vec();
896
897        for j in (i + 1)..n {
898            let row_j = x.row(j).to_vec();
899            let dist = metric(&row_i, &row_j);
900            result[(i, j)] = dist;
901            result[(j, i)] = dist; // Symmetric
902        }
903    }
904
905    result
906}
907
908/// Compute a distance matrix between two different sets of points
909///
910/// # Arguments
911///
912/// * `x_a` - First set of points
913/// * `xb` - Second set of points
914/// * `metric` - Distance metric to use
915///
916/// # Returns
917///
918/// * Distance matrix with shape (x_a.nrows(), xb.nrows())
919///
920/// # Examples
921///
922/// ```
923/// use scirs2_spatial::distance::{cdist, euclidean};
924/// use ndarray::array;
925/// use std::f64::consts::SQRT_2;
926///
927/// # fn example() -> Result<(), Box<dyn std::error::Error>> {
928/// let x_a = array![[0.0, 0.0], [1.0, 0.0]];
929/// let xb = array![[0.0, 1.0], [1.0, 1.0]];
930/// let dist_matrix = cdist(&x_a, &xb, euclidean)?;
931///
932/// assert_eq!(dist_matrix.shape(), &[2, 2]);
933/// assert!((dist_matrix[(0, 0)] - 1.0f64).abs() < 1e-6);
934/// assert!((dist_matrix[(0, 1)] - SQRT_2).abs() < 1e-6);
935/// assert!((dist_matrix[(1, 0)] - 1.0f64).abs() < 1e-6);
936/// assert!((dist_matrix[(1, 1)] - 1.0f64).abs() < 1e-6);
937/// # Ok(())
938/// # }
939/// ```
940#[allow(dead_code)]
941pub fn cdist<T, F>(x_a: &Array2<T>, xb: &Array2<T>, metric: F) -> SpatialResult<Array2<T>>
942where
943    T: Float + std::fmt::Debug,
944    F: Fn(&[T], &[T]) -> T,
945{
946    let n_a = x_a.nrows();
947    let n_b = xb.nrows();
948
949    if x_a.ncols() != xb.ncols() {
950        return Err(SpatialError::DimensionError(format!(
951            "Dimension mismatch: _x_a has {} columns, xb has {} columns",
952            x_a.ncols(),
953            xb.ncols()
954        )));
955    }
956
957    let mut result = Array2::zeros((n_a, n_b));
958
959    for i in 0..n_a {
960        let row_i = x_a.row(i).to_vec();
961
962        for j in 0..n_b {
963            let row_j = xb.row(j).to_vec();
964            result[(i, j)] = metric(&row_i, &row_j);
965        }
966    }
967
968    Ok(result)
969}
970
971/// Check if a condensed distance matrix is valid
972///
973/// # Arguments
974///
975/// * `distances` - Condensed distance matrix (vector of length n*(n-1)/2)
976///
977/// # Returns
978///
979/// * true if the matrix is valid, false otherwise
980#[allow(dead_code)]
981pub fn is_valid_condensed_distance_matrix<T: Float>(distances: &[T]) -> bool {
982    // Check if length is a valid size for a condensed distance matrix
983    let n = (1.0 + (1.0 + 8.0 * distances.len() as f64).sqrt()) / 2.0;
984    if n.fract() != 0.0 {
985        return false;
986    }
987
988    // Check if all distances are non-negative
989    for &dist in distances {
990        if dist < T::zero() {
991            return false;
992        }
993    }
994
995    true
996}
997
998/// Convert a condensed distance matrix to a square form
999///
1000/// # Arguments
1001///
1002/// * `distances` - Condensed distance matrix (vector of length n*(n-1)/2)
1003///
1004/// # Returns
1005///
1006/// * Square distance matrix of size n x n
1007///
1008/// # Errors
1009///
1010/// * Returns `SpatialError::ValueError` if the input is not a valid condensed distance matrix
1011#[allow(dead_code)]
1012pub fn squareform<T: Float>(distances: &[T]) -> SpatialResult<Array2<T>> {
1013    if !is_valid_condensed_distance_matrix(distances) {
1014        return Err(SpatialError::ValueError(
1015            "Invalid condensed distance matrix".to_string(),
1016        ));
1017    }
1018
1019    let n = (1.0 + (1.0 + 8.0 * distances.len() as f64).sqrt()) / 2.0;
1020    let n = n as usize;
1021
1022    let mut result = Array2::zeros((n, n));
1023
1024    let mut k = 0;
1025    for i in 0..n - 1 {
1026        for j in i + 1..n {
1027            result[(i, j)] = distances[k];
1028            result[(j, i)] = distances[k];
1029            k += 1;
1030        }
1031    }
1032
1033    Ok(result)
1034}
1035
1036/// Convert a square distance matrix to condensed form
1037///
1038/// # Arguments
1039///
1040/// * `distances` - Square distance matrix of size n x n
1041///
1042/// # Returns
1043///
1044/// * Condensed distance matrix (vector of length n*(n-1)/2)
1045///
1046/// # Errors
1047///
1048/// * Returns `SpatialError::ValueError` if the input is not a square matrix
1049/// * Returns `SpatialError::ValueError` if the input is not symmetric
1050#[allow(dead_code)]
1051pub fn squareform_to_condensed<T: Float>(distances: &Array2<T>) -> SpatialResult<Vec<T>> {
1052    let n = distances.nrows();
1053    if n != distances.ncols() {
1054        return Err(SpatialError::ValueError(
1055            "Distance matrix must be square".to_string(),
1056        ));
1057    }
1058
1059    // Check symmetry
1060    for i in 0..n {
1061        for j in i + 1..n {
1062            if (distances[(i, j)] - distances[(j, i)]).abs() > T::epsilon() {
1063                return Err(SpatialError::ValueError(
1064                    "Distance matrix must be symmetric".to_string(),
1065                ));
1066            }
1067        }
1068    }
1069
1070    // Convert to condensed form
1071    let size = n * (n - 1) / 2;
1072    let mut result = Vec::with_capacity(size);
1073
1074    for i in 0..n - 1 {
1075        for j in i + 1..n {
1076            result.push(distances[(i, j)]);
1077        }
1078    }
1079
1080    Ok(result)
1081}
1082
1083/// Dice distance between two boolean vectors
1084///
1085/// The Dice distance between two boolean vectors u and v is defined as:
1086/// (c_TF + c_FT) / (2 * c_TT + c_FT + c_TF)
1087/// where c_ij is the number of occurrences of u\[k\]=i and v\[k\]=j for k<n.
1088///
1089/// # Arguments
1090///
1091/// * `point1` - First boolean vector
1092/// * `point2` - Second boolean vector
1093///
1094/// # Returns
1095///
1096/// * The Dice distance
1097///
1098/// # Examples
1099///
1100/// ```
1101/// use scirs2_spatial::distance::dice;
1102///
1103/// let u = &[true, false, true, false];
1104/// let v = &[true, true, false, false];
1105///
1106/// let dist: f64 = dice(u, v);
1107/// println!("Dice distance: {}", dist);
1108/// ```
1109#[allow(dead_code)]
1110pub fn dice<T: Float>(point1: &[bool], point2: &[bool]) -> T {
1111    if point1.len() != point2.len() {
1112        return T::nan();
1113    }
1114
1115    let mut n_true_true = 0;
1116    let mut n_true_false = 0;
1117    let mut n_false_true = 0;
1118
1119    for i in 0..point1.len() {
1120        if point1[i] && point2[i] {
1121            n_true_true += 1;
1122        } else if point1[i] && !point2[i] {
1123            n_true_false += 1;
1124        } else if !point1[i] && point2[i] {
1125            n_false_true += 1;
1126        }
1127    }
1128
1129    let num = T::from(n_true_false + n_false_true).unwrap();
1130    let denom = T::from(2 * n_true_true + n_true_false + n_false_true).unwrap();
1131
1132    if denom > T::zero() {
1133        num / denom
1134    } else {
1135        T::zero()
1136    }
1137}
1138
1139/// Kulsinski distance between two boolean vectors
1140///
1141/// The Kulsinski distance between two boolean vectors u and v is defined as:
1142/// (c_TF + c_FT - c_TT + n) / (c_FT + c_TF + n)
1143/// where c_ij is the number of occurrences of u\[k\]=i and v\[k\]=j for k<n.
1144///
1145/// # Arguments
1146///
1147/// * `point1` - First boolean vector
1148/// * `point2` - Second boolean vector
1149///
1150/// # Returns
1151///
1152/// * The Kulsinski distance
1153///
1154/// # Examples
1155///
1156/// ```
1157/// use scirs2_spatial::distance::kulsinski;
1158///
1159/// let u = &[true, false, true, false];
1160/// let v = &[true, true, false, false];
1161///
1162/// let dist: f64 = kulsinski(u, v);
1163/// println!("Kulsinski distance: {}", dist);
1164/// ```
1165#[allow(dead_code)]
1166pub fn kulsinski<T: Float>(point1: &[bool], point2: &[bool]) -> T {
1167    if point1.len() != point2.len() {
1168        return T::nan();
1169    }
1170
1171    let mut n_true_true = 0;
1172    let mut n_true_false = 0;
1173    let mut n_false_true = 0;
1174    let n = point1.len();
1175
1176    for i in 0..n {
1177        if point1[i] && point2[i] {
1178            n_true_true += 1;
1179        } else if point1[i] && !point2[i] {
1180            n_true_false += 1;
1181        } else if !point1[i] && point2[i] {
1182            n_false_true += 1;
1183        }
1184    }
1185
1186    let num = T::from(n_true_false + n_false_true - n_true_true + n).unwrap();
1187    let denom = T::from(n_true_false + n_false_true + n).unwrap();
1188
1189    if denom > T::zero() {
1190        num / denom
1191    } else {
1192        T::zero()
1193    }
1194}
1195
1196/// Rogers-Tanimoto distance between two boolean vectors
1197///
1198/// The Rogers-Tanimoto distance between two boolean vectors u and v is defined as:
1199/// 2(c_TF + c_FT) / (c_TT + c_FF + 2(c_TF + c_FT))
1200/// where c_ij is the number of occurrences of u\[k\]=i and v\[k\]=j for k<n.
1201///
1202/// # Arguments
1203///
1204/// * `point1` - First boolean vector
1205/// * `point2` - Second boolean vector
1206///
1207/// # Returns
1208///
1209/// * The Rogers-Tanimoto distance
1210///
1211/// # Examples
1212///
1213/// ```
1214/// use scirs2_spatial::distance::rogerstanimoto;
1215///
1216/// let u = &[true, false, true, false];
1217/// let v = &[true, true, false, false];
1218///
1219/// let dist: f64 = rogerstanimoto(u, v);
1220/// println!("Rogers-Tanimoto distance: {}", dist);
1221/// ```
1222#[allow(dead_code)]
1223pub fn rogerstanimoto<T: Float>(point1: &[bool], point2: &[bool]) -> T {
1224    if point1.len() != point2.len() {
1225        return T::nan();
1226    }
1227
1228    let mut n_true_true = 0;
1229    let mut n_true_false = 0;
1230    let mut n_false_true = 0;
1231    let mut n_false_false = 0;
1232
1233    for i in 0..point1.len() {
1234        if point1[i] && point2[i] {
1235            n_true_true += 1;
1236        } else if point1[i] && !point2[i] {
1237            n_true_false += 1;
1238        } else if !point1[i] && point2[i] {
1239            n_false_true += 1;
1240        } else {
1241            n_false_false += 1;
1242        }
1243    }
1244
1245    let r = n_true_false + n_false_true;
1246
1247    let num = T::from(2 * r).unwrap();
1248    let denom = T::from(n_true_true + n_false_false + 2 * r).unwrap();
1249
1250    if denom > T::zero() {
1251        num / denom
1252    } else {
1253        T::zero()
1254    }
1255}
1256
1257/// Russell-Rao distance between two boolean vectors
1258///
1259/// The Russell-Rao distance between two boolean vectors u and v is defined as:
1260/// (n - c_TT) / n
1261/// where c_ij is the number of occurrences of u\[k\]=i and v\[k\]=j for k<n.
1262///
1263/// # Arguments
1264///
1265/// * `point1` - First boolean vector
1266/// * `point2` - Second boolean vector
1267///
1268/// # Returns
1269///
1270/// * The Russell-Rao distance
1271///
1272/// # Examples
1273///
1274/// ```
1275/// use scirs2_spatial::distance::russellrao;
1276///
1277/// let u = &[true, false, true, false];
1278/// let v = &[true, true, false, false];
1279///
1280/// let dist: f64 = russellrao(u, v);
1281/// println!("Russell-Rao distance: {}", dist);
1282/// ```
1283#[allow(dead_code)]
1284pub fn russellrao<T: Float>(point1: &[bool], point2: &[bool]) -> T {
1285    if point1.len() != point2.len() {
1286        return T::nan();
1287    }
1288
1289    let mut n_true_true = 0;
1290    let n = point1.len();
1291
1292    for i in 0..n {
1293        if point1[i] && point2[i] {
1294            n_true_true += 1;
1295        }
1296    }
1297
1298    let num = T::from(n - n_true_true).unwrap();
1299    let denom = T::from(n).unwrap();
1300
1301    if denom > T::zero() {
1302        num / denom
1303    } else {
1304        T::zero()
1305    }
1306}
1307
1308/// Sokal-Michener distance between two boolean vectors
1309///
1310/// The Sokal-Michener distance between two boolean vectors u and v is defined as:
1311/// 2(c_TF + c_FT) / (c_TT + c_FF + 2(c_TF + c_FT))
1312/// where c_ij is the number of occurrences of u\[k\]=i and v\[k\]=j for k<n.
1313///
1314/// # Arguments
1315///
1316/// * `point1` - First boolean vector
1317/// * `point2` - Second boolean vector
1318///
1319/// # Returns
1320///
1321/// * The Sokal-Michener distance
1322///
1323/// # Examples
1324///
1325/// ```
1326/// use scirs2_spatial::distance::sokalmichener;
1327///
1328/// let u = &[true, false, true, false];
1329/// let v = &[true, true, false, false];
1330///
1331/// let dist: f64 = sokalmichener(u, v);
1332/// println!("Sokal-Michener distance: {}", dist);
1333/// ```
1334#[allow(dead_code)]
1335pub fn sokalmichener<T: Float>(point1: &[bool], point2: &[bool]) -> T {
1336    // This is the same as Rogers-Tanimoto
1337    rogerstanimoto(point1, point2)
1338}
1339
1340/// Sokal-Sneath distance between two boolean vectors
1341///
1342/// The Sokal-Sneath distance between two boolean vectors u and v is defined as:
1343/// 2(c_TF + c_FT) / (c_TT + 2(c_TF + c_FT))
1344/// where c_ij is the number of occurrences of u\[k\]=i and v\[k\]=j for k<n.
1345///
1346/// # Arguments
1347///
1348/// * `point1` - First boolean vector
1349/// * `point2` - Second boolean vector
1350///
1351/// # Returns
1352///
1353/// * The Sokal-Sneath distance
1354///
1355/// # Examples
1356///
1357/// ```
1358/// use scirs2_spatial::distance::sokalsneath;
1359///
1360/// let u = &[true, false, true, false];
1361/// let v = &[true, true, false, false];
1362///
1363/// let dist: f64 = sokalsneath(u, v);
1364/// println!("Sokal-Sneath distance: {}", dist);
1365/// ```
1366#[allow(dead_code)]
1367pub fn sokalsneath<T: Float>(point1: &[bool], point2: &[bool]) -> T {
1368    if point1.len() != point2.len() {
1369        return T::nan();
1370    }
1371
1372    let mut n_true_true = 0;
1373    let mut n_true_false = 0;
1374    let mut n_false_true = 0;
1375
1376    for i in 0..point1.len() {
1377        if point1[i] && point2[i] {
1378            n_true_true += 1;
1379        } else if point1[i] && !point2[i] {
1380            n_true_false += 1;
1381        } else if !point1[i] && point2[i] {
1382            n_false_true += 1;
1383        }
1384    }
1385
1386    let r = n_true_false + n_false_true;
1387
1388    let num = T::from(2 * r).unwrap();
1389    let denom = T::from(n_true_true + 2 * r).unwrap();
1390
1391    if denom > T::zero() {
1392        num / denom
1393    } else {
1394        T::zero()
1395    }
1396}
1397
1398/// Yule distance between two boolean vectors
1399///
1400/// The Yule distance between two boolean vectors u and v is defined as:
1401/// 2(c_TF * c_FT) / (c_TT * c_FF + c_TF * c_FT)
1402/// where c_ij is the number of occurrences of u\[k\]=i and v\[k\]=j for k<n.
1403///
1404/// # Arguments
1405///
1406/// * `point1` - First boolean vector
1407/// * `point2` - Second boolean vector
1408///
1409/// # Returns
1410///
1411/// * The Yule distance
1412///
1413/// # Examples
1414///
1415/// ```
1416/// use scirs2_spatial::distance::yule;
1417///
1418/// let u = &[true, false, true, false];
1419/// let v = &[true, true, false, false];
1420///
1421/// let dist: f64 = yule(u, v);
1422/// println!("Yule distance: {}", dist);
1423/// ```
1424#[allow(dead_code)]
1425pub fn yule<T: Float>(point1: &[bool], point2: &[bool]) -> T {
1426    if point1.len() != point2.len() {
1427        return T::nan();
1428    }
1429
1430    let mut n_true_true = 0;
1431    let mut n_true_false = 0;
1432    let mut n_false_true = 0;
1433    let mut n_false_false = 0;
1434
1435    for i in 0..point1.len() {
1436        if point1[i] && point2[i] {
1437            n_true_true += 1;
1438        } else if point1[i] && !point2[i] {
1439            n_true_false += 1;
1440        } else if !point1[i] && point2[i] {
1441            n_false_true += 1;
1442        } else {
1443            n_false_false += 1;
1444        }
1445    }
1446
1447    let num = T::from(2 * n_true_false * n_false_true).unwrap();
1448    let denom = T::from(n_true_true * n_false_false + n_true_false * n_false_true).unwrap();
1449
1450    if denom > T::zero() {
1451        num / denom
1452    } else {
1453        T::zero()
1454    }
1455}
1456
1457#[cfg(test)]
1458mod tests {
1459    use super::*;
1460    use approx::assert_relative_eq;
1461    use ndarray::arr2;
1462
1463    #[test]
1464    fn test_euclidean_distance() {
1465        let point1 = &[1.0, 2.0, 3.0];
1466        let point2 = &[4.0, 5.0, 6.0];
1467
1468        assert_relative_eq!(euclidean(point1, point2), 5.196152422706632, epsilon = 1e-6);
1469    }
1470
1471    #[test]
1472    fn test_manhattan_distance() {
1473        let point1 = &[1.0, 2.0, 3.0];
1474        let point2 = &[4.0, 5.0, 6.0];
1475
1476        assert_relative_eq!(manhattan(point1, point2), 9.0, epsilon = 1e-6);
1477    }
1478
1479    #[test]
1480    fn test_chebyshev_distance() {
1481        let point1 = &[1.0, 2.0, 3.0];
1482        let point2 = &[4.0, 5.0, 6.0];
1483
1484        assert_relative_eq!(chebyshev(point1, point2), 3.0, epsilon = 1e-6);
1485    }
1486
1487    #[test]
1488    fn test_minkowski_distance() {
1489        let point1 = &[1.0, 2.0, 3.0];
1490        let point2 = &[4.0, 5.0, 6.0];
1491
1492        // p = 1 (Manhattan)
1493        assert_relative_eq!(minkowski(point1, point2, 1.0), 9.0, epsilon = 1e-6);
1494
1495        // p = 2 (Euclidean)
1496        assert_relative_eq!(
1497            minkowski(point1, point2, 2.0),
1498            5.196152422706632,
1499            epsilon = 1e-6
1500        );
1501
1502        // p = infinity (Chebyshev)
1503        assert_relative_eq!(
1504            minkowski(point1, point2, f64::INFINITY),
1505            3.0,
1506            epsilon = 1e-6
1507        );
1508
1509        // p = 3
1510        assert_relative_eq!(minkowski(point1, point2, 3.0), 4.3267, epsilon = 1e-4);
1511    }
1512
1513    #[test]
1514    fn test_canberra_distance() {
1515        let point1 = &[1.0, 2.0, 3.0];
1516        let point2 = &[4.0, 5.0, 6.0];
1517
1518        assert_relative_eq!(canberra(point1, point2), 1.5, epsilon = 1e-6);
1519    }
1520
1521    #[test]
1522    fn test_cosine_distance() {
1523        // Orthogonal vectors should have distance 1
1524        let point1 = &[1.0, 0.0];
1525        let point2 = &[0.0, 1.0];
1526
1527        assert_relative_eq!(cosine(point1, point2), 1.0, epsilon = 1e-6);
1528
1529        // Parallel vectors should have distance 0
1530        let point3 = &[1.0, 2.0];
1531        let point4 = &[2.0, 4.0];
1532
1533        assert_relative_eq!(cosine(point3, point4), 0.0, epsilon = 1e-6);
1534
1535        // 45 degree angle should have distance 1 - sqrt(2)/2
1536        let point5 = &[1.0, 1.0];
1537        let point6 = &[1.0, 0.0];
1538
1539        assert_relative_eq!(cosine(point5, point6), 0.2928932188134525, epsilon = 1e-6);
1540    }
1541
1542    #[test]
1543    fn test_correlation_distance() {
1544        // Perfectly anti-correlated should have distance 2
1545        let point1 = &[1.0, 2.0, 3.0];
1546        let point2 = &[3.0, 2.0, 1.0];
1547
1548        assert_relative_eq!(correlation(point1, point2), 2.0, epsilon = 1e-6);
1549
1550        // Perfectly correlated should have distance 0
1551        let point3 = &[1.0, 2.0, 3.0];
1552        let point4 = &[2.0, 4.0, 6.0];
1553
1554        assert_relative_eq!(correlation(point3, point4), 0.0, epsilon = 1e-6);
1555    }
1556
1557    #[test]
1558    fn test_jaccard_distance() {
1559        let point1 = &[1.0, 0.0, 1.0];
1560        let point2 = &[0.0, 1.0, 1.0];
1561
1562        // 1 element in common, 2 elements different = 2/3
1563        assert_relative_eq!(jaccard(point1, point2), 2.0 / 3.0, epsilon = 1e-6);
1564
1565        // Empty sets should have distance 0
1566        let point3 = &[0.0, 0.0, 0.0];
1567        let point4 = &[0.0, 0.0, 0.0];
1568
1569        assert_relative_eq!(jaccard(point3, point4), 0.0, epsilon = 1e-6);
1570
1571        // No elements in common should have distance 1
1572        let point5 = &[1.0, 1.0, 0.0];
1573        let point6 = &[0.0, 0.0, 1.0];
1574
1575        assert_relative_eq!(jaccard(point5, point6), 1.0, epsilon = 1e-6);
1576    }
1577
1578    #[test]
1579    fn test_pdist() {
1580        let points = arr2(&[[0.0, 0.0], [1.0, 0.0], [0.0, 1.0]]);
1581
1582        let dist_matrix = pdist(&points, euclidean);
1583
1584        assert_eq!(dist_matrix.shape(), &[3, 3]);
1585
1586        // Check diagonal is zero
1587        assert_relative_eq!(dist_matrix[(0, 0)], 0.0, epsilon = 1e-6);
1588        assert_relative_eq!(dist_matrix[(1, 1)], 0.0, epsilon = 1e-6);
1589        assert_relative_eq!(dist_matrix[(2, 2)], 0.0, epsilon = 1e-6);
1590
1591        // Check off-diagonal elements
1592        assert_relative_eq!(dist_matrix[(0, 1)], 1.0, epsilon = 1e-6);
1593        assert_relative_eq!(dist_matrix[(0, 2)], 1.0, epsilon = 1e-6);
1594        assert_relative_eq!(
1595            dist_matrix[(1, 2)],
1596            std::f64::consts::SQRT_2,
1597            epsilon = 1e-6
1598        );
1599
1600        // Check symmetry
1601        assert_relative_eq!(dist_matrix[(1, 0)], dist_matrix[(0, 1)], epsilon = 1e-6);
1602        assert_relative_eq!(dist_matrix[(2, 0)], dist_matrix[(0, 2)], epsilon = 1e-6);
1603        assert_relative_eq!(dist_matrix[(2, 1)], dist_matrix[(1, 2)], epsilon = 1e-6);
1604    }
1605
1606    #[test]
1607    fn test_cdist() {
1608        let x_a = arr2(&[[0.0, 0.0], [1.0, 0.0]]);
1609
1610        let xb = arr2(&[[0.0, 1.0], [1.0, 1.0]]);
1611
1612        let dist_matrix = cdist(&x_a, &xb, euclidean).unwrap();
1613
1614        assert_eq!(dist_matrix.shape(), &[2, 2]);
1615
1616        assert_relative_eq!(dist_matrix[(0, 0)], 1.0, epsilon = 1e-6);
1617        assert_relative_eq!(
1618            dist_matrix[(0, 1)],
1619            std::f64::consts::SQRT_2,
1620            epsilon = 1e-6
1621        );
1622        assert_relative_eq!(
1623            dist_matrix[(1, 0)],
1624            std::f64::consts::SQRT_2,
1625            epsilon = 1e-6
1626        );
1627        assert_relative_eq!(dist_matrix[(1, 1)], 1.0, epsilon = 1e-6);
1628    }
1629
1630    #[test]
1631    fn test_braycurtis_distance() {
1632        let point1 = &[1.0, 2.0, 3.0];
1633        let point2 = &[2.0, 3.0, 4.0];
1634
1635        let dist = braycurtis(point1, point2);
1636        // Sum of differences: |1-2| + |2-3| + |3-4| = 3
1637        // Sum of absolute sums: |1+2| + |2+3| + |3+4| = 3 + 5 + 7 = 15
1638        // Distance: 3/15 = 1/5 = 0.2
1639        assert_relative_eq!(dist, 0.2, epsilon = 1e-6);
1640
1641        // Test identical vectors (should be 0)
1642        let point3 = &[1.0, 2.0, 3.0];
1643        let point4 = &[1.0, 2.0, 3.0];
1644        assert_relative_eq!(braycurtis(point3, point4), 0.0, epsilon = 1e-6);
1645
1646        // Test with zeros in both vectors
1647        let point5 = &[0.0, 0.0];
1648        let point6 = &[0.0, 0.0];
1649        assert_relative_eq!(braycurtis(point5, point6), 0.0, epsilon = 1e-6);
1650    }
1651
1652    #[test]
1653    fn test_squareform() {
1654        // Test conversion from condensed to square form
1655        let condensed = vec![1.0, 2.0, 3.0];
1656        let square = squareform(&condensed).unwrap();
1657
1658        assert_eq!(square.shape(), &[3, 3]);
1659        assert_relative_eq!(square[(0, 1)], 1.0, epsilon = 1e-6);
1660        assert_relative_eq!(square[(0, 2)], 2.0, epsilon = 1e-6);
1661        assert_relative_eq!(square[(1, 2)], 3.0, epsilon = 1e-6);
1662
1663        // Test conversion from square to condensed form
1664        let condensed2 = squareform_to_condensed(&square).unwrap();
1665        assert_eq!(condensed2, condensed);
1666    }
1667}