///|
/// A physical dimension expressed over the seven SI base dimensions:
/// length (L), mass (M), time (T), electric current (I), thermodynamic
/// temperature (Θ), amount of substance (N) and luminous intensity (J).
///
/// A `Dimension` reuses the algebra layer's `Monomial`: the symbols are the
/// base-dimension tags and the coefficient is always 1 (dimensions carry no
/// scale). Two dimensions are equal exactly when their exponent vectors match,
/// which is the foundation of dimensional checking in the quantity layer.
///
/// # Example
///
/// ```mbt check
/// test {
///   // Velocity has dimension length / time.
///   let velocity = Dimension::length().div(Dimension::time())
///   let acceleration = velocity.div(Dimension::time())
///   assert_eq(acceleration, Dimension::length().div(Dimension::time().pow(2)))
/// }
/// ```
pub struct Dimension {
  repr : @algebra.Monomial
} derive(Eq, Debug)

///|
/// Manual `Show` (so `assert_eq` can print diffs on failure), delegating to the
/// underlying monomial.
pub impl Show for Dimension with fn output(self, logger) {
  Show::output(self.repr, logger)
}

///|
/// Allows `lhs * rhs` as shorthand for `lhs.mul(rhs)`.
///
/// # Example
///
/// ```mbt check
/// test {
///   let area = Dimension::length() * Dimension::length()
///   assert_eq(area, Dimension::length().pow(2))
/// }
/// ```
pub impl Mul for Dimension with fn mul(self : Dimension, other : Dimension) -> Dimension {
  { repr: self.repr.mul(other.repr) }
}

///|
/// Allows `lhs / rhs` as shorthand for `lhs.div(rhs)`.
///
/// # Example
///
/// ```mbt check
/// test {
///   let velocity = Dimension::length() / Dimension::time()
///   assert_eq(velocity, Dimension::length().div(Dimension::time()))
/// }
/// ```
pub impl Div for Dimension with fn div(self : Dimension, other : Dimension) -> Dimension {
  { repr: self.repr.div(other.repr) }
}

///|
/// Builds a base dimension from its single-letter tag.
fn base(symbol : String) -> Dimension {
  { repr: @algebra.Monomial::symbol(symbol) }
}

///|
/// Builds an extension dimension from a caller-owned symbol.
///
/// This is intended for domain packages that need dimensions outside the seven
/// SI base dimensions, while keeping the core dimension package independent
/// from those domains.
///
/// # Example
///
/// ```mbt check
/// test {
///   let information = Dimension::custom("Info")
///   assert_true(information.is_same(Dimension::custom("Info")))
///   assert_false(information.is_same(Dimension::length()))
/// }
/// ```
pub fn Dimension::custom(symbol : String) -> Dimension {
  base(symbol)
}

///|
/// The length dimension (L).
///
/// # Example
///
/// ```mbt check
/// test {
///   assert_false(Dimension::length() == Dimension::mass())
/// }
/// ```
pub fn Dimension::length() -> Dimension {
  base("L")
}

///|
/// The mass dimension (M).
///
/// # Example
///
/// ```mbt check
/// test {
///   // Momentum has dimension mass * length / time.
///   let momentum = Dimension::mass()
///     .mul(Dimension::length())
///     .div(Dimension::time())
///   assert_false(momentum.is_dimensionless())
/// }
/// ```
pub fn Dimension::mass() -> Dimension {
  base("M")
}

///|
/// The time dimension (T).
///
/// # Example
///
/// ```mbt check
/// test {
///   // Frequency has dimension 1 / time.
///   let frequency = Dimension::dimensionless().div(Dimension::time())
///   assert_eq(frequency, Dimension::time().pow(-1))
/// }
/// ```
pub fn Dimension::time() -> Dimension {
  base("T")
}

///|
/// The electric current dimension (I).
///
/// # Example
///
/// ```mbt check
/// test {
///   // Electric charge has dimension current * time.
///   let charge = Dimension::electric_current().mul(Dimension::time())
///   assert_false(charge.is_dimensionless())
/// }
/// ```
pub fn Dimension::electric_current() -> Dimension {
  base("I")
}

