Packages
vtc
0.9.1
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
@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.
- `:off`: Pass value through without rounding.
"""
@spec round(Ratio.t(), :closest | :floor | :ceil) :: 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, :off), do: x
# Handles roundinf to the closest integer. Adapted loosly 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 < 0, do: -round_closest(-n, d)
defp round_closest(n, d) when 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(x, divisor) when is_integer(x) and x < 0, do: divrem(%Ratio{numerator: x, denominator: 1}, divisor)
def divrem(%{numerator: n} = dividend, divisor) when n < 0,
do: dividend |> Ratio.abs() |> divrem(divisor) |> then(fn {q, r} -> {-q, r} end)
def divrem(dividend, divisor) do
quotient = dividend |> Ratio.new() |> Ratio.div(Ratio.new(divisor)) |> Ratio.floor()
remainder = Ratio.sub(dividend, Ratio.mult(divisor, Ratio.new(quotient)))
{quotient, remainder}
end
end