///|
pub struct IntervalSummary {
  start : Double
  stop : Double
  covered : Double
  coverage : Double
  integral : Double
  weighted_mean : Double?
  segments : Int
}

///|
/// Integrate clipped adjacent segments with valid endpoints, no bridging nulls/gaps.
pub fn summarize_interval(
  log : Log,
  name : String,
  start : Double,
  stop : Double,
  max_gap : Double,
) -> IntervalSummary raise WellError {
  if !finite(start) || !finite(stop) || start >= stop || !finite(stop - start) {
    fail("interval requires finite start < stop")
  }
  if !finite(max_gap) || max_gap <= 0.0 {
    fail("interval max_gap must be positive")
  }
  let sorted = ascending(log)
  let c = curve_index(sorted, name)
  let mut covered = 0.0
  let mut integral = 0.0
  let mut correction = 0.0
  let mut segments = 0
  for i in 1.. max_gap || db <= start || da >= stop {
      continue
    }
    match (a[c], b[c]) {
      (Some(x), Some(y)) => {
        let left = if da < start { start } else { da }
        let right = if db > stop { stop } else { db }
        let wl = (left - da) / gap
        let wr = (right - da) / gap
        let vl = (1.0 - wl) * x + wl * y
        let vr = (1.0 - wr) * x + wr * y
        let area = (0.5 * vl + 0.5 * vr) * (right - left)
        if !finite(area) {
          fail("interval integral overflow")
        }
        let adjusted = area - correction
        let next = integral + adjusted
        correction = next - integral - adjusted
        integral = next
        covered += right - left
        segments += 1
      }
      _ => ()
    }
  }
  if !finite(integral) {
    fail("interval total overflow")
  }
  let weighted_mean = if covered == 0.0 {
    None
  } else {
    Some(integral / covered)
  }
  IntervalSummary::{
    start,
    stop,
    covered,
    coverage: covered / (stop - start),
    integral,
    weighted_mean,
    segments,
  }
}