text:
using Test
using DandelionWebSockets: SendBinaryFrame, ClientPingRequest, CloseRequest, SocketClosed
using DandelionWebSockets: protocolstate

@testset "Client to server       " begin
    @testset "Send single-frame text message; message is sent" begin
        # Requirement
        # @5_1-1 Client masks frame
        # @5_4-1 Unfragmented message
        # @6_1-3 Encapsulating data in a single frame message
        # @6_1-7 Masking client frames

        # Arrange
        mask = b"\x01\x02\x03\x04"
        logic, handler, writer = makeclientlogic(mask=mask)

        # Act
        handle(logic, SendTextFrame("Hello", true, OPCODE_TEXT))

        # Assert
        frame = getframeunmasked(writer, 1, mask)
        @test frame.payload == b"Hello"
        @test frame.opcode == OPCODE_TEXT
    end

    @testset "Send single-frame text message; FIN bit is set on first frame" begin
        # Requirement
        # @5_2-1 FIN bit is set on the first frame
        # @5_4-1 Unfragmented message

        # Arrange
        mask = b"\x01\x02\x03\x04"
        logic, handler, writer = makeclientlogic(mask=mask)

        # Act
        handle(logic, SendTextFrame("Hello", true, OPCODE_TEXT))

        # Assert
        frame = getframeunmasked(writer, 1, mask)
        @test frame.fin == true
    end

    @testset "Send single-frame binary message; message is sent" begin
        # Requirement
        # @5_1-1 Client masks frame

        # Arrange
        mask = b"\x01\x02\x03\x04"
        logic, handler, writer = makeclientlogic(mask=mask)

        # Act
        handle(logic, SendBinaryFrame(b"Hello", true, OPCODE_BINARY))

        # Assert
        frame = getframeunmasked(writer, 1, mask)
        @test frame.payload == b"Hello"
        @test frame.fin == true
        @test frame.opcode == OPCODE_BINARY
    end

    @testset "extended payload" begin
        @testset "payload size is 125; payload length is 125, no extended payload" begin
            # Requirement
            # @5_2-6 Payload length, 0-125 bytes

            # Arrange
            logic, handler, writer = makeclientlogic()

            # Act
            payload = zeros(UInt8, 125)
            handle(logic, SendBinaryFrame(payload, true, OPCODE_BINARY))

            # Assert
            frame = getframe(writer, 1)
            @test frame.len == 125
            @test frame.extended_len == 0
        end

        @testset "payload size is 126; payload length is 126, extended payload is 126" begin
            # Requirement
            # @5_2-7 Payload length, 126 bytes

            # Arrange
            logic, handler, writer = makeclientlogic()

            # Act
            payload = zeros(UInt8, 126)
            handle(logic, SendBinaryFrame(payload, true, OPCODE_BINARY))

            # Assert
            frame = getframe(writer, 1)
            # When the frames len field says 126, then that means a 16-bit extended length.
            @test frame.len == 126
            @test frame.extended_len == 126
        end

        @testset "payload size is 127; payload length is 126, extended payload is 127" begin
            # Requirement
            # @5_2-8 Payload length, 127 bytes

            # Arrange
            logic, handler, writer = makeclientlogic()

            # Act
            payload = zeros(UInt8, 127)
            handle(logic, SendBinaryFrame(payload, true, OPCODE_BINARY))

            # Assert
            frame = getframe(writer, 1)
            # When the frames len field says 126, then that means a 16-bit extended length.
            @test frame.len == 126
            @test frame.extended_len == 127
        end

        @testset "payload size is 128; payload length is 126, extended payload is 128" begin
            # Requirement
            # @5_2-9 Minimal encoding of payload length

            # Arrange
            logic, handler, writer = makeclientlogic()

            # Act
            payload = zeros(UInt8, 128)
            handle(logic, SendBinaryFrame(payload, true, OPCODE_BINARY))

            # Assert
            frame = getframe(writer, 1)
            # When the frames len field says 126, then that means a 16-bit extended length.
            @test frame.len == 126
            @test frame.extended_len == 128
        end

        @testset "payload size is 256; extended payload length is 256" begin
            # Requirement
            # @5_2-9 Minimal encoding of payload length

            # Arrange
            logic, handler, writer = makeclientlogic()

            # Act
            payload = zeros(UInt8, 256)
            handle(logic, SendBinaryFrame(payload, true, OPCODE_BINARY))

            # Assert
            frame = getframe(writer, 1)
            @test frame.len == 126
            @test frame.extended_len == 256
        end

        @testset "payload size 65535 (0xFFFF); extended payload length is 65536" begin
            # Requirement
            # @5_2-9 Minimal encoding of payload length

            # This is the max payload size representable by the 16-bit extended length,
            # where len is 126.
            # Arrange
            logic, handler, writer = makeclientlogic()

            # Act
            payload = zeros(UInt8, 65535)
            handle(logic, SendBinaryFrame(payload, true, OPCODE_BINARY))

            # Assert
            frame = getframe(writer, 1)
            @test frame.len == 126
            @test frame.extended_len == 65535
        end

        @testset "payload size is 65536; len is 127 and extended length is 65536" begin
            # Requirement
            # @5_2-9 Minimal encoding of payload length

            # This is one byte more than the max payload size representable by the 16-bit extended
            # length, and then len is 127 and the 64-bit extended length is used.
            # Arrange
            logic, handler, writer = makeclientlogic()

            # Act
            payload = zeros(UInt8, 65536)
            handle(logic, SendBinaryFrame(payload, true, OPCODE_BINARY))

            # Assert
            frame = getframe(writer, 1)
            @test frame.len == 127
            @test frame.extended_len == 65536
        end
    end

    @testset "send a message in two fragments; two fragments are written" begin
        mask1 = b"\x01\x02\x03\x04"
        local mask2 = b"\x05\x06\x07\x08"
        logic, handler, writer = makeclientlogic(mask=[mask1; mask2])

        handle(logic, SendTextFrame("Hel", false, OPCODE_TEXT))
        handle(logic, SendTextFrame("lo", true, OPCODE_TEXT))

        frame1 = getframeunmasked(writer, 1, mask1)
        @test frame1.opcode == OPCODE_TEXT
        @test frame1.payload == b"Hel"
        @test frame1.fin == false

        frame2 = getframeunmasked(writer, 2, mask2)
        @test frame2.opcode == OPCODE_TEXT
        @test frame2.payload == b"lo"
        @test frame2.fin == true
    end

    @testset "connection is in CLOSING, trying to send a message; no message is sent" begin
        # Requirement
        # @5_5_1-4 No frames after Close frame
        # @6_1-1   Sending data only on OPEN connections
        # @6_1-2   Connection closes when sending data

        logic, handler, writer = makeclientlogic()
        handle(logic, CloseRequest())
        clearframeswritten(writer)

        handle(logic, SendTextFrame("Hello", true, OPCODE_TEXT))

        @test get_no_of_frames_written(writer) == 0
    end

    @testset "connection is in CLOSED, trying to send a message; no message is sent" begin
        # Requirement
        # @5_5_1-4 No frames after Close frame
        # @6_1-1   Sending data only on OPEN connections
        # @6_1-2   Connection closes when sending data

        logic, handler, writer = makeclientlogic()
        handle(logic, CloseRequest())
        handle(logic, FrameFromServer(closeframe_from_server()))
        handle(logic, SocketClosed())
        clearframeswritten(writer)

        handle(logic, SendTextFrame("Hello", true, OPCODE_TEXT))

        @test get_no_of_frames_written(writer) == 0
    end

    @testset "sending a ping to the server; Ponger is made aware that a ping was sent" begin
        logic, handler, writer, ponger = makeclientlogic()

        handle(logic, ClientPingRequest())

        @test ponger.no_of_pings_sent == 1
    end

    @testset "sending a ping to the server; a ping frame is sent" begin
        # Requirement
        # @5_5_2-4 Ping frame validity

        logic, handler, writer = makeclientlogic()

        handle(logic, ClientPingRequest())

        frame = getframe(writer, 1)
        @test frame.opcode == OPCODE_PING
    end

    @testset "pings are not sent in non-open states" begin
        @testset "state is CLOSING; no ping is sent" begin
            # Requirement
            # @5_5_2-2 Pong response

            logic, handler, writer, ponger = makeclientlogic()
            handle(logic, CloseRequest())
            clearframeswritten(writer)

            handle(logic, ClientPingRequest())

            @test ponger.no_of_pings_sent == 0
            @test get_no_of_frames_written(writer) == 0
        end

        @testset "state is CLOSED; no ping is sent" begin
            # Requirement
            # @5_5_2-2 Pong response

            logic, handler, writer, ponger = makeclientlogic()
            handle(logic, CloseRequest())
            handle(logic, FrameFromServer(closeframe_from_server()))
            handle(logic, SocketClosed())
            clearframeswritten(writer)

            handle(logic, ClientPingRequest())

            @test ponger.no_of_pings_sent == 0
            @test get_no_of_frames_written(writer) == 0
        end
    end
end


text:
# This file is a part of Julia. License is MIT: https://julialang.org/license

module Order

import ..@__MODULE__, ..parentmodule
const Base = parentmodule(@__MODULE__)
import .Base:
    AbstractVector, @propagate_inbounds, isless, identity, getindex, reverse,
    +, -, !, &, <, |

## notions of element ordering ##

export # not exported by Base
    Ordering, Forward, Reverse,
    By, Lt, Perm,
    ReverseOrdering, ForwardOrdering,
    DirectOrdering,
    lt, ord, ordtype

"""
    Base.Order.Ordering

Abstract type which represents a total order on some set of elements.

Use [`Base.Order.lt`](@ref) to compare two elements according to the ordering.
"""
abstract type Ordering end

struct ForwardOrdering <: Ordering end

"""
    ReverseOrdering(fwd::Ordering=Forward)

A wrapper which reverses an ordering.

For a given `Ordering` `o`, the following holds for all  `a`, `b`:

    lt(ReverseOrdering(o), a, b) == lt(o, b, a)
"""
struct ReverseOrdering{Fwd<:Ordering} <: Ordering
    fwd::Fwd
end

ReverseOrdering(rev::ReverseOrdering) = rev.fwd
ReverseOrdering(fwd::Fwd) where {Fwd} = ReverseOrdering{Fwd}(fwd)
ReverseOrdering() = ReverseOrdering(ForwardOrdering())

"""
    reverse(o::Base.Ordering)

reverses ordering specified by `o`.

"""
reverse(o::Ordering) = ReverseOrdering(o)

const DirectOrdering = Union{ForwardOrdering,ReverseOrdering{ForwardOrdering}}

"""
    Base.Order.Forward

Default ordering according to [`isless`](@ref).
"""
const Forward = ForwardOrdering()

"""
    Base.Order.Reverse

Reverse ordering according to [`isless`](@ref).
"""
const Reverse = ReverseOrdering()

"""
    By(by, order::Ordering=Forward)

`Ordering` which applies `order` to elements after they have been transformed
by the function `by`.
"""
struct By{T, O} <: Ordering
    by::T
    order::O
end

# backwards compatibility with VERSION < v"1.5-"
By(by) = By(by, Forward)

"""
    Lt(lt)

`Ordering` which calls `lt(a, b)` to compare elements. `lt` should
obey the same rules as implementations of [`isless`](@ref).
"""
struct Lt{T} <: Ordering
    lt::T
end

"""
    Perm(order::Ordering, data::AbstractVector)

`Ordering` on the indices of `data` where `i` is less than `j` if `data[i]` is
less than `data[j]` according to `order`. In the case that `data[i]` and
`data[j]` are equal, `i` and `j` are compared by numeric value.
"""
struct Perm{O<:Ordering,V<:AbstractVector} <: Ordering
    order::O
    data::V
end

ReverseOrdering(by::By) = By(by.by, ReverseOrdering(by.order))
ReverseOrdering(perm::Perm) = Perm(ReverseOrdering(perm.order), perm.data)

"""
    lt(o::Ordering, a, b)

Test whether `a` is less than `b` according to the ordering `o`.
"""
lt(o::ForwardOrdering,       a, b) = isless(a,b)
lt(o::ReverseOrdering,       a, b) = lt(o.fwd,b,a)
lt(o::By,                    a, b) = lt(o.order,o.by(a),o.by(b))
lt(o::Lt,                    a, b) = o.lt(a,b)

@propagate_inbounds function lt(p::Perm, a::Integer, b::Integer)
    da = p.data[a]
    db = p.data[b]
    lt(p.order, da, db) | (!lt(p.order, db, da) & (a < b))
end

_ord(lt::typeof(isless), by::typeof(identity), order::Ordering) = order
_ord(lt::typeof(isless), by,                   order::Ordering) = By(by, order)

