text:
import ..Cos: CosXString

export
    pdFontIsBold,
    pdFontIsItalic,
    pdFontIsFixedW,
    pdFontIsAllCap,
    pdFontIsSmallCap

using Rectangle

#=
Sample CMaps are available now as 8.cmap and 16.cmap in the test/files directory
for 8 and 16-bit toUnicode CMaps.
CMaps can have both 8 and 16 bit ranges in the same CMap file as well.
=#

const beginbfchar = b"beginbfchar"
const endbfchar   = b"endbfchar"
const beginbfrange = b"beginbfrange"
const endbfrange = b"endbfrange"
const begincodespacerange = b"begincodespacerange"
const endcodespacerange = b"endcodespacerange"

mutable struct CMap
    code_space::IntervalTree{UInt8,
                             Union{CosNullType, IntervalTree{UInt8, CosNullType}}}
    range_map::IntervalTree{UInt8,
                            Union{CosObject, IntervalTree{UInt8, CosObject}}}
    function CMap()
        cs = IntervalTree{UInt8,
                          Union{CosNullType, IntervalTree{UInt8, CosNullType}}}()
        rm = IntervalTree{UInt8,
                          Union{CosObject, IntervalTree{UInt8, CosObject}}}()
        new(cs, rm)
    end
end

function show(io::IO, cmap::CMap)
    show(io, "Code Space:\n")
    show(io, cmap.code_space)
    show(io, "Range Map:\n")
    show(io, cmap.range_map)
end

const FontUnicodeMapping = Union{Dict{UInt8, Char}, CMap, Nothing}

#=
mutable struct FontUnicodeMapping
    encoding::Dict{UInt8, Char}
    cmap::CMap
    hasCMap::Bool
    FontUnicodeMapping() = new(Dict{UInt8, Char}(), CMap(), false)
end
=#

function merge_encoding!(fum::Dict{UInt8, Char}, encoding::CosName,
                         doc::CosDoc, font::IDDRef{CosDict})
    encoding_mapping =
        encoding == cn"WinAnsiEncoding"   ? WINEncoding_to_Unicode :
        encoding == cn"MacRomanEncoding"  ? MACEncoding_to_Unicode :
        encoding == cn"MacExpertEncoding" ? MEXEncoding_to_Unicode :
                                            STDEncoding_to_Unicode
    merge!(fum, encoding_mapping)
    return fum
end

abstract type FontType end
struct FontType1    <: FontType end
struct FontType3    <: FontType end
struct FontMMType1  <: FontType end
struct FontTrueType <: FontType end
struct FontDefType  <: FontType end

function FontType(subtype::CosName)
    subtype === cn"Type1"      && return FontType1()
    subtype === cn"Type3"      && return FontType3()
    subtype === cn"TrueType"   && return FontTrueType()
    subtype === cn"MMType1"    && return FontMMType1()
    return FontDefType()
end

merge_encoding!(fum::FontUnicodeMapping, ftype::FontType,
                doc::CosDoc, font::IDDRef{CosDict}) = fum

function merge_encoding!(fum::Dict{UInt8, Char},
                         ftype::Union{FontType1, FontMMType1},
                         doc::CosDoc, font::IDDRef{CosDict})
    basefont = cosDocGetObject(doc, font, cn"BaseFont")
    basefont_with_subset = CDTextString(basefont)
    basefont_str = rsplit(basefont_with_subset, '+';limit=2)[end]
    enc = basefont_str == "Symbol"       ? SYMEncoding_to_Unicode :
          basefont_str == "ZapfDingbats" ? ZAPEncoding_to_Unicode :
                                           STDEncoding_to_Unicode
    merge!(fum, enc)
    return fum
end

# for type 0 use cmap.
# for symbol and zapfdingbats - use font encoding
# for others use STD Encoding
# Reading encoding from the font files in case of Symbolic fonts are not
# supported.
# Font subset is addressed with font name identification.
function merge_encoding!(fum::Dict{UInt8, Char}, encoding::CosNullType,
                         doc::CosDoc, font::IDDRef{CosDict})
    subtype  = cosDocGetObject(doc, font, cn"Subtype")
    subtype === CosNull && return fum
    return merge_encoding!(fum, FontType(subtype), doc, font)
end

function merge_encoding!(fum::Dict{UInt8, Char},
                         encoding::IDD{CosDict},
                         doc::CosDoc, font::IDDRef{CosDict})
    baseenc = cosDocGetObject(doc, encoding, cn"BaseEncoding")
    merge_encoding!(fum, baseenc, doc, font)
    # Add the Differences
    subtype = cosDocGetObject(doc, font, cn"Subtype")
    subtype === cn"Type3" && return fum
    diff = cosDocGetObject(doc, encoding, cn"Differences")
    diff === CosNull && return fum
    values = get(diff)
    d = Dict{UInt8, CosName}()
    cid = 0xff
    for v in values
        if v isa CosInt
            cid = UInt8(get(v))
        else
            m = match(r"^uni[0-9A-F]+$",String(v))
            if isnothing(m)
                d[cid] = v
            else
                i = parse(Int,String(v)[4:end]; base=16)
                if i in keys(fum)
                    fum[cid] = fum[i]
                else
                    d[cid] = v
                end
            end
            cid += 1
        end
    end

    dict_to_unicode = dict_remap(d, AGL_Glyph_to_Unicode)
    merge!(fum, dict_to_unicode)

    return fum
end

function get_unicode_mapping(doc::CosDoc, font::IDDRef{CosDict})
    toUnicode = cosDocGetObject(doc, font, cn"ToUnicode")
    toUnicode !== CosNull &&
        return get_unicode_mapping(toUnicode)
    encoding = cosDocGetObject(doc, font, cn"Encoding")
    d = merge_encoding!(Dict{UInt8, Char}(), encoding, doc, font)
    return length(d) == 0 ? nothing : d
end

function get_unicode_mapping(cmap_stm::CosIndirectObject{CosStream})
    io = get(cmap_stm)
    try
        return read_cmap(io)
    finally
        util_close(io)
    end
end

function update_glyph_id_std_14(cosdoc, cosfont,
                                glyph_name_to_cid, cid_to_glyph_name)
    basefont = cosDocGetObject(cosdoc, cosfont, cn"BaseFont")
    basefont === CosNull && return false
    basefont_with_subset = CDTextString(basefont)
    basefont_str = String(rsplit(basefont_with_subset, '+';limit=2)[end])
    basefont_str in ADOBE_STD_14 || return false
    gn2cid, cid2gn =
        basefont_str == "Symbol" ?
        (GlyphName_to_SYMEncoding, SYMEncoding_to_GlyphName) :
        basefont_str == "ZapfDingbats" ?
        (GlyphName_to_ZAPEncoding, ZAPEncoding_to_GlyphName) :
        (GlyphName_to_STDEncoding, STDEncoding_to_GlyphName)
    merge!(glyph_name_to_cid, gn2cid)
    merge!(cid_to_glyph_name, cid2gn)
    return true
end

function get_glyph_id_mapping(cosdoc::CosDoc, cosfont::IDD{CosDict})
    glyph_name_to_cid, cid_to_glyph_name =
        Dict{CosName, UInt8}(), Dict{UInt8, CosName}()
    cosfont === CosNull && return glyph_name_to_cid, cid_to_glyph_name
    subtype = cosDocGetObject(cosdoc, cosfont, cn"Subtype")
    subtype === cn"Type0" && return glyph_name_to_cid, cid_to_glyph_name

    update_glyph_id_std_14(cosdoc, cosfont, glyph_name_to_cid, cid_to_glyph_name)

    encoding = cosDocGetObject(cosdoc, cosfont, cn"Encoding")
    encoding === CosNull && return glyph_name_to_cid, cid_to_glyph_name

    baseenc = typeof(encoding) === CosName ? encoding :
        cosDocGetObject(cosdoc, encoding, cn"BaseEncoding")
    gn2cid, cid2gn =
        baseenc == cn"WinAnsiEncoding"   ?
        (GlyphName_to_WINEncoding, WINEncoding_to_GlyphName) :
        baseenc == cn"MacRomanEncoding"  ?
        (GlyphName_to_MACEncoding, MACEncoding_to_GlyphName) :
        baseenc == cn"MacExpertEncoding" ?
        (Glyphname_to_MEXEncoding, MEXEncoding_to_GlyphName) :
        (GlyphName_to_STDEncoding, STDEncoding_to_GlyphName)

    if subtype !== cn"Type3"
        merge!(glyph_name_to_cid, gn2cid)
        merge!(cid_to_glyph_name, cid2gn)
    end
    typeof(encoding) === CosName && return glyph_name_to_cid, cid_to_glyph_name
    diff = cosDocGetObject(cosdoc, encoding, cn"Differences")
    diff === CosNull && return glyph_name_to_cid, cid_to_glyph_name
    values = get(diff)
    cid = 0x00
    for v in values
        if v isa CosInt
            cid = UInt8(get(v))
        else
            glyph_name_to_cid[v] = cid
            cid_to_glyph_name[cid] = v
            cid += 0x1
        end
    end
    return glyph_name_to_cid, cid_to_glyph_name
end

get_encoded_string(s::CosString, fum::Union{Dict{UInt8, Char}, CMap}) =
    get_encoded_string(Vector{UInt8}(s), fum)

function get_encoded_string(v::Union{Vector{UInt8}, NTuple{N, UInt8}},
                                    fum::Dict{UInt8, Char}) where N
    length(v) == 0 && return ""
    return String(NativeEncodingToUnicode(v, fum))
end

function get_unicode_chars(b::UInt8, i::Interval, v::Union{CosXString, CosArray})
    f = i.lo
    l = i.hi
    if v isa CosXString
        bytes = Vector{UInt8}(v)
        carr = get_unicode_chars(bytes)
        carr[1] += (b - f)  # Only one char should be generated here
    elseif v isa CosArray
        arr = get(v)
        xstr = arr[b - f + 1]
        @assert xstr isa CosXString
        bytes = Vector{UInt8}(xstr)
        carr = get_unicode_chars(bytes)
    end
    return carr
end

function get_unicode_chars(barr::Vector{UInt8})
    l = length(barr)
    nb = 0
    retarr = Vector{Char}()
    while nb < l
        b1, b2 = barr[1], barr[2]
        nb += 2
        c = 0
        if 0xD8  <= b1 <= 0xDB
            # UTF-16 Supplementary plane = 4 bytes
            b1 -= 0xD8
            c = b1
            c = c*256 + b2
            b3 = barr[3]
            b4 = barr[4]
            nb += 2
            if 0xDC <= b3 <= 0xDF
                b3 -= 0xDC
                c1 = b3
                c1 = c1*256 + b4
                c  = c*1024 + c1
                c += 0x10000
            end
        else
            c = b1*256 + b2
        end
        push!(retarr, Char(c))
    end
    return retarr
end

get_encoded_string(s::CosString, cmap::CMap) =
    get_encoded_string(Vector{UInt8}(s), cmap::CMap)

function get_encoded_string(barr, cmap::CMap)
    cs = cmap.code_space
    rm = cmap.range_map
    l = length(barr)
    b1 = b2 = 0x0
    carr = Vector{Char}()
    retarr = Vector{Char}()
    i = 0
    while i < l
        b1 = barr[i+=1]
        xs = intersect(cs, Interval(b1, b1))
        # When byte range is not in code space we should not return NUL.
        if length(xs) == 0
            push!(carr, Char(0))
            continue
        end
        # Some cmaps do not call out single byte ranges explicitly in the
        # code space. So may need to decipher the existence of a single
        # byte vs 2-byte code from the range map. See `else` below.
        itree = xs[1][2]
        # This case is very clearly a single byte range
        itv = intersect(rm, Interval(b1, b1))
        if itree === CosNull
            if length(itv) > 0
                carr = get_unicode_chars(b1, itv[1][1], itv[1][2])
            else
                push!(carr, Char(0))
            end
        else
            itree1 = itv
            if length(itree1) == 0
                push!(carr, Char(0))
                continue
            end
            # This is a single byte range case
            if itree1[1][2] isa CosObject
                carr = get_unicode_chars(b1, itree1[1][1], itree1[1][2])
            else
                b2 = barr[i+=1]
                itv = intersect(itree1[1][2], Interval(b2, b2))
                if length(itv) > 0
                    carr = get_unicode_chars(b2, itv[1][1], itv[1][2])
                else
                    push!(carr, Char(0))
                end
            end
        end
        append!(retarr, carr)
    end
    return retarr
end

cmap_command(b::Vector{UInt8}) =
    length(b), b != beginbfchar && b != beginbfrange && b != begincodespacerange ?
    nothing : Symbol(String(b))

function on_cmap_command!(stm::IO, command::Symbol,
                         params::Vector{CosInt}, cmap::CMap)
    n = get(pop!(params))
    o1, o2, o3 = CosNull, CosNull, CosNull
    for i = 1:n
        o1 = parse_value(stm)
        @assert isa(o1, CosXString)
        d1 = Vector{UInt8}(o1)
        o2 = (command == :beginbfchar) ? o1 : parse_value(stm)
        @assert isa(o2, CosXString)
        d2 = Vector{UInt8}(o2)
        if (command != :begincodespacerange)
            o3 = parse_value(stm)
            @assert isa(o3, CosXString) || isa(o3, CosArray)
            l = length(d1)
            if l == 1
                cmap.range_map[Interval(d1[1], d2[1])] = o3
            else
                imap = get!(cmap.range_map, Interval(d1[1], d2[1]),
                            IntervalTree{UInt8, CosObject}())
                imap[Interval(d1[2], d2[2])] = o3
            end
        else
            l = length(d1)
            if l == 1
                cmap.code_space[Interval(d1[1], d2[1])] = CosNull
            else
                imap = IntervalTree{UInt8, CosNullType}()
                imap[Interval(d1[2], d2[2])] = CosNull
                cmap.code_space[Interval(d1[1], d2[1])] = imap
            end
        end
    end
    return cmap
