use super::{
abstract_index::{AbstractIndex, AbstractIndexError},
dimension::DimensionError,
representation::{
BaseRepName, LibraryRep, LibrarySlot, RepName, Representation, RepresentationError,
},
};
use crate::structure::dimension::Dimension;
use bincode::Encode;
use serde::{Deserialize, Serialize};
use std::hash::Hash;
use std::{
cmp::Ordering,
fmt::{Debug, Display},
};
#[cfg(feature = "shadowing")]
use symbolica::{
atom::{Atom, AtomView, Symbol},
{function, symbol},
};
use thiserror::Error;
#[derive(
Debug,
Copy,
Clone,
Ord,
PartialOrd,
Eq,
PartialEq,
Hash,
Serialize,
Deserialize,
Encode,
bincode_trait_derive::Decode,
// bincode_trait_derive::BorrowDecodeFromDecode,
)]
#[cfg_attr(
feature = "shadowing",
trait_decode(trait = symbolica::state::HasStateMap),
)]
pub struct Slot<T: RepName, Aind = AbstractIndex> {
pub(crate) rep: Representation<T>,
pub aind: Aind,
}
impl<T: RepName, Aind> Slot<T, Aind> {
pub fn cast<U: RepName + From<T>>(self) -> Slot<U, Aind> {
Slot {
aind: self.aind,
rep: Representation {
dim: self.rep.dim,
rep: U::from(self.rep.rep),
},
}
}
}
#[derive(Error, Debug)]
pub enum SlotError {
#[error("Dimension is not concrete")]
NotConcrete,
#[error("Empty structure")]
EmptyStructure,
#[error("Argument is not a natural number")]
NotNatural,
#[error("Abstract index error :{0}")]
AindError(#[from] AbstractIndexError),
#[error("Representation error :{0}")]
RepError(#[from] RepresentationError),
#[error("Argument is not a number")]
NotNumber,
#[error("No more arguments")]
NoMoreArguments,
#[error("Too many arguments")]
TooManyArguments,
#[error("Not a slot, isn't a representation")]
NotRepresentation,
#[error("Not a slot, is composite")]
Composite,
#[error("{0}")]
DimErr(#[from] DimensionError),
#[error("{0}")]
Any(#[from] anyhow::Error),
}
#[cfg(feature = "shadowing")]
impl<'a, T: RepName, Aind> TryFrom<AtomView<'a>> for Slot<T, Aind>
where
Aind: ParseableAind,
{
type Error = SlotError;
fn try_from(value: AtomView<'a>) -> Result<Self, Self::Error> {
let (rep, mut iter) = if let AtomView::Fun(f) = value {
let name = f.get_symbol();
let innerf = f.iter().next().ok_or(SlotError::Composite)?;
if let AtomView::Fun(innerf) = innerf {
let rep =
T::try_from_symbol(innerf.get_symbol(), name).map_err(SlotError::RepError)?;
(rep, innerf.iter())
} else {
let rep = T::try_from_symbol_coerced(name).map_err(SlotError::RepError)?;
(rep, f.iter())
}
} else {
return Err(SlotError::Composite);
};
let dim: Dimension = if let Some(a) = iter.next() {
Dimension::try_from(a).map_err(SlotError::DimErr)?
} else {
return Err(SlotError::NoMoreArguments);
};
let index: Aind = if let Some(a) = iter.next() {
Ok(Aind::from_view(a).map_err(|a| a.into())?)
} else {
Err(SlotError::NoMoreArguments)
}?;
if if let Some(a) = iter.next() {
Ok(Aind::from_view(a).map_err(|a| a.into())?)
} else {
Err(SlotError::NoMoreArguments)
}
.is_ok()
{
return Err(SlotError::TooManyArguments);
}
Ok(Slot {
rep: Representation { dim, rep },
aind: index,
})
}
}
pub trait ConstructibleSlot<T: RepName, Aind> {
fn new(rep: T, dim: Dimension, aind: Aind) -> Self;
}
impl<T: BaseRepName, Aind> ConstructibleSlot<T, Aind> for Slot<T, Aind> {
fn new(_: T, dim: Dimension, aind: Aind) -> Self {
Slot {
aind,
rep: Representation {
dim,
rep: T::default(),
},
}
}
}
pub trait AbsInd:
Copy + PartialEq + Eq + Debug + Clone + Hash + Ord + Display + Send + Sync + 'static
{
}
#[cfg(feature = "shadowing")]
pub trait ParseableAind: Sized {
type Error: Into<SlotError>;
fn from_view(view: AtomView<'_>) -> Result<Self, Self::Error>;
fn to_atom(&self) -> Atom;
}
pub trait DummyAind {
fn new_dummy() -> Self;
fn new_dummy_at(i: usize) -> Self;
fn is_dummy(&self) -> bool;
}
pub trait IsAbstractSlot: Copy + PartialEq + Eq + Debug + Clone + Hash + Ord + Display {
type Aind: AbsInd;
type R: RepName;
fn reindex(self, id: Self::Aind) -> Self;
fn dim(&self) -> Dimension;
fn to_dummy_rep(&self) -> LibrarySlot<Self::Aind> {
let rep = self.rep().to_dummy().to_lib();
let aind = self.aind();
Slot { rep, aind }
}
fn to_dummy_ind(&self) -> LibrarySlot<Self::Aind>
where
Self::Aind: DummyAind,
{
Slot {
rep: self.rep().to_lib(),
aind: Self::Aind::new_dummy(),
}
}
fn to_lib(&self) -> LibrarySlot<Self::Aind> {
let rep: LibraryRep = self.rep_name().into();
rep.new_slot(self.dim(), self.aind())
}
fn aind(&self) -> Self::Aind;
fn set_aind(&mut self, aind: Self::Aind);
fn rep_name(&self) -> Self::R;
fn rep(&self) -> Representation<Self::R> {
Representation {
dim: self.dim(),
rep: self.rep_name(),
}
}
#[cfg(feature = "shadowing")]
fn to_atom(&self) -> Atom
where
Self::Aind: ParseableAind;
#[cfg(feature = "shadowing")]
fn to_symbolic_wrapped(&self) -> Atom
where
Self::Aind: ParseableAind;
}
pub trait DualSlotTo: IsAbstractSlot {
type Dual: IsAbstractSlot;
fn dual(&self) -> Self::Dual;
fn matches(&self, other: &Self::Dual) -> bool;
fn match_cmp(&self, other: &Self::Dual) -> Ordering;
}
impl<T: RepName, A: AbsInd> IsAbstractSlot for Slot<T, A> {
type Aind = A;
type R = T;
fn dim(&self) -> Dimension {
self.rep.dim
}
fn reindex(mut self, id: Self::Aind) -> Self {
self.aind = id;
self
}
fn aind(&self) -> Self::Aind {
self.aind
}
fn rep_name(&self) -> Self::R {
self.rep.rep
}
fn set_aind(&mut self, aind: Self::Aind) {
self.aind = aind;
}
#[cfg(feature = "shadowing")]
fn to_atom(&self) -> Atom
where
Self::Aind: ParseableAind,
{
self.rep.to_symbolic([self.aind.to_atom()])
}
#[cfg(feature = "shadowing")]
fn to_symbolic_wrapped(&self) -> Atom
where
Self::Aind: ParseableAind,
{
use symbolica::function;
self.rep
.to_symbolic([function!(symbol!("indexid"), self.aind.to_atom())])
}
}
impl<T: RepName, Aind: AbsInd> std::fmt::Display for Slot<T, Aind> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.rep.rep.is_self_dual() {
write!(f, "{}|{}", self.rep, self.aind)
} else if self.rep.rep.is_dual() {
write!(f, "{}|{:-}", self.rep, self.aind)
} else {
write!(f, "{}|{:+}", self.rep, self.aind)
}
}
}
#[cfg(feature = "shadowing")]
impl<T: RepName, Aind: AbsInd> Slot<T, Aind>
where
Atom: From<Aind>,
{
pub fn to_pattern(&self, dimension: Symbol) -> Atom {
self.rep
.rep
.to_symbolic([Atom::var(dimension), Atom::from(self.aind)])
}
}
impl<T: RepName, Aind: AbsInd> DualSlotTo for Slot<T, Aind> {
type Dual = Slot<T::Dual, Aind>;
fn dual(&self) -> Slot<T::Dual, Aind> {
Slot {
rep: self.rep.dual(),
aind: self.aind,
}
}
fn matches(&self, other: &Self::Dual) -> bool {
self.rep.matches(&other.rep) && self.aind() == other.aind()
}
fn match_cmp(&self, other: &Self::Dual) -> Ordering {
self.rep
.match_cmp(&other.rep)
.then(self.aind.cmp(&other.aind))
}
}
#[cfg(test)]
#[cfg(feature = "shadowing")]
mod shadowing_tests {
use insta::assert_snapshot;
use symbolica::{atom::AtomCore, parse, symbol};
use crate::structure::{
representation::{DualLorentz, LibraryRep, Lorentz, RepName, Representation},
slot::{DualSlotTo, IsAbstractSlot, Slot},
};
#[test]
fn doc_slot() {
let mink: Representation<Lorentz> = Lorentz {}.new_rep(4);
let mud: Slot<Lorentz> = mink.slot(0);
let muu: Slot<DualLorentz> = mink.slot(0).dual();
assert!(mud.matches(&muu));
assert_eq!("lor🠓4|₀", format!("{muu}"));
let custom_mink = LibraryRep::new_dual("custom_lor").unwrap();
let nud: Slot<LibraryRep> = custom_mink.new_slot(4, 0);
let nuu: Slot<LibraryRep> = nud.dual();
assert!(nuu.matches(&nud));
assert_eq!("custom_lor🠓4|₀", format!("{nuu}"));
}
#[test]
fn to_symbolic() {
let mink = Lorentz {}.new_rep(4);
let mu: Slot<Lorentz> = mink.slot(0);
println!("{}", mu.to_atom());
assert_snapshot!( mu.to_atom().to_canonical_string(),@"spenso::{}::lor(4,0)");
let mink = Lorentz {}.new_rep(4);
let mu: Slot<Lorentz> = mink.slot(0);
let atom = mu.to_atom();
let slot = Slot::try_from(atom.as_view()).unwrap();
assert_eq!(slot, mu);
}
#[test]
fn slot_from_atom_view() {
let mink = Lorentz {}.new_rep(4);
let mu = mink.slot(0);
let atom = mu.to_atom();
assert_eq!(Slot::try_from(atom.as_view()).unwrap(), mu);
assert_eq!(
Slot::<Lorentz>::try_from(atom.as_view()).unwrap().dual(),
mu.dual()
);
assert_eq!(
Slot::try_from(mu.dual().to_atom().as_view()).unwrap(),
mu.dual()
);
let expr = parse!("dind(lor(4,-1))");
let _slot: Slot<LibraryRep> = Slot::try_from(expr.as_view()).unwrap();
let _slot: Slot<DualLorentz> = Slot::try_from(expr.as_view()).unwrap();
println!("{}", _slot.to_symbolic_wrapped());
println!("{}", _slot.to_pattern(symbol!("d_")));
}
}