use num_traits::{Zero, real::Real as _};
use std::ops::{Add, Index, IndexMut, Mul, Neg, Sub};
#[cfg(feature = "testing")]
use super::Chart;
use super::{Field, LieGroup, Real, Metric};
use crate::impl_group_via_add;
pub trait Euclidean: Bilinear<F: Real> + InnerProduct {
#[cfg(feature = "testing")]
fn check_pythagorean(a: &Self, b: &Self) -> bool
where
Self: Sub<Output = Self> + Clone,
{
let dist_sq = a.distance(b);
let dist_sq = dist_sq * dist_sq;
let diff = a.clone() - b.clone();
let norm_sq = diff.norm_squared();
dist_sq == norm_sq
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct Dual<V: Vector>(V);
impl<V: Vector> Dual<V> {
pub fn from_raw(v: V) -> Self {
Self(v)
}
pub fn to_raw(v: Self) -> V {
v.0
}
}
impl<V: Vector> Vector for Dual<V> {
type F = V::F;
const N: usize = V::N;
type Iter<'a>
= V::Iter<'a>
where
Self: 'a;
fn iter(&self) -> Self::Iter<'_> {
self.0.iter()
}
fn from_fn(f: impl Fn(usize) -> Self::F) -> Self {
Self(V::from_fn(f))
}
}
impl<V: Vector> Zero for Dual<V> {
fn zero() -> Self {
Self(V::zero())
}
fn is_zero(&self) -> bool {
V::is_zero(&self.0)
}
}
impl<V: Vector> Add<Self> for Dual<V> {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self(self.0 + rhs.0)
}
}
impl<V: Vector> Neg for Dual<V> {
type Output = Self;
fn neg(self) -> Self::Output {
Self(-self.0)
}
}
impl<V: Vector> Sub<Self> for Dual<V> {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
Self(self.0 - rhs.0)
}
}
impl<V: Vector> Mul<V::F> for Dual<V> {
type Output = Self;
fn mul(self, rhs: V::F) -> Self::Output {
Self(self.0 * rhs)
}
}
impl<V: Vector> Index<usize> for Dual<V> {
type Output = V::F;
fn index(&self, index: usize) -> &Self::Output {
&self.0[index]
}
}
impl<V: Vector> IndexMut<usize> for Dual<V> {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
&mut self.0[index]
}
}
pub trait Vector:
LieGroup<Self>
+ Add<Output = Self>
+ Sub<Output = Self>
+ Mul<Self::F, Output = Self>
+ Neg<Output = Self>
+ Zero
+ Index<usize, Output = Self::F>
+ IndexMut<usize>
+ Copy
+ std::fmt::Debug
{
type F: Field;
const N: usize;
type Iter<'a>: Iterator<Item = &'a Self::F>
where
Self: 'a;
fn iter(&self) -> Self::Iter<'_>;
fn from_fn(f: impl Fn(usize) -> Self::F) -> Self;
fn pairing(&self, rhs: &Dual<Self>) -> Self::F {
self.iter()
.zip(rhs.iter())
.fold(Self::F::zero(), |acc, (&a, &b)| acc + a * b)
}
fn collapse(v: Dual<Dual<Self>>) -> Self {
v.0.0
}
fn from_array<const N: usize>(arr: [Self::F; N]) -> Self {
const { assert!(Self::N == N) }
Self::from_fn(|i| arr[i])
}
fn to_array<const N: usize>(self) -> [Self::F; N] {
const { assert!(Self::N == N) }
std::array::from_fn(|i| self[i])
}
#[cfg(feature = "testing")]
fn check_global_chart(p: &Self, q: &Self) -> bool {
let chart = Self::chart_at(p);
chart.to_local(q).is_some()
}
}
pub trait Form: Vector {
fn flat(&self) -> Dual<Self>;
fn dot(&self, b: &Self) -> Self::F {
self.pairing(&b.flat())
}
fn self_dot(&self) -> Self::F {
self.dot(self)
}
#[cfg(feature = "testing")]
fn check_dot_agrees_with_pairing(a: &Self, b: &Self) -> bool {
a.pairing(&b.flat()) == a.dot(b)
}
#[cfg(feature = "testing")]
fn check_translation_invariance(a: &Self, b: &Self, c: &Self) -> bool
where
Self: Add<Output = Self> + Sub<Output = Self> + Clone,
{
let diff = a.clone() - b.clone();
let diff_translated = (a.clone() + c.clone()) - (b.clone() + c.clone());
diff.self_dot() == diff_translated.self_dot()
}
#[cfg(feature = "testing")]
fn check_global_geodesic_scaling(p: &Self, v: Self, t: Self::F) -> bool
where
Self: PartialEq,
{
let chart = Self::chart_at(p);
match (
chart.to_local(&chart.to_global(v * t)),
chart.to_local(&chart.to_global(v)),
) {
(Some(tv_local), Some(v_local)) => tv_local == v_local * t,
_ => false,
}
}
}
pub trait Nondegenerate: Form {
fn sharp(v: Dual<Self>) -> Self;
#[cfg(feature = "testing")]
fn check_isomorphism(a: &Self) -> bool
where
Self: PartialEq<Self>,
{
let flat = a.flat();
Self::sharp(flat) == *a && Dual::<Self>::sharp(flat.flat()) == flat
}
}
impl<V: Nondegenerate> Form for Dual<V> {
fn flat(&self) -> Dual<Self> {
Dual(Dual(V::sharp(*self)))
}
}
impl<V: Nondegenerate> Nondegenerate for Dual<V> {
fn sharp(v: Dual<Self>) -> Self {
v.0.0.flat()
}
}
impl_group_via_add!(V, V: Vector);
impl<E: Vector> LieGroup<E> for E {
fn identity_exp(v: E) -> Self {
v
}
fn identity_log(p: &Self) -> Option<E> {
Some(*p)
}
}
pub trait Bilinear: Sesquilinear {}
impl<F: Field<Fixed = F>, V: Sesquilinear<F = F>> Bilinear for V {}
pub trait Sesquilinear: Form {
fn norm_squared(&self) -> <Self::F as Field>::Fixed {
self.dot(self).to_fixed()
}
#[cfg(feature = "testing")]
fn check_hermitian_symmetry(a: Self, b: Self) -> bool {
a.dot(&b) == b.dot(&a).conj()
}
#[cfg(feature = "testing")]
fn check_additivity(a: Self, b: Self, c: Self) -> bool
where
Self: Add<Output = Self> + Clone,
{
(a.clone() + b.clone()).dot(&c) == a.dot(&c) + b.dot(&c)
}
#[cfg(feature = "testing")]
fn check_scalar_linearity(a: Self, c: Self, k: Self::F) -> bool
where
Self: Mul<Self::F, Output = Self> + Clone,
{
(a.clone() * k).dot(&c) == k * a.dot(&c)
}
}
pub trait InnerProduct: Sesquilinear + Metric<R = <Self::F as Field>::Fixed>
where
<Self::F as Field>::Fixed: Real,
{
fn norm(&self) -> <Self::F as Field>::Fixed {
self.norm_squared().sqrt()
}
#[cfg(feature = "testing")]
fn check_positive_definite(a: Self) -> bool
where
Self: Zero + PartialEq,
{
a == Self::zero() || a.norm() > <Self::F as Field>::Fixed::zero()
}
#[cfg(feature = "testing")]
fn check_metric_compatibility(a: Self, b: Self) -> bool {
a.sub(b).norm_squared().sqrt() == a.distance(&b)
}
}
impl<P: Sesquilinear + Metric<R = <Self::F as Field>::Fixed>> InnerProduct for P where
<Self::F as Field>::Fixed: Real
{
}