text:
function create_tarball(
    predicate::Function,
    tar::IO,
    root::String;
    buf::Vector{UInt8} = Vector{UInt8}(undef, DEFAULT_BUFFER_SIZE),
)
    write_tarball(tar, root, buf=buf) do sys_path, tar_path
        hdr = path_header(sys_path, tar_path)
        hdr.type != :directory && return hdr, sys_path
        paths = Dict{String,String}()
        for name in readdir(sys_path)
            sys_path′ = joinpath(sys_path, name)
            predicate(sys_path′) || continue
            paths[name] = sys_path′
        end
        return hdr, paths
    end
end

function recreate_tarball(
    tar::IO,
    root::String,
    skeleton::IO;
    buf::Vector{UInt8} = Vector{UInt8}(undef, DEFAULT_BUFFER_SIZE),
)
    check_skeleton_header(skeleton, buf=buf)
    globals = Dict{String,String}()
    while !eof(skeleton)
        hdr = read_header(skeleton, globals=globals, buf=buf, tee=tar)
        hdr === nothing && break
        check_header(hdr)
        sys_path = joinpath(root, hdr.path)
        if hdr.type == :file
            write_data(tar, sys_path, size=hdr.size, buf=buf)
        end
    end
end

function rewrite_tarball(
    predicate::Function,
    old_tar::IO,
    new_tar::IO;
    buf::Vector{UInt8} = Vector{UInt8}(undef, DEFAULT_BUFFER_SIZE),
)
    tree = Dict{String,Any}()
    read_tarball(predicate, old_tar; buf=buf) do hdr, parts
        isempty(parts) && return
        node = tree
        name = pop!(parts)
        for part in parts
            node′ = get(node, part, nothing)
            if !(node′ isa Dict)
                node′ = node[part] = Dict{String,Any}()
            end
            node = node′
        end
        if hdr.type == :hardlink
            node′ = tree
            for part in split(hdr.link, '/')
                node′ = node′[part]
            end
            hdr′ = Header(node′[1], path=hdr.path, mode=hdr.mode)
            node[name] = (hdr′, node′[2])
        else
            if !(hdr.type == :directory && get(node, name, nothing) isa Dict)
                node[name] = (hdr, position(old_tar))
            end
            skip_data(old_tar, hdr.size)
        end
    end
    write_tarball(new_tar, tree, buf=buf) do node, tar_path
        if node isa Dict
            hdr = Header(tar_path, :directory, 0o755, 0, "")
            return hdr, node
        else
            hdr, pos = node
            mode = hdr.type == :file && iszero(hdr.mode & 0o100) ? 0o644 : 0o755
            hdr′ = Header(hdr; path=tar_path, mode=mode)
            data = hdr.type == :directory ? nothing : (old_tar, pos)
            return hdr′, data
        end
    end
end

function write_tarball(
    callback::Function,
    tar::IO,
    sys_path::Any,
    tar_path::String = ".";
    buf::Vector{UInt8} = Vector{UInt8}(undef, DEFAULT_BUFFER_SIZE),
)
    hdr, data = callback(sys_path, tar_path)
    if hdr.type == :directory
        data isa Union{Nothing, AbstractDict{<:AbstractString}} ||
            error("callback must return a dict of strings, got: $(repr(data))")
    else
        data isa Union{Nothing, AbstractString, IO, Tuple{IO,Integer}} ||
            error("callback must return nothing, string or IO, got: $(repr(data))")
    end
    w = 0
    if tar_path != "."
        w += write_tarball(tar, hdr, data, buf=buf)
    end
    data isa AbstractDict && for name in sort!(collect(keys(data)))
        sys_path′ = data[name]
        tar_path′ = tar_path == "." ? name : "$tar_path/$name"
        w += write_tarball(callback, tar, sys_path′, tar_path′, buf=buf)
    end
    if tar_path == "." && w == 0
        w += write_tarball(tar, hdr, data, buf=buf)
    end
    return w
end

function write_tarball(
    tar::IO,
    hdr::Header,
    data::Any = nothing;
    buf::Vector{UInt8} = Vector{UInt8}(undef, DEFAULT_BUFFER_SIZE),
)
    check_header(hdr)
    w = write_header(tar, hdr, buf=buf)
    if hdr.type == :file
        data isa Union{AbstractString, IO, Tuple{IO,Integer}} ||
            throw(ArgumentError("file record requires path or IO: $(repr(hdr))"))
        w += write_data(tar, data, size=hdr.size, buf=buf)
    end
    return w
end

function write_header(
    tar::IO,
    hdr::Header;
    buf::Vector{UInt8} = Vector{UInt8}(undef, DEFAULT_BUFFER_SIZE),
)
    # extract values
    path = hdr.path
    size = hdr.size
    link = hdr.link

    # determine if an extended header is needed
    extended = Pair{String,String}[]
    # WARNING: don't change the order of these insertions
    # they are inserted and emitted in sorted order by key
    if ncodeunits(link) > 100
        push!(extended, "linkpath" => link)
        link = "" # empty in standard header
    end
    prefix = ""
    name = path
    if ncodeunits(path) > 100
        if ncodeunits(path) < 256 && (i = findprev('/', path, 100)) !== nothing
            # try splitting into prefix and name
            prefix = path[1:prevind(path, i)]
            name   = path[nextind(path, i):end]
        end
        if ncodeunits(name) > 100 || ncodeunits(prefix) > 155
            push!(extended, "path" => path)
            prefix = name = "" # empty in standard header
        end
    end
    if (size >>> (3*12)) != 0
        push!(extended, "size" => string(size))
        # still written in binary in standard header
    end

    # emit extended header if necessary
    w = 0
    if !isempty(extended)
        @assert issorted(extended)
        w += write_extended_header(tar, extended, buf=buf)
    end
    # emit standard header
    std_hdr = Header(hdr; link=link)
    w += write_standard_header(tar, std_hdr, name=name, prefix=prefix, buf=buf)
end

function write_extended_header(
    tar::IO,
    metadata::Vector{Pair{String,String}};
    type::Symbol = :x, # default: non-global extended header
    name::AbstractString = "",
    prefix::AbstractString = "",
    link::AbstractString = "",
    mode::Integer = 0o000,
    buf::Vector{UInt8} = Vector{UInt8}(undef, DEFAULT_BUFFER_SIZE),
)
    type in (:x, :g) ||
        throw(ArgumentError("invalid type flag for extended header: $(repr(type))"))
    d = IOBuffer()
    for (key, val) in metadata
        entry = " $key=$val\n"
        n = l = ncodeunits(entry)
        while n < l + ndigits(n)
            n = l + ndigits(n)
        end
        @assert n == l + ndigits(n)
        write(d, "$n$entry")
    end
    path = isempty(name) || isempty(prefix) ? "$prefix$name" : "$prefix/$name"
    hdr = Header(path, type, mode, position(d), link)
    w = write_standard_header(tar, hdr, name=name, prefix=prefix, buf=buf)
    w += write_data(tar, seekstart(d), size=hdr.size, buf=buf)
end

function write_standard_header(
    tar::IO,
    hdr::Header;
    name::AbstractString = "",
    prefix::AbstractString = "",
    buf::Vector{UInt8} = Vector{UInt8}(undef, DEFAULT_BUFFER_SIZE),
)
    name = String(name)
    prefix = String(prefix)
    type = from_symbolic_type(hdr.type)
    link = hdr.link

    # octal strings for size and mode
    m = string(hdr.mode, base=8, pad=6)
    s = string(hdr.size, base=8, pad=11)

    # error checking (presumes checks done by write_header)
    hdr.size < 0 &&
        throw(ArgumentError("negative file size is invalid: $(hdr.size)"))
    ncodeunits(prefix) ≤ 155 ||
        throw(ArgumentError("path prefix too long for standard header: $(repr(prefix))"))
    ncodeunits(name) ≤ 100 ||
        throw(ArgumentError("path name too long for standard header: $(repr(name))"))
    ncodeunits(link) ≤ 100 ||
        throw(ArgumentError("symlink target too long for standard header: $(repr(link))"))
    ncodeunits(m) ≤ 6 ||
        throw(ArgumentError("mode too large for standard header: 0o$m"))
    ncodeunits(s) ≤ 12 ||
        isempty(name) && isempty(prefix) || # after extended header, large size ok
        throw(ArgumentError("size too large for standard header: $size (0o$s)"))
    isascii(type) ||
        throw(ArgumentError("non-ASCII type flag value: $(repr(type))"))

    # construct header block
    data = view(buf, 1:512)
    h = IOBuffer(fill!(data, 0x00), write=true, truncate=false)
    write(h, name)              # name
    seek(h, 100)
    write(h, "$m \0")           # mode
    write(h, "000000 \0")       # uid
    write(h, "000000 \0")       # gid
    if ncodeunits(s) ≤ 12       # size
        write(h, s)
        if ncodeunits(s) < 12
            write(h, ' ')
        end
    else
        @assert isempty(name) && isempty(prefix) # after extended header
        # emulate GNU tar: write binary size with leading bit set
        write(h, 0x80 | ((size >> (8*11)) % UInt8))
        for i = 10:-1:0
            write(h, (size >> 8i) % UInt8)
        end
    end
    write(h, "00000000000 ")    # mtime
    write(h, "        ")        # chksum (blank)
    write(h, type)              # typeflag
    @assert position(h) == 157
    write(h, link)              # linkname
    seek(h, 257)
    write(h, "ustar\0")         # magic
    write(h, "00")              # version
    skip(h, 64)                 # uname & gname
    write(h, "000000 \0")       # devmajor
    write(h, "000000 \0")       # devminor
    @assert position(h) == 345
    write(h, prefix)            # prefix

    # fix the checksum
    c = string(sum(data), base=8, pad=6)
    @assert ncodeunits(c) ≤ 6
    seek(h, 148)
    write(h, "$c\0 ")
    @assert position(h) == 156

    # write header
    w = write(tar, data)
    @assert w == 512
    return w
end

function write_data(
    tar::IO,
    data::IO;
    size::Integer,
    buf::Vector{UInt8} = Vector{UInt8}(undef, DEFAULT_BUFFER_SIZE),
)
    size < 0 &&
        throw(ArgumentError("cannot write negative data: $size"))
    w, t = 0, round_up(size)
    while size > 0
        b = min(size, length(buf))
        n = readbytes!(data, buf, b)
        n < b && eof(data) && throw(EOFError())
        w += write(tar, view(buf, 1:n))
        size -= n
        t -= n
    end
    @assert size == 0
    @assert 0 ≤ t < 512
    t > 0 && (w += write(tar, fill!(view(buf, 1:t), 0)))
    return w
end

function write_data(
    tar::IO,
    (data, pos)::Tuple{IO,Integer};
    size::Integer,
    buf::Vector{UInt8} = Vector{UInt8}(undef, DEFAULT_BUFFER_SIZE),
)
    seek(data, pos)
    write_data(tar, data, size=size, buf=buf)
end

function write_data(
    tar::IO,
    file::String;
    size::Integer,
    buf::Vector{UInt8} = Vector{UInt8}(undef, DEFAULT_BUFFER_SIZE),
)
    open(file) do data
        write_data(tar, data, size=size, buf=buf)
        eof(data) || error("data file too large: $data")
    end
end


text:
struct TrainingSet{DT <: Number, PS <: ParameterSpace}
    paramspace::PS

    X::Array{DT,4}
    V::Array{DT,4}
    A::Array{DT,4}
    Φ::Array{DT,4}
    W::Array{DT,2}
    K::Array{DT,2}
    M::Array{DT,2}

    function TrainingSet(pspace::PS, X::Array{DT}, V::Array{DT}, A::Array{DT}, Φ::Array{DT}, W::Array{DT}, K::Array{DT}, M::Array{DT}) where {DT,PS}
        new{DT,PS}(pspace, X, V, A, Φ, W, K, M)
    end
end

function TrainingSet(DT, ns, nd, np, nh, pspace::ParameterSpace)
    X = zeros(nd, np, ns, length(pspace))
    V = zeros(nd, np, ns, length(pspace))
    A = zeros(nd, np, ns, length(pspace))
    Φ = zeros(nd, nh, ns, length(pspace))
    W = zeros(ns, length(pspace))
    K = zeros(ns, length(pspace))
    M = zeros(ns, length(pspace))
    TrainingSet(pspace, X, V, A, Φ, W, K, M)
end

function TrainingSet(ns::Int, poisson::PoissonSolver{DT}, particles::ParticleList{DT}, pspace::ParameterSpace) where {DT}
    TrainingSet(DT, ns, 1, length(particles), length(poisson), pspace)
end

Base.:(==)(ts1::TrainingSet, ts2::TrainingSet) = (
                        ts1.paramspace == ts2.paramspace
                     && ts1.X == ts2.X
                     && ts1.V == ts2.V
                     && ts1.A == ts2.A
                     && ts1.Φ == ts2.Φ
                     && ts1.W == ts2.W
                     && ts1.K == ts2.K
                     && ts1.M == ts2.M)

function TrainingSet(h5::H5DataStore, path::AbstractString="/")
    X = read(h5["$path/X"])
    V = read(h5["$path/V"])
    A = read(h5["$path/A"])
    Φ = read(h5["$path/Φ"])
    W = read(h5["$path/W"])
    K = read(h5["$path/K"])
    M = read(h5["$path/M"])

    pspace = ParameterSpace(h5, "$path/parameterspace")

    TrainingSet(pspace, X, V, A, Φ, W, K, M)
end

function h5save(TS::TrainingSet, h5::H5DataStore, path::AbstractString="/")
    s = _create_group(h5, path)
    s["X"] = TS.X
    s["V"] = TS.V
    s["A"] = TS.A
    s["Φ"] = TS.Φ
    s["W"] = TS.W
    s["K"] = TS.K
    s["M"] = TS.M

    h5save(TS.paramspace, h5, "$path/parameterspace")
end

function h5load(::Type{TrainingSet}, h5::H5DataStore; path::AbstractString="/")
    TrainingSet(h5, path)
end

ext: jl


text:
require(dirname(@__FILE__) * "/../src/Epicenter.jl")
using Epicenter

type TestThing
    member1
end

# test data
testmodel = TestThing({ "string_key" => 69 })

# test symbol tree
record(:testmodel, :member1, "string_key")

record(:testmodel, :member1)

record(:testmodel)
# -------

@assert hasrecord(:testmodel)
@assert hasrecord(:testmodel, :member1)
@assert hasrecord(:testmodel, :member1, "string_key")

