use super::tensors::{ContravariantIndex, CovariantIndex, Matrix, Tensor};
use crate::typenum::consts::U2;
use crate::typenum::uint::Unsigned;
use crate::typenum::Pow;
use generic_array::{ArrayLength, GenericArray};
use std::fmt;
use std::ops::{Index, IndexMut};
pub trait CoordinateSystem: Sized {
type Dimension: Unsigned + ArrayLength<f64> + ArrayLength<usize>;
fn small(_: &Point<Self>) -> f64 {
0.01
}
fn dimension() -> usize {
Self::Dimension::to_usize()
}
}
pub struct Point<T: CoordinateSystem> {
x: GenericArray<f64, T::Dimension>,
}
impl<T> Point<T>
where
T: CoordinateSystem,
{
pub fn new(coords: GenericArray<f64, T::Dimension>) -> Point<T> {
Point { x: coords }
}
pub fn from_slice(coords: &[f64]) -> Point<T> {
Point {
x: GenericArray::clone_from_slice(coords),
}
}
pub fn coords_array(&self) -> &GenericArray<f64, T::Dimension> {
&self.x
}
}
impl<T> Clone for Point<T>
where
T: CoordinateSystem,
{
fn clone(&self) -> Point<T> {
Point::new(self.x.clone())
}
}
impl<T> Copy for Point<T>
where
T: CoordinateSystem,
<T::Dimension as ArrayLength<f64>>::ArrayType: Copy,
{
}
impl<T> Index<usize> for Point<T>
where
T: CoordinateSystem,
{
type Output = f64;
fn index(&self, idx: usize) -> &f64 {
&self.x[idx]
}
}
impl<T> IndexMut<usize> for Point<T>
where
T: CoordinateSystem,
{
fn index_mut(&mut self, idx: usize) -> &mut f64 {
&mut self.x[idx]
}
}
impl<T> PartialEq<Point<T>> for Point<T>
where
T: CoordinateSystem,
{
fn eq(&self, rhs: &Point<T>) -> bool {
(0..T::dimension()).all(|i| self[i] == rhs[i])
}
}
impl<T> Eq for Point<T> where T: CoordinateSystem {}
impl<T> fmt::Debug for Point<T>
where
T: CoordinateSystem,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Point{:?}", &self.x)
}
}
pub trait ConversionTo<T: CoordinateSystem + 'static>: CoordinateSystem
where
T::Dimension: Pow<U2>,
<T::Dimension as Pow<U2>>::Output: ArrayLength<f64>,
{
fn convert_point(p: &Point<Self>) -> Point<T>;
fn jacobian(p: &Point<Self>) -> Matrix<T> {
let d = Self::dimension();
let mut result = Matrix::zero(Self::convert_point(p));
let h = Self::small(p);
for j in 0..d {
let mut x = p.clone();
x[j] = x[j] - h;
let y1 = Self::convert_point(&x);
x[j] = x[j] + h * 2.0;
let y2 = Self::convert_point(&x);
for i in 0..d {
let index = [i, j];
result[&index[..]] = (y2[i] - y1[i]) / (2.0 * h);
}
}
result
}
fn inv_jacobian(p: &Point<Self>) -> Tensor<T, (CovariantIndex, ContravariantIndex)> {
ConversionTo::<T>::jacobian(p).inverse().unwrap()
}
}