use core::ops::Mul;
use num_traits::{Inv, One, Zero};
use crate::{
complex::Complex,
coords::Coords,
impl_group_via_mul, impl_lie_group_via_quotient, impl_vector_ops,
matrix::{Matrix, MatrixExponential},
traits::{
Atomic, BothSided, CField, Dual, Form, LieGroup, NatZero, Nondegenerate, Point, Quotient,
Real, Right, RootOfUnity, Sesquilinear, Tensor,
},
};
pub type Minkowski<R> = Coords<R, 4, 1>;
#[derive(Debug, Copy, Clone)]
pub struct Sl<V: Tensor<F: CField>, const N: usize>(Matrix<V, N>);
pub type Sl2c<R> = Sl<Coords<Complex<R>, 2>, 2>;
impl<V: Tensor<F: CField>, const N: usize> PartialEq for Sl<V, N> {
fn eq(&self, other: &Self) -> bool {
self.0.eq(&other.0)
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct Lorentz<R: Real>(Sl2c<R>);
impl<R: Real> Quotient<Sl2c<R>, RootOfUnity<Complex<R>, 2>, SlAlgebra<Complex<R>, 2, 3>>
for Lorentz<R>
{
fn new(g: Sl2c<R>) -> Self {
let neg_g = Sl(g.0 * (-Complex::<R>::one()));
let [re, im] = g.0.trace().into();
if R::zero() < re {
return Self(g);
}
if re < R::zero() {
return Self(neg_g);
}
if R::zero() < im {
return Self(g);
}
if im < R::zero() {
return Self(neg_g);
}
let g_wins =
g.0.flat_iter()
.zip(neg_g.0.flat_iter())
.find_map(|(&a, &b)| {
let [are, aim] = a.into();
let [bre, bim] = b.into();
if are < bre {
Some(true)
} else if bre < are {
Some(false)
} else if aim < bim {
Some(true)
} else if bim < aim {
Some(false)
} else {
None
}
})
.expect("g tolerantly equals -g despite det(g) = 1 — shouldn't be possible");
if g_wins { Self(g) } else { Self(neg_g) }
}
fn lift(&self) -> Sl2c<R> {
self.0
}
fn embed(h: RootOfUnity<Complex<R>, 2>) -> Sl2c<R> {
if h.is_one() {
Sl2c::one()
} else {
Sl(-Matrix::one())
}
}
}
impl_lie_group_via_quotient!(Lorentz<R>, Sl2c<R>, RootOfUnity<Complex<R>,2>, SlAlgebra<Complex<R>, 2, 3>, R: Real);
impl<V: Tensor<F: CField>, const N: usize> Sl<V, N> {
pub fn trace(&self) -> V::F {
self.0.trace()
}
}
impl<V: Tensor<F: CField>, const N: usize> One for Sl<V, N> {
fn one() -> Self {
Self(Matrix::one())
}
}
impl<V: Tensor<F: CField>, const N: usize> Mul for Sl<V, N> {
type Output = Self;
fn mul(self, rhs: Self) -> Self::Output {
Self(self.0 * rhs.0)
}
}
impl<V: Tensor<F: CField>, const N: usize> Inv for Sl<V, N> {
type Output = Self;
fn inv(self) -> Self::Output {
match N {
0 => self,
1 => self,
2 => {
let [[a, b], [c, d]] = self.0.destructure();
let mut output = Matrix::zero();
output[(0, 0)] = d;
output[(0, 1)] = -b;
output[(1, 0)] = -c;
output[(1, 1)] = a;
Sl(output)
}
_ => Sl(self.0.inverse()),
}
}
}
impl_group_via_mul!(Sl<V, N>, V: Tensor<F: CField>, const N: usize);
impl<F: CField<Characteristic = NatZero>> LieGroup<SlAlgebra<F, 2, 3>> for Sl<Coords<F, 2>, 2>
where
Matrix<Coords<F, 2>, 2>: MatrixExponential,
{
fn identity_exp(v: SlAlgebra<F, 2, 3>) -> Self {
Self(Matrix::exp(&v.matrix()))
}
fn identity_log(p: &Self) -> Option<SlAlgebra<F, 2, 3>> {
Matrix::log(&p.0).map(|x| SlAlgebra::from_matrix(x))
}
}
#[derive(Debug, Copy, Clone)]
pub struct SlAlgebra<F: CField<Characteristic = NatZero>, const N: usize, const D: usize>(
Coords<F, D>,
);
impl<F: CField<Characteristic = NatZero>, const N: usize, const D: usize> PartialEq
for SlAlgebra<F, N, D>
{
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl<F: CField<Characteristic = NatZero>, const N: usize, const D: usize> From<Coords<F, D>>
for SlAlgebra<F, N, D>
{
fn from(value: Coords<F, D>) -> Self {
const {
assert!(D == N * N - 1);
}
Self(value)
}
}
impl<F: CField<Characteristic = NatZero>, const N: usize, const D: usize> From<[F; D]>
for SlAlgebra<F, N, D>
{
fn from(value: [F; D]) -> Self {
const {
assert!(D == N * N - 1);
}
Coords::from(value).into()
}
}
impl<F: CField<Characteristic = NatZero>, const N: usize, const D: usize> From<SlAlgebra<F, N, D>>
for [F; D]
{
fn from(value: SlAlgebra<F, N, D>) -> Self {
value.0.into()
}
}
impl_vector_ops!(SlAlgebra<F, N, D>, F: CField<Characteristic = NatZero>, const N: usize, const D: usize);
impl<F: CField<Characteristic = NatZero>, const N: usize, const D: usize> SlAlgebra<F, N, D> {
fn matrix(&self) -> Matrix<Coords<F, N>, N> {
let mut out = [[F::zero(); N]; N];
let mut index = 0;
for i in 0..N {
for j in 0..N {
if i != j {
out[i][j] = self[index];
index += 1;
}
}
}
for k in 0..N - 1 {
let c = self[index];
out[k][k] = out[k][k] + c;
out[k + 1][k + 1] = out[k + 1][k + 1] - c;
index += 1;
}
Matrix::new(out)
}
fn from_matrix(m: Matrix<Coords<F, N>, N>) -> Self {
const {
assert!(D == N * N - 1);
}
let mut out = [F::zero(); D];
let mut index = 0;
for i in 0..N {
for j in 0..N {
if i != j {
out[index] = m[(i, j)];
index += 1;
}
}
}
let mut accum = F::zero();
for k in 0..N - 1 {
accum = accum + m[(k, k)];
out[index] = accum;
index += 1;
}
Self(out.into())
}
}
fn offdiag_index<const N: usize>(i: usize, j: usize) -> usize {
debug_assert!(i != j);
let before = i * (N - 1);
before + if j < i { j } else { j - 1 }
}
impl<F: CField<Characteristic = NatZero>, const N: usize, const D: usize> Form
for SlAlgebra<F, N, D>
{
fn flat(&self) -> Dual<Self> {
let mut out = *self;
for i in 0..N {
for j in (i + 1)..N {
let a = offdiag_index::<N>(i, j);
let b = offdiag_index::<N>(j, i);
out.0.swap(a, b);
}
}
let base = N * (N - 1);
for i in 0..N - 1 {
let mut x = self[base + i] + self[base + i];
if i > 0 {
x = x - self[base + i - 1];
}
if i + 1 < N - 1 {
x = x - self[base + i + 1];
}
out[base + i] = x;
}
Dual::from_raw(out)
}
}
impl<F: CField<Characteristic = NatZero>, const N: usize, const D: usize> Nondegenerate
for SlAlgebra<F, N, D>
{
fn sharp(v: Dual<Self>) -> Self {
let mut out = Dual::to_raw(v);
for i in 0..N {
for j in (i + 1)..N {
let a = offdiag_index::<N>(i, j);
let b = offdiag_index::<N>(j, i);
out.0.swap(a, b);
}
}
let base = N * (N - 1);
for i in 0..N - 1 {
let mut sum = F::zero();
for j in 0..N - 1 {
let coeff_num = (usize::min(i, j) + 1) * (N - usize::max(i, j) - 1);
let coeff = F::from_nat(coeff_num).div(F::from_nat(N));
sum = sum + coeff * v[j + base];
}
out[base + i] = sum;
}
out
}
}
impl<F: CField<Fixed = F, Characteristic = NatZero>, const N: usize, const D: usize> Sesquilinear
for SlAlgebra<F, N, D>
{
}
impl<F: CField<Characteristic = NatZero>, const N: usize, const D: usize> Tensor
for SlAlgebra<F, N, D>
{
type F = F;
type Hand = Right;
type Action = BothSided;
type Normalization = Atomic;
type Array<T: Point> = [T; D];
fn from_fn(f: impl FnMut(usize) -> Self::F) -> Self {
const {
assert!(D == N * N - 1);
}
Self(Coords::<F, D>::from_fn(f))
}
}
impl<F: CField<Characteristic = NatZero>, const N: usize, const D: usize> AsRef<[F; D]>
for SlAlgebra<F, N, D>
{
fn as_ref(&self) -> &[F; D] {
&self.0
}
}
impl<F: CField<Characteristic = NatZero>, const N: usize, const D: usize> AsMut<[F; D]>
for SlAlgebra<F, N, D>
{
fn as_mut(&mut self) -> &mut [F; D] {
&mut self.0
}
}