pub(super) const WORK_CEILING: usize = 192;
pub(super) fn budget(movable: usize, per_candidate: usize) -> usize {
if movable > MOST_MOVABLE {
return 0;
}
WORK_CEILING / per_candidate.max(1)
}
#[derive(Debug, Clone, Copy)]
pub(super) struct Wrap {
pub anchor: Option<usize>,
pub span: f32,
}
const MOST_MOVABLE: usize = 6;
struct Slots {
offset: Vec<usize>,
anchor: Vec<Option<usize>>,
}
impl Slots {
fn new(cables: &[Vec<Wrap>]) -> Self {
let mut offset = Vec::with_capacity(cables.len() + 1);
let mut anchor = Vec::new();
for wraps in cables {
offset.push(anchor.len());
anchor.extend(wraps.iter().map(|wrap| wrap.anchor));
}
offset.push(anchor.len());
Self { offset, anchor }
}
fn total(&self) -> usize {
self.anchor.len()
}
fn of(&self, cable: usize) -> std::ops::Range<usize> {
self.offset[cable]..self.offset[cable + 1]
}
}
#[derive(Debug, Clone, Copy)]
struct Seat {
slot: usize,
cable: usize,
span: f32,
}
pub(super) fn contested(cables: &[Vec<Wrap>], anchors: usize) -> Vec<usize> {
let routes: Vec<Vec<usize>> = cables
.iter()
.filter_map(|wraps| {
let route: Vec<usize> = wraps.iter().filter_map(|wrap| wrap.anchor).collect();
(route.len() > 1).then_some(route)
})
.collect();
let mut contested = vec![false; anchors];
for (at, one) in routes.iter().enumerate() {
for other in &routes[at + 1..] {
let shared = one.iter().filter(|anchor| other.contains(anchor));
if shared.clone().count() > 1 {
for &anchor in shared {
if let Some(flag) = contested.get_mut(anchor) {
*flag = true;
}
}
}
}
}
(0..anchors).filter(|&anchor| contested[anchor]).collect()
}
pub(super) fn assign(
cables: &[Vec<Wrap>],
anchors: usize,
contested: &[usize],
budget: usize,
cost: &mut dyn FnMut(&[Vec<u8>]) -> usize,
) -> Vec<Vec<u8>> {
let slots = Slots::new(cables);
let mut seats = seed(cables, &slots, anchors);
if !contested.is_empty() && budget > 1 {
refine(&mut seats, &slots, contested, budget, cost);
}
arrange(&slots, &seats)
}
fn seed(cables: &[Vec<Wrap>], slots: &Slots, anchors: usize) -> Vec<Vec<Seat>> {
let mut seats: Vec<Vec<Seat>> = vec![Vec::new(); anchors];
for (cable, wraps) in cables.iter().enumerate() {
for (slot, wrap) in slots.of(cable).zip(wraps) {
if let Some(anchor) = wrap.anchor
&& let Some(seats) = seats.get_mut(anchor)
{
seats.push(Seat {
slot,
cable,
span: wrap.span,
});
}
}
}
for seats in &mut seats {
seats.sort_by(|a, b| a.span.total_cmp(&b.span).then(a.cable.cmp(&b.cable)));
}
seats
}
fn refine(
seats: &mut [Vec<Seat>],
slots: &Slots,
contested: &[usize],
budget: usize,
cost: &mut dyn FnMut(&[Vec<u8>]) -> usize,
) {
let measure = |seats: &[Vec<Seat>], cost: &mut dyn FnMut(&[Vec<u8>]) -> usize| {
let arrangement = arrange(slots, seats);
(cost(&arrangement), inversions(seats))
};
let riders = riders(seats, contested);
let mut best = measure(seats, cost);
let mut spent = 1;
let mut improving = true;
while improving && spent < budget {
improving = false;
for &anchor in contested {
for inner in 0..seats[anchor].len() {
for outer in inner + 1..seats[anchor].len() {
if spent >= budget {
return;
}
seats[anchor].swap(inner, outer);
spent += 1;
let score = measure(seats, cost);
if score < best {
best = score;
improving = true;
} else {
seats[anchor].swap(inner, outer);
}
}
}
}
for (i, &one) in riders.iter().enumerate() {
for &other in &riders[i + 1..] {
if spent >= budget {
return;
}
if couple(seats, contested, one, other) < 2 {
couple(seats, contested, one, other);
continue;
}
spent += 1;
let score = measure(seats, cost);
if score < best {
best = score;
improving = true;
} else {
couple(seats, contested, one, other);
}
}
}
}
}
fn riders(seats: &[Vec<Seat>], contested: &[usize]) -> Vec<usize> {
let mut riders: Vec<usize> = contested
.iter()
.flat_map(|&anchor| seats[anchor].iter().map(|seat| seat.cable))
.collect();
riders.sort_unstable();
riders.dedup();
riders
}
fn couple(seats: &mut [Vec<Seat>], contested: &[usize], one: usize, other: usize) -> usize {
let mut moved = 0;
for &anchor in contested {
let at = |cable: usize| seats[anchor].iter().position(|seat| seat.cable == cable);
if let (Some(a), Some(b)) = (at(one), at(other)) {
seats[anchor].swap(a, b);
moved += 1;
}
}
moved
}
fn inversions(seats: &[Vec<Seat>]) -> usize {
let mut count = 0;
for seats in seats {
for (i, outer) in seats.iter().enumerate() {
for inner in &seats[i + 1..] {
count += usize::from(outer.span > inner.span);
}
}
}
count
}
fn arrange(slots: &Slots, seats: &[Vec<Seat>]) -> Vec<Vec<u8>> {
let mut orbit = vec![0usize; slots.total()];
for seats in seats {
for (at, seat) in seats.iter().enumerate() {
orbit[seat.slot] = at;
}
}
(0..slots.offset.len() - 1)
.map(|cable| {
slots
.of(cable)
.map(|slot| u8::try_from(orbit[slot]).unwrap_or(u8::MAX))
.collect()
})
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
fn cable(wraps: &[(usize, f32)]) -> Vec<Wrap> {
wraps
.iter()
.map(|&(anchor, span)| Wrap {
anchor: Some(anchor),
span,
})
.collect()
}
fn spotless(_: &[Vec<u8>]) -> usize {
0
}
#[test]
fn a_lone_anchor_keeps_the_containment_order() {
let cables = vec![cable(&[(0, 2.0)]), cable(&[(0, 0.5)])];
let assigned = assign(
&cables,
1,
&contested(&cables, 1),
budget(cables.len(), 3),
&mut spotless,
);
assert_eq!(assigned[1][0], 0, "the narrower wrap is innermost");
assert_eq!(assigned[0][0], 1);
}
#[test]
fn a_graph_without_a_corridor_measures_nothing() {
let cables = vec![cable(&[(0, 2.0)]), cable(&[(0, 0.5)]), cable(&[(1, 1.0)])];
let mut calls = 0;
let mut counted = |_: &[Vec<u8>]| {
calls += 1;
0
};
assign(
&cables,
2,
&contested(&cables, 2),
budget(cables.len(), 3),
&mut counted,
);
assert_eq!(calls, 0, "a graph with no corridor was measured anyway");
}
#[test]
fn a_measured_corridor_crossing_outranks_containment() {
let cables = vec![cable(&[(0, 0.5), (1, 2.0)]), cable(&[(0, 2.0), (1, 0.5)])];
let mut cost = |arrangement: &[Vec<u8>]| {
let matched =
(arrangement[0][0] < arrangement[1][0]) == (arrangement[0][1] < arrangement[1][1]);
usize::from(!matched)
};
let assigned = assign(
&cables,
2,
&contested(&cables, 2),
budget(cables.len(), 3),
&mut cost,
);
assert_eq!(
(assigned[0][0] < assigned[1][0]),
(assigned[0][1] < assigned[1][1]),
"the search kept an arrangement the cost calls a crossing: {assigned:?}",
);
}
#[test]
fn the_search_never_accepts_a_worse_arrangement() {
let cables = vec![
cable(&[(0, 0.3), (1, 1.2)]),
cable(&[(0, 1.2), (1, 0.3)]),
cable(&[(0, 2.7), (1, 2.7)]),
];
let seeded = assign(
&cables,
2,
&contested(&cables, 2),
budget(cables.len(), 3),
&mut spotless,
);
let mut cost = |arrangement: &[Vec<u8>]| usize::from(arrangement != seeded);
assert_eq!(
assign(
&cables,
2,
&contested(&cables, 2),
budget(cables.len(), 3),
&mut cost
),
seeded,
"the search walked away from the only clean arrangement",
);
}
#[test]
fn the_same_graph_assigns_the_same_orbits() {
let cables = vec![
cable(&[(0, 1.0), (1, 1.0)]),
cable(&[(0, 1.0), (1, 1.0)]),
cable(&[(1, 0.2)]),
];
let mut wobbly = |arrangement: &[Vec<u8>]| arrangement[0][0] as usize;
let once = assign(
&cables,
2,
&contested(&cables, 2),
budget(cables.len(), 3),
&mut wobbly,
);
let twice = assign(
&cables,
2,
&contested(&cables, 2),
budget(cables.len(), 3),
&mut wobbly,
);
assert_eq!(once, twice);
}
#[test]
fn a_detour_still_counts_as_sharing_the_anchors() {
let cables = vec![
cable(&[(0, 2.0), (1, 1.0), (2, 0.5)]),
cable(&[(0, 0.5), (2, 2.0)]),
];
let mut calls = 0;
let mut counted = |_: &[Vec<u8>]| {
calls += 1;
0
};
assign(
&cables,
3,
&contested(&cables, 3),
budget(cables.len(), 3),
&mut counted,
);
assert!(
calls > 0,
"a pair sharing two anchors was not measured at all",
);
}
#[test]
fn a_cursor_ring_takes_no_orbit() {
let cables = vec![vec![
Wrap {
anchor: None,
span: 1.0,
},
Wrap {
anchor: Some(0),
span: 1.0,
},
]];
assert_eq!(
assign(
&cables,
1,
&contested(&cables, 1),
budget(cables.len(), 3),
&mut spotless
)[0],
vec![0, 0]
);
}
}