use super::capability::Capability;
use super::requirement::{MatchFailure, Requirement};
pub const SEARCH_BUDGET: usize = 100_000;
pub type Assignment = Vec<Vec<usize>>;
struct Search<'a> {
exclusive: Vec<(usize, usize, Vec<usize>)>,
shared: Vec<(usize, usize, &'a [usize])>,
used: Vec<bool>,
chosen: Vec<Vec<usize>>,
steps: usize,
}
impl Search<'_> {
fn exclusive_from(&mut self, k: usize) -> bool {
if k == self.exclusive.len() {
return self.shared_fits();
}
let (_, need, ref cands) = self.exclusive[k];
let cands = cands.clone();
let mut pick = Vec::with_capacity(need);
self.choose(k, &cands, 0, need, &mut pick)
}
fn choose(
&mut self,
k: usize,
cands: &[usize],
from: usize,
need: usize,
pick: &mut Vec<usize>,
) -> bool {
self.steps += 1;
if self.steps > SEARCH_BUDGET {
return false;
}
if need == 0 {
self.chosen[k] = pick.clone();
return self.exclusive_from(k + 1);
}
for i in from..cands.len() {
if cands.len() - i < need {
break;
}
let c = cands[i];
if self.used[c] {
continue;
}
self.used[c] = true;
pick.push(c);
let ok = self.choose(k, cands, i + 1, need - 1, pick);
pick.pop();
self.used[c] = false;
if ok {
return true;
}
if self.steps > SEARCH_BUDGET {
return false;
}
}
false
}
fn shared_fits(&self) -> bool {
self.shared
.iter()
.all(|(_, need, cands)| cands.iter().filter(|&&c| !self.used[c]).count() >= *need)
}
}
pub fn match_all(
reqs: &[Requirement],
caps: &[Capability],
) -> Result<Assignment, Vec<(usize, MatchFailure)>> {
let mut failures = Vec::new();
let mut cands: Vec<Vec<usize>> = vec![Vec::new(); reqs.len()];
for (ri, req) in reqs.iter().enumerate() {
match req.candidates(caps) {
Ok(c) if c.len() >= req.count as usize => cands[ri] = c,
Ok(c) => failures.push((
ri,
MatchFailure::InsufficientCount {
have: c.len() as u32,
need: req.count,
},
)),
Err(f) => failures.push((ri, f)),
}
}
if !failures.is_empty() {
return Err(failures);
}
let mut demand = vec![0usize; caps.len()];
cands.iter().flatten().for_each(|&c| demand[c] += 1);
let mut exclusive: Vec<(usize, usize, Vec<usize>)> = reqs
.iter()
.enumerate()
.filter(|(_, r)| r.exclusive)
.map(|(ri, r)| {
let mut c = cands[ri].clone();
c.sort_by_key(|&i| (demand[i], i));
(ri, r.count as usize, c)
})
.collect();
exclusive.sort_by_key(|(ri, need, c)| (c.len() - need, *ri));
let shared = reqs
.iter()
.enumerate()
.filter(|(_, r)| !r.exclusive)
.map(|(ri, r)| (ri, r.count as usize, cands[ri].as_slice()))
.collect();
let mut s = Search {
exclusive,
shared,
used: vec![false; caps.len()],
chosen: vec![Vec::new(); reqs.len()],
steps: 0,
};
if s.exclusive_from(0) {
let mut claimed = vec![false; caps.len()];
let mut out: Assignment = vec![Vec::new(); reqs.len()];
for (k, (ri, _, _)) in s.exclusive.iter().enumerate() {
s.chosen[k].iter().for_each(|&i| claimed[i] = true);
out[*ri] = s.chosen[k].clone();
}
for (ri, need, c) in &s.shared {
out[*ri] = c
.iter()
.copied()
.filter(|&i| !claimed[i])
.take(*need)
.collect();
}
return Ok(out);
}
let mut claimable = vec![false; caps.len()];
for (_, _, c) in &s.exclusive {
c.iter().for_each(|&i| claimable[i] = true);
}
Err(reqs
.iter()
.enumerate()
.filter(|(ri, r)| r.exclusive || cands[*ri].iter().any(|&i| claimable[i]))
.map(|(ri, r)| (ri, MatchFailure::Contended { need: r.count }))
.collect())
}