end

on_cmap_command!(stm::IO, command::CosObject,
                 params::Vector{CosInt}, cmap::CMap) = nothing

function read_cmap(stm::IO)
    tcmap = CMap()
    params = Vector{CosInt}()
    while !eof(stm)
        obj = parse_value(stm, cmap_command)
        if isa(obj, CosInt)
            push!(params, obj)
        end
        (obj == :beginbfchar || obj == :beginbfrange ||
         obj == :begincodespacerange) &&
            on_cmap_command!(stm, obj, params, tcmap)
    end
    return tcmap
end

struct CIDWidth
    imap::IntervalTree{UInt16, Float32}
    dw::Float32
end

CIDWidth(m::IntervalTree{UInt16, Float32}) = CIDWidth(m, 1000f0)
CIDWidth(tdw::Float32) = CIDWidth(IntervalTree{UInt16, Float32}(), tdw)
CIDWidth() = CIDWidth(1000f0)

mutable struct PDFont
    doc::PDDoc
    obj::IDD{CosDict}
    widths::Union{AdobeFontMetrics, Vector{Float32}, CIDWidth}
    fum::FontUnicodeMapping
    glyph_name_to_cid::Dict{CosName, UInt8}
    cid_to_glyph_name::Dict{UInt8, CosName}
    flags::UInt32
    fontname::CosName
    props::Dict
    @inline function PDFont(doc::PDDoc, cosfont::IDD{CosDict})
        fum = get_unicode_mapping(doc.cosDoc, cosfont)
        widths = get_font_widths(doc.cosDoc, cosfont)
        glyph_name_to_cid, cid_to_glyph_name =
            get_glyph_id_mapping(doc.cosDoc, cosfont)
        flags = get_font_flags(doc, cosfont, widths)
        fontname = get_font_name(doc, cosfont, widths)
        props = Dict()
        return new(doc, cosfont, widths, fum, glyph_name_to_cid,
                   cid_to_glyph_name, flags, fontname, props)
    end
end

INIT_CODE(::CIDWidth) = 0x0000
SPACE_CODE(w::CIDWidth) = get_character_code(cn"space", w)
INIT_CODE(x) = 0x00
SPACE_CODE(x) = get_character_code(cn"space", x)

function FontType(font::PDFont)
    subtype = cosDocGetObject(font.doc.cosDoc, font.obj, cn"Subtype")
    subtype === CosNull && return FontDefType()
    return FontType(subtype)
end

"""
```
    pdFontIsBold(pdfont::PDFont)    ->Bool
```
    Returns `true` is the fonts have the attribute specified
"""
pdFontIsBold(pdfont::PDFont)     = (pdfont.flags & 0x80000000) > 0

"""
```
    pdFontIsItalic(pdfont::PDFont)  ->Bool
```
    Returns `true` is the fonts have the attribute specified
"""
pdFontIsItalic(pdfont::PDFont)   = (pdfont.flags & 0x00000040) > 0

"""
```
    pdFontIsFixedW(pdfont::PDFont)  ->Bool
```
    Returns `true` is the fonts have the attribute specified
"""
pdFontIsFixedW(pdfont::PDFont)   = (pdfont.flags & 0x00000001) > 0

"""
```
    pdFontIsAllCap(pdfont::PDFont)  ->Bool
```
    Returns `true` is the fonts have the attribute specified
"""
pdFontIsAllCap(pdfont::PDFont)   = (pdfont.flags & 0x00010000) > 0

"""
```
    pdFontIsSmallCap(pdfont::PDFont)->Bool
```
    Returns `true` is the fonts have the attribute specified
"""
pdFontIsSmallCap(pdfont::PDFont) = (pdfont.flags & 0x00020000) > 0

# Not supported FD attribute in CIDFonts
@inline function get_font_flags(doc::PDDoc, cosfont::IDD{CosDict}, widths)
    flags = 0x00000000
    refdesc = cosDocGetObject(doc.cosDoc, cosfont, cn"FontDescriptor")
    refdesc === CosNull && return get_font_flags(widths)
    cosflags = cosDocGetObject(doc.cosDoc, refdesc, cn"Flags")
    cfweight = cosDocGetObject(doc.cosDoc, refdesc, cn"FontWeight")
    cfname   = cosDocGetObject(doc.cosDoc, refdesc, cn"FontName")
    cfweight !== CosNull && get(cfweight) >= 700 && (flags |= 0x80000000)
    cfname   !== CosNull &&
        (occursin("Bold", string(cfname)) ||
         occursin("bold", string(cfname))) &&
        (flags |= 0x80000000)
    cosflags !== CosNull && (flags += UInt32(get(cosflags)))
    return flags
end
get_font_flags(x) = 0x00000000

@inline function get_font_name(doc::PDDoc, cosfont::IDD{CosDict}, widths)
    refdesc = cosDocGetObject(doc.cosDoc, cosfont, cn"FontDescriptor")
    refdesc === CosNull && return get_font_name(cosfont, widths)
    return cosDocGetObject(doc.cosDoc, refdesc, cn"FontName")
end
#Not implemented for CIDFonts
get_font_name(cosfont::IDD{CosDict}, ::CIDWidth) = cn""
function get_font_name(cosfont::IDD{CosDict}, x)
    subtype   = get(cosfont, cn"Subtype")
    if subtype === cn"Type3"
        name = get(cosfont, cn"Name")
        name === CosNull && return cn"Type3"
        return name
    end
    basefname = get(cosfont, cn"BaseFont")
    basefname === CosNull &&
        error("Non-standard 14 fonts having no BaseFont")
    return basefname
end

function get_character_code(name::CosName, pdfont::PDFont)
    length(pdfont.glyph_name_to_cid) > 0 &&
        return get(pdfont.glyph_name_to_cid, name, INIT_CODE(pdfont.widths))
    return get_character_code(name, pdfont.widths)
end

get_character_code(name::CosName, w::CIDWidth) =
    UInt16(get(AGL_Glyph_to_Unicode, name, INIT_CODE(w)))

get_character_code(name::CosName, w) =
    get(GlyphName_to_STDEncoding, name, INIT_CODE(w))

get_encoded_string(s, pdfont::PDFont) = get_encoded_string(s, pdfont.fum)

get_encoded_string(s, pdfont::Nothing) = CDTextString(s)

get_char(barr, w) = iterate(barr)
function get_char(barr, w::CIDWidth)
    next = iterate(barr)
    next === nothing && return nothing
    (b1, state) = next
    next = iterate(barr, state)
    @assert next !== nothing "Error in obtaining character data"
    (b2, state) = next
    return (UInt16(b1*0x0100 + b2), state)
end

function get_char(barr, state, w::CIDWidth)
    next = iterate(barr, state)
    next === nothing && return nothing
    (b1, state) = next
    next = iterate(barr, state)
    @assert next !== nothing "Error in obtaining character data"
    (b2, state) = next
    return (UInt16(b1*0x0100 + b2), state)
end
get_char(barr, state, w) = iterate(barr, state)

function get_string_width(barr::Vector{UInt8}, widths, pc, tfs, tj, tc, tw)
    totalw = 0f0
    next = get_char(barr, widths)
    while next !== nothing
        c, st = next
        w = get_character_width(c, widths)
        kw = get_kern_width(pc, c, widths)
        w = (w - tj)*tfs / 1000f0 + ((c == SPACE_CODE(widths)) ? tw : tc)
        w += kw
        pc = c
        tj = 0f0
        totalw += w
        next = get_char(barr, st, widths)
    end
    return totalw
end

function get_TextBox(ss::Vector{Union{CosXString, CosLiteralString,
                                      CosFloat, CosInt}},
                     pdfont::PDFont,
                     tfs, tc, tw, th)
    totalw = 0f0
    tj = 0f0
    text = ""
    for s in ss
        if s isa CosXString || s isa CosLiteralString
            prev_char = INIT_CODE(pdfont.widths)
            t = String(get_encoded_string(s, pdfont))
            if (-tj) > 180 && length(t) > 0 && t[1] != ' ' &&
                length(text) > 0 && text[end] != ' '
                text *= " "
            end
            text *= t
            barr = Vector{UInt8}(s)
            totalw += get_string_width(barr, pdfont.widths, prev_char,
                                       tfs, tj, tc, tw)
            tj = 0f0
        elseif s isa CosFloat || s isa CosInt
            tj = s |> get |> Float32
        end
    end
    totalw *= th
    return text, totalw, tfs
end

function get_character_width(cid::UInt16, w::CIDWidth)
    itv = intersect(w.imap, Interval(cid, cid))
    length(itv) == 0 && return w.dw
    return itv[1][2]
end


text:
#---------------------------------------------------------
# # [SpecialMatrices overview](@id 1-overview)
#---------------------------------------------------------

# This page illustrates the Julia package
# [`SpecialMatrices`](https://github.com/JuliaMatrices/SpecialMatrices.jl).

# ### Setup

# Packages needed here.

using SpecialMatrices

# ### Cauchy

Cauchy(collect(1:3), collect(2:4))

ext: jl


text:
"""
    Plane(P::AbstractVector, Vx::AbstractVector, Vy::AbstractVector)
Specifies a 2D plane in the so-called parameter form using 3 vectors.
Here the first argument `P` is a vector on the plane, the two vectors `Vx` and `Vy` are two other vectors, which span the plane and should ideally be orthogonal.
"""
struct Plane
    stütz::AbstractVector
    Vx::AbstractVector
    Vy::AbstractVector
    function Plane(stütz::AbstractVector{<:Number}, Vx::AbstractVector{<:Number}, Vy::AbstractVector{<:Number}; Make2ndOrthogonal::Bool=true)
        if length(stütz) == 2 stütz = [stütz[1],stütz[2],0] end
        !(length(stütz) == length(Vx) == length(Vy)) && throw("Dimension mismatch. length(stütz) = $(length(stütz)), length(Vx) = $(length(Vx)), length(Vy) = $(length(Vy))")

        (Make2ndOrthogonal && abs(dot(Vx,Vy)) > 4e-15) && return Plane(stütz, Vx, Make2ndOrthogonal(Vx,Vy))

        if length(stütz) < 20
            stütz = SVector{length(Vx)}(floatify(stütz));     Vx = SVector{length(Vx)}(floatify(Vx))
            Vy = SVector{length(Vx)}(floatify(Vy))
            return new(stütz, Vx, Vy)
        else
            return new(floatify(stütz), floatify(Vx), floatify(Vy))
        end
    end
end

length(PL::Plane) = length(PL.stütz)

function MLEinPlane(DM::AbstractDataModel, PL::Plane, start::AbstractVector{<:Number}=0.0001rand(2); tol::Real=1e-8)
    length(start) != 2 && throw("Dimensional Mismatch.")
    PlanarLogPrior = EmbedLogPrior(DM, PL)
    planarmod(x, θ::AbstractVector{<:Number}; kwargs...) = Predictor(DM)(x, PlaneCoordinates(PL,θ); kwargs...)
    return try
        # faster but sometimes problems with ForwarDiff-generated gradients in LsqFit
        curve_fit(Data(DM), planarmod, start, PlanarLogPrior; tol=tol).param
    catch;
        planardmod(x, θ::AbstractVector{<:Number}; kwargs...) = dPredictor(DM)(x, PlaneCoordinates(PL,θ); kwargs...) * [PL.Vx PL.Vy]
        curve_fit(Data(DM), planarmod, planardmod, start, PlanarLogPrior; tol=tol).param
    end
end

function PlanarDataModel(DM::AbstractDataModel, PL::Plane)
    @assert DM isa DataModel
    model = Predictor(DM);      dmodel = dPredictor(DM)
    newmod = (x,θ::AbstractVector{<:Number}; kwargs...) -> model(x, PlaneCoordinates(PL,θ); kwargs...)
    dnewmod = (x,θ::AbstractVector{<:Number}; kwargs...) -> dmodel(x, PlaneCoordinates(PL,θ); kwargs...) * [PL.Vx PL.Vy]
    PlanarLogPrior = EmbedLogPrior(DM, PL)
    mle = MLEinPlane(DM, PL)
    DataModel(Data(DM), newmod, dnewmod, mle, loglikelihood(DM, PlaneCoordinates(PL, mle), PlanarLogPrior), PlanarLogPrior, true)
end

# Performance gains of using static vectors is lost if their length exceeds 32
BasisVectorSV(Slot::Int, dims::Int) = SVector{dims}(Float64(i == Slot) for i in 1:dims)

"""
    BasisVector(Slot::Int, dims::Int) -> Vector{Float64}
Computes a standard basis vector of length `dims`, i.e. whose components are all zero except for the component `Slot`, which has a value of one.
"""
function BasisVector(Slot::Int, dims::Int)
    Res = zeros(dims);    Res[Slot] = 1.;    Res
end

"""
    PlaneCoordinates(PL::Plane, v::AbstractVector{<:Number})
Returns an n-dimensional vector from a tuple of two real numbers which correspond to the coordinates in the 2D `Plane`.
"""
PlaneCoordinates(PL::Plane, v::AbstractVector) = PL.stütz + [PL.Vx PL.Vy] * v
PlaneCoordinates(PL::Plane) = x->PlaneCoordinates(PL,x)

