use std::fmt::Display;
use itertools::Itertools;
use ndarray::prelude::*;
use crate::types::Labels;
#[allow(clippy::upper_case_acronyms)]
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
#[repr(C)]
enum PKS {
Unknown,
Forbidden,
Required,
}
impl PKS {
#[inline]
pub const fn is_unknown(&self) -> bool {
matches!(self, Self::Unknown)
}
#[inline]
pub const fn is_forbidden(&self) -> bool {
matches!(self, Self::Forbidden)
}
#[inline]
pub const fn is_required(&self) -> bool {
matches!(self, Self::Required)
}
}
impl Display for PKS {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Unknown => write!(f, "Unknown"),
Self::Forbidden => write!(f, "Forbidden"),
Self::Required => write!(f, "Required"),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct PK {
labels: Labels,
adjacency_matrix: Array2<PKS>,
}
impl PK {
pub fn new<I, J, K, L>(labels: Labels, forbidden: I, required: J, temporal_order: K) -> Self
where
I: IntoIterator<Item = (usize, usize)>,
J: IntoIterator<Item = (usize, usize)>,
K: IntoIterator<Item = L>,
L: IntoIterator<Item = usize>,
{
let n = labels.len();
let mut adjacency_matrix = Array::from_elem((n, n), PKS::Unknown);
forbidden.into_iter().for_each(|(i, j)| {
adjacency_matrix[[i, j]] = PKS::Forbidden;
});
required.into_iter().for_each(|(i, j)| {
assert!(
adjacency_matrix[[i, j]].is_unknown(),
"Edge ({i}, {j}) is already set to a non-unknown state: \n\
\t expected: ({i}, {j}) set to 'Unknown', \n\",
\t found: ({i}, {j}) set to '{}'.",
adjacency_matrix[[i, j]]
);
adjacency_matrix[[i, j]] = PKS::Required;
});
let temporal_order: Vec<Vec<_>> = temporal_order
.into_iter()
.map(|tier| tier.into_iter().collect())
.collect();
temporal_order.iter().enumerate().for_each(|(t, tier)| {
let previous_tiers = temporal_order[..t].iter().flatten();
tier.iter()
.cartesian_product(previous_tiers)
.for_each(|(&i, &j)| {
assert!(
!adjacency_matrix[[i, j]].is_required(),
"Edge ({i}, {j}) is already set to a 'Required' state: \n\
\t expected: ({i}, {j}) set to 'Unknown' or 'Forbidden', \n\",
\t found: ({i}, {j}) set to '{}'.",
adjacency_matrix[[i, j]]
);
adjacency_matrix[[i, j]] = PKS::Forbidden;
});
});
Self {
labels,
adjacency_matrix,
}
}
#[inline]
pub const fn labels(&self) -> &Labels {
&self.labels
}
#[inline]
pub fn is_unknown(&self, x: usize, y: usize) -> bool {
self.adjacency_matrix[[x, y]].is_unknown()
}
pub fn unknown_edges(&self) -> Vec<(usize, usize)> {
self.adjacency_matrix
.indexed_iter()
.filter_map(|((i, j), &state)| {
if state.is_unknown() {
Some((i, j))
} else {
None
}
})
.collect()
}
#[inline]
pub fn is_forbidden(&self, x: usize, y: usize) -> bool {
self.adjacency_matrix[[x, y]].is_forbidden()
}
pub fn forbidden_edges(&self) -> Vec<(usize, usize)> {
self.adjacency_matrix
.indexed_iter()
.filter_map(|((i, j), &state)| {
if state.is_forbidden() {
Some((i, j))
} else {
None
}
})
.collect()
}
#[inline]
pub fn is_required(&self, x: usize, y: usize) -> bool {
self.adjacency_matrix[[x, y]].is_required()
}
pub fn required_edges(&self) -> Vec<(usize, usize)> {
self.adjacency_matrix
.indexed_iter()
.filter_map(|((i, j), &state)| {
if state.is_required() {
Some((i, j))
} else {
None
}
})
.collect()
}
}