81 lines
2.5 KiB
Elixir
81 lines
2.5 KiB
Elixir
defmodule RationalNumbers do
|
|
@type rational :: {integer, integer}
|
|
alias Kernel, as: K
|
|
|
|
@doc """
|
|
Add two rational numbers
|
|
"""
|
|
@spec add(a :: rational, b :: rational) :: rational
|
|
def add({an, ad}, {bn, bd}), do: {an * bd + bn * ad, ad * bd} |> reduce()
|
|
|
|
@doc """
|
|
Subtract two rational numbers
|
|
"""
|
|
@spec subtract(a :: rational, b :: rational) :: rational
|
|
def subtract({an, ad}, {bn, bd}), do: {an * bd - bn * ad, ad * bd} |> reduce()
|
|
|
|
@doc """
|
|
Multiply two rational numbers
|
|
"""
|
|
@spec multiply(a :: rational, b :: rational) :: rational
|
|
def multiply({an, ad}, {bn, bd}), do: {an * bn, ad * bd} |> reduce()
|
|
|
|
@doc """
|
|
Divide two rational numbers
|
|
"""
|
|
@spec divide_by(num :: rational, den :: rational) :: rational
|
|
def divide_by({an, ad}, {bn, bd}) when bn != 0, do: {an * bd, bn * ad} |> reduce()
|
|
|
|
@doc """
|
|
Absolute value of a rational number
|
|
"""
|
|
@spec abs(a :: rational) :: rational
|
|
def abs({an, ad}), do: {K.abs(an), K.abs(ad)} |> reduce()
|
|
|
|
@doc """
|
|
Exponentiation of a rational number by an integer
|
|
"""
|
|
@spec pow_rational(a :: rational, n :: integer | float) :: rational
|
|
def pow_rational({an, ad}, n) when is_integer(n) and n >= 0, do: {an ** n, ad ** n} |> reduce()
|
|
|
|
def pow_rational({an, ad}, n) when is_integer(n) and n < 0,
|
|
do: {ad ** K.abs(n), an ** K.abs(n)} |> reduce()
|
|
|
|
def pow_rational({an, ad}, x) when is_float(x), do: ad ** x / an ** x
|
|
|
|
@doc """
|
|
Exponentiation of a real number by a rational number
|
|
"""
|
|
@spec pow_real(x :: integer, n :: rational) :: float
|
|
def pow_real(x, {_nn, nd} = n) when is_integer(x) and nd < 0, do: pow_real(x, normalize(n))
|
|
def pow_real(x, {nn, nd}) when is_integer(x), do: x ** nn ** (1 / nd)
|
|
|
|
@doc """
|
|
Reduce a rational number to its lowest terms
|
|
"""
|
|
@spec reduce(a :: rational) :: rational
|
|
def reduce({an, ad} = a),
|
|
do: a |> normalize() |> cut(Integer.gcd(an, ad))
|
|
|
|
# @doc """
|
|
# Divides a nominator and denominator by integer
|
|
# """
|
|
@spec cut(a :: rational, n :: integer) :: rational
|
|
defp cut({an, ad}, n), do: {K.div(an, n), K.div(ad, n)}
|
|
|
|
# @doc """
|
|
# Turns signs of nominator and denominator when negative denominator
|
|
# """
|
|
@spec normalize(a :: rational) :: rational
|
|
defp normalize({an, ad}) when ad < 0, do: {K.-(an), K.-(ad)}
|
|
defp normalize({an, ad}), do: {an, ad}
|
|
|
|
# @doc """
|
|
# Calculate a greatest common divisor of two integers
|
|
# """
|
|
# @spec gcd(i :: integer, j :: integer) :: integer
|
|
# defp gcd(i, 0), do: K.abs(i)
|
|
# defp gcd(0, j), do: K.abs(j)
|
|
# defp gcd(i, j), do: gcd(j, K.rem(i, j))
|
|
end
|