Shift(PlaneBegin::Plane, PlaneEnd::Plane) = TranslatePlane(PlaneEnd, PlaneEnd.stütz - PlaneBegin.stütz)

IsOnPlane(PL::Plane, x::AbstractVector, ProjectionOp::AbstractMatrix=ProjectionOperator(PL))::Bool = DistanceToPlane(PL, x, ProjectionOp) < 4e-15
TranslatePlane(PL::Plane, v::AbstractVector) = Plane(PL.stütz + v, PL.Vx, PL.Vy)
RotatePlane(PL::Plane, rads::Real=π/2) = ((S,C) = sincos(rads);   Plane(PL.stütz, C*PL.Vx + S*PL.Vy, C*PL.Vy - S*PL.Vx))
function RotationMatrix(PL::Plane, rads::Real)
    V = PL.Vx*transpose(PL.Vx) + PL.Vy*transpose(PL.Vy)
    W = PL.Vx*transpose(PL.Vy) - PL.Vy*transpose(PL.Vx)
    Diagonal(ones(length(PL.stütz))) + (cos(rads)-1.)*V -sin(rads)*W
end
RotateVector(PL::Plane, v::AbstractVector, rads::Real) = RotationMatrix(PL,rads) * v

function RotatedVector(α::Real, n1::Int, n2::Int, tot::Int)
    @assert (n1 ≤ tot && n2 ≤ tot && n1 != n2 && all(x->(x>0), [n1,n2,tot]))
    res = zeros(tot);   res[n1] = cos(α);   res[n2] = sin(α);   res
end

function DecomposeWRTPlane(PL::Plane, x::AbstractVector)
    @assert IsOnPlane(PL,x)
    V = x - PL.stütz
    [dot(V, PL.Vx), dot(V, PL.Vy)]
end

DistanceToPlane(PL::Plane, x::AbstractVector, ProjectionOp::AbstractMatrix=ProjectionOperator(PL)) = (Diagonal(ones(length(x))) - ProjectionOp) * (x - PL.stütz) |> norm
ProjectOntoPlane(PL::Plane, x::AbstractVector, ProjectionOp::AbstractMatrix=ProjectionOperator(PL)) = ProjectionOp * (x - PL.stütz) + PL.stütz

function ProjectionOperator(A::AbstractMatrix)
    size(A,2) != 2 && println("ProjectionOperator: Matrix size $(size(A)) not as expected.")
    A * inv(transpose(A) * A) * transpose(A)
end
ProjectionOperator(PL::Plane) = ProjectionOperator([PL.Vx PL.Vy])

IsNormalToPlane(PL::Plane, v::AbstractVector)::Bool = abs(dot(PL.Vx, v)) < 4e-15 && abs(dot(PL.Vy, v)) < 4e-15

function Make2ndOrthogonal(X::AbstractVector,Y::AbstractVector)
    Basis = GramSchmidt(floatify([X,Y]))
    # Maybe add check for orientation?
    return Basis[2]
end

"""
    MinimizeOnPlane(PL::Plane,F::Function,initial::AbstractVector=[1,-1.]; tol::Real=1e-5)
Minimizes given function in Plane and returns the optimal point in the ambient space in which the plane lies.
"""
function MinimizeOnPlane(PL::Plane, F::Function, initial::AbstractVector=[1e-2,-1e-2]; tol::Real=1e-5, meth::Optim.AbstractOptimizer=LBFGS(), kwargs...)
    # G(x) = F(PlaneCoordinates(PL,x))
    X = InformationGeometry.minimize(F∘PlaneCoordinates(PL), initial; tol=tol, meth=meth, kwargs...)
    # X = Optim.minimizer(optimize(G,initial, BFGS(), Optim.Options(g_tol=tol), autodiff = :forward))
    PlaneCoordinates(PL,X)
end

"""
    ProjectOnto(v::AbstractVector, u::AbstractVector)
Project `v` onto `u`.
"""
ProjectOnto(v::AbstractVector, u::AbstractVector) = (dot(v,u) / dot(u,u)) * u

"""
    ParallelPlanes(PL::Plane, v::AbstractVector, range) -> Vector{Plane}
Returns Vector of Planes which have been translated by `a .* v` for all `a` in `range`.
"""
function ParallelPlanes(PL::Plane, v::AbstractVector, range::AbstractVector{<:Real})
    norm(v) == 0. && throw("Direction cannot be null vector.")
    # PL.Projector * v == v && throw("Plane and vector linearly dependent.")
    ProjectOntoPlane(PL,v) == v && throw("Plane and vector linearly dependent.")
    [TranslatePlane(PL, ran .* v) for ran in range]
end

function GramSchmidt(v::AbstractVector, dim::Int=length(v))
    Basis = Vector{Vector{suff(v)}}(undef,dim)
    Basis[1] = normalize(v)
    for i in 2:dim
        Basis[i] = BasisVector(i,dim)
    end;    GramSchmidt(Basis)
end
function GramSchmidt(Basis::AbstractVector{<:AbstractVector{<:AbstractFloat}})
    ONBasis = floatify(Basis)
    for j in 2:length(Basis)
        for i in 2:j
            Basis[j] .-= ProjectOnto(Basis[j],ONBasis[i-1])
        end
    end
    for i in 1:length(ONBasis) normalize!(ONBasis[i]) end
    ONBasis
end

"""
    HyperPlane(basepoint, dir1, ...) -> Function
Returns an embedding function which translates points from HyperPlane coordinates to the ambient space.
"""
function HyperPlane(args...)
    @assert all(x->typeof(x) <: AbstractVector{<:Number},args) && ConsistentElDims(args) > 0
    argdim = length(args) -1
    function EmbedIntoHyperPlane(θ::AbstractVector{<:Number})
        @assert length(θ) == argdim
        args[1] + sum(θ[i] * args[i+1] for i in 1:argdim)
    end
end

"""
The `HyperCube` type is used to specify a cuboid region in the form of a cartesian product of ``N`` real intervals, thereby offering a convenient way of passing domains for integration or plotting between functions.
A `HyperCube` object `cube` type has two fields: `cube.L` and `cube.U` which are two vectors which respectively store the lower and upper boundaries of the real intervals in order.
Examples for constructing `HyperCube`s:
```julia
HyperCube([[1,3],[π,2π],[-500,100]])
HyperCube([1,π,-500],[3,2π,100])
HyperCube([[-1,1]])
HyperCube([-1,1])
HyperCube(collect([-7,7.] for i in 1:3))
```
Examples of quantities that can be computed from and operations involving a `HyperCube` object `X`:
```julia
CubeVol(X)
TranslateCube(X,v::AbstractVector)
CubeWidths(X)
```
"""
struct HyperCube{Q<:Number} <: Cuboid
    L::AbstractVector{Q}
    U::AbstractVector{Q}
    HyperCube(C::HyperCube; Padding::Number=0.) = HyperCube(C.L, C.U; Padding=Padding)
    function HyperCube(lowers::AbstractVector{<:Number}, uppers::AbstractVector{<:Number}; Padding::Number=0.)
        @assert length(lowers) == length(uppers)
        if Padding != 0.
            diff = (uppers - lowers) .* (Padding / 2.)
            lowers -= diff;     uppers += diff
        end
        !all(lowers .≤ uppers) && throw("First argument of HyperCube must be larger than second.")
        if length(lowers) < 20
            return new{suff(lowers)}(SVector{length(lowers)}(floatify(lowers)), SVector{length(uppers)}(floatify(uppers)))
        else
            return new{suff(lowers)}(floatify(lowers),floatify(uppers))
        end
    end
    function HyperCube(H::AbstractVector{<:AbstractVector{<:Number}}; Padding::Number=0.)
        len = length(H[1]);        !all(x->(length(x) == len),H) && throw("Inconsistent lengths.")
        M = Unpack(H);        HyperCube(M[:,1],M[:,2]; Padding=Padding)
    end
    function HyperCube(T::AbstractVector{<:Tuple{<:Real,<:Real}}; Padding::Number=0.)
        HyperCube([T[i][1] for i in 1:length(T)], [T[i][2] for i in 1:length(T)]; Padding=Padding)
    end
    function HyperCube(vals::AbstractVector{<:Number}; Padding::Number=0.)
        length(vals) != 2 && throw("Input has too many components.")
        HyperCube([vals[1]],[vals[2]]; Padding=Padding)
    end
    HyperCube(vals::Tuple{<:Number,<:Number}; Padding::Number=0.) = HyperCube([vals[1]],[vals[2]]; Padding=Padding)
    HyperCube(center::AbstractVector{<:Number}, width::Real) = HyperCube(center .- (width/2), center .+ (width/2))
end

length(Cube::HyperCube) = length(Cube.L)

"""
    Inside(Cube::HyperCube, p::AbstractVector{<:Number}) -> Bool
Checks whether a point `p` lies inside `Cube`.
"""
Inside(Cube::HyperCube, p::AbstractVector{<:Number}) = all(Cube.L .≤ p) && all(p .≤ Cube.U)

import Base.in
"""
    in(Cube::HyperCube, p::AbstractVector{<:Number}) -> Bool
Checks whether a point `p` lies inside `Cube`.
"""
in(p::AbstractVector{<:Number}, Cube::HyperCube) = Inside(Cube, p)

"""
    ConstructCube(M::Matrix{<:Number}; Padding::Number=1/50) -> HyperCube
Returns a `HyperCube` which encloses the extrema of the columns of the input matrix.
"""
ConstructCube(M::AbstractMatrix{<:Number}; Padding::Number=0.) = HyperCube([minimum(M[:,i]) for i in 1:size(M,2)], [maximum(M[:,i]) for i in 1:size(M,2)]; Padding=Padding)
ConstructCube(V::AbstractVector{<:Number}; Padding::Number=0.) = HyperCube(extrema(V); Padding=Padding)
ConstructCube(PL::Plane, sol::AbstractODESolution; Padding::Number=0.) = ConstructCube(Deplanarize(PL,sol; N=300); Padding=Padding)
ConstructCube(Ps::AbstractVector{<:AbstractVector{<:Number}}; Padding::Number=0.) = ConstructCube(Unpack(Ps); Padding=Padding)

# Could speed this up by just using the points in sol.u without interpolation.
function ConstructCube(sol::AbstractODESolution, Npoints::Int=200; Padding::Number=0.)
    ConstructCube(Unpack(map(sol,range(sol.t[1],sol.t[end];length=Npoints))); Padding=Padding)
end
function ConstructCube(sols::AbstractVector{<:AbstractODESolution}, Npoints::Int=200; Padding::Number=0.)
    mapreduce(sol->ConstructCube(sol, Npoints; Padding=Padding), union, sols)
end

ConstructCube(Tup::Tuple{AbstractVector{<:Plane},AbstractVector{<:AbstractODESolution}}; Padding=0.) = ConstructCube(Tup[1], Tup[2]; Padding=Padding)
function ConstructCube(Planes::AbstractVector{<:Plane}, sols::AbstractVector{<:AbstractODESolution}; Padding=0.)
    @assert length(Planes) == length(sols)
    reduce(union, map((x,y)->ConstructCube(x,y; Padding=Padding), Planes, sols))
end

"""
    CubeWidths(H::HyperCube) -> Vector
Returns vector of widths of the `HyperCube`.
"""
CubeWidths(Cube::HyperCube) = Cube.U - Cube.L

"""
    CubeVol(Cube::HyperCube) -> Real
Computes volume of a `HyperCube` as the product of its sidelengths.
"""
CubeVol(Cube::HyperCube) = prod(CubeWidths(Cube))

"""
    Center(Cube::HyperCube) -> Vector
Returns center of mass of `Cube`.
"""
Center(Cube::HyperCube) = 0.5 * (Cube.L + Cube.U)

"""
    TranslateCube(Cube::HyperCube,x::AbstractVector{<:Number}) -> HyperCube
Returns a `HyperCube` object which has been translated by `x`.
"""
TranslateCube(Cube::HyperCube, x::AbstractVector{<:Number}) = HyperCube(Cube.L + x, Cube.U + x)

"""
    ResizeCube(Cube::HyperCube, factor::Real=1.) -> HyperCube
Resizes given `Cube` evenly in all directions but keeps center of mass fixed.
"""
function ResizeCube(Cube::HyperCube, factor::Real=1.)
    @assert factor > 0.
    center = Center(Cube);      halfwidths = (0.5*factor) * CubeWidths(Cube)
    HyperCube(center-halfwidths, center+halfwidths)
end

DomainSamples(Domain::Union{Tuple{Real,Real}, HyperCube}; N::Int=500) = DomainSamples(Domain, N)
DomainSamples(Cube::HyperCube, N::Int) = length(Cube) == 1 ? DomainSamples((Cube.L[1],Cube.U[1]), N) : throw("Domain not suitable.")
function DomainSamples(Domain::Tuple{Real,Real}, N::Int)
    @assert N > 2 && Domain[1] < Domain[2]
    range(Domain[1], Domain[2]; length=N) |> collect
end

import Base.range
function range(C::HyperCube; length::Int=100, kwargs...)
    @assert size(C.L,1) == 1 && length > 1
    range(C.L[1], C.U[1]; length=length, kwargs...)
end

DropCubeDims(Cube::HyperCube, dim::Int) = DropCubeDims(Cube, [dim])
function DropCubeDims(Cube::HyperCube, dims::AbstractVector{<:Int})
    @assert all(dim -> 1 ≤ dim ≤ length(Cube), dims)
    HyperCube(Drop(Cube.L, dims), Drop(Cube.U, dims))
    # keep = trues(length(Cube));     keep[dims] .= false
    # HyperCube(Cube.L[keep], Cube.U[keep])
