text:
#########################################################################
#
#   zeronode() : Builds the graph generating the diff accumulator starting node
#
#########################################################################
#
#   - Generated graph contains an External node :tv that should be mapped to the source node
#
#########################################################################

# wrapper for nodes, with error message showing nodes
function zeronode(n::ExNode)
    try
        return zeronode(n.val)
    catch e
        error("[zeronode] $e for node $(repr(n)[1:min(40, end)])")
    end
end

## 0. where derivation makes no sense
zeronode(t::Type{Symbol}) = tograph( :(0.) )
zeronode(t::Type{DataType}) = tograph( :(0.) )
zeronode(t::Type{TypeConstructor}) = tograph( :(0.) )
zeronode(t::Type{Type}) = tograph( :(0.) )
zeronode(t::Type{Function}) = tograph( :(0.) )
zeronode(t::Type{Module}) = tograph( :(0.) )

# 0. for scalars
zeronode{T<:Real}(t::Type{T})    = tograph( :(0.) )

# [0.,0.] for ranges and Complex
zeronode{T<:Complex}(t::Type{T}) = tograph( :( zeros(2) ) )
zeronode{T<:Range}(t::Type{T}) = tograph( :( zeros(2) ) )

# Float64 arrays for ..
zeronode{T<:BitArray}(t::Type{T}) = tograph( :( zeros(size(tv)) ) )
zeronode{T<:Real}(t::Type{Array{T}}) = tograph( :( zeros(size(tv)) ) )

function zeronode(v)

    # Arrays or equivalent
    if method_exists(getindex, (v, Int64)) && # can be indexed
       method_exists(eltype, (v,)) &&
       method_exists(size, (v,))              # has a shape

        if eltype(v) <: Real
            return tograph( :( zeros(size(tv)) ) )  # array of zeros

        elseif isleaftype(eltype(v))
            # build element constructor
            n2 = NConst(:abcd, [], [], eltype(v), false)
            ge = zeronode(n2)

            # build loop sub-graph
            fg = ExGraph()
            ni  = addnode!(fg, NExt(:i))  ; fg.exti[ni] = :i    # loop index
            nv  = addnode!(fg, NExt(:v))  ; fg.exti[nv] = :v    # array to fill
            nt  = addnode!(fg, NExt(:tv)) ; fg.exti[nt] = :tv   # ref node
            nt2 = addnode!(fg, NRef(:getidx, [nt, ni]))         # ref node[i]

            nr = addgraph!(ge, fg, Dict( :tv => nt2))
            ns = addnode!(fg, NSRef(:setidx, [nv, nr, ni]) )
            fg.seti[ns] = :v

            # build final graph
            g  = tograph( :( Array(Any, size(tv)) ) )
            nv = getnode(g.seti, nothing) ; fg.exto[nv] = :v
            nt = addnode!(g, NExt(:tv))   ; fg.exto[nt] = :tv ; g.exti[nt] = :tv
            nr = addgraph!( :( 1:length(nv) ), g, Dict( :nv => nv) )
            nf = addnode!(g, NFor( Any[:i, fg], [ nr, nv, nt ]) )
            ns = addnode!(g, NIn( :v, [ nf ]) ) ; fg.seto[ns] = :v
            g.seti[ns] = nothing

            return g
        else
          error("Can't build a diff accumulator for node (avoid Array{Any} types)")
        end

    # Tuples or equivalent
    # TODO  :could tuples give tuple accumulators ?
    elseif method_exists(getindex, (v, Int64)) && # can be indexed
           method_exists(eltype, (v,)) &&
           method_exists(length, (v,))            # has a length (typically a Tuple)

        if eltype(v) <: Real
            return tograph( :( zeros(length(tv)) ) )  # array of zeros

        elseif isleaftype(eltype(v))
            # build element constructor
            n2 = NConst(:abcd, [], [], eltype(v), false)
            ge = zeronode(n2)

            # build loop sub-graph
            fg = ExGraph()
            ni  = addnode!(fg, NExt(:i))  ; fg.exti[ni] = :i    # loop index
            nv  = addnode!(fg, NExt(:v))  ; fg.exti[nv] = :v    # array to fill
            nt  = addnode!(fg, NExt(:tv)) ; fg.exti[nt] = :tv   # ref node
            nt2 = addnode!(fg, NRef(:getidx, [nt, ni]))         # ref node[i]

            nr = addgraph!(ge, fg, Dict( :tv => nt2))
            ns = addnode!(fg, NSRef(:setidx, [nv, nr, ni]) )
            fg.seti[ns] = :v

            # build final graph
            g  = tograph( :( Array(Any, size(tv)) ) )
            nv = getnode(g.seti, nothing) ; fg.exto[nv] = :v
            nt = addnode!(g, NExt(:tv))   ; fg.exto[nt] = :tv ; g.exti[nt] = :tv
            nr = addgraph!( :( 1:length(nv) ), g, Dict( :nv => nv) )
            nf = addnode!(g, NFor( Any[:i, fg], [ nr, nv, nt ]) )
            ns = addnode!(g, NIn( :v, [ nf ]) ) ; fg.seto[ns] = :v
            g.seti[ns] = nothing

            return g
        else
            g  = tograph( :( Array(Any, $(length(v.parameters)) ) ) )
            nv = addnode!(g, NExt(:tv)) ; g.exti[nv] = :tv
            # TODO : optimize to an array{Float64} instead of array{Any} if all fields are Reals

            for i in 1:length(v.parameters)
                ni      = addnode!(g, NConst(i))
                nf      = addnode!(g, NRef(:getidx, [ getnode(g.exti, :tv), ni ], [], v.parameters[i], false) )

                ng      = zeronode( nf )
                nn      = addgraph!(ng, g, Dict( :tv => nf ))
                ns      = addnode!(g, NSRef(:setidx, [getnode(g.seti, nothing), nn, ni]))
                g.seti[ns] = nothing
            end

            return g
        end

    # Composite types or equivalent
    elseif Base.isstructtype(v) && # type with fields
           method_exists(fieldnames, (v,))

        g  = tograph( :( Array(Any, $(length(fieldnames(v))) ) ) )
        nv = addnode!(g, NExt(:tv)) ; g.exti[nv] = :tv

        for (i, n2) in enumerate(fieldnames(v))  # i, n2 = 1, :val
            # create node for holding field value
            nf      = addnode!(g, NDot(QuoteNode(n2), [ getnode(g.exti, :tv) ], [], fieldtype(v, n2), false) )

            ng      = zeronode( nf )
            nn      = addgraph!(ng, g, Dict( :tv => nf ))
            ni      = addnode!(g, NConst(i))
            ns      = addnode!(g, NSRef(:setidx, [getnode(g.seti, nothing), nn, ni]))
            g.seti[ns] = nothing
        end

        return g

    else
        error("Unable to build diff accumulator")

    end
end


text:
@testset "1019.next-greater-node-in-linked-list.jl" begin
    @test next_larger_nodes(ListNode{Int}([1, 7, 5, 1, 9, 2, 5, 1])) ==
          [7, 9, 9, 9, 0, 5, 0, 0]
    @test next_larger_nodes(ListNode{Int}([2, 7, 4, 3, 5])) == [7, 0, 5, 5, 0]
    @test next_larger_nodes(ListNode{Int}([2, 1, 5])) == [5, 5, 0]
end

ext: jl


text:
export AbstractExplorer

"""
    (p::AbstractExplorer)(x)
    (p::AbstractExplorer)(x, mask)

Define how to select an action based on action values.
"""
abstract type AbstractExplorer end

function (p::AbstractExplorer)(x) end
function (p::AbstractExplorer)(x, mask) end

"""
    get_prob(p::AbstractExplorer, x) -> AbstractDistribution

Get the action distribution given action values.
"""
function RLBase.get_prob(p::AbstractExplorer, x) end

"""
    get_prob(p::AbstractExplorer, x, mask)

Similart to `get_prob(p::AbstractExplorer, x)`, but here only the `mask`ed elements are considered.
"""
function RLBase.get_prob(p::AbstractExplorer, x, mask) end


text:
module ConstructionToolsBase

using ...DielectricFunctions,
...Shapes,
...Bravais,
...DefineSimulation

export Subdomains,
add_subdomain,
build_domain

"""
    subdomains = Subdomains()
    subdomains = Subdomains(is_in_subdomain::Array{AbstractShape}, params::Array{Dict}, type::Array{AbstractShape}, ε::Array{Function}, F::Array{Function})

`Subdomains()` initializes empty region

`Subdomains(functions, params, types, ε's, F's)` creates region with specified parameters
"""
struct Subdomains{TSD,TP,TDT,TE,TF}
    is_in_subdomain::TSD
    params::TP
    type::TDT
    ε::TE
    F::TF

    function Subdomains()
        new{Tuple{},Tuple{},Tuple{},Tuple{},Tuple{}}((), (), (), (), ())
    end
    function Subdomains(is_in_subdomain::Tsd, params::Tp, type::Tdt, ε::Te, F::Tf) where {Tsd,Tp,Tdt,Te,Tf}#{Tsd<:NTuple{N,AbstractShape},Tp<:NTuple{N,AbstractDict},Tdt<:AbstractDomain,} where N
        new{Tsd,Tp,Tdt,Te,Tf}(is_in_subdomain, params, type, ε, F)
    end

    function Base.show(io::IO, sbd::Subdomains)
        print(io, "Subdomains, with ", length(sbd.is_in_subdomain), " subdomains:")
        temp0 = [["\n\t\tshape: ", sbd.is_in_subdomain[i]] for i ∈ eachindex(sbd.params)]
        temp1 = [["\n\t\tε: ", sbd.ε[i]] for i ∈ eachindex(sbd.ε)]
        temp2 = [["\n\t\tF: ", sbd.F[i]] for i ∈ eachindex(sbd.F)]
        temp3 = [["\n\t\tparams: ", sbd.params[i]] for i ∈ eachindex(sbd.params)]
        temp4 = [["\n\t\ttype: ", sbd.type[i]] for i ∈ eachindex(sbd.type)]
        for i ∈ eachindex(temp1)
            print(io, "\n\tsubdomain ", i)
            print(io, temp0[i]...)
            print(io, temp1[i]...)
            print(io, temp2[i]...)
            print(io, temp3[i]...)
            print(io, temp4[i]...)
        end
    end
end

################################################################################
# GENERIC REGION CONSTRUCTION
################################################################################
"""
    add_subdomain(is_in_subdomain::AbstractShape, params::Dict, type::Symbol, ε::Function, F::Function, old_subdomains::Subdomains)
"""
function add_subdomain(
                    is_in_subdomain::AbstractShape,
                    params = Dict(),
                    type::AbstractDomain = GenericDomain(),
                    ε::Function = piecewise_constant_ε,
                    F::Function = piecewise_constant_F,
                    old_subdomains::Subdomains = Subdomains()
                    )

    return Subdomains(
                tuple(is_in_subdomain, old_subdomains.is_in_subdomain...),
                tuple(params, old_subdomains.params...),
                tuple(type, old_subdomains.type...),
                tuple(ε, old_subdomains.ε...),
                tuple(F, old_subdomains.F...)
                )
end
"""
    add_subdomain(domain::Domain; is_in_subdomain::AbstractShape, params::Dict, type::Symbol, ε::Function, F::Function)
"""
function add_subdomain(
            domain::Domain;
            is_in_subdomain::AbstractShape,
            params = Dict(),
            type::AbstractDomain = GenericDomain(),
            ε::Function = piecewise_constant_ε,
            F::Function = piecewise_constant_F
            )

    # subdomains =  Subdomains(
    #             tuple(is_in_subdomain, domain.is_in_subdomain...),
    #             tuple(params, domain.subdomain_params...),
    #             tuple(type, domain.subdomain_type...),
    #             tuple(ε, domain.subdomain_ε...),
    #             tuple(F, domain.subdomain_F...)
    #             )

    subdomains =  Subdomains(domain.is_in_subdomain,
                    domain.subdomain_params,
                    domain.subdomain_type,
                    domain.subdomain_ε,
                    domain.subdomain_F)

    return build_domain(domain,subdomains)
end
"""
    domain = add_subdomain(domain, subdomains)
    domain = add_subdomain(subdomains, domain)
"""
function add_subdomain(domain::Domain, subdomains::Subdomains)

    subdomains = Subdomains(
                tuple(subdomains.is_in_subdomain, domain.is_in_subdomain...),
                tuple(subdomains.params, domain.subdomain_params...),
                tuple(subdomains.type, domain.subdomain_type...),
                tuple(subdomains.ε, domain.subdomain_ε...),
                tuple(subdomains.F, domain.subdomain_F...)
                )

    return build_domain(subdomains;
        is_in_domain = domain.is_in_domain,
        domain_params = domain.domain_params,
        domain_type = domain.domain_type,
        domain_ε = domain.domain_ε,
        domain_F = domain.domain_F,
        lattice = domain.lattice,
        which_asymptote = domain.which_asymptote,
        which_waveguide = domain.which_waveguide
        )
end
function add_subdomain(subdomains::Subdomains, domain::Domain)
    return add_subdomain(domain, subdomain)
end

"""
    build_domain
"""
function build_domain(
            subomains::Tsd = Subdomains();
            is_in_domain::Tsh = Universe(),
            domain_params::Td = Dict(:n₁ => 1, :n₂ => 0, :F => 0),
            domain_type::Tdt = GenericDomain(),
            domain_ε::Function = piecewise_constant_ε,
            domain_F::Function = piecewise_constant_F,
            lattice::BravaisLattice = BravaisLattice(),
            which_asymptote::Symbol = :none,
            which_waveguide::Int = 0
            ) where {Tsd<:Subdomains, Tsh<:AbstractShape, Td<:Dict, Tdt<:AbstractDomain}

    return Domain(; is_in_domain=is_in_domain,
                    domain_params=domain_params,
                    domain_type=domain_type,
                    domain_ε=domain_ε,
                    domain_F=domain_F,
                    is_in_subdomain = subomains.is_in_subdomain,
                    subdomain_params = subomains.params,
                    subdomain_type = subomains.type,
                    subdomain_ε = subomains.ε,
                    subdomain_F = subomains.F,
                    lattice=lattice,
                    which_asymptote=which_asymptote,
                    which_waveguide=which_waveguide
                    )
end

################################################################################
# SHAPE OVERLOADING
################################################################################
"""
    subdomains = Circle(R ,x0 ,y0 ,params::Dict, subdomains::Subdomains; type=:background, ε=piecewise_constant_ε, F=piecewise_constant_F)
"""
function Shapes.Circle(R::Number, x0::Number, y0::Number,
            params::Dict,
            subdomains::Subdomains;
            type::AbstractDomain=GenericDomain(),
            ε::Function = piecewise_constant_ε,
            F::Function = piecewise_constant_F)

    return add_subdomain(Circle(R,x0,y0), params, type, ε, F, subdomains)
