use super::dist::SELECTOR_BASE;
use super::ir::{ArrayNode, Expr, VarId};
use std::collections::BTreeSet;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Cycle;
fn split(e: &Expr, others: &mut BTreeSet<VarId>, args: &mut BTreeSet<VarId>) {
match e {
Expr::Array(ArrayNode::Call { args: a, .. }) => {
for x in a {
x.for_each_var(&mut |v, _| {
args.insert(v);
});
}
}
Expr::Var(v, _) => {
others.insert(*v);
}
other => {
let _ = other.try_map_children::<()>(&mut |c| {
split(c, others, args);
Ok(c.clone())
});
}
}
}
pub fn stages(n_vars: usize, conjuncts: &[Expr]) -> Result<Vec<u32>, Cycle> {
let mut edges: BTreeSet<(usize, usize)> = BTreeSet::new();
for c in conjuncts {
let (mut others, mut args) = (BTreeSet::new(), BTreeSet::new());
split(c, &mut others, &mut args);
let real =
|v: &VarId| (*v < SELECTOR_BASE && (*v as usize) < n_vars).then_some(*v as usize);
let args: Vec<usize> = args.iter().filter_map(real).collect();
for o in others.iter().filter_map(real) {
if args.contains(&o) {
continue;
}
for &a in &args {
edges.insert((a, o));
}
}
}
let mut indeg = vec![0usize; n_vars];
let mut succ: Vec<Vec<usize>> = vec![Vec::new(); n_vars];
for &(a, b) in &edges {
indeg[b] += 1;
succ[a].push(b);
}
let mut stage = vec![0u32; n_vars];
let mut ready: Vec<usize> = (0..n_vars).filter(|&v| indeg[v] == 0).collect();
let mut done = 0;
while let Some(v) = ready.pop() {
done += 1;
for &w in &succ[v] {
stage[w] = stage[w].max(stage[v] + 1);
indeg[w] -= 1;
if indeg[w] == 0 {
ready.push(w);
}
}
}
if done != n_vars {
return Err(Cycle);
}
Ok(stage)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::constraints::ir::{BinOp, Ty};
fn s32() -> Ty {
Ty::signed(32)
}
fn var(i: u32) -> Expr {
Expr::Var(i, s32())
}
fn call(args: Vec<Expr>) -> Expr {
Expr::Array(ArrayNode::Call {
func: 0,
args,
ty: s32(),
})
}
fn eq(a: Expr, b: Expr) -> Expr {
Expr::Binary(BinOp::Eq, Box::new(a), Box::new(b), Ty::BIT)
}
#[test]
fn no_call_means_one_stage() {
assert_eq!(stages(3, &[eq(var(0), var(1))]), Ok(vec![0, 0, 0]));
}
#[test]
fn an_argument_is_solved_before_the_other_variables_of_its_constraint() {
let c = eq(var(0), call(vec![var(1)]));
assert_eq!(stages(2, &[c]), Ok(vec![1, 0]));
}
#[test]
fn chains_take_the_longest_path() {
let cs = [
eq(var(0), call(vec![var(1)])),
eq(var(1), call(vec![var(2)])),
];
assert_eq!(stages(3, &cs), Ok(vec![2, 1, 0]));
}
#[test]
fn two_arguments_of_one_constraint_share_a_stage() {
let c = eq(call(vec![var(0)]), call(vec![var(1)]));
assert_eq!(stages(2, &[c]), Ok(vec![0, 0]));
assert_eq!(stages(1, &[eq(var(0), call(vec![var(0)]))]), Ok(vec![0]));
}
#[test]
fn nested_calls_put_every_inner_variable_before_the_rest() {
let c = eq(var(0), call(vec![call(vec![var(1), var(2)])]));
assert_eq!(stages(3, &[c]), Ok(vec![1, 0, 0]));
}
#[test]
fn a_circular_order_is_an_error() {
let cs = [
eq(var(0), call(vec![var(1)])),
eq(var(1), call(vec![var(0)])),
];
assert_eq!(stages(2, &cs), Err(Cycle));
}
#[test]
fn selector_variables_take_no_part() {
let c = eq(var(SELECTOR_BASE), call(vec![var(0)]));
assert_eq!(stages(1, &[c]), Ok(vec![0]));
}
}