text:
import Base: rand,
             ∈,
             isempty

export Hyperplane,
       an_element

"""
    Hyperplane{N<:Real} <: AbstractPolyhedron{N}

Type that represents a hyperplane of the form ``a⋅x = b``.

### Fields

- `a` -- normal direction (non-zero)
- `b` -- constraint

### Examples

The plane ``y = 0``:

```jldoctest
julia> Hyperplane([0, 1.], 0.)
Hyperplane{Float64}([0.0, 1.0], 0.0)
```
"""
struct Hyperplane{N<:Real} <: AbstractPolyhedron{N}
    a::AbstractVector{N}
    b::N

    function Hyperplane(a::AbstractVector{N}, b::N) where {N<:Real}
        @assert !iszero(a) "a hyperplane needs a non-zero normal vector"
        return new{N}(a, b)
    end
end

isoperationtype(::Type{<:Hyperplane}) = false
isconvextype(::Type{<:Hyperplane}) = true

# --- polyhedron interface functions ---

"""
    constraints_list(hp::Hyperplane{N}) where {N<:Real}

Return the list of constraints of a hyperplane.

### Input

- `hp` -- hyperplane

### Output

A list containing two half-spaces.
"""
function constraints_list(hp::Hyperplane{N}) where {N<:Real}
    return _constraints_list_hyperplane(hp.a, hp.b)
end

# --- LazySet interface functions ---

"""
    dim(hp::Hyperplane)

Return the dimension of a hyperplane.

### Input

- `hp` -- hyperplane

### Output

The ambient dimension of the hyperplane.
"""
function dim(hp::Hyperplane)
    return length(hp.a)
end

"""
    ρ(d::AbstractVector{N}, hp::Hyperplane{N}) where {N<:Real}

Evaluate the support function of a hyperplane in a given direction.

### Input

- `d`  -- direction
- `hp` -- hyperplane

### Output

The support function of the hyperplane.
If the set is unbounded in the given direction, the result is `Inf`.
"""
function ρ(d::AbstractVector{N}, hp::Hyperplane{N}) where {N<:Real}
    v, unbounded = σ_helper(d, hp, error_unbounded=false)
    if unbounded
        return N(Inf)
    end
    return dot(d, v)
end

"""
    σ(d::AbstractVector{N}, hp::Hyperplane{N}) where {N<:Real}

Return the support vector of a hyperplane.

### Input

- `d`  -- direction
- `hp` -- hyperplane

### Output

The support vector in the given direction, which is only defined in the
following two cases:
1. The direction has norm zero.
2. The direction is the hyperplane's normal direction or its opposite direction.
In all cases, the result is any point on the hyperplane.
Otherwise this function throws an error.
"""
function σ(d::AbstractVector{N}, hp::Hyperplane{N}) where {N<:Real}
    v, unbounded = σ_helper(d, hp, error_unbounded=true)
    return v
end

"""
    isbounded(hp::Hyperplane)

Determine whether a hyperplane is bounded.

### Input

- `hp` -- hyperplane

### Output

`false`.
"""
function isbounded(::Hyperplane)
    return false
end

"""
    isuniversal(hp::Hyperplane{N}, [witness]::Bool=false) where {N<:Real}

Check whether a hyperplane is universal.

### Input

- `P`       -- hyperplane
- `witness` -- (optional, default: `false`) compute a witness if activated

### Output

* If `witness` option is deactivated: `false`
* If `witness` option is activated: `(false, v)` where ``v ∉ P``

### Algorithm

A witness is produced by adding the normal vector to an element on the
hyperplane.
"""
function isuniversal(hp::Hyperplane{N}, witness::Bool=false) where {N<:Real}
    if witness
        v = an_element(hp) + hp.a
        return (false, v)
    else
        return false
    end
end

"""
    an_element(hp::Hyperplane{N}) where {N<:Real}

Return some element of a hyperplane.

### Input

- `hp` -- hyperplane

### Output

An element on the hyperplane.
"""
function an_element(hp::Hyperplane{N}) where {N<:Real}
    return an_element_helper(hp)
end

"""
    ∈(x::AbstractVector{N}, hp::Hyperplane{N}) where {N<:Real}

Check whether a given point is contained in a hyperplane.

### Input

- `x` -- point/vector
- `hp` -- hyperplane

### Output

`true` iff ``x ∈ hp``.

### Algorithm

We just check if ``x`` satisfies ``a⋅x = b``.
"""
function ∈(x::AbstractVector{N}, hp::Hyperplane{N}) where {N<:Real}
    return dot(x, hp.a) == hp.b
