# `Predicator.Conformance.Features`
[🔗](https://github.com/riddler/predicator-ex/blob/v9.4.1/lib/predicator/conformance/features.ex#L1)

Feature tags for the conformance corpus (`px-35i.4`).

Tiers (`Predicator.Instructions.tier/1`) are the coarse cut: "everything a
tier-N implementation needs". Feature tags are the finer cut *across*
tiers - `durations`, `strict_equality`, `short_circuit` - so an
implementation that wants one advanced feature early can select for it
without taking its whole tier, and a sibling reports what it lacks in these
terms (bead px-35i.4's description).

The full tag vocabulary: `comparison`, `strict_equality`, `short_circuit`,
`arithmetic`, `membership`, `access`, `objects`, `dates`, `datetimes`,
`durations`, `functions`, `legacy_logical`, `undefined`, `errors`, `casts`.

This is a good-faith mapping, not an exhaustive one - see the per-clause
comments below for the simplifications taken. `lit`, `load`, `not`, and
`unary_bang` carry no tag of their own; they show up in nearly every case
and would just add noise.

# `outcome`

```elixir
@type outcome() :: {:result, term()} | {:error, struct() | :retired}
```

A case outcome, as computed by the generator - what the tags are derived
from. `:retired` stands in for a real error struct when a case's `expected`
is frozen data rather than a live evaluation (`Predicator.Conformance.
Generator`'s retired-opcode path) - `outcome_tags/1`'s `{:error, _other}`
catch-all maps it to the same `"errors"` tag a real struct would produce.

# `compute`

```elixir
@spec compute(Predicator.Types.instruction_list(), map(), outcome(), [String.t()]) ::
  [String.t()]
```

Computes the feature tag set for a case: opcode-derived tags from
`instructions`, plus value-derived tags from `context` and the case
`outcome`, merged with any authored `extra_tags`.

Returns a sorted, deduplicated list of tag strings - sorted so the shipped
corpus is deterministic (`Predicator.Conformance.JSON`'s canonical encoder
sorts map keys but not list contents, so this function does its own
sorting).

## Examples

    iex> Predicator.Conformance.Features.compute([["lit", 1]], %{}, {:result, 1}, [])
    []

    iex> Predicator.Conformance.Features.compute([["compare", "GT"]], %{}, {:result, true}, [])
    ["comparison"]

    iex> Predicator.Conformance.Features.compute([["compare", "STRICT_EQ"]], %{}, {:result, true}, [])
    ["comparison", "strict_equality"]

    iex> Predicator.Conformance.Features.compute([["and"]], %{}, {:result, true}, [])
    ["legacy_logical"]

    iex> Predicator.Conformance.Features.compute([["add"]], %{}, {:result, :undefined}, ["custom"])
    ["arithmetic", "custom", "undefined"]

---

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