///|
/// A small path-set network model. Paths use zero-based component indexes.
pub struct NetworkReliability {
  component_count : Int
  paths : Array[Array[Int]]
  reliability : Double
  path_contributions : Array[Double]
}

///|
pub fn network_reliability(
  component_count~ : Int,
  paths~ : Array[Array[Int]],
  reliability~ : Double,
  path_contributions~ : Array[Double],
) -> NetworkReliability {
  { component_count, paths, reliability, path_contributions }
}

///|
pub fn path_reliability(
  path : Array[Int],
  components : Array[Double],
) -> Double {
  let mut result = 1.0
  for component in path {
    result *= components[component]
  }
  result
}

///|
pub fn network_reliability_from_paths(
  component_count : Int,
  paths : Array[Array[Int]],
  components : Array[Double],
) -> NetworkReliability {
  if components.length() != component_count || paths.is_empty() {
    abort("invalid network dimensions")
  }
  let contributions = paths.map(path => path_reliability(path, components))
  let mut reliability = 0.0
  for contribution in contributions {
    reliability += contribution
  }
  reliability = reliability.min(1.0)
  network_reliability(
    component_count~,
    paths~,
    reliability~,
    path_contributions=contributions,
  )
}

///|
pub fn network_component_importance(
  network : NetworkReliability,
  components : Array[Double],
) -> Array[Double] {
  if components.length() != network.component_count {
    abort("component count mismatch")
  }
  Array::makei(network.component_count, component => {
    let mut contribution = 0.0
    for i in 0.. Array[Array[Int]] {
  let result : Array[Array[Int]] = []
  for path in paths {
    result.push(path.copy())
  }
  result.sort_by((left, right) => {
    if left.length() < right.length() {
      -1
    } else if left.length() > right.length() {
      1
    } else {
      0
    }
  })
  result
}

///|
pub fn bridge_component_reliability(
  left : Double,
  bridge : Double,
  right : Double,
) -> Double {
  let series = left * bridge * right
  let bypass = left * right
  (series + bypass - series * bypass).min(1.0)
}

///|
pub fn network_failure_probability(network : NetworkReliability) -> Double {
  1.0 - network.reliability
}

///|
pub fn network_redundancy_gain(
  base : NetworkReliability,
  redundant : NetworkReliability,
) -> Double {
  redundant.reliability - base.reliability
}