Feel.Type.NumberSet (feel v0.1.0)

Copy Markdown View Source

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 Decimals 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.

Summary

Types

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

t()

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

Functions

Every number there is.

Returns true for the set of every number.

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.

Returns true when the number is in the set.

Returns true for a set with no number in it.

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.

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

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

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.

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

The largest number of decimal128: 9.999999999999999999999999999999999E+6144.

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).

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.

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

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

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

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

Types

bound()

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

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

interval()

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

t()

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

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

Functions

all()

@spec all() :: t()

Every number there is.

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

all?(set)

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

Returns true for the set of every number.

complement(set)

@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?(set, number)

@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?(set)

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

Returns true for a set with no number in it.

format(set, text \\ &number_text/1)

@spec format(t(), (Decimal.t() -> 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(a, b, approximation \\ :outer)

@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(lower, upper)

@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(set)

@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(list)

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

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

max_value()

@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(intervals, approximation \\ :outer)

@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(number)

@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(arg1)

@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?(a, b)

@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(a, b, approximation \\ :outer)

@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(set)

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

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