text:
#=
test_parser:
- Julia version:
- Author: bramb
- Date: 2019-02-01
=#
using Test

@testset "simple_tagml_tokenizer" begin
    using HyperGraphTools.SimpleTAGMLTokenizer

    tagml = "[root>[a>[name>Cookie Monster<name] [b>loves<a] [name>Stroopwafels<name]<b]<root]"
    tokens = tokenize(tagml)
    @test tokens == [
        Token("[root>",OPENTAG),
        Token("[a>",OPENTAG),
        Token("[name>",OPENTAG),
        Token("Cookie Monster",TEXT),
        Token("<name]",CLOSETAG),
        Token(" ",TEXT),
        Token("[b>",OPENTAG),
        Token("loves",TEXT),
        Token("<a]",CLOSETAG),
        Token(" ",TEXT),
        Token("[name>",OPENTAG),
        Token("Stroopwafels",TEXT),
        Token("<name]",CLOSETAG),
        Token("<b]",CLOSETAG),
        Token("<root]",CLOSETAG)
    ]

    tagml = "pre[x>Just text<x]post"
    tokens = tokenize(tagml)
    @test tokens == [
      Token("pre", ERROR),
      Token("[x>", OPENTAG),
      Token("Just text", TEXT),
      Token("<x]", CLOSETAG),
      Token("post", ERROR)
    ]
end

max_stars_repo_name: brambg/HyperGraphTools

max_issues_repo_name: rhdekker/HyperGraphTools

max_forks_repo_name: rhdekker/HyperGraphTools


text:
using DirectDetections, Distributions
using MCMCChains

# Create a function mapping (age_Myr, mass_Mjup) -> temp_K
const cooling_tracks = sonora_cooling_interpolator()

# Create functions mapping (temp_K, mass_Mjup) -> absolute magnitude
const sonora_temp_mass_Z = sonora_photometry_interpolator(:MKO_Z)
const sonora_temp_mass_J = sonora_photometry_interpolator(:MKO_J)
const sonora_temp_mass_L = sonora_photometry_interpolator(:Keck_L′)

# We then need to convert from absolute magnitude to relative magnitude vs star, and then
# to contrast in linear units.
# N.B. the package doesn't actually care about any of these units or details. Just make sure
# the units of your data (photometry/images) are the same as your models.

# You could just as easily use apparent magnitudes and
# m - M = 5log(d/10)
d = 43
M = 5.3 - 5log(d/10)

const star_abs_mag_Z = 5.0
const star_abs_mag_J = 5.1
const star_abs_mag_L = 5.2

##
@named b = DirectDetections.Planet(
    Priors(
        mass = Uniform(0.6, 75),
        a = Uniform(1, 30),
        e = Beta(1.2, 10),
        i = Normal(0.1, 0.2),
        ω = Uniform(0, 2pi),
        Ω = Uniform(0, pi),
        τ = Uniform(0, 1),
    ),
    # Flux will be calculated from planet mass and system age
    Derived(
        Z = (sys, pl) -> sonora_temp_mass_Z(cooling_tracks(sys.age, pl.mass), pl.mass),
        J = (sys, pl) -> sonora_temp_mass_J(cooling_tracks(sys.age, pl.mass), pl.mass),
        L = (sys, pl) -> sonora_temp_mass_L(cooling_tracks(sys.age, pl.mass), pl.mass),
        # We inculde teff in the chains to ease analysis (though it can be recalculated after the fact using age and mass)
        teff = (sys, pl) -> cooling_tracks(sys.age, pl.mass),
    ),
    Photometry(
        (band = :Z, phot=15.0, σ_phot=3.),
        (band = :J, phot=13.5, σ_phot=0.5),
        (band = :L, phot=11.0, σ_phot=1.0)
    ),
    Astrometry(
        (epoch=mjd("2016-12-15"), ra=0.133*1e3, dec=-0.174*1e3, σ_ra=0.007*1e3, σ_dec=0.007*1e3),
        (epoch=mjd("2017-03-12"), ra=0.126*1e3, dec=-0.176*1e3, σ_ra=0.004*1e3, σ_dec=0.004*1e3),
        (epoch=mjd("2017-03-13"), ra=0.127*1e3, dec=-0.172*1e3, σ_ra=0.004*1e3, σ_dec=0.004*1e3),
        (epoch=mjd("2018-02-08"), ra=0.083*1e3, dec=-0.133*1e3, σ_ra=0.010*1e3, σ_dec=0.010*1e3),
        (epoch=mjd("2018-11-28"), ra=0.058*1e3, dec=-0.122*1e3, σ_ra=0.010*1e3, σ_dec=0.020*1e3),
        (epoch=mjd("2018-12-15"), ra=0.056*1e3, dec=-0.104*1e3, σ_ra=0.008*1e3, σ_dec=0.008*1e3),
    )
)

@named HD12345 = System(
    Priors(
        # age = TruncatedNormal(40, 15, 5, Inf),
        age = Uniform(5, 100),
        μ = Normal(1.61, 0.05),
        plx = gaia_plx(gaia_id=756291174721509376),
    ),
    # ProperMotionAnomHGCA(gaia_id=756291174721509376),
    ProperMotionAnomHGCA(gaia_id=2832463659640297472), #HR8799...
    b,
)

##
chains = DirectDetections.hmc(
    HD12345, 0.85,
    MCMCThreads(),
    num_chains=2,
    adaptation =   1_000,
    iterations = 20_000,
)

##
gelmandiag(chains)
##
##

# ##
# gelmandiag(chain)
# ##
# plotmodel(chain,color=:mass,pmascatter=:mass)
# ##
# using PairPlots
# PairPlots.corner(chain)

# ##
# table = (
#     age=chain["age"],
#     mass=chain["b[mass]"],
#     sma=chain["b[a]"],
#     Z=chain["b[Z]"],
#     J=chain["b[J]"],
#     L=chain["b[L]"],
# )
# PairPlots.corner(table)
# # PairPlots.corner(table, filterscatter=false, plotcontours=false)

# ##
# p = plot(yflip=true,yguide="abs. mag")
# for planet in chain.info.model.planets
#     phot = planet.photometry
#     N= 1500
#     ii = rand(1:size(chain,1)*size(chain,3),N)
#     Zs = chain["b[Z]"][ii]
#     Js = chain["b[J]"][ii]
#     Ls = chain["b[L]"][ii]
#     plot!(vcat(Zs', Js', Ls'), color=:black, lw=1,  alpha=0.05, label="")
#     # scatter!(Ls', color=:black, lw=1,  alpha=0.05, label="")
#     scatter!(vcat(Zs', Js', Ls'), color=:black, lw=1, ms=4, alpha=0.01, label="")
#     scatter!(
#         collect(phot.phot),
#         yerr=collect(phot.σ_phot),
#         xticks=(collect(eachindex(phot.phot)), string.(phot.band)),
#         color=:red,
#         markerstrokecolor=:red,
#         label="Measured"
#     )
# end
# p

ext: jl


text:
export Lanczos

abstract type AbstractLanczos <: InterpolationType end

"""
    Lanczos{N}(a=4)

Lanczos resampling via a kernel with scale parameter `a` and support over `N` neighbors.

This form of interpolation is merely the discrete convolution of the samples with a Lanczos kernel of size `a`. The size is directly related to how "far" the interpolation will reach for information, and has `O(N^2)` impact on runtime. An alternative implementation matching `lanczos4` from OpenCV is available as Lanczos4OpenCV.
"""
struct Lanczos{N} <: AbstractLanczos
    a::Int

    function Lanczos{N}(a) where N
        N < a && @warn "Using a smaller support than scale for Lanczos window. Proceed with caution."
        new{N}(a)
    end
end

Lanczos(a=4) = Lanczos{a}(a)

"""
    LanczosInterpolation
"""
struct LanczosInterpolation{T,N,IT <: DimSpec{AbstractLanczos},A <: AbstractArray{T,N},P <: Tuple{Vararg{AbstractArray,N}}} <: AbstractInterpolation{T,N,IT}
    coefs::A
    parentaxes::P
    it::IT
end

@generated degree(::Lanczos{N}) where {N} = :($N)

getknots(itp::LanczosInterpolation) = axes(itp)
coefficients(itp::LanczosInterpolation) = itp.coefs
itpflag(itp::LanczosInterpolation) = itp.it

size(itp::LanczosInterpolation) = map(length, itp.parentaxes)
axes(itp::LanczosInterpolation) = itp.parentaxes
lbounds(itp::LanczosInterpolation) = map(first, itp.parentaxes)
ubounds(itp::LanczosInterpolation) = map(last, itp.parentaxes)

function interpolate(A::AbstractArray{T}, it::AbstractLanczos) where T
    Apad = copy_with_padding(float(T), A, it)
    return LanczosInterpolation(Apad, axes(A), it)
end