end
"""
    domain = Circle(R, x0, y0, params::Dict, domain::Domain; type=:background, ε=piecewise_constant_ε, F=piecewise_constant_F)
"""
function Shapes.Circle(R::Number, x0::Number, y0::Number,
            params::Dict,
            domain::Domain;
            type::AbstractDomain=GenericDomain(),
            ε::Function = piecewise_constant_ε,
            F::Function = piecewise_constant_F)

    subdomains = Circle(R, x0, y0, params, Subdomains(); type=type, ε=ε, F=F)
    return add_subdomain(domain, subdomains)
end
"""
    domain = Circle(R, x0, y0, params::Dict; type=:background, ε=piecewise_constant_ε, F=piecewise_constant_F)
"""
function Shapes.Circle(R::Number, x0::Number, y0::Number,
            params::Dict;
            type::AbstractDomain=GenericDomain(),
            ε::Function = piecewise_constant_ε,
            F::Function = piecewise_constant_F)

    return build_domain(;is_in_domain=Circle(R,x0,y0), domain_params=params,
                            domain_type=type, domain_ε=ε, domain_F=F)
end

"""
    subdomains = Ellipse(a, b, x0, y0, θ, params::Dict, subdomains::Subdomains; type=:background, ε=piecewise_constant_ε, F=piecewise_constant_F)
"""
function Shapes.Ellipse(a::Number, b::Number, x0::Number, y0::Number, θ::Number,
            params::Dict,
            subdomains::Subdomains;
            type::AbstractDomain=GenericDomain(),
            ε::Function = piecewise_constant_ε,
            F::Function = piecewise_constant_F)

    return add_subdomain(Ellipse(a,b,x0,y0,θ), params, type, ε, F, subdomains)
end
"""
    domain = Ellipse(a, b, x0, y0, θ, params::Dict, domain::Domain; type=:background, ε=piecewise_constant_ε, F=piecewise_constant_F)
"""
function Shapes.Ellipse(a::Number, b::Number, x0::Number, y0::Number, θ::Number,
            params::Dict,
            domain::Domain;
            type::AbstractDomain=GenericDomain(),
            ε::Function = piecewise_constant_ε,
            F::Function = piecewise_constant_F)

    subdomains = Ellipse(a, b, x0, y0, θ, params, Subdomains(); type=type, ε=ε, F=F)
    return add_subdomain(domain, subdomains)
end
"""
    domain = Ellipse(a, b, x0, y0, θ, params::Dict; type=:background, ε=piecewise_constant_ε, F=piecewise_constant_F)
"""
function Shapes.Ellipse(a::Number, b::Number, x0::Number, y0::Number, θ::Number,
            params::Dict;
            type::AbstractDomain=GenericDomain(),
            ε::Function = piecewise_constant_ε,
            F::Function = piecewise_constant_F)

    return build_domain(;is_in_domain=Ellipse(a,b,x0,y0,θ), domain_params=params,
                            domain_type=type, domain_ε=ε, domain_F=F)
end

"""
    subdomains = Square(a, x0, y0, θ, params::Dict, subdomains::Subdomains; type=:background, ε=piecewise_constant_ε, F=piecewise_constant_F)
"""
function Shapes.Square(a::Number, x0::Number, y0::Number, θ::Number,
            params::Dict,
            subdomains::Subdomains;
            type::AbstractDomain=GenericDomain(),
            ε::Function = piecewise_constant_ε,
            F::Function = piecewise_constant_F)

    return add_subdomain(Square(a,x0,y0,θ), params, type, ε, F, subdomains)
end
"""
    domain = Square(a, x0, y0, θ, params::Dict, domain::Domain; type=:background, ε=piecewise_constant_ε, F=piecewise_constant_F)
"""
function Shapes.Square(a::Number, x0::Number, y0::Number, θ::Number,
            params::Dict,
            domain::Domain;
            type::AbstractDomain=GenericDomain(),
            ε::Function = piecewise_constant_ε,
            F::Function = piecewise_constant_F)

    subdomains = Square(a, x0, y0, θ, params, Subdomains(); type=type, ε=ε, F=F)
    return add_subdomain(domain, subdomains)
end
"""
    domain = Square(a, x0, y0, θ, params::Dict; type=:background, ε=piecewise_constant_ε, F=piecewise_constant_F)
"""
function Shapes.Square(a::Number, x0::Number, y0::Number, θ::Number,
            params::Dict;
            type::AbstractDomain=GenericDomain(),
            ε::Function = piecewise_constant_ε,
            F::Function = piecewise_constant_F)

    return build_domain(;is_in_domain=Square(a,x0,y0,θ), domain_params=params,
                            domain_type=type, domain_ε=ε, domain_F=F)
end

"""
    subdomains = Rectangle(a, b, x0, y0, θ, params::Dict, subdomains::Subdomains; type=:background, ε=piecewise_constant_ε, F=piecewise_constant_F)
"""
function Shapes.Rectangle(a::Number, b::Number, x0::Number, y0::Number, θ::Number,
            params::Dict,
            subdomains::Subdomains;
            type::AbstractDomain=GenericDomain(),
            ε::Function = piecewise_constant_ε,
            F::Function = piecewise_constant_F)

    return add_subdomain(Rectangle(a, b, x0, y0, θ), params, type, ε, F, subdomains)
end
"""
    domain = Rectangle(a, b, x0, y0, θ, params::Dict, domain::Domain; type=:background, ε=piecewise_constant_ε, F=piecewise_constant_F)
"""
function Shapes.Rectangle(a::Number, b::Number, x0::Number, y0::Number, θ::Number,
            params::Dict,
            domain::Domain;
            type::AbstractDomain=GenericDomain(),
            ε::Function = piecewise_constant_ε,
            F::Function = piecewise_constant_F)

    subdomains = Rectangle(a, b, x0, y0, θ, params, Subdomains(); type=type, ε=ε, F=F)
    return add_subdomain(domain, subdomains)
end
"""
    domain = Rectangle(a, b, x0, y0, θ, params::Dict; type=:background, ε=piecewise_constant_ε, F=piecewise_constant_F)
"""
function Shapes.Rectangle(a::Number, b::Number, x0::Number, y0::Number, θ::Number,
            params::Dict;
            type::AbstractDomain=GenericDomain(),
            ε::Function = piecewise_constant_ε,
            F::Function = piecewise_constant_F)

    return build_domain(;is_in_domain=Rectangle(a,b,x0,y0,θ), domain_params=params,
                            domain_type=type, domain_ε=ε, domain_F=F)
end

"""
    subdomains = Parallelogram(a, b, α, x0, y0, θ, params::Dict, subdomains::Subdomains; type=:background, ε=piecewise_constant_ε, F=piecewise_constant_F)
"""
function Shapes.Parallelogram(a::Number, b::Number, α::Number, x0::Number, y0::Number, θ::Number,
            params::Dict,
            subdomains::Subdomains;
            type::AbstractDomain=GenericDomain(),
            ε::Function = piecewise_constant_ε,
            F::Function = piecewise_constant_F)

    return add_subdomain(Parallelogram(a, b, α, x0, y0, θ), params, type, ε, F, subdomains)
end
"""
    domain = Parallelogram(a, b, α, x0, y0, θ, params::Dict, domain::Domain; type=:background, ε=piecewise_constant_ε, F=piecewise_constant_F)
"""
function Shapes.Parallelogram(a::Number, b::Number, α::Number, x0::Number, y0::Number, θ::Number,
            params::Dict,
            domain::Domain;
            type::AbstractDomain=GenericDomain(),
            ε::Function = piecewise_constant_ε,
            F::Function = piecewise_constant_F)

    subdomains = Parallelogram(a, b, α, x0, y0, θ, params, Subdomains(); type=type, ε=ε, F=F)
    return add_subdomain(domain, subdomains)
end
"""
    domain = Parallelogram(a, b, α, x0, y0, θ, params::Dict; type=:background, ε=piecewise_constant_ε, F=piecewise_constant_F)
"""
function Shapes.Parallelogram(a::Number, b::Number, α::Number, x0::Number, y0::Number, θ::Number,
            params::Dict;
            type::AbstractDomain=GenericDomain(),
            ε::Function = piecewise_constant_ε,
            F::Function = piecewise_constant_F)

    return build_domain(;is_in_domain=Parallelogram(a,b,α,x0,y0,θ), domain_params=params,
                            domain_type=type, domain_ε=ε, domain_F=F)
end

"""
    subdomains = DeformedDisk(a, b, x0, y0, θ, params::Dict, subdomains::Subdomains; type=:background, ε=piecewise_constant_ε, F=piecewise_constant_F)
"""
function Shapes.DeformedDisk(R::Number, x0::Number, y0::Number, M::AbstractArray, a::AbstractArray, φ::AbstractArray,
            params::Dict,
            subdomains::Subdomains;
            type::AbstractDomain=GenericDomain(),
            ε::Function = piecewise_constant_ε,
            F::Function = piecewise_constant_F)

    return add_subdomain(DeformedDisk(R, x0, y0, M, a, φ), params, type, ε, F, subdomains)
end
"""
    domain = DeformedDisk(a, b, x0, y0, θ, params::Dict, domain::Domain; type=:background, ε=piecewise_constant_ε, F=piecewise_constant_F)
"""
function Shapes.DeformedDisk(R::Number, x0::Number, y0::Number, M::AbstractArray, a::AbstractArray, φ::AbstractArray,
            params::Dict,
            domain::Domain;
            type::AbstractDomain=GenericDomain(),
            ε::Function = piecewise_constant_ε,
            F::Function = piecewise_constant_F)

    subdomains = DeformedDisk(R, x0, y0, M, a, φ, params, Subdomains(); type=type, ε=ε, F=F)
    return add_subdomain(domain, subdomains)
end
"""
    domain = DeformedDisk(a, b, x0, y0, θ, params::Dict; type=:background, ε=piecewise_constant_ε, F=piecewise_constant_F)
"""
function Shapes.DeformedDisk(R::Number, x0::Number, y0::Number, M::AbstractArray, a::AbstractArray, φ::AbstractArray,
            params::Dict;
            type::AbstractDomain=GenericDomain(),
            ε::Function = piecewise_constant_ε,
            F::Function = piecewise_constant_F)

    return build_domain(;is_in_domain=DeformedDisk(R,x0,y0,M,a,φ), domain_params=params,
                            domain_type=type, domain_ε=ε, domain_F=F)
end

"""
    subdomains = Universe(params::Dict, subdomains::Subdomains; type=:background, ε=piecewise_constant_ε, F=piecewise_constant_F)
"""
function Shapes.Universe(
            params::Dict,
            subdomains::Subdomains;
            type::AbstractDomain=GenericDomain(),
            ε::Function = piecewise_constant_ε,
            F::Function = piecewise_constant_F)

    return add_subdomain(Universe(), params, type, ε, F, subdomains)
end
"""
    domain = Universe(params::Dict; type=:background, ε=piecewise_constant_ε, F=piecewise_constant_F)
"""
function Shapes.Universe(
            params::Dict;
            type::AbstractDomain=GenericDomain(),
            ε::Function = piecewise_constant_ε,
            F::Function = piecewise_constant_F)

    return build_domain(;is_in_domain=Universe(), domain_params=params,
                            domain_type=type, domain_ε=ε, domain_F=F)
end
"""
    domain = Universe(params::Dict; type=:background, ε=piecewise_constant_ε, F=piecewise_constant_F)
"""
function Shapes.Universe(
            n₁::Number;
            type::AbstractDomain=GenericDomain(),
            ε::Function = piecewise_constant_ε,
            F::Function = piecewise_constant_F)

    return build_domain(;is_in_domain=Universe(), domain_params=Dict(:n₁=>n₁),
                            domain_type=type, domain_ε=ε, domain_F=F)
end

end #module


text:
printstyled("  writesac from SeisEvent\n", color=:light_green)
writesac(rse_wb(8))


text:
using Distributed
addprocs(8)
@everywhere include("reproducibility.jl")
include("reproducibility.jl")

##
@everywhere using LightGraphs
@everywhere using LinearAlgebra
@everywhere using SparseArrays
@everywhere using Expokit
using LambertW
using Base.Iterators
using NPZ

##

@everywhere function hamiltonian_and_normed_eigen(g)
    h = -Matrix{Float64}(laplacian_matrix(g))
    eigdec = eigen(h)
    # eigenvalues are nonnegative
    h ./= abs(minimum(eigdec.values))
    eigdec.values ./= abs(minimum(eigdec.values))
    # eigenvalues are in [-1,0]
    eigdec.values .+= 1
    h += I
    # eigenvalues are in [0,1], spectral gap next to 1

    λ1, λ2, λn = eigdec.values[[end, end-1, 1]]
    a = -(λ2 + λn)/2
    eigdec.values .+= a
    h += a*I
    eigdec.values ./= 1 + a
    h ./= 1 + a
    sparse(h), eigdec
end

@everywhere function true_probability(h, eigdec)
    n = size(h, 1)
    a = eigdec.vectors[1,1:end-1]
    eigs = eigdec.values[1:end-1]

    gamma = sum(abs2.(a) ./ (1 .- eigs)) / sum(abs2.(a))
    opt_time = pi*sqrt(n)/2
    #opt_time = pi/2/abs(a[1,end]) * sqrt(sum(abs2.(a) ./ (1 .- eigs).^2))

    init_state = fill(1/sqrt(n), n)
    hamiltonian = gamma*h
    hamiltonian[1,1] += 1.
    f_state = expmv(opt_time, -1im*hamiltonian, init_state)
    abs2(f_state[1])
end

@everywhere function create_data(n::Int, p::Float64)
    g = erdos_renyi(n, p)
    cc = connected_components(g)
    g = induced_subgraph(g, cc[findmax(length.(cc))[2]])[1]
    @assert is_connected(g)
    @assert nv(g) > n/2

    h, eigdec = hamiltonian_and_normed_eigen(g)
    c = eigdec.values[end-1]

    (1-c)/(1+c), true_probability(h, eigdec)
end

p0s = .5:.5:2
@everywhere p(p0, n) = p0*log(n)/n
repeating = 50

data_n = 100:100:2000

for p0 = p0s
    data = zeros(length(data_n), repeating, 2)
    for (ind_n, n) = enumerate(data_n)
        println("n $n, p0 $p0")
        @time data_tmp = pmap(_ -> create_data(n, p(p0, n)), 1:repeating)
        for (m, d_tmp) = enumerate(data_tmp)
            data[ind_n,m,:] .= d_tmp
        end
    end
    npzwrite("hiding_search/sparse_ER_probs_and_bounds_$p0.npz", data)
end
rmprocs()


text:
@everywhere function is_it_exit_par(A,H,tol)
    Z=A[1]+im *A[2]
    W=A[3]+im *A[4]
    h=1/(2*H)
    a=(2+h-2*sqrt(h+1))/h;
    lin_impulse=im*(1+a )

    z_1pos=.5*(Z+lin_impulse+W)
    z_1neg=.5*(Z-lin_impulse-W)
    z_2pos=.5*(-Z+lin_impulse-W)
    z_2neg=.5*(-Z-lin_impulse+W)

    #1pos and 1neg bound
    d_11=abs.(z_1pos-z_1neg)
    #1pos and 2neg bound
    d_12=abs.(z_1pos-z_2neg)

    delta_11=abs(d_11[1]-d_11[2])
    delta_12=abs(d_12[1]-d_12[2])

    return delta_11<tol || delta_12<tol
    # return delta_11, delta_12