end

"""
    rand(::Type{Hyperplane}; [N]::Type{<:Real}=Float64, [dim]::Int=2,
         [rng]::AbstractRNG=GLOBAL_RNG, [seed]::Union{Int, Nothing}=nothing)

Create a random hyperplane.

### Input

- `Hyperplane` -- type for dispatch
- `N`          -- (optional, default: `Float64`) numeric type
- `dim`        -- (optional, default: 2) dimension
- `rng`        -- (optional, default: `GLOBAL_RNG`) random number generator
- `seed`       -- (optional, default: `nothing`) seed for reseeding

### Output

A random hyperplane.

### Algorithm

All numbers are normally distributed with mean 0 and standard deviation 1.
Additionally, the constraint `a` is nonzero.
"""
function rand(::Type{Hyperplane};
              N::Type{<:Real}=Float64,
              dim::Int=2,
              rng::AbstractRNG=GLOBAL_RNG,
              seed::Union{Int, Nothing}=nothing)
    rng = reseed(rng, seed)
    a = randn(rng, N, dim)
    while iszero(a)
        a = randn(rng, N, dim)
    end
    b = randn(rng, N)
    return Hyperplane(a, b)
end

"""
    isempty(hp::Hyperplane)

Return if a hyperplane is empty or not.

### Input

- `hp` -- hyperplane

### Output

`false`.
"""
function isempty(hp::Hyperplane)
    return false
end

"""
    constrained_dimensions(hp::Hyperplane{N}) where {N<:Real}

Return the indices in which a hyperplane is constrained.

### Input

- `hp` -- hyperplane

### Output

A vector of ascending indices `i` such that the hyperplane is constrained in
dimension `i`.

### Examples

A 2D hyperplane with constraint ``x1 = 0`` is constrained in dimension 1 only.
"""
function constrained_dimensions(hp::Hyperplane{N}) where {N<:Real}
    return nonzero_indices(hp.a)
end

# --- Hyperplane functions ---

"""
```
    σ_helper(d::AbstractVector{N},
             hp::Hyperplane{N};
             error_unbounded::Bool=true,
             [halfspace]::Bool=false) where {N<:Real}
```

Return the support vector of a hyperplane.

### Input

- `d`         -- direction
- `hp`        -- hyperplane
- `error_unbounded` -- (optional, default: `true`) `true` if an error should be
                 thrown whenever the set is
                 unbounded in the given direction
- `halfspace` -- (optional, default: `false`) `true` if the support vector
                 should be computed for a half-space

### Output

A pair `(v, b)` where `v` is a vector and `b` is a Boolean flag.

The flag `b` is `false` in one of the following cases:
1. The direction has norm zero.
2. The direction is the hyperplane's normal direction.
3. The direction is the opposite of the hyperplane's normal direction and
`halfspace` is `false`.
In all these cases, `v` is any point on the hyperplane.

Otherwise, the flag `b` is `true`, the set is unbounded in the given direction,
and `v` is any vector.

If `error_unbounded` is `true` and the set is unbounded in the given direction,
this function throws an error instead of returning.

### Notes

For correctness, consider the [weak duality of
LPs](https://en.wikipedia.org/wiki/Linear_programming#Duality):
If the primal is unbounded, then the dual is infeasible.
Since there is only a single constraint, the feasible set of the dual problem is
`hp.a ⋅ y == d`, `y >= 0` (with objective function `hp.b ⋅ y`).
It is easy to see that this problem is infeasible whenever `a` is not parallel
to `d`.
"""
@inline function σ_helper(d::AbstractVector{N},
                          hp::Hyperplane{N};
                          error_unbounded::Bool=true,
                          halfspace::Bool=false) where {N<:Real}
    @assert (length(d) == dim(hp)) "cannot compute the support vector of a " *
        "$(dim(hp))-dimensional " * (halfspace ? "halfspace" : "hyperplane") *
        " along a vector of length $(length(d))"

    first_nonzero_entry_a = -1
    unbounded = false
    if iszero(d)
        # zero vector
        return (an_element(hp), false)
    else
        # not the zero vector, check if it is a normal vector
        factor = zero(N)
        for i in 1:length(hp.a)
            if hp.a[i] == 0
                if d[i] != 0
                    unbounded = true
                    break
                end
            else
                if d[i] == 0
                    unbounded = true
                    break
                elseif first_nonzero_entry_a == -1
                    factor = hp.a[i] / d[i]
                    first_nonzero_entry_a = i
                    if halfspace && factor < 0
                        unbounded = true
                        break
                    end
                elseif d[i] * factor != hp.a[i]
                    unbounded = true
                    break
                end
            end
        end
        if !unbounded
            return (an_element_helper(hp, first_nonzero_entry_a), false)
        end
        if error_unbounded
            error("the support vector for the " *
                (halfspace ? "halfspace" : "hyperplane") * " with normal " *
                "direction $(hp.a) is not defined along a direction $d")
        end
        # the first return value does not have a meaning here
        return (d, true)
    end
