# View Source Formulae(formulae v0.15.2)

A set of functions to deal with analytical formulae.

The typical way of using this module would be to call `Formulae.compile/1` on the binary representing the string.

``````iex|1 ▶ f = Formulae.compile "a + :math.sin(3.14 * div(b, 2)) - c"

#ℱ<[
sigil: "~F[a + :math.sin(3.14 * div(b, 2)) - c]",
eval: &:"Elixir.Formulae.a + :math.sin(3.14 * div(b, 2)) - c".eval/1,
formula: "a + :math.sin(3.14 * div(b, 2)) - c",
guard: nil,
module: :"Elixir.Formulae.a + :math.sin(3.14 * div(b, 2)) - c",
variables: [:a, :b, :c],
options: [defaults: [], imports: [:...], evaluator: :function, alias: nil]
]>``````

Now the formula is compiled and might be invoked by calling `Formulae.eval/2` passing a formula and bindings. First call to `eval/2` would lazily compile the module if needed.

``````iex|2 ▶ f.eval.(a: 3, b: 4, c: 2)
0.9968146982068622``````

The formulae might be curried.

``````iex|3 ▶ Formulae.curry(f, a: 3, b: 4)

#ℱ<[
sigil: "~F[3 + :math.sin(3.14 * div(4, 2)) - c]",
eval: &:"Elixir.Formulae.3 + :math.sin(3.14 * div(4, 2)) - c".eval/1,
formula: "3 + :math.sin(3.14 * div(4, 2)) - c",
guard: nil,
module: :"Elixir.Formulae.3 + :math.sin(3.14 * div(4, 2)) - c",
variables: [:c],
options: [defaults: [], imports: [:...], evaluator: :function, alias: nil]
]>``````

Since `v0.10.0` there is an ability to pass `defaults` via `options`.

Examples:

``````iex> "z + t" |> Formulae.compile(defaults: [t: 5]) |> Formulae.eval(t: 10, z: 3)
13
iex> "z + t" |> Formulae.compile(defaults: [t: 5]) |> Formulae.eval(z: 3)
8``````

# Summary

## Types

The formulae is internally represented as struct, exposing the original binary representing the formula, AST, the module this formula was compiled into, variables (bindings) this formula has and the evaluator, which is the function of arity one, accepting the bindings as a keyword list and returning the result of this formula application.

## Functions

Returns the binding this formula requires.

Revalidates the formula with bindings given. Returns true if the formula strictly evaluates to `true`, `false` otherwise. Compiles the formula before evaluation if needed.

Generated clauses for `n ∈ [1..42]` to be used with dynamic number

Compiles the formula into module.

Checks whether the formula was already compiled into module.

Curries the formula by substituting the known bindings into it.

Checks whether the formula was already compiled into module. Similar to `compiled?/1`, but returns what `Code.ensure_compiled/1` returns.

Evaluates the formula returning the result back.

Evaluates the formula returning the result back; throws in a case of unseccessful processing.

Evaluates normalized representation of formula.

Lists all the compiled formulas.

normalize(input) deprecated

Returns a normalized representation for the formula given.

Generated clauses for `n ∈ [1..12]` to be used with dynamic number

Purges and discards the module for the formula given (if exists.)

Produces the normalized representation of formula. If the rho is an instance of `Integer` or `Float`, it’s left intact, otherwise it’s moved to the left side with negation.

# t()

View Source
```@type t() :: %{
__struct__: atom(),
formula: binary(),
ast: nil | Macro.t(),
eval: nil | (keyword() -> any()),
guard: nil | Macro.t(),
module: nil | atom(),
variables: nil | [atom()],
options: options()
}```

The formulae is internally represented as struct, exposing the original binary representing the formula, AST, the module this formula was compiled into, variables (bindings) this formula has and the evaluator, which is the function of arity one, accepting the bindings as a keyword list and returning the result of this formula application.

# bindings?(formula, bindings \\ [], options \\ [imports: :none])

View Source
This function is deprecated. Use `Formulae.compile/1` and `%Formulae{}.variables` or `Formula.curry/2` instead.

Returns the binding this formula requires.

## Examples

``````iex> "a > 5" |> Formulae.bindings?()
~w|a|a

iex> ":math.sin(a / (3.14 * b)) > c" |> Formulae.bindings?([], imports: [:math])
~w|a b c|a

