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

What an operation does to a set of numbers (D-39).

Given the sets of numbers that the operands can be (`Feel.Type.NumberSet`), each function
gives the set of numbers the result can be, and whether the result can be `null` without an
operand being so: an overflow past decimal128, a division by zero, and a result that is too
small to write. The runtime (`Feel.Runtime.Number`) computes the ends of the intervals, so the
rounding to 34 digits is the run's own; rounding keeps the order of numbers, so the end of an
interval is the end of what the run can give. The ends of a result are always included, which
can only make a set larger.

**Too small to write (D-161).** The run gives `null` for a product or a quotient that is not zero
and whose value needs a last digit below 1E-6176 (D-59): `37.5 / 9.99E+6144`, and, for the
smallest numbers there are, `1E-6176 / 2`. The ends of an interval do not show that for a number
inside one, so it is counted from the sizes. A result of 1E-6143 or more is written with its 34
digits from the first one, and they reach down to -6176, so only a smaller result can be too
small; and it can be unless an operand is always a whole number (a multiple of 1E-6176 times a
whole number is one again), or the divisor is 1 or -1. So `x / 2` is possibly `null` for every `x`
in `[0..150]`, 1E-6176 being one of them, and `x * 2` is not. The allowed values of `x` decide:
`[0.001..150]` rules it out. Where both sets are single numbers the ends are exact, and nothing
 is added.

An overflow does not leave the set: a result outside the range of decimal128 is `null` (the run
also says so for one too small to write, D-59), so the set keeps the numbers inside
the range, up to its ends and down to zero, and the second part of the result says that `null`
is possible.

    iex> alias Feel.Type.NumberSet
    iex> a = NumberSet.interval(Decimal.new(0), Decimal.new(150))
    iex> {set, null?} = Feel.Analysis.Intervals.add(a, NumberSet.point(Decimal.new(1)))
    iex> {NumberSet.format(set), null?}
    {"[1..151]", false}

# `decision`

```elixir
@type decision() :: :always | :never | :unknown
```

What a comparison of two sets of numbers is, for every pair of numbers.

# `options`

```elixir
@type options() :: [whole: boolean(), grains: {integer() | nil, integer() | nil}]
```

`whole: true` says that both operands are whole numbers, such as months. `grains: {a, b}` says how fine the numbers of each
operand are (`Feel.Analysis.Grain`, D-161): a multiple of 10^-a, and of 10^-b; `nil` for none that is known.

# `result`

```elixir
@type result() :: {Feel.Type.NumberSet.t(), boolean()}
```

The numbers of a result, and whether it can be `null` because of the operation.

# `add`

```elixir
@spec add(Feel.Type.NumberSet.t(), Feel.Type.NumberSet.t()) :: result()
```

The numbers `a + b` can be.

# `decide`

```elixir
@spec decide(atom(), Feel.Type.NumberSet.t(), Feel.Type.NumberSet.t()) :: decision()
```

Whether `a op b` is true for every pair of numbers of the two sets (`:always`), for none
(`:never`), or for some (`:unknown`). `op` is one of `:eq`, `:neq`, `:lt`, `:lte`, `:gt`,
`:gte`. Neither set may be empty.

    iex> alias Feel.Type.NumberSet
    iex> low = NumberSet.interval(Decimal.new(0), Decimal.new(150))
    iex> high = NumberSet.point(Decimal.new(200))
    iex> {Feel.Analysis.Intervals.decide(:gt, low, high), Feel.Analysis.Intervals.decide(:lt, low, high)}
    {:never, :always}
    iex> Feel.Analysis.Intervals.decide(:eq, low, NumberSet.point(Decimal.new(5)))
    :unknown

# `divide`

```elixir
@spec divide(Feel.Type.NumberSet.t(), Feel.Type.NumberSet.t(), options()) :: result()
```

The numbers `a / b` can be. The result can be `null` when `b` has zero in it, when the
quotient can pass the range of decimal128, and when it can be too small to write (D-161).

    iex> alias Feel.Type.NumberSet
    iex> a = NumberSet.interval(Decimal.new(10), Decimal.new(20))
    iex> b = NumberSet.interval(Decimal.new(2), Decimal.new(5))
    iex> {set, null?} = Feel.Analysis.Intervals.divide(a, b)
    iex> {NumberSet.format(set), null?}
    {"[2..10]", false}
    iex> {set, null?} = Feel.Analysis.Intervals.divide(a, NumberSet.point(Decimal.new(0)))
    iex> {set, null?}
    {[], true}
    iex> c = NumberSet.interval(Decimal.new(0), Decimal.new(150))
    iex> {_set, null?} = Feel.Analysis.Intervals.divide(c, NumberSet.point(Decimal.new(2)))
    iex> null?
    true

# `multiply`

```elixir
@spec multiply(Feel.Type.NumberSet.t(), Feel.Type.NumberSet.t(), options()) ::
  result()
```