end

"""
    FaceCenters(Cube::HyperCube) -> Vector{Vector}
Returns a `Vector` of the `2n`-many face centers of a `n`-dimensional `Cube`.
"""
function FaceCenters(Cube::HyperCube)
    C = Center(Cube);   W = 0.5CubeWidths(Cube)
    vcat(map(i->C-W[i]*BasisVector(i,length(C)), 1:length(C)), map(i->C+W[i]*BasisVector(i,length(C)), 1:length(C)))
end

"""
    Corners(C::HyperCube) -> Vector{Vector}
Returns the `2^n` corner points of a `n`-dimensional `HyperCube`.
"""
Corners(C::HyperCube{T}) where T<:Number = Corners([T[]], [(C.L[i], C.U[i]) for i in 1:length(C)])
function Corners(Res::AbstractVector{<:AbstractVector}, C::AbstractVector{<:Tuple})
    length(C) == 0 && return Res
    Tup = popfirst!(C)
    Corners(vcat([append!(copy(r), Tup[1]) for r in Res], [append!(copy(r), Tup[2]) for r in Res]), C)
    # n = length(Res)
    # for r in Res
    #     append!(r, Tup[1])
    # end
    # NewRes = vcat(Res,Res)
    # for i in n+1:2n
    #     NewRes[i][end] = Tup[2]
    # end
    # Corners(NewRes,C)
end

import Base: vcat
vcat(C1::HyperCube, C2::HyperCube) = HyperCube(vcat(C1.L,C2.L), vcat(C1.U,C2.U); Padding=0.0)

import Base: union, intersect
"""
    intersect(A::HyperCube, B::HyperCube) -> HyperCube
    intersect(Cubes::AbstractVector{<:HyperCube}) -> HyperCube
Returns new `HyperCube` which is the intersection of the given `HyperCube`s.
"""
intersect(A::HyperCube, B::HyperCube) = intersect([A, B])
function intersect(Cubes::AbstractVector{<:HyperCube})
    LowerMatrix = [Cubes[i].L for i in 1:length(Cubes)] |> Unpack
    UpperMatrix = [Cubes[i].U for i in 1:length(Cubes)] |> Unpack
    HyperCube([maximum(col) for col in eachcol(LowerMatrix)], [minimum(col) for col in eachcol(UpperMatrix)])
end

"""
    union(A::HyperCube, B::HyperCube) -> HyperCube
    union(Cubes::AbstractVector{<:HyperCube}) -> HyperCube
Returns new `HyperCube` which contains both given `HyperCube`s.
That is, the returned cube is strictly speaking not the union, but a cover (which contains the union).
"""
union(A::HyperCube, B::HyperCube) = union([A, B])
function union(Cubes::AbstractVector{<:HyperCube})
    LowerMatrix = [Cubes[i].L for i in 1:length(Cubes)] |> Unpack
    UpperMatrix = [Cubes[i].U for i in 1:length(Cubes)] |> Unpack
    HyperCube([minimum(col) for col in eachcol(LowerMatrix)], [maximum(col) for col in eachcol(UpperMatrix)])
end

import Base.==
==(A::HyperCube, B::HyperCube) = A.L == B.L && A.U == B.U
==(A::Plane, B::Plane) = A.stütz == B.stütz && A.Vx == B.Vx && A.Vy == B.Vy

PositiveDomain(n::Int, maxval::Real=1e5) = (@assert maxval > 1e-16;     HyperCube(1e-16ones(n), fill(maxval,n)))
PositiveDomain(indxs::BoolVector, maxval::Real=1e5) = (@assert maxval > 1e-16;     HyperCube([(indxs[i] ? 1e-16 : -maxval) for i in eachindex(indxs)], fill(maxval,length(indxs))))
NegativeDomain(n::Int, maxval::Real=1e5) = (@assert maxval > 1e-16;     HyperCube(fill(-maxval,n), -1e-16ones(n)))
NegativeDomain(indxs::BoolVector, maxval::Real=1e5) = (@assert maxval > 1e-16;     HyperCube(fill(-maxval,length(indxs)), [(indxs[i] ? -1e-16 : maxval) for i in eachindex(indxs)]))
FullDomain(n::Int, maxval::Real=1e5) = (@assert maxval > 1e-16;     HyperCube(fill(-maxval,n), fill(maxval,n)))

import Base.rand
rand(C::HyperCube) = rand(C, Val(length(C)))
rand(C::HyperCube, ::Val{1}) = C.L[1] + (C.U[1] - C.L[1])*rand()
function rand(C::HyperCube, ::Val)
    f(l,u) = l + (u-l)*rand()
    map(f,C.L,C.U)
end

import Base.clamp
clamp(x::AbstractVector, C::HyperCube) = clamp(x, C.L, C.U)

import Base: log, exp
log(C::HyperCube) = HyperCube(log.(C.L), log.(C.U))
exp(C::HyperCube) = HyperCube(exp.(C.L), exp.(C.U))

struct EmbeddedODESolution{T,N,uType,uType2,EType,tType,rateType,P,A,IType,DE} <: AbstractODESolution{T,N,uType}
    u::uType
    u_analytic::uType2
    errors::EType
    t::tType
    k::rateType
    prob::P
    alg::A
    interp::IType
    dense::Bool
    tslocation::Int
    destats::DE
    retcode::Symbol
    Embedding::Function
end
(ES::EmbeddedODESolution)(t::Real,deriv::Type=Val{0};idxs=nothing,continuity=:left) = ES.Embedding(ES.interp(t,idxs,deriv,ES.prob.p,continuity))
(ES::EmbeddedODESolution)(Ts::AbstractVector{<:Real},deriv::Type=Val{0};kwargs...) = map(t->ES(t,deriv;kwargs...),Ts)
# Need to use push-forward to embed vectors, i.e. ForwardDiff.jacobian(Embedding, basepoint) * tangentvector
# (ES::EmbeddedODESolution)(v,t,deriv::Type=Val{0};idxs=nothing,continuity=:left) = sol.interp(v,t,idxs,deriv,sol.prob.p,continuity)

function EmbeddedODESolution(u, u_analytic, errors, t, k, prob, alg, interp, dense, tslocation, destats, retcode, Embedding)
    EmbeddedODESolution{typeof(u[1]), length(u[1]), typeof(u), typeof(u_analytic), typeof(errors),
    typeof(t), typeof(k), typeof(prob), typeof(alg), typeof(interp),typeof(destats)}(
    u, u_analytic, errors, t, k, prob, alg, interp, dense, tslocation, destats, retcode, Embedding)
end

"""
    EmbeddedODESolution(sol::AbstractODESolution, Embedding::Function) -> AbstractODESolution
    EmbeddedODESolution(sol::AbstractODESolution, PL::Plane) -> AbstractODESolution
Maps the solution `sol(t)` to some ODE into a larger space via `Embedding∘sol`.
"""
function EmbeddedODESolution(sol::AbstractODESolution{T,N,uType}, Embedding::Function=identity) where {T,N,uType}
    newu = map(Embedding, sol.u);    newk = [map(Embedding, k) for k in sol.k]
    EmbeddedODESolution(newu, isnothing(sol.u_analytic) ? nothing : Embedding∘sol.u_analytic, # Is this translation correct?
                 sol.errors, sol.t, newk, sol.prob, sol.alg,
                 sol.interp, # Leaving old interp object as is and only using embedding on calls of EmbeddedODESolution objects themselves.
                 sol.dense, sol.tslocation, sol.destats, sol.retcode, Embedding)
end
EmbeddedODESolution(sol::AbstractODESolution, PL::Plane) = EmbeddedODESolution(sol, PlaneCoordinates(PL))
function EmbeddedODESolution(sols::AbstractVector{<:AbstractODESolution}, Planes::AbstractVector{<:Plane})
    @assert length(sols) == length(Planes)
    map(EmbeddedODESolution, sols, Planes)
end
EmbeddedODESolution(PL::Union{Plane, AbstractVector{<:Plane}}, sol::Union{AbstractODESolution,AbstractVector{<:AbstractODESolution}}) = EmbeddedODESolution(sol, PL)
EmbeddedODESolution(Embedding::Function, sol::AbstractODESolution) = EmbeddedODESolution(sol, Embedding)


text:
# parallel park
T = Float64

# model
model = TrajectoryOptimization.Dynamics.car
n = model.n; m = model.m
model_d = rk3(model)

# cost
x0 = [0.; 0.; 0.]
xf = [0.; 1.; 0.]
tf =  3.
Qf = 100.0*Diagonal(I,n)
Q = (1e-2)*Diagonal(I,n)
R = (1e-2)*Diagonal(I,m)

# constraints
u_bnd = 2.
x_min = [-0.25; -0.001; -Inf]
x_max = [0.25; 1.001; Inf]
bnd1 = BoundConstraint(n,m,u_min=-u_bnd,u_max=u_bnd)
bnd2 = BoundConstraint(n,m,x_min=x_min,x_max=x_max,u_min=-u_bnd,u_max=u_bnd)

goal = goal_constraint(xf)

# problem
N = 51
dt = 0.06

U = [ones(m) for k = 1:N-1]
obj = LQRObjective(Q,R,Qf,xf,N)

constraints = Constraints(N)
constraints[1] += bnd1
for k = 2:N-1
    constraints[k] += bnd2
end
constraints[N] += goal

parallel_park = Problem(model_d,obj,U,constraints=constraints,dt=dt,x0=x0,xf=xf)

max_issues_repo_name: MehrdadZareian/GMARA

max_forks_repo_name: MehrdadZareian/GMARA


text:
using BSON: BSONDict
"""
	newentry(v)

creates new entry describing json according to the type of v
"""
newentry(::AbstractDict) = DictEntry()
newentry(v::A) where {A<:StringOrNumber} = Entry(v)
newentry(v::AbstractVector) = isempty(v) ? ArrayEntry(nothing) : ArrayEntry(newentry(v[1]))
function newentry!(v)
	c = newentry(v)
	update!(c, v)
	c
end

"""
	schema(samples::AbstractArray{<:Dict})
	schema(samples::AbstractArray{<:AbstractString})
	schema(samples::AbstractArray, map_fun::Function)
	schema(map_fun::Function, samples::AbstractArray)

creates schema from an array of parsed or unparsed JSONs.
"""
function schema(samples::AbstractArray, map_fun::Function)
	schema = DictEntry()
	failed = Vector{Int}()
	for (i, f) in enumerate(samples)
		try
			update!(schema, map_fun(f))
		catch
			push!(failed, i)
		end
	end
	if !isempty(failed)
		println("failed on $(length(failed)) samples")
		l = min(10, length(failed))
		println("for example on samples with indexes$(failed[1:l])")
	end
	schema
end

schema(map_fun::Function, samples::AbstractArray) = schema(samples, map_fun)
schema(samples::AbstractArray{<:Dict}) = schema(samples, identity)
schema(samples::AbstractArray{<:AbstractString}) = schema(samples, JSON.parse)
# BSON integration
schema(samples::AbstractArray{<:BSONDict}) = schema(samples, identity)


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

using Test

# helper function for returning stderr and stdout
# from running a command (ignoring failure status)
function readchomperrors(exename::Cmd)
    out = Base.PipeEndpoint()
    err = Base.PipeEndpoint()
    p = run(exename, devnull, out, err, wait=false)
    o = @async(readchomp(out))
    e = @async(readchomp(err))
    return (success(p), fetch(o), fetch(e))
end

let exename = Base.julia_cmd()
    @show readchomperrors(`$exename -e "f() = f(); f()"`)
    @show readchomperrors(`$exename -e "f() = f(); fetch(@async f())"`)
end

max_stars_repo_name: vanillajonathan/julia

max_issues_repo_name: vanillajonathan/julia

max_forks_repo_name: vanillajonathan/julia


text:
include("custom_graph.jl")

"""

        greedy_d1(G)

Find a coloring of a given input graph such that
no two vertices connected by an edge have the same
color using greedy approach. The number of colors
used may be equal or greater than the chromatic
number χ(G) of the graph.
"""
function greedy_d1(G::CGraph)
    V = num_vertices(G)
    result = zeros(Int64, V)
    result[1] = 1
    available = zeros(Int64, V)
    for i = 2:V
        for j in neighbors(G, i)
            if result[j] != 0
                available[result[j]] = 1
            end
        end
        for cr = 1:V
            if available[cr] == 0
                result[i] = cr
                break
            end
        end
        available = zeros(Int64, V)
    end
    return result
end


text:
# This is the GUI client.

# First start the server
# > julia src/server/main.jl
# and then the client
# > julia src/client/main.jl

# The server contains the simulation.

# -------------------------------------------------------------------------
# Dependencies
# -------------------------------------------------------------------------
# add JSON
# add CSyntax
# add CImGui
include("../server/simulation/simulation.jl")

include("../data/model/model.jl")

include("../client/app/handlers.jl")

include("app/socket.jl")

include("app/app.jl")


text:
#####################################################
### Project of IN480 - From Python 2.7 to Julia 0.6
### --- SIMPLEXN - serial version (everywhere) ---
### Authors: Fabio Fatelli, Emanuele Loprevite
#####################################################

