text:
in_channel = 3
out_channel = 5
N = 6
adj = [0. 2. 2. 0. 0. 0.;
       2. 0. 1. 0. 2. 0.;
       2. 1. 0. 5. 0. 2.;
       0. 0. 5. 0. 0. 0.;
       0. 2. 0. 0. 0. 0.;
       0. 0. 2. 0. 0. 0.]
deg = [4. 0. 0. 0. 0. 0.;
       0. 5. 0. 0. 0. 0.;
       0. 0. 10. 0. 0. 0.;
       0. 0. 0. 5. 0. 0.;
       0. 0. 0. 0. 2. 0.;
       0. 0. 0. 0. 0. 2.]
lap = [4. -2. -2. 0. 0. 0.;
       -2. 5. -1. 0. -2. 0.;
       -2. -1. 10. -5. 0. -2.;
       0. 0. -5. 5. 0. 0.;
       0. -2. 0. 0. 2. 0.;
       0. 0. -2. 0. 0. 2.]
norm_lap = [1. -2/sqrt(4*5) -2/sqrt(4*10) 0. 0. 0.;
            -2/sqrt(4*5) 1. -1/sqrt(5*10) 0. -2/sqrt(2*5) 0.;
            -2/sqrt(4*10) -1/sqrt(5*10) 1. -5/sqrt(5*10) 0. -2/sqrt(2*10);
            0. 0. -5/sqrt(5*10) 1. 0. 0.;
            0. -2/sqrt(2*5) 0. 0. 1. 0.;
            0. 0. -2/sqrt(2*10) 0. 0. 1.]

ug = SimpleWeightedGraph(6)
add_edge!(ug, 1, 2, 2); add_edge!(ug, 1, 3, 2); add_edge!(ug, 2, 3, 1)
add_edge!(ug, 3, 4, 5); add_edge!(ug, 2, 5, 2); add_edge!(ug, 3, 6, 2)

@testset "weightedgraphs" begin
    @testset "linalg" begin
        for T in [Int8, Int16, Int32, Int64, Int128]
            @test degree_matrix(adj, T, dir=:out) == T.(deg)
            @test degree_matrix(adj, T, dir=:out) == degree_matrix(adj, T, dir=:in)
            @test degree_matrix(adj, T, dir=:out) == degree_matrix(adj, T, dir=:both)
            @test laplacian_matrix(adj, T) == T.(lap)
        end
        for T in [Float16, Float32, Float64]
            @test degree_matrix(adj, T, dir=:out) == T.(deg)
            @test degree_matrix(adj, T, dir=:out) == degree_matrix(adj, T, dir=:in)
            @test degree_matrix(adj, T, dir=:out) == degree_matrix(adj, T, dir=:both)
            @test laplacian_matrix(adj, T) == T.(lap)
            @test normalized_laplacian(adj, T) ≈ T.(norm_lap)
        end
    end

    @testset "GCNConv" begin
        gc = GCNConv(ug, in_channel=>out_channel)
        @test size(gc.weight) == (out_channel, in_channel)
        @test size(gc.bias) == (out_channel,)
        @test graph(gc.graph)[] === ug
    end

    @testset "ChebConv" begin
        k = 4
        cc = ChebConv(ug, in_channel=>out_channel, k)
        @test size(cc.weight) == (out_channel, in_channel, k)
        @test size(cc.bias) == (out_channel,)
        @test size(cc.L̃) == (N, N)
        @test cc.k == k
        @test cc.in_channel == in_channel
        @test cc.out_channel == out_channel
    end

    @testset "GraphConv" begin
        gc = GraphConv(ug, in_channel=>out_channel)
        @test gc.adjlist == [[2,3], [1,3,5], [1,2,4,6], [3], [2], [3]]
        @test size(gc.weight1) == (out_channel, in_channel)
        @test size(gc.weight2) == (out_channel, in_channel)
        @test size(gc.bias) == (out_channel,)
    end

    @testset "GATConv" begin
        for heads = [1, 5]
            for concat = [true, false]
                gat = GATConv(ug, in_channel=>out_channel, heads=heads, concat=concat)
                @test gat.adjlist == [[2,3], [1,3,5], [1,2,4,6], [3], [2], [3]]
                @test size(gat.weight) == (out_channel * heads, in_channel)
                @test size(gat.bias) == (out_channel * heads,)
                @test size(gat.a) == (2*out_channel, heads, 1)
            end
        end
    end

    @testset "GatedGraphConv" begin
        num_layers = 3
        ggc = GatedGraphConv(ug, out_channel, num_layers)
        @test ggc.adjlist == [[2,3], [1,3,5], [1,2,4,6], [3], [2], [3]]
        @test size(ggc.weight) == (out_channel, out_channel, num_layers)
    end

    @testset "EdgeConv" begin
        ec = EdgeConv(ug, Dense(2*in_channel, out_channel))
        @test ec.adjlist == [[2,3], [1,3,5], [1,2,4,6], [3], [2], [3]]
    end
end


text:
function roots(R)

    @info "Finding the roots..."

    # Compact notation for the possible parities
    ϵ = [1 -1]
    # Maximum value for x≡k⋅R=E⋅R/ħ with Emax=Λ, which will appear as argument for the Bessel function
    xmax = Λ * R / ħ

    # Empty vector for roots and their corresponding quantum numbers l and ϵ
    xroot = Float64[]
    lroot = Int64[]
    ϵroot = Int64[]

    # Routine for finding roots, performed until no roots are found two consecutive times
    stillsearch = true
    θ = false
    li = 0
    while stillsearch
        for ϵi in ϵ
            # Boundary condition
            boundary(x) = sphericalbessel(x, li) - ϵi * sphericalbessel(x, li + ϵi)
            if (li!=0) || (ϵi!=-1)
                # Function for finding the root of the boundary condition in the interval [0,xmax]
                root = find_zeros(boundary, zero(xmax), xmax)
                @debug l, root, θ
                if isempty(root)
                    if θ
                        stillsearch=false
                    else
                        θ = true
                    end
                else
                    roots = [r for r in root]
                    append!(xroot, roots)
                    # Notice that, for a given li and ϵi, multiple roots may be found
                    append!(lroot, fill(li, length(roots)))
                    append!(ϵroot, fill(ϵi, length(roots)))
                end
            end
        end
        li = li+1
    end

    # Number of roots
    κ = length(xroot)

    @info "Done!"
    @debug "Roots found at: " xroot
    @debug "Corresponding l: " lroot
    @debug "Corresponding ϵ: " ϵroot

    return xroot, lroot, ϵroot, κ

end

ext: jl

max_stars_repo_name: avramescudana/QuarkDroplets

max_issues_repo_name: avramescudana/QuarkDroplets

max_forks_repo_name: avramescudana/QuarkDroplets


text:
abstract type delVortDef end

mutable struct delNone <:delVortDef end

mutable struct delSpalart <: delVortDef
    limit :: Int64
    dist :: Float64
    tol :: Float64
end


text:
using Pkg

Pkg.activate(pwd())
Pkg.resolve()

max_stars_repo_name: paulcsmith/web-frameworks

max_issues_repo_name: paulcsmith/web-frameworks

max_forks_repo_name: paulcsmith/web-frameworks


text:
# Note that this script can accept some limited command-line arguments, run
# `julia build_tarballs.jl --help` to see a usage message.
using BinaryBuilder, Pkg

name = "HGSCVRP"
version = v"2.0.0"

# Collection of sources required to complete build
sources = [
    GitSource("https://github.com/vidalt/HGS-CVRP.git", "b062d5fccb1f64864dd65a94dac35929059ab089")
]

# Bash recipe for building across all platforms
script = raw"""
cd $WORKSPACE/srcdir/HGS-CVRP
mkdir build && cd build
cmake -DCMAKE_INSTALL_PREFIX=$prefix \
    -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TARGET_TOOLCHAIN} \
    -DCMAKE_BUILD_TYPE=Release \
    ..
make -j${nproc}
make install
"""

# These are the platforms we will build for by default, unless further
# platforms are passed in on the command line
platforms = supported_platforms()
platforms = expand_cxxstring_abis(platforms)

# The products that we will ensure are always built
products = [
    LibraryProduct("libhgscvrp", :libhgscvrp),
    ExecutableProduct("hgs", :hgs)
]

# Dependencies that must be installed before this package can be built
dependencies = Dependency[
]

# Build the tarballs, and possibly a `build.jl` as well.
build_tarballs(ARGS, name, version, sources, script, platforms, products, dependencies; julia_compat="1.6")


text:
version=""

if length(ARGS) > 0 && ARGS[1] != ""
	index=0
	println("params:")
	for x in ARGS; @printf("%s%s",index>0 ? ", " : "", x); index+=1 end
	println()
	version=replace(ARGS[1],r"[.][0-9]+$","")
else
	println("No Parameters given.")
end

include(string("source/$version/main.jl"))
main()

max_stars_repo_name: Gilga/JGE

max_issues_repo_name: Gilga/JGE

max_forks_repo_name: Gilga/JGE


text:
struct MaybeVector{T} <: AbstractVector{T}
    length::UInt8
    data::T

    MaybeVector{T}() where {T} = new{T}(0x00)
    MaybeVector{T}(x::T) where {T} = new{T}(0x01, x)
end

MaybeVector() = MaybeVector{Any}()
MaybeVector(x::T) where {T} = MaybeVector{T}(x)
MaybeVector{T}(x) where {T} = MaybeVector{T}(convert(T, x))

Base.axes(a::MaybeVector) = (Base.OneTo(a.length),)
Base.size(a::MaybeVector) = (a.length,)
Base.IndexStyle(::Type{<:MaybeVector}) = IndexLinear()
@inline function Base.getindex(a::MaybeVector, i::Integer)
    @boundscheck if a.length != 0x01 || i != 1
        throw(BoundsError(a, i))
    end
    return a.data
end
@inline function Base.getindex(a::MaybeVector)
    @boundscheck if a.length != 0x01
        throw(BoundsError(a, i))
    end
    return a.data
end


text:
# subtract one unit to the power limit
# SP-
export psp_decrementplimit

const DECREMENT_PLIMIT = [0x53, 0x50, 0x2D, 0x0D]

"""
    psp_decrementplimit(io)

Subtract one unit to the power limit.
"""
function psp_decrementplimit(io_psp::IO)
  write(io_psp, DECREMENT_PLIMIT)
  return nothing
end


