text:
using Flux: @functor
using Flux: softmax, σ

import Flux.hidden, Flux.trainable, Flux.LSTMCell

mutable struct DNCCell{C, C2, T, S, M}
    controller::C
    readvectors::S
    outputlayer::C2
    X::Integer
    Y::Integer
    clipvalue::Union{Real, Nothing}
    memoryaccess::MemoryAccess{M, T, S}
end

DNCCell(controller, in::Int, out::Int, controut::Int, N::Int, W::Int, R::Int, B::Int; clipvalue=nothing, init=Flux.glorot_normal) =
    DNCCell(
        controller,
        zeros(Float32, R*W, B),
        Dense(controut+R*W, out),
        in, out, clipvalue,
        MemoryAccess(controut, N, W, R, B; init=init))

DNCCell(in::Int, out::Int, controut::Int, N::Int, W::Int, R::Int, B::Int; clipvalue=nothing, init=Flux.glorot_normal) =
    DNCCell(
        MyLSTM(B, inputsize(in, R, W), controut; init=init),
        zeros(Float32, R*W, B),
        Dense(controut+R*W, out),
        in, out, clipvalue,
        MemoryAccess(controut, N, W, R, B; init=init))

function (m::DNCCell)(h, x)
    out = clip(m.controller([x;h]), m.clipvalue)
    r = m.memoryaccess(out)
    r = reshape(r, size(r,1)*size(r, 2), size(r, 3))
    return r, clip(m.outputlayer([out;r]), m.clipvalue)
end

hidden(m::DNCCell) = m.readvectors

trainable(m::DNCCell) = (m.controller, m.outputlayer, trainable(m.memoryaccess))
@functor DNCCell

import Base.show
function Base.show(io::IO, l::DNCCell)
    print(io, "DNCCell($(l.X), $(l.Y))")
end

mutable struct MyRecur{T, S}
  cell::T
  init::S
  state::S
end

MyRecur(m, h = hidden(m)) = MyRecur(m, h, h)

function (m::MyRecur)(xs...)
  h, y = m.cell(m.state, xs...)
  m.state = h
  return y
end

@functor MyRecur cell, init

Base.show(io::IO, m::MyRecur) = print(io, "MyRecur(", m.cell, ")")

"""
    Dnc(controller, in::Integer, out::Integer, N::Integer, W::Integer, R::Integer)

Initialise a Differentiable Neural Computer with memory size (N, W) and R read heads.

"""
Dnc(a...; ka...) = MyRecur(DNCCell(a...; ka...))

function MyLSTM(batchsize, a...; ka...)
    cell = LSTMCell(a...; ka...)
    h = reshape.(repeat.(hidden(cell), batchsize), :, batchsize)
    MyRecur(cell, h)
end


text:
using CouenneNL
using JuMP
using Test

@testset "CouenneNL" begin
    @testset "installation" begin
        @test isfile(CouenneNL.couenne_executable)
        run(`$(CouenneNL.couenne_executable) --version`)
    end

    @testset "linear models" begin
        @testset "one variable" begin
            m = Model(solver=CouenneNLSolver())
            @variable m -1 <= x <= 1
            @objective m Min x
            solve(m)
            @test getvalue(x) ≈ -1
        end

        @testset "two variables" begin
            m = Model(solver=CouenneNLSolver())
            @variable m -1 <= x <= 1
            @variable m -1 <= y <= 1
            @constraint m x >= -0.5
            @constraint m y >= 0.1
            @objective m Min x + y
            solve(m)
            @test getvalue(x) ≈ -0.5
            @test getvalue(y) ≈ 0.1
        end
    end

    @testset "nonlinear models" begin
        @testset "convex quadratic constraint" begin
            m = Model(solver=CouenneNLSolver())
            @variable m -1 <= x <= 1
            @variable m -1 <= y <= 1
            @constraint m x >= -0.5
            @constraint m y >= 0.1
            @variable m z
            @NLconstraint m (x - -0.5)^2 + (y - -0.2)^2 <= z
            @objective m Min z
            solve(m)
            @test getvalue(x) ≈ -0.5 atol=1e-4
            @test getvalue(y) ≈ 0.1 atol=1e-4
        end

        @testset "nonconvex constraint" begin
            m = Model(solver=CouenneNLSolver())
            @variable m -2 <= x <= 2
            @variable m -2 <= y <= 2
            @NLconstraint m x^2 + y^2 == 1
            @NLobjective m Min (x - 0.1)^2 + (y - 0.1)^2
            solve(m)
            @test getvalue(x) ≈ 1/√2
            @test getvalue(y) ≈ 1/√2
        end
    end
end

ext: jl


text:
using Images, TestImages, Color, FixedPointNumbers
using Base.Test

img = testimage("autumn_leaves")
@assert colorspace(img) == "RGBA"
@assert ndims(img) == 2
@assert colordim(img) == 0
@assert eltype(img) == AlphaColorValue{RGB{Ufixed16}, Ufixed16}

img = testimage("cameraman")
@assert colorspace(img) == "Gray"
@assert ndims(img) == 2
@assert colordim(img) == 0
@assert eltype(img) == Gray{Ufixed8}

img = testimage("earth_apollo17")
@assert colorspace(img) == "RGB4"
@assert ndims(img) == 2
@assert colordim(img) == 0
@assert eltype(img) == RGB4{Ufixed8}

img = testimage("fabio")
@assert colorspace(img) == "Gray"
@assert ndims(img) == 2
@assert colordim(img) == 0
@assert eltype(img) == Gray{Ufixed8}

img = testimage("house")
@assert colorspace(img) == "GrayAlpha"
@assert ndims(img) == 2
@assert colordim(img) == 0
@assert eltype(img) == AlphaColorValue{Gray{Ufixed8}, Ufixed8}

img = testimage("jetplane")
@assert colorspace(img) == "GrayAlpha"
@assert ndims(img) == 2
@assert colordim(img) == 0
@assert eltype(img) == AlphaColorValue{Gray{Ufixed8}, Ufixed8}

img = testimage("lighthouse")
@assert colorspace(img) == "RGB4"
@assert ndims(img) == 2
@assert colordim(img) == 0
@assert eltype(img) == RGB4{Ufixed8}

img = testimage("mandrill")
@assert colorspace(img) == "RGB"
@assert ndims(img) == 2
@assert colordim(img) == 0
@assert eltype(img) == RGB{Ufixed8}

img = testimage("moonsurface")
@assert colorspace(img) == "Gray"
@assert ndims(img) == 2
@assert colordim(img) == 0
@assert eltype(img) == Gray{Ufixed8}

img = testimage("mountainstream")
@assert colorspace(img) == "RGB4"
@assert ndims(img) == 2
@assert colordim(img) == 0
@assert eltype(img) == RGB4{Ufixed8}


text:
const snapsvgjs = joinpath(@__DIR__, "..", "deps", "snap.svg-min.js")
const viewerjs = joinpath(@__DIR__, "viewer.js")

function escape_html(str::AbstractString)
    s = replace(str, '<' => "<")
    s = replace(s, '>' => ">")
    s = replace(s, '&' => "&")
    s
end

function escape_script(js::AbstractString)
    s = replace(js, '\x0b' => "\\x0b")
    s = replace(s,  '\x0c' => "\\x0c")
    replace(s, "]]" => "] ]")
end

function modify_amd(js::AbstractString)
    replace(js, "define([" => "define('ProfileSVG/snap.svg', [")
end

function simplify(x::Real, digits=2)
    ra, rd = round(x), round(x, digits=digits)
    ra == rd ? Int(ra) : rd
end

isdarkcolor(c::Color) = gray(Gray(c)) < 0.65

function write_svgdeclaration(io::IO)
    println(io, """<?xml version="1.0" standalone="no"?>""")
    println(io, """<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">""")
end

function write_svgheader(io::IO, fig_id, width, height,
                         bgcolor, fontcolor, frameopacity,
                         font, fontsize, notext, xstep, timeunit, delay)
    w = simplify(width)
    h = simplify(height)
    caption_size = simplify(fontsize * 1.4)
    x_cap = simplify(width * 0.5)
    y_cap = simplify(fontsize * 2)
    x_msg = simplify(fontsize * 0.8)
    y_msg = height - caption_size
    textcolor = isempty(fontcolor) ? "black" : fontcolor
    caption_color = textcolor
    if isempty(fontcolor) && startswith(bgcolor, "#") && isdarkcolor(parse(Gray, bgcolor))
        caption_color = "white"
    end
    bg_fill = isempty(bgcolor) ? "url(#$fig_id-background)" : bgcolor
    details_bg_fill = isempty(bgcolor) ? "white" : bgcolor
    text_stroke_opacity = notext ? 0.0 : 0.35
    op = simplify(frameopacity)
    hover_opacity = simplify(op > 0.75 ? op - 0.25 : op + 0.25)
    print(io,
        """
        <svg version="1.1" width="$w" height="$h" viewBox="0 0 $w $h"
             xmlns="http://www.w3.org/2000/svg" id="$fig_id">
        <defs>
        """)
    isempty(bgcolor) && print(io,
        """
            <linearGradient id="$fig_id-background" y1="0" y2="1" x1="0" x2="0">
                <stop stop-color="#eeeeee" offset="5%" />
                <stop stop-color="#eeeeb0" offset="95%" />
            </linearGradient>
        """)
    print(io,
        """
            <clipPath id="$fig_id-clip">
                <rect x="0" y="0" width="$w" height="$h"/>
            </clipPath>
        </defs>
        <style type="text/css">
            #$fig_id text {
                pointer-events: none;
                font-family: $font;
                font-size: $(simplify(fontsize))px;
                fill: $textcolor;
            }
            text#$fig_id-caption {
                font-size: $(caption_size)px;
                fill: $caption_color;
                text-anchor: middle;
            }
            #$fig_id-bg {
                fill: $bg_fill;
            }
            #$fig_id-viewport rect, #$fig_id-viewport path {
                vector-effect: non-scaling-stroke;
                fill-opacity: $op;
            }
            #$fig_id-viewport text {
                stroke: $textcolor;
                stroke-width: 0;
                stroke-opacity: $text_stroke_opacity;
            }
            #$fig_id-viewport rect:hover, #$fig_id-viewport path:hover {
                fill-opacity: $hover_opacity;
                stroke: $textcolor;
                stroke-width: 0.5;
            }
            #$fig_id-viewport + rect {
                fill: $details_bg_fill;
                opacity: 0.8;
            }
            text#$fig_id-details{
                fill: $caption_color;
            }
        """)
    isempty(fontcolor) && print(io,
        """
            #$fig_id-viewport text.w {
                fill: white;
                stroke: white;
            }
        """)
    dataxstep = timeunit !== :none ? " data-xstep=\"$xstep\"" : ""
    datatunit = timeunit !== :none ? " data-tunit=\"$timeunit\"" : ""
    datadelay = timeunit !== :none ? " data-delay=\"$delay\"" : ""
    print(io,
        """
        </style>
        <g id="$fig_id-frame" clip-path="url(#$fig_id-clip)">
        <rect id="$fig_id-bg" x="0" y="0" width="$w" height="$h"/>
        <text id="$fig_id-caption" x="$x_cap" y="$y_cap">Profile results</text>
        <g id="$fig_id-viewport" transform="scale(1)"$dataxstep$datatunit$datadelay>
        """)
end

function write_svgflamerect(io::IO, xstart, ystart, width, height, roundradius,
                            shortinfo, dirinfo, color, bw)
    x = simplify(xstart)
    y = simplify(ystart)
    yt = simplify(y + height * 0.75)
    w = simplify(simplify(width + xstart) - x)
    h = simplify(simplify(height + ystart) - y)
    r = simplify(roundradius)
    sinfo = escape_html(shortinfo)
    dinfo = escape_html(dirinfo)
    classw = (bw & isdarkcolor(color)) ? " class=\"w\"" : ""
    if r > zero(r)
        print(io, """<rect x="$x" y="$y" width="$w" height="$h" rx="$r" """)
    else
        print(io, """<path d="M$x,$(y)v$(h)h$(w)v-$(h)z" """)
    end
    println(io, """fill="#$(hex(color))" data-dinfo="$dinfo"/>""")
    println(io, """<text x="$x" dx="4" y="$yt"$classw>$sinfo</text>""")
end

function write_svgfooter(io::IO, fig_id)
    println(io, "</g></g>")
    println(io, "<script><![CDATA[")
    println(io, modify_amd(escape_script(read(snapsvgjs, String))))
    println(io, "]]></script>")
    println(io, "<script><![CDATA[")
    println(io, escape_script(read(viewerjs, String)))
    println(io, "]]></script>")
    print(io,
        """
        <script><![CDATA[
        if (typeof require === 'function' && define.amd) {
            require(['ProfileSVG'], function (ProfileSVG) {
                ProfileSVG.initialize("$fig_id");
            });
        } else {
            ProfileSVG.initialize("$fig_id");
        }
        ]]></script>
        """)
    println(io, "</svg>")
end


text:
"""
Function creates an Vector{String} of all files matching pattern. Mirrors list.files() from R. Prints whole path
"""
function lf(;path::String=pwd(), pattern::Regex=nothing, sub::Bool=true)

    files = Vector{String}()

    if sub

        for cpath = walkdir(path)

            x = joinpath.(cpath[1], cpath[3])

            if !isempty(x)
                idx = occursin.(pattern, cpath[3])
                append!(files, x[idx] )
            end

        end

    else

        files = filter(x->occursin(pattern,x), first(walkdir(path))[3])

    end

    return(files)

end


text:
import LinearAlgebra.Symmetric

