use crate::*;
use core::iter::{repeat, zip};
#[cfg(not(feature = "std"))]
use num_traits::*;
macro_rules! impl_norm {
($(($name:ident, $err:ident)),+) => {$(
/// Calculate the norm (vector length) according to the origin (zeros).
#[doc = concat![" [`Distance::", stringify!($err), "()`]."]]
#[inline]
fn $name(&self) -> f64 {
self.$err(&Origin)
}
)+};
}
#[inline]
fn sum(x: f64, y: f64) -> f64 {
x + y
}
fn impl_err<A, B, F1, F2>(a: &A, b: &B, map: F1, fold: F2) -> f64
where
A: Distance + ?Sized,
B: Distance + ?Sized,
F1: Fn(f64) -> f64,
F2: Fn(f64, f64) -> f64,
{
macro_rules! err_calc {
($iter:expr) => {
$iter.map(|x| map(cmp(x))).fold(0., fold)
};
}
#[inline]
fn cmp((a, b): (&f64, &f64)) -> f64 {
a - b
}
let a = a.as_components();
let b = b.as_components();
match (a.size_hint().1, b.size_hint().1) {
(None, None) => panic!("The size of the data is unknown"),
(Some(n), Some(m)) if n != m => {
if n > m {
err_calc!(zip(a, b.chain(repeat(&0.))))
} else {
err_calc!(zip(b, a.chain(repeat(&0.))))
}
}
_ => err_calc!(zip(a, b)),
}
}
pub trait Distance {
fn as_components(&self) -> impl Iterator<Item = &f64>;
fn square_err(&self, rhs: &impl Distance) -> f64 {
impl_err(self, rhs, util::pow2, sum)
}
fn l0_err(&self, rhs: &impl Distance) -> f64 {
let bin = |x: f64| (x.abs() < f64::EPSILON) as u8 as f64;
impl_err(self, rhs, bin, sum)
}
fn l1_err(&self, rhs: &impl Distance) -> f64 {
impl_err(self, rhs, |x| x.abs(), sum)
}
fn l2_err(&self, rhs: &impl Distance) -> f64 {
self.square_err(rhs).sqrt()
}
fn lp_err(&self, rhs: &impl Distance, p: f64) -> f64 {
impl_err(self, rhs, |x| x.abs().powf(p), sum).powf(p.recip())
}
fn linf_err(&self, rhs: &impl Distance) -> f64 {
impl_err(self, rhs, |x| x.abs(), f64::max)
}
impl_norm!(
(l0_norm, l0_err),
(l1_norm, l1_err),
(l2_norm, l2_err),
(linf_norm, linf_err)
);
fn lp_norm(&self, p: f64) -> f64 {
self.lp_err(&Origin, p)
}
}
struct Origin;
impl Distance for Origin {
fn as_components(&self) -> impl Iterator<Item = &f64> {
repeat(&0.)
}
}
impl Distance for [f64] {
fn as_components(&self) -> impl Iterator<Item = &f64> {
self.iter()
}
}
impl<const D: usize> Distance for [f64; D] {
fn as_components(&self) -> impl Iterator<Item = &f64> {
self.iter()
}
}
impl<const D: usize> Distance for Efd<D>
where
U<D>: EfdDim<D>,
{
fn as_components(&self) -> impl Iterator<Item = &f64> {
EfdComponents {
iter: self.coeffs_iter().flatten(),
size: self.harmonic() * D * 2,
}
}
}
struct EfdComponents<I> {
iter: I,
size: usize,
}
impl<'a, I> Iterator for EfdComponents<I>
where
I: Iterator<Item = &'a f64>,
{
type Item = &'a f64;
fn next(&mut self) -> Option<Self::Item> {
self.iter.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.size, Some(self.size))
}
}