///|
/// ReLU activation.
/// Output shape is the same as input shape.
pub fn relu(x : Array[Double]) -> Array[Double] {
  let output = Array::make(x.length(), 0.0)
  for i = 0; i < x.length(); i = i + 1 {
    output[i] = if x[i] > 0.0 { x[i] } else { 0.0 }
  }
  output
}

///|
/// Sigmoid activation.
/// Output shape is the same as input shape.
pub fn sigmoid(x : Array[Double]) -> Array[Double] {
  let output = Array::make(x.length(), 0.0)
  for i = 0; i < x.length(); i = i + 1 {
    output[i] = 1.0 / (1.0 + @math.exp(-x[i]))
  }
  output
}

///|
/// GELU activation using tanh approximation.
/// Output shape is the same as input shape.
pub fn gelu(x : Array[Double]) -> Array[Double] {
  let output = Array::make(x.length(), 0.0)
  let sqrt_2_over_pi = 0.7978845608028654
  for i = 0; i < x.length(); i = i + 1 {
    let v = x[i]
    let inner = sqrt_2_over_pi * (v + 0.044715 * v * v * v)
    output[i] = 0.5 * v * (1.0 + @math.tanh(inner))
  }
  output
}