"""
    path_to_root(tree, id)
    path_to_root(node)

Return the series of ancestors from a `node` to the root of the tree.
"""
function path_to_root(c::RNode)
    E = RNode[]
    while indegree(c) > 0
        p = parent(c)
        push!(E, p)
        c = p
    end

    return E
end
# path_to_root(tree, id) = get_id.(path_to_root(get_node(tree, id)))

"""
    anydifferent(coll, n = length(coll))

Check if at least one element in a `coll`ection is different from the others.

The length `n` of the collection can be given if it is known.
"""
function anydifferent(coll, n = length(coll))
    if n < 2
        msg = "cannot check equality with fewer than 2 elements in the collection"
        throw(ErrorException(msg))
    end
    n == 2 && return coll[1] ≠ coll[2]

    a = first(coll)
    @inbounds for i in 2:n
         a ≠ coll[i] && return true
         a = coll[i]
    end

    return false
end

function _findmrca_method1(tree, V)
    n = length(V)    # Will throw exception in `anydifferent` if n < 2
    E = path_to_root.(V)

    # anc = nothing    # Will return `nothing` if the nodes do not belong to the same tree
    anc = getroot(tree)    # Will return `nothing` if the nodes do not belong to the same tree
    for e in zip(Iterators.reverse.(E)...)
        anydifferent(e, n) && break
        anc = first(e)
    end

    # if anc ∈ V
    #     msg ="one of the nodes is the most recent common ancestor of the others"
    #     throw(ErrorException(msg))
    # end

    return anc
end

function _findmrca_method2(p, anc, list, nf, nt)   # nf = number of targets found, nt = number of targets
    nf += p ∈ list ? 1 : 0
    nfc = 0   # Number of targets found under child c
    for c in children(p)
        anc, list, nfc, nt = _findmrca_method2(c, anc, list, 0, nt)
        nf += nfc
        if nf == nt
            anc = isnothing(anc) ? p : anc   #! No longer needed depending on intended behaviour
            break
        end
    end

    return (anc, list, nf, nt)
end

"""
    findmrca(tree, list)

Return the most recent common ancestor of a `list` of nodes in a `tree`.
If a list of node IDs is given, the ID of the most recent common ancestor node is returned.
Return nothing if there is no common ancestor of all the nodes, i.e. when all the nodes do not belong to the same tree.

Warning: Having nodes repeated in the list can produce nonsensical results.
"""
function findmrca(tree, list::Vector{<:AbstractRNode}; method = :auto) where T
    #! mrca of a node and its parent is the parent of the paren!
    #TODO: Decide behaviour for cases where there are nodes in the list that are ancestral to others.
    #TODO: Run more benchmar to find a better threshold for using method 2
    n = length(list)

    if method == :auto
        if n < 64
            # Faster for small n, deep MRCA
            _findmrca_method1(tree, list)
        else
            # Faster for large n, shallow MRCA
            _findmrca_method2(getroot(tree), getroot(tree), list, 0, n)[1]
        end
    elseif method == 1
        return _findmrca_method1(tree, list)
    elseif method == 2
        return _findmrca_method2(getroot(tree), getroot(tree), list, 0, n)[1]
    else
        throw(ErrorException("invalid method \"$(method)\""))
    end
end

function findmrca(tree, list::Vector{<:Real}; method = :auto)
    anc = findmrca(tree, tree.nodes[list], method = method)

    return isnothing(anc) ? 0 : getid(anc)
end

function _fill_mrca_matrix!(p, m, n)
    foundp = falses(n)   # Descendants found under this node (start with none)

    #= The following line means that the MRCA of `p` and any of its children is `p`. Moving the
    line under the next loop makes it so the MRCA of `p` and any of its children is `nothing`.
    This behaviour must be consistent with get_mrca. =#
    @inbounds foundp[getid(p)] = true  # Set node `p` as found

    @inbounds for c in children(p)
        foundc = _fill_mrca_matrix!(c, m, n)
        m[foundp, foundc] .= getid(p)
        foundp .|= foundc
    end

    return foundp
end

"""
    mrca_matrix(tree)

Return a symmetric square matrix where each cell [i, j] contains the ID of the most recent common ancestor of node #`i` and node#`j`.
The diagonal of the matrix contains zeros.
"""
function mrca_matrix(tree)
    #TODO: Determine how to deal with the root
    n = nnode(tree)
    m = zeros(Int, n, n)
    _fill_mrca_matrix!(getroot(tree), m, n)

    return Symmetric(m, :U)
end

function _node_depths(p, D)
    d = 0
    for c in children(p)
        dc = _node_depths(c, D)
        d = max(d, dc)
    end

    D[get_id(p)] = d

    return d + 1
end

"""
    node_depths(tree)

Return a vector with the depth of all the nodes in a `tree`. The depth of a node is the length of the longest path between it and an outer node. The depth of outer nodes is zero. The root of the tree should have the greatest depth.
"""
function node_depths(tree)
    D = zeros(Int, nnode(tree))
    _node_depths(getroot(tree), D)

    return D
end

max_stars_repo_name: eascarrunz/RootedTrees

max_issues_repo_name: eascarrunz/RootedTrees

max_forks_repo_name: eascarrunz/RootedTrees


text:
# This file was generated by the Julia Swagger Code Generator
# Do not modify this file directly. Modify the swagger specification instead.

mutable struct ConnectionMonitorDestination <: SwaggerModel
    resourceId::Any # spec type: Union{ Nothing, String } # spec name: resourceId
    address::Any # spec type: Union{ Nothing, String } # spec name: address
    port::Any # spec type: Union{ Nothing, Int32 } # spec name: port

    function ConnectionMonitorDestination(;resourceId=nothing, address=nothing, port=nothing)
        o = new()
        validate_property(ConnectionMonitorDestination, Symbol("resourceId"), resourceId)
        setfield!(o, Symbol("resourceId"), resourceId)
        validate_property(ConnectionMonitorDestination, Symbol("address"), address)
        setfield!(o, Symbol("address"), address)
        validate_property(ConnectionMonitorDestination, Symbol("port"), port)
        setfield!(o, Symbol("port"), port)
        o
    end
end # type ConnectionMonitorDestination

const _property_map_ConnectionMonitorDestination = Dict{Symbol,Symbol}(Symbol("resourceId")=>Symbol("resourceId"), Symbol("address")=>Symbol("address"), Symbol("port")=>Symbol("port"))
const _property_types_ConnectionMonitorDestination = Dict{Symbol,String}(Symbol("resourceId")=>"String", Symbol("address")=>"String", Symbol("port")=>"Int32")
Base.propertynames(::Type{ ConnectionMonitorDestination }) = collect(keys(_property_map_ConnectionMonitorDestination))
Swagger.property_type(::Type{ ConnectionMonitorDestination }, name::Symbol) = Union{Nothing,eval(Base.Meta.parse(_property_types_ConnectionMonitorDestination[name]))}
Swagger.field_name(::Type{ ConnectionMonitorDestination }, property_name::Symbol) =  _property_map_ConnectionMonitorDestination[property_name]

function check_required(o::ConnectionMonitorDestination)
    true
end

function validate_property(::Type{ ConnectionMonitorDestination }, name::Symbol, val)
end


text:
using Test
using ImplicitGraphs

G = iGrid()
@test G[(1, 1), (1, 2)]
@test length(G[(0, 0)]) == 4
@test dist(G, (0, 0), (1, 1)) == 2

G = iCycle(10)
@test has(G, 1, 10)

@test length(find_path(G, 1, 5)) == 5

G = iCycle(10, false)
@test !has(G, 1, 10)

G = iKnight()
@test deg(G, (0, 0)) == 8

G = iCube(4)
@test dist(G, "0000", "1111") == 4

G = iShift([1, 2, 3], 5)
@test deg(G, (1, 1, 1, 1, 1)) == 3

G = iKnight()
s = (5, 5)
t = (0, 0)
score(vw) = sum(abs.(vw))
P = guided_path_finder(G, s, t, score = score, depth = 2)
@test length(P) > 0


text:
"""
    calculate_eest(M::AbstractManifold, utilde, uprev, u, abstol, reltol, internalnorm, t)

Estimate error of a solution of an ODE on manifold `M`.

# Arguments

- `utilde` -- point on `M` for error estimation,
- `uprev` -- point from before the current step,
- `u` -- point after the current step`,
- `abstol` - abolute tolerance,
- `reltol` - relative tolerance,
- `internalnorm` -- copied `internalnorm` from the integrator,
- `t` -- time at which the error is estimated.
"""
function calculate_eest(
    M::AbstractManifold,
    utilde,
    uprev,
    u,
    abstol,
    reltol,
    internalnorm,
    t,
)
    reltol_thing = max(reltol_norm(M, u), reltol_norm(M, uprev))
    return distance(M, u, utilde) / (abstol + reltol_thing * reltol)
end

"""
    reltol_norm(M::AbstractManifold, u)

Estimate the fraction `d_{min}/eps(number_eltype(u))` where `d_{min}` is the distance
between `u`, a point on `M`, and the nearest distinct point on `M` representable in the
representation of `u`.
"""
function reltol_norm(::AbstractManifold, u)
    return norm(u)
end
function reltol_norm(M::ProductManifold, u::ProductRepr)
    mapped_norms = map((Mi, ui) -> reltol_norm(Mi, ui), M.manifolds, u.parts)
    return mean(mapped_norms)
end


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

module Logging

# For now, simply import most names from Base - we don't want to fully
# stabilize this API for 1.0 so it should officially live here in a stdlib
# package.
#
# See #24490

import Base.CoreLogging:
    LogLevel, BelowMinLevel, Debug, Info, Warn, Error, AboveMaxLevel,
    AbstractLogger,
    NullLogger,
    handle_message, shouldlog, min_enabled_level, catch_exceptions,
    @debug,
    @info,
    @warn,
    @error,
    @logmsg,
    with_logger,
    current_logger,
    global_logger,
    disable_logging,
    SimpleLogger

export
    AbstractLogger,
    LogLevel,
    NullLogger,
    @debug,
    @info,
    @warn,
    @error,
    @logmsg,
    with_logger,
    current_logger,
    global_logger,
    disable_logging,
    SimpleLogger,
    ConsoleLogger

include("ConsoleLogger.jl")

# The following are also part of the public API, but not exported:
#
# 1. Log levels:
#   BelowMinLevel, Debug, Info, Warn, Error, AboveMaxLevel,
#
# 2. AbstractLogger message related functions:
#  handle_message, shouldlog, min_enabled_level, catch_exceptions,

function __init__()
    global_logger(ConsoleLogger(stderr))
end

end

max_stars_repo_name: greimel/julia

max_issues_repo_name: greimel/julia

max_forks_repo_name: greimel/julia


text:
################################################# FILE DESCRIPTION #########################################################

# This file contains methods to merge graphs

################################################# IMPORT/EXPORT ############################################################

################################################# ADJACENCY ################################################################
"""
Merge two graphs into one. Currently this method assumes that both graphs have the
same vertices, and doesn't combine their data, but does a union on their edges.
"""
function Base.merge(g1::Graph, g2::Graph) # TODO: Merge edge vertex properties, edge properties and labels
   if nv(g1) != nv(g2)
      error("Both graphs must have the same vertices")
   end
   Nv = nv(g1)

   es = vcat(edges(g1), edges(g2))
   sv = SparseMatrixCSC(Nv, es)
   reorder!(sv)

   Ne = nnz(sv)

   Graph(Nv, Ne, sv, DataFrame(), DataFrame(), IdentityLM(Nv))
end


text:
#**************************************************************************************
# Input_Console.jl
# =============== part of the GeoEfficiency.jl package.
#
# all the input from the console to the package is handled by some function here.
#
#**************************************************************************************

#------------------ consts - globals - imports ----------------------------

using .MathConstants

#---------------------------- input ---------------------------------

"""# UnExported

	input(message::AbstractString = "?: ", incolor::Symbol = :green; default::AbstractString ="", timeout::Union{Nothing, Int} = nothing)

return a string represent the user respond delimited by new line excluding the new line.
prompt the user with the massage `message` defaults to `? `.
wait until the user type its respond and press return.

## KW arguments
*  default : the default value if the user just prress return or did not give respose in time.
*  timeout : time in seconds that the prompt is waiting for the user respose.

`incolor` specify the prompt message text color, default to ``:green`` may take any of the values
`:black`, `:blue`, `:cyan`, `:green`, `:light_black`, `:light_blue`, `:light_cyan`, `:light_green`,
`:light_magenta`, `:light_red`, `:light_yellow`, `:magenta`, `:red`, `:white`, or `:yellow`.

!!! color
		The effect of color is not allways respected in all teriminals as some color may be simplly
		ignored by some teriminals.

"""
function input(message::AbstractString = "?", incolor::Symbol = :green; default::AbstractString = "", timeout::Union{Nothing, Int} = nothing)::AbstractString
	if !isnothing(timeout)
        @assert timeout > 0 "Timeout must be greater than 0 seconds"
        msg = !isempty(default) ? "$message [$default] timeout $timeout seconds: " : "$message: "
        tr = Timer(timeout)
    else
        msg = !isempty(default) ? "$message [$default]: " : "$message: "
    end

	printstyled(msg, color = incolor, bold = true)
	t = @async readline( keep=false)

	while !istaskdone(t) && (!isnothing(timeout) && isopen(tr))
        sleep(0.1)
    end
    if !isnothing(timeout) && !istaskdone(t)
        try
            Base.throwto(t, InterruptException())
        catch
        end
        return default
	end

	uinput = fetch(t)
    isempty(uinput) ? default : uinput
end # function