function _ord(lt, by, order::Ordering)
    if order === Forward
        return Lt((x, y) -> lt(by(x), by(y)))
    elseif order === Reverse
        return Lt((x, y) -> lt(by(y), by(x)))
    else
        error("Passing both lt= and order= arguments is ambiguous; please pass order=Forward or order=Reverse (or leave default)")
    end
end

"""
    ord(lt, by, rev::Union{Bool, Nothing}, order::Ordering=Forward)

Construct an [`Ordering`](@ref) object from the same arguments used by
[`sort!`](@ref).
Elements are first transformed by the function `by` (which may be
[`identity`](@ref)) and are then compared according to either the function `lt`
or an existing ordering `order`. `lt` should be [`isless`](@ref) or a function
which obeys similar rules. Finally, the resulting order is reversed if
`rev=true`.

Passing an `lt` other than `isless` along with an `order` other than
[`Base.Order.Forward`](@ref) or [`Base.Order.Reverse`](@ref) is not permitted,
otherwise all options are independent and can be used together in all possible
combinations.
"""
ord(lt, by, rev::Nothing, order::Ordering=Forward) = _ord(lt, by, order)

function ord(lt, by, rev::Bool, order::Ordering=Forward)
    o = _ord(lt, by, order)
    return rev ? ReverseOrdering(o) : o
end

# This function is not in use anywhere in Base but we observed
# use in sorting-related packages (#34719). It's probably best to move
# this functionality to those packages in the future; let's remind/force
# ourselves to deprecate this in v2.0.
# The following clause means `if VERSION < v"2.0-"` but it also works during
# bootstrap. For the same reason, we need to write `Int32` instead of `Cint`.
if ccall(:jl_ver_major, Int32, ()) < 2
    ordtype(o::ReverseOrdering, vs::AbstractArray) = ordtype(o.fwd, vs)
    ordtype(o::Perm,            vs::AbstractArray) = ordtype(o.order, o.data)
    # TODO: here, we really want the return type of o.by, without calling it
    ordtype(o::By,              vs::AbstractArray) = try typeof(o.by(vs[1])) catch; Any end
    ordtype(o::Ordering,        vs::AbstractArray) = eltype(vs)
end

end

ext: jl

max_stars_repo_name: BSnelling/julia

max_issues_repo_name: BSnelling/julia

max_forks_repo_name: BSnelling/julia


text:
module Intersections

using FromFile: @from
using StaticArrays: SVector, SMatrix
using LinearAlgebra: cross, normalize, norm, dot
@from "OpticalInterfaces.jl" import OpticalInterfaces:
    #     OpticalInterface,
    #     InterfaceMode,
    #     Reflect,
    #     Transmit,
    #     ReflectOrTransmit,
    #     NullInterface,
    #     FresnelInterface,
    #     opaqueinterface,
    #     NullOrFresnel,
    AllOpticalInterfaces
@from "Transforms.jl" import Transforms: Transform, identitytransform, rotate
@from "Rays.jl" import Rays

# ============================================================================ #
# 88 88b 88 888888 888888 88""Yb .dP"Y8 888888  dP""b8 888888 88  dP"Yb  88b 88
# 88 88Yb88   88   88__   88__dP `Ybo." 88__   dP   `"   88   88 dP   Yb 88Yb88
# 88 88 Y88   88   88""   88"Yb  o.`Y8b 88""   Yb        88   88 Yb   dP 88 Y88
# 88 88  Y8   88   888888 88  Yb 8bodP' 888888  YboodP   88   88  YbodP  88  Y8
# ============================================================================ #

"""
Each [`Interval`](@ref) consists of two `IntervalPoint`s, one of
[`RayOrigin`](@ref), [`Intersection`](@ref) or [`Infinity`](@ref).
"""
abstract type IntervalPoint{T <: Real} end
Base.eltype(::IntervalPoint{T}) where {T <: Real} = T

abstract type FinitePoint{T} <: IntervalPoint{T} end
Base.eltype(::FinitePoint{T}) where {T <: Real} = T

"""
    Intersection{T,N} <: IntervalPoint{T}

Represents the point at which an [`Ray`](@ref) hits a [`Surface`](@ref).
This consists of the distance along the ray, the intersection point in world
space, the normal in world space, the UV on the surface and the [`OpticalInterface`](@ref) hit.

Has the following accessor methods:
```julia
point(a::Intersection{T,N}) -> SVector{N,T} # NOTE: this is in Surfaces.jl now
normal(a::Intersection{T,N}) -> SVector{N,T}
uv(a::Intersection{T,N}) -> SVector{2,T}
u(a::Intersection{T,N}) -> T
v(a::Intersection{T,N}) -> T
α(a::Intersection{T,N}) -> T
interface(a::Intersection{T,N}) -> OpticalInterface{T}
flippednormal(a::Intersection{T,N}) -> Bool
```
"""
struct Intersection{T, N} <: FinitePoint{T}
    α::T # value of ray parameter at the point of intersection
    point::SVector{N, T}
    normal::SVector{N, T}
    u::T
    v::T
    interface::AllOpticalInterfaces{T} # returns a union of all OpticalInterface
    # subtypes - can't have an abstract type here as it results in allocations
    flippednormal::Bool

    function Intersection(
        α::T,
        point::SVector{N, T},
        normal::SVector{N, T},
        u::T,
        v::T,
        interface::AllOpticalInterfaces{T};
        flippednormal = false,
    ) where {T <: Real, N}
        return new{T, N}(
            α,
            point,
            normalize(normal),
            u,
            v,
            interface,
            flippednormal,
        )
    end

    function Intersection(
        α::T,
        point::AbstractVector{T},
        normal::AbstractVector{T},
        u::T,
        v::T,
        interface::AllOpticalInterfaces{T};
        flippednormal = false,
    ) where {T <: Real}
        @assert length(point) == length(normal)
        N = length(point)
        return new{T, N}(
            α,
            SVector{N, T}(point),
            SVector{N, T}(normal),
            u,
            v,
            interface,
            flippednormal,
        )
    end
end

normal(a::Intersection{T, N}) where {T <: Real, N} = a.normal
uv(a::Intersection{T, N}) where {T <: Real, N} = SVector{2, T}(a.u, a.v)
u(a::Intersection{T, N}) where {T <: Real, N} = a.u
v(a::Intersection{T, N}) where {T <: Real, N} = a.v
Rays.α(a::Intersection{T, N}) where {T <: Real, N} = a.α
interface(a::Intersection{T, N}) where {T <: Real, N} = a.interface
flippednormal(a::Intersection{T, N}) where {T <: Real, N} = a.flippednormal

function Base.print(io::IO, a::Intersection{T, N}) where {T <: Real, N}
    println(io, Intersection{T, N})
    println(io, "α \t$(Rays.α(a))")
    println(io, "Point \t$(a.point)")
    println(io, "Normal \t$(normal(a))")
    println(io, "normal is flipped? \t$(flippednormal(a))")
    println(io, "u \t$(u(a))")
    println(io, "v \t$(v(a))")
    return println(io, "interface \t$(interface(a))")
end

"""
    reversenormal(a::Intersection{T,N})

Used by the CSG complement operator (i.e. [`-`](@ref)) to reverse the inside
outside sense of the object.
"""
function reversenormal(a::Intersection{T, N}) where {T <: Real, N}
    return Intersection(
        Rays.α(a),
        a.point,
        -normal(a),
        u(a),
        v(a),
        interface(a),
        flippednormal = !flippednormal(a),
    )
end

"""
    Infinity{T} <: IntervalPoint{T}

Point representing ∞ within an [`Interval`](@ref).

```julia
Infinity(T = Float64)
Infinity{T}()
```
"""
struct Infinity{T} <: IntervalPoint{T}
    Infinity(::Type{T} = Float64) where {T <: Real} = new{T}()
    Infinity{T}() where {T <: Real} = new{T}()
end
"""
    RayOrigin{T} <: IntervalPoint{T}

Point representing 0 within an [`Interval`](@ref), i.e. the start of the ray.

```julia
RayOrigin(T = Float64)
RayOrigin{T}()
```
"""
struct RayOrigin{T} <: FinitePoint{T}
    RayOrigin(::Type{T} = Float64) where {T <: Real} = new{T}()
    RayOrigin{T}() where {T <: Real} = new{T}()
end

Base.:(==)(::RayOrigin{P}, ::RayOrigin{P}) where {P <: Real} = true
Base.:(==)(::Infinity{P}, ::Infinity{P}) where {P <: Real} = true
function Base.:(==)(
    a::Intersection{P, N},
    b::Intersection{P, N},
) where {P <: Real, N}
    return Rays.α(a) == Rays.α(b)
end
Base.:(==)(::IntervalPoint{P}, ::IntervalPoint{P}) where {P <: Real} = false

Base.:(<=)(::RayOrigin{P}, ::RayOrigin{P}) where {P <: Real} = true
Base.:(<=)(::Infinity{P}, ::Infinity{P}) where {P <: Real} = true
Base.:(<=)(a::IntervalPoint{P}, b::Infinity{P}) where {P <: Real} = true
function Base.:(<=)(
    a::Intersection{P, N},
    b::Intersection{P, N},
) where {P <: Real, N}
    return Rays.α(a) <= Rays.α(b)
end
Base.:(<=)(a::RayOrigin{P}, b::Infinity{P}) where {P <: Real} = true
Base.:(<=)(a::RayOrigin{P}, b::IntervalPoint{P}) where {P <: Real} = true
Base.:(<=)(::IntervalPoint{P}, ::IntervalPoint{P}) where {P <: Real} = false

Base.:(>=)(::RayOrigin{P}, ::RayOrigin{P}) where {P <: Real} = true
Base.:(>=)(::Infinity{P}, ::Infinity{P}) where {P <: Real} = true
Base.:(>=)(a::IntervalPoint{P}, b::Infinity{P}) where {P <: Real} = false
function Base.:(>=)(
    a::Intersection{P, N},
    b::Intersection{P, N},
) where {P <: Real, N}
    return Rays.α(a) >= Rays.α(b)
end
Base.:(>=)(a::RayOrigin{P}, b::Infinity{P}) where {P <: Real} = false
Base.:(>=)(a::RayOrigin{P}, b::IntervalPoint{P}) where {P <: Real} = false
Base.:(>=)(::IntervalPoint{P}, ::IntervalPoint{P}) where {P <: Real} = false

Base.:(<)(::Infinity{P}, ::Infinity{P}) where {P <: Real} = false
Base.:(<)(::IntervalPoint{P}, ::Infinity{P}) where {P <: Real} = true
function Base.:(<)(
    a::Intersection{P, N},
    b::Intersection{P, N},
) where {P <: Real, N}
    return Rays.α(a) < Rays.α(b)
end

Base.:(>)(::Infinity{P}, ::Infinity{P}) where {P <: Real} = false
Base.:(>)(::IntervalPoint{P}, ::Infinity{P}) where {P <: Real} = false
function Base.:(>)(
    a::Intersection{P, N},
    b::Intersection{P, N},
) where {P <: Real, N}
    return Rays.α(a) > Rays.α(b)
end

function Base.isless(a::IntervalPoint{P}, b::IntervalPoint{P}) where {P <: Real}
    return Rays.α(a) < Rays.α(b)
end

Base.eltype(::Intersection{T, N}) where {T <: Real, N} = T

"""
    isinfinity(a) -> Bool

Returns true if `a` is [`Infinity`](@ref). In performance critical contexts use
`a isa Infinity{T}`.

"""
isinfinity(::Infinity) = true
isinfinity(::Any) = false

Rays.α(::Infinity{T}) where {T <: AbstractFloat} = typemax(T)
Base.eltype(::Infinity{T}) where {T <: Real} = T

"""
    israyorigin(a) -> Bool

Returns true if `a` is [`RayOrigin`](@ref). In performance critical contexts use
`a isa RayOrigin{T}`.

"""
israyorigin(::Any) = false
israyorigin(::RayOrigin) = true

Rays.α(::RayOrigin{T}) where {T <: Real} = zero(T)
Base.eltype(::RayOrigin{T}) where {T <: Real} = T

"""
Apply a Transform to an Intersection object
"""
function Base.:*(
    a::Transform{T},
    intsct::Intersection{T, 3},
)::Intersection{T, 3} where {T <: Real}
    u, v = uv(intsct)
    i = interface(intsct)
    return Intersection(
        Rays.α(intsct),
        a * intsct.point,
        rotate(a, normal(intsct)),
        u,
        v,
        interface(intsct),
        flippednormal = flippednormal(intsct),
    )
end