end

"""
    an_element_helper(hp::Hyperplane{N},
                      [nonzero_entry_a]::Int) where {N<:Real}

Helper function that computes an element on a hyperplane's hyperplane.

### Input

- `hp` -- hyperplane
- `nonzero_entry_a` -- (optional, default: computes the first index) index `i`
                       such that `hp.a[i]` is different from 0

### Output

An element on a hyperplane.

### Algorithm

We compute the point on the hyperplane as follows:
- We already found a nonzero entry of ``a`` in dimension, say, ``i``.
- We set ``x[i] = b / a[i]``.
- We set ``x[j] = 0`` for all ``j ≠ i``.
"""
@inline function an_element_helper(hp::Hyperplane{N},
                                   nonzero_entry_a::Int=findnext(x -> x!=zero(N), hp.a, 1)
                                  ) where {N<:Real}
    @assert nonzero_entry_a in 1:length(hp.a) "invalid index " *
        "$nonzero_entry_a for hyperplane"
    x = zeros(N, dim(hp))
    x[nonzero_entry_a] = hp.b / hp.a[nonzero_entry_a]
    return x
end

# internal helper function
function _constraints_list_hyperplane(a::AbstractVector{N}, b::N
                                     ) where {N<:Real}
    return [HalfSpace(a, b), HalfSpace(-a, -b)]
end

function _linear_map_hrep_helper(M::AbstractMatrix{N}, P::Hyperplane{N},
                                 algo::AbstractLinearMapAlgorithm) where {N<:Real}
    constraints = _linear_map_hrep(M, P, algo)
    if length(constraints) == 2
        # assuming these constraints define a hyperplane
        c = first(constraints)
        return Hyperplane(c.a, c.b)
    elseif isempty(constraints)
        return Universe{N}(size(M, 1))
    else
        error("unexpected number of $(length(constraints)) constraints")
    end
end

"""
    translate(hp::Hyperplane{N}, v::AbstractVector{N}; share::Bool=false
             ) where {N<:Real}

Translate (i.e., shift) a hyperplane by a given vector.

### Input

- `hp`    -- hyperplane
- `v`     -- translation vector
- `share` -- (optional, default: `false`) flag for sharing unmodified parts of
             the original set representation

### Output

A translated hyperplane.

### Notes

The normal vectors of the hyperplane (vector `a` in `a⋅x = b`) is shared with
the original hyperplane if `share == true`.

### Algorithm

A hyperplane ``a⋅x = b`` is transformed to the hyperplane ``a⋅x = b + a⋅v``.
In other words, we add the dot product ``a⋅v`` to ``b``.
"""
function translate(hp::Hyperplane{N}, v::AbstractVector{N}; share::Bool=false
                  ) where {N<:Real}
    @assert length(v) == dim(hp) "cannot translate a $(dim(hp))-dimensional " *
                                 "set by a $(length(v))-dimensional vector"
    a = share ? hp.a : copy(hp.a)
    b = hp.b + dot(hp.a, v)
    return Hyperplane(a, b)
end


text:
using APMAE4001
using Test

@testset "APMAE4001.jl" begin
    include("TestDoolittle.jl")
    include("TestInverseMatrices.jl")
end

ext: jl


text:
include("../core/layers.jl")
include("../core/blocks.jl")
include("../../configs.jl")

"""
Non-Deformable Context Head Module Network components for the RetinaFace.
"""
struct SSH conv128; conv64_1_1; conv64_1_2; conv64_2_1; conv64_2_2; end

function SSH(; input_dim=256, dtype=Array{Float32})
    out_dim = Int(floor(input_dim/4))
    return SSH(
        ConvBn(3, 3, input_dim, Int(floor(input_dim/2)), bias=false, dtype=dtype, padding=1),

        ConvBn(3, 3, input_dim, out_dim, padding=1, bias=false, dtype=dtype, f=relu),
        ConvBn(3, 3, out_dim, out_dim, padding=1, bias=false, dtype=dtype),

        ConvBn(3, 3, out_dim, out_dim, padding=1, bias=false, dtype=dtype, f=relu),
        ConvBn(3, 3, out_dim, out_dim, padding=1, bias=false, dtype=dtype)
    )