end


text:
const stdformat = DateFormat("yyyy-mm-ddTHH:MM:SS")

isobjects(coll::PLEXOSCollection) = coll.parentclass.name == "System"

phase_rgx = r"^t_phase_(\d)$"
phasenames = Dict{Int,String}()
phasetypes = Symbol[]
phasename(s::Symbol) = uppercase(string(s))

period_rgx = r"^t_period_(\d)$"
periodnames = Dict{Int,String}()
periodtypes = Tuple{Symbol,Symbol}[]
periodname(s::Symbol) = replace(string(s), r"^(.+)s$" => s"\1")

for x in plexostables

    phasematch = match(phase_rgx, x.name)
    if phasematch !== nothing
        phase = parse(Int, phasematch[1])
        phasenames[phase] = phasename(x.fieldname)
        push!(phasetypes, x.fieldname)
    end

    isnothing(x.timestampfield) && continue
    periodmatch = match(period_rgx, x.name)
    if periodmatch !== nothing
        periodtype = parse(Int, periodmatch[1])
        periodnames[periodtype] = periodname(x.fieldname)
        push!(periodtypes, (x.fieldname, x.timestampfield))
    end

end

function h5plexosname(coll::PLEXOSCollection)
    prefix = if isobjects(coll)
        ""
    elseif isnothing(coll.complementname)
        coll.parentclass.name * "_"
    else
        coll.complementname * "_"
    end
    return sanitize(prefix * coll.name)
end

function string_uint32_table!(
    f::HDF5.Group, tablename::String, strlen::Int,
    colnames::Tuple{String,String}, data::Vector{Tuple{String,UInt32}})

    nrows = length(data)

    stringtype_id = HDF5.h5t_copy(HDF5.hdf5_type_id(String))
    HDF5.h5t_set_size(stringtype_id, strlen)
    stringtype = HDF5.Datatype(stringtype_id)
    rowlen = strlen + sizeof(UInt32)

    dt_id = HDF5.h5t_create(HDF5.H5T_COMPOUND, rowlen)
    HDF5.h5t_insert(dt_id, first(colnames), 0, stringtype)
    HDF5.h5t_insert(dt_id, last(colnames), strlen, datatype(UInt32))

    rawdata = Vector{UInt8}(undef, rowlen * nrows)
    for (i, row) in enumerate(data)
        rowbytes = vcat(UInt8.(convertstring(first(row), strlen)),
                        reinterpret(UInt8, [last(row)]))
        rawdata[((i-1)*rowlen + 1):(i*rowlen)] = rowbytes
    end

    dset = create_dataset(f, tablename, HDF5.Datatype(dt_id),
                          HDF5.dataspace((nrows,)))

    HDF5.h5d_write(
        dset, dt_id, HDF5.H5S_ALL, HDF5.H5S_ALL, HDF5.H5P_DEFAULT, rawdata)

    return

end

function string_table!(
    f::HDF5.Group, tablename::String, strlen::Int,
    colnames::NTuple{N,String}, data::Vector{NTuple{N,String}}) where N

    nrows = length(data)

    stringtype_id = HDF5.h5t_copy(HDF5.hdf5_type_id(String))
    HDF5.h5t_set_size(stringtype_id, strlen)
    stringtype = HDF5.Datatype(stringtype_id)

    dt_id = HDF5.h5t_create(HDF5.H5T_COMPOUND, N * strlen)
    for (i, colname) in enumerate(colnames)
        HDF5.h5t_insert(dt_id, colname, (i-1)*strlen, stringtype)
    end

    strings = vcat(collect.(data)...)
    charlists = convertstring.(strings, strlen)
    rawdata = UInt8.(vcat(charlists...))

    dset = create_dataset(f, tablename, HDF5.Datatype(dt_id),
                    HDF5.dataspace((nrows,)))
    HDF5.h5d_write(
        dset, dt_id, HDF5.H5S_ALL, HDF5.H5S_ALL, HDF5.H5P_DEFAULT, rawdata)

    return

end

function convertstring(s::AbstractString, strlen::Int)

    oldstring = ascii(s)
    newstring = fill('\0', strlen)

    for i in 1:min(length(oldstring), length(newstring))
        newstring[i] = oldstring[i]
    end

    return newstring

end

sanitize(s::AbstractString) = replace(lowercase(ascii(s)), " " => "")

function findsample(systemdata::PLEXOSSolutionDataset, samplename::String)
    i = findfirst(s -> s.name == samplename, systemdata.samples)
    isnothing(i) && error("Sample '$samplename' does not exist in the solution.")
    return systemdata.samples[i]
end


text:
struct SVHN2 <: ColorImage{MLImage} end
getModule(::SVHN2) = MLDatasets.SVHN2
size(::SVHN2) = (73257, 0, 26032)
problem(::SVHN2) = Classification

max_stars_repo_name: GruAna/PreprocessData

max_issues_repo_name: GruAna/PreprocessData

max_forks_repo_name: GruAna/PreprocessData


text:
using PackageCompiler
using MKL_jll

include("../src/install.jl")
enable_mkl_startup()


text:
include("src/init.jl")# Include all relevant functions

include("src/sac/policy.jl")
include("src/sac/gradients.jl")
include("src/sac/updates.jl")

#If the architectur should be modified a bit, use this algorithm:
include("src/td3/episodes.jl")

#Pl  = init_θ_pol_SAC(params.structure_net_pl)
Pl  = init_θ_single(params.structure_net_pl)

Vf  = init_θ_single(params.structure_net_vf)
Qf1 = init_θ_single(params.structure_net_qf)
Qf2 = init_θ_single(params.structure_net_qf)
Vf_tar = copy(Vf)

best_net = copy(Pl)
res = zeros(params.n_iter)
prec = zeros(params.n_iter)
all_traj  = init_all_traj(params)

function soft_actor_critic(params::Params, Pl::Any, Vf::Any, Qf1::Any, Qf2::Any, Vf_tar::Any, res::Array{Float64,1},
    prec::Array{Float64,1}, all_traj::Traj_Data, best_net::Any)

    #Init Part:
    best_net = copy(Pl)
    min_cost = Inf
    #------------------------
    #Initialize the Policy
    opt_para_pl  = optimizers(Pl,  Adam; lr = params.lr_pl)
    opt_para_vf  = optimizers(Vf,  Adam; lr = params.lr_vf)
    opt_para_qf1 = optimizers(Qf1, Adam; lr = 2.0*params.lr_qf)
    opt_para_qf2 = optimizers(Qf2, Adam; lr = 0.5*params.lr_qf)

    #Init Progress bar
    progress = Progress(params.n_iter,1)

    # Init rms
    rms_all = init_rms(params.s_init)
    rms_loc = init_rms(params.s_init)

    a_t_old = all_traj.traj[1].a_t
    weights_old = zeros(length(net_to_vec(Pl)),2)

    #buffer = Traj_Data(undef,0,0) #empty buffer
    buffer = Traj_Data()
    buffer.traj = Array{Trajectory,1}(undef,0)
    buffer.timesteps = 0
    buffer.n_traj = 0

    #Initialize with supervised Training of Value function:
    #parallel_episodes_SAC(params,Pl,Vf,all_traj,rms_all)
    parallel_episodes(params,Pl,Vf,all_traj,rms_all)

    #two_pass_variance!(all_traj, rms_all, params)

    mini_batch = build_minibatches(all_traj,params)

    pump_buffer!(buffer, mini_batch, params)

    for i_iter = 1:params.n_iter

        #Save data in intervalls
        if floor(i_iter/100) == ceil(i_iter/100)
            #make_video(all_traj,params, i_iter)
            save_data(res, prec, Pl, Vf, all_traj, i_iter)
        end

        #-----------------------------------------------------------------------
        #Solve it in Parallel!
        res[i_iter], prec[i_iter] = parallel_episodes_SAC(params,Pl,Vf,all_traj,rms_all)

        #Reorganice Data in minibatches
        mini_batch = build_minibatches(all_traj,params)

        #Pump the data into the buffer
        pump_buffer!(buffer, mini_batch, params)

        #Update all networks going over the collected data:
        println(" ")
        do_sac_update_parallel!(Pl, Qf1, Qf2, Vf, Vf_tar, buffer,
        params, opt_para_pl, opt_para_qf1, opt_para_qf2, opt_para_vf)

        if min_cost > res[i_iter]
            min_cost = res[i_iter]
            best_net = copy(Pl)
            println("new_best: ",min_cost)
        end

        #5.Visualize Progress
        ProgressMeter.update!(progress,i_iter) #update progress bar

        #6. Plot data of interest
        if floor(i_iter/5) == ceil(i_iter/5)
            print("  new visualization: ")
            a_t_old ,weights_old = plot_stuff(params, all_traj, a_t_old,
            weights_old, i_iter,Pl, Qf1, res, prec/params.n_traj, Pl, Pl)
        end
        print("  Result:  ", res[i_iter])
    end
end

soft_actor_critic(params, Pl, Vf, Qf1, Qf2, Vf_tar, res, prec, all_traj, best_net)

max_stars_repo_name: NikonPic/REINFORCEMENT-JULIA

max_issues_repo_name: NikonPic/REINFORCEMENT-JULIA

max_forks_repo_name: NikonPic/REINFORCEMENT-JULIA


text:
module LibGLFW

using GLFW_jll

# typedef void ( * GLFWglproc ) ( void )
const GLFWglproc = Ptr{Cvoid}

# typedef void ( * GLFWvkproc ) ( void )
const GLFWvkproc = Ptr{Cvoid}

mutable struct GLFWmonitor end

mutable struct GLFWwindow end

mutable struct GLFWcursor end

# typedef void ( * GLFWerrorfun ) ( int , const char * )
const GLFWerrorfun = Ptr{Cvoid}

# typedef void ( * GLFWwindowposfun ) ( GLFWwindow * , int , int )
const GLFWwindowposfun = Ptr{Cvoid}

# typedef void ( * GLFWwindowsizefun ) ( GLFWwindow * , int , int )
const GLFWwindowsizefun = Ptr{Cvoid}

# typedef void ( * GLFWwindowclosefun ) ( GLFWwindow * )
const GLFWwindowclosefun = Ptr{Cvoid}

# typedef void ( * GLFWwindowrefreshfun ) ( GLFWwindow * )
const GLFWwindowrefreshfun = Ptr{Cvoid}

# typedef void ( * GLFWwindowfocusfun ) ( GLFWwindow * , int )
const GLFWwindowfocusfun = Ptr{Cvoid}

# typedef void ( * GLFWwindowiconifyfun ) ( GLFWwindow * , int )
const GLFWwindowiconifyfun = Ptr{Cvoid}

# typedef void ( * GLFWwindowmaximizefun ) ( GLFWwindow * , int )
const GLFWwindowmaximizefun = Ptr{Cvoid}

# typedef void ( * GLFWframebuffersizefun ) ( GLFWwindow * , int , int )
const GLFWframebuffersizefun = Ptr{Cvoid}

# typedef void ( * GLFWwindowcontentscalefun ) ( GLFWwindow * , float , float )
const GLFWwindowcontentscalefun = Ptr{Cvoid}

# typedef void ( * GLFWmousebuttonfun ) ( GLFWwindow * , int , int , int )
const GLFWmousebuttonfun = Ptr{Cvoid}

# typedef void ( * GLFWcursorposfun ) ( GLFWwindow * , double , double )
const GLFWcursorposfun = Ptr{Cvoid}

# typedef void ( * GLFWcursorenterfun ) ( GLFWwindow * , int )
const GLFWcursorenterfun = Ptr{Cvoid}

# typedef void ( * GLFWscrollfun ) ( GLFWwindow * , double , double )
const GLFWscrollfun = Ptr{Cvoid}

# typedef void ( * GLFWkeyfun ) ( GLFWwindow * , int , int , int , int )
const GLFWkeyfun = Ptr{Cvoid}

# typedef void ( * GLFWcharfun ) ( GLFWwindow * , unsigned int )
const GLFWcharfun = Ptr{Cvoid}

# typedef void ( * GLFWcharmodsfun ) ( GLFWwindow * , unsigned int , int )
const GLFWcharmodsfun = Ptr{Cvoid}

# typedef void ( * GLFWdropfun ) ( GLFWwindow * , int , const char * [ ] )
const GLFWdropfun = Ptr{Cvoid}

# typedef void ( * GLFWmonitorfun ) ( GLFWmonitor * , int )
const GLFWmonitorfun = Ptr{Cvoid}

# typedef void ( * GLFWjoystickfun ) ( int , int )
const GLFWjoystickfun = Ptr{Cvoid}

struct GLFWvidmode
    width::Cint
    height::Cint
    redBits::Cint
    greenBits::Cint
    blueBits::Cint
    refreshRate::Cint
end

struct GLFWgammaramp
    red::Ptr{Cushort}
    green::Ptr{Cushort}
    blue::Ptr{Cushort}
    size::Cuint
end

struct GLFWimage
    width::Cint
    height::Cint
    pixels::Ptr{Cuchar}
end

struct GLFWgamepadstate
    buttons::NTuple{15, Cuchar}
    axes::NTuple{6, Cfloat}
end

function glfwInit()
    ccall((:glfwInit, libglfw), Cint, ())
end

function glfwTerminate()
    ccall((:glfwTerminate, libglfw), Cvoid, ())
end

function glfwInitHint(hint, value)
    ccall((:glfwInitHint, libglfw), Cvoid, (Cint, Cint), hint, value)
end

function glfwGetVersion(major, minor, rev)
    ccall((:glfwGetVersion, libglfw), Cvoid, (Ptr{Cint}, Ptr{Cint}, Ptr{Cint}), major, minor, rev)
end

function glfwGetVersionString()
    ccall((:glfwGetVersionString, libglfw), Ptr{Cchar}, ())
end

function glfwGetError(description)
    ccall((:glfwGetError, libglfw), Cint, (Ptr{Ptr{Cchar}},), description)
end

function glfwSetErrorCallback(callback)
    ccall((:glfwSetErrorCallback, libglfw), GLFWerrorfun, (GLFWerrorfun,), callback)
end

function glfwGetMonitors(count)
    ccall((:glfwGetMonitors, libglfw), Ptr{Ptr{GLFWmonitor}}, (Ptr{Cint},), count)
end

function glfwGetPrimaryMonitor()
    ccall((:glfwGetPrimaryMonitor, libglfw), Ptr{GLFWmonitor}, ())
end

function glfwGetMonitorPos(monitor, xpos, ypos)
    ccall((:glfwGetMonitorPos, libglfw), Cvoid, (Ptr{GLFWmonitor}, Ptr{Cint}, Ptr{Cint}), monitor, xpos, ypos)
end

function glfwGetMonitorWorkarea(monitor, xpos, ypos, width, height)
    ccall((:glfwGetMonitorWorkarea, libglfw), Cvoid, (Ptr{GLFWmonitor}, Ptr{Cint}, Ptr{Cint}, Ptr{Cint}, Ptr{Cint}), monitor, xpos, ypos, width, height)
