# FakeDecimal

If you deal with legacy Erlang code, you may know the old `erlang_decimal`
library. On Hex it's published as `erlang_decimal`, but it registers its OTP
application (and its module) under the plain name `decimal` — the same name the
modern Elixir `decimal` library uses for its own OTP application. Since a BEAM
node can't run two applications with the same name, you can't depend on both at
once.

`fake_decimal` solves this by providing the exact same API as the old `decimal`
module, implemented on top of the modern Elixir `Decimal` library. Depend on
`fake_decimal` (which pulls in the real `decimal` app) instead of
`erlang_decimal`, and your legacy code can keep calling `:decimal.add/2`,
`:decimal.round/3`, and so on without ever knowing it's talking to the new
library underneath.

## Installation

Add `fake_decimal` to your list of dependencies in `mix.exs`:

```elixir
def deps do
  [
    {:fake_decimal, "~> 0.1.1"}
  ]
end
```

Documentation is available on [HexDocs](https://hexdocs.pm/fake_decimal).

## The `decimal.hrl` header

`erlang_decimal` ships an `include/decimal.hrl` with convenience macros
(`?add/2`, `?divide/2`, `?to_binary/1`, `?d_context`, …) that wrap the plain
`decimal` functions. `fake_decimal` ships the same header, unchanged — the
macros only expand to `decimal:...` calls, which is exactly what this library
provides.

The one thing you have to adjust in legacy modules is the include path, because
the OTP application is now called `fake_decimal`:

```erlang
%% -include_lib("decimal/include/decimal.hrl").
-include_lib("fake_decimal/include/decimal.hrl").
```

The knobs the header honours are unchanged too, so you can still override them
before including it:

```erlang
-define(d_precision, 20).
-define(d_rounding, round_down).
-define(d_pretty, false).
-define(d_no_autocast, true).  %% skip the implicit ?to_decimal/1 on arguments
-include_lib("fake_decimal/include/decimal.hrl").
```

## Modules and types

Both modules of the original library are provided: `decimal` and
`decimal_conv`. The latter — parsing, formatting and float conversion — is a
direct port of the original Erlang source rather than a shim, because it is
what decides which strings are valid and which digits a float produces.

All four exported types are reproduced with identical definitions, so `-spec`s
in legacy modules keep resolving under Dialyzer:

```erlang
-spec total(decimal:decimal(), decimal:decimal()) -> decimal:decimal().
-spec context() -> decimal:opts().
-spec mode() -> decimal:rounding_algorithm().
-spec print_opts() -> decimal_conv:binary_opts().
```

## Known differences

Two remain, both of which only ever *accept more* or return an equal value, so
neither can break code that worked against the original:

  * `add/2`, `sub/2` and `mult/2` return normalized tuples — `add({1,0}, {9,0})`
    gives `{1,1}` where the original gives `{10,0}`. Numerically equal, and
    `reduce/1` maps both to the same tuple, but exact pattern matches will see
    the difference.

  * Functions that take a `{coef, exp}` tuple in the original also accept the
    old `{sign, coef, exp}` format, `Decimal` structs, integers, floats,
    strings and charlists here.

Everything else — rounding, division, square root, comparison, formatting,
parsing, and the errors raised on invalid input — matches the original.

Property-based tests compare `fake_decimal` against a real instance of
`erlang_decimal`, loaded in a separate node, to make sure the two behave
identically.

See the @moduledoc in `lib/fake_decimal.ex` for the full API and the handful of
intentional deviations from the original.
