use std::cell::{Cell, RefCell};
use std::collections::BTreeMap;
use super::Router;
use super::audit;
use super::bundle::End;
use super::graph::Axis;
use super::path;
use super::runs::Chain;
#[derive(Default)]
pub struct Probe {
offered: [Cell<bool>; 2],
short: RefCell<BTreeMap<(usize, Axis), usize>>,
}
impl Probe {
pub fn entries(&self, end: End, offered: bool) {
let e = match end {
End::A => 0,
End::B => 1,
};
self.offered[e].set(self.offered[e].get() | offered);
}
pub fn lanes_short(&self, world: usize, axis: Axis, lanes: usize) {
let mut short = self.short.borrow_mut();
let worst = short.entry((world, axis)).or_insert(0);
*worst = (*worst).max(lanes);
}
fn deficits(self) -> Option<BTreeMap<(usize, Axis), usize>> {
let entered = self.offered.iter().all(Cell::get);
let short = self.short.into_inner();
(entered && !short.is_empty()).then_some(short)
}
}
pub fn starved(
router: &Router,
raw: &[Option<Chain>],
impossible: &[usize],
) -> BTreeMap<String, (f64, f64)> {
let c = router.clearance;
let mut out: BTreeMap<String, (f64, f64)> = BTreeMap::new();
let mut grow = |path: String, vertical_gap: bool, px: f64| {
let entry = out.entry(path).or_insert((0.0, 0.0));
if vertical_gap {
entry.0 = entry.0.max(px);
} else {
entry.1 = entry.1.max(px);
}
};
let mut probed: Vec<usize> = impossible
.iter()
.map(|&m| audit::bundle_of(router, m))
.collect();
probed.sort_unstable();
probed.dedup();
for bi in probed {
let members = router.bundles[bi].members.clone();
let rep = &router.reqs[members[0]];
let occ = audit::occupancy_without(raw, &members, c);
let ports = audit::ports_without(raw, &members, c);
let probe = Probe::default();
let _ = router.route_bundle(
bi,
&occ,
&ports,
path::FREE,
&[],
[None, None],
false,
true,
Some(&probe),
);
for (path, rect) in [(&rep.a_path, rep.a_rect), (&rep.b_path, rep.b_rect)] {
for (parent, vertical_gap, px) in sealed_sides(router, path, rect, c) {
grow(parent, vertical_gap, px);
}
}
let Some(deficits) = probe.deficits() else {
continue;
};
for ((w, axis), lanes) in deficits {
let need = lanes as f64 * c;
let path = router.worlds[w].path.clone();
match axis {
Axis::H => grow(path, true, need),
Axis::V => grow(path, false, need),
}
}
}
out
}
fn sealed_sides(
router: &Router,
path: &str,
rect: super::rect::Rect,
c: f64,
) -> Vec<(String, bool, f64)> {
let parent = super::parent_path(path);
let mut out = Vec::new();
for sibling in router.index.child_rects(&parent) {
if sibling.x0 == rect.x0 && sibling.y0 == rect.y0 && sibling.x1 == rect.x1 {
continue;
}
let across = sibling.x1.min(rect.x1) > sibling.x0.max(rect.x0);
let down = sibling.y1.min(rect.y1) > sibling.y0.max(rect.y0);
let gap = if across && sibling.y1 <= rect.y0 {
Some((true, rect.y0 - sibling.y1))
} else if across && sibling.y0 >= rect.y1 {
Some((true, sibling.y0 - rect.y1))
} else if down && sibling.x1 <= rect.x0 {
Some((false, rect.x0 - sibling.x1))
} else if down && sibling.x0 >= rect.x1 {
Some((false, sibling.x0 - rect.x1))
} else {
None
};
if let Some((vertical_gap, g)) = gap
&& g < 2.0 * c
{
out.push((parent.clone(), vertical_gap, 2.0 * c - g));
}
}
out
}