@inline function (itp::LanczosInterpolation{T,N})(x::Vararg{<:Number,N}) where {T,N}
    @boundscheck (checkbounds(Bool, itp, x...) || Base.throw_boundserror(itp, x))
    wis = weightedindexes((value_weights,), itpinfo(itp)..., x)
    itp.coefs[wis...]
end

function weightedindex_parts(fs, it::AbstractLanczos, ax::AbstractUnitRange{<:Integer}, x)
    pos, δx = positions(it, ax, x)
    (position = pos, coefs = fmap(fs, it, δx))
end

function positions(it::AbstractLanczos, ax, x)
    xf = floorbounds(x, ax)
    δx = x - xf
    fast_trunc(Int, xf) - degree(it) + 1, δx
end

function value_weights(it::Lanczos, δx::S) where S
    N = degree(it)
    # short-circuit if integral
    isinteger(δx) && return ntuple(i->convert(float(S), i == N - δx), Val(2N))

    # LUTs
    #it.a === N === 4 && return _lanczos4(δx)

    cs = ntuple(i -> lanczos(N - i + δx, it.a, N), Val(2N))
    sum_cs = sum(cs)
    normed_cs = ntuple(i -> cs[i] / sum_cs, Val(length(cs)))
    return normed_cs
end

function padded_axis(ax::AbstractUnitRange, it::AbstractLanczos)
    N = degree(it)
    return first(ax) - N + 1:last(ax) + N
end

# precise implementations for fast evaluation of common kernels

"""
    lanczos(x, a, n=a)

Implementation of the [Lanczos kernel](https://en.wikipedia.org/wiki/Lanczos_resampling)
"""
lanczos(x::T, a::Integer, n=a) where {T} = abs(x) < n ? T(sinc(x) * sinc(x / a)) : zero(T)


text:
const _ranks = (
    domain     = 0,
    kingdom    = 1,
    phylum     = 2,
    class      = 3,
    order      = 4,
    family     = 5,
    genus      = 6,
    species    = 7,
    subspecies = 8,
    strain     = 9
)

const _shortranks = (
    d = :domain,
    k = :kingdom,
    p = :phylum,
    c = :class,
    o = :order,
    f = :family,
    g = :genus,
    s = :species,
    t = :subspecies,
    u = missing
)

"""
    Taxon(name::String, rank::Union{Missing, Symbol, Int}) <: AbstractFeature
    Taxon(name::String)

Microbial taxon with a name and a rank that can be one of

0. `:domain`
1. `:kingom`
2. `:phylum`
3. `:class`
4. `:order`
5. `:faamily`
6. `:genus`
7. `:species`
8. `:subspecies`
9. `:strain`

or `missing`. Contructors can also use numbers 0-9, or pass a string alone
(in which case the `taxon` will be stored as `missing`)
"""
struct Taxon <: AbstractFeature
    name::String
    rank::Union{Missing, Symbol}

    Taxon(s::AbstractString, ::Missing) = new(s, missing)
    Taxon(s::AbstractString, rank::Symbol) = in(rank, keys(_ranks)) ?
                                                        new(s, rank)  :
                                                        error("Invalid rank $rank, must be one of $(keys(_ranks))")
end

Taxon(n::AbstractString, rank::Int) = 0 <= rank <= 9 ?
                                            Taxon(n, keys(_ranks)[rank+1]) :
                                            error("Invalid rank $rank, must be one of $_ranks")
Taxon(n::AbstractString) = Taxon(n, missing)

function Base.String(t::Taxon)
    if hasrank(t)
        return taxrank(t) == :strain ? string("t__", name(t)) : string(first(string(taxrank(t))), "__", name(t))
    else
        return string("u__", name(t))
    end
end

"""
    taxon(::AbstractString)

Return a [`Taxon`](@ref) from a string representation.
If the string contains taxonomic rank information in the form
`"x__Thename"` where `x` is the first letter of the rank,
this information will be used.

## Examples

```julia-repl
julia> taxon("Unknown")
Taxon("Unknown", missing)

julia> taxon("s__Prevotella_copri")
Taxon("Prevotella_copri", :species)
```
"""
function taxon(n::AbstractString)
    m = match(r"^([dkpcofgstu])__(.+)", n)
    isnothing(m) && return Taxon(n)
    return Taxon(string(m.captures[2]), _shortranks[Symbol(m.captures[1])])
end

"""
    taxrank(t::Union{Taxon, missing})

Get the `rank` field from an `Taxon`.
Returns `missing` if the rank is not set.
"""
taxrank(t::Taxon) = t.rank
taxrank(::Missing) = missing

"""
    hasrank(t::Taxon)::Bool

Pretty self-explanatory.
"""
hasrank(t::Taxon) = !ismissing(taxrank(t))

"""
    GeneFunction(name::String, taxon::Union{Taxon, String, Missing}) <: AbstractFeature
    GeneFunction(name::String)

Microbial gene function object with optional stratification (`taxon`).
"""
struct GeneFunction <: AbstractFeature
    name::String
    taxon::Union{Missing, Taxon}
end

GeneFunction(n::AbstractString) = GeneFunction(n, missing)
GeneFunction(n::AbstractString, t::AbstractString) = GeneFunction(n, taxon(t))

"""
    taxon(t::GeneFunction)

Get the `taxon` field from a `GeneFunction`.
Returns `missing` if the taxon is not set.
"""
taxon(gf::GeneFunction) = gf.taxon

"""
    hastaxon(t::GeneFunction)::Bool

Pretty self-explanatory.
"""
hastaxon(gf::GeneFunction) = !ismissing(taxon(gf))

"""
    taxrank(gf::GeneFunction)

Get the `rank` field from the Taxon, if `gf` has one.
Returns `missing` if the taxon or rank is not set.
"""
taxrank(gf::GeneFunction) = taxrank(taxon(gf))

"""
    hasrank(t::GeneFunction)::Bool

Pretty self-explanatory.
"""
hasrank(gf::GeneFunction) = hastaxon(gf) && !ismissing(taxrank(gf))

Base.String(gf::GeneFunction) = hastaxon(gf) ? string(name(gf), '|', String(taxon(gf))) : name(gf)

"""
    genefunction(n::AbstractString)

Make a gene function from a string,
Converting anything after an initial `|` as a [`Taxon`](@ref).
"""
function genefunction(n::AbstractString)
    if contains(n, '|')
        spl = split(n, '|')
        return GeneFunction(string(spl[1]), taxon(join(spl[2:end], '|')))
    else
        return GeneFunction(n)
    end
end

Base.string(f::AbstractFeature) = String(f)


text:
# ------------------------------------------------------------------
# Licensed under the MIT License. See LICENSE in the project root.
# ------------------------------------------------------------------

"""
    SpatialPredicatePartition(predicate)

A method for partitioning spatial objects with a given spatial
`predicate` function. Two coordinates `x` and `y` are part of
the same subset whenever `predicate(x, y) == true`.
"""
struct SpatialPredicatePartition <: SPredicatePartitionMethod
  pred::Function
end

(p::SpatialPredicatePartition)(x, y) = p.pred(x, y)


text:
using Test
using StaticArrays
using Rotations
using LinearAlgebra
using BenchmarkTools
using RobotDynamics

import RobotDynamics: LieState
const RD = RobotDynamics

function error_state_quat(x::AbstractVector, x0::AbstractVector)
    dq1 = UnitQuaternion(x[4],x[5],x[6],x[7]) ⊖ UnitQuaternion(x0[4],x0[5],x0[6],x0[7])
    dq2 = UnitQuaternion(x[10],x[11],x[12],x[13]) ⊖ UnitQuaternion(x0[10],x0[11],x0[12],x0[13])
    dx = x - x0
    SA[dx[1], dx[2], dx[3], dq1[1], dq1[2], dq1[3], dx[8], dx[9], dq2[1], dq2[2], dq2[3], dx[14], dx[15], dx[16]]
end

function error_state_rp(x::AbstractVector, x0::AbstractVector)
    dq1 = RodriguesParam(x[4],x[5],x[6]) ⊖ RodriguesParam(x0[4],x0[5],x0[6])
    dq2 = RodriguesParam(x[9],x[10],x[11]) ⊖ RodriguesParam(x0[9],x0[10],x0[11])
    dx = x - x0
    SA[dx[1], dx[2], dx[3], dq1[1], dq1[2], dq1[3],
       dx[7], dx[8],        dq2[1], dq2[2], dq2[3], dx[12], dx[13], dx[14]]
end

# Test params
@test Rotations.params(UnitQuaternion) == 4
@test Rotations.params(RodriguesParam) == 3
@test Rotations.params(MRP) == 3
@test Rotations.params(RotMatrix3) == 9
@test Rotations.params(RotMatrix2) == 4

