text:
# function getclumps(dataobject::InfoType, var::Symbol;
#                     xrange::Array{<:Any,1}=[missing, missing],
#                     yrange::Array{<:Any,1}=[missing, missing],
#                     zrange::Array{<:Any,1}=[missing, missing],
#                     center::Array{<:Any,1}=[0., 0., 0.],
#                     range_unit::Symbol=:standard,
#                     print_filenames::Bool=false,
#                     verbose::Bool=verbose_mode)
#
#     return  getclumps(dataobject, vars=[var],
#                     xrange=xrange,
#                     yrange=yrange,
#                     zrange=zrange,
#                     center=center,
#                     range_unit=range_unit,
#                     print_filenames=print_filenames,
#                     verbose=verbose)
#
# end

"""
#### Read the clump-data:
- selected variables
- limited to a spatial range
- print the name of each data-file before reading it
- toggle verbose mode

```julia
getclumps(  dataobject::InfoType;
            vars::Array{Symbol,1}=[:all],
            xrange::Array{<:Any,1}=[missing, missing],
            yrange::Array{<:Any,1}=[missing, missing],
            zrange::Array{<:Any,1}=[missing, missing],
            center::Array{<:Any,1}=[0., 0., 0.],
            range_unit::Symbol=:standard,
            print_filenames::Bool=false,
            verbose::Bool=verbose_mode)
```
#### Returns an object of type ClumpDataType, containing the clump-data table, the selected options and the simulation ScaleType and summary of the InfoType
```julia
return ClumpDataType()

# get an overview of the returned fields:
# e.g.:
julia> info = getinfo(100)
julia> clumps  = getclumps(info)
julia> viewfields(clumps)
#or:
julia> fieldnames(clumps)
```

#### Arguments
##### Required:
- **`dataobject`:** needs to be of type: "InfoType", created by the function *getinfo*
##### Predefined/Optional Keywords:
- **`vars`:** Currently, the length of the loaded variable list can be modified *(see examples below).
- **`xrange`:** the range between [xmin, xmax] in units given by argument `range_unit` and relative to the given `center`; zero length for xmin=xmax=0. is converted to maximum possible length
- **`yrange`:** the range between [ymin, ymax] in units given by argument `range_unit` and relative to the given `center`; zero length for ymin=ymax=0. is converted to maximum possible length
- **`zrange`:** the range between [zmin, zmax] in units given by argument `range_unit` and relative to the given `center`; zero length for zmin=zmax=0. is converted to maximum possible length
- **`range_unit`:** the units of the given ranges: :standard (code units), :Mpc, :kpc, :pc, :mpc, :ly, :au , :km, :cm (of typye Symbol) ..etc. ; see for defined length-scales viewfields(info.scale)
- **`center`:** in units given by argument `range_unit`; by default [0., 0., 0.]; the box-center can be selected by e.g. [:bc], [:boxcenter], [value, :bc, :bc], etc..
- **`print_filenames`:** print on screen the current processed particle file of each CPU
- **`verbose`:** print timestamp, selected vars and ranges on screen; default: set by the variable `verbose_mode`

### Defined Methods - function defined for different arguments
- getclumps(dataobject::InfoType; ...) # no given variables -> all variables loaded
- getclumps(dataobject::InfoType, vars::Array{Symbol,1}; ...)  # one or several given variables -> array needed

#### Examples
```julia
# read simulation information
julia> info = getinfo(420)

# Example 1:
# read clump data of all variables, full-box
julia> clumps = getclumps(info)

# Example 2:
# read clump data of all variables
# data range 20x20x4 kpc; ranges are given in kpc relative to the box (here: 48 kpc) center at 24 kpc
julia> clumps = getclumps(    info,
                              xrange=[-10.,10.],
                              yrange=[-10.,10.],
                              zrange=[-2.,2.],
                              center=[24., 24., 24.],
                              range_unit=:kpc )

# Example 3:
# give the center of the box by simply passing: center = [:bc] or center = [:boxcenter]
# this is equivalent to center=[24.,24.,24.] in Example 2
# the following combination is also possible: e.g. center=[:bc, 12., 34.], etc.
julia> clumps = getclumps(  info,
                            xrange=[-10.,10.],
                            yrange=[-10.,10.],
                            zrange=[-2.,2.],
                            center=[33., bc:, 10.],
                            range_unit=:kpc )

# Example 4:
# Load less than the found 12 columns from the header of the clump files;
# Pass an array with the variables to the keyword argument *vars*.
# The order of the variables has to be consistent with the header in the clump files:
julia> lumps = getclumps(info, [ :index, :lev, :parent, :ncell,
                                 :peak_x, :peak_y, :peak_z ])

# Example 5:
# Load more than the found 12 columns from the header of the clump files.
# E.g. the list can be extended with more names if there are more columns
# in the data than given by the header in the files.
# The order of the variables has to be consistent with the header in the clump files:
julia> clumps = getclumps(info, [   :index, :lev, :parent, :ncell,
                                    :peak_x, :peak_y, :peak_z,
                                    Symbol("rho-"), Symbol("rho+"),
                                    :rho_av, :mass_cl, :relevance,
                                    :vx, :vy, :vz ])
...
```

"""
function getclumps(dataobject::InfoType, vars::Array{Symbol,1};
                    xrange::Array{<:Any,1}=[missing, missing],
                    yrange::Array{<:Any,1}=[missing, missing],
                    zrange::Array{<:Any,1}=[missing, missing],
                    center::Array{<:Any,1}=[0., 0., 0.],
                    range_unit::Symbol=:standard,
                    print_filenames::Bool=false,
                    verbose::Bool=verbose_mode)

    return  getclumps(dataobject,
                    vars=vars,
                    xrange=xrange,
                    yrange=yrange,
                    zrange=zrange,
                    center=center,
                    range_unit=range_unit,
                    print_filenames=print_filenames,
                    verbose=verbose)

end

function getclumps(dataobject::InfoType;
                    vars::Array{Symbol,1}=[:all],
                    xrange::Array{<:Any,1}=[missing, missing],
                    yrange::Array{<:Any,1}=[missing, missing],
                    zrange::Array{<:Any,1}=[missing, missing],
                    center::Array{<:Any,1}=[0., 0., 0.],
                    range_unit::Symbol=:standard,
                    print_filenames::Bool=false,
                    verbose::Bool=verbose_mode)

    printtime("Get clump data: ", verbose)
    checkfortype(dataobject, :clumps)

    boxlen = dataobject.boxlen

    # convert given ranges and print overview on screen
    ranges = prepranges(dataobject, range_unit, verbose, xrange, yrange, zrange, center)

    fnames = createpath(dataobject.output, dataobject.path)

    # read clumps-data of the selected variables
    column_names, NColumns = getclumpvariables(dataobject, vars, fnames)

    if verbose
        println("Read $NColumns colums: ")
        println(column_names)
    end

    data = readclumps(dataobject, fnames, NColumns, column_names, print_filenames)

    # filter data out of selected ranges
    if ranges[1] !=0. || ranges[2] !=1. || ranges[3] !=0. || ranges[4] !=1. || ranges[5] !=0. || ranges[6] !=1.
        data = filter(p->       p.peak_x >=  ranges[1] * boxlen &&
                                p.peak_x <=  ranges[2] * boxlen  &&
                                p.peak_y >=  ranges[3] * boxlen  &&
                                p.peak_y <=  ranges[4] * boxlen  &&
                                p.peak_z >=  ranges[5] * boxlen  &&
                                p.peak_z <=  ranges[6] * boxlen, data)
    end

    printtablememory(data, verbose)

    clumpdata = ClumpDataType()
    clumpdata.data = data
    clumpdata.info = dataobject
    clumpdata.boxlen = dataobject.boxlen
    clumpdata.ranges = ranges
    clumpdata.selected_clumpvars = column_names
    clumpdata.used_descriptors = Dict()
    clumpdata.scale = dataobject.scale
    return clumpdata
end

function getclumpvariables(dataobject::InfoType, vars::Array{Symbol,1}, fnames::FileNamesType)
    if vars == [:all]
        # MERA: Read column names
        #--------------------------------------------

        # get header of first clump file
        f = open(dataobject.fnames.clumps * "txt00001")
        lines = readlines(f)
        column_names = Symbol.( split(lines[1]) )
        NColumns = length(split(lines[1]))
        close(f)

    else
        NColumns = length(vars)
        column_names = vars
    end

    return column_names, NColumns
end

function readclumps(dataobject::InfoType, fnames::FileNamesType, NColumns::Int, column_names::Array{Symbol,1}, print_filenames::Bool=false)

    clcol = zeros(Float64, 1, NColumns)
    NCpu = dataobject.ncpu
    data = 0.
    create_table = 1
    line_beginning = 1
    for NC=1:NCpu

        clumpspath = getproc2string(fnames.clumps, true, NC)

        if print_filenames println(clumpspath) end
        f = open(clumpspath)

        lines = readlines(f)

        if length(lines) > 1

            for i=2:length(lines)
                    #NColumns = length(split(lines[i]))
                    for j = 1:NColumns
                        #println(i, " ", j)
                        clcol[j] = parse(Float64, split(lines[i])[j] )
                    end

                if create_table == 0
                    data_buffer = table( [clcol[:, k ] for k = 1:NColumns ]...,names=collect(column_names) )
                    data = merge(data, data_buffer)

                end

                if create_table == 1
                    data = table( [clcol[:, k ] for k = 1:NColumns ]...,names=collect(column_names) )
                    create_table = 0
                end

            end
        end
        close(f)

    end

    return data
end


text:
module CheckExpect

    # package code goes here

    # import packages
    using AnsiColor

    ## include clear
    include("clear.jl");

    ## Clear and Close Figures
    clear();
    cla();                               # clear current axis
    clf();                               # clear current plot
    close();                             # close figure window

    # test suite Data Definitions and Functions
    results = Dict();
    results["total"] = 0;
    results["bad"] = 0;

    ## report
    # https://libraries.io/julia/AnsiColor
    function report()
    begin
            total = results["total"];
            bad =  results["bad"];
            passed = total - bad;
            if total >= 1 && passed >= 1
                println(colorize(:light_white,"Of $total tests, $bad failed, $passed passed.", background="light_black", mode="blink"))
            else
                println(colorize(:red,"Of $total test, $bad failed, $passed passed.", background="light_black", mode="blink"))
            end
    end
    end

    function checkExpect(f::Function, params::Any, expected::Any, name::AbstractString ="")
        begin
            # run unit test
            result = f(params...);

            if strwidth(chomp(name)) < 0 || chomp(name) != ""
                println("for unit test: " * "$name")
            else
                println("for unit test: " * "test description unknown")
            end

            if result != expected
                println("test: " * colorize(:red, "failed", background="black", mode="bold"))
                println("Expected $expected" * ", but was $result")
                println("info: " * colorize(:light_yellow, "Algorithm design is not well formed!", background="black", mode="bold"))
                println("")
                results["bad"] += 1;
                results["total"] += 1;
            else
                println("test: " * colorize(:green,"passed", background="black", mode="bold"))
                println("Function: $f consumed the following argument(s) $params, and produced $result, which matches $expected the expected result.")
                println("info: " * colorize(:light_blue, "Algorithm is well formed!", background="black", mode="default"))
                println("")
                #results["good"] += 1;
                results["total"] += 1;
            end
            # report test results
            report();

            return result
        end
    end

end # module

ext: jl


text:
__precompile__(false)

module DefaultModelData

const DATA_DUMMY = Float32[0,0,0]
export DATA_DUMMY

const DATA_CUBE = Float32[
  # Bottom
  -1.0, -1.0, -1.0,
  1.0, -1.0, -1.0,
  -1.0, -1.0, 1.0,
  1.0, -1.0, -1.0,
  1.0, -1.0, 1.0,
  -1.0, -1.0, 1.0,

  # Top
  -1.0, 1.0, -1.0,
  -1.0, 1.0, 1.0,
  1.0, 1.0, -1.0,
  1.0, 1.0, -1.0,
  -1.0, 1.0, 1.0,
  1.0, 1.0, 1.0,

  # Front
  -1.0, -1.0, 1.0,
  1.0, -1.0, 1.0,
  -1.0, 1.0, 1.0,
  1.0, -1.0, 1.0,
  1.0, 1.0, 1.0,
  -1.0, 1.0, 1.0,

  # Back
  -1.0, -1.0, -1.0,
  -1.0, 1.0, -1.0,
  1.0, -1.0, -1.0,
  1.0, -1.0, -1.0,
  -1.0, 1.0, -1.0,
  1.0, 1.0, -1.0,

  # Left
  -1.0, -1.0, 1.0,
  -1.0, 1.0, -1.0,
  -1.0, -1.0, -1.0,
  -1.0, -1.0, 1.0,
  -1.0, 1.0, 1.0,
  -1.0, 1.0, -1.0,

  # Right
  1.0, -1.0, 1.0,
  1.0, -1.0, -1.0,
  1.0, 1.0, -1.0,
  1.0, -1.0, 1.0,
  1.0, 1.0, -1.0,
  1.0, 1.0, 1.0,
]