#---------------------------- getfloat -----------------------------------

"""# UnExported

	getfloat(prompt::AbstractString = "?: ", from::Real = -Inf, to::Real = Inf; KW...)::Float64

prompts the user with the massage `prompt` defaults to `?: ` to input a numerical **expression**
evaluate to a numerical value.
check that the numerical value is in interval [`from`, `to`[ by default [-∞,	∞[ before returning it as a `Float64`.
throws `ArgumentError` when the given interval is not valid.
if the numerical expression fail to evaluated to numerical value or the numerical value is not in the valid interval
the function will warn the user and **reprompt** him to give a valid expresion.

## KW arguments
*  value::AbstractString`="nothing"` : if provided the function will not ask for input from the
   `console` and take it as if it where inputted from the  `console` [`for test propose mainly`].
*  lower::Bool`=true` : whether or not to inculde `from` as accepted value.
*  upper::Bool`=false` : whether or not to inculde `to` as accepted value.

!!! note
		A blank input (i.e just a return) is considered as being `0.0`.
		Input from the `console` can be numerical expression not just a number.
		expression like ``5/2`` ; ``5//2`` ; ``pi`` ; ``π/2`` ; ``exp(2)`` ; ``1E-2 `` ; ``5.2/3`` ;
		``sin(1)`` ;  ``sin(1)^2`` are all valid expressions.

# Examples
```
julia> getfloat("input a number:", value="3")
3.0

julia> getfloat("input a number:", value="")
0.0

julia> getfloat("input a number:", value="5/2")
2.5

julia> getfloat("input a number:", value="5//2")
2.5

julia> getfloat("input a number:", value="pi")
3.141592653589793

julia> getfloat("input a number:", value="-2")
-2.0

julia> getfloat("input a number:", value="sin(1)^2")
0.7080734182735712

julia> getfloat("input a number:", 1, 5, value="5", upper=true)
5.0
```

"""
function getfloat(prompt::AbstractString = "?: ", from::Real = -Inf, to::Real = Inf;
				value::Union{Nothing, AbstractString} = nothing , lower::Bool = true, upper::Bool = false)::Float64
	if isnothing(value)
		value = input(prompt)
	end

	if isempty(value)
		value = "0.0"  # just pressing return is interpreted as <0.0>
	end

	local val::Float64 = 0.0
	try
		val =  Meta.parse(value) |> eval |> float
		@assert from < val < to || (lower && from == val) || (upper && to == val)

	catch err
		if isa(err, AssertionError)
			let interval = "interval '$(lower ? '[' : ']') $from, $to $(upper ? ']' : '[')'"
				lower || upper || from != to ||	ArgumentError("the $interval is not valid") |> throw
				from <= to || ArgumentError("the $interval is not valid") |> throw
				@warn("""the input '$value' evaluated to be outside the $interval.
				\n Please: provide an adequate value""", _file=nothing)
			end #let
    	else
			@warn("""the input '$value' cannot be parsed to a valid numerical value!,
			\n Please: provide a valid expression""", _file=nothing)
    	end #if

		return getfloat(prompt, from, to; lower = lower, upper = upper)
	end #try
	return val
end

max_stars_repo_name: DrKrar/GeoEfficiency

max_issues_repo_name: DrKrar/GeoEfficiency

max_forks_repo_name: DrKrar/GeoEfficiency


text:
function parse_positions(io)
    lines = readlines(io)
    return parse_ints_from_row(lines[1]; sep=",")
end

function fuelb(dist)
    return dist*(dist+1)/2
end

function consumed_fuel(positions, target; fuel_func=identity)
    return sum(fuel_func(abs(p-target)) for p in positions)
end

function median(values)
    svals = sort(values)
    n = length(svals)
    return svals[div(n, 2, RoundNearest)]
end

function day07a(positions)
    med = median(positions)
    return consumed_fuel(positions, med)
end

# Analytical solution to minimize_p sum_{x\in X} (x-p)(x-p+1)/2
# The minimizer shall be p = (2s+n)/(2n) where s = sum(X) and n = length(X).
# But it gives wrong result for the B part, and I do not know why.
# The minimizer is somewhere between 464 and 465, but the above computation returns 465.009...
# If the +n part is removed from the numerator, we have computation for the mean.
# But mean is a minimizer for sum x^2, which is not exactly what we are optimizing here...

function day07b_analytical(positions)
    f(p) = consumed_fuel(positions, p, fuel_func = fuelb)
    m = minimizer(positions)
    m1, m2 = Int(round(m, RoundDown)), Int(round(m, RoundUp))
    f1 = f(m1)
    f2 = f(m2)
    return convert(Int, min(f1, f2))
end

function minimizer(values)
    n = length(values)
    s = sum(values)
    return (2s + n) / (2n)
end

function day07b(positions)
    left, right = minimum(positions), maximum(positions)
    f(p) = consumed_fuel(positions, p, fuel_func = fuelb)
    popt = argmin(f, left:right)
    return convert(Int, f(popt))
end

function day07(io)
    positions = parse_positions(io)
    return (day07a(positions), day07b(positions))
end


text:
abstract type Model end

struct Gaussian <: Model end

struct Student <: Model end

struct Logistic <: Model end

struct Pois <: Model end

struct NegBin <: Model end


text:
module TestBasics

using Test
using InitialValues
using InitialValues: isknown, hasinitialvalue

@testset for op in [*, +, |, &, min, max, Base.add_sum, Base.mul_prod]
    @test op(Init(op), :anything) === :anything
    @test hasinitialvalue(op)
    @test hasinitialvalue(typeof(op))
    @test isknown(Init(op))
end

@testset "show" begin
    @testset "$desired" for (op, desired) in [
        (+, "Init(+)"),
        (*, "Init(*)"),
        (|, "Init(|)"),
        (&, "Init(&)"),
        (min, "Init(min)"),
        (max, "Init(max)"),
    ]
        @test repr(Init(op); context=:limit => true) == desired
        @test repr(Init(op)) == "InitialValues.$desired"
        @test string(Init(op)) == "InitialValues.$desired"
    end
end

@testset "hasinitialvalue" begin
    @test !hasinitialvalue(-)
    @test !hasinitialvalue(typeof(-))
    @test !hasinitialvalue(Int)
    @test !hasinitialvalue(Type{Int})
end

@testset "missing" begin
    @test min(Init(min), missing) === missing
    @test max(Init(max), missing) === missing
end

@testset "convert" begin
    @testset "float" begin
        @test float(Init(+)) === 0.0
        @test float(Init(*)) === 1.0
        @test float(Init(Base.add_sum)) === 0.0
        @test float(Init(Base.mul_prod)) === 1.0
    end
    @testset "Integer" begin
        @test Integer(Init(+)) === 0
        @test Integer(Init(*)) === 1
        @test Integer(Init(Base.add_sum)) === 0
        @test Integer(Init(Base.mul_prod)) === 1
    end
    @testset for T in [
        Int,
        UInt8,
        Float64,
        Float32,
    ]
        @test convert(T, Init(+))::T == 0
        @test convert(T, Init(*))::T == 1
        @test convert(T, Init(Base.add_sum))::T == 0
        @test convert(T, Init(Base.mul_prod))::T == 1
    end

    @test convert(String, Init(*)) === ""
end

end  # module


text:
"""
    DiagnosticVariable

Currently only holds information about a diagnostics variable.

Standard names are from:
http://cfconventions.org/Data/cf-standard-names/71/build/cf-standard-name-table.html

TODO: will expand to include definition (code) as well.
"""
struct DiagnosticVariable
    name::String
    attrib::OrderedDict

    DiagnosticVariable(name::String, attrib::OrderedDict = OrderedDict()) =
        new(name, attrib)
end
const Variables = OrderedDict{String, DiagnosticVariable}()

function var_attrib(
    units::String,
    long_name::String,
    standard_name::String,
    fill_value::Union{Nothing, Any} = nothing,
)
    attrib = OrderedDict(
        "units" => units,
        "long_name" => long_name,
        "standard_name" => standard_name,
    )
    if !isnothing(fill_value)
        attrib["_FillValue"] = fill_value
    end
    return attrib
end

