use std::{slice, collections::BTreeMap, convert::TryFrom};
use rand::{RngCore, Rng};
use crate::{
ContextHandle, Contextual, Face, NodeID, Capacity, Weight, Solution, State, AcesError,
AcesErrorKind,
};
#[derive(Default, Debug)]
pub struct FiringComponent {
pre_set: BTreeMap<NodeID, Weight>,
post_set: BTreeMap<NodeID, (Weight, Capacity)>,
}
impl FiringComponent {
pub fn is_enabled(&self, state: &State) -> bool {
for (&node_id, &weight) in self.pre_set.iter() {
let tokens = state.get(node_id);
if tokens.is_zero() {
if weight.is_finite() {
return false
}
} else if tokens < weight {
return false
}
}
for (&node_id, &(weight, capacity)) in self.post_set.iter() {
if let Some(tokens_after) = state.get(node_id).checked_add(weight) {
if tokens_after > capacity {
return false
}
} else {
warn!("Overflow of state at {:?} when checking enablement", node_id);
if capacity.is_finite() {
return false
}
}
}
true
}
pub fn fire(&self, state: &mut State) -> Result<(), AcesError> {
for (&node_id, &weight) in self.pre_set.iter() {
state.decrease(node_id, weight)?;
}
for (&node_id, &(weight, _)) in self.post_set.iter() {
state.increase(node_id, weight)?;
}
Ok(())
}
}
impl TryFrom<Solution> for FiringComponent {
type Error = AcesError;
#[allow(clippy::map_entry)]
fn try_from(sol: Solution) -> Result<Self, Self::Error> {
let ctx = sol.get_context().lock().unwrap();
let mut pre_set = BTreeMap::new();
let mut post_set = BTreeMap::new();
'outer_forks: for &fork_id in sol.get_fork_set() {
let weight = ctx.get_weight(fork_id.get());
let fork = ctx
.get_fork(fork_id)
.ok_or_else(|| AcesError::from(AcesErrorKind::ForkMissingForID(fork_id)))?;
let tx_node_id = fork.get_host_id();
for &node_id in sol.get_pre_set().iter() {
if node_id == tx_node_id {
if pre_set.contains_key(&node_id) {
return Err(AcesError::from(AcesErrorKind::FiringNodeDuplicated(Face::Tx)))
} else {
pre_set.insert(node_id, weight);
continue 'outer_forks
}
}
}
return Err(AcesError::from(AcesErrorKind::FiringNodeMissing(Face::Tx)))
}
'outer_joins: for &join_id in sol.get_join_set() {
let weight = ctx.get_weight(join_id.get());
let join = ctx
.get_join(join_id)
.ok_or_else(|| AcesError::from(AcesErrorKind::JoinMissingForID(join_id)))?;
let rx_node_id = join.get_host_id();
for &node_id in sol.get_post_set().iter() {
if node_id == rx_node_id {
if post_set.contains_key(&node_id) {
return Err(AcesError::from(AcesErrorKind::FiringNodeDuplicated(Face::Rx)))
} else {
let capacity = ctx.get_capacity(node_id);
post_set.insert(node_id, (weight, capacity));
continue 'outer_joins
}
}
}
return Err(AcesError::from(AcesErrorKind::FiringNodeMissing(Face::Rx)))
}
Ok(FiringComponent { pre_set, post_set })
}
}
impl Contextual for FiringComponent {
fn format(&self, ctx: &ContextHandle) -> Result<String, AcesError> {
let mut result = String::new();
if self.pre_set.is_empty() {
result.push_str("{} => {");
} else {
result.push('{');
for node_id in self.pre_set.keys() {
result.push(' ');
result.push_str(node_id.format(ctx)?.as_str());
}
result.push_str(" } => {");
}
if self.post_set.is_empty() {
result.push('}');
} else {
for node_id in self.post_set.keys() {
result.push(' ');
result.push_str(node_id.format(ctx)?.as_str());
}
result.push_str(" }");
}
Ok(result)
}
}
#[derive(Default, Debug)]
pub struct FiringSet {
fcs: Vec<FiringComponent>,
}
impl FiringSet {
#[inline]
pub fn as_slice(&self) -> &[FiringComponent] {
self.fcs.as_slice()
}
pub fn get_enabled(&self, state: &State) -> FiringSequence {
let mut enabled_fcs = FiringSequence::new();
for (pos, fc) in self.fcs.as_slice().iter().enumerate() {
if fc.is_enabled(state) {
enabled_fcs.push(pos)
}
}
enabled_fcs
}
}
impl From<Vec<FiringComponent>> for FiringSet {
#[inline]
fn from(fcs: Vec<FiringComponent>) -> Self {
FiringSet { fcs }
}
}
#[derive(Default, Debug)]
pub struct FiringSequence {
fcs: Vec<(usize, bool)>,
}
impl FiringSequence {
pub fn new() -> Self {
Default::default()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.fcs.is_empty()
}
#[inline]
pub fn len(&self) -> usize {
self.fcs.len()
}
#[inline]
pub fn push(&mut self, fc_id: usize) {
self.fcs.push((fc_id, true))
}
#[inline]
pub fn first(&self) -> Option<usize> {
self.fcs.first().map(|r| r.0)
}
#[inline]
pub fn get(&self, pos: usize) -> Option<usize> {
self.fcs.get(pos).map(|r| r.0)
}
pub fn get_random<R: RngCore>(&self, rng: &mut R) -> Option<usize> {
match self.fcs.len() {
0 => None,
1 => Some(self.fcs[0].0),
n => Some(self.fcs[rng.gen_range(0, n)].0),
}
}
pub fn iter<'b, 'a: 'b>(&'a self, firing_set: &'b FiringSet) -> FiringIter<'b> {
FiringIter { iter: self.fcs.iter(), fset: firing_set }
}
pub fn par_iter<'b, 'a: 'b>(&'a self, firing_set: &'b FiringSet) -> FiringParIter<'b> {
FiringParIter { iter: self.fcs.iter(), fset: firing_set }
}
}
pub struct FiringIter<'a> {
iter: slice::Iter<'a, (usize, bool)>,
fset: &'a FiringSet,
}
impl<'a> Iterator for FiringIter<'a> {
type Item = &'a FiringComponent;
fn next(&mut self) -> Option<Self::Item> {
if let Some((pos, _)) = self.iter.next() {
let fc = self.fset.fcs.get(*pos).expect("Broken firing sequence.");
Some(fc)
} else {
None
}
}
}
pub struct FiringParIter<'a> {
iter: slice::Iter<'a, (usize, bool)>,
fset: &'a FiringSet,
}
impl<'a> Iterator for FiringParIter<'a> {
type Item = Vec<&'a FiringComponent>;
fn next(&mut self) -> Option<Self::Item> {
let mut fcs = Vec::new();
while let Some((pos, fin)) = self.iter.next() {
let fc = self.fset.fcs.get(*pos).expect("Broken firing sequence.");
fcs.push(fc);
if *fin {
return Some(fcs)
}
}
None
}
}