///|
/// The thermodynamic temperature dimension (Θ).
///
/// # Example
///
/// ```mbt check
/// test {
///   assert_false(Dimension::temperature() == Dimension::mass())
/// }
/// ```
pub fn Dimension::temperature() -> Dimension {
  base("Θ")
}

///|
/// The amount-of-substance dimension (N).
///
/// # Example
///
/// ```mbt check
/// test {
///   assert_false(Dimension::amount_of_substance() == Dimension::length())
/// }
/// ```
pub fn Dimension::amount_of_substance() -> Dimension {
  base("N")
}

///|
/// The luminous-intensity dimension (J).
///
/// # Example
///
/// ```mbt check
/// test {
///   assert_false(Dimension::luminous_intensity() == Dimension::time())
/// }
/// ```
pub fn Dimension::luminous_intensity() -> Dimension {
  base("J")
}

///|
/// The dimensionless quantity (no base dimensions).
///
/// # Example
///
/// ```mbt check
/// test {
///   assert_true(Dimension::dimensionless().is_dimensionless())
///   // A ratio of like dimensions is dimensionless.
///   assert_eq(
///     Dimension::length().div(Dimension::length()),
///     Dimension::dimensionless(),
///   )
/// }
/// ```
pub fn Dimension::dimensionless() -> Dimension {
  { repr: @algebra.Monomial::one() }
}

///|
/// Multiplies two dimensions (adds their exponent vectors).
///
/// # Example
///
/// ```mbt check
/// test {
///   // Area is length * length.
///   assert_eq(
///     Dimension::length().mul(Dimension::length()),
///     Dimension::length().pow(2),
///   )
/// }
/// ```
pub fn Dimension::mul(self : Dimension, other : Dimension) -> Dimension {
  { repr: self.repr.mul(other.repr) }
}

///|
/// Divides one dimension by another (subtracts exponent vectors).
///
/// # Example
///
/// ```mbt check
/// test {
///   // Velocity is length / time; multiplying back by time gives length.
///   let v = Dimension::length().div(Dimension::time())
///   assert_eq(v.mul(Dimension::time()), Dimension::length())
/// }
/// ```
pub fn Dimension::div(self : Dimension, other : Dimension) -> Dimension {
  { repr: self.repr.div(other.repr) }
}

///|
/// Raises a dimension to an integer power.
///
/// # Example
///
/// ```mbt check
/// test {
///   // Volume is length^3.
///   let volume = Dimension::length().pow(3)
///   assert_eq(
///     volume,
///     Dimension::length().mul(Dimension::length()).mul(Dimension::length()),
///   )
/// }
/// ```
pub fn Dimension::pow(self : Dimension, n : Int) -> Dimension {
  { repr: self.repr.pow(n) }
}

///|
/// Returns whether this is the dimensionless quantity.
///
/// # Example
///
/// ```mbt check
/// test {
///   assert_true(Dimension::length().div(Dimension::length()).is_dimensionless())
///   assert_false(Dimension::length().is_dimensionless())
/// }
/// ```
pub fn Dimension::is_dimensionless(self : Dimension) -> Bool {
  self.repr.is_dimensionless()
}

///|
/// Returns whether two dimensions are the same physical dimension. This is the
/// check the quantity layer uses to allow addition and subtraction.
///
/// # Example
///
/// ```mbt check
/// test {
///   // Two ways of building the force dimension agree.
///   let f1 = Dimension::mass()
///     .mul(Dimension::length())
///     .div(Dimension::time().pow(2))
///   let f2 = Dimension::mass().mul(
///     Dimension::length().div(Dimension::time().pow(2)),
///   )
///   assert_true(f1.is_same(f2))
/// }
/// ```
pub fn Dimension::is_same(self : Dimension, other : Dimension) -> Bool {
  self.repr.same_factors(other.repr)
}