# `Feel.Analysis.Narrowing`
[🔗](https://github.com/FeeJai/BDM/blob/feel-v0.1.0/apps/feel/lib/feel/analysis/narrowing.ex#L1)

What a condition says about the names it reads (D-194).

`if c then a else b` runs `a` only when `c` is `true`, and `b` for anything else, `null` included. So
in `a` a name that `c` compares has a value for which `c` can be `true`, and in `b` one for which it
can be something else: after `x > 5` is true, `x` is a number above 5 and not `null`; after `x = null`
is not true, `x` is not `null`; after `y != 0` is true, `y` is not 0. `facts/2` reads that off the
resolved condition, for one of the two branches, and `narrow/2` applies it to the type of a name.

A fact is a function from the type of a name to the alternatives that are left. It only ever takes
alternatives away, or makes the set of values of one smaller, so a type that is narrowed is still a
type of every value the name can have in the branch. Only the cases that can be read safely are read:

  * a name that is the whole condition (`if flag`), and
  * a comparison `= != < <= > >=` of a name with any expression, whichever side the name is on,
  * joined by `and` and `or`, which meet and join the facts as the logic does.

A condition of anything else says nothing.

## Paths

What a condition says about a path `p.age` (D-65) is said in the same way, and holds for the same
reason: a value is bound once, so `p.age` is the same in every read of it. The subject of such a fact
is `{:path, "p", ["age"]}`, the name and the keys, and only a path that starts at a variable has one:
a key of a context literal or a name that something else binds can be shadowed (D-26) and a fact about
it would be about another value. `path_subject/1` finds it; the analyzer narrows the path nodes that
have one.

## Locals

A local, a parameter of a function or an entry of a context, is narrowed in the same way (D-193). It is told by the
depth of the context that binds it and its name, `{:local, id, name}`, and `{:local, meta, name}` has the `id` in its
`meta`: a local of the same name that is bound again deeper, in the branch of the condition, is another value and
takes no fact. The `id` is a number and not a reference, since the IR is hashed to name the module of the BEAM backend.

## The comparison

The value of a name is compared with an expression of a type. When the comparison is `true` the
value is of a kind that the type can be compared with, and in the set that `Feel.Analysis.TestSets`
gives: `x > 5` leaves the numbers above 5, and a string, a boolean, a list and `null` are gone, for
they give `null`. When it is not `true` the value is one that makes it `false` or `null`, which is
every value, except where the comparison is `true` for sure: `x != null` is `true` for every value that
is not `null`, so when it is not `true` the name is `null`, and `x = 5` is `true` only for 5 when the
other side is exactly 5, so the name is anything but 5. The sets of the values that make it `false`
are used only when `null` cannot be the outcome, which is when the other side is of one kind and not
`null` (`x > y` for a number `y` that is never `null`), and every value is left otherwise.

`null = x` is `false` for a value that is not `null` and `null != x` is `true`, so a name that can be
`null` stays `null` in the branch of `x = null` and goes from the other; that is the guard a decision
most often makes.

The strings of a type are a set (`Feel.Type.StringSet`, D-39), and what a comparison leaves of them is
a set too, and exactly: after `s < "b"` an `s` of `{"a", "b", "c"}` is `{"a"}`, and when it is not true
it is `{"b", "c"}`; after `s != "a"` a bare `string` is `string except "a"`, and so is it when `s = "a"`
is not true; against a set of strings it is some string of it. The sets are those of `TestSets`, and
the set of the name is cut by them, ties included (the run says `"a" <= "a "` is false). An other
side that is unknown has nothing to leave out, and does not narrow.

## Operations

A comparison of an arithmetic operation (`x + 1 > 5`, `n * 2 = 5`, `-n > 3`) is not a comparison of a name, but says
two things of the names it reads (D-194, D-122). When it is true the operation is not `null`, so its operands are not,
and they are of kinds that give a value of a kind the other side has (`x * 2 = 5` is not true of a duration, which a
number multiplies to a duration). And the number it gives is one that the comparison is true for (`> 5`: the numbers
above 5), so the numbers of its operands are those that give such a number with some number of the other operand
(`Feel.Analysis.Preimage`): `x + 1 > 5` leaves `x` above 4, `n * 2 = 5` leaves 2.5, and `n + m > 150` for `m` in
`[0..100]` leaves `n` above 50. An operand that is an operation is read in turn (`n * 2 + 1 > 51` leaves `n` above
25), and so are the tests of an `in` (`n + 1 in [2..5]`). It is read only in the branch where the comparison is true,
for the other is also taken when the operation is `null` for lack of range, a zero divisor, or a result too small to
write; and only if the other operand of the operation is a kind that the operation is read for, since a name of a
type that is not known can be anything.

A call of a number built-in is read in the same way (D-161, step C): `abs(x) < 100` leaves `x` in `(-100..100)`, `floor(x) = 3`
leaves `[3..4)`, `sqrt(x) < 10` leaves `[0..100)`, `round half up(x, 1) = 2.5` leaves `[2.45..2.55)`, and `exp(x) > 1` the
numbers above 0 (`Feel.Analysis.Images.Number.Preimage` has the numbers of each). The call is not `null` when the comparison
is true, so its arguments are numbers (a list of one is the number it has, and stays), and it is read in the branch where the
comparison is true only. A call that is an operand of an operation, or has an operation or another call as its argument, is
read in turn (`abs(x + 1) < 5`, `floor(abs(x)) = 3`). A scale of a rounding function has to be one number, or the call
says nothing of its argument.

A call of any other built-in function is read for the kinds of its arguments (D-170): when `string length(s) > 3`, `day of week(d) =
"Monday"` or `sum(xs) > 5` is true the call is not `null`, so each argument is one of the kinds for which the call gives a value
of the kind of the other side (`Feel.Analysis.Images` says which), and a `null` is one of them only for a call that takes one
(`is(x, null) = true`). The length of a string says more: `string length(s) > 0` is not true of the empty string, and
`string length(s) = 0` is true of nothing else.

The operation can be one on dates and durations (D-122): the images of its value are the days of a date, the months
of a years and months duration and the seconds of a days and time duration, and a comparison leaves the images of the
kind of the other side (`Feel.Analysis.Operands`). `d + u > @"2020-06-30"` leaves the dates from the 21st of June
for a `u` of at most ten days, `e + @"P1M" = @"2021-02-28"` the 28th to the 31st of January, and `n * u > @"P5D"` the
numbers above a half; a number times a duration is a duration, so it is read for the kind that the other side is.

## And and or

A conjunction is true when both are, so in `then` the facts of both hold (the narrowings follow each
other); it is not true when either is not, so in `else` a name is narrowed only if both sides narrow
it, to the alternatives that either leaves. A disjunction is the same the other way round.

    iex> {:ok, ast} = Feel.parse("x > 5")
    iex> {:ok, %{ir: condition}} = Feel.Analysis.Analyzer.analyze(ast, [])
    iex> %{"x" => fact} = Feel.Analysis.Narrowing.facts(condition, :then)
    iex> Feel.Analysis.Narrowing.narrow([:any, :null], fact) |> Feel.Type.Union.format()
    "number in (5..max]"
    iex> %{"x" => fact} = Feel.Analysis.Narrowing.facts(condition, :else)
    iex> Feel.Analysis.Narrowing.narrow([:number, :null], fact) |> Feel.Type.Union.format()
    "number in [-max..5] | Null"

# `branch`

```elixir
@type branch() :: :then | :else | false | :null
```

What a condition said, as the branch of a conditional is told: `:then` is where it was `true`, `:else` where it was not
(`false`, `null`, or a value that is no boolean, since `if` takes the else branch for them all, Table 50), and the
two outcomes that `:else` is made of: `:false` where it was `false`, and `:null` where it was `null` or no boolean
(D-194). `not(c)` is `true` where `c` is `false`, so it needs the facts of the outcomes of `c`.

# `fact`

```elixir
@type fact() :: (Feel.Type.Union.t() -&gt; [Feel.Type.Union.member()])
```

What is known about a name: the alternatives of its type that are left. It is empty when the fact
leaves none, which says that the branch cannot be taken.

# `facts`

```elixir
@type facts() :: %{optional(subject()) =&gt; fact()}
```

The facts about the names and the paths a condition reads, for a branch.

# `kinds`

```elixir
@type kinds() :: Feel.Analysis.Narrowing.Kinds.t()
```

The kinds of subject some facts are about (`:name` for a variable, and the tag of the others, as `t:subject/0` has them),
and the subjects that are paths from each name and each local (`Feel.Analysis.Narrowing.Kinds`).

# `memo_key`

```elixir
@type memo_key() :: nil | reference() | {module(), atom(), reference()}
```

A key of the process dictionary that `narrow_sources/3` keeps its answers under: `nil` keeps none.

# `memos`

```elixir
@type memos() :: (subject(), fact() -&gt;
              {[{Feel.Type.Union.t(), [Feel.Type.Union.member()]}],
               :answered | :applied})
```

What the caller knows of how a subject is read: given the subject and a fact about it that was made of others, the types the
fact will be applied to where it is read, each with what the fact leaves of it (`[]` if it does not know any), and whether
the reads are `:answered` by those or the fact has to be `:applied` to some type (one that it has no answer for). The fact
is made to answer those at once (`combine/5`).

# `origin`

```elixir
@type origin() ::
  {:node, Feel.IR.t(), cache :: nil | {memo_key(), reference()}}
  | {:facts, %{required(branch()) =&gt; facts()}, MapSet.t(subject())}
```

What a name that is bound to the value of a condition says of the names the condition read (D-194): the
node of the condition, whose facts are made where the name is read, once (`cache`: where they are kept, in the process, for
the time of the analysis), or, for a parameter whose argument was written in another place, the facts of each branch as they
were made there, with the subjects that the condition has in an operation.

# `origins`

```elixir
@type origins() :: (pos_integer(), Feel.Name.t() -&gt; origin() | nil)
```

The origin of the local of a depth and a name, or `nil` if it has none.

# `profiled`

```elixir
@type profiled() :: {facts(), kinds()}
```

Facts and the kinds of subject they are about, which `kinds/1` would say of them.

# `subject`

```elixir
@type subject() ::
  Feel.Name.t()
  | {:path, Feel.Name.t(), [Feel.Name.t(), ...]}
  | {:local, pos_integer(), Feel.Name.t()}
  | {:local_path, pos_integer(), Feel.Name.t(), [Feel.Name.t(), ...]}
  | {:item_path, pos_integer(), [Feel.Name.t()]}
  | {:item_entry, pos_integer(), Feel.Name.t(), Feel.Type.Union.t()}
```

What a fact is about: a variable, or the entry or property of one that a path selects (`p.age`, then
`{:path, "p", ["age"]}`); a local, which is told by the depth of the context that binds it and its name
(`{:local, 2, "a"}`, and `{:local_path, 2, "a", ["age"]}` for `a.age`, D-193); or, in the brackets of a filter, the
element of the filter of a depth or an entry of it (`{:item_path, 1, []}` for `item`, `{:item_path, 1, ["x"]}`
for `x` and for `item.x`).

# `call_subjects`

```elixir
@spec call_subjects(Feel.IR.t()) :: [subject()]
```

The subjects that the condition compares through a call of a number function that is a hull (`floor(x) = 2.5`, `exp(x) = 3`,
`round half up(x, 1) > 0.25`): the names, locals, paths and elements of a filter that are read inside such a call, on
either side of a comparison or in an operation or a call of `abs`, `sqrt` or `modulo` on a side of it (`floor(x) + 1 = 3.5`,
`abs(floor(x)) = 2.5`), as the value of a `between` or its bounds or as the subject of an `in`, and in the operands of an `and`
or an `or`. The rounding functions, `exp` and `log` are the hulls (`@hull_calls`).

It is where a fact says more of a name than the type of the condition does. The type of a comparison is decided from the sets of
its two sides (`Feel.Analysis.Inference.compare/3`), and the set of the result of such a call is a hull: `floor(x)` is
`number`, or an interval of the numbers, and `floor(x) = 2.5` is undecided for it, though no number gives a result that is not
whole. The fact is the numbers that give a result in the set the comparison leaves (`Feel.Analysis.Images.Number.Preimage`), and it
leaves none. The comparison of a name, of an operation on names, or of a call that gives an interval for an interval, is
decided by the sets as the facts are, and a fact there leaves nothing only where the type of the condition has said so already
(D-194 has the measure of that).

    iex> {:ok, ast} = Feel.parse("floor(x) = 2.5 and y > 1 and exp(z) < 3")
    iex> {:ok, %{ir: ir}} = Feel.Analysis.Analyzer.analyze(ast, [])
    iex> Feel.Analysis.Narrowing.call_subjects(ir)
    ["x", "z"]
    iex> {:ok, ast} = Feel.parse("abs(floor(x)) = 2.5 or x + 1 = 3 or abs(y) < 2 or count(s) = 1")
    iex> {:ok, %{ir: ir}} = Feel.Analysis.Analyzer.analyze(ast, [])
    iex> Feel.Analysis.Narrowing.call_subjects(ir)
    ["x"]

# `chain_facts`

```elixir
@spec chain_facts(Feel.IR.t(), facts(), [Feel.Name.t(), ...]) :: %{
  optional(pos_integer()) =&gt; fact()
}
```

The facts about the paths of a chain of selections `node.k1.k2…`, by the number of links they are at: for the keys
`["a", "b"]` of `p.a.b`, the fact about `p.a` is at the first link and the one about `p.a.b` at the second. A fact about
`p.x`, or about `p.a.c`, is at none.

The analyzer asks it once for a chain and gives each link the fact that is at it (and `path_sources/3` gives the source
the same, first). A source that has a condition on each of `n` different paths from it has `n` facts, and a chain is
asked of them without looking at all of them (D-154): one look-up for each link, in the facts. A chain that is longer
than there are facts to look at is the other way round, the facts are looked at (a look-up of each link makes the key of
each, which is `n` for the n-th).

    iex> {:ok, ast} = Feel.parse("p.age > 5")
    iex> {:ok, %{ir: {:compare, _, :gt, {:path, _, source, "age"}, _} = condition}} = Feel.Analysis.Analyzer.analyze(ast, [])
    iex> facts = Feel.Analysis.Narrowing.facts(condition, :then)
    iex> %{1 => fact} = Feel.Analysis.Narrowing.chain_facts(source, facts, ["age"])
    iex> Feel.Analysis.Narrowing.narrow([:any, :null], fact) |> Feel.Type.Union.format()
    "number in (5..max]"
    iex> Feel.Analysis.Narrowing.chain_facts(source, facts, ["name"])
    %{}

# `combine`

```elixir
@spec combine(:and | :or, branch(), profiled(), profiled(), memos()) ::
  {profiled(), [subject()]}
```

Returns the facts of `left and right` or `left or right` in a branch from the facts of the two operands in it, which
is what `facts/2` makes of the node. A caller that has the facts of the left operand already (the analyzer, in a chain
of `and` or of `or`, where it has been made from the one before) needs no walk of the operands to have them.

A conjunction is `true` when both are, so there the facts of both hold; it is not when either is not, so a name is
narrowed only if both narrow it. A disjunction is the other way round.

`memos` says, of a subject that a conjunction has two facts about, the types it is read at and what the fact of the two
leaves of each. The fact is then made to answer those types with that, and any other as it would have been narrowed without.
A chain of `n` conditions about a name is then `n` steps, where a read of the name in the n-th applied `n` facts (D-194).

    iex> fact = fn type -> Enum.reject(type, &(&1 == :null)) end
    iex> left = Feel.Analysis.Narrowing.profile(%{"x" => fact})
    iex> right = Feel.Analysis.Narrowing.profile(%{"y" => fact})
    iex> {{facts, kinds}, applied} = Feel.Analysis.Narrowing.combine(:and, :then, left, right)
    iex> {Map.keys(facts), Feel.Analysis.Narrowing.Kinds.to_list(kinds), applied}
    {["x", "y"], [:name], []}
    iex> {{facts, _kinds}, []} = Feel.Analysis.Narrowing.combine(:and, :else, left, right)
    iex> facts
    %{}

The subjects that two facts were made one of, and that `memos` did not answer (`:applied`), are given with the facts: a
read of one applies as many facts as it was made of, and the analyzer counts them (`max_narrowing_facts`, D-154).

# `facts`

```elixir
@spec facts(Feel.IR.t(), branch()) :: facts()
```

Returns what the resolved condition says about its names in a branch of the conditional: in the
`:then` branch the condition was `true`, and in the `:else` branch it was not. The two outcomes that `:else` is made
of are asked by `:false` (the condition was `false`) and `:null` (it was `null`, or a value that is no boolean).

Every set of facts is a superset of the states that have its outcome, so a shape that is not read says nothing, and
the facts of `:false` and `:null` are never narrower than the run has them. Where a shape has no rule of its own for
them they are the facts of `:else`, which holds both.

    iex> {:ok, ast} = Feel.parse("not(x = null)")
    iex> {:ok, %{ir: condition}} = Feel.Analysis.Analyzer.analyze(ast, [])
    iex> %{"x" => fact} = Feel.Analysis.Narrowing.facts(condition, :then)
    iex> Feel.Analysis.Narrowing.narrow([:number, :null], fact) |> Feel.Type.Union.format()
    "number"

# `facts`

```elixir
@spec facts(Feel.IR.t(), branch(), origins()) :: facts()
```

`facts/2` for a condition that reads names that are bound to conditions: `origins` says, for the local of a depth and a name,
what it is bound to. A name that was bound to `x > 5` and is read as a condition (`if ok`) says what `x > 5` does in the branch,
besides what it says of `ok`: `ok` is the value that `x > 5` had, and `x` is the value that it had.

    iex> {:ok, ast} = Feel.parse("{ok: x > 5, r: if ok then x else 0}")
    iex> {:ok, %{ir: {:context, _, entries}}} = Feel.Analysis.Analyzer.analyze(ast, [])
    iex> %{"ok" => ok, "r" => {:if, _, {:local, %{id: id}, "ok"} = read, _, _}} = Map.new(entries)
    iex> origins = fn ^id, "ok" -> Feel.Analysis.Narrowing.origin_of(ok, nil) end
    iex> %{"x" => fact} = Feel.Analysis.Narrowing.facts(read, :then, origins)
    iex> Feel.Analysis.Narrowing.narrow([:number, :null], fact) |> Feel.Type.Union.format()
    "number in (5..max]"

# `forget`

```elixir
@spec forget(memo_key()) :: :ok
```

Takes away what `origin_of/2` kept under the key.

# `item_subject`

```elixir
@spec item_subject(Feel.IR.t()) :: {:ok, subject()} | :error
```

The subject of the element of a filter, or of an entry of it, that a name in the brackets reads
(`{:item_path, depth, keys}`, with no keys for the element itself). Anything else has none.

    iex> Feel.Analysis.Narrowing.item_subject({:item, %{span: {0, 4}, type: [:number], depth: 1}})
    {:ok, {:item_path, 1, []}}
    iex> Feel.Analysis.Narrowing.item_subject({:item_field, %{span: {0, 1}, type: [:number], depth: 2}, "a"})
    {:ok, {:item_path, 2, ["a"]}}
    iex> Feel.Analysis.Narrowing.item_subject({:const, %{span: {0, 1}, type: [:null]}, nil})
    :error

# `kinds`

```elixir
@spec kinds(facts()) :: kinds()
```

The kinds of subject the facts are about, and the subjects that are paths from each name.

# `meet`

```elixir
@spec meet(facts(), facts()) :: facts()
```

Returns the facts that hold when two sets of facts both do: a name that both narrow is narrowed by
the first and then by the second.

    iex> fact = fn type -> Enum.reject(type, &(&1 == :null)) end
    iex> facts = Feel.Analysis.Narrowing.meet(%{"x" => fact}, %{"x" => fact, "y" => fact})
    iex> Map.keys(facts)
    ["x", "y"]

# `narrow`

```elixir
@spec narrow(Feel.Type.Union.t(), fact()) :: Feel.Type.Union.t()
```

Narrows the type of a name by a fact. A fact that leaves nothing says the branch cannot run, and
then any type will do, so the type is returned as it is (`narrowed/2` tells the two apart). So is a type that the fact
leaves as it was; the type is in normal form (`Feel.Type.Union`), as the types of the analysis are.

    iex> fact = fn type -> Enum.reject(type, &(&1 == :null)) end
    iex> Feel.Analysis.Narrowing.narrow([:number, :null], fact)
    [:number]
    iex> Feel.Analysis.Narrowing.narrow([:null], fact)
    [:null]

# `narrow_source`

```elixir
@spec narrow_source(Feel.Type.Union.t(), [Feel.Name.t(), ...], fact()) ::
  Feel.Type.Union.t()
```

Narrows the type of the source of a path by a fact about the path (D-194): a name `p` is read in a branch where the
condition said something of `p.age`, and `p` has to be a value that has that.

An alternative of the source stays if the selection from it can be what the fact leaves, and goes if it cannot: a
`Null` and a number select `Null`, so `p.age > 5` leaves neither. A context type that lists the entry has it narrowed;
one that does not list it gets it listed, when what the fact leaves is not `null`, for then the entry is there (an
entry that is not there is selected as `null`). `Any` is taken kind by kind where the fact leaves no `null`, since no
value of a function has an entry. A path of several keys narrows the entries in turn. Like `narrow/2` it returns
the type as it is when nothing is left, for the branch cannot run.

    iex> source = [{:context, %{"age" => [:number, :null]}}, :null]
    iex> {:ok, ast} = Feel.parse("p.age > 5")
    iex> {:ok, %{ir: condition}} = Feel.Analysis.Analyzer.analyze(ast, [])
    iex> %{{:path, "p", ["age"]} => fact} = Feel.Analysis.Narrowing.facts(condition, :then)
    iex> Feel.Analysis.Narrowing.narrow_source(source, ["age"], fact) |> Feel.Type.Union.format()
    "context<age: number in (5..max]>"

# `narrow_sources`

```elixir
@spec narrow_sources(
  Feel.Type.Union.t(),
  [{[Feel.Name.t(), ...], fact()}],
  memo_key()
) ::
  Feel.Type.Union.t()
```

Narrows the type of a source by the facts about several of its paths: `narrow_source/3` for each, in an order that keeps
the most (D-194). The facts are about the properties of a date, and a set of days is kept in at most 16 pieces: the
year leaves few, the month some more, and the day of the month and the weekday many, so `d.month = 2 and d.day = 29`
is the one day of the leap years, where the day first is a piece for each month and the one that holds them all. The
facts of the other paths follow, in the order they are given.

`memo` is a key under which the answers are kept in the process (`nil`, the default, keeps none): a fact is a function of
its argument, and the reads of a name in a chain of conditions are narrowed by nearly the same facts in the same order
(D-154). Whoever gives a key takes the answers away with `Process.delete/1` when it is done with them; the analyzer does,
at the end of `analyze/2`.

    iex> {:ok, ast} = Feel.parse("d.day = 29 and d.month = 2")
    iex> {:ok, %{ir: condition}} = Feel.Analysis.Analyzer.analyze(ast, [])
    iex> facts = Feel.Analysis.Narrowing.facts(condition, :then)
    iex> sources = for {{:path, "d", keys}, fact} <- facts, do: {keys, fact}
    iex> days = Feel.Type.NumberSet.interval(Decimal.new(Date.to_gregorian_days(~D[2019-01-01])), Decimal.new(Date.to_gregorian_days(~D[2022-12-31])))
    iex> Feel.Analysis.Narrowing.narrow_sources([{:date, days}], sources) |> Feel.Type.Union.format()
    "date = 2020-02-29"

# `narrowed`

```elixir
@spec narrowed(Feel.Type.Union.t(), fact()) :: {:ok, Feel.Type.Union.t()} | :none
```

The type that `narrow/2` gives, or `:none` when the fact leaves nothing of it: no value of the type is one that the
branch can have, so the place the type is read at cannot be reached (D-152).

    iex> fact = fn type -> Enum.reject(type, &(&1 == :null)) end
    iex> Feel.Analysis.Narrowing.narrowed([:number, :null], fact)
    {:ok, [:number]}
    iex> Feel.Analysis.Narrowing.narrowed([:null], fact)
    :none

# `operation_subjects`

```elixir
@spec operation_subjects(Feel.IR.t()) :: MapSet.t(subject())
```

The subjects that the condition has in an operation, or in an `instance of`: the names, locals, paths and elements of a filter
that are read inside an arithmetic operation, a negation, or the operand of an `instance of`. What a comparison of one says of them
is that the operation is not `null` (D-194), and for some sets of values it is (`1 / x` for the set `0`, `x ** 1000` for the very
large numbers) and for others it is not; what `x instance of T` says when it is not true depends on the type that `x` is as a whole.
Such a fact leaves a set as a whole, or none of it, and not the part of it that is in the set of the fact, which every other fact that
this module makes does. Where the analyzer answers a fact about a path from the part of a set that is in another, it does not
for a subject in an operation. A subject that is not in one has facts of the kind that it can, whatever the other
conditions of the chain are.

    iex> {:ok, ast} = Feel.parse("1 / x > 0 and y > 1")
    iex> {:ok, %{ir: ir}} = Feel.Analysis.Analyzer.analyze(ast, [])
    iex> Feel.Analysis.Narrowing.operation_subjects(ir)
    MapSet.new(["x"])
    iex> {:ok, ast} = Feel.parse("p.age + 1 > 0 and not(p.age instance of number) and y = 1")
    iex> {:ok, %{ir: ir}} = Feel.Analysis.Analyzer.analyze(ast, [])
    iex> Feel.Analysis.Narrowing.operation_subjects(ir) |> Enum.sort()
    [{:path, "p", ["age"]}, "p"]

# `operation_subjects`

```elixir
@spec operation_subjects(Feel.IR.t(), origins()) :: MapSet.t(subject())
```

`operation_subjects/1` for a condition that reads names bound to conditions (`facts/3`): the subjects that those conditions have
in an operation are among them, since what they say of the subject is what the facts of the condition say.

# `origin_of`

```elixir
@spec origin_of(Feel.IR.t(), memo_key()) :: origin() | nil
```

The origin a name has that is bound to the value of `node`: the node itself, if it is a condition or the name of a value
that says something when it is `true`, and `nil` for anything else (a number, a call, a list), which says nothing.

The facts of the node are made when the name is read as a condition, and are kept under `memo` (a key of the process, which
whoever gives it takes away with `forget/1` when it is done with it; `nil` keeps nothing): a name that is bound to a condition
that reads a name that is bound to one (`{a1: x > 1, a2: a1 and x > 2, …}`) would otherwise make the facts of the chain again
at each read, and apply them one after the other.

# `path_reads`

```elixir
@spec path_reads(Feel.Type.Union.t(), [Feel.Name.t(), ...], fact(), boolean()) ::
  {[{Feel.Type.Union.t(), [Feel.Type.Union.member()]}], :answered | :applied}
```

The types that a fact about the path `keys` from a source of the type `root` is applied to when the path is read, with what
the fact leaves of each: `narrow_sources/2` applies it to the entry of each alternative of the source, and the path
node to the type that is selected from the source it leaves. The first are found by doing it, with a fact that notes what it
is given, and so is the type that is selected from the source as it was, which is the same at every term of a chain.

The type selected from the source that is left is the one that changes with each term, and it is answered without applying the
fact to it (D-194), in the cases that follow, and in no other:

  * it is what is selected from the source as it was;
  * it is within that, and it and what the fact leaves of that have only sets in them (numbers, strings, booleans, the
    amounts and days of a duration or a date) and `Null`: a fact that leaves of a set what is in a set of its own leaves
    of a part of it the part that is in that set (`restrict/3`), so it leaves of it what it leaves of that, in it;
  * every alternative of the source that is left is `Null` or a context that has the entry, so what is selected is what the
    fact left of the entries, and a fact applied to what it left of a type leaves that.

The fact is applied to it as to any other type if none of them is so, and when `claims?` is `false`: a condition on an operation
(`1 / x > 0`) leaves of a set what depends on the set, as a whole, and not the part of it that is in another (`operation_subjects/1`).
That is the second thing that is given with the reads, `:applied`; they are `:answered` when the fact was not applied to anything that
changes with each term.

    iex> fact = fn type -> Enum.reject(type, &(&1 == :null)) end
    iex> root = [{:context, %{"age" => [:number, :null]}}, :null]
    iex> {reads, :answered} = Feel.Analysis.Narrowing.path_reads(root, ["age"], fact)
    iex> for {input, output} <- reads, do: {Feel.Type.Union.format(input), Feel.Type.Union.format(output)}
    [{"number | Null", "number"}, {"Null", "Never"}, {"number", "number"}]

# `path_sources`

```elixir
@spec path_sources(Feel.IR.t(), facts(), [Feel.Name.t(), ...] | nil, kinds() | nil) ::
  [
    {[Feel.Name.t(), ...], fact()}
  ]
```

The facts that narrow a name or a local that is read, as `{keys, fact}`: the ones about the paths from it. For a read that is the
source of a chain of selections `p.a.b` (`keys` given), the facts at the links of the chain come first, and the others that
are about a path from `p`, up to `16` of them, follow. For any other read (`keys` is `nil`) it is the others.

The others are taken from the facts as they come, until there are enough, so a name with few facts has them all (and is
narrowed by all of them, as it is by all there are), and a name with many has some of them.

With the kinds of the facts (`kinds/1`, or the ones a profile has) a name that no fact is about a path from is read
without a look at the facts, and a name that some are about is read with a look at those: the work of a read is that of
the facts about the name, and not of all the facts, which in a chain of a condition on a path from a different name each
was a look at the facts of every name before it, for each read. The others are the same facts, in the same order, as
without the kinds.

    iex> {:ok, ast} = Feel.parse("p.age > 5 and p.size < 3")
    iex> {:ok, %{ir: {:logic, _, :and, _, _} = ir}} = Feel.Analysis.Analyzer.analyze(ast, [])
    iex> {:logic, _, :and, left, right} = ir
    iex> facts = Feel.Analysis.Narrowing.facts(left, :then)
    iex> {:compare, _, :lt, {:path, _, source, "size"}, _} = right
    iex> [{["age"], _fact}] = Feel.Analysis.Narrowing.path_sources(source, facts, ["size"])
    iex> Feel.Analysis.Narrowing.path_sources(source, facts, nil) |> length()
    1
    iex> kinds = Feel.Analysis.Narrowing.kinds(facts)
    iex> [{["age"], _fact}] = Feel.Analysis.Narrowing.path_sources(source, facts, ["size"], kinds)
    iex> Feel.Analysis.Narrowing.path_sources({:var, %{}, "q"}, facts, nil, kinds)
    []

# `path_subject`

```elixir
@spec path_subject(Feel.IR.t()) :: {:ok, subject()} | :error
```

The subject of a path that starts at a variable: `{:path, name, keys}`. A path that starts at
anything else has none.

    iex> {:ok, ast} = Feel.parse("p.address.city")
    iex> {:ok, %{ir: ir}} = Feel.Analysis.Analyzer.analyze(ast, [])
    iex> Feel.Analysis.Narrowing.path_subject(ir)
    {:ok, {:path, "p", ["address", "city"]}}
    iex> {:ok, ast} = Feel.parse("{p: 1}.p")
    iex> {:ok, %{ir: ir}} = Feel.Analysis.Analyzer.analyze(ast, [])
    iex> Feel.Analysis.Narrowing.path_subject(ir)
    :error

# `profile`

```elixir
@spec profile(facts()) :: profiled()
```

The facts with the kinds of subject they are about. The analyzer asks of the facts around a name whether any is about a
path from it, and of the facts a condition leaves whether the right operand reads any: a look at all of them each time,
which in a chain of a thousand conditions is a million looks. The kinds of the facts of a chain are made from those of
the part of it before (`combine/5`).

    iex> fact = fn type -> type end
    iex> {_facts, kinds} = Feel.Analysis.Narrowing.profile(%{"x" => fact, {:path, "p", ["age"]} => fact})
    iex> Feel.Analysis.Narrowing.Kinds.to_list(kinds)
    [:name, :path]

# `reads_a_fact?`

```elixir
@spec reads_a_fact?(profiled(), MapSet.t(Feel.Name.t())) :: boolean()
```

Whether a read of any of the names is narrowed by the facts: one of them is about a name, or about a path from it. A
fact about a local or an element of a filter is taken as read, since those are not names.

It looks at the facts or at the names, whichever there are fewer of, and in the names at a look-up in the facts and in the
kinds: the right operand of a chain of conditions reads a few names, and the facts around it are of all that went before.

    iex> fact = fn type -> type end
    iex> profiled = Feel.Analysis.Narrowing.profile(%{{:path, "p", ["age"]} => fact})
    iex> Feel.Analysis.Narrowing.reads_a_fact?(profiled, MapSet.new(["p", "q"]))
    true
    iex> Feel.Analysis.Narrowing.reads_a_fact?(profiled, MapSet.new(["q"]))
    false

# `restrict_by_test`

```elixir
@spec restrict_by_test(Feel.Type.Union.t(), Feel.IR.t(), branch()) ::
  Feel.Type.Union.t()
```

The values of a type that one unary test accepts, read as the condition of the input `?` that it is (D-161). A constraint of
DMN, an `allowedValues` or a `typeConstraint`, is a unary test, so it says what a condition says: the same facts are read,
with the operations (`? * 2 < 100`), the properties of a date, a duration and a time (`?.year >= 2020`), the tests of an `in`
and the rest of what narrows a name.

The type is that of the input, which has no `null` (the tests do not run for it), and the result has none. `:then` is the
values for which the test is `true`, `:else` those for which it is not `true` (`false` or `null`), a superset of where it is
`false`, which `not(...)` needs. A test that says nothing of `?` accepts every value, and the type is returned as it is. One
that no value passes gives `[]`.

    iex> {:ok, ast} = Feel.parse("? * 2 < 100", entry: :unary_tests)
    iex> {:ok, %{ir: {:unary_tests, _, _, [test]}}} = Feel.Analysis.Analyzer.analyze(ast, input: :number, declarations: :ignore)
    iex> Feel.Analysis.Narrowing.restrict_by_test([:number], test, :then) |> Feel.Type.Union.format()
    "number in [-max..50)"

---

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