Ash.Range (ash v3.35.0)

Copy Markdown View Source

A range of values: a lower bound, an upper bound, and whether each is inclusive.

Ash.Type.Range casts to and from it. In bounds, [ or ] is inclusive and ( or ) exclusive. The default, :"[)", is inclusive-exclusive. A nil bound is unbounded.

A range holding no values is empty: see empty/0.

PostgreSQL

Ash.Range follows PostgreSQL's ranges: the same bounds, every empty range cast to one, every range holding the empty range, and the same sort order (compare/2).

the questionPostgreSQLAsh.Range
do they share a value?&&, "overlaps"intersects?/2
does one hold a value, or the other range?@>, "contains"contains?/2

| does one meet the other, either way round? | -|- | adjacent?/2 | | which of Allen's relations holds? | — | relation/2 |

PostgreSQL's overlaps and contains are wider than Allen's: [1, 10) intersects and contains [3, 5), and their relation is :contains.

Summary

Types

One of Allen's thirteen interval relations.

t()

Functions

Whether one range meets the other, in either order: [1, 5) and [5, 9) are adjacent, [1, 5] and [5, 9) share 5, and [1, 5) and (5, 9) leave it out. An empty range is adjacent to nothing.

Compares two ranges: empty first, then by lower bound, then by upper bound. Unbounded below sorts first, and unbounded above last. Of two bounds on the same value, [1 sorts before (1, and 5) before 5].

Whether the range holds value, which may be a value or another range.

The empty range, holding no values. A range such as [1, 1) holds none, and Ash.Type.Range casts it to this.

Whether the range holds no values: empty/0, or bounds holding nothing, such as [3, 1) or [1, 1). A range unbounded at either end is never empty.

Whether two ranges share a value: [1, 3) and [3, 5) do not, [1, 3] and [3, 5) do. An empty range intersects nothing.

Whether bounds makes the lower bound inclusive.

Which of Allen's thirteen relations left has to right, or nil if either is empty. Exactly one holds for two non-empty ranges.

The thirteen relations relation/2 returns, in Allen's order: from left furthest before right to furthest after. Their positions can index a thirteen-bit mask.

Whether bounds makes the upper bound inclusive.

Whether bounds is one of :"[)", :"[]", :"()" or :"(]".

Types

allen()

@type allen() ::
  :precedes
  | :meets
  | :overlaps
  | :finished_by
  | :contains
  | :starts
  | :equals
  | :started_by
  | :during
  | :finishes
  | :overlapped_by
  | :met_by
  | :preceded_by

One of Allen's thirteen interval relations.

bounds()

@type bounds() :: :"[)" | :"[]" | :"()" | :"(]"

t()

@type t() :: %Ash.Range{
  bounds: bounds(),
  empty?: boolean(),
  lower: term() | nil,
  upper: term() | nil
}

Functions

adjacent?(left, right)

@spec adjacent?(t(), t()) :: boolean()

Whether one range meets the other, in either order: [1, 5) and [5, 9) are adjacent, [1, 5] and [5, 9) share 5, and [1, 5) and (5, 9) leave it out. An empty range is adjacent to nothing.

Consecutive ranges, each meeting the next, hold every value from the first lower bound to the last upper bound once.

Compare ranges cast by Ash.Type.Range: as integers, [1, 4] and [5, 9) are adjacent, since [1, 4] is cast to [1, 5).

compare(left, right)

@spec compare(t(), t()) :: :lt | :eq | :gt

Compares two ranges: empty first, then by lower bound, then by upper bound. Unbounded below sorts first, and unbounded above last. Of two bounds on the same value, [1 sorts before (1, and 5) before 5].

A sort order: [1, 10) sorts before [3, 5) though it holds it.

contains?(range, value)

@spec contains?(t(), term()) :: boolean()

Whether the range holds value, which may be a value or another range.

A range holds another range lying within it, and every range holds the empty range.

empty()

@spec empty() :: t()

The empty range, holding no values. A range such as [1, 1) holds none, and Ash.Type.Range casts it to this.

iex> Ash.Range.empty()
%Ash.Range{lower: nil, upper: nil, bounds: :"[)", empty?: true}

empty?(range)

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

Whether the range holds no values: empty/0, or bounds holding nothing, such as [3, 1) or [1, 1). A range unbounded at either end is never empty.

intersects?(left, right)

@spec intersects?(t(), t()) :: boolean()

Whether two ranges share a value: [1, 3) and [3, 5) do not, [1, 3] and [3, 5) do. An empty range intersects nothing.

Allen's :overlaps is narrower: [1, 10) intersects [3, 5), and relation/2 gives :contains.

lower_inclusive?(bounds)

@spec lower_inclusive?(bounds()) :: boolean()

Whether bounds makes the lower bound inclusive.

relation(left, right)

@spec relation(t(), t()) :: allen() | nil

Which of Allen's thirteen relations left has to right, or nil if either is empty. Exactly one holds for two non-empty ranges.

[1, 5) meets [5, 9): one ends where the other begins, and exactly one includes that value. [1, 5] overlaps [5, 9), and [1, 5) precedes (5, 9).

iex> Ash.Range.relation(%Ash.Range{lower: 1, upper: 5}, %Ash.Range{lower: 5, upper: 9})
:meets

relations()

@spec relations() :: [allen()]

The thirteen relations relation/2 returns, in Allen's order: from left furthest before right to furthest after. Their positions can index a thirteen-bit mask.

iex> Ash.Range.relations()
[:precedes, :meets, :overlaps, :finished_by, :contains, :starts, :equals, :started_by,
 :during, :finishes, :overlapped_by, :met_by, :preceded_by]

upper_inclusive?(bounds)

@spec upper_inclusive?(bounds()) :: boolean()

Whether bounds makes the upper bound inclusive.

valid_bounds?(bounds)

@spec valid_bounds?(term()) :: boolean()

Whether bounds is one of :"[)", :"[]", :"()" or :"(]".