# Custom Datatypes

*class* Range

Represents an interval between values.

The `Range`

type behaves the same as the EdgeDB `range`

type.
Ranges can have a lower and upper boundary, which can be inclusive or
exclusive, or omitted completely (`null`

). By default, the lower boundary
is inclusive, and the upper boundary exclusive.

Depending on the type of the range, `T`

, the range is either discrete or
continuous.
Discrete range types: `Range<int>`

Continuous range types: `Range<double>`

, `Range<DateTime>`

Discrete ranges are normalised upon creation, such that the lower boundary
becomes inclusive, and the upper boundary becomes exclusive.

If a range can contain no values, it is considered ‘empty’. Empty ranges
are all considered equal to each other. An empty range can be created
either by the `Range.empty()`

constructor, creating a range where the lower
and upper boundaries are equal, and at least one boundary is exclusive, or
as the result of some operation on a range.

*constructor*
`Range<T>()`

```
Range<T>(
T? lower,
T? upper,
{bool? incLower,
bool? incUpper}
)
```

Creates a new Range.

If not given, `incLower`

and `incUpper`

default to `true`

and `false`

respectively.

*property*
`.incLower`

`bool get incLower`

Whether the lower boundary is inclusive. Is always `false`

for unspecified
boundaries and empty ranges.

*property*
`.incUpper`

`bool get incUpper`

Whether the upper boundary is inclusive. Is always `false`

for unspecified
boundaries and empty ranges.

*method*
`.compareTo()`

```
int compareTo(
Range<T> other
)
```

Compares this object to another object.

Returns a value like a Comparator when comparing `this`

to `other`

.
That is, it returns a negative integer if `this`

is ordered before `other`

,
a positive integer if `this`

is ordered after `other`

,
and zero if `this`

and `other`

are ordered together.

The `other`

argument must be a value that is comparable to this object.

*method*
`.containsRange()`

```
bool containsRange(
Range<T> range
)
```

Checks whether `range`

is entirely within this range.

*method*
`.overlaps()`

```
bool overlaps(
Range<T> other
)
```

Checks whether `other`

range overlaps this range.

*method*
`.toString()`

`String toString()`

String representation of the range.

Inclusive boundaries are denoted by `[]`

brackets, and exclusive
boundaries by `()`

. If the range is empty, returns the string `'empty'`

.

*method*
`.unpack()`

```
Iterable<T> unpack(
{Object? step}
)
```

If the range is discrete and no `step`

is provided, returns an `Iterable`

of all values in the range. Otherwise returns an `Iterable`

of each
value starting at the lower bound, increasing by `step`

up to the
upper bound.

An error is thrown if the range is unbounded (ie. either `lower`

or
`upper`

are `null`

), or the `step`

parameter is not given for
non-discrete ranges.

*operator*
`+`

```
Range<T> operator +(
Range<T> other
)
```

Returns the union of two ranges.

Throws an error if the result is not a single continuous range.

*operator*
`-`

```
Range<T> operator -(
Range<T> other
)
```

Subtracts one range from another.

Throws an error if the result is not a single continuous range.

*operator*
`<`

```
bool operator <(
Range<T> other
)
```

Returns whether this range is before the `other`

range.

A range is considered to be ordered before another range if its lower bound is lower than the other. If the lower bounds are equal, the upper bounds are checked. An empty range is considered lower than a non-empty range, and unspecified lower/upper bounds are considered lower/greater than specified lower/upper bounds respectively.

*operator*
`<=`

```
bool operator <=(
Range<T> other
)
```

Returns whether this range is before or equal to the `other`

range.

A range is considered to be ordered before another range if its lower bound is lower than the other. If the lower bounds are equal, the upper bounds are checked. An empty range is considered lower than a non-empty range, and unspecified lower/upper bounds are considered lower/greater than specified lower/upper bounds respectively.

*operator*
`>`

```
bool operator >(
Range<T> other
)
```

Returns whether this range is after the `other`

range.

A range is considered to be ordered after another range if its lower bound is greater than the other. If the lower bounds are equal, the upper bounds are checked. An empty range is considered lower than a non-empty range, and unspecified lower/upper bounds are considered lower/greater than specified lower/upper bounds respectively.

*operator*
`>=`

```
bool operator >=(
Range<T> other
)
```

