use crate::fuse::Class;
use crate::place::{Device, Expert, Place};
pub fn expert_class() -> Class {
Class::Route
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Plan {
hops: Vec<Expert>,
}
impl FromIterator<Expert> for Plan {
fn from_iter<I: IntoIterator<Item = Expert>>(hops: I) -> Plan {
Plan {
hops: hops.into_iter().collect(),
}
}
}
impl Plan {
pub fn hops(&self) -> &[Expert] {
&self.hops
}
pub fn devices(&self, pi: &Place) -> Option<Vec<Device>> {
self.hops.iter().map(|&e| pi.device(e)).collect()
}
pub fn crossings(&self, pi: &Place) -> Option<Vec<usize>> {
let devices = self.devices(pi)?;
Some(
devices
.windows(2)
.enumerate()
.filter(|(_, w)| w[0] != w[1])
.map(|(i, _)| i + 1)
.collect(),
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn applying_any_expert_is_the_same_class_a_fence_that_is_not_a_map() {
for id in [0usize, 1, 7, 4096] {
let _ = Expert(id); assert_eq!(
expert_class(),
Class::Route,
"the class must not depend on which expert"
);
}
assert!(Class::Route.fences(), "a Route is a fence");
assert!(!Class::Route.is_map(), "a Route is not a Map");
assert!(
Class::Map.is_map() && !Class::Map.fences(),
"a Map still composes"
);
}
#[test]
fn crossings_come_from_pi_not_from_the_plan() {
let plan: Plan = [Expert(0), Expert(1), Expert(2)].into_iter().collect();
let together: Place = [
(Expert(0), Device(0)),
(Expert(1), Device(0)),
(Expert(2), Device(0)),
]
.into_iter()
.collect();
assert_eq!(
plan.devices(&together),
Some(vec![Device(0), Device(0), Device(0)])
);
assert_eq!(
plan.crossings(&together),
Some(vec![]),
"co-located Routes cross nothing"
);
let split: Place = [
(Expert(0), Device(0)),
(Expert(1), Device(1)),
(Expert(2), Device(1)),
]
.into_iter()
.collect();
assert_eq!(plan.crossings(&split), Some(vec![1]));
let pingpong: Place = [
(Expert(0), Device(0)),
(Expert(1), Device(1)),
(Expert(2), Device(0)),
]
.into_iter()
.collect();
assert_eq!(plan.crossings(&pingpong), Some(vec![1, 2]));
}
#[test]
fn an_unplaced_hop_leaves_the_plan_without_a_path() {
let plan: Plan = [Expert(0), Expert(1)].into_iter().collect();
let partial: Place = [(Expert(0), Device(0))].into_iter().collect();
assert_eq!(plan.devices(&partial), None, "Expert(1) is unplaced");
assert_eq!(plan.crossings(&partial), None);
}
#[test]
fn a_lone_hop_has_no_crossing() {
let one: Plan = [Expert(3)].into_iter().collect();
let pi: Place = [(Expert(3), Device(2))].into_iter().collect();
assert_eq!(one.crossings(&pi), Some(vec![]));
let none: Plan = core::iter::empty().collect();
assert_eq!(none.crossings(&pi), Some(vec![]));
}
}