///|
pub(all) struct ChangeWitness {
  packet : Packet
  before : Decision
  after : Decision
} derive(Eq, Debug)

///|
pub struct DiffReport {
  newly_allowed : ChangeWitness?
  newly_denied : ChangeWitness?
  retained_nodes : Int
} derive(Eq, Debug)

///|
pub fn DiffReport::equivalent(self : DiffReport) -> Bool {
  self.newly_allowed is None && self.newly_denied is None
}

///|
pub fn DiffReport::allowed_witness(self : DiffReport) -> ChangeWitness? {
  self.newly_allowed
}

///|
pub fn DiffReport::denied_witness(self : DiffReport) -> ChangeWitness? {
  self.newly_denied
}

///|
pub fn DiffReport::node_count(self : DiffReport) -> Int {
  self.retained_nodes
}

///|
fn replay_change(
  packet : Packet?,
  before : Policy,
  after : Policy,
  expected_before : Action,
  expected_after : Action,
) -> ChangeWitness? raise PolicyError {
  match packet {
    None => None
    Some(packet) => {
      let old = before.evaluate(packet)
      let next = after.evaluate(packet)
      if old.action != expected_before || next.action != expected_after {
        raise Internal("internal semantic witness replay mismatch")
      }
      Some({ packet, before: old, after: next })
    }
  }
}

///|
/// Exact equality over the finite IPv4/TCP/UDP model, not device equivalence.
pub fn compare(
  before : Policy,
  after : Policy,
  max_nodes? : Int = 100000,
) -> DiffReport raise PolicyError {
  let e = Engine::new(max_nodes)
  let old = e.compile(before).allowed
  let next = e.compile(after).allowed
  let newly_allowed = replay_change(
    e.witness(e.both(e.not(old), next)),
    before,
    after,
    Deny,
    Allow,
  )
  let newly_denied = replay_change(
    e.witness(e.both(old, e.not(next))),
    before,
    after,
    Allow,
    Deny,
  )
  {
    newly_allowed,
    newly_denied,
    retained_nodes: e.manager.retained_node_count(),
  }
}