# Test index functions
R = UnitQuaternion{Float64}
P = (3,2,3)
s = LieState{R,P}()
@test length(s) == 16
@test RD.errstate_dim(s) == 14
@test RobotDynamics.vec_inds(R,P,1) == 1:3   == RobotDynamics.inds(R,P,1)
@test RobotDynamics.rot_inds(R,P,1) == 4:7   == RobotDynamics.inds(R,P,2)
@test RobotDynamics.vec_inds(R,P,2) == 8:9   == RobotDynamics.inds(R,P,3)
@test RobotDynamics.rot_inds(R,P,2) == 10:13 == RobotDynamics.inds(R,P,4)
@test RobotDynamics.vec_inds(R,P,3) == 14:16 == RobotDynamics.inds(R,P,5)
@test RobotDynamics.QuatState(16, SA[4,10]) === s
@test RobotDynamics.QuatState(16, @MVector [4,10]) === s
@test RobotDynamics.QuatState(16, (4,10)) === s
@test RobotDynamics.num_rotations(s) == 2
@test length(typeof(s)) == length(s)

n = length(s)
x  = @MVector rand(n)
x0 = @MVector rand(n)
@test RobotDynamics.state_diff(s, x, x0) ≈ error_state_quat(x, x0)

# Test with RP
R = RodriguesParam{Float64}
P = (3,2,3)
s = LieState{R,P}()
@test LieState(R,P) === s
@test LieState(R,3,2,3) === s
@test length(s) == 14
@test RD.errstate_dim(s) == 14
@test RobotDynamics.vec_inds(R,P,1) == 1:3   == RobotDynamics.inds(R,P,1)
@test RobotDynamics.rot_inds(R,P,1) == 4:6   == RobotDynamics.inds(R,P,2)
@test RobotDynamics.vec_inds(R,P,2) == 7:8   == RobotDynamics.inds(R,P,3)
@test RobotDynamics.rot_inds(R,P,2) == 9:11  == RobotDynamics.inds(R,P,4)
@test RobotDynamics.vec_inds(R,P,3) == 12:14 == RobotDynamics.inds(R,P,5)
@test RobotDynamics.state_diff(s, x, x0) ≈ error_state_rp(x, x0)

# @btime error_state($s, $x, $x0)
# @btime error_state_rp($x, $x0)

# state diff jacobian
R = UnitQuaternion{Float64}
P = (3,2,3)
s = LieState{R,P}()
n = length(s)
n̄ = RD.errstate_dim(s)
G = zeros(n,n̄)
RobotDynamics.errstate_jacobian!(s, G, x)

q1 = UnitQuaternion(x[4],x[5],x[6],x[7])
q2 = UnitQuaternion(x[10],x[11],x[12],x[13])
G1 = Rotations.∇differential(q1)
G2 = Rotations.∇differential(q2)
G0 = cat(I(3),G1,I(2),G2,I(3),dims=(1,2))
@test G0 ≈ G

# state diff jacobian jacobian
∇G = SizedMatrix{n̄,n̄}(zeros(n̄,n̄))
dx = @SVector rand(n)

dq1 = SVector(dx[4],dx[5],dx[6],dx[7])
dq2 = SVector(dx[10],dx[11],dx[12],dx[13])
RobotDynamics.∇errstate_jacobian!(s, ∇G, x, dx)
∇G1 = Rotations.∇²differential(q1, dq1)
∇G2 = Rotations.∇²differential(q2, dq2)
∇G0 = cat(zeros(3,3), ∇G1, zeros(2,2), ∇G2, zeros(3,3), dims=(1,2))
@test Matrix(∇G0) ≈ Matrix(∇G)

struct LieBody <: RD.LieGroupModel end
RobotDynamics.control_dim(::LieBody) = 4
RobotDynamics.LieState(::LieBody) = LieState(UnitQuaternion{Float64},(3,2,3))

model = LieBody()
@test s === LieState(model)
G .= 0
∇G .= 0
RobotDynamics.errstate_jacobian!(model, G, x)
RobotDynamics.∇errstate_jacobian!(model, ∇G, x, dx)
@test G0 ≈ G
@test ∇G0 ≈ ∇G


text:
#
# MmapIO
#
# An IO built on top of mmap to avoid the overhead of ordinary disk IO
if Sys.iswindows()
    const MMAP_GROW_SIZE = 2^19
    const FILE_GROW_SIZE = 2^19
else
    const MMAP_GROW_SIZE = 2^24
    const FILE_GROW_SIZE = 2^18
end

const Plain = Union{Int16,Int32,Int64,Int128,UInt16,UInt32,UInt64,UInt128,Float16,Float32,
                    Float64}
const PlainType = Union{Type{Int16},Type{Int32},Type{Int64},Type{Int128},Type{UInt16},
                        Type{UInt32},Type{UInt64},Type{UInt128},Type{Float16},
                        Type{Float32},Type{Float64}}

mutable struct MmapIO <: IO
    f::IOStream
    write::Bool
    n::Int
    startptr::Ptr{Cvoid}
    curptr::Ptr{Cvoid}
    endptr::Ptr{Cvoid}
    @static if Sys.iswindows()
        mapping::Ptr{Cvoid}
    end
    # The following two fields used to be global constants but
    # that breaks concurrent writes on separate threads
    checksum_pos::Vector{Int64}
    nchecksum::Int64
end

if Sys.isunix()
    function mmap!(io::MmapIO, n::Int)
        oldptr = io.startptr
        newptr = ccall(:jl_mmap, Ptr{Cvoid}, (Ptr{Cvoid}, Csize_t, Cint, Cint, Cint, Int64),
                       C_NULL, n, Mmap.PROT_READ | (io.write*Mmap.PROT_WRITE),
                       Mmap.MAP_SHARED, fd(io.f), 0)
        systemerror("mmap", newptr == Ptr{Nothing}(-1))
        io.n = n
        io.curptr += newptr - oldptr
        io.startptr = newptr
    end

    munmap(io::MmapIO) =
        systemerror("munmap",
                    ccall(:munmap, Cint, (Ptr{Cvoid}, Int), io.startptr, io.n) != 0)

    function msync(io::MmapIO, offset::Integer=0, len::Integer=UInt(io.endptr - io.startptr),
                   invalidate::Bool=false)
        # shift `offset` to start of page boundary
        offset_page::Int64 = div(offset, Mmap.PAGESIZE) * Mmap.PAGESIZE
        # add (offset - offset_page) to `len` to get total length of memory-mapped region
        mmaplen::Int64 = (offset - offset_page) + len
        systemerror("msync",
                    ccall(:msync, Cint, (Ptr{Cvoid}, Csize_t, Cint),
                          io.startptr + offset_page, mmaplen,
                          invalidate ? Mmap.MS_INVALIDATE : Mmap.MS_SYNC) != 0)
    end
elseif Sys.iswindows()
    const DWORD = Culong
    function mmap!(io::MmapIO, n::Int)
        oldptr = io.startptr
        mapping = ccall(:CreateFileMappingW, stdcall, Ptr{Cvoid},
                        (Base.Libc.WindowsRawSocket, Ptr{Cvoid}, DWORD, DWORD, DWORD, Ptr{Cvoid}),
                        Mmap.gethandle(io.f), C_NULL,
                        io.write ? Mmap.PAGE_READWRITE : Mmap.PAGE_READONLY, n >> 32,
                        n % UInt32, C_NULL)
        systemerror("CreateFileMappingW", mapping == C_NULL)
        newptr = ccall(:MapViewOfFile, stdcall, Ptr{Cvoid},
                       (Ptr{Cvoid}, DWORD, DWORD, DWORD, Csize_t),
                        mapping, io.write ? Mmap.FILE_MAP_WRITE : Mmap.FILE_MAP_READ, 0, 0,
                        n)
        systemerror("MapViewOfFile", newptr == C_NULL)
        io.n = n
        io.curptr += newptr - oldptr
        io.mapping = mapping
        io.startptr = newptr
    end

    function munmap(io::MmapIO)
        systemerror("UnmapViewOfFile",
                    ccall(:UnmapViewOfFile, stdcall, Cint, (Ptr{Cvoid},), io.startptr) == 0)
        systemerror("CloseHandle",
                    ccall(:CloseHandle, stdcall, Cint, (Ptr{Cvoid},), io.mapping) == 0)
    end

    function msync(io::MmapIO, offset::Integer=0, len::Integer=UInt(io.endptr - io.startptr),
                   invalidate::Bool=false)
        # shift `offset` to start of page boundary
        offset_page::Int64 = div(offset, Mmap.PAGESIZE) * Mmap.PAGESIZE
        # add (offset - offset_page) to `len` to get total length of memory-mapped region
        mmaplen::Int64 = (offset - offset_page) + len
        systemerror("FlushViewOfFile",
                    ccall(:FlushViewOfFile, stdcall, Cint,
                          (Ptr{Cvoid}, Csize_t), io.startptr + offset_page, mmaplen) == 0)
    end
