///|
/// Quality metrics for comparing preview and final render sample plans.
pub struct RenderQualityMetrics2 {
  temporal_error : Double
  spatial_error : Double
  color_error : Double
  dropped_frames : Int
  coverage : Double
} derive(Debug)

///|
pub fn RenderQualityMetrics2::temporal_error(
  self : RenderQualityMetrics2,
) -> Double {
  self.temporal_error
}

///|
pub fn RenderQualityMetrics2::spatial_error(
  self : RenderQualityMetrics2,
) -> Double {
  self.spatial_error
}

///|
pub fn RenderQualityMetrics2::color_error(
  self : RenderQualityMetrics2,
) -> Double {
  self.color_error
}

///|
pub fn RenderQualityMetrics2::dropped_frames(
  self : RenderQualityMetrics2,
) -> Int {
  self.dropped_frames
}

///|
pub fn RenderQualityMetrics2::coverage(self : RenderQualityMetrics2) -> Double {
  self.coverage
}

///|
pub fn evaluate_render_quality(
  expected : Array[Double],
  actual : Array[Double],
  expected_colors : Array[Rgba],
  actual_colors : Array[Rgba],
) -> RenderQualityMetrics2 {
  let count = expected.length().min(actual.length())
  let mut temporal = 0.0
  let mut spatial = 0.0
  for index in 0.. Bool {
  self.temporal_error <= temporal_limit &&
  self.spatial_error <= spatial_limit &&
  self.color_error <= color_limit &&
  self.dropped_frames == 0
}

///|
pub fn RenderQualityMetrics2::score(self : RenderQualityMetrics2) -> Double {
  let error = self.temporal_error + self.spatial_error + self.color_error
  clamp01(self.coverage / (1.0 + error))
}

///|
pub struct QualityBudget2 {
  temporal_limit : Double
  spatial_limit : Double
  color_limit : Double
  minimum_coverage : Double
} derive(Debug)

///|
pub fn quality_budget2(
  temporal_limit : Double,
  spatial_limit : Double,
  color_limit : Double,
  minimum_coverage : Double,
) -> QualityBudget2 {
  {
    temporal_limit: temporal_limit.max(0.0),
    spatial_limit: spatial_limit.max(0.0),
    color_limit: color_limit.max(0.0),
    minimum_coverage: clamp01(minimum_coverage),
  }
}

///|
pub fn QualityBudget2::accepts(
  self : QualityBudget2,
  metrics : RenderQualityMetrics2,
) -> Bool {
  metrics.passed(self.temporal_limit, self.spatial_limit, self.color_limit) &&
  metrics.coverage >= self.minimum_coverage
}

///|
pub fn quality_budget_limits2(budget : QualityBudget2) -> Array[Double] {
  [
    budget.temporal_limit,
    budget.spatial_limit,
    budget.color_limit,
    budget.minimum_coverage,
  ]
}