# Set operations

DomainSets implements a number of standard set operations. In some cases the operation can be performed analytically and a concrete domain is returned. More often than not, however, the operation results in a lazy structure.

Functions like uniondomain and intersectdomain are not restricted to arguments of type Domain. Thus, one can construct the lazy union of any two objects, regardless of their types.

A distinction is made for set operations between functions in lowercase, such as uniondomain, and capitalized functions, in this case UnionDomain. The former attempts to simplify the arguments (see Canonical domains) and returns a domain that is mathematically equivalent to the union of the two domains. The latter is the constructor of a type and hence always returns an instance of UnionDomain.

Note

For concrete subtypes of Domain one can use standard Julia operators and functions for common set operations. The relevant symbols and functions are ∪ (union), ∩ (intersect) and ∖ (setdiff). For example:

julia> UnitBall(3) ∩ Point([0,0,0])
Point([0.0, 0.0, 0.0])
Warning

Set operations in DomainSets are often not type-stable. For example, by convention uniondomain(d1,d2) returns a domain of the simplest type that is mathematically equivalent to the union of d1 and d2. The union of two overlapping intervals is a single interval, but the union of non-overlapping intervals is a UnionDomain:

julia> uniondomain(1..3, 2..4)
1 .. 4
julia> uniondomain(1..3, 5..7)
(1 .. 3) ∪ (5 .. 7)

When type-safety is important, use the corresponding constructor:

julia> UnionDomain(1..3, 2..4)
(1 .. 3) ∪ (2 .. 4)

## Product domains

Product domains are created most easily by invoking the ProductDomain constructor. The constructor of the associated abstract type ProductDomain returns an instance of a suitable concrete subtype. In many cases a user need not be aware of which type is being returned by ProductDomain, as it always behaves like the requested domain.

DomainSets.ProductDomainMethod
ProductDomain(domains...)
ProductDomain{T}(domains...)

Return a concrete subtype of ProductDomain which agrees mathematically with the cartesian product of the given domains.

The concrete subtype being returned depends on T. If T is provided, it will be the eltype of the product domain. If T is not provided, a suitable choice is deduced from the arguments.

A number of concrete product domain types are implemented. They differ in what the eltype of the product domain is. In the most generic case T is a tuple, with each element representing the element type of the corresponding factor. A VcatDomain is a special case for product domains of Euclidean type, i.e., one in which each factor has a scalar or statically-sized vector as element type. Finally, a VectorProductDomain has a Vector element type whose dimension is determined by the number of factors. The ProductDomain aims to return the most specialized type of domain, but the individual constructors may be invoked to ensure a specific one.

julia> ProductDomain(2..4.5, 3.0..5.0)
(2.0 .. 4.5) × (3.0 .. 5.0)

julia> eltype(ProductDomain(2..4.5, 3.0..5.0))
SVector{2, Float64}

julia> [2.4, 4] ∈ ProductDomain(2..4.5, 3.0..5.0)
true

julia> TupleProductDomain(2..4.5, 3.0..5.0)
(2.0 .. 4.5) × (3.0 .. 5.0)

julia> eltype(TupleProductDomain(2..4.5, 3.0..5.0))
Tuple{Float64, Float64}

julia> (2.4, 4) ∈ TupleProductDomain(2..4.5, 3.0..5.0)
true

julia> ProductDomain([ i..i+1.0 for i in 1:10])
(1.0 .. 2.0) × (2.0 .. 3.0) × (3.0 .. 4.0) × ... × (10.0 .. 11.0)

julia> eltype(ProductDomain([ i..i+1.0 for i in 1:10]))
Vector{Float64}

julia> 1:10 ∈ ProductDomain([ i..i+1.0 for i in 1:10])
true

It is noteworthy that a VcatDomain can cope with the concatenation of scalars and vectors. In the example below, a cylinder is represented as the product of a two-dimensional disk with a one-dimensional interval.

julia> using DomainSets: ×

julia> cylinder = UnitDisk() × UnitInterval()
UnitDisk() × (0.0 .. 1.0 (Unit))

julia> eltype(cylinder)
SVector{3, Float64}

julia> [0.4,0.2,0.6] ∈ cylinder
true

A number of concrete product domains are implemented in DomainSets.

DomainSets.RectangleType
Rectangle(a, b)
Rectangle(domains::ClosedInterval...)
Rectangle{T}(domains::ClosedInterval...)

A rectangular domain in n dimensions with extrema determined by the vectors or points a and b or by the endpoints of the given intervals.

## Set union

The mathematical union of two sets is guaranteed by uniondomain, while a lazy union is returned by UnionDomain.

For vectors and sets in Julia, uniondomain returns precisely what the standard union operation would do, while UnionDomain returns a lazy construct:

julia> uniondomain(1:3, 10)
4-element Vector{Int64}:
1
2
3
10
julia> UnionDomain(1:3, 10)
1:3 ∪ 10

julia> 10 ∈ ans
true

## Set intersection

The mathematical intersection of two sets is guaranteed by intersectdomain, while a lazy intersection is returned by IntersectDomain.

## Set difference

The mathematical difference of two sets is guaranteed by setdiffdomain, while a lazy difference is returned by SetdiffDomain.