use super::{ProcessId, ResourceId};
use std::{
collections::{hash_map::Entry, HashMap},
fmt::Debug,
hash::Hash,
ops::{Add, Sub},
};
pub struct Environment<R, P> {
resources: HashMap<ResourceId, R>,
processes: HashMap<ProcessId, P>,
initial: Inventory<ResourceId>,
}
#[derive(Default, Debug, Clone)]
pub(crate) struct Inventory<RP> {
pub items: HashMap<RP, u64>,
}
#[derive(Debug)]
pub struct Step<A> {
pub(super) process: ProcessId,
pub(super) inventory_after: Inventory<ResourceId>,
pub(super) algorithm_data: A,
}
#[derive(Debug)]
pub struct Path<A> {
pub(super) steps: Vec<Step<A>>,
}
impl<A> Default for Path<A> {
fn default() -> Self { Self { steps: Vec::new() } }
}
impl<A> Path<A> {
pub fn processes(&self) -> Inventory<ProcessId> {
let mut inventory = Inventory::default();
for step in &self.steps {
inventory.push(step.process);
}
inventory
}
pub fn push(&mut self, step: Step<A>) { self.steps.push(step) }
}
impl<RP> Inventory<RP>
where
RP: PartialEq + Eq + Hash + Default + Clone,
{
#[inline(always)]
pub fn push(&mut self, element: RP) { *self.items.entry(element).or_default() += 1; }
pub fn with_list(list: &[RP]) -> Self {
let mut items = HashMap::new();
for e in list {
*items.entry(e.clone()).or_default() += 1;
}
Self { items }
}
#[allow(dead_code)]
pub fn missing(&self, needed: Self) -> Self {
let mut missing = Self::default();
for (rid, count) in needed.items.into_iter() {
let contains = self.items.get(&rid).cloned().unwrap_or_default();
if contains < count {
missing.items.insert(rid, count - contains);
}
}
missing
}
pub fn contains(&self, other: &Self) -> bool {
for (rid, count) in other.items.iter() {
let contains = self.items.get(rid).cloned().unwrap_or_default();
if contains < *count {
return false;
}
}
true
}
}
impl<RP: Debug + Hash + Eq> Add for Inventory<RP> {
type Output = Inventory<RP>;
fn add(mut self, other: Self) -> Self {
for (rid, count) in other.items.into_iter() {
*self.items.entry(rid).or_default() += count;
}
self
}
}
impl<RP: Debug + Hash + Eq + Clone> Sub for Inventory<RP> {
type Output = Inventory<RP>;
fn sub(mut self, other: Self) -> Self {
for (rid, count) in other.items.into_iter() {
match self.items.entry(rid.clone()) {
Entry::Occupied(mut occ) => {
*occ.get_mut() -= count;
if *occ.get() == 0u64 {
occ.remove();
}
},
Entry::Vacant(_) if count > 0 => {
panic!("Sub Underflow for Inventory {:?}, it has no ressource {:?}", self, rid)
},
Entry::Vacant(_) => {},
}
}
self
}
}
impl<R, P> Environment<R, P> {
pub(crate) fn new(
resources: HashMap<ResourceId, R>,
processes: HashMap<ProcessId, P>,
initial: Inventory<ResourceId>,
) -> Self {
Self {
processes,
resources,
initial,
}
}
#[allow(dead_code)]
pub fn resources(&self) -> &HashMap<ResourceId, R> { &self.resources }
pub fn processes(&self) -> &HashMap<ProcessId, P> { &self.processes }
pub fn initial(&self) -> &Inventory<ResourceId> { &self.initial }
#[allow(dead_code)]
pub fn initial_mut(&mut self) -> &mut Inventory<ResourceId> { &mut self.initial }
}
#[cfg(test)]
mod tests {
use super::Inventory;
#[test]
fn inventory_missing() {
let have = Inventory {
items: [(1, 2), (2, 3), (3, 4)].into(),
};
let want = Inventory {
items: [(0, 1), (1, 1), (2, 3), (3, 5), (4, 5)].into(),
};
let missing = have.missing(want);
assert_eq!(missing.items, [(0, 1), (3, 1), (4, 5)].into())
}
#[test]
fn inventory_with_list() {
let have = Inventory::with_list(&[0, 1, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 3]);
let want = Inventory {
items: [(0, 1), (1, 1), (2, 3), (3, 5), (4, 5)].into(),
};
assert_eq!(have.items, want.items);
}
#[test]
fn inventory_contains() {
let a = Inventory::with_list(&[0, 1, 1, 1, 3, 3]);
let b = Inventory::with_list(&[0, 1]);
let c = Inventory::with_list(&[0, 1, 1, 1, 1, 3, 3]);
assert!(a.contains(&a));
assert!(a.contains(&b));
assert!(!a.contains(&c));
assert!(!b.contains(&a));
assert!(b.contains(&b));
assert!(!b.contains(&c));
assert!(c.contains(&a));
assert!(c.contains(&b));
assert!(c.contains(&c));
}
}