///|
pub fn count_single_split_cases(input : Bytes) -> Int {
  input.length() + 1
}

///|
pub fn count_three_way_cases(
  first_cuts : Array[Int],
  second_cuts : Array[Int],
) -> Int {
  first_cuts.length() * second_cuts.length()
}

///|
pub fn feed_single_split(
  input : Bytes,
  split : Int,
  policy : DecodePolicy,
) -> DecodeProgress {
  let decoder = Decoder::new(policy)
  let first = Bytes::from_array(input.to_array()[0:split])
  let second = Bytes::from_array(input.to_array()[split:])
  match decoder.feed(first) {
    Done(frame) => Done(frame)
    Failed(error) => Failed(error)
    NeedMore => decoder.feed(second)
  }
}

///|
pub fn feed_three_way(
  input : Bytes,
  first_cut : Int,
  second_cut : Int,
  policy : DecodePolicy,
) -> DecodeProgress {
  let decoder = Decoder::new(policy)
  match decoder.feed(Bytes::from_array(input.to_array()[0:first_cut])) {
    Done(frame) => Done(frame)
    Failed(error) => Failed(error)
    NeedMore =>
      match
        decoder.feed(Bytes::from_array(input.to_array()[first_cut:second_cut])) {
        Done(frame) => Done(frame)
        Failed(error) => Failed(error)
        NeedMore =>
          decoder.feed(Bytes::from_array(input.to_array()[second_cut:]))
      }
  }
}

///|
pub fn assert_all_single_splits(input : Bytes, policy : DecodePolicy) -> Int {
  for split = 0; split <= input.length(); split = split + 1 {
    match feed_single_split(input, split, policy) {
      Done(_) => ()
      NeedMore =>
        abort("single split unexpectedly needed more data at \{split}")
      Failed(error) =>
        abort("single split failed at \{split}: \{error.context}")
    }
  }
  input.length() + 1
}

///|
pub fn assert_selected_three_way_splits(
  input : Bytes,
  cuts : Array[Int],
  policy : DecodePolicy,
) -> Int {
  let mut count = 0
  for first in cuts {
    for second in cuts {
      if first <= second && second <= input.length() {
        match feed_three_way(input, first, second, policy) {
          Done(_) => ()
          NeedMore => abort("three-way split unexpectedly needed more data")
          Failed(error) => abort("three-way split failed: \{error.context}")
        }
        count = count + 1
      }
    }
  }
  count
}

///|
pub fn assert_all_prefix_truncations(
  header : Bytes,
  policy : DecodePolicy,
) -> Int {
  for length = 0; length < header.length(); length = length + 1 {
    let decoder = Decoder::new(policy)
    match decoder.feed(Bytes::from_array(header.to_array()[0:length])) {
      NeedMore => ()
      Failed(error) =>
        if error.kind != NeedMoreData &&
          error.kind != TruncatedAddress &&
          error.kind != TruncatedTlv {
          abort("unexpected truncation error")
        }
      Done(_) => abort("truncated header decoded as complete")
    }
  }
  header.length()
}