Returns whether this range is after or equal to the `other`

range.

A range is considered to be ordered after another range if its lower bound is greater than the other. If the lower bounds are equal, the upper bounds are checked. An empty range is considered lower than a non-empty range, and unspecified lower/upper bounds are considered lower/greater than specified lower/upper bounds respectively.

*class* MultiRange

*property*
`.iterator`

`Iterator<Range<T>> get iterator`

An iterator that iterates over the elements of this set.

The order of iteration is defined by the individual `Set`

implementation,
but must be consistent between changes to the set.

*property*
`.length`

`int get length`

The number of elements in this.

Counting all elements may involve iterating through all elements and can
therefore be slow.
Some iterables have a more efficient way to find the number of elements.
These *must* override the default implementation of `length`

.

*method*
`.add()`

```
bool add(
Range<T> value
)
```

Adds `value`

to the set.

Returns `true`

if `value`

(or an equal value) was not yet in the set.
Otherwise returns `false`

and the set is not changed.

Example:

```
final dateTimes = <DateTime>{};
final time1 = DateTime.fromMillisecondsSinceEpoch(0);
final time2 = DateTime.fromMillisecondsSinceEpoch(0);
// time1 and time2 are equal, but not identical.
assert(time1 == time2);
assert(!identical(time1, time2));
final time1Added = dateTimes.add(time1);
print(time1Added); // true
// A value equal to time2 exists already in the set, and the call to
// add doesn't change the set.
final time2Added = dateTimes.add(time2);
print(time2Added); // false
print(dateTimes); // {1970-01-01 02:00:00.000}
assert(dateTimes.length == 1);
assert(identical(time1, dateTimes.first));
print(dateTimes.length);
```

*method*
`.contains()`

```
bool contains(
Object? element
)
```

Whether `value`

is in the set.

```
final characters = <String>{'A', 'B', 'C'};
final containsB = characters.contains('B'); // true
final containsD = characters.contains('D'); // false
```

*method*
`.lookup()`

```
Range<T>? lookup(
Object? element
)
```

If an object equal to `object`

is in the set, return it.

Checks whether `object`

is in the set, like contains, and if so,
returns the object in the set, otherwise returns `null`

.

If the equality relation used by the set is not identity,
then the returned object may not be *identical* to `object`

.
Some set implementations may not be able to implement this method.
If the contains method is computed,
rather than being based on an actual object instance,
then there may not be a specific object instance representing the
set element.

```
final characters = <String>{'A', 'B', 'C'};
final containsB = characters.lookup('B');
print(containsB); // B
final containsD = characters.lookup('D');
print(containsD); // null
```

*method*
`.remove()`

```
bool remove(
Object? value
)
```

Removes `value`

from the set.

Returns `true`

if `value`

was in the set, and `false`

if not.
The method has no effect if `value`

was not in the set.

```
final characters = <String>{'A', 'B', 'C'};
final didRemoveB = characters.remove('B'); // true
final didRemoveD = characters.remove('D'); // false
print(characters); // {A, C}
```

*method*
`.toSet()`

`Set<Range<T>> toSet()`

Creates a Set with the same elements and behavior as this `Set`

.

The returned set behaves the same as this set with regard to adding and removing elements. It initially contains the same elements. If this set specifies an ordering of the elements, the returned set will have the same order.

*method*
`.toString()`

`String toString()`

A string representation of this object.

Some classes have a default textual representation,
often paired with a static `parse`

function (like int.parse).
These classes will provide the textual representation as
their string representation.

Other classes have no meaningful textual representation
that a program will care about.
Such classes will typically override `toString`

to provide
useful information when inspecting the object,
mainly for debugging or logging.

*class* ConfigMemory

Represents an amount of memory in bytes.

Uses the base-2 `KiB`

notation (1024 bytes), instead of the more
ambiguous ‘kB’, which can mean 1000 or 1024 bytes.

*method*
`.toString()`

`String toString()`

A string representation of this object.

Some classes have a default textual representation,
often paired with a static `parse`

function (like int.parse).
These classes will provide the textual representation as
their string representation.

Other classes have no meaningful textual representation
that a program will care about.
Such classes will typically override `toString`

to provide
useful information when inspecting the object,
mainly for debugging or logging.