end

function glfwGetMonitorPhysicalSize(monitor, widthMM, heightMM)
    ccall((:glfwGetMonitorPhysicalSize, libglfw), Cvoid, (Ptr{GLFWmonitor}, Ptr{Cint}, Ptr{Cint}), monitor, widthMM, heightMM)
end

function glfwGetMonitorContentScale(monitor, xscale, yscale)
    ccall((:glfwGetMonitorContentScale, libglfw), Cvoid, (Ptr{GLFWmonitor}, Ptr{Cfloat}, Ptr{Cfloat}), monitor, xscale, yscale)
end

function glfwGetMonitorName(monitor)
    ccall((:glfwGetMonitorName, libglfw), Ptr{Cchar}, (Ptr{GLFWmonitor},), monitor)
end

function glfwSetMonitorUserPointer(monitor, pointer)
    ccall((:glfwSetMonitorUserPointer, libglfw), Cvoid, (Ptr{GLFWmonitor}, Ptr{Cvoid}), monitor, pointer)
end

function glfwGetMonitorUserPointer(monitor)
    ccall((:glfwGetMonitorUserPointer, libglfw), Ptr{Cvoid}, (Ptr{GLFWmonitor},), monitor)
end

function glfwSetMonitorCallback(callback)
    ccall((:glfwSetMonitorCallback, libglfw), GLFWmonitorfun, (GLFWmonitorfun,), callback)
end

function glfwGetVideoModes(monitor, count)
    ccall((:glfwGetVideoModes, libglfw), Ptr{GLFWvidmode}, (Ptr{GLFWmonitor}, Ptr{Cint}), monitor, count)
end

function glfwGetVideoMode(monitor)
    ccall((:glfwGetVideoMode, libglfw), Ptr{GLFWvidmode}, (Ptr{GLFWmonitor},), monitor)
end

function glfwSetGamma(monitor, gamma)
    ccall((:glfwSetGamma, libglfw), Cvoid, (Ptr{GLFWmonitor}, Cfloat), monitor, gamma)
end

function glfwGetGammaRamp(monitor)
    ccall((:glfwGetGammaRamp, libglfw), Ptr{GLFWgammaramp}, (Ptr{GLFWmonitor},), monitor)
end

function glfwSetGammaRamp(monitor, ramp)
    ccall((:glfwSetGammaRamp, libglfw), Cvoid, (Ptr{GLFWmonitor}, Ptr{GLFWgammaramp}), monitor, ramp)
end

function glfwDefaultWindowHints()
    ccall((:glfwDefaultWindowHints, libglfw), Cvoid, ())
end

function glfwWindowHint(hint, value)
    ccall((:glfwWindowHint, libglfw), Cvoid, (Cint, Cint), hint, value)
end

function glfwWindowHintString(hint, value)
    ccall((:glfwWindowHintString, libglfw), Cvoid, (Cint, Ptr{Cchar}), hint, value)
end

function glfwCreateWindow(width, height, title, monitor, share)
    ccall((:glfwCreateWindow, libglfw), Ptr{GLFWwindow}, (Cint, Cint, Ptr{Cchar}, Ptr{GLFWmonitor}, Ptr{GLFWwindow}), width, height, title, monitor, share)
end

function glfwDestroyWindow(window)
    ccall((:glfwDestroyWindow, libglfw), Cvoid, (Ptr{GLFWwindow},), window)
end

function glfwWindowShouldClose(window)
    ccall((:glfwWindowShouldClose, libglfw), Cint, (Ptr{GLFWwindow},), window)
end

function glfwSetWindowShouldClose(window, value)
    ccall((:glfwSetWindowShouldClose, libglfw), Cvoid, (Ptr{GLFWwindow}, Cint), window, value)
end

function glfwSetWindowTitle(window, title)
    ccall((:glfwSetWindowTitle, libglfw), Cvoid, (Ptr{GLFWwindow}, Ptr{Cchar}), window, title)
end

function glfwSetWindowIcon(window, count, images)
    ccall((:glfwSetWindowIcon, libglfw), Cvoid, (Ptr{GLFWwindow}, Cint, Ptr{GLFWimage}), window, count, images)
end

function glfwGetWindowPos(window, xpos, ypos)
    ccall((:glfwGetWindowPos, libglfw), Cvoid, (Ptr{GLFWwindow}, Ptr{Cint}, Ptr{Cint}), window, xpos, ypos)
end

function glfwSetWindowPos(window, xpos, ypos)
    ccall((:glfwSetWindowPos, libglfw), Cvoid, (Ptr{GLFWwindow}, Cint, Cint), window, xpos, ypos)
end

function glfwGetWindowSize(window, width, height)
    ccall((:glfwGetWindowSize, libglfw), Cvoid, (Ptr{GLFWwindow}, Ptr{Cint}, Ptr{Cint}), window, width, height)
end

function glfwSetWindowSizeLimits(window, minwidth, minheight, maxwidth, maxheight)
    ccall((:glfwSetWindowSizeLimits, libglfw), Cvoid, (Ptr{GLFWwindow}, Cint, Cint, Cint, Cint), window, minwidth, minheight, maxwidth, maxheight)
end

function glfwSetWindowAspectRatio(window, numer, denom)
    ccall((:glfwSetWindowAspectRatio, libglfw), Cvoid, (Ptr{GLFWwindow}, Cint, Cint), window, numer, denom)
end

function glfwSetWindowSize(window, width, height)
    ccall((:glfwSetWindowSize, libglfw), Cvoid, (Ptr{GLFWwindow}, Cint, Cint), window, width, height)
end

function glfwGetFramebufferSize(window, width, height)
    ccall((:glfwGetFramebufferSize, libglfw), Cvoid, (Ptr{GLFWwindow}, Ptr{Cint}, Ptr{Cint}), window, width, height)
end

function glfwGetWindowFrameSize(window, left, top, right, bottom)
    ccall((:glfwGetWindowFrameSize, libglfw), Cvoid, (Ptr{GLFWwindow}, Ptr{Cint}, Ptr{Cint}, Ptr{Cint}, Ptr{Cint}), window, left, top, right, bottom)
end

function glfwGetWindowContentScale(window, xscale, yscale)
    ccall((:glfwGetWindowContentScale, libglfw), Cvoid, (Ptr{GLFWwindow}, Ptr{Cfloat}, Ptr{Cfloat}), window, xscale, yscale)
end

function glfwGetWindowOpacity(window)
    ccall((:glfwGetWindowOpacity, libglfw), Cfloat, (Ptr{GLFWwindow},), window)
end

function glfwSetWindowOpacity(window, opacity)
    ccall((:glfwSetWindowOpacity, libglfw), Cvoid, (Ptr{GLFWwindow}, Cfloat), window, opacity)
end

function glfwIconifyWindow(window)
    ccall((:glfwIconifyWindow, libglfw), Cvoid, (Ptr{GLFWwindow},), window)
end

function glfwRestoreWindow(window)
    ccall((:glfwRestoreWindow, libglfw), Cvoid, (Ptr{GLFWwindow},), window)
end

function glfwMaximizeWindow(window)
    ccall((:glfwMaximizeWindow, libglfw), Cvoid, (Ptr{GLFWwindow},), window)
end

function glfwShowWindow(window)
    ccall((:glfwShowWindow, libglfw), Cvoid, (Ptr{GLFWwindow},), window)
end

function glfwHideWindow(window)
    ccall((:glfwHideWindow, libglfw), Cvoid, (Ptr{GLFWwindow},), window)
end

function glfwFocusWindow(window)
    ccall((:glfwFocusWindow, libglfw), Cvoid, (Ptr{GLFWwindow},), window)
end

function glfwRequestWindowAttention(window)
    ccall((:glfwRequestWindowAttention, libglfw), Cvoid, (Ptr{GLFWwindow},), window)
end

function glfwGetWindowMonitor(window)
    ccall((:glfwGetWindowMonitor, libglfw), Ptr{GLFWmonitor}, (Ptr{GLFWwindow},), window)
end

function glfwSetWindowMonitor(window, monitor, xpos, ypos, width, height, refreshRate)
    ccall((:glfwSetWindowMonitor, libglfw), Cvoid, (Ptr{GLFWwindow}, Ptr{GLFWmonitor}, Cint, Cint, Cint, Cint, Cint), window, monitor, xpos, ypos, width, height, refreshRate)
end

function glfwGetWindowAttrib(window, attrib)
    ccall((:glfwGetWindowAttrib, libglfw), Cint, (Ptr{GLFWwindow}, Cint), window, attrib)
end

function glfwSetWindowAttrib(window, attrib, value)
    ccall((:glfwSetWindowAttrib, libglfw), Cvoid, (Ptr{GLFWwindow}, Cint, Cint), window, attrib, value)
end

function glfwSetWindowUserPointer(window, pointer)
    ccall((:glfwSetWindowUserPointer, libglfw), Cvoid, (Ptr{GLFWwindow}, Ptr{Cvoid}), window, pointer)
end

function glfwGetWindowUserPointer(window)
    ccall((:glfwGetWindowUserPointer, libglfw), Ptr{Cvoid}, (Ptr{GLFWwindow},), window)
end

function glfwSetWindowPosCallback(window, callback)
    ccall((:glfwSetWindowPosCallback, libglfw), GLFWwindowposfun, (Ptr{GLFWwindow}, GLFWwindowposfun), window, callback)
end

function glfwSetWindowSizeCallback(window, callback)
    ccall((:glfwSetWindowSizeCallback, libglfw), GLFWwindowsizefun, (Ptr{GLFWwindow}, GLFWwindowsizefun), window, callback)
end

function glfwSetWindowCloseCallback(window, callback)
    ccall((:glfwSetWindowCloseCallback, libglfw), GLFWwindowclosefun, (Ptr{GLFWwindow}, GLFWwindowclosefun), window, callback)
end

function glfwSetWindowRefreshCallback(window, callback)
    ccall((:glfwSetWindowRefreshCallback, libglfw), GLFWwindowrefreshfun, (Ptr{GLFWwindow}, GLFWwindowrefreshfun), window, callback)
end

function glfwSetWindowFocusCallback(window, callback)
    ccall((:glfwSetWindowFocusCallback, libglfw), GLFWwindowfocusfun, (Ptr{GLFWwindow}, GLFWwindowfocusfun), window, callback)
end

function glfwSetWindowIconifyCallback(window, callback)
    ccall((:glfwSetWindowIconifyCallback, libglfw), GLFWwindowiconifyfun, (Ptr{GLFWwindow}, GLFWwindowiconifyfun), window, callback)
end

function glfwSetWindowMaximizeCallback(window, callback)
    ccall((:glfwSetWindowMaximizeCallback, libglfw), GLFWwindowmaximizefun, (Ptr{GLFWwindow}, GLFWwindowmaximizefun), window, callback)
end

function glfwSetFramebufferSizeCallback(window, callback)
    ccall((:glfwSetFramebufferSizeCallback, libglfw), GLFWframebuffersizefun, (Ptr{GLFWwindow}, GLFWframebuffersizefun), window, callback)
end

function glfwSetWindowContentScaleCallback(window, callback)
    ccall((:glfwSetWindowContentScaleCallback, libglfw), GLFWwindowcontentscalefun, (Ptr{GLFWwindow}, GLFWwindowcontentscalefun), window, callback)
end

function glfwPollEvents()
    ccall((:glfwPollEvents, libglfw), Cvoid, ())
end

function glfwWaitEvents()
    ccall((:glfwWaitEvents, libglfw), Cvoid, ())
end

function glfwWaitEventsTimeout(timeout)
    ccall((:glfwWaitEventsTimeout, libglfw), Cvoid, (Cdouble,), timeout)
end

function glfwPostEmptyEvent()
    ccall((:glfwPostEmptyEvent, libglfw), Cvoid, ())
end

function glfwGetInputMode(window, mode)
    ccall((:glfwGetInputMode, libglfw), Cint, (Ptr{GLFWwindow}, Cint), window, mode)
end

function glfwSetInputMode(window, mode, value)
    ccall((:glfwSetInputMode, libglfw), Cvoid, (Ptr{GLFWwindow}, Cint, Cint), window, mode, value)
end

function glfwRawMouseMotionSupported()
    ccall((:glfwRawMouseMotionSupported, libglfw), Cint, ())
end

function glfwGetKeyName(key, scancode)
    ccall((:glfwGetKeyName, libglfw), Ptr{Cchar}, (Cint, Cint), key, scancode)
end

function glfwGetKeyScancode(key)
    ccall((:glfwGetKeyScancode, libglfw), Cint, (Cint,), key)
end

function glfwGetKey(window, key)
    ccall((:glfwGetKey, libglfw), Cint, (Ptr{GLFWwindow}, Cint), window, key)
end

function glfwGetMouseButton(window, button)
    ccall((:glfwGetMouseButton, libglfw), Cint, (Ptr{GLFWwindow}, Cint), window, button)
end

function glfwGetCursorPos(window, xpos, ypos)
    ccall((:glfwGetCursorPos, libglfw), Cvoid, (Ptr{GLFWwindow}, Ptr{Cdouble}, Ptr{Cdouble}), window, xpos, ypos)
end

function glfwSetCursorPos(window, xpos, ypos)
    ccall((:glfwSetCursorPos, libglfw), Cvoid, (Ptr{GLFWwindow}, Cdouble, Cdouble), window, xpos, ypos)
end

function glfwCreateCursor(image, xhot, yhot)
    ccall((:glfwCreateCursor, libglfw), Ptr{GLFWcursor}, (Ptr{GLFWimage}, Cint, Cint), image, xhot, yhot)
end

function glfwCreateStandardCursor(shape)
    ccall((:glfwCreateStandardCursor, libglfw), Ptr{GLFWcursor}, (Cint,), shape)
end

function glfwDestroyCursor(cursor)
    ccall((:glfwDestroyCursor, libglfw), Cvoid, (Ptr{GLFWcursor},), cursor)
end

function glfwSetCursor(window, cursor)
    ccall((:glfwSetCursor, libglfw), Cvoid, (Ptr{GLFWwindow}, Ptr{GLFWcursor}), window, cursor)
end

function glfwSetKeyCallback(window, callback)
    ccall((:glfwSetKeyCallback, libglfw), GLFWkeyfun, (Ptr{GLFWwindow}, GLFWkeyfun), window, callback)
end

function glfwSetCharCallback(window, callback)
    ccall((:glfwSetCharCallback, libglfw), GLFWcharfun, (Ptr{GLFWwindow}, GLFWcharfun), window, callback)
end

function glfwSetCharModsCallback(window, callback)
    ccall((:glfwSetCharModsCallback, libglfw), GLFWcharmodsfun, (Ptr{GLFWwindow}, GLFWcharmodsfun), window, callback)
end

function glfwSetMouseButtonCallback(window, callback)
    ccall((:glfwSetMouseButtonCallback, libglfw), GLFWmousebuttonfun, (Ptr{GLFWwindow}, GLFWmousebuttonfun), window, callback)
