# `Ash.Range`
[🔗](https://github.com/ash-project/ash/blob/v3.33.0/lib/ash/range.ex#L5)

A continuous range of values of some inner type, with inclusive/exclusive bounds.

The value representation for `Ash.Type.Range`. `bounds` follows Postgres range
notation: the first character is the lower bound, the second the upper —
`[` / `]` inclusive, `(` / `)` exclusive. A `nil` `lower`/`upper` is an
unbounded (infinite) end. The default `:"[)"` (lower-inclusive, upper-exclusive)
is the convention that lets adjacent ranges tile a timeline without overlap.

A range containing no points is empty, and every empty range is the same range.
`Ash.Type.Range` casts any such range to `empty/0`, whose bounds are dropped —
as Postgres does — so that empty ranges compare equal and survive storage in a
data layer that keeps no bounds for them.

# `allen`

```elixir
@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`

```elixir
@type bounds() :: :&quot;[)&quot; | :&quot;[]&quot; | :&quot;()&quot; | :&quot;(]&quot;
```

# `t`

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

# `adjacent?`

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

Whether two ranges are adjacent: one ends exactly where the other begins, with no
point between them and none shared.

The seam counts only when exactly one side includes it, so `[1,5)` is adjacent to
`[5,9)`, where `[1,5]` overlaps it and `(5,9)` leaves a gap. Symmetric, unlike
Allen's *meets*, which is directional. An empty range is adjacent to nothing.

Adjacency is what lets a series of ranges tile: each meets the next, covering
everything between the first lower bound and the last upper without overlapping.

For a discrete inner type this is a question about the canonical form — `[1,4]` and
`[5,9)` are adjacent as integers but not as decimals — so compare values that have
been through `Ash.Type.Range`, which canonicalises them. Matches Postgres `-|-`.

# `compare`

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

Compares two ranges as Postgres orders them: empty first, then by lower bound, then
by upper, an unbounded end as `-∞`/`+∞`, and the earlier boundary first where two
bounds name the same value (`[1` before `(1`, `5)` before `5]`).

A sort order rather than containment: `[1,10)` sorting before `[3,5)` says nothing
about one holding the other.

# `contains?`

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

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

An unbounded end holds everything beyond it, and an empty range holds no point.
Each bound is compared with `Comp`, so an inner type behaves inside a range as
it does outside one, and a bound that excludes its own value (`(` or `)`) is
not held.

A range holds another when the second lies within the first, sharing an endpoint
or being equal included. Every range holds the empty range, as Postgres `@>` does.

# `empty`

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

The empty range: the one range containing no points.

# `empty?`

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

Whether the range contains no points.

True for `empty/0`, and for a bounded range whose bounds admit nothing: a lower
above its upper, or bounds that meet without both including the point they meet
at. An unbounded end is never empty.

# `intersects?`

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

Whether two ranges share any point.

An empty range intersects nothing, not even itself. Each range must start at or
before the other ends, and a boundary the two ranges share counts only when both
sides include it — so `[1,3)` and `[3,5)` do not intersect, where `[1,3]` and
`[3,5)` do. Bounds are compared with `Comp`, as everywhere else here.

Named for what it answers rather than for the operator it backs. Postgres calls
`&&` "overlap" and `range_overlaps/2` keeps that name, but Allen's *overlaps* is
the narrower relation where two ranges cross with neither containing the other —
under which `[1,10)` and `[3,5)` do **not** overlap. This returns true for them.

# `lower_inclusive?`

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

Whether the range's lower bound includes the point it names.

# `relation`

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

Which of Allen's thirteen relations `left` bears to `right`, or `nil` if either is empty.

Exactly one holds for any pair of non-empty ranges, so the answer classifies rather
than tests. An empty range has no relation to anything: it precedes nothing and is
during nothing, having no points to be positioned by.

Two ranges *meet* when one ends where the other begins and exactly one of them
includes that point — `[1,5)` meets `[5,9)`, where `[1,5]` overlaps it and `(5,9)`
merely follows it. Matches Postgres `-|-`.

# `relations`

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

Every relation `relation/2` can answer, in Allen's canonical order.

Sorted by how far `left` begins before `right`, then by how far it ends before, with
`equals` at the centre and each relation the converse of its mirror. A set of
relations is conventionally a thirteen-bit mask, so these are the bit positions.

# `upper_inclusive?`

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

Whether the range's upper bound includes the point it names.

# `valid_bounds?`

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

Whether the given atom is a valid bounds specifier.

---

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