"""
    setup_variables()

Called at module initialization to define all currently defined diagnostic
variables.
"""
function setup_variables()
    # les long_name: "x-velocity"
    Variables["u"] = DiagnosticVariable(
        "u",
        var_attrib("m s^-1", "zonal wind", "eastward_wind"),
    )
    # les long_name: "y-velocity"
    Variables["v"] = DiagnosticVariable(
        "v",
        var_attrib("m s^-1", "meridional wind", "northward_wind"),
    )
    # les long_name = "z-velocity"
    Variables["w"] = DiagnosticVariable(
        "w",
        var_attrib("m s^-1", "vertical wind", "upward_air_velocity"),
    )
    Variables["rho"] = DiagnosticVariable(
        "rho",
        var_attrib("kg m^-3", "air density", "air_density"),
    )
    Variables["temp"] = DiagnosticVariable(
        "temp",
        var_attrib("K", "air temperature", "air_temperature"),
    )
    Variables["pres"] = DiagnosticVariable(
        "pres",
        var_attrib("Pa", "air pressure", "air_pressure"),
    )
    Variables["thd"] = DiagnosticVariable(
        "thd",
        var_attrib(
            "K",
            "dry potential temperature",
            "air_potential_temperature",
        ),
    )
    Variables["thv"] = DiagnosticVariable(
        "thv",
        var_attrib(
            "K",
            "virtual potential temperature",
            "virtual_potential_temperature",
        ),
    )
    Variables["et"] = DiagnosticVariable(
        "et",
        var_attrib(
            "J kg^-1",
            "total specific energy",
            "specific_dry_energy_of_air",
        ),
    )
    Variables["ei"] = DiagnosticVariable(
        "ei",
        var_attrib("J kg^-1", "specific internal energy", "internal_energy"),
    )
    Variables["ht"] = DiagnosticVariable(
        "ht",
        var_attrib("J kg^-1", "specific enthalpy based on total energy", ""),
    )
    Variables["hi"] = DiagnosticVariable(
        "hi",
        var_attrib(
            "J kg^-1",
            "specific enthalpy based on internal energy",
            "atmosphere_enthalpy_content",
        ),
    )
    Variables["vort"] = DiagnosticVariable(
        "vort",
        var_attrib(
            "s^-1",
            "vertical component of relative velocity",
            "atmosphere_relative_velocity",
        ),
    )
    Variables["avg_rho"] = DiagnosticVariable(
        "avg_rho",
        var_attrib("kg m^-3", "air density", "air_density"),
    )
    Variables["qt"] = DiagnosticVariable(
        "qt",
        var_attrib(
            "kg kg^-1",
            "mass fraction of total water in air (qv+ql+qi)",
            "mass_fraction_of_water_in_air",
        ),
    )
    Variables["ql"] = DiagnosticVariable(
        "ql",
        var_attrib(
            "kg kg^-1",
            "mass fraction of liquid water in air",
            "mass_fraction_of_cloud_liquid_water_in_air",
        ),
    )
    Variables["qv"] = DiagnosticVariable(
        "qv",
        var_attrib(
            "kg kg^-1",
            "mass fraction of water vapor in air",
            "specific_humidity",
        ),
    )
    Variables["qi"] = DiagnosticVariable(
        "qi",
        var_attrib(
            "kg kg^-1",
            "mass fraction of ice in air",
            "mass_fraction_of_cloud_ice_in_air",
        ),
    )
    Variables["thl"] = DiagnosticVariable(
        "thl",
        var_attrib("K", "liquid-ice potential temperature", ""),
    )
    Variables["cld_frac"] = DiagnosticVariable(
        "cld_frac",
        var_attrib(
            "",
            "cloud fraction",
            "cloud_area_fraction_in_atmosphere_layer",
        ),
    )
    Variables["var_u"] = DiagnosticVariable(
        "var_u",
        var_attrib("m^2 s^-2", "variance of x-velocity", ""),
    )
    Variables["var_v"] = DiagnosticVariable(
        "var_v",
        var_attrib("m^2 s^-2", "variance of y-velocity", ""),
    )
    Variables["var_w"] = DiagnosticVariable(
        "var_w",
        var_attrib("m^2 s^-2", "variance of z-velocity", ""),
    )
    Variables["w3"] = DiagnosticVariable(
        "w3",
        var_attrib("m^3 s^-3", "third moment of z-velocity", ""),
    )
    Variables["tke"] = DiagnosticVariable(
        "tke",
        var_attrib("m^2 s^-2", "turbulent kinetic energy", ""),
    )
    Variables["var_qt"] = DiagnosticVariable(
        "var_qt",
        var_attrib("kg^2 kg^-2", "variance of total specific humidity", ""),
    )
    Variables["var_thl"] = DiagnosticVariable(
        "var_thl",
        var_attrib("K^2", "variance of liquid-ice potential temperature", ""),
    )
    Variables["var_ei"] = DiagnosticVariable(
        "var_ei",
        var_attrib("J^2 kg^-2", "variance of specific internal energy", ""),
    )
    Variables["cov_w_u"] = DiagnosticVariable(
        "cov_w_u",
        var_attrib("m^2 s^-2", "vertical eddy flux of x-velocity", ""),
    )
    Variables["cov_w_v"] = DiagnosticVariable(
        "cov_w_v",
        var_attrib("m^2 s^-2", "vertical eddy flux of y-velocity", ""),
    )
    Variables["cov_w_rho"] = DiagnosticVariable(
        "cov_w_rho",
        var_attrib("kg m^-2 s^-1", "vertical eddy flux of density", ""),
    )
    Variables["cov_w_qt"] = DiagnosticVariable(
        "cov_w_qt",
        var_attrib(
            "kg kg^-1 m s^-1",
            "vertical eddy flux of total specific humidity",
            "",
        ),
    )
    Variables["cov_w_ql"] = DiagnosticVariable(
        "cov_w_ql",
        var_attrib(
            "kg kg^-1 m s^-1",
            "vertical eddy flux of liquid water specific humidity",
            "",
        ),
    )
    Variables["cov_w_qi"] = DiagnosticVariable(
        "cov_w_qi",
        var_attrib(
            "kg kg^-1 m s^-1",
            "vertical eddy flux of cloud ice specific humidity",
            "",
        ),
    )
    Variables["cov_w_qv"] = DiagnosticVariable(
        "cov_w_qv",
        var_attrib(
            "kg kg^-1 m s^-1",
            "vertical eddy flux of water vapor specific humidity",
            "",
        ),
    )
    Variables["cov_w_thd"] = DiagnosticVariable(
        "cov_w_thd",
        var_attrib(
            "K m s^-1",
            "vertical eddy flux of dry potential temperature",
            "",
        ),
    )
    Variables["cov_w_thv"] = DiagnosticVariable(
        "cov_w_thv",
        var_attrib(
            "K m s^-1",
            "vertical eddy flux of virtual potential temperature",
            "",
        ),
    )
    Variables["cov_w_thl"] = DiagnosticVariable(
        "cov_w_thl",
        var_attrib(
            "K m s^-1",
            "vertical eddy flux of liquid-ice potential temperature",
            "",
        ),
    )
    Variables["cov_w_ei"] = DiagnosticVariable(
        "cov_w_ei",
        var_attrib(
            "J kg^-1 m s^-1",
            "vertical eddy flux of specific internal energy",
            "",
        ),
    )
    Variables["cov_qt_thl"] = DiagnosticVariable(
        "cov_qt_thl",
        var_attrib(
            "kg kg^-1 K",
            "covariance of total specific humidity and liquid-ice potential temperature",
            "",
        ),
    )
    Variables["cov_qt_ei"] = DiagnosticVariable(
        "cov_qt_ei",
        var_attrib(
            "kg kg^-1 J kg^-1",
            "covariance of total specific humidity and specific internal energy",
            "",
        ),
    )
    Variables["w_qt_sgs"] = DiagnosticVariable(
        "w_qt_sgs",
        var_attrib(
            "kg kg^-1 m s^-1",
            "vertical sgs flux of total specific humidity",
            "",
        ),
    )
    Variables["w_ht_sgs"] = DiagnosticVariable(
        "w_ht_sgs",
        var_attrib(
            "kg kg^-1 m s^-1",
            "vertical sgs flux of total specific enthalpy",
            "",
        ),
    )
    Variables["cld_cover"] = DiagnosticVariable(
        "cld_cover",
        var_attrib("", "cloud cover", "cloud_area_fraction"),
    )
    Variables["cld_top"] = DiagnosticVariable(
        "cld_top",
        var_attrib("m", "cloud top", "cloud_top_altitude"),
    )
    Variables["cld_base"] = DiagnosticVariable(
        "cld_base",
        var_attrib("m", "cloud base", "cloud_base_altitude"),
    )
    Variables["lwp"] = DiagnosticVariable(
        "lwp",
        var_attrib(
            "kg m^-2",
            "liquid water path",
            "atmosphere_mass_content_of_cloud_condensed_water",
        ),
    )
    Variables["core_frac"] = DiagnosticVariable(
        "core_frac",
        var_attrib("", "cloud core fraction", ""),
    )
    Variables["u_core"] = DiagnosticVariable(
        "u_core",
        var_attrib("m s^-1", "cloud core x-velocity", ""),
    )
    Variables["v_core"] = DiagnosticVariable(
        "v_core",
        var_attrib("m s^-1", "cloud core y-velocity", ""),
    )
    Variables["w_core"] = DiagnosticVariable(
        "w_core",
        var_attrib("m s^-1", "cloud core z-velocity", ""),
    )
    Variables["avg_rho_core"] = DiagnosticVariable(
        "avg_rho_core",
        var_attrib("kg m^-3", "cloud core air density", ""),
    )
    Variables["rho_core"] = DiagnosticVariable(
        "rho_core",
        var_attrib("kg m^-3", "cloud core (density-averaged) air density", ""),
    )
    Variables["qt_core"] = DiagnosticVariable(
        "qt_core",
        var_attrib("kg m^-3", "cloud core total specific humidity", ""),
    )
    Variables["ql_core"] = DiagnosticVariable(
        "ql_core",
        var_attrib("kg m^-3", "cloud core liquid water specific humidity", ""),
    )
    Variables["thv_core"] = DiagnosticVariable(
        "thv_core",
        var_attrib("K", "cloud core virtual potential temperature", ""),
    )
    Variables["thl_core"] = DiagnosticVariable(
        "thl_core",
        var_attrib("K", "cloud core liquid-ice potential temperature", ""),
    )
    Variables["ei_core"] = DiagnosticVariable(
        "ei_core",
        var_attrib("J kg-1", "cloud core specific internal energy", ""),
    )
    Variables["var_u_core"] = DiagnosticVariable(
        "var_u_core",
        var_attrib("m^2 s^-2", "cloud core variance of x-velocity", ""),
    )
    Variables["var_v_core"] = DiagnosticVariable(
        "var_v_core",
        var_attrib("m^2 s^-2", "cloud core variance of y-velocity", ""),
    )
    Variables["var_w_core"] = DiagnosticVariable(
        "var_w_core",
        var_attrib("m^2 s^-2", "cloud core variance of z-velocity", ""),
    )
    Variables["var_qt_core"] = DiagnosticVariable(
        "var_qt_core",
        var_attrib(
            "kg^2 kg^-2",
            "cloud core variance of total specific humidity",
            "",
        ),
    )
    Variables["var_thl_core"] = DiagnosticVariable(
        "var_thl_core",
        var_attrib(
            "K^2",
            "cloud core variance of liquid-ice potential temperature",
            "",
        ),
    )
    Variables["var_ei_core"] = DiagnosticVariable(
        "var_ei_core",
        var_attrib(
            "J^2 kg^-2",
            "cloud core variance of specific internal energy",
            "",
        ),
    )
    Variables["cov_w_rho_core"] = DiagnosticVariable(
        "cov_w_rho_core",
        var_attrib(
            "kg m^-2 s^-1",
            "cloud core vertical eddy flux of density",
            "",
        ),
    )
    Variables["cov_w_qt_core"] = DiagnosticVariable(
        "cov_w_qt_core",
        var_attrib(
            "kg kg^-1 m s^-1",
            "cloud core vertical eddy flux of specific humidity",
            "",
        ),
    )
    Variables["cov_w_thl_core"] = DiagnosticVariable(
        "cov_w_thl_core",
        var_attrib(
            "K m s^-1",
            "cloud core vertical eddy flux of liquid-ice potential temperature",
            "",
        ),
    )
    Variables["cov_w_ei_core"] = DiagnosticVariable(
        "cov_w_ei_core",
        var_attrib(
            "J kg^-1 m^-1 s^-1",
            "cloud core vertical eddy flux of specific internal energy",
            "",
        ),
    )
    Variables["cov_qt_thl_core"] = DiagnosticVariable(
        "cov_qt_thl_core",
        var_attrib(
            "kg kg^-1 K",
            "cloud core covariance of total specific humidity and liquid-ice potential temperature",
            "",
        ),
    )
    Variables["cov_qt_ei_core"] = DiagnosticVariable(
        "cov_qt_ei_core",
        var_attrib(
            "kg kg^-1 J kg^-1",
            "cloud core covariance of total specific humidity and specific internal energy",
            "",
        ),
    )
    Variables["E_k"] = DiagnosticVariable(
        "E_k",
        var_attrib(
            "",
            "volumetrically-averaged dimensionless kinetic energy",
            "",
        ),
    )
    Variables["dE"] = DiagnosticVariable(
        "dE",
        var_attrib(
            "",
            "volumetrically-averaged kinetic energy dissipation",
            "",
        ),
    )
    Variables["mass_loss"] =
        DiagnosticVariable("mass_loss", var_attrib("", "", ""))
    Variables["energy_loss"] =
        DiagnosticVariable("energy_loss", var_attrib("", "", ""))
end

max_stars_repo_name: mwarusz/CLIMA

max_issues_repo_name: mwarusz/CLIMA

max_forks_repo_name: mwarusz/CLIMA


text:
using StaticArrays
using ACEtb.SlaterKoster: get_l, get_bidx, bond_to_idx, sksignmat

export @skh_str, SKH

# this assumes that the coordinates are normalised
# TODO: α=φ, β=θ
"""
INPUTS: (x,y,z): This represents a position vector which is given using Cartesian coordinates.
RETURNS: The corresponding polar coordinates - radial, azimuthal, polar.
"""
function carttospher(x,y,z)
   r = sqrt(x*x+y*y+z*z)
   θ = acos(z/r)
   if x != 0
      φ = atan(y, x)
   else
      φ = sign(y) * π * 0.5
   end
   return φ, θ
end

struct SKH{SIGN}
   orbitals::Vector{SKOrbital}    # this should become a `species => orbitals` list
   bonds::Vector{SKBond}       # this should become a `(spec1, spec2) => bonds list`
   b2o::Vector{Tuple{Int,Int}}
   locorbidx::Vector{Vector{Int}}
   sig::Type{SIGN}
end

Base.String(H::SKH) = ("sk:" * prod((o.str*":") for o in H.orbitals))[1:end-1]

Base.show(io::IO, H::SKH) = write(io, String(H))

import Base.==
==(H1::SKH, H2::SKH) = all( getproperty(H1, p) == getproperty(H2, p)
                            for p in fieldnames(SKH) )

nbonds(H::SKH) = length(H.bonds)
norbitals(H::SKH) = length(H.orbitals)

"""
`allbonds(orbitals)` : compute all the `SKBond`s between any two
`SKOrbital`s in the list `orbitals` and return as `Vector`.
"""
function allbonds(orbitals::Vector{<: SKOrbital})
   norb = length(orbitals)
   bonds = SKBond[]
   idx = 0
   for i1 = 1:norb, i2 = 1:norb
      o1, o2 = orbitals[i1], orbitals[i2]
      for sym in bondtypes(o1, o2)
         idx += 1
         push!(bonds, SKBond(o1, o2, sym, idx))
      end
   end
   return bonds
end

function SKH(orbitals::AbstractVector{<: SKOrbital},
             bonds::AbstractVector{<: SKBond},
             sig = StandardSigns)
   # construct local orbital -> index mapping
   locorbidx = Vector{Int}[]
   idx = 0
   for orb in orbitals
      len = 2 * get_l(orb) + 1
      push!(locorbidx, Vector{Int}(idx .+ (1:len)))
      idx += len
   end
   # construct local bonds -> orbitals connectivity
   b2o = Tuple{Int, Int}[]
   for b in bonds
      i1 = findfirst(o -> o == b.o1, orbitals)
      i2 = findfirst(o -> o == b.o2, orbitals)
      # insist on an ordering convention
      push!(b2o, (i1, i2))
   end
   return SKH(orbitals, bonds, b2o, locorbidx, sig)
end

_create_idx(orbitals::AbstractVector{<: SKOrbital}) =
   [ SKOrbital(o, idx) for (idx, o) in enumerate(orbitals) ]

function SKH(orbitals::AbstractVector{<: SKOrbital}, sig=StandardSigns)
   orbs_indx = _create_idx(orbitals)
   return SKH(orbs_indx, allbonds(orbs_indx), sig)
end

# experimental constructor for a TB model
function SKH(str::AbstractString, sig = StandardSigns)
   if '0' <= str[1] <= '9'
      @assert iseven(length(str))
      orbitals = [ SKOrbital(str[n:n+1]) for n in 1:2:length(str) ]
   else
      orbitals = [ SKOrbital(str[n:n])   for n = 1:length(str) ]
   end
   return SKH(orbitals, sig)
end

macro skh_str(s) SKH(s) end

max_locidx(H::SKH) = maximum(maximum(I) for I in H.locorbidx)

alloc_block(H::SKH) = zeros(max_locidx(H::SKH), max_locidx(H::SKH))

"""
`sk2cart:` assemble a Slater-Koster matrix block.

**Warning:** this is type-unstable and should not be used to assemble large
Hamiltonians.
"""
function sk2cart_other(H::SKH, R, V; WriteAllow::Bool=true)
   φ, θ = carttospher(R[1], R[2], R[3])
   E = alloc_block(H)
   for (b, Vb, (io1, io2)) in zip(H.bonds, V, H.b2o)
      G12 = CodeGeneration.sk_gen(b, φ, θ, W=Val(WriteAllow))
      I1 = H.locorbidx[io1]
      I2 = H.locorbidx[io2]
      E[I1, I2] .+= (sksign(b) * Vb) * G12
   end
   return E
end

function sk2cart_FHIaims(H::SKH, R, V; WriteAllow::Bool=true)
   φ, θ = carttospher(R[1], R[2], R[3])
   E = alloc_block(H)
   for (b, Vb, (io1, io2)) in zip(H.bonds, V, H.b2o)
      G12 = CodeGeneration.sk_gen(b, φ, θ, W=Val(WriteAllow))
      I1 = H.locorbidx[io1]
      I2 = H.locorbidx[io2]
      E[I1, I2] .+= (sksign(b) * Vb) * G12 .* sksignmat(b)
   end
   return E
end