text:
    using Distributed
    addprocs(length(Sys.cpu_info())-1)

    @everywhere using DifferentialEquations
    @everywhere using OrdinaryDiffEq
    using DiffEqBase
    @everywhere using Sundials
    using Plots
    # using DiffEqGPU
    @everywhere using ODEInterfaceDiffEq
    using DataFrames
    using CSV
    @everywhere using Statistics
    @everywhere using LinearAlgebra
    using JLD
    @everywhere using StatsBase
    @everywhere using Random
    using SharedArrays
    @everywhere using BlackBoxOptim
    @everywhere using BlackBoxOptim: num_func_evals

    @everywhere include("E:\\UNI\\D_Drive\\PhD\\JULIAlang\\Generalisation_PLacExample\\Examples\\ModelsFunctions\\PLacExample_Model.jl")

    @everywhere function selectObsSim(simul)

        simObs = zeros(size(simul)[1], length(Obs));
        if typeof(Obs)==Array{Int,1}
            simObs = simul[:,(Obs),1];
        else
            coun = 1;
            for j in 1:length(Obs)
               if !(occursin.("+", Obs[j])) && !(occursin.("-", Obs[j])) &&
               !(occursin.("/", Obs[j])) && !(occursin.("*", Obs[j])) &&
                !(occursin.("^", Obs[j]))
                    tind = findall(x->x==Obs[j], stName)[1];
                    simObs[:,coun] = simul[:,tind,1];
                    coun +=1;
                else #This is the complicated stuff where we need to evaluate the function written as a string
                    tmp1 = Obs[j];
                    for k in 1:length(stName)
                        if occursin(stName[k], Obs[j])
                            tmp1 = replace(tmp1, stName[k]=>string(simul[:,k,1]))
                        end
                    end
                    simObs[:,coun] = eval(Meta.parse(tmp1));
                    coun +=1;
                end
            end
        end
        return(simObs)
    end

    @everywhere function parOptim(opts)
        ress = convert(SharedArray{Float64,2}, zeros(9,length(opts)));
        arr = @sync @distributed (vcat) for i in 1:length(opts)
            resT = bboptimize(opts[string("Opt_", i)]);
            ress[:,i] = resT.archive_output.best_candidate;

            chr = (0,i);
            tmpcv = vcat(chr, convcur[i]);
            tmpcv
        end;
        return ress, arr;
    end

    @everywhere function loglike(dats, mes, errs)

        # Check that the dimensions of all the vectors are the same
        if (size(dats) != size(mes)) || (size(dats) != size(errs))
            println("Please, check the vectors dimensions!")
            return
        end

        # Computation of log likelihood
        nd = length(dats); # Number of time points
        llk_nd = zeros(nd);

        for i in 1:nd
            tmp1 = log(2*pi);
            tmp2 = log(errs[i]^2);
            tmp3 = ((mes[i]-dats[i])^2)/(errs[i]^2);

            llk_nd[i] = (-0.5)*(tmp1+tmp2+tmp3);

            # Check to avoid Nans (should not be required)
            if isnan(llk_nd[i])
                llk_nd[i] = 0;
            end
        end
        llk = sum(llk_nd);

        return(llk);
    end

    @everywhere function objectiveMLEPLacExample(theta)

        LLK_Exps = zeros(1, Nexp);
        simobs = Array{Any,1}(undef,Nexp);
        for i in 1:Nexp
            simul = PLacExample_SolveAll(tim[i], theta, esp[i], inp[i], ini[i], smp[i], prr[i])
            simobs[i] = selectObsSim(simul);
            llkobs = zeros(1,length(Obs));
            for j in 1:length(Obs)
                llkobs[1,j] = loglike(dataM[i][:,j], simobs[i][:,j], dataE[i][j]);
            end
            LLK_Exps[1,i] = sum(llkobs);
        end
        return(-sum(LLK_Exps))
    end

    @everywhere function restructInputs(model_def, mle_def, expp)
        r = convert.(Int, 1:model_def["nInp"]:(length(mle_def["uInd"][expp])));
        inputs = zeros(convert.(Int,length(mle_def["uInd"][expp])));
        for j in 1:convert.(Int,length(mle_def["uInd"][expp])/model_def["nInp"])
            for k in 0:(model_def["nInp"]-1)
                inputs[r[j]+k] = mle_def["uInd"][expp][j,(k+1)];
            end
        end
        return(inputs)
    end

    function RunMLEPLacExample(model_def, mle_def)

        @spawnat 1 mle_def
        @spawnat 2 mle_def
        @spawnat 3 mle_def
        @spawnat 4 mle_def
        @spawnat 5 mle_def
        @spawnat 6 mle_def
        @spawnat 7 mle_def
        @spawnat 8 mle_def
        @spawnat 9 mle_def
        @spawnat 10 mle_def
        @spawnat 11 mle_def
        @spawnat 12 mle_def

        @spawnat 1 model_def
        @spawnat 2 model_def
        @spawnat 3 model_def
        @spawnat 4 model_def
        @spawnat 5 model_def
        @spawnat 6 model_def
        @spawnat 7 model_def
        @spawnat 8 model_def
        @spawnat 9 model_def
        @spawnat 10 model_def
        @spawnat 11 model_def
        @spawnat 12 model_def

        @everywhere global Nexp = mle_def["Nexp"];
        @everywhere global Obs = mle_def["Obs"];
        @everywhere global stName = model_def["stName"];

        @everywhere global dataM = mle_def["DataMean"];
        @everywhere global dataE = mle_def["DataError"];

        @everywhere ts = Array{Any,1}(undef,mle_def["Nexp"]);
        @everywhere sp = Array{Any,1}(undef,mle_def["Nexp"]);
        @everywhere ivss = Array{Any,1}(undef,mle_def["Nexp"]);
        @everywhere pre = Array{Any,1}(undef,mle_def["Nexp"]);
        @everywhere samps = Array{Any,1}(undef,mle_def["Nexp"]);
        @everywhere inputs = Array{Any,1}(undef,mle_def["Nexp"]);

        for i in 1:mle_def["Nexp"]
            global ts[i] = collect(0.0:round(mle_def["finalTime"][i]))';
            global sp[i] = [convert(Int,v) for v in (round.(mle_def["switchT"][i])')];
            global ivss[i] = mle_def["y0"][i];
            if model_def["Y0eqs"] != [] # ON inputs
                global pre[i] = mle_def["preInd"][i];
            else
                global pre[i] = [];
            end
            global samps[i] = convert.(Int, round.(mle_def["tsamps"][i]));
            global inputs[i] = restructInputs(model_def, mle_def, i);
        end

        @spawnat 1 ts
        @spawnat 1 sp
        @spawnat 1 ivss
        @spawnat 1 pre
        @spawnat 1 samps
        @spawnat 1 inputs
        @spawnat 2 ts
        @spawnat 2 sp
        @spawnat 2 ivss
        @spawnat 2 pre
        @spawnat 2 samps
        @spawnat 2 inputs
        @spawnat 3 ts
        @spawnat 3 sp
        @spawnat 3 ivss
        @spawnat 3 pre
        @spawnat 3 samps
        @spawnat 3 inputs
        @spawnat 4 ts
        @spawnat 4 sp
        @spawnat 4 ivss
        @spawnat 4 pre
        @spawnat 4 samps
        @spawnat 4 inputs
        @spawnat 5 ts
        @spawnat 5 sp
        @spawnat 5 ivss
        @spawnat 5 pre
        @spawnat 5 samps
        @spawnat 5 inputs
        @spawnat 6 ts
        @spawnat 6 sp
        @spawnat 6 ivss
        @spawnat 6 pre
        @spawnat 6 samps
        @spawnat 6 inputs
        @spawnat 7 ts
        @spawnat 7 sp
        @spawnat 7 ivss
        @spawnat 7 pre
        @spawnat 7 samps
        @spawnat 7 inputs
        @spawnat 8 ts
        @spawnat 8 sp
        @spawnat 8 ivss
        @spawnat 8 pre
        @spawnat 8 samps
        @spawnat 8 inputs
        @spawnat 9 ts
        @spawnat 9 sp
        @spawnat 9 ivss
        @spawnat 9 pre
        @spawnat 9 samps
        @spawnat 9 inputs
        @spawnat 10 ts
        @spawnat 10 sp
        @spawnat 10 ivss
        @spawnat 10 pre
        @spawnat 10 samps
        @spawnat 10 inputs
        @spawnat 11 ts
        @spawnat 11 sp
        @spawnat 11 ivss
        @spawnat 11 pre
        @spawnat 11 samps
        @spawnat 11 inputs
        @spawnat 12 ts
        @spawnat 12 sp
        @spawnat 12 ivss
        @spawnat 12 pre
        @spawnat 12 samps
        @spawnat 12 inputs

        @everywhere global tim = ts;
        @everywhere global esp = sp;
        @everywhere global inp = inputs;
        @everywhere global ini = ivss;
        @everywhere global prr = pre;
        @everywhere global smp = samps;

        @everywhere lb = mle_def["thetaMIN"];
        @everywhere up = mle_def["thetaMAX"];

        @everywhere rang = [Array{Tuple{Int, Int}}(undef, length(lb))];
        @everywhere rang = [(lb[i], up[i]) for i in 1:length(lb)];

        @everywhere chains = mle_def["runs"];
        @everywhere opts = Dict()

        @everywhere convcur = Array{Any,1}(undef, chains);
        for i in 1:chains
            convcur[i] = Array{Tuple{Int, Float64},1}();
            callback = oc -> push!(convcur[i], (num_func_evals(oc), best_fitness(oc)))
            opts[string("Opt_", i)] = bbsetup(objectiveMLEPLacExample; SearchRange = rang, NumDimensions = length(lb),
                Method = :adaptive_de_rand_1_bin_radiuslimited, MaxTime = 10, TraceMode = :silent, CallbackFunction = callback, CallbackInterval = 0.0);
        end

        println("----------------------------------------- OPTIMISATION STARTS! -----------------------------------------")

        @time begin
            tet, convcur2 = parOptim(opts);

        convcur3 = Array{Any}(undef,chains)
        i1 = 1;
        i2 = 1;
        k = 1;
        for h in 2:length(convcur2)
            if convcur2[h][1] < convcur2[h-1][1]
                i2 = h-1;
                convcur3[k] = convcur2[i1:i2];
                i1 = h;
                k += 1;
            elseif h == length(convcur2)
                i2 = h;
                convcur3[k] = convcur2[i1:i2];
            end
        end
        @everywhere convcur = Array{Any}(undef,chains);
        for k in 1:chains
            tmpch = convcur3[k][1][2];
            convcur[tmpch] = convcur3[k][2:end];
        end

        end

        println("----------------------------------------- OPTIMISATION ENDED -----------------------------------------")

        names = model_def["parName"];

        alltog = Array{Dict{String,Any},1}(undef,chains)
        for i in 1:(chains)
            global tmp = Dict{String, Any}()
            for j in 1:length(names)
                tmp[names[j]] = tet[j,i];
            end
            alltog[i] = tmp
        end

        mle_res = Dict();
        mle_res["Theta"] = convert(Array,tet); # Best thetas results
        mle_res["convCurv"] = convcur; # Convergence curves
        mle_res["StanDict"] = alltog; # Best thetas in the structure Stan needs as initial guess.

        bcfv = zeros(length(convcur))
        for k in 1:length(convcur)
            bcfv[k] = convcur[k][end][2]
        end
        mle_res["BestCFV"] = bcfv; # Best cost function values for each run

        tpin = findfirst(x->x==minimum(bcfv), bcfv);
        mle_res["BestTheta"] = tet[:,tpin]; # Best theta value from all runs

        return(mle_res)

    end

max_stars_repo_name: DavidGomezC/BOMBSbeta

max_issues_repo_name: DavidGomezC/BOMBSbeta

max_forks_repo_name: DavidGomezC/BOMBSbeta


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

if VERSION < v"0.4-"
    if Pkg.installed( "Dates" ) == nothing
        Pkg.add( "Dates" )
    end
end

module TermWin

using Compat
using Formatting

if VERSION < v"0.4-"
    using Dates
    const fnames = Base.names
else
    using Base.Dates
    const fnames = Base.fieldnames
end

using DataArrays
using DataFrames
using DataFramesMeta

macro lintpragma( s )
end

debugloghandle = nothing

function logstart()
    global debugloghandle
    debugloghandle = open( joinpath( Pkg.dir( "TermWin" ), "debug.log" ), "a+" )
end

function log{T<:AbstractString}( s::T )
    global debugloghandle
    if debugloghandle != nothing
        write( debugloghandle, string(now()) * " " )
        write( debugloghandle, s )
        write( debugloghandle, "\n" )
        flush( debugloghandle )
    end
end

import Base.getindex

# uncomment this to provide logging functionality
# or just call TermWin.logstart() in user's script
# logstart()

include( "consts.jl")
include( "twtypes.jl")
include( "twobj.jl")
include( "twscreen.jl")
include( "ccall.jl" )
include( "strutils.jl")
include( "format.jl" )
include( "dfutils.jl" )
#include( "twprogress.jl")
include( "twviewer.jl")
include( "twentry.jl")
include( "readtoken.jl" )
include( "twtree.jl" )
include( "twfunc.jl" )
include( "twpopup.jl" )
include( "twmultiselect.jl")
include( "twcalendar.jl")
include( "twdftable.jl" )
include( "twlist.jl" )
include( "precompile.jl" )

export tshow, activateTwObj, registerTwObj, unregisterTwObj
export trun # experimental
export TwObj, TwScreen, rootTwScreen
export newTwScreen
export newTwEntry, newTwTree, newTwFunc, newTwViewer
export newTwCalendar, newTwPopup, newTwMultiSelect
export newTwDfTable, newTwList

export uniqvalue, unionall
export CalcPivot, discretize, topnames
export FormatHints
export COLOR_PAIR

rootwin = nothing
rootTwScreen = nothing
callcount = 0
acs_map_arr = UInt8[]
COLORS = 8
COLOR_PAIRS = 16

function extractkwarg!( kwargs::Array{Any,1}, sym::Symbol, def::Any )
    for (i,t) in enumerate( kwargs )
        if t[1] == sym
            deleteat!( kwargs, i )
            return t[2]
        end
    end
    return def
end

function initsession()
    global rootwin, libncurses, acs_map_arr, COLORS, COLOR_PAIRS
    global rootTwScreen
    global widgetStaggerPosx
    global widgetStaggerPosy

    widgetStaggerPosx = 0
    widgetStaggerPosy = 0
    if rootwin == nothing || rootwin == C_NULL
        ENV["ESCDELAY"] = "25"
        rootwin = initscr()
        if rootwin == C_NULL
            println( "cannot create root win in ncurses")
            return
        end
        if !has_colors()
            ccall( dlsym( libncurses, :endwin), Void, () )
            throw( "terminal doesn't support colors")
        end
        mousemask( BUTTON1_PRESSED | REPORT_MOUSE_POSITION )
        acs_map_ptr = cglobal( Libdl.dlsym( libncurses, :acs_map), UInt32 )
        acs_map_arr = pointer_to_array( acs_map_ptr, 128)

        start_color()
        # figure out how many colors are supported
        colorsptr = cglobal( Libdl.dlsym( libncurses, :COLORS), Int16 )
        colorsarr = pointer_to_array( colorsptr, 1 )
        COLORS = colorsarr[1]

        colorsptr = cglobal( Libdl.dlsym( libncurses, :COLOR_PAIRS), Int16 )
        colorsarr = pointer_to_array( colorsptr, 1 )
        COLOR_PAIRS = colorsarr[1]

        init_pair( 1, COLOR_RED,    COLOR_BLACK )
        init_pair( 2, COLOR_GREEN,  COLOR_BLACK )
        init_pair( 3, COLOR_YELLOW, COLOR_BLACK )
        init_pair( 4, COLOR_BLUE,   COLOR_BLACK )
        init_pair( 5, COLOR_MAGENTA, COLOR_BLACK )
        init_pair( 6, COLOR_CYAN,   COLOR_BLACK )
        init_pair( 7, COLOR_WHITE,  COLOR_BLACK )
        if COLOR_PAIRS >= 16 && COLORS >= 256 # blue background
            init_pair( 8, COLOR_BLACK,  21 ) # black on BRIGHT blue
            init_pair( 9, COLOR_RED,    19 ) # red on blue
            init_pair( 10, COLOR_GREEN,  19 ) # green on blue
            init_pair( 11, COLOR_YELLOW, 19 ) # yellow on blue
            init_pair( 12, COLOR_WHITE,   52 ) # white on dark RED
            init_pair( 13, COLOR_WHITE,  234 ) # white on dark gray
            init_pair( 14, COLOR_CYAN,   19 ) # cyan on blue
            init_pair( 15, COLOR_WHITE,  19 ) # white on blue
        end
        if COLOR_PAIRS >= 24 && COLORS >= 256 # dark red background
            init_pair( 16, COLOR_BLACK,  52 )
            init_pair( 17, COLOR_RED,    52 )
            init_pair( 18, COLOR_GREEN,  52 )
            init_pair( 19, COLOR_YELLOW, 52 )
            init_pair( 20, COLOR_BLUE,   52 )
            init_pair( 21, COLOR_MAGENTA, 52)
            init_pair( 22, COLOR_CYAN,   52 )
            init_pair( 23, COLOR_WHITE,  52 )
        end
        if COLOR_PAIRS >= 32 && COLORS >= 256 # dark red background
            init_pair( 24, 56,  COLOR_BLACK ) # light purple on black
            init_pair( 25, 56,  19 ) # light purple on dark blue
            init_pair( 26, COLOR_GREEN,  19 ) # purple on dark blue
            init_pair( 27, COLOR_YELLOW,  19 ) # yellow on dark blue
            init_pair( 28, 8,  19 ) # gray on dark blue
            init_pair( 29, COLOR_RED, 235 ) # red on dark gray
            init_pair( 30, COLOR_WHITE, 17 ) # white on dark blue
            init_pair( 31, COLOR_RED  , 17 ) # red on dark blue
        end
        keypad( rootwin, true )
        mouseinterval( 0 )
        nodelay( rootwin, true )
        notimeout( rootwin, false )
        wtimeout( rootwin, 100 )
        curs_set( 0 )
        rootTwScreen = newTwScreen( rootwin )
        msg = string( @compat Char( 0xb83) ) * " TermWin: Please wait ..."
        mvwprintw( rootwin, (@compat trunc(Int, rootTwScreen.height / 2)),
            (@compat trunc(Int, ( rootTwScreen.width - length(msg))/2)), "%s", msg)
        wrefresh( rootwin )
    else
        # in case the terminal has been resized
        maxy, maxx = getwinmaxyx( rootwin )
        if maxy != rootTwScreen.height || maxx != rootTwScreen.width
            wresize( rootwin, maxy, maxx )
        end
        wrefresh( rootwin )
    end
end

function get_acs_val( c::Char )
    #= This is what you should use

        ACS_ULCORNER = get_acs_val('l') /* upper left corner */
        ACS_LLCORNER = get_acs_val('m') /* lower left corner */
        ACS_URCORNER = get_acs_val('k') /* upper right corner */
        ACS_LRCORNER = get_acs_val('j') /* lower right corner */
        ACS_LTEE = get_acs_val('t') /* tee pointing right */
        ACS_RTEE = get_acs_val('u') /* tee pointing left */
        ACS_BTEE = get_acs_val('v') /* tee pointing up */
        ACS_TTEE = get_acs_val('w') /* tee pointing down */
        ACS_HLINE = get_acs_val('q') /* horizontal line */
        ACS_VLINE = get_acs_val('x') /* vertical line */
        ACS_PLUS = get_acs_val('n') /* large plus or crossover */
        ACS_S1  = get_acs_val('o') /* scan line 1 */
        ACS_S9  = get_acs_val('s') /* scan line 9 */
        ACS_DIAMOND = get_acs_val('`') /* diamond */
        ACS_CKBOARD = get_acs_val('a') /* checker board (stipple) */
        ACS_DEGREE = get_acs_val('f') /* degree symbol */
        ACS_PLMINUS = get_acs_val('g') /* plus/minus */
        ACS_BULLET = get_acs_val('~') /* bullet */
        ACS_LARROW = get_acs_val(',') /* arrow pointing left */
        ACS_RARROW = get_acs_val('+') /* arrow pointing right */
        ACS_DARROW = get_acs_val('.') /* arrow pointing down */
        ACS_UARROW = get_acs_val('-') /* arrow pointing up */
        ACS_BOARD = get_acs_val('h') /* board of squares */
        ACS_LANTERN = get_acs_val('i') /* lantern symbol */
        ACS_BLOCK = get_acs_val('0') /* solid square block */
    =#
    acs_map_arr[ (@compat Int( @compat UInt8( c ) )) + 1 ]
end

function endsession()
    endwin()
end

function tshow_( x::Number; kwargs... )
    typx = typeof( x )
    if typx <: Integer && typx <: Unsigned
        s = @sprintf( "0x%x", x )
    else
        s = string( x )
    end
    tshow_( s; kwargs... )
end

tshow_( x::Symbol; kwargs... ) = tshow_( ":"*string(x); kwargs... )
tshow_( x::Ptr; kwargs... ) = tshow_( string(x); kwargs... )
function tshow_( x::WeakRef; kwargs... )
    if x.value == nothing
        tshow_( "WeakRef: nothing"; kwargs... )
    else
        tshow_( x.value; kwargs... )
    end
end

function tshow_{T<:AbstractString}( x::T; kwargs... )
    pos = :center
    if length(x) > 100
        pos = :staggered
    end
    posx  = extractkwarg!( kwargs, :posx, pos )
    posy  = extractkwarg!( kwargs, :posy, pos )
    newTwViewer( rootTwScreen, x; posy = posy, posx = posx, kwargs... )
end

function tshow_( f::Function; kwargs... )
    try
        mt = methods( f )
        return newTwFunc( rootTwScreen, mt; kwargs... )
    catch y
        return tshow_( string(f) * ": (anonymous?)\n" * string(y) ; kwargs... )
    end
end

function tshow_( mt::MethodTable; kwargs... )
    newTwFunc( rootTwScreen, mt; kwargs... )
end

function tshow_( ms::Array{Method,1}; kwargs... )
    newTwFunc( rootTwScreen, ms; kwargs... )
end

function tshow_( df::DataFrame; kwargs... )
    newTwDfTable( rootTwScreen, df; kwargs... )
end

function tshow_( o::TwObj; kwargs... )
    @lintpragma( "Ignore unused kwargs")
    registerTwObj( rootTwScreen, o )
    return o
end

function tshow_( x; kwargs... )
    newTwTree( rootTwScreen, x; kwargs... )
end

function winnewcenter( ysize, xsize, locy=0.5, locx=0.5 )
    global rootwin
    (maxy, maxx) = getwinmaxyx( rootwin )
    local cols, lines, origx, origy
    if isa( ysize, Int )
        lines = ysize
    elseif isa( ysize, Float64 ) && 0.0 < ysize <= 1.0
        lines = @compat Int( maxy * ysize )
        if lines == 0
            throw( "lines are too small")
        end
    else
        throw( "illegal ysize " * string( ysize ) )
    end

    if isa( xsize, Int )
        cols = xsize
    elseif isa( xsize, Float64 ) && 0.0 < xsize <= 1.0
        cols = @compat Int( maxx * xsize )
        if cols == 0
            throw( "cols are too small")
        end
    else
        throw( "illegal xsize " * string( ysize ) )
    end

    if isa( locy, Int )
        origy = max( 0, min( locy, maxy-lines-1 ) )
    elseif isa( locy, Float64 ) && 0.0 <= locy <= 1.0
        origy = @compat Int( floor( locy * ( maxy - lines ) ) )
    else
        throw( "illegal locy " * string( locy) )
    end

    if isa( locx, Int )
        origx = max( 0, min( locx, maxx-cols-1 ) )
    elseif isa( locx, Float64 ) && 0.0 <= locx <= 1.0
        origx = @compat Int( floor( locx * ( maxx - cols ) ) )
    else
        throw( "illegal locx " * string( locx) )
    end
    win = newwin( lines, cols, origy, origx )
    cbreak()
    noecho()
    keypad( win, true )
    nodelay( win, true )
    notimeout( win, false )
    wtimeout( win, 100 )
    win
end

function titleof( x::Any )
    typx = typeof( x )
    if typx == Module || typx == Function
        return utf8( string( x ) )
    else
        return utf8( string( typx ) )
    end
end

# it'd return the widget, which can be displayed again.
function tshow( x; kwargs... )
    global callcount, rootwin, rootTwScreen
    title = extractkwarg!( kwargs, :title, titleof(x) )
    widget = nothing
    if callcount == 0
        initsession()
        callcount += 1
        werase( rootwin )
        try
            widget = tshow_( x; title=title, kwargs... )
            if widget != nothing
                activateTwObj( rootTwScreen )
            end
        catch err
            callcount -= 1
            endsession()
            rethrow( err )
        end
        callcount -= 1
        endsession()
        return widget
    else
        found = false
        for o in rootTwScreen.data.objects
            if !in( objtype( o ), [ :Entry, :Viewer, :Calendar ] ) && isequal( o.value, x )
                raiseTwObject( o )
                found = true
                widget = o
                break
            end
        end
        if !found
            widget = nothing
            try
                widget = tshow_(x; title=title, kwargs... )
            catch err
                bt = catch_backtrace()
                msg = wordwrap( string(err) * "\n" * string( bt ), 80 )
                widget = tshow_( msg; title=utf8("Error") )
            end
            if widget != nothing
                refresh( rootTwScreen )
            end
        end
        return widget
    end
end

# f is a no-arg function
function trun( f::Function; kwargs... )
    # async start the function
    # start the progress bar window and listen to it
    global callcount, rootwin, rootTwScreen
    @async begin
        problem = false
        updateProgressChannel( :init, nothing )
        try
            val = f()
            updateProgressChannel( :done, val )
        catch er
            updateProgressChannel( :error, er )
        end
    end

    ret = nothing

    if callcount == 0
        initsession()
        callcount += 1
        werase( rootwin )
        try
            o = newTwProgress( rootTwScreen; kwargs... )
            if o != nothing
                activateTwObj( rootTwScreen )
                ret = o.value
            end
        catch er
            callcount -= 1
            endsession()
            rethrow( er )
        end
        callcount -= 1
        endsession()
        return ret
    else
        found = false
        for o in rootTwScreen.data.objects
            if objtype( o ) == :Progress
                raiseTwObject( o )
                o.isvisible = true
                found = true
                break
            end
        end
        if !found
            o = newTwProgress( rootTwScreen; kwargs... )
            o.hasFocus = true
            rootTwScreen.data.focus = length( rootTwScreen.data.objects )
            refresh( rootTwScreen )
        end
        return nothing
    end
end

function testkeydialog()
    width = 42
    initsession()
    win = winnewcenter( 6, width )
    panel = new_panel( win )
    box( win, 0, 0 )
    title = utf8( "Test Key/Mouse/Unicode" )
    keyhint = "[Esc to continue]"

    mvwprintw( win, 0, (@compat Int( (width-length(title))>>1)), "%s", title )
    mvwprintw( win, 5, (@compat Int( (width-length(keyhint))>>1)), "%s", keyhint )
    update_panels()
    doupdate()
    local token
    while( (token = readtoken( win )) != :esc )
        if token == :nochar
            continue
        end
        k = ""
        if isa( token, AbstractString )
            for c in token
                if isprint( c ) && isascii( c )
                    k *= string(c)
                else
                    k *= @sprintf( "{%x}", @compat UInt(c))
                end
            end
            k = k * repeat( " ", 21-length(k) )
            mvwprintw( win, 1, 2, "%s", k)
            if 1 <= (@compat UInt64(token[1])) <= 127
                mvwprintw( win, 2, 2, "%s", "acs_val:        " )
                mvwaddch( win, 2,11, get_acs_val( token[1] ) )

                #mvwprintw( win, 3,2, "%s", "acs_val_wch:     " )
                #mvwadd_wch( win, 3,11, get_acs_val( token[1] ) )
            else
                mvwprintw( win, 2, 2, "%s        ", "print  :" * string(token[1]) )
            end
        elseif token == :KEY_MOUSE
            ( state, x, y, bs ) = getmouse()
            mvwprintw( win, 1,2, "%s", @sprintf( "x:%d y:%d   ", x,y ))
            mvwprintw( win, 2,2, ":%s      ", string(state))
            #=
            for j = 1:2
                k = ""
                for i = 1:16
                    k *= @sprintf( "%02x", bs[i+(j-1)*16] )
                end
                mvwprintw( win, j, 2, "%s", k )
            end
            =#
        elseif isa( token, Symbol )
            k = ":" * string(token)
            mvwprintw( win, 2, 2, "%s", "                " )
            k = k * repeat( " ", 21-length(k) )
            mvwprintw( win, 1, 2, "%s", k)
        end
        update_panels()
        doupdate()
    end
    del_panel(panel)
    delwin( win )
    endsession()
end

end


text:
using UncertaintyQuantification

function likelihood(x)
    eigvaln = [1.51 0.33; 4.01 0.30; 3.16 0.27; 3.21 0.18; 2.19 0.33; 1.71 0.23;
 2.73 0.21; 5.51 0.20; 1.95 0.11; 4.48 0.20; 1.43 0.16; 2.91 0.26; 3.81 0.23;
 3.58 0.25; 2.62 0.25]

    sig = [1 0.1]

    la1 = (x[1] + 2*x[2] + sqrt(x[1].^2 + 4*x[2].^2))/2
    la2 = (x[1] + 2*x[2] - sqrt(x[1].^2 + 4*x[2].^2))/2

    model = [la1 la2]
    rv = 0

    for i in 1:2
        for j in 1:15
            rv += ((eigvaln[j,i] - model[i])/sig[i])^2
        end
    end
    return exp(-1/2*rv)
 end

 tuningsig = [0.04 0; 0 0.04]
 propdist = MvNormal([0;0], tuningsig)
 propdistsample() = rand(propdist)
 propdistpdf(x) = pdf(propdist, x)

 priorsample(n) = rand(Uniform(0.001,4),(n, 2))

 function uniformprior(x)
    if x[1] > 0.001 && x[1] < 4 && x[2] > 0.001 && x[2] < 4
        rv = 1
    else
        rv = 0
    end
    return  rv
 end

 function term(sset, j)
    #cov = std(sset)/mean(sset)
    #coefficient of variance is an alternative way to terminate the sampler
    rv = true
    if j >= 1 rv = false end
    return rv
 end

tmcmcexsample = tmcmc(likelihood, uniformprior, priorsample, 1000, 0.001)

smcexsample = smc(likelihood, uniformprior, priorsample, propdistpdf, propdistsample, 1000, term)

mhexsample = mh(likelihood, uniformprior, propdistpdf, propdistsample, [2.84, 2.33], 1000, 0)


text:
# Masks

const SF_FORMAT_ENDMASK   = 0x30000000
const SF_FORMAT_TYPEMASK  = 0x0FFF0000
const SF_FORMAT_SUBMASK   = 0x0000FFFF

# Endian-ness options

const SF_ENDIAN_FILE      = 0x00000000 # Default file endian-ness.
const SF_ENDIAN_LITTLE    = 0x10000000 # Force little endian-ness.
const SF_ENDIAN_BIG       = 0x20000000 # Force big endian-ness.
const SF_ENDIAN_CPU       = 0x30000000 # Force CPU endian-ness.

# Major Formats

const SF_FORMAT_WAV       = 0x00010000 # Microsoft WAV format (little endian).
const SF_FORMAT_AIFF      = 0x00020000 # Apple/SGI AIFF format (big endian).
const SF_FORMAT_AU        = 0x00030000 # Sun/NeXT AU format (big endian).
const SF_FORMAT_RAW       = 0x00040000 # RAW PCM data.
const SF_FORMAT_PAF       = 0x00050000 # Ensoniq PARIS file format.
const SF_FORMAT_SVX       = 0x00060000 # Amiga IFF / SVX8 / SV16 format.
const SF_FORMAT_NIST      = 0x00070000 # Sphere NIST format.
const SF_FORMAT_VOC       = 0x00080000 # VOC files.
const SF_FORMAT_IRCAM     = 0x000A0000 # Berkeley/IRCAM/CARL
const SF_FORMAT_W64       = 0x000B0000 # Sonic Foundry's 64 bit RIFF/WAV
const SF_FORMAT_MAT4      = 0x000C0000 # Matlab (tm) V4.2 / GNU Octave 2.0
const SF_FORMAT_MAT5      = 0x000D0000 # Matlab (tm) V5.0 / GNU Octave 2.1
const SF_FORMAT_PVF       = 0x000E0000 # Portable Voice Format
const SF_FORMAT_XI        = 0x000F0000 # Fasttracker 2 Extended Instrument
const SF_FORMAT_HTK       = 0x00100000 # HMM Tool Kit format
const SF_FORMAT_SDS       = 0x00110000 # Midi Sample Dump Standard
const SF_FORMAT_AVR       = 0x00120000 # Audio Visual Research
const SF_FORMAT_WAVEX     = 0x00130000 # MS WAVE with WAVEFORMATEX
const SF_FORMAT_SD2       = 0x00160000 # Sound Designer 2
const SF_FORMAT_FLAC      = 0x00170000 # FLAC lossless file format
const SF_FORMAT_CAF       = 0x00180000 # Core Audio File format
const SF_FORMAT_WVE       = 0x00190000 # Psion WVE format
const SF_FORMAT_OGG       = 0x00200000 # Xiph OGG container
const SF_FORMAT_MPC2K     = 0x00210000 # Akai MPC 2000 sampler
const SF_FORMAT_RF64      = 0x00220000 # RF64 WAV file

# SubFormats

const SF_FORMAT_PCM_S8    = 0x00000001 # Signed 8 bit data
const SF_FORMAT_PCM_16    = 0x00000002 # Signed 16 bit data
const SF_FORMAT_PCM_24    = 0x00000003 # Signed 24 bit data
const SF_FORMAT_PCM_32    = 0x00000004 # Signed 32 bit data
const SF_FORMAT_PCM_U8    = 0x00000005 # Unsigned 8 bit data (WAV and RAW only)
const SF_FORMAT_FLOAT     = 0x00000006 # 32 bit float data
const SF_FORMAT_DOUBLE    = 0x00000007 # 64 bit float data
const SF_FORMAT_ULAW      = 0x00000010 # U-Law encoded.
const SF_FORMAT_ALAW      = 0x00000011 # A-Law encoded.
const SF_FORMAT_IMA_ADPCM = 0x00000012 # IMA ADPCM.
const SF_FORMAT_MS_ADPCM  = 0x00000013 # Microsoft ADPCM.
const SF_FORMAT_GSM610    = 0x00000020 # GSM 6.10 encoding.
const SF_FORMAT_VOX_ADPCM = 0x00000021 # Oki Dialogic ADPCM encoding.
const SF_FORMAT_G721_32   = 0x00000030 # 32kbs G721 ADPCM encoding.
const SF_FORMAT_G723_24   = 0x00000031 # 24kbs G723 ADPCM encoding.
const SF_FORMAT_G723_40   = 0x00000032 # 40kbs G723 ADPCM encoding.
const SF_FORMAT_DWVW_12   = 0x00000040 # 12 bit Delta Width Variable Word encoding.
const SF_FORMAT_DWVW_16   = 0x00000041 # 16 bit Delta Width Variable Word encoding.
const SF_FORMAT_DWVW_24   = 0x00000042 # 24 bit Delta Width Variable Word encoding.
const SF_FORMAT_DWVW_N    = 0x00000043 # N bit Delta Width Variable Word encoding.
const SF_FORMAT_DPCM_8    = 0x00000050 # 8 bit differential PCM (XI only)
const SF_FORMAT_DPCM_16   = 0x00000051 # 16 bit differential PCM (XI only)
const SF_FORMAT_VORBIS    = 0x00000060 # Xiph Vorbis encoding.


text:
# SPDX-License-Identifier: MIT

#======================================================================
Liftering
======================================================================#

function makelifter(N, L)
	t = Vector(1:N)
	1 .+ L/2 * sin.(π * t / L)
end

#======================================================================
Pre-emphasis
======================================================================#

function preemphasis!(x; k=0.97)
	prev = x[1]
	for i in 2:length(x)
        prev2 = x[i]
		x[i] = x[i] - k*prev
		prev = prev2
	end
end

#======================================================================
Delta features
======================================================================#

function delta(X::AbstractMatrix{T}, deltawin::Int = 2) where T
    D, N = size(X)
    Δ = zeros(T, D, N)
    norm = T(2 * sum(collect(1:deltawin).^2))
    for n in 1:N
        for θ in 1:deltawin
            Δ[:, n] += (θ * (X[:,min(N, n+θ)] - X[:,max(1,n-θ)])) / norm
        end
    end
    Δ
end

#======================================================================
Framing
======================================================================#

struct FrameIterator{T<:AbstractVector}
    x::T
    framesize::Int64
    hopsize::Int64
end

function Base.length(it::FrameIterator)
    N = length(it.x)
    N > it.framesize ? 1 + (N - it.framesize) ÷ it.hopsize : 0
end

function Base.iterate(it::FrameIterator, idx=1)
    1 + length(it.x) - idx < it.framesize &&  return nothing
    (view(it.x, idx:(idx + it.framesize - 1)), idx + it.hopsize)
end

eachframe(x::AbstractVector; srate=16000, frameduration=0.025, framestep=0.01) =
    FrameIterator(x, Int64(srate * frameduration), Int64(srate * framestep))

#======================================================================
Window functions
======================================================================#

function HannWindow(T::Type, N::Int)
    T(.5) .* (1 .- cos.(T(2π) .* Vector{T}(0:N-1) ./ (N-1)))
end
HannWindow(N::Int) = HannWindow(Float64, N)

function HammingWindow(T::Type, N::Int)
    T(0.54) .- T(0.46) .* cos.(T(2π) .* Vector{T}(0:N-1) ./ (N-1))
end
HammingWindow(N::Int) = HammingWindow(Float64, N)

RectangularWindow(T::Type, N::Int) = ones(T, N)
RectangularWindow(N::Int) = RectangularWindow(Float64, N)

#======================================================================
Filter bank
======================================================================#

mel2freq(mel::Real) = 700 * (exp(mel / 1127) - 1)
freq2mel(freq::Real) = 1127 * (log(1 + (freq / 700)))

# Create a set of triangular filters
function filterbank(n::Int; srate::Real = 16000, fftlen::Int = 512,
                    lofreq::Real = 80, hifreq::Real = 7600)

    # Convert the cut-off frequencies into mel
    lomel = freq2mel(lofreq)
    himel = freq2mel(hifreq)

    # Centers (in mel and freqs) of the filterbank
    melcenters = range(lomel, himel, length = n + 2)
    freqcenters = mel2freq.(melcenters)

    # Now get the centers in terms of FFT bins
    bincenters = 1 .+ Int64.(floor.( fftlen .* freqcenters ./ srate ))

    # Allocate the matrix which will store the filters
    D = Int64(ceil(fftlen / 2))
    F = zeros(n, D)

    # Construct the "triangle"
    for f = 1:n
        d1 = bincenters[f + 1] - bincenters[f]
        d2 = bincenters[f + 2] - bincenters[f + 1]

        s = bincenters[f]
        e = bincenters[f + 1]
        F[f, s:e] = range(0, 1, length = d1 + 1)

        s = bincenters[f + 1]
        e = bincenters[f + 2]
        F[f, s:e] = range(1, 0, length = d2 + 1)
    end
    F
end

@deprecate FilterBank(args...; kwargs...) filterbank(args...; kwargs...)

max_stars_repo_name: lucasondel/SpeechFeatures

max_issues_repo_name: BUTSpeechFIT/SpeechFeatures

max_forks_repo_name: BUTSpeechFIT/SpeechFeatures


text:
using Documenter
# using Literate

#const literate_dir = joinpath(@__DIR__, "literate")
#const generated_dir = joinpath(@__DIR__, "src", "generated")

@info "Loading OncoDiversity.jl"
using OncoDiversity

# # XXX: Work around old LaTeX distribution in GitHub CI.
# @info "Building Literate.jl docs"
# if haskey(ENV, "GITHUB_ACTIONS")
#     import TikzPictures
#     TikzPictures.standaloneWorkaround(true)
#   end

# # Set Literate.jl config if not being compiled on recognized service.
# config = Dict{String,String}()
# if !(haskey(ENV, "GITHUB_ACTIONS") || haskey(ENV, "GITLAB_CI"))
#   config["nbviewer_root_url"] = "https://nbviewer.jupyter.org/github/mcfefa/OncoDiversity.jl/blob/gh-pages/dev"
#   config["repo_root_url"] = "https://github.com/mcfefa/OncoDiversity.jl/blob/master/docs"
# end

# for (root, dirs, files) in walkdir(literate_dir)
#   out_dir = joinpath(generated_dir, relpath(root, literate_dir))
#   for file in files
#     if last(splitext(file)) == ".jl"
#       Literate.markdown(joinpath(root, file), out_dir;
#         config=config, documenter=true, credit=false)
#       Literate.notebook(joinpath(root, file), out_dir;
#         execute=true, documenter=true, credit=false)
#     end
#   end
# end

@info "Building Documenter.jl docs"

makedocs(;
    modules=[OncoDiversity],
    authors="Meghan Ferrall-Fairbanks <meghan.ferrall.fairbanks@gmail.com> and contributors",
    repo="https://github.com/mcfefa/OncoDiversity.jl/blob/{commit}{path}#L{line}",
    sitename="OncoDiversity.jl",
    format=Documenter.HTML(;
        prettyurls=get(ENV, "CI", "false") == "true",
        canonical="https://mcfefa.github.io/OncoDiversity.jl",
        assets=String[],
    ),
    pages=[
        "Home" => "index.md",
    ],
)

@info "Deploying docs"
deploydocs(
  target = "build",
  repo   = "github.com/mcfefa/OncoDiversity.jl.git",
  branch = "gh-pages"
)

max_stars_repo_name: mcfefa/vdjDiversity


text:
# inspired by pytorch geometric's message passing implementation
# https://arxiv.org/abs/1903.02428
function gather(data::AbstractMatrix, sources::AbstractVector)
    return data[:, sources]
end

function gather(data::AbstractVector, sources::AbstractVector)
    data = reshape(data, (1, length(data)))
    gather(data, sources)
end

function scatter(data::AbstractMatrix, targets::AbstractVector, bins, ⊕)
    output = similar(data, (size(data, 1), bins))
    output .= 0

    for i in 1:length(targets)
        output[:, targets[i]] = ⊕(output[:, targets[i]], data[:, i])
    end

    return output
end

function scatter(data::AbstractVector, targets::AbstractVector, bins, ⊕)
    data = reshape(data, (1, length(data)))
    scatter(data, targets, bins, ⊕)[1, :]
end

# derivatives!
# TODO: correct gradients for NON addition aggregation
function ChainRulesCore.rrule(::typeof(gather), data::AbstractMatrix, sources::AbstractVector)
    pullback(Ω̄) = (NoTangent(), scatter(Ω̄, sources, size(data, 2), +), ZeroTangent())
    return gather(data, sources), pullback
end

function ChainRulesCore.rrule(::typeof(scatter), data::AbstractMatrix, targets::AbstractVector, bins, ⊕)
    pullback(Ω̄) = (NoTangent(), gather(Ω̄, targets), ZeroTangent(), ZeroTangent(), ZeroTangent())
    return scatter(data, targets, bins, ⊕), pullback
end


text:
@testset "Intersection of two lines" begin

    line_a = Line([0, 0], [1, 0])
    line_b = Line([0, 0], [0, 1])
    @test intersect(line_a, line_b) == [0, 0]

    line_b = Line([5, -3], [0, 1])
    @test intersect(line_a, line_b) == [5, 0]

    @test intersect(Line([0, 0], [1, 1]), Line([1, 0], [1, -1])) == [0.5, 0.5]

    @test intersect(Line([0, 0, 0], [1, 1, 1]), Line([1, 1, 0], [0, 0, 1])) ≈ ones(3)

    line = Line([0, 0], [1, 1])
    line_almost_parallel = Line([1, 0], [1, 1.01])
    intersect(line, line_almost_parallel)

    message = "The lines are parallel."
    @test_throws ArgumentError(message) intersect(Line([0, 0], [1, 1]), Line([1, 0], [1, 1]))

    message = "The lines are not coplanar."
    line_a = Line([0, 0, 0], [1, 0, 0])
    line_b = Line([0, 1, 0], [0, 0, 1])
    @test_throws ArgumentError(message) intersect(line_a, line_b)
end

@testset "Intersection of plane and line" begin

    origin = [0, 0, 0]

    line = Line(origin, [0, 0, 1])
    plane = Plane(origin, [0, 0, 1])

    @test intersect(plane, line) == [0, 0, 0]
    @test intersect(plane, Line([3, 5, 2], [0, 0, 1])) == [3, 5, 0]

    message = "The line and plane are parallel."
    @test_throws ArgumentError(message) intersect(plane, Line(origin, [1, 0, 0]))

    # The line is almost parallel to the plane.
    # This throws an error if the tolerance is large enough.
    line_almost_parallel = Line([0, 0, 1], [1, 0, 1e-2])
    intersect(plane, line_almost_parallel)
    @test_throws ArgumentError(message) intersect(plane, line_almost_parallel; atol=1e-2)
end

@testset "Intersection of circle/sphere and line" begin

    for (circle_or_sphere, line, point_expected_a, point_expected_b) in [
        (Circle([0, 0], 1), Line([0, 0], [1, 0]), [-1, 0], [1, 0]),
        (Circle([0, 0], 1), Line([0, 0], [0, 1]), [0, -1], [0, 1]),
        (Circle([0, 0], 1), Line([0, 1], [1, 0]), [0, 1], [0, 1]),
        (
            Circle([0, 0], 1),
            Line([0, 0.5], [1, 0]),
            [-√3 / 2, 0.5],
            [√3 / 2, 0.5],
        ),
        (Circle([1, 0], 1), Line([0, 0], [1, 0]), [0, 0], [2, 0]),
        (Circle([1.5, 0], 1), Line([0, 0], [1, 0]), [0.5, 0], [2.5, 0]),
        (Sphere([0, 0, 0], 1), Line([0, 0, 0], [1, 0, 0]), [-1, 0, 0], [1, 0, 0]),
    ]

        point_a, point_b = intersect(circle_or_sphere, line)

        @test point_a == point_expected_a && point_b == point_expected_b
    end

    template = "The {} and line do not intersect."

    for (circle_or_sphere, line) in [
        (Circle([0, 0], 1), Line([0, 2], [1, 0])),
        (Sphere([0, 0, 0], 1), Line([0, 2, 0], [1, 0, 0])),
        (Circle([-5, 2], 1), Line([0, 0], [1, 0])),
        (Sphere([0, 0, 3], 1), Line([0, 0, 0], [1, 0, 0])),
        (Circle([0, 0], 0.5), Line([0, 1], [1, 0])),
        (Sphere([0, 0, 0], 0.5), Line([0, 1, 0], [1, 0, 0])),
        (Sphere([0, 0, 0], 1), Line([0, 1, 1], [1, 0, 0])),
    ]

        if circle_or_sphere isa Circle
            name = "circle"
        else
            name = "sphere"
        end

        message = format(template, name)

        @test_throws ArgumentError(message) intersect(circle_or_sphere, line)
    end
end

@testset "Intersection of two planes" begin

    for (plane_a, plane_b, line_expected) in [
        (Plane(zeros(3), [0, 0, 1]), Plane(zeros(3), [0, 1, 0]), Line(zeros(3), [1, 0, 0])),
        (Plane(zeros(3), [0, 0, 1]), Plane([0, 0, 1], [1, 0, 1]), Line([1, 0, 0], [0, 1, 0])),
        (Plane(zeros(3), [-1, 1, 0]), Plane([8, 0, 0], [1, 1, 0]), Line([4, 4, 0], [0, 0, -1])),
        (Plane([-1, 0, 0], [-1, 0, 1]), Plane([1, 0, 0], [1, 0, 1]), Line([0, 0, 1], [0, 1, 0])),
    ]
        line_intersection = intersect(plane_a, plane_b)
        @test line_intersection ≈ line_expected
    end

    message = "The planes are parallel."
    for (plane_a, plane_b, atol, throws_error) in [
        (Plane(zeros(3), [0, 0, 1]), Plane(zeros(3), [0, 0, 1]), 0, true),
        (Plane(zeros(3), [0, 0, 1]), Plane(zeros(3), [0, 0, -5]), 0, true),
        (Plane(zeros(3), [0, 0, 1]), Plane(zeros(3), [0, 1e-2, 1]), 0, false),
        (Plane(zeros(3), [0, 0, 1]), Plane(zeros(3), [0, 1e-2, 1]), 1e-3, true),
    ]
        if throws_error
            @test_throws ArgumentError(message) intersect(plane_a, plane_b, atol=atol)
        else
            intersect(plane_a, plane_b, atol=atol)
        end
    end
end


text:
"""

S2SData(data1, data2; batch=128, ftype=Float32, dense=false) creates a
data generator that can be used with an S2S model.  The source data1
and target data2 should be sequence generators, i.e. next(data1)
should deliver a vector of Ints that represent the next sequence.
This division of labor allows different file formats to be supported.

maxtoken(sgen) should give the largest integer produced by sequence
generator sgen.  Note that sequence generators do not generate eos
tokens, S2SData uses maxtoken(sgen)+1 as eos by convention.  Sequence
generators do generate unk tokens (which should be included in the
maxtoken(sgen) count).

The following transformations are performed by an S2SData generator:

* sequences are minibatched according to the batch argument.
* sequences in a minibatch padded to all be the same length.
* the source sequences are generated in reverse order.
* source tokens are presented as (x,nothing) pairs
* target tokens are presented as (x[t-1],x[t]) pairs

Example:
```
source:
The dog ran
The next sentence

target:
El perror corrio
La frase siguiente
```
order of items generated by S2SData(source, target):
```
(<s>,nothing)
(ran,nothing)
(dog,nothing)
(The,nothing)
(<s>,El)
(El,perror)
(perror,corrio)
(corrio,<s>)
(<s>,nothing)
(sentence,nothing)
(next,nothing)
(The,nothing)
(<s>,La)
(La,frase)
(frase,siguiente)
(siguiente,<s>)
```
(except each word will be represented by a one-hot vector, and with
minibatch > 1, words from multiple sentences will be concatented in
a matrix.)

Note that the end-of-sentence markers <s> are automatically inserted
by the S2SData generator and are not present in the source or the
target.  The training will be faster if adjacent sequence lengths are
similar, but S2SData does not do any sorting, this should be done by
the sequence generators.  The S2S model switches between encoding and
decoding using y=nothing as an indicator.

"""
type S2SData; bgen1; bgen2;
    function S2SData(sgen1, sgen2; batchsize=128, ftype=Float32, dense=false, o...)
        new(S2SBatch(sgen1, batchsize, ftype, dense),
            S2SBatch(sgen2, batchsize, ftype, dense))
    end
end

typealias S2SDataFile Union{AbstractString,Cmd}

"With two filename arguments, assume SequencePerLine format"
function S2SData(file1::S2SDataFile, file2::S2SDataFile; dict1=nothing, dict2=nothing, o...)
    isa(dict1,AbstractString) && (dict1 = readvocab(dict1))
    isa(dict2,AbstractString) && (dict2 = readvocab(dict2))
    sgen1 = SequencePerLine(file1; dict=dict1, o...)
    sgen2 = SequencePerLine(file2; dict=dict2, o...)
    S2SData(sgen1, sgen2; o...)
end

"With a single filename argument, use copy for target sequence"
function S2SData(file::S2SDataFile; dict=nothing, o...)
    isa(dict,AbstractString) && (dict = readvocab(dict))
    sgen1 = SequencePerLine(file; dict=dict, o...)
    sgen2 = SequencePerLine(file; dict=dict, o...)
    S2SData(sgen1, sgen2; o...)
end

type S2SBatch; sgen; state; batch; x; y; mask; done;
    function S2SBatch(sgen, batchsize, ftype, dense)
        if dense
            x = zeros(ftype, eos(sgen), batchsize)
            y = zeros(ftype, eos(sgen), batchsize)
        else
            x = sponehot(ftype, eos(sgen), batchsize)
            y = sponehot(ftype, eos(sgen), batchsize)
        end
        mask = zeros(Cuchar, batchsize)
        batch = Array(Any, batchsize)
        new(sgen, nothing, batch, x, y, mask, false)
    end
end

import Base: start, done, next

# the S2SData state consists of (nword, encode), where nword is the
# number of words completed in the current batch, and encode is a
# boolean indicating whether we are in the encoding phase.

function start(d::S2SData)
    d.bgen1.done = d.bgen2.done = false
    d.bgen1.state = start(d.bgen1.sgen)
    d.bgen2.state = start(d.bgen2.sgen)
    nextbatch(d.bgen1)
    nextbatch(d.bgen2)
    return (0, true)
end

# We stop when there is not enough data to fill a batch

done(d::S2SData, state)=(d.bgen1.done && d.bgen2.done)

# S2SData.next returns the next token

function next(d::S2SData, state)
    (nword, encode) = state
    encode ?
    nextencode(d.bgen1, nword) :
    nextdecode(d.bgen2, nword)
end

function nextencode(b::S2SBatch, nword)
    maxlen = 1 + maximum(map(length, b.batch))
    w = maxlen-nword            # generating in reverse
    for s=1:length(b.batch)
        n=length(b.batch[s])
        xword = (w <= n ? b.batch[s][w] : w == n+1 ? eos(b.sgen) : 0)
        setrow!(b.x, xword, s)
        b.mask[s] = (xword == 0 ? 0 : 1)
    end
    nword += 1
    nword == maxlen && (nextbatch(b); nword = 0)
    return ((b.x, nothing, b.mask), (nword, (nword>0)))
end

function nextdecode(b::S2SBatch, nword)
    maxlen = 1 + maximum(map(length, b.batch))
    for s=1:length(b.batch)
        n=length(b.batch[s])
        xword = (nword == 0 ? eos(b.sgen) : nword <= n ? b.batch[s][nword] : 0)
        yword = (nword < n ? b.batch[s][nword+1] : nword == n ? eos(b.sgen) : 0)
        setrow!(b.x, xword, s)
        setrow!(b.y, yword, s)
        b.mask[s] = (xword == 0 ? 0 : 1)
    end
    nword += 1
    nword == maxlen && (nextbatch(b); nword = 0)
    return ((b.x, b.y, b.mask), (nword, (nword==0)))
end

function nextbatch(b::S2SBatch)
    for i=1:length(b.batch)
        done(b.sgen, b.state) && (b.done=true; return)
        (s, b.state) = next(b.sgen, b.state)
        b.batch[i] = s
    end
end

# TODO: these assume one hot columns, make them more general.
setrow!(x::SparseMatrixCSC,i,j)=(i>0 ? (x.rowval[j] = i; x.nzval[j] = 1) : (x.rowval[j]=1; x.nzval[j]=0); x)
setrow!(x::Array,i,j)=(x[:,j]=0; i>0 && (x[i,j]=1); x)

# Sequence generators do not generate eos, the s2s generator does.  It
# makes it maxtoken(sgen)+1 by convention.  So maxtoken of the s2s
# generator is one more than the maxtoken of the sequence generator.

eos(sgen)=maxtoken(sgen)+1
maxtoken(s::S2SData,i)=(i==1 ? eos(s.bgen1.sgen) : i==2 ? eos(s.bgen2.sgen) : error())

function readvocab(file) # TODO: test with cmd e.g. `zcat foo.gz`
    d = Dict{Any,Int}()
    open(file) do f
        for l in eachline(f)
            for w in split(l)
                get!(d, w, 1+length(d))
            end
        end
    end
    return d
end

# Some utilities for evaluation:

function dict2arr(d::Dict)
    a = cell(length(d))
    for (k,v) in d
        a[v] = k
    end
    return a
end

function lookup(s,d::Dict,a=dict2arr(d))
    if isa(s,Integer)
        a[s]
    else
        map(x->lookup(x,d,a), s)
    end
end

# function exactmatch(m::S2S, data::S2SData; beamsize=10, nbest=10)
#     global s1 = data.bgen1.sgen
#     global s2 = data.bgen2.sgen
#     global pr = predict(m,s1; beamsize=beamsize, nbest=nbest)
#     mean(map(first,pr) .== collect(s2))
# end

function kmatch(a, b, k)
    mean(map((x,y)->in(x,y[1:k]), a, b))
end

### DEAD CODE:

# data1 = loadseq(file1, dict1)
# data2 = loadseq(file2, dict2)
# @assert length(data1) == length(data2)
# sorted = sortblocks(data1; batch=batch, block=block)
# data1 = data1[sorted]
# data2 = data2[sorted]
# ns = length(data1)
# batch > ns && (batch = ns; warn("Changing batchsize to $batch"))
# skip = ns % batch
# if skip > 0
#     keep = ns - skip
#     nw1 = sum(map(length, sub(data1,1:keep)))
#     nw2 = sum(map(length, sub(data2,1:keep)))
#     warn("Skipping $ns % $batch = $skip lines at the end leaving ns=$keep nw1=$nw1 nw2=$nw2.")
# end

# function sortblocks(data; batch=128, block=10)
#     perm = Int[]
#     n = length(data)
#     bb = batch*block
#     for i=1:bb:n
#         j=i+bb-1
#         j > n && (j=n)
#         pi = sortperm(sub(data,i:j), by=length)
#         append!(perm, (pi + (i-1)))
#     end
#     return perm
# end

# function loadseq(fname::AbstractString, dict=Dict{Any,Int32}())
#     data = Vector{Int32}[]
#     isempty(dict) && (dict[eosstr]=eos)
#     open(fname) do f
#         for l in eachline(f)
#             sent = Int32[]
#             for w in split(l)
#                 push!(sent, get!(dict, w, 1+length(dict)))
#             end
#             push!(data, sent)
#         end
#     end
#     info("Read $fname[ns=$(length(data)),nw=$(mapreduce(length,+,data)),nd=$(length(dict))]")
#     return data
# end

# zerocolumn(x::SparseMatrixCSC,j)=(x.nzval[j]==0)
# zerocolumn(x::Array,j)=(findfirst(sub(x,:,j))==0)

# TODO: the sequence generators should write to their internal buffers
# and we should do an explicit copy here.

# We need eos in the constructor.
# We need data in batch before first call to next.
# Somebody needs to alloc x,y

# OLD_S2S=0
# "<s>"=>1)
#     OLD_S2S==1 && (d["<s>"]=1)
# w == "<unk>" && continue
# @show get(d,"<s>",nothing)
# @show get(d,"<unk>",nothing)
# @show length(d)

# (r,s) =
# (x,y,m) = r
# # println((convert(Vector{Int},m), x.rowval, y==nothing?y:y.rowval))
# (r,s)


text:
export MinimaxPolicy

"""
    MinimaxPolicy(;value_function, depth::Int)
The minimax algorithm with [Alpha-beta pruning](https://en.wikipedia.org/wiki/Alpha-beta_pruning)
## Keyword Arguments
- `maximum_depth::Int=30`, the maximum depth of search.
- `value_function=nothing`, estimate the value of `env`. `value_function(env) -> Number`. It is only called after searching for `maximum_depth` and the `env` is not terminated yet.
"""
Base.@kwdef mutable struct MinimaxPolicy{F} <: AbstractPolicy
    maximum_depth::Int = 30
    value_function::F = nothing
    v::Float64 = 0.0
end

(p::MinimaxPolicy)(env::AbstractEnv) = p(env, DynamicStyle(env), NumAgentStyle(env))

function (p::MinimaxPolicy)(env::AbstractEnv, ::Sequential, ::MultiAgent{2})
    if is_terminated(env)
        rand(action_space(env))  # just a dummy action
    else
        a, v = α_β_search(
            env,
            p.value_function,
            p.maximum_depth,
            -Inf,
            Inf,
            current_player(env),
        )
        p.v = v  # for debug only
        a
    end
end

function α_β_search(env::AbstractEnv, value_function, depth, α, β, maximizing_role)
    if is_terminated(env)
        nothing, reward(env, maximizing_role)
    elseif depth == 0
        nothing, value_function(env)
    elseif current_player(env) == maximizing_role
        legal_actions = legal_action_space(env)
        best_action = legal_actions[1]
        v = -Inf
        for a in legal_actions
            node = child(env, a)
            _, v_node = α_β_search(node, value_function, depth - 1, α, β, maximizing_role)
            if v_node > v
                v = v_node
                best_action = a
            end
            α = max(α, v)
            α >= β && break  # β cut-off
        end
        best_action, v
    else
        legal_actions = legal_action_space(env)
        best_action = legal_actions[1]
        v = Inf
        for a in legal_actions
            node = child(env, a)
            _, v_node = α_β_search(node, value_function, depth - 1, α, β, maximizing_role)
            if v_node < v
                v = v_node
                best_action = a
            end
            β = min(β, v)
            β <= α && break  # α cut-off
        end
        best_action, v
    end
end


text:
using AdvancedTopics
using DataFrames
using JLD2

include("util.jl")

function perform_tests(training_samples::T₁,
                       training_targets::T₂,
                       testing_samples::T₃,
                       testing_targets::T₄,
                       n_neurons::Int;
                       boundary_offset::Int = 1,
                       batch_size::Int = 100) where {T₁ <: AbstractMatrix,
                                                     T₂ <: AbstractVector,
                                                     T₃ <: AbstractMatrix,
                                                     T₄ <: AbstractVector}
    X₀ = training_samples
    t₀ = training_targets
    X₁ = testing_samples
    t₁ = testing_targets
    L = n_neurons

    W, fs = train_kde_comparators(Float64, X₀, t₀, L, boundary_offset = boundary_offset)

    elm_sig = ELM{Float64}(X₀, t₀, W, [Sigmoid() for f in 1:length(fs)], batch_size = batch_size)
    elm_kde = ELM{Float64}(X₀, t₀, W, fs, batch_size = batch_size)

    y₀_sig = predict(elm_sig, X₀)
    y₀_kde = predict(elm_kde, X₀)
    y₁_sig = predict(elm_sig, X₁)
    y₁_kde = predict(elm_kde, X₁)

    training_error_sig = calculate_error(y₀_sig, t₀)
    training_error_kde = calculate_error(y₀_kde, t₀)
    testing_error_sig = calculate_error(y₁_sig, t₁)
    testing_error_kde = calculate_error(y₁_kde, t₁)

    (training_error_sig, training_error_kde,
     testing_error_sig, testing_error_kde)
end

function run_experiments()
    X₀, t₀ = mnist_training_data()
    X₁, t₁ = mnist_testing_data()
    n_neurons = [2500]
    trials = 1:30

    for L in n_neurons
        results = DataFrame(:trial => collect(trials),
                            :sigmoid_training_error => zeros(Float64, length(trials)),
                            :kde_training_error => zeros(Float64, length(trials)),
                            :sigmoid_testing_error => zeros(Float64, length(trials)),
                            :kde_testing_error => zeros(Float64, length(trials)))

        for trial in trials
            (training_error_sig, training_error_kde,
             testing_error_sig, testing_error_kde) = perform_tests(X₀, t₀, X₁, t₁, L, batch_size = 100)

            results[trial,:sigmoid_training_error] = training_error_sig
            results[trial,:kde_training_error] = training_error_kde
            results[trial,:sigmoid_testing_error] = testing_error_sig
            results[trial,:kde_testing_error] = testing_error_kde

            println("$(L) neurons, trial $(trial)")
        end

        @save "results/mnist_$(L)_neurons.jld2" results
    end
end

run_experiments()

@load "results/mnist_2500_neurons.jld2" results
@show results[:,:sigmoid_training_error]
@show results[:,:kde_training_error]


text:
# Template showing the use of GeneratorUtils, with default I/O stdin and stdout. The output
# is fine without modification on some exercises. Use the override! mechanism to control
# output (see docs in GeneratorUtils).

using GeneratorUtils

using DataStructures: OrderedDict
using JSON

# Modify as desired.
const IO_IN = stdin
const IO_OUT = stdout

function main()
    # Use OrderedDicts to keep the order of function arguments for tests.
    canonical_data = tighten(JSON.parse(IO_IN; dicttype=OrderedDict))

    # for case in canonical_data["cases"]
    #     for subcase in case["cases"]
    #         override!(
    #             subcase,
    #             key=val
    #         )
    #     end
    # end

    print_runtests(IO_OUT, canonical_data)
end

main()


text:
# Accessors
getLabelDict(dfg::SymbolDFG) = copy(dfg.g.fadjdict)
getDescription(dfg::SymbolDFG) = dfg.description
setDescription(dfg::SymbolDFG, description::String) = dfg.description = description
getAddHistory(dfg::SymbolDFG) = dfg.addHistory
getSolverParams(dfg::SymbolDFG) = dfg.solverParams

# setSolverParams(dfg::SymbolDFG, solverParams) = dfg.solverParams = solverParams
function setSolverParams(dfg::SymbolDFG, solverParams::P) where P <: AbstractParams
  dfg.solverParams = solverParams
end

"""
    $(SIGNATURES)
True if the variable or factor exists in the graph.
"""
function exists(dfg::SymbolDFG{P,V,F}, node::V) where {P <:AbstractParams, V <: DFGNode, F <: DFGNode, N <: DFGNode}
    return haskey(dfg.g.variables, node.label)
end

function exists(dfg::SymbolDFG{P,V,F}, node::F) where {P <:AbstractParams, V <: DFGNode, F <: DFGNode, N <: DFGNode}
    return haskey(dfg.g.factors, node.label)
end

exists(dfg::SymbolDFG, nId::Symbol) = haskey(dfg.g.fadjdict, nId)

exists(dfg::SymbolDFG, node::DFGNode) = exists(dfg, node.label)

function isVariable(dfg::SymbolDFG{P,V,F}, sym::Symbol) where {P <:AbstractParams, V <: DFGNode, F <: DFGNode}
	return haskey(dfg.g.variables, sym)
end

function isFactor(dfg::SymbolDFG{P,V,F}, sym::Symbol) where {P <:AbstractParams, V <: DFGNode, F <: DFGNode}
	return haskey(dfg.g.factors, sym)
end

"""
    $(SIGNATURES)
Add a DFGVariable to a DFG.
"""
function addVariable!(dfg::SymbolDFG, variable::DFGVariable)::Bool
    #TODO should this be an error
    if haskey(dfg.g.variables, variable.label)
        error("Variable '$(variable.label)' already exists in the factor graph")
    end

    #NOTE Internal ID always set to zero as it is not needed?
    # variable._internalId = 0

    SymbolFactorGraphs.addVariable!(dfg.g, variable) || return false

    # Track insertion
    push!(dfg.addHistory, variable.label)

    return true
end

"""
    $(SIGNATURES)
Add a DFGFactor to a DFG.
"""
function addFactor!(dfg::SymbolDFG, variables::Vector{DFGVariable}, factor::DFGFactor)::Bool
    # if haskey(dfg.g.metaindex[:label], factor.label)
    #     error("Factor '$(factor.label)' already exists in the factor graph")
    # end
    #TODO should this be an error
    if haskey(dfg.g.factors, factor.label)
        error("Factor '$(factor.label)' already exists in the factor graph")
    end
    # for v in variables
    #     if !(v.label in keys(dfg.g.metaindex[:label]))
    #         error("Variable '$(v.label)' not found in graph when creating Factor '$(factor.label)'")
    #     end
    # end

    variableLabels = map(v->v.label, variables)

    factor._variableOrderSymbols = copy(variableLabels)

    return SymbolFactorGraphs.addFactor!(dfg.g, variableLabels, factor)
end

"""
    $(SIGNATURES)
Add a DFGFactor to a DFG.
"""
function addFactor!(dfg::SymbolDFG, variableLabels::Vector{Symbol}, factor::DFGFactor)::Bool
    #TODO should this be an error
    if haskey(dfg.g.factors, factor.label)
        error("Factor '$(factor.label)' already exists in the factor graph")
    end

    factor._variableOrderSymbols = variableLabels

    return SymbolFactorGraphs.addFactor!(dfg.g, variableLabels, factor)
end

"""
    $(SIGNATURES)
Get a DFGVariable from a DFG using its label.
"""
function getVariable(dfg::SymbolDFG, label::Symbol)::DFGVariable
    return dfg.g.variables[label]
end

getVariable(dfg::SymbolDFG, label::String) = getVariable(dfg, Symbol(label))

"""
    $(SIGNATURES)
Get a DFGFactor from a DFG using its label.
"""
function getFactor(dfg::SymbolDFG, label::Symbol)::DFGFactor
    return dfg.g.factors[label]
end

getFactor(dfg::SymbolDFG, label::String) = getFactor(dfg, Symbol(label))

"""
    $(SIGNATURES)
Update a complete DFGVariable in the DFG.
"""
function updateVariable!(dfg::SymbolDFG, variable::DFGVariable)::DFGVariable
    if !haskey(dfg.g.variables, variable.label)
        error("Variable label '$(variable.label)' does not exist in the factor graph")
    end
    dfg.g.variables[variable.label] = variable
    return variable
end

"""
    $(SIGNATURES)
Update a complete DFGFactor in the DFG.
"""
function updateFactor!(dfg::SymbolDFG, factor::DFGFactor)::DFGFactor
    if !haskey(dfg.g.factors, factor.label)
        error("Factor label '$(factor.label)' does not exist in the factor graph")
    end
    dfg.g.factors[factor.label] = factor
    return factor
end

"""
    $(SIGNATURES)
Delete a DFGVariable from the DFG using its label.
"""
function deleteVariable!(dfg::SymbolDFG, label::Symbol)::DFGVariable
    if !haskey(dfg.g.variables, label)
        error("Variable label '$(label)' does not exist in the factor graph")
    end
    variable = dfg.g.variables[label]
    rem_vertex!(dfg.g, label)

    return variable
end

#Alias
"""
    $(SIGNATURES)
Delete a referenced DFGVariable from the DFG.
"""
deleteVariable!(dfg::SymbolDFG, variable::DFGVariable)::DFGVariable = deleteVariable!(dfg, variable.label)

"""
    $(SIGNATURES)
Delete a DFGFactor from the DFG using its label.
"""
function deleteFactor!(dfg::SymbolDFG, label::Symbol)::DFGFactor
    if !haskey(dfg.g.factors, label)
        error("Factor label '$(label)' does not exist in the factor graph")
    end
    factor = dfg.g.factors[label]
    variable = rem_vertex!(dfg.g, label)
    return factor
end

# Alias
"""
    $(SIGNATURES)
Delete the referened DFGFactor from the DFG.
"""
deleteFactor!(dfg::SymbolDFG, factor::DFGFactor)::DFGFactor = deleteFactor!(dfg, factor.label)

"""
    $(SIGNATURES)
List the DFGVariables in the DFG.
Optionally specify a label regular expression to retrieves a subset of the variables.
"""
function getVariables(dfg::SymbolDFG, regexFilter::Union{Nothing, Regex}=nothing; tags::Vector{Symbol}=Symbol[], solvable::Int=0)::Vector{DFGVariable}

    # variables = map(v -> v.dfgNode, filter(n -> n.dfgNode isa DFGVariable, vertices(dfg.g)))
    variables = collect(values(dfg.g.variables))
    if regexFilter != nothing
        variables = filter(v -> occursin(regexFilter, String(v.label)), variables)
    end
    if solvable != 0
        variables = filter(v -> _isSolvable(dfg, v.label, solvable), variables)
    end
    if length(tags) > 0
        mask = map(v -> length(intersect(v.tags, tags)) > 0, variables )
        return variables[mask]
    end
    return variables
end

"""
    $(SIGNATURES)
Get a list of IDs of the DFGVariables in the DFG.
Optionally specify a label regular expression to retrieves a subset of the variables.

Example
```julia
getVariableIds(dfg, r"l", tags=[:APRILTAG;])
```

Related

ls
"""
getVariableIds(dfg::SymbolDFG, regexFilter::Union{Nothing, Regex}=nothing; tags::Vector{Symbol}=Symbol[], solvable::Int=0)::Vector{Symbol} = map(v -> v.label, getVariables(dfg, regexFilter, tags=tags, solvable=solvable))

# Alias
"""
    $(SIGNATURES)
List the DFGVariables in the DFG.
Optionally specify a label regular expression to retrieves a subset of the variables.
"""
ls(dfg::SymbolDFG, regexFilter::Union{Nothing, Regex}=nothing; tags::Vector{Symbol}=Symbol[], solvable::Int=0)::Vector{Symbol} = getVariableIds(dfg, regexFilter, tags=tags, solvable=solvable)

"""
    $(SIGNATURES)
List the DFGFactors in the DFG.
Optionally specify a label regular expression to retrieves a subset of the factors.
"""
function getFactors(dfg::SymbolDFG, regexFilter::Union{Nothing, Regex}=nothing; solvable::Int=0)::Vector{DFGFactor}
    # factors = map(v -> v.dfgNode, filter(n -> n.dfgNode isa DFGFactor, vertices(dfg.g)))
    factors = collect(values(dfg.g.factors))
    if regexFilter != nothing
        factors = filter(f -> occursin(regexFilter, String(f.label)), factors)
    end
    if solvable != 0
        factors = filter(f -> _isSolvable(dfg, f.label, solvable), factors)
    end
    return factors
end

"""
    $(SIGNATURES)
Get a list of the IDs of the DFGFactors in the DFG.
Optionally specify a label regular expression to retrieves a subset of the factors.
"""
getFactorIds(dfg::SymbolDFG, regexFilter::Union{Nothing, Regex}=nothing; solvable::Int=0)::Vector{Symbol} = map(f -> f.label, getFactors(dfg, regexFilter, solvable=solvable))

"""
    $(SIGNATURES)
Checks if the graph is fully connected, returns true if so.
"""
function isFullyConnected(dfg::SymbolDFG)::Bool
    return length(LightGraphs.connected_components(dfg.g)) == 1
end

#Alias
"""
    $(SIGNATURES)
Checks if the graph is not fully connected, returns true if it is not contiguous.
"""
hasOrphans(dfg::SymbolDFG)::Bool = !isFullyConnected(dfg)

function _isSolvable(dfg::SymbolDFG, label::Symbol, solvable::Int)::Bool

    haskey(dfg.g.variables, label) && (return dfg.g.variables[label].solvable >= solvable)
    haskey(dfg.g.factors, label) && (return dfg.g.factors[label].solvable >= solvable)

    #TODO should this be a breaking error?
    @error "Node not in factor or variable"
    return false
end

"""
    $(SIGNATURES)
Retrieve a list of labels of the immediate neighbors around a given variable or factor.
"""
function getNeighbors(dfg::SymbolDFG, node::DFGNode; solvable::Int=0)::Vector{Symbol}
    label = node.label
    if !haskey(dfg.g.fadjdict, label)
        error("Variable/factor with label '$(node.label)' does not exist in the factor graph")
    end

    neighbors_ll =  copy(outneighbors(dfg.g, label))
    # Additional filtering
    solvable != 0 && filter!(lbl -> _isSolvable(dfg, lbl, solvable), neighbors_ll)

    # Variable sorting (order is important)
    if node isa DFGFactor
        order = intersect(node._variableOrderSymbols, neighbors_ll)#map(v->v.dfgNode.label, neighbors))
        return order
    end

    return neighbors_ll
end

"""
    $(SIGNATURES)
Retrieve a list of labels of the immediate neighbors around a given variable or factor specified by its label.
"""
function getNeighbors(dfg::SymbolDFG, label::Symbol; solvable::Int=0)::Vector{Symbol}  where T <: DFGNode
    if !haskey(dfg.g.fadjdict, label)
        error("Variable/factor with label '$(label)' does not exist in the factor graph")
    end

    neighbors_ll =  copy(outneighbors(dfg.g, label))
    # Additional filtering
    solvable != 0 && filter!(lbl -> _isSolvable(dfg, lbl, solvable), neighbors_ll)

    # Variable sorting (order is important)
    if haskey(dfg.g.factors, label)
        order = intersect(dfg.g.factors[label]._variableOrderSymbols, neighbors_ll)#map(v->v.dfgNode.label, neighbors))
        return order
    end

    return neighbors_ll

end

# function _copyIntoGraph!(sourceDFG{AbstractParam,DFGVariable,DFGFactor}::SymbolDFG, destDFG{AbstractParam,DFGVariable,DFGFactor}::SymbolDFG, labels::Vector{Symbol}, includeOrphanFactors::Bool=false)::Nothing
function _copyIntoGraph!(sourceDFG::SymbolDFG, destDFG::SymbolDFG, labels::Vector{Symbol}, includeOrphanFactors::Bool=false)::Nothing

    # Add all variables first,
    for v in values(sourceDFG.g.variables)
        if v.label in labels
            exists(destDFG, v) ? (@warn "_copyIntoGraph $(v.label) exists, ignoring") : addVariable!(destDFG, deepcopy(v))
        end
    end

    # And then all factors to the destDFG.
    for f in values(sourceDFG.g.factors)
        if f.label in labels
            # Get the original factor variables (we need them to create it)
            neigh_labels = outneighbors(sourceDFG.g, f.label)

            # Find the labels and associated variables in our new subgraph
            factVariables = DFGVariable[]
            for v_lab in neigh_labels
                if haskey(destDFG.g.variables, v_lab)
                    push!(factVariables, getVariable(destDFG, v_lab))
                    #otherwise ignore
                end
            end

            # Only if we have all of them should we add it (otherwise strange things may happen on evaluation)
            if includeOrphanFactors || length(factVariables) == length(neigh_labels)
                exists(destDFG, f.label) ? (@warn "_copyIntoGraph $(f.label) exists, ignoring") : addFactor!(destDFG, factVariables, deepcopy(f))
            end
        end
    end
    return nothing
end

# moved to abstract
# """
#     $(SIGNATURES)
# Retrieve a deep subgraph copy around a given variable or factor.
# Optionally provide a distance to specify the number of edges should be followed.
# Optionally provide an existing subgraph addToDFG, the extracted nodes will be copied into this graph. By default a new subgraph will be created.
# Note: By default orphaned factors (where the subgraph does not contain all the related variables) are not returned. Set includeOrphanFactors to return the orphans irrespective of whether the subgraph contains all the variables.
# """
# function getSubgraphAroundNode(dfg::SymbolDFG, node::DFGNode, distance::Int64=1, includeOrphanFactors::Bool=false, addToDFG::SymbolDFG=SymbolDFG{AbstractParams}(); solvable::Int=0)::SymbolDFG
#     if !exists(dfg,node.label)
#         error("Variable/factor with label '$(node.label)' does not exist in the factor graph")
#     end
#
#     nodelabel = node.label
#     # Build a list of all unique neighbors inside 'distance'
#     neighborList = Symbol[nodelabel]
#
#     curList = Symbol[nodelabel]
#
#     for dist in 1:distance
#         newNeighbors = Symbol[]
#         for cl in curList
#             neighbors = outneighbors(dfg.g, cl)
#             for neighbor in neighbors
#                 if !(neighbor in neighborList) && _isSolvable(dfg, neighbor, solvable)
#                     push!(neighborList, neighbor)
#                     push!(newNeighbors, neighbor)
#                 end
#             end
#         end
#         curList = newNeighbors
#     end
#
#
#     # _copyIntoGraph!(dfg, addToDFG, map(n->get_prop(dfg.g, n, :label), ns), includeOrphanFactors)
#     _copyIntoGraph!(dfg, addToDFG, neighborList, includeOrphanFactors)
#     return addToDFG
# end

"""
    $(SIGNATURES)
Get a deep subgraph copy from the DFG given a list of variables and factors.
Optionally provide an existing subgraph addToDFG, the extracted nodes will be copied into this graph. By default a new subgraph will be created.
Note: By default orphaned factors (where the subgraph does not contain all the related variables) are not returned. Set includeOrphanFactors to return the orphans irrespective of whether the subgraph contains all the variables.
"""
function getSubgraph(dfg::SymbolDFG, variableFactorLabels::Vector{Symbol}, includeOrphanFactors::Bool=false, addToDFG::SymbolDFG=SymbolDFG{AbstractParams}())::SymbolDFG
    for label in variableFactorLabels
        if !exists(dfg, label)
            error("Variable/factor with label '$(label)' does not exist in the factor graph")
        end
    end

    _copyIntoGraph!(dfg, addToDFG, variableFactorLabels, includeOrphanFactors)
    return addToDFG
end

"""
    $(SIGNATURES)
Get an adjacency matrix for the DFG, returned as a Matrix{Union{Nothing, Symbol}}.
Rows are all factors, columns are all variables, and each cell contains either nothing or the symbol of the relating factor.
The first row and first column are factor and variable headings respectively.
"""
function getIncidenceMatrix(dfg::SymbolDFG; solvable::Int=0)::Matrix{Union{Nothing, Symbol}}
    varLabels = map(v->v.label, getVariables(dfg, solvable=solvable))
    factLabels = map(f->f.label, getFactors(dfg, solvable=solvable))
    vDict = Dict(varLabels .=> [1:length(varLabels)...].+1)

    adjMat = Matrix{Union{Nothing, Symbol}}(nothing, length(factLabels)+1, length(varLabels)+1)
    # Set row/col headings
    adjMat[2:end, 1] = factLabels
    adjMat[1, 2:end] = varLabels
    for (fIndex, factLabel) in enumerate(factLabels)
        factVars = getNeighbors(dfg, getFactor(dfg, factLabel))
        map(vLabel -> adjMat[fIndex+1,vDict[vLabel]] = factLabel, factVars)
    end
    return adjMat
end

function getIncidenceMatrixSparse(dfg::SymbolDFG; solvable::Int=0)::Tuple{LightGraphs.SparseMatrixCSC, Vector{Symbol}, Vector{Symbol}}
    varLabels = getVariableIds(dfg, solvable=solvable)
    factLabels = getFactorIds(dfg, solvable=solvable)

    vDict = Dict(varLabels .=> [1:length(varLabels)...])

    adjMat = spzeros(Int, length(factLabels), length(varLabels))

    for (fIndex, factLabel) in enumerate(factLabels)
        factVars = outneighbors(dfg.g, factLabel)
        map(vLabel -> adjMat[fIndex,vDict[vLabel]] = 1, factVars)
    end
    return adjMat, varLabels, factLabels
end

"""
    $(SIGNATURES)
Gets an empty and unique CloudGraphsDFG derived from an existing DFG.
"""
function _getDuplicatedEmptyDFG(dfg::SymbolDFG{T,V,F})::SymbolDFG where {T <: AbstractParams, V <: DFGNode, F <:DFGNode}
    newDfg = SymbolDFG{T, V, F}(;
        userId=dfg.userId, robotId=dfg.robotId, sessionId=dfg.sessionId,
        params=deepcopy(dfg.solverParams))
    newDfg.description ="(Copy of) $(dfg.description)"
    return newDfg
end


text:
function parse_as_much_as_possible(str::AbstractString, startidx)
    # This should be doable in a better way
    l = lastindex(str)
    idx = startidx
    expr = nothing
    offset = startidx
    while idx <= l
        s = SubString(str, startidx, idx)
        maybe_expr, maybe_offset = Meta.parse(s, 1; raise=false, greedy=true, depwarn=false)
        if !isa(maybe_expr, Expr) || !(maybe_expr.head == :error || maybe_expr.head ==:incomplete)
            expr = maybe_expr
            offset = maybe_offset
        end
        idx = nextind(str, idx)
    end
    return expr, offset
end

function get_function_in_module_or_Main(m::Module, f::Symbol)
    if !isdefined(m, f)
        !isdefined(Main, f) && return nothing
        m = Main
    end
    f = getfield(m, f)
    return f isa Function ? f : nothing
end

function add_breakpoint!(state::DebuggerState, cmd::AbstractString)
    cmd = strip(cmd)
    bp_error(err) = (printstyled(stderr, err, "\n", color = Base.error_color()); false)
    undef_func(m, f) = bp_error("$f in " * (m !== Main ? "$m or"  : "") * " Main is either not a function or not defined")
    isempty(cmd) && return bp_error()
    frame = active_frame(state)

    location_expr, offset = parse_as_much_as_possible(cmd, 1)
    location_expr === nothing && return bp_error("failed to parse breakpoint expression")
    cond_expr, _ = parse_as_much_as_possible(cmd, offset)

    # Check if it is just a number, in that case, breakpoint is at current file
    if location_expr isa Integer
        line = location_expr
        ret = JuliaInterpreter.whereis(frame)
        if ret === nothing
            return bp_error("could not determine location info of current frame")
        end
        current_file, current_line = ret
        breakpoint(current_file, line, cond_expr)
        return true
    end

    line = nothing
    if isexpr(location_expr, :call) && location_expr.args[1] == :(:)
        line = location_expr.args[3]
        line isa Integer || return bp_error("line number to the right of `:` should be given as an integer")
        location_expr = location_expr.args[2]
    end

    # File
    if location_expr isa String
        file = location_expr
        if line === nothing
            return bp_error("breakpoints in files need a line number")
        end
        @info "added breakpoint at $file:$line"
        breakpoint(file, line, cond_expr)
        return true
    end

    f = nothing
    if location_expr isa Symbol || location_expr isa Expr
        m = moduleof(frame)
        has_args = false
        if location_expr isa Symbol
            fsym = location_expr
            f = get_function_in_module_or_Main(m, fsym)
        else
            # check that the expr is a chain of getproperty calls
            expr = fsym = location_expr
            if isexpr(expr, :call)
                has_args = true
                expr = expr.args[1]
            end
            while expr isa Expr
                if expr.head == Symbol(".") && length(expr.args) == 2 && expr.args[2] isa QuoteNode
                    expr = expr.args[1]
                else
                    @goto not_a_function
                end
            end
            for m in (moduleof(frame), Main)
                try
                    f_eval = Base.eval(m, has_args ? location_expr.args[1] : location_expr)
                    if f_eval isa Function
                        f = f_eval
                        break
                    end
                catch
                end
            end
        end
        f === nothing && return undef_func(m, fsym)
        if !has_args
            @info "added breakpoint for function $f" * (line === nothing ? "" : ":$line")
            breakpoint(f, line, cond_expr)
            return true
        end
    end
    @label not_a_function
    if f === nothing
        location_expr isa Expr || return bp_error("failed to parse breakpoint expression")
        location_expr.head == :call || return bp_error("expected a call expression or an expression that evaluates to a function")
        m = moduleof(frame)
        f = get_function_in_module_or_Main(m, fsym)
        f === nothing && return undef_func(m, fsym)
    end

    fsym, f_args = location_expr.args[1], location_expr.args[2:end]
    type_args = false
    if any(arg -> isexpr(arg, :(::)), f_args)
        if !all(arg -> arg.head == :(::), f_args)
            return bp_error("all arguments should have `::` if one does")
        end
        f_args = [arg.args[1] for arg in f_args]
        type_args = true
    end

    locals = filter(v -> v.name != Symbol(""), JuliaInterpreter.locals(frame))

    res = nothing
    arg_types = []
    for arg in f_args
        for m in (moduleof(frame), Main)
            try
                res = interpret_variable(arg, locals, m)
            catch e
                e isa UndefVarError || rethrow()
            end
        end
        if res === nothing
            return bp_error("could not find function argument $arg")
        end
        push!(arg_types, type_args ? res : typeof(res))
    end

    breakpoint(f, Tuple(arg_types), something(line, 0), cond_expr)

    @info "added breakpoint for method $f(" * join("::" .* string.(arg_types), ", ") * ")"
    return true
end

function interpret_variable(arg, locals, m::Module)
    eval_expr = Expr(:let,
        Expr(:block,
            map(x->Expr(:(=), x...), [(v.name, maybe_quote(v.value)) for v in locals])...),
        Expr(:block, arg)
        )
    return Core.eval(m, eval_expr)
end

function show_breakpoints(io::IO, state::DebuggerState)
    if JuliaInterpreter.break_on_error[] || JuliaInterpreter.break_on_throw[]
        println(io, "Breaking on ", JuliaInterpreter.break_on_throw[] ? "throw" : "error")
    end

    bps = JuliaInterpreter.breakpoints()
    if !isempty(bps)
        for (i, bp) in enumerate(bps)
            println(io, "$i] $bp")
        end
        println(io)
    end
end

function check_breakpoint_index(i)
    if !checkbounds(Bool, JuliaInterpreter.breakpoints(), i)
        printstyled(stderr, "ERROR: breakpoint $i does not exist\n"; color=Base.error_color())
        return false
    end
    return true
end

function toggle_breakpoint!(state)
    foreach(JuliaInterpreter.toggle, JuliaInterpreter.breakpoints())
    return true
end

function toggle_breakpoint!(state, i)
    check_breakpoint_index(i) || return false
    JuliaInterpreter.toggle(JuliaInterpreter.breakpoints()[i])
    return true
end

disable_breakpoint!(state) = (JuliaInterpreter.disable(); true)

function disable_breakpoint!(state, i)
    check_breakpoint_index(i) || return false
    JuliaInterpreter.disable(JuliaInterpreter.breakpoints()[i])
    return true
end

enable_breakpoint!(state) = (JuliaInterpreter.enable(); true)

function enable_breakpoint!(state, i)
    check_breakpoint_index(i) || return false
    JuliaInterpreter.enable(JuliaInterpreter.breakpoints()[i])
    return true
end

remove_breakpoint!(state::DebuggerState) = (JuliaInterpreter.remove(); true)

function remove_breakpoint!(state::DebuggerState, i::Int)
    check_breakpoint_index(i) || return false
    JuliaInterpreter.remove(JuliaInterpreter.breakpoints()[i])
    return true
end