# Generation of the output model vertices in a multiple extrusion of a LAR model
@everywhere function larExtrude1{T<:Real}(model::Tuple{Array{Array{T,1},1},Array{Array{Int64,1},1}},pattern::Array{Int64,1})
	V, FV = model
	d, m = length(FV[1]), length(pattern)
	coords = cumsum(append!([0],abs.(pattern))) # built-in function cumsum
	offset, outcells, rangelimit = length(V), Array{Int64}(m,0), d*m
	for cell in FV
		tube = [v+k*offset for k in 0:m for v in cell]
		celltube = Int64[]
		for k in 1:rangelimit
			append!(celltube,tube[k:k+d])
		end
		outcells = hcat(outcells,reshape(celltube,d*(d+1),m)')
	end
	cellGroups = Int64[]
	for k in 1:m
		if pattern[k]>0
			cellGroups = vcat(cellGroups,outcells[k,:])
		end
	end
	outVertices = [vcat(v,z) for z in coords for v in V]
	outCellGroups = Array{Int64,1}[]
	for k in 1:d+1:length(cellGroups)
		append!(outCellGroups,[cellGroups[k:k+d]])
	end
	return outVertices, outCellGroups
end

# Generation of simplicial grids of any dimension and shape
@everywhere function larSimplexGrid1(shape::Array{Int64,1})
	model = [Int64[]],[[0]] # the empty simplicial model
	for item in shape
		model = larExtrude1(model,repmat([1],item))
	end
	return model
end

# Extraction of non-oriented (d-1)-facets of d-dimensional simplices
@everywhere using Combinatorics # for the function combinations()

@everywhere function larSimplexFacets(simplices::Array{Array{Int64,1},1})
	out = Array{Int64,1}[]
	d = length(simplices[1])
	for simplex in simplices
		append!(out,collect(combinations(simplex,d-1)))
	end
	return sort!(unique(out),lt=lexless)
end

# Transformation to triangles by sorting circularly the vertices of faces
@everywhere function quads2tria{T<:Real}(model::Tuple{Array{Array{T,1},1},Array{Array{Int64,1},1}})
	V, FV = model
	if typeof(V) != Array{Array{Float64,1},1}
		V = convert(Array{Array{Float64,1},1},V)
	end
	out = Array{Int64,1}[]
	nverts = length(V)-1
	for face in FV
		arr = [V[v+1] for v in face]
		centroid = sum(arr)/length(arr)
		append!(V,[centroid])
		nverts += 1
		v1, v2 = V[face[1]+1]-centroid, V[face[2]+1]-centroid
		v3 = cross(v1,v2)
		if norm(v3) < 1/(10^3)
			v1, v2 = V[face[1]+1]-centroid, V[face[3]+1]-centroid
			v3 = cross(v1,v2)
		end
		transf = inv(hcat(v1,v2,v3)')
		verts = [(V[v+1]'*transf)'[1:end-1] for v in face]
		tcentroid = sum(verts)/length(verts)
		tverts = [v-tcentroid for v in verts]
		iterator = collect(zip(tverts,face))
		rverts = [[atan2(reverse(iterator[i][1])...),iterator[i][2]] for i in 1:length(iterator)]
		rvertsS = sort(rverts,lt=(x,y)->isless(x[1],y[1]))
		ord = [pair[2] for pair in rvertsS]
		append!(ord,ord[1])
		edges = [[i[2],ord[i[1]+1]] for i in enumerate(ord[1:end-1])]
		triangles = [prepend!(edge,nverts) for edge in edges]
		append!(out,triangles)
	end
	return V, out
end

max_stars_repo_name: EmaLoprevite/Simplexn

max_issues_repo_name: EmaLoprevite/Simplexn

max_forks_repo_name: EmaLoprevite/Simplexn


text:
#### Main file ####
# Use this file to run the scripts all at once with conditional arguments
# Useful to update results & figures
# BUT be careful as some steps might take a while

# Load required packages & scripts
include("required.jl")

## Part I - Data preparation ####

# Set conditional arguments used in scripts
save_prepdata = true # should preparation data be overwritten (optional)
save_figures = true # should figures be overwritten (optional)

# 0a. EBD data extraction (if needed) (possibly very long)
@info "Running 00a_ebd_extraction.R"
@time run("Rscript src/00a_ebd_extraction.R")

# 0b. EBD data preparation (if needed)
@info "Running 00b_ebd_preparation.jl"
@time include("00b_ebd_preparation.jl")

# 0c. Landcover data preparation (if needed)
@info "Running 00c_landcover.jl"
@time include("00c_landcover.jl")

## Part II - Distributions & modelling ####

# Set conditional arguments
outcome = "raw" # desired outcome (required)
create_distributions = true # should distributions be computed (optional, loaded otherwise)
save_data = true # should data files be overwritten (optional)
save_figures = true # should figures be overwritten (optional)

# 1. Generate presence-absence data & single species map
@info "Running 01_distributions.jl"
@time include("01_distributions.jl")

# 2. Train BART models
@info "Running 02_training_bart.R"
@time run("Rscript src/02_training_bart.R")

# 3. Get BART predictions
@info "Running 03_predictions_bart.jl"
@time include("03_predictions_bart.jl")

## Part III - Main analyses ####

# Set conditional arguments
outcome = "raw" # desired outcome (required), choose either raw or bart
outcome = "bart"
save_figures = true # should figures be overwritten (optional)

# 4. Run main analyses (Y-matrix, richness, LCBD, relationship)
@info "Running 04_full-extent.jl"
@time include("04_full-extent.jl")

# 5. Run subareas analyses
@info "Running 05_subareas.jl"
@time include("05_subareas.jl")

## Part IV - Additional analyses ####

# These analyses will re-run the previous scripts.
# Better to reset the conditional arguments to avoid overwriting things
outcome = nothing
save_figures = nothing
save_data = nothing

# Set conditional arguments for additional analyses scripts
save_additional_figures = true
save_additional_data = true

# 6. Run rare species analyses
# (This script needs one of the two outcomes)
outcome = "raw"
outcome = "bart"
@info "Running 06_rare-species.jl"
@time include("06_rare-species.jl")

# Better to reset the outcome after this script
outcome = nothing

# 7-8-9. Run comparison analyses (between raw data & predictions)
# (These scripts don't need to set the outcome)

# 7. Prepare data
@info "Running 07_comparison_data.jl"
@time include("07_comparison_data.jl")

# 8. Run GLMs
@info "Running 08_comparison_glm.R"
@time run(`Rscript src/08_comparison_glm.R`) # this script might overflow memory

# 9. Compare results
@info "Running 09_comparison_plots.jl"
@time include("09_comparison_plots.jl")

# Better to reset the outcome after these scripts
outcome = nothing

max_stars_repo_name: gabrieldansereau/betadiversity-hotspots

max_issues_repo_name: gabrieldansereau/betadiversity-hotspots

max_forks_repo_name: gabrieldansereau/betadiversity-hotspots


text:
export beamsearch, max_violation!

"""
    beamsearch(initstate, beamsize, expand)

Requirements of state:
* member: score
* member: step
"""
function beamsearch{T}(initstate::T, beamsize::Int, expand::Function)
    chart = Vector{T}[]
    push!(chart, [initstate])
    lessthan{T}(x::T, y::T) = x.score > y.score

    k = 1
    while k <= length(chart)
        states = chart[k]
        length(states) > beamsize && sort!(states, lt=lessthan)
        for i = 1:min(beamsize, length(states))
            for s in expand(states[i])
                while s.step > length(chart)
                    push!(chart, T[])
                end
                push!(chart[s.step], s)
            end
        end
        k += 1
    end
    sort!(chart[end], lt=lessthan)
    chart
end

function to_seq{T}(finalstate::T)
    seq = T[]
    s = finalstate
    # while s != nothing
    while s.step > 1
        unshift!(seq, s)
        s = s.prev
    end
    unshift!(seq, s)
    seq
end

"""
    max_violation!

Ref: L. Huang et al, "Structured Perceptron with Inexact Seatch", ACL 2012.
"""
function max_violation!{T}(gold::T, pred::T, train_gold, train_pred)
    goldseq, predseq = to_seq(gold), to_seq(pred)
    maxk, maxv = 1, 0.0
    for k = 1:length(goldseq)
        v = predseq[k].score - goldseq[k].score
        if k == 1 || v >= maxv
            maxk = k
            maxv = v
        end
    end
    for k = 2:maxk
        train_gold(goldseq[k])
        train_pred(predseq[k])
    end
end

#=
st_lessthan(x::State, y::State) = y.score < x.score

function beamsearch(beamsize::Int, initstate::State, expand::Function)
    chart = Vector{State}[[initstate]]
    i = 1
    while i <= length(chart)
        states = chart[i]
        length(states) > beamsize && sort!(states, lt=st_lessthan)
        for j = 1:min(beamsize, length(states))
            for s in expand(states[j])
                while s.step > length(chart) push!(chart, []) end
                push!(chart[s.step], s)
            end
        end
        i += 1
    end
    sort!(chart[end], lt=st_lessthan)
    chart[end][1]
end

function state2array(s::State)
    res = Vector{State}(s.step)
    st = s
    while st.step > 1
        res[st.step] = st
        st = st.prev
    end
    res[st.step] = st
    res
end

function maxviolate!(gold::State, pred::State)
    golds = state2array(gold)
    preds = state2array(pred)
    maxv  = typemin(Float); maxk = 1
    for k = 2:min(length(golds), length(preds))
        v = preds[k].score - golds[k].score
        if v >= maxv
            maxv, maxk = v, k
        end
    end
    for i = 2:maxk
        traingold!(model, golds[i])
        trainpred!(model, preds[i])
    end
end
=#


text:
#=
------------------------
STO-3g Basis function  -
------------------------
The STO-3G basis is a very well-known minimal basis set which contracts 3 Gaussian functions to approximate the more accurate (but more difficult to compute) Slater type orbitals.
=#

# Builds a STO-3G basis that aproximates a single slater type orbital  with Slater orbital exponent zeta
function STO3G(center, zeta)
  scaling = zeta^2
  sto = STONG(3,[0.444635, 0.535328, 0.154329],
	      [Gaussian1s(scaling*.109818, center),
               Gaussian1s(scaling*.405771, center),
               Gaussian1s(scaling*2.22766, center)])
  return sto
end

# procurar ver de onde sairam of parâmetros de contração


text:
struct DecayingShearFlow <: FluidFlowProblem
    rho_0::Float64
    u_max::Float64
    ν::Float64
    NX::Int64
    NY::Int64
    domain_size::Tuple{Float64, Float64}
    static::Bool
    A::Float64
    B::Float64
    k_x::Float64
    k_y::Float64
end
function DecayingShearFlow(
    ν_lb = 1.0 / 6.0,
    scale = 2,
    NX = 8 * scale,
    NY = NX,
    domain_size = (2pi, 2pi);
    static = true,
    A = 1.0,
    B = 1.0,
    k_x = 1.0,
    k_y = 0.0,
)
    u_max = 0.02 / scale
    Re = NX * u_max / ν_lb

    if (k_y == 0.0)
        NY = 3
    end
    if (k_x == 0.0)
        NX = 3
    end

    return DecayingShearFlow(1.0, u_max, ν_lb, NX, NY, domain_size, static, A, B, k_x, k_y)
end

function density(
    q::Quadrature,
    problem::DecayingShearFlow,
    x::Float64,
    y::Float64,
    time::Float64 = 0.0,
)
    return 1.0
    return pressure(q, problem, x, y, time)
end

function pressure(
    q::Quadrature,
    problem::DecayingShearFlow,
    x::Float64,
    y::Float64,
    time::Float64 = 0.0,
)
    # return 1.0
    A = problem.A
    B = problem.B

    k_y = problem.k_y
    k_x = problem.k_x

    p =
        1 +
        problem.B *
        (0.025) *
        q.speed_of_sound_squared *
        problem.u_max^2 *
        B *
        sin(k_x * (x - A * time))^2 *
        decay(problem, x, y, time)^2
    return p
end

function velocity(problem::DecayingShearFlow, x::Float64, y::Float64, time::Float64 = 0.0)
    A = problem.A
    B = problem.B

    k_y = problem.k_y
    k_x = problem.k_x
    if problem.static
        return [
            A * cos(k_y * y - k_y * B * time)
            B * cos(k_x * x - k_x * A * time)
        ]
    end

    return [
        A * cos(k_y * y - k_y * B * time) * exp(-1.0 * k_y^2 * viscosity(problem) * time)
        B * cos(k_x * x - k_x * A * time) * exp(-1.0 * k_x^2 * viscosity(problem) * time)
    ]
end
function velocity_gradient(
    problem::DecayingShearFlow,
    x::Float64,
    y::Float64,
    time::Float64 = 0.0,
)
    A = problem.A
    B = problem.B
    k_y = problem.k_y
    k_x = problem.k_x

    u_x = 0.0
    u_y = -A * k_y * sin(k_y * (y - B * time))
    v_x = -B * k_x * sin(k_x * (x - A * time))
    v_y = 0.0

    if !problem.static
        u_y *= exp(-1.0 * k_y^2 * viscosity(problem) * time)
        v_x *= exp(-1.0 * k_x^2 * viscosity(problem) * time)
    end

    return [u_x v_x; u_y v_y]
end

function decay(problem::DecayingShearFlow, x::Float64, y::Float64, time::Float64)
    if (problem.static)
        return 1.0
    end

    A = problem.A
    B = problem.B

    k_y = problem.k_y
    k_x = problem.k_x

    return exp(-1.0 * k_x^2 * viscosity(problem) * time)
end

function force(problem::DecayingShearFlow, x::Float64, y::Float64, time::Float64 = 0.0)
    if !problem.static
        return [0.0 0.0]
    end

    A = problem.A
    B = problem.B

    k_y = problem.k_y
    k_x = problem.k_x

    return [
        viscosity(problem) * k_y^2 * A * cos(k_y * y - k_y * B * time),
        viscosity(problem) * k_x^2 * B * cos(k_x * x - k_x * A * time),
    ]
end

has_external_force(problem::DecayingShearFlow) = problem.static


text:
@inline CI(a...) = CartesianIndex(a...)
"""
    δ(i,N::Int)
    δ(i,I::CartesianIndex{N}) where {N}

Return a CartesianIndex of dimension `N` which is one at index `i` and zero elsewhere.
"""
@inline δ(i,N::Int) = CI(ntuple(j -> j==i ? 1 : 0, N))
@inline δ(i,I::CartesianIndex{N}) where {N} = δ(i,N)

"""
    inside(dims)
    inside(a) = inside(size(a))

Return CartesianIndices range excluding the ghost-cells on the boundaries of
a _scalar_ array `a` with `dims=size(a)`.
"""
@inline inside(dims::NTuple{N}) where {N} = CartesianIndices(ntuple(i-> 2:dims[i]-1,N))
@inline inside(a::AbstractArray) = inside(size(a))

"""
    inside_u(dims,j)

Return CartesianIndices range excluding the ghost-cells on the boundaries of
a _vector_ array on face `j` with size `dims`.
"""
function inside_u(dims::NTuple{N},j) where {N}
    CartesianIndices(ntuple( i-> i==j ? (3:dims[i]-1) : (2:dims[i]), N))
end
splitn(n) = Base.front(n),n[end]
size_u(u) = splitn(size(u))

"""
    L₂(a)

L₂ norm of array `a` excluding ghosts.
"""
L₂(a) = sum(@inbounds(abs2(a[I])) for I ∈ inside(a))

"""
    @inside <expr>

Simple macro to automate efficient loops over cells excluding ghosts. For example

    @inside p[I] = sum(I.I)

becomes

    @inbounds @simd for I ∈ inside(p)
        p[I] = sum(I.I)
    end
"""
macro inside(ex)
    a,I = Meta.parse.(split(string(ex.args[1]),union("[",",","]")))
    return quote
        WaterLily.@loop $ex over $I ∈ inside($a)
    end |> esc
end

macro loop(args...)
    ex,_,itr = args
    op,I,R = itr.args
    @assert op ∈ (:(∈),:(in))

    if Threads.nthreads()!=1    #multithread mode
        return quote
            @inbounds Threads.@threads for $I ∈ $R
                $ex
            end
        end |> esc
    else
        return quote            #single thread mode
            @inbounds @simd for $I ∈ $R
                $ex
            end
        end |> esc
    end
end

function median(a,b,c)
    if a>b
        b>=c && return b
        a>c && return c
    else
        b<=c && return b
        a<c && return c
    end
    return a
end

using StaticArrays
"""
    loc(i,I)

Location in space of the cell at CartesianIndex `I` at face `i`.
Using `i=0` returns the cell center s.t. `loc = I`.
"""
@inline loc(i,I) = SVector(I.I .- 0.5 .* δ(i,I).I)

"""
    apply!(f, c)

Apply a vector function `f(i,x)` to the faces of a uniform staggered array `c`.
"""
function apply!(f,c)
    N,n = size_u(c)
    for i ∈ 1:n
        @loop c[I,i] = f(i,loc(i,I)) over I ∈ CartesianIndices(N)
    end
end

"""
    slice(dims,i,j,low=1)

Return `CartesianIndices` range slicing through an array of size `dims` in
dimension `j` at index `i`. `low` optionally sets the lower extent of the range
in the other dimensions.
"""
function slice(dims::NTuple{N},i,j,low=1) where N
    CartesianIndices(ntuple( k-> k==j ? (i:i) : (low:dims[k]), N))
end

"""
    BC!(a,A,f=1)

Apply boundary conditions to the ghost cells of a _vector_ field. A Dirichlet
condition `a[I,i]=f*A[i]` is applied to the vector component _normal_ to the domain
boundary. For example `aₓ(x)=f*Aₓ ∀ x ∈ minmax(X)`. A zero Nuemann condition
is applied to the tangential components.
"""
function BC!(a,A,f=1)
    N,n = size_u(a)
    for j ∈ 1:n, i ∈ 1:n
        if i==j # Normal direction, Dirichlet
            for s ∈ (1,2,N[j])
                @loop a[I,i] = f*A[i] over I ∈ slice(N,s,j)
            end
        else    # Tangential directions, Neumann
            @loop a[I,i] = a[I+δ(j,I),i] over I ∈ slice(N,1,j)
            @loop a[I,i] = a[I-δ(j,I),i] over I ∈ slice(N,N[j],j)
        end
    end
end

"""
    BC!(a)

Apply zero Nuemann boundary conditions to the ghost cells of a _scalar_ field.
"""
function BC!(a)
    N = size(a)
    for j ∈ 1:length(N)
        @loop a[I] = a[I+δ(j,I)] over I ∈ slice(N,1,j)
        @loop a[I] = a[I-δ(j,I)] over I ∈ slice(N,N[j],j)
    end
end


text:
macro enforce(expr) # strong @assert
    msg = string(expr)
    return :($(esc(expr)) ? $(nothing) : throw(AssertionError($msg)))
end

function _precompile_()
    ccall(:jl_generating_output, Cint, ()) == 1 || return nothing

    @enforce Base.precompile(Tuple{Type{PeriodicGraph},String})
    @enforce Base.precompile(Tuple{typeof(PeriodicGraphs.extended_gcd),Vector{Int}})

    for i in 0:3
        @enforce Base.precompile(Tuple{Type{PeriodicEdge{i}},Int,Int,NTuple{i, Int}})
        @enforce Base.precompile(Tuple{Type{PeriodicVertex{i}},Int})

        @enforce Base.precompile(Tuple{Type{PeriodicEdge},Int,Int,NTuple{i, Int}})
        @enforce Base.precompile(Tuple{Type{PeriodicEdge},Int,Int,SVector{i, Int}})
        @enforce Base.precompile(Tuple{Type{PeriodicEdge},Int,PeriodicVertex{i}})
        @enforce Base.precompile(Tuple{Type{PeriodicEdge},Tuple{Int,Int,NTuple{i, Int}}})

        @enforce Base.precompile(Tuple{Type{PeriodicGraph{i}}})
        @enforce Base.precompile(Tuple{Type{PeriodicGraph{i}},Vector{PeriodicEdge{i}}})
        @enforce Base.precompile(Tuple{Type{PeriodicGraph{i}},String})
        @enforce Base.precompile(Tuple{Type{PeriodicGraph},PeriodicGraph{i}})

        @enforce Base.precompile(Tuple{typeof(==),PeriodicVertex{i},PeriodicVertex{i}})
        @enforce Base.precompile(Tuple{typeof(<),PeriodicVertex{i},PeriodicVertex{i}})
        @enforce Base.precompile(Tuple{typeof(isless),PeriodicVertex{i},PeriodicVertex{i}})
        @enforce Base.precompile(Tuple{typeof(==),PeriodicEdge{i},PeriodicEdge{i}})
        @enforce Base.precompile(Tuple{typeof(<),PeriodicEdge{i},PeriodicEdge{i}})
        @enforce Base.precompile(Tuple{typeof(==),PeriodicGraph{i},PeriodicGraph{i}})

        @enforce Base.precompile(Tuple{typeof(Graphs._neighborhood),PeriodicGraph{i},Int,Int,Graphs.DefaultDistance,Function})
        @enforce Base.precompile(Tuple{typeof(Graphs.add_edge!),PeriodicGraph{i},PeriodicEdge{i}})
        @enforce Base.precompile(Tuple{typeof(add_vertex!),PeriodicGraph{i}})
        @enforce Base.precompile(Tuple{typeof(Graphs.add_vertices!),PeriodicGraph{i},Int})
        @enforce Base.precompile(Tuple{typeof(Graphs.rem_edge!),PeriodicGraph{i},PeriodicEdge{i}})
        @enforce Base.precompile(Tuple{typeof(Graphs.rem_vertex!),PeriodicGraph{i},Int})
        @enforce Base.precompile(Tuple{typeof(collect),PeriodicGraphs.PeriodicEdgeIter{i}})
        @enforce Base.precompile(Tuple{typeof(Graphs.connected_components),PeriodicGraph{i}})
        @enforce Base.precompile(Tuple{typeof(coordination_sequence),PeriodicGraph{i},Int,Int})
        @enforce Base.precompile(Tuple{typeof(dimensionality),PeriodicGraph{i}})
        @enforce Base.precompile(Tuple{typeof(find_edges),PeriodicGraph{i},Int,Int})
        @enforce Base.precompile(Tuple{typeof(Graphs.has_edge),PeriodicGraph{i},Int,Int})
        @enforce Base.precompile(Tuple{typeof(Graphs.has_edge),PeriodicGraph{i},PeriodicEdge{i}})
        @enforce Base.precompile(Tuple{typeof(Graphs.induced_subgraph),PeriodicGraph{i},Vector{Int}})
        @enforce Base.precompile(Tuple{typeof(isempty),PeriodicGraphs.PeriodicEdgeIter{i}})
        @enforce Base.precompile(Tuple{typeof(iterate),PeriodicEdgeIter{i}})
        @enforce Base.precompile(Tuple{typeof(Graphs.neighborhood),PeriodicGraph{i},Int,Int})
        @enforce Base.precompile(Tuple{typeof(offset_representatives!),PeriodicGraph{i},Vector{Any}})
        @enforce Base.precompile(Tuple{typeof(offset_representatives!),PeriodicGraph{i},Vector{Int}})
        @enforce Base.precompile(Tuple{typeof(offset_representatives!),PeriodicGraph{i},Vector{NTuple{i,Int}}})
        @enforce Base.precompile(Tuple{typeof(Graphs.reverse),PeriodicEdge{i}})
        @enforce Base.precompile(Tuple{typeof(Base.string),PeriodicEdge{i}})
        @enforce Base.precompile(Tuple{typeof(Base.string),PeriodicGraph{i}})
        @enforce Base.precompile(Tuple{typeof(Base.string),PeriodicVertex{i}})
        @enforce Base.precompile(Tuple{typeof(swap_axes!),PeriodicGraph{i},Vector{Int}})
        @enforce Base.precompile(Tuple{typeof(swap_axes!),PeriodicGraph{i},NTuple{i,Int}})
        @enforce Base.precompile(Tuple{typeof(vertex_permutation),PeriodicGraph{i},Vector{Any}})
        @enforce Base.precompile(Tuple{typeof(periodiccellgraph),PeriodicGraph{i}})
        @enforce Base.precompile(Tuple{typeof(PeriodicGraphs.hash_position),SVector{i,Int}})

        for j in 0:3
            @enforce Base.precompile(Tuple{typeof(PeriodicGraphs.change_dimension),Type{PeriodicGraph{i}},PeriodicGraph{j}})
        end
    end
end

_precompile_()


text:
@testset "properties" begin

@testset "topology" begin
    cubane = smilestomol("C12C3C4C1C1C4C3C12")
    @test iterate(atom_sssrsizes(cubane)[1])[1] == 4
    @test iterate(bond_sssrsizes(cubane)[8])[1] == 4
    @test count(x->x==2, atom_sssrcount(cubane)) == 4
    @test count(x->x==2, bond_sssrcount(cubane)) == 8
    biphenyl = smilestomol("C1CCCCC1C1CCCCC1")
    @test isringatom(biphenyl)[6]
    @test isringatom(biphenyl)[7]
    @test !isringbond(biphenyl)[7]
end

@testset "elemental" begin
    alkyne = smilestomol("CC=CC#C")
    @test pielectron(alkyne) == [0, 1, 1, 2, 2]
    pyrrole = smilestomol("C1=CC=CN1")
    @test pielectron(pyrrole) == [1, 1, 1, 1, 0]
    azide = smilestomol("CC(=O)N=N=N")
    @test pielectron(azide) == [0, 1, 1, 1, 2, 1]
    amide = smilestomol("CCC(=O)N")
    @test ishacceptor(amide) == [0, 0, 0, 1, 1]
    @test ishdonor(amide) == [0, 0, 0, 0, 1]
    fluoro = smilestomol("CCN(CO)CF")
    @test ishacceptor(fluoro) == [0, 0, 1, 0, 1, 0, 1]
    @test ishdonor(fluoro) == [0, 0, 0, 0, 1, 0, 0]
end

@testset "rotatable" begin
    Phe = smilestomol("N[C@@H](CC1=CC=CC=C1)C(O)=O")
    @test rotatablecount(Phe) == 3
    KCl = smilestomol("[K+].[Cl-]")
    @test rotatablecount(KCl) == 0
    dipyridamole = smilestomol(
        "n3c(nc2c(nc(nc2N1CCCCC1)N(CCO)CCO)c3N4CCCCC4)N(CCO)CCO")
    @test rotatablecount(dipyridamole) == 12
    paclitaxel = smilestomol(
        "CC1=C2[C@@]([C@]([C@H]([C@@H]3[C@]4([C@H](OC4)C[C@@H]([C@]3(C(=O)[C@@H]2OC(=O)C)C)O)OC(=O)C)OC(=O)c5ccccc5)(C[C@@H]1OC(=O)[C@H](O)[C@@H](NC(=O)c6ccccc6)c7ccccc7)O)(C)C"
    )
    @test rotatablecount(paclitaxel) == 15
end

@testset "aromatic" begin
    phenol = smilestomol("C=1C=CC=CC=1O")  # not carbonyl O
    @test count(isaromatic(phenol)) == 6

    furan = smilestomol("o1cccc1")
    @test count(isaromatic(furan)) == 5

    quinone = smilestomol("C1(=O)C=CC(=O)C=C1")
    @test count(isaromatic(quinone)) == 0

    tropone = smilestomol("C1(=O)C=CC=CC=C1")
    @test count(isaromatic(tropone)) == 7

    azepine = smilestomol("N1C=CC=CC=C1")
    @test count(isaromatic(azepine)) == 0

    thiopheneoxide = smilestomol("C1=CC=CS1=O")
    @test count(isaromatic(thiopheneoxide)) == 0

    dihydroazaborine = smilestomol("C1=CC=CBN1")
    @test count(isaromatic(dihydroazaborine)) == 6

    fulvene = smilestomol("C1=CC=CC1=C")
    @test count(isaromatic(fulvene)) == 0

    cyclopropenyl = smilestomol("C1=C[C+]1")
    @test count(isaromatic(cyclopropenyl)) == 3

    hexatriene = smilestomol("C=CC=CC=C")
    @test count(isaromatic(hexatriene)) == 0

    borazine = smilestomol("B1NBNBN1")
    @test count(isaromatic(borazine)) == 6

    methylenedioxybenzene = smilestomol("C1=CC=CC2=C1OCO2")
    @test count(isaromatic(methylenedioxybenzene)) == 6

    naphthalene = smilestomol("C=1C=CC=C2C1C=CC=C2")
    @test count(isaromatic(naphthalene)) == 10

    coumarin = smilestomol("C1=CC(=O)OC2=C1C=CC=C2")
    @test count(isaromatic(coumarin)) == 10

    pyrene = smilestomol("C12=CC=C3C=CC=C4C=CC(C2=C34)=CC=C1")
    @test count(isaromatic(pyrene)) == 16

    caffeine = smilestomol("CN1C=NC2=C1C(=O)N(C)C(=O)N2C")
    @test count(isaromatic(caffeine)) == 9

    # Difficult aromaticities
    quinodimethane = smilestomol("C1=CC=CC(=C)C1=C")
    @test_broken count(isaromatic(azulene)) == 10

    dihydronaphthalene = smilestomol("C=1CCC=C2C1C=CC=C2")
    @test_broken count(isaromatic(dihydronaphthalene)) == 10

    azulene = smilestomol("C=1C=CC=2C1C=CC=CC2")
    @test_broken count(isaromatic(azulene)) == 10

    pyromellitimide = smilestomol("C1(=O)NC(=O)C=2C1=CC=3C(=O)NC(=O)C=3C=2")
    @test count(isaromatic(pyromellitimide)) == 6
end

end # properties


text:
# ------------------------------------------------------------------------------------------
# # Sequence classification model for IMDB Sentiment Analysis
# (c) Deniz Yuret, 2019
# * Objectives: Learn the structure of the IMDB dataset and train a simple RNN model.
# * Prerequisites: [RNN models](60.rnn.ipynb)
# ------------------------------------------------------------------------------------------

# Set display width, load packages, import symbols
ENV["COLUMNS"] = 72
using Pkg; haskey(Pkg.installed(),"Knet") || Pkg.add("Knet")
using Statistics: mean
using Knet: Knet, AutoGrad, RNN, param, dropout, minibatch, nll, accuracy, progress!, adam, save, load, gc

# Set constants for the model and training
EPOCHS=3          # Number of training epochs
BATCHSIZE=64      # Number of instances in a minibatch
EMBEDSIZE=125     # Word embedding size
NUMHIDDEN=100     # Hidden layer size
MAXLEN=150        # maximum size of the word sequence, pad shorter sequences, truncate longer ones
VOCABSIZE=30000   # maximum vocabulary size, keep the most frequent 30K, map the rest to UNK token
NUMCLASS=2        # number of output classes
DROPOUT=0.5       # Dropout rate
LR=0.001          # Learning rate
BETA_1=0.9        # Adam optimization parameter
BETA_2=0.999      # Adam optimization parameter
EPS=1e-08         # Adam optimization parameter

# ------------------------------------------------------------------------------------------
# ## Load and view data
# ------------------------------------------------------------------------------------------

include(Knet.dir("data","imdb.jl"))   # defines imdb loader

@doc imdb

@time (xtrn,ytrn,xtst,ytst,imdbdict)=imdb(maxlen=MAXLEN,maxval=VOCABSIZE);

println.(summary.((xtrn,ytrn,xtst,ytst,imdbdict)));

# Words are encoded with integers
rand(xtrn)'

# Each word sequence is padded or truncated to length 150
length.(xtrn)'

# Define a function that can print the actual words:
imdbvocab = Array{String}(undef,length(imdbdict))
for (k,v) in imdbdict; imdbvocab[v]=k; end
imdbvocab[VOCABSIZE-2:VOCABSIZE] = ["<unk>","<s>","<pad>"]
function reviewstring(x,y=0)
    x = x[x.!=VOCABSIZE] # remove pads
    """$(("Sample","Negative","Positive")[y+1]) review:\n$(join(imdbvocab[x]," "))"""
end

# Hit Ctrl-Enter to see random reviews:
r = rand(1:length(xtrn))
println(reviewstring(xtrn[r],ytrn[r]))

# Here are the labels: 1=negative, 2=positive
ytrn'

# ------------------------------------------------------------------------------------------
# ## Define the model
# ------------------------------------------------------------------------------------------

struct SequenceClassifier; input; rnn; output; pdrop; end

SequenceClassifier(input::Int, embed::Int, hidden::Int, output::Int; pdrop=0) =
    SequenceClassifier(param(embed,input), RNN(embed,hidden,rnnType=:gru), param(output,hidden), pdrop)

function (sc::SequenceClassifier)(input)
    embed = sc.input[:, permutedims(hcat(input...))]
    embed = dropout(embed,sc.pdrop)
    hidden = sc.rnn(embed)
    hidden = dropout(hidden,sc.pdrop)
    return sc.output * hidden[:,:,end]
end

(sc::SequenceClassifier)(input,output) = nll(sc(input),output)

# ------------------------------------------------------------------------------------------
# ## Experiment
# ------------------------------------------------------------------------------------------

dtrn = minibatch(xtrn,ytrn,BATCHSIZE;shuffle=true)
dtst = minibatch(xtst,ytst,BATCHSIZE)
length.((dtrn,dtst))

# For running experiments
function trainresults(file,maker; o...)
    if (print("Train from scratch? "); readline()[1]=='y')
        model = maker()
        progress!(adam(model,repeat(dtrn,EPOCHS);lr=LR,beta1=BETA_1,beta2=BETA_2,eps=EPS))
        Knet.save(file,"model",model)
        Knet.gc() # To save gpu memory
    else
        isfile(file) || download("http://people.csail.mit.edu/deniz/models/tutorial/$file",file)
        model = Knet.load(file,"model")
    end
    return model
end

maker() = SequenceClassifier(VOCABSIZE,EMBEDSIZE,NUMHIDDEN,NUMCLASS,pdrop=DROPOUT)
# model = maker()
# nll(model,dtrn), nll(model,dtst), accuracy(model,dtrn), accuracy(model,dtst)
# (0.69312066f0, 0.69312423f0, 0.5135817307692307, 0.5096153846153846)

# 2.51e-01  100.00%┣████████████████████┫ 1170/1170 [00:16/00:16, 75.46i/s]
model = trainresults("imdbmodel113.jld2",maker);

#nll(model,dtrn), nll(model,dtst), accuracy(model,dtrn), accuracy(model,dtst)
# (0.059155148f0, 0.3877507f0, 0.9846153846153847, 0.8583733974358975)

# ------------------------------------------------------------------------------------------
# ## Playground
# ------------------------------------------------------------------------------------------

predictstring(x)="\nPrediction: " * ("Negative","Positive")[argmax(Array(vec(model([x]))))]
UNK = VOCABSIZE-2
str2ids(s::String)=[(i=get(imdbdict,w,UNK); i>=UNK ? UNK : i) for w in split(lowercase(s))]

# Here we can see predictions for random reviews from the test set; hit Ctrl-Enter to sample:
r = rand(1:length(xtst))
println(reviewstring(xtst[r],ytst[r]))
println(predictstring(xtst[r]))

# Here the user can enter their own reviews and classify them:
println(predictstring(str2ids(readline(stdin))))

max_stars_repo_name: grenkoca/JuliaTutorials

max_issues_repo_name: grenkoca/JuliaTutorials

max_forks_repo_name: grenkoca/JuliaTutorials


text:
using HDF5
using Convex
using Mosek, MosekTools

include("../verification/ai2zPQ.jl");

function dist_to_zonotope_p(reach, point; p = 2)
    G = reach.generators
    c = reach.center
    n, m = size(G)
    x = Variable(m)
    obj = norm(G * x + c - point, p)
    prob = minimize(obj, [x <= 1.0, x >= -1.0])
    solve!(prob, Mosek.Optimizer(LOG=0, MAX_NUM_WARNINGS=0))
    #@assert prob.status == OPTIMAL "Solve must result in optimal status"
    return prob.optval
end

function get_minimum_radius(network, lbs, ubs, y₀; p = 2)
    evaluate_objective(network, x) = -norm(y₀ - NeuralVerification.compute_output(network, x), p)
    optimize_reach(reach) = -dist_to_zonotope_p(reach, y₀; p=p)
    return priority_optimization(network, lbs, ubs, optimize_reach, evaluate_objective)
end

function get_minimum_radii(network, real_images, labels; state_eps = 1e-5, p = 2)
    n = size(real_images, 2)
    xs = zeros(4, n)
    unders = zeros(n)
    overs = zeros(n)

    for i = 1:n
        i % 10 == 0 ? println(i) : nothing

        lbs = [-0.8, -0.8, labels[1, i] / 6.366468343804353 - state_eps,
                                    labels[2, i] / 17.248858791583547 - state_eps]
        ubs = [0.8, 0.8, labels[1, i] / 6.366468343804353 + state_eps,
                                    labels[2, i] / 17.248858791583547 + state_eps]

        y₀ = (real_images[:, i] .* 2) .- 1;

        x, under, over = get_minimum_radius(network, lbs, ubs, y₀, p = p)
        xs[:, i] = x
        unders[i] = -over
        overs[i] = -under
    end

    return xs, unders, overs
end

network = read_nnet("../gan_training/generators/supervised_mlp.nnet");

fn = "../../data/SK_DownsampledGANFocusAreaData.h5"
images = h5read(fn, "y_train")
images = reshape(images, 16*8, :)
y = h5read(fn, "X_train")[1:2, :]

@time xs, unders, overs = get_minimum_radii(network, images, y, p = 2)

max_stars_repo_name: sisl/VerifyGAN

max_issues_repo_name: sisl/VerifyGAN

max_forks_repo_name: sisl/VerifyGAN


text:
"""
Filetypes used to decide which rendering methods in [`Documenter.Writers`](@ref) are called.

The only supported format is currently `Markdown`.
"""
module Formats

import ..Documenter

using Compat, DocStringExtensions

"""
Represents the output format. Possible values are `Markdown`, `LaTeX`, and `HTML`.
"""
@enum(
    Format,
    Markdown,
    LaTeX,
    HTML,
)

"""
$(SIGNATURES)

Converts a [`Format`](@ref) value to a `MIME` type.
"""
function mimetype(f::Format)
    f ≡ Markdown ? MIME"text/plain"() :
    f ≡ LaTeX    ? MIME"text/latex"() :
    f ≡ HTML     ? MIME"text/html"()  :
        error("unexpected format.")
end

function extension(f::Format, file)
    path, _ = splitext(file)
    string(path, extension(f))
end

function extension(f::Format)
    f ≡ Markdown ? ".md"   :
    f ≡ LaTeX    ? ".tex"  :
    f ≡ HTML     ? ".html" :
        error("unexpected format.")
end

end


text:
t = Template(; user=me)
pkg_dir = joinpath(t.dir, test_pkg)

@testset "GitLabCI" begin
    @testset "Plugin creation" begin
        p = GitLabCI()
        @test p.gitignore == ["*.jl.cov", "*.jl.*.cov", "*.jl.mem"]
        @test p.src == joinpath(PkgTemplates.DEFAULTS_DIR, "gitlab-ci.yml")
        @test p.dest == ".gitlab-ci.yml"
        @test p.badges == [
			Badge(
				"Build Status",
				"https://gitlab.com/{{USER}}/{{PKGNAME}}.jl/badges/master/build.svg",
				"https://gitlab.com/{{USER}}/{{PKGNAME}}.jl/pipelines",
			),
			Badge(
				"Coverage",
				"https://gitlab.com/{{USER}}/{{PKGNAME}}.jl/badges/master/coverage.svg",
				"https://gitlab.com/{{USER}}/{{PKGNAME}}.jl/commits/master",
			),
        ]
        @test p.view == Dict("GITLABCOVERAGE" => true)
        p = GitLabCI(; config_file=nothing)
        @test p.src === nothing
        p = GitLabCI(; config_file=test_file)
        @test p.src == test_file
        @test_throws ArgumentError GitLabCI(; config_file=fake_path)
		p = GitLabCI(; coverage=false)
		@test p.badges == [
			Badge(
				"Build Status",
				"https://gitlab.com/{{USER}}/{{PKGNAME}}.jl/badges/master/build.svg",
				"https://gitlab.com/{{USER}}/{{PKGNAME}}.jl/pipelines",
			),
		]
		@test p.view == Dict("GITLABCOVERAGE" => false)
    end

    @testset "Badge generation" begin
        p = GitLabCI()
        @test badges(p, me, test_pkg) == [
	    	"[![Build Status](https://gitlab.com/$me/$test_pkg.jl/badges/master/build.svg)](https://gitlab.com/$me/$test_pkg.jl/pipelines)",
			"[![Coverage](https://gitlab.com/$me/$test_pkg.jl/badges/master/coverage.svg)](https://gitlab.com/$me/$test_pkg.jl/commits/master)",
		]
    end

    @testset "File generation" begin
        p = GitLabCI()
        @test gen_plugin(p, t, test_pkg) == [".gitlab-ci.yml"]
        @test isfile(joinpath(pkg_dir, ".gitlab-ci.yml"))
        gitlab = read(joinpath(pkg_dir, ".gitlab-ci.yml"), String)
        # The default plugin should enable the coverage step.
        @test occursin("using Coverage", gitlab)
        rm(joinpath(pkg_dir, ".gitlab-ci.yml"))

        p = GitLabCI(; coverage=false)
        gen_plugin(p, t, test_pkg)
        gitlab = read(joinpath(pkg_dir, ".gitlab-ci.yml"), String)
        # If coverage is false, there should be no coverage step.
        @test !occursin("using Coverage", gitlab)
        rm(joinpath(pkg_dir, ".gitlab-ci.yml"))
        p = GitLabCI(; config_file=nothing)

        @test isempty(gen_plugin(p, t, test_pkg))
        @test !isfile(joinpath(pkg_dir, ".gitlab-ci.yml"))
    end
end

rm(pkg_dir; recursive=true)


text:
using VulkanCore
using VulkanCore.LibVulkan

# utils
"""
    unsafe_strings2pp(names) -> Ptr{String}
Dump a pointer that is of type `Ptr{String}` from a Julia `String` array.
"""
unsafe_strings2pp(names::Vector{String}) = Base.unsafe_convert(Ptr{Cstring}, Base.cconvert(Ptr{Cstring}, names))

"""
    to_string(x::NTuple{N,UInt8}) -> String
Convert a `NTuple{N,UInt8}` to `String` dropping all of the `\0`s.
"""
to_string(x::NTuple{N,UInt8}) where {N} = rstrip(String(collect(x)), '\0')

"""
    int2version(::Type{VersionNumber}, ver::Integer) -> VersionNumber
Convert a Vulkan version integer to a `major.minor.patch` `VersionNumber`.
"""
int2version(v::Integer) = VersionNumber(VK_VERSION_MAJOR(v), VK_VERSION_MINOR(v), VK_VERSION_PATCH(v))

# extension & layer checking
struct ExtensionProperties
    name::String
    version::VersionNumber
end
ExtensionProperties(extension::VkExtensionProperties) =
    ExtensionProperties(to_string(extension.extensionName), int2version(extension.specVersion))

struct LayerProperties
    name::String
    spec_ver::VersionNumber
    impl_ver::VersionNumber
    description::String
end
LayerProperties(layer::VkLayerProperties) = LayerProperties(
    to_string(layer.layerName),
    int2version(layer.specVersion),
    int2version(layer.implementationVersion),
    to_string(layer.description),
)

"""
    get_supported_extensions() -> Vector{String}
Return a vector of supported extensions.
"""
function get_supported_extensions()
    count_ref = Ref{Cuint}(0)
    vkEnumerateInstanceExtensionProperties(C_NULL, count_ref, C_NULL)
    count = count_ref[]
    extensions = Vector{VkExtensionProperties}(undef, count)
    vkEnumerateInstanceExtensionProperties(C_NULL, count_ref, extensions)
    return [ExtensionProperties(ext) for ext in extensions]
end

"""
    check_extensions(required_extensions::Vector{<:AbstractString}) -> Bool
Return `true` when all of the `required_extensions` are supported.
"""
function check_extensions(required_extensions::Vector{<:AbstractString})
    supported = get_supported_extensions()
    @info "available extensions:"
    for x in supported
        @info "  $(x.name): $(x.version)"
    end
    names = [x.name for x in supported]
    if all(x->x in names, required_extensions)
        return true
    else
        @error "not all required extensions are supported."
        return false
    end
end

"""
    get_supported_layers() -> Vector{String}
Return a vector of supported layers.
"""
function get_supported_layers()
    count_ref = Ref{Cuint}(0)
    vkEnumerateInstanceLayerProperties(count_ref, C_NULL)
    count = count_ref[]
    layers = Vector{VkLayerProperties}(undef, count)
    vkEnumerateInstanceLayerProperties(count_ref, layers)
    return [LayerProperties(layer) for layer in layers]
end

"""
    check_layers(required_layers::Vector{<:AbstractString}) -> Bool
Return `true` when all of the `required_layers` are supported.
"""
function check_layers(required_layers::Vector{<:AbstractString})
    supported = get_supported_layers()
    @info "available layers:"
    for x in supported
        @info "  $(x.name): $(x.description)($(x.spec_ver)) -- implementation version: $(x.impl_ver)"
    end
    names = [layer.name for layer in supported]
    if all(x->x in names, required_layers)
        return true
    else
        @error "not all required layers are supported."
        return false
    end
end

# instance
function LibVulkan.VkApplicationInfo(app_name::AbstractString, app_ver::VersionNumber, engine_name::AbstractString, engine_ver::VersionNumber, api_ver::Integer)
    sType = VK_STRUCTURE_TYPE_APPLICATION_INFO
    pNext = C_NULL    # reserved for extension-specific structure
    pApplicationName = pointer(app_name)
    vkApplicationVersion = VK_MAKE_VERSION(app_ver.major, app_ver.minor, app_ver.patch)
    pEngineName = pointer(engine_name)
    vkEngineVersion = VK_MAKE_VERSION(engine_ver.major, engine_ver.minor, engine_ver.patch)
    return VkApplicationInfo(sType, pNext, pApplicationName, vkApplicationVersion, pEngineName, vkEngineVersion, Cuint(api_ver))
end

function LibVulkan.VkInstanceCreateInfo(app_info_ref::Ref{VkApplicationInfo}, layers::Vector{String}, extensions::Vector{String})
    sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO
    pNext = C_NULL       # reserved for extension-specific structure
    flags = UInt32(0)    # reserved for future use
    ppEnabledLayerNames = isempty(layers) ? C_NULL : unsafe_strings2pp(layers)
    ppEnabledExtensionNames = isempty(extensions) ? C_NULL : unsafe_strings2pp(extensions)
    pApplicationInfo = Base.unsafe_convert(Ptr{VkApplicationInfo}, app_info_ref)
    return VkInstanceCreateInfo(sType, pNext, flags, pApplicationInfo, length(layers), ppEnabledLayerNames, length(extensions), ppEnabledExtensionNames)
end

function LibVulkan.VkInstanceCreateInfo(app_info_ref::Ref{VkApplicationInfo}, debug_info_ref::Ref{VkDebugUtilsMessengerCreateInfoEXT}, layers::Vector{String}, extensions::Vector{String})
    sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO
    pNext = Base.unsafe_convert(Ptr{Cvoid}, debug_info_ref)
    flags = UInt32(0)    # reserved for future use
    ppEnabledLayerNames = isempty(layers) ? C_NULL : unsafe_strings2pp(layers)
    ppEnabledExtensionNames = isempty(extensions) ? C_NULL : unsafe_strings2pp(extensions)
    pApplicationInfo = Base.unsafe_convert(Ptr{VkApplicationInfo}, app_info_ref)
    return VkInstanceCreateInfo(sType, pNext, flags, pApplicationInfo, length(layers), ppEnabledLayerNames, length(extensions), ppEnabledExtensionNames)
end

# debug
function LibVulkan.VkDebugUtilsMessengerCreateInfoEXT(messageSeverity, messageType, pfnUserCallback, pUserData)
    sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT
    pNext = C_NULL       # reserved for extension-specific structure
    flags = UInt32(0)    # reserved for future use
    return VkDebugUtilsMessengerCreateInfoEXT(sType, pNext, flags, messageSeverity, messageType, pfnUserCallback, pUserData)
end

function LibVulkan.VkDebugUtilsMessengerCreateInfoEXT(pfnUserCallback, pUserData)
    messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT |
                      VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT |
                      VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
                      VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT
    messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
                  VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
                  VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT
    return VkDebugUtilsMessengerCreateInfoEXT(messageSeverity, messageType, pfnUserCallback, pUserData)
end

function CreateDebugUtilsMessengerEXT(instance::VkInstance, pCreateInfo::Ref{VkDebugUtilsMessengerCreateInfoEXT}, pAllocator, pDebugMessenger::Ref{VkDebugUtilsMessengerEXT})
    fp = vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT")
    if fp != C_NULL
        ccall(fp, VkResult, (VkInstance, Ptr{VkDebugUtilsMessengerCreateInfoEXT}, Ptr{VkAllocationCallbacks}, Ptr{VkDebugUtilsMessengerEXT}), instance, pCreateInfo, pAllocator, pDebugMessenger)
    else
        return VK_ERROR_EXTENSION_NOT_PRESENT
    end
end

function DestroyDebugUtilsMessengerEXT(instance::VkInstance, debugMessenger::VkDebugUtilsMessengerEXT, pAllocator)
    fp = vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT")
    if fp != C_NULL
        ccall(fp, VkResult, (VkInstance, VkDebugUtilsMessengerEXT, Ptr{VkAllocationCallbacks}), instance, debugMessenger, pAllocator)
    end
end

max_stars_repo_name: Gnimuc/Videre

max_issues_repo_name: Gnimuc/Videre

max_forks_repo_name: Gnimuc/Videre


text:
module Utils
export nonempty, unwrap_where, rewrap_where, split_where, issomething, change_name!,
  get_name, find_object, get_object,
  @ifsomething, iterate_start, IterateStart, Iterator,
  True, False,
  isexpr

using ProxyInterfaces

const True = Val{true}
const False = Val{false}

nonempty(a) = !isempty(a)

"""
    isexpr(expr) -> Bool
    isexpr(expr, head) -> Bool

Checks whether given value isa `Base.Expr` and if further given `head`, it also checks whether
the `head` matches `expr.head`.

# Examples
```jldoctest
julia> using ExprParsers

julia> EP.isexpr(:(a = hi))
true
julia> EP.isexpr(12)
false
julia> EP.isexpr(:(f(a) = a), :(=))
true
julia> EP.isexpr(:(f(a) = a), :function)
false
```
"""
isexpr(::Expr) = true
isexpr(other) = false
isexpr(expr::Expr, head::Symbol) = expr.head == head
isexpr(other, head::Symbol) = false

"""
needed to properly match function signatures

similar to Base.unwrap_unionall, but for Base.Expr
"""
function unwrap_where(e::Base.Expr)
  if e.head == :where
    unwrap_where(e.args[1])
  else
    e
  end
end

"""
similar to Base.rewrap_unionall, but for Base.Expr
"""
rewrap_where(unwrapped, original) = unwrapped
function rewrap_where(unwrapped, original::Base.Expr)
  if original.head == :where
    Base.Expr(:where, rewrap_where(unwrapped, original.args[1]), original.args[2:end]...)
  else
    unwrapped
  end
end

"""
Example
```
julia> ExprParsers.Utils.split_where(:(f(a::A) where {C, Y <: A} where B where A ))
(:(f(a::A)), Any[:A, :B, :C, :(Y <: A)])
```
"""
split_where(e) = (e, [])
function split_where(e::Base.Expr)
  if e.head == :where
    subexpr, wheres = split_where(e.args[1])
    # nested wheres to the back, they may depend on the outer where
    subexpr, [e.args[2:end]; wheres]
  else
    e, []
  end
end

Base.get(::Val{T}) where T = T

issomething(::Nothing) = false
issomething(::Any) = true

"""
    IterTools.@ifsomething expr
If `expr` evaluates to `nothing`, equivalent to `return nothing`, otherwise the macro
evaluates to the value of `expr`. Not exported, useful for implementing iterators.
```jldoctest
julia> using ExprParsers

julia> ExprParsers.@ifsomething iterate(1:2)
(1, 1)
julia> let elt, state = ExprParsers.@ifsomething iterate(1:2, 2); println("not reached"); end
```
"""
macro ifsomething(ex)
    quote
        result = $(esc(ex))
        result === nothing && return nothing
        result
    end
end

# little helper to write easier code for iteration (CAUTION: this will lead to type-instable code)
struct IterateStart end
const iterate_start = IterateStart()
Base.iterate(a, ::IterateStart) = Base.iterate(a)

# possibility to use Iterators as elementwise Parsers of possibly unknown length
"""
    EP.Iterator(some_iterable)

Mark an iterable explicitly as an Iterator to add support for elementwise `parse_expr`.

# Examples

```jldoctest
julia> using ExprParsers; using Base.Iterators

julia> parser = EP.Iterator(repeated(4));

julia> parse_expr(parser, [4, 4])
2-element Array{Int64,1}:
 4
 4
julia> parse_expr(parser, [4, 4, 4, 4])
4-element Array{Int64,1}:
 4
 4
 4
 4
julia> parse_expr(parser, [3, 4])
ERROR: ParseError: Using default `==` comparison, but parser `4` ≠ value `3`.
```
"""
struct Iterator{T}
  iterator::T
end
ProxyInterfaces.iterator(i::Iterator) = i.iterator
ProxyInterfaces.iterator(::Base.Type{Iterator{T}}) where T = T
ProxyInterfaces.@iterator Iterator

end  # module