export DATA_CUBE

const DATA_CUBE_VERTEX = Float32[
  1f0,  1,  1,  # 0
  -1,  1,  1,   # 1
  -1, -1,  1,   # 2
  1, -1,  1,    # 3
  1, -1, -1,    # 4
  -1, -1, -1,   # 5
  -1,  1, -1,   # 6
  1,  1, -1,    # 7
]

export DATA_CUBE_VERTEX

const DATA_CUBE_INDEX = UInt32[
  0, 1, 2, 2, 3, 0, # Front face
  7, 4, 5, 5, 6, 7, # Back face
  6, 5, 2, 2, 1, 6, # Left face
  7, 0, 3, 3, 4, 7, # Right face
  7, 6, 1, 1, 0, 7, # Top face
  3, 2, 5, 5, 4, 3  # Bottom face
]

export DATA_CUBE_INDEX

const DATA_PLANE_VERTEX  = Float32[
  1f0,  0,  1,  # 0
  1, 0, -1,     # 1
  -1,  0,  1,   # 2
  -1, 0, -1     # 3
]

export DATA_PLANE_VERTEX

const DATA_PLANE_INDEX = UInt32[
  0, 1, 2, 3 #GL_TRIANGLE_STRIP
  #0, 1, 2, 2, 1, 3 #GL_TRIANGLES
]

export DATA_PLANE_INDEX

const DATA_PLANE2D_VERTEX_STRIP = Float32[-1, -1, 1, -1, -1, 1, 1, 1]

export DATA_PLANE2D_VERTEX_STRIP

end #DefaultModelData

max_stars_repo_name: Gilga/JuliaOpenGL

max_issues_repo_name: Gilga/JuliaOpenGL

max_forks_repo_name: Gilga/JuliaOpenGL


text:
#' Environment
using Pkg
Pkg.activate(@__DIR__)
cd(@__DIR__)
Pkg.status()

#' Load library to main worker
@time using Revise, Distributed, RetroSignalModel, CSV
import RetroSignalModel as rs
#' Add workers
addprocs(exeflags="--project=$(Base.active_project())");
@show nprocs();

#' Load library to workers
@time @everywhere using Revise, RetroSignalModel;

#' Parameter searching
@time df = rs.paramSearching(rs.rtgM4(); num_sim=1e9, save_iter=1e6,distributed=true, saveall=false)

#'
CSV.write("data/solution_rtgM4.csv", df)


text:
# Pushing current dir to import modules.
push!(LOAD_PATH, "./src")
# Including the necessary files.
include("./FireflyModule.jl")
include("./NodeModule.jl")
include("./arc_distance.jl")
include("./create_distance_matrix.jl")
include("./euclidean_distance.jl")
include("./generate_mutated_fireflies.jl")
include("./init_firefly.jl")
include("./inversion_mutation.jl")
include("./path_cost.jl")
include("./read_nodes.jl")

using Pkg, Random

Pkg.update()
Pkg.add("PyPlot")

import NodeModule.Node, FireflyModule.Firefly, Dates
using PyPlot, Logging

now = string(Dates.now())

file_name = get(ENV, "TSP_FILE", nothing)
if file_name != nothing
    println("Reading from: ", file_name)
    nodes = read_nodes(file_name)
    distance_matrix = create_distance_matrix(nodes)
end

# Checking if logs direcitory is available!
# If is not create one!
if !isdir("logs") mkdir("logs"); end

if '/' in file_name
    tsp_file_name = split(file_name, "/")[end]
    io_log = open("logs/$tsp_file_name-$now.txt", "w+")
else
    tsp_file_name = file_name
    io_log = open("logs/$tsp_file_name-$now.txt", "w+")
end

# Setting Logger!
logger = SimpleLogger(io_log)
global_logger(logger)

# Setting hyperparameters!
LIGHT_ABSORPTION_COEFF = 0.2
ATTRACTION_COEFF = 1
ITERATION_NUMBER = 100
POPULATION_NUMBER = 150
NUMBER_OF_MUTATION = 10

# Main firefly algorithm
bests = []
pop = [Firefly(copy(nodes), -1.0) for _ in 1:POPULATION_NUMBER]
init_firefly_paths(pop, distance_matrix)
println(length(pop), " Sized population created!")
for t in 1:ITERATION_NUMBER
    new_pop = []
    for f1 in pop
        for f2 in pop
            if f1.cost < f2.cost
                mutated_f1s = generate_mutated_fireflies(f2, distance_matrix, arc_distance(f1, f2), NUMBER_OF_MUTATION)
                new_pop = [new_pop ; mutated_f1s]
                push!(new_pop, f2)
            end
        end
    end
    [push!(pop, n) for n in new_pop]
    sorted_pop = sort!(pop, by=p->p.cost)
    while length(pop) != POPULATION_NUMBER
        pop!(pop)
    end
    push!(bests, sorted_pop[1])
    current_best_cost = copy(bests[end].cost)
    now = string(Dates.now())
    println("Step:",t, " - Best ", bests[end])
    @info("Step:$t, Population Size:$POPULATION_NUMBER, Mutation Number:$NUMBER_OF_MUTATION, Date:$now",
          current_best_cost)
end

close(io_log)

# Checking if graphs direcitory is available for saving graphs!
# If is not create one!
if !isdir("graphs") mkdir("graphs"); end

# Plotting and printing the best solutions.
best = sort(pop, by=p->p.cost)[1]
best_x = [n.x for n in best.path]
best_y = [n.y for n in best.path]
best_cost = best.cost
plot(best_x, best_y, "-")
x = [n.x for n in nodes]
y = [n.y for n in nodes]
plot(x, y, "ro", markersize=2.0)
xlabel("X Dimension")
ylabel("Y Dimension")
title("Firefly Cost: $best_cost", fontsize=10)
suptitle("File: $file_name", fontweight="bold")
fig_name = "graphs/best_firefly_path_$tsp_file_name-cost_$best_cost.png"
savefig(fig_name)
println("Saved figure:",fig_name)
clf()

x_cost = [b.cost for b in bests]
plot(x_cost)
grid(true)
ylabel("Path Cost")
xlabel("Iteration Number")
title("Firefly Iteration Graphic", fontsize=12)
fig_name = "graphs/iteration_$tsp_file_name-cost_$best_cost.png"
savefig(fig_name)
println("Saved figure:",fig_name)

max_stars_repo_name: zahidkizmaz/firefly-algorithm-tsp

max_issues_repo_name: zahidkizmaz/firefly-algorithm-tsp

max_forks_repo_name: zahidkizmaz/firefly-algorithm-tsp


text:
#=
LatinStr/_LatinStr type (ISO Latin1 8-bit subset of Unicode)

Copyright 2017 Gandalf Software, Inc., Scott P. Jones, and other contributors to the Julia language
Licensed under MIT License, see LICENSE.md
Based in part on code for ASCIIString that used to be in Julia
=#

## overload methods for efficiency ##

is_ascii(str::Str{C}) where {C<:_LatinCSE}    = false
is_latin(str::Str{C}) where {C<:Latin_CSEs}   = true
is_bmp(str::Str{C}) where {C<:Latin_CSEs}     = true
is_unicode(str::Str{C}) where {C<:Latin_CSEs} = true

const _UBS = Str{<:Union{ASCIICSE, Latin_CSEs}}

function string(collection::_UBS...)
    length(collection) == 1 && return collection[1]
    len = 0
    @inbounds for str in collection
        len += ncodeunits(str)
    end
    buf, pnt = _allocate(len)
    @inbounds for str in collection
        len = ncodeunits(str)
        _memcpy(pnt, pointer(str), len)
        pnt += len
    end
    Str(LatinCSE, buf)
end

## transcoding to Latin1 ##

convert(::Type{T}, s::T) where {T<:Str{<:Latin_CSEs}} = s
convert(::Type{T}, s::Str{<:_UBS}) where {T<:Str{<:Latin_CSEs}} = T(s.data)
convert(::Type{T}, s::Str{UTF8CSE}) where {T<:Str{<:Latin_CSEs}} = convert(T, s.data)

# Assumes that has already been checked for validity
function _utf8_to_latin(pnt::Ptr{UInt8}, len)
    buf, out = _allocate(UInt8, len)
    fin = out + len
    while out < fin
        ch = get_codeunit(pnt)
        # Handle ASCII characters
        ch > 0x7f && (ch = ((ch & 3) << 6) | (get_codeunit(pnt += 1) & 0x3f))
        pnt += 1
        set_codeunit!(out, ch)
        out += 1
    end
    buf
end

function _latin_to_utf8(pnt::Ptr{UInt8}, len)
    buf, out = _allocate(UInt8, len)
    fin = out + len
    while out < fin
        ch = get_codeunit(pnt)
        pnt += 1
        if ch > 0x7f
            set_codeunit!(out, 0xc0 | (ch >>> 6))
            ch = 0x80 | (ch & 0x3f)
            out += 1
        end
        set_codeunit!(out, ch)
        out += 1
    end
    buf
end

# Fast conversion from LatinStr or _LatinStr to UTF8Str
function convert(::Type{<:Str{UTF8CSE}}, str::Str{<:Latin_CSEs})
    # handle zero length string quickly
    (len = ncodeunits(str)) == 0 && return empty_utf8
    @preserve str begin
        pnt = pointer(str)
        Str(UTF8CSE, (cnt = count_latin(len, pnt)) != 0 ? _latin_to_utf8(pnt, len + cnt) : str)
    end
end

# Fast conversion from LatinStr or _LatinStr to String
function convert(::Type{String}, str::Str{<:Latin_CSEs})
    # handle zero length string quickly
    (len = ncodeunits(str)) == 0 && return empty_string
    @preserve str begin
        pnt = pointer(str)
        (cnt = count_latin(len, pnt)) != 0 ? _latin_to_utf8(pnt, len + cnt) : str.data
    end
end

function convert(::Type{<:Str{C}}, str::AbstractString) where {C<:Latin_CSEs}
    # Might want to have invalids_as here
    len, flags = unsafe_check_string(str)
    (flags & ~(UTF_LONG | UTF_LATIN1)) == 0 || unierror(UTF_ERR_INVALID_LATIN1)
    buf, pnt = _allocate(UInt8, len)
    @inbounds for ch in str
        set_codeunit!(pnt, ch%UInt8)
        pnt += 1
    end
    Str((C === _LatinCSE && flags == 0) ? ASCIICSE : C, buf)
end

function convert(::Type{<:Str{LatinCSE}}, str::MS_ByteStr)
    # handle zero length string quickly
    (siz = sizeof(str)) == 0 && return empty_latin
    @preserve str begin
        pnt = pointer(str)
        # get number of bytes to allocate
        len, flags, num4byte, num3byte, num2byte, latinbyte = fast_check_string(pnt, siz)
        num4byte + num3byte + num2byte == 0 || unierror(UTF_ERR_INVALID_LATIN1)
        Str(LatinCSE, flags == 0 ? _copysub(str) : _utf8_to_latin(pnt, len))
    end
end

_convert(::Type{_LatinCSE}, ch::UInt8) = _convert(ch <= 0x7f ? ASCIICSE : _LatinCSE, ch)
convert(::Type{<:Str{_LatinCSE}}, ch::Unsigned) =
    ch < 0xff ? _convert(_LatinCSE, ch%UInt8) : unierror(UTF_ERR_LATIN1, ch)

function convert(::Type{<:Str{_LatinCSE}}, str::MS_ByteStr)
    # handle zero length string quickly
    (siz = sizeof(str)) == 0 && return empty_ascii
    @preserve str begin
        pnt = pointer(str)
        # get number of bytes to allocate
        len, flags, num4byte, num3byte, num2byte, latinbyte = fast_check_string(pnt, siz)
        num4byte + num3byte + num2byte == 0 || unierror(UTF_ERR_INVALID_LATIN1)
        Str(latinbyte == 0 ? ASCIICSE : _LatinCSE,
            flags == 0 ? _copysub(str) : _utf8_to_latin(pnt, len))
    end
end

