use std::borrow::Cow;
use std::collections::{HashMap, HashSet};
use std::fmt;
use crate::testing::specs::csp::{CspValidationResult, Event, Process, ProcessSpec, State};
use crate::trace::ConsumedTrace;
pub mod algebra;
pub mod operators;
pub mod verification;
pub use verification::{DeadlockChecker, DeterminismChecker, LivelockChecker};
#[derive(Debug, Clone, PartialEq)]
pub enum CompositionOperator {
Synchronized,
Interleaved,
Interface { events: Vec<&'static str> },
Alphabetized { left: Vec<&'static str>, right: Vec<&'static str> },
}
pub trait CompositionSpec {
fn process() -> Process;
fn metadata() -> CompositionMetadata {
CompositionMetadata::default()
}
fn verify_properties() -> Result<(), CompositionError> {
Ok(())
}
}
impl<T: CompositionSpec> ProcessSpec for T {
fn validate_trace(&self, trace: &ConsumedTrace) -> CspValidationResult {
let process = T::process();
process.validate_trace(trace)
}
fn to_process_cow(&self) -> Cow<'_, Process> {
Cow::Owned(T::process())
}
}
#[derive(Debug, Clone)]
pub struct CompositionMetadata {
pub name: &'static str,
pub description: Option<&'static str>,
pub component_processes: Vec<&'static str>,
pub sync_alphabet: HashSet<Event>,
pub properties: CompositionProperties,
}
impl Default for CompositionMetadata {
fn default() -> Self {
Self {
name: "UnnamedComposition",
description: None,
component_processes: Vec::new(),
sync_alphabet: HashSet::new(),
properties: CompositionProperties::default(),
}
}
}
#[derive(Debug, Clone, Default)]
pub struct CompositionProperties {
pub deadlock_free: Option<bool>,
pub livelock_free: Option<bool>,
pub deterministic: Option<bool>,
}
#[derive(Debug, Clone)]
pub enum CompositionError {
AlphabetMismatch { expected: HashSet<Event>, actual: HashSet<Event> },
DeadlockDetected { state: State },
LivelockDetected { cycle: Vec<State> },
NonDeterminismDetected { state: State, events: Vec<Event> },
InvalidComposition { reason: String },
ProcessConstructionFailed { reason: String },
}
impl fmt::Display for CompositionError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::AlphabetMismatch { expected, actual } => {
write!(f, "Alphabet mismatch: expected {:?}, got {:?}", expected, actual)
}
Self::DeadlockDetected { state } => write!(f, "Deadlock detected at state {}", state),
Self::LivelockDetected { cycle } => {
write!(f, "Livelock detected in cycle: {:?}", cycle)
}
Self::NonDeterminismDetected { state, events } => {
write!(f, "Non-determinism at state {}: events {:?}", state, events)
}
Self::InvalidComposition { reason } => write!(f, "Invalid composition: {}", reason),
Self::ProcessConstructionFailed { reason } => {
write!(f, "Process construction failed: {}", reason)
}
}
}
}
impl std::error::Error for CompositionError {}
#[derive(Debug, Clone)]
pub enum ProcessExpr {
Atomic { name: &'static str, process: Box<Process> },
Synchronized { left: Box<ProcessExpr>, right: Box<ProcessExpr> },
Interleaved { left: Box<ProcessExpr>, right: Box<ProcessExpr> },
InterfaceParallel { left: Box<ProcessExpr>, sync_alphabet: HashSet<Event>, right: Box<ProcessExpr> },
AlphabetizedParallel {
left: Box<ProcessExpr>,
left_alphabet: HashSet<Event>,
right_alphabet: HashSet<Event>,
right: Box<ProcessExpr>,
},
Sequential { first: Box<ProcessExpr>, second: Box<ProcessExpr> },
Hiding { process: Box<ProcessExpr>, hidden_events: HashSet<Event> },
Renaming { process: Box<ProcessExpr>, mapping: HashMap<Event, Event> },
ExternalChoice { left: Box<ProcessExpr>, right: Box<ProcessExpr> },
InternalChoice { left: Box<ProcessExpr>, right: Box<ProcessExpr> },
}
impl ProcessExpr {
pub fn atomic(name: &'static str, process: Process) -> Self {
Self::Atomic { name, process: Box::new(process) }
}
pub fn name(&self) -> String {
match self {
Self::Atomic { name, .. } => name.to_string(),
Self::Synchronized { left, right } => format!("({} || {})", left.name(), right.name()),
Self::Interleaved { left, right } => format!("({} ||| {})", left.name(), right.name()),
Self::InterfaceParallel { left, right, .. } => {
format!("({} [|A|] {})", left.name(), right.name())
}
Self::AlphabetizedParallel { left, right, .. } => {
format!("({} [|α|] {})", left.name(), right.name())
}
Self::Sequential { first, second } => {
format!("({} ; {})", first.name(), second.name())
}
Self::Hiding { process, .. } => format!("({} \\ A)", process.name()),
Self::Renaming { process, .. } => format!("({} [[r]])", process.name()),
Self::ExternalChoice { left, right } => {
format!("({} [] {})", left.name(), right.name())
}
Self::InternalChoice { left, right } => {
format!("({} |~| {})", left.name(), right.name())
}
}
}
pub fn evaluate(&self) -> Result<Process, CompositionError> {
match self {
Self::Atomic { process, .. } => Ok((**process).clone()),
Self::Synchronized { left, right } => {
let p = left.evaluate()?;
let q = right.evaluate()?;
Process::synchronized_parallel(&p, &q)
}
Self::Interleaved { left, right } => {
let p = left.evaluate()?;
let q = right.evaluate()?;
Process::interleaved_parallel(&p, &q)
}
Self::InterfaceParallel { left, sync_alphabet, right } => {
let p = left.evaluate()?;
let q = right.evaluate()?;
Process::interface_parallel(&p, &q, sync_alphabet.clone())
}
Self::AlphabetizedParallel { left, left_alphabet, right_alphabet, right } => {
let p = left.evaluate()?;
let q = right.evaluate()?;
Process::alphabetized_parallel(&p, left_alphabet.clone(), &q, right_alphabet.clone())
}
Self::Sequential { first, second } => {
let p = first.evaluate()?;
let q = second.evaluate()?;
Process::sequential(&p, &q)
}
Self::Hiding { process, hidden_events } => {
let p = process.evaluate()?;
p.hide(hidden_events.clone())
}
Self::Renaming { process, mapping } => {
let p = process.evaluate()?;
p.rename(mapping.clone())
}
Self::ExternalChoice { left, right } => {
let p = left.evaluate()?;
let q = right.evaluate()?;
Process::external_choice(&p, &q)
}
Self::InternalChoice { left, right } => {
let p = left.evaluate()?;
let q = right.evaluate()?;
Process::internal_choice(&p, &q)
}
}
}
}