View Source Warder.Range (warder v0.1.2)
Range Implementation
Creation / Manipulation
Ranges should not be created or modified manually. Use
new/3
,new!/3
orempty/0
to create ranges.
Ecto Type
If ecto
and postgrex
are installed, you can use the Warder.Range
type:
defmodule Model do
use Ecto.Schema
alias Warder.Range
@type t :: %{range: Range.t(integer())}
schema "models" do
field :range, Range,
db_type: :int8range,
inner_type: :integer
end
end
Options
db_type
- The database type to use for the range column. For example:int8range
.inner_type
- The ecto type of the elements in the range. For example:integer
.
Summary
Types
An empty range is one that does not contain any values between the lower and upper bounds.
Range with specified bounds.
A range of any subtype
.
A specified or empty range with the bound type of subtype
.
Functions
Are the ranges adjacent?
Compare ranges for ordering
Does first
contain second
?
Computes the difference of the ranges.
Computes the difference of the ranges.
Create a new empty range.
Computes the intersection of the ranges.
Guard to detect empty ranges.
Is the first range strictly left of the second?
Computes the smallest range that includes both of the given ranges.
Create a new range for the given bounds.
Create a new range for the given bounds.
Does the first range not extend to the left of the second?
Does the first range not extend to the right of the second?
Does the first range overlap with the second range, that is, have any elements in common?
Is the first range strictly right of the second?
Computes the union of the ranges.
Computes the union of the ranges.
Types
@type empty() :: %Warder.Range{
lower: :empty,
lower_inclusive: nil,
upper: :empty,
upper_inclusive: nil
}
An empty range is one that does not contain any values between the lower and upper bounds.
@type specified(subtype) :: %Warder.Range{ lower: subtype | :unbound, lower_inclusive: boolean(), upper: subtype | :unbound, upper_inclusive: boolean() }
Range with specified bounds.
Fields
lower
- The lower bound of the range.upper
- The upper bound of the range.lower_inclusive
- Whether the lower bound is inclusive.upper_inclusive
- Whether the upper bound is inclusive.
A range of any subtype
.
A specified or empty range with the bound type of subtype
.
Functions
@spec adjacent?(first :: t(subtype), second :: t(subtype)) :: boolean() when subtype: Warder.Element.t()
Are the ranges adjacent?
Examples
iex> Warder.Range.adjacent?(Warder.Range.new!(1, 10), Warder.Range.new!(10, 20))
true
iex> Warder.Range.adjacent?(Warder.Range.new!(1, 10), Warder.Range.new!(15, 20))
false
@spec compare(first :: t(subtype), second :: t(subtype)) :: :lt | :eq | :gt when subtype: Warder.Element.t()
Compare ranges for ordering
The module Warder.Range
can be used as the sorter
argument for
Enum.sort/2
and Enum.sort_by/3
.
Examples
iex> Warder.Range.compare(Warder.Range.new!(1, 10), Warder.Range.new!(5, 15))
:lt
iex> Warder.Range.compare(Warder.Range.new!(1, 10), Warder.Range.new!(1, 10))
:eq
iex> Warder.Range.compare(Warder.Range.new!(1, 10), Warder.Range.new!(0, 5))
:gt
@spec contains?(first :: t(subtype) | subtype, second :: t(subtype) | subtype) :: boolean() when subtype: Warder.Element.t()
Does first
contain second
?
Both first
and second
can either be a range or an element.
Examples
iex> Warder.Range.contains?(Warder.Range.new!(1, 10), Warder.Range.new!(5, 10))
true
iex> Warder.Range.contains?(Warder.Range.new!(1, 10), Warder.Range.new!(15, 20))
false
iex> Warder.Range.contains?(Warder.Range.new!(1, 10), 5)
true
iex> Warder.Range.contains?(Warder.Range.new!(1, 10), 20)
false
@spec difference(first :: t(subtype), second :: t(subtype)) :: {:ok, t(subtype)} | {:error, Warder.Range.DisjointRangesError.t()} when subtype: Warder.Element.t()
Computes the difference of the ranges.
The second range must not be contained in the first in such a way that the difference would not be a single range.
Examples
iex> Warder.Range.difference(Warder.Range.new!(1, 10), Warder.Range.new!(5, 15))
{:ok, %Warder.Range{lower: 1, upper: 5, lower_inclusive: true, upper_inclusive: false}}
iex> Warder.Range.difference(Warder.Range.new!(1, 10), Warder.Range.new!(15, 20))
{:ok, %Warder.Range{lower: 1, upper: 10, lower_inclusive: true, upper_inclusive: false}}
iex> Warder.Range.difference(Warder.Range.new!(1, 10), Warder.Range.new!(2, 8))
{:error, %Warder.Range.DisjointRangesError{
lower: %Warder.Range{lower: 1, upper: 2, lower_inclusive: true, upper_inclusive: false},
upper: %Warder.Range{lower: 8, upper: 10, lower_inclusive: true, upper_inclusive: false}
}}
@spec difference!(first :: t(subtype), second :: t(subtype)) :: t(subtype) when subtype: Warder.Element.t()
Computes the difference of the ranges.
See difference/2
for details.
Examples
iex> Warder.Range.difference!(Warder.Range.new!(1, 10), Warder.Range.new!(5, 15))
%Warder.Range{lower: 1, upper: 5, lower_inclusive: true, upper_inclusive: false}
iex> Warder.Range.difference!(Warder.Range.new!(1, 10), Warder.Range.new!(2, 8))
** (Warder.Range.DisjointRangesError) The result of range difference would not be contiguous.
Before difference: %Warder.Range{lower: 1, upper: 2, lower_inclusive: true, upper_inclusive: false}
After difference: %Warder.Range{lower: 8, upper: 10, lower_inclusive: true, upper_inclusive: false}
@spec empty() :: empty()
Create a new empty range.
Examples
iex> Warder.Range.empty()
%Warder.Range{lower: :empty, lower_inclusive: nil, upper: :empty, upper_inclusive: nil}
@spec intersection(first :: t(subtype), second :: t(subtype)) :: t(subtype) when subtype: Warder.Element.t()
Computes the intersection of the ranges.
Examples
iex> Warder.Range.intersection(Warder.Range.new!(1, 10), Warder.Range.new!(5, 15))
%Warder.Range{lower: 5, upper: 10, lower_inclusive: true, upper_inclusive: false}
iex> Warder.Range.intersection(Warder.Range.new!(1, 10), Warder.Range.new!(15, 20))
%Warder.Range{lower: :empty, upper: :empty, lower_inclusive: nil, upper_inclusive: nil}
Guard to detect empty ranges.
@spec left?(first :: t(subtype), second :: t(subtype)) :: boolean() when subtype: Warder.Element.t()
Is the first range strictly left of the second?
Examples
iex> Warder.Range.left?(Warder.Range.new!(1, 10), Warder.Range.new!(11, 20))
true
iex> Warder.Range.left?(Warder.Range.new!(1, 10), Warder.Range.new!(5, 15))
false
@spec merge(first :: t(subtype), second :: t(subtype)) :: t(subtype) when subtype: Warder.Element.t()
Computes the smallest range that includes both of the given ranges.
Examples
iex> Warder.Range.merge(Warder.Range.new!(1, 10), Warder.Range.new!(5, 15))
%Warder.Range{lower: 1, upper: 15, lower_inclusive: true, upper_inclusive: false}
iex> Warder.Range.merge(Warder.Range.new!(1, 10), Warder.Range.new!(15, 20))
%Warder.Range{lower: 1, upper: 20, lower_inclusive: true, upper_inclusive: false}
@spec new( lower :: subtype | :unbound, upper :: subtype | :unbound, options :: [lower_inclusive: boolean(), upper_inclusive: boolean()] ) :: {:ok, t(subtype)} | {:error, Warder.Range.BoundOrderError.t()} when subtype: Warder.Element.t()
Create a new range for the given bounds.
The lower bound must be less than or equal to the upper bound.
If the bounds are equal, that is, there are no values between the lower and upper bound, the resulting range is empty.
Canonicalization
Ranges are canonicalized on creation. Therefore the bounds & inclusion might shift to ensure the range is in a consistent state while keeping the same meaning.
Options
:lower_inclusive
- Whether the lower bound is inclusive. Defaults totrue
.:upper_inclusive
- Whether the upper bound is inclusive. Defaults tofalse
.
Examples
iex> Warder.Range.new(1, 10)
{:ok, %Warder.Range{lower: 1, upper: 10, lower_inclusive: true, upper_inclusive: false}}
iex> Warder.Range.new(:unbound, 10)
{:ok, %Warder.Range{lower: :unbound, upper: 10, lower_inclusive: true, upper_inclusive: false}}
iex> Warder.Range.new(1, 1, upper_inclusive: false)
{:ok, %Warder.Range{lower: :empty, upper: :empty, lower_inclusive: nil, upper_inclusive: nil}}
iex> Warder.Range.new(10, 1)
{:error, %Warder.Range.BoundOrderError{lower: 10, upper: 1}}
@spec new!( lower :: subtype, upper :: subtype, options :: [lower_inclusive: boolean(), upper_inclusive: boolean()] ) :: t(subtype) when subtype: Warder.Element.t()
Create a new range for the given bounds.
See new/3
for details.
Examples
iex> Warder.Range.new!(1, 10)
%Warder.Range{lower: 1, upper: 10, lower_inclusive: true, upper_inclusive: false}
iex> Warder.Range.new!(10, 1)
** (Warder.Range.BoundOrderError) The range lower bound (10) must be less than or equal to range upper bound (1).
@spec no_extend_left?(first :: t(subtype), second :: t(subtype)) :: boolean() when subtype: Warder.Element.t()
Does the first range not extend to the left of the second?
Examples
iex> Warder.Range.no_extend_left?(Warder.Range.new!(5, 15), Warder.Range.new!(1, 10))
true
iex> Warder.Range.no_extend_left?(Warder.Range.new!(1, 10), Warder.Range.new!(5, 10))
false
@spec no_extend_right?(first :: t(subtype), second :: t(subtype)) :: boolean() when subtype: Warder.Element.t()
Does the first range not extend to the right of the second?
Examples
iex> Warder.Range.no_extend_right?(Warder.Range.new!(1, 10), Warder.Range.new!(5, 15))
true
iex> Warder.Range.no_extend_right?(Warder.Range.new!(1, 15), Warder.Range.new!(5, 10))
false
@spec overlap?(first :: t(subtype), second :: t(subtype)) :: boolean() when subtype: Warder.Element.t()
Does the first range overlap with the second range, that is, have any elements in common?
Examples
iex> Warder.Range.overlap?(Warder.Range.new!(1, 10), Warder.Range.new!(5, 15))
true
iex> Warder.Range.overlap?(Warder.Range.new!(1, 10), Warder.Range.new!(15, 20))
false
@spec right?(first :: t(subtype), second :: t(subtype)) :: boolean() when subtype: Warder.Element.t()
Is the first range strictly right of the second?
Examples
iex> Warder.Range.right?(Warder.Range.new!(11, 20), Warder.Range.new!(1, 10))
true
iex> Warder.Range.right?(Warder.Range.new!(5, 15), Warder.Range.new!(1, 10))
false
@spec union(first :: t(subtype), second :: t(subtype)) :: {:ok, t(subtype)} | {:error, Warder.Range.NotContiguousError.t()} when subtype: Warder.Element.t()
Computes the union of the ranges.
The ranges must overlap or be adjacent, so that the union is a single range.
Examples
iex> Warder.Range.union(Warder.Range.new!(1, 10), Warder.Range.new!(5, 15))
{:ok, %Warder.Range{lower: 1, upper: 15, lower_inclusive: true, upper_inclusive: false}}
iex> Warder.Range.union(Warder.Range.new!(1, 10), Warder.Range.new!(15, 20))
{:error, %Warder.Range.NotContiguousError{
first: %Warder.Range{lower: 1, upper: 10, lower_inclusive: true, upper_inclusive: false},
second: %Warder.Range{lower: 15, upper: 20, lower_inclusive: true, upper_inclusive: false}
}}
@spec union!(first :: t(subtype), second :: t(subtype)) :: t(subtype) when subtype: Warder.Element.t()
Computes the union of the ranges.
See union/2
.
Examples
iex> Warder.Range.union!(Warder.Range.new!(1, 10), Warder.Range.new!(5, 15))
%Warder.Range{lower: 1, upper: 15, lower_inclusive: true, upper_inclusive: false}
iex> Warder.Range.union!(Warder.Range.new!(1, 10), Warder.Range.new!(15, 20))
** (Warder.Range.NotContiguousError) The first range and second range are not contiguous.
First: %Warder.Range{lower: 1, upper: 10, lower_inclusive: true, upper_inclusive: false}
Second: %Warder.Range{lower: 15, upper: 20, lower_inclusive: true, upper_inclusive: false}