end

function (ssh::SSH)(x; train=true)
    o1 = ssh.conv128(x, train=train)
    o2_1 = ssh.conv64_1_1(x, train=train)
    o2_2 = ssh.conv64_1_2(o2_1, train=train)
    o3_1 = ssh.conv64_2_1(o2_1, train=train)
    o3_2 = ssh.conv64_2_2(o3_1, train=train)
    combined = cat(o1, o2_2, o3_2, dims=3)
    return relu.(combined)
end

max_stars_repo_name: barisbatuhan/retinaface

max_issues_repo_name: barisbatuhan/retinaface

max_forks_repo_name: barisbatuhan/retinaface


text:
@testset "frac_hour" begin
    @test frac_hour(Minute, 1+1/60) == Hour(1) + Minute(1)
    @test frac_hour(1+1/60) == Hour(1) + Minute(1)
end

@testset "moving_average" begin
    GPPd = @pipe sin.((1:365).*π./365) .+ 0.5 .* rand(365) |> allowmissing(_)
    GPPd[100:120] .= missing
    GPPd_smooth = moving_average(GPPd, 15)
    #plot(1:365, GPPd)
    #plot!(1:365, GPPd_smooth)
    @test length(GPPd_smooth) == length(GPPd)
    @test GPPd_smooth[1] == mean(GPPd[1:8])
    @test GPPd_smooth[8] == mean(GPPd[1:15])
    @test isfinite(GPPd_smooth[100])
    @test ismissing(GPPd_smooth[101])
end

@testset "get_nonoverlapping_periods" begin
    dfo = DataFrame(
        p_start = DateTime(2021) .+ Hour.(SA[0,4,5,10]),
        p_end = DateTime(2021) .+ Hour.(SA[2,5,6,11]),
        )
    dfno = get_nonoverlapping_periods(dfo)
    @test dfno.p_start == DateTime(2021) .+ Hour.(SA[0,4,10])
    @test dfno.p_end == DateTime(2021) .+ Hour.(SA[2,6,11])
    #
    # extend last row
    dfo = DataFrame(
        p_start = DateTime(2021) .+ Hour.(SA[0,4,5]),
        p_end = DateTime(2021) .+ Hour.(SA[2,5,6]),
        )
    dfno = get_nonoverlapping_periods(dfo)
    @test dfno.p_start == DateTime(2021) .+ Hour.(SA[0,4])
    @test dfno.p_end == DateTime(2021) .+ Hour.(SA[2,6])
end

@testset "set_datetime_ydh!" begin
    dfo = DataFrame(year = 2021, doy = repeat(1:3, inner = 48), hour = repeat(0.0:0.5:23.5, outer = 3))
    df = copy(dfo)
    df2 = set_datetime_ydh!(df)
    @test df2 === df
    @test df2.datetime[1] == ZonedDateTime(DateTime(2021), tz"UTC")
    @test all(Dates.year.(df2.datetime) .== dfo.year)
    @test all(Dates.dayofyear.(df2.datetime) .== dfo.doy)
    @test all(Dates.hour.(df2.datetime) .+ Dates.minute.(df2.datetime)/60 .== dfo.hour)
end


text:
@testset "negativemultinomial" begin
    # TODO add tests
end


text:
abstract type AbsLinearOperator end

"""
    row_valdiff(op::AbsLinearOperator, v::State) -> OpConnection

Returns all non-zero elements in the row represented by the state `v` of
operator `op`. The result is an `OpConnection` type.
"""
row_valdiff(op::AbsLinearOperator, v::State) = row_valdiff!(OpConnection(op), op, v)

"""
    row_valdiff!(opconn::OpConnection, op::AbsLinearOperator, v::State)

Returns all non-zero elements in the row represented by the state `v` of
operator `op` by mutating in-place the `OpConnection` object `opconn`
"""
function row_valdiff! end

row_valdiff_index(op::AbsLinearOperator, v::State) = row_valdiff_index!(OpConnectionIndex(op), op, v)
function row_valdiff_index! end


text:
"""
   set_size!(o, t)

Internal function to set a tuple indicating (width, height) size of the object.
"""
set_size!(o::Union{Figure,Colorbar}, t::T2F) = (o.size = t)