sk2cart(H::SKH, R, V; FHIaims::Bool=true, WriteAllow::Bool=true) = FHIaims ? sk2cart_FHIaims(H::SKH, R, V, WriteAllow=WriteAllow) : sk2cart_other(H::SKH, R, V, WriteAllow=WriteAllow)

function sk2cart_onsite(H::SKH, Rlist, Vlist)
   E = alloc_block(H)
   for (R, V) in zip(Rlist, Vlist)
       φ, θ = carttospher(R[1], R[2], R[3])
       for (b, Vb, (io1, io2)) in zip(H.bonds, V, H.b2o)
           G12 = CodeGeneration.sk_gen(b, φ, θ)
           I1 = H.locorbidx[io1]
           I2 = H.locorbidx[io2]
           E[I1, I2] .+= (sksign(b) * Vb) * G12
       end
   end
   return E
end

function sk2cart_onsite_FHIaims(H::SKH, Rlist, Vlist)
   E = alloc_block(H)
   for (R, V) in zip(Rlist, Vlist)
       φ, θ = carttospher(R[1], R[2], R[3])
       for (b, Vb, (io1, io2)) in zip(H.bonds, V, H.b2o)
           G12 = CodeGeneration.sk_gen(b, φ, θ)
           I1 = H.locorbidx[io1]
           I2 = H.locorbidx[io2]
           E[I1, I2] .+= (sksign(b) * Vb) * G12 .* sksignmat(b)
       end
   end
   return E
end

sk2cart_onsite(H::SKH, R, V; FHIaims=false) = FHIaims ? sk2cart_onsite_FHIaims(H::SKH, R, V) : sk2cart_onsite_other(H::SKH, R, V)

function sk2cart_num_other(H::SKH, R, V)
   φ, θ = carttospher(R[1], R[2], R[3])
   E = alloc_block(H)
   for (b, Vb, (io1, io2)) in zip(H.bonds, V, H.b2o)
      G12 = CodeGeneration.sk_num(b, φ, θ)
      I1 = H.locorbidx[io1]
      I2 = H.locorbidx[io2]
      E[I1, I2] .+= (sksign(b) * Vb) * G12
   end
   return E
end

function sk2cart_num_FHIaims(H::SKH, R, V)
   φ, θ = carttospher(R[1], R[2], R[3])
   E = alloc_block(H)
   for (b, Vb, (io1, io2)) in zip(H.bonds, V, H.b2o)
      G12 = CodeGeneration.sk_num(b, φ, θ)
      I1 = H.locorbidx[io1]
      I2 = H.locorbidx[io2]
      E[I1, I2] .+= (sksign(b) * Vb) * G12 .* sksignmat(b)
   end
   return E
end

sk2cart_num(H::SKH, R, V; FHIaims=true) = FHIaims ? sk2cart_num_FHIaims(H::SKH, R, V) : sk2cart_num_other(H::SKH, R, V)

"""
todo doc
"""
function cart2sk_other(H::SKH, R, E::AbstractArray; WriteAllow::Bool=true)
   φ, θ = carttospher(R[1], R[2], R[3])
   V = zeros(length(H.bonds))
   for (I, (b, (io1, io2))) in enumerate(zip(H.bonds, H.b2o))
      G12 = CodeGeneration.sk_gen(b, φ, θ, W=Val(WriteAllow))
      I1 = H.locorbidx[io1]
      I2 = H.locorbidx[io2]
      b_l = get_bidx(b) # bond symbol to L
      val = sum(sksign(b) * E[I1, I2] .* G12)
      if b_l > 0 # for bonds other than 's' or l>0
            V[I] += 0.5 * val
      else # for 's' bond or l=0
            V[I] += val
      end
   end
   return V
end

function cart2sk_FHIaims(H::SKH, R, E::AbstractArray; WriteAllow::Bool=true)
   φ, θ = carttospher(R[1], R[2], R[3])
   V = zeros(length(H.bonds))
   for (I, (b, (io1, io2))) in enumerate(zip(H.bonds, H.b2o))
      G12 = CodeGeneration.sk_gen(b, φ, θ, W=Val(WriteAllow))
      I1 = H.locorbidx[io1]
      I2 = H.locorbidx[io2]
      b_l = get_bidx(b) # bond symbol to L
      val = sum(sksign(b) * E[I1, I2] .* G12 .* sksignmat(b))
      if b_l > 0 # for bonds other than 's' or l>0
            V[I] += 0.5 * val
      else # for 's' bond or l=0
            V[I] += val
      end
   end
   return V
end

cart2sk(H::SKH, R, V; FHIaims::Bool=true, WriteAllow::Bool=true) = FHIaims ? cart2sk_FHIaims(H::SKH, R, V; WriteAllow=WriteAllow) : cart2sk_other(H::SKH, R, V; WriteAllow=WriteAllow)

function cart2sk_num_other(H::SKH, R, E::AbstractArray)
   φ, θ = carttospher(R[1], R[2], R[3])
   V = zeros(length(H.bonds))
   for (I, (b, (io1, io2))) in enumerate(zip(H.bonds, H.b2o))
      G12 = CodeGeneration.sk_num(b, φ, θ)
      I1 = H.locorbidx[io1]
      I2 = H.locorbidx[io2]
      b_l = get_bidx(b) # bond symbol to L
      val = sum(sksign(b) * E[I1, I2] .* G12)
      if b_l > 0 # for bonds other than 's' or l>0
            V[I] += 0.5 * val
      else # for 's' bond or l=0
            V[I] += val
      end
   end
   return V
end

function cart2sk_num_FHIaims(H::SKH, R, E::AbstractArray)
   φ, θ = carttospher(R[1], R[2], R[3])
   V = zeros(length(H.bonds))
   for (I, (b, (io1, io2))) in enumerate(zip(H.bonds, H.b2o))
      G12 = CodeGeneration.sk_num(b, φ, θ)
      I1 = H.locorbidx[io1]
      I2 = H.locorbidx[io2]
      b_l = get_bidx(b) # bond symbol to L
      val = sum(sksign(b) * E[I1, I2] .* G12 .* sksignmat(b))
      if b_l > 0 # for bonds other than 's' or l>0
            V[I] += 0.5 * val
      else # for 's' bond or l=0
            V[I] += val
      end
   end
   return V
end

cart2sk_num(H::SKH, R, V; FHIaims=true) = FHIaims ? cart2sk_num_FHIaims(H::SKH, R, V) : cart2sk_num_other(H::SKH, R, V)


text:
using PQEcolaPoint
using Dates

# what is the contract?
short_put_contract = PutContractModel()
short_put_contract.ticker = "XYZ"
short_put_contract.expiration_date = Date(2022, 04, 14)
short_put_contract.strike_price = 105.0
short_put_contract.premium = 0.0
short_put_contract.number_of_contracts = 1
short_put_contract.direction = 1

# other version -
val_2 = θ(short_put_contract; Sₒ=111.69, number_of_levels=80, σ=0.5486, T=(28.0 / 365), μ=0.0045)


text:
__precompile__()

module RomanNumerals

# Defines the exception the struct RomanNumeral
include("types.jl")

# Defines the functions toroman and parse(RomanNumeral, x), to convert from
# Roman-formatted string to int and vice versa
include("roman_conversion.jl")

# Defines new method dispatching specific to Roman numerals
include("arithmetic.jl")

export RomanNumeral, @rn_str

end # module RomanNumerals


text:
module Vehicle

# ------------------------------------------------------------------
# 1. Export/Imports
# ------------------------------------------------------------------
export go!, land!

# ------------------------------------------------------------------
# 2. Interface documentation
# ------------------------------------------------------------------
# A vehicle (v) must implement the following functions:
#
# power_on!(v) - turn on the vehicle's engine
# power_off!(v) - turn off the vehicle's engine
# turn!(v, direction) - steer the vehicle to the specified direction
# move!(v, distance) - move the vehicle by the specified distance
# position(v) - returns the (x,y) position of the vehicle
# engage_wheels!(v) - engage wheels for landing.  Optional.
# has_wheels(v) - returns true if the vehicle has wheels.

# ------------------------------------------------------------------
# 3. Generic definitions for the interface
# ------------------------------------------------------------------
"""Turn on the vehicle's engine"""
function power_on! end

"""Turn off the vehicle's engine"""
function power_off! end

"""Steer the vehicle to the specified direction"""
function turn! end

"""Move the vehicle by the specified distance"""
function move! end

"""Get the (x, y) position of the vehicle"""
function position end

# soft contracts
"""Engage wheels for landing. Optional"""
engage_wheels!(args...) = nothing

# trait
"""Returns true if the vehicle has wheels"""
has_wheels(vehicle) = error("Not implemented.")

# ------------------------------------------------------------------
# 4. Game logic
# ------------------------------------------------------------------

# Returns a travel plan from current position to destination
function travel_path(position, destination)
    return round(π/6, digits=2), 1000   # just a test
end

# Space travel logic
function go!(vehicle, destination)
    power_on!(vehicle)
    direction, distance = travel_path(position(vehicle), destination)
    turn!(vehicle, direction)
    move!(vehicle, distance)
    power_off!(vehicle)
    nothing
end

# Landing
function land!(vehicle)
    engage_wheels!(vehicle)
    println("Landing vehicle: ", vehicle)
end

# Landing (using trait)
function land2!(vehicle)
    has_wheels(vehicle) && engage_wheels!(vehicle)
    println("Landing vehicle: ", vehicle)
end

end # module


text:
struct HiddenMarkovModel{Tv <: AbstractFloat, Ti <: Integer}
    aca::AhoCorasickAutomaton{Ti}
    pos::Vector{String}
    hpr::Vector{Tv}
    h2h::Matrix{Tv}
    h2v::Vector{Dict{Int, Tv}}
    MAX::Tv
    sumhpr::Tv
    sumh2hdim1::Matrix{Tv}
    sumh2vdimv::Vector{Tv}
end

function HiddenMarkovModel{Tv, Ti}(;
    model::Union{String, Nothing} = joinpath(dirname(pathof(ChineseTokenizers)), "..", "chmm"),
    poswords::Union{String, Nothing} = nothing,
    userdict::Union{String, Nothing} = nothing) where {Tv, Ti}
    if model == nothing
        @assert poswords != nothing "poswords must not be nothing."
        dict = Dict{String, Int}()
        if userdict != nothing
            open(userdict, "r") do io
                for line in eachline(io)
                    cells = split(line, " ")
                    word = String(cells[1])
                    if !haskey(dict, word)
                        dict[word] = length(dict) + 1
                    end
                end
            end
        end
        poss = Dict{String, Int}()
        hpr = Tv[]
        h2h = Dict{Int, Tv}[]
        h2v = Dict{Int, Tv}[]
        open(poswords, "r") do io
            for line in eachline(io)
                nword = parse(Int, line)
                if nword <= 0 continue end
                poswords = Vector{Tuple{String, String}}()
                tmp = nword
                for line2 in eachline(io)
                    cells = split(line2)
                    pos  = String(split(cells[1], "-")[1])
                    word = String(cells[2])
                    push!(poswords, (pos, word))
                    tmp -= 1
                    if tmp == 0 break end
                end
                # hpr, h2h, h2v
                for i = 1:nword
                    pos  = poswords[i][1]
                    word = poswords[i][2]
                    ih = get(poss, pos, length(poss) + 1)
                    if ih == length(poss) + 1
                        poss[pos] = ih
                        push!(hpr, 0)
                        push!(h2h, Dict{Int, Tv}())
                        push!(h2v, Dict{Int, Tv}())
                    end
                    iw = get(dict, word, length(dict) + 1)
                    if iw == length(dict) + 1
                        dict[word] = iw
                    end
                    if i == 1 hpr[ih] += 1 end
                    if i > 1
                        ph = poss[poswords[i - 1][1]]
                        if !haskey(h2h[ph], ih) h2h[ph][ih] = 0 end
                        h2h[ph][ih] += 1
                    end
                    if !haskey(h2v[ih], iw) h2v[ih][iw] = 0 end
                    h2v[ih][iw] += 1
                end
            end
        end
        nword = length(dict)
        aca = AhoCorasickAutomaton{Ti}(dict; sort = true)
        npos = length(poss);
        pos = Vector{String}(undef, npos)
        for (p, i) in poss pos[i] = p end
        sumhpr = norm!(hpr)
        h2h2 = zeros(Tv, npos, npos)
        for (ih, hs) in enumerate(h2h)
            for (ih2, cnt) in hs
                h2h2[ih2, ih] += cnt
            end
        end
        sumh2hdim1 = norm!(h2h2)
        sumh2vdimv = norm!(h2v)
        MAX = mapreduce(x -> maximum(values(x)), max, h2v)
        return HiddenMarkovModel{Tv, Ti}(aca, pos, hpr, h2h2, h2v, MAX, sumhpr, sumh2hdim1, sumh2vdimv)
    else
        old = open(model, "r") do io read(io, HiddenMarkovModel) end
        if poswords == nothing && userdict == nothing return old end
        dict = Dict(collect(old.aca))
        posid = Dict{String, Int}(); for (id, pos) in enumerate(old.pos) posid[pos] = id end
        hpr = denorm!(old.hpr, old.sumhpr)
        h2h = denorm!(old.h2h, old.sumh2hdim1)
        h2v = denorm!(old.h2v, old.sumh2vdimv)
        if userdict != nothing
            open(userdict, "r") do io
                for line in eachline(io)
                    cells = split(line)
                    word = cells[1]
                    pos  = length(cells) > 1 ? cells[2] : "NN"
                    iw   = get(dict, word, length(dict) + 1)
                    if iw == length(dict) + 1 dict[word] = iw end
                    ih   = get(posid, pos, 0)
                    @assert ih != 0 "Userdict contains an unrecognizable POS - " * pos
                    if !haskey(h2v[ih], iw) h2v[ih][iw] = 0 end
                    h2v[ih][iw] += 1
                end
            end
        end
        if poswords != nothing
            open(poswords, "r") do io
                for line in eachline(io)
                    nword = parse(Int, line)
                    ph = 0
                    for i = 1:nword
                        cells = split(readline(io))
                        pos  = cells[1]
                        word = cells[2]
                        iw   = get(dict, word, length(dict) + 1)
                        if iw == length(dict) + 1 dict[word] = iw end
                        ih   = get(posid, pos, 0)
                        @assert ih != 0 "Poswords contains an unrecognizable POS - " * pos
                        if !haskey(h2v[ih], iw) h2v[ih][iw] = 0 end
                        h2v[ih][iw] += 1
                        if i == 1 hpr[ih] += 1 end
                        if i >  1 h2h[ih, ph] += 1 end
                        ph = ih
                    end
                end
            end
        end
        nword = length(dict)
        aca = AhoCorasickAutomaton{Ti}(dict; sort = true)
        pos = Vector{String}(undef, length(posid)); for (p, id) in posid pos[id] = p end
        sumhpr     = norm!(hpr)
        sumh2hdim1 = norm!(h2h)
        sumh2vdimv = norm!(h2v)
        MAX = mapreduce(x -> maximum(values(x)), max, h2v)
        return HiddenMarkovModel{Tv, Ti}(aca, pos, hpr, h2h, h2v, MAX, sumhpr, sumh2hdim1, sumh2vdimv)
    end