The numbers `a * b` can be. The result can be `null` when it can pass the range of decimal128,
and when it can be too small to write (D-161).

    iex> alias Feel.Type.NumberSet
    iex> a = NumberSet.interval(Decimal.new(0), Decimal.new(150))
    iex> {set, null?} = Feel.Analysis.Intervals.multiply(a, NumberSet.point(Decimal.new(2)))
    iex> {NumberSet.format(set), null?}
    {"[0..300]", false}
    iex> {_set, null?} = Feel.Analysis.Intervals.multiply(a, NumberSet.point(Decimal.new("0.5")))
    iex> null?
    true

# `negate`

```elixir
@spec negate(Feel.Type.NumberSet.t()) :: Feel.Type.NumberSet.t()
```

The numbers `-a` can be. An overflow is not possible, and the ends keep whether they are
included.

    iex> alias Feel.Type.NumberSet
    iex> Feel.Analysis.Intervals.negate(NumberSet.interval({Decimal.new(0), :open}, Decimal.new(5))) |> NumberSet.format()
    "[-5..0)"

# `of_one`

```elixir
@spec of_one(
  :add | :subtract | :multiply | :divide,
  Feel.Type.NumberSet.t(),
  options()
) :: result()
```

The numbers `x op x` can be for one number `x` of `a` that is read twice (D-170): the two operands are one value, so the
pairs of two different numbers of `a` that `add/2` and the rest work out are not there. `x - x` is `0`, `x / x` is `1` (and
`null` for the `x` that is `0`), `x * x` is never below zero, and `x + x` is `2x`, which is not the sum of two numbers of
a set that has a gap in it. The second part says, as for the others, whether the result can be `null` because of the
operation: an overflow, a result too small to write, a zero divisor. The set may not be empty.

    iex> alias Feel.Type.NumberSet
    iex> a = NumberSet.interval(Decimal.new(-5), Decimal.new(3))
    iex> {set, null?} = Feel.Analysis.Intervals.of_one(:multiply, a, [])
    iex> {NumberSet.format(set), null?}
    {"[0..25]", true}
    iex> {set, null?} = Feel.Analysis.Intervals.of_one(:subtract, a, [])
    iex> {NumberSet.format(set), null?}
    {"0", false}
    iex> {set, null?} = Feel.Analysis.Intervals.of_one(:divide, a, [])
    iex> {NumberSet.format(set), null?}
    {"1", true}

# `power`

```elixir
@spec power(Feel.Type.NumberSet.t(), Feel.Type.NumberSet.t()) :: result()
```

The numbers `a ** b` can be, and whether the result can be `null` without an operand being so (D-161).

A power is `null` when the run has no value for it or no room for it: a zero to a negative exponent
(a division by zero), a negative number to an exponent that is not a whole number (no real value), a
result past the range of decimal128, and one that is too small to write (D-59), which is not zero and
needs a last digit below 1E-6176. The last two are counted in both directions, so `x ** 2` can be `null`
for an `x` in `[0..150]` (1E-6176 squared is far below the smallest number) and not for one in
`[0.001..150]`.

For a positive base `x ** y` is `exp(y * ln x)`, which is linear in each of `ln x` and `y`, so the least
and the greatest result for a pair of intervals are at their corners, and the run's own `Number.power/2`
gives them. A base that only gets near zero is at least 1E-6176, the smallest number there is. A negative
base has a value for a whole exponent only, and then it is the size of the positive one with the sign of
the parity of the exponent. A zero base is 0 for a positive exponent and 1 for the exponent 0, and any base
to the exponent 0 is 1. A result that overflows stands for the end of the range when it is too large and
for zero when it is too small, as for a product (D-161). Where the intervals are not single numbers the
corners do not show a number that is too small to write inside them, so the result can be `null` when its
smallest corner is below the size that is always written.

    iex> alias Feel.Type.NumberSet
    iex> five_to_ten = NumberSet.interval(Decimal.new(5), Decimal.new(10))
    iex> {set, null?} = Feel.Analysis.Intervals.power(NumberSet.point(Decimal.new(0)), five_to_ten)
    iex> {NumberSet.format(set), null?}
    {"0", false}
    iex> {set, null?} = Feel.Analysis.Intervals.power(five_to_ten, NumberSet.point(Decimal.new(2)))
    iex> {NumberSet.format(set), null?}
    {"[25..100]", false}
    iex> {set, null?} = Feel.Analysis.Intervals.power(NumberSet.point(Decimal.new(2)), five_to_ten)
    iex> {NumberSet.format(set), null?}
    {"[32..1024]", false}
    iex> to_150 = NumberSet.interval(Decimal.new(0), Decimal.new(150))
    iex> {set, null?} = Feel.Analysis.Intervals.power(to_150, NumberSet.point(Decimal.new(2)))
    iex> {NumberSet.format(set), null?}
    {"[0..22500]", true}
    iex> {set, null?} = Feel.Analysis.Intervals.power(NumberSet.point(Decimal.new(0)), NumberSet.interval(Decimal.new(-1), Decimal.new(1)))
    iex> {NumberSet.format(set), null?}
    {"0, 1", true}

# `subtract`

```elixir
@spec subtract(Feel.Type.NumberSet.t(), Feel.Type.NumberSet.t()) :: result()
```

The numbers `a - b` can be.

---

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