# ============================================================================ #
#        88 88b 88 888888 888888 88""Yb Yb    dP    db    88
#        88 88Yb88   88   88__   88__dP  Yb  dP    dPYb   88
#        88 88 Y88   88   88""   88"Yb    YbdP    dP__Yb  88  .o
#        88 88  Y8   88   888888 88  Yb    YP    dP""""Yb 88ood8
# ============================================================================ #

# MIT license
# Copyright (c) Microsoft Corporation. All rights reserved.
# See LICENSE in the project root for full license information.

abstract type AbstractRayInterval{T <: Real} end

"""
    EmptyInterval{T} <: AbstractRayInterval{T}

An interval with no [`Intersection`](@ref)s which is also not infinite.

```
EmptyInterval(T = Float64)
EmptyInterval{T}()
```
"""
struct EmptyInterval{T} <: AbstractRayInterval{T}
    EmptyInterval(::Type{T} = Float64) where {T <: Real} = new{T}()
    EmptyInterval{T}() where {T <: Real} = new{T}()
end

"""
    Interval{T} <: AbstractRayInterval{T}

Datatype representing an interval between two [`IntervalPoint`](@ref)s on a ray.

The lower element can either be [`RayOrigin`](@ref) or an [`Intersection`](@ref).
The upper element can either be an [`Intersection`](@ref) or [`Infinity`](@ref).

```julia
positivehalfspace(int::Intersection) ->
    Interval with lower = int, upper = Infinity
rayorigininterval(int::Intersection) ->
    Interval with lower = RayOrigin, upper = int
Interval(low, high)
```

Has the following accessor methods:
```julia
lower(a::Interval{T}) -> Union{RayOrigin{T},Intersection{T,3}}
upper(a::Interval{T}) -> Union{Intersection{T,3},Infinity{T}}
```
"""
struct Interval{R} <: AbstractRayInterval{R}
    lower::Union{RayOrigin{R}, Intersection{R, 3}} #making this a Union of two
    # concrete types allows the compiler to statically enough space to hold the
    # larger of the two. The entire Interval struct can then be stack allocated.
    upper::Union{Intersection{R, 3}, Infinity{R}} #same as above

    function Interval(
        low::S,
        high::T,
    ) where {
        R <: Real,
        S <: Union{RayOrigin{R}, Intersection{R, 3}},
        T <: Union{Intersection{R, 3}, Infinity{R}},
    }
        @assert low <= high
        return new{R}(low, high)
    end
end

function Base.show(io::IO, a::Interval{R}) where {R <: Real}
    println(io, Interval{R})
    println(io, "\tLower \n\t\t$(lower(a))")
    return println(io, "\tUpper \n\t\t$(upper(a))")
end

Base.eltype(::Interval{R}) where {R} = R

lower(a::Interval) = a.lower
upper(a::Interval) = a.upper

# ============================================================================ #

"""
To prevent allocations we have a manually managed pool of arrays of
[`Interval`](@ref)s which are used to store values during execution.
The memory is kept allocated and reused across runs of functions like
[`trace`](@ref).

`threadedintervalpool` is a global threadsafe pool which is accessed through
the functions:
```julia
newinintervalpool!(::Type{T} = Float64, tid::Int = Threads.threadid(
    )) -> Vector{Interval{T}}
indexednewinintervalpool!(::Type{T} = Float64, tid::Int = Threads.threadid(
    )) -> Tuple{Int,Vector{Interval{T}}}
emptyintervalpool!(::Type{T} = Float64, tid::Int = Threads.threadid())
getfromintervalpool([::Type{T} = Float64], id::Int, tid::Int = Threads.threadid(
    )) -> Vector{Interval{T}}
```
"""
struct IntervalPool{T <: Real}
    allocated::Vector{Vector{Interval{T}}}
    unallocated::Vector{Vector{Interval{T}}}

    function IntervalPool{T}() where {T <: Real}
        return new{T}(
            Vector{Vector{Interval{T}}}(),
            Vector{Vector{Interval{T}}}(),
        )
    end
end

function allocate!(a::IntervalPool{T}) where {T <: Real}
    if length(a.unallocated) === 0
        #this will extend the array with a new Vector of intervals.
        push!(a.unallocated, Vector{Interval{T}}())
    end

    temp = pop!(a.unallocated)
    Base.empty!(temp) #resets count in array but doesn't reclaim space
    push!(a.allocated, temp)
    return temp
end

function empty!(a::IntervalPool{T}) where {T <: Real}
    while length(a.allocated) > 0
        temp = pop!(a.allocated)
        Base.empty!(temp) #not strictly necessary since allocate will reset
        # this array to empty before pushing it back on allocated stack. But this
        # ensures the interval pool is in a consistent emptied state upon
        # completion of this function.
        push!(a.unallocated, temp)
    end
end

# Allocate a zero length array which will be filled with Threads.nthreads()
# entries by the __init__ method for OpticSim module. Have to do this in the
#    __init__ method because this captures the load time environment. const
#    values are evaluated at precompile time and the number of threads in these
#    two environments can be different.
const threadedintervalpool = Vector{Dict{DataType, IntervalPool}}()
#const threadedintervalpool = [Dict{DataType,IntervalPool}(
# [Float64 => IntervalPool{Float64}()]) for _ in 1:Threads.nthreads()]

function newinintervalpool!(
    ::Type{T} = Float64,
    tid::Int = Threads.threadid(),
)::Vector{Interval{T}} where {T <: Real}
    if T ∉ keys(threadedintervalpool[tid])
        # if the type of the interval pool has changed then we need to refill
        # it with the correct type
        threadedintervalpool[tid][T] = IntervalPool{T}()
    end
    return allocate!(threadedintervalpool[tid][T])
end

function indexednewinintervalpool!(
    ::Type{T} = Float64,
    tid::Int = Threads.threadid(),
)::Tuple{Int, Vector{Interval{T}}} where {T <: Real}
    a = newinintervalpool!(T, tid)
    index = length(threadedintervalpool[tid][T].allocated)
    return index, a
end

function emptyintervalpool!(
    ::Type{T} = Float64,
    tid::Int = Threads.threadid(),
) where {T}
    if T ∈ keys(threadedintervalpool[tid])
        empty!(threadedintervalpool[tid][T])
    end
end

getfromintervalpool(
    id::Int,
    tid::Int = Threads.threadid(),
)::Vector{Interval{Float64}} = getfromintervalpool(Float64, id, tid)

function getfromintervalpool(
    ::Type{T},
    id::Int,
    tid::Int = Threads.threadid(),
)::Vector{Interval{T}} where {T <: Real}
    return threadedintervalpool[tid][T].allocated[id]
end

# ============================================================================ #

macro inplaceinsertionsort(array, f)
    return esc(quote
        for i in 2:length($array)
            value = $array[i]
            j = i - 1
            while j > 0 && $f($array[j]) > $f(value)
                $array[j + 1] = $array[j]
                j = j - 1
            end
            $array[j + 1] = value
        end
    end)
end

"""
Datatype representing an ordered series of disjoint intervals on a ray.
An arbitrary array of `Interval`s can be input to the constructor and they will
automatically be processed into a valid `DisjointUnion`
(or a single [`Interval`](@ref) if appropriate).

```julia
DisjointUnion(intervals::AbstractVector{Interval{R}})
```
"""
struct DisjointUnion{T <: Real}
    intervalarrayindex::Int

    function DisjointUnion(
        a::Interval{R},
        b::Interval{R},
    )::Union{DisjointUnion{R}, Interval{R}} where {R <: Real}
        temp = newinintervalpool!(R)
        push!(temp, a)
        push!(temp, b)
        return DisjointUnion(temp)
    end

    function DisjointUnion(
        intervals::AbstractVector{Interval{R}},
    )::Union{DisjointUnion{R}, Interval{R}} where {R <: Real}
        if length(intervals) === 1
            return intervals[1]
        end

        @inplaceinsertionsort(intervals, upper)

        index, result = indexednewinintervalpool!(R)

        while length(intervals) > 0
            current = pop!(intervals)

            while length(intervals) > 0
                temp = intervalintersection(current, last(intervals))
                if !(temp isa EmptyInterval{R})
                    current = intervalunion(current, last(intervals))
                    pop!(intervals)
                else
                    break
                end
            end

            push!(result, current)
        end

        if length(result) === 1
            return result[1]
        else
            @inplaceinsertionsort(result, lower)
            return new{R}(index)
        end
    end
end

function intervals(a::DisjointUnion{R}) where {R <: Real}
    return getfromintervalpool(R, a.intervalarrayindex)
end
function Base.show(io::IO, a::DisjointUnion{R}) where {R <: Real}
    println(io, DisjointUnion{R})
    for i in intervals(a)
        show(io, i)
    end
end

Base.getindex(a::DisjointUnion, index::Int) = intervals(a)[index]
Base.firstindex(a::DisjointUnion) = firstindex(intervals(a))
Base.lastindex(a::DisjointUnion) = lastindex(intervals(a))
Base.length(a::DisjointUnion) = length(intervals(a))
function Base.:(==)(a::DisjointUnion, b::DisjointUnion)
    return intervals(a) == intervals(b)
end
# this works so long as the elements in the intervals array are structs
# and not arrays.

# ============================================================================ #

"""
    isemptyinterval(a) -> Bool

Returns true if `a` is an [`EmptyInterval`](@ref). In performance critical
contexts use `a isa EmptyInterval{T}`.

"""
isemptyinterval(::EmptyInterval) = true
isemptyinterval(::Interval) = false
isemptyinterval(::DisjointUnion) = false

"""
    ispositivehalfspace(a) -> Bool

Returns true if `upper(a)` is [`Infinity`](@ref). In performance critical
contexts check directly i.e. `upper(a) isa Infinity{T}`.

"""
function ispositivehalfspace(a::Interval{T}) where {T <: Real}
    return upper(a) isa Infinity{T}
end

"""
    israyorigininterval(a) -> Bool

Returns true if `lower(a)` is [`RayOrigin`](@ref). In performance critical
contexts check directly i.e. `lower(a) isa RayOrigin{T}`.

"""
function israyorigininterval(a::Interval{T}) where {T <: Real}
    return lower(a) isa RayOrigin{T}
end

function isinfiniteinterval(a::Interval{T}) where {T <: Real}
    return israyorigininterval(a) && ispositivehalfspace(a)
end

function positivehalfspace(a::RayOrigin{T}) where {T <: Real}
    return Interval(a, Infinity(T))
end
function positivehalfspace(a::Intersection{T, N}) where {T <: Real, N}
    return Interval(a, Infinity(T))
end

function rayorigininterval(a::Intersection{T, N}) where {T <: Real, N}
    return Interval(RayOrigin(T), a)
end # special kind of interval that has ray origin as lower bound.
# α(a::RayOrigin) will always return 0 so CSG operations should work.
function rayorigininterval(a::Infinity{T}) where {T <: Real}
    return Interval(RayOrigin(T), a)
end

"""
    halfspaceintersection(a::Interval{T}) -> Intersection{T,3}

Returns the [`Intersection`](@ref) from a half space [`Interval`](@ref),
throws an error if not a half space.
"""
function halfspaceintersection(
    a::Interval{T},
)::Intersection{T, 3} where {T <: Real}
    la = lower(a)
    ua = upper(a)
    if ua isa Infinity{T} && !(la isa RayOrigin{T})
        return la
    elseif la isa RayOrigin{T} && !(ua isa Infinity{T})
        return ua
    else
        return throw(ErrorException("Not a half-space: $a"))
    end
end

"""
    closestintersection(a::Union{EmptyInterval{T},Interval{T},DisjointUnion{T}},
    ignorenull::Bool = true) -> Union{Nothing,Intersection{T,3}}

Returns the closest [`Intersection`](@ref) from an [`Interval`](@ref) or
[`DisjointUnion`](@ref). Ignores intersection with null interfaces if
`ignorenull` is true. Will return `nothing` if there is no valid intersection.
"""
closestintersection(::EmptyInterval, ::Bool = true) = nothing
function closestintersection(
    a::Interval{T},
    ignorenull::Bool = true,
)::Union{Nothing, Intersection{T, 3}} where {T <: Real}
    la = lower(a)
    ua = upper(a)
    if la isa RayOrigin{T}
        if !(ua isa Infinity{T}) &&
           !(ignorenull && interface(ua) isa NullInterface{T})
            return ua
        else
            return nothing
        end
    elseif !(ignorenull && interface(la) isa NullInterface{T})
        return la
    else
        return nothing
    end
end
function closestintersection(
    a::DisjointUnion{T},
    ignorenull::Bool = true,
)::Union{Nothing, Intersection{T, 3}} where {T <: Real}
    for i in intervals(a)
        c = closestintersection(i, ignorenull)
        if c !== nothing
            return c
        end
    end
    return nothing