#@assert !hasrecord(:testmodel, :member1, "string_key", "does_not_exist")

@assert getrecord(:testmodel) != nothing
@assert getrecord(:testmodel, :member1, "string_key") == 69


text:
module Protoboard

using LCAlgebra
using FF
using SparseArrays
import SparseArrays: nonzeroinds

# Constraint system chunk. 1st element is one signal.
# Other elements may be corresponded to other signals, shifted to any offset.
# nsignals - number of signals
# xsignals - number of external signals (>=1, because 1st signal is external everywhere)
# muted - bitmask of muted signals, array from 1 to nsignals

mutable struct CSTemplate
  nsignals::Int64
  xsignals::Int64
  unmuted::Vector{Int64}
  constraints::Vector{QEQ}
end

and(l::Bool...) = reduce((x::Bool, y::Bool)->x&&y, l)

function optimize!(cs::CSTemplate)
  constraints = cs.constraints
  nconstraints=length(constraints)
  nsignals=cs.nsignals
  xsignals=cs.xsignals
  unmuteds = Set(1:nsignals)
  unmutedc = Set(1:nconstraints)
  linearc = Set()

  indexs = Vector{Set}
  indexc = Vector{Set}

  function mute(ic::Int64, is::Int64)
    #mute constraint
    for j in indexc[ic]
      delete!(indexs[j], ic)
    end
    delete!(unmutedc, ic)

    #mute signal
    local c = constraints[ic]
    for i in indexs[is]
      if i != ic
        constraints[i] = substitute(constraints[i], is, c)
        local newindexc = Set(ids(constraints[i]))
        local to_delete = setdiff(indexc[ic], newindexc)
        local to_insert = setdiff(newindexc, indexc[ic])
        indexc[ic] = newindexc
        for j in to_delete
          delete!(indexs[j], i)
        end
        for j in to_insert
          insert!(indexs[j], i)
        end
        if islc(constraints[i])
          push!(linearc, i)
        end
      end
    end
    delete!(unmuteds, is)
  end

  for i in 1:nsignals
    indexs[i] = Set()
  end

  for i in 1:nconstraints
    c = cs.constraints[i]
    ids = ids(c)
    indexc[i] = Set(ids)
    for j in ids
      push!(indexs[j], i)
    end
  end

  for i in 1:nconstraints
    if islc(constraints[i])
      push!(linearc, i)
    end
  end

  while length(linearc) > 0
    for i in collect(linearc)
      c = constraints[i]
      if iszero(c)
        delete!(linearc, i)
        for j in indexc[i]
          delete!(indexs[j], i)
        end
        delete!(unmutedc, i)
      else
        delete!(linearc, i)
        ls = maximum(indexc[i])
        if ls > xsignals
          mute(i, ls)
        end
      end
    end
  end

  cs.unmuted = collect(unmuteds)
  cs.constraints = cs.constraints[collect(unmutedc)]
  return cs
end

end # module


text:
module Probabilistic

using Base.Meta: quot
import Base: copy
using Distributions
using MacroTools
using MacroTools: postwalk, prettify
using PyPlot

include("model.jl")
include("plot.jl")

include("compiler/utils.jl")
include("compiler/compiler.jl")

include("distributions/bimix.jl")
include("distributions/multi.jl")

include("sampling/trace.jl")
include("sampling/metropolis.jl")
include("sampling/sample.jl")

export @model, Model, condition, observe, sample, Metropolis, BiMix, Multi, plotresult

end


text:
using Revise
using SnpArrays
using DelimitedFiles
using Random
using MendelIHT
using Knockoffs
using LinearAlgebra
using GLMNet
using Distributions
using DataFrames
using CSV
using Printf
BLAS.set_num_threads(1)

# predict with estimated β̂ (R2 = 1 - RSS/TSS)
function R2(X::AbstractMatrix, y::AbstractVector, β̂::AbstractVector)
    μ = y - X * β̂
    tss = y .- mean(y)
    return 1 - dot(μ, μ) / dot(tss, tss)
end

# predict with a low dimensional fit
function R2(Xtrain::AbstractMatrix, Xtest::AbstractMatrix,
    ytrain::AbstractVector, ytest::AbstractVector, β̂::AbstractVector)
    # fit low dimensional model on original data
    idx = findall(!iszero, β̂)
    β_new = zeros(length(β̂))
    β_new[idx] .= Xtrain[:, idx] \ ytrain
    # predict with low diemensional model on new data
    μ = ytest - Xtest * β_new
    t = ytest .- mean(ytest)
    return 1 - dot(μ, μ) / dot(t, t)
end

function TP(correct_groups, signif_groups)
    return length(signif_groups ∩ correct_groups) / length(correct_groups)
end

function FDR(correct_groups, signif_groups)
    FP = length(signif_groups) - length(signif_groups ∩ correct_groups) # number of false positives
    # FPR = FP / (FP + TN) # https://en.wikipedia.org/wiki/False_positive_rate#Definition
    FDR = FP / length(signif_groups)
    return FDR
end
function tune_k(y::AbstractVector, xko_la::AbstractMatrix, original::Vector{Int},
    knockoff::Vector{Int}, groups::Vector{Int}, fdr::Float64, best_k::Int
    )
    # do a grid search for best sparsity level
    best_β = Float64[]
    best_err = Inf
    for cur_k in best_k:5:round(Int, 1.5best_k)
        result = fit_iht(y, xko_la, k=cur_k, init_beta=true, max_iter=500)
        W = coefficient_diff(result.beta, groups, original, knockoff)
        τ = threshold(W, fdr, :knockoff)
        detected = count(x -> x ≥ τ, W)
        if abs(detected - best_k) < best_err
            best_β = copy(result.beta)
            best_err = abs(detected - best_k)
        end
        println("wrapped CV says best_k = $best_k; using k = $cur_k detected $detected")
        GC.gc()
    end
    return best_β
end
function get_signif_groups(β, groups)
    correct_groups = Int[]
    for i in findall(!iszero, β)
        g = groups[i]
        g ∈ correct_groups || push!(correct_groups, g)
    end
    return correct_groups
end

# todo: use_PCA and bolt
function run_sims(
    x::SnpArray,
    original::Vector{Int},
    knockoff::Vector{Int},
    groups::Vector{Int},
    seed::Int,
    confounders::Int = 0
    )
    #
    # import data (first 10000 samples of chr 10)
    #
    chr = 10
    xla = convert(Matrix{Float64}, @view(x[1:10000, original]), center=true, scale=true, impute=true)
    xko_la = convert(Matrix{Float64}, @view(x[1:10000, :]), center=true, scale=true, impute=true)
    cur_dir = pwd()

    #
    # Import PCs
    #
    z = readdlm("/scratch/users/bbchu/ukb_SHAPEIT/subset_pca/ukb.10k.chr$chr.projections.txt")
    standardize!(z)

    #
    # simulate phenotypes using UKB chr10 subset
    #
    n, p = size(xla)
    # simulate β
    Random.seed!(seed)
    k = 100 # number of causal SNPs
    h2 = 0.5 # heritability
    d = Normal(0, sqrt(h2 / (2k))) # from paper: Efficient Implementation of Penalized Regression for Genetic Risk Prediction
    β = zeros(p)
    β[1:k] .= rand(d, k)
    shuffle!(β)
    # simulate y
    ϵ = Normal(0, 1 - h2)
    y = xla * β + rand(ϵ, n)
    #
    # confounders are PCs and have effect size ±0.2
    #
    if confounders > 0
        PCs = z[:, 1:confounders]
        γ = [rand(-1:2:1) * 0.2 for i in 1:confounders]
        y += PCs * γ
    end

    #
    # Run standard IHT
    #
    Random.seed!(seed)
    path = 10:10:200
    mses = cv_iht(y, xla, path=path, init_beta=true)
    GC.gc()
    Random.seed!(seed)
    k_rough_guess = path[argmin(mses)]
    dense_path = (k_rough_guess - 9):(k_rough_guess + 9)
    mses_new = cv_iht(y, xla, path=dense_path, init_beta=true)
    GC.gc()
    Random.seed!(seed)
    iht_result = fit_iht(y, xla, k=dense_path[argmin(mses_new)], init_beta=true, max_iter=500)
    @show iht_result
    GC.gc()

    #
    # Run standard lasso
    #
    Random.seed!(seed)
    @time lasso_cv = glmnetcv(xla, y, nfolds=5, parallel=true)

    #
    # run knockoff IHT
    #
    Random.seed!(seed)
    path = 10:10:200
    mses = cv_iht(y, xko_la, path=path, init_beta=true)
    GC.gc()
    Random.seed!(seed)
    k_rough_guess = path[argmin(mses)]
    dense_path = (k_rough_guess - 9):(k_rough_guess + 9)
    mses_new = cv_iht(y, xko_la, path=dense_path, init_beta=true)
    GC.gc()
    Random.seed!(seed)
    ko_iht_result = fit_iht(y, xko_la, k=dense_path[argmin(mses_new)],
        init_beta=true, max_iter=500)
    @show ko_iht_result

    #
    # Run knockoff lasso
    #
    Random.seed!(seed)
    @time ko_lasso_cv = glmnetcv(xko_la, y, nfolds=5, parallel=true)

    for fdr in [0.05, 0.1, 0.25, 0.5]
        top_dir = cur_dir * "/fdr$fdr/"
        new_dir = cur_dir * "/fdr$fdr/sim$seed"
        isdir(top_dir) || mkdir(top_dir)
        isdir(new_dir) || mkdir(new_dir)
        cd(new_dir)
        writedlm("y_true.txt", y)
        # writedlm("y_true_confound.txt", y)
        writedlm("beta_true.txt", β)

        #
        # write results
        #
        writedlm("iht.beta", iht_result.beta)
        writedlm("lasso.beta", coef(lasso_cv))
        writedlm("iht.knockoff.beta", ko_iht_result.beta)
        writedlm("lasso.knockoff.beta", coef(ko_lasso_cv))

        #
        # run knockoff IHT with wrapped cross validation
        #
        Random.seed!(seed)
        path = 10:10:200
        z = ones(Float64, 10000)
        mses = cv_iht_knockoff(y, xko_la, z, original, knockoff, fdr, path=path,
            init_beta=true, group_ko=groups)
        GC.gc()
        Random.seed!(seed)
        k_rough_guess = path[argmin(mses)]
        dense_path = (k_rough_guess - 9):(k_rough_guess + 9)
        mses_new = cv_iht_knockoff(y, xko_la, z, original, knockoff, fdr,
            path=dense_path, init_beta=true, group_ko=groups)
        GC.gc()
        # adjust sparsity level so it best matches sparsity chosen by ko filter
        Random.seed!(seed)
        best_k = dense_path[argmin(mses_new)]
        best_β = tune_k(y, xko_la, original, knockoff, groups, fdr, best_k)
        writedlm("iht.knockoff.cv.beta", best_β)

        #
        # compare R2 across populations, save result in a dataframe
        #
        β_iht = vec(readdlm("iht.beta"))
        β_iht_knockoff = extract_beta(vec(readdlm("iht.knockoff.beta")), fdr, groups,
            original, knockoff)
        β_iht_knockoff_cv = extract_beta(vec(readdlm("iht.knockoff.cv.beta")),
            fdr, groups, original, knockoff)
        # β_iht = β_iht_knockoff = β_iht_knockoff_cv = zeros(p)
        β_lasso = coef(lasso_cv)
        β_lasso_knockoff = extract_beta(coef(ko_lasso_cv),
            fdr, groups, original, knockoff)

        populations = ["african", "asian", "bangladeshi", "british", "caribbean", "chinese",
            "indian", "irish", "pakistani", "white_asian", "white_black", "white"]

        df = DataFrame(pop = String[], IHT_R2 = Float64[], IHT_ko_R2 = Float64[],
            IHT_ko_cv_R2 = Float64[], LASSO_R2 = Float64[], LASSO_ko_R2 = Float64[])

        for pop in populations
            xtest = SnpArray("/scratch/users/bbchu/ukb_SHAPEIT/populations/chr10/ukb.chr$chr.$pop.bed")
            Xtest = SnpLinAlg{Float64}(xtest, center=true, scale=true, impute=true)
            # simulate "true" phenotypes for these populations
            Random.seed!(seed)
            ytest = Xtest * β + rand(ϵ, size(Xtest, 1))
            # IHT
            iht_r2 = R2(Xtest, ytest, β_iht)
            # knockoff IHT
            iht_ko_r2 = R2(xla, Xtest, y, ytest, β_iht_knockoff)
            # knockoff IHT cv
            iht_ko_cv_r2 = R2(xla, Xtest, y, ytest, β_iht_knockoff_cv)
            # lasso β
            lasso_r2 = R2(Xtest, ytest, β_lasso)
            # knockoff lasso β
            lasso_ko_r2 = R2(xla, Xtest, y, ytest, β_lasso_knockoff)
            # save to dataframe
            push!(df, hcat(pop, iht_r2, iht_ko_r2, iht_ko_cv_r2,
                lasso_r2, lasso_ko_r2))
            # push!(df, hcat(pop, 0, 0, 0, lasso_r2, lasso_ko_r2))
            GC.gc()
        end

        # count non-zero entries of β returned from cross validation
        push!(df, hcat("beta_non_zero_count", count(!iszero, β_iht),
            count(!iszero, vec(readdlm("iht.knockoff.beta"))),
            count(!iszero, vec(readdlm("iht.knockoff.cv.beta"))),
            count(!iszero, β_lasso),
            count(!iszero, coef(ko_lasso_cv))
            ))
        # count non-zero entries after knockoff filter
        push!(df, hcat("beta_selected", count(!iszero, β_iht),
            count(!iszero, β_iht_knockoff),
            count(!iszero, β_iht_knockoff_cv), count(!iszero, β_lasso),
            count(!iszero, β_lasso_knockoff)
            ))
        # count TP proportion
        correct_snps = findall(!iszero, vec(readdlm("beta_true.txt")))
        correct_groups = get_signif_groups(β, groups)
        push!(df, hcat("TPP", TP(correct_snps, findall(!iszero, β_iht)),
            TP(correct_groups, get_signif_groups(β_iht_knockoff, groups)),
            TP(correct_groups, get_signif_groups(β_iht_knockoff_cv, groups)),
            TP(correct_groups, get_signif_groups(β_lasso, groups)),
            TP(correct_groups, get_signif_groups(β_lasso_knockoff, groups))
            ))
        # count FDR
        push!(df, hcat("FDR", FDR(correct_snps, findall(!iszero, β_iht)),
            FDR(correct_groups, get_signif_groups(β_iht_knockoff, groups)),
            FDR(correct_groups, get_signif_groups(β_iht_knockoff_cv, groups)),
            FDR(correct_groups, get_signif_groups(β_lasso, groups)),
            FDR(correct_groups, get_signif_groups(β_lasso_knockoff, groups))
            ))

        @show df
        CSV.write("summary.txt", df)
    end