end

function glfwSetCursorPosCallback(window, callback)
    ccall((:glfwSetCursorPosCallback, libglfw), GLFWcursorposfun, (Ptr{GLFWwindow}, GLFWcursorposfun), window, callback)
end

function glfwSetCursorEnterCallback(window, callback)
    ccall((:glfwSetCursorEnterCallback, libglfw), GLFWcursorenterfun, (Ptr{GLFWwindow}, GLFWcursorenterfun), window, callback)
end

function glfwSetScrollCallback(window, callback)
    ccall((:glfwSetScrollCallback, libglfw), GLFWscrollfun, (Ptr{GLFWwindow}, GLFWscrollfun), window, callback)
end

function glfwSetDropCallback(window, callback)
    ccall((:glfwSetDropCallback, libglfw), GLFWdropfun, (Ptr{GLFWwindow}, GLFWdropfun), window, callback)
end

function glfwJoystickPresent(jid)
    ccall((:glfwJoystickPresent, libglfw), Cint, (Cint,), jid)
end

function glfwGetJoystickAxes(jid, count)
    ccall((:glfwGetJoystickAxes, libglfw), Ptr{Cfloat}, (Cint, Ptr{Cint}), jid, count)
end

function glfwGetJoystickButtons(jid, count)
    ccall((:glfwGetJoystickButtons, libglfw), Ptr{Cuchar}, (Cint, Ptr{Cint}), jid, count)
end

function glfwGetJoystickHats(jid, count)
    ccall((:glfwGetJoystickHats, libglfw), Ptr{Cuchar}, (Cint, Ptr{Cint}), jid, count)
end

function glfwGetJoystickName(jid)
    ccall((:glfwGetJoystickName, libglfw), Ptr{Cchar}, (Cint,), jid)
end

function glfwGetJoystickGUID(jid)
    ccall((:glfwGetJoystickGUID, libglfw), Ptr{Cchar}, (Cint,), jid)
end

function glfwSetJoystickUserPointer(jid, pointer)
    ccall((:glfwSetJoystickUserPointer, libglfw), Cvoid, (Cint, Ptr{Cvoid}), jid, pointer)
end

function glfwGetJoystickUserPointer(jid)
    ccall((:glfwGetJoystickUserPointer, libglfw), Ptr{Cvoid}, (Cint,), jid)
end

function glfwJoystickIsGamepad(jid)
    ccall((:glfwJoystickIsGamepad, libglfw), Cint, (Cint,), jid)
end

function glfwSetJoystickCallback(callback)
    ccall((:glfwSetJoystickCallback, libglfw), GLFWjoystickfun, (GLFWjoystickfun,), callback)
end

function glfwUpdateGamepadMappings(string)
    ccall((:glfwUpdateGamepadMappings, libglfw), Cint, (Ptr{Cchar},), string)
end

function glfwGetGamepadName(jid)
    ccall((:glfwGetGamepadName, libglfw), Ptr{Cchar}, (Cint,), jid)
end

function glfwGetGamepadState(jid, state)
    ccall((:glfwGetGamepadState, libglfw), Cint, (Cint, Ptr{GLFWgamepadstate}), jid, state)
end

function glfwSetClipboardString(window, string)
    ccall((:glfwSetClipboardString, libglfw), Cvoid, (Ptr{GLFWwindow}, Ptr{Cchar}), window, string)
end

function glfwGetClipboardString(window)
    ccall((:glfwGetClipboardString, libglfw), Ptr{Cchar}, (Ptr{GLFWwindow},), window)
end

function glfwGetTime()
    ccall((:glfwGetTime, libglfw), Cdouble, ())
end

function glfwSetTime(time)
    ccall((:glfwSetTime, libglfw), Cvoid, (Cdouble,), time)
end

function glfwGetTimerValue()
    ccall((:glfwGetTimerValue, libglfw), UInt64, ())
end

function glfwGetTimerFrequency()
    ccall((:glfwGetTimerFrequency, libglfw), UInt64, ())
end

function glfwMakeContextCurrent(window)
    ccall((:glfwMakeContextCurrent, libglfw), Cvoid, (Ptr{GLFWwindow},), window)
end

function glfwGetCurrentContext()
    ccall((:glfwGetCurrentContext, libglfw), Ptr{GLFWwindow}, ())
end

function glfwSwapBuffers(window)
    ccall((:glfwSwapBuffers, libglfw), Cvoid, (Ptr{GLFWwindow},), window)
end

function glfwSwapInterval(interval)
    ccall((:glfwSwapInterval, libglfw), Cvoid, (Cint,), interval)
end

function glfwExtensionSupported(extension)
    ccall((:glfwExtensionSupported, libglfw), Cint, (Ptr{Cchar},), extension)
end

function glfwGetProcAddress(procname)
    ccall((:glfwGetProcAddress, libglfw), GLFWglproc, (Ptr{Cchar},), procname)
end

function glfwVulkanSupported()
    ccall((:glfwVulkanSupported, libglfw), Cint, ())
end

function glfwGetRequiredInstanceExtensions(count)
    ccall((:glfwGetRequiredInstanceExtensions, libglfw), Ptr{Ptr{Cchar}}, (Ptr{UInt32},), count)
end

const GLFW_VERSION_MAJOR = 3

const GLFW_VERSION_MINOR = 3

const GLFW_VERSION_REVISION = 4

const GLFW_TRUE = 1

const GLFW_FALSE = 0

const GLFW_RELEASE = 0

const GLFW_PRESS = 1

const GLFW_REPEAT = 2

const GLFW_HAT_CENTERED = 0

const GLFW_HAT_UP = 1

const GLFW_HAT_RIGHT = 2

const GLFW_HAT_DOWN = 4

const GLFW_HAT_LEFT = 8

const GLFW_HAT_RIGHT_UP = GLFW_HAT_RIGHT | GLFW_HAT_UP

const GLFW_HAT_RIGHT_DOWN = GLFW_HAT_RIGHT | GLFW_HAT_DOWN

const GLFW_HAT_LEFT_UP = GLFW_HAT_LEFT | GLFW_HAT_UP

const GLFW_HAT_LEFT_DOWN = GLFW_HAT_LEFT | GLFW_HAT_DOWN

const GLFW_KEY_UNKNOWN = -1

const GLFW_KEY_SPACE = 32

const GLFW_KEY_APOSTROPHE = 39

const GLFW_KEY_COMMA = 44

const GLFW_KEY_MINUS = 45

const GLFW_KEY_PERIOD = 46

const GLFW_KEY_SLASH = 47

const GLFW_KEY_0 = 48

const GLFW_KEY_1 = 49

const GLFW_KEY_2 = 50

const GLFW_KEY_3 = 51

const GLFW_KEY_4 = 52

const GLFW_KEY_5 = 53

const GLFW_KEY_6 = 54

const GLFW_KEY_7 = 55

const GLFW_KEY_8 = 56

const GLFW_KEY_9 = 57

const GLFW_KEY_SEMICOLON = 59

const GLFW_KEY_EQUAL = 61

const GLFW_KEY_A = 65

const GLFW_KEY_B = 66

const GLFW_KEY_C = 67

const GLFW_KEY_D = 68

const GLFW_KEY_E = 69

const GLFW_KEY_F = 70

const GLFW_KEY_G = 71

const GLFW_KEY_I = 73

const GLFW_KEY_J = 74

const GLFW_KEY_K = 75

const GLFW_KEY_L = 76

const GLFW_KEY_M = 77

const GLFW_KEY_N = 78

const GLFW_KEY_O = 79

const GLFW_KEY_P = 80

const GLFW_KEY_Q = 81

const GLFW_KEY_R = 82

const GLFW_KEY_S = 83

const GLFW_KEY_T = 84

const GLFW_KEY_U = 85

const GLFW_KEY_V = 86

const GLFW_KEY_W = 87

const GLFW_KEY_X = 88

const GLFW_KEY_Y = 89

const GLFW_KEY_Z = 90

const GLFW_KEY_LEFT_BRACKET = 91

const GLFW_KEY_BACKSLASH = 92

const GLFW_KEY_RIGHT_BRACKET = 93

const GLFW_KEY_GRAVE_ACCENT = 96

const GLFW_KEY_WORLD_1 = 161

const GLFW_KEY_WORLD_2 = 162

const GLFW_KEY_ESCAPE = 256

const GLFW_KEY_ENTER = 257

const GLFW_KEY_TAB = 258

const GLFW_KEY_BACKSPACE = 259

const GLFW_KEY_INSERT = 260

const GLFW_KEY_DELETE = 261

const GLFW_KEY_RIGHT = 262

const GLFW_KEY_LEFT = 263

const GLFW_KEY_DOWN = 264

const GLFW_KEY_UP = 265

const GLFW_KEY_PAGE_UP = 266

const GLFW_KEY_PAGE_DOWN = 267

const GLFW_KEY_HOME = 268

const GLFW_KEY_END = 269

const GLFW_KEY_CAPS_LOCK = 280

const GLFW_KEY_SCROLL_LOCK = 281

const GLFW_KEY_NUM_LOCK = 282

const GLFW_KEY_PRINT_SCREEN = 283

const GLFW_KEY_PAUSE = 284

const GLFW_KEY_F1 = 290

const GLFW_KEY_F2 = 291

const GLFW_KEY_F3 = 292

const GLFW_KEY_F4 = 293

const GLFW_KEY_F5 = 294

const GLFW_KEY_F6 = 295

const GLFW_KEY_F7 = 296

const GLFW_KEY_F8 = 297

const GLFW_KEY_F9 = 298

const GLFW_KEY_F10 = 299

const GLFW_KEY_F11 = 300

const GLFW_KEY_F12 = 301

const GLFW_KEY_F13 = 302

const GLFW_KEY_F14 = 303

const GLFW_KEY_F15 = 304

const GLFW_KEY_F16 = 305

const GLFW_KEY_F17 = 306

const GLFW_KEY_F18 = 307

const GLFW_KEY_F19 = 308

const GLFW_KEY_F20 = 309

const GLFW_KEY_F21 = 310

const GLFW_KEY_F22 = 311

const GLFW_KEY_F23 = 312

const GLFW_KEY_F24 = 313

const GLFW_KEY_F25 = 314

const GLFW_KEY_KP_0 = 320

const GLFW_KEY_KP_1 = 321

const GLFW_KEY_KP_2 = 322

const GLFW_KEY_KP_3 = 323

const GLFW_KEY_KP_4 = 324

const GLFW_KEY_KP_5 = 325

const GLFW_KEY_KP_6 = 326

const GLFW_KEY_KP_7 = 327

const GLFW_KEY_KP_8 = 328

const GLFW_KEY_KP_9 = 329

const GLFW_KEY_KP_DECIMAL = 330

const GLFW_KEY_KP_DIVIDE = 331

const GLFW_KEY_KP_MULTIPLY = 332

const GLFW_KEY_KP_SUBTRACT = 333

const GLFW_KEY_KP_ADD = 334

const GLFW_KEY_KP_ENTER = 335

const GLFW_KEY_KP_EQUAL = 336

const GLFW_KEY_LEFT_SHIFT = 340

const GLFW_KEY_LEFT_CONTROL = 341

const GLFW_KEY_LEFT_ALT = 342

const GLFW_KEY_LEFT_SUPER = 343

const GLFW_KEY_RIGHT_SHIFT = 344

const GLFW_KEY_RIGHT_CONTROL = 345

const GLFW_KEY_RIGHT_ALT = 346

const GLFW_KEY_RIGHT_SUPER = 347

const GLFW_KEY_MENU = 348

const GLFW_KEY_LAST = GLFW_KEY_MENU

const GLFW_MOD_SHIFT = 0x0001

const GLFW_MOD_CONTROL = 0x0002

const GLFW_MOD_ALT = 0x0004

const GLFW_MOD_SUPER = 0x0008

const GLFW_MOD_CAPS_LOCK = 0x0010

const GLFW_MOD_NUM_LOCK = 0x0020

const GLFW_MOUSE_BUTTON_1 = 0

const GLFW_MOUSE_BUTTON_2 = 1

const GLFW_MOUSE_BUTTON_3 = 2

const GLFW_MOUSE_BUTTON_4 = 3

const GLFW_MOUSE_BUTTON_5 = 4

const GLFW_MOUSE_BUTTON_6 = 5

const GLFW_MOUSE_BUTTON_7 = 6

const GLFW_MOUSE_BUTTON_8 = 7

const GLFW_MOUSE_BUTTON_LAST = GLFW_MOUSE_BUTTON_8

const GLFW_MOUSE_BUTTON_LEFT = GLFW_MOUSE_BUTTON_1

const GLFW_MOUSE_BUTTON_RIGHT = GLFW_MOUSE_BUTTON_2

const GLFW_MOUSE_BUTTON_MIDDLE = GLFW_MOUSE_BUTTON_3

const GLFW_JOYSTICK_1 = 0

const GLFW_JOYSTICK_2 = 1

const GLFW_JOYSTICK_3 = 2

const GLFW_JOYSTICK_4 = 3

const GLFW_JOYSTICK_5 = 4

const GLFW_JOYSTICK_6 = 5

const GLFW_JOYSTICK_7 = 6

const GLFW_JOYSTICK_8 = 7

const GLFW_JOYSTICK_9 = 8

const GLFW_JOYSTICK_10 = 9

const GLFW_JOYSTICK_11 = 10

const GLFW_JOYSTICK_12 = 11

const GLFW_JOYSTICK_13 = 12

const GLFW_JOYSTICK_14 = 13

const GLFW_JOYSTICK_15 = 14

const GLFW_JOYSTICK_16 = 15

const GLFW_JOYSTICK_LAST = GLFW_JOYSTICK_16

const GLFW_GAMEPAD_BUTTON_A = 0

const GLFW_GAMEPAD_BUTTON_B = 1

const GLFW_GAMEPAD_BUTTON_X = 2

const GLFW_GAMEPAD_BUTTON_Y = 3

const GLFW_GAMEPAD_BUTTON_LEFT_BUMPER = 4

const GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER = 5

const GLFW_GAMEPAD_BUTTON_BACK = 6

const GLFW_GAMEPAD_BUTTON_START = 7

const GLFW_GAMEPAD_BUTTON_GUIDE = 8

const GLFW_GAMEPAD_BUTTON_LEFT_THUMB = 9

const GLFW_GAMEPAD_BUTTON_RIGHT_THUMB = 10

const GLFW_GAMEPAD_BUTTON_DPAD_UP = 11

const GLFW_GAMEPAD_BUTTON_DPAD_RIGHT = 12

const GLFW_GAMEPAD_BUTTON_DPAD_DOWN = 13

const GLFW_GAMEPAD_BUTTON_DPAD_LEFT = 14

const GLFW_GAMEPAD_BUTTON_LAST = GLFW_GAMEPAD_BUTTON_DPAD_LEFT

const GLFW_GAMEPAD_BUTTON_CROSS = GLFW_GAMEPAD_BUTTON_A

const GLFW_GAMEPAD_BUTTON_CIRCLE = GLFW_GAMEPAD_BUTTON_B

