///|
pub(all) struct RankAgreement {
  spearman : Double?
  kendall_tau_b : Double?
  concordant_pairs : Int32
  discordant_pairs : Int32
  shared_winners : Array[String]
} derive(@debug.Debug)

///|
fn aligned_indices(a : Ranking, b : Ranking) -> Array[Int32] raise ChoiceError {
  if a.labels.length() != b.labels.length() {
    raise Invalid("ranking label sets differ")
  }
  let indices : Array[Int32] = []
  for label in a.labels {
    let mut found = -1
    for j, candidate in b.labels {
      if label == candidate {
        found = j
        break
      }
    }
    if found < 0 {
      raise Invalid("ranking label sets differ")
    }
    indices.push(found)
  }
  indices
}

///|
fn midranks(r : Ranking) -> Array[Double] {
  r.ranks.map(rank => {
    let count = r.ranks.filter(value => value == rank).length()
    rank.to_double() + (count - 1).to_double() / 2.0
  })
}

///|
pub fn rank_agreement(
  left : Ranking,
  right : Ranking,
) -> RankAgreement raise ChoiceError {
  let a = validated_ranking(left)
  let b = validated_ranking(right)
  let indices = aligned_indices(a, b)
  let x = midranks(a)
  let rb = midranks(b)
  let y = indices.map(i => rb[i])
  let n = x.length()
  let xm = sum(x) / n.to_double()
  let ym = sum(y) / n.to_double()
  let mut xx = 0.0
  let mut yy = 0.0
  let mut xy = 0.0
  let mut concordant_pairs = 0
  let mut discordant_pairs = 0
  let mut tied_left = 0
  let mut tied_right = 0
  for i in 0.. 0.0 {
        concordant_pairs += 1
      } else {
        discordant_pairs += 1
      }
    }
  }
  let spearman = if xx == 0.0 || yy == 0.0 {
    None
  } else {
    Some(xy / (xx * yy).sqrt())
  }
  let comparable = concordant_pairs + discordant_pairs
  let denominator = ((comparable + tied_left).to_double() *
  (comparable + tied_right).to_double()).sqrt()
  let kendall_tau_b = if denominator == 0.0 {
    None
  } else {
    Some((concordant_pairs - discordant_pairs).to_double() / denominator)
  }
  let shared_winners = [
    for i in 0.. a.labels[i]
  ]
  {
    spearman,
    kendall_tau_b,
    concordant_pairs,
    discordant_pairs,
    shared_winners,
  }
}

///|
pub extend RankAgreement with @debug.Debug::{to_repr}