end

function reversenormal(a::Interval{T})::Interval{T} where {T <: Real}
    la = lower(a)
    ua = upper(a)
    if la isa RayOrigin{T}
        if ua isa Infinity{T}
            return a
        else
            return Interval(la, reversenormal(ua))
        end
    else
        if ua isa Infinity{T}
            return Interval(reversenormal(la), ua)
        else
            return Interval(reversenormal(la), reversenormal(ua))
        end
    end
end

function reversenormal(a::DisjointUnion{R})::DisjointUnion{R} where {R <: Real}
    intvls = newinintervalpool!(R)
    for i in intervals(a)
        push!(intvls, reversenormal(i))
    end
    return DisjointUnion(intvls)
end

# ============================================================================ #

macro intervalintersectionhigh(low)
    esc(quote
        if ua isa Infinity{R}
            if ub isa Infinity{R}
                return Interval($low, Infinity(R))
            else
                if ub <= $low
                    return EmptyInterval(R)
                end
                return Interval($low, ub)
            end
        else
            if ub isa Infinity{R}
                if ua <= $low
                    return EmptyInterval(R)
                end
                return Interval($low, ua)
            else
                high = min(ua, ub)
                if high <= $low
                    return EmptyInterval(R)
                end
                return Interval($low, high)
            end
        end
    end)
end

function intervalintersection(
    a::Interval{R},
    b::Interval{R},
)::Union{EmptyInterval{R}, Interval{R}} where {R <: Real}
    # This method is just doing the below, but to avoid type ambiguities
    # things have to be much more complicated
    # if upper(a) <= lower(b) || upper(b) <= lower(a)
    #     return EmptyInterval(R)
    # end
    # low = max(lower(a), lower(b))
    # high = min(upper(a), upper(b))
    # return Interval(low, high)

    la = lower(a)
    lb = lower(b)
    ua = upper(a)
    ub = upper(b)
    if la isa RayOrigin{R}
        if lb isa RayOrigin{R}
            @intervalintersectionhigh(RayOrigin(R))
        else
            @intervalintersectionhigh(lb)
        end
    else
        if lb isa RayOrigin{R}
            @intervalintersectionhigh(la)
        else
            low = max(la, lb)
            @intervalintersectionhigh(low)
        end
    end
end

function intervalintersection(
    ::EmptyInterval{T},
    ::EmptyInterval{T},
) where {T <: Real}
    return EmptyInterval(T)
end
function intervalintersection(
    ::EmptyInterval{T},
    ::Interval{T},
) where {T <: Real}
    return EmptyInterval(T)
end
function intervalintersection(
    ::Interval{T},
    ::EmptyInterval{T},
) where {T <: Real}
    return EmptyInterval(T)
end
function intervalintersection(
    ::EmptyInterval{T},
    ::DisjointUnion{T},
) where {T <: Real}
    return EmptyInterval(T)
end
function intervalintersection(
    ::DisjointUnion{T},
    ::EmptyInterval{T},
) where {T <: Real}
    return EmptyInterval(T)
end
function intervalintersection(
    a::Interval{T},
    b::DisjointUnion{T},
) where {T <: Real}
    return intervalintersection(a, intervals(b))
end
function intervalintersection(
    a::DisjointUnion{T},
    b::Interval{T},
) where {T <: Real}
    return intervalintersection(b, intervals(a))
end
function intervalintersection(
    a::DisjointUnion{T},
    b::DisjointUnion{T},
) where {T <: Real}
    return intervalintersection(intervals(a), intervals(b))
end

function intervalintersection(
    a::Interval{T},
    b::AbstractVector{Interval{T}},
)::Union{EmptyInterval{T}, Interval{T}, DisjointUnion{T}} where {T <: Real}
    temp = nothing
    int1 = nothing
    for bint in b
        intsct = intervalintersection(a, bint)
        if !(intsct isa EmptyInterval{T})
            if int1 === nothing
                int1 = intsct
            elseif temp === nothing
                temp = newinintervalpool!(T)
                push!(temp, int1)
                push!(temp, intsct)
            else
                push!(temp, intsct)
            end
        end
    end
    if int1 === nothing
        return EmptyInterval(T)
    elseif int1 !== nothing && (temp === nothing)
        return int1
    else
        return DisjointUnion(temp)
    end
end

function intervalintersection(
    a::AbstractVector{Interval{T}},
    b::AbstractVector{Interval{T}},
)::Union{EmptyInterval{T}, Interval{T}, DisjointUnion{T}} where {T <: Real}
    temp = newinintervalpool!(T)
    for aint in a
        for bint in b
            intsct = intervalintersection(aint, bint)
            if !(intsct isa EmptyInterval{T})
                push!(temp, intsct)
            end
        end
    end
    if length(temp) == 0
        return EmptyInterval(T)
    else
        return DisjointUnion(temp)
    end
end

# ============================================================================ #

macro intervalunionhigh(low)
    esc(quote
        if ua isa Infinity{R}
            if ub isa Infinity{R}
                return Interval($low, Infinity(R))
            else
                if ub < la
                    return DisjointUnion(a, b)
                end
                return Interval($low, Infinity(R))
            end
        else
            if ub isa Infinity{R}
                if ua < lb
                    return DisjointUnion(a, b)
                end
                return Interval($low, Infinity(R))
            else
                if ua < lb || ub < la
                    return DisjointUnion(a, b)
                end
                return Interval($low, max(ua, ub))
            end
        end
    end)
end

function intervalunion(
    a::Interval{R},
    b::Interval{R},
)::Union{Interval{R}, DisjointUnion{R}} where {R <: Real}
    # This method is just doing the below, but to avoid type ambiguities things
    # have to be much more complicated
    # if upper(a) < lower(b) || upper(b) < lower(a)
    #     return DisjointUnion(a, b)
    # else
    #     low = min(lower(a), lower(b))
    #     high = max(upper(a), upper(b))
    #     return Interval(low, high)
    # end

    ua = upper(a)
    ub = upper(b)
    la = lower(a)
    lb = lower(b)
    if la isa RayOrigin{R}
        if lb isa RayOrigin{R}
            @intervalunionhigh(RayOrigin(R))
        else
            @intervalunionhigh(RayOrigin(R))
        end
    else
        if lb isa RayOrigin{R}
            @intervalunionhigh(RayOrigin(R))
        else
            low = min(la, lb)
            @intervalunionhigh(low)
        end
    end
end

function intervalunion(a::Interval{T}, b::DisjointUnion{T}) where {T <: Real}
    return intervalunion(b, a)
end
function intervalunion(a::DisjointUnion{T}, b::Interval{T}) where {T <: Real}
    temp = newinintervalpool!(T)
    for intvl in intervals(a)
        push!(temp, intvl)
    end
    push!(temp, b)
    return DisjointUnion(temp)
end
function intervalunion(
    a::DisjointUnion{T},
    b::DisjointUnion{T},
) where {T <: Real}
    temp = newinintervalpool!(T)
    for intvl in intervals(a)
        push!(temp, intvl)
    end
    for intvl in intervals(b)
        push!(temp, intvl)
    end
    return DisjointUnion(temp)
end
function intervalunion(::EmptyInterval{T}, ::EmptyInterval{T}) where {T <: Real}
    return EmptyInterval(T)
end
intervalunion(::EmptyInterval{T}, b::Interval{T}) where {T <: Real} = b
intervalunion(a::Interval{T}, ::EmptyInterval{T}) where {T <: Real} = a
intervalunion(::EmptyInterval{T}, b::DisjointUnion{T}) where {T <: Real} = b
intervalunion(a::DisjointUnion{T}, ::EmptyInterval{T}) where {T <: Real} = a

# ============================================================================ #

function intervalcomplement(a::DisjointUnion{T}) where {T <: Real}
    res = rayorigininterval(Infinity(T))
    for intrval in intervals(a)
        @assert !(intrval isa EmptyInterval{T}) //
                "should never have an empty interval in a DisjointUnion"
        compl =
            intervalcomplement(intrval)::Union{Interval{T}, DisjointUnion{T}}
        if compl isa Interval{T}
            res = intervalintersection(res, compl)
        else
            res = intervalintersection(res, compl)
        end
    end
    return res
end

function intervalcomplement(::EmptyInterval{T}) where {T <: Real}
    return Interval(RayOrigin(T), Infinity(T))
end
function intervalcomplement(a::Interval{T}) where {T <: Real}
    ua = upper(a)
    la = lower(a)
    if la isa RayOrigin{T}
        if ua isa Infinity{T}
            return EmptyInterval(T)
        else
            return positivehalfspace(ua) # this is not strictly correct
        end
    else
        if ua isa Infinity{T}
            return rayorigininterval(la)
        else
            return DisjointUnion(rayorigininterval(la), positivehalfspace(ua))
        end
    end
end

function difference(a::DisjointUnion, b::DisjointUnion)
    return intervalintersection(a, intervalcomplement(b))
end

"""
Apply a Transform to an Interval object
"""
function Base.:*(transformation::Transform{T}, a::Interval{T}) where {T <: Real}
    # looks ridiculous but necessary to dissambiguate the elements of the interval
    u = upper(a)
    l = lower(a)
    if l isa RayOrigin{T}
        if u isa Infinity{T}
            return Interval(l, u)
        else
            u = transformation * u
            return Interval(l, u)
        end
    else
        l = transformation * l
        if u isa Infinity{T}
            return Interval(l, u)

        else
            u = transformation * u
            return Interval(l, u)
        end
    end
end

end # module


text:
module JQL
# JQL is not a Query Language
#
# Wrap lazy-eager hash joins, of various flavours, in an iterator

import Base.∩, Base.∪

export AbstractJoinType, Inner, Outer, Left, Right, LeftSemi, RightSemi, AbstractJoin, Join, ∪, ∩

abstract AbstractJoinType <: Enum
abstract AbstractJoin{J<:AbstractJoinType, I, T}
immutable Inner <: AbstractJoinType end
immutable Outer <: AbstractJoinType end
immutable Left <: AbstractJoinType end
immutable Right <: AbstractJoinType end
immutable LeftSemi <: AbstractJoinType end
immutable RightSemi <: AbstractJoinType end

# Light weight pointers and metadata of the sources to join
immutable Join{J<:AbstractJoinType, I, T} <: AbstractJoin{J, I, T}
	large::I
	small::I
	parity::Bool
	ratio::Int64
	remainder::Int64
	function call{J<:AbstractJoinType, I}(::Type{Join}, ::Type{J}, left::I, right::I)
		if eltype(left) != eltype(right)     # this would be weird if it happened; nonetheless
			error("Column types do not match") # it is possible to write type unstable iterators
		end
		if length(left) <= length(right)
			return new{J, I, eltype(left)}(right, left, true, div(right, left), right % left )
		else
			return new{J, I, eltype(left)}(left, right, false, div(left, right), left % right)
		end
	end
end

# Nicer than subscripting tuples
immutable _JoinValue
	large::Vector{Int64}
	small::Vector{Int64}
end

# Would this be more efficient as object id dictionaries?
immutable _JoinState{T}
	reserve::Dict{T, _JoinValue}
	consign::Dict{T, _JoinValue}
	largemarchin::Int64
	smallmarchin::Int64
	largemarchout::Int64
	smallmarchout::Int64
end

# ToDo: drop J and fix on Inner, as this algorithm is just the inner join
# ToDo: use ratio/remainder to skip march in the small compared to the large
# with an initial preallocation of the small of remainder size.

# Alternating march in until there is a hit, then send through done to next for march out
function Base.start{J<:AbstractJoinType, I, T}(iterator::Join{J, I, T})
	reserve = Dict{T, _JoinValue}()
	consign = Dict{T, _JoinValue}()
	sizehint!(reserve, length(iterator.small))
	for i = 1:iterator.smalllength

		# March in from the small source
		smallvalue = get!(reserve, iterator.small(i), _JoinValue(Vector{Int64}(0), Vector{Int64}(0)))
		push!(smallvalue.small, i)

		# March in from the large source
		largevalue = get!(reserve, iterator.large(i), _JoinValue(Vector{Int64}(0), Vector{Int64}(0)))
		push!(largevalue.large, i)

		# Return the hit
		if length(smallvalue.large) > 0 || length(largevalue.small) > 0
			return _JoinState(reserve, consign, i, i, length(largevalue.small), length(smallvalue.large))
		end
	end

	# Both sources are the same size, so we are done
	if length(iterator.small) == length(iterator.large)
		return _JoinState(reserve, consign, length(iterator.large) + 1, length(iterator.small) + 1, 0, 0)
	end

	# Still data left in the large source, we only need roll call, without marching in
	for i = (length(iterator.small) + 1):length(iterator.large)
		largevalue = get(reserve, iterator.large(i), _JoinValue(Vector{Int64}(0), Vector{Int64}(0)))
		if length(largevalue.small) > 0
			return _JoinState(reserve, consign, i, length(iterator.small) + 1, length(largevalue.small), 0)
		end
	end

	# No matches found
	_JoinState(reserve, consign, length(iterator.large) + 1, length(iterator.small) + 1, 0, 0)