iex> "a + b * 4 - :math.pow(c, 2) / d > 1.0 * e" |> Formulae.bindings?([], imports: :math)
~w|a b c d e|a``````

# check(string, bindings \\ [], options \\ [])

View Source
This function is deprecated. Use `Formulae.eval/2` instead.
```@spec check(string :: binary(), bindings :: keyword(), options :: options()) ::
boolean()```

Revalidates the formula with bindings given. Returns true if the formula strictly evaluates to `true`, `false` otherwise. Compiles the formula before evaluation if needed.

# combinations(l, n)

View Source
`@spec combinations(list :: list(), count :: non_neg_integer()) :: [list()]`

Generated clauses for `n ∈ [1..42]` to be used with dynamic number

# compile(input, options \\ [])

View Source
`@spec compile(t() | binary(), options :: options()) :: t()`

Compiles the formula into module.

The allowed imports must be specified explicitly with `imports: :all` or a list of allowed imports `imports: [DateTime, Range]`.

Examples:

``````iex> f = Formulae.compile("rem(a, 5) - b == 0")
iex> f.formula
"rem(a, 5) - b == 0"
iex> f.variables
[:a, :b]
iex> f.module
:"Elixir.Formulae.rem(a, 5) - b == 0"
iex> f.module.eval(a: 12, b: 2)
true

iex> f = Formulae.compile("rem(a, 5) + b == a")
iex> f.variables
[:a, :b]
iex> f.eval.(a: 7, b: 5)
true
iex> f.eval.(a: 7, b: 0)
false``````

# compiled?(input, options \\ [])

View Source
`@spec compiled?(binary() | t(), options :: options()) :: boolean()`

Checks whether the formula was already compiled into module.

Typically one does not need to call this function, since this check would be nevertheless transparently performed before the evaluation.

Examples:

``````iex> Formulae.compiled?("foo > 42")
false
iex> Formulae.compile("foo > 42")
iex> Formulae.compiled?("foo > 42")
true``````

# curry(input, binding \\ [], options \\ [])

View Source
```@spec curry(input :: t() | binary(), binding :: keyword(), options :: options()) ::
t()```

Curries the formula by substituting the known bindings into it.

## Example

``````iex> Formulae.curry("(temp - foo * 4) > speed / 3.14", temp: 7, speed: 3.14).formula
"7 - foo * 4 > 3.14 / 3.14"``````

# ensure_compiled(input, options \\ [])

View Source
```@spec ensure_compiled(binary() | t(), options :: options()) ::
{:module, module()}
| {:error,
:embedded
| :nofile
| :unavailable
| {:external_module, module()}}```

Checks whether the formula was already compiled into module. Similar to `compiled?/1`, but returns what `Code.ensure_compiled/1` returns.

Typically one does not need to call this function, since this check would be nevertheless transparently performed before the evaluation.

Examples:

``````iex> Formulae.ensure_compiled("bar > 42")
{:error, :nofile}
iex> Formulae.compile("bar > 42")
iex> Formulae.ensure_compiled("bar > 42")
{:module, :"Elixir.Formulae.bar > 42"}``````

# eval(input, bindings \\ [], options \\ [imports: :none])

View Source
```@spec eval(input :: binary() | t(), bindings :: keyword(), options :: options()) ::
term() | {:error, any()}```

Evaluates the formula returning the result back.

Examples:

``````iex> Formulae.eval("rem(a, 5) + rem(b, 4) == 0", a: 20, b: 20)
true
iex> Formulae.eval("rem(a, 5) == 0", a: 21)
false
iex> Formulae.eval("rem(a, 5) + rem(b, 4)", a: 21, b: 22)
3
iex> Formulae.eval("rem(a, 5) == b", [a: 8], defaults: [b: 3])
true
iex> Formulae.eval("rem(a, 5) == c", [a: 8, c: 3], defaults: [b: 3])
true
iex> Formulae.eval("to_integer(s) == i", [s: "42", i: 42], imports: [String])
true``````

Binary input is deprecated, create a formula explicitly with `Formulae.compile/2` and then pass it as the first argument to `eval/2`".

The call to `eval/3` would compile the formulae with default options.

# eval!(input, bindings \\ [], options \\ [])

View Source