"""
   set_texlabels!(o, v)

Internal function to set a bool variable indicating whether the figure has LaTex labels or not.
If there is no texpreamble defined, a simple one is instantiated with `amssymb`.
"""
function set_texlabels!(o::Figure, v::Bool)
   o.texlabels = ifelse(v, v, ∅)
   isnothing(o.texpreamble) && set_texpreamble!(o, t"\usepackage{amssymb}")
end

"""
   set_texscale!(o, v)

Sets a string variable indicating the type of scaling (`fixed`, `scale` or `none`)
* `fixed`: keep LaTeX in std size as close as possible to the ambient fontsize
* `scale`: force LaTeX to use a size corresponding to the ambient fontsize
* `none`: do not scale LaTeX.
"""
function set_texscale!(o::Figure, v::String)
   @assert get_backend() == GLE "texscale/only GLE backend supported"
   (v ∈ GLE_TEXSCALE) || throw(OptionValueError("texscale", v))
   o.texscale = v
   return nothing
end

"""
   set_texpreamble!

Sets the tex preamble that should be used (e.g. with font packages or symbol
packages). Bear in mind that only `pdflatex` can be used and so not all font
packages can be used nor some commands like `fontspec` which are meant for
XeLaTeX or LuaLaTex.
"""
set_texpreamble!(o::Figure, v::String) = (o.texpreamble = v)

"""
   set_transparency!

Sets a bool variable corresponding to whether to use cairo (allow transparency)
or not. This is for instance useful if some of the fill colors used are
transparent.
"""
set_transparency!(o::Figure, v::Bool) = (o.transparency = v)


text:
## Reading the file
function readcodelines(io::IO)::Vector{String}
    """Reads the entire code. Returns an array of strings. Also removes comments and connects lines split with '\\'"""
    function readcodeline(io::IO)::String
        line::String = string(strip(readline(io)))
        comment = findfirst("@", line)
        if comment != nothing #&& comment != 1
            line = line[1:comment[1]-1]
        end
        if line == "" #|| comment == 1
            return ""
        end
        if line[end] == '\\'
            if (eof(io))
                throw(ErrorException("Error when reading the file '\\' at the end of file"))
            end
            line = line[1:end-1]*' '*readcodeline(io)
        end
        line
    end
    lines::Array{String, 1} = []
    while !eof(io)
        line::String = readcodeline(io)
        if line != ""
            push!(lines, line)
        end
    end
    lines
end

##
include("Core/AetherlangCore.jl"); Token = Union{String, AetherlangObject}
include("Tokenizer.jl")
include("InterpretationDimensions.jl")
include("LibUse.jl")
include("Eval.jl")

if length(ARGS) < 1
    println("No file provided for interpretation (ARGS[1] was empty). Aetherlang does not support interactive mode.")
    exit()
end
try
    open(ARGS[1], "r") do file
        global lines = [aetherlang_tokenize(l) for l in readcodelines(file)]
    end
catch e
    aetherlang_print_exception(e, ["__"], "__")
    exit()
end

ether = Namespace()
mainnamespace = Namespace(
    "void" => AetherlangObject(),
    "use" => AetherlangObject(lib_use),
    "true" => AetherlangObject(1),
    "dimname" => AetherlangObject("main")
)
## Main loop (Dimension execution)
tofinish   = Set{String}() # contains names of dimensions and rituals to finish
dimensions = Dimension[]
rituals    = Dimension[]
push!(dimensions, Dimension(1, length(lines), mainnamespace, "main"))
@async while length(dimensions)>0
    print("") # io crutchfix??
    for i in 1:length(rituals)
        i > length(rituals) && break
        if rituals[i].name in tofinish
            pop!(tofinish, rituals[i].name)
            deleteat!(rituals, i)
        else
            if !rituals[i].busy
                rituals[i].busy = true
                @async dimension_forward!(rituals[i])
            end
        end
    end
end
while length(dimensions) > 0
    print("") # io crutchfix??
    global dimensions
    for i in 1:length(dimensions)
        i > length(dimensions) && break
        if dimensions[i].name in tofinish
            pop!(tofinish, dimensions[i].name)
            deleteat!(dimensions, i)
        else
            if !dimensions[i].busy
                dimensions[i].busy = true
                @async dimension_forward!(dimensions[i])
            end
        end
    end
end

max_stars_repo_name: Aetherlang/aetherlang

max_issues_repo_name: gojakuch/aetherlang

max_forks_repo_name: gojakuch/aetherlang


text:
struct KW{name, T}
    data::T

    KW{name, T}(x) where {name, T} = new{name::Symbol, T}(x)
end
KW{name}(x::T) where {name, T} = KW{name, T}(x)

name(::KW{n}) where {n} = n::Symbol
Base.get(kw::KW) = kw.data

