///|
fn evaluate_array(
  program : Program,
  plan : ArrayPlan,
  value : Json,
  path : String,
  failures : Array[Fault]?,
  depth : Int32,
  limit : Int32,
  scope : Array[Int32],
  evaluated : EvaluationMarks?,
) -> Bool raise {
  guard value is Array(values) else { return true }
  let mut valid = true
  // Only homogeneous arrays of constructed primitive values can be hashed
  // without losing the exact decimal and recursive JSON equality contract.
  let mut unique_kind = if plan.unique && values.length() >= 16 { 3 } else { 0 }
  if unique_kind != 0 {
    for item in values {
      match item {
        String(_) =>
          if unique_kind == 3 {
            unique_kind = 1
          } else if unique_kind != 1 {
            unique_kind = 0
            break
          }
        Number(_, repr~) => {
          if repr is Some(_) {
            unique_kind = 0
            break
          }
          if unique_kind == 3 {
            unique_kind = 2
          } else if unique_kind != 2 {
            unique_kind = 0
            break
          }
        }
        _ => {
          unique_kind = 0
          break
        }
      }
    }
  }
  // Preserve the original string-set allocation behavior; only primitive
  // doubles benefit from the specialized IEEE-bit hashing path.
  let seen : Set[String] = Set([])
  let seen_numbers : Set[Double]? = if unique_kind == 2 {
    Some(Set([]))
  } else {
    None
  }
  if plan.minimum is Some(bound) && values.length() < bound {
    valid = fail(
      failures,
      path,
      pointer_step(plan.path, "minItems"),
      "minItems",
      "array has too few items",
    )
    if failures is None {
      return false
    }
  }
  if plan.maximum is Some(bound) && values.length() > bound {
    valid = fail(
      failures,
      path,
      pointer_step(plan.path, "maxItems"),
      "maxItems",
      "array has too many items",
    )
    if failures is None {
      return false
    }
  }
  let contains = fresh_marks(evaluated is Some(_))
  let mut prefix_valid = true
  let mut items_valid = true
  let mut matches = 0
  for index, child in values {
    let child_path = if failures is None {
      ""
    } else {
      pointer_step(path, index.to_string())
    }
    let schema = if index < plan.prefix.length() {
      Some(plan.prefix[index])
    } else {
      plan.items
    }
    if schema is Some(node) &&
      !evaluate(
        program,
        node,
        child,
        child_path,
        failures,
        depth + 1,
        limit,
        scope~,
      ) {
      valid = false
      if index < plan.prefix.length() {
        prefix_valid = false
      } else {
        items_valid = false
      }
      if failures is None {
        return false
      }
    }
    if plan.contains is Some(node) &&
      evaluate(program, node, child, child_path, None, depth + 1, limit, scope~) {
      matches += 1
      if contains is Some(marks) {
        marks.items.add(index)
      }
    }
    if plan.unique {
      let duplicate = if unique_kind == 1 {
        let key = match child {
          String(text) => text
          _ => abort("unique kind")
        }
        !seen.add_and_check(key)
      } else if unique_kind == 2 {
        let number = match child {
          Number(number, ..) => number
          _ => abort("unique kind")
        }
        // Core Hash[Double] normalizes signed zero and hashes the IEEE bits
        // directly. No decimal rendering or string allocation per element.
        let seen = seen_numbers.unwrap()
        !seen.add_and_check(number)
      } else {
        let mut duplicate = false
        // SAFETY (Core Array::unsafe_get): index is from the outer
        // enumeration of values, hence 0 <= previous < index < values.length().
        // Neither schema_equal nor this loop mutates values.
        for previous in 0.. bound {
      valid = fail(
        failures,
        path,
        pointer_step(plan.path, "maxContains"),
        "maxContains",
        "too many items matched contains",
      )
      contains_valid = false
    }
    if contains_valid {
      merge_marks(evaluated, contains)
    }
  }
  valid
}