Packages
beancount_ex
0.6.0
An idiomatic Elixir interface to Beancount that serves as the long-term behavioral oracle for a future native Elixir General Ledger.
Current section
Files
Jump to
Current section
Files
lib/beancount/engine/elixir/tolerance.ex
defmodule Beancount.Engine.Elixir.Tolerance do
@moduledoc false
alias Beancount.Engine.Elixir.Options
@spec infer(Options.t(), String.t(), [Decimal.t()]) :: Decimal.t()
def infer(%Options{} = options, _currency, amounts) do
amounts
|> Enum.map(&precision_tolerance/1)
|> Enum.concat([options.inferred_tolerance_default])
|> Enum.reject(&is_nil/1)
|> case do
[] -> Decimal.new(0)
amounts -> Enum.reduce(amounts, &Decimal.max/2)
end
|> Decimal.mult(options.inferred_tolerance_multiplier)
|> Decimal.mult(options.tolerance_multiplier)
end
@spec within?(Decimal.t(), Decimal.t(), Decimal.t()) :: boolean()
def within?(actual, expected, tolerance) do
Decimal.sub(actual, expected) |> Decimal.abs() |> Decimal.lte?(tolerance)
end
defp precision_tolerance(%Decimal{} = amount) do
amount
|> Decimal.normalize()
|> Decimal.to_string(:normal)
|> decimal_places()
|> then(fn places ->
if places == 0 do
Decimal.new("0.5")
else
divisor = Decimal.new("1" <> String.duplicate("0", places))
scale = Decimal.div(Decimal.new(1), divisor)
Decimal.mult(Decimal.new("0.5"), scale)
end
end)
end
defp decimal_places(string) do
case String.split(string, ".") do
[_int] -> 0
[_, frac] -> String.length(frac)
end
end
end