Base.show(io::IO, kw::KW) = print(io, "@kw($(name(kw))=$(get(kw)))")

macro kw(expr)
    if !(expr isa Expr)
        error("@kw macro expects an expression like `name = value` or `(n1 = v1, n2 = v2, ...). Got $expr")
    end

    if iskw(expr)
        return :(KW{$(QuoteNode(expr.args[1]::Symbol))}($(esc(expr.args[2]))))
    elseif head.expr == :tuple
        return Expr(:tuple, map(ex -> :(@kw ex), expr.args))
    else
        error("@kw macro expects an expression like `name = value` or `(n1 = v1, n2 = v2, ...). Got $expr")
    end
end

# A special kind of tuple type which holds all the values

struct KWSentinal
end

const KWTuple = Tuple{KWSentinal, Vararg{KW}}

@inline Base.getindex(kwt::KWTuple, name::Symbol) = kwt[Val{name}]
@inline function Base.getindex(kwt::KWTuple, ::Type{Val{name}}) where name
    _get(Val{name}, kwt...)
end

@inline _get(::Type{Val{name}}, ::KWSentinal, kws...) where {name} = _get(Val{name}, kws...)
@inline _get(::Type{Val{name}}, ::KW, kws...) where {name} = _get(Val{name}, kws...)
@inline _get(::Type{Val{name}}, kw::KW{name}, kws...) where {name} = get(kw)
@inline _get(::Type{Val{name}}) where {name} = error("Cannot find keyword with binding $name")


text:
module RocketIterableObservableTest

using Test
using Rocket

include("../test_helpers.jl")

import Base: iterate, length, eltype

@testset "IterableObservable" begin

    println("Testing: iterable")

    struct CustomEmptyIterable end

    Base.iterate(::CustomEmptyIterable) = nothing
    Base.length(::CustomEmptyIterable) = 0
    Base.eltype(::Type{ <: CustomEmptyIterable }) = Int

    struct CustomSingleValueIterable end

    Base.iterate(::CustomSingleValueIterable) = (1, nothing)
    Base.iterate(::CustomSingleValueIterable, ::Nothing) = nothing
    Base.length(::CustomSingleValueIterable) = 1
    Base.eltype(::Type{ <: CustomSingleValueIterable }) = Int

    @testset begin
        source = iterable([ 0 ])
        io = IOBuffer()

        show(io, source)

        printed = String(take!(io))

        @test occursin("IterableObservable", printed)
        @test occursin(string(eltype(source)), printed)
    end

    run_testset([
        (
            source = iterable([ 1, 2, 3 ]),
            values = @ts([ 1, 2, 3, c ]),
            source_type = Int
        ),
        (
            source = iterable("Hello!"),
            values = @ts([ 'H', 'e', 'l', 'l', 'o', '!', c ]),
            source_type = Char
        ),
        (
            source = iterable("H"),
            values = @ts([ 'H', c ]),
            source_type = Char
        ),
        (
            source = iterable((0, 1, 2)),
            values = @ts([ 0, 1, 2, c ]),
            source_type = Int
        ),
        (
            source = iterable((0, 1, 2), scheduler = AsyncScheduler(0)),
            values = @ts([ 0 ] ~ [ 1 ] ~ [ 2 ] ~ c),
            source_type = Int
        ),
        (
            source = iterable(CustomEmptyIterable()),
            values = @ts(c),
            source_type = Int
        ),
        (
            source = iterable(CustomSingleValueIterable()),
            values = @ts([ 1, c ]),
            source_type = Int
        )
    ])

end

end


text:
module Comparison
using  ...Matching.Environment
using  ...PatternStructure.Trees
using  ...PatternStructure.Checks
using  ...PatternStructure.Special
using  ...PatternStructure.SlurpTypes
import Base: ==, ⊆, ⊇ ;
export compare_trees, conflicts, ⊇;

==(a::PatternTree, b::PatternTree) = compare_trees(a,b) == :equal
 ⊇(a::PatternTree, b::PatternTree) = compare_trees(a,b) in [:equal, :superset]
 ⊆(a::PatternTree, b::PatternTree) = compare_trees(a,b) in [:equal, :subset]

conflicts(a::PatternTree, b::PatternTree) = compare_trees(a,b) == :conflicts

abstract type PatternData{P} end

mutable struct PatternNodeData{P,N} <: PatternData{P}
  pattern::P
  index::Int
  repeats::N
  real_length::Int
end

struct PatternGenericData{P} <: PatternData{P}
  pattern::P
end

