use core::scalar
use core::error
@description("Extract the plain value of a quantity (the `20` in `20 km/h`). This can be useful in generic code, but should generally be avoided otherwise.")
@example("value_of(20 km/h)")
fn value_of<T: Dim>(x: T) -> Scalar
@description("Extract the unit of a quantity (the `km/h` in `20 km/h`). This can be useful in generic code, but should generally be avoided otherwise. Returns an error if the quantity is zero.")
@example("unit_of(20 km/h)")
fn unit_of<T: Dim>(x: T) -> T = if x_value == 0 then error("Invalid argument: cannot call `unit_of` on a value that evaluates to 0") else x / value_of(x)
where x_value = value_of(x)
@description("Extract the base unit of a quantity without prefixes (e.g., `base_unit_of(5 km)` returns `m` instead of `km`). This can be useful for normalizing values. Returns an error if the quantity is zero.")
@example("base_unit_of(5 km)")
@example("5 km / base_unit_of(5 km)")
fn base_unit_of<T: Dim>(x: T) -> T
@description("Returns true if `quantity` has the same unit as `unit_query`, or if `quantity` evaluates to zero.")
@example("has_unit(20 km/h, km/h)")
@example("has_unit(20 km/h, m/s)")
fn has_unit<T: Dim>(quantity: T, unit_query: T) -> Bool
@description("Returns true if `quantity` is dimensionless, or if `quantity` is zero.")
@example("is_dimensionless(10)")
@example("is_dimensionless(10 km/h)")
fn is_dimensionless<T: Dim>(quantity: T) -> Bool
@description("Returns a string representation of the unit of `quantity`. Returns an empty string if `quantity` is dimensionless.")
@example("unit_name(20)")
@example("unit_name(20 m^2)")
@example("unit_name(20 km/h)")
fn unit_name<T: Dim>(quantity: T) -> String
# TODO: Once we support explicitly passing arguments to type parameters, we can remove the second argument and
# replace this with `fn quantity_cast<To: Dim, From: Dim>(f: From) -> To` and call it with `quantity_cast::<Length>(…)`.
@description("Unsafe function that returns the quantity `from` unmodified with the target dimension `To`. This can be useful in generic code, but should generally be avoided otherwise.")
@example("quantity_cast(1 nm, m)")
fn quantity_cast<From: Dim, To: Dim>(f: From, t: To) -> To