end

#
# import key and data (fastphase knockoffs)
#
# seed = parse(Int, ARGS[1])
# resolution = 5
# confounders = 0 # 1 PC
# keyfile = "/scratch/users/bbchu/ukb_fastPHASE/groups/Radj$(resolution)_K50_s0/ukb_gen_chr10.key"
# df = CSV.read(keyfile, DataFrame)
# groups = convert(Vector{Int}, df[!, :Group])
# knockoff_idx = convert(BitVector, df[!, :Knockoff])
# original = findall(knockoff_idx .== false)
# knockoff = findall(knockoff_idx)
# x = SnpArray("/scratch/users/bbchu/ukb/groups/Radj$(resolution)_K50_s0/ukb_gen_chr10.bed")
# x = SnpArray("/scratch/users/bbchu/ukb/groups/Radj$(resolution)_K50_s0/decorrelated/ukb_gen_chr10_decorrelated.bed") # decorrelated knockoffs

#
# import shapeit knockoffs
#
seed = parse(Int, ARGS[1])
resolution = 5
confounders = 0 # 1 PC
df = CSV.read("ukb_gen_chr10_ibd1_res$(resolution)_grp.txt", DataFrame)
groups = repeat(df[!, :Group], inner=2)
plinkname = "/scratch/users/bbchu/ukb_SHAPEIT/knockoffs/ukb_gen_chr10_ibd1_res$(resolution)"
xdata = SnpData(plinkname)
isknockoff = endswith.(xdata.snp_info[!, :snpid], ".k")
original, knockoff = Int[], Int[]
for i in 1:size(xdata)[2]
    isknockoff[i] ? push!(knockoff, i) : push!(original, i)
end
x = xdata.snparray

#
# Run simulation (via `julia prs.jl n r`)
# where n is a seed and r is resolution (20, 5, ...etc)
#
run_sims(x, original, knockoff, groups, seed)


text:
module GenGlobal

export @GenGlobal

"""
    @GenGlobal

Declare expression `VARNAME` to be a global variable, export it, and generate an exported functions
`set_VARNAME()` that sets the global and `get_VARNAME()` that gets the variable

# Example
```julia-repl
@GenGlobal myglob1 myglob2

#errors out myglob1
#errors out myglob2

set_myglob1(1.0)
set_myglob2(Matrix(1.0I,3,3))

myglob1
myglob2
```
"""
macro GenGlobal(globalnames::Symbol...)
    # uses https://stackoverflow.com/questions/31313040/julia-automatically-generate-functions-and-export-them
    e = quote end  # start out with a blank quoted expression
    for varname in globalnames
        setname = Symbol("set_$(varname)")   # create your function name
        getname = Symbol("get_$(varname)")   # create your function name

    # this next part creates another quoted expression, which are just the 2 statements
    # we want to add for this function... the export call and the function definition
    # note: wrap the variable in "esc" when you want to use a value from macro scope.
    #       If you forget the esc, it will look for a variable named "maximumfilter" in the
    #       calling scope, which will probably give an error (or worse, will be totally wrong
    #       and reference the wrong thing)
    blk = quote
        # declare variable to be global w/in module
        global $(esc(varname))

        # export set_ and get_
        export $(esc(setname)), $(esc(getname))

        # set
        function $(esc(setname))(x::T) where {T}
            global $(esc(varname))
            $(esc(varname)) = x::T
        end

        # get
        function $(esc(getname))()
            return $(esc(varname))
        end
    end

    # an "Expr" object is just a tree... do "dump(e)" or "dump(blk)" to see it
    # the "args" of the blk expression are the export and method definition... we can
    # just append the vector to the end of the "e" args
    append!(e.args, blk.args)
    # append!(e.args, blk2.args)
  end

  # macros return expression objects that get evaluated in the caller's scope
  e
end

include(joinpath("TestFunctions","testModule.jl"))
# include(joinpath(Pkg.dir("GenGlobal"),"src","testFunctions", "testModule.jl"))

end # module


text:
# # Driving in a Stadium

#md # [![](https://img.shields.io/badge/show-nbviewer-579ACA.svg)](@__NBVIEWER_ROOT_URL__/notebooks/stadium.ipynb)

# This example demonstrates a 2D driving simulation where cars drive around a three-lane stadium.
# The entities are defined by the types:
#
# - `S` - `VehicleState`, containing the vehicle position (both globally and relative to the lane) and speed
# - `D` - `VehicleDef`, containing length, width, and class
# - `I` - `Symbol`, a unique label for each vehicle
#
# The environment is represented by a `Roadway` object which
# allows to define roads consisting of multiple lanes based on the RNDF format.

# We load relevant modules and generate a 3-lane stadium roadway:

using AutomotiveDrivingModels
using AutoViz
AutoViz.colortheme["background"] = colorant"white"; # hide
using Distributions

roadway = gen_stadium_roadway(3)
snapshot = render([roadway])
#md write("stadium.svg", snapshot) # hide
#md # ![three lane stadium](stadium.svg)

# As a next step, let's populate a scene with vehicles

w = DEFAULT_LANE_WIDTH
scene = Scene([
    Entity(VehicleState(VecSE2(10.0,  -w, 0.0), roadway, 29.0), VehicleDef(), :alice),
    Entity(VehicleState(VecSE2(40.0, 0.0, 0.0), roadway, 22.0), VehicleDef(), :bob),
    Entity(VehicleState(VecSE2(30.0, -2w, 0.0), roadway, 27.0), VehicleDef(), :charlie),
])
car_colors = get_pastel_car_colors(scene)
renderables = [
    roadway,
    (FancyCar(car=veh, color=car_colors[veh.id]) for veh in scene)...
]
snapshot = render(renderables)
#md write("stadium_with_cars.svg", snapshot) # hide
#md # ![stadium with cars](stadium_with_cars.svg)

# We can assign driver models to each agent and simulate the scenario.

timestep = 0.1
nticks = 300

models = Dict{Symbol, DriverModel}(
    :alice => LatLonSeparableDriver( # produces LatLonAccels
        ProportionalLaneTracker(), # lateral model
        IntelligentDriverModel(), # longitudinal model
    ),
    :bob => Tim2DDriver(
         mlane = MOBIL(),
    ),
    :charlie => StaticDriver{AccelTurnrate, MvNormal}(
        MvNormal([0.0,0.0], [1.0,0.1])
    )
)

set_desired_speed!(models[:alice],   12.0)
set_desired_speed!(models[:bob],     10.0)
set_desired_speed!(models[:charlie],  8.0)

scenes = simulate(scene, roadway, models, nticks, timestep)
# md nothing # hide

# An animation of the simulation can be rendered using the `Reel` package

using Reel
using Printf

camera = TargetFollowCamera(:alice; zoom=10.)

animation = roll(fps=1.0/timestep, duration=nticks*timestep) do t, dt
    i = Int(floor(t/dt)) + 1
    update_camera!(camera, scenes[i])
    renderables = [
        roadway,
        (FancyCar(car=veh, color=car_colors[veh.id]) for veh in scenes[i])...,
        IDOverlay(x_off=-2, y_off=1, scene=scenes[i]),
        TextOverlay(text=[@sprintf("time: %.1fs", t)], pos=VecE2(40,40), font_size=24)
    ]
    render(renderables, camera=camera)
end

#md write("animated_stadium.gif", animation) # hide
#md # ![animated stadium with cars](animated_stadium.gif)

# Alternatively, one can also use the `Interact` framework to inspect the simulation record interactively.

#md using Interact
#md using Reel
#md using Blink
#md
#md w = Window()
#md viz = @manipulate for step in 1 : length(scenes)
#md     render([roadway, scenes[step]])
#md end
#md body!(w, viz)

# The simulation results can be saved to a text file. Only entities using `VehicleState` and `VehicleDef` are supported out of the box.
# If you wish to do IO operation with different states and definition types you have to implement `Base.write` and `Base.read` for those new types.

open("2Dstadium_listrec.txt", "w") do io
    write(io, scenes)
end

# The trajectory data file can be loaded in a similar way. You need to specify the output type.

loaded_scenes = open("2Dstadium_listrec.txt", "r") do io
    read(io, Vector{EntityScene{VehicleState, VehicleDef, String}})
end
render([roadway, loaded_scenes[1]])
#md write("stadium_with_cars_loaded.svg", snapshot) # hide
#md # ![stadium with cars after load](stadium_with_cars_loaded.svg)


text:
#=

  Photo problem in Julia ConstraintSolver.jl

  Problem statement from Mozart/Oz tutorial:
  http://www.mozart-oz.org/home/doc/fdt/node37.html#section.reified.photo
  """
  Betty, Chris, Donald, Fred, Gary, Mary, and Paul want to align in one row for
  taking a photo. Some of them have preferences next to whom they want to stand:

     1. Betty wants to stand next to Gary and Mary.
     2. Chris wants to stand next to Betty and Gary.
     3. Fred wants to stand next to Mary and Donald.
     4. Paul wants to stand next to Fred and Donald.

  Obviously, it is impossible to satisfy all preferences. Can you find an alignment that maximizes the number of satisfied preferences?
  """

  Oz solution:
    6 # alignment(betty:5  chris:6  donald:1  fred:3  gary:7   mary:4   paul:2)
  [5, 6, 1, 3, 7, 4, 2]

  There are 8 solutions:

  x = [3, 1, 6, 5, 2, 4, 7]
  x = [3, 1, 7, 5, 2, 4, 6]
  x = [3, 2, 6, 5, 1, 4, 7]
  x = [3, 2, 7, 5, 1, 4, 6]
  x = [5, 6, 1, 3, 7, 4, 2]  (the Oz solution.)
  x = [5, 6, 2, 3, 7, 4, 1]
  x = [5, 7, 1, 3, 6, 4, 2]
  x = [5, 7, 2, 3, 6, 4, 1]

  ConstraintSolver.jl is only fast for the above problem.
  The other two are not so fast...

  Model created by Hakan Kjellerstrand, hakank@gmail.com
  See also my Julia page: http://www.hakank.org/julia/

=#
using ConstraintSolver, JuMP
using Cbc, GLPK, Ipopt
const CS = ConstraintSolver
include("constraints_utils.jl")

function photo_problem(problem,print_solutions=true,all_solutions=true)

    cbc_optimizer = optimizer_with_attributes(Cbc.Optimizer, "logLevel" => 0)
    glpk_optimizer = optimizer_with_attributes(GLPK.Optimizer)
    ipopt_optimizer = optimizer_with_attributes(Ipopt.Optimizer)

    model = Model(optimizer_with_attributes(CS.Optimizer,   # "all_solutions"=> all_solutions,
                                                            "all_optimal_solutions"=>all_solutions,
                                                            "logging"=>[],

                                                            "traverse_strategy"=>:BFS,
                                                            # "traverse_strategy"=>:DFS,
                                                            # "traverse_strategy"=>:DBFS,

                                                            # "branch_split"=>:Smallest,
                                                            "branch_split"=>:Biggest,
                                                            # "branch_split"=>:InHalf,

                                                            # https://wikunia.github.io/ConstraintSolver.jl/stable/options/#branch_strategy-(:Auto)
                                                            "branch_strategy" => :IMPS, # default
                                                            # "branch_strategy" => :ABS, # Activity Based Search
                                                            # "activity.decay" => 0.999, # default 0.999
                                                            # "activity.max_probes" => 10, # default, 10
                                                            # "activity.max_confidence_deviation" => 20, # default 20

                                                            # "simplify"=>false,
                                                            # "simplify"=>true, # default

                                                            "time_limit"=> 6,

                                                            # "backtrack" => false, # default true
                                                            # "backtrack_sorting" => false, # default true

                                                            # "lp_optimizer" => cbc_optimizer,
                                                            # "lp_optimizer" => glpk_optimizer,
                                                            # "lp_optimizer" => ipopt_optimizer,
                                        ))

    n = problem[:n]
    preferences = problem[:preferences]
    n_prefs, _ = size(preferences)
    @variable(model, 1 <= x[1:n] <= n, Int)
    @variable(model, 0 <= z <= n_prefs, Int)

    @constraint(model, x in CS.AllDifferent())

    # Count the number of successful preferences,
    # i.e when distance is == 1
    diffs = @variable(model, [1:n_prefs], CS.Integers(1:n_prefs))
    bs = @variable(model, [1:n_prefs], Bin) # has diff == 1?
    c = 1
    for (p1,p2) in eachrow(preferences)
        my_abs(model,x[p1],x[p2], diffs[c])
        @constraint(model,bs[c] := {diffs[c] == 1})
        c += 1
    end
    @constraint(model,z == sum(bs))

    @objective(model,Max,z)

    # Solve the problem
    optimize!(model)

    status = JuMP.termination_status(model)
    # println("status:$status")
    if status == MOI.OPTIMAL
        num_sols = MOI.get(model, MOI.ResultCount())
        println("num_sols:$num_sols\n")
        if print_solutions
            for sol in 1:num_sols
                # println("solution #$sol")
                x_val = convert.(Integer,JuMP.value.(x; result=sol))
                diffs_val = convert.(Integer,JuMP.value.(diffs; result=sol))
                bs_val = convert.(Integer,JuMP.value.(bs; result=sol))
                z_val = convert.(Integer,JuMP.value.(z; result=sol))
                println("z: $z_val")
                println("x: $x_val")
                println("diffs:$diffs_val")
                println("bs   :$bs_val")
                #=
                c = 1
                for (p1,p2) in eachrow(preferences)
                    println("($p1,$p2) pos: $(x_val[p1]) $(x_val[p2]) diff $(diffs_val[c]) b:$(bs_val[c])")
                    c += 1
                end
                =#
                println()

            end
        end
    else
        println("status:$status")
    end

    return status
end