end

# March out the finds, small then big, until there are none, and then alternatingly march
# in until there is another hit
function Base.next{J<:AbstractJoinType, I, T}(iterator::Join{J, I}, state::_JoinState{T})

	# We are in next so there must be something to return, start by marching out the small
	# finds, on the left
	if state.smallmarchout > 0 && iterator.parity
		item = (iterator.small[state.smallmarchin], state.smallmarchin, state.reserve[iterator.small[state.smallmarchin]].large[state.smallmarchout])
		largemarchout = state.largemarchout
		smallmarchout = state.smallmarchout - 1

	# March out the small finds, on the right
	elseif state.smallmarchout > 0
		item = (iterator.small[state.smallmarchin], state.reserve[iterator.small[state.smallmarchin]].large[state.smallmarchout], state.smallmarchin)
		largemarchout = state.largemarchout
		smallmarchout = state.smallmarchout - 1

	# March out the large finds, on the right
	elseif state.largemarchout > 0 && iterator.parity
		ritem = (iterator.large[state.largemarchin], state.reserve[iterator.large[state.largemarchin]].small[state.largemarchout], state.largemarchin)
		largemarchout = state.largemarchout - 1
		smallmarchout = state.smallmarchout

	# March out the large finds, on the left
	elseif state.largemarchout > 0
		item = (iterator.large[state.largemarchin], state.largemarchin, state.reserve[iterator.large[state.largemarchin]].small[state.largemarchout])
		largemarchout = state.largemarchout
		smallmarchout = state.smallmarchout - 1

	# Opps something went wrong, maybe next was called out of loop, or sequence
	else
		error("next() was called on an invalid state")
	end

	# More left to march out after current, safe to loop through done to next
	if largemarchout > 0 || smallmarchout > 0
		return (item, _JoinState(state.reserve, state.consign, state.largemarchin, state.smallmarchin, largemarchout, smallmarchout))
	end

	# Nothing left to march out after the current item, so proceed with alternating march in
	if state.smallmarchin < length(iterator.small)
		for i = (state.smallmarchin + 1):length(iterator.small)

			# March in from the small source
			smallvalue = get!(state.reserve, iterator.small(i), _JoinValue(Vector{Int64}(0), Vector{Int64}(0)))
			push!(smallvalue.small, i)

			# March in from the large source
			largevalue = get!(state.reserve, iterator.large(i), _JoinValue(Vector{Int64}(0), Vector{Int64}(0)))
			push!(largevalue.large, i)

			# Return the hit
			if length(smallvalue.large) > 0 || length(largevalue.small) > 0
				return (item, _JoinState(state.reserve, state.consign, i, i, length(largevalue.small), length(smallvalue.large)))
			end
		end
	end

	# Both sources are the same size, so we are done
	if length(iterator.small) == length(iterator.large)
		return (item, _JoinState(state.reserve, state.consign, length(iterator.small) + 1, length(iterator.large) + 1, 0, 0))
	end

	# Still data left in the large source, we only need roll call, without marching in
	for i = (length(iterator.small) + 1):length(iterator.large)
		largevalue = get(state.reserve, iterator.large(i), _JoinValue(Vector{Int64}(0), Vector{Int64}(0)))
		if length(largevalue.small) > 0
			return (item, _JoinState(state.reserve, state.consign, i, length(iterator.small) + 1, length(largevalue.small), 0))
		end
	end

	# Whew! made it all the way through and found no more matches than the current one.
	(item, _JoinState(state.reserve, state.consign, length(iterator.large) + 1, length(iterator.small) + 1, 0, 0))
end
Base.done{J<:AbstractJoinType, I, T}(iterator::Join{J, I, T}, state::_JoinState{T}) =
	state.largemarchin > length(iterator.large) &&
	state.smallmarchin > length(iterator.small) &&
	state.largemarchout < 1 &&
	state.smallmarchout < 1
Base.eltype{J<:AbstractJoinType, I, T}(::Type{Join{J, I, T}}) = Tuple{T, Int64, Int64}

# These are just examples. Eventually they will be replaced with relational algebra symbols
∩(left::Tuple{Vararg{AbstractVector}}, right::Tuple{Vararg{AbstractVector}}) = Join(Inner, zip(left...), zip(right...))
∪(left::Tuple{Vararg{AbstractVector}}, right::Tuple{Vararg{AbstractVector}}) = Join(Outer, zip(left...), zip(right...))

end


text:
# Apply Dirichlet boundary condition
struct Dirichlet{T}  #<: Constraint
    prescribed_dofs::Vector{Int}
    values::Vector{T}
end

function Dirichlet(u::TrialFunction, mesh::PolygonalMesh, faceset::String,f::Function)
    Dirichlet(getfunctionspace(u), mesh, getfaceset(mesh, faceset), f)
end

function Dirichlet(fs::ScalarTraceFunctionSpace{2,T}, mesh::PolygonalMesh, faceset::Set{Int},f::Function) where {T}
    #Project function on faces
    n_dof = getnbasefunctions(fs)
    n_qpoints = getnquadpoints(fs)
    n_faces = length(faceset)
    prescribed_dofs = Vector{Int}(undef, n_faces*n_dof)
    values = Vector{T}(undef, n_faces*n_dof)
    k = 0
    for face_idx in 1:getnfaces(mesh)
        let face_idx::Int = face_idx
        if face_idx ∈ faceset
            @assert getcell(2,face_idx,mesh) == 0 "Face $face_idx is not in boundary"
            cell_idx = getcell(1,face_idx,mesh)
            cell = mesh.cells[cell_idx]
            face_lidx = findall(x -> x == face_idx,cell.faces)[1]
            orientation = face_orientation(mesh, cell_idx, face_lidx)
            N = zero(T)
            coords = get_coordinates(cell, mesh)
            #Solve system ∫ k ϕiϕj = ∫ f ϕi  we assume basis is orthogonal
            for i in 1:n_dof
                k += 1
                for q_point in 1:n_qpoints
                    N += fs.fs.qr_weights[q_point]*f(spatial_coordinate(fs, face_lidx, q_point, coords, orientation))*shape_value(fs,q_point,i)
                end
                values[k] = N
                prescribed_dofs[k] = face_idx*n_dof-n_dof + i
            end
        end
        end
    end
    return Dirichlet(prescribed_dofs, values)
end

function Dirichlet(u::TrialFunction, dh::DofHandler, faceset::String,f::Union{Function,Vector})
    Dirichlet(u, dh, getfaceset(dh.mesh, faceset), getfunctionspace(u), getfiniteelement(getfunctionspace(u)), f)
end

#TODO: only works for nodal basis
function Dirichlet(field::TrialFunction{2,T,refshape}, dh::DofHandler, faceset::Set{Int}, fs::DiscreteFunctionSpace, felem::FiniteElement, f::Union{Function,Vector{T}}) where {T,refshape}
    fi = find_field(dh, field)
    #compute total number of dofs
    n_qpoints = getnquadpoints(fs)
    n_max_topology_elements = maximum(keys(gettopology(refshape, Val{2})))
    nelementdofs = Int[]
    ncellelementdofs = Int[]
    # Compute dofs for element and total element dofs for cell
    for n_element in 0:n_max_topology_elements-1
        n_el_dofs_cell = gettopology(felem)[n_element]
        n_el_cell = gettopology(refshape, Val{2})[n_element]
        @assert mod(n_el_dofs_cell, n_el_cell) == 0
        push!(nelementdofs,Int(n_el_dofs_cell/n_el_cell))
        push!(ncellelementdofs,n_el_dofs_cell)
    end

    prescribed_dofs = Vector{Int}()
    values = Vector{T}()
    for face_idx in 1:getnfaces(dh.mesh)
        let face_idx::Int = face_idx
        if face_idx ∈ faceset
            @assert getcell(2,face_idx,dh.mesh) == 0 "Face $face_idx is not in boundary"
            cell_idx = getcell(1,face_idx,dh.mesh)
            cell = dh.mesh.cells[cell_idx]
            face_lidx::Int = findall(x -> x == face_idx,cell.faces)[1]
            l_dof = Int[]
            offset::Int = dh.cell_dofs_offset[cell_idx] - 1 + field_offset(dh, field)
            for j = 1:2 #Add vertex dofs
                local_offset::Int = reference_edge_nodes(refshape,Val{2})[face_lidx][j]
                if !(dh.cell_dofs[offset+local_offset] ∈ prescribed_dofs)
                    push!(prescribed_dofs, dh.cell_dofs[offset+local_offset])
                    push!(l_dof, local_offset)
                end
            end
            for j in 1:nelementdofs[2]  #Add face dofs
                local_offset::Int = ncellelementdofs[1] + nelementdofs[2]*(face_lidx-1) + j
                if !(dh.cell_dofs[offset+local_offset] ∈ prescribed_dofs)
                    push!(prescribed_dofs, dh.cell_dofs[offset+local_offset])
                    push!(l_dof, local_offset)
                end
            end
            _push_values!(values, cell, dh.mesh, l_dof, felem, field, f)
        end
        end
    end
    #now put all in order
    p = sortperm(prescribed_dofs)
    return Dirichlet(prescribed_dofs[p], values[p])
end

function _push_values!(values::Vector, cell::Cell, mesh::PolygonalMesh,l_dof::Vector{Int}, felem::FiniteElement, field::TrialFunction, f::Function)
    #TODO: We now assume a nodal base...
    coords = get_coordinates(cell, mesh)
    for i in l_dof
        vals = f(spatial_nodal_coordinate(felem,i,coords))
        for k in 1:getncomponents(field)
            push!(values,vals[k])
        end
    end
end

function _push_values!(values::Vector, cell::Cell, mesh::PolygonalMesh,l_dof::Vector{Int}, felem::FiniteElement, field::TrialFunction, vals::Vector{T}) where {T}
    #TODO: We now assume a nodal base...
    for i in l_dof
        for k in 1:getncomponents(field)
            push!(values,vals[k])
        end
    end
end

@enum(ApplyStrategy, APPLY_TRANSPOSE, APPLY_INPLACE)

function apply!(KK::Union{SparseMatrixCSC,Symmetric}, f::AbstractVector, dirichlet::Dirichlet;
                strategy::ApplyStrategy=APPLY_TRANSPOSE)
    K = isa(KK, Symmetric) ? KK.data : KK
    @assert length(f) == 0 || length(f) == size(K, 1)
    @boundscheck checkbounds(K, dirichlet.prescribed_dofs, dirichlet.prescribed_dofs)
    @boundscheck length(f) == 0 || checkbounds(f, dirichlet.prescribed_dofs)

    m = meandiag(K) # Use the mean of the diagonal here to not ruin things for iterative solver
    @inbounds for i in 1:length(dirichlet.values)
        d = dirichlet.prescribed_dofs[i]
        v = dirichlet.values[i]

        if v != 0
            for j in nzrange(K, d)
                f[K.rowval[j]] -= v * K.nzval[j]
            end
        end
    end
    zero_out_columns!(K, dirichlet.prescribed_dofs)
    if strategy == APPLY_TRANSPOSE
        K′ = copy(K)
        transpose!(K′, K)
        zero_out_columns!(K′, dirichlet.prescribed_dofs)
        transpose!(K, K′)
    elseif strategy == APPLY_INPLACE
        K[dirichlet.prescribed_dofs, :] = 0
    else
        error("Unknown apply strategy")
    end
    @inbounds for i in 1:length(dirichlet.values)
        d = dirichlet.prescribed_dofs[i]
        v = dirichlet.values[i]
        K[d, d] = m
        if length(f) != 0
            f[d] = v * m
        end
    end
end

# columns need to be stored entries, this is not checked
function zero_out_columns!(K, dofs::Vector{Int}) # can be removed in 0.7 with #24711 merged
    #@debug assert(issorted(dofs))
    for col in dofs
        r = nzrange(K, col)
        K.nzval[r] .= 0.0
    end
end

function meandiag(K::AbstractMatrix)
    z = zero(eltype(K))
    for i in 1:size(K, 1)
        z += abs(K[i, i])
    end
    return z / size(K, 1)
end


text:
# Quadratic terms & constraints
#

