Packages

Common unit conversions for Elixir.

Current section

Files

Jump to
units lib units.ex
Raw

lib/units.ex

defmodule Units do
@moduledoc """
Arithmetic for converting SI unit prefixes.
## Examples
iex> Units.from(1, :mega, :byte) |> Units.to_int(:kilo, :byte)
1024
iex> Units.from(1, :mega, :byte) |> Units.to_int(:byte)
1048576
iex> Units.from(2, :kilo, :meter) |> Units.to_int(:centi, :meter)
200000
The following units of measurement are recognized:
### :meter
`:pico`, `:nano`, `:micro`, `:milli`, `:centi`, `:deci`, `:deca`, `:hecto`, `:kilo`, `:mega`, `:giga`
### :byte
`:kilo`, `:mega`, `:giga`, `:tera`, `:peta`
"""
@index %{:byte => [
{:one, {1024, 1024}},
{:kilo, {1024, 1024}},
{:mega, {1024, 1024}},
{:giga, {1024, 1024}},
{:tera, {1024, 1024}},
{:peta, {1024, 1024}}],
:meter => [
{:pico, {1000, 1000}},
{:nano, {1000, 1000}},
{:micro, {1000, 1000}},
{:milli, {1000, 10}},
{:centi, {10, 10}},
{:deci, {10, 10}},
{:one, {10, 10}},
{:deca, {10, 10}},
{:hecto, {10, 10}},
{:kilo, {10, 1000}},
{:mega, {1000, 1000}},
{:giga, {1000, 1000}}
]
}
@doc """
Creates new unit and uses a single unit of measurement.
Example:
iex> Units.from(1, :meter)
%{amount: 1, quantity: :meter, unit: :one}
"""
def from(amount, quantity), do: from(amount, :one, quantity)
@doc """
Creates new unit.
Example:
iex> Units.from(2, :kilo, :byte)
%{amount: 2, quantity: :byte, unit: :kilo}
"""
def from(amount, unit, quantity)
when is_atom(unit)
and is_integer(amount), do: %{amount: amount, unit: unit, quantity: quantity}
@doc """
Converts a unit to another and returns a unit. Uses a single unit of measurement.
Example:
iex> Units.from(1, :kilo, :meter) |> Units.to(:meter)
%{amount: 1000, quantity: :meter, unit: :one}
"""
def to(amount, quantity), do: to(amount, :one, quantity)
@doc """
Converts a unit to another and returns a unit.
Example:
iex> Units.from(100, :deci, :meter) |> Units.to(:centi, :meter)
%{amount: 1000, quantity: :meter, unit: :centi}
"""
def to(%{amount: amount, unit: src_unit, quantity: src_quantity}, dst_unit, dst_quantity)
when is_integer(amount)
and is_atom(src_unit)
and is_atom(dst_unit)
and is_atom(dst_quantity)
and src_quantity == dst_quantity do
index = @index[dst_quantity]
%{amount: (convert index, amount, unit_index(index, src_unit), unit_index(index, dst_unit)), unit: dst_unit, quantity: dst_quantity}
end
@doc """
Converts a unit to another and returns an integer. Short for to/2 |> int/1
"""
def to_int(amount, quantity), do: to_int(amount, :one, quantity)
@doc """
Converts a unit to another and returns an integer. Short for to/3 |> int/1
"""
def to_int(amount, dst_unit, quantity), do: to(amount, dst_unit, quantity) |> int
@doc """
Converts a unit to another and returns a float. Short for to/2 |> float/1
"""
def to_float(amount, quantity), do: to_float(amount, :one, quantity)
@doc """
Converts a unit to another and returns a float. Short for to/3 |> float/1
"""
def to_float(amount, dst_unit, quantity), do: to(amount, dst_unit, quantity) |> float
@doc """
Returns the amount as an integer
"""
def int(%{amount: amount})
when is_integer(amount), do: amount
@doc """
Returns the amount as an integer
"""
def int(%{amount: amount})
when is_float(amount), do: Float.floor amount
@doc """
Returns the amount as a float
"""
def float(%{amount: amount}), do: amount
defp unit_index(index, unit), do: Enum.find_index(index, fn {x, _} -> x == unit end)
defp convert(index, amount, src, dst) when src < dst do
{_, {_, step}} = Enum.at(index, src)
convert index, (amount / step), src + 1, dst
end
defp convert(index, amount, src, dst) when src > dst do
{_, {step, _}} = Enum.at(index, src)
convert index, (amount * step), src - 1, dst
end
defp convert(_, amount, src, dst) when src == dst, do: amount
end