Packages
ex_cldr_units
1.0.0-rc.0
3.20.5
3.20.4
3.20.3
3.20.2
3.20.1
3.20.0
3.19.2
3.19.1
3.19.0
3.18.1
3.18.0
3.17.2
3.17.1
3.17.0
3.16.5
3.16.4
3.16.3
3.16.2
3.16.1
3.16.0
3.15.0
3.14.0
3.13.3
3.13.2
3.13.1
3.13.0
3.12.2
3.12.1
3.12.0
3.11.0
3.10.0
3.9.2
3.9.1
3.9.0
3.8.0
3.8.0-rc.2
3.8.0-rc.1
3.8.0-rc.0
3.7.1
3.7.0
3.6.0
3.5.3
3.5.2
3.5.1
3.5.0
3.5.0-rc.1
3.5.0-rc.0
3.4.0
3.4.0-rc.0
3.3.1
3.3.0
3.3.0-rc.0
3.2.1
3.2.0
3.1.2
3.1.1
3.1.0
3.0.1
3.0.0
3.0.0-rc.0
2.8.1
2.8.0
2.7.0
2.6.1
2.6.0
2.5.3
2.5.2
2.5.1
2.5.0
2.4.0
2.3.3
2.3.2
retired
2.3.1
2.3.0
2.2.0
2.1.0
2.0.0
1.3.0
1.2.2
1.2.1
1.2.0
1.1.1
1.1.0
retired
1.0.1
retired
1.0.0
retired
1.0.0-rc.0
retired
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.3.1
0.3.0
0.2.1
0.2.0
0.1.3
0.1.2
0.1.1
0.1.0
Unit formatting (volume, area, length, ...), conversion and arithmetic functions based upon the Common Locale Data Repository (CLDR).
Retired package: Deprecated
Current section
Files
Jump to
Current section
Files
lib/cldr/math.ex
defmodule Cldr.Unit.Math do
@moduledoc """
Simple arithmetic functions for the `Unit.t` type
"""
alias Cldr.Unit
alias Cldr.Unit.Conversion
import Kernel, except: [div: 2]
import Unit, only: [incompatible_unit_error: 2]
@doc """
Adds two compatible `%Unit{}` types
## Options
* `unit_1` and `unit_2` are compatible Units
returned by `Cldr.Unit.new/2`
## Returns
* A `%Unit{}` of the same time as `unit_1` with a value
that is the sum of `unit_1` and the potentially converted
`unit_2` or
* {:error, {IncompatibleUnitError, message}}
## Examples
iex> Cldr.Unit.Math.add Cldr.Unit.new!(:foot, 1), Cldr.Unit.new!(:foot, 1)
#Unit<:foot, 2>
iex> Cldr.Unit.Math.add Cldr.Unit.new!(:foot, 1), Cldr.Unit.new!(:mile, 1)
#Unit<:foot, 5280.945925937846>
iex> Cldr.Unit.Math.add Cldr.Unit.new!(:foot, 1), Cldr.Unit.new!(:gallon, 1)
{:error, {Cldr.Unit.IncompatibleUnitError,
"Operations can only be performed between units of the same type. Received #Unit<:foot, 1> and #Unit<:gallon, 1>"}}
"""
@spec add(unit_1 :: Unit.t, unit_2 :: Unit.t) ::
Unit.t | {:error, {Unit.IncompatibleUnitError, String.t}}
def add(%Unit{unit: unit, value: value_1}, %Unit{unit: unit, value: value_2})
when is_number(value_1) and is_number(value_2) do
Unit.new!(unit, value_1 + value_2)
end
def add(%Unit{unit: unit, value: %Decimal{} = value_1},
%Unit{unit: unit, value: %Decimal{} = value_2}) do
Unit.new!(unit, Decimal.add(value_1, value_2))
end
def add(%Unit{unit: unit, value: %Decimal{}} = unit_1,
%Unit{unit: unit, value: value_2})
when is_number(value_2) do
add(unit_1, Unit.new!(unit, Decimal.new(value_2)))
end
def add(%Unit{unit: unit, value: value_2},
%Unit{unit: unit, value: %Decimal{}} = unit_1)
when is_number(value_2) do
add(unit_1, Unit.new!(unit, Decimal.new(value_2)))
end
def add(%Unit{unit: unit_type_1} = unit_1, %Unit{unit: unit_type_2} = unit_2) do
if Unit.compatible?(unit_type_1, unit_type_2) do
add(unit_1, Conversion.convert(unit_2, unit_type_1))
else
{:error, incompatible_unit_error(unit_1, unit_2)}
end
end
@doc """
Adds two compatible `%Unit{}` types
and raises on error
## Options
* `unit_1` and `unit_2` are compatible Units
returned by `Cldr.Unit.new/2`
## Returns
* A `%Unit{}` of the same time as `unit_1` with a value
that is the sum of `unit_1` and the potentially converted
`unit_2` or
* Raises an exception
"""
@spec add!(unit_1 :: Unit.t, unit_2 :: Unit.t) ::
Unit.t | no_return()
def add!(unit_1, unit_2) do
case add(unit_1, unit_2) do
{:error, {exception, reason}} -> raise exception, reason
unit -> unit
end
end
@doc """
Subtracts two compatible `%Unit{}` types
## Options
* `unit_1` and `unit_2` are compatible Units
returned by `Cldr.Unit.new/2`
## Returns
* A `%Unit{}` of the same time as `unit_1` with a value
that is the difference between `unit_1` and the potentially
converted `unit_2`
* `{:error, {IncompatibleUnitError, message}}`
## Examples
iex> Cldr.Unit.sub Cldr.Unit.new!(:kilogram, 5), Cldr.Unit.new!(:pound, 1)
#Unit<:kilogram, 4.54640709056436>
iex> Cldr.Unit.sub Cldr.Unit.new!(:pint, 5), Cldr.Unit.new!(:liter, 1)
#Unit<:pint, 2.88662>
iex> Cldr.Unit.sub Cldr.Unit.new!(:pint, 5), Cldr.Unit.new!(:pint, 1)
#Unit<:pint, 4>
"""
@spec sub(unit_1 :: Unit.t, unit_2 :: Unit.t) ::
Unit.t | {:error, {Unit.IncompatibleUnitError, String.t}}
def sub(%Unit{unit: unit, value: value_1}, %Unit{unit: unit, value: value_2})
when is_number(value_1) and is_number(value_2) do
Unit.new!(unit, value_1 - value_2)
end
def sub(%Unit{unit: unit, value: %Decimal{} = value_1},
%Unit{unit: unit, value: %Decimal{} = value_2}) do
Unit.new!(unit, Decimal.sub(value_1, value_2))
end
def sub(%Unit{unit: unit, value: %Decimal{}} = unit_1,
%Unit{unit: unit, value: value_2})
when is_number(value_2) do
sub(unit_1, Unit.new!(unit, Decimal.new(value_2)))
end
def sub(%Unit{unit: unit, value: value_2},
%Unit{unit: unit, value: %Decimal{}} = unit_1)
when is_number(value_2) do
sub(unit_1, Unit.new!(unit, Decimal.new(value_2)))
end
def sub(%Unit{unit: unit_type_1} = unit_1, %Unit{unit: unit_type_2} = unit_2) do
if Unit.compatible?(unit_type_1, unit_type_2) do
sub(unit_1, Conversion.convert(unit_2, unit_type_1))
else
{:error, incompatible_unit_error(unit_1, unit_2)}
end
end
@doc """
Subtracts two compatible `%Unit{}` types
and raises on error
## Options
* `unit_1` and `unit_2` are compatible Units
returned by `Cldr.Unit.new/2`
## Returns
* A `%Unit{}` of the same time as `unit_1` with a value
that is the difference between `unit_1` and the potentially
converted `unit_2`
* Raises an exception
"""
@spec sub!(unit_1 :: Unit.t, unit_2 :: Unit.t) ::
Unit.t | no_return()
def sub!(unit_1, unit_2) do
case sub(unit_1, unit_2) do
{:error, {exception, reason}} -> raise exception, reason
unit -> unit
end
end
@doc """
Multiplies two compatible `%Unit{}` types
## Options
* `unit_1` and `unit_2` are compatible Units
returned by `Cldr.Unit.new/2`
## Returns
* A `%Unit{}` of the same time as `unit_1` with a value
that is the product of `unit_1` and the potentially
converted `unit_2`
* `{:error, {IncompatibleUnitError, message}}`
## Examples
iex> Cldr.Unit.mult Cldr.Unit.new!(:kilogram, 5), Cldr.Unit.new!(:pound, 1)
#Unit<:kilogram, 2.2679645471781984>
iex> Cldr.Unit.mult Cldr.Unit.new!(:pint, 5), Cldr.Unit.new!(:liter, 1)
#Unit<:pint, 10.566899999999999>
iex> Cldr.Unit.mult Cldr.Unit.new!(:pint, 5), Cldr.Unit.new!(:pint, 1)
#Unit<:pint, 5>
"""
@spec mult(unit_1 :: Unit.t, unit_2 :: Unit.t) ::
Unit.t | {:error, {Unit.IncompatibleUnitError, String.t}}
def mult(%Unit{unit: unit, value: value_1}, %Unit{unit: unit, value: value_2})
when is_number(value_1) and is_number(value_2) do
Unit.new!(unit, value_1 * value_2)
end
def mult(%Unit{unit: unit, value: %Decimal{} = value_1},
%Unit{unit: unit, value: %Decimal{} = value_2}) do
Unit.new!(unit, Decimal.mult(value_1, value_2))
end
def mult(%Unit{unit: unit, value: %Decimal{}} = unit_1,
%Unit{unit: unit, value: value_2})
when is_number(value_2) do
mult(unit_1, Unit.new!(unit, Decimal.new(value_2)))
end
def mult(%Unit{unit: unit, value: value_2},
%Unit{unit: unit, value: %Decimal{}} = unit_1)
when is_number(value_2) do
mult(unit_1, Unit.new!(unit, Decimal.new(value_2)))
end
def mult(%Unit{unit: unit_type_1} = unit_1, %Unit{unit: unit_type_2} = unit_2) do
if Unit.compatible?(unit_type_1, unit_type_2) do
mult(unit_1, Conversion.convert(unit_2, unit_type_1))
else
{:error, incompatible_unit_error(unit_1, unit_2)}
end
end
@doc """
Multiplies two compatible `%Unit{}` types
and raises on error
## Options
* `unit_1` and `unit_2` are compatible Units
returned by `Cldr.Unit.new/2`
## Returns
* A `%Unit{}` of the same time as `unit_1` with a value
that is the product of `unit_1` and the potentially
converted `unit_2`
* Raises an exception
"""
@spec mult!(unit_1 :: Unit.t, unit_2 :: Unit.t) ::
Unit.t | no_return()
def mult!(unit_1, unit_2) do
case mult(unit_1, unit_2) do
{:error, {exception, reason}} -> raise exception, reason
unit -> unit
end
end
@doc """
Divides one compatible `%Unit{}` type by another
## Options
* `unit_1` and `unit_2` are compatible Units
returned by `Cldr.Unit.new/2`
## Returns
* A `%Unit{}` of the same time as `unit_1` with a value
that is the dividend of `unit_1` and the potentially
converted `unit_2`
* `{:error, {IncompatibleUnitError, message}}`
## Examples
iex> Cldr.Unit.div Cldr.Unit.new!(:kilogram, 5), Cldr.Unit.new!(:pound, 1)
#Unit<:kilogram, 11.023100000000001>
iex> Cldr.Unit.div Cldr.Unit.new!(:pint, 5), Cldr.Unit.new!(:liter, 1)
#Unit<:pint, 2.365878355998448>
iex> Cldr.Unit.div Cldr.Unit.new!(:pint, 5), Cldr.Unit.new!(:pint, 1)
#Unit<:pint, 5.0>
"""
@spec div(unit_1 :: Unit.t, unit_2 :: Unit.t) ::
Unit.t | {:error, {Unit.IncompatibleUnitError, String.t}}
def div(%Unit{unit: unit, value: value_1}, %Unit{unit: unit, value: value_2})
when is_number(value_1) and is_number(value_2) do
Unit.new!(unit, value_1 / value_2)
end
def div(%Unit{unit: unit, value: %Decimal{} = value_1},
%Unit{unit: unit, value: %Decimal{} = value_2}) do
Unit.new!(unit, Decimal.div(value_1, value_2))
end
def div(%Unit{unit: unit, value: %Decimal{}} = unit_1,
%Unit{unit: unit, value: value_2})
when is_number(value_2) do
div(unit_1, Unit.new!(unit, Decimal.new(value_2)))
end
def div(%Unit{unit: unit, value: value_2},
%Unit{unit: unit, value: %Decimal{}} = unit_1)
when is_number(value_2) do
div(unit_1, Unit.new!(unit, Decimal.new(value_2)))
end
def div(%Unit{unit: unit_type_1} = unit_1, %Unit{unit: unit_type_2} = unit_2) do
if Unit.compatible?(unit_type_1, unit_type_2) do
div(unit_1, Conversion.convert(unit_2, unit_type_1))
else
{:error, incompatible_unit_error(unit_1, unit_2)}
end
end
@doc """
Divides one compatible `%Unit{}` type by another
and raises on error
## Options
* `unit_1` and `unit_2` are compatible Units
returned by `Cldr.Unit.new/2`
## Returns
* A `%Unit{}` of the same time as `unit_1` with a value
that is the dividend of `unit_1` and the potentially
converted `unit_2`
* Raises an exception
"""
@spec div!(unit_1 :: Unit.t, unit_2 :: Unit.t) ::
Unit.t | no_return()
def div!(unit_1, unit_2) do
case div(unit_1, unit_2) do
{:error, {exception, reason}} -> raise exception, reason
unit -> unit
end
end
end