function convert(::Type{<:Str{C}}, vec::Vector{CU}) where {C<:Latin_CSEs,CU<:CodeUnitTypes}
    # handle zero length string quickly
    (len = length(vec)) == 0 && return C === _LatinCSE ? empty_ascii : empty_latin
    @preserve vec begin
        pnt = pointer(vec)
        # get number of bytes to allocate
        len, flags, num4byte, num3byte, num2byte, latinbyte = fast_check_string(pnt, len)
        num4byte + num3byte + num2byte == 0 || unierror(UTF_ERR_INVALID_LATIN1)
        Str((C === _LatinCSE && latinbyte == 0) ? ASCIICSE : C,
            (CU !== UInt8 || flags == 0) ? _cvtsize(UInt8, pnt, len) : _utf8_to_latin(pnt, len))
    end
end

function convert(::Type{<:Str{C}},
                 str::MaybeSub{<:Str{<:Union{Text2CSE,Text4CSE}}}) where {C<:Latin_CSEs}
    # handle zero length string quickly
    (len = ncodeunits(str)) == 0 && return C === _LatinCSE ? empty_ascii : empty_latin
    @preserve str begin
        pnt = pointer(str)
        # get number of bytes to allocate
        len, flags, num4byte, num3byte, num2byte, latinbyte = fast_check_string(pnt, len)
        num4byte + num3byte + num2byte == 0 || unierror(UTF_ERR_INVALID_LATIN1)
        Str((C === _LatinCSE && latinbyte == 0) ? ASCIICSE : C, _cvtsize(UInt8, pnt, len))
    end
end


text:
export AbstractForwardSensSpaceAdapter

abstract type AbstractForwardSensSpaceAdapter end

include("sparse/fsspaceadapterssparse.jl")


text:
"""
The default user interface for AlphaZero.

The user interface is fully separated from the core algorithm and can therefore
be replaced easily.
"""
module UserInterface

  export Log
  export explore
  export Session, resume!, save

  using ..AlphaZero

  import Plots
  import JSON3
  using Base: @kwdef
  using Statistics: mean
  using Formatting: format, fmt
  using Crayons: @crayon_str
  using Colors: @colorant_str
  using ProgressMeter
  using Serialization: serialize, deserialize

  include("log.jl")
  using .Log

  include("explorer.jl")
  include("plots.jl")
  include("json.jl")
  include("session.jl")

end


text:
module DemeNet

include("Types/Types.jl")
include("Plugins/Plugins.jl")
include("Keys/Keys.jl")
include("Demes/Demes.jl")
include("Profiles/Profiles.jl")
include("TOMLIO/TOMLIO.jl")
include("Crypto/Crypto.jl")

#function serialize end
#function deserialize end

import .Plugins: AbstractPlugin, Plugin, Notary, Cypher, CypherSuite
import .Keys: Signer, Certificate, Contract, Consensus, Intent, Envelope, verify
import .Types: ID, DemeID, AbstractID, datadir, keydir # keydir should be deprecated in the future
import .Demes: DemeSpec, Deme, save #, Signer
import .Profiles: Profile
import .TOMLIO: serialize, deserialize
import .Crypto: DHsym, DHasym

Plugin(::Type{T},deme::Deme) where T <: AbstractPlugin = Plugin[deme.spec.peacefounder](T,deme)
Plugin(::Type{T},deme::Deme,config) where T <: AbstractPlugin = Plugin[deme.spec.peacefounder](T,deme,config)

abstract type AbstractInitializer <: AbstractPlugin end
AbstractInitializer(deme) = Plugin(AbstractInitializer,deme)

### THE API of AbstractInitializer. Perhaps some kind of trait abstraction is needed ###
init(::AbstractInitializer,port::Dict) = error("Contract not satisfied")
config(::AbstractInitializer) = error("Contract not satisfied")
updateconfig(::AbstractInitializer) = error("Contract not satisfied")

end


text:
test_consecutive_with_preceding_env = {
    "coverage"  => Array[
        [2*ones(Int,8)],
        [3*ones(Int,8)],
        [3*ones(Int,8)],
        [2*ones(Int,8)],
    ],
}

test_consecutive_with_preceding_employees = {
    "a" => {
        "hours_min" => 15,
        "hours_max" => 17,
        "shift_count_min" => 2,
        "shift_count_max" => 2,
        "availability"  => Array[
            [ones(Int,8)],
            [ones(Int,8)],
            [ones(Int,8)],
            [ones(Int,8)],
        ],
        "longest_availability" => 8*ones(Int, 4),
        "worked_day_preceding_week" => true,
    },
    "b" => {
        "hours_min" => 15,
        "hours_max" => 17,
        "shift_count_min" => 2,
        "shift_count_max" => 2,
        "availability"  => Array[
            [ones(Int,8)],
            [ones(Int,8)],
            [ones(Int,8)],
            [ones(Int,8)],
        ],
        "longest_availability" => 8*ones(Int, 4),
        "worked_day_preceding_week" => true,
    },
    "c" => {
        "hours_min" => 15,
        "hours_max" => 17,
        "shift_count_min" => 2,
        "shift_count_max" => 2,
        "availability"  => Array[
            [ones(Int,8)],
            [ones(Int,8)],
            [ones(Int,8)],
            [ones(Int,8)],
        ],
        "longest_availability" => 8*ones(Int, 4),
        "worked_day_preceding_week" => true,
    },
    "d" => {
        "hours_min" => 15,
        "hours_max" => 17,
        "shift_count_min" => 2,
        "shift_count_max" => 2,
        "availability"  => Array[
            [ones(Int,8)],
            [ones(Int,8)],
            [ones(Int,8)],
            [ones(Int,8)],
        ],
        "longest_availability" => 8*ones(Int, 4),
        "worked_day_preceding_week" => true,
    },
    "e" => {
        # must be scheduled days 2->3, but gets consec day off due to
        # having worked the day preceding the week
        "hours_min" => 15,
        "hours_max" => 17,
        "shift_count_min" => 2,
        "shift_count_max" => 2,
        "availability"  => Array[
            [ones(Int,8)],
            [ones(Int,8)],
            [ones(Int,8)],
            [zeros(Int,8)],
        ],
        "longest_availability" => [8, 8, 8, 0],
        "worked_day_preceding_week" => false,
    },
}

max_stars_repo_name: shdann/autoshift

max_issues_repo_name: shdann/autoshift

max_forks_repo_name: shdann/autoshift


text:
immutable Arc
    next::Int
end

type WFSM{T}
    states::Vector{T}
    arcs::Vector{Arc}
end

max_stars_repo_name: hshindo/JukaiNLP

max_issues_repo_name: hshindo/JukaiNLP

max_forks_repo_name: hshindo/JukaiNLP


text:
# This includes interpolation methods

export Interp1D, Interp2D, HInterp1D, HInterp2D, interpolate, project

const PointVector{Dim} = AbstractVector{<:AbstractPoint{Dim, T}} where {T}
const Tessellation = Union{<:LineString, <:PyObject}

abstract type AbstractInterp end
abstract type AbstractCurveInterp   <: AbstractInterp end       # One dimensional interpolations
abstract type AbstractSurfaceInterp <: AbstractInterp end       # Two dimensional interpolations

"""
    $TYPEDEF

One dimensional fractal interpolation. IFS is defined as
```math
    w_n(x, y) = \\begin{bmatrix}
        a_{11, n} & 0 \\
        a_{21, n} & a_{22, n}
    \\end{bmatrix}
    \\begin{bmatrix}
        b_{1, n} \\
        b_{2, n}
    \\end{bmatrix} \\quad n = 1, 2, \\ldots, N
```
Here ``a_{22, n}, n = 1, 2, \\ldots, N`` are the free variables. While constructing an `Interp1D`, the free varaibles can be
specfied as either a single real value or a vector of real values. If a single real value is specified, it assumed that
``a_{22} = a_{22, n}, n = 1, 2, \\ldots, N``.

# Fields

    $TYPEDFIELDS
"""
struct Interp1D{T<:Union{<:Real, <:AbstractVector{<:Real}}} <: AbstractCurveInterp
    """Free variables"""
    freevars::T
end

"""
    $TYPEDEF

Two dimensional fractal interpolation. IFS is defined as
```math
    w_n(x, y) = \\begin{bmatrix}
        a_{11, n}   & a_{12, n}     & 0         \\
        a_{21, n}   & a_{22, n}     & 0         \\
        a_{31, n}   & a_{32, n}     & a_{33, n} \\
    \\end{bmatrix}
    \\begin{bmatrix}
        b_{1, n} \\
        b_{2, n} \\
        b_{3, n} \\
    \\end{bmatrix} \\quad n = 1, 2, \\ldots, N
```
Here ``a_{33,n}, n = 1, 2, \\ldots, N` are the free variables. While constructing an `InterpdD`, the free variables can be
specfied as either a single real value or a vector of real values. If a single real value is specified, it assumed that
``a_{33, n}, n = 1, 2, \\ldots, N``.

# Fields

    $TYPEDFIELDS
"""
struct Interp2D{T<:Union{<:Real, <:AbstractVector{<:Real}}} <: AbstractSurfaceInterp
    """Free variables"""
    freevars::T
end

"""
    $TYPEDEF

One dimensional hidden fractal interpolation. IFS is defined as
```math
    w_n(x, y) = \\begin{bmatrix}
        a_{11, n}   & 0             & 0         \\
        a_{21, n}   & a_{22, n}     & a_{23, n} \\
        a_{31, n}   & a_{32, n}     & a_{33, n} \\
    \\end{bmatrix}
    \\begin{bmatrix}
        b_{1, n} \\
        b_{2, n} \\
        b_{3, n} \\
    \\end{bmatrix} \\quad n = 1, 2, \\ldots, N
```
Here
```math
    \\begin{bmatrix}
        a_{22, n} & a_{23, n} \\
        a_{32, n} & a_{33, n}
    \\end{bmatrix}
```
are the free variables. While constructing an `HInterp1D`, the free variables can be specfied as either a single real matrix
or a vector of real matrices. If a single real matrix is specified, it assumed that
```math
    \\begin{bmatrix}
        a_{22} & a_{23} \\
        a_{32} & a_{33}
    \\end{bmatrix}
    \\begin{bmatrix}
        a_{22, n} & a_{23, n} \\
        a_{32, n} & a_{33, n}
    \\end{bmatrix}
    \\quad \\forall n = 1, 2, \\ldots, N
```

# Fields

    $TYPEDFIELDS
"""
struct HInterp1D{T<:Union{<:AbstractMatrix, <:AbstractVector{<:AbstractMatrix}}} <: AbstractCurveInterp
    """Free variables"""
    freevars::T
end

"""
    $TYPEDEF

Two dimensional hidden fractal interpolation. IFS is defined as
```math
    w_n(x, y) = \\begin{bmatrix}
        a_{11, n}   & a_{11, n}     & 0             & 0         \\
        a_{21, n}   & a_{22, n}     & 0             & 0         \\
        a_{31, n}   & a_{32, n}     & a_{33, n}     & a_{34, n} \\
        a_{41, n}   & a_{42, n}     & a_{43, n}     & a_{44, n} \\
    \\end{bmatrix}
    \\begin{bmatrix}
        b_{1, n} \\
        b_{2, n} \\
        b_{3, n} \\
        b_{4, n} \\
    \\end{bmatrix} \\quad n = 1, 2, \\ldots, N
```
Here
```math
    \\begin{bmatrix}
        a_{33, n} & a_{34, n} \\
        a_{43, n} & a_{44, n}
    \\end{bmatrix}
```
are the free variables. While constructing an `HInterp2D`, the free variables can be specfied as either a single real matrix
or a vector of real matrices. If a single real matrix is specified, it assumed that
```math
    \\begin{bmatrix}
        a_{33} & a_{34} \\
        a_{43} & a_{44}
    \\end{bmatrix}
    \\begin{bmatrix}
        a_{33, n} & a_{34, n} \\
        a_{43, n} & a_{44, n}
    \\end{bmatrix}
    \\quad \\forall n = 1, 2, \\ldots, N
```

# Fields

    $TYPEDFIELDS
"""
struct HInterp2D{T<:Union{<:AbstractMatrix, <:AbstractVector{<:AbstractMatrix}}} <: AbstractSurfaceInterp
    """Free variables"""
    freevars::T
end

# Note: For plotting recipes, Interpolant should a subtype of Function
"""
    $TYPEDEF

Interpolation object.

# Fields

    $TYPEDFIELDS
"""
struct Interpolant{T1<:IFS, T2, T3<:AbstractInterp} <: Function
    """IFS """
    ifs::T1
    """Interpolation function"""
    itp::T2
    """Interpolation method"""
    method::T3
end

(interp::Interpolant)(x...) = interp.itp(x...)

