///|
pub fn EpochFuseFilter::build(
  entries : Array[EpochHash],
) -> Result[EpochFuseFilter, FuseError] {
  if entries.length() == 0 {
    return Err(EmptyInput)
  }
  let sorted = entries.copy()
  sorted.sort_by((left, right) => {
    let order = left.epoch.compare(right.epoch)
    if order == 0 {
      left.hash.compare(right.hash)
    } else {
      order
    }
  })
  let epochs : Array[FuseEpoch] = []
  let mut cursor = 0
  while cursor < sorted.length() {
    let epoch = sorted[cursor].epoch
    let hashes : Array[Int] = []
    while cursor < sorted.length() && sorted[cursor].epoch == epoch {
      hashes.push(sorted[cursor].hash)
      cursor = cursor + 1
    }
    match BinaryFuseFilter::build(hashes) {
      Ok(filter) => epochs.push({ epoch, filter })
      Err(error) => return Err(error)
    }
  }
  Ok({ epochs, })
}

///|
pub fn EpochFuseFilter::contains_at(
  self : EpochFuseFilter,
  epoch : Int,
  hash : Int,
) -> Bool {
  match self.find_epoch(epoch) {
    Some(filter) => filter.contains(hash)
    None => false
  }
}

///|
pub fn EpochFuseFilter::contains_any(
  self : EpochFuseFilter,
  hash : Int,
) -> Bool {
  for item in self.epochs {
    if item.filter.contains(hash) {
      return true
    }
  }
  false
}

///|
pub fn EpochFuseFilter::epoch_count(self : EpochFuseFilter) -> Int {
  self.epochs.length()
}

///|
pub fn EpochFuseFilter::stats(self : EpochFuseFilter) -> EpochStats {
  let mut key_count = 0
  for item in self.epochs {
    key_count = key_count + item.filter.len()
  }
  {
    epoch_count: self.epochs.length(),
    key_count,
    first_epoch: self.epochs[0].epoch,
    last_epoch: self.epochs[self.epochs.length() - 1].epoch,
  }
}

///|
pub fn EpochFuseFilter::keep_from(
  self : EpochFuseFilter,
  minimum_epoch : Int,
) -> EpochFuseFilter {
  let epochs : Array[FuseEpoch] = []
  for item in self.epochs {
    if item.epoch >= minimum_epoch {
      epochs.push(item)
    }
  }
  { epochs, }
}

///|
pub fn EpochFuseFilter::validate(
  self : EpochFuseFilter,
  entries : Array[EpochHash],
) -> Bool {
  if self.epochs.length() == 0 {
    return false
  }
  for index in 1..= self.epochs[index].epoch {
      return false
    }
  }
  for entry in entries {
    if entry.hash < 0 || !self.contains_at(entry.epoch, entry.hash) {
      return false
    }
  }
  true
}

///|
fn EpochFuseFilter::find_epoch(
  self : EpochFuseFilter,
  epoch : Int,
) -> BinaryFuseFilter? {
  let mut low = 0
  let mut high = self.epochs.length()
  while low < high {
    let middle = low + (high - low) / 2
    let item = self.epochs[middle]
    if item.epoch == epoch {
      return Some(item.filter)
    }
    if item.epoch < epoch {
      low = middle + 1
    } else {
      high = middle
    }
  }
  None
}