PatternData(p::PatternNode, args...) = PatternNodeData(p, args...)
PatternData(p) = PatternGenericData(p)

struct PatternCmp{P<:Union{PatternTree, PatternCheck}}
  data::PatternData{P}
  vars::Variables
  freeze::Bool

  function PatternCmp(p::P, vars=Variables(), freeze=false, repeats=1) where P <: PatternNode
    new{P}(PatternNodeData(p, 1, repeats, length(p.children)), vars, freeze)
  end

  function PatternCmp(p::P, vars=Variables(), freeze=false) where P
    new{P}(PatternGenericData(p), vars, freeze)
  end

  function PatternCmp(d::PatternData{P}, vars, freeze=false) where P
    new{P}(d, vars, freeze)
  end
end

pattern(p::PatternCmp) = p.data.pattern

macro cmp(a::Union{Expr,Symbol})
  @assert a isa Symbol || a.head == :.

  let root, fields_f
    dotroot(x::Expr, f=identity)   = dotroot(x.args[1], y->(Expr(:., y, f(x.args[2]))))
    dotroot(x::Symbol, f=identity) = x, y->f(:($x.data.pattern))

    root, fields_f = dotroot(a)

    esc(:($PatternCmp($(fields_f(root)), $root.vars, $root.freeze)))
  end
end

const PC = PatternCmp

Base.isempty(p::PC{PatternNode{H}}) where H =
  isempty(pattern(p).children)

real_length(p::PC{P}) where P <: PatternNode =
  length(pattern(p).children)

Base.length(p::PC{P}) where P <: PatternNode =
  real_length(p)*p.data.repeats

finished(p::PC{P}) where P <: PatternNode =
  p.data.index > length(p)

getchild(p::PC{P}) where P <: PatternNode =
  finished(p) ?
    throw(BoundsError(pattern(p).children, p.data.index)) :
    PatternCmp(pattern(p).children[max(1, p.data.index%(p.data.real_length+1))], p.vars, is_slurp(pattern(p)) || p.freeze)

function setchild!(p::PC{P}, i) where P <: PatternNode
  p.data.index = i
end

function next_child!(p::PC{P}) where P <: PatternNode
  p.data.index += 1
end

repeat(p::PatternCmp, repeats::Union{Int, Float64}) =
  PatternCmp(PatternData(pattern(p), p.data.index, repeats, real_length(p)), p.vars)

compare_trees(a::PatternTree, b::PatternTree) =
  compare_trees(PatternCmp(a), PatternCmp(b))

compare_trees(a::PC{PatternRoot}, b::PC{PatternRoot}) =
  compare_trees(@cmp(a.child), @cmp(b.child))

compare_trees(a::PC{PatternNode{H1}}, b::PC{PatternNode{H2}}) where {H1,H2} =
  compatible_heads(pattern(a).head, pattern(b).head) ?
    compare_children(a, b) : :unequal

function compare_children(a, b)
  if isempty(a)
    if isempty(b)
      return :equal
    else
      reverse_comparison_result(compare_children(b, a))
    end
  else
    let result=:equal, onfailure=Tuple{Int,Int,Symbol}[]
      while true

        if !finished(a) && !finished(b)
          result = update_result(result, compare_child(a, b, onfailure))

        elseif !finished(a)
          result = is_slurp(pattern(getchild(a))) ? update_result(result, :superset) : :unequal
          next_child!(a)

        elseif !finished(b)
          result = is_slurp(pattern(getchild(b))) ? update_result(result, :subset) : :unequal
          next_child!(b)

        else
          return result
        end

        if result in (:conflicts, :unequal)
          if isempty(onfailure)
            return result
          else
            a_index, b_index, result = pop!(onfailure)
            setchild!(a, a_index)
            setchild!(b, b_index)
          end
        end
      end
    end
  end
end

function compare_child(a, b, onfailure)
  ca = getchild(a)
  cb = getchild(b)

  isla, islb = is_slurp(pattern(ca)), is_slurp(pattern(cb))

  if isla && islb
    let result = compare_slurps(ca, cb)
      if result in (:equal, :subset, :superset)
        push!(onfailure, (a.data.index, b.data.index+1, :superset))
        push!(onfailure, (a.data.index+1, b.data.index, :subset))
        next_child!(a)
        next_child!(b)
        return result
      else
        return :unequal
      end
    end
  elseif isla
    ca = repeat(ca, Inf)
    while !finished(b) && compare_trees(getchild(ca), getchild(b)) in (:superset, :equal)
      push!(onfailure, (a.data.index+1, b.data.index, :superset))
      next_child!(ca)
      next_child!(b)
    end
    next_child!(a)
    return :superset

  elseif islb
    let rev_onfailure=Tuple{Int,Int,Symbol}[]
      compare_child(b, a, rev_onfailure)
      for onf in rev_onfailure
        push!(onfailure, (onf[2], onf[1], reverse_comparison_result(onf[3])))
      end
      return :subset
    end
  else
    next_child!(a)
    next_child!(b)
    compare_trees(ca, cb)
  end
