///|
/// Singleton decision projection. This is pignistic probability, not belief.
/// Conflict is removed by conditioning on directly summed nonempty mass.
pub fn Mass::pignistic(
  self : Mass,
  min_survival~ : Double = 1.0e-12,
) -> Array[Double] raise BeliefError {
  check_survival_threshold(min_survival)
  let surviving = self.survival()
  if surviving <= min_survival {
    raise TotalConflict
  }
  let result = Array::make(self.domain.size(), 0.0)
  let corrections = Array::make(result.length(), 0.0)
  for (mask, value) in self.focals {
    if mask != 0 {
      let share = value / surviving / cardinality(mask).to_double()
      for i = 0; i < result.length(); i = i + 1 {
        if (mask & (1 << i)) != 0 {
          let adjusted = share - corrections[i]
          let next = result[i] + adjusted
          corrections[i] = next - result[i] - adjusted
          result[i] = next
        }
      }
    }
  }
  result
}

///|
pub struct HypothesisScore {
  index : Int32
  label : String
  belief : Double
  plausibility : Double
  pignistic : Double
} derive(Debug, Eq)

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

///|
pub extend HypothesisScore with Eq::{equal, not_equal}

///|
/// Stable descending pignistic ranking: ties retain original frame order.
/// Belief and plausibility columns remain unnormalized open-world bounds.
pub fn Mass::ranking(
  self : Mass,
  min_survival~ : Double = 1.0e-12,
) -> Array[HypothesisScore] raise BeliefError {
  let probabilities = self.pignistic(min_survival~)
  let result : Array[HypothesisScore] = []
  for index = 0; index < self.domain.size(); index = index + 1 {
    let mask = 1 << index
    result.push({
      index,
      label: self.domain.names[index],
      belief: self.belief(mask),
      plausibility: self.plausibility(mask),
      pignistic: probabilities[index],
    })
  }
  for i = 1; i < result.length(); i = i + 1 {
    let item = result[i]
    let mut j = i
    while j > 0 && result[j - 1].pignistic < item.pignistic {
      result[j] = result[j - 1]
      j = j - 1
    }
    result[j] = item
  }
  result
}