"""
    $SIGNATURES

Returns an [`Interpolant`](@ref) by interpolating `pts` using `method`. `f0` is the initial function. `niter` is the number
of iterations during the construction of the interpolant. `method` can be

* [`Interp1D`](@ref)
* [`HInterp1D`](@ref)
* [`Interp2D`](@ref)
* [`HInterp2D`](@ref)

# Example
```julia
julia> xi, dx, xf = 0., 0.01, 10.;

julia> x = collect(xi : dx : xf);

julia> y = sin.(x);

julia> pts = collect.(zip(x, y));

julia> interp = integrate(pts, Interp1D(0.001));  # One dimensional interpolation.
```
"""
interpolate(pts::AbstractVector{<:AbstractVector{<:Real}}, method::AbstractInterp; f0 = getinitf(method), niter::Int = 10) =
    interpolate(map(pnt -> Point(pnt...), pts), method, f0=f0, niter=niter)

function interpolate(pts::PointVector, method::AbstractInterp; f0 = getinitf(method), niter::Int = 10)
    tess = tessellate(pts, method)
    transforms = gettransforms(pts, method)
    mappings = getmappings(transforms, method)
    itp = wrap(f0, tess, mappings, niter)[1]
    Interpolant(IFS(transforms), itp, method)
end

getinitf(::Interp1D)   = x -> 0.
getinitf(::HInterp1D)  = x -> [0., 0.]
getinitf(::Interp2D)   = (x, y) -> 0.
getinitf(::HInterp2D)  = (x, y) -> [0., 0.]

"""
    $SIGNATURES

Projects `pts` on a lower dimensional space by dropping the `drop` number of indexes.
"""
project(pts::PointVector, drop::Int=1)     = [Point(pnt[1 : end - drop]...) for pnt in pts]
project(pts::PointVector{2}, ::Interp1D)   = project(pts, 1)
project(pts::PointVector{3}, ::HInterp1D)  = project(pts, 2)
project(pts::PointVector{3}, ::Interp2D)   = project(pts, 1)
project(pts::PointVector{4}, ::HInterp2D)  = project(pts, 2)

# We use tessellation to locate the points. The fractal interpolant that is returned by `interpolate` function is a piecewise
# function. So to calculate the interpolant `interp` at a point `pnt`, we need to locate `pnt` to find the correct subdomain.
# We use tessellations to locate the points. See also `locate` function

tessellate(pts::PointVector{2}, method::Interp1D)  = LineString(project(pts, method))
tessellate(pts::PointVector{3}, method::HInterp1D) = LineString(project(pts, method))
tessellate(pts::PointVector{3}, method::Interp2D)  = spt.Delaunay(project(pts, method))
tessellate(pts::PointVector{4}, method::HInterp2D) = spt.Delaunay(project(pts, method))

# Because of finite precison arithmetic, the location of a valid point may fail. To overcome this, we can switch to arbitrary
# precision arithmetic (by using BigFloat) in the expense of increasing the computationonal complexity. However, not to
# increase the computationonal complexity directly, the strategy employed here is this:  before switching completely to
# arbitrary precision arithmetic, we first try to locate the point with finite precision (Float64 precision). If the point
# cannot be located, we double the precision and try to locate the point again. We do this until maximum allowed
# precison(specified to be 1024 bits as MAXPREC). If MAXPREC is reached and the point still cannot be found, the point
# location fails with an error message.

_locate(pnt::AbstractPoint{1, T}, tess::LineString) where {T} = findfirst(((p1, p2),) -> p1[1] ≤ pnt[1] ≤ p2[1], tess)
_locate(pnt::AbstractPoint{2, T}, tess::PyObject)   where {T} = tess.find_simplex(pnt)[1] + 1
function locate(pnt::AbstractPoint, tess::Tessellation)
    n = _locate(pnt, tess)
    n == 0 || return n
    pnt = doubleprecision(pnt)
    locate(pnt, tess)
end

doubleprecision(pnt::AbstractPoint{N, <:Real}) where {N} = Point(BigFloat.(pnt)...)
function doubleprecision(pnt::AbstractPoint{N, <:BigFloat}) where {N}
    prec = 2 * precision(BigFloat)
    prec ≤ MAXPREC ? setprecision(prec) : error("Exceeded maximum allowed precision $MAXPREC")
    Point(BigFloat.(pnt)...)
end

# IFS coefficients of the interpolant is found by using a linear algrebraic equation system using the boundary conditions.
# Each subtransformation in the transformations of a IFS maps a larger domain (Line in case of curve interpolation and
# Triangle in case of surface interpolation) to a smaller domain. These mappings maps the boundary points of the larger
# domains to boundary points of the smaller domains. `partition` function returns these smaller domains.

partition(pts::PointVector, method::AbstractCurveInterp) = LineString(pts)
function partition(pts::PointVector, method::AbstractSurfaceInterp, tess::Tessellation=tessellate(pts, method))
    trifaces = [TriangleFace(val...) for val in eachrow(tess.simplices .+ 1)]
    GeometryBasics.Mesh(pts, trifaces)
end

# In case of curve interpolations, the boundary is the line that connects the endpoints of the interpolation domain. In case
# of surface interpolations, the interpolation domain is triangle, than that triangle is the boundary. If the interpolation
# domain is ngon (such as, tetragon, pentagon, hexagon, etc.) the triangle that can be drawn inside the convex hull of the
# points and that has the largest area is returned.

