defmodule Statistics.Math do
@e :math.exp(1)
@pi :math.pi()
@doc """
Get square root
return sqrt from Erlang
## Examples
iex> Statistics.Math.sqrt(9)
3.0
iex> Statistics.Math.sqrt(99)
9.9498743710662
"""
@spec sqrt(number) :: number
defdelegate sqrt(num), to: :math
@doc """
Get power from Erlang
This is needed because Elixir doesn't
currently have the `**` operator
## Examples
iex> Statistics.Math.pow(2,3)
8.0
iex> Statistics.Math.pow(9,9)
387420489.0
iex> Statistics.Math.pow(2,0)
1
iex> Statistics.Math.pow(-2, 1.5)
-2.8284271247461903
iex> Statistics.Math.pow(0, 5)
0
"""
@spec pow(number, number) :: number
def pow(_, 0), do: 1
def pow(0, pow) when pow >= 0, do: 0
# Erlang doesn't like raising negative numbers to non-integer powers
def pow(num, pow) when num < 0 and is_float(pow) do
:math.pow(-num, pow) * -1
end
# otherwise let erlang do it
defdelegate pow(num, pow), to: :math
@doc """
The constant *e*
## Examples
iex> Statistics.Math.e
2.718281828459045
"""
@spec e() :: number
def e do
@e
end
@doc """
The constant *pi*
(returned from Erlang Math module)
## Examples
iex> Statistics.Math.pi
3.141592653589793
"""
@spec pi() :: number
def pi do
@pi
end
@doc """
The natural log
( from Erlang Math module)
## Examples
iex> Statistics.Math.ln(20)
2.995732273553991
iex> Statistics.Math.ln(200)
5.298317366548036
"""
@spec ln(number) :: number
defdelegate ln(i), to: :math, as: :log
@doc """
Exponent function
Raise *e* to given power
## Examples
iex> Statistics.Math.exp(5.6)
270.42640742615254
"""
@spec exp(number) :: number
defdelegate exp(x), to: :math
@doc """
Get a random number from erlang
"""
@spec rand() :: number
defdelegate rand(), to: :rand, as: :uniform
@doc """
Round a decimal to a specific precision
## Examples
iex> Statistics.Math.round(0.123456, 4)
0.1235
"""
@spec round(number, number) :: number
def round(x, precision) do
p = pow(10, precision)
:erlang.round(x * p) / p
end
@doc """
Floor function
## Examples
iex> Statistics.Math.floor(3.999)
3.0
"""
@spec floor(number) :: number
def floor(x) do
f = :erlang.trunc(x) * 1.0
cond do
x - f >= 0 ->
f
x - f < 0 ->
f - 1
end
end
@doc """
Ceiling function
## Examples
iex> Statistics.Math.ceil(3.999)
4.0
"""
@spec ceil(number) :: number
def ceil(x) do
f = :erlang.trunc(x) * 1.0
cond do
x - f > 0 ->
f + 1
x - f <= 0 ->
f
end
end
@doc """
Get the absolute value of a number
## Examples
iex> Statistics.Math.abs(-4)
4
"""
@spec abs(number) :: number
defdelegate abs(x), to: :erlang
@doc """
Factorial!
"""
@spec factorial(non_neg_integer) :: non_neg_integer
def factorial(n) when n < 0 do
raise ArithmeticError, message: "Argument n must be a positive number"
end
def factorial(n) when n == 0 or n == 1 do
1
end
def factorial(n) do
(to_int(n) - 1)..1
|> Enum.to_list()
|> List.foldl(n, fn x, acc -> x * acc end)
end
@doc """
Get the base integer from a float
## Examples
iex> Statistics.Math.to_int(66.6666)
66
"""
@spec to_int(number) :: integer
defdelegate to_int(f), to: :erlang, as: :trunc
@doc """
The number of k combinations of n
Both arguments must be integers
## Examples
iex> Statistics.Math.combination(10, 3)
120
"""
@spec combination(non_neg_integer, non_neg_integer) :: non_neg_integer
def combination(n, k) do
:erlang.div(factorial(n), factorial(k) * factorial(n - k))
end
@doc """
The number of k permuations of n
## Examples
iex> Statistics.Math.permutation(10, 3)
720
"""
@spec permutation(non_neg_integer, non_neg_integer) :: non_neg_integer
def permutation(n, k) do
:erlang.div(factorial(n), factorial(n - k))
end
end