///|
/// Different comparisons answer different questions; none implies source identity.
pub(all) struct MapComparison {
  same_source : Bool
  same_topology : Bool
  same_cut_geometry : Bool
  same_size_bands : Bool
  same_fragment_sequences : Bool
  added_top_cuts : Array[Int]
  removed_top_cuts : Array[Int]
} derive(Eq, Debug, ToJson)

///|
/// Sort literal oriented sequences; this ignores fragment order, NOT base rotation.
fn fragment_sequences(report : Digest) -> Array[String] {
  let strings = report.fragments.map(f => f.sequence)
  strings.sort()
  strings
}

///|
/// Verify both reports then distinguish coordinate, size and sequence equivalence.
/// Rotated circular references may share size bands while their coordinates differ.
pub fn compare_maps(
  left_dna : Dna,
  left_enzymes : Array[Enzyme],
  left : Digest,
  right_dna : Dna,
  right_enzymes : Array[Enzyme],
  right : Digest,
) -> MapComparison raise RestrictError {
  verify_digest(left_dna, left_enzymes, left)
  verify_digest(right_dna, right_enzymes, right)
  let lt = left.cuts.map(s => s.top)
  let rt = right.cuts.map(s => s.top)
  let lset : Map[Int, Bool] = Map([])
  let rset : Map[Int, Bool] = Map([])
  for x in lt {
    lset[x] = true
  }
  for x in rt {
    rset[x] = true
  }
  {
    same_source: left_dna.bases == right_dna.bases,
    same_topology: left.topology == right.topology,
    same_cut_geometry: left.topology == right.topology &&
    left.source_length == right.source_length &&
    left.cuts.map(s => (s.top, s.stagger)) ==
    right.cuts.map(s => (s.top, s.stagger)),
    same_size_bands: size_bands(left.fragments) == size_bands(right.fragments),
    same_fragment_sequences: fragment_sequences(left) ==
    fragment_sequences(right),
    added_top_cuts: rt.filter(x => !lset.contains(x)),
    removed_top_cuts: lt.filter(x => !rset.contains(x)),
  }
}