use crate::law::{IsTrue, Require};
use core::marker::PhantomData;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct Atom;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct PartialOrder;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct Choice;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct Loop;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct Silent;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct Irreducible;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct AcyclicPartialOrder;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct ProcessTreeProjectable;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct ExceedsProcessTree;
mod acyclic_witness_seal {
pub trait Sealed {}
impl Sealed for super::AcyclicPartialOrder {}
}
pub trait AcyclicWitness: acyclic_witness_seal::Sealed {}
impl AcyclicWitness for AcyclicPartialOrder {}
pub fn assert_acyclic<W: AcyclicWitness>(_marker: W) -> bool {
true
}
mod tree_projectable_seal {
pub trait Sealed {}
impl Sealed for super::ProcessTreeProjectable {}
}
pub trait TreeProjectable: tree_projectable_seal::Sealed {}
impl TreeProjectable for ProcessTreeProjectable {}
pub fn assert_tree_projectable<P: TreeProjectable>(_marker: P) -> bool {
true
}
#[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct PowlNodeId(pub usize);
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PowlNodeKind {
Atom(String),
Silent,
Choice(Vec<PowlNodeId>),
Loop {
body: PowlNodeId,
redo: Option<PowlNodeId>,
},
PartialOrder(Vec<PowlNodeId>),
ChoiceGraph {
nodes: Vec<PowlNodeId>,
edges: Vec<ChoiceGraphEdge>,
},
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PowlNode<W = ()> {
pub id: PowlNodeId,
pub kind: PowlNodeKind,
pub witness: PhantomData<W>,
}
impl<W> PowlNode<W> {
pub fn new(id: PowlNodeId, kind: PowlNodeKind) -> Self {
Self {
id,
kind,
witness: PhantomData,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PowlChoiceNode {
pub branches: Vec<PowlNodeId>,
}
impl PowlChoiceNode {
pub fn new(branches: Vec<PowlNodeId>) -> Self {
Self { branches }
}
#[inline]
pub fn branch_count(&self) -> usize {
self.branches.len()
}
#[inline]
pub fn is_well_formed(&self) -> bool {
self.branches.len() >= 2
}
#[must_use = "check the shape-check result"]
pub fn validate(&self) -> Result<&[PowlNodeId], PowlRefusal> {
if self.is_well_formed() {
Ok(&self.branches)
} else {
Err(PowlRefusal::InvalidChoice)
}
}
}
pub struct TypedPowlLoopNode<Children, const ARITY: usize>
where
Require<{ ARITY == 2 }>: IsTrue,
{
pub children: Children,
}
impl<Children, const ARITY: usize> TypedPowlLoopNode<Children, ARITY>
where
Require<{ ARITY == 2 }>: IsTrue,
{
pub fn new(children: Children) -> Self {
TypedPowlLoopNode { children }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct OrderEdge {
pub from: PowlNodeId,
pub to: PowlNodeId,
}
impl OrderEdge {
pub fn new(from: PowlNodeId, to: PowlNodeId) -> Self {
Self { from, to }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ChoiceGraphEdge {
pub from: PowlNodeId,
pub to: PowlNodeId,
}
impl ChoiceGraphEdge {
pub fn new(from: PowlNodeId, to: PowlNodeId) -> Self {
Self { from, to }
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct Powl {
pub nodes: Vec<PowlNode>,
pub edges: Vec<OrderEdge>,
pub root: Option<PowlNodeId>,
}
impl Powl {
pub fn new() -> Self {
Self::default()
}
pub fn node_count(&self) -> usize {
self.nodes.len()
}
pub fn validate(&self) -> Result<(), PowlRefusal> {
for node in &self.nodes {
match &node.kind {
PowlNodeKind::Choice(branches) => {
if branches.len() < 2 {
return Err(PowlRefusal::InvalidChoice);
}
}
PowlNodeKind::Loop { body, redo } => {
let node_ids: std::collections::HashSet<usize> =
self.nodes.iter().map(|n| n.id.0).collect();
if !node_ids.contains(&body.0) {
return Err(PowlRefusal::InvalidLoop);
}
if let Some(r) = redo {
if !node_ids.contains(&r.0) {
return Err(PowlRefusal::InvalidLoop);
}
}
}
PowlNodeKind::PartialOrder(children) => {
let child_set: std::collections::HashSet<PowlNodeId> =
children.iter().cloned().collect();
let mut adj: std::collections::HashMap<PowlNodeId, Vec<PowlNodeId>> =
std::collections::HashMap::new();
let mut in_degree: std::collections::HashMap<PowlNodeId, usize> =
std::collections::HashMap::new();
for &c in children {
adj.entry(c).or_default();
in_degree.entry(c).or_insert(0);
}
for edge in &self.edges {
if child_set.contains(&edge.from) && child_set.contains(&edge.to) {
adj.entry(edge.from).or_default().push(edge.to);
*in_degree.entry(edge.to).or_insert(0) += 1;
}
}
let mut queue: std::collections::VecDeque<PowlNodeId> = children
.iter()
.copied()
.filter(|c| in_degree.get(c).copied().unwrap_or(0) == 0)
.collect();
let mut visited = 0;
while let Some(u) = queue.pop_front() {
visited += 1;
if let Some(neighbors) = adj.get(&u) {
for &v in neighbors {
if let Some(deg) = in_degree.get_mut(&v) {
*deg -= 1;
if *deg == 0 {
queue.push_back(v);
}
}
}
}
}
if visited != children.len() {
return Err(PowlRefusal::CyclicPartialOrder);
}
}
PowlNodeKind::ChoiceGraph {
nodes: cg_nodes,
edges: cg_edges,
} => {
if cg_nodes.len() < 2 {
return Err(PowlRefusal::ChoiceGraphDisconnected);
}
let start = cg_nodes[0];
let end = cg_nodes[cg_nodes.len() - 1];
let node_set: std::collections::HashSet<PowlNodeId> =
cg_nodes.iter().cloned().collect();
for edge in cg_edges {
if !node_set.contains(&edge.from) || !node_set.contains(&edge.to) {
return Err(PowlRefusal::ChoiceGraphDisconnected);
}
}
let mut forward_adj: std::collections::HashMap<PowlNodeId, Vec<PowlNodeId>> =
std::collections::HashMap::new();
let mut backward_adj: std::collections::HashMap<PowlNodeId, Vec<PowlNodeId>> =
std::collections::HashMap::new();
for edge in cg_edges {
forward_adj.entry(edge.from).or_default().push(edge.to);
backward_adj.entry(edge.to).or_default().push(edge.from);
}
let mut forward_visited = std::collections::HashSet::new();
let mut queue = std::collections::VecDeque::new();
queue.push_back(start);
forward_visited.insert(start);
while let Some(u) = queue.pop_front() {
if let Some(neighbors) = forward_adj.get(&u) {
for &v in neighbors {
if forward_visited.insert(v) {
queue.push_back(v);
}
}
}
}
let mut backward_visited = std::collections::HashSet::new();
queue.clear();
queue.push_back(end);
backward_visited.insert(end);
while let Some(u) = queue.pop_front() {
if let Some(parents) = backward_adj.get(&u) {
for &v in parents {
if backward_visited.insert(v) {
queue.push_back(v);
}
}
}
}
for &node in cg_nodes {
if !forward_visited.contains(&node) || !backward_visited.contains(&node) {
return Err(PowlRefusal::ChoiceGraphDisconnected);
}
}
}
_ => {}
}
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum PowlRefusal {
CyclicPartialOrder,
InvalidChoice,
InvalidChoiceArity {
declared: usize,
required_min: usize,
},
InvalidLoop,
LoopMissingDoBody,
IrreducibleProjection,
LanguageMismatch,
ChoiceGraphDisconnected,
}
impl core::fmt::Display for PowlRefusal {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
PowlRefusal::CyclicPartialOrder => write!(f, "POWL refused: CyclicPartialOrder"),
PowlRefusal::InvalidChoice => write!(f, "POWL refused: InvalidChoice"),
PowlRefusal::InvalidChoiceArity { declared, required_min } => write!(
f,
"POWL refused: InvalidChoiceArity (declared={declared}, required_min={required_min})"
),
PowlRefusal::InvalidLoop => write!(f, "POWL refused: InvalidLoop"),
PowlRefusal::LoopMissingDoBody => write!(f, "POWL refused: LoopMissingDoBody"),
PowlRefusal::IrreducibleProjection => {
write!(f, "POWL refused: IrreducibleProjection")
}
PowlRefusal::LanguageMismatch => write!(f, "POWL refused: LanguageMismatch"),
PowlRefusal::ChoiceGraphDisconnected => {
write!(f, "POWL refused: ChoiceGraphDisconnected")
}
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum StandaloneChoiceGraphNode {
Start,
End,
Activity(String),
SubModel(u32),
}
pub type ChoiceGraphNode = StandaloneChoiceGraphNode;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ChoiceGraph {
pub nodes: Vec<StandaloneChoiceGraphNode>,
pub edges: Vec<(usize, usize)>,
pub start_idx: usize,
pub end_idx: usize,
}
impl ChoiceGraph {
pub fn new(nodes: Vec<StandaloneChoiceGraphNode>, edges: Vec<(usize, usize)>) -> Self {
let end_idx = nodes.len().saturating_sub(1);
ChoiceGraph {
nodes,
edges,
start_idx: 0,
end_idx,
}
}
pub fn successors(&self, node_idx: usize) -> Vec<usize> {
self.edges
.iter()
.filter_map(|&(a, b)| if a == node_idx { Some(b) } else { None })
.collect()
}
pub fn predecessors(&self, node_idx: usize) -> Vec<usize> {
self.edges
.iter()
.filter_map(|&(a, b)| if b == node_idx { Some(a) } else { None })
.collect()
}
pub fn has_empty_path(&self) -> bool {
self.edges
.iter()
.any(|&(a, b)| a == self.start_idx && b == self.end_idx)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RefusedProjection {
reason: PowlRefusal,
}
impl RefusedProjection {
#[inline]
pub fn new(reason: PowlRefusal) -> Self {
Self { reason }
}
#[inline]
pub fn reason(&self) -> &PowlRefusal {
&self.reason
}
#[inline]
pub fn into_reason(self) -> PowlRefusal {
self.reason
}
}
impl core::fmt::Display for RefusedProjection {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
core::fmt::Display::fmt(&self.reason, f)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Powl8Op {
Sequence,
XorChoice,
Parallel,
Loop,
StrictPartialOrder,
ChoiceGraph,
Silent,
Activity,
}
mod wfnet2powl_seal {
pub(super) struct WfNet2PowlSeal;
}
pub struct WfNet2PowlWitness {
pub context: String,
_seal: wfnet2powl_seal::WfNet2PowlSeal,
}
impl WfNet2PowlWitness {
pub fn new_internal(context: impl Into<String>) -> Self {
WfNet2PowlWitness {
context: context.into(),
_seal: wfnet2powl_seal::WfNet2PowlSeal,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_powl_validate_empty() {
let p = Powl::new();
assert!(p.validate().is_ok());
}
#[test]
fn test_powl_validate_invalid_choice() {
let mut p = Powl::new();
p.nodes.push(PowlNode::new(
PowlNodeId(0),
PowlNodeKind::Choice(vec![PowlNodeId(1)]),
));
assert_eq!(p.validate(), Err(PowlRefusal::InvalidChoice));
}
#[test]
fn test_powl_validate_cyclic_partial_order() {
let mut p = Powl::new();
p.nodes.push(PowlNode::new(
PowlNodeId(0),
PowlNodeKind::PartialOrder(vec![PowlNodeId(1), PowlNodeId(2)]),
));
p.edges.push(OrderEdge::new(PowlNodeId(1), PowlNodeId(2)));
p.edges.push(OrderEdge::new(PowlNodeId(2), PowlNodeId(1)));
assert_eq!(p.validate(), Err(PowlRefusal::CyclicPartialOrder));
}
#[test]
fn test_powl_validate_choice_graph_disconnected() {
let mut p = Powl::new();
let start = PowlNodeId(0);
let x1 = PowlNodeId(1);
let end = PowlNodeId(2);
p.nodes.push(PowlNode::new(
PowlNodeId(10),
PowlNodeKind::ChoiceGraph {
nodes: vec![start, x1, end],
edges: vec![ChoiceGraphEdge::new(start, end)], },
));
assert_eq!(p.validate(), Err(PowlRefusal::ChoiceGraphDisconnected));
}
}