# `Feel.Type.NumberSet`
[🔗](https://github.com/FeeJai/BDM/blob/feel-v0.1.0/apps/feel/lib/feel/type/number_set.ex#L1)

A set of numbers, as intervals (D-39).

A set of numbers is a list of intervals that do not touch, in order, each from a lower bound
to an upper bound. A bound is a number and says whether it is in the interval, `:closed`, or
not, `:open`: `[0..150]` is `{{0, :closed}, {150, :closed}}`, and `(0..5]` is
`{{0, :open}, {5, :closed}}`. The numbers are the `Decimal`s of FEEL, and the sets lie within
the range of decimal128, from `-max_value/0` to `max_value/0`, since there are no others.
`all/0` is that range, and an empty set is `[]`.

`Feel.Type.Union` holds one of these on a number alternative, as `{:number, set}`, to say which
numbers a value can be, and on a date or a duration alternative to say which values it can be, by
the numbers that stand for them (D-109, `Feel.Type.Image`). The functions here are the algebra of the sets: union, intersection,
complement, and the tests `contains?/2` and `subset?/2`. The rules for what an operation
does to a set are in `Feel.Analysis.Intervals`.

A set is built with `new/1`, which puts the intervals in order, merges those that overlap or
touch (`[0..5)` and `[5..9]` are `[0..9]`, but `[0..5)` and `(5..9]` are not, since 5 is in
neither), and drops the empty ones. The default `:outer` approximation replaces more than 16
intervals by their hull, suitable for possible-value upper bounds. `:inner` keeps at most 16
intervals without filling gaps, suitable for certainly-accepted lower bounds. `:exact` keeps
every interval. Complement and inclusion are exact; they must not complement a widened gap set.

# `bound`

```elixir
@type bound() :: {Decimal.t(), :closed | :open}
```

A bound of an interval: a number, and whether the interval includes it.

# `interval`

```elixir
@type interval() :: {bound(), bound()}
```

# `t`

```elixir
@type t() :: [interval()]
```

Intervals in order that do not touch, each with a number in it.

# `all`

```elixir
@spec all() :: t()
```

Every number there is.

    iex> Feel.Type.NumberSet.all?(Feel.Type.NumberSet.all())
    true

# `all?`

```elixir
@spec all?(t()) :: boolean()
```

Returns `true` for the set of every number.

# `complement`

```elixir
@spec complement(t()) :: t()
```

The numbers that are in the range of decimal128 and not in the set, without approximation.
The complement of 16 separated interior intervals needs 17 intervals and retains all of them.

    iex> Feel.Type.NumberSet.point(Decimal.new(0)) |> Feel.Type.NumberSet.complement() |> Feel.Type.NumberSet.format()
    "[-max..0), (0..max]"

# `contains?`

```elixir
@spec contains?(t(), Decimal.t()) :: boolean()
```

Returns `true` when the number is in the set.

    iex> set = Feel.Type.NumberSet.interval({Decimal.new(0), :open}, Decimal.new(5))
    iex> {Feel.Type.NumberSet.contains?(set, Decimal.new(5)), Feel.Type.NumberSet.contains?(set, Decimal.new(0))}
    {true, false}

# `empty?`

```elixir
@spec empty?(t()) :: boolean()
```

Returns `true` for a set with no number in it.

# `format`

```elixir
@spec format(t(), (Decimal.t() -&gt; String.t())) :: String.t()
```

Writes a set: `[0..150]` for an interval, with `(` and `)` for a bound that is left out,
`3` for one number, and `-max` and `max` for the ends of the range of decimal128. Several
intervals are separated by commas.

A set can stand for something other than numbers (the days of a date, the seconds of a
duration; see `Feel.Type.Image`). `text` then writes each bound, and gets every number
but the ends of the range of decimal128, which are `-max` and `max` as above.

    iex> Feel.Type.NumberSet.format(Feel.Type.NumberSet.all())
    "[-max..max]"
    iex> set = Feel.Type.NumberSet.interval(Decimal.new(1), Decimal.new(3))
    iex> Feel.Type.NumberSet.format(set, &("<" <> Decimal.to_string(&1) <> ">"))
    "[<1>..<3>]"

The default writes a number in full, with its scale, unless its first digit is more than 34 places
from the point: `1E-6176` is not a 0, a point and 6,175 more zeros.

    iex> Feel.Type.NumberSet.format(Feel.Type.NumberSet.point(Decimal.new(1, 1, -6176)))
    "1E-6176"
    iex> set = Feel.Type.NumberSet.interval(Decimal.new(1), Decimal.new(1, 5, 6143))
    iex> Feel.Type.NumberSet.format(set)
    "[1..5E+6143]"
    iex> Feel.Type.NumberSet.format(Feel.Type.NumberSet.point(Decimal.new(1, 1, -34)))
    "0.0000000000000000000000000000000001"

# `intersect`

```elixir
@spec intersect(t(), t(), :outer | :inner | :exact) :: t()
```

The numbers that are in both sets, with the approximation policy of `new/2`.

    iex> a = Feel.Type.NumberSet.interval(Decimal.new(0), Decimal.new(10))
    iex> b = Feel.Type.NumberSet.interval({Decimal.new(5), :open}, Decimal.new(20))
    iex> a |> Feel.Type.NumberSet.intersect(b) |> Feel.Type.NumberSet.format()
    "(5..10]"

# `interval`

```elixir
@spec interval(Decimal.t() | bound(), Decimal.t() | bound()) :: t()
```

The interval from `lower` to `upper`: each is a number, or `{number, :open}` for a number
that is not in it.

    iex> Feel.Type.NumberSet.interval(Decimal.new(0), Decimal.new(150)) |> Feel.Type.NumberSet.format()
    "[0..150]"
    iex> Feel.Type.NumberSet.interval({Decimal.new(0), :open}, Decimal.new(5)) |> Feel.Type.NumberSet.format()
    "(0..5]"

# `join_whole_neighbours`

```elixir
@spec join_whole_neighbours(t()) :: t()
```

The set with the intervals that meet at whole numbers made one, for the numbers that are whole: `[1..3]` and `[4..6]` hold the
same whole numbers as `[1..6]`, and the days of two dates that follow one another are such (a date is the number of its
day, `Feel.Type.Image`). It is not for a set of numbers that may have a fraction, which the two do not hold the same of.

    iex> a = Feel.Type.NumberSet.interval(Decimal.new(1), Decimal.new(3))
    iex> b = Feel.Type.NumberSet.interval(Decimal.new(4), Decimal.new(6))
    iex> a |> Feel.Type.NumberSet.union(b) |> Feel.Type.NumberSet.join_whole_neighbours() |> Feel.Type.NumberSet.format()
    "[1..6]"
    iex> c = Feel.Type.NumberSet.interval(Decimal.new(8), Decimal.new(9))
    iex> a |> Feel.Type.NumberSet.union(c) |> Feel.Type.NumberSet.join_whole_neighbours() |> Feel.Type.NumberSet.format()
    "[1..3], [8..9]"

# `lower`

```elixir
@spec lower(t()) :: bound() | nil
```

The lowest bound of the set, or `nil` for an empty set.

# `max_value`

```elixir
@spec max_value() :: Decimal.t()
```

The largest number of decimal128: 9.999999999999999999999999999999999E+6144.

    iex> Decimal.to_string(Feel.Type.NumberSet.max_value(), :scientific)
    "9.999999999999999999999999999999999E+6144"

# `new`

```elixir
@spec new([interval()], :outer | :inner | :exact) :: t()
```

Builds a set from intervals, in any order and form: they are cut to the range of decimal128,
the empty ones dropped, and those that overlap or touch merged. The optional approximation
is `:outer` (the default, at most 16 intervals enlarged to their hull), `:inner` (at most 16
intervals retained as a subset), or `:exact` (no interval cap).

    iex> intervals = [
    ...>   {{Decimal.new(5), :closed}, {Decimal.new(9), :closed}},
    ...>   {{Decimal.new(0), :closed}, {Decimal.new(5), :open}}
    ...> ]
    iex> intervals |> Feel.Type.NumberSet.new() |> Feel.Type.NumberSet.format()
    "[0..9]"

# `point`

```elixir
@spec point(Decimal.t()) :: t()
```

A set of one number. A number outside the range of decimal128 is not a number of FEEL, and
gives every number, since nothing is known about it.

    iex> Feel.Type.NumberSet.point(Decimal.new(3)) |> Feel.Type.NumberSet.format()
    "3"

# `single`

```elixir
@spec single(t()) :: {:ok, Decimal.t()} | :error
```

Returns the number of a set of one number, or `:error`.

    iex> Feel.Type.NumberSet.single(Feel.Type.NumberSet.point(Decimal.new(3)))
    {:ok, Decimal.new(3)}
    iex> Feel.Type.NumberSet.single(Feel.Type.NumberSet.all())
    :error

# `subset?`

```elixir
@spec subset?(t(), t()) :: boolean()
```

Returns `true` when every number of the first set is in the second.

    iex> inner = Feel.Type.NumberSet.interval(Decimal.new(1), Decimal.new(2))
    iex> outer = Feel.Type.NumberSet.interval(Decimal.new(0), Decimal.new(5))
    iex> {Feel.Type.NumberSet.subset?(inner, outer), Feel.Type.NumberSet.subset?(outer, inner)}
    {true, false}

# `union`

```elixir
@spec union(t(), t(), :outer | :inner | :exact) :: t()
```

The numbers that are in either set, with the approximation policy of `new/2`.

    iex> a = Feel.Type.NumberSet.interval(Decimal.new(0), Decimal.new(5))
    iex> b = Feel.Type.NumberSet.interval(Decimal.new(10), Decimal.new(20))
    iex> a |> Feel.Type.NumberSet.union(b) |> Feel.Type.NumberSet.format()
    "[0..5], [10..20]"

# `upper`

```elixir
@spec upper(t()) :: bound() | nil
```

The highest bound of the set, or `nil` for an empty set.

---

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