hora 0.1.1

Hora Search Everywhere
use crate::core::calc::same_dimension;
#[cfg(feature = "simd")]
use packed_simd::{f32x16, f64x8};

pub trait SIMDOptmized<T = Self> {
    fn dot_product(a: &[T], b: &[T]) -> Result<T, &'static str>;
    fn manhattan_distance(a: &[T], b: &[T]) -> Result<T, &'static str>;
    fn euclidean_distance(a: &[T], b: &[T]) -> Result<T, &'static str>;
}

macro_rules! simd_optimized_impl {
    (  $type_id:ident, $simd_type:ident ,$size: expr ,$simd_size:expr) => {
        impl SIMDOptmized for $type_id {
            fn dot_product(a: &[$type_id], b: &[$type_id]) -> Result<$type_id, &'static str> {
                assert_eq!(a.len(), b.len());

                #[cfg(feature = $simd_size)]
                {
                    let size = a.len() - (a.len() % $size);
                    let c = a
                        .chunks_exact($size)
                        .map($simd_type::from_slice_unaligned)
                        .zip(b.chunks_exact($size).map($simd_type::from_slice_unaligned))
                        .map(|(a, b)| a * b)
                        .sum::<$simd_type>()
                        .sum();
                    let d: $type_id = a[size..].iter().zip(&b[size..]).map(|(p, q)| p * q).sum();
                    Ok(-(c + d))
                }
                #[cfg(not(feature = $simd_size))]
                {
                    Ok(-(a.iter().zip(b).map(|(p, q)| p * q).sum::<$type_id>()))
                }
            }

            fn manhattan_distance(
                a: &[$type_id],
                b: &[$type_id],
            ) -> Result<$type_id, &'static str> {
                assert_eq!(a.len(), b.len());

                #[cfg(feature = $simd_size)]
                {
                    let size = a.len() - (a.len() % $size);
                    let c = a
                        .chunks_exact($size)
                        .map($simd_type::from_slice_unaligned)
                        .zip(b.chunks_exact($size).map($simd_type::from_slice_unaligned))
                        .map(|(a, b)| (a - b).abs())
                        .sum::<$simd_type>()
                        .sum();
                    let d: $type_id = a[size..]
                        .iter()
                        .zip(&b[size..])
                        .map(|(p, q)| (p - q).abs())
                        .sum();
                    Ok(c + d)
                }

                #[cfg(not(feature = $simd_size))]
                {
                    Ok(a.iter()
                        .zip(b)
                        .map(|(p, q)| (p - q).abs())
                        .sum::<$type_id>())
                }
            }

            fn euclidean_distance(
                a: &[$type_id],
                b: &[$type_id],
            ) -> Result<$type_id, &'static str> {
                same_dimension(a, b)?;

                #[cfg(feature = $simd_size)]
                {
                    let size = a.len() - (a.len() % $size);
                    let c = a
                        .chunks_exact($size)
                        .map($simd_type::from_slice_unaligned)
                        .zip(b.chunks_exact($size).map($simd_type::from_slice_unaligned))
                        .map(|(a, b)| {
                            let c = (a - b);
                            c * c
                        })
                        .sum::<$simd_type>()
                        .sum();

                    let d: $type_id = a[size..]
                        .iter()
                        .zip(&b[size..])
                        .map(|(p, q)| (p - q).powi(2))
                        .sum();
                    Ok((d + c))
                }
                #[cfg(not(feature = $simd_size))]
                {
                    Ok(a.iter()
                        .zip(b)
                        .map(|(p, q)| (p - q).powi(2))
                        .sum::<$type_id>())
                }
            }
        }
    };
}

simd_optimized_impl!(f32, f32x16, 16, "simd");
simd_optimized_impl!(f64, f64x8, 8, "simd");