end

function compare_slurps(a, b)
  if compatible_heads(pattern(a).head, pattern(b).head)
    if real_length(a) == real_length(b)
      compare_children(a, b)

    elseif (real_length(a) % real_length(b)) == 0
      res = compare_children(a, repeat(b, div(real_length(a), real_length(b))))
      res == :equal ? :superset : res

    elseif (real_length(b) % real_length(a)) == 0
      reverse_comparison_result(compare_slurps(b, a))

    else
      return :unequal
    end
  else
    return :unequal
  end
end

function compare_trees(a::PC{PatternGate}, b::PC{PatternGate})
  checks =  compare_checks(@cmp(a.check), @cmp(b.check))
  checks == :equal ?
    compare_trees(@cmp(a.child), @cmp(b.child)) : checks
end

function compare_trees(a::PC{PatternGate}, b)
  checks =  compare_checks(@cmp(a.check), b)
  checks == :superset ? :superset : :unequal
end

function compare_trees(a, b::PC{PatternGate})
  checks =  compare_checks(a, @cmp(b.check))
  checks == :subset ? :subset : :unequal
end

compare_checks(a::PC{Binding}, b::PC{Binding}) =
  a.freeze ? check_frozen_variable(a, b) :
  b.freeze ? check_frozen_variable(b, a) :
  match_variable!(a.vars, pattern(a).name, pattern(b).name) &&
  match_variable!(b.vars, pattern(b).name, pattern(a).name)  ? :equal : :unequal

compare_checks(a::PC{Binding}, b) =
  a.freeze ? check_frozen_variable(a, b) : check_and_match_variable(a, b)

compare_checks(a, b::PC{Binding}) = reverse_comparison_result(compare_checks(b, a))

check_frozen_variable(a, b) =
  !haskey(a.vars, pattern(a).name) || a.vars[pattern(a).name] == pattern(b) ? :superset : :unequal

check_and_match_variable(a, b) =
  match_variable!(a.vars, pattern(a).name, pattern(b)) ? :superset : :unequal

compare_checks(a::PC{EqualityCheck{T}}, b::PC{EqualityCheck{T}}) where T =
  pattern(a).value == pattern(b).value ? :equal : :unequal

compare_checks(a::PC{PredicateCheck}, b::PC{PredicateCheck}) =
  pattern(a).predicate == pattern(b).predicate ? :equal : :unequal

compare_checks(a::PC{TypeCheck{T1}}, b::PC{TypeCheck{T2}}) where {T1, T2} =
  T1 == T2 ? :equal    :
  T2 <: T1 ? :superset :
  T1 <: T2 ? :subset   :
             :unequal

compare_checks(a, b) = :unequal

compare_trees(a::PC{PatternLeaf}, b::PC{PatternLeaf}) = :equal
compare_trees(a, b) = :unequal

update_result(final, result) =
  result == :equal   ? final    :
  final  == :equal   ? result   :
  final  == :unequal ? :unequal :
  (result == :subset   && final == :superset) ||
  (result == :superset && final == :subset)   ?
    :conflicts : result

reverse_comparison_result(res) = res == :superset ? :subset : res == :subset ? :superset : res

compatible_heads(x::H, y::H) where H = x == y
compatible_heads(::S1, ::S2) where {S1 <: LazySlurp,   S2 <: LazySlurp}   = true
compatible_heads(::S1, ::S2) where {S1 <: GreedySlurp, S2 <: GreedySlurp} = true
compatible_heads(a,b) = false

end


text:
using Documenter
using Tissue

makedocs(
    sitename = "Tissue",
    format = Documenter.HTML(),
    modules = [Tissue],
    pages = [
        "Introduction" => "index.md",
        "Getting Started" => "getting_started.md",
        "API reference" => "api_reference.md",
    ],
)

deploydocs(
    repo = "github.com/plafer/Tissue.jl.git",
    push_preview = true,
)


text:
abstract Classical3dLattice{T} <: ClassicalLattice{T}

function getSpaceDimension(lattice::Classical3dLattice)
	return 3
end