end

function MmapIO(fname::AbstractString, write::Bool, create::Bool, truncate::Bool)
    truncate && !write && throw(ArgumentError("cannot truncate file that is not writable"))

    f = open(fname, read = true, write = write,
             create = create, truncate = truncate, append = false)
    initialsz = truncate ? Int64(0) : filesize(fname)
    @static if Int == Int32
        initialsz > typemax(Int) && error("cannot read a file greater than 2GB on a 32-bit system")
    end
    n = (initialsz % Int) + (write ? MMAP_GROW_SIZE : 0)
    n < 0 && (n = typemax(Int))

    @static if Sys.iswindows()
        io = MmapIO(f, write, 0, C_NULL, C_NULL, C_NULL, C_NULL, Int64[], 0)
    else
        io = MmapIO(f, write, 0, C_NULL, C_NULL, C_NULL, Int64[], 0)
    end
    mmap!(io, n)
    io.endptr = io.startptr + (initialsz % Int)

    io
end

Base.show(io::IO, ::MmapIO) = print(io, "MmapIO")

if Sys.islinux()
    # This is used to be "pwrite" but that is actually not defined behaviour
    # and fails on NFS systems. "ftruncate" is recommended instead
    # TODO: Benchmark on Windows
    grow(io::IOStream, sz::Integer) =
        systemerror("ftruncate",
            ccall(:ftruncate, Cint, (Cint, Int64),
            fd(io), sz) != 0)

else
    grow(io::IOStream, sz::Integer) = truncate(io, sz)
end

function Base.resize!(io::MmapIO, newend::Ptr{Cvoid})
    io.write || throw(EOFError())

    # Resize file
    ptr = io.startptr
    newsz = Int(max(newend - ptr, io.curptr - ptr + FILE_GROW_SIZE))
    @static if !Sys.iswindows()
        grow(io.f, newsz)
    end

    if newsz > io.n
        # If we have not mapped enough memory, map more
        munmap(io)
        ptr = mmap!(io, max(newsz, io.n + MMAP_GROW_SIZE))
    end

    # Set new end
    io.endptr = ptr + newsz
    io
end

@inline function ensureroom(io::MmapIO, n::Int)
    ep = io.curptr + n
    if ep > io.endptr
        resize!(io, ep)
    end
    nothing
end

function truncate_and_close(io::MmapIO, endpos::Integer)
    io.write && msync(io)
    munmap(io)
    truncate(io.f, endpos)
    close(io.f)
end

function Base.close(io::MmapIO)
    io.write && msync(io)
    munmap(io)
    close(io.f)
end

@inline function _write(io::MmapIO, x)
    cp = io.curptr
    ep = cp + sizeof(x)
    if ep > io.endptr
        resize!(io, ep)
        cp = io.curptr
        ep = cp + sizeof(x)
    end
    unsafe_store!(Ptr{typeof(x)}(cp), x)
    io.curptr = ep
    return sizeof(x)
end
@inline Base.write(io::MmapIO, x::UInt8) = _write(io, x)
@inline Base.write(io::MmapIO, x::Int8) = _write(io, x)
@inline Base.write(io::MmapIO, x::Plain)  = _write(io, x)

function Base.unsafe_write(io::MmapIO, x::Ptr{UInt8}, n::UInt)
    cp = io.curptr
    ep = cp + n
    if ep > io.endptr
        resize!(io, ep)
        cp = io.curptr
        ep = cp + n
    end
    unsafe_copyto!(Ptr{UInt8}(cp), x, n)
    io.curptr = ep
    return n
end

@inline function _read(io::MmapIO, T::DataType)
    cp = io.curptr
    ep = cp + sizeof(T)
    ep > io.endptr && throw(EOFError())
    v = unsafe_load(Ptr{T}(cp))
    io.curptr = ep
    v
end
@inline Base.read(io::MmapIO, T::Type{UInt8}) = _read(io, T)
@inline Base.read(io::MmapIO, T::Type{Int8}) = _read(io, T)
@inline Base.read(io::MmapIO, T::PlainType) = _read(io, T)

function Base.read(io::MmapIO, ::Type{T}, n::Int) where T
    cp = io.curptr
    ep = cp + sizeof(T)*n
    ep > io.endptr && throw(EOFError())
    arr = Vector{T}(undef, n)
    unsafe_copyto!(pointer(arr), Ptr{T}(cp), n)
    io.curptr = ep
    arr
end
Base.read(io::MmapIO, ::Type{T}, n::Integer) where {T} =
    read(io, T, Int(n))

# Read a null-terminated string
function read_bytestring(io::MmapIO)
    # TODO do not try to read outside the buffer
    cp = io.curptr
    str = unsafe_string(convert(Ptr{UInt8}, cp))
    io.curptr = cp + sizeof(str) + 1
    str
end

@inline function Base.seek(io::MmapIO, offset::Integer)
    if io.startptr + offset > io.endptr
        resize!(io, io.startptr + offset)
    end
    io.curptr = io.startptr + offset
    nothing
end

@inline function Base.skip(io::MmapIO, offset::Integer)
    if io.curptr + offset > io.endptr
        resize!(io, io.curptr + offset)
    end
    io.curptr += offset
    nothing
end

Base.position(io::MmapIO) = Int64(io.curptr - io.startptr)

"""
    IndirectPointer

When writing data, we may need to enlarge the memory mapping, which would invalidate any
memory addresses arising from the old `mmap` pointer. `IndirectPointer` holds a pointer to
the `startptr` field of an MmapIO, and the offset relative to that pointer. It defers
computing a memory address until converted to a Ptr{T}, so the memory mapping can be
enlarged and addresses will remain valid.
"""
struct IndirectPointer
    ptr::Ptr{Ptr{Cvoid}}
    offset::Int
end

function IndirectPointer(io::MmapIO, offset::Integer=position(io))
    IndirectPointer(pointer_from_objref(io) + fieldoffset(MmapIO, 4), offset)
end
Base.:+(x::IndirectPointer, y::Integer) = IndirectPointer(x.ptr, x.offset+y)
Base.convert(::Type{Ptr{T}}, x::IndirectPointer) where {T} = Ptr{T}(unsafe_load(x.ptr) + x.offset)

# We sometimes need to compute checksums. We do this by first calling begin_checksum when
# starting to handle whatever needs checksumming, and calling end_checksum afterwards. Note
# that we never compute nested checksums, but we may compute multiple checksums
# simultaneously. This strategy is not thread-safe.

function begin_checksum_read(io::MmapIO)
    idx = io.nchecksum += 1
    if idx > length(io.checksum_pos)
        push!(io.checksum_pos, position(io))
    else
        @inbounds io.checksum_pos[idx] = position(io)
    end
    io
end
function begin_checksum_write(io::MmapIO, sz::Integer)
    ensureroom(io, sz)
    begin_checksum_read(io)
end
function end_checksum(io::MmapIO)
    @inbounds v = io.checksum_pos[io.nchecksum]
    io.nchecksum -= 1
    Lookup3.hash(Ptr{UInt8}(io.startptr + v), position(io) - v)
end


text:
include("common.jl")

config = Dict{String, Any}()
for d in readdir()
    if isdir(d) && isfile(joinpath(d, "config.jl"))
        include(joinpath(d, "config.jl"))
    end
end

# Setup environment for making automated plots
ENV["GKS_ENCODING"] = "utf8"
ENV["GKSwstype"]    = "100"
ENV["PLOTS_TEST"]   = "true"

for (key, cfg) in pairs(config)
    plot_convergence_summary(cfg)
end

# Special case for AlVac:
# For AlVac_Kerker no calculation is converged, so use the converged energy from AlVac
# to get a meaningful plot
cfg_AlVac        = config[joinpath(@__DIR__, "AlVac")]
cfg_AlVac_Kerker = config[joinpath(@__DIR__, "AlVac_Kerker")]
plot_convergence_summary(cfg_AlVac_Kerker, mintotals=collect_minimal_total_energy(cfg_AlVac))

max_stars_repo_name: mfherbst/supporting-adaptive-damping

max_issues_repo_name: mfherbst/supporting-adaptive-damping

max_forks_repo_name: mfherbst/supporting-adaptive-damping


text:
function matview(A::AbstractArray{T,N}) where {T,N}
    n = size(A,1)
    m = size(A,2)
    L = n*m
    reinterpret(SArray{Tuple{n,m},T,2,L}, reshape(A, (L, map(i->size(A,i),3:N)...,)))
end

extractmat(A::AbstractArray{SMatrix{N,M,T}}) where {N,M,T} =
    reshape(reinterpret(T, A), (N,M, size(A)...,))

# Array of Vectors
vecview(A::AbstractArray{T,N}) where {T,N} =
    reinterpret(SVector{size(A,1),T},A)
