use std::collections::{HashMap, HashSet, VecDeque};
use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum Action {
Input(String),
Output(String),
Tau,
}
impl Action {
pub fn input(name: &str) -> Self {
Action::Input(name.to_string())
}
pub fn output(name: &str) -> Self {
Action::Output(name.to_string())
}
pub fn is_tau(&self) -> bool {
matches!(self, Action::Tau)
}
pub fn is_visible(&self) -> bool {
!self.is_tau()
}
pub fn complement(&self) -> Action {
match self {
Action::Input(s) => Action::Output(s.clone()),
Action::Output(s) => Action::Input(s.clone()),
Action::Tau => Action::Tau,
}
}
pub fn channel(&self) -> Option<&str> {
match self {
Action::Input(s) | Action::Output(s) => Some(s.as_str()),
Action::Tau => None,
}
}
}
impl fmt::Display for Action {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Action::Input(s) => write!(f, "{}", s),
Action::Output(s) => write!(f, "{}̄", s),
Action::Tau => write!(f, "τ"),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum CcsProcess {
Nil,
Prefix(Action, Box<CcsProcess>),
Choice(Box<CcsProcess>, Box<CcsProcess>),
Parallel(Box<CcsProcess>, Box<CcsProcess>),
Restriction(Box<CcsProcess>, HashSet<String>),
Relabeling(Box<CcsProcess>, HashMap<String, String>),
Var(String),
Rec(String, Box<CcsProcess>),
}
impl CcsProcess {
pub fn prefix(a: Action, p: CcsProcess) -> Self {
CcsProcess::Prefix(a, Box::new(p))
}
pub fn choice(p: CcsProcess, q: CcsProcess) -> Self {
CcsProcess::Choice(Box::new(p), Box::new(q))
}
pub fn parallel(p: CcsProcess, q: CcsProcess) -> Self {
CcsProcess::Parallel(Box::new(p), Box::new(q))
}
pub fn restrict(p: CcsProcess, labels: HashSet<String>) -> Self {
CcsProcess::Restriction(Box::new(p), labels)
}
pub fn relabel(p: CcsProcess, map: HashMap<String, String>) -> Self {
CcsProcess::Relabeling(Box::new(p), map)
}
pub fn rec(var: &str, p: CcsProcess) -> Self {
CcsProcess::Rec(var.to_string(), Box::new(p))
}
pub fn substitute(&self, var: &str, sub: &CcsProcess) -> CcsProcess {
match self {
CcsProcess::Nil => CcsProcess::Nil,
CcsProcess::Prefix(a, p) => {
CcsProcess::Prefix(a.clone(), Box::new(p.substitute(var, sub)))
}
CcsProcess::Choice(p, q) => CcsProcess::Choice(
Box::new(p.substitute(var, sub)),
Box::new(q.substitute(var, sub)),
),
CcsProcess::Parallel(p, q) => CcsProcess::Parallel(
Box::new(p.substitute(var, sub)),
Box::new(q.substitute(var, sub)),
),
CcsProcess::Restriction(p, l) => {
CcsProcess::Restriction(Box::new(p.substitute(var, sub)), l.clone())
}
CcsProcess::Relabeling(p, f) => {
CcsProcess::Relabeling(Box::new(p.substitute(var, sub)), f.clone())
}
CcsProcess::Var(x) => {
if x == var {
sub.clone()
} else {
CcsProcess::Var(x.clone())
}
}
CcsProcess::Rec(x, p) => {
if x == var {
CcsProcess::Rec(x.clone(), p.clone())
} else {
CcsProcess::Rec(x.clone(), Box::new(p.substitute(var, sub)))
}
}
}
}
pub fn unfold(&self) -> CcsProcess {
match self {
CcsProcess::Rec(x, p) => {
let rec = self.clone();
p.substitute(x, &rec)
}
_ => self.clone(),
}
}
}
impl fmt::Display for CcsProcess {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
CcsProcess::Nil => write!(f, "0"),
CcsProcess::Prefix(a, p) => write!(f, "{}.{}", a, p),
CcsProcess::Choice(p, q) => write!(f, "({} + {})", p, q),
CcsProcess::Parallel(p, q) => write!(f, "({} | {})", p, q),
CcsProcess::Restriction(p, l) => {
let mut labels: Vec<&str> = l.iter().map(|s| s.as_str()).collect();
labels.sort();
write!(f, "({} \\ {{{}}})", p, labels.join(","))
}
CcsProcess::Relabeling(p, m) => {
let mut pairs: Vec<(&str, &str)> =
m.iter().map(|(k, v)| (k.as_str(), v.as_str())).collect();
pairs.sort();
let map_str: Vec<String> =
pairs.iter().map(|(k, v)| format!("{}/{}", v, k)).collect();
write!(f, "{}[{}]", p, map_str.join(","))
}
CcsProcess::Var(x) => write!(f, "{}", x),
CcsProcess::Rec(x, p) => write!(f, "μ{}.{}", x, p),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum CspProcess {
Stop,
Skip,
Prefix(String, Box<CspProcess>),
ExternalChoice(Box<CspProcess>, Box<CspProcess>),
InternalChoice(Box<CspProcess>, Box<CspProcess>),
AlphaParallel(Box<CspProcess>, HashSet<String>, Box<CspProcess>),
Sequential(Box<CspProcess>, Box<CspProcess>),
Interrupt(Box<CspProcess>, Box<CspProcess>),
Hide(Box<CspProcess>, HashSet<String>),
Ref(String),
}
impl fmt::Display for CspProcess {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
CspProcess::Stop => write!(f, "STOP"),
CspProcess::Skip => write!(f, "SKIP"),
CspProcess::Prefix(a, p) => write!(f, "{} → {}", a, p),
CspProcess::ExternalChoice(p, q) => write!(f, "({} □ {})", p, q),
CspProcess::InternalChoice(p, q) => write!(f, "({} ⊓ {})", p, q),
CspProcess::AlphaParallel(p, a, q) => {
let mut events: Vec<&str> = a.iter().map(|s| s.as_str()).collect();
events.sort();
write!(f, "({} ‖_{{{}}} {})", p, events.join(","), q)
}
CspProcess::Sequential(p, q) => write!(f, "({} ; {})", p, q),
CspProcess::Interrupt(p, q) => write!(f, "({} △ {})", p, q),
CspProcess::Hide(p, a) => {
let mut events: Vec<&str> = a.iter().map(|s| s.as_str()).collect();
events.sort();
write!(f, "({} \\ {{{}}})", p, events.join(","))
}
CspProcess::Ref(name) => write!(f, "{}", name),
}
}
}
pub type State = usize;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Transition {
pub source: State,
pub action: Action,
pub target: State,
}
impl Transition {
pub fn new(source: State, action: Action, target: State) -> Self {
Transition {
source,
action,
target,
}
}
}
impl fmt::Display for Transition {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} --{}--> {}", self.source, self.action, self.target)
}
}
#[derive(Debug, Clone)]
pub struct Lts {
pub num_states: usize,
pub initial: State,
pub transitions: Vec<Transition>,
}
impl Lts {
pub fn new(num_states: usize, initial: State, transitions: Vec<Transition>) -> Self {
Lts {
num_states,
initial,
transitions,
}
}
pub fn transitions_from(&self, s: State) -> Vec<&Transition> {
self.transitions.iter().filter(|t| t.source == s).collect()
}
pub fn transitions_by_action(&self, s: State, a: &Action) -> Vec<State> {
self.transitions
.iter()
.filter(|t| t.source == s && &t.action == a)
.map(|t| t.target)
.collect()
}
pub fn weak_transitions(&self, s: State, a: &Action) -> HashSet<State> {
if a.is_tau() {
let mut reachable = self.tau_closure(s);
reachable.insert(s);
reachable
} else {
let pre_states = {
let mut set = self.tau_closure(s);
set.insert(s);
set
};
let mut result = HashSet::new();
for pre in pre_states {
for &mid in &self.transitions_by_action(pre, a) {
let post = {
let mut set = self.tau_closure(mid);
set.insert(mid);
set
};
result.extend(post);
}
}
result
}
}
pub fn tau_closure(&self, s: State) -> HashSet<State> {
let mut visited = HashSet::new();
let mut queue = VecDeque::new();
queue.push_back(s);
while let Some(cur) = queue.pop_front() {
for &next in &self.transitions_by_action(cur, &Action::Tau) {
if visited.insert(next) {
queue.push_back(next);
}
}
}
visited
}
pub fn alphabet(&self) -> HashSet<Action> {
self.transitions.iter().map(|t| t.action.clone()).collect()
}
pub fn visible_alphabet(&self) -> HashSet<Action> {
self.alphabet()
.into_iter()
.filter(|a| a.is_visible())
.collect()
}
}
#[derive(Debug, Clone)]
pub struct BisimulationRelation {
pub pairs: HashSet<(State, State)>,
}
impl BisimulationRelation {
pub fn empty() -> Self {
BisimulationRelation {
pairs: HashSet::new(),
}
}
pub fn add(&mut self, s: State, t: State) {
self.pairs.insert((s, t));
}
pub fn contains(&self, s: State, t: State) -> bool {
self.pairs.contains(&(s, t))
}
pub fn size(&self) -> usize {
self.pairs.len()
}
}
pub type Trace = Vec<Action>;
#[derive(Debug, Clone)]
pub struct TraceSet {
pub traces: HashSet<Vec<Action>>,
}
impl TraceSet {
pub fn new() -> Self {
let mut traces = HashSet::new();
traces.insert(vec![]); TraceSet { traces }
}
pub fn contains(&self, trace: &[Action]) -> bool {
self.traces.contains(trace)
}
pub fn len(&self) -> usize {
self.traces.len()
}
pub fn is_empty(&self) -> bool {
self.traces.is_empty()
}
pub fn is_trivial(&self) -> bool {
self.traces.len() == 1 && self.traces.contains(&vec![])
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Failure {
pub trace: Vec<Action>,
pub refusal: HashSet<Action>,
}
impl Failure {
pub fn new(trace: Vec<Action>, refusal: HashSet<Action>) -> Self {
Failure { trace, refusal }
}
}
#[derive(Debug, Clone)]
pub struct FailuresModel {
pub failures: Vec<Failure>,
pub traces: TraceSet,
}
impl FailuresModel {
pub fn new() -> Self {
FailuresModel {
failures: Vec::new(),
traces: TraceSet::new(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum HmlFormula {
True,
False,
And(Box<HmlFormula>, Box<HmlFormula>),
Or(Box<HmlFormula>, Box<HmlFormula>),
Not(Box<HmlFormula>),
Diamond(Action, Box<HmlFormula>),
Box_(Action, Box<HmlFormula>),
}
impl HmlFormula {
pub fn diamond(a: Action, phi: HmlFormula) -> Self {
HmlFormula::Diamond(a, Box::new(phi))
}
pub fn box_(a: Action, phi: HmlFormula) -> Self {
HmlFormula::Box_(a, Box::new(phi))
}
pub fn and(phi: HmlFormula, psi: HmlFormula) -> Self {
HmlFormula::And(Box::new(phi), Box::new(psi))
}
pub fn or(phi: HmlFormula, psi: HmlFormula) -> Self {
HmlFormula::Or(Box::new(phi), Box::new(psi))
}
pub fn not(phi: HmlFormula) -> Self {
HmlFormula::Not(Box::new(phi))
}
}
impl fmt::Display for HmlFormula {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
HmlFormula::True => write!(f, "tt"),
HmlFormula::False => write!(f, "ff"),
HmlFormula::And(p, q) => write!(f, "({} ∧ {})", p, q),
HmlFormula::Or(p, q) => write!(f, "({} ∨ {})", p, q),
HmlFormula::Not(p) => write!(f, "¬{}", p),
HmlFormula::Diamond(a, p) => write!(f, "⟨{}⟩{}", a, p),
HmlFormula::Box_(a, p) => write!(f, "[{}]{}", a, p),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Test {
Success,
Fail,
Offer(String, Box<Test>),
TChoice(Box<Test>, Box<Test>),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TestOutcome {
Pass,
Fail,
Diverge,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum BehavioralEquivalence {
StrongBisimilarity,
WeakBisimilarity,
BranchingBisimilarity,
ReadySimulation,
FailuresEquivalence,
TraceEquivalence,
}
impl fmt::Display for BehavioralEquivalence {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
BehavioralEquivalence::StrongBisimilarity => write!(f, "strong bisimilarity (~)"),
BehavioralEquivalence::WeakBisimilarity => write!(f, "weak bisimilarity (≈)"),
BehavioralEquivalence::BranchingBisimilarity => write!(f, "branching bisimilarity"),
BehavioralEquivalence::ReadySimulation => write!(f, "ready simulation"),
BehavioralEquivalence::FailuresEquivalence => write!(f, "failures equivalence"),
BehavioralEquivalence::TraceEquivalence => write!(f, "trace equivalence"),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct StructuralCongruenceClass {
pub representative: CcsProcess,
pub reductions: usize,
}