///|
/// Cross-series relationship helpers for multivariate monitoring.
pub fn safe_correlation(left : Array[Double], right : Array[Double]) -> Double {
  clamp_probability((correlation(left, right) + 1.0) / 2.0) * 2.0 - 1.0
}

///|
pub fn covariance_matrix(series : Array[Array[Double]]) -> SquareMatrix {
  let dimension = series.length()
  let result = SquareMatrix::new(dimension)
  for row in 0.. SquareMatrix {
  let dimension = series.length()
  let result = SquareMatrix::identity(dimension)
  for row in 0.. Double {
  if series.length() < 2 {
    return 0.0
  }
  let mut total = 0.0
  let mut pairs = 0
  for row in 0.. Double {
  if lag < 0 {
    return lagged_correlation(right, left, -lag)
  }
  if lag >= left.length() || lag >= right.length() {
    return 0.0
  }
  let a : Array[Double] = []
  let b : Array[Double] = []
  let length = if left.length() - lag < right.length() {
    left.length() - lag
  } else {
    right.length()
  }
  for i in 0.. Int {
  let bound = if maximum_lag < 0 { 0 } else { maximum_lag }
  let mut best = 0
  let mut best_score = absolute(lagged_correlation(left, right, 0))
  for lag in 1..<(bound + 1) {
    let score = absolute(lagged_correlation(left, right, lag))
    if score > best_score {
      best = lag
      best_score = score
    }
  }
  best
}

///|
pub fn rolling_correlation(
  left : Array[Double],
  right : Array[Double],
  window_size : Int,
) -> Array[Double] {
  let result : Array[Double] = []
  let safe = if window_size < 2 { 2 } else { window_size }
  let length = if left.length() < right.length() {
    left.length()
  } else {
    right.length()
  }
  for i in 0.. Double {
  let before_matrix = correlation_matrix(before)
  let after_matrix = correlation_matrix(after)
  let dimension = if before.length() < after.length() {
    before.length()
  } else {
    after.length()
  }
  let mut total = 0.0
  for row in 0.. DetectionResult {
  let score = correlated_change_score(before, after)
  DetectionResult::new(
    score >= threshold,
    score,
    clamp_probability(score),
    DistributionShift,
    0,
    evidence=score,
  )
}