extractvec(A::AbstractArray{SVector{L,T}}) where {L,T} =
    reshape(reinterpret(T, A), (L, size(A)...,))


text:
module PaddleBattle

println("Start")

using ApplicationBuilderAppUtils
import Statistics: mean

using SimpleDirectMediaLayer
SDL2 = SimpleDirectMediaLayer

# True if this file is being run through the interpreter, and false if being
# compiled.
debug = true

# Override SDL libs locations if this script is being compiled for mac .app builds
if ApplicationBuilderAppUtils.is_static_compiling()
    debug = false
    #  (note that you can still change these values b/c no functions have
    #  actually been called yet, and so the original constants haven't been
    #  "compiled in".)
    if Sys.isapple()
        @eval SDL2 libSDL2 = "libSDL2.dylib"
        @eval SDL2 libSDL2_ttf = "libSDL2_ttf.dylib"
        @eval SDL2 libSDL2_mixer = "libSDL2_mixer.dylib"
    elseif Sys.iswindows()
        @eval SDL2 libSDL2 = "SDL2.dll"
        @eval SDL2 libSDL2_ttf = "SDL2_ttf.dll"
        @eval SDL2 libSDL2_mixer = "SDL2_mixer.dll"
    end
end

const assets = "assets" # directory path for game assets relative to pwd().

include("timing.jl")
include("config.jl")
include("objects.jl")
include("display.jl")
include("keyboard.jl")
include("menu.jl")

const kGAME_NAME = "Paddle Battle"
const kSAFE_GAME_NAME = "PaddleBattle"
const kBUNDLE_ORGANIZATION = "nhdalyMadeThis"

# -------- Opening a window ---------------
# Forward reference for @cfunction
function windowEventWatcher end

# Note: These are all Atomics, since they can be modified by the
# windowEventWatcher callback, which can run in another thread!
winWidth, winHeight = Threads.Atomic{Int32}(800), Threads.Atomic{Int32}(600)
winWidth_highDPI, winHeight_highDPI = Threads.Atomic{Int32}(800), Threads.Atomic{Int32}(600)
function makeWinRenderer()
    global winWidth, winHeight, winWidth_highDPI, winHeight_highDPI

    win = SDL2.CreateWindow(kGAME_NAME,
        Int32(SDL2.WINDOWPOS_CENTERED()), Int32(SDL2.WINDOWPOS_CENTERED()), winWidth[], winHeight[],
        UInt32(SDL2.WINDOW_ALLOW_HIGHDPI|SDL2.WINDOW_OPENGL|SDL2.WINDOW_RESIZABLE|SDL2.WINDOW_SHOWN));
    SDL2.SetWindowMinimumSize(win, minWinWidth, minWinHeight)
    SDL2.AddEventWatch(@cfunction(windowEventWatcher, Cint, (Ptr{Nothing}, Ptr{SDL2.Event})), win);

    # Find out how big the created window actually was (depends on the system):
    winWidth[], winHeight[], winWidth_highDPI[], winHeight_highDPI[] = getWindowSize(win)
    #cam.w[], cam.h[] = winWidth_highDPI, winHeight_highDPI

    renderer = SDL2.CreateRenderer(win, Int32(-1), UInt32(SDL2.RENDERER_ACCELERATED | SDL2.RENDERER_PRESENTVSYNC))
    SDL2.SetRenderDrawBlendMode(renderer, UInt32(SDL2.BLENDMODE_BLEND))
    return win,renderer
end

# This huge function handles all window events. I believe it needs to be a
# callback instead of just the regular pollEvent because the main thread is
# paused while resizing, whereas this callback continues to trigger.
function windowEventWatcher(data_ptr::Ptr{Nothing}, event_ptr::Ptr{SDL2.Event})::Cint
    global winWidth, winHeight, cam, window_paused, renderer, win
    ev = unsafe_load(event_ptr, 1)
    ee = ev._Event
    t = UInt32(ee[4]) << 24 | UInt32(ee[3]) << 16 | UInt32(ee[2]) << 8 | ee[1]
    t = SDL2.Event(t)
    if (t == SDL2.WindowEvent)
        event = unsafe_load( Ptr{SDL2.WindowEvent}(pointer_from_objref(ev)) )
        winevent = event.event;  # confusing, but that's what the field is called.
        if (winevent == SDL2.WINDOWEVENT_RESIZED || winevent == SDL2.WINDOWEVENT_SIZE_CHANGED)
            curPaused = window_paused[]
            window_paused[] = 1  # Stop game playing so resizing doesn't cause problems.
            winID = event.windowID
            eventWin = SDL2.GetWindowFromID(winID);
            if (eventWin == data_ptr)
                w,h,w_highDPI,h_highDPI = getWindowSize(eventWin)
                winWidth[], winHeight[] = w, h
                winWidth_highDPI[], winHeight_highDPI[] = w_highDPI, h_highDPI
                cam.w[], cam.h[] = winWidth_highDPI[], winHeight_highDPI[]
                recenterButtons!()
            end
            # Note: render after every resize event. I tried limiting it with a
            # timer, but it's hard to tune (too infrequent and the screen
            # blinks) & it didn't seem to reduce cpu significantly.
            render(sceneStack[end], renderer, eventWin)
            SDL2.GL_SwapWindow(eventWin);
            window_paused[] = curPaused  # Allow game to resume now that resizing is done.
        elseif (winevent == SDL2.WINDOWEVENT_FOCUS_LOST || winevent == SDL2.WINDOWEVENT_HIDDEN || winevent == SDL2.WINDOWEVENT_MINIMIZED)
            # Stop game playing so resizing doesn't cause problems.
            if !debug  # For debug builds, allow editing while playing
                window_paused[] = 1
            end
        elseif (winevent == SDL2.WINDOWEVENT_FOCUS_GAINED || winevent == SDL2.WINDOWEVENT_SHOWN)
            window_paused[] = 0
        end
        # Note that window events pause the game, so at the end of any window
        # event, restart the timer so it doesn't have a HUGE frame.
        start!(timer)
    end
    return 0
end

function getWindowSize(win)
    w,h,w_highDPI,h_highDPI = Int32[0],Int32[0],Int32[0],Int32[0]
    SDL2.GetWindowSize(win, w, h)
    SDL2.GL_GetDrawableSize(win, w_highDPI, h_highDPI)
    return w[],h[],w_highDPI[],h_highDPI[]
end

# Having a QuitException is useful for testing, since an exception will simply
# pause the interpreter. For release builds, the catch() block will call quitSDL().
struct QuitException <: Exception end

function quitSDL(win)
    # Need to close the callback before quitting SDL to prevent it from hanging
    # https://github.com/n0name/2D_Engine/issues/3
    SDL2.DelEventWatch(@cfunction(windowEventWatcher, Cint, (Ptr{Nothing}, Ptr{SDL2.Event})), win);
    SDL2.Mix_CloseAudio()
    SDL2.TTF_Quit()
    SDL2.Quit()
end

# -------------- Game ------------------------------

# Game State Globals
renderer = win = nothing
paddleA = Paddle(WorldPos(0,200), Vector2D(0,0), 200)
paddleB = Paddle(WorldPos(0,-200), Vector2D(0,0), 200)
ball = Ball(WorldPos(0,0), Vector2D(0,-ballSpeed))
cam = nothing
scoreA = 0
scoreB = 0
paused_ = true # start paused to show the initial menu.
paused = Ref(paused_)
window_paused = Threads.Atomic{UInt8}(0) # Whether or not the game should be running (if lost focus)
game_started_ = true # start paused to show the initial menu.
game_started = Ref(game_started_)
playing_ = true
playing = Ref(playing_)
debugText = false
audioEnabled = true
last_10_frame_times = [1.0]
timer = Timer()
i = 1

sceneStack = []  # Used to keep track of the current scene
"""
    runSceneGameLoop(scene, renderer, win, inSceneVar::Ref{Bool})
The main game loop. Implements the poll, render, update loop, delegating to the
current scene (pushes it to the top of sceneStack).
 - Polls SDL events and passes them to `handleEvents!(scene, e, t)`.
 - Calls `render(scene, renderer, win)`.
 - Calls `performUpdates!(scene, dt)`.
This loop continues until the provided `inSceneVar` is false, then pops the
scene off the sceneStack.
"""
function runSceneGameLoop(scene, renderer, win, inSceneVar::Ref{Bool})
    global last_10_frame_times, i
    push!(sceneStack, scene)
    start!(timer)
    while (inSceneVar[])
        # Don't run if game is paused by system (resizing, lost focus, etc)
        while window_paused[] != 0  # Note that this will be fixed by windowEventWatcher
            e,_ = pollEvent!()
            t = getEventType(e)
            if t == SDL2.QUIT
                throw(QuitException())
            end
            sleep(0.1)
        end
        # Reload config for debug
        if (debug) reloadConfigsFile() end

        # Handle Events
        hadEvents = true
        while hadEvents
            e,hadEvents = pollEvent!()
            t = getEventType(e)
            handleEvents!(scene,e,t)
        end

        # Render
        render(scene, renderer, win)
        if (debug && debugText) renderFPS(renderer,last_10_frame_times) end
        SDL2.RenderPresent(renderer)

        # Update
        dt = elapsed(timer)
        # Don't let the game proceed at fewer than this frames per second. If an
        # update takes too long, allow the game to actually slow, rather than
        # having too big of frames.
        min_fps = 20.0
        dt = min(dt, 1.0/min_fps)
        start!(timer)
        if debug
            last_10_frame_times = push!(last_10_frame_times, dt)
            if length(last_10_frame_times) > 10; popfirst!(last_10_frame_times) ; end
        end

        performUpdates!(scene, dt)
        #sleep(0.01)

        if (playing[] == false)
            throw(QuitException())
        end

        i += 1
    end
    pop!(sceneStack)