Evaluates the formula returning the result back; throws in a case of unseccessful processing.

Examples:

``````iex> Formulae.eval!("rem(a, 5) == 0", a: 20)
true
iex> Formulae.eval!("rem(a, 5) == 0")
** (Formulae.RunnerError) Formula ~F[rem(a, 5) == 0] failed to run (compile): [:missing_arguments], wrong or incomplete evaluator call: [given_keys: [], expected_keys: [:a]].``````

# evaluate(input, binding \\ [], options \\ [])

View Source
This function is deprecated. Use `Formulae.eval/2` instead.
```@spec evaluate(
input :: binary() | tuple(),
binding :: keyword(),
options :: options()
) ::
boolean() | no_return()```

Evaluates normalized representation of formula.

## Examples

``````iex> Formulae.eval("3 > 2")
true

iex> Formulae.eval("3 < 2")
false

iex> Formulae.eval("a < 2", a: 1)
true

iex> Formulae.eval("a > 2", a: 1)
false

iex> Formulae.eval("a < 2", [])
{:error, {:missing_arguments, [given_keys: [], expected_keys: [:a]]}}

iex> Formulae.eval!("a < 2", [])
** (Formulae.RunnerError) Formula ~F[a < 2] failed to run (compile): [:missing_arguments], wrong or incomplete evaluator call: [given_keys: [], expected_keys: [:a]].

iex> Formulae.eval("a + 2 == 3", a: 1)
true

iex> Formulae.eval("a + 2 == 3", a: 2)
false

iex> Formulae.eval(~S|a == "3"|, a: "3")
true

iex> Formulae.eval(~S|a == "3"|, a: 3)
false

iex> Formulae.eval(~S|a == "3"|, a: "hello")
false

iex> Formulae.eval("a + 2 == 3", a: 2)
false

iex> Formulae.eval(~S|a == "3"|, a: "3")
true

iex> Formulae.eval("a_b_c_490000 > 2", a_b_c_490000: 3)
true``````

# formulas(include_internals? \\ false)

View Source
`@spec formulas(include_internals? :: boolean()) :: %{optional(binary()) => module()}`

Lists all the compiled formulas.

# normalize(input)

View Source
This function is deprecated. Use `Formulae.compile/1` and `%Formulae{}.variables` instead.

Returns a normalized representation for the formula given.

# permutations(l, n)

View Source
`@spec permutations(list :: list(), count :: non_neg_integer()) :: [list()]`

Generated clauses for `n ∈ [1..12]` to be used with dynamic number

# purge(input, options \\ [])

View Source
```@spec purge(t() | binary(), options()) ::
:ok | {:error, :not_compiled} | {:error, :code_delete}```

Purges and discards the module for the formula given (if exists.)

# unit(input, env \\ [])

View Source
This function is deprecated. Use `Formulae.eval/2` instead.

Produces the normalized representation of formula. If the rho is an instance of `Integer` or `Float`, it’s left intact, otherwise it’s moved to the left side with negation.

## Examples

``````iex > Formulae.unit("3 > 2")
{"3 > 2", {:>, [], [3, 2]}}

iex > Formulae.unit("3 - a > 2")
{"3 - a > 2", {:>, [], [{:-, [line: 1], [3, {:a, [line: 1], nil}]}, 2]}}

iex > Formulae.unit("3 > A + 2")
{"3 > a + 2",
{:>, [],
[{:-, [context: Formulae, import: Kernel],
[3, {:+, [line: 1], [{:a, [line: 1], nil}, 2]}]}, 0]}}

iex > Formulae.unit("3 >= a + 2")
{"3 >= a + 2",
{:>=, [],
[{:-, [context: Formulae, import: Kernel],
[3, {:+, [line: 1], [{:a, [line: 1], nil}, 2]}]}, 0]}}

iex > Formulae.unit("3 a > A + 2")
** (Formulae.SyntaxError) Formula [3 a > A + 2] syntax is incorrect (parsing): syntax error before: “a”.

iex > Formulae.unit("a + 2 = 3")
{"a + 2 = 3", {:==, [], [{:+, [line: 1], [{:a, [line: 1], nil}, 2]}, 3]}}

iex > Formulae.unit(~S|A = "3"|)
{"a = \"3\"", {:==, [], [{:a, [line: 1], nil}, "3"]}}``````