///|
/// A detected Western Electric rule violation.
pub(all) struct RuleViolation {
  rule : Int
  start_index : Int
  indices : Array[Int]
  message : String
} derive(Debug, ToJson)

///|
fn same_side(
  values : Array[Double],
  mean : Double,
  start : Int,
  length : Int,
  threshold : Double,
) -> Bool {
  let first = values[start] - mean
  if first == 0.0 {
    false
  } else {
    let positive = first > 0.0
    let mut valid = true
    for i in start..<(start + length) {
      let delta = values[i] - mean
      if delta.abs() <= threshold || (delta > 0.0) != positive {
        valid = false
      }
    }
    valid
  }
}

///|
/// Detect the four commonly used Western Electric rules.
pub fn detect_western_electric(
  values : Array[Double],
  mean~ : Double,
  sigma~ : Double,
) -> Array[RuleViolation] {
  let violations : Array[RuleViolation] = []
  if sigma <= 0.0 {
    violations
  } else {
    for i in 0.. 3.0 * sigma {
        violations.push({
          rule: 1,
          start_index: i,
          indices: [i],
          message: "one point beyond three sigma",
        })
      }
    }
    if values.length() >= 3 {
      for i in 0..<(values.length() - 2) {
        let mut positive = 0
        let mut negative = 0
        for j in i..<(i + 3) {
          let delta = values[j] - mean
          if delta > 2.0 * sigma {
            positive += 1
          }
          if delta < -2.0 * sigma {
            negative += 1
          }
        }
        if positive >= 2 || negative >= 2 {
          violations.push({
            rule: 2,
            start_index: i,
            indices: [i, i + 1, i + 2],
            message: "two of three beyond two sigma",
          })
        }
      }
    }
    if values.length() >= 5 {
      for i in 0..<(values.length() - 4) {
        let mut positive = 0
        let mut negative = 0
        for j in i..<(i + 5) {
          let delta = values[j] - mean
          if delta > sigma {
            positive += 1
          }
          if delta < -sigma {
            negative += 1
          }
        }
        if positive >= 4 || negative >= 4 {
          violations.push({
            rule: 3,
            start_index: i,
            indices: [i, i + 1, i + 2, i + 3, i + 4],
            message: "four of five beyond one sigma",
          })
        }
      }
    }
    if values.length() >= 8 {
      for i in 0..<(values.length() - 7) {
        if same_side(values, mean, i, 8, 0.0) {
          let indices : Array[Int] = []
          for j in i..<(i + 8) {
            indices.push(j)
          }
          violations.push({
            rule: 4,
            start_index: i,
            indices,
            message: "eight consecutive points on one side",
          })
        }
      }
    }
    violations
  }
}