end
# Scenes must overload these functions:
#  render(scene, renderer, win)
#  handleEvents!(scene, e,t)
# Scenes can optionally overload this function:
function performUpdates!(scene, dt) end  # default

# ------------------------------------------

function pollEvent!()
    #SDL2.Event() = [SDL2.Event(NTuple{56, Uint8}(zeros(56,1)))]
    SDL_Event() = Array{UInt8}(zeros(56))
    e = SDL_Event()
    success = (SDL2.PollEvent(e) != 0)
    return e,success
end
function getEventType(e::Array{UInt8})
    # HAHA This is still pretty janky, but I guess that's all you can do w/ unions.
    bitcat(UInt32, e[4:-1:1])
end
function getEventType(e::SDL2.Event)
    e._Event[1]
end

function bitcat(outType::Type{T}, arr)::T where T<:Number
    out = zero(outType)
    for x in arr
        out = out << (sizeof(x)*8)
        out |= convert(T, x)  # the `convert` prevents signed T from promoting to Int64.
    end
    out
end

function renderFPS(renderer,last_10_frame_times)
    fps = Int(floor(1.0/mean(last_10_frame_times)))
    txt = "FPS: $fps"
    renderText(renderer, cam, txt, UIPixelPos(winWidth[]*1/5, 200))
end

# -------------- Game Scene ---------------------
struct GameScene end

function handleEvents!(scene::GameScene, e,t)
    global playing,paused
    # Handle Events
    if (t == SDL2.KEYDOWN || t == SDL2.KEYUP);  handleKeyPress(e,t);
    elseif (t == SDL2.QUIT);  playing[] = false;
    end

    if (paused[])
         pause!(timer)
         enterPauseGameLoop(renderer,win)
         unpause!(timer)
    end
end

function render(scene::GameScene, renderer, win)
    global ball,scoreA,scoreB,last_10_frame_times,paused,playing

    color = kBackgroundColor
    SDL2.SetRenderDrawColor(renderer, Int64(color.r), Int64(color.g), Int64(color.b), Int64(color.a))
    SDL2.RenderClear(renderer)

    renderScore(renderer)
    render(paddleA, cam, renderer)
    render(paddleB, cam, renderer)

    render(ball, cam, renderer)
end

function renderScore(renderer)
    # Size the text with a single-digit score so it doesn't move when score hits double-digits.
    txtW,_ = sizeText(cam, "Player 1: 0", defaultFontName, defaultFontSize)
    hcat_render_text(["Player 1: $scoreA","Player 2: $scoreB"], renderer, cam,
         100, UIPixelPos(screenCenterX(), 20)
         ; fixedWidth=txtW)
end

function performUpdates!(scene::GameScene, dt)
    global ball, paddleA, paddleB, scoreB, scoreA
    #if didCollide(ball, paddleA, dt);
    #     ball.pos = ball.pos - ball.vel  # undo update
    #     collide!(ball, paddleA);
    #end
    #if didCollide(ball, paddleB, dt);
    #     ball.pos = ball.pos - ball.vel  # undo update
    #     collide!(ball, paddleB);
    #end
    if willCollide(ball, paddleA, dt); collide!(ball, paddleA); end
    if willCollide(ball, paddleB, dt); collide!(ball, paddleB); end
    if (willCollide(ball, paddleA,dt) || willCollide(ball, paddleB,dt))
        # STUCK GOING TOO FAST
        slowed_dt = dt
        while (willCollide(ball, paddleA, slowed_dt) || willCollide(ball, paddleB, slowed_dt))
            slowed_dt *= .1
        end
        update!(ball, slowed_dt)
    else
        update!(ball, dt)
    end
    update!(paddleA, paddleAKeys, dt)
    update!(paddleB, paddleBKeys, dt)

    if (scoreA >= winningScore)  enterWinnerGameLoop(renderer,win, "Player 1")
    elseif (scoreB >= winningScore)  enterWinnerGameLoop(renderer,win, "Player 2")
    end
end

function enterWinnerGameLoop(renderer,win, winnerName)
    # Reset the buttons to the beginning of the game.
    buttons[:bRestart].enabled = false # Nothing to restart
    buttons[:bNewContinue].text = "New Game"
    global paused,game_started; paused[] = true; game_started[] = false;

    # Move the ball off-screen here so it doesn't show up on the winning
    # player screen.
    ball.pos = WorldPos(winWidth[] + 20, 0);

    scene = PauseScene("$winnerName wins!!", "")
    runSceneGameLoop(scene, renderer, win, paused)

    # When the pause scene returns, reset the game before starting.
    resetGame()
end
function resetGame()
    global scoreA,scoreB
    scoreB = scoreA = 0
    ball.pos = WorldPos(0,0)
    ball.vel = Vector2D(0,rand([ballSpeed,-ballSpeed]))

    paddleA.pos = WorldPos(0,200)
    paddleB.pos = WorldPos(0,-200)
end

mutable struct KeyControls
    rightDown::Bool
    leftDown::Bool
    KeyControls() = new(false,false)
end
const paddleAKeys = KeyControls()
const paddleBKeys = KeyControls()
mutable struct GameControls
    escapeDown::Bool
    GameControls() = new(false)
end
const gameControls = GameControls()

getKeySym(e) = bitcat(UInt32, e[24:-1:21])
function handleKeyPress(e,t)
    global paused,debugText
    keySym = getKeySym(e)
    keyDown = (t == SDL2.KEYDOWN)
    if (keySym == keySettings[:keyALeft])
        paddleAKeys.leftDown = keyDown
    elseif (keySym == keySettings[:keyARight])
        paddleAKeys.rightDown = keyDown
    elseif (keySym == keySettings[:keyBLeft])
        paddleBKeys.leftDown = keyDown
    elseif (keySym == keySettings[:keyBRight])
        paddleBKeys.rightDown = keyDown
    elseif (keySym == SDL2.SDLK_ESCAPE)
        if (!gameControls.escapeDown && keyDown)
            if game_started[]  # Escape shouldn't start the game.
                paused[] = !paused[]
            end
        end
        gameControls.escapeDown = keyDown
    elseif (keySym == SDL2.SDLK_BACKQUOTE)
        keyDown && (debugText = !debugText)
    end
end

# -------------- Pause Scene ---------------------
struct PauseScene
    titleText::String
    subtitleText::String
end
function enterPauseGameLoop(renderer,win)
    global paused
    scene = PauseScene("$kGAME_NAME", "Main Menu")
    runSceneGameLoop(scene, renderer, win, paused)
end
function handleEvents!(scene::PauseScene, e,t)
    global playing,paused
    # Handle Events
    if (t == SDL2.KEYDOWN || t == SDL2.KEYUP);  handleKeyPress(e,t);
    elseif (t == SDL2.MOUSEBUTTONUP || t == SDL2.MOUSEBUTTONDOWN)
        b = handleMouseClickButton!(e,t);
        if (b != nothing); run(b); end
    elseif (t == SDL2.QUIT);
        playing[]=false; paused[]=false;
    end
end