const GLFW_GAMEPAD_BUTTON_SQUARE = GLFW_GAMEPAD_BUTTON_X

const GLFW_GAMEPAD_BUTTON_TRIANGLE = GLFW_GAMEPAD_BUTTON_Y

const GLFW_GAMEPAD_AXIS_LEFT_X = 0

const GLFW_GAMEPAD_AXIS_LEFT_Y = 1

const GLFW_GAMEPAD_AXIS_RIGHT_X = 2

const GLFW_GAMEPAD_AXIS_RIGHT_Y = 3

const GLFW_GAMEPAD_AXIS_LEFT_TRIGGER = 4

const GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER = 5

const GLFW_GAMEPAD_AXIS_LAST = GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER

const GLFW_NO_ERROR = 0

const GLFW_NOT_INITIALIZED = 0x00010001

const GLFW_NO_CURRENT_CONTEXT = 0x00010002

const GLFW_INVALID_ENUM = 0x00010003

const GLFW_INVALID_VALUE = 0x00010004

const GLFW_OUT_OF_MEMORY = 0x00010005

const GLFW_API_UNAVAILABLE = 0x00010006

const GLFW_VERSION_UNAVAILABLE = 0x00010007

const GLFW_PLATFORM_ERROR = 0x00010008

const GLFW_FORMAT_UNAVAILABLE = 0x00010009

const GLFW_NO_WINDOW_CONTEXT = 0x0001000a

const GLFW_FOCUSED = 0x00020001

const GLFW_ICONIFIED = 0x00020002

const GLFW_RESIZABLE = 0x00020003

const GLFW_VISIBLE = 0x00020004

const GLFW_DECORATED = 0x00020005

const GLFW_AUTO_ICONIFY = 0x00020006

const GLFW_FLOATING = 0x00020007

const GLFW_MAXIMIZED = 0x00020008

const GLFW_CENTER_CURSOR = 0x00020009

const GLFW_TRANSPARENT_FRAMEBUFFER = 0x0002000a

const GLFW_HOVERED = 0x0002000b

const GLFW_FOCUS_ON_SHOW = 0x0002000c

const GLFW_RED_BITS = 0x00021001

const GLFW_GREEN_BITS = 0x00021002

const GLFW_BLUE_BITS = 0x00021003

const GLFW_ALPHA_BITS = 0x00021004

const GLFW_DEPTH_BITS = 0x00021005

const GLFW_STENCIL_BITS = 0x00021006

const GLFW_ACCUM_RED_BITS = 0x00021007

const GLFW_ACCUM_GREEN_BITS = 0x00021008

const GLFW_ACCUM_BLUE_BITS = 0x00021009

const GLFW_ACCUM_ALPHA_BITS = 0x0002100a

const GLFW_AUX_BUFFERS = 0x0002100b

const GLFW_STEREO = 0x0002100c

const GLFW_SAMPLES = 0x0002100d

const GLFW_SRGB_CAPABLE = 0x0002100e

const GLFW_REFRESH_RATE = 0x0002100f

const GLFW_DOUBLEBUFFER = 0x00021010

const GLFW_CLIENT_API = 0x00022001

const GLFW_CONTEXT_VERSION_MAJOR = 0x00022002

const GLFW_CONTEXT_VERSION_MINOR = 0x00022003

const GLFW_CONTEXT_REVISION = 0x00022004

const GLFW_CONTEXT_ROBUSTNESS = 0x00022005

const GLFW_OPENGL_FORWARD_COMPAT = 0x00022006

const GLFW_OPENGL_DEBUG_CONTEXT = 0x00022007

const GLFW_OPENGL_PROFILE = 0x00022008

const GLFW_CONTEXT_RELEASE_BEHAVIOR = 0x00022009

const GLFW_CONTEXT_NO_ERROR = 0x0002200a

const GLFW_CONTEXT_CREATION_API = 0x0002200b

const GLFW_SCALE_TO_MONITOR = 0x0002200c

const GLFW_COCOA_RETINA_FRAMEBUFFER = 0x00023001

const GLFW_COCOA_FRAME_NAME = 0x00023002

const GLFW_COCOA_GRAPHICS_SWITCHING = 0x00023003

const GLFW_X11_CLASS_NAME = 0x00024001

const GLFW_X11_INSTANCE_NAME = 0x00024002

const GLFW_NO_API = 0

const GLFW_OPENGL_API = 0x00030001

const GLFW_OPENGL_ES_API = 0x00030002

const GLFW_NO_ROBUSTNESS = 0

const GLFW_NO_RESET_NOTIFICATION = 0x00031001

const GLFW_LOSE_CONTEXT_ON_RESET = 0x00031002

const GLFW_OPENGL_ANY_PROFILE = 0

const GLFW_OPENGL_CORE_PROFILE = 0x00032001

const GLFW_OPENGL_COMPAT_PROFILE = 0x00032002

const GLFW_CURSOR = 0x00033001

const GLFW_STICKY_KEYS = 0x00033002

const GLFW_STICKY_MOUSE_BUTTONS = 0x00033003

const GLFW_LOCK_KEY_MODS = 0x00033004

const GLFW_RAW_MOUSE_MOTION = 0x00033005

const GLFW_CURSOR_NORMAL = 0x00034001

const GLFW_CURSOR_HIDDEN = 0x00034002

const GLFW_CURSOR_DISABLED = 0x00034003

const GLFW_ANY_RELEASE_BEHAVIOR = 0

const GLFW_RELEASE_BEHAVIOR_FLUSH = 0x00035001

const GLFW_RELEASE_BEHAVIOR_NONE = 0x00035002

const GLFW_NATIVE_CONTEXT_API = 0x00036001

const GLFW_EGL_CONTEXT_API = 0x00036002

const GLFW_OSMESA_CONTEXT_API = 0x00036003

const GLFW_ARROW_CURSOR = 0x00036001

const GLFW_IBEAM_CURSOR = 0x00036002

const GLFW_CROSSHAIR_CURSOR = 0x00036003

const GLFW_HAND_CURSOR = 0x00036004

const GLFW_HRESIZE_CURSOR = 0x00036005

const GLFW_VRESIZE_CURSOR = 0x00036006

const GLFW_CONNECTED = 0x00040001

const GLFW_DISCONNECTED = 0x00040002

const GLFW_JOYSTICK_HAT_BUTTONS = 0x00050001

const GLFW_COCOA_CHDIR_RESOURCES = 0x00051001

const GLFW_COCOA_MENUBAR = 0x00051002

const GLFW_DONT_CARE = -1

# exports
const PREFIXES = ["glfw", "GLFW"]
for name in names(@__MODULE__; all=true), prefix in PREFIXES
    if startswith(string(name), prefix)
        @eval export $name
    end
end

end # module


text:
if Compat.is_windows()
    import WinReg

    function locate_rhome()
        if haskey(ENV,"R_HOME")
            Rhome = ENV["R_HOME"]
        else
            Rhome = WinReg.querykey(WinReg.HKEY_LOCAL_MACHINE, "Software\\R-Core\\R","InstallPath")
        end

        if isdir(Rhome)
            println("R installation found at \"$Rhome\"")
            return Rhome
        end
        error("Could not locate R installation. Try setting \"R_HOME\" environmental variable.")
    end

    const Rhome = locate_rhome()
    const Ruser = homedir()
    const libR = Libdl.find_library(["R"],[joinpath(Rhome,"bin",Sys.WORD_SIZE==64?"x64":"i386")])

    function ask_yes_no_cancel(prompt::Ptr{Cchar})
        println(isdefined(Core, :String) ? String(prompt) : bytestring(prompt))
        query = readline(STDIN)
        c = uppercase(query[1])
        local r::Cint
        r = (c=='Y' ? 1 : c=='N' ? -1 : 0)
        return r
    end

    """
        RStart

    This type mirrors `structRstart` in `R_ext/RStartup.h`. It is used to initialize the R engine.
    """
    type RStart # mirror structRstart in R_ext/RStartup.h
        R_Quiet::Cint
        R_Slave::Cint
        R_Interactive::Cint
        R_Verbose::Cint
        LoadSiteFile::Cint
        LoadInitFile::Cint
        DebugInitFile::Cint
        RestoreAction::Cint
        SaveAction::Cint
        vsize::Csize_t
        nsize::Csize_t
        max_vsize::Csize_t
        max_nsize::Csize_t
        ppsize::Csize_t
        NoRenviron::Cint
        rhome::Ptr{Cchar}
        home::Ptr{Cchar}
        ReadConsole::Ptr{Void}
        WriteConsole::Ptr{Void}
        CallBack::Ptr{Void}
        ShowMessage::Ptr{Void}
        YesNoCancel::Ptr{Void}
        Busy::Ptr{Void}
        CharacterMode::Cint
        WriteConsoleEx::Ptr{Void}
    end
    RStart() = RStart(0,0,0,0,0,
                      0,0,0,0,0,
                      0,0,0,0,0,
                      C_NULL,C_NULL,
                      C_NULL,C_NULL,C_NULL,C_NULL,
                      C_NULL,C_NULL,2,C_NULL)

end

if Compat.is_unix()
    function locate_rhome()
        if haskey(ENV,"R_HOME")
            Rhome = ENV["R_HOME"]
        else
            Rhome = readchomp(`R RHOME`)
        end
        if isdir(Rhome)
            println("R installation found at \"$Rhome\"")
            return Rhome
        end
        error("Could not find R installation. Try setting \"R_HOME\" environmental variable.")
    end

    const Rhome = locate_rhome()
    const libR  = Libdl.find_library(["libR"],[joinpath(Rhome,"lib")])
end

const Rembedded = Ref{Bool}(false)

"""
    initEmbeddedR()

This initializes an embedded R session. It should only be called when R is not already running (e.g. if Julia is running inside an R session)
"""
function initEmbeddedR()

    # disable R signal handling
    unsafe_store!(cglobal((:R_SignalHandlers,RCall.libR),Cint),0)

    if Compat.is_windows()
        # TODO: Use direct Windows interface, see §8.2.2 "Calling R.dll directly"
        # of "Writing R Extensions" (aka R-exts)

        ccall(:_wputenv,Cint,(Cwstring,),"PATH="*ENV["PATH"]*";"*dirname(libR))
        ccall(:_wputenv,Cint,(Cwstring,),"HOME="*homedir())

        argv = ["REmbeddedJulia","--silent","--no-save"]
        i = ccall((:Rf_initEmbeddedR,libR),Cint,(Cint,Ptr{Ptr{Cchar}}),length(argv),argv)
        if i == 0
            error("Could not start embedded R session.")
        end

        rs = RStart()
        ccall((:R_DefParams,libR),Void,(Ptr{RStart},),&rs)

        rs.rhome          = ccall((:get_R_HOME,libR),Ptr{Cchar},())
        rs.home           = ccall((:getRUser,libR),Ptr{Cchar},())
        rs.ReadConsole    = cglobal((:R_ReadConsole,libR), Void)
        rs.CallBack       = cfunction(event_callback,Void,())
        rs.ShowMessage    = cglobal((:R_ShowMessage,libR),Void)
        rs.YesNoCancel    = cfunction(ask_yes_no_cancel,Cint,(Ptr{Cchar},))
        rs.Busy           = cglobal((:R_Busy,libR),Void)
        rs.WriteConsole   = C_NULL
        rs.WriteConsoleEx = cfunction(write_console_ex,Void,(Ptr{UInt8},Cint,Cint))

        ccall((:R_SetParams,libR),Void,(Ptr{RStart},),&rs)
    end

    if Compat.is_unix()
        # set necessary environmental variables
        ENV["R_HOME"] = Rhome
        ENV["R_DOC_DIR"] = joinpath(Rhome,"doc")
        ENV["R_INCLUDE_DIR"] = joinpath(Rhome,"include")
        ENV["R_SHARE_DIR"] = joinpath(Rhome,"share")

        # initialize library
        argv = ["REmbeddedJulia","--silent","--no-save"]
        i = ccall((:Rf_initEmbeddedR,libR),Cint,(Cint,Ptr{Ptr{Cchar}}),length(argv),argv)
        if i == 0
            error("Could not start embedded R session.")
        end

        ptr_write_console_ex = cfunction(write_console_ex,Void,(Ptr{UInt8},Cint,Cint))
        unsafe_store!(cglobal((:ptr_R_WriteConsole,libR),Ptr{Void}), C_NULL)
        unsafe_store!(cglobal((:ptr_R_WriteConsoleEx,libR),Ptr{Void}), ptr_write_console_ex)
        unsafe_store!(cglobal((:R_Consolefile,libR),Ptr{Void}), C_NULL)
        unsafe_store!(cglobal((:R_Outputfile,libR),Ptr{Void}), C_NULL)
        ptr_polled_events = cfunction(polled_events,Void,())
        unsafe_store!(cglobal((:R_PolledEvents,libR),Ptr{Void}), ptr_polled_events)
    end

    Rembedded[] = true
    atexit(endEmbeddedR)

end

"""
    endEmbeddedR()

Close embedded R session.
"""
function endEmbeddedR()
    if Rembedded[]
        ccall((:Rf_endEmbeddedR,libR),Void,(Cint,),0)
        Rembedded[] = false
    end
end

function __init__()
    # Check if R already running
    Rinited = unsafe_load(cglobal((:R_NilValue,libR),Ptr{Void})) != C_NULL

    if !Rinited
        initEmbeddedR()
    end

    ip = ccall((:Rf_ScalarInteger,libR),Ptr{Void},(Cint,),0)
    voffset[] = ccall((:INTEGER,libR),Ptr{Void},(Ptr{Void},),ip) - ip

    Const.load()

    # set up function callbacks
    juliaCallback.p = makeNativeSymbolRef(cfunction(julia_extptr_callback,UnknownSxpPtr,(ListSxpPtr,)))
    juliaDecref[] = cfunction(decref_extptr,Void,(ExtPtrSxpPtr,))

    if !Rinited
        # print warnings as they arise
        # we can't use Rf_PrintWarnings as not exported on all platforms.
        rcall_p(:options,warn=1)

        # R gui eventloop
        isinteractive() && rgui_init()
        # R REPL mode
        isdefined(Base, :active_repl) && isinteractive() && repl_init(Base.active_repl)
    end

    # # IJulia hooks
    isdefined(Main, :IJulia) && Main.IJulia.inited && ijulia_init()
end


text:
#---------------------------------------------------------
# # [SPECTrecon 2D use](@id 5-2d)
#---------------------------------------------------------

# This page describes how to perform 2D SPECT forward and back-projection
# using the Julia package
# [`SPECTrecon`](https://github.com/JeffFessler/SPECTrecon.jl).

# ### Setup

# Packages needed here.

using SPECTrecon
using MIRTjim: jim, prompt
using ImagePhantoms: shepp_logan, SheppLoganEmis
using LinearAlgebra: mul!
using LinearMapsAA: LinearMapAA
using Plots: plot, default; default(markerstrokecolor=:auto)

# The following line is helpful when running this example.jl file as a script;
# this way it will prompt user to hit a key after each figure is displayed.

isinteractive() ? jim(:prompt, true) : prompt(:draw);

