# `Feel.Runtime.Compare`
[🔗](https://github.com/FeeJai/BDM/blob/feel-v0.1.0/apps/feel/lib/feel/runtime/compare.ex#L1)

Equality and ordering: `=`, `!=`, `<`, `<=`, `>`, and `>=` (DMN 1.6, Tables 50
and 53 to 55).

Each function takes the two values and returns an `t:Feel.Runtime.Env.outcome/0`.

## Equality

Two values are equal only when they are of the same kind (`Feel.Runtime.Kind`);
`"1" = 1` is `null`, with a type mismatch. `null` is the exception (Table 50):
`null = null` is `true`, and `null = x` is `false` for any other value, so `=`
and `!=` never give `null` because of a `null` operand. Within a kind:

  * numbers are equal by value, not by scale: `1 = 1.000`.
  * strings are equal when they are the same code points: no case folding, no
    normalization.
  * dates, and the durations of one kind, are equal by value. A years and months
    duration and a days and time duration are different kinds, so they never compare
    (D-54).
  * times, and dates and times, are equal as `Feel.Runtime.Temporal.compare/3` says:
    by instant when both have a time offset or a zone, and by their local fields when
    one has none (D-54). A date and a date and time are different kinds.
  * lists are equal when they have the same length and equal elements, contexts when
    they have the same keys and equal values, ranges when they have the same
    endpoints, the same inclusion of them, and were written the same way (`(< 10)` is not
    `(null..10)`, D-61). An element that cannot be compared
    makes the result `null`, unless another element is already unequal; then it is
    `false`. A list or a context that is the same term as the other, or has the same terms in it, is equal without its
    elements being compared (D-168), so it is equal if a term in it is not a value of FEEL (an atom, a float, a `NaN`:
    `Feel.Runtime.Kind.of/1` is `:unknown` for it) and is the same one on both sides, where that term alone is not equal to itself.

`!=` is `not (=)`.

## Ordering

`<`, `<=`, `>`, and `>=` are defined for numbers, strings, dates, times, dates and times, and
durations of one kind (Table 55), and give `null` for everything else. Strings compare by code point,
with the shorter of two strings that start alike first (the specification pads with
U+0000, so trailing U+0000 characters do not count). A `null` operand of `<` or `>` gives
`:missing`, and one of `<=` or `>=` is `null` with an error, `:null_operand`, as the TCK flags the one and not
the other (`Feel.Runtime.NullOperand`, D-103); operands of different kinds, or of a kind that has no order (booleans,
lists, contexts, ranges, functions), give a type mismatch.

This is the specification's sentence that the operators "are defined only for the
datatypes listed in Table 55". Its other sentence, that `<=` is `<` or `=`, still holds
within those types, but it is not applied to the others: `null <= null` and
`true <= true` are `null`, not `true`. The two readings differ only there, and the TCK
has no test for either (decision D-04, D-103). Reading the formula literally is the
`order/5` clause for the unordered kinds.

## Functions

Two functions are equal when they are the same function value, and unequal otherwise (notes 05, [E13];
D-82): the specification says "the same parameters, body and scope", and the identity of the closure is
that, taken for a function that was written once and bound to a name. Two functions that are written alike in
two places are two values, so they are unequal. A function has no order.

# `equal`

```elixir
@spec equal(Feel.Value.t(), Feel.Value.t()) :: Feel.Runtime.Env.outcome()
```

Tests two values for equality.

    iex> Feel.Runtime.Compare.equal(Decimal.new("1"), Decimal.new("1.000"))
    {:ok, true}
    iex> Feel.Runtime.Compare.equal(nil, nil)
    {:ok, true}
    iex> Feel.Runtime.Compare.equal(Decimal.new(1), nil)
    {:ok, false}
    iex> {:error, diagnostic} = Feel.Runtime.Compare.equal("1", Decimal.new(1))
    iex> diagnostic.message
    "cannot compare a string with a number"

# `greater_or_equal`

```elixir
@spec greater_or_equal(Feel.Value.t(), Feel.Value.t()) :: Feel.Runtime.Env.outcome()
```

Tests whether the first value is greater than or equal to the second.

    iex> Feel.Runtime.Compare.greater_or_equal(Decimal.new(2), Decimal.new(3))
    {:ok, false}

# `greater_than`

```elixir
@spec greater_than(Feel.Value.t(), Feel.Value.t()) :: Feel.Runtime.Env.outcome()
```

Tests whether the first value is greater than the second.

    iex> Feel.Runtime.Compare.greater_than("b", "a")
    {:ok, true}

# `less_or_equal`

```elixir
@spec less_or_equal(Feel.Value.t(), Feel.Value.t()) :: Feel.Runtime.Env.outcome()
```

Tests whether the first value is less than or equal to the second.

    iex> Feel.Runtime.Compare.less_or_equal(Decimal.new(1), Decimal.new(1))
    {:ok, true}
    iex> {:error, diagnostic} = Feel.Runtime.Compare.less_or_equal(nil, nil)
    iex> diagnostic.code
    :null_operand
    iex> {:error, diagnostic} = Feel.Runtime.Compare.less_or_equal(Decimal.new(1), nil)
    iex> diagnostic.message
    "an operand of <= is null"

# `less_than`

```elixir
@spec less_than(Feel.Value.t(), Feel.Value.t()) :: Feel.Runtime.Env.outcome()
```

Tests whether the first value is less than the second.

    iex> Feel.Runtime.Compare.less_than("a", "ab")
    {:ok, true}
    iex> Feel.Runtime.Compare.less_than(Decimal.new(1), nil)
    :missing

# `not_equal`

```elixir
@spec not_equal(Feel.Value.t(), Feel.Value.t()) :: Feel.Runtime.Env.outcome()
```

Tests two values for inequality: the negation of `equal/2`.

    iex> Feel.Runtime.Compare.not_equal(Decimal.new(1), nil)
    {:ok, true}
    iex> Feel.Runtime.Compare.not_equal("a", "a")
    {:ok, false}

# `ordering`

```elixir
@spec ordering(:lt | :lte | :gt | :gte, Feel.Value.t(), Feel.Value.t()) ::
  Feel.Runtime.Env.outcome()
```

The comparison of an IR node (`t:Feel.IR.comparison_operator/0`) that is an ordering: `:lt`, `:lte`,
`:gt` or `:gte`.

    iex> Feel.Runtime.Compare.ordering(:lte, "a", "b")
    {:ok, true}
    iex> Feel.Runtime.Compare.ordering(:gt, "a", "b")
    {:ok, false}

---

*Consult [api-reference.md](api-reference.md) for complete listing*
