///|
pub struct Vec3 {
  x : Double
  y : Double
  z : Double
} derive(Debug, Eq)

///|
pub fn Vec3::new(x : Double, y : Double, z : Double) -> Vec3 {
  { x, y, z }
}

///|
pub fn Vec3::zero() -> Vec3 {
  { x: 0.0, y: 0.0, z: 0.0 }
}

///|
pub fn Vec3::add(a : Vec3, b : Vec3) -> Vec3 {
  { x: a.x + b.x, y: a.y + b.y, z: a.z + b.z }
}

///|
pub fn Vec3::sub(a : Vec3, b : Vec3) -> Vec3 {
  { x: a.x - b.x, y: a.y - b.y, z: a.z - b.z }
}

///|
pub fn Vec3::scale(v : Vec3, k : Double) -> Vec3 {
  { x: v.x * k, y: v.y * k, z: v.z * k }
}

///|
pub fn Vec3::dot(a : Vec3, b : Vec3) -> Double {
  a.x * b.x + a.y * b.y + a.z * b.z
}

///|
pub fn Vec3::cross(a : Vec3, b : Vec3) -> Vec3 {
  {
    x: a.y * b.z - a.z * b.y,
    y: a.z * b.x - a.x * b.z,
    z: a.x * b.y - a.y * b.x,
  }
}

///|
pub fn Vec3::norm_squared(v : Vec3) -> Double {
  v.dot(v)
}

///|
pub fn Vec3::norm(v : Vec3) -> Double {
  v.norm_squared().sqrt()
}

///|
pub fn Vec3::unit(v : Vec3) -> Vec3 {
  let n = v.norm()
  if n == 0.0 {
    Vec3::zero()
  } else {
    v.scale(1.0 / n)
  }
}

///|
pub fn Vec3::distance(a : Vec3, b : Vec3) -> Double {
  a.sub(b).norm()
}

///|
pub fn Vec3::angle_between(a : Vec3, b : Vec3) -> Double {
  let denom = a.norm() * b.norm()
  if denom == 0.0 {
    0.0
  } else {
    @math.acos(clamp(a.dot(b) / denom, -1.0, 1.0))
  }
}

///|
pub fn Vec3::lerp(a : Vec3, b : Vec3, t : Double) -> Vec3 {
  a.scale(1.0 - t).add(b.scale(t))
}