///|
/// Softmax over a 1D array using numerically stable max subtraction.
/// Output shape is the same as input shape.
pub fn softmax(x : Array[Double]) -> Array[Double] {
  let len = x.length()
  let output = Array::make(len, 0.0)
  if len == 0 {
    output
  } else {
    let mut max_v = x[0]
    for i = 1; i < len; i = i + 1 {
      if x[i] > max_v {
        max_v = x[i]
      }
    }
    let mut sum = 0.0
    for i = 0; i < len; i = i + 1 {
      let v = @math.exp(x[i] - max_v)
      output[i] = v
      sum = sum + v
    }
    for i = 0; i < len; i = i + 1 {
      output[i] = output[i] / sum
    }
    output
  }
}

///|
/// LogSoftmax over a 1D array using numerically stable max subtraction.
/// Output shape is the same as input shape.
pub fn log_softmax(x : Array[Double]) -> Array[Double] {
  let len = x.length()
  let output = Array::make(len, 0.0)
  if len == 0 {
    output
  } else {
    let mut max_v = x[0]
    for i = 1; i < len; i = i + 1 {
      if x[i] > max_v {
        max_v = x[i]
      }
    }
    let mut sum = 0.0
    for i = 0; i < len; i = i + 1 {
      sum = sum + @math.exp(x[i] - max_v)
    }
    let log_sum = @math.ln(sum)
    for i = 0; i < len; i = i + 1 {
      output[i] = x[i] - max_v - log_sum
    }
    output
  }
}