///|
/// Axis-Aligned Spatiotemporal Bounding Box (4D Hypervolume) in continuous (x, y, z, t) space.
pub struct Box4D {
  min_x : Double
  min_y : Double
  min_z : Double
  min_t : Double
  max_x : Double
  max_y : Double
  max_z : Double
  max_t : Double
} derive(Eq, Debug, Default)

///|
/// Constructs a new normalized Box4D where min <= max along each axis.
pub fn Box4D::new(
  min_x : Double,
  min_y : Double,
  min_z : Double,
  min_t : Double,
  max_x : Double,
  max_y : Double,
  max_z : Double,
  max_t : Double,
) -> Box4D {
  let norm_min_x = if min_x < max_x { min_x } else { max_x }
  let norm_max_x = if min_x > max_x { min_x } else { max_x }
  let norm_min_y = if min_y < max_y { min_y } else { max_y }
  let norm_max_y = if min_y > max_y { min_y } else { max_y }
  let norm_min_z = if min_z < max_z { min_z } else { max_z }
  let norm_max_z = if min_z > max_z { min_z } else { max_z }
  let norm_min_t = if min_t < max_t { min_t } else { max_t }
  let norm_max_t = if min_t > max_t { min_t } else { max_t }
  Box4D::{
    min_x: norm_min_x,
    min_y: norm_min_y,
    min_z: norm_min_z,
    min_t: norm_min_t,
    max_x: norm_max_x,
    max_y: norm_max_y,
    max_z: norm_max_z,
    max_t: norm_max_t,
  }
}

///|
/// Constructs a degenerate zero-hypervolume Box4D centered at (x, y, z, t).
pub fn Box4D::from_point(
  x : Double,
  y : Double,
  z : Double,
  t : Double,
) -> Box4D {
  Box4D::{
    min_x: x,
    min_y: y,
    min_z: z,
    min_t: t,
    max_x: x,
    max_y: y,
    max_z: z,
    max_t: t,
  }
}

///|
/// Returns true if this 4D box intersects another 4D box across all dimensions (x, y, z, t).
pub fn Box4D::intersects(self : Box4D, other : Box4D) -> Bool {
  !(self.max_x < other.min_x ||
  self.min_x > other.max_x ||
  self.max_y < other.min_y ||
  self.min_y > other.max_y ||
  self.max_z < other.min_z ||
  self.min_z > other.max_z ||
  self.max_t < other.min_t ||
  self.min_t > other.max_t)
}

///|
/// Returns true if 4D coordinate (x, y, z, t) is contained within or on boundaries.
pub fn Box4D::contains_point(
  self : Box4D,
  x : Double,
  y : Double,
  z : Double,
  t : Double,
) -> Bool {
  self.min_x <= x &&
  x <= self.max_x &&
  self.min_y <= y &&
  y <= self.max_y &&
  self.min_z <= z &&
  z <= self.max_z &&
  self.min_t <= t &&
  t <= self.max_t
}

///|
/// Returns true if this box completely contains another 4D box.
pub fn Box4D::contains_box(self : Box4D, other : Box4D) -> Bool {
  self.min_x <= other.min_x &&
  other.max_x <= self.max_x &&
  self.min_y <= other.min_y &&
  other.max_y <= self.max_y &&
  self.min_z <= other.min_z &&
  other.max_z <= self.max_z &&
  self.min_t <= other.min_t &&
  other.max_t <= self.max_t
}

///|
/// Computes the Minimum Bounding Hypervolume enclosing both 4D boxes.
pub fn Box4D::union(self : Box4D, other : Box4D) -> Box4D {
  let min_x = if self.min_x < other.min_x { self.min_x } else { other.min_x }
  let min_y = if self.min_y < other.min_y { self.min_y } else { other.min_y }
  let min_z = if self.min_z < other.min_z { self.min_z } else { other.min_z }
  let min_t = if self.min_t < other.min_t { self.min_t } else { other.min_t }
  let max_x = if self.max_x > other.max_x { self.max_x } else { other.max_x }
  let max_y = if self.max_y > other.max_y { self.max_y } else { other.max_y }
  let max_z = if self.max_z > other.max_z { self.max_z } else { other.max_z }
  let max_t = if self.max_t > other.max_t { self.max_t } else { other.max_t }
  Box4D::{ min_x, min_y, min_z, min_t, max_x, max_y, max_z, max_t, }
}

///|
/// Computes intersection of two 4D boxes, or None if disjoint.
pub fn Box4D::intersection(self : Box4D, other : Box4D) -> Box4D? {
  if !self.intersects(other) {
    None
  } else {
    let min_x = if self.min_x > other.min_x { self.min_x } else { other.min_x }
    let min_y = if self.min_y > other.min_y { self.min_y } else { other.min_y }
    let min_z = if self.min_z > other.min_z { self.min_z } else { other.min_z }
    let min_t = if self.min_t > other.min_t { self.min_t } else { other.min_t }
    let max_x = if self.max_x < other.max_x { self.max_x } else { other.max_x }
    let max_y = if self.max_y < other.max_y { self.max_y } else { other.max_y }
    let max_z = if self.max_z < other.max_z { self.max_z } else { other.max_z }
    let max_t = if self.max_t < other.max_t { self.max_t } else { other.max_t }
    Some(Box4D::{ min_x, min_y, min_z, min_t, max_x, max_y, max_z, max_t, })
  }
}

///|
/// Computes the 4D hypervolume (length_x * length_y * length_z * length_t).
pub fn Box4D::hyper_volume(self : Box4D) -> Double {
  (self.max_x - self.min_x) *
  (self.max_y - self.min_y) *
  (self.max_z - self.min_z) *
  (self.max_t - self.min_t)
}

///|
/// Extracts the spatial 3D bounding box (Box3D) ignoring time dimension.
pub fn Box4D::to_box3d(self : Box4D) -> Box3D {
  Box3D::{
    min_x: self.min_x,
    min_y: self.min_y,
    min_z: self.min_z,
    max_x: self.max_x,
    max_y: self.max_y,
    max_z: self.max_z,
  }
}

///|
/// Expands 4D box by delta margins along each dimension.
pub fn Box4D::expand(
  self : Box4D,
  dx : Double,
  dy : Double,
  dz : Double,
  dt : Double,
) -> Box4D {
  Box4D::new(
    self.min_x - dx,
    self.min_y - dy,
    self.min_z - dz,
    self.min_t - dt,
    self.max_x + dx,
    self.max_y + dy,
    self.max_z + dz,
    self.max_t + dt,
  )
}

///|
/// Checks if this 4D box intersects another at a specific instant of time t.
pub fn Box4D::intersects_at_time(
  self : Box4D,
  other : Box4D,
  t : Double,
) -> Bool {
  if t < self.min_t || t > self.max_t || t < other.min_t || t > other.max_t {
    false
  } else {
    self.to_box3d().intersects(other.to_box3d())
  }
}