heartIcon = nothing
jlLogoIcon = nothing
function render(scene::PauseScene, renderer, win)
    global heartIcon, jlLogoIcon
    if heartIcon == nothing || jlLogoIcon == nothing
        heart_surface = SDL2.LoadBMP("assets/heart.bmp")
        heartIcon = SDL2.CreateTextureFromSurface(renderer, heart_surface) # Will be C_NULL on failure.
        SDL2.FreeSurface(heart_surface)
        jlLogo_surface = SDL2.LoadBMP("assets/jllogo.bmp")
        jlLogoIcon = SDL2.CreateTextureFromSurface(renderer, jlLogo_surface) # Will be C_NULL on failure.
        SDL2.FreeSurface(jlLogo_surface)
    end
    screenRect = SDL2.Rect(0,0, cam.w[], cam.h[])
    # First render the scene under the pause menu so it looks like the pause is over it.
    if (length(sceneStack) > 1) render(sceneStack[end-1], renderer, win) end
    color = kBackgroundColor
    SDL2.SetRenderDrawColor(renderer, Int64(color.r), Int64(color.g), Int64(color.b), 200) # transparent
    SDL2.RenderFillRect(renderer, Ref(screenRect))
    renderText(renderer, cam, scene.titleText, screenOffsetFromCenter(0,-149)
               ; fontSize=kPauseSceneTitleFontSize)
    renderText(renderer, cam, scene.subtitleText, screenOffsetFromCenter(0,-109); fontSize = kPauseSceneSubtitleFontSize)
    for b in values(buttons)
        render(b, cam, renderer)
    end
    renderText(renderer, cam, "Player 1 Controls", UIPixelPos(paddleAControlsX(),winHeight[]-169); fontSize = kControlsHeaderFontSize)
    renderText(renderer, cam, "Player 2 Controls", UIPixelPos(paddleBControlsX(),winHeight[]-169); fontSize = kControlsHeaderFontSize)
    renderText(renderer, cam, kCopyrightNotices[1], UIPixelPos(screenCenterX(), winHeight[] - 20);
            fontName="assets/fonts/FiraCode/ttf/FiraCode-Regular.ttf",
            fontSize=10)
    renderText(renderer, cam, kCopyrightNotices[2], UIPixelPos(screenCenterX(), winHeight[] - 8);
            fontName="assets/fonts/FiraCode/ttf/FiraCode-Regular.ttf",
            fontSize=10)

    _, heartPos, _, jlLogoPos =
      hcat_render_text(kProgrammedWithJuliaText, renderer, cam,
         0, UIPixelPos(screenCenterX(), winHeight[] - 35);
          fontName="assets/fonts/FiraCode/ttf/FiraCode-Regular.ttf",
          fontSize=16)
    render(heartIcon, heartPos, cam, renderer; size=UIPixelPos(16,16))
    render(jlLogoIcon, jlLogoPos, cam, renderer; size=UIPixelPos(16,16))
end

clickedButton = nothing
function handleMouseClickButton!(e, clickType)
    global clickedButton
    mx = bitcat(UInt32, e[24:-1:21])
    my = bitcat(UInt32, e[28:-1:25])
    mButton = e[17]
    if mButton != SDL2.BUTTON_LEFT
        return
    end
    didClickButton = false
    for b in values(buttons)
        if mouseOnButton(UIPixelPos(mx,my),b,cam)
            if (clickType == SDL2.MOUSEBUTTONDOWN)
                clickedButton = b
                didClickButton = true
                break
            elseif clickedButton == b && clickType == SDL2.MOUSEBUTTONUP
                clickedButton = nothing
                didClickButton = true
                return b
            end
        end
    end
    if clickedButton != nothing && clickType == SDL2.MOUSEBUTTONUP && didClickButton == false
        clickedButton = nothing
    end
    return nothing
end

function mouseOnButton(m::UIPixelPos, b::CheckboxButton, cam)
    return mouseOnButton(m, b.button, cam)
end
function mouseOnButton(m::UIPixelPos, b::AbstractButton, cam)
    if (!b.enabled) return false end
    topLeft = UIPixelPos(b.pos.x - b.w/2.0, b.pos.y - b.h/2.0)
    if m.x > topLeft.x && m.x <= topLeft.x + b.w &&
        m.y > topLeft.y && m.y <= topLeft.y + b.h
        return true
    end
    return false
end

function load_audio_files()
    global pingSound, scoreSound, badKeySound
    pingSound = SDL2.Mix_LoadWAV( "$assets/ping.wav" );
    scoreSound = SDL2.Mix_LoadWAV( "$assets/score.wav" );
    badKeySound = SDL2.Mix_LoadWAV( "$assets/ping.wav" );
end

Base.@ccallable function julia_main(ARGS::Vector{String})::Cint
    global renderer, win, paused,game_started, cam
    ApplicationBuilderAppUtils.cd_to_bundle_resources()
    win = nothing
    try
        SDL2.init()
        init_prefspath()
        load_prefs_backup()
        load_audio_files()
        music = SDL2.Mix_LoadMUS( "$assets/music.wav" );
        win,renderer = makeWinRenderer()
        cam = Camera(WorldPos(0,0),
                     Threads.Atomic{Int32}(winWidth_highDPI[]),
                     Threads.Atomic{Int32}(winHeight_highDPI[]))
        global paused,game_started; paused[] = true; game_started[] = false;
        # Warm up
        for i in 1:3
            pollEvent!()
            SDL2.SetRenderDrawColor(renderer, 200, 200, 200, 255)
            SDL2.RenderClear(renderer)
            SDL2.RenderPresent(renderer)
            #sleep(0.01)
        end
        audioEnabled && SDL2.Mix_PlayMusic( music, Int32(-1) )
        recenterButtons!()
        resetGame();  # Initialize game stuff.
        println("Preferences file: \"$prefsfile\"")
        playing[] = paused[] = true
        scene = GameScene()
        runSceneGameLoop(scene, renderer, win, playing)
    catch e
        if isa(e, QuitException)
            quitSDL(win)
        else
            rethrow()  # Every other kind of exception
        end
    end
        return 0
end

#julia_main([""])  # no julia_main if currently compiling.

end

max_stars_repo_name: NHDaly/pongClone

max_issues_repo_name: NHDaly/pongClone

max_forks_repo_name: NHDaly/pongClone


text:
"""
    _get_authors_from_PDF(dir::String) -> Vector{String}

Extract all authors from all PDFs found recursively in the passed directory.

# Arguments
- `dir::String`: $(DIR_ARGUMENT)

# Returns
- `Vector{String}`: the authors

# Throws
- Exceptions from: [`get_authors_titles`](@ref)

# Example
```jldoctest; output = false
using PDFHighlights

path_to_pdf_dir = joinpath(pathof(PDFHighlights) |> dirname |> dirname, "test", "pdf")

PDFHighlights.Internal.PDF._get_authors_from_PDF(path_to_pdf_dir) == ["Pavel Sobolev"]

# output

true
```
"""
function _get_authors_from_PDF(dir::String)::Vector{String}
    authors, _ = get_authors_titles(dir)
    return authors
end


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

using Markdown
using InteractiveUtils

# ╔═╡ a6f1b546-2383-11eb-3f21-9dc5e48afc99
using Revise

# ╔═╡ 2247a01e-2381-11eb-277c-310a471ef951
begin
	using Tilings
	using Luxor
	using Colors
end

# ╔═╡ 55b888b6-2381-11eb-1e14-19f9b90edf5d
@draw begin
	n = 6
	tile = Tilings.polytile([10*n*Point(real(exp(im*2*pi*x/n)), imag(exp(im*2*pi*x/n))) for x in 0:(n-1)])
	Tilings.draw(tile)
end

# ╔═╡ 606250f2-2385-11eb-1f87-9b4aeba63f73

# ╔═╡ 56b6d738-2382-11eb-1910-5faa6076fe30
@svg begin
	function ngonstyle(x)
		setline(2.0)
		setcolor("red")
		fillpreserve()
		setcolor("black")
		strokepath()
	end
	# rotate(pi/2)
	background(Colors.RGB(1.0,0.0,0.0))
	scale(1.5)
	Tilings.hexagonaltile(Tilings.ngontile(6,30, ngonstyle), 40,5)
	scale(1/1.5)
	setcolor((1.0,0.5,0.5))
	ngon(0,0,30*6,6,0,:fill)
	setcolor("black")
	Tilings.hexagonaltile(Tilings.ngontile(6,30,ngonstyle), 35,2)
	rotate(pi/2)
	# draw(Tilings.ngontile(6,35*sqrt(3)))
	draw(Tilings.ngontile(6,35*2*sqrt(3)))
	draw(Tilings.ngontile(6,35*1.5*4))
	draw(Tilings.ngontile(6,35*1.5*6))
	draw(Tilings.ngontile(6,35*1.5*8))
	rotate(pi/2)
	draw(Tilings.ngontile(6,30*6))
	# draw(Tilings.ngontile(6,30*12))
end

# ╔═╡ c62acd62-23b2-11eb-3579-0f0eb87ffcdb
30*7/(1.5*35)