function add_qpterms!(model::Model, qr::IVec, qc::IVec, qv::FVec)
    n = num_var(model)
    ((m = length(qr)) == length(qc) == length(qv)) || error("Inconsistent argument dimensions.")
    nqv = copy(qv)
    Q = sparse(qr, qc, nqv, n, n)
    if istriu(Q) || istril(Q) || issym(Q)
        Q = Q + Q' - spdiagm(diag(Q)) # reconstruct full matrix like CPLEX wants
    else
        error("Matrix Q must be either symmetric or triangular")
    end
    if nnz(Q) > 0
        qmatcnt = Array(Cint, n)
        for k = 1:n
          qmatcnt[k] = Q.colptr[k+1] - Q.colptr[k]
        end
        stat = @cpx_ccall(copyquad, Cint, (
                          Ptr{Void},
                          Ptr{Void},
                          Ptr{Cint},
                          Ptr{Cint},
                          Ptr{Cint},
                          Ptr{Cdouble}
                          ),
                          model.env.ptr, model.lp, convert(Array{Cint,1}, Q.colptr[1:end-1].-1), convert(Array{Cint,1},qmatcnt), convert(Array{Cint,1}, Q.rowval.-1), Q.nzval)
        if stat != 0
            throw(CplexError(model.env, stat))
        end
    end
    model.has_qc = true
    nothing
end

function add_qpterms!(model::Model, qr::Vector, qc::Vector, qv::Vector)
    add_qpterms!(model, ivec(qr), ivec(qc), fvec(qv))
end

function add_qpterms!(model, H::SparseMatrixCSC{Float64}) # H must be symmetric
    n = num_var(model)
    (H.m == n && H.n == n) || error("H must be an n-by-n symmetric matrix.")

    nnz_h = nnz(H)
    qr = Array(Cint, nnz_h)
    qc = Array(Cint, nnz_h)
    qv = Array(Float64, nnz_h)
    k = 0

    colptr::Vector{Int} = H.colptr
    nzval::Vector{Float64} = H.nzval

    for i = 1 : n
        qi::Cint = convert(Cint, i)
        for j = colptr[i]:(colptr[i+1]-1)
            qj = convert(Cint, H.rowval[j])

            if qi <= qj
                k += 1
                qr[k] = qi
                qc[k] = qj
                qv[k] = nzval[j]
            end
        end
    end

    add_qpterms!(model, qr[1:k], qc[1:k], qv[1:k])
end

function add_qpterms!(model, H::Matrix{Float64}) # H must be symmetric
    n = num_var(model)
    size(H) == (n, n) || error("H must be an n-by-n symmetric matrix.")

    nmax = div(n * (n + 1), 2)
    qr = Array(Cint, nmax)
    qc = Array(Cint, nmax)
    qv = Array(Float64, nmax)
    k::Int = 0

    for i = 1 : n
        qi = convert(Cint, i)
        for j = i : n
            v = H[j, i]
            if v != 0.
                k += 1
                qr[k] = qi
                qc[k] = convert(Cint, j)
                qv[k] = v
            end
        end
    end

    add_qpterms!(model, qr[1:k], qc[1:k], qv[1:k])
end

function add_diag_qpterms!(model, H::Vector)  # H stores only the diagonal element
    n = num_var(model)
    n == length(H) || error("Incompatible dimensions.")
    q = [convert(Cint,1):convert(Cint,n)]
    add_qpterms!(model, q, q, fvec(h))
end

function add_diag_qpterms!(model, hv::Real)  # all diagonal elements are H
    n = num_var(model)
    q = [convert(Cint,1):convert(Cint,n)]
    add_qpterms!(model, q, q, fill(float64(hv), n))
end

# add_qconstr!

function add_qconstr!(model::Model, lind::IVec, lval::FVec, qr::IVec, qc::IVec, qv::FVec, rel::Cchar, rhs::Float64)
    qnnz = length(qr)
    qnnz == length(qc) == length(qv) || error("Inconsistent argument dimensions.")

    lnnz = length(lind)
    lnnz == length(lval) || error("Inconsistent argument dimensions.")

    if qnnz > 0
        stat = @cpx_ccall(addqconstr, Cint, (
                          Ptr{Void},    # env
                          Ptr{Void},    # model
                          Cint,         # lnnz
                          Cint,         # qnnz
                          Float64,      # rhs
                          Cchar,        # sense
                          Ptr{Cint},    # lind
                          Ptr{Float64}, # lval
                          Ptr{Cint},    # qrow
                          Ptr{Cint},    # qcol
                          Ptr{Float64}, # qval
                          Ptr{UInt8}    # name
                          ),
                          model.env.ptr, model.lp, lnnz, qnnz, rhs, rel, lind.-1, lval, qr.-1, qc.-1, qv, C_NULL)
        if stat != 0
            throw(CplexError(model.env, stat))
        end
        model.has_qc = true
    end
    nothing
end

const sensemap = Dict('=' => 'E', '<' => 'L', '>' => 'G')
function add_qconstr!(model::Model, lind::Vector, lval::Vector, qr::Vector, qc::Vector, qv::Vector{Float64}, rel::GChars, rhs::Real)
    add_qconstr!(model, ivec(lind), fvec(lval), ivec(qr), ivec(qc), fvec(qv), cchar(sensemap[rel]), float(rhs))
end

function num_qconstr(model::Model)
    ncons = @cpx_ccall(getnumqconstrs, Cint, (
                       Ptr{Void},
                       Ptr{Void}
                       ),
                       model.env.ptr, model.lp)
    return ncons
end


text:
const head_xml = """<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.20.0 -->
<interface>
<requires lib="gtk+" version="3.20"/>
<object class="GtkWindow" id="window.run.wJqRk">
<property name="can_focus">False</property>
<property name="title" translatable="yes">BeetleWay</property>
<child>
<object class="GtkBox" id="some1.wJqRk">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkBox" id="some2.wJqRk">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkToggleButton" id="done.run.wJqRk">
<property name="label" translatable="yes">Done</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="cancel.run.wJqRk">
<property name="label" translatable="yes">Cancel</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkBox" id="some3.wJqRk">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkGrid" id="grid.run.wJqRk">
<property name="visible">True</property>
<property name="can_focus">False</property>
"""
label_xml(row::Int, text::String) = """<child>
<object class="GtkLabel" id="some4$row.$text.wJqRk">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">$text</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">$row</property>
</packing>
</child>
"""
dropdown_xml(row::Int, id::Symbol) = """<child>
<object class="GtkComboBoxText" id="$id">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">$row</property>
</packing>
</child>
"""
textbox_xml(row::Int, id::Symbol) = """<child>
<object class="GtkTextView" id="$id">
<property name="visible">True</property>
<property name="can_focus">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">$row</property>
</packing>
</child>
"""
const tail_xml = """</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkBox" id="some5.wJqRk">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkLabel" id="some6.wJqRk">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Comment</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkTextView" id="comment.run.wJqRk">
<property name="width_request">100</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
</object>
</interface>"""
parse2glade(x::OrderedDict{Symbol,Tuple{Symbol,String}}) = open(joinpath(@__DIR__, "run.glade"), "w") do o
    print(o, head_xml)
    for (i, (id, (widget, l))) in enumerate(x)
        print(o, label_xml(i - 1, l))
    end
    for (i, (id, (widget, l))) in enumerate(x)
        txt = widget == :dropdown ? dropdown_xml(i - 1, id) : textbox_xml(i - 1, id)
        print(o, txt)
    end
    print(o, tail_xml)
end


text:
function getprop(value, name)
   getproperty(value, Symbol(name))
end

# "d$key" will result in a lookup instead of a access of fields
function getprop(d::AbstractDict{K, V}, key::K) where {K, V}
   d[key]
end

function setprop!(value, name, x)
   setproperty!(value, Symbol(name), x)
   return nothing
end

function setprop!(d::AbstractDict{K, V}, key::K, x) where {K, V}
   d[key] = x
   return nothing
end

"""
    dimensionidxs(x)
Returns a proper index for one dimension when indexing multiple elements in
a multidimensional array.
"""
function dimensionidxs(x)
   x
end

function dimensionidxs(x::Number)
   [Int(x)]
end

function dimensionidxs(x::AbstractArray{Float64})
   convert(Vector{Int}, x)
end

"""
    getidx(collection, key)
Extract a single element from a collection.
Called by `collection[[key]]` in R.
"""
function getidx(collection, key)
   getindex(collection, key)
end

# get one element in a multidimensional array
function getidx(collection, key::Vararg{<:Number})
   intkey = map(Int, key)
   getindex(collection, intkey...)
end

"""
    getidxs(collection, idx)
Access multiple elements in a collection.
Called via "collection[idx]" in R.
Expected to return a vector or a vector-like object.
The exact return type may depend on the implementation of `getindex`
of the specific collection.
"""
function getidxs(collection, idx::Number)
   getindex(collection, [idx])
end

# indexing with e.g. v[1]
function getidxs(collection, idx::Float64)
   getindex(collection, [Int(idx)])
end

# indexing with e.g. v[1:4]
function getidxs(collection, idxs::Vector{Float64})
   getindex(collection, map(Int, idxs))
end

# indexing with e.g. v[v > 4]
function getidxs(collection, idxs::Union{AbstractVector, AbstractRange})
   getindex(collection, idxs)
end

# multidimensional indexing with a single index, e.g. v[1,2,3]
function getidxs(collection,
      keys::Vararg{<:Union{AbstractArray, AbstractRange, Float64, Int}})
   dimidxs = map(dimensionidxs, keys)
   getindex(collection, dimidxs...)
end

# indexing dictionary with e.g. d["hi"]
function getidxs(d::AbstractDict{K, V}, key::K) where {K, V}
   [d[key]]
end

# indexing dictionary with e.g. d[c("hi", "you")]
function getidxs(d::AbstractDict{K, V}, keys::Vararg{K}) where {K, V}
   ret = Vector{V}(undef, length(keys))
   for i in eachindex(keys)
      ret[i] = d[keys[i]]
   end
   ret
end

"""
    setidx!(collection, value, key)
Set a single element in a collection.
Called by `collection[[key]] <- value` in R.
"""
function setidx!(collection, value, key)
   setindex!(collection, value, key)
   return nothing
end

# replace single element in multidimensional array
function setidx!(collection, value, key::Vararg{<:Number})
   intkey = map(Int, key)
   setindex!(collection, value, intkey...)
   return nothing
end

function check_lengths_for_assignment(keys, values)
   if length(values) != length(keys)
      error("Number of items to replace is not equal to replacement length")
   end
end

"""
    setidxs!(collection, values, keys)
Set multiple elements in a collection.
Called by `collection[key] <- value` in R.
"""
function setidxs!(collection, value,
      keys::Vararg{<:Union{AbstractArray, AbstractRange, Float64, Int}})

   dimidxs = map(dimensionidxs, keys)
   collection[dimidxs...] .= value
   return nothing
end

# replace single element in multidimensional array
function setidxs!(collection, value, key::Vararg{<:Union{Int, Float64}})
   intkey = map(Int, key)
   setindex!(collection, value, intkey...)
   return nothing
end

# replace multiple elements in a multidimensional array with multiple values
function setidxs!(collection::AbstractArray{T}, value::AbstractArray{T},
      keys::Vararg{<:Union{AbstractArray, AbstractRange, Float64, Int}}) where T

   dimidxs = map(dimensionidxs, keys)
   collection[dimidxs...] .= value
   return nothing
end

# replace multiple elements in a multidimensional array with a single value
function setidxs!(collection::AbstractArray{T}, value::T,
      keys::Vararg{<:Union{AbstractArray, AbstractRange, Float64, Int}}) where T

   dimidxs = map(dimensionidxs, keys)
   collection[dimidxs...] .= repeat([value], length.(keys)...)
   return nothing
end

# replace single element in dictionary with e.g. d["hi"]
function setidxs!(d::AbstractDict{K, V}, value, key::K) where {K, V}
   d[key] = value
   return nothing
end

# replace multiple elements in dictionary with e.g. d[c("hi", "you")]
function setidxs!(d::AbstractDict{K, V}, values::Vector, keys::Vararg{K}) where {K, V}
   check_lengths_for_assignment(keys, values)
   map(i -> d[keys[i]] = values[i], eachindex(keys))
   return nothing
end

function getdim(x::T) where {T <: Union{AbstractArray, Tuple}}
   collect(size(x))
end


text:
using Distributions, PyPlot

