///|
pub fn ts_mean(values : Array[Double]) -> Double {
  if values.length() == 0 {
    0.0
  } else {
    let mut total = 0.0
    for value in values {
      total += value
    }
    total / values.length().to_double()
  }
}

///|
pub fn ts_difference(values : Array[Double], lag : Int) -> Array[Double] {
  let result : Array[Double] = []
  if lag <= 0 || lag >= values.length() {
    return result
  }
  for i in lag.. Array[Double] {
  ts_difference(values, period)
}

///|
pub fn ts_moving_min(values : Array[Double], window : Int) -> Array[Double] {
  let result : Array[Double] = []
  if window <= 0 {
    return result
  }
  let mut i = window - 1
  while i < values.length() {
    let mut low = values[i - window + 1]
    let mut j = i - window + 2
    while j <= i {
      low = low.min(values[j])
      j += 1
    }
    result.push(low)
    i += 1
  }
  result
}

///|
pub fn ts_moving_max(values : Array[Double], window : Int) -> Array[Double] {
  let result : Array[Double] = []
  if window <= 0 {
    return result
  }
  let mut i = window - 1
  while i < values.length() {
    let mut high = values[i - window + 1]
    let mut j = i - window + 2
    while j <= i {
      high = high.max(values[j])
      j += 1
    }
    result.push(high)
    i += 1
  }
  result
}

///|
pub fn ts_moving_range(values : Array[Double], window : Int) -> Array[Double] {
  let lows = ts_moving_min(values, window)
  let highs = ts_moving_max(values, window)
  let result : Array[Double] = []
  for i in 0.. Array[Double] {
  let result : Array[Double] = []
  if window <= 0 || values.length() < window {
    return result
  }
  let mut i = 0
  while i < values.length() - window + 1 {
    result.push(ts_mean(values[i:i + window].to_owned()))
    i += 1
  }
  result
}

///|
pub fn ts_detrend(values : Array[Double]) -> Array[Double] {
  let result : Array[Double] = []
  if values.length() == 0 {
    return result
  }
  let mean = ts_mean(values)
  for i, value in values {
    let trend = if values.length() <= 1 {
      mean
    } else {
      values[0] +
      (values[values.length() - 1] - values[0]) *
      i.to_double() /
      (values.length() - 1).to_double()
    }
    result.push(value - trend)
  }
  result
}

///|
pub fn ts_autocovariance(values : Array[Double], lag : Int) -> Double {
  if lag < 0 || lag >= values.length() {
    0.0
  } else {
    let mean = ts_mean(values)
    let mut total = 0.0
    for i in lag.. Double {
  let base = ts_autocovariance(values, 0)
  if base <= 0.0 {
    0.0
  } else {
    ts_autocovariance(values, lag) / base
  }
}

///|
pub fn ts_peak_index(values : Array[Double]) -> Int {
  if values.length() == 0 {
    -1
  } else {
    let mut index = 0
    for i in 1.. values[index] {
        index = i
      }
    }
    index
  }
}

///|
pub fn ts_trough_index(values : Array[Double]) -> Int {
  if values.length() == 0 {
    -1
  } else {
    let mut index = 0
    for i in 1.. Double {
  if values.length() == 0 {
    0.0
  } else {
    values[ts_peak_index(values)] - values[ts_trough_index(values)]
  }
}

///|
pub fn ts_count_crossings(values : Array[Double], level : Double) -> Int {
  if values.length() < 2 {
    0
  } else {
    let mut count = 0
    for i in 1..= level) != (values[i - 1] >= level) {
        count += 1
      }
    }
    count
  }
}

///|
pub fn ts_downsample(values : Array[Double], step : Int) -> Array[Double] {
  let result : Array[Double] = []
  if step <= 0 {
    return result
  }
  for i in 0.. Array[Double] {
  let result = values.copy()
  while result.length() < length {
    result.push(fill)
  }
  result
}

///|
pub fn ts_last(values : Array[Double], fallback : Double) -> Double {
  values.last().unwrap_or(fallback)
}

///|
pub fn ts_first(values : Array[Double], fallback : Double) -> Double {
  if values.length() == 0 {
    fallback
  } else {
    values[0]
  }
}