///|
fn layer_norm_context(
  x : Tensor,
  weight : Tensor,
  bias : Tensor,
) -> AutogradContext? {
  match (result_context(x, weight), bias.context) {
    (Some(ctx), Some(bctx)) => {
      require_same_context(ctx, bctx)
      Some(ctx)
    }
    (Some(ctx), None) => Some(ctx)
    (None, Some(ctx)) => Some(ctx)
    (None, None) => None
  }
}

///|
pub fn Tensor::layer_norm(
  self : Tensor,
  weight : Tensor,
  bias : Tensor,
  eps : Double,
) -> Tensor {
  if eps < 0.0 {
    abort("layer_norm eps must not be negative")
  }
  if self.shape.length() == 0 {
    abort("layer_norm requires at least one dimension")
  }
  let hidden = self.shape[self.shape.length() - 1]
  if weight.shape != [hidden] || bias.shape != [hidden] {
    abort("layer_norm weight and bias must match the last dimension")
  }
  let rows = self.data.length() / hidden
  let out = Array::make(self.data.length(), 0.0)
  for row in 0..