///|
/// Authenticate NSEC3 NXDOMAIN with one parameter set and complete proof closure.
/// Opt-out coverage is deliberately Indeterminate, never Secure NXDOMAIN.
pub fn nsec3_nxdomain(
  proofs : Array[AuthenticatedSet],
  zone : Name,
  name : Name,
  now : UInt32,
  limits : Limits,
) -> Result[Denial, DnsError] {
  match limits.validate() {
    Ok(_) => ()
    Err(e) => return Err(e)
  }
  match valid_query(zone, name, 1) {
    Ok(_) => ()
    Err(e) => return Err(e)
  }
  if name == zone {
    return Err(Bogus("zone apex exists"))
  }
  if name.label_count() - zone.label_count() > limits.max_chain {
    return Err(ResourceLimit("NSEC3 ancestor hashing depth"))
  }
  if proofs.is_empty() || proofs.length() > limits.max_proofs {
    return Err(ResourceLimit("NSEC3 proof count"))
  }
  let decoded : Array[(Nsec3, UInt32)] = []
  for proof in proofs {
    let rr = match ensure_proof(proof, zone, 50, now) {
      Ok(r) => r
      Err(e) => return Err(e)
    }
    let n = match Nsec3::parse(rr, zone, limits) {
      Ok(n) => n
      Err(e) => return Err(e)
    }
    if !decoded.is_empty() &&
      (n.salt != decoded[0].0.salt || n.iterations != decoded[0].0.iterations) {
      return Err(Bogus("mixed NSEC3 salts or iteration parameters"))
    }
    let ttl = match proof.remaining(now) {
      Ok(t) => t
      Err(e) => return Err(e)
    }
    decoded.push((n, ttl))
  }
  let params = decoded[0].0
  let query_hash = match
    nsec3_hash(name, params.salt, params.iterations, limits) {
    Ok(b) => b
    Err(e) => return Err(e)
  }
  for entry in decoded {
    if entry.0.owner_hash == query_hash {
      return Err(Bogus("NXDOMAIN query hash exists"))
    }
  }
  let mut closest : Name? = None
  let mut closest_ttl = 0U
  let mut candidate = name.parent()
  while candidate is Some(c) {
    if !c.is_subdomain(zone) {
      break
    }
    let hash = match nsec3_hash(c, params.salt, params.iterations, limits) {
      Ok(b) => b
      Err(e) => return Err(e)
    }
    for entry in decoded {
      if entry.0.owner_hash == hash {
        let bitmap = entry.0.bitmap
        if bitmap.contains(5) ||
          bitmap.contains(39) ||
          (bitmap.contains(2) && !bitmap.contains(6)) {
          return Err(Indeterminate("closest encloser is alias or delegation"))
        }
        closest = Some(c)
        closest_ttl = entry.1
        break
      }
    }
    if closest is Some(_) {
      break
    }
    candidate = c.parent()
  }
  let ce = match closest {
    Some(c) => c
    None => return Err(Indeterminate("missing matching closest-encloser hash"))
  }
  let next = match next_closer(name, ce) {
    Ok(n) => n
    Err(e) => return Err(e)
  }
  let wildcard = match ce.wildcard() {
    Ok(n) => n
    Err(e) => return Err(e)
  }
  let next_hash = match
    nsec3_hash(next, params.salt, params.iterations, limits) {
    Ok(b) => b
    Err(e) => return Err(e)
  }
  let wildcard_hash = match
    nsec3_hash(wildcard, params.salt, params.iterations, limits) {
    Ok(b) => b
    Err(e) => return Err(e)
  }
  let mut next_ttl : UInt32? = None
  let mut wildcard_ttl : UInt32? = None
  let mut opt_out = false
  for entry in decoded {
    let n = entry.0
    if n.owner_hash == wildcard_hash {
      return Err(Bogus("wildcard hash exists"))
    }
    if n.covers_hash(next_hash) {
      if n.opt_out {
        opt_out = true
      } else {
        next_ttl = Some(entry.1)
      }
    }
    if n.covers_hash(wildcard_hash) {
      if n.opt_out {
        opt_out = true
      } else {
        wildcard_ttl = Some(entry.1)
      }
    }
  }
  match (next_ttl, wildcard_ttl) {
    (Some(a), Some(b)) =>
      Ok({ kind: NxDomain, lifetime: min_ttl(closest_ttl, min_ttl(a, b)), })
    _ =>
      if opt_out {
        Err(Indeterminate("opt-out cannot authenticate general NXDOMAIN"))
      } else {
        Err(Indeterminate("missing next-closer or wildcard NSEC3 coverage"))
      }
  }
}