function plotGMM(X::Matrix, clusters::Vector, γ::Matrix)
    # Plot data set and (fitted) mixture model consisting of two Gaussian distributions
    # X contains a 2-d data set (every column holds a data point)
    # clusters holds the 2 Gaussian elements of the mixture model
    # γ contains p(cluster|X), and should contain NaN elements if not yet known

    # Plot contours of the element distributions
    K = length(clusters)
    for k=1:K
        X1=Matrix{Float64}(undef,50,50); X2=Matrix{Float64}(undef,50,50); d=Matrix{Float64}(undef,50,50)
        # Create bounding box for thse contour plot
        lims = [-2*sqrt(cov(clusters[k])[1,1]) 2*sqrt(cov(clusters[k])[1,1]);
                -2*sqrt(cov(clusters[k])[2,2]) 2*sqrt(cov(clusters[k])[2,2])] + repeat(mean(clusters[k]), 1, 2)
        for i=1:50
            for j=1:50
                X1[i,j] = (i-1)/50 * abs.(lims[1,2]-lims[1,1]) + lims[1,1]
                X2[i,j] = (j-1)/50 * abs.(lims[2,2]-lims[2,1]) + lims[2,1]
                d[i,j] = pdf(clusters[k], [X1[i,j];X2[i,j]])
            end
        end
        contour(X1, X2, d, 3)
    end

    # Plot data points
    if isnan(γ[1,1])
        scatter(X[1,:], X[2,:], marker="o", s=20, c="k")
    else
        scatter(X[1,:], X[2,:], marker="o", s=20, c=γ[1,:], cmap="PiYG")
    end

    # Figure make-up
    plotlimits = hcat(minimum(X,dims=2), maximum(X,dims=2))
    margin = 0.2*abs.(plotlimits[:,1] - plotlimits[:,2])
    xlim([plotlimits[1,1]-margin[1]; plotlimits[1,2]+margin[1]])
    ylim([plotlimits[2,1]-margin[2]; plotlimits[2,2]+margin[2]])
end

max_stars_repo_name: Yikeru/BMLIP

max_issues_repo_name: Yikeru/BMLIP

max_forks_repo_name: Yikeru/BMLIP


text:
export Lexer, tokenize

const KEYWORDS = [
    "predicate",
    "var",
    "constraint",
    "of",
    "in",
    "array",
    "solve",
    "set",
    "satisfy",
    "minimize",
    "maximize",
]

const TYPES = ["bool", "int", "float"]

struct Lexer{IO_t <: IO}
    io::IO_t
end

Lexer(s::String) = Lexer(IOBuffer(s))

function Base.collect(lex::Lexer)
    a = Token[]
    for token in lex
        push!(a, token)
    end
    return a
end

tokenize(s) = collect(Lexer(s))

function Base.iterate(l::Lexer)
    seekstart(l.io)
    _cache = IOBuffer(; sizehint=100)
    iterate(l, (l.io, _cache, 1))
end

function Base.iterate(l::Lexer, state)
    (io, _cache, ln) = state

    eof(io) && return nothing

    # skip whitespaces
    c = readchar(io)
    while c ∈ (' ', '\t')
        c = readchar(io)
    end

    # check for newline
    while c == '\n'
        ln = ln + 1
        state = (io, _cache, ln) # update in satte
        eof(io) && return nothing
        c = readchar(io)
    end

    # check for magic charactes
    if c == '('
        return (Token(:parentheses_l, ln), state)
    elseif c == ')'
        return (Token(:parentheses_r, ln), state)
    elseif c == '['
        return (Token(:bracket_l, ln), state)
    elseif c == ']'
        return (Token(:bracket_r, ln), state)
    elseif c == '{'
        return (Token(:brace_l, ln), state)
    elseif c == '}'
        return (Token(:brace_r, ln), state)
    elseif c == ';'
        return (Token(:semicolon, ln), state)
    elseif c == ':'
        if peek1(io) == ':'
            skip(io, 1)
            return (Token(:doublecolon, ln), state)
        else
            return (Token(:colon, ln), state)
        end
    elseif c == ','
        return (Token(:comma, ln), state)
    elseif c == '='
        return (Token(:equalsign, ln), state)
    elseif c == '.'
        read_single(io, '.'; needsmatch=true)
        return (Token(:dotdot, ln), state)
    end

    # string literal
    if c == '\"'
        if peek1(io) == peek2(io) == '\"'
            readchar(io); readchar(io);
            read_while!(_cache, io, '\"'; condition=innone)
            if peek1(io) == peek2(io) == peek3(io) == '\"'
                readchar(io); readchar(io); readchar(io);
                word = String(take!(_cache))
                return (Token(:string_literal, word, ln), state)
            else
                error("string musst end with 3 \"")
            end
        else
            error("there is a \" but not 3 of them (╯°□°）╯︵ ┻━┻")
        end
    end

    # check for word
    # the '_':'_' seems awkward but makes the types homogenious and faster
    # XXX: i allow _ as begin for all identifiers
    if inany(c, 'a':'z', 'A':'Z', '_':'_')
        write(_cache, c)
        read_while!(_cache, io, ('a':'z', 'A':'Z', '0':'9', '_':'_'))
        word = String(take!(_cache))
        if word ∈ KEYWORDS
            return (Token(:keyword, word, ln), state)
        elseif word ∈ TYPES
            return (Token(:basic_par_type, word, ln), state)
        elseif word ∈ ["true", "false"]
            return (Token(:bool_literal, word, ln), state)
        else
            return (Token(:identifier, word, ln), state)
        end
    end

    # number literal
    write(_cache, c) # start cache with single char
    if c == '-'
        c = readchar(io)
        @assert c ∈ '0':'9' "Found minus without following number!"
        write(_cache, c)
    end

    if inany(c, '0':'9')
        if c=='0' && peek1(io) ∈ 'x' # hex literal
            write(_cache, readchar(io)) # write the peeked x
            read_while!(_cache, io, ('0':'9', 'a':'f', 'A':'F'); needsmatch=true)
            word = String(take!(_cache))
            return (Token(:int_literal, word, ln), state)
        elseif c=='0' && peek1(io) ∈ 'o' #?? literal
            write(_cache, readchar(io)) # write the peeked o
            read_while!(_cache, io, '0':'7'; needsmatch=true)
            peek1(io) ∉ '8':'9' || error("oh boy that doesnt fit the oct literal")
            word = String(take!(_cache))
            return (Token(:int_literal, word, ln), state)
        else
            read_while!(_cache, io, '0':'9') # read rest of number
            if peek1(io) ∈ ('.', 'e', 'E') && inany(peek2(io), '0':'9', '+':'+', '-':'-')# ouuhh it's a float
                read_single!(_cache, io, '.')
                read_while!(_cache, io, '0':'9')
                found_e = read_single!(_cache, io, ('e','E'))
                if found_e
                    read_single!(_cache, io, ('+','-'))
                    read_while!(_cache, io, '0':'9')
                end
                word = String(take!(_cache))
                return (Token(:float_literal, word, ln), state)
            else # this is an iteger!
                word = String(take!(_cache))
                return (Token(:int_literal, word, ln), state)
            end
        end #
    end

    error("No token found: ", context(io))
end

function read_while!(_target, io, collections; needsmatch=false, condition=inany)
    i = 0
    c = readchar(io)
    while condition(c, collections)
        write(_target, c)
        i += 1
        c = readchar(io)
    end
    c===nothing || skip(io, -1) # go one back unless eof was reached

    needsmatch && i==0 && error("Nothing matches $collections here: $(context(io))")
    return i
end

function read_while(args...; kwargs...)
    # probably slow due to slurping, but okay for debugging/testing
    _target = IOBuffer()
    read_while!(_target, args...; kwargs...)
    return String(take!(_target))
end

function read_single!(_target, io, collections; needsmatch=false, condition=inany)
    c = readchar(io)
    if condition(c, collections)
        write(_target, c)
        return true
    else
        c===nothing || skip(io, -1) # go one back unless eof was reached
        needsmatch && error("Nothing matches $collections here: $(context(io))")
        return false
    end
end

function read_single(args...; kwargs...)
    # probably slow due to slurping, but okay for debugging/testing
    _target = IOBuffer(sizehint=1)
    read_while!(_target, args...; kwargs...)
    return String(take!(_target))
end

inany(e, collections...) = inany(e, collections)
function inany(e, collections::Tuple)
    for c in collections
        e ∈ c && return true
    end
    return false
end

innone(e, collections...) = innone(e, collections)
function innone(e, collections::Tuple)
    for c in collections
        e ∉ c && return true
    end
    return false
end

function peekn(i::Int, io)
    pos = position(io)
    skip(io, i-1)
    r = readchar(io)
    seek(io, pos)
    return r
end
peek1(io) = peekn(1, io)
peek2(io) = peekn(2, io)
peek3(io) = peekn(3, io)

function readchar(io, ifeof=nothing)
    eof(io) && return ifeof
    return read(io, Char)
end

function context(io::IO; before=10, after=10)
    pos = position(io)
    skip(io, -before)
    str = ""
    for i in 1:before
        str *= readchar(io, "EOF")
    end
    str *= "^" * readchar(io, "EOF") * "^"
    for i in 1:after
        str *= readchar(io, "EOF")
    end
    seek(io, pos)
    return str
end


text:
# productspace.jl

"""
By convention a geometric space with a tuple type `T` represents a product space
of the individual entries of `T`.
"""
const ProductSpace{T <: Tuple} = GeometricSpace{T}

# Identify with the × operator and the cross function
cross(x::GeometricSpace...) = cartesianproduct(x...)
cross(x::Type{G1}, y::Type{G2}) where {G1 <: GeometricSpace, G2 <: GeometricSpace} = cartesianproduct(x, y)

# Don't create a cartesian product of just one element
# cartesianproduct(a::GeometricSpace) = cartesianproduct(typeof(a))
cartesianproduct(a::GeometricSpace, b::GeometricSpace) = cartesianproduct(typeof(a), typeof(b))()
cartesianproduct(a::GeometricSpace, b::GeometricSpace, c::GeometricSpace) = cartesianproduct(typeof(a), typeof(b), typeof(c))()
cartesianproduct(a::GeometricSpace, b::GeometricSpace, c::GeometricSpace, d::GeometricSpace) = cartesianproduct(typeof(a), typeof(b), typeof(c), typeof(d))()

# Don't create a cartesian product of just one element
# cartesianproduct(::Type{GeometricSpace{T}}) where {T} = ProductSpace{Tuple{T}}
cartesianproduct(::Type{GeometricSpace{T}}, ::Type{GeometricSpace{S}}) where {T,S} = ProductSpace{Tuple{T,S}}
cartesianproduct(::Type{GeometricSpace{T}}, ::Type{GeometricSpace{S}}, ::Type{GeometricSpace{U}}) where {T,S,U} = ProductSpace{Tuple{T,S,U}}
cartesianproduct(::Type{GeometricSpace{T}}, ::Type{GeometricSpace{S}}, ::Type{GeometricSpace{U}}, ::Type{GeometricSpace{V}}) where {T,S,U,V} = ProductSpace{Tuple{T,S,U,V}}

# A `cartesianproduct(a)` with just a single element returns `a`.
# zero(::Type{ProductSpace{Tuple{T}}}) where {T} = (zero(T),)
zero(::Type{ProductSpace{Tuple{T,S}}}) where {T,S} = (zero(T), zero(S))
zero(::Type{ProductSpace{Tuple{T,S,U}}}) where {T,S,U} = (zero(T), zero(S), zero(U))
zero(::Type{ProductSpace{Tuple{T,S,U,V}}}) where {T,S,U,V} = (zero(T), zero(S), zero(U), zero(V))
zero(::Type{ProductSpace{NTuple{N,T}}}) where {N,T} = ntuple(k->zero(T),Val{N})

## Isomorphisms

# # Due to #22239, we temporarily disable this isomorphism
# # We identity (T,[T,T]) with [T,T,T]

## isomorphism_reduction(::Type{VectorSpace{3,T}}, ::Type{ProductSpace{Tuple{S,SVector{2,S}}}}) where {T,S} =
##     (GeometricSpace{T}, GeometricSpace{S})

## convert_space(::Type{ProductSpace{Tuple{T,SVector{2,T}}}}, x::SVector{3,T}) where {T} = (x[1], SVector{2,T}(x[2],x[3]))
## convert_space(::Type{VectorSpace{3,T}}, x::Tuple{T,SVector{2,T}}) where {T} = SVector{3,T}(x[1], x[2][1], x[2][2])

# We identity ((T,T),T) with [T,T,T]

isomorphism_reduction(::Type{VectorSpace{3,T}}, ::Type{ProductSpace{Tuple{Tuple{S,S},S}}}) where {T,S} =
    (GeometricSpace{T}, GeometricSpace{S})

convert_space(::Type{ProductSpace{Tuple{Tuple{T,T},T}}}, x::SVector{3,T}) where {T} = ((x[1], x[2]), x[3])
convert_space(::Type{VectorSpace{3,T}}, x::Tuple{Tuple{T,T},T}) where {T} = SVector{3,T}(x[1][1], x[1][2], x[2])