end

HiddenMarkovModel() = HiddenMarkovModel{Float32, Int32}(;)

function chmm()
    poswords = joinpath(dirname(pathof(ChineseTokenizers)), "..", "data", "posword")
    userdict = joinpath(dirname(pathof(ChineseTokenizers)), "..", "data", "userdict")
    return HiddenMarkovModel{Float32, Int32}(; poswords = poswords, userdict = userdict, model = nothing)
end

function display(obj::HiddenMarkovModel{Tv, Ti}) where Tv where Ti
    title = string(typeof(obj), " ", (nword = length(obj.aca), npos = length(obj.pos), nbyte = Base.format_bytes(Base.summarysize(obj))))
    println(title)
    rows = Any[["POS", "nhead", "tot", "unique", "examples"]]
    description = stat(obj)
    for i = 1:length(obj.pos)
        r = description[i]
        push!(rows, [r.pos, r.nhead, r.tot, length(r.vs), r.pos == "URL" ? "Omitted." : string(r.vs[1:min(5, end)])])
    end
    return display(Markdown.MD(Markdown.Table(rows, Symbol[:l, :r, :r, :r, :l])))
end

function split(text::AbstractString, obj::HiddenMarkovModel{Tv, Ti}) where {Tv, Ti}
    codes = codeunits(text)
    nc = length(codes)
    aca = obj.aca; pos = obj.pos;
    hpr = obj.hpr; h2h = obj.h2h;
    INF = obj.MAX * nc; h2v = obj.h2v;
    matches = collect(eachmatch(aca, text))
    sort!(matches)
    nm = length(matches)
    nh = length(pos)
    dp = fill(Tv(Inf), nh, nc)
    bk = fill((0, 0), nh, nc)
    cover = fill(false, nc)
    for m in matches cover[m.s:m.t] .= true end
    pm = 1
    sc = 1
    while sc <= nc
        if !cover[sc]
            tc = sc + 1
            while tc <= nc && !cover[tc] tc += 1 end
            tc -= 1
            for sh in 1:nh
                for th in 1:nh
                    cur = (sc == 1 ? hpr[sh] : dp[sh, sc - 1]) + h2h[th, sh] + get(h2v[th], 0, INF)
                    if cur < dp[th, tc]
                        dp[th, tc] = cur
                        bk[th, tc] = (-sh, sc - 1)
                    end
                end
            end
            sc = tc + 1
        else
            while pm <= nm && matches[pm].s < sc pm += 1 end
            while pm <= nm && matches[pm].s == sc
                m = matches[pm]
                tc = m.t
                iw = m.i
                for sh in 1:nh
                    for th in 1:nh
                        cur = (sc == 1 ? hpr[sh] : dp[sh, sc - 1]) + h2h[th, sh] + get(h2v[th], iw, INF)
                        if cur < dp[th, tc]
                            dp[th, tc] = cur
                            bk[th, tc] = (sh, sc - 1)
                        end
                    end
                end
                pm += 1
            end
            sc += 1
        end
    end
    poss = String[]
    word = String[]
    th = findmin(dp[:, nc])[2]
    tc = nc
    while 1 <= tc
        sh = bk[th, tc][1]
        sc = bk[th, tc][2]
        if sh < 1
            sh = -sh
            push!(poss, pos[th] * "?")
            push!(word, String(codes[sc + 1:tc]))
        else
            push!(poss, pos[th])
            push!(word, String(codes[sc + 1:tc]))
        end
        th = sh
        tc = sc
    end
    reverse!(poss)
    reverse!(word)
    # return (res = segs, dp = dp, segs = map(x -> String(codes[x.s:x.t]), matches))
    return hcat(poss, word)
end


text:
"""
    pwd2PyPath()

Adds the present working directory to the python path variable.
"""
function pwd2PyPath()
    unshift!(PyVector(pyimport("sys")["path"]), "")
end
export pwd2PyPath

# TODO make work with arbitrary variables
"""
    @pyslice slice

Gets a slice of a python object.  Does not shift indices.
"""
macro pyslice(slice)
    @assert slice.head == :ref
    obj = slice.args[1]
    slice_ = Expr(:quote, slice)
    o = quote
        pyeval(string($slice_), $obj=$obj)
    end
    return esc(o)
end
export @pyslice


text:
# Implements a single stack Nim game.
# https://en.wikipedia.org/wiki/Nim
# > Nim is typically played as a misère game, in which the player to take the
# > last object loses.
#
# The interesting thing about this game is that we can represent it as
# executing multiple actions in a single turn. This will help us prepare
# the infrastructure for Paco Ŝako.

# We represent the board as a vector of 15 integers from {0, 1}.
# The player may take away any of the remaining tokens of the board or pass.
# Passing is only allowed, after the player has taken at least one token.

mutable struct Nim <: Game
  board :: Vector{Int}
  current_player :: Int
  actions_left :: Int
  status :: Status
end

Nim() = Nim(ones(Int, 15), 1, 3, Status())

function Base.copy(s :: Nim) :: Nim
  Nim(
    copy(s.board),
    s.current_player,
    s.actions_left,
    s.status,
  )
end

current_player(game :: Nim) :: Int = game.current_player

# Returns a list of the Indices of all legal actions
function legal_actions(game :: Nim) :: Vector{ActionIndex}
  if is_over(game)
    ActionIndex[]
  elseif game.actions_left == 0
    [16]
  else
    tokens_left = ActionIndex[ index for index in 1:15 if game.board[index] == 1 ]
    if game.actions_left == 3
      tokens_left
    else
      [tokens_left; 16]
    end
  end
end

# A action is legal, if the board position is still empty
function is_action_legal(game :: Nim, index :: ActionIndex) :: Bool
  if index == 16
    game.actions_left < 3 && !is_over(game)
  elseif game.actions_left > 0
    game.board[index] == 1 && !is_over(game)
  else
    false
  end
end

function apply_action!(game :: Nim, index :: ActionIndex) :: Nim
  @assert is_action_legal(game, index) "Action $index is not allowed."

  if index == 16
    game.current_player = -game.current_player
    game.actions_left = 3
    game
  else
    game.board[index] = 0
    game.actions_left -= 1
    # Update the status cache
    game.status = nim_status(game.board, game.current_player)
    game
  end
end

# execute this function after a player takes a token to decide the game status.
function nim_status(board :: Vector{Int}, current_player :: Int) :: Status
  # The player that takes the last tokes looses the game
  if iszero(board)
    Status(-current_player)
  else
    Status()
  end
end

status(game :: Nim) :: Status = game.status

policy_length(:: Type{Nim}) :: Int = 16

# Size of the data representation of the game
# T T T T T T T T T T T T T T T A A A
# Where T represents a token on the board and M represents an action left.
Base.size(:: Type{Nim}) :: Tuple{Int, Int, Int} = (15 + 3, 1, 1)

# Data representation of the game as layered 2d image
function representation(game :: Nim) :: Array{Float32, 3}
  reshape([game.board; represent_actions_left(game.actions_left)], (15+3, 1, 1))
end

function represent_actions_left(actions_left :: Int) :: Vector{Float32}
  if actions_left == 0
    [0, 0, 0]
  elseif actions_left == 1
    [1, 0, 0]
  elseif actions_left == 2
    [1, 1, 0]
  else
    [1, 1, 1]
  end
end

# TODO: Define a probabilistic augument method.

function draw(game :: Nim) :: Nothing
  tokenSymbols = Dict(1 => "T", 0 => "_")
  actionSymbols = Dict(1 => "A", 0 => "_")

  for i in 1:15
    print(" $(tokenSymbols[game.board[i]])")
  end
  print(" |")
  actions = represent_actions_left(game.actions_left)
  for i in 1:3
    print(" $(actionSymbols[actions[i]])")
  end
  println()
end

max_stars_repo_name: roSievers/jtac