# ╔═╡ 5338dda0-238b-11eb-2dd2-6f4bed2df8a0
@draw begin
	local N = 4
	local sidelen = 80
	local height = sidelen*real(exp(im*pi/N))
	local function sstyle(x)
		setcolor((0.4+0.5*rand(),0.1,0.4+0.2*rand()))
		fillpreserve()
		setcolor("black")
		strokepath()
	end
	setline(1.0)
	local stile = Tilings.polytile([
			Point(0.0, 0.0),
			sidelen*Point(real(exp(im*pi/N)), imag(exp(im*pi/N))),
			sidelen*Point(real(exp(-im*pi/N)), imag(exp(-im*pi/N)))], sstyle)
	for i=1:N
		rotate(pi*2/N)
		Tilings.draw(stile)
		@layer begin
			rotate(pi)
			translate(-2*height,0)
			draw(stile)
		end
		@layer begin
			rotate(pi+pi/N)
			translate(-sidelen,0)
			rotate(pi+pi/N)
			draw(stile)
			rotate(-2*pi/N)
			draw(stile)
		end
	end

	for i=1:2N
		setline(1)
		rotate(pi*2/N)
		@layer begin
			rotate(pi+pi/N)
			translate(-sidelen,0)
			rotate(pi/N)
			translate(-2height,0)
			draw(stile)
			translate(2height,0)
			rotate(-2*pi/N)
			translate(-2height,0)
			draw(stile)
		end
		@layer begin
			translate(2height,0)
			draw(stile)
		end
		@layer begin
			rotate(pi/N)
			translate(2sidelen,0)
			rotate(pi/N)
			draw(stile)
			rotate(-2pi/N)
			draw(stile)
		end
	end
end

# ╔═╡ 7297798a-23a0-11eb-0bbc-a38faf9ca80e
function spiral(N=17, dent=N*0.59)
	@draw begin
		sidelen = 100
		# dent = N*0.59
		height = sidelen*real(exp(im*pi/N))
		function sstyle(x)
			setcolor((0.3+0.7*rand(),0.0,0.2+0.5*rand()))
			# sethue(randomhue())
			fillpreserve()
			setcolor("black")
			strokepath()
		end
		setline(1.0)
		stile = Tilings.polytile([
				Point(0.0, 0.0),
				sidelen*0.6*Point(real(exp(im*(pi+dent)/N)), imag(exp(im*(pi+dent)/N))),
				sidelen*Point(real(exp(im*pi/N)), imag(exp(im*pi/N))),
				sidelen*Point(real(exp(-im*pi/N)), imag(exp(-im*pi/N))),
				sidelen*0.6*Point(real(exp(-im*(pi-dent)/N)), imag(exp(-im*(pi-dent)/N)))],
			sstyle)
		for i=1:N
			rotate(pi*2/N)
			Tilings.draw(stile)
			@layer begin
				rotate(pi)
				translate(-2*height,0)
				draw(stile)
			end
			@layer begin
				transform([-1 0 0 1 0 0])
				rotate(pi+pi/N)
				translate(sidelen,0)
				rotate(pi/N)
				draw(stile)
				rotate(-2*pi/N)
				draw(stile)
			end
		end

		for i=1:2N
			setline(1)
			rotate(pi*2/N)
			@layer begin
				rotate(pi+pi/N)
				translate(-sidelen,0)
				rotate(pi/N)
				translate(-2height,0)
				draw(stile)
				translate(2height,0)
				rotate(-2*pi/N)
				translate(-2height,0)
				draw(stile)
			end
			@layer begin
				translate(2height,0)
				transform([1 0 0 -1 0 0])
				draw(stile)
			end
			@layer begin
				rotate(pi/N)
				translate(2sidelen,0)
				rotate(pi/N)
				transform([1 0 0 -1 0 0])
				draw(stile)
				rotate(2pi/N)
				draw(stile)
			end
		end
	end
end

# ╔═╡ 4f93fd3c-23a0-11eb-187a-31f128f923af
spiral(8, 8*.59)

# ╔═╡ 3063b7e6-2462-11eb-2285-a978a12c540d
function spiral2()
	@svg begin
		local function sstyle(x)
			setcolor((0.3+0.6*rand(),0.05,0.3+0.4*rand()))
			fillpreserve()
			setline(1)
			setcolor("lavender")
			strokepath()
		end
		setline(0.5)
		θ = pi/12
		slen = 13.0
		# translate(0,150)
		a1 = exp(-θ*im)
		a2 = a1 + exp(0θ*im)
		a3 = a2 + exp(1θ*im)
		a4 = a3 + exp(2θ*im)
		b1 = complex(1.0,0.0)
		b2 = b1 + exp(θ*im)
		b3 = b2 + exp(2θ*im)
		b4 = b3 + exp(3θ*im)

		points = [Point(0.0, 0.0),
				  Point(reim(a1)), Point(reim(a2)), Point(reim(a3)), Point(reim(a4)),
				  Point(reim(b4)), Point(reim(b3)), Point(reim(b2)), Point(reim(b1))]
		local stile = Tilings.polytile(points, sstyle)
		scale(slen)

		rotate(5θ)
		translate(reim(b4*0.5)...)
		rotate(-5θ)

		@layer begin
			rotate(-7θ)
			for i=1:8
				draw(stile)
				rotate(θ)
			end
			draw(stile)
			for j=0:7
				for i=1:12
					for k=1:j
						translate(reim(b3-a4)...)
						draw(stile)
						@layer begin
							translate(reim(a4)...)
							rotate(-2θ)
							transform([1 0 0 -1 0 0])
							translate(reim(-b4)...)
							draw(stile)
						end
					end
					translate(reim(b3-a4)...)
					draw(stile)
					rotate(θ)
					draw(stile)
					@layer begin
						translate(reim(a4)...)
						rotate(-3θ)
						transform([1 0 0 -1 0 0])
						translate(reim(-b3)...)
						draw(stile)
					end
				end
			end
		end
		transform([-1 0 0 1 0 0])
		rotate(5θ)
		translate(reim(-b4)...)
		@layer begin
			for i=1:15
				draw(stile)
				rotate(-θ)
			end
			draw(stile)
			# setline(3)
			for j=0:7
				for i=1:(12 + (j==0))
					for k=1:j
						draw(stile)
						@layer begin
							translate(reim(a4)...)
							rotate(-2θ)
							transform([1 0 0 -1 0 0])
							translate(reim(-b4)...)
							draw(stile)
						end
						translate(reim(-b3+a4)...)
					end

					draw(stile)
					rotate(-θ)
					draw(stile)
					@layer begin
						translate(reim(a4)...)
						rotate(-2θ)
						transform([1 0 0 -1 0 0])
						translate(reim(-b4)...)
						draw(stile)
					end
					translate(reim(-b3+a4)...)
				end
			end
		end
	end
end

# ╔═╡ 65841844-2462-11eb-2c40-e908aa584922
spiral2()

# ╔═╡ Cell order:
# ╠═a6f1b546-2383-11eb-3f21-9dc5e48afc99
# ╠═2247a01e-2381-11eb-277c-310a471ef951
# ╠═55b888b6-2381-11eb-1e14-19f9b90edf5d
# ╠═606250f2-2385-11eb-1f87-9b4aeba63f73
# ╠═56b6d738-2382-11eb-1910-5faa6076fe30
# ╠═c62acd62-23b2-11eb-3579-0f0eb87ffcdb
# ╠═5338dda0-238b-11eb-2dd2-6f4bed2df8a0
# ╠═7297798a-23a0-11eb-0bbc-a38faf9ca80e
# ╠═4f93fd3c-23a0-11eb-187a-31f128f923af
# ╠═3063b7e6-2462-11eb-2285-a978a12c540d
# ╠═65841844-2462-11eb-2c40-e908aa584922


text:
"""
    count_nucleotides(strand)

The frequency of each nucleotide within `strand` as a dictionary.

Invalid strands raise a `DomainError`.

"""
function count_nucleotides(strand)
  dna = Dict('A'=>0,'C'=>0,'G'=>0,'T'=>0)

  for i in strand
    try
      dna[i] += 1
    catch
      throw(DomainError("Invalid Key"))
    end
  end

  return dna
end


text:
some_elm(::Type{Nothing}) = nothing

# just write it out as nothing
compress_then_write(b::Vector{Nothing}, io) = (len = 0, type = Nothing, orig_len = length(b))

# the dispatch for Union{T, Missing}
# 1. comporess the missing
# 2. and also load the missing
compress_then_write(b::Vector{Union{T, Nothing}}, io) where T = begin
    #S = nonmissingtype(eltype(b))
    b_S = T[isnothing(b) ? some_elm(T) : b for b in b]

    metadata = compress_then_write(b_S, io)

    b_m = isnothing.(b)
    metadata2 = compress_then_write(b_m, io)

    (
     len = max(metadata.len, metadata2.len),
     type = T,
     metadata = metadata,
     missingmeta = metadata2,
    )
end

column_loader!(buffer, ::Type{Union{Nothing,T}}, io, metadata) where {T} = begin
    # read the content
    Tmeta = metadata.metadata

    t_pre = Vector{Union{Nothing, T}}(column_loader!(buffer, Tmeta.type, io, Tmeta))
    m = column_loader(Bool, io, metadata.missingmeta)

    t_pre[m] .= nothing
    t_pre
end

column_loader!(_, ::Type{Nothing}, _, metadata) = Vector{Nothing}(nothing, metadata.orig_len)