# # Due to #22239, we temporarily disable this isomorphism
# # We identity (T,(T,T)) with [T,T,T]
#
# isomorphism_reduction(::Type{VectorSpace{3,T}}, ::Type{ProductSpace{Tuple{S,Tuple{S,S}}}}) where {T,S} =
#     (GeometricSpace{T}, GeometricSpace{S})
#
# convert_space(::Type{ProductSpace{Tuple{T,Tuple{T,T}}}}, x::SVector{3,T}) where {T} = (x[1], (x[2],x[3]))
# convert_space(::Type{VectorSpace{3,T}}, x::Tuple{T,Tuple{T,T}}) where {T} = SVector{3,T}(x[1], x[2][1], x[2][2])
#
# # Due to #22239, we temporarily disable this isomorphism
# # We identity ([T,T],T) with [T,T,T]
#
@generated function isomorphism_reduction(::Type{VectorSpace{N,T}}, ::Type{ProductSpace{Tuple{SVector{M,S},S}}}) where {N,M,T,S}
    if M+1 == N
        quote (GeometricSpace{T}, GeometricSpace{S}) end
    else
        quote False end
    end
end

@generated function isomorphism_reduction(::Type{VectorSpace{N,T}}, ::Type{ProductSpace{Tuple{S,SVector{M,S}}}}) where {N,M,T,S}
    if M+1 == N
        quote (GeometricSpace{T}, GeometricSpace{S}) end
    else
        quote False end
    end
end

@generated function convert_space(::Type{ProductSpace{Tuple{SVector{M,T},T}}}, x::SVector{N,T}) where {N,M,T}
    if M+1 == N
        quote
            sv = @ncall $M SVector{$M,T} i->x[i]
            (sv, x[$N])
        end
    else
        quote
            throw(ArgumentError())
        end
    end
end

# Following is commented out as it is broken in Julia v1.1 (https://github.com/JuliaLang/julia/issues/31861)

# function convert_space(::Type{ProductSpace{Tuple{T,SVector{M,T}}}}, x::SVector{N,T}) where {N,M,T}
#     if M+1 == N
#         quote
#             # sv = @ncall $M SVector{$M,T} i->x[i+1]
#             (x[1], sv)
#         end
#     else
#         quote
#             throw(ArgumentError())
#         end
#     end
# end

convert_space(::Type{ProductSpace{Tuple{T,SVector{2,T}}}}, x::SVector{3,T}) where T =
    (x[1], SVector(x[2],x[3]))

convert_space(::Type{VectorSpace{3,T}}, x::Tuple{SVector{2,T},T}) where {T} = SVector{3,T}(x[1][1], x[1][2], x[2])

# We can identify a product space with equal types with a vector space of the
# same dimension and type. The agreement is between NTuple{N,T} and SVector{N,T}.

isomorphism_reduction(::Type{VectorSpace{N,T}}, ::Type{ProductSpace{NTuple{N,S}}}) where {N,T,S} =
    (GeometricSpace{T}, GeometricSpace{S})

convert_space(::Type{ProductSpace{NTuple{N,T}}}, x::SVector{N,T}) where {N,T} = Tuple(x)
convert_space(::Type{VectorSpace{N,T}}, x::NTuple{N,T}) where {N,T} = SVector{N,T}(x)


text:
VERSION >= v"0.4.0-dev+6521" && __precompile__(true)

include("JLGEngine.jl")

importall LoggerManager

""" TODO """
module App

using Images
using ImageMagick
using Quaternions
#using Mustache
using ModernGL
#using Reactive
using FixedPointNumbers
using ColorTypes
using Compat
using FileIO

using CoreExtended
using RessourceManager
using TimeManager
using FileManager
using Environment
using JLScriptManager
using WindowManager

using JLGEngine
using JLGEngine.GraphicsManager
using JLGEngine.LibGL
using JLGEngine.RenderManager

""" TODO """
type ScriptRefs <: JLComponent
	WINDOW	::Any
	GD		::Any

	ScriptRefs() = new()
end

""" TODO """
type ScriptState <: JLComponent
	isInitalized		::Bool
	isRunning			::Bool
	isTerminated		::Bool
	isRenderRunOnce		::Bool
	isRenderOn			::Bool
	ticks				::Integer
	ticksTotal			::Integer
	ticksTime			::Base.RefValue{Float64}
	startTime			::Real
	currentTime			::Real

	ScriptState() = new(false,false,false,false,true,0,0,Ref(0.0),0.0,0.0)
end

### configure App
#function __init__()
#end

#Environment.StartMainLogger()
#Logging.configure(level=Logging.OFF)

#console = Environment.CreateLogger(:Console, "console.log")
# Logging.configure(console, output=STDOUT)
# log = Environment.CreateLogger(:Shader, "shader.log")
# Logging.configure(log,level=OFF)
# Environment.LogTest()
# Logging.debug(log,"debug message")
# Logging.info(log,"info message")
# Logging.warn(log,log,"warning message")
# Logging.err(log,"error message")
# Logging.critical(log,"critical message")
###

# (errorRead, errorWrite) = redirect_stderr()
#
# atexit(function ()
#     close(errorWrite)
#     errors = readavailable(errorRead)
#     close(errorRead)
#     logfile = open(string(Environment.GetPath(:LOGS),"errors.log"), "a")
#     write(logfile, errors)
#     close(logfile)
# end)

""" TODO """
function start()
	# (errorRead, errorWrite) = redirect_stderr()

	# atexit(function ()
			# close(errorWrite)

			# errors = readavailable(errorRead)

			# close(errorRead)

			# logfile = open("errors.log", "a")
			# write(logfile, errors)
			# close(logfile)
	# end)

	#(outRead, outWrite) = redirect_stdout()
	#
#	print("Test")
	#print("ing")
	#
	#close(outWrite)
	#
	#data = readavailable(outRead)
	#
	#close(outRead)

	#logfile = open("debug.log", "a")
	#write(logfile, data)
	#close(logfile)

	signalTime = 0

	#function updateSignals()
	#	result = TimeManager.OnTick(0.01, signalTime)
	#	if result[1]
	#		signalTime = result[2]
	#		Reactive.run(1)
	#	end
	#end

#macro ifund(exp)
#    local e = :($exp)
#    isdefined(e.args[1]) ? :($(e.args[1])) : :($(esc(exp)))
#end

	JLGEngine.init()

	RessourceManager.AddPath(:LOGS,"logs")
	RessourceManager.AddPath(:SCRIPTS,"scripts")
	RessourceManager.AddPath(:SHADERS,"shaders")

	println("---------------------------------------------------------------------")
	println("Start Program @ ", now())
	versioninfo()

	#try
	WINDOW = WindowManager.create("Julia GLFW Window",(800,600))

	#(GLFW.OPENGL_FORWARD_COMPAT, true)
	#(GLFW.OPENGL_PROFILE, GLFW.OPENGL_CORE_PROFILE)
	WindowManager.open(WINDOW, [(:OPENGL_DEBUG_CONTEXT,true),(:SAMPLES, 4),(:CONTEXT_VERSION_MAJOR,3),(:CONTEXT_VERSION_MINOR,3)])

	println(GRAPHICSDRIVER().GetVersion(:VERSION))
	println("---------------------------------------------------------------------")

	GRAPHICSDRIVER().init()

	RenderManager.init()

	programTick=0.1

  """ TODO """
	function tickUpdate(script::JLScript)
		script.state.currentTime = currentTime(script.state.startTime)
		script.state.ticks+=1
		if OnTime(1.0,script.state.ticksTime)
			script.state.ticksTotal=script.state.ticks
			script.state.ticks=0
		end
	end

  """ TODO """
	function reload(script::JLScript)
		script.state.isRunning=false
		script.state.isRenderRunOnce=false
		script.state.isRenderOn=true

		JLScriptManager.run(script)

		if !script.state.isInitalized
			script(:OnInit)

			OnRender=JLScriptManager.exists(script,:OnRender) ? () -> script(:OnRender) : nothing
			RenderManager.setOnRender(() -> script(:OnPreRender),() -> script(:OnPostRender),OnRender)
			RenderManager.setOnDraw(() -> script(:OnPreDraw),() -> script(:OnPostDraw))

			script.state.isInitalized=true
		end

		script(:OnReload)
	end

  """ TODO """
	function create(id::Symbol,path::String)
		script = JLScript(id, FileSource(path))
		script.state = ScriptState()
		script.objref = ScriptRefs()
		script.objref.WINDOW = WINDOW
		script.objref.GD = GRAPHICSDRIVER()

		WindowManager.setListener(WINDOW,script.id,(args...)->script(args...))

		list=Dict{Symbol,Function}(
			 :scriptTime => () -> script.state.currentTime
			,:OnTick => function(tick::Real)
				m=modf(tick)
				t=m[1]>0?1/m[1]:0
				g=m[2]>0?(TimeManager.now() % m[2]) :0
				println(m," | ", t," | ",g," | ",script.state.ticks/script.state.ticksTotal)
				g < 1 && (script.state.ticksTotal==0?true:(script.state.ticks/script.state.ticksTotal == t))
			end
			,:reload => function()
				println("Reload Script...")
				programTick=0.1
				RenderManager.reset()
				GRAPHICSDRIVER().resetRegisters()
				reload(script)
			end
			,:setProgramTick => (tick::Real) -> programTick = tick
			,:setRenderUpdate => (turn::Bool) -> script.state.isRenderOn = turn
		)

		script.extern = merge(script.extern,list)

		registerFileUpdate(script.source, (source::FileSource) -> list[:reload]())
	end

  """ TODO """
	function run(script::JLScript)
		if !script.state.isRunning
			script(:OnStart)
			RenderManager.start()
			script.state.isRunning=true
		end

		tickUpdate(script)
		script(:OnUpdate)
		RenderManager.update()

		if script.state.isRenderOn
			RenderManager.render()
			if !script.state.isRenderRunOnce script.state.isRenderRunOnce=true end
		end
	end

	# create script
	create(:input, "scripts/input.jl")

	WindowManager.loop(WINDOW, function()
		Libc.systemsleep(programTick)
		runOnFileUpdate()
		JLScriptManager.loop(run)
	end)

	# bad()
#catch err
#	VLWindow.terminate()
#	println("There was an error: ", err)
	#readline()
#end

end

end

max_stars_repo_name: Gilga/JGE

max_issues_repo_name: Gilga/JGE

max_forks_repo_name: Gilga/JGE


text:
function _precompile_()
    ccall(:jl_generating_output, Cint, ()) == 1 || return nothing
    crayon"red"
    print(IOBuffer(), Crayon(foreground = :red), "In red. ", Crayon(bold = true), "Red and bold")
    precompile(Tuple{typeof(Base.print), Base.TTY, Crayons.Crayon})
end

_precompile_()


text:
# This file is auto-generated by AWSMetadata.jl
using AWS
using AWS.AWSServices: workmailmessageflow
using AWS.Compat
using AWS.UUIDs

"""
    get_raw_message_content(message_id)
    get_raw_message_content(message_id, params::Dict{String,<:Any})

Retrieves the raw content of an in-transit email message, in MIME format.

# Arguments
- `message_id`: The identifier of the email message to retrieve.

"""
function get_raw_message_content(
    messageId; aws_config::AbstractAWSConfig=global_aws_config()
)
    return workmailmessageflow(
        "GET",
        "/messages/$(messageId)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function get_raw_message_content(
    messageId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return workmailmessageflow(
        "GET",
        "/messages/$(messageId)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    put_raw_message_content(content, message_id)
    put_raw_message_content(content, message_id, params::Dict{String,<:Any})

Updates the raw content of an in-transit email message, in MIME format. This example
describes how to update in-transit email message. For more information and examples for
using this API, see  Updating message content with AWS Lambda.  Updates to an in-transit
message only appear when you call PutRawMessageContent from an AWS Lambda function
configured with a synchronous  Run Lambda rule. If you call PutRawMessageContent on a
delivered or sent message, the message remains unchanged, even though GetRawMessageContent
returns an updated message.

# Arguments
- `content`: Describes the raw message content of the updated email message.
- `message_id`: The identifier of the email message being updated.

"""
function put_raw_message_content(
    content, messageId; aws_config::AbstractAWSConfig=global_aws_config()
)
    return workmailmessageflow(
        "POST",
        "/messages/$(messageId)",
        Dict{String,Any}("content" => content);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function put_raw_message_content(
    content,
    messageId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return workmailmessageflow(
        "POST",
        "/messages/$(messageId)",
        Dict{String,Any}(mergewith(_merge, Dict{String,Any}("content" => content), params));
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end