text:
function maketable2(panel::DataFrame; trialrunonly::Bool=trialrunonly,
    runpanela::Bool=true,
    runpanelb::Bool=true,
    runpanelc::Bool=true,
    runpaneld::Bool=true
    )::Nothing
  #load the data we will work with
  #panel::DataFrame = constructportfolios(refreshportfolios=false, trialrunonly=trialrunonly)
  sort!(panel, [:permno, :date])
  apanel::DataFrame = panel[panel.annflag,:]

  ################common data
  cqports::PortfolioConstructions = leverageconstructions()
  T2_FNAMES::Vector{Symbol} = CQUART_FNAMES_D
  T2_FVALS::Vector{Symbol} = CQUART_FVALS_D
  T2_FGROUPS::Vector{Symbol} = CQUART_FGROUPS_D
  T2_FNAMES_ALT::Vector{Symbol} = CQUART_FNAMES_ALT_D
  T2_FGROUPS_ALT::Vector{Symbol} = CQUART_FGROUPS_ALT_D

  ################Table A
  T2_PANEL_A_ROWKEYS::Vector{Symbol} = [:mean, :std, :stdxt]

  T2_PANEL_A_ROWLABEL::Dict = Dict(
    :mean=>"Mean",
    :std=>"SD",
    :stdxt=>"SD(X,T)")

  T2_PANEL_A_IDX::Dict = Dict(
    :mean=>1,
    :std=>2,
    :stdxt=>3)
  T2_PANEL_A_NAME::String = "t2panela-$(REPLICATION_TYPE[])"

  runpanela && tablexpanelA(apanel,
    fvals = T2_FVALS,
    rowkeys=T2_PANEL_A_ROWKEYS,
    rowlabel=T2_PANEL_A_ROWLABEL,
    idx=T2_PANEL_A_IDX,
    tablename=T2_PANEL_A_NAME)

  #####################TABLE B
  T2_PANEL_B_ROWKEYS::Vector{Symbol} = [
    :leverageQmeans,
    :quartilenum,
    :LLsd,
    :sd,
    :Δsd,
    :LLret12m,
    :ret12m,
    :Δret12m]

  T2_PANEL_B_ROWLABEL::Dict = Dict(
    :leverageQmeans=>"Leverage Q Means",
    :quartilenum=>"Quartile \\#",
    :LLsd=>"\\smallskip SD Net Lagged\$^2\$",
    :sd=>"SD Net Lead",
    :Δsd=>"SD Net 2Y Delta",
    :LLret12m=>"\\smallskip Compound Raw Lagged\$^2\$",
    :ret12m=>"Compound Raw Lead",
    :Δret12m=>"Compound Raw 2Y Delta")

  T2_PANEL_B_IDX::Dict = Dict(
    T2_PANEL_B_ROWKEYS[i]=>i for i ∈ 1:(length(T2_PANEL_B_ROWKEYS)))

  #the below is to help with future abstraction if necessary
  T2_PANEL_B_VALCOLIDX::Dict= Dict(f=>f for f ∈ T2_FVALS)

  #these are pairs of lagged and unlagged fields
  T2_PANEL_B_VOLFIELDS::NTuple{2,Symbol} = (:LLvol252d, :vol252d)
  T2_PANEL_B_RETFIELDS::NTuple{2,Symbol} = (:LLret12m, :ret12m)

  T2_PANEL_B_NAME::String = "t2panelb-$(REPLICATION_TYPE[])"

  runpanelb && tablexpanelB(apanel,
    fvals = T2_FVALS,
    rowkeys=T2_PANEL_B_ROWKEYS,
    rowlabel=T2_PANEL_B_ROWLABEL,
    idx=T2_PANEL_B_IDX,
    valcolidx = T2_PANEL_B_VALCOLIDX,
    tablename=T2_PANEL_B_NAME,
    volfields=T2_PANEL_B_VOLFIELDS,
    retfields=T2_PANEL_B_RETFIELDS)

  ###################PANEL C############

  T2_PANEL_C_ROWKEYS::Vector{Symbol} = [:constant, :plusxmkt, :plussmbhml, :plusrmwcma, :plusumd]

  T2_PANEL_C_ROWLABEL::Dict = Dict(
    :constant=>"Constant",
    :plusxmkt=>"+XMKT",
    :plussmbhml=>"+SMB+HML",
    :plusrmwcma=>"+RMW+CMA",
    :plusumd=>"+UMD")

  T2_PANEL_C_CONTROLS::Dict = Dict(
    :constant=>CONTROL0,
    :plusxmkt=>[:mkt],
    :plussmbhml=>[:mkt, :P_ff3m_smb,	:P_ff3m_hml],
    :plusrmwcma=>[:mkt, :P_ff5_smb,	:P_ff5_hml,	:P_ff5_rmw,	:P_ff5_cma],
    :plusumd=>[:mkt, :P_ff5_smb,	:P_ff5_hml,	:P_ff5_rmw,	:P_ff5_cma, :P_ff3m_umd])

  T2_PANEL_C_FOCALS::Dict = Dict(
    :constant=>T2_FNAMES,
    :plusxmkt=>T2_FNAMES,
    :plussmbhml=>T2_FNAMES,
    :plusrmwcma=>T2_FNAMES,
    :plusumd=>T2_FNAMES)

  T2_PANEL_C_NAME::String = "t2panelc-$(REPLICATION_TYPE[])"

  T2_PANEL_C_MIN_ROWS::Int = 24

  runpanelc && tablexpanelC(panel,
    fvals=T2_FVALS,
    fgroups=T2_FGROUPS,
    Fnames = T2_FNAMES,
    rowkeys=T2_PANEL_C_ROWKEYS,
    rowlabel=T2_PANEL_C_ROWLABEL,
    controls=T2_PANEL_C_CONTROLS,
    focals=T2_PANEL_C_FOCALS,
    tablename=T2_PANEL_C_NAME,
    minrowspergroup = T2_PANEL_C_MIN_ROWS
  )

  #########################PANEL D#########################
  T2_PANEL_D_CONTROLS::Dict = Dict(
    :quartiles=>CONTROL0,
    :quartiles4ctrl=>T1_CONTROL4,
    :quartiles10ctrl=>T1_CONTROL10,
    :values=>CONTROL0,
    :values4ctrl=>T1_CONTROL4,
    :values10ctrl=>T1_CONTROL10,
    :quartiles_ac=>CONTROL0,
    :quartiles_lret=>T1_CONTROL10)

  T2_PANEL_D_RET::Dict = Dict(
    :quartiles=>:ret,
    :quartiles4ctrl=>:ret,
    :quartiles10ctrl=>:ret,
    :values=>:ret,
    :values4ctrl=>:ret,
    :values10ctrl=>:ret,
    :quartiles_ac=>:ret,
    :quartiles_lret=>:lret)

  T2_PANEL_D_ROWKEYS::Vector{Symbol} = [:quartiles, :quartiles4ctrl, :quartiles10ctrl, :values,
    :values4ctrl, :values10ctrl, :quartiles_ac, :quartiles_lret]

  T2_PANEL_D_ROWLABEL::Dict = Dict(
    :quartiles=>"Leverage Quartiles",
    :quartiles4ctrl=>"... +4 vars",
    :quartiles10ctrl=>"... +10 vars",
    :values=>"Leverage Values",
    :values4ctrl=>"... +4 vars",
    :values10ctrl=>"... +10 vars",
    :quartiles_ac=>"\\midrule Asset-Controlled Quartiles",
    :quartiles_lret=>"Log Returns +10 vars")

  T2_PANEL_D_FOCALS::Dict = Dict(
    :quartiles=>T2_FGROUPS,
    :quartiles4ctrl=>T2_FGROUPS,
    :quartiles10ctrl=>T2_FGROUPS,
    :values=>T2_FVALS,
    :values4ctrl=>T2_FVALS,
    :values10ctrl=>T2_FVALS,
    :quartiles_ac=>T2_FGROUPS_ALT,
    :quartiles_lret=>T2_FGROUPS)

  T2_PANEL_D_NAME::String = "t2paneld-$(REPLICATION_TYPE[])"

  T2_PANEL_D_MIN_ROWS::Int = 24

  runpaneld && tablexpanelD(panel,
      fvals=T2_FVALS,
      fgroups=T2_FGROUPS,
      ret = T2_PANEL_D_RET,
      rowkeys=T2_PANEL_D_ROWKEYS,
      rowlabel=T2_PANEL_D_ROWLABEL,
      controls=T2_PANEL_D_CONTROLS,
      focals=T2_PANEL_D_FOCALS,
      tablename=T2_PANEL_D_NAME,
      minrowspergroup = T2_PANEL_D_MIN_ROWS
    )

  return nothing
end

max_stars_repo_name: clintonTE/CCA

max_issues_repo_name: clintonTE/CCA

max_forks_repo_name: clintonTE/CCA


text:
abstract type Abstract_GrB_Struct end
abstract type Abstract_GrB_Type <: Abstract_GrB_Struct end
abstract type AbstractDescriptor <: Abstract_GrB_Struct end
abstract type AbstractOp end
abstract type AbstractUnaryOp <: AbstractOp end
abstract type AbstractBinaryOp <: AbstractOp end
abstract type AbstractSelectOp <: AbstractOp end
abstract type AbstractMonoid <: AbstractOp end
abstract type AbstractSemiring <: AbstractOp end

isloaded(o::AbstractOp) = !isempty(o.pointers)

function validtypes(o::AbstractOp)
    isloaded(o) || load(o)
    return keys(o.pointers)
end

function Base.getindex(o::AbstractOp, t::DataType)
    isloaded(o) || load(o)
    getindex(o.pointers, t)
end


text:
@doc """
    is_next(token)

!!! note "Advanced"
    is_next(token; <keyword arguments>)

Check if `token` is the next token in the speech; if yes, return `true`, else, return `false`. If `token` is an array of strings, check if any of them is the next token in the speech. A call to `is_next` does by default not consume the next token (nor fix the recognizer to be used for its recognition).

# Arguments
- `token::String | AbstractArray{String}`: the token(s) to compare the next token in the speech against.
!!! note "Advanced keyword arguments"
	- `modelname::String=DEFAULT_MODEL_NAME`: the name of the model to be used for the recognition in the token comparison (the name must be one of the keys of the modeldirs dictionary passed to `init_jsi`).
	- `consume_if_match::Bool=false`: whether the next token is to be consumed in case of a match.
	- `timeout::Float64=Inf`: timeout after which to abort waiting for a next token to be spoken.
	- `use_max_accuracy::Bool=false`: whether to use maxium accuracy for the recognition of the next token (rather than maximum speed). It is only recommended to set `use_max_accuracy=true` if the next cluster of tokens is in any case to be recognised with maxium accuracy (typically used with a free speech recognizer, i.e. with a large vocabulary),

See also: [`init_jsi`](@ref)
"""
is_next

