use std::fmt::Display;
use serde::{Deserialize, Serialize};
use crate::{data::EventLike, vectors::Vec4, LadduError, LadduResult};
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Particle {
label: String,
source: ParticleSource,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum ParticleSource {
Stored {
p4_name: String,
},
Fixed {
p4: Vec4,
},
Missing,
Composite {
daughters: Box<[Particle; 2]>,
},
}
impl Particle {
pub fn stored(id: impl Into<String>) -> Self {
let id = id.into();
Self {
label: id.clone(),
source: ParticleSource::Stored { p4_name: id },
}
}
pub fn fixed(label: impl Into<String>, p4: Vec4) -> Self {
Self {
label: label.into(),
source: ParticleSource::Fixed { p4 },
}
}
pub fn missing(label: impl Into<String>) -> Self {
Self {
label: label.into(),
source: ParticleSource::Missing,
}
}
pub fn composite(
label: impl Into<String>,
daughters: (&Particle, &Particle),
) -> LadduResult<Self> {
let daughters = [daughters.0.clone(), daughters.1.clone()];
if daughters.iter().any(Self::contains_missing) {
return Err(LadduError::Custom(
"missing particles cannot be used as composite daughters".to_string(),
));
}
Ok(Self {
label: label.into(),
source: ParticleSource::Composite {
daughters: Box::new(daughters),
},
})
}
pub fn label(&self) -> &str {
&self.label
}
pub const fn source(&self) -> &ParticleSource {
&self.source
}
pub const fn is_missing(&self) -> bool {
matches!(self.source, ParticleSource::Missing)
}
pub(super) fn contains_missing(&self) -> bool {
self.is_missing() || self.daughters().iter().any(Self::contains_missing)
}
pub fn daughters(&self) -> &[Particle] {
match &self.source {
ParticleSource::Composite { daughters } => daughters.as_slice(),
_ => &[],
}
}
pub(super) fn contains_id(&self, particle: &str) -> bool {
if self.label() == particle {
return true;
}
self.daughters()
.iter()
.any(|daughter| daughter.contains_id(particle))
}
pub(super) fn parent_of_id(&self, child: &str) -> Option<&Particle> {
if self
.daughters()
.iter()
.any(|daughter| daughter.label() == child)
{
return Some(self);
}
self.daughters()
.iter()
.find_map(|daughter| daughter.parent_of_id(child))
}
}
impl Display for Particle {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.label)
}
}
pub(super) fn resolve_particle_direct(
event: &dyn EventLike,
particle: &Particle,
) -> LadduResult<Option<Vec4>> {
match particle.source() {
ParticleSource::Stored { p4_name } => event
.p4(p4_name)
.ok_or_else(|| LadduError::Custom(format!("unknown p4 column '{p4_name}'")))
.map(Some),
ParticleSource::Fixed { p4 } => Ok(Some(*p4)),
ParticleSource::Missing => Ok(None),
ParticleSource::Composite { daughters } => daughters
.iter()
.map(|daughter| {
resolve_particle_direct(event, daughter)?.ok_or_else(|| {
LadduError::Custom(format!(
"missing daughter '{}' cannot be resolved inside composite '{}'",
daughter.label(),
particle.label()
))
})
})
.try_fold(Vec4::new(0.0, 0.0, 0.0, 0.0), |acc, value| {
value.map(|value| acc + value)
})
.map(Some),
}
}