# ### Overview

# Real SPECT systems are inherently 3D imaging systems,
# but for the purpose of prototyping algorithms
# it can be useful to work with 2D simulations.

# Currently, "2D" here means a 3D array with `nz=1`,
# i.e., a single slice.
# The key to working with a single slice
# is that the package allows the PSFs
# to have rectangular support `px × pz`
# where `pz = 1`, i.e., no blur along the axial (z) direction.

# ### Example

# Start with a simple 2D digital phantom.

T = Float32
nx,ny,nz = 128,128,1
xtrue = T.(shepp_logan(nx, SheppLoganEmis()))
xtrue = reshape(xtrue, nx, ny, 1) # 3D array with nz=1
jim(xtrue, "xtrue: SheppLoganEmis with size $(size(xtrue))")

# ### PSF

# Create a synthetic depth-dependent PSF for a single view
px,pz = 11,1 # pz=1 is crucial for 2D work
psf1 = psf_gauss( ; ny, px, pz, fwhm_start = 1, fwhm_end = 4) # (px,pz,ny)
tmp = reshape(psf1, px, ny) / maximum(psf1) # (px,ny)
hx = (px-1)÷2
plot(-hx:hx, tmp[:,[1:9:end-10;end]], markershape=:o, label="",
    title = "Depth-dependent PSF profiles",
    xtick = [-hx, -2, 0, 2, hx], # (-1:1) .* ((px-1)÷2),
    ytick = [0; round.(tmp[hx+1,end] * [0.5,1], digits=2); 0.5; 1],
)
prompt()

# In general the PSF can vary from view to view
# due to non-circular detector orbits.
# For simplicity, here we illustrate the case
# where the PSF is the same for every view.

nview = 80
psfs = repeat(psf1, 1, 1, 1, nview)
size(psfs)

# ### Basic SPECT forward projection

# Here is a simple illustration
# of a SPECT forward projection operation.
# (This is a memory inefficient way to do it!)

dy = 4 # transaxial pixel size in mm
mumap = zeros(T, size(xtrue)) # μ-map just zero for illustration here
views = project(xtrue, mumap, psfs, dy) # [nx,1,nview]
sino = reshape(views, nx, nview)
size(sino)

# Display the calculated (i.e., simulated) projection views

jim(sino, "Sinogram")

# ### Basic SPECT back projection

# This illustrates an "unfiltered backprojection"
# that leads to a very blurry image
# (again, with a simple memory inefficient usage).

# First, back-project two "rays"
# to illustrate the depth-dependent PSF.
sino1 = zeros(T, nx, nview)
sino1[nx÷2, nview÷5] = 1
sino1[nx÷2, 1] = 1
sino1 = reshape(sino1, nx, nz, nview)
back1 = backproject(sino1, mumap, psfs, dy)
jim(back1, "Back-projection of two rays")

# Now back-project all the views of the phantom.

back = backproject(views, mumap, psfs, dy)
jim(back, "Back-projection of ytrue")

# ### Memory efficiency

# For iterative reconstruction,
# one must do forward and back-projection repeatedly.
# It is more efficient to pre-allocate work arrays
# for those operations,
# instead of repeatedly making system calls.

# Here we illustrate the memory efficient versions
# that are recommended for iterative SPECT reconstruction.

# First construction the SPECT plan.

#viewangle = (0:(nview-1)) * 2π # default
plan = SPECTplan(mumap, psfs, dy; T)

# Mutating version of forward projection:

tmp = Array{T}(undef, nx, nz, nview)
project!(tmp, xtrue, plan)
@assert tmp == views

# Mutating version of back-projection:

tmp = Array{T}(undef, nx, ny, nz)
backproject!(tmp, views, plan)
@assert tmp == back

# ### Using `LinearMapsAA`

# Calling `project!` and `backproject!` repeatedly
# leads to application-specific code.
# More general code uses the fact that SPECT projection and back-projection
# are linear operations,
# so we use `LinearMapAA` to define a "system matrix" for these operations.

forw! = (y,x) -> project!(y, x, plan)
back! = (x,y) -> backproject!(x, y, plan)
idim = (nx,ny,nz)
odim = (nx,nz,nview)
A = LinearMapAA(forw!, back!, (prod(odim),prod(idim)); T, odim, idim)

# Simple forward and back-projection:
@assert A * xtrue == views
@assert A' * views == back