let
    global next_token, is_next, _is_next, are_next, recognizer, force_reset_previous, all_consumed, was_partial_recognition, reset_all, do_delayed_resets # NOTE: recogniser needs to be declared global here, even if elsewhere the method created here might not be used, as else we do not have access to the other reconizer methods here.
    recognizers_to_reset = Vector{PyObject}()
    active_recognizer::Union{Nothing, PyObject} = nothing
    was_partial_result = false
    token_buffer = Vector{String}()
    i = 0
	all_consumed()::Bool            = (i >= length(token_buffer))
	was_partial_recognition()::Bool = was_partial_result

    function next_token(recognizer::PyObject, noise_tokens::AbstractArray{String}; consume::Bool=true, timeout::Float64=Inf, use_partial_recognitions::Bool=false, restart_recognition::Bool=false, ignore_unknown::Bool=true)
		ignore_tokens = ignore_unknown ? [noise_tokens..., UNKNOWN_TOKEN] : noise_tokens
		if (recognizer != active_recognizer && !isnothing(active_recognizer) && !isempty(token_buffer) && i==0) # If the recognizer was changed despite that tokens were recognized, but none was consumed, then we will always want to restart recognition.
			 restart_recognition = true
		end
		if (!was_partial_result) do_delayed_resets(;hard=false) end                                 # When a result was found, then soft resets that were previously delayed to keep latency minimal can now be performed.
		if (recognizer != active_recognizer && was_partial_result && !isnothing(active_recognizer)) # Reset the active recognizer if a new recognizer will become active. #NOTE: Reset after a result is not needed and a hard reset leads to the following Vosk error in that case: ASSERTION_FAILED (VoskAPI:ComputeFinalCosts():lattice-faster-decoder.cc:540) Assertion failed: (!decoding_finalized_)
			if restart_recognition push!(recognizers_to_reset, active_recognizer)
			else                   reset(active_recognizer; hard=true)
			end
		end
		t  = 0.0
        t0 = tic()
        token = ""
        while token == "" && (t < timeout)
			if all_consumed() || (recognizer != active_recognizer) || (!use_partial_recognitions && was_partial_result && !all_consumed())  # NOTE: if there are tokens left in the buffer that have been recognised with a different recogniser we cannot use them for safety reasons.
				reset_audio_buffer = (i >= length(token_buffer)) && !was_partial_result && !restart_recognition                             # Reset the audio buffer if all tokens in the token buffer were consumed, the last recognition was not partial and we do not want to restart (meaning here redo) the recognition. This means that the last recognition is now final and cannot be revised anymore.
				@debug "==========================================\nRecognition parameters:" use_partial_recognitions timeout restart=restart_recognition reset_audio_buffer
	            if use_partial_recognitions
	                text, is_partial_result, has_timed_out = next_partial_recognition(recognizer; timeout=timeout, restart=restart_recognition, reset_audio_buffer=reset_audio_buffer)
	            else
	                text = next_recognition(recognizer; timeout=timeout, restart=restart_recognition, reset_audio_buffer=reset_audio_buffer)
	                is_partial_result = false
	            end
				if restart_recognition  || ((recognizer == active_recognizer) && was_partial_result)
					tokens = filter(x -> x ∉ ignore_tokens, split(text))
					if !startswith(join(tokens, " "), join(token_buffer[1:i], " "))  # NOTE: a maybe cheaper, but less safe alternative would probably be `!issubset(token_buffer[1:i], tokens)`. It is less safe as issubset will not guarantee the order (for the same reason, it might be more expensive in the end: more cases to test...).
						@debug "Insecurity - after restart?:" restart=restart_recognition
						msg = "Insecurity in recognition: the tokens recognised in the previous (partial) recognition that have been consumed are not a subset of the tokens now recognised (token_buffer: $(token_buffer[1:i]); tokens: $tokens)"
						if (is_partial_result) recognizer.Reset() end                #NOTE: Reset after a result is not needed and leads to the following Vosk error: ASSERTION_FAILED (VoskAPI:ComputeFinalCosts():lattice-faster-decoder.cc:540) Assertion failed: (!decoding_finalized_)
				        reset_token_buffer()
						reset_audio()
						active_recognizer = nothing
						was_partial_result = false
						@InsecureRecognitionException(msg)
					end
					token_buffer = tokens
				else
		            token_buffer = filter(x -> x ∉ ignore_tokens, split(text))
		            i = 0
				end
				was_partial_result = is_partial_result
				active_recognizer  = recognizer
	        end
	        if i < length(token_buffer)
				i += 1
				@debug "" token_buffer token=token_buffer[i]
				token = token_buffer[i]
				if (!consume) i-=1 end
			end
			restart_recognition = false  # A restart needs to happen in the first iteration if set, then not anymore.
			t = toc(t0)
        end
        return token
    end

	function next_token(recognizer_info::Tuple{Symbol,Symbol,<:AbstractArray{String},String}, noise_tokens::AbstractArray{String}; consume::Bool=true, timeout::Float64=Inf, use_partial_recognitions::Bool=false, force_dynamic_recognizer::Bool=false, ignore_unknown::Bool=true)
		if (i >= length(token_buffer)) && !was_partial_result && !force_dynamic_recognizer  # If all tokens in the buffer were consumed and the last recognition was not partial, then we can swap the recogniser without having to consider the last recognitions (i.e., get the recognizer created in init_jsi)...
			f_name, voicearg = recognizer_info[1:2]
			next_token(recognizer(f_name, voicearg), noise_tokens; consume=consume, timeout=timeout, use_partial_recognitions=use_partial_recognitions, ignore_unknown=ignore_unknown)
		else                                                                                # ...else, we are swapping the recognizer while the recognition was only partial and/or not all tokens consumed. Thus, the new recognizer needs to include the audio of the last partial recognition and must be able to recognize the already consumed tokens, i.e., be dynamically created.
			valid_input, modelname = recognizer_info[3:4]
			next_token(recognizer(valid_input, noise_tokens; modelname=modelname), noise_tokens; consume=consume, timeout=timeout, use_partial_recognitions=use_partial_recognitions, restart_recognition=true, ignore_unknown=ignore_unknown)
        end
	end

	function next_token(valid_input::AbstractArray{String}; modelname::String=DEFAULT_MODEL_NAME, noise_tokens::AbstractArray{String}=noises(modelname), consume::Bool=true, timeout::Float64=Inf, use_partial_recognitions::Bool=false, ignore_unknown::Bool=true)
		recognizer_info = (Symbol(), Symbol(), valid_input, modelname)
		next_token(recognizer_info, noise_tokens; consume=consume, timeout=timeout, use_partial_recognitions=use_partial_recognitions, force_dynamic_recognizer=true, ignore_unknown=ignore_unknown)
	end

	#NOTE: this function will only consume the next token if `consume_if_match` is set true and the token matches.
	function _is_next(token::Union{String,AbstractArray{String}}, recognizer_or_info::Union{PyObject, Tuple{Symbol,Symbol,<:AbstractArray{String},String}}, noise_tokens::AbstractArray{String}; consume_if_match::Bool=false, timeout::Float64=Inf, use_partial_recognitions::Bool=false, force_dynamic_recognizer::Bool=false, ignore_unknown::Bool=false)
		test_token = next_token(recognizer_or_info, noise_tokens; consume=true, timeout=timeout, use_partial_recognitions=use_partial_recognitions, force_dynamic_recognizer=force_dynamic_recognizer, ignore_unknown=ignore_unknown)
		is_match = isa(token, String) ? (test_token == token) : (test_token in token)
		if !(consume_if_match && is_match) i -= 1 end # Correct the token_buffer index in order to return the same token again in the next next_token call.
		return is_match
	end

	function is_next(token::Union{String,AbstractArray{String}}, valid_input::AbstractArray{String}; modelname::String=DEFAULT_MODEL_NAME, noise_tokens::AbstractArray{String}=noises(modelname), consume_if_match::Bool=false, timeout::Float64=Inf, use_max_accuracy::Bool=true, ignore_unknown::Bool=false)
		recognizer_info = (Symbol(), Symbol(), valid_input, modelname)
		_is_next(token, recognizer_info, noise_tokens; consume_if_match=consume_if_match, timeout=timeout, use_partial_recognitions=!use_max_accuracy, force_dynamic_recognizer=true, ignore_unknown=ignore_unknown)
	end

	function is_next(token::Union{String,AbstractArray{String}}; modelname::String=DEFAULT_MODEL_NAME, noise_tokens::AbstractArray{String}=noises(modelname), consume_if_match::Bool=false, timeout::Float64=Inf, use_max_accuracy::Bool=true, ignore_unknown::Bool=false)
		valid_input = isa(token, String) ? [token] : token
		is_next(token, valid_input; modelname=modelname, noise_tokens=noise_tokens, consume_if_match=consume_if_match, timeout=timeout, use_max_accuracy=use_max_accuracy, ignore_unknown=ignore_unknown)
	end

	#NOTE: this function will only consume the next tokens if `consume_if_match` is set true and all the tokens match.
	function _are_next(token::Union{String,AbstractArray{String}}, recognizer_or_info::Union{PyObject, Tuple{Symbol,Symbol,<:AbstractArray{String},String}}, noise_tokens::AbstractArray{String}; consume_if_match::Bool=false, timeout::Float64=Inf, use_partial_recognitions::Bool=false, force_dynamic_recognizer::Bool=false, ignore_unknown::Bool=false)
		match = String[]
		test_token = next_token(recognizer_or_info, noise_tokens; consume=true, timeout=timeout, use_partial_recognitions=use_partial_recognitions, force_dynamic_recognizer=force_dynamic_recognizer, ignore_unknown=ignore_unknown)
		consumed = 1
		is_match = isa(token, String) ? (test_token == token) : (test_token in token)
		if is_match
			push!(match, test_token)
			while is_match && !all_consumed()
				test_token = next_token(recognizer_or_info, noise_tokens; consume=true, timeout=timeout, use_partial_recognitions=use_partial_recognitions, force_dynamic_recognizer=force_dynamic_recognizer, ignore_unknown=ignore_unknown)
				consumed += 1
				is_match = isa(token, String) ? (test_token == token) : (test_token in token)
				if is_match
					push!(match, test_token)
			    else
					i -= 1
					consumed -= 1 # Correct the token_buffer index in order to return the same token again in the next next_token call.
				end
			end
			if (!is_match) match = String[] end
		end
		if !(consume_if_match && is_match) i -= consumed end # Correct the token_buffer index in order to return the same tokens again in the next next_token call.
		return is_match, match
	end

	function are_next(token::Union{String,AbstractArray{String}}, valid_input::AbstractArray{String}; modelname::String=DEFAULT_MODEL_NAME, noise_tokens::AbstractArray{String}=noises(modelname), consume_if_match::Bool=false, timeout::Float64=Inf, use_max_accuracy::Bool=true, ignore_unknown::Bool=false)
		recognizer_info = (Symbol(), Symbol(), valid_input, modelname)
		_are_next(token, recognizer_info, noise_tokens; consume_if_match=consume_if_match, timeout=timeout, use_partial_recognitions=!use_max_accuracy, force_dynamic_recognizer=true, ignore_unknown=ignore_unknown)
	end

	function are_next(token::Union{String,AbstractArray{String}}; modelname::String=DEFAULT_MODEL_NAME, noise_tokens::AbstractArray{String}=noises(modelname), consume_if_match::Bool=false, timeout::Float64=Inf, use_max_accuracy::Bool=true, ignore_unknown::Bool=false)
		valid_input = isa(token, String) ? [token] : token
		are_next(token, valid_input; modelname=modelname, noise_tokens=noise_tokens, consume_if_match=consume_if_match, timeout=timeout, use_max_accuracy=use_max_accuracy, ignore_unknown=ignore_unknown)
	end

	# Create dynamically a recognizer based on the valid input, model and the consumed tokens recognised in the current audio_buffer.
	function recognizer(valid_input::AbstractArray{String}, noise_tokens::AbstractArray{String}; modelname::String=DEFAULT_MODEL_NAME)
		ignore_tokens = [noise_tokens..., UNKNOWN_TOKEN]
		consumed_tokens = join(token_buffer[1:i], " ")
		if isempty(consumed_tokens)
			valid_strings = [valid_input..., ignore_tokens...]
		else
			valid_strings = map([valid_input..., ignore_tokens...]) do x
				join((consumed_tokens, x), " ")
			end
		end
		@debug "Dynamic recognizer created for the following grammar: $valid_strings"
		grammar = json(valid_strings)
		return Vosk.KaldiRecognizer(model(modelname), SAMPLERATE, grammar)
	end

    function reset_token_buffer()
        token_buffer = Vector{String}()
        i = 0
    end

	# #NOTE: this function which is to be called after consuming a partial result, continues with the recognition until a result is obtained.
	function reset(recognizer::PyObject; timeout::Float64=60.0, hard::Bool=true)
		@debug "Resetting recognizer ($(hard ? "hard" : "soft") reset)."
		if (hard) recognizer.Reset()                                                                    # NOTE: a hard reset may lead to a audio cut in the middle of speech and as a result recognise some tokens twice etc.
		else      next_recognition(recognizer; timeout=timeout, restart=true, reset_audio_buffer=false) # NOTE: as soft reset will lead to lost tokens, if it is not followed by a restart
		end
	    return
	end

	function do_delayed_resets(; timeout::Float64=60.0, hard::Bool=true)
		for r in recognizers_to_reset
			reset(r; timeout=timeout, hard=hard)
		end
		recognizers_to_reset = Vector{PyObject}()
	end

	function force_reset_previous(recognizer::Union{PyObject, Nothing}; timeout::Float64=60.0, hard::Bool=false)
		if (recognizer != active_recognizer && was_partial_result && !isnothing(active_recognizer)) # Reset the active recognizer if a new recognizer will become active.
			reset(active_recognizer; timeout=timeout, hard=hard)
			do_delayed_resets(;timeout=timeout, hard=hard)
			reset_token_buffer()
			reset_audio()
			active_recognizer = nothing
			was_partial_result = false
		end
	end

	function reset_all(; timeout::Float64=60.0, hard::Bool=false, exclude_active::Bool=false)
		if (!exclude_active) reset(active_recognizer; timeout=timeout, hard=hard) end
		do_delayed_resets(;timeout=timeout, hard=hard)
		reset_token_buffer()
		reset_audio()
		active_recognizer = nothing
		was_partial_result = false
	end

end

let
    global next_recognition, next_partial_recognition, t0_latency, reset_audio
	audio_buffer          = zeros(UInt8, AUDIO_ALLOC_GRANULARITY)
	audio_chunk           = zeros(UInt8, AUDIO_READ_MAX)
	i                     = 0
    partial_result_old    = ""
	has_timed_out         = false
    t_read_sum            = 0.0
    t_read_max            = 0.0
    t_recognize_sum       = 0.0
    t_recognize_max       = 0.0
    t_sum                 = 0.0
    t_max                 = 0.0
    bytes_read_sum        = 0
    it_result             = 0 # iteration (while not converged to result)
    _t0_latency::Float64  = 0.0
    t0_latency()::Float64 = _t0_latency
	reset_audio()         = (i = 0; return)

    function next_partial_recognition(recognizer::PyObject; timeout::Float64=60.0, restart::Bool=false, reset_audio_buffer::Bool=false)
        is_partial_result = true
        partial_result    = ""
        text              = ""
		if (reset_audio_buffer) i = 0 end
		if (has_timed_out) restart = true end
		if (restart) partial_result_old = "" end
		i0 = i
		it = 0
        t  = 0.0
        t0 = tic()
        while (text == "") && (t < timeout)
            t1 = tic()
			if i + AUDIO_READ_MAX > length(audio_buffer)
				resize!(audio_buffer, length(audio_buffer) + AUDIO_ALLOC_GRANULARITY)
			end
			tic();  bytes_read = readbytes!(recorder(), audio_chunk);  t_read_sum+=toc(); t_read_max=max(t_read_max,toc()) #; println("t_read: $(toc())")
            if bytes_read > 0
				it += 1
                _t0_latency = tic() # NOTE: when the while loop is left, this value will contain the time right before the call to the recognizer, which lead to a successful partial or full recognition. It allows to compute the latency from when the reading of a command was completed (which can be considered equivalent to the time it the speaker completed it, if bytes_read per iteration is small) to the invocation of a command (toc() needs to be called right before its invocation).
				audio_buffer[i+1:i+bytes_read] .= audio_chunk[1:bytes_read]
				i += bytes_read
				if restart && (it == 1)
					audio = pybytes(audio_buffer[1:i])                                                                #NOTE: an allocation is unavoidable in the case of a restrt as pybytes cannot handle views. However, it is required only in the first iteration.
				else
					audio = (bytes_read < AUDIO_READ_MAX) ? pybytes(audio_chunk[1:bytes_read]) : pybytes(audio_chunk) #NOTE: an allocation should only be done if bytes_read < AUDIO_READ_MAX (required in order to avoid having random data at the end of the audio_chunk).
				end
                tic();  exitcode = recognizer.AcceptWaveform(audio);  t_recognize_sum+=toc(); t_recognize_max=max(t_recognize_max,toc())  #; println("t_recognize: $(toc())")
                is_partial_result = (exitcode == 0)
                if is_partial_result
                    partial_result = recognizer.PartialResult()
                    if (partial_result != partial_result_old) text = (JSON.parse(partial_result))["partial"] end
                else
                    result = recognizer.Result()
                    text = (JSON.parse(result))["text"]
                end
                bytes_read_sum += bytes_read
				if (i0 == 0 || has_timed_out) && (i >= 2*AUDIO_HISTORY_MIN) && (text == "")   # If we have started without audio history to consider, then we cut off silence at its beginning every now and then (to avoid that if we restart, we have a lot of silence to process). We know that what we cut off is silence, because else we would have obtained a partial recognition (AUDIO_HISTORY_MIN must be bigger than the amount of audio that maximally leads to a partial recognition if it is not silence).
					@debug "Cutting off silence in audio beginning:" i0 has_timed_out i AUDIO_HISTORY_MIN text
					audio_buffer[1:AUDIO_HISTORY_MIN] .= @view audio_buffer[i-AUDIO_HISTORY_MIN+1:i]
					i = AUDIO_HISTORY_MIN
				end
            end
            t = toc(t0); #; println("t: $t")
            t_sum+=toc(t1); t_max=max(t_max,toc(t1))
        end
		it_result += it
        @debug join([".........................................."
			"Iterations: $it_result (average Bytes/it_result: $(bytes_read_sum/it_result))"
        	"READ:      throughput [KB/s]: $(round(bytes_read_sum/t_read_sum/1e3, sigdigits=2)) (average [s]: $(round(t_read_sum/it_result, sigdigits=2)), max [s]: $(round(t_read_max, sigdigits=2)), sum [s]: $(round(t_read_sum, sigdigits=2)))"
        	"RECOGNISE: throughput [KB/s]: $(round(bytes_read_sum/t_recognize_sum/1e3, sigdigits=2)) (average [s]: $(round(t_recognize_sum/it_result, sigdigits=2)) max [s]: $(round(t_recognize_max, sigdigits=2)), sum [s]: $(round(t_recognize_sum, sigdigits=2)))"
        	"TOTAL:     effective throughput [KB/s]: $(round(bytes_read_sum/t_sum/1e3, sigdigits=2)) (average [s]: $(round(t_sum/it_result, sigdigits=2)) max [s]: $(round(t_max, sigdigits=2)), sum [s]: $(round(t_sum, sigdigits=2)))"
			is_partial_result ? "Partial result: $text" : "Result: $text"
		], "\n")
        if is_partial_result
            partial_result_old = partial_result
        else
            partial_result_old = ""
            t_read_sum         = 0.0
            t_recognize_sum    = 0.0
            t_sum              = 0.0
            bytes_read_sum     = 0
            it_result          = 0
        end
        has_timed_out = (t >= timeout)
		if (has_timed_out) _t0_latency=0.0 end
        return text, is_partial_result, has_timed_out
    end

    function next_recognition(recognizer::PyObject; timeout::Float64=120.0, restart::Bool=false, reset_audio_buffer::Bool=false)
        is_partial_result = true
        text = ""
        t  = 0.0
        t0 = tic()
		text, is_partial_result = next_partial_recognition(recognizer; timeout=timeout, restart=restart, reset_audio_buffer=reset_audio_buffer)
		t = toc(t0)
        while is_partial_result && (t < timeout)
            text, is_partial_result = next_partial_recognition(recognizer; timeout=timeout, restart=false, reset_audio_buffer=false)
            t = toc(t0)
        end
        return text
    end

end