Packages
vtc
0.17.5
0.17.5
0.17.4
0.17.3
0.17.2
0.17.1
0.17.0
0.16.9
0.16.8
0.16.7
0.16.6
0.16.5
0.16.4
0.16.2
0.16.1
0.16.0
0.15.4
0.15.3
0.15.2
0.15.1
0.15.0
0.14.5
0.14.4
0.14.3
0.14.2
0.14.1
0.14.0
0.13.15
0.13.14
0.13.13
0.13.12
0.13.11
0.13.10
0.13.9
0.13.8
0.13.7
0.13.6
0.13.5
0.13.4
0.13.3
0.13.2
0.13.1
0.13.0
0.12.1
0.12.0
0.11.1
0.11.0
0.10.10
0.10.9
0.10.8
0.10.7
0.10.6
0.10.5
0.10.4
0.10.3
0.10.2
0.10.1
0.10.0
0.9.2
0.9.1
0.9.0
0.8.4
0.8.3
0.8.2
0.8.1
0.8.0
0.7.3
0.7.2
0.7.1
0.7.0
0.6.1
0.6.0
0.5.3
0.5.2
0.5.1
0.4.0
0.3.9
0.3.8
0.3.7
0.3.6
0.3.5
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.2.6
0.2.5
0.2.4
0.2.3
0.2.2
0.2.1
0.2.0
0.1.9
0.1.8
0.1.7
0.1.6
0.1.5
0.1.4
0.1.3
0.1.2
0.1.1
A SMPTE timecode library for Elixir
Current section
Files
Jump to
Current section
Files
lib/utils/rational.ex
defmodule Vtc.Utils.Rational do
@moduledoc false
import Kernel, except: [rem: 2]
@doc """
Rounds `x` based on `method`.
## Arguments
- `x`: The Rational value to round.
- `method`: Rounding strategy. Defaults to `:closest`.
- `:closest`: Round the to the closet whole frame, rounding up when fractional
remainder is equal to `1/2`.
- `:floor`: Always round down to the closest whole-frame.
- `:ciel`: Always round up to the closest whole-frame.
- `:trunc`: Always rounds towards zero.
- `:off`: Pass value through without rounding.
"""
@spec round(Ratio.t(), :closest | :floor | :ceil | :trunc) :: integer()
@spec round(Ratio.t(), :off) :: Ratio.t()
def round(x, method \\ :closest)
def round(%{numerator: n, denominator: d}, :closest), do: round_closest(n, d)
def round(x, :floor), do: Ratio.floor(x)
def round(x, :ceil), do: Ratio.ceil(x)
def round(x, :trunc), do: Ratio.trunc(x)
def round(x, :off), do: x
# Handles rounding to the closest integer. Adapted loosely from Python's
# implementation, only rounds up rather than down:
# https://github.com/python/cpython/blob/3.11/Lib/fractions.py
@spec round_closest(integer(), pos_integer()) :: integer()
defp round_closest(n, d) when n * d < 0, do: -round_closest(-n, d)
defp round_closest(n, d) when Kernel.rem(n, d) * 2 < d, do: div(n, d)
defp round_closest(n, d), do: div(n, d) + 1
@doc """
Does the divrem operation on a rational vale, returns a
{whole_dividend, rational_remainder} tuple.
"""
@spec divrem(Ratio.t(), Ratio.t() | number()) :: {integer(), Ratio.t()}
def divrem(dividend, divisor) do
dividend = Ratio.new(dividend)
divisor = Ratio.new(divisor)
# Round towards zero instead of infinity
quotient = dividend |> Ratio.div(divisor) |> then(&div(&1.numerator, &1.denominator))
remainder = rem(dividend, divisor)
{quotient, remainder}
end
@spec rem(Ratio.t(), Ratio.t()) :: Ratio.t()
def rem(dividend, divisor) do
numerator = Kernel.rem(dividend.numerator * divisor.denominator, divisor.numerator * dividend.denominator)
denominator = dividend.denominator * divisor.denominator
result = Ratio.new(numerator, denominator)
if dividend.numerator < 0 do
%Ratio{numerator: abs(result.numerator) * -1, denominator: result.denominator}
else
result
end
end
end