# Mutating version:
tmp = Array{T}(undef, nx, nz, nview)
mul!(tmp, A, xtrue)
@assert tmp == views
tmp = Array{T}(undef, nx, ny, nz)
mul!(tmp, A', views)
@assert tmp == back

# ### Gram matrix impulse response

points = zeros(T, nx, ny, nz)
points[nx÷2,ny÷2,1] = 1
points[3nx÷4,ny÷4,1] = 1

impulse = A' * (A * points)
jim(impulse, "Impulse response of A'A")


text:
### A Pluto.jl notebook ###
# v0.12.20

using Markdown
using InteractiveUtils

# ╔═╡ 11c4ccdd-3d86-48ce-b471-3a581902c345
begin
	using Pkg
	Pkg.activate(mktempdir())
	Pkg.Registry.update()
	Pkg.add("Yao")
	Pkg.add("YaoPlots")
end

# ╔═╡ f6fa5d36-1dd4-11eb-1c33-3344c7697e82
using Yao, YaoPlots

# ╔═╡ 3c87d83e-1dca-11eb-2068-6fa8ceb4849a
md"## More Quantum Gates"

# ╔═╡ 5545465e-1dca-11eb-0273-6d32f757b4a2
md"As you might have thought, there do exist gates, other than the previously defined ones."

# ╔═╡ 8def8f9e-1dca-11eb-32cf-fdb2038a8feb
md"### Single qubit gates"

# ╔═╡ 83f65eb6-1dca-11eb-32cb-c577ad1d0b68
md"#### The `` R_𝜑^Z `` gate"

# ╔═╡ 9d8d835c-1dca-11eb-0ab7-01a80ca23e30
md"Passing a qubit through the `` R_𝜑^Z `` is equivalent to multiplying its state vector by `` \begin{bmatrix}1 & 0\\0 & e^{𝜑i}\end{bmatrix} ``.
Remember, `` e^{iθ} = \cos(𝜃) + i\sin(𝜃) ``.

The `` R_𝜑^Z `` gate can be alternatively denoted by, `` \begin{bmatrix}e^{-𝜑i/2} & 0\\0 & e^{𝜑i/2}\end{bmatrix} ``. Its just the original matrix, multiplied by `` e^{-𝜑i/2} ``. We can do this since multiplication by `` e^{-𝜑i/2} `` is not *observable* during measurement as its a complex unit and `` | e^{iθ} | = |\cos(𝜃) + i\sin(𝜃)| = 1 . `` Remember that the abstract value of a complex number `` a + ib ``, i.e.,  `` |a + ib| = \sqrt{a^2 + b^2} `` and `` \sin^2θ + \cos^2θ = 1. ``

Considering a qubit, `` a|0〉 + b|0〉 , `` passing it through the `` R_\frac{𝞹}{2}^Z `` gate is equivalent to `` \begin{bmatrix}1 & 0\\0 & e^{𝜋i/2}\end{bmatrix} `` . And since `` \cos(\frac{𝞹}{2}) = 0`` and `` \sin(\frac{𝞹}{2}) = 1 , `` we can rewrite the above as, ``\begin{bmatrix}1 & 0\\0 & i\end{bmatrix}`` `` \begin{bmatrix}a\\b\end{bmatrix} ``."

# ╔═╡ 8cca417e-1dd4-11eb-2a18-bf9b80da3fd2
md"Lets try the above in Yao! The `` R_𝜑^Z `` gate can be used in Yao with the shift *block*. "

# ╔═╡ 1f7c993e-1dd5-11eb-2f13-7711d2426eb1
begin
	qubit = rand_state(1)
	state(qubit)
end

# ╔═╡ 5960e486-1dd5-11eb-2253-838f59b06d4c
state(qubit |> chain(1, put(1=>shift(π/2))))

# ╔═╡ ac10f6f8-1dd5-11eb-2cf1-29480480572a
md"As expected the output was `` \begin{bmatrix}a\\ib\end{bmatrix} ``. Remember, `` i = \sqrt{-1} `` and `` i^2 = -1 ``. (Also, note that in Julia, imaginary number 𝑖 is represented by im.)"

# ╔═╡ af22f8a8-1ddc-11eb-2958-bb45cdadf44f
md"Also, `` R_𝜋^Z `` gate is equivalent to Z gate."

# ╔═╡ df5a774e-1ddc-11eb-2e86-1dc8dd502e26
round.(Matrix(chain(1, put(1=>shift(π))))) == round.(Matrix(chain(1, put(1=>Z)))) #The round functions "rounds-off" the elements of the matrices

# ╔═╡ c1f87994-1e7c-11eb-3da3-21aa52e80520
md"Its represented in a circuit diagram by,"

# ╔═╡ cda277ce-1e7c-11eb-246c-0b97bb66ab73
plot(chain(1, put(1=>shift(π/3))))

# ╔═╡ a1173196-1dd9-11eb-394d-271a3871e181
md"#### The T Gate"

# ╔═╡ b1cc417a-1dd9-11eb-0156-67fd10743000
md"The T gate is equivalent to `` R_\frac{𝞹}{4}^Z . `` In its matrix form, it can be written as `` \begin{bmatrix}1 & 0\\0 & \frac{1 + i}{\sqrt{2}}\end{bmatrix} ``. Nevertheless, in Yao, it can be used by using the **T** *block* ."

# ╔═╡ 67b2bc30-1dda-11eb-2d81-edd411a207b2
state(qubit |> chain(1, put(1=>T)))

# ╔═╡ 7d546e10-1dda-11eb-2b9a-abe61e9edf6c
md"Also,"

# ╔═╡ 83dfc128-1dda-11eb-37d9-37b959ea5f58
Matrix(chain(1, put(1=>shift(π/4)))) == Matrix(chain(1, put(1=>T)))

# ╔═╡ ea6c8192-1e7c-11eb-30e5-8d65801ce703
md"Its circuit diagram representation looks somewhat like - "

# ╔═╡ feab87f0-1e7c-11eb-075b-a55e9d86184c
plot(chain(1, put(1=>T)))

# ╔═╡ 408ce964-1de4-11eb-2830-f14bdc87f899
md"#### The `` R_𝜑^X `` gate"

# ╔═╡ 09a069f0-1df6-11eb-19de-5da4bb572510
md"Similar to the `` R_𝜑^Z `` gate, the `` R_𝜑^X `` gate can be represented by `` \begin{bmatrix}\cos(\frac{𝜑}{2}) & -\sin(\frac{𝜑}{2})i\\-\sin(\frac{𝜑}{2})i & \cos(\frac{𝜑}{2})\end{bmatrix} ``."

# ╔═╡ 5a6f6786-1df7-11eb-0f06-5b229b0935bb
md"#### The `` R_𝜑^Y `` gate"

# ╔═╡ 6a775062-1df7-11eb-1ea2-7d5517774328
md"Similar to the `` R_𝜑^Z `` gate, the `` R_𝜑^X `` gate can be represented by `` \begin{bmatrix}\cos(\frac{𝜑}{2}) & -\sin(\frac{𝜑}{2})\\\sin(\frac{𝜑}{2}) & \cos(\frac{𝜑}{2})\end{bmatrix} ``."

# ╔═╡ 8d65504c-1df7-11eb-0f3a-93796bf5b7f9
md"They can be represented in Yao using the **Rx** and **Ry** *blocks* respectively"

# ╔═╡ e10cd2c0-1e00-11eb-3c86-2d958e7aa92f
state(qubit |> chain(1, put(1=>Rx(π))))

# ╔═╡ 38071b04-1e00-11eb-1c6c-03f03ce37a92
state(qubit |> chain(1, put(1=>Ry(π))))

# ╔═╡ 451b4c6a-1e1f-11eb-18f0-e301d5d34c93
md"There's also an **Rz** *block* which which represents the alternative form of `` R_𝜑^Z `` matrix, i.e., `` \begin{bmatrix}e^{-𝜑i/2} & 0\\0 & e^{𝜑i/2}\end{bmatrix} ``."

# ╔═╡ ea7cda52-1e1f-11eb-2cdc-e9a04181acfb
state(qubit |> chain(1, put(1=>Rz(π))))

# ╔═╡ 8c6c62c4-1e20-11eb-1017-f51535591214
md"Note that the absolute value of both the shift and Rz blocks are same."

# ╔═╡ a214d034-1e20-11eb-2975-6f440fee2ee5
abs.(Matrix(chain(1, put(1=>shift(π/5))))) == abs.(Matrix(chain(1, put(1=>Rz(π/5)))))

# ╔═╡ 16d650bc-1e7d-11eb-39d0-33ba9cb031ed
md"The circuit diagram representations of Rx, Ry and Rz blocks, respectively"

# ╔═╡ 275a85be-1e7d-11eb-1114-f9872f35915d
plot(chain(1, put(1=>Rx(π/5)), put(1=>Ry(π/3)), put(1=>Rz(π/7))))

# ╔═╡ 3355c7c4-1e21-11eb-1d88-6f5a6b5c51a8
md"### Multi-qubit Gates"

# ╔═╡ 4afe09cc-1e21-11eb-1eec-6b6d78e76aae
md"#### The SWAP Gate"

# ╔═╡ 812b528e-1e21-11eb-3219-a9ce5de87933
md"The SWAP gate swaps the state of two qubits. It can be represented by the matrix,
$\begin{bmatrix}1 & 0 & 0 & 0\\0 & 0 & 1 & 0\\0 & 1 & 0 & 0\\0 & 0 & 0 & 1\end{bmatrix}$. Its represented in Yao via the **swap** *block*."

# ╔═╡ b85aaad6-1e79-11eb-2d2a-2f0a8fdb9336
begin
	q = rand_state(2)
	state(q)
end

# ╔═╡ 63dd442a-1e7c-11eb-3c73-7186108b87b4
state(q |> chain(2, swap(1,2)))

# ╔═╡ 5c1a804c-1e7a-11eb-22ae-df5da105a8d7
md"The SWAP gate has the following circuit diagram representation"

# ╔═╡ cd7061a8-1e7d-11eb-3239-8f8410988851
plot(chain(2, swap(1,2)))

# ╔═╡ 5890257c-1e7e-11eb-2880-1d4257468ffc
md"There's a Toffoli gate, an S gate, a CSWAP gate, a `` \; C R_𝜑^{X,Y,Z} `` gate and probably a lot more. They can all be constructed using the existing blocks in Yao."

# ╔═╡ c1d38e3e-1e7e-11eb-07ac-5345d88b4781
let
	toffoli_Gate = chain(3, control(1:2, 3=>X)) #The toffoli gate
	plot(toffoli_Gate)
end

# ╔═╡ 512086b4-1e7f-11eb-1f4b-cde0db3c9734
let
	S_Gate = chain(1, put(1 => label(shift(π/2), "S"))) #The S gate
	plot(S_Gate)
end

# ╔═╡ 8c44d66e-1e7f-11eb-2821-5532c1fdb89d
let
	CRXYZ_Gates = chain(2, control(1, 2=>Rx(π/5)), control(1, 2=>Ry(π/7)), control(1, 2=>Rz(π/11)))
	plot(CRXYZ_Gates)
end

# ╔═╡ 07b469b8-1e85-11eb-1b2c-f3fb85382b66
let
	CSWAP = chain(3, control(1, 2:3=>SWAP)) #The CSWAP gate
	plot(CSWAP)
end

# ╔═╡ 96d92a88-4459-11eb-29ee-336cbba060a7
md"### The Measure Gate"

# ╔═╡ b23f15ca-4459-11eb-3089-f7ea50aaf5de
md"We already know how to measure the qubits. We can do it in the circuit itself too."

# ╔═╡ c9233378-4459-11eb-0aa9-af1cd8cef6ca
begin
	MeasureGate = chain(2, repeat(H, 1:2), Measure(2, locs=1:2))
	plot(MeasureGate)
end

# ╔═╡ 1615f882-445a-11eb-3a16-c76bb2305ff5
md"Note that now, when we measure them using the measure block, the output remains unchanged, even though we should've a 25% chance of getting `` |00〉, |01〉, |10〉 `` or `` |11〉 ``."

# ╔═╡ 9626495a-445a-11eb-0a2d-2168c66ad1e0
zero_state(2) |> MeasureGate |> r->measure(r, nshots=1024)

# ╔═╡ cd54fd7c-445a-11eb-0015-bd814cd6293f
md"Without the Measure gate,"

# ╔═╡ dbe0b9ee-445a-11eb-3526-29900707092e
zero_state(2) |> repeat(2, H, 1:2) |> r->measure(r, nshots=1024)

# ╔═╡ 06d1ed92-4485-11eb-3485-d1be36b9f68b
md"### The LabeledBlock"

# ╔═╡ 15f64d2c-4485-11eb-3749-a33d80bf5b6b
md"Its used for easily plotting circuits as boxes for simpler visualization."

# ╔═╡ 2c8d9af4-4485-11eb-238e-b36d910466fc
let
	a = chain(3, repeat(H, 1:2), put(3=>X))
	b = chain(2, repeat(Y, 1:2))
	circuit = chain(3, put(1:3 => label(a, "circuit a")), put(2:3 => label(b, "circuit b")))
	plot(circuit)
end

# ╔═╡ f5e2eb8a-4484-11eb-0957-41f14bbfc3d9
md"### Daggered Block"

# ╔═╡ 00b09d5a-4485-11eb-2857-011786a85254
md"We use Daggered block to build circuits which undo the effects of a particular circuit.

Let's take an example. Remember Bell Circuit?"

# ╔═╡ ec2e6fb4-4485-11eb-351d-4781925a47d3
begin
	bellcircuit = chain(2, put(1=>H), control(1, 2=>X))
	plot(bellcircuit)
end

# ╔═╡ 1791bfd0-4486-11eb-0107-e345d88b2ddb
state(zero_state(2) |> bellcircuit)

# ╔═╡ 61a0a0a0-4486-11eb-2f6a-750de59d5988
md"Building the Reverse Bell Circuit is as easy as,"

# ╔═╡ 771faeba-4486-11eb-07ac-4dc4d52d4726
state(zero_state(2) |> bellcircuit |> Daggered(bellcircuit))

# ╔═╡ b7d76bd4-4486-11eb-3565-ab0ef9238acf
md"Plotting the Daggered Block is a bit tricky! YaoPlots doesn't support DaggeredBlock yet. There are two alternatives to this."

# ╔═╡ 39890364-4488-11eb-22d7-33a8db179379
plot(Daggered(bellcircuit))

# ╔═╡ 4eb6a852-4488-11eb-0e42-7be38fca7571
md"One way is to use the label block"

# ╔═╡ 11a084ac-4487-11eb-30ea-539b38e0231f
let
	reversebellcircuit = put(2, 1:2 => label(Daggered(bellcircuit), "Bell\n Circuit†"))
	plot(reversebellcircuit)
end

# ╔═╡ 49ed44ca-4488-11eb-07dd-27f8ca6a1d41
md"Another way, is to use ` ' `."

# ╔═╡ 6325cf34-4488-11eb-0415-1d1f2fa0f1ba
plot(bellcircuit')

# ╔═╡ 6f5420f8-4488-11eb-3cf9-03675dc3c8db
md"Whats the difference between the DaggeredBlock and adjoint ` ' ` ?

One is a function of Yao, while another of Julia `Base`. Both perform the same operation on their input.

**Note:** _Its recommended to always use `'` over DaggeredBlock_."

# ╔═╡ f612e17a-475c-11eb-080a-ab0967411f64
md"### The Kron Block"

# ╔═╡ fc6c126c-475c-11eb-3efa-d5ffc4347358
md"Consider you've to make the below circuit."

# ╔═╡ 0c13552c-475d-11eb-23a8-ad67f6bb93bc
plot(kron(X,Y,Z,H,Rx(15)))

# ╔═╡ 2c87c612-475d-11eb-3af7-9542b157252f
md"Tired of using `put` block after `put` block?

Presenting the kron block, where you can just input the gates on every qubit, one by one."

# ╔═╡ 616d3b96-475d-11eb-1500-5bbf4a263df8
plot(chain(5, put(1:3 => kron(X,Y,Z)), put(3:5 => kron(H,Rx(15),T))))

# ╔═╡ d643aa86-475d-11eb-3af7-0195206e2f64
md"### The Rotation Gate"

# ╔═╡ e39911c6-475d-11eb-2ef5-f743c27d0d07
md"Its the general version of the Rx, Ry and Rz gates you saw above"

# ╔═╡ fb813b74-475d-11eb-1323-6faf1b221dad
plot(chain(1, rot(X, 15), rot(Y, 16)))

# ╔═╡ Cell order:
# ╠═11c4ccdd-3d86-48ce-b471-3a581902c345
# ╟─3c87d83e-1dca-11eb-2068-6fa8ceb4849a
# ╟─5545465e-1dca-11eb-0273-6d32f757b4a2
# ╟─8def8f9e-1dca-11eb-32cf-fdb2038a8feb
# ╟─83f65eb6-1dca-11eb-32cb-c577ad1d0b68
# ╟─9d8d835c-1dca-11eb-0ab7-01a80ca23e30
# ╟─8cca417e-1dd4-11eb-2a18-bf9b80da3fd2
# ╠═f6fa5d36-1dd4-11eb-1c33-3344c7697e82
# ╠═1f7c993e-1dd5-11eb-2f13-7711d2426eb1
# ╠═5960e486-1dd5-11eb-2253-838f59b06d4c
# ╟─ac10f6f8-1dd5-11eb-2cf1-29480480572a
# ╟─af22f8a8-1ddc-11eb-2958-bb45cdadf44f
# ╠═df5a774e-1ddc-11eb-2e86-1dc8dd502e26
# ╟─c1f87994-1e7c-11eb-3da3-21aa52e80520
# ╠═cda277ce-1e7c-11eb-246c-0b97bb66ab73
# ╟─a1173196-1dd9-11eb-394d-271a3871e181
# ╟─b1cc417a-1dd9-11eb-0156-67fd10743000
# ╠═67b2bc30-1dda-11eb-2d81-edd411a207b2
# ╟─7d546e10-1dda-11eb-2b9a-abe61e9edf6c
# ╠═83dfc128-1dda-11eb-37d9-37b959ea5f58
# ╟─ea6c8192-1e7c-11eb-30e5-8d65801ce703
# ╟─feab87f0-1e7c-11eb-075b-a55e9d86184c
# ╟─408ce964-1de4-11eb-2830-f14bdc87f899
# ╟─09a069f0-1df6-11eb-19de-5da4bb572510
# ╟─5a6f6786-1df7-11eb-0f06-5b229b0935bb
# ╟─6a775062-1df7-11eb-1ea2-7d5517774328
# ╟─8d65504c-1df7-11eb-0f3a-93796bf5b7f9
# ╠═e10cd2c0-1e00-11eb-3c86-2d958e7aa92f
# ╠═38071b04-1e00-11eb-1c6c-03f03ce37a92
# ╟─451b4c6a-1e1f-11eb-18f0-e301d5d34c93
# ╠═ea7cda52-1e1f-11eb-2cdc-e9a04181acfb
# ╟─8c6c62c4-1e20-11eb-1017-f51535591214
# ╠═a214d034-1e20-11eb-2975-6f440fee2ee5
# ╟─16d650bc-1e7d-11eb-39d0-33ba9cb031ed
# ╟─275a85be-1e7d-11eb-1114-f9872f35915d
# ╟─3355c7c4-1e21-11eb-1d88-6f5a6b5c51a8
# ╟─4afe09cc-1e21-11eb-1eec-6b6d78e76aae
# ╟─812b528e-1e21-11eb-3219-a9ce5de87933
# ╠═b85aaad6-1e79-11eb-2d2a-2f0a8fdb9336
# ╠═63dd442a-1e7c-11eb-3c73-7186108b87b4
# ╟─5c1a804c-1e7a-11eb-22ae-df5da105a8d7
# ╟─cd7061a8-1e7d-11eb-3239-8f8410988851
# ╟─5890257c-1e7e-11eb-2880-1d4257468ffc
# ╠═c1d38e3e-1e7e-11eb-07ac-5345d88b4781
# ╠═512086b4-1e7f-11eb-1f4b-cde0db3c9734
# ╠═8c44d66e-1e7f-11eb-2821-5532c1fdb89d
# ╠═07b469b8-1e85-11eb-1b2c-f3fb85382b66
# ╟─96d92a88-4459-11eb-29ee-336cbba060a7
# ╟─b23f15ca-4459-11eb-3089-f7ea50aaf5de
# ╠═c9233378-4459-11eb-0aa9-af1cd8cef6ca
# ╟─1615f882-445a-11eb-3a16-c76bb2305ff5
# ╠═9626495a-445a-11eb-0a2d-2168c66ad1e0
# ╟─cd54fd7c-445a-11eb-0015-bd814cd6293f
# ╠═dbe0b9ee-445a-11eb-3526-29900707092e
# ╟─06d1ed92-4485-11eb-3485-d1be36b9f68b
# ╟─15f64d2c-4485-11eb-3749-a33d80bf5b6b
# ╠═2c8d9af4-4485-11eb-238e-b36d910466fc
# ╟─f5e2eb8a-4484-11eb-0957-41f14bbfc3d9
# ╟─00b09d5a-4485-11eb-2857-011786a85254
# ╠═ec2e6fb4-4485-11eb-351d-4781925a47d3
# ╠═1791bfd0-4486-11eb-0107-e345d88b2ddb
# ╟─61a0a0a0-4486-11eb-2f6a-750de59d5988
# ╠═771faeba-4486-11eb-07ac-4dc4d52d4726
# ╟─b7d76bd4-4486-11eb-3565-ab0ef9238acf
# ╠═39890364-4488-11eb-22d7-33a8db179379
# ╟─4eb6a852-4488-11eb-0e42-7be38fca7571
# ╠═11a084ac-4487-11eb-30ea-539b38e0231f
# ╟─49ed44ca-4488-11eb-07dd-27f8ca6a1d41
# ╠═6325cf34-4488-11eb-0415-1d1f2fa0f1ba
# ╟─6f5420f8-4488-11eb-3cf9-03675dc3c8db
# ╟─f612e17a-475c-11eb-080a-ab0967411f64
# ╟─fc6c126c-475c-11eb-3efa-d5ffc4347358
# ╠═0c13552c-475d-11eb-23a8-ad67f6bb93bc
# ╟─2c87c612-475d-11eb-3af7-9542b157252f
# ╠═616d3b96-475d-11eb-1500-5bbf4a263df8
# ╟─d643aa86-475d-11eb-3af7-0195206e2f64
# ╟─e39911c6-475d-11eb-2ef5-f743c27d0d07
# ╠═fb813b74-475d-11eb-1323-6faf1b221dad


text:
include("../../../src/plotSol.jl")
using LinearAlgebra
using DifferentialEquations
using Revise

function DoublePendulumMinReal(m,l,theta1,theta2, tInt, tEnd)
    grav = -9.806
    function thdotDouble(x)
        thDiff = x[3] - x[1] # theta2 - theta1
        A = zeros(4,4); b = zeros(4)
        A[1,1] = 1; b[1] = x[2]
        A[2,:] = [0 4/3 0 cos(thDiff)/2]; b[2] = 3*grav/2/l*sin(x[1]) + sin(thDiff)/2*(x[4]^2)
        A[3,3] = 1; b[3] = x[4]
        A[4,:] = [0 cos(thDiff)/2 0 1/3]; b[4] = grav/2/l*sin(x[3]) - sin(thDiff)/2*(x[2]^2)
        xdot = A\b
        return xdot
    end

    function mainDynMinRealDouble(x,p,t)
        dx = thdotDouble(x)
        return dx
    end

    x0 = [theta1; 0.0; theta2; 0.0]
    prob = ODEProblem(mainDynMinRealDouble,x0,(0.0,tEnd))
    sol = solve(prob,Tsit5(),saveat = tInt,reltol=1e-10,abstol=1e-10)
    tSim = sol.t
    th1Sol = sol[1,:]
    th1DotSol = sol[2,:]
    th2Sol = sol[3,:]
    th2DotSol = sol[4,:]
    x1Sol = zeros(length(tSim)); x2Sol = zeros(length(tSim));
    z1Sol = zeros(length(tSim)); z2Sol = zeros(length(tSim));
    y1Sol = zeros(length(tSim));
    v1Sol = zeros(length(tSim),3); v2Sol = zeros(length(tSim),3);
    r2Sol = zeros(length(tSim),3);
    for i=1:length(tSim)
        x1Sol[i] = l/2*sin(th1Sol[i])
        z1Sol[i] = -l/2*cos(th1Sol[i])
        v1Sol[i,:] = [l/2*cos(th1Sol[i])*th1DotSol[i] 0.0 l/2*sin(th1Sol[i])*th1DotSol[i]];

        x2Sol[i] = l*sin(th1Sol[i]) + l/2*sin(th2Sol[i])
        z2Sol[i] = -l*cos(th1Sol[i]) - l/2*cos(th2Sol[i])
        v2Sol[i,:] = 2*v1Sol[i,:] + permutedims([l/2*cos(th2Sol[i])*th2DotSol[i] 0.0 l/2*sin(th2Sol[i])*th2DotSol[i]]);
    end
    r1Sol = [x1Sol y1Sol z1Sol]
    r2Sol = [x2Sol y1Sol z2Sol]
    ω1Sol = [y1Sol -th1DotSol y1Sol]
    ω2Sol = [y1Sol -th2DotSol y1Sol]
    return r1Sol, r2Sol, v1Sol, v2Sol, ω1Sol, ω2Sol
end

max_stars_repo_name: isrlab/Multibody-Dynamics

max_issues_repo_name: isrlab/Multibody-Dynamics

max_forks_repo_name: isrlab/Multibody-Dynamics