///|
pub struct SignalStats {
  changes : Int
  first_time : Int64?
  last_time : Int64?
  rising_edges : Int
  falling_edges : Int
  unknown_values : Int
} derive(Debug)

///|
pub fn stats(trace : Trace, signal_id : String) -> SignalStats {
  let mut changes = 0
  let mut rising = 0
  let mut falling = 0
  let mut unknown = 0
  let mut first : Int64? = None
  let mut last : Int64? = None
  let mut previous : Char? = None
  for change in trace.changes {
    if change.id != signal_id {
      continue
    }
    changes += 1
    if first is None {
      first = Some(change.time)
    }
    last = Some(change.time)
    match change.value {
      Scalar(value) => {
        if value == 'x' || value == 'X' || value == 'z' || value == 'Z' {
          unknown += 1
        }
        match previous {
          Some('0') if value == '1' => rising += 1
          Some('1') if value == '0' => falling += 1
          _ => ()
        }
        previous = Some(value)
      }
      _ => ()
    }
  }
  {
    changes,
    first_time: first,
    last_time: last,
    rising_edges: rising,
    falling_edges: falling,
    unknown_values: unknown,
  }
}

///|
pub fn all_stats(trace : Trace) -> Array[(String, SignalStats)] {
  trace.signals.map(fn(signal) { (signal.id, stats(trace, signal.id)) })
}

///|
pub fn duty_cycle(trace : Trace, signal_id : String) -> Double? {
  let mut high : Int64 = 0L
  let mut start : Int64? = None
  let mut previous : Char? = None
  for change in trace.changes {
    if change.id != signal_id {
      continue
    }
    match previous {
      Some('1') =>
        match start {
          Some(t) => high += change.time - t
          None => ()
        }
      _ => ()
    }
    match change.value {
      Scalar('1') => start = Some(change.time)
      Scalar(_) => start = None
      _ => start = None
    }
    match change.value {
      Scalar(c) => previous = Some(c)
      _ => previous = None
    }
  }
  if trace.end_time <= 0L {
    None
  } else {
    Some(high.to_double() / trace.end_time.to_double())
  }
}