getboundary(pts::PointVector, ::AbstractCurveInterp) = Line(pts[1], pts[end])
function getboundary(pts::PointVector, method::AbstractSurfaceInterp)
    # TODO: Add methods for nonconvex boundaries #42
    hull = spt.ConvexHull(collect(hcat(collect.(project(pts, method))...)') )
    if length(hull.vertices) == 3   # Triangle
        Triangle(Point.(pts[hull.vertices .+ 1])...)
    else    # Ngon (tetragon, pentagon, hexagon, etc...)
        polygon = Ngon(
            SVector{length(hull.vertices)}(Point.(pts[hull.vertices .+ 1]))
        )
        msh = GeometryBasics.mesh(polygon)
        idx = argmax([area(trig.points) for trig in msh])
        msh[idx]
    end
end

# `gettransforms` returns tranforms that map outer domains to smaller domains in the interpolation domain.
function gettransforms(pts::PointVector, method::AbstractInterp)
    parts = partition(pts, method)
    n = length(parts)
    freevars = typeof(method.freevars) <: AbstractVector ? method.freevars : fill(method.freevars, n)
    boundary = getboundary(pts, method)
    map(((domain, freevar),) -> _gettransform(boundary, domain, freevar), zip(parts, freevars))
end

# Interp1D
function _gettransform(outline::Line, inline::Line, freevar::Real)
    outmat = collect(hcat(coordinates(outline)...)')
    inmat = collect(hcat(coordinates(inline)...)')
    inmat[:, end] -= outmat[:, end] * freevar
    outmat[:, end] .= 1
    a11, b1, a21, b2 = outmat \ inmat
    A = [a11    0;
         a21    freevar]
    b = [b1, b2]
	Transformation(A, b)
end

# HInterp1D
function _gettransform(outline::Line, inline::Line, freevar::AbstractMatrix)
    outmat = collect(hcat(coordinates(outline)...)')
    inmat = collect(hcat(coordinates(inline)...)')
    inmat[:, 2 : 3] -= outmat[:, 2 : 3] * freevar'
    outmat = [outmat[:, 1] ones(2)]
    a11, b1, a21, b2, a31, b3 = outmat \ inmat
    A = [a11    0               0;
         a21    freevar[1,1]    freevar[1,2];
         a31    freevar[2,1]    freevar[2,2]
         ]
    b = [b1, b2, b3]
	Transformation(A, b)
end

# Interp2D
function _gettransform(outtrig::Triangle, intrig::Triangle, freevar::Real)
    outmat = collect(hcat(coordinates(outtrig)...)')
    inmat = collect(hcat(coordinates(intrig)...)')
    inmat[:, end] -= outmat[:, end] * freevar
    outmat[:, end] .= 1
    a11, a12, b1, a21, a22, b2, a31, a32, b3 = outmat \ inmat
    A = [a11    a12    0;
         a21    a22    0;
         a31    a32    freevar
         ]
    b = [b1, b2, b3]
	Transformation(A, b)
end

# HInterp2D
function _gettransform(outtrig::Triangle, intrig::Triangle, freevar::AbstractMatrix)
    outmat = collect(hcat(coordinates(outtrig)...)')
    inmat = collect(hcat(coordinates(intrig)...)')
    inmat[:, 3:4] -= outmat[:, 3:4] * freevar'
    outmat = [outmat[:, 1:2] ones(3)]
    a11, a12, b1, a21, a22, b2, a31, a32, b3, a41, a42, b4 = outmat \ inmat
    A = [a11    a12    0                0;
         a21    a22    0                0;
         a31    a32    freevar[1, 1]    freevar[1, 2];
         a41    a42    freevar[2, 1]    freevar[2, 2];
         ]
    b = [b1, b2, b3, b4]
	Transformation(A, b)
end

# Here transforms and mappings are different. The type of a transform is FractalTools.Transformation. A
# FractalTools.Transformation object basically consists of the matrix A and vector b of an affine transformation w(x) = A * x
# + b. However, a mapping is a subtransformation (defined as Ln: Ω ↦ Ωₙ and Fn: Ω × Rᵐ ↦ Rᵖ, where Ω is the interpolation
# domain and Ωₙ is the subdomain obtained by partitioning the interpolation domian) of an affine transformation in an IFS.
# So, we compute the mappings (i.e. subtransformations) from transforms and use directly the mappings to compute the
# interpolant.

getmappings(transforms, method) = map(transform -> _getmapping(transform, method), transforms)

function _getmapping(transform, method::Interp1D)
    (a11, a21, _, a22), (b1, b2) = transform.A, transform.b
    linv = x -> (x - b1) / a11
    F = (x, y) -> a21 * x + a22 * y + b2
    (linv, F)
end

function _getmapping(transform, method::HInterp1D)
    (a11, a21, a31, _, a22, a32, _, a23, a33), (b1, b2, b3) = transform.A, transform.b
    linv = x -> (x - b1) / a11
    F = (x, y, z) ->  [a21 a22 a23; a31 a32 a33] * [x, y, z] + [b2, b3]
    (linv, F)
end

function _getmapping(transform, method::Interp2D)
    (a11, a21, a31, a12, a22, a32, _, _, a33), (b1, b2, b3) = transform.A, transform.b
    linv = (x, y) -> [a11 a12; a21 a22] \ ([x, y] - [b1, b2])
    F = (x, y, z) ->  a31 * x + a32 * y + a33 * z + b3
    (linv, F)
end

function _getmapping(transform, method::HInterp2D)
    (a11, a21, a31, a41, a12, a22, a32, a42, _, _, a33, a34, _, _, a43, a44), (b1, b2, b3, b4) = transform.A, transform.b
    linv = (x, y) -> [a11 a12; a21 a22] \ ([x, y] - [b1, b2])
    F = (x, y, z, t) ->  [a31 a32 a33 a34; a41 a42 a43 a44] * [x, y, z, t] + [b3, b4]
    (linv, F)
end

# Main iteration of the initial function `f0` for `niter` iterations.
# TODO: Calculate `niter` with respect to a given ϵ
wrap(f0, tess::Tessellation, mappings::AbstractVector{<:Tuple{T, S}}, niter::Int) where {T, S} =
    ((f0, tess, mappings)) |> ∘((wrapper for i in 1 : niter)...)

function wrapper((f, tess, mappings))
    function fnext(x...)
        pnt = Point(x...)
        n = locate(pnt, tess)
        # n == 0 && error("Point $pnt cannot be found.")
        linv, F = mappings[n]
        val = linv(x...)
        F(val..., f(val...)...)
    end, tess, mappings
end


text:
module WebUI

using ..Registrator: pull_request_contents
import RegistryTools

using Dates
using GitForge, GitForge.GitHub, GitForge.GitLab
using HTTP
using JSON
using Mux
using Pkg, Pkg.TOML
using Sockets
using TimeToLive
using Logging
using Mustache

using ..Messaging

const ROUTES = Dict(
    :INDEX => "/",
    :AUTH => "/auth",
    :CALLBACK => "/callback",
    :SELECT => "/select",
    :REGISTER => "/register",
    :STATUS => "/status",
)

const DOCS = "https://juliaregistries.github.io/Registrator.jl/stable/webui/#Usage-(For-Package-Maintainers)-1"
const CONFIG = Dict{String, Any}()

include("../management.jl")
const httpsock = Ref{Sockets.TCPServer}()

include("providers.jl")

# The target registry.
struct Registry{F <: GitForge.Forge, R}
    forge::F
    repo::R
    url::String
    clone::String
    deps::Vector{String}
    enable_release_notes::Bool
end

# U is a User type, e.g. GitHub.User.
struct User{U, F <: GitForge.Forge}
    user::U
    forge::F
end

struct RegistrationData
    project::Pkg.Types.Project
    tree::String
    repo::Union{GitForge.GitHub.Repo, GitForge.GitLab.Project}
    user::Union{GitForge.GitHub.User, GitForge.GitLab.User}
    ref::String
    commit::String
    notes::String
    is_ssh::Bool
end

const event_queue = Channel{RegistrationData}(1024)

struct RegistrationState
    msg::String
    state::Symbol
end

const PROVIDERS = Dict{String, Provider}()
const REGISTRY = Ref{Registry}()
const USERS = TTL{String, User}(Hour(1))
const REGISTRATIONS = Dict{String, RegistrationState}()

###########
# Helpers #
###########

include("webutils.jl")
include("gitutils.jl")

##########
# Routes #
##########

include("routes/index.jl")
include("routes/auth.jl")
include("routes/callback.jl")
include("routes/select.jl")
include("routes/register.jl")
include("routes/status.jl")

##############
# Entrypoint #
##############

function init_registry()
    url = CONFIG["registry_url"]
    k = get(CONFIG, "registry_provider") do
        if occursin("github", url)
            "github"
        else
            "gitlab"
        end
    end
    haskey(PROVIDERS, k) || error("Unsupported registry host")
    forge = PROVIDERS[k].client
    owner, name = splitrepo(url)
    repo = @gf get_repo(forge, owner, name)
    repo === nothing && error("Registry lookup failed")
    clone = get(CONFIG, "registry_clone_url", url)
    deps = map(String, get(CONFIG, "registry_deps", String[]))
    enable_release_notes = !get(CONFIG, "disable_release_notes", false)
    REGISTRY[] = Registry(forge, repo, url, clone, deps, enable_release_notes)
end

for f in [:index, :auth, :callback, :select, :register]
    @eval $f(func::Function, r::HTTP.Request) = func($f(r))
end

error_handler(f::Function, r::HTTP.Request) = try
    f(r)
catch e
    println(get_backtrace(e))
    @error "Handler error" route=r.target
    html(500, "Server error, sorry!")
end

pathmatch(p::AbstractString, f::Function) = branch(r -> first(split(r.target, "?")) == p, f)

function action(regdata::RegistrationData, zsock::RequestSocket)
    regp = RegistryTools.RegisterParams(cloneurl(regdata.repo, regdata.is_ssh), regdata.project, regdata.tree;
        registry=REGISTRY[].clone, registry_deps=REGISTRY[].deps, push=true,
    )
    branch = sendrecv(zsock, regp; nretry=5)
    if branch === nothing || get(branch.metadata, "error", nothing) !== nothing
        if branch === nothing
            msg = "ERROR: Registrator backend service unreachable"
        else
            msg = "Registration failed: " * branch.metadata["error"]
        end
        state = :errored
    else
        title, body = pull_request_contents(;
            registration_type=get(branch.metadata, "kind", ""),
            package=regdata.project.name,
            repo=web_url(regdata.repo),
            user=display_user(regdata.user),
            gitref=regdata.ref,
            version=regdata.project.version,
            commit=regdata.commit,
            release_notes=regdata.notes,
        )

        # Make the PR.
        pr = @gf make_registration_request(REGISTRY[], branch.branch, title, body)
        if pr === nothing
            msg = "Registration failed: Making pull request failed"
            state = :errored
        else
            url = web_url(pr)
            msg = """Registry PR successfully created, see it here!"""
            state = :success
        end
    end
    @debug msg
    REGISTRATIONS[regdata.commit] = RegistrationState(msg, state)
end

function request_processor(zsock::RequestSocket)
    do_action() = action(take!(event_queue), zsock)
    handle_exception(ex) = ex isa InvalidStateException && ex.state === :closed ? :exit : :continue
    keep_running() = isopen(httpsock[])
    recover("request_processor", keep_running, do_action, handle_exception)
end

function start_server(ip::IPAddr, port::Int)
    httpsock[] = Sockets.listen(ip, port)
    @app server = (
        error_handler,
        pathmatch(ROUTES[:INDEX], index),
        pathmatch(ROUTES[:AUTH], auth),
        pathmatch(ROUTES[:CALLBACK], callback),
        pathmatch(ROUTES[:SELECT], select),
        pathmatch(ROUTES[:REGISTER], register),
        pathmatch(ROUTES[:STATUS], status),
        r -> html(404, "Page not found"),
    )
    do_action() = wait(serve(server, ip, port; server=httpsock[], readtimeout=0))
    handle_exception(ex) = ex isa Base.IOError && ex.code == -103 ? :exit : :continue
    keep_running() = isopen(httpsock[])
    recover("webui", keep_running, do_action, handle_exception)
end

function main(config::AbstractString=isempty(ARGS) ? "config.toml" : first(ARGS))
    merge!(CONFIG, TOML.parsefile(config)["web"])
    if get(CONFIG, "enable_logging", true)
        global_logger(SimpleLogger(stdout, get_log_level(get(CONFIG, "log_level", "INFO"))))
    end
    zsock = RequestSocket(get(CONFIG, "backend_port", 5555))

    if haskey(CONFIG, "route_prefix")
        for (k, v) in ROUTES
            ROUTES[k] = CONFIG["route_prefix"] * v
        end
    end

    init_providers()
    init_registry()

    ip = CONFIG["ip"] == "localhost" ? Sockets.localhost : parse(IPAddr, CONFIG["ip"])
    port = CONFIG["port"]

    @info "Starting WebUI" ip port
    monitor = @async status_monitor(CONFIG["stop_file"], event_queue, httpsock)
    reqproc = @async request_processor(zsock)
    start_server(ip, port)
    wait(reqproc)
    wait(monitor)

    # The !stopped! part is grep'ed by restart.sh, do not change
    @info "Server !stopped!"
end

end


text:
using Test
using Dates
using ExtensibleScheduler
using ExtensibleScheduler: TriggerOffset, get_next_dt_fire

@testset "TriggerOffset" begin
    @testset "+" begin
        for (t_fire_at, offset, dt_previous_fire, dt_now, dt_next_fire) in [
            (DateTime(2009, 7, 6), Dates.Minute(1), DateTime(0), DateTime(2008, 5, 4), DateTime(2009, 7, 6, 0, 1))
        ]
            trigger = TriggerOffset(Trigger(t_fire_at), offset)
            @test get_next_dt_fire(trigger, dt_previous_fire, dt_now) == dt_next_fire

            trigger = Trigger(t_fire_at) + TriggerOffset(offset)
            @test get_next_dt_fire(trigger, dt_previous_fire, dt_now) == dt_next_fire

            trigger = TriggerOffset(offset) + Trigger(t_fire_at)
            @test get_next_dt_fire(trigger, dt_previous_fire, dt_now) == dt_next_fire
        end
    end

    @testset "-" begin
        for (t_fire_at, offset, dt_previous_fire, dt_now, dt_next_fire) in [
            (DateTime(2009, 7, 6), Dates.Minute(1), DateTime(0), DateTime(2008, 5, 4), DateTime(2009, 7, 5, 23, 59))
        ]
            trigger = Trigger(t_fire_at) - TriggerOffset(offset)
            @test get_next_dt_fire(trigger, dt_previous_fire, dt_now) == dt_next_fire

            trigger = TriggerOffset(offset) - Trigger(t_fire_at)
            @test get_next_dt_fire(trigger, dt_previous_fire, dt_now) == dt_next_fire
        end
    end
end


text:
#!/usr/bin/env julia

# RainFARM
# Stochastic downscaling following
# D'Onofrio et al. 2014, J of Hydrometeorology 15 , 830-843 and
# Rebora et. al 2006, JHM 7, 724
# Includes orographic corrections

# Implementation in Julia language

# Copyright (c) 2016, Jost von Hardenberg - ISAC-CNR, Italy

using RainFARM
using ArgParse
using Compat, Compat.Printf

function parse_commandline()
    s = ArgParseSettings()

    @add_arg_table s begin
        "--slope", "-s"
            help = "spatial spectral slope"
            arg_type = Float64
            default = 0.0
        "--nens", "-e"
            help = "number of ensemble members"
            arg_type = Int
            default = 1
        "--nf", "-n"
            help = "Subdivisions for downscaling"
            arg_type = Int
            default = 2
        "--region", "-r", "-R"
            help = "Indices for region to cutout imin/imax/jmin/jmax"
            arg_type = AbstractString
            default = "0/0/0/0"
        "--weights", "-w", "--weight"
            help = "Weights file"
            arg_type = AbstractString
            default = ""
        "--outfile", "-o", "--out"
            help = "Output filename radix"
            arg_type = AbstractString
            default = "rainfarm"
        "infile"
            help = "The input file to downscale"
            arg_type = AbstractString
            required = true
        "--varname", "-v"
            help = "Input variable name"
            arg_type = AbstractString
            default = ""
        "--global", "-g"
            action = :store_true
            help = "conserve precipitation over full domain"
        "--conv", "-c"
            action = :store_true
            help = "conserve precipitation using convolution"
        "--kmin", "-k"
            help = "Minimum wavenumber"
            arg_type = Int
            default = 1
    end

    s.description="RainFARM downscaling: creates NENS realizations, downscaling INFILE, increasing spatial resolution by a factor NF. The slope is computed automatically unless specified. \ua0 Weights can be created with rfweights.jl"

    return parse_args(s)
end

args = parse_commandline()
nf=args["nf"]
filenc=args["infile"]
weightsnc=args["weights"]
nens=args["nens"]
varname=args["varname"]
fnbase=args["outfile"]
sx=args["slope"]
fglob=args["global"]
fsmooth=args["conv"]
region=args["region"]
kmin=args["kmin"]

imin=parse(Int,split(region,"/")[1])
imax=parse(Int,split(region,"/")[2])
jmin=parse(Int,split(region,"/")[3])
jmax=parse(Int,split(region,"/")[4])

println("Downscaling ",filenc)

(pr,lon_mat,lat_mat,varname)=read_netcdf2d(filenc, varname);

# Creo la griglia fine
(lon_f, lat_f)=lon_lat_fine(lon_mat, lat_mat,nf);

ns=size(lon_f)
println("Output size: ",ns)

if(imin==0)
   imin=1
   jmin=1
   if(length(ns)==2)
     imax=ns[1]; jmax=ns[2];
   else
     imax=ns[1]; (jmax,)=size(lat_f);
   end
else
   println("Cutout region: ",imin,"/",imax,"/",jmin,"/",jmax)
end

if(length(ns)==2)
  lon_fr=lon_f[imin:imax,jmin:jmax]
  lat_fr=lat_f[imin:imax,jmin:jmax]
else
  lon_fr=lon_f[imin:imax]
  lat_fr=lat_f[jmin:jmax]
end

if(sx==0.)
# Calcolo fft3d e slope
(fxp,ftp)=fft3d(pr);
sx=fitslopex(fxp,kmin=kmin);
println("Computed spatial spectral slope: ",sx)
else
println("Fixed spatial spectral slope: ",sx)
end

#if(varnc=="")
#   varnc="pr"
#end

if(fglob)
  println("Conserving only global precipitation")
end

if(weightsnc!="")
    println("Using weights file ",weightsnc)
    (ww,lon_mat2,lat_mat2)=read_netcdf2d(weightsnc, "");
else
    ww=1.
end
# Downscaling
for iens=1:nens
@compat  @printf("Realization %d\n",iens)
  @time rd=rainfarm(pr, sx, nf, ww,fglob=fglob,fsmooth=fsmooth,verbose=true);
@compat  fname=@sprintf("%s_%04d.nc",fnbase,iens);
  write_netcdf2d(fname,rd[imin:imax,jmin:jmax,:],lon_fr,lat_fr,varname,filenc)
end


text:
function solve()
    a, b, h, m = [parse(Int, x) for x in split(readline())]
    h_angle = 2π * (60h + m) / (12*60)
    m_angle = 2π * m / 60
    angle = abs(h_angle - m_angle)
    angle = angle > π ? 2π - angle : angle
    sqrt(a^2+b^2 - 2*a*b * cos(angle))
end

println(solve())

max_stars_repo_name: aishikawa/atcoder-julia

max_issues_repo_name: aishikawa/atcoder-julia

max_forks_repo_name: aishikawa/atcoder-julia


text:
using SNOW
using Test
using Zygote

checkallocations = false
snopttest = false

@testset "derivatives" begin

function test1!(g, x)

    f = x[1]^2 - x[2]

    Zygote.ignore() do
        g[1] = x[2] - 2*x[1]
        g[2] = -x[2]
        g[3] = x[1]^2
    end
    return f
end

# ---- test sparsity pattern  --------
ng = 3
lx = [-5; -5]
ux = [5; 5]
sp = SparsePattern(ForwardAD(), test1!, ng, lx, ux)
@test sp.rows == [1, 3, 1, 2]
@test sp.cols == [1, 1, 2, 2]
# -------------------------------------

# ----- test derivatives ------------
nx = 2
ng = 3
g = zeros(ng)
df = zeros(nx)
dg = zeros(ng*nx)
x = [1.0, 2.0]

# forward AD
cache = SNOW.createcache(DensePattern(), ForwardAD(), test1!, nx, ng)
SNOW.evaluate!(g, df, dg, x, cache)

@test df == [2*x[1]; -1.0]
@test dg == [-2.0, 0.0, 2*x[1], 1.0, -1.0, 0.0]

# reverse AD
cache = SNOW.createcache(DensePattern(), ReverseAD(), test1!, nx, ng)
SNOW.evaluate!(g, df, dg, x, cache)
@test df == [2*x[1]; -1.0]
@test dg == [-2.0, 0.0, 2*x[1], 1.0, -1.0, 0.0]

# finite diff
cache = SNOW.createcache(DensePattern(), ForwardFD(), test1!, nx, ng)
SNOW.evaluate!(g, df, dg, x, cache)

@test isapprox(df, [2*x[1]; -1.0])
@test isapprox(dg, [-2.0, 0.0, 2*x[1], 1.0, -1.0, 0.0])

# sparse with reverse and forward
dg = zeros(length(sp.rows))
cache = SNOW.createcache(sp, [ReverseAD(), ForwardAD()], test1!, nx, ng)
SNOW.evaluate!(g, df, dg, x, cache)

@test df == [2*x[1]; -1.0]
@test dg == [-2.0, 2*x[1], 1.0, -1.0]

# sparse with zygote and forward
dg = zeros(length(sp.rows))
cache = SNOW.createcache(sp, [RevZyg(), ForwardAD()], test1!, nx, ng)
SNOW.evaluate!(g, df, dg, x, cache)

@test df == [2*x[1]; -1.0]
@test dg == [-2.0, 2*x[1], 1.0, -1.0]

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

end

# if checkallocations
# # -------- test allocations --------------
#     using BenchmarkTools

#     function test2!(g, x)
#         f = x[1]^2 - x[2]

#         Zygote.ignore() do
#             nx = length(x)
#             for i = 1:nx
#                 g[i] = x[i]^2
#             end
#             g[nx+1:end] .= x[1]
#         end
#         return f
#     end

#     nx = 300
#     ng = 500
#     g = zeros(ng)
#     df = zeros(nx)
#     dg = zeros(ng*nx)
#     x = 2*ones(nx)
#     cache = SNOW.createcache(DensePattern(), ForwardAD(), test2!, nx, ng)
#     @btime SNOW.evaluate!($g, $df, $dg, $x, $cache)
#     # 577.123 μs (6 allocations: 2.30 MiB)

#     cache = SNOW.createcache(DensePattern(), ReverseAD(), test2!, nx, ng)
#     @btime SNOW.evaluate!($g, $df, $dg, $x, $cache)
#     # 4.188 ms (6 allocations: 2.30 MiB)

#     cache = SNOW.createcache(DensePattern(), FD("forward"), test2!, nx, ng)
#     @btime SNOW.evaluate!($g, $df, $dg, $x, $cache)
#     # 358.457 μs (6 allocations: 2.30 MiB)

#     lx = -5*ones(nx)
#     ux = 5*ones(nx)
#     sp = SparsePattern(ForwardAD(), test2!, ng, lx, ux)
#     dg = zeros(length(sp.rows))
#     cache = SNOW.createcache(sp, [ReverseAD(), ForwardAD()], test2!, nx, ng)
#     @btime SNOW.evaluate!($g, $df, $dg, $x, $cache)
#     # 17.391 μs (0 allocations: 0 bytes)

#     cache = SNOW.createcache(sp, [RevZyg(), ForwardAD()], test2!, nx, ng)
#     @btime SNOW.evaluate!($g, $df, $dg, $x, $cache)
#     # 9.865 μs (6 allocations: 11.63 KiB)
# end
# # ----------------------------------------

@testset "optimization" begin

function barnes(g, x)

    a1 = 75.196
    a3 = 0.12694
    a5 = 1.0345e-5
    a7 = 0.030234
    a9 = 3.5256e-5
    a11 = 0.25645
    a13 = 1.3514e-5
    a15 = -5.2375e-6
    a17 = 7.0e-10
    a19 = -1.6638e-6
    a21 = 0.0005
    a2 = -3.8112
    a4 = -2.0567e-3
    a6 = -6.8306
    a8 = -1.28134e-3
    a10 = -2.266e-7
    a12 = -3.4604e-3
    a14 = -28.106
    a16 = -6.3e-8
    a18 = 3.4054e-4
    a20 = -2.8673

    x1 = x[1]
    x2 = x[2]
    y1 = x1*x2
    y2 = y1*x1
    y3 = x2^2
    y4 = x1^2

    # --- function value ---

    f = a1 + a2*x1 + a3*y4 + a4*y4*x1 + a5*y4^2 +
        a6*x2 + a7*y1 + a8*x1*y1 + a9*y1*y4 + a10*y2*y4 +
        a11*y3 + a12*x2*y3 + a13*y3^2 + a14/(x2+1) +
        a15*y3*y4 + a16*y1*y4*x2 + a17*y1*y3*y4 + a18*x1*y3 +
        a19*y1*y3 + a20*exp(a21*y1)

    # --- constraints ---

    g[1] = 1 - y1/700.0
    g[2] = y4/25.0^2 - x2/5.0
    g[3] = (x1/500.0- 0.11) - (x2/50.0-1)^2

    return f
end

x0 = [10.0; 10.0]
lx = [0.0; 0.0]
ux = [65.0; 70.0]

ng = 3
options = Options(solver=IPOPT(), derivatives=ForwardFD())
xopt, fopt, info, out = minimize(barnes, x0, ng, lx, ux, -Inf, 0.0, options)

@test isapprox(xopt[1], 49.5263; atol=1e-4)
@test isapprox(xopt[2], 19.6228; atol=1e-4)
@test isapprox(fopt, -31.6368; atol=1e-4)
@test info == :Solve_Succeeded || info == :Solved_To_Acceptable_Level

if snopttest
    options = Options(solver=SNOPT(), derivatives=ForwardAD())
    xopt, fopt, info, out = minimize(barnes, x0, ng, lx, ux, -Inf, 0.0, options)

    @test isapprox(xopt[1], 49.5263; atol=1e-4)
    @test isapprox(xopt[2], 19.6228; atol=1e-4)
    @test isapprox(fopt, -31.6368; atol=1e-4)
    @test info == "Finished successfully: optimality conditions satisfied"

    options = Options(solver=SNOPT(), derivatives=ComplexStep())
    xopt, fopt, info, out = minimize(barnes, x0, ng, lx, ux, -Inf, 0.0, options)

    @test isapprox(xopt[1], 49.5263; atol=1e-4)
    @test isapprox(xopt[2], 19.6228; atol=1e-4)
    @test isapprox(fopt, -31.6368; atol=1e-4)
    @test info == "Finished successfully: optimality conditions satisfied"
end

function sparsegrad(g, x)

    f = x[1]^2 - x[2]

    g[1] = x[2] - 2*x[1]
    g[2] = -x[2]

    return f
end

x0 = [0.0; 0.0]
lx = [-10.0, -10.0]
ux = [10.0, 10.0]
ng = 2

# detect sparsity pattern
sp = SparsePattern(ForwardAD(), sparsegrad, ng, lx, ux)

if snopttest
    options = Options(solver=SNOPT(), derivatives=[ReverseAD(), ForwardAD()], sparsity=sp)
    xopt, fopt, info, out = minimize(sparsegrad, x0, ng, lx, ux, -Inf, 0.0, options)

    @test isapprox(xopt[1], 1.0; atol=1e-6)
    @test isapprox(xopt[2], 2.0; atol=1e-6)
    @test isapprox(fopt, -1.0; atol=1e-5)
    @test info == "Finished successfully: optimality conditions satisfied"
end

end

# @testset "problem format" begin

# function test3!(g, prob, x)
#     return nothing
# end

# prob = SNOW.createproblem(test3!, "test3")

# x = [1.0, 2.0, 3.0]
# lx = [0.0, 0.0, 0.0]
# ux = 10*ones(3)
# names = ["c1", "c2", "c3"]
# SNOW.add_dv!(prob, x, lx, ux, names)

# x2 = [8.0, 2.0]
# lx2 = [0.0, 0.0, 0.0]
# ux2 = 5*ones(3)
# names2 = ["t1", "t2"]
# SNOW.add_dv!(prob, x2, lx2, ux2, names2)

# x = rand(5)
# SNOW.get_dvs(prob, x)

# end


text:
export
    PathNode,
    get_s,
    get_a,
    get_sp

"""
    PathNode{S,A}

Includes current state `s`, action `a`, next state `sp`
"""
@with_kw struct PathNode{S,A}
    s::S = S() # state
    a::A = A() # action
    sp::S = S() # next state
end
"""
    get_s

Get the first state in a `PathNode`.
"""
get_s(p::P) where {P<:PathNode} = p.s
"""
    get_a

Get the action in a `PathNode`.
"""
get_a(p::P) where {P<:PathNode} = p.a
"""
    get_sp

Get the next state in a `PathNode`.
"""
get_sp(p::P) where {P<:PathNode} = p.sp
state_type(p::PathNode{S,A}) where {S,A} = S
action_type(p::PathNode{S,A}) where {S,A} = A
Base.string(p::PathNode) = "$(string(get_s(p))) -- $(string(get_a(p))) -- $(string(get_sp(p)))"

export
    Path,
    node_type,
    get_cost,
    set_cost!,
    get_initial_state,
    get_final_state,
    get_start_time,
    get_end_time,
    get_path_node,
    extend_path!,
    extend_path

abstract type AbstractPath end

"""
    Path{S,A,C}

Encodes a motion plan as a sequence of `PathNode{S,A}`s
"""
@with_kw mutable struct Path{S,A,C} <: AbstractPath
    path_nodes  ::Vector{PathNode{S,A}} = Vector{PathNode{S,A}}()
    s0          ::S                     = get_s(get(path_nodes, 1, PathNode{S,A}()))
    cost        ::C                     = typemax(C)
end
Path(v::Vector{P}) where {P<:PathNode}      = Path(path_nodes=v,s0=get_s(get(v,1,P())),cost=0.0)
state_type(p::Path{S,A,C}) where {S,A,C}    = S
action_type(p::Path{S,A,C}) where {S,A,C}   = A
node_type(p) = PathNode{state_type(p),action_type(p)}
cost_type(p::Path{S,A,C}) where {S,A,C}     = C
Base.cat(p::P,x::N,i...) where {P<:Path,N<:PathNode} = P(s0=p.s0,path_nodes=cat(p.path_nodes,x,dims=1),cost=p.cost)
Base.get(p::P,i,default=node_type(p)) where {P<:Path}    = get(p.path_nodes,i,default)
Base.getindex(p::P,i) where {P<:Path}       = getindex(p.path_nodes,i)
Base.setindex!(p::P,x,i) where {P<:Path}    = setindex!(p.path_nodes,x,i)
Base.length(p::P) where {P<:Path}           = length(p.path_nodes)
function Base.push!(p::P,n::N) where {P<:Path,N<:PathNode}
    if length(p.path_nodes) == 0
        p.s0 = n.s
    end
    push!(p.path_nodes,n)
end
get_cost(p::P) where {P<:Path}              = p.cost
function set_cost!(p::P,cost) where {P<:Path}
    p.cost = cost
end
Base.copy(p::P) where {P<:Path}             = Path(s0=p.s0,path_nodes=copy(p.path_nodes),cost=p.cost)

function get_initial_state(path::P) where {P<:Path}
    if length(path) > 0
        s0 = get_s(get(path,1,node_type(path)()))
    else
        s0 = path.s0
    end
    # @assert get_t(s0) == 0
    return s0
end
function get_final_state(path::P) where {P<:Path}
    if length(path) > 0
        sf = get_sp(get(path,length(path),node_type(path)()))
    else
        sf = path.s0
    end
    # @assert get_t(sf) == length(path) "length(path) = $(length(path)), but get_t(get_final_state(path)) = $(get_t(sf))"
    return sf
end

export
    TimeIndexedState,
    get_start_index,
    get_index_from_time,
    get_end_index

get_start_index(path::P) where {P<:AbstractPath} = 0
get_end_index(path::P) where {P<:AbstractPath} = length(path) + get_start_index(path)
get_index_from_time(path::P,t::Int) where {P<:AbstractPath} = t - get_start_index(path)
abstract type TimeIndexedState end
get_start_index(path::P) where {S<:TimeIndexedState,A,C,P<:Path{S,A,C}} = 1 - get_time_index(path.s0)

"""
    returns the `PathNode` (s,a,s') corresponding to step `t` of `path`

    If `t` is greater than the length of `path`, the `PathNode` returned
    is (s,wait(s),s) corresponding to waiting at that node of the path.

    path[t] is the path node that begins at t-1 and terminates at t
"""
function get_path_node(path::P,t::Int) where {P<:AbstractPath}
    t_idx = get_index_from_time(path,t)
    if 1 <= t_idx <= length(path)
        return path[t_idx]
    elseif t_idx == 0
        return node_type(path)(sp=get_initial_state(path))
    else
        t₀ = get_index_from_time(path,get_end_index(path))
        if t₀ <= 0
            node = node_type(path)(sp=get_initial_state(path))
        else
            node = get(path,length(path),node_type(path)(s=get_initial_state(path)))
        end
        s = get_s(node)
        a = get_a(node)
        sp = get_sp(node)
        for τ in t₀+1:t_idx
            s = sp
            a = wait(s)
            sp = get_next_state(s,a)
        end
        return PathNode(s,a,sp)
    end
end
function get_path_node(path::P,t::Float64) where {P<:AbstractPath}
    @assert abs(t - Int(round(t))) < 0.01
    get_path_node(path,Int(round(t)))
end

# """
#     `get_a(path,t)`
#
#     Returns the action at time `t`.
#
#     If `t` is greater than the length of `path`, the returned action defaults to
#     a `wait` action.
# """
get_s(path::P, t::Int) where {P<:Path} = get_s(get_path_node(path,t))
get_a(path::P, t::Int) where {P<:Path} = get_a(get_path_node(path,t))
get_sp(path::P, t::Int) where {P<:Path} = get_sp(get_path_node(path,t))

"""
    extend_path!(path,T)

    Extends `path` to match a given length `T` by adding `PathNode`s
    corresponding to waiting at the final state.

    args:
    - path      the path to be extended
    - the desired length of the new path
"""
function extend_path!(path::P,T::Int) where {P<:Path}
    # while length(path) < T
    # while get_index_from_time(path,get_end_index(path)) < T
    while get_end_index(path) < T
        s = get_final_state(path)
        a = wait(s)
        push!(path,PathNode(s,wait(s),get_next_state(s,a)))
    end
    return path
end

"""
    extend_path(path,T)

    Extends a copy of `path` to match a given length `T` by adding `PathNode`s
    corresponding to waiting at the final state.

    args:
    - path      the path to be extended
    - the desired length of the new path
"""
function extend_path(path::P,args...) where {P<:Path}
    new_path = copy(path)
    extend_path!(new_path,args...)
    return new_path
end

export
    AbstractCostModel,
    cost_type

"""
    `AbstractCostModel{T}`
"""
abstract type AbstractCostModel{T} end
cost_type(model::M) where {T,M<:AbstractCostModel{T}} = T

export
    LowLevelSolution,
    path_type,
    get_paths,
    get_path_costs,
    set_solution_path!,
    set_path_cost!

"""
    `LowLevelSolution{S,A,T,C}`

    Contains a list of agent paths and the associated costs.
    Params:
    - `S` is the state type
    - `A` is the action type
    - `T` is the cost type
    - `C` is the cost model type
    Elements:
    - `paths::Vector{Path{S,A,T}}` is the vector of paths
    - `costs::Vector{T}` is the vector of costs, one per path
    - `cost::T` is the total cost for the entire solution
"""
@with_kw_noshow mutable struct LowLevelSolution{S,A,T,C<:AbstractCostModel{T}}
    paths::Vector{Path{S,A,T}}  = Vector{Path{S,A,T}}()
    cost_model::C               = C() # TODO C() is a problem
    costs::Vector{T}            = Vector{cost_type(cost_model)}(map(i->get_initial_cost(cost_model),1:length(paths)))
    cost::T                     = get_initial_cost(cost_model)
end
state_type(s::LowLevelSolution{S,A,T,C}) where {S,A,T,C}    = S
action_type(s::LowLevelSolution{S,A,T,C}) where {S,A,T,C}   = A
cost_type(s::LowLevelSolution{S,A,T,C}) where {S,A,T,C}     = T
path_type(s) = Path{state_type(s),action_type(s),cost_type(s)}
Base.copy(solution::L) where {L <: LowLevelSolution} = L(
    paths=copy(solution.paths), # NOTE don't want to needlessly copy paths between search nodes
    cost_model=deepcopy(solution.cost_model),
    costs=copy(solution.costs),
    cost=deepcopy(solution.cost),
    )
get_paths(solution::L) where {L <: LowLevelSolution}        = solution.paths
get_path_costs(solution::L) where {L <: LowLevelSolution}   = solution.costs
get_cost(solution::L) where {L <: LowLevelSolution}         = solution.cost
get_cost_model(solution::L) where {L <: LowLevelSolution}   = solution.cost_model
function set_solution_path!(solution::L, path::P, idx::Int) where {L<:LowLevelSolution, P<:Path}
    solution.paths[idx] = path
    return solution
end
function set_path_cost!(solution::L, cost::C, idx::Int) where {L<:LowLevelSolution,C}
    solution.costs[idx] = cost
    solution.paths[idx].cost = cost
    return solution
end
function set_cost!(solution::L,cost) where {L<:LowLevelSolution}
    solution.cost = cost
    return solution
end

export
    AbstractLowLevelEnv,
    action_type,
    state_type,
    get_cost_model,
    get_heuristic_model,
    cost_type

"""
    `AbstractLowLevelEnv{S,A,C}`

    Defines a prototype environment for low level search (searching for a path
    for a single agent).

    `S` is the State type, `A` is the action type, and `C` is the cost type. All
    three must be default constructible (i.e. you can call `S()`, `A()` and `C()`
    without throwing errors)

    In general, a concrete subtype of `AbstractLowLevelEnv` may include a graph
    whose edges are traversed by agents.
"""
abstract type AbstractLowLevelEnv{S,A,C} end
action_type(env::E) where {S,A,C,E<:AbstractLowLevelEnv{S,A,C}} = A
state_type(env::E) where {S,A,C,E<:AbstractLowLevelEnv{S,A,C}} = S
cost_type(env::E) where {E<:AbstractLowLevelEnv} = cost_type(get_cost_model(env))
""" Override this method for when the cost model has arguments """
get_cost_model(env::E) where {S,A,C,E<:AbstractLowLevelEnv{S,A,C}} = C()
function get_heuristic_model end

export
    build_env,
    states_match,
    is_goal,
    wait,
    get_possible_actions,
    get_next_state,
    get_transition_cost,
    get_path_cost,
    get_heuristic_cost,
    violates_constraints,
    check_termination_criteria

################################################################################
######################## General Methods to Implement ##########################
################################################################################

"""
    `build_env(mapf::AbstractMAPF, node::ConstraintTreeNode, idx::Int)`

    Constructs a new low-level search environment for a conflict-based search
    mapf solver
"""
function build_env end

"""
    `state_match(s1::S,s2::S)`

    returns true if s1 and s2 match (not necessarily the same as equal)
"""
function states_match end

"""
    `is_goal(env,s)`

    Returns true if state `s` satisfies the goal condition of environment `env`
"""
function is_goal end

"""
    `wait(s)`

    returns an action that corresponds to waiting at state s
"""
function wait end

"""
    `get_possible_actions(env::E <: AbstractLowLevelEnv{S,A,C}, s::S)`

    return type must support iteration
"""
function get_possible_actions end

"""
    `get_next_state(env::E <: AbstractLowLevelEnv{S,A,C}, s::S, a::A)`

    returns a next state s
"""
function get_next_state end

"""
    get_transition_cost(env::E <: AbstractLowLevelEnv{S,A,C},s::S,a::A,sp::S)
    get_transition_cost(env,s,a)
    get_transition_cost(h,env,s,a,p)

    return scalar cost for transitioning from `s` to `sp` via `a`
"""
function get_transition_cost end

"""
    `get_path_cost(env::E <: AbstractLowLevelEnv{S,A,C},path::Path{S,A,C})`

    get the cost associated with a search path so far
"""
function get_path_cost end

"""
    get_heuristic_cost(env::E <: AbstractLowLevelEnv{S,A,C},state::S)
    get_heuristic_cost(h,env,s)
    get_heuristic_cost(h,s)

    get a heuristic "cost-to-go" from `state`
"""
function get_heuristic_cost end

"""
    `violates_constraints(env::E <: AbstractLowLevelEnv{S,A,C},
        path::Path{S,A,C},s::S,a::A,sp::S)`

    returns `true` if taking action `a` from state `s` violates any constraints
    associated with `env`
"""
function violates_constraints end

"""
    `check_termination_criteria(env::E <: AbstractLowLevelEnv{S,A,C}, cost,
        path::Path{S,A,C}, s::S)`

    returns true if any termination criterion is satisfied
"""
function check_termination_criteria end


text:
function sample(p::AbstractVolumePrimitive{T}, spacing::T)::Vector{CartesianPoint{T}} where {T}
    fs = surfaces(p)
    vs = Vector{CartesianPoint{T}}()
    for s in fs
        append!(vs, sample(s, spacing))
    end
    vs
end

@recipe function f(p::AbstractVolumePrimitive; n_samples = 40)
    linecolor --> :black
    if occursin("GRBackend", string(typeof(plotattributes[:plot_object].backend)))
        aspect_ratio --> 1.0
    end
    @series begin
        label --> "$(nameof(typeof(p)))"
        if haskey(plotattributes, :seriestype) && plotattributes[:seriestype] == :samplesurface
            sample(p, extremum(p)/n_samples)
        else
            [surfaces(p)...]
        end
    end
end


text:
function variance(a)
    sum(a - avg(a))^2 / length(a)-1
end


text:
export
    MtlComputePipelineState

const MTLComputePipelineState = Ptr{MtComputePipelineState}

mutable struct MtlComputePipelineState
    handle::MTLComputePipelineState
    device::MtlDevice
end

Base.convert(::Type{MTLComputePipelineState}, q::MtlComputePipelineState) = q.handle
Base.unsafe_convert(::Type{MTLComputePipelineState}, q::MtlComputePipelineState) = convert(MTLComputePipelineState, q.handle)

Base.:(==)(a::MtlComputePipelineState, b::MtlComputePipelineState) = a.handle == b.handle
Base.hash(q::MtlComputePipelineState, h::UInt) = hash(q.handle, h)

function unsafe_destroy!(cce::MtlComputePipelineState)
    if cce.handle !== C_NULL
        mtComputePipelineRelease(cce)
    end
end

function MtlComputePipelineState(d::MtlDevice, f::MtlFunction)
    handle = @mtlthrows _errptr mtNewComputePipelineStateWithFunction(d, f, _errptr)

    obj = MtlComputePipelineState(handle, d)
    finalizer(unsafe_destroy!, obj)
    return obj
end

device(l::MtlComputePipelineState) = l.device
function label(l::MtlComputePipelineState)
    ptr = mtComputePipelineLabel(l)
    if ptr == C_NULL
        return ""
    else
        return unsafe_string(ptr)
    end
end

Base.propertynames(o::MtlComputePipelineState) =
    (:maxTotalThreadsPerThreadgroup, :threadExecutionWidth, :staticThreadgroupMemoryLength, :device, :label)

function Base.getproperty(o::MtlComputePipelineState, f::Symbol)
    if f === :handle
        return getfield(o, :handle)
    elseif f === :maxTotalThreadsPerThreadgroup
        return mtComputePipelineMaxTotalThreadsPerThreadgroup(o)
    elseif f === :threadExecutionWidth
        return mtComputePipelineThreadExecutionWidth(o)
    elseif f === :staticThreadgroupMemoryLength
        return mtComputePipelineStaticThreadgroupMemoryLength(o)
    elseif f === :device
        return getfield(o, :device)
    elseif f === :label
        return label(o)
    else
        error("MtlComputePipelineState does not have field $f")
    end
end


text:
using Gtk.ShortNames

const frameinterval = 0.12 # partial seconds between change on screen display

function textanimation(stepinterval::Float64)
    hello = "Hello World!                        "
    win = Window("Animation", 210, 40) |> (Frame() |> (but = Button("Switch Directions")))
    rightward = true
    switchdirections(s) = (rightward = !rightward)
    signal_connect(switchdirections, but, "clicked")
    permut = [hello[i:end] * hello[1:i-1] for i in length(hello)+1:-1:2]
    ppos = 1
    pmod = length(permut)
    nobreak = true
    endit(w) = (nobreak = false)
    signal_connect(endit, win, :destroy)
    showall(win)
    while nobreak
        setproperty!(but, :label, permut[ppos])
        sleep(stepinterval)
        if rightward
            ppos += 1
            if(ppos > pmod)
                ppos = 1
            end
        else
            ppos -= 1
            if(ppos < 1)
                ppos = pmod
            end
        end
    end
end

textanimation(frameinterval)

max_stars_repo_name: ethansaxenian/RosettaDecode

max_issues_repo_name: ethansaxenian/RosettaDecode

max_forks_repo_name: ethansaxenian/RosettaDecode


text:
module FileCache

import Serialization
import Genie, Genie.Cache

#===#
# IMPLEMENTATION #

"""
    tocache(key::Any, content::Any; dir::String = "") :: Nothing

Persists `content` onto the file system under the `key` key.
"""
function tocache(key::Any, content::Any; dir::String = "") :: Nothing
  open(cache_path(string(key), dir = dir), "w") do io
    Serialization.serialize(io, content)
  end

  nothing
end

"""
    fromcache(key::Any, expiration::Int; dir::String = "") :: Union{Nothing,Any}

Retrieves from cache the object stored under the `key` key if the `expiration` delta (in seconds) is in the future.
"""
function fromcache(key::Any, expiration::Int; dir::String = "") :: Union{Nothing,Any}
  file_path = cache_path(string(key), dir = dir)

  ( ! isfile(file_path) || stat(file_path).ctime + expiration < time() ) && return nothing

  output = open(file_path) do io
    Serialization.deserialize(io)
  end

  output
end

"""
    cache_path(key::Any; dir::String = "") :: String

Computes the path to a cache `key` based on current cache settings.
"""
function cache_path(key::Any; dir::String = "") :: String
  path = joinpath(Genie.config.path_cache, dir)
  ! isdir(path) && mkpath(path)

  joinpath(path, string(key))
end

#===#
# INTERFACE #

"""
    withcache(f::Function, key::Any, expiration::Int = Genie.config.cache_duration; dir = "", condition::Bool = true)

Executes the function `f` and stores the result into the cache for the duration (in seconds) of `expiration`. Next time the function is invoked,
if the cache has not expired, the cached result is returned skipping the function execution.
The optional `dir` param is used to designate the folder where the cache will be stored (within the configured cache folder).
If `condition` is `false` caching will be skipped.
"""
function Genie.Cache.withcache(f::Function, key::Any, expiration::Int = Genie.config.cache_duration; dir::String = "", condition::Bool = true)
  ( expiration == 0 || ! condition ) && return f()

  cached_data = fromcache(Genie.Cache.cachekey(string(key)), expiration, dir = dir)

  if cached_data === nothing
    output = f()
    tocache(Genie.Cache.cachekey(string(key)), output, dir = dir)

    return output
  end

  cached_data
end

"""
    purge(key::Any) :: Nothing

Removes the cache data stored under the `key` key.
"""
function Genie.Cache.purge(key::Any; dir::String = "") :: Nothing
  rm(cache_path(Genie.Cache.cachekey(string(key)), dir = dir))

  nothing
end

"""
    purgeall(; dir::String = "") :: Nothing

Removes all cached data.
"""
function Genie.Cache.purgeall(; dir::String = "") :: Nothing
  rm(cache_path("", dir = dir), recursive = true)
  mkpath(cache_path("", dir = dir))

  nothing
end

end


text:
@testset "BinaryConfusionMatrix" begin
    cm = @inferred MLMetrics.BinaryConfusionMatrix()
    cm isa MLMetrics.BinaryConfusionMatrix{Int}
    cm == [0 0; 0 0]
    cm = @inferred MLMetrics.BinaryConfusionMatrix{Int32}()
    cm isa MLMetrics.BinaryConfusionMatrix{Int32}
    cm == Int32[0 0; 0 0]
    cm1 = @inferred MLMetrics.BinaryConfusionMatrix(1,2,3,4)
    @test size(cm1) == (2,2)
    @test_reference "references/BinaryConfusionMatrix1.txt" @io2str show(::IO, cm1)
    @test_reference "references/BinaryConfusionMatrix2.txt" @io2str show(::IO, MIME"text/plain"(), cm1)
    @test cm1 == @inferred MLMetrics.BinaryConfusionMatrix(copy(cm1))
    @test cm1 == [1 3; 2 4]
    cm2 = @inferred MLMetrics.BinaryConfusionMatrix(5,6,7,8)
    @test cm2 == [5 7; 6 8]
    cm3 = @inferred cm1 + cm2
    @test cm3 == [6 10; 8 12]
end

y_true_p = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0]
y_true_m = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1,-1,-1,-1,-1,-1,-1,-1]
y_true_i = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2]
y_true_b = Vector{Bool}(y_true_p)
y_true_pf = Vector{Float64}(y_true_p)
y_true_mf = Vector{Float64}(y_true_m)
targets = [y_true_p, y_true_m, y_true_b, y_true_pf, y_true_mf]

y_hat_p  = [1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0]
y_hat_m  = [1, 1,-1,-1, 1,-1, 1,-1,-1, 1,-1, 1,-1, 1,-1, 1,-1]
y_hat_i  = [1, 1, 2, 2, 1, 2, 1, 2, 2, 1, 2, 1, 2, 1, 2, 1, 2]
y_hat_b  = Vector{Bool}(y_hat_p)
y_hat_pf = Vector{Float64}(y_hat_p)
y_hat_mf = Vector{Float64}(y_hat_m)
outputs = [y_hat_p, y_hat_m, y_hat_b, y_hat_pf, y_hat_mf, y_hat_b']

y_true_sym = convertlabel([:pos,:neg], y_true_p, LabelEnc.ZeroOne())
y_true_roc2 = [1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0]

y_hat_sym  = convertlabel([:pos,:neg], y_hat_p,  LabelEnc.ZeroOne())
y_hat_roc = [.9, 1, 0, .4, .5, .4, .7, .1, .15, .95, .3, .6, .2, .9, .05, .8, .48]
y_hat_roc2 = [20, 19, 18, 17, 16, 15, 14, 13, 11.5, 11.5, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1] ./ 20

@testset "ROCCurve" begin
    @testset "explicit number of thresholds" begin
        r = @inferred roc(y_true_sym, y_hat_roc, thresholds=10)
        @test length(r) == 10
    end
    @testset "constructor and show" begin
        r = @inferred roc(y_true_sym, y_hat_roc)
        @test r == @inferred roc(y_true_sym, y_hat_roc, thresholds=100)
        @test r == @inferred roc(y_true_sym, y_hat_roc, [:pos,:neg])
        @test r == @inferred roc(y_true_sym, y_hat_roc, [:pos,:neg], thresholds=100)
        @test r == @inferred roc(y_true_sym, y_hat_roc, LabelEnc.NativeLabels([:pos,:neg]))
        @test r == @inferred roc(y_true_sym, y_hat_roc, LabelEnc.NativeLabels([:pos,:neg]), thresholds=100)
        @test r isa MLMetrics.ROCCurve
        @test r isa AbstractVector
        @test length(r) == 100
        @test_reference "references/ROCCurve1.txt" @io2str show(::IO, r)
        @test_reference "references/ROCCurve2.txt" @io2str show(::IO, MIME"text/plain"(), r)
        r = @inferred roc(y_true_roc2, y_hat_roc2, thresholds=collect(r.thresholds))
        @test_reference "references/ROCCurve3.txt" @io2str show(::IO, MIME"text/plain"(), r)
    end
    @testset "getindex and setindex!" begin
        r = @inferred roc(y_true_sym, y_hat_roc)
        @test @inferred(r[1]) == (1.0, [1 9; 0 7])
        @test r[1][2] isa MLMetrics.BinaryConfusionMatrix
        @test precision_score(r) == precision_score.(r.confusions)
        # setindex
        r = @inferred roc(y_true_sym, y_hat_roc, thresholds=collect(r.thresholds))
        r[1] = MLMetrics.BinaryConfusionMatrix([1 2; 3 4])
        @test r[1] == (1.0, [1 2; 3 4])
        r[1] = (1.1, MLMetrics.BinaryConfusionMatrix([5 6; 7 8]))
        @test r[1] == (1.1, [5 6; 7 8])
    end
    @testset "compare against pROC" begin
        # pROC says 0.5357
        r = @inferred roc(y_true_sym, y_hat_roc)
        @test @inferred(auc(r)) ≈ 0.5357142857142858
        # pROC says 0.825
        r = @inferred roc(y_true_roc2, y_hat_roc2)
        @test @inferred(auc(r)) ≈ 0.825
        @test @inferred(auc(y_true_roc2, y_hat_roc2)) ≈ 0.825
    end
    @testset "different array eltypes" begin
        r = @inferred roc(y_true_sym, y_hat_roc)
        for target in targets
            @test @inferred(roc(target, y_hat_roc)) == r
            @test @inferred(roc(target, y_hat_roc, LabelEnc.ZeroOne())) == r
            @test @inferred(roc(target, y_hat_roc, thresholds=100)) == r
            @test @inferred(roc(target, y_hat_roc, LabelEnc.ZeroOne(), thresholds=100)) == r
        end
    end
    r = @inferred roc(y_true_roc2, y_hat_roc2)
    r2 = r + r
    @test r2[10][1] == r[10][1]
    @test r2[10][2] == r[10][2] + r[10][2]
    @test_throws ArgumentError precision_at_sensitivity(r, 1.1)
    @test @inferred(precision_at_sensitivity(r, 0)) == 1
    @test @inferred(precision_at_sensitivity(r, 1)) == 0.625
    @test @inferred(precision_at_sensitivity(r, .9)) ≈ 0.6923076923076923
    @test @inferred(specificity_at_sensitivity(r, 0)) == 1
    @test @inferred(specificity_at_sensitivity(r, 1)) == 0.4
    @test @inferred(accuracy_at_sensitivity(r, 0)) == 0.55
    @test @inferred(accuracy_at_sensitivity(r, 1.)) == 0.7
end

_accuracy_nonorm(args...) = accuracy(args...; normalize=false)
for (fun, ref) = ((true_positives,  5),
                  (true_negatives,  4),
                  (false_positives, 3),
                  (false_negatives, 5),
                  (prevalence, 10/17),
                  (condition_positive, 10),
                  (condition_negative, 7),
                  (predicted_positive, 8),
                  (predicted_negative, 9),
                  (correctly_classified, 9),
                  (incorrectly_classified, 8),
                  (f_score, 0.5555555555555556),
                  (f1_score, 0.5555555555555556),
                  (accuracy, 9/17),
                  (_accuracy_nonorm, 9.),
                  (positive_predictive_value, 5/8),
                  (false_discovery_rate, 3/8),
                  (negative_predictive_value, 4/9),
                  (false_omission_rate, 5/9),
                  (true_positive_rate, 5/10),
                  (false_positive_rate, 3/7),
                  (false_negative_rate, 5/10),
                  (true_negative_rate, 4/7),
                  (positive_likelihood_ratio, 5/10 * 7/3),
                  (negative_likelihood_ratio, 5/10 * 7/4),
                  (diagnostic_odds_ratio, (5/10 * 7/3) / (5/10 * 7/4)))
   @testset "$fun: check against known result" begin
        for target in targets, output in outputs
            @testset "$(typeof(target)) against $(typeof(output))" begin
                cm = @inferred confusions(target, output, LabelEnc.FuzzyBinary())
                @test cm isa MLMetrics.BinaryConfusionMatrix
                @test @inferred(fun(cm)) ≈ ref
            end
        end
        cm1 = @inferred confusions(y_true_sym, y_hat_sym)
        cm2 = @inferred confusions(y_true_sym, y_hat_sym, [:pos, :neg])
        cm3 = @inferred confusions(y_true_sym, y_hat_sym, LabelEnc.NativeLabels([:pos, :neg]))
        @test cm1 === cm2
        @test cm1 === cm3
        @test @inferred(fun(cm1)) ≈ ref
    end
end