///|
/// Summary of stereo depth recovered from disparity measurements.
pub(all) struct StereoDepthReport {
  total : Int
  valid_count : Int
  invalid_count : Int
  mean : Double
  minimum : Double
  maximum : Double
} derive(Debug, Eq)

///|
pub fn disparity_to_depth(
  disparity : Double,
  baseline : Double,
  focal : Double,
) -> Double {
  if disparity <= 0.0 || baseline <= 0.0 || focal <= 0.0 {
    0.0
  } else {
    baseline * focal / disparity
  }
}

///|
pub fn stereo_depth_report(
  disparities : ArrayView[Double],
  baseline~ : Double,
  focal~ : Double,
) -> StereoDepthReport {
  let mut valid = 0
  let mut sum = 0.0
  let mut minimum = 0.0
  let mut maximum = 0.0
  for disparity in disparities {
    let depth = disparity_to_depth(disparity, baseline, focal)
    if depth > 0.0 {
      if valid == 0 {
        minimum = depth
        maximum = depth
      } else {
        minimum = minimum.min(depth)
        maximum = maximum.max(depth)
      }
      sum += depth
      valid += 1
    }
  }
  {
    total: disparities.length(),
    valid_count: valid,
    invalid_count: disparities.length() - valid,
    mean: if valid == 0 {
      0.0
    } else {
      sum / valid.to_double()
    },
    minimum: if valid == 0 {
      0.0
    } else {
      minimum
    },
    maximum: if valid == 0 {
      0.0
    } else {
      maximum
    },
  }
}

///|
pub fn stereo_depths2(
  disparities : ArrayView[Double],
  baseline : Double,
  focal : Double,
) -> Array[Double] {
  let result : Array[Double] = []
  for disparity in disparities {
    result.push(disparity_to_depth(disparity, baseline, focal))
  }
  result
}

///|
pub fn depth_to_disparity(
  depth : Double,
  baseline : Double,
  focal : Double,
) -> Double {
  if depth <= 0.0 || baseline <= 0.0 || focal <= 0.0 {
    0.0
  } else {
    baseline * focal / depth
  }
}

///|
pub fn disparity_error_to_depth_error(
  disparity : Double,
  disparity_error : Double,
  baseline : Double,
  focal : Double,
) -> Double {
  let depth = disparity_to_depth(disparity, baseline, focal)
  if depth <= 0.0 {
    0.0
  } else {
    disparity_error.abs() / disparity.max(0.000000000001) * depth
  }
}

///|
pub fn stereo_valid_ratio(report : StereoDepthReport) -> Double {
  if report.total == 0 {
    0.0
  } else {
    report.valid_count.to_double() / report.total.to_double()
  }
}

///|
pub fn stereo_report_is_usable(
  report : StereoDepthReport,
  minimum_ratio : Double,
  maximum_span : Double,
) -> Bool {
  stereo_valid_ratio(report) >= minimum_ratio &&
  report.maximum - report.minimum <= maximum_span
}

///|
pub fn clip_disparities(
  disparities : ArrayView[Double],
  minimum : Double,
  maximum : Double,
) -> Array[Double] {
  let result : Array[Double] = []
  for disparity in disparities {
    result.push(disparity.max(minimum).min(maximum))
  }
  result
}

///|
pub fn disparity_histogram(
  disparities : ArrayView[Double],
  bins : Int,
  minimum : Double,
  maximum : Double,
) -> Array[Int] {
  let result : Array[Int] = []
  for _ in 0..= minimum && disparity <= maximum && result.length() > 0 {
      let index = ((disparity - minimum) / width * result.length().to_double())
        .to_int()
        .max(0)
        .min(result.length() - 1)
      result[index] += 1
    }
  }
  result
}

///|
pub fn stereo_consistency(
  left : ArrayView[Double],
  right : ArrayView[Double],
  tolerance : Double,
) -> Double {
  let count = left.length().min(right.length())
  if count == 0 {
    0.0
  } else {
    let mut valid = 0
    for i in 0..