Current section
Files
Jump to
Current section
Files
lib/math.ex
defmodule Funx.Math do
@moduledoc """
[](https://livebook.dev/run?url=https%3A%2F%2Fgithub.com%2FJKWA%2Ffunx%2Fblob%2Fmain%2Flivebooks%2Fmath%2Fmath.livemd)
Provides mathematical operations using Monoids.
This module uses the `Sum` and `Product` monoids to perform operations
such as addition and multiplication over values or lists of values.
"""
import Funx.Monoid.Utils, only: [m_append: 3, m_concat: 2]
import Funx.Monad, only: [bind: 2, map: 2]
alias Funx.Monad.Maybe
alias Funx.Monoid.{Max, Min, Product, Sum}
@doc """
Sums two numbers using the `Sum` monoid.
## Examples
iex> Funx.Math.sum(1, 2)
3
"""
@spec sum(number(), number()) :: number()
def sum(a, b) do
m_append(%Sum{}, a, b)
end
@doc """
Sums a list of numbers using the `Sum` monoid.
## Examples
iex> Funx.Math.sum([1, 2, 3])
6
iex> Funx.Math.sum([])
0
"""
@spec sum([number()]) :: number()
def sum(list) when is_list(list) do
m_concat(%Sum{}, list)
end
@doc """
Multiplies two numbers using the `Product` monoid.
## Examples
iex> Funx.Math.product(3, 4)
12
"""
@spec product(number(), number()) :: number()
def product(a, b) do
m_append(%Product{}, a, b)
end
@doc """
Multiplies a list of numbers using the `Product` monoid.
## Examples
iex> Funx.Math.product([2, 3, 4])
24
iex> Funx.Math.product([])
1
"""
@spec product([number()]) :: number()
def product(list) when is_list(list) do
m_concat(%Product{}, list)
end
@doc """
Returns the maximum of two numbers using the `Max` monoid.
## Examples
iex> Funx.Math.max(3, 7)
7
iex> Funx.Math.max(-1, -5)
-1
"""
@spec max(number(), number()) :: number()
def max(a, b) do
m_append(%Max{value: Float.min_finite()}, a, b)
end
@doc """
Finds the maximum value in a list using the `Max` monoid.
Returns `Float.min_finite()` if the list is empty.
## Examples
iex> Funx.Math.max([3, 7, 2])
7
iex> Funx.Math.max([])
Float.min_finite()
"""
@spec max([number()]) :: number()
def max(list) when is_list(list) do
m_concat(%Max{value: Float.min_finite()}, list)
end
@doc """
Returns the minimum of two numbers using the `Min` monoid.
## Examples
iex> Funx.Math.min(3, 7)
3
iex> Funx.Math.min(-1, -5)
-5
"""
@spec min(number(), number()) :: number()
def min(a, b) do
m_append(%Min{value: Float.max_finite()}, a, b)
end
@doc """
Finds the minimum value in a list using the `Min` monoid.
Returns `Float.max_finite()` if the list is empty.
## Examples
iex> Funx.Math.min([3, 7, 2])
2
iex> Funx.Math.min([])
Float.max_finite()
"""
@spec min([number()]) :: number()
def min(list) when is_list(list) do
m_concat(%Min{value: Float.max_finite()}, list)
end
@doc """
Computes the arithmetic mean of a list of numbers.
Returns `Nothing` if the list is empty.
## Examples
iex> Funx.Math.mean([1, 2, 3, 4])
Funx.Monad.Maybe.pure(2.5)
iex> Funx.Math.mean([])
Funx.Monad.Maybe.nothing()
"""
@spec mean([number()]) :: Maybe.t(number())
def mean([]), do: Maybe.nothing()
def mean(list) when is_list(list) do
Maybe.pure(sum(list) / length(list))
end
@doc """
Computes the range (difference between max and min) of a list.
Returns `nothing()` if the list is empty.
## Examples
iex> Funx.Math.range([3, 7, 2])
Funx.Monad.Maybe.pure(5)
iex> Funx.Math.range([])
Funx.Monad.Maybe.nothing()
"""
@spec range([number()]) :: Maybe.t(number())
def range([]), do: Maybe.nothing()
def range(list) when is_list(list) do
Maybe.pure(max(list) - min(list))
end
@doc """
Computes the square of a number.
## Examples
iex> Funx.Math.square(3)
9
iex> Funx.Math.square(-4)
16
"""
@spec square(number()) :: number()
@spec square([number()]) :: [number()]
def square(list) when is_list(list), do: map(list, &square/1)
def square(x) when is_number(x), do: product(x, x)
# @spec square(number()) :: number()
@doc """
Computes the sum of squares of a list of numbers.
Returns `0` if the list is empty.
## Examples
iex> Funx.Math.sum_of_squares([1, 2, 3])
14
iex> Funx.Math.sum_of_squares([-2, 5])
29
iex> Funx.Math.sum_of_squares([])
0
"""
@spec sum_of_squares([number()]) :: number()
def sum_of_squares(list) when is_list(list) do
list |> square() |> sum()
end
@doc """
Computes the deviations from the mean for a list of numbers.
Returns `Nothing` if the list is empty.
## Examples
iex> Funx.Math.deviation([1, 2, 3, 4])
Funx.Monad.Maybe.pure([-1.5, -0.5, 0.5, 1.5])
iex> Funx.Math.deviation([5, 5, 5])
Funx.Monad.Maybe.pure([0.0, 0.0, 0.0])
iex> Funx.Math.deviation([])
Funx.Monad.Maybe.nothing()
"""
@spec deviation([number()]) :: Maybe.t([number()])
def deviation(list) when is_list(list) do
list
|> mean()
|> bind(fn mean ->
Maybe.pure(map(list, &(&1 - mean)))
end)
end
@doc """
Computes the variance of a list of numbers.
Returns `Nothing` if the list is empty.
## Examples
iex> Funx.Math.variance([1, 2, 3, 4])
Funx.Monad.Maybe.pure(1.25)
iex> Funx.Math.variance([5, 5, 5])
Funx.Monad.Maybe.pure(0.0)
iex> Funx.Math.variance([])
Funx.Monad.Maybe.nothing()
"""
@spec variance([number()]) :: Maybe.t(number())
def variance(list) when is_list(list) do
list
|> deviation()
|> bind(fn deviations ->
Maybe.pure(sum_of_squares(deviations) / length(list))
end)
end
@doc """
Computes the standard deviation of a list of numbers.
Returns `Nothing` if the list is empty.
## Examples
iex> Funx.Math.std_dev([1, 2, 3, 4])
Funx.Monad.Maybe.pure(1.118033988749895)
iex> Funx.Math.std_dev([5, 5, 5])
Funx.Monad.Maybe.pure(0.0)
iex> Funx.Math.std_dev([])
Funx.Monad.Maybe.nothing()
"""
@spec std_dev([number()]) :: Maybe.t(number())
def std_dev(list) when is_list(list) do
list
|> variance()
|> bind(fn var ->
Maybe.pure(:math.sqrt(var))
end)
end
end