///|
priv struct Compiled {
  allowed : @bdd.Bdd
  effective : Array[@bdd.Bdd]
  predicates : Array[@bdd.Bdd]
}

///|
fn Engine::compile(
  self : Engine,
  policy : Policy,
) -> Compiled raise PolicyError {
  let mut remaining = self.manager.true_bdd()
  let mut allowed = self.manager.false_bdd()
  let effective = []
  let predicates = []
  for rule in policy.rules {
    let predicate = self.predicate(rule)
    let hit = self.both(remaining, predicate)
    predicates.push(predicate)
    effective.push(hit)
    if rule.action == Allow {
      allowed = self.or(allowed, hit)
    }
    remaining = self.both(remaining, self.not(predicate))
  }
  if policy.default_action == Allow {
    allowed = self.or(allowed, remaining)
  }
  { allowed, effective, predicates }
}

///|
/// Finds a packet receiving a requested decision, not merely matching a rule.
pub fn Policy::find(
  self : Policy,
  action : Action,
  max_nodes? : Int = 100000,
) -> Packet? raise PolicyError {
  let e = Engine::new(max_nodes)
  let c = e.compile(self)
  let root = if action == Allow { c.allowed } else { e.not(c.allowed) }
  let packet = e.witness(root)
  if packet is Some(p) {
    if self.evaluate(p).action != action {
      raise Internal("internal decision witness mismatch")
    }
  }
  packet
}