photo_problems = Dict(

    #
    # Problem 1 (see above):
    # 1. Betty wants to stand next to Gary and Mary.
    #     1 : 5, 6
    # 2. Chris wants to stand next to Betty and Gary.
    #     2 : 1, 5
    # 3. Fred wants to stand next to Mary and Donald.
    #     4 : 6, 3
    # 4. Paul wants to stand next to Fred and Donald.
    #     7 : 4, 3
    #
    :1  =>  Dict(
        :n => 7,
        :preferences =>
            resize_matrix([[1,5],
             [1,6],
             [2,1],
             [2,5],
             [4,6],
             [4,3],
             [7,4],
             [7,3]]),
        :all_opt_solutions => true

    ),

    # From http://www.g12.cs.mu.oz.au/minizinc/photo.data2
    :2  =>  Dict(
        :n => 11,
        :preferences => resize_matrix(
             [[1,3],
              [1,5],
              [1,8],
              [2,5],
              [2,9],
              [3,4],
              [3,5],
              [4,1],
              [4,5],
              [4,10],
              [5,6],
              [5,1],
              [6,1],
              [6,9],
              [7,3],
              [7,8],
              [8,9],
              [8,7],
              [9,10],
              [10,11]]),
        :all_opt_solutions => false
    ),

    # From http://www.ampl.com/NEW/LOGIC/EXAMPLES/photo9.dat
    # (This seems to be a simplified of #2
    :3 =>  Dict(
        :n => 9,
        :preferences => resize_matrix(
             [[1,3],
              [1,5],
              [1,8],
              [2,5],
              [2,9],
              [3,4],
              [3,5],
              [4,1],
              [4,5],
              [5,1],
              [5,6],
              [6,1],
              [6,9],
              [7,3],
              [7,8],
              [8,7],
              [8,9]]
        ),
        :all_opt_solutions => false
    )
)

for p in sort(collect(keys(photo_problems)))
    println("\nProblem $p")
    @time photo_problem(photo_problems[p],true,photo_problems[p][:all_opt_solutions])
    # @time photo_problem(photo_problems[p],true,false)
end

# @time photo_problem(photo_problems[:3],true,false)

max_stars_repo_name: tias/hakank

max_issues_repo_name: tias/hakank

max_forks_repo_name: tias/hakank


text:
function equindex2pass(data::Array)
    rst = Vector{Int}(0)
    suml, sumr, ddelayed = 0, sum(data), 0
    for (i, d) in enumerate(data)
        suml += ddelayed
        sumr -= d
        ddelayed = d
        if suml == sumr
            push!(rst, i)
        end
    end
    return rst
end

@show equindex2pass([1, -1, 1, -1, 1, -1, 1])
@show equindex2pass([1, 2, 2, 1])
@show equindex2pass([-7, 1, 5, 2, -4, 3, 0])

max_stars_repo_name: ethansaxenian/RosettaDecode

max_issues_repo_name: ethansaxenian/RosettaDecode

max_forks_repo_name: ethansaxenian/RosettaDecode


text:
using Pandas
using Test
import DataFrames
using PyCall
using Dates

df = DataFrame(Dict(:name=>["a", "b"], :age=>[27, 30]))
age = values(df.age)
age[2] = 31
@test loc(df)[1, "age"] == 31

query(df, :(age!=27))  # Issue #26

text = repr(MIME("text/html"), df)
@test text isa String
@test occursin("<table", text)
@test occursin("age", text)

df = read_csv(joinpath(dirname(@__FILE__), "test.csv"))
typeof(df)
@test isa(df, Pandas.DataFrame)

include("test_tabletraits.jl")
include("test_tables.jl")

@test !isempty(df)
empty!(df)
@test isempty(df)

x = Series([3,5], index=[:a, :b])

@test x.a == 3
@test x[:a] == 3
@test loc(x)[:a] == 3
@test x.b == 5
@test iloc(x)[1] == 3
@test iloc(x)[2] == 5
@test length(x) == 2
@test values(x+1) == [4, 6]
@test sum(x) == 8
if !Sys.iswindows()
    @test eltype(x) == Int64
end
@test all(iloc(x)[1:2]==x)

# Rolling
roll = rolling(Series([1,2,3,4,5]), 3)
@test isequal(values(mean(roll)), [NaN, NaN, 2.0, 3.0, 4.0])

# Issue #71
julia_df = DataFrames.DataFrame(x=[1,2], y=[missing, missing])
py_df = Pandas.DataFrame(julia_df)
expected_df = Pandas.DataFrame(:x=>[1,2], :y=>[NaN, NaN])[["x", "y"]]
@test Pandas.equals(py_df, expected_df)

# Issue #68
py"""
import pandas as pd

def get_df():
    df = pd.DataFrame({
        "a":pd.to_datetime(["2021.01.15","2021.01.15","2020.04.06"])
    })
    return df
"""

py_df = py"get_df"()|>Pandas.DataFrame
julia_df = DataFrames.DataFrame(py_df)

@test julia_df.a == [DateTime(2021, 1, 15), DateTime(2021, 1, 15), DateTime(2020, 4, 6)]

# Issue #72
julia_df= DataFrames.DataFrame(C = 1:4, A = 5:8, B = 9:12)
py_df = Pandas.DataFrame(julia_df)
@test all(Pandas.columns(py_df) .== ["C","A","B"])


text:
#!/usr/bin/env julia

using Luxor

using Test

function grid_test(fname)
    Drawing("A0landscape", fname)
    background("white")
    translate(200, 200)
    fontsize(14)
    setline(0.2)
    rulers()
    side = 100
    grid = GridRect(O, side, side, 1000, 1600)
    for p in 1:121
        randomhue()
        # rectangular
        pt = nextgridpoint(grid)
        box(pt, side, side, :fill)
        sethue("white")
        text(string(p, ":", grid.rownumber, ":", grid.colnumber), pt, halign=:center, valign=:middle)
    end
    translate(Luxor.current_width()/2, 0)
    rulers()
    centers = 100
    grid = GridHex(O, centers, 800, 800)
    lastpt = O
    for p in 1:30
        # hex grid
        pt = nextgridpoint(grid)
        randomhue()
        ngon(pt, centers, 6, pi/2, :stroke)
        sethue("black")
        text(string(p, ":", grid.rownumber, ":", grid.colnumber), pt, halign=:center, valign=:middle)
        if grid.rownumber > 6
            break
        end
        lastpt = pt
    end
    sethue("black")

    arrow(O, Point(O.x + (sqrt(3) * centers) / 2, 0))
    circle(O, (sqrt(3) * centers) / 2, :stroke)
    circle(O, centers, :stroke)

    finish()
end

grid_test("grid-test.pdf")


text:
#=
Test whether binding markings works correctly.

Adapted from https://github.com/julia-vscode/CSTParser.jl/blob/e4e32477a1e7b4d57652b3755659f169b50fb60e/test/bindings.jl
=#

using Atom: traverse_expr!, bindingof
using CSTParser
using CSTParser: typof

function collect_bindings(ex)
    traverse_expr!(ex)
    return _collect_bindings(ex)
end
function _collect_bindings(x, out = String[])
    if bindingof(x) !== nothing
        push!(out, bindingof(x).name)
    end
    if (typof(x) === CSTParser.BinaryOpCall && CSTParser.is_assignment(x) && !CSTParser.is_func_call(x)) || typof(x) === CSTParser.Filter
        _collect_bindings(x.args[3], out)
        _collect_bindings(x.args[2], out)
        _collect_bindings(x.args[1], out)
    elseif typof(x) === CSTParser.WhereOpCall
        @inbounds for i = 3:length(x.args)
            _collect_bindings(x.args[i], out)
        end
        _collect_bindings(x.args[1], out)
        _collect_bindings(x.args[2], out)
    elseif typof(x) === CSTParser.Generator
        @inbounds for i = 2:length(x.args)
            _collect_bindings(x.args[i], out)
        end
        _collect_bindings(x.args[1], out)
    elseif x.args === nothing
    else
        @inbounds for a in x.args
            _collect_bindings(a, out)
        end
    end
    return out
end

@test collect_bindings(CSTParser.parse("x = 1")) == ["x"]
@test collect_bindings(CSTParser.parse("(x) = 1")) == ["x"]
@test collect_bindings(CSTParser.parse("x, y =  1")) == ["x", "y"]
@test collect_bindings(CSTParser.parse("(x, y) =  1")) == ["x", "y"]
@test collect_bindings(CSTParser.parse("x = y =  1")) == ["y", "x"]
@test collect_bindings(CSTParser.parse("x::T = 1")) == ["x"]

@test collect_bindings(CSTParser.parse("f() = x")) == ["f"]
@test collect_bindings(CSTParser.parse("f(a) = x")) == ["f", "a"]
@test collect_bindings(CSTParser.parse("f(a) = x")) == ["f", "a"]
@test collect_bindings(CSTParser.parse("f(x::T) where {T <: S} where R = x")) == ["f", "R", "T","x"]
@test collect_bindings(CSTParser.parse("function f end")) == ["f"]
@test collect_bindings(CSTParser.parse("function f() end")) == ["f"]
@test collect_bindings(CSTParser.parse("function f() where T end")) == ["f", "T"]

@test collect_bindings(CSTParser.parse("macro m end")) == ["m"]
@test collect_bindings(CSTParser.parse("macro m() end")) == ["m"]

@test collect_bindings(CSTParser.parse("abstract type T end")) == ["T"]
@test collect_bindings(CSTParser.parse("abstract type T <: S end")) == ["T"]
@test collect_bindings(CSTParser.parse("abstract type T{S} end")) == ["T", "S"]

@test collect_bindings(CSTParser.parse("primitive type T 4 end")) == ["T"]
@test collect_bindings(CSTParser.parse("primitive type T <: S 4 end")) == ["T"]
@test collect_bindings(CSTParser.parse("primitive type T{S} 4 end")) == ["T", "S"]

@test collect_bindings(CSTParser.parse("struct T end")) == ["T"]
@test collect_bindings(CSTParser.parse("struct T <: S end")) == ["T"]
@test collect_bindings(CSTParser.parse("struct T{S} end")) == ["T" ,"S"]
@test collect_bindings(CSTParser.parse("struct T\nx end")) == ["T", "x"]
@test collect_bindings(CSTParser.parse("struct T\nT() = new()\n end")) == ["T", "T"]

@test collect_bindings(CSTParser.parse("mutable struct T end")) == ["T"]

@test collect_bindings(CSTParser.parse("for i = 1 end")) == ["i"]
@test collect_bindings(CSTParser.parse("let i = 1 end")) == ["i"]
@test collect_bindings(CSTParser.parse("[i for i = 1]")) == ["i"]
@test collect_bindings(CSTParser.parse("[i for i in 1]")) == ["i"]
@test collect_bindings(CSTParser.parse("try catch e end")) == ["e"]
@test collect_bindings(CSTParser.parse("map() do x end")) == ["x"]

@test collect_bindings(CSTParser.parse("(a,b)->x")) == ["a", "b"]
collect_bindings(CSTParser.parse("function f(a::T = 1) end")) == ["f", "a"]

let cst = CSTParser.parse("function a::T * b::T end")
    traverse_expr!(cst)
    @test bindingof(cst[2][1]) !== nothing
    @test bindingof(cst[2][3]) !== nothing
end

@test collect_bindings(CSTParser.parse("-(x::T) = x")) == ["-", "x"]

# https://github.com/JunoLab/Juno.jl/issues/502
@test isempty(collect_bindings(CSTParser.parse("ary[ind] = rand(Int)")))


text:
#---------------------------------------------------------------------#
#This routine advances the solution in time using Split-Explicit using
#a simple General Order RK method.
#Written by F.X. Giraldo / P.R. Mugg on 9/20/19
#           Department of Applied Mathematics
#           Naval Postgraduate School
#           Monterey, CA 93943-5216
#---------------------------------------------------------------------#
function WS_rk3(q0,c,Δt,time)

    #1st RK Stage
    Q = q0
    (rhs,f,g) = rhs_function(Q,c,time)
    Q = q0 + Δt/3*(f + g)

    #2nd RK Stage
    (rhs,f,g) = rhs_function(Q,c,time + Δt/3)
    Q = q0 + Δt/2*(f + g)

    #3rd RK Stage
    (rhs,f,g) = rhs_function(Q,c,time + Δt/2)
    Q = q0 + Δt*(f + g)

    #Update
    return qp = Q

end #end function


text:
centralpolymat_m_ks = [
    [
    (1, 4), # compile run
    (1, 40),
    (1, 80),
    (1, 120),
    (1, 160),
    ],
    [
    (2, 2), # compile run
    (2, 6),
    (2, 9),
    (2, 12),
    (2, 15),
    ],
    [
    (3, 2), # compile run
    (4, 3),
    (4, 4),
    (4, 5),
    ],
    [
    (3, 2), # compile run
    (8, 2),
    (8, 3),
    ],
    ]
centralpolymat_insts(ext::MatSpecExt) = [
    [(m, k, ext) for (m, k) in mks]
    for mks in centralpolymat_m_ks
    ]

insts = OrderedDict()
insts["nat"] = (nothing, vcat(
    centralpolymat_insts(MatNegEntropyConj()),
    centralpolymat_insts(MatPower12Conj(1.5)),
    ))
insts["ext"] = (nothing, vcat(
    centralpolymat_insts(MatNegEntropyConjEigOrd()),
    centralpolymat_insts(MatPower12ConjEigOrd(1.5)),
    ))
return (CentralPolyMatJuMP, insts)


text:
const em = emulate
const issub = isnewsubtype

@test issub(em(Int), em(Number))
@test issub(NewBottom, em(Base.Bottom))
@test issub(NewBottom, em(Any))

typelist = (Int8, Int16, Int, Signed, UInt32, Unsigned, Integer, Rational, Real, Number, BigInt, BigFloat, Complex128, Complex, Irrational)
for s in typelist, t in typelist
    @test issub(em(s), em(t)) == (s <: t)
end

typelist = (Int8, Array, Array{T,1} where T, Array{T,N} where {T<:Float64, Int8<:N<:Real})
for s in typelist, t in typelist
    @test issub(em(s), em(t)) == (s <: t)
    issub(em(s), em(t)) == (s <: t) || println("(", s, ") <: (", t, ") ", (s<:t))
end

typelist = (Vector{T} where Signed<:T<:Number, Vector{T} where Integer<:T<:Real)
for s in typelist, t in typelist
    issub(em(s), em(t)) == (s <: t) || println("(", s, ") <: (", t, ") ", (s<:t))
    @test issub(em(s), em(t)) == (s <: t)
end

typelist = (Union{Int,Signed}, Int, UInt8)
for s in typelist, t in typelist
    issub(em(s), em(t)) == (s <: t) || println("(", s, ") <: (", t, ") ", (s<:t))
    @test issub(em(s), em(t)) == (s <: t)
end

typelist = (Tuple{T, T} where T, Tuple{S, T} where {S, T})
for s in typelist, t in typelist
    issub(em(s), em(t)) == (s <: t) || println("(", s, ") <: (", t, ") ", (s<:t))
    @test issub(em(s), em(t)) == (s <: t)
end

typelist = (Tuple{Vector{S}, Vector{S}, Vector{T}} where {S,T}, Tuple{Vector{S}, Vector{T}, Vector{T}} where {S,T})
for s in typelist, t in typelist
    r = issub(em(s), em(t)) == (s <: t)
    r || println(STDERR, "(", s, ") <: (", t, ") ", (s<:t))
    if r
        @test issub(em(s), em(t)) == (s <: t)
    else
        @test_broken issub(em(s), em(t)) == (s <: t)
    end
end

typelist = (Tuple{Int, Integer}, Tuple{Integer, Int}, Tuple{S, T} where S<:T where T)
for s in typelist, t in typelist
    r = issub(em(s), em(t)) == (s <: t)
    r || println(STDERR, "(", s, ") <: (", t, ") ", (s<:t))
    if r
        @test issub(em(s), em(t)) == (s <: t)
    else
        @test_broken issub(em(s), em(t)) == (s <: t)
    end
end

typelist = (Union{Int8,Int32}, Union{Int8, Int32, Int16})
for s in typelist, t in typelist
    r = issub(em(s), em(t)) == (s <: t)
    r || println(STDERR, "(", s, ") <: (", t, ") ", (s<:t))
    if r
        @test issub(em(s), em(t)) == (s <: t)
    else
        @test_broken issub(em(s), em(t)) == (s <: t)
    end
end


text:
### struct for defining the Muller-Brown potential
mutable struct MullerBrown <: AbstractSinglePotential
  dim::Int64
  A::Vector{Float64}
  a::Vector{Float64}
  b::Vector{Float64}
  c::Vector{Float64}
  x0s::Vector{Float64}
  y0s::Vector{Float64}
  f::Vector{Float64}
  em::Vector{Float64}
  function MullerBrown()
    new(2,[-200.,-100.,-170.,15.],[-1.,-1.,-6.5,0.7],[0.,0.,11.,0.6],[-10.,-10.,-6.5,0.7],[1.,0.,-0.5,-1.],[0.,0.5,1.5,1.],zeros(Float64,2),zeros(Float64,12))
  end
end

function getdimensionality(mb::MullerBrown)
  return 2
end

"""
Potential functions
"""
function potential(x::Vector{Float64}, mb::MullerBrown)
  @. @views mb.em[1:4] = x[1].-mb.x0s
  @. @views mb.em[5:8] = x[2].-mb.y0s
  return sum(@. @views mb.A.*exp.(mb.a.*mb.em[1:4].^2 + mb.b.*mb.em[1:4].*mb.em[5:8] + mb.c.*mb.em[5:8].^2))
end

function potential(system::AbstractSystem, mb::MullerBrown)
  @. @views mb.em[1:4] = system.x[1].-mb.x0s
  @. @views mb.em[5:8] = system.x[2].-mb.y0s
  return sum(@. @views mb.A.*exp.(mb.a.*mb.em[1:4].^2 + mb.b.*mb.em[1:4].*mb.em[5:8] + mb.c.*mb.em[5:8].^2))
end

"""
Force functions with return value
"""
function force(x::Vector{Float64}, mb::MullerBrown)
  @. @views em[1:4]  = x[1].-mb.x0s
  @. @views em[5:8]  = x[2].-mb.y0s
  @. @views em[9:12] = exp.(mb.a.*em[1:4].^2 + mb.b.*em[1:4].*em[5:8] + mb.c.*em[5:8].^2)
  fout::Vector{Float64} = zeros(Float64,2)
  f[1] = -sum(@. @views mb.A.*(2 .*mb.a.*em[1:4] + mb.b.*em[5:8]).*em[9:12])
  f[2] = -sum(@. @views mb.A.*(2 .*mb.c.*em[5:8] + mb.b.*em[1:4]).*em[9:12])
  return fout
end

function force(system::AbstractSystem, mb::MullerBrown)
  @. @views mb.em[1:4]  = system.x[1].-mb.x0s
  @. @views mb.em[5:8]  = system.x[2].-mb.y0s
  @. @views mb.em[9:12] = exp.(mb.a.*mb.em[1:4].^2 + mb.b.*mb.em[1:4].*mb.em[5:8] + mb.c.*mb.em[5:8].^2)
  fout::Vector{Float64} = zeros(Float64,2)
  f[1] = -sum(@. @views mb.A.*(2 .*mb.a.*mb.em[1:4] + mb.b.*mb.em[5:8]).*mb.em[9:12])
  f[2] = -sum(@. @views mb.A.*(2 .*mb.c.*mb.em[5:8] + mb.b.*mb.em[1:4]).*mb.em[9:12])
  return fout
end

"""
Force functions with in-place changing
"""
function force!(x::Vector{Float64}, f::Vector{Float64}, mb::MullerBrown)
  @. @views mb.em[1:4]  = x[1].-mb.x0s
  @. @views mb.em[5:8]  = x[2].-mb.y0s
  @. @views mb.em[9:12] = exp.(mb.a.*(mb.em[1:4].^2) + mb.b.*(mb.em[1:4].*mb.em[5:8]) + mb.c.*(mb.em[5:8].^2))
  f[1] = -sum(@. @views mb.A.*(2 .*mb.a.*mb.em[1:4] + mb.b.*mb.em[5:8]).*mb.em[9:12])
  f[2] = -sum(@. @views mb.A.*(2 .*mb.c.*mb.em[5:8] + mb.b.*mb.em[1:4]).*mb.em[9:12])
end

function force!(system::AbstractSystem, mb::MullerBrown)
  @. @views mb.em[1:4]  = system.x[1].-mb.x0s
  @. @views mb.em[5:8]  = system.x[2].-mb.y0s
  @. @views mb.em[9:12] = exp.(mb.a.*(mb.em[1:4].^2) + mb.b.*(mb.em[1:4].*mb.em[5:8]) + mb.c.*(mb.em[5:8].^2))
  mb.f[1] = -sum(@. @views mb.A.*(2 .*mb.a.*mb.em[1:4] + mb.b.*mb.em[5:8]).*mb.em[9:12])
  mb.f[2] = -sum(@. @views mb.A.*(2 .*mb.c.*mb.em[5:8] + mb.b.*mb.em[1:4]).*mb.em[9:12])
end


text:
struct PathCurve
    pt1::Point
    pt2::Point
    pt3::Point
end

struct PathMove
    pt1::Point
end

struct PathLine
    pt1::Point
end

struct PathClose
end

const PathElement = Union{PathCurve, PathMove, PathLine, PathClose}

"""
A Path object contains, in the `.path` field, a vector of
`PathElement`s (`PathCurve`, `PathMove`, `PathLine`,
`PathClose`) that describe a Cairo path. Use `drawpath()` to
draw it.

```
Path([PathMove(Point(2.0, 90.5625)),
      PathCurve(Point(4.08203, 68.16015), Point(11.28, 45.28), Point(24.8828, 26.40234)),
      PathLine(Point(2.0, 90.5625)),
      PathClose()
     ])
```

"""
struct Path
    path::Vector{PathElement}
end

"""
    Path(ptlist::Vector{Point}; close=false))

Create a Path from the points in `ptlist`.
"""
function Path(ptlist::Vector{Point};
        close=false)
    p = Path(PathElement[])
    push!(p.path, PathMove(ptlist[1]))
    for pt in ptlist[2:end]
        push!(p.path, PathLine(pt))
    end
    if close
        push!(p.path, PathClose())
    end
    return p
end

Base.size(cp::Path) = length(cp.path)
Base.length(cp::Path) = length(cp.path)
Base.getindex(cp::Path, i::Int) = cp.path[i]
Base.IndexStyle(cp::Path) = IndexLinear()
Base.setindex!(cp::Path, el, e::Integer) = setindex!(cp.path, el, e)
Base.keys(cp::Path) = keys(cp.path)
Base.eltype(::Type{Path}) = PathElement

function Base.iterate(cp::Path)
    if length(cp.path) != 0
        return (first(cp.path), 1)
    else
        return nothing
    end
end

function Base.iterate(cp::Path, state)
    if state > length(cp.path)
        return nothing
    else
        return (cp.path[state], state + 1)
    end
end

Base.getindex(cp::Path, r::Union{LinRange, UnitRange, StepRangeLen}) = getindex(cp.path, r)
Base.lastindex(cp::Path) = length(cp.path)

function Base.show(io::IO, cp::Path)
    if iszero(length(cp))
        print(io, "Path is empty")
    else
        println(io, "Path([")
        for p in cp.path[1:end-1]
            println(io, " $(p),")
        end
        println(io, " $(cp.path[end])")
        println(io, "])\n")
    end
end

drawpath(cpe::PathMove) = move(cpe.pt1)
drawpath(cpe::PathLine) = line(cpe.pt1)
drawpath(cpe::PathCurve) = curve(cpe.pt1, cpe.pt2, cpe.pt3)
drawpath(cpe::PathClose) = closepath()

"""
    storepath()

Obtain the current Cairo path and make a Luxor Path object,
which is an array of PathElements.

Returns the Path object.

See also getpath() and getpathflat().
"""
function storepath()
    path = PathElement[]
    for e in getpath()
        if e.element_type == 0
            (x, y) = e.points
            push!(path, PathMove(Point(x, y)))
        elseif e.element_type == 1
            (x, y) = e.points
            push!(path, PathLine(Point(x, y)))
        elseif e.element_type == 2
            (x1, y1, x2, y2, x3, y3) = e.points
            push!(path, PathCurve(Point(x1, y1), Point(x2, y2), Point(x3, y3)))
        elseif e.element_type == 3
            push!(path, PathClose())
        else
            error("unknown Cairo Path Entry " * repr(e.element_type))
            error("unknown Cairo Path Entry " * repr(e.points))
        end
    end
    return Path(path)
end

"""
    drawpath(cp::Path; action=:none, startnewpath=true)
    drawpath(cp::Path, action; startnewpath=true)

Make the Luxor path stored in `cp` and apply the `action`.

To make paths, follow some path construction functions such
as `move()`, `line()`, and `curve()` with the `storepath()`
function.

By default, `startnewpath=true`, which starts a new path,
discarding any existing path contents.

TODO Return something more useful than a Boolean!
"""
function drawpath(cp::Path;
        action=:none, startnewpath=true)
    startnewpath && newpath()
    for p in cp
          drawpath(p)
    end
    do_action(action)
end

drawpath(cp::Path, action::Symbol; startnewpath=true) = drawpath(cp::Path; action=action, startnewpath=startnewpath)

"""
    polytopath(ptlist)

Convert a polygon to a Path object.
```julia
@draw drawpath(polytopath(ngon(O, 145, 5, vertices=true)), action=:fill)
```
"""
function polytopath(ptlist::Vector{Point})
    path = PathElement[]
    push!(path, PathMove(ptlist[1]))
    for pt in ptlist[2:end]
        push!(path, PathLine(pt))
    end
    push!(path, PathClose())
    return Path(path)
end
"""
    bezierpathtopath(bp::BezierPath)

Convert a Bezier path to a Path object.

```julia
@draw drawpath(polytopath(ngon(O, 145, 5, vertices=true)), action=:fill)
```
"""
function bezierpathtopath(bp::BezierPath)
    path = PathElement[]
    push!(path, PathMove(first(first(bp))))
    for p in bp
        push!(path, PathCurve(p[2], p[3], p[4]))
    end
    return Path(path)
end

"""
    BoundingBox(path::Path)

Find bounding box of a stored Path (made with `storepath()`).
"""
function BoundingBox(path::Path)
    # start with empty bbox, grow it using points in path
    counter = 0
    bbox = BoundingBox(O, O)
    currentpoint = O
    for p in path
        counter += 1
        if p isa PathMove
            if isapprox(boxdiagonal(bbox), 0.0) # bbox empty?
                bbox = BoundingBox(p.pt1, p.pt1)
            else
                bbox += BoundingBox(currentpoint, p.pt1)
            end
            currentpoint = p.pt1
        elseif p isa PathLine
            if isapprox(boxdiagonal(bbox), 0.0) # bbox empty?
                bbox = BoundingBox(currentpoint, p.pt1)
            else
                bbox += BoundingBox(currentpoint, p.pt1)
            end
            currentpoint = p.pt1
        elseif p isa PathCurve
            # Wikipedia says a Bezier curve is completely contained within the
            # convex hull of its control points, so we can just do this:
            phbbox = BoundingBox(polyhull([currentpoint, p.pt1, p.pt2, p.pt3]))
            if isapprox(boxdiagonal(bbox), 0.0) # bbox empty?
                bbox = phbbox
            else
                bbox += phbbox
            end
            currentpoint = p.pt3
        end
    end
    return bbox
end

"""
    get_bezier_points(bps::BezierPathSegment;
        steps=10)

The flattening: return a list of all the points on the
Bezier curve, including start and end, using `steps` to
determine the accuracy.
"""
function get_bezier_points(bps::BezierPathSegment;
        steps=10)
    return bezier.(range(0.0, 1.0, length=steps), bps...)
end

"""
    get_bezier_length(bps::BezierPathSegment;
        steps=10)

Return the length of a BezierPathSegment, using `steps` to
determine the accuracy, by stepping  through the curve and
finding all the points,  and then measuring between them.

This is obviously just an approximation; the maths to do
it properly is too difficult for me. :(
"""
function get_bezier_length(bps::BezierPathSegment;
        steps=10)
    LUT  = get_bezier_points(bps, steps=steps)
    curvelength = 0.0
    for i in 1:length(LUT) - 1
        curvelength += distance(LUT[i], LUT[mod1(i + 1, end)])
    end
    return curvelength
end

"""
    pathlength(path::Path;
        steps=10)

Return the length of a Path.

The `steps` parameter is used when approximating the length of any curve (Bezier) sections.
"""
function pathlength(path::Path;
        steps=10)
    currentpoint = O
    firstpoint = currentpoint
    plength = 0.0
    for pathelement in path
        if pathelement isa PathMove # pt1
            firstpoint = pathelement.pt1
            currentpoint = pathelement.pt1
        elseif pathelement isa PathCurve # pt1 pt2  pt3
            plength += get_bezier_length(BezierPathSegment(currentpoint, pathelement.pt1, pathelement.pt2, pathelement.pt3), steps=steps)
            currentpoint = pathelement.pt3
        elseif pathelement isa PathLine  # pt1
            plength += distance(currentpoint, pathelement.pt1)
            currentpoint = pathelement.pt1
        elseif pathelement isa PathClose
            # I think Close is just drawing to the point established by previous Move...
            plength += distance(firstpoint, currentpoint)
        end
    end
    return plength
end

"""
    drawpath(path::Path, k::Real;
        steps=10, # used when approximating Bezier curve segments
        action=:none,
        startnewpath=true,
        pathlength = 0.0)

Draw the path in `path` starting at the beginning and
stopping at `k` between 0 and 1. So if `k` is 0.5, half the
path is drawn.

Returns the last point processed.

The function calculates the length of the entire path before
drawing it. If you want to draw a large path more than once,
it might be more efficient to calculate the length of the
path first, and provide it to the `pathlength` keyword.

The `steps` parameter is used when approximating the length of any curve (Bezier) sections.
"""
function drawpath(path::Path, k::Real;
        steps=10, # used when approximating Bezier curve segments
        action=:none,
        startnewpath=true,
        pathlength = 0.0)

    if iszero(pathlength)
       pathlength = Luxor.pathlength(path)
    end
    requiredlength = k * pathlength
    currentlength = 0
    startnewpath && newpath()
    # firstpoint is the point we will return to for a Close
    # currentpoint is Cairo's current point
    # mostrecentpoint is the point we last visited
    currentpoint = mostrecentpoint = firstpoint = O
    for pathelement in path
        if pathelement isa PathMove # pt1
            currentpoint = firstpoint = pathelement.pt1
            drawpath(pathelement)
        elseif pathelement isa PathCurve # pt1 pt2  pt3
            plength = Luxor.get_bezier_length(BezierPathSegment(currentpoint, pathelement.pt1, pathelement.pt2, pathelement.pt3), steps=steps)
            currentlength += plength
            if currentlength > requiredlength
                # we mustn't draw all of this curve, since it overshoots
                overshoot  = (currentlength - requiredlength) / pathlength
                # just draw the overshoot fraction of the curve
                bps = BezierPathSegment(currentpoint, pathelement.pt1, pathelement.pt2, pathelement.pt3)
                newbezier = bezier.(range(0.0, overshoot, length=steps), bps...)
                # using the last three, currentpoint is the first pt
                drawpath(PathCurve(newbezier[2], newbezier[3], newbezier[4]))
            else
                drawpath(pathelement)
            end
            mostrecentpoint = currentpoint # remember how far we got
            currentpoint = pathelement.pt3 # update currentpoint
        elseif pathelement isa PathLine  # pt1
            plength = distance(currentpoint, pathelement.pt1)
            currentlength += plength
            if currentlength > requiredlength
                # we mustn't draw all of this line, since it overshoots
                overshoot  = (currentlength - requiredlength) / pathlength
                # just draw the overshoot fraction of the line
            else
                drawpath(pathelement)
            end
            mostrecentpoint = currentpoint
            currentpoint = pathelement.pt1
        elseif pathelement isa PathClose
            # I think Close is just drawing to the point established by previous Move...
            drawpath(PathLine(firstpoint))
            closepath() # ???
        end
        currentlength > requiredlength && break
    end
    do_action(action)
    return mostrecentpoint
end

drawpath(path::Path, k::Real, act::Symbol;
        steps=10, # used when approximating Bezier curve segments
        startnewpath=true,
        pathlength = 0.0) = drawpath(path, k;
            steps = steps,
            action = act,
            startnewpath = startnewpath,
            pathlength = pathlength)

"""
    pathsample(path::Path, spacing;
        steps=10)

Return a new Path that resamples the `path` such that each
line and curve of the original path is divided into sections
that are approximately `spacing` units long.

The `steps` parameter is used when approximating the length
of any curve (Bezier) sections. For measurement purposes,
each Bezier curve is divided in `steps` straight lines; the
error will be smaller for flatter curves and larger for more
curvy ones.
"""
function pathsample(path::Path, spacing;
        steps=10)
    isapprox(spacing, 0.0) && throw(error("pathsample(): a spacing of $(spacing) is too close to zero"))
    newpath = PathElement[]
    currentpoint = O
    firstpoint = currentpoint
    for pathelement in path
        if pathelement isa PathMove # pt1
            push!(newpath, pathelement)
            firstpoint = pathelement.pt1
            currentpoint = pathelement.pt1
        elseif pathelement isa PathCurve # pt1 pt2 pt3
            bps = BezierPathSegment(currentpoint,
                pathelement.pt1,
                pathelement.pt2,
                pathelement.pt3)
            plength = Luxor.get_bezier_length(bps, steps=steps)
            _step = spacing/plength
            for i in range(0.0, 1.0 - _step, step=_step)
                bezsegment = trimbezier(bps, i, i + _step)
                push!(newpath, PathCurve(bezsegment.cp1, bezsegment.cp2, bezsegment.p2))
            end
            currentpoint = pathelement.pt3
        elseif pathelement isa PathLine  # pt1
            plength = distance(currentpoint, pathelement.pt1)
            _step = spacing/plength
            for i in range(0.0, 1.0, step=_step)
                push!(newpath, PathLine(between(currentpoint, pathelement.pt1, i)))
            end
            currentpoint = pathelement.pt1
        elseif pathelement isa PathClose
            # I think Close is just drawing to the point established by previous Move...
            plength = distance(currentpoint, firstpoint)
            if !iszero(plength)
                _step = spacing/plength
                for i in range(0.0, 1.0, step=_step)
                    push!(newpath, PathLine(between(currentpoint, firstpoint, i)))
                end
            end
            push!(newpath, PathLine(firstpoint))
        end
    end
    return Path(newpath)
end


text:
using DataFrames
using CSV

cd("/Users/kiristern/Documents/GitHub/SDM/")

predators = CSV.read("data/preds/originals/Vulpes_vulpes.csv", delim="\t")

p1 = CSV.read("data/prey_sp/Armadillidium_opacum.csv", delim="\t")

p2 = CSV.read("data/prey_sp/Lithobius_piceus.csv", delim="\t")

p3 = CSV.read("data/prey_sp/Tetrix_subulata.csv", delim="\t")

p4 = CSV.read("data/prey_sp/Conocephalus_fuscus.csv", delim="\t")

p5 = CSV.read("data/prey_sp/Chorthippus_montanus.csv", delim="\t")

p6 = CSV.read("data/prey_sp/Staphylinus_erythropterus.csv", delim="\t")

p7 = CSV.read("data/prey_sp/Conocephalus_fuscus.csv", delim="\t")

p8 = CSV.read("data/prey_sp/Rana_temporaria.csv", delim="\t")
#cannot convert "missing" values to Float64, so must dropmissing first
p8 = dropmissing(p8, [:decimalLongitude])
# convert longitude character "string" into "Float64"
p8.decimalLongitude = parse.(Float64, p8.decimalLongitude)

p9 = CSV.read("data/prey_sp/Hyla_arborea.csv", delim="\t")

p10 = CSV.read("data/prey_sp/Sylvia_atricapilla.csv", delim="\t")

p11 = CSV.read("data/prey_sp/Microtus_agrestis.csv", delim="\t")

p12 = CSV.read("data/prey_sp/Threskiornis_molucca.csv", delim="\t")

p13 = CSV.read("data/prey_sp/Lepus_europaeus.csv", delim="\t")

p14 = CSV.read("data/prey_sp/Timarcha_tenebricosa.csv", delim="\t")

p15 = CSV.read("data/prey_sp/Sciurus_vulgaris.csv", delim="\t")

p16 = CSV.read("data/prey_sp/Cynomys_ludovicianus.csv", delim="\t")

#create one dataframe with predators/prey dataframes
pred_prey = vcat(predators, p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,p11,p12,p13,p14,p15,p16)

pred_prey = select(pred_prey, [:species, :decimalLatitude, :decimalLongitude, :year])

pred_prey = dropmissing!(pred_prey, [:decimalLatitude, :decimalLongitude, :year])
pred_prey.decimalLongitude = parse.(Float64, pred_prey.decimalLongitude)

CSV.write("data/pred_prey/vulpes_vulpes_america.csv", pred_prey)

show(by(pred_prey, :species, :species => length), allcols=true)

max_stars_repo_name: kiristern/SDM

max_issues_repo_name: kiristern/SDM

max_forks_repo_name: kiristern/SDM


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

using BenchmarkTools
import TOML
import Random
import StableRNGs

randstring(args...) = Random.randstring(StableRNGs.StableRNG(0), args...)
rand(args...) = Random.rand(StableRNGs.StableRNG(0), args...)

const SUITE = BenchmarkGroup()

##########
# Arrays #
##########

SUITE["arrays"] = BenchmarkGroup()

const homogeneous_array = "foo = [" * join(ones(Int, 10^5), ',') * "]"
SUITE["arrays"]["homogeneous"] = @benchmarkable TOML.parse(homogeneous_array)

const heterogeneous_array = "foo = [1.0," * join(ones(Int, 10^5), ',') * "]"
SUITE["arrays"]["heterogeneous"] = @benchmarkable TOML.parse(heterogeneous_array)

###########
# Strings #
###########

SUITE["strings"] = BenchmarkGroup()

const long_string = "foo = " * repr(randstring(10^6))
SUITE["strings"]["long"] = @benchmarkable TOML.parse(long_string)

const short_strings = "foo = [" * join([repr(randstring(3)) for i in 1:10^4], ',') * "]"
SUITE["strings"]["short"] = @benchmarkable TOML.parse(short_strings)

###########
# Numbers #
###########

SUITE["numbers"] = BenchmarkGroup()

const integer_array = "foo = [" * join(rand(Int, 10^5), ',') * "]"
SUITE["numbers"]["integers"] = @benchmarkable TOML.parse(integer_array)

const float_array = "foo = [" * join(rand(10^5), ',') * "]"
SUITE["numbers"]["floats"] = @benchmarkable TOML.parse(float_array)

###################
# Array of Tables #
###################

SUITE["array of tables"] = BenchmarkGroup()

const many_empty_array_of_tables =
    repeat("[[foo]] \n", 10^4)
SUITE["array of tables"]["empty"] = @benchmarkable TOML.parse(many_empty_array_of_tables)

#################
# Registry file #
#################

SUITE["registry"] = BenchmarkGroup()

const registry_toml = read(joinpath(@__DIR__, "files", "Registry.toml"), String)
SUITE["registry"]["Registry.toml"] = @benchmarkable TOML.parse(registry_toml)

const compat_toml = read(joinpath(@__DIR__, "files", "Compat.toml"), String)
SUITE["registry"]["Compat.toml"] = @benchmarkable TOML.parse(compat_toml)

############
# Overhead #
############

SUITE["parse empty"] = @benchmarkable TOML.parse("")

max_stars_repo_name: vanillajonathan/julia

max_issues_repo_name: vanillajonathan/julia

max_forks_repo_name: vanillajonathan/julia


text:
using MinimalPerfectHashes
using Base.Test

months = ["jan", "feb", "mar", "apr", "may", "jun", "jul",
          "aug", "sep", "oct", "nov", "dec"]

mph = MinimalPerfectHash(months)

results = map((x) -> hash(x, mph), months)

@test length(results) == 12
@test maximum(results)    == 12
@test minimum(results)    == 1


text:
# Main constants
const BOT_REPO_URL = "https://github.com/Humans-of-Julia/HoJBot.jl"

const ACTIVE_COMMANDS = LittleDict([
    :gm => false,
    :ig => true,
    :j => true,
    :react => true,
    :tz => true,
    :discourse => true,
    :src => true,
])

const COMMANDS_NAMES = LittleDict([
    :gm => :game_master,
    :ig => :ig,
    :j => :julia_doc,
    :react => :reaction,
    :tz => :time_zone,
    :discourse => :discourse,
    :src => :source,
])

const HANDLERS_LIST = [
    (:discourse, MessageReactionAdd, true),
    (:mod,       MessageCreate,      true),
    (:reaction,  MessageCreate,      true),
    (:whistle,   MessageReactionAdd, true),
]

const OPT_SERVICES_LIST = [:game_master, :reaction]


text:
struct DCG_CAP4 <: AbstractProgramme end
typeProgramme = DCG_CAP4()
nameProgramme = "DCG_CAP4"

# Main Exercises.
BenchT1 = Exercise(
    name = bench["flat"],
    equipment = equipment["bb"],
    trainingMax = 92.5,
)
BenchT2 = Exercise(
    name = bench["cg"],
    equipment = equipment["bb"],
    trainingMax = 75,
)
push!(exerProg, "BenchT1" => (exercise = BenchT1, progression = DCG_CAP4_Bench_T1))
push!(exerProg, "BenchT2" => (exercise = BenchT2, progression = DCG_CAP4_Bench_T2))

DeadliftT1 = Exercise(
    name = deadlift["dl"],
    equipment = equipment["bb"],
    trainingMax = 160,
)
DeadliftT2 = Exercise(
    name = deadlift["sumo"],
    equipment = equipment["bb"],
    trainingMax = 142.5,
)
push!(exerProg, "DeadliftT1" => (exercise = DeadliftT1, progression = DCG_CAP4_Deadlift_T1))
push!(exerProg, "DeadliftT2" => (exercise = DeadliftT2, progression = DCG_CAP4_Deadlift_T2))

SquatT1 = Exercise(
    name = squat["low"],
    equipment = equipment["bb"],
    trainingMax = 112.5,
)
SquatT2 = Exercise(
    name = squat["front"],
    equipment = equipment["bb"],
    trainingMax = 82.5,
)
push!(exerProg, "SquatT1" => (exercise = SquatT1, progression = DCG_CAP4_Squat_T1))
push!(exerProg, "SquatT2" => (exercise = SquatT2, progression = DCG_CAP4_Squat_T2))

RowT1 = Exercise(
    name = row["pendlay"],
    modality = modality["cheat"],
    equipment = equipment["bb"],
    trainingMax = 112.5,
)
RowT2 = Exercise(
    name = row["kroc"],
    equipment = equipment["db"],
    trainingMax = 40,
)
push!(exerProg, "RowT1" => (exercise = RowT1, progression = DCG_CAP4_Row_T1))
push!(exerProg, "RowT2" => (exercise = RowT2, progression = DCG_CAP4_Row_T2))

PressT1 = Exercise(
    name = press["ohp"],
    equipment = equipment["bb"],
    trainingMax = 57.5,
)
PressT2 = Exercise(
    name = press["ohp"],
    equipment = equipment["bb"],
    trainingMax = 57.5,
)
push!(exerProg, "PressT1" => (exercise = PressT1, progression = DCG_CAP4_OHP_T1))
push!(exerProg, "PressT2" => (exercise = PressT2, progression = DCG_CAP4_OHP_T2))

# Accessories
# Squat accessories.
lunges = Exercise(;
    name = squat["lunge"],
    modality = [modality["walking"], modality["backstep"], modality["dragon"]],
    equipment = equipment["db"],
    trainingMax = 47.5,
)
bulgarianSplit = Exercise(;
    name = squat["bulgarian"],
    equipment = equipment["db"],
    trainingMax = 42.5,
)
calfRaise = Exercise(;
    name = squat["calf raise"],
    modality = modality["standing"],
    equipment = equipment["db"],
    trainingMax = 17.5,
)
ProgLunges = deepcopy(h_lmhd)
ProgBulg = deepcopy(h_lmhd)
ProgCalfRaise = deepcopy(l_lmhd)
push!(exerProg, "lunges" => (exercise = lunges, progression = ProgLunges))
push!(exerProg, "bulgarianSplit" => (exercise = bulgarianSplit, progression = ProgBulg))
push!(exerProg, "calfRaise" => (exercise = calfRaise, progression = ProgCalfRaise))

# Grip
wristExt = Exercise(;
    name = grip["wrist extension"],
    equipment = equipment["db"],
    trainingMax = 10,
    roundBase = 1,
)
wristCurl = Exercise(;
    name = grip["wrist curl"],
    equipment = equipment["db"],
    trainingMax = 10,
    roundBase = 1,
)
platePinch = Exercise(;
    name = grip["plate pinch"],
    equipment = equipment["plate"],
    trainingMax = 15,
)
fingerCurl = Exercise(;
    name = grip["finger curl"],
    equipment = equipment["db"],
    trainingMax = 17.5,
)
ProgWristExt = deepcopy(AMRAP_Grip)
ProgWristCurl = deepcopy(AMRAP_Grip)
ProgPlatePinch = deepcopy(AMRAP_Grip)
ProgFingerCurl = deepcopy(AMRAP_Grip)
push!(exerProg, "wristExt" => (exercise = wristExt, progression = ProgWristExt))
push!(exerProg, "wristCurl" => (exercise = wristCurl, progression = ProgWristCurl))
push!(exerProg, "platePinch" => (exercise = platePinch, progression = ProgPlatePinch))
push!(exerProg, "fingerCurl" => (exercise = fingerCurl, progression = ProgFingerCurl))

# Pulling
facePull = Exercise(;
    name = pull["face"],
    equipment = equipment["cable"],
    trainingMax = 32.5,
)
pullup = Exercise(;
    name = pull["pullup"],
    equipment = equipment["pullup bar"],
    trainingMax = 0,
    roundBase = 1.25,
)
hammerCurl = Exercise(;
    name = pull["hammer"],
    equipment = [equipment["db"], equipment["fat grips"]],
    trainingMax = 17.5,
)
zottmanCurl = Exercise(;
    name = pull["zottman"],
    equipment = [equipment["db"], equipment["fat grips"]],
    trainingMax = 12.5,
)
ProgFacePull = deepcopy(l_lmhd)
ProgPullup = deepcopy(h_lmhd)
ProgHammerCurl = deepcopy(l_lmhd)
ProgZottmanCurl = deepcopy(l_lmhd)
push!(exerProg, "facePull" => (exercise = facePull, progression = ProgFacePull))
push!(exerProg, "pullup" => (exercise = pullup, progression = ProgPullup))
push!(exerProg, "hammerCurl" => (exercise = hammerCurl, progression = ProgHammerCurl))
push!(exerProg, "zottmanCurl" => (exercise = zottmanCurl, progression = ProgZottmanCurl))

# Pushing
inclineDBBench = Exercise(;
    name = bench["db incline"],
    equipment = equipment["db"],
    trainingMax = 45,
)
dips = Exercise(;
    name = bench["dips"],
    equipment = [equipment["parallette"], equipment["chain belt"]],
    trainingMax = 0,
    roundBase = 1.25,
)
sideRaise = Exercise(;
    name = press["side raise"],
    equipment = equipment["db"],
    trainingMax = 12.5,
)
ProgInclineDBBench = deepcopy(m_lmhd)
ProgDips = deepcopy(m_lmhd)
ProgSideRaise = deepcopy(l_lmhd)
push!(
    exerProg,
    "inclineDBBench" => (exercise = inclineDBBench, progression = ProgInclineDBBench),
)
push!(exerProg, "dips" => (exercise = dips, progression = ProgDips))
push!(exerProg, "sideRaise" => (exercise = sideRaise, progression = ProgSideRaise))

# Core
dragonFlag = Exercise(;
    name = core["dragon flag"],
    equipment = equipment["none"],
    trainingMax = 100,
)
vsit = Exercise(;
    name = core["v sit"],
    equipment = equipment["parallette"],
    trainingMax = 100,
)
legRaise = Exercise(;
    name = core["leg raise"],
    equipment = equipment["pullup bar"],
    trainingMax = 10,
)
toeToBar = Exercise(;
    name = core["toes to bar"],
    equipment = equipment["pullup bar"],
    trainingMax = 100,
)
abRollout = Exercise(;
    name = core["ab roll"],
    modality = modality["kneeling"],
    equipment = equipment["ab wheel"],
    trainingMax = 100,
)
landMine = Exercise(;
    name = core["landmine"],
    equipment = equipment["bb"],
    trainingMax = 47.5,
)
paraCrunch = Exercise(;
    name = core["parallette crunch"],
    equipment = [equipment["parallette"], equipment["db"]],
    trainingMax = 0,
)
ProgDragonFlag = deepcopy(AMRAP_Cali)
ProgVsit = deepcopy(AMRAP_Cali)
ProgLegRaise = deepcopy(m_lmhd)
ProgToeToBar = deepcopy(AMRAP_Cali)
ProgAbRollout = deepcopy(AMRAP_Cali)
ProgLandMine = deepcopy(m_lmhd)
ProgParaCrunch = deepcopy(m_lmhd)
push!(exerProg, "dragonFlag" => (exercise = dragonFlag, progression = ProgDragonFlag))
push!(exerProg, "vsit" => (exercise = vsit, progression = ProgVsit))
push!(exerProg, "legRaise" => (exercise = legRaise, progression = ProgLegRaise))
push!(exerProg, "toeToBar" => (exercise = toeToBar, progression = ProgToeToBar))
push!(exerProg, "abRollout" => (exercise = abRollout, progression = ProgAbRollout))
push!(exerProg, "landMine" => (exercise = landMine, progression = ProgLandMine))
push!(exerProg, "paraCrunch" => (exercise = paraCrunch, progression = ProgParaCrunch))

# Calisthenics Isometrics
frontLever = Exercise(;
    name = isometric["front"],
    trainingMax = 100,
)
backLever = Exercise(;
    name = isometric["back"],
    trainingMax = 100,
)
ProgFrontLever = deepcopy(AMRAP_Cali)
ProgBackLever = deepcopy(AMRAP_Cali)
push!(exerProg, "frontLever" => (exercise = frontLever, progression = ProgFrontLever))
push!(exerProg, "backLever" => (exercise = backLever, progression = ProgBackLever))

## Weeks
## Weeks
import Lifting: makeDays
function makeDays(::DCG_CAP4, dict::Dict)
    week1 = [[], [], [], [], [], [], []]
    week2 = [[], [], [], [], [], [], []]
    week3 = [[], [], [], [], [], [], []]
    week4 = [[], [], [], [], [], [], []]

    push!(week1[1], dict["BenchT1"][1], dict["BenchT1"][2], 1)
    push!(week1[1], dict["BenchT2"][1], dict["BenchT2"][2], 1)
    push!(week1[1], dict["wristCurl"][1], dict["wristCurl"][2])
    push!(week1[1], dict["wristExt"][1], dict["wristExt"][2])
    push!(week1[1], dict["dragonFlag"][1], dict["dragonFlag"][2])
    push!(week1[1], dict["frontLever"][1], dict["frontLever"][2])
    push!(week1[1], dict["lunges"][1], dict["lunges"][2], 3)
    push!(week1[1], dict["facePull"][1], dict["facePull"][2], 4)
    push!(week1[1], dict["legRaise"][1], dict["legRaise"][2], 2)

    push!(week1[2], dict["DeadliftT2"][1], dict["DeadliftT2"][2], 1)
    push!(week1[2], dict["RowT1"][1], dict["RowT1"][2], 1)
    push!(week1[2], dict["sideRaise"][1], dict["sideRaise"][2], 1)
    push!(week1[2], dict["hammerCurl"][1], dict["hammerCurl"][2], 4)
    push!(week1[2], dict["inclineDBBench"][1], dict["inclineDBBench"][2], 2)
    push!(week1[2], dict["platePinch"][1], dict["platePinch"][2])
    push!(week1[2], dict["zottmanCurl"][1], dict["zottmanCurl"][2], 4)
    push!(week1[2], dict["vsit"][1], dict["vsit"][2])

    push!(week1[3], dict["SquatT1"][1], dict["SquatT1"][2], 1)
    push!(week1[3], dict["PressT2"][1], dict["PressT2"][2], 1)
    push!(week1[3], dict["backLever"][1], dict["backLever"][2])
    push!(week1[3], dict["pullup"][1], dict["pullup"][2], 4)
    push!(week1[3], dict["calfRaise"][1], dict["calfRaise"][2], 2)
    push!(week1[3], dict["fingerCurl"][1], dict["fingerCurl"][2])
    push!(week1[3], dict["dips"][1], dict["dips"][2], 2)
    push!(week1[3], dict["abRollout"][1], dict["abRollout"][2])

    push!(week1[4], dict["BenchT1"][1], dict["BenchT1"][2], 2)
    push!(week1[4], dict["BenchT2"][1], dict["BenchT2"][2], 2)
    push!(week1[4], dict["wristCurl"][1], dict["wristCurl"][2])
    push!(week1[4], dict["wristExt"][1], dict["wristExt"][2])
    push!(week1[4], dict["landMine"][1], dict["landMine"][2], 2)
    push!(week1[4], dict["frontLever"][1], dict["frontLever"][2])
    push!(week1[4], dict["bulgarianSplit"][1], dict["bulgarianSplit"][2], 2)
    push!(week1[4], dict["facePull"][1], dict["facePull"][2], 4)
    push!(week1[4], dict["toeToBar"][1], dict["toeToBar"][2])

    push!(week1[5], dict["DeadliftT1"][1], dict["DeadliftT1"][2], 1)
    push!(week1[5], dict["RowT2"][1], dict["RowT2"][2], 1)
    push!(week1[5], dict["sideRaise"][1], dict["sideRaise"][2], 1)
    push!(week1[5], dict["hammerCurl"][1], dict["hammerCurl"][2], 4)
    push!(week1[5], dict["inclineDBBench"][1], dict["inclineDBBench"][2], 2)
    push!(week1[5], dict["platePinch"][1], dict["platePinch"][2])
    push!(week1[5], dict["zottmanCurl"][1], dict["zottmanCurl"][2], 4)
    push!(week1[5], dict["vsit"][1], dict["vsit"][2])

    push!(week1[6], dict["PressT1"][1], dict["PressT1"][2], 1)
    push!(week1[6], dict["SquatT2"][1], dict["SquatT2"][2], 1)
    push!(week1[6], dict["backLever"][1], dict["backLever"][2])
    push!(week1[6], dict["pullup"][1], dict["pullup"][2], 4)
    push!(week1[6], dict["calfRaise"][1], dict["calfRaise"][2], 2)
    push!(week1[6], dict["fingerCurl"][1], dict["fingerCurl"][2])
    push!(week1[6], dict["dips"][1], dict["dips"][2], 2)
    push!(week1[6], dict["paraCrunch"][1], dict["paraCrunch"][2], 2)

    # Week 2
    push!(week2[1], dict["BenchT1"][1], dict["BenchT1"][2], 3)
    push!(week2[1], dict["BenchT2"][1], dict["BenchT2"][2], 3)
    push!(week2[1], dict["wristCurl"][1], dict["wristCurl"][2])
    push!(week2[1], dict["wristExt"][1], dict["wristExt"][2])
    push!(week2[1], dict["dragonFlag"][1], dict["dragonFlag"][2])
    push!(week2[1], dict["frontLever"][1], dict["frontLever"][2])
    push!(week2[1], dict["lunges"][1], dict["lunges"][2], 4)
    push!(week2[1], dict["facePull"][1], dict["facePull"][2], 1)
    push!(week2[1], dict["legRaise"][1], dict["legRaise"][2], 3)

    push!(week2[2], dict["DeadliftT2"][1], dict["DeadliftT2"][2], 2)
    push!(week2[2], dict["RowT1"][1], dict["RowT1"][2], 2)
    push!(week2[2], dict["sideRaise"][1], dict["sideRaise"][2], 2)
    push!(week2[2], dict["hammerCurl"][1], dict["hammerCurl"][2], 1)
    push!(week2[2], dict["inclineDBBench"][1], dict["inclineDBBench"][2], 3)
    push!(week2[2], dict["platePinch"][1], dict["platePinch"][2])
    push!(week2[2], dict["zottmanCurl"][1], dict["zottmanCurl"][2], 1)
    push!(week2[2], dict["vsit"][1], dict["vsit"][2])

    push!(week2[3], dict["SquatT1"][1], dict["SquatT1"][2], 2)
    push!(week2[3], dict["PressT2"][1], dict["PressT2"][2], 2)
    push!(week2[3], dict["backLever"][1], dict["backLever"][2])
    push!(week2[3], dict["pullup"][1], dict["pullup"][2], 1)
    push!(week2[3], dict["calfRaise"][1], dict["calfRaise"][2], 3)
    push!(week2[3], dict["fingerCurl"][1], dict["fingerCurl"][2])
    push!(week2[3], dict["dips"][1], dict["dips"][2], 3)
    push!(week2[3], dict["abRollout"][1], dict["abRollout"][2])

    push!(week2[4], dict["BenchT1"][1], dict["BenchT1"][2], 4)
    push!(week2[4], dict["BenchT2"][1], dict["BenchT2"][2], 4)
    push!(week2[4], dict["wristCurl"][1], dict["wristCurl"][2])
    push!(week2[4], dict["wristExt"][1], dict["wristExt"][2])
    push!(week2[4], dict["landMine"][1], dict["landMine"][2], 3)
    push!(week2[4], dict["frontLever"][1], dict["frontLever"][2])
    push!(week2[4], dict["bulgarianSplit"][1], dict["bulgarianSplit"][2], 3)
    push!(week2[4], dict["facePull"][1], dict["facePull"][2], 1)
    push!(week2[4], dict["toeToBar"][1], dict["toeToBar"][2])

    push!(week2[5], dict["DeadliftT1"][1], dict["DeadliftT1"][2], 2)
    push!(week2[5], dict["RowT2"][1], dict["RowT2"][2], 2)
    push!(week2[5], dict["sideRaise"][1], dict["sideRaise"][2], 2)
    push!(week2[5], dict["hammerCurl"][1], dict["hammerCurl"][2], 1)
    push!(week2[5], dict["inclineDBBench"][1], dict["inclineDBBench"][2], 3)
    push!(week2[5], dict["platePinch"][1], dict["platePinch"][2])
    push!(week2[5], dict["zottmanCurl"][1], dict["zottmanCurl"][2], 1)
    push!(week2[5], dict["vsit"][1], dict["vsit"][2])

    push!(week2[6], dict["PressT1"][1], dict["PressT1"][2], 2)
    push!(week2[6], dict["SquatT2"][1], dict["SquatT2"][2], 2)
    push!(week2[6], dict["backLever"][1], dict["backLever"][2])
    push!(week2[6], dict["pullup"][1], dict["pullup"][2], 1)
    push!(week2[6], dict["calfRaise"][1], dict["calfRaise"][2], 3)
    push!(week2[6], dict["fingerCurl"][1], dict["fingerCurl"][2])
    push!(week2[6], dict["dips"][1], dict["dips"][2], 3)
    push!(week2[6], dict["paraCrunch"][1], dict["paraCrunch"][2], 3)

    # Week3
    push!(week3[1], dict["BenchT1"][1], dict["BenchT1"][2], 5)
    push!(week3[1], dict["BenchT2"][1], dict["BenchT2"][2], 5)
    push!(week3[1], dict["wristCurl"][1], dict["wristCurl"][2])
    push!(week3[1], dict["wristExt"][1], dict["wristExt"][2])
    push!(week3[1], dict["dragonFlag"][1], dict["dragonFlag"][2])
    push!(week3[1], dict["frontLever"][1], dict["frontLever"][2])
    push!(week3[1], dict["lunges"][1], dict["lunges"][2], 1)
    push!(week3[1], dict["facePull"][1], dict["facePull"][2], 2)
    push!(week3[1], dict["legRaise"][1], dict["legRaise"][2], 4)

    push!(week3[2], dict["DeadliftT2"][1], dict["DeadliftT2"][2], 3)
    push!(week3[2], dict["RowT1"][1], dict["RowT1"][2], 3)
    push!(week3[2], dict["sideRaise"][1], dict["sideRaise"][2], 3)
    push!(week3[2], dict["hammerCurl"][1], dict["hammerCurl"][2], 2)
    push!(week3[2], dict["inclineDBBench"][1], dict["inclineDBBench"][2], 4)
    push!(week3[2], dict["platePinch"][1], dict["platePinch"][2])
    push!(week3[2], dict["zottmanCurl"][1], dict["zottmanCurl"][2], 2)
    push!(week3[2], dict["vsit"][1], dict["vsit"][2])

    push!(week3[3], dict["SquatT1"][1], dict["SquatT1"][2], 3)
    push!(week3[3], dict["PressT2"][1], dict["PressT2"][2], 3)
    push!(week3[3], dict["backLever"][1], dict["backLever"][2])
    push!(week3[3], dict["pullup"][1], dict["pullup"][2], 2)
    push!(week3[3], dict["calfRaise"][1], dict["calfRaise"][2], 4)
    push!(week3[3], dict["fingerCurl"][1], dict["fingerCurl"][2])
    push!(week3[3], dict["dips"][1], dict["dips"][2], 4)
    push!(week3[3], dict["abRollout"][1], dict["abRollout"][2])

    push!(week3[4], dict["BenchT1"][1], dict["BenchT1"][2], 6)
    push!(week3[4], dict["BenchT2"][1], dict["BenchT2"][2], 6)
    push!(week3[4], dict["wristCurl"][1], dict["wristCurl"][2])
    push!(week3[4], dict["wristExt"][1], dict["wristExt"][2])
    push!(week3[4], dict["landMine"][1], dict["landMine"][2], 4)
    push!(week3[4], dict["frontLever"][1], dict["frontLever"][2])
    push!(week3[4], dict["bulgarianSplit"][1], dict["bulgarianSplit"][2], 4)
    push!(week3[4], dict["facePull"][1], dict["facePull"][2], 2)
    push!(week3[4], dict["toeToBar"][1], dict["toeToBar"][2])

    push!(week3[5], dict["DeadliftT1"][1], dict["DeadliftT1"][2], 3)
    push!(week3[5], dict["RowT2"][1], dict["RowT2"][2], 3)
    push!(week3[5], dict["sideRaise"][1], dict["sideRaise"][2], 3)
    push!(week3[5], dict["hammerCurl"][1], dict["hammerCurl"][2], 2)
    push!(week3[5], dict["inclineDBBench"][1], dict["inclineDBBench"][2], 4)
    push!(week3[5], dict["platePinch"][1], dict["platePinch"][2])
    push!(week3[5], dict["zottmanCurl"][1], dict["zottmanCurl"][2], 2)
    push!(week3[5], dict["vsit"][1], dict["vsit"][2])

    push!(week3[6], dict["PressT1"][1], dict["PressT1"][2], 3)
    push!(week3[6], dict["SquatT2"][1], dict["SquatT2"][2], 3)
    push!(week3[6], dict["backLever"][1], dict["backLever"][2])
    push!(week3[6], dict["pullup"][1], dict["pullup"][2], 2)
    push!(week3[6], dict["calfRaise"][1], dict["calfRaise"][2], 4)
    push!(week3[6], dict["fingerCurl"][1], dict["fingerCurl"][2])
    push!(week3[6], dict["dips"][1], dict["dips"][2], 4)
    push!(week3[6], dict["paraCrunch"][1], dict["paraCrunch"][2], 4)

    # Week 4
    push!(week4[1], dict["BenchT1"][1], dict["BenchT1"][2], 7)
    push!(week4[1], dict["BenchT2"][1], dict["BenchT2"][2], 7)
    push!(week4[1], dict["wristCurl"][1], dict["wristCurl"][2])
    push!(week4[1], dict["wristExt"][1], dict["wristExt"][2])
    push!(week4[1], dict["dragonFlag"][1], dict["dragonFlag"][2])
    push!(week4[1], dict["frontLever"][1], dict["frontLever"][2])
    push!(week4[1], dict["lunges"][1], dict["lunges"][2], 2)
    push!(week4[1], dict["facePull"][1], dict["facePull"][2], 3)
    push!(week4[1], dict["legRaise"][1], dict["legRaise"][2], 1)

    push!(week4[2], dict["DeadliftT2"][1], dict["DeadliftT2"][2], 4)
    push!(week4[2], dict["RowT1"][1], dict["RowT1"][2], 4)
    push!(week4[2], dict["sideRaise"][1], dict["sideRaise"][2], 4)
    push!(week4[2], dict["hammerCurl"][1], dict["hammerCurl"][2], 3)
    push!(week4[2], dict["inclineDBBench"][1], dict["inclineDBBench"][2], 1)
    push!(week4[2], dict["platePinch"][1], dict["platePinch"][2])
    push!(week4[2], dict["zottmanCurl"][1], dict["zottmanCurl"][2], 3)
    push!(week4[2], dict["vsit"][1], dict["vsit"][2])

    push!(week4[3], dict["SquatT1"][1], dict["SquatT1"][2], 4)
    push!(week4[3], dict["PressT2"][1], dict["PressT2"][2], 4)
    push!(week4[3], dict["backLever"][1], dict["backLever"][2])
    push!(week4[3], dict["pullup"][1], dict["pullup"][2], 3)
    push!(week4[3], dict["calfRaise"][1], dict["calfRaise"][2], 1)
    push!(week4[3], dict["fingerCurl"][1], dict["fingerCurl"][2])
    push!(week4[3], dict["dips"][1], dict["dips"][2], 1)
    push!(week4[3], dict["abRollout"][1], dict["abRollout"][2])

    push!(week4[4], dict["BenchT1"][1], dict["BenchT1"][2], 8)
    push!(week4[4], dict["BenchT2"][1], dict["BenchT2"][2], 8)
    push!(week4[4], dict["wristCurl"][1], dict["wristCurl"][2])
    push!(week4[4], dict["wristExt"][1], dict["wristExt"][2])
    push!(week4[4], dict["landMine"][1], dict["landMine"][2], 1)
    push!(week4[4], dict["frontLever"][1], dict["frontLever"][2])
    push!(week4[4], dict["bulgarianSplit"][1], dict["bulgarianSplit"][2], 1)
    push!(week4[4], dict["facePull"][1], dict["facePull"][2], 3)
    push!(week4[4], dict["toeToBar"][1], dict["toeToBar"][2])

    push!(week4[5], dict["DeadliftT1"][1], dict["DeadliftT1"][2], 4)
    push!(week4[5], dict["RowT2"][1], dict["RowT2"][2], 4)
    push!(week4[5], dict["sideRaise"][1], dict["sideRaise"][2], 4)
    push!(week4[5], dict["hammerCurl"][1], dict["hammerCurl"][2], 3)
    push!(week4[5], dict["inclineDBBench"][1], dict["inclineDBBench"][2], 1)
    push!(week4[5], dict["platePinch"][1], dict["platePinch"][2])
    push!(week4[5], dict["zottmanCurl"][1], dict["zottmanCurl"][2], 3)
    push!(week4[5], dict["vsit"][1], dict["vsit"][2])

    push!(week4[6], dict["PressT1"][1], dict["PressT1"][2], 4)
    push!(week4[6], dict["SquatT2"][1], dict["SquatT2"][2], 4)
    push!(week4[6], dict["backLever"][1], dict["backLever"][2])
    push!(week4[6], dict["pullup"][1], dict["pullup"][2], 3)
    push!(week4[6], dict["calfRaise"][1], dict["calfRaise"][2], 1)
    push!(week4[6], dict["fingerCurl"][1], dict["fingerCurl"][2])
    push!(week4[6], dict["dips"][1], dict["dips"][2], 1)
    push!(week4[6], dict["paraCrunch"][1], dict["paraCrunch"][2], 1)

    return [
        week1[1],
        week1[2],
        week1[3],
        week1[4],
        "Rest",
        week1[5],
        week1[6],
        week2[1],
        week2[2],
        "Rest",
        week2[3],
        week2[4],
        week2[5],
        week2[6],
        "Rest",
        week3[1],
        week3[2],
        week3[3],
        week3[4],
        "Rest",
        week3[5],
        week3[6],
        week4[1],
        week4[2],
        "Rest",
        week4[3],
        week4[4],
        week4[5],
        "Rest",
        week4[6],
        "Rest"
    ]
end

days = makeDays(DCG_CAP4(), exerProg)
push!(
    Lifting_Programmes,
    nameProgramme => Programme(typeProgramme, nameProgramme, exerProg, days),
)

max_stars_repo_name: dcelisgarza/Lifting


text:
struct Worker_Monitor
    idx::Dict{Integer,Integer}
    persist::BitMatrix
    last_seen::Matrix{Float64}
end

function Worker_Monitor(wk_pool::Vector{<:Integer})
    idx=Dict{Integer,Integer}()
    for (n,wk) in enumerate(wk_pool)
        idx[wk]=n
    end
    persist=trues(1,length(wk_pool))
    last_seen=[time() for x in 1:1, y in 1:length(wk_pool)]
    return Worker_Monitor(idx, persist, last_seen)
end

function update_worker_monitor!(mon,wk,persist)
    mon.persist[1,mon.idx[wk]]=persist
    mon.last_seen[1,mon.idx[wk]]=time()
end

function Base.show(io::IO, mon::Worker_Monitor; progress=false)
    printstyled("Worker Diagnostics\n", bold=true)
    pers=heatmap(float.(mon.persist), colormap=persistcolor, title="Peristence",labels=false)
    ls=heatmap([time()-ls for ls in mon.last_seen], title="Last Seen",labels=false)
    show(pers)
    println()
    show(ls)
    progress && return nrows(pers.graphics)+nrows(ls.graphics)+7
end

                function persistcolor(z, zmin, zmax)
                    z==1. && return 154
                    z==0. && return 160
                end


text:
import Lint: level, Location

msg1 = LintMessage(Location("none", 1), :W001, "", "foo", "message")
msg2 = LintMessage(Location("none", 2), :E002, "", "", "message\nmessage")
msg3 = LintMessage(Location("none", 1), :W001, "", "foo", "message")

@test string(msg1) == "none:1 W001 foo: message"
@test string(msg2) == "none:2 E002 : message\n              message"

@test sprint(show, msg1) == string(msg1)

@test msg1 == msg3

@test !isless(msg1, msg3)
@test !isless(msg3, msg1)

@test isless(msg1, LintMessage(Location("none2", 1), :W001, "", "foo", "message"))
@test isless(msg1, LintMessage(Location("none", 1), :I001, "", "foo", "message"))
@test !isless(msg1, LintMessage(Location("none", 1), :E001, "", "foo", "message"))
@test isless(msg1, LintMessage(Location("none", 2), :W001, "", "foo", "message"))
@test isless(msg1, LintMessage(Location("none", 1), :W001, "", "foo2", "message"))
@test isless(msg1, LintMessage(Location("none", 1), :W001, "", "foo", "message2"))

@test !iserror(msg1)
@test iswarning(msg1)
@test !isinfo(msg1)

@test iserror(msg2)
@test !iswarning(msg2)
@test !isinfo(msg2)

@test level(msg1) == :WARN
@test level(msg2) == :ERROR


text:
fade(x) = span(".fade", x)

icon(x) = span(".icon.icon-$x", [])