use batch_impl::{batch_impl_only, batch_trait};
use crate::op::{Additive, Multiplicative};
use crate::tower::{
AbelianGroup, CommutativeRing, Field, FiniteDimInnerSpace, FiniteDimVectorSpace, FreeModule,
Group, InnerSpace, JoinSemilattice, Lattice, Loop, Magma, MeetSemilattice, Module, Monoid,
NormedSpace, Quasigroup, Ring, Semigroup, Semiring, VectorSpace,
};
#[batch_impl_only(
@trait[<Additive>,<Multiplicative>]
(<@trait<>>,).1..=16 impl{(A@..,)} #combine{( @(@A::combine(&self.@0, &rhs.@0),).. )},
)]
trait Magma<Op: Operator> {
fn combine(&self, rhs: &Self) -> Self;
}
#[batch_impl_only(
@trait[<Additive>,<Multiplicative>]
(<@trait<> >,).1..=16 impl{(A@..,)} #identity{( @(@A::identity(),).. )},
)]
trait Monoid<Op: Operator>: Semigroup<Op> {
fn identity() -> Self;
}
#[batch_impl_only(
@trait (<@trait<>>,).1..=16 impl{(A@..,)} #inverse{( @(@A::inverse(&self.@0),).. )},
)]
trait Group<Op: Operator>: Loop<Op> {
fn inverse(&self) -> Self;
}
#[batch_impl_only(
@trait<Additive, Multiplicative> (<@trait<>>,).1..=16 where
@1..: @trait<Additive, Multiplicative, Scalar = @0::Scalar>,
impl{(A@..,)} #Scalar{A0::Scalar} #scale{( @(@A::scale(&s, v.@0),).. )},
)]
trait Module<Oa: Operator, Om: Operator>: AbelianGroup<Oa> {
type Scalar;
fn scale(s: &Self::Scalar, v: Self) -> Self;
}
batch_trait! {
@tr_tup_to=(<@trait<> >,).1..=;
@am=Additive, Multiplicative;
Semigroup: @trait[<Additive>, <Multiplicative>] @tr_tup_to 16;
Quasigroup: @tr_tup_to 16;
Loop: @tr_tup_to 16;
AbelianGroup: @tr_tup_to 16;
Semiring: @tr_tup_to 16;
Ring: @tr_tup_to 16;
CommutativeRing: @tr_tup_to 16;
VectorSpace: @trait<@am> @tr_tup_to 16 where
@1..: @trait<@am,Scalar = @0::Scalar>,
Self::Scalar: Field<@am>,
;
Lattice: @tr_tup_to 12;
FiniteDimInnerSpace: @trait<@am> (f64,).1..=16;
}
#[batch_impl_only(
(<@trait>,).1..=12 impl{(A@..,)} #meet{( @(@A::meet(&self.@0, &other.@0),).. )},
)]
trait MeetSemilattice: Sized + PartialOrd {
fn meet(&self, other: &Self) -> Self;
}
#[batch_impl_only(
(<@trait>,).1..=12 impl{(A@..,)} #join{( @(@A::join(&self.@0, &other.@0),).. )},
)]
trait JoinSemilattice: Sized + PartialOrd {
fn join(&self, other: &Self) -> Self;
}
#[batch_impl_only(
@trait<Additive, Multiplicative> (f64,).1..=16 impl{(A@..,)} impl{@trait<>}
#RealField{f64}
#norm_squared{@(self.@0 * self.@0+)..0.}
#scale_real{(@(<f64 as Module<>>::scale(&r, self.@0),)..)},
)]
trait NormedSpace<Oa: Operator, Om: Operator>: VectorSpace<Oa, Om> {
type RealField;
fn norm_squared(&self) -> Self::RealField;
fn scale_real(&self, r: Self::RealField) -> Self;
}
#[batch_impl_only(
@trait<Additive, Multiplicative> (f64,).1..=16 impl{(A@..,)}
#inner_product{@(self.@0 * other.@0+)..0.},
)]
trait InnerSpace<Oa: Operator, Om: Operator>: NormedSpace<Oa, Om> {
fn inner_product(&self, other: &Self) -> Self::RealField;
}
#[batch_impl_only(
@trait<Additive, Multiplicative> (f64,).1..=16 impl{(A@..,)}
#dimension{@(1+)..0}
#canonical_basis_element{( @(if _i == @0 { 1.0 } else { 0.0 },).. )}
#dot{@(self.@0 * other.@0+)..0.},
)]
trait FiniteDimVectorSpace<Oa: Operator, Om: Operator>: VectorSpace<Oa, Om> {
fn dimension() -> usize;
fn canonical_basis_element(_i: usize) -> Self;
fn dot(&self, other: &Self) -> Self::Scalar;
}
#[batch_impl_only(
@trait<Additive, Multiplicative> (f64,).1..=16 impl{(A@..,)}
#rank{@(1+)..0}
#basis_element{( @(if _i == @0 { 1.0 } else { 0.0 },).. )}
#coordinate{match i { @( @0 => self.@0, ).. _ => unreachable!() }},
)]
trait FreeModule<Oa: Operator, Om: Operator>: Module<Oa, Om> {
fn rank() -> usize;
fn basis_element(_i: usize) -> Self;
fn coordinate(&self, i: usize) -> Self::Scalar;
}
#[cfg(test)]
mod tests {
use super::*;
use crate::tower::{FiniteDimVectorSpace, NormedSpace};
type A16 = (i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32);
type F16 = (f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64);
#[test]
fn arity_16_componentwise_add() {
let a: A16 = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
let b: A16 = (16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1);
let s = <A16 as Magma<Additive>>::combine(&a, &b);
assert_eq!(s.0, 17);
assert_eq!(s.7, 17);
assert_eq!(s.15, 17);
}
#[test]
fn arity_16_finite_dim() {
let v: F16 =
(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0);
assert_eq!(F16::dimension(), 16);
let e3 = F16::canonical_basis_element(2);
assert_eq!(e3.0, 0.0);
assert_eq!(e3.1, 0.0);
assert_eq!(e3.2, 1.0);
assert_eq!(e3.15, 0.0);
assert_eq!(v.dot(&v), 1496.0);
assert_eq!(v.norm_squared(), 1496.0);
}
#[test]
fn free_module_f64_tuples() {
use crate::tower::FreeModule;
type F2 = (f64, f64);
assert_eq!(<F2 as FreeModule<Additive, Multiplicative>>::rank(), 2);
let e0 = <F2 as FreeModule<Additive, Multiplicative>>::basis_element(0);
assert_eq!(e0.0, 1.0);
assert_eq!(e0.1, 0.0);
let e1 = <F2 as FreeModule<Additive, Multiplicative>>::basis_element(1);
assert_eq!(e1.0, 0.0);
assert_eq!(e1.1, 1.0);
let v = (3.0f64, 4.0);
assert_eq!(<F2 as FreeModule<Additive, Multiplicative>>::coordinate(&v, 0), 3.0);
assert_eq!(<F2 as FreeModule<Additive, Multiplicative>>::coordinate(&v, 1), 4.0);
}
}