text:
using CombinedUncertainDiffEq
using ComponentArrays
using ConcreteStructs
using DifferentialEquations
using DiffEqUncertainty
using Plots
using UnPack

## Functions
# Two-dimensional vector with x and y components
Vec2(; x=0.0, y=0.0) = ComponentArray(; x, y)

# Dynamics
function simple_lander_2D!(D, vars, params, t; T=0.0, θ=0.0)
    @unpack r, v = vars
    @unpack g, m = params

    Fx, Fy = T .* sincos(θ)

    D.r .= v
    D.v.x = Fx/m
    D.v.y = -g + Fy/m

    return nothing
end

# For applying control inputs functions through keyword arguments
function apply_inputs(func; kwargs...)
    simfun(dx, x, p, t) = func(dx, x, p, t; map(f->f(x,p,t), (;kwargs...))...)
    return simfun
end

# Neural stuff
dense_layer(n_in, n_out) = ComponentArray(W=rand(n_out, n_in), b=zeros(Float32, n_out))

# Controls
@concrete struct Saturated <: Function
    f
    lb
    ub
end
(f::Saturated)(args...; kwargs...) = clamp(f.f(args...; kwargs...), f.lb, f.ub)

function dense_neural_controller(layer_size, act_fun=identity, out_fun=only)
    layers = ComponentArray(;
        L1 = dense_layer(2, layer_size),
        L2 = dense_layer(layer_size, 1),
    )

    f = function (x, p, t)
        @unpack L1, L2 = p
        out = act_fun.(L2.W * act_fun.(L1.W * x .+ L1.b) .+ L2.b)
        return out_fun(out)
    end

    return layers, f
end

soft_sign(x, α=1e2) = 1 / (1 + exp(-α*x))
≤ₛ(x, y) = soft_sign(y - x)

# Loss function for training
meets_requirements(sol, reqs) = all(reqs.lower .≤ₛ sol.u[end] .≤ₛ reqs.upper)

# Return true if vehicle hits ground
hits_ground(vars, t, integrator) = vars.r.y

## Single-run Problem Definition
# Control functions
thrust = function (x, p, t)
    @unpack mag, tstart, duration = p.control.thrust
    return if tstart ≤ t ≤ tstart+duration
        mag
    else
        zero(mag)
    end
end
angle = Saturated((x, p, t) -> -0.1x.v.x - 0.005x.r.x, -π/2, π/2)

# Initial conditions
ic = ComponentArray{Float64}(
    r = Vec2(y=127),
    v = Vec2(x=15, y=-50),
)

# Parameters
p = ComponentArray(;
    g = 9.80665,
    m = 50,
    control = (
        thrust = (
            mag = 1000,
            tstart = 0,
            duration = 5,
        ),
    ),
)

# ODE callback function (stop simulation when vehicle hits the ground)
cb = ContinuousCallback(hits_ground, terminate!, save_positions=(false,false))

# Single-run problem
sim_fun = apply_inputs(simple_lander_2D!; T=thrust, θ=angle)
prob = ODEProblem(sim_fun, ic, (0.0, 100.0), p; callback=cb)

## Koopman Problem Definition
uncertain_ic = ComponentArray{Any}(
    r = (
        x = 0.0,
        y = 120..150,
    ),
    v = (
        x = 0 ± 20,
        y = -45 ± 5,
    ),
)

uncertain_p = ComponentArray{Any}(;
    g = 9.80665,
    m = 50 ± 3,
    control = (
        thrust = (
            mag = 1000 ± 10,
            tstart = 0.0..0.5,
            duration = 5 ± 0.1,
        ),
    ),
)

requirements = ComponentArray(
    lower = (
        r = (
            x = -50,
            y = -50,
        ),
        v = (
            x = -2,
            y = -5,
        ),
    ),
    upper = (
        r = (
            x = 50,
            y = 50,
        ),
        v = (
            x = 2,
            y = 5,
        ),
    )
)

##
koop_sol = koopman_expectation(
    sol -> meets_requirements(sol, requirements),
    prob,
    uncertain_ic,
    uncertain_p,
    Tsit5();
    quad_alg = CubaDivonne(),
)


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

@doc raw"""IngressRule represents the rules mapping the paths under a specified host to the related backend services. Incoming requests are first evaluated for a host match, then routed to the backend associated with the matching IngressRuleValue.

    IoK8sApiNetworkingV1beta1IngressRule(;
        host=nothing,
        http=nothing,
    )

    - host::String : Host is the fully qualified domain name of a network host, as defined by RFC 3986. Note the following deviations from the \"host\" part of the URI as defined in the RFC: 1. IPs are not allowed. Currently an IngressRuleValue can only apply to the    IP in the Spec of the parent Ingress. 2. The `:` delimiter is not respected because ports are not allowed.    Currently the port of an Ingress is implicitly :80 for http and    :443 for https. Both these may change in the future. Incoming requests are matched against the host before the IngressRuleValue. If the host is unspecified, the Ingress routes all traffic based on the specified IngressRuleValue.
    - http::IoK8sApiNetworkingV1beta1HTTPIngressRuleValue
"""
mutable struct IoK8sApiNetworkingV1beta1IngressRule <: SwaggerModel
    host::Any # spec type: Union{ Nothing, String } # spec name: host
    http::Any # spec type: Union{ Nothing, IoK8sApiNetworkingV1beta1HTTPIngressRuleValue } # spec name: http

    function IoK8sApiNetworkingV1beta1IngressRule(;host=nothing, http=nothing)
        o = new()
        validate_property(IoK8sApiNetworkingV1beta1IngressRule, Symbol("host"), host)
        setfield!(o, Symbol("host"), host)
        validate_property(IoK8sApiNetworkingV1beta1IngressRule, Symbol("http"), http)
        setfield!(o, Symbol("http"), http)
        o
    end
end # type IoK8sApiNetworkingV1beta1IngressRule

const _property_map_IoK8sApiNetworkingV1beta1IngressRule = Dict{Symbol,Symbol}(Symbol("host")=>Symbol("host"), Symbol("http")=>Symbol("http"))
const _property_types_IoK8sApiNetworkingV1beta1IngressRule = Dict{Symbol,String}(Symbol("host")=>"String", Symbol("http")=>"IoK8sApiNetworkingV1beta1HTTPIngressRuleValue")
Base.propertynames(::Type{ IoK8sApiNetworkingV1beta1IngressRule }) = collect(keys(_property_map_IoK8sApiNetworkingV1beta1IngressRule))
Swagger.property_type(::Type{ IoK8sApiNetworkingV1beta1IngressRule }, name::Symbol) = Union{Nothing,eval(Base.Meta.parse(_property_types_IoK8sApiNetworkingV1beta1IngressRule[name]))}
Swagger.field_name(::Type{ IoK8sApiNetworkingV1beta1IngressRule }, property_name::Symbol) =  _property_map_IoK8sApiNetworkingV1beta1IngressRule[property_name]

function check_required(o::IoK8sApiNetworkingV1beta1IngressRule)
    true
end

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

ext: jl


text:
dflt = Dict{String, Any}(
    "min_nv" => 1000,
    "max_nv" => 15000
)

process_kwargs = Dict(
  "save_basic_features_instances" => Dict("recompute" => false),
  "save_single_features_instances" => dflt,
  "save_features_instances" => merge(dflt, Dict("recompute" => false)),
  "serialize_instances" => merge(dflt, Dict("serialize_path" => "data/serialize_networks",
                                             "graphs_path" => "data/graphs")),
  "generate_decompositions_20dist3cholesky" => merge(dflt, Dict("cliques_path" => "data/cliques",
                                                "cliquetrees_path" => "data/cliquetrees",
                                                "graphs_path" => "data/graphs",
                                                "extension_alg" => "cholesky",
                                                "preprocess_path" => "data/configs/preprocess_20dist3.json")),
  "generate_decompositions_20dist3minimum_degree" => merge(dflt, Dict("cliques_path" => "data/cliques",
                                                "cliquetrees_path" => "data/cliquetrees",
                                                "graphs_path" => "data/graphs",
                                                "extension_alg" => "minimum_degree",
                                                "preprocess_path" => "data/configs/preprocess_20dist3.json")),
  "generate_decompositions_0cholesky" => merge(dflt, Dict("cliques_path" => "data/cliques",
                                                "cliquetrees_path" => "data/cliquetrees",
                                                "graphs_path" => "data/graphs",
                                                "extension_alg" => "cholesky",
                                                "preprocess_path" => "data/configs/preprocess_0.json")),
  "generate_decompositions_0minimum_degree" => merge(dflt, Dict("cliques_path" => "data/cliques",
                                                "cliquetrees_path" => "data/cliquetrees",
                                                "graphs_path" => "data/graphs",
                                                "extension_alg" => "minimum_degree",
                                                "preprocess_path" => "data/configs/preprocess_0.json")),
  "merge_decompositions" => merge(dflt, Dict("heuristic" => ["molzahn"],
                                             "heuristic_switch" => [0],
                                             "treshold_name" => "cliques_nv_up",
                                             "merge_kwargs" => Dict("treshold_percent" => 0.1))),
  "combine_decompositions" => merge(dflt, Dict("how" => "cliques_intersect",
                                               "extension_alg" => "cholesky",
                                               "exclude" => ["combine"])),
  "delete_duplicates" => Dict(),
  "export_matpowerm_instances" => merge(dflt, Dict("export_dir" => "data/matpowerm_instance"))
)

max_stars_repo_name: charlyalizadeh/PowerFlowNetworks

max_issues_repo_name: charlyalizadeh/PowerFlowNetworks

max_forks_repo_name: charlyalizadeh/PowerFlowNetworks


text:
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
#
# Description
#
#   Print function of the text backend.
#
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

# Low-level function to print the table using the text backend.
function _pt_text(io::IO, pinfo::PrintInfo;
                  border_crayon::Crayon = Crayon(),
                  header_crayon::Union{Crayon,Vector{Crayon}} = Crayon(bold = true),
                  subheader_crayon::Union{Crayon,Vector{Crayon}} = Crayon(foreground = :dark_gray),
                  rownum_header_crayon::Crayon = Crayon(bold = true),
                  text_crayon::Crayon = Crayon(),
                  omitted_cell_summary_crayon::Crayon = Crayon(foreground = :red),
                  autowrap::Bool = false,
                  body_hlines::Vector{Int} = Int[],
                  body_hlines_format::Union{Nothing,NTuple{4,Char}} = nothing,
                  continuation_row_alignment::Symbol = :c,
                  crop::Symbol = get(io, :limit, false) ? :both : :none,
                  crop_subheader::Bool = false,
                  crop_num_lines_at_beginning::Int = 0,
                  columns_width::Union{Int,AbstractVector{Int}} = 0,
                  display_size::Tuple{Int,Int} = displaysize(io),
                  equal_columns_width::Bool = false,
                  ellipsis_line_skip::Integer = 0,
                  highlighters::Union{Highlighter,Tuple} = (),
                  hlines::Union{Nothing,Symbol,AbstractVector} = nothing,
                  linebreaks::Bool = false,
                  maximum_columns_width::Union{Int,AbstractVector{Int}} = 0,
                  minimum_columns_width::Union{Int,AbstractVector{Int}} = 0,
                  newline_at_end::Bool = true,
                  overwrite::Bool = false,
                  noheader::Bool = false,
                  nosubheader::Bool = false,
                  row_name_crayon::Crayon = Crayon(bold = true),
                  row_name_header_crayon::Crayon = Crayon(bold = true),
                  row_number_alignment::Symbol = :r,
                  show_omitted_cell_summary::Bool = true,
                  sortkeys::Bool = false,
                  tf::TextFormat = tf_unicode,
                  title_autowrap::Bool = false,
                  title_crayon::Crayon = Crayon(bold = true),
                  title_same_width_as_table::Bool = false,
                  vcrop_mode::Symbol = :bottom,
                  vlines::Union{Nothing,Symbol,AbstractVector} = nothing)

    # Unpack fields of `pinfo`.
    data                    = pinfo.data
    header                  = pinfo.header
    id_cols                 = pinfo.id_cols
    id_rows                 = pinfo.id_rows
    num_rows                = pinfo.num_rows
    num_cols                = pinfo.num_cols
    num_printed_cols        = pinfo.num_printed_cols
    num_printed_rows        = pinfo.num_printed_rows
    header_num_rows         = pinfo.header_num_rows
    header_num_cols         = pinfo.header_num_cols
    show_row_number         = pinfo.show_row_number
    row_number_column_title = pinfo.row_number_column_title
    show_row_names          = pinfo.show_row_names
    row_names               = pinfo.row_names
    row_name_alignment      = pinfo.row_name_alignment
    row_name_column_title   = pinfo.row_name_column_title
    alignment               = pinfo.alignment
    cell_alignment          = pinfo.cell_alignment
    formatters              = pinfo.formatters
    compact_printing        = pinfo.compact_printing
    title                   = pinfo.title
    title_alignment         = pinfo.title_alignment
    header_alignment        = pinfo.header_alignment
    header_cell_alignment   = pinfo.header_cell_alignment
    cell_first_line_only    = pinfo.cell_first_line_only
    renderer                = pinfo.renderer

    # Input variables verification and initial setup
    # ==========================================================================

    # Let's create a `IOBuffer` to write everything and then transfer to `io`.
    io_has_color = get(io, :color, false)
    buf_io       = IOBuffer()
    buf          = IOContext(buf_io, :color => io_has_color)
    display      = Display(has_color = io_has_color)

    # Check which type of cropping the user wants.
    if crop == :both
        display.size = display_size
    elseif crop == :vertical
        display.size = (display_size[1], -1)
    elseif crop == :horizontal
        display.size = (-1, display_size[2])
    else
        # If the table will not be cropped, then we should never show an omitted
        # cell summary.
        show_omitted_cell_summary = false
    end

    # Make sure that `vcrop_mode` is valid.
    vcrop_mode ∉ [:bottom, :middle] && (vcrop_mode = :bottom)

    crop_num_lines_at_beginning < 0 && (crop_num_lines_at_beginning = 0)

    !noheader && num_cols != header_num_cols &&
    error("The header length must be equal to the number of columns.")

    # Additional processing necessary if the user wants to print the header.
    if !noheader
        # If the user do not want to print the sub-header but wants to print the
        # header, then just force the number of rows in header to be 1.
        if nosubheader
            # Now, `header` will be a view of the first line of the matrix that
            # has the header.
            header = @view header[1:header_num_rows:end]
            header_num_rows = 1
        end

        # Transform some keywords that are single elements to vectors.
        if typeof(header_crayon) == Crayon
            header_crayon = [header_crayon for i = 1:num_cols]
        else
            length(header_crayon) != num_cols &&
            error("The length of `header_crayon` must be the same as the number of columns.")
        end

        if typeof(subheader_crayon) == Crayon
            subheader_crayon = [subheader_crayon for i = 1:num_cols]
        else
            length(subheader_crayon) != num_cols &&
            error("The length of `subheader_crayon` must be the same as the number of columns.")
        end
    end

    # Make sure that `highlighters` is always a tuple.
    !(highlighters isa Tuple) && (highlighters = (highlighters,))

    # Make sure that `maximum_columns_width` is always a vector.
    typeof(maximum_columns_width) <: Integer &&
        (maximum_columns_width = ones(Int, num_cols)*maximum_columns_width)

    # Make sure that `minimum_columns_width` is always a vector.
    typeof(minimum_columns_width) <: Integer &&
        (minimum_columns_width = ones(Int, num_cols)*minimum_columns_width)

    # Check which columns must have fixed sizes.
    typeof(columns_width) <: Integer && (columns_width = ones(Int, num_cols)*columns_width)
    length(columns_width) != num_cols && error("The length of `columns_width` must be the same as the number of columns.")
    fixed_col_width = map(w->w > 0, columns_width)

    # The number of lines that must be skipped from printing ellipsis must be
    # greater of equal 0.
    (ellipsis_line_skip < 0) && (ellipsis_line_skip = 0)

    # Assign the minimum size to those columns that does not have a fixed size.
    for i = 1:num_cols
        !fixed_col_width[i] && (columns_width[i] = minimum_columns_width[i])
    end

    # Increase the number of printed columns if the user wants to show
    # additional columns.
    Δc = show_row_names + show_row_number
    num_printed_cols += Δc

    # If the user wants to horizontally crop the printing, then it is not
    # necessary to process all the lines. We will to process, at most, the
    # number of lines in the display.
    if display.size[1] > 0
        num_printed_rows = min(num_printed_rows, display.size[1])
    end

    # If the user wants to vertically crop the printing, then it is not
    # necessary to process all the columns. However, we cannot know at this
    # stage the size of each column. Thus, this initial algorithm uses the fact
    # that each column printed will have at least 4 spaces.
    if display.size[2] > 0
        num_printed_cols = min(num_printed_cols, ceil(Int, display.size[2]/4))
    end

    # We need to process at least the row number and row name columns to avoid
    # errors.
    num_printed_cols < Δc && (num_printed_cols = Δc)

    # Number of rows and columns after filtering.
    num_filtered_rows = length(id_rows)
    num_filtered_cols = length(id_cols)

    # Create the string matrices that will be printed
    # ==========================================================================

    # Get the string which is printed when `print` is called in each element of
    # the matrix. Notice that we must create only the matrix with the printed
    # rows and columns.
    header_str = Matrix{String}(undef, header_num_rows, num_printed_cols)
    header_len = zeros(Int, header_num_rows, num_printed_cols)

    data_str   = Matrix{Vector{String}}(undef,
                                        num_printed_rows,
                                        num_printed_cols)
    data_len   = Matrix{Vector{Int}}(undef,
                                     num_printed_rows,
                                     num_printed_cols)

    num_lines_in_row = ones(Int, num_printed_rows)

    # The variable `columns_width` is the specification of the user for the
    # columns width. The variable `cols_width` contains the actual size of each
    # column. This is necessary because if the user asks for a width equal or
    # lower than 0 in a column, then the width will be automatically computed to
    # fit the longest field.
    #
    # By default, if the user wants to show the row number of the row names,
    # those columns will have the size computed automatically.
    cols_width = Vector{Int}(undef, num_printed_cols)

    for i = (Δc+1):num_printed_cols
        cols_width[i] = columns_width[id_cols[i-Δc]]
    end

    # Auxiliary variables to adjust `alignment` based on the new columns added
    # to the table.
    alignment_add = Symbol[]

    # Vectors with the order that the rows must be filled
    # --------------------------------------------------------------------------

    # The vector `jvec` contains the indices in the printed matrix whereas the
    # vector `jrvec` contains the indices in the data matrix.
    jvec, jrvec = _compute_row_fill_vectors(id_rows,
                                            num_printed_rows,
                                            vcrop_mode)

    # Row numbers
    # --------------------------------------------------------------------------

    if show_row_number
        @inbounds _fill_row_number_column!(header_str,
                                           header_len,
                                           data_str,
                                           data_len,
                                           cols_width,
                                           id_rows,
                                           jvec,
                                           jrvec,
                                           noheader,
                                           num_rows,
                                           row_number_column_title)

        # Add information about the row number column to the variables.
        push!(alignment_add, row_number_alignment)
    end

    # Row names
    # --------------------------------------------------------------------------

    if show_row_names
        @inbounds _fill_row_name_column!(header_str,
                                         header_len,
                                         data_str,
                                         data_len,
                                         cols_width,
                                         row_names,
                                         jvec,
                                         jrvec,
                                         Δc,
                                         compact_printing,
                                         renderer,
                                         row_name_column_title)

        # Add information about the row name column to the variables.
        push!(alignment_add, row_name_alignment)
    end

    !isempty(alignment_add) && (alignment = vcat(alignment_add, alignment))

    # Table data
    # --------------------------------------------------------------------------

    num_printed_cols, num_printed_rows =
        @inbounds _fill_matrix_data!(header_str,
                                     header_len,
                                     data_str,
                                     data_len,
                                     cols_width,
                                     id_cols,
                                     id_rows,
                                     num_lines_in_row,
                                     jvec,
                                     jrvec,
                                     Δc,
                                     columns_width,
                                     data,
                                     header,
                                     formatters,
                                     display,
                                     # Configuration options.
                                     autowrap,
                                     cell_first_line_only,
                                     compact_printing,
                                     crop_subheader,
                                     fixed_col_width,
                                     linebreaks,
                                     maximum_columns_width,
                                     noheader,
                                     renderer,
                                     vcrop_mode)

    # If the user wants all the columns with the same size, then select the
    # larger.
    equal_columns_width && (cols_width = [maximum(cols_width) for i = 1:num_printed_cols])

    # Compute where the horizontal and vertical lines must be drawn
    # --------------------------------------------------------------------------

    # Create the format of the horizontal lines.
    if body_hlines_format == nothing
        body_hlines_format = (tf.left_intersection, tf.middle_intersection,
                              tf.right_intersection, tf.row)
    end

    hlines == nothing && (hlines = tf.hlines)
    hlines = _process_hlines(hlines, body_hlines, num_printed_rows, noheader)

    # Check if the last horizontal line must be drawn. This is required when
    # computing the moment that the display will be cropped.
    draw_last_hline = (num_printed_rows + !noheader) ∈ hlines

    # Process `vlines`.
    vlines == nothing && (vlines = tf.vlines)
    vlines = _process_vlines(vlines, num_printed_cols)

    # Compute the table width
    # --------------------------------------------------------------------------

    table_width = sum(cols_width) + 2length(cols_width) + length(vlines)
    display.size[2] > 0 && table_width > display.size[2] && (table_width = display.size[2])

    # Process the title
    # --------------------------------------------------------------------------

    # Process the title separating the tokens.
    title_tokens = String[]

    if length(title) > 0
        # Compute the title width.
        title_width = title_same_width_as_table ? table_width : display.size[2]

        # If the title width is not higher than 0, then we should only print the
        # title.
        if title_width ≤ 0
            push!(title_tokens, title)

        # Otherwise, we must check for the alignments.
        else
            title_tokens_raw = string.(split(title, '\n'))
            title_autowrap && (title_tokens_raw = _str_autowrap(title_tokens_raw, title_width))
            num_tokens = length(title_tokens_raw)

            @inbounds for i = 1:num_tokens
                token = title_tokens_raw[i]
                token_str = _str_aligned(token, title_alignment, title_width)[1]
                push!(title_tokens, token_str)
            end

            # Sum the number of lines in the title to the number of lines that
            # must be available at the beginning of the table.
            crop_num_lines_at_beginning += length(title_tokens_raw)
        end
    end

    # Compute the table data printing recipe
    # --------------------------------------------------------------------------

    # Number of additional lines that must be consider to crop the display
    # vertically.
    Δdisplay_lines = 1 + newline_at_end + draw_last_hline + crop_num_lines_at_beginning

    row_printing_recipe, col_printing_recipe, num_omitted_rows, num_omitted_cols =
        _create_printing_recipe(display,
                                header_num_rows,
                                num_filtered_rows,
                                num_filtered_cols,
                                num_printed_rows,
                                num_printed_cols,
                                num_lines_in_row,
                                cols_width,
                                id_rows,
                                hlines,
                                vlines,
                                Δdisplay_lines,
                                Δc,
                                # Configurations
                                crop,
                                noheader,
                                show_omitted_cell_summary,
                                vcrop_mode)

    #                           Print the table
    # ==========================================================================

    # Title
    # ==========================================================================

    # Process the title separating the tokens.
    if length(title) > 0
        # Print the title.
        display.has_color && print(buf, title_crayon)
        num_tokens = length(title_tokens)

        @inbounds for i = 1:num_tokens
            print(buf, rstrip(title_tokens[i]))

            # In the last line we must not add the new line character
            # because we need to reset the crayon first if the display
            # supports colors.
            i != num_tokens && println(buf)
        end
        display.has_color && print(buf, _reset_crayon)
        println(buf)
    end

    # If there is no column or row to be printed, then just exit.
    if (num_printed_cols == 0) || (num_printed_rows == 0)
        @goto print_to_output
    end

    # Top table line
    # ==========================================================================

    0 ∈ hlines && _draw_line!(display, buf, tf.up_left_corner,
                              tf.up_intersection, tf.up_right_corner, tf.row,
                              border_crayon, cols_width, vlines)

    # Header
    # ==========================================================================

    # Header and sub-header texts
    # --------------------------------------------------------------------------

    if !noheader
        _print_table_header!(buf,
                             display,
                             header,
                             header_str,
                             header_len,
                             id_cols,
                             id_rows,
                             num_printed_cols,
                             Δc,
                             cols_width,
                             vlines,
                              # Configurations.
                             alignment,
                             header_alignment,
                             header_cell_alignment,
                             show_row_names,
                             show_row_number,
                             tf,
                             # Crayons.
                             border_crayon,
                             header_crayon,
                             subheader_crayon,
                             rownum_header_crayon,
                             row_name_header_crayon)

        # Bottom header line
        #-----------------------------------------------------------------------

        1 ∈ hlines && _draw_line!(display, buf, tf.left_intersection,
                                  tf.middle_intersection,
                                  tf.right_intersection, tf.row,
                                  border_crayon, cols_width, vlines)
    end

    # Data
    # ==========================================================================

    @inbounds _print_table_data(buf,
                                display,
                                data,
                                data_str,
                                data_len,
                                id_cols,
                                id_rows,
                                Δc,
                                cols_width,
                                vlines,
                                row_printing_recipe,
                                col_printing_recipe,
                                # Configurations.
                                alignment,
                                body_hlines_format,
                                cell_alignment,
                                continuation_row_alignment,
                                ellipsis_line_skip,
                                highlighters,
                                noheader,
                                show_row_names,
                                show_row_number,
                                tf,
                                # Crayons.
                                border_crayon,
                                row_name_crayon,
                                text_crayon)

    # Bottom table line
    # ==========================================================================

    draw_last_hline &&
        _draw_line!(display, buf, tf.bottom_left_corner, tf.bottom_intersection,
                    tf.bottom_right_corner, tf.row, border_crayon, cols_width,
                    vlines)

    # Summary of the omitted cells
    # ==========================================================================

    if show_omitted_cell_summary && ((num_omitted_cols + num_omitted_rows) > 0)
        cs_str_col = ""
        cs_str_and = ""
        cs_str_row = ""

        if num_omitted_cols > 0
            cs_str_col = string(num_omitted_cols)
            cs_str_col *= num_omitted_cols > 1 ? " columns" : " column"
        end

        if num_omitted_rows > 0
            cs_str_row = string(num_omitted_rows)
            cs_str_row *= num_omitted_rows > 1 ? " rows" : " row"

            num_omitted_cols > 0 && (cs_str_and = " and ")
        end

        cs_str = cs_str_col * cs_str_and * cs_str_row * " omitted"

        textwidth(cs_str) < table_width &&
            (cs_str = _str_aligned(cs_str, :r, table_width)[1])

        display.has_color && print(buf, omitted_cell_summary_crayon)
        print(buf, cs_str)
        display.has_color && print(buf, _reset_crayon)
        println(buf)
    end

    @label print_to_output

    # Overwrite table
    # ==========================================================================

    # If `overwrite` is `true`, then delete the exact number of lines of the
    # table. This can be used to replace the table in the display continuously.

    str_overwrite = overwrite ? "\e[1F\e[2K"^(display.row - 1) : ""

    # Print the buffer
    # ==========================================================================
    output_str = String(take!(buf_io))

    # Check if the user does not want a newline at end.
    if !newline_at_end && (output_str[end] == '\n')
        output_str = first(output_str, length(output_str)-1)
    end

    print(io, str_overwrite * output_str)

    return nothing
end


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

include("../perfutil.jl")

include("eig.jl")
include("factorizations.jl")

# The test below is not a perf test but it takes too long for the normal test suite.
# This tests a work around for a bug in LAPACK where the work space is truncated
# to a too small number because the number is too big to be represented as a Float32.
# See the calculation for lwork in our sgesdd wrapper in lapack.jl.
println("testing work space bug in sgesdd")
svd(rand(Float32, 9537, 9537))

max_stars_repo_name: caodi/julia

max_issues_repo_name: caodi/julia

max_forks_repo_name: caodi/julia


text:
"""
    TypeS()

Lorem ipsum dolor sit amet.
"""
struct TypeS

end


text:
# https://github.com/mbaz/Gaston.
const G = Gaston
const GastonSubplot = G.Plot
const GASTON_MARKER_SCALING = 1.3 / 5.0
const GNUPLOT_DPI = 72  # Compensate for DPI with increased resolution

# --------------------------------------------
# These functions are called by Plots
# --------------------------------------------

# Create the window/figure for this backend.
function _create_backend_figure(plt::Plot{GastonBackend})
    xsize, ysize = plt.attr[:size]
    G.set(termopts="""size $xsize,$ysize""")

    state_handle = G.nexthandle() # for now all the figures will be kept
    plt.o = G.newfigure(state_handle)
end

function _before_layout_calcs(plt::Plot{GastonBackend})
    # Initialize all the subplots first
    plt.o.subplots = G.SubPlot[]

    n1 = n2 = 0
    if length(plt.inset_subplots) > 0
        n1, sps = gaston_get_subplots(0, plt.inset_subplots, plt.layout)
        gaston_init_subplots(plt, sps)
    end

    if length(plt.subplots) > 0
        n2, sps = gaston_get_subplots(0, plt.subplots, plt.layout)
    end

    n = n1 + n2
    if n != length(plt.subplots)
        @error "Gaston: $n != $(length(plt.subplots))"
    end

    # FIXME: find a way to support nested layouts
    # e.g. figures spanning multiple rows/cols
    plt.o.layout = gaston_init_subplots(plt, sps)

    # Then add the series (curves in gaston)
    for series in plt.series_list
        gaston_add_series(plt, series)
    end
    nothing
end

function _update_min_padding!(sp::Subplot{GastonBackend})
    # FIXME: make this more flexible
    sp.minpad = (20mm, 5mm, 2mm, 10mm)
end

_update_plot_object(plt::Plot{GastonBackend}) = nothing

for (mime, term) in (
    "application/eps"         => "epscairo",   # NEED fixing TODO
    "image/eps"               => "epslatex",   # NEED fixing TODO
    "application/pdf"         => "pdfcairo",   # NEED fixing TODO
    "application/postscript"  => "postscript", # NEED fixing TODO
    "image/svg+xml"           => "svg",
    "text/latex"              => "tikz",       # NEED fixing TODO
    "application/x-tex"       => "epslatex",   # NEED fixing TODO
    "text/plain"              => "dumb",       # NEED fixing TODO
)
    @eval function _show(io::IO, ::MIME{Symbol($mime)}, plt::Plot{GastonBackend})
        xsize, ysize = plt.attr[:size]
        termopts = """size $xsize,$ysize"""

        tmpfile = G.tempname() * "." * $term
        G.save(
            term=$term,
            output=tmpfile,
            handle=plt.o.handle,
            saveopts=termopts
        )
        while !isfile(tmpfile) end  # avoid race condition with read in next line
        write(io, read(tmpfile))
        rm(tmpfile, force=true)
        nothing
    end
end

function _show(io::IO, mime::MIME{Symbol("image/png")}, plt::Plot{GastonBackend})
    scaling = plt.attr[:dpi] / GNUPLOT_DPI
    xsize, ysize = plt.attr[:size] .* scaling

    # Scale all plot elements to match Plots.jl DPI standard
    termopts = """size $xsize,$ysize fontscale $scaling lw $scaling dl $scaling ps $scaling"""

    tmpfile = G.tempname()
    G.save(
        term="pngcairo",
        output=tmpfile,
        handle=plt.o.handle,
        saveopts=termopts
    )
    while !isfile(tmpfile) end  # avoid race condition with read in next line
    write(io, read(tmpfile))
    rm(tmpfile, force=true)
    nothing
end

_display(plt::Plot{GastonBackend}) = display(plt.o)

# --------------------------------------------
# These functions are gaston specific
# --------------------------------------------

function gaston_get_subplots(n, plt_subplots, layout)
    nr, nc = size(layout)
    sps = Array{Any}(undef, nr, nc)
    for r ∈ 1:nr, c ∈ 1:nc  # NOTE: col major
        l = layout[r, c]
        if l isa GridLayout
            n, sub = gaston_get_subplots(n, plt_subplots, l)
            sps[r, c] = size(sub) == (1, 1) ? only(sub) : sub
        else
            sps[r, c] = get(l.attr, :blank, false) ? nothing : plt_subplots[n += 1]
        end
    end
    return n, sps
end

function gaston_init_subplots(plt, sps)
    sz = nr, nc = size(sps)
    for c ∈ 1:nc, r ∈ 1:nr  # NOTE: row major
        sp = sps[r, c]
        if sp isa Subplot || sp === nothing
            gaston_init_subplot(plt, sp)
        else
            gaston_init_subplots(plt, sp)
            sz = max.(sz, size(sp))
        end
    end
    return sz
end

function gaston_init_subplot(plt::Plot{GastonBackend}, sp::Subplot{GastonBackend})
    if sp === nothing
        push!(plt.o.subplots, sp)
    else
        sp.o = GastonSubplot(
            dims=RecipesPipeline.is3d(sp) ? 3 : 2,
            axesconf=gaston_parse_axes_args(plt, sp),  # Gnuplot string
            curves=[]
        )
        push!(plt.o.subplots, sp.o)
    end
    nothing
end

function gaston_add_series(plt::Plot{GastonBackend}, series::Series)
    # Gaston.Curve = Plots.Series
    sp = series[:subplot]
    g_sp = sp.o  # Gaston subplot object

    if series[:seriestype] ∈ (:heatmap, :contour) && g_sp.dims == 2
        g_sp.dims = 3  # FIXME: this is ugly, we need heatmap/contour to use splot, not plot
    end

    x = series[:x]
    y = series[:y]
    z = g_sp.dims == 2 ? nothing : series[:z]
    if z isa Surface
        z = z.surf
    end

    seriesconf = gaston_seriesconf!(sp, series)  # Gnuplot string
    c = G.Curve(x, y, z, nothing, seriesconf)

    isfirst = length(g_sp.curves) == 0 ? true : false
    push!(g_sp.curves, c)
    G.write_data(c, g_sp.dims, g_sp.datafile, append = isfirst ? false : true)
    nothing
end

function gaston_seriesconf!(sp, series::Series)
    gsp = sp.o
    curveconf = String[]
    st = series[:seriestype]

    clims = get_clims(sp, series)
    if st ∈ (:scatter, :scatter3d)
        pt, ps, lc = gaston_mk_ms_mc(series)
        push!(curveconf, """with points pt $pt ps $ps lc $lc""")
    elseif st ∈ (:path, :straightline, :path3d)
        lc, dt, lw = gaston_lc_ls_lw(series)
        if series[:markershape] == :none # simplepath
            push!(curveconf, """with lines lc $lc dt $dt lw $lw""")
        else
            pt, ps = gaston_mk_ms_mc(series)
            push!(curveconf, """with lp lc $lc dt $dt lw $lw pt $pt ps $ps""")
        end
    elseif st == :shape
        fc = gaston_color(series[:fillcolor], series[:fillalpha])
        fs = "solid"
        lc, _ = gaston_lc_ls_lw(series)
        push!(curveconf, """with filledcurves fc $fc fs $fs border lc $lc""")
    elseif st == :steppre
        push!(curveconf, """with steps""")
    elseif st == :steppost
        push!(curveconf, """with fsteps""")  # Not sure if not the other way
    elseif st ∈ (:contour, :contour3d)
        push!(curveconf, """with lines""")
        if st == :contour
            gsp.axesconf *= """\nset view map\nunset surface"""
        end
        levels = join(map(string, collect(contour_levels(series, clims))), ", ")
        gsp.axesconf *= """\nset contour base\nset cntrparam levels discrete $levels """
    elseif st ∈ (:surface, :heatmap)
        palette = gaston_palette(series[:seriescolor])
        gsp.axesconf *= """\nset palette model RGB defined $palette"""
        if st == :heatmap
            gsp.axesconf *= """\nset view map"""
        end
        push!(curveconf, """with pm3d""")
    elseif st == :wireframe
        lc, dt, lw = gaston_lc_ls_lw(series)
        push!(curveconf, """with lines lc $lc dt $dt lw $lw""")
    else
        @warn "Gaston: $st is not implemented yet"
    end

    # label
    push!(curveconf, """title "$(series[:label])" """)
    return join(curveconf, " ")
end

function gaston_parse_axes_args(plt::Plot{GastonBackend}, sp::Subplot{GastonBackend})
    axesconf = String[]
    # Standard 2d axis
    if !ispolar(sp) && !RecipesPipeline.is3d(sp)
        # TODO: configure grid, axis spines, thickness
    end

    for letter in (:x, :y, :z)
        axis_attr = sp.attr[Symbol(letter, :axis)]
        # label names
        push!(axesconf, """set $(letter)label "$(axis_attr[:guide])"  """)
        push!(axesconf, """set $(letter)label font "$(axis_attr[:guidefontfamily]), $(axis_attr[:guidefontsize])"  """)

        # Handle ticks
        # ticksyle
        push!(axesconf, """set $(letter)tics font "$(axis_attr[:tickfontfamily]), $(axis_attr[:tickfontsize])"  """)
        push!(axesconf, """set $(letter)tics textcolor rgb "#$(hex(axis_attr[:tickfontcolor], :rrggbb))" """)
        push!(axesconf, """set $(letter)tics  $(axis_attr[:tick_direction])""")

        mirror = axis_attr[:mirror] ? "mirror" : "nomirror"
        push!(axesconf, """set $(letter)tics $(mirror) """)

        logscale = if axis_attr[:scale] == :identity
            "nologscale"
        elseif axis_attr[:scale] == :log10
            "logscale"
        end
        push!(axesconf, """set $logscale $(letter)""")

        # tick locations
        if axis_attr[:ticks] != :native
            # axis limits
            from, to = axis_limits(sp, letter)
            push!(axesconf, """set $(letter)range [$from:$to]""")

            ticks = get_ticks(sp, axis_attr)
            gaston_set_ticks!(axesconf, ticks, letter)
        end

        ratio = get_aspect_ratio(sp)
        if ratio != :none
            if ratio == :equal
                ratio = -1
            end
            push!(axesconf, """set size ratio $ratio""")
        end
    end
    gaston_set_legend!(axesconf, sp) # Set legend params

    if sp[:title] != nothing
        push!(axesconf, """set title '$(sp[:title])' """)
        push!(axesconf, """set title font '$(sp[:titlefontfamily]), $(sp[:titlefontsize])' """)
    end

    return join(axesconf, "\n")
end

function gaston_set_ticks!(axesconf, ticks, letter)
    ticks == :auto && return
    if ticks ∈ (:none, nothing, false)
        push!(axesconf, """unset $(letter)tics """)
        return
    end

    ttype = ticksType(ticks)
    if ttype == :ticks
        tick_locations = @view ticks[:]
        gaston_tick_string = []
        for i in eachindex(tick_locations)
            lac = tick_locations[i]
            push!(gaston_tick_string, "$loc")
        end
        push!(
            axesconf,
            "set $(letter)tics (" * join(gaston_tick_string, ", ") * ")"
        )
    elseif ttype == :ticks_and_labels
        tick_locations = @view ticks[1][:]
        tick_labels = @view ticks[2][:]
        gaston_tick_string = []
        for i in eachindex(tick_locations)
            loc = tick_locations[i]
            lab = gaston_enclose_tick_string(tick_labels[i])
            push!(gaston_tick_string, "'$lab' $loc")
        end
        push!(
            axesconf,
            "set $(letter)tics (" * join(gaston_tick_string, ", ") * ")"
        )
    else
        error("Invalid input for $(letter)ticks: $ticks")
    end
    nothing
end

function gaston_set_legend!(axesconf, sp)
    leg = sp[:legend]
    if sp[:legend] ∉ (:none, :inline)
        if leg == :best
            leg = :topright
        end

        if occursin("outer", string(leg))
            push!(axesconf, "set key outside")
        else
            push!(axesconf, "set key inside")
        end
        positions = ["top", "bottom", "left", "right"]
        for position in positions
            if occursin(position, string(leg))
                push!(axesconf, "set key $position")
            end
        end
        if sp[:legendtitle] != nothing
            push!(axesconf, "set key title '$(sp[:legendtitle])'")
        end
        push!(axesconf, "set key box linewidth 1")
        push!(axesconf, "set key opaque")

        push!(axesconf, "set border back")
        push!(axesconf, """set key font "$(sp[:legendfontfamily]), $(sp[:legendfontsize])"  """)
    else
        push!(axesconf, "set key off")

    end
    nothing
end

# --------------------------------------------
# Helpers
# --------------------------------------------

gaston_lc_ls_lw(series::Series) = (
    gaston_color(series[:linecolor], series[:linealpha]),
    gaston_linestyle(series[:linestyle]),
    series[:linewidth],
)

gaston_mk_ms_mc(series::Series) = (
    gaston_marker(series[:markershape]),
    series[:markersize] * GASTON_MARKER_SCALING,
    gaston_color(series[:markercolor], series[:markeralpha]),
)

function gaston_palette(gradient)
    palette = String[]
    n = -1
    for rgba ∈ gradient  # FIXME: naive conversion, inefficient ?
        push!(palette, "$(n += 1) $(rgba.r) $(rgba.g) $(rgba.b)")
    end
    return '(' * join(palette, ", ") * ')'
end

function gaston_marker(marker)
    marker == :none && return -1
    marker == :circle && return 7
    marker == :rect && return 5
    marker == :diamond && return 28
    marker == :utriangle && return 9
    marker == :dtriangle && return 11
    marker == :+ && return 1
    marker == :x && return 2
    marker == :star5 && return 3
    marker == :pentagon && return 15
    marker == :pixel && return 0

    @warn "Unsupported marker $marker"
    return 1
end

function gaston_color(color, alpha=0.)
    col = single_color(color)  # in case of gradients
    col = alphacolor(col, alpha == nothing ? 0. : alpha)  # add a default alpha if non existent
    return """rgb "#$(hex(col, :aarrggbb))" """
end

function gaston_linestyle(style)
    style == :solid && return "1"
    style == :dash && return "2"
    style == :dot && return "3"
    style == :dashdot && return "4"
    style == :dashdotdot && return "5"
end

function gaston_enclose_tick_string(tick_string)
    if findfirst("^", tick_string) == nothing
        return tick_string
    end

    base, power = split(tick_string, "^")
    power = string("{", power, "}")
    return string(base, "^", power)
end


text:
import GraphIdx
import HDF5: h5read, h5write

fname = ARGS[1]
outfn = ARGS[2]

dn = 1
y = h5read(fname, "y")
graph = GraphIdx.Grid.GridGraph(size(y)..., dn)
edges = GraphIdx.Grid.collect_edges(graph)
edges = edges[1]
head = [h for (h, _) in edges]
tail = [t for (_, t) in edges]
h5write(outfn, "head", head .- 1)
h5write(outfn, "tail", tail .- 1)

max_stars_repo_name: EQt/treelas

max_issues_repo_name: EQt/treelas

max_forks_repo_name: EQt/treelas


text:
c0=readcore("data/kb003.cnf")
r1,c1=simpleprover("data/kb001.cnf",5,3)
r2,c2=simpleprover("data/kb002.cnf",5,3)
r3,c3=simpleprover("data/kb003.cnf",5,3)

printcore(c3)
printproofs1(c3)
printmgus(c3)

max_stars_repo_name: sazare/cheapViews

max_issues_repo_name: sazare/cheapViews

max_forks_repo_name: sazare/cheapViews


text:
#=
Challenge #263 [Easy] Calculating Shannon Entropy of a String

Description

Shannon entropy was introduced by Claude E. Shannon in his 1948 paper "A
Mathematical Theory of Communication". Somewhat related to the physical and
chemical concept entropy, the Shannon entropy measures the uncertainty associated
with a random variable, i.e. the expected value of the information in the message
(in classical informatics it is measured in bits). This is a key concept in
information theory and has consequences for things like compression, cryptography
and privacy, and more.
The Shannon entropy H of input sequence X is calculated as -1 times the sum of
the frequency of the symbol i times the log base 2 of the frequency:

            n
            _   count(i)          count(i)
H(X) = -1 * >   --------- * log  (--------)
            -       N          2      N
            i=1

(That funny thing is the summation for i=1 to n. I didn't see a good way to do
this in Reddit's markup so I did some crude ASCII art.)
For more, see Wikipedia for Entropy in information theory).

Input Description

You'll be given a string, one per line, for which you should calculate the
Shannon entropy. Examples:

1223334444
Hello, world!

Output Description

Your program should emit the calculated entropy values for the strings to at least
five decimal places. Examples:

1.84644
3.18083

Challenge Input

122333444455555666666777777788888888
563881467447538846567288767728553786
https://www.reddit.com/r/dailyprogrammer
int main(int argc, char *argv[])

Challenge Output

2.794208683
2.794208683
4.056198332
3.866729296

(source: https://redd.it/4fc896)
=#

function __shannon_entropy( string::AbstractString )
    local frequency::Dict{Char, Integer} = Dict{Char, Integer}()

    for i in 1:length( string )
        if haskey( frequency, string[ i ] )
            frequency[ string[ i ] ] += 1
        else
            frequency[ string[ i ] ] = 1
        end
    end

    return frequency
end

function Shannon_entropy( string::AbstractString )
    local entropy::AbstractFloat = 0
    local frequency::Dict{Char, Integer} = __shannon_entropy( string )
    local N::Integer = length( string )

    for i in values( frequency )
        entropy += ( i/N ) * log2( i/N )
    end
    entropy *= -1

    return @sprintf "%.9f" entropy
end

println( Shannon_entropy( "1223334444" ) )
println( Shannon_entropy( "Hello, world!") )
println( Shannon_entropy( "122333444455555666666777777788888888") )
println( Shannon_entropy( "563881467447538846567288767728553786") )
println( Shannon_entropy( "https://www.reddit.com/r/dailyprogrammer") )
println( Shannon_entropy( "int main(int argc, char *argv[])") )

max_stars_repo_name: farmfirst/dailyprogrammer

max_issues_repo_name: farmfirst/dailyprogrammer

max_forks_repo_name: farmfirst/dailyprogrammer


text:
abstract type AbstractContour end

struct ImaginaryContour <: AbstractContour
  branches::NTuple{1, Branch}
  β::Float64
end
function ImaginaryContour(;β)
  branches = (Branch(imaginary_branch, β),)
  return ImaginaryContour(branches, β)
end

struct KeldyshContour <: AbstractContour
  branches::NTuple{2, Branch}
  tmax::Float64
end
function KeldyshContour(; tmax)
  branches = (Branch(forward_branch, tmax), Branch(backward_branch, tmax))
  return KeldyshContour(branches, tmax)
end

struct FullContour <: AbstractContour
  branches::NTuple{3, Branch}
  tmax::Float64
  β::Float64
end
function FullContour(; tmax, β)
  branches = (Branch(forward_branch, tmax), Branch(backward_branch, tmax), Branch(imaginary_branch, β))
  return FullContour(branches, tmax, β)
end

nbranches(::Type{FullContour}) = 3
nbranches(::Type{KeldyshContour}) = 2
nbranches(::Type{ImaginaryContour}) = 1
nbranches(c::AbstractContour) = nbranches(typeof(c))

Base.length(c::ImaginaryContour) = c.β
Base.length(c::KeldyshContour) = 2*c.tmax
Base.length(c::FullContour) = 2*c.tmax + c.β

function get_point(c::AbstractContour, ref)
  @assert ref >= 0.0 && ref <= length(c)

  for b in c.branches
    lb = length(b)
    if ref <= lb
      return b(ref / lb)
    else
      ref -= lb
    end
  end
end

(c::AbstractContour)(ref::Real) = get_point(c, ref)

function twist(c::FullContour)
  n = nbranches(c)
  FullContour(ntuple(i -> c.branches[mod1(i+1,n)], n), c.tmax, c.β)
end

function twist(c::KeldyshContour)
  n = nbranches(c)
  KeldyshContour(ntuple(i -> c.branches[mod1(i+1,n)], n), c.tmax)
end

"""
θ(t1, t2) = t1 >= t2 ? 1.0 : 0.0
"""
function heaviside(c::AbstractContour, t1::BranchPoint, t2::BranchPoint)
  if t1.domain == t2.domain
    return t1.ref >= t2.ref
  else
    return findfirst(b -> b.domain == t1.domain, c.branches) >
           findfirst(b -> b.domain == t2.domain, c.branches)
  end
end

"""
Heaviside function on standard keldysh contour
"""
function heaviside(t1::BranchPoint, t2::BranchPoint)
  t1.domain == t2.domain ?  t1.ref >= t2.ref : t1.domain > t2.domain
end

function Base.getindex(c::AbstractContour, d::BranchEnum)
  idx = findfirst(b -> b.domain == d, c.branches)
  return isnothing(idx) ? nothing : c.branches[idx]
end

Base.in(b::BranchEnum, ::Type{FullContour}) = true
Base.in(b::BranchEnum, ::Type{KeldyshContour}) = b == imaginary_branch ? false : true
Base.in(b::BranchEnum, ::Type{ImaginaryContour}) = b == imaginary_branch ? true : false
Base.in(b::BranchEnum, c::AbstractContour) = in(b, typeof(c))


text:
opt_success = (:NLOPT_SUCCESS, :NLOPT_STOPVAL_REACHED, :NLOPT_FTOL_REACHED, :NLOPT_XTOL_REACHED, :NLOPT_MAXEVAL_REACHED,
  :NLOPT_MAXTIME_REACHED)

opt_failure = (:NLOPT_FAILURE, :NLOPT_INVALID_ARGS, :NLOPT_OUT_OF_MEMORY, :NLOPT_ROUNDOFF_LIMITED, :NLOPT_FORCED_STOP)

# MCML decoder for non-coherent UTURUniChannelCSK
immutable UTURUniChannelCSKMCMLDecoder <: MCMLDecoder
  carrier::Carrier
  nmc::Int64
  opt::Vector{NLoptimizer} # NLopt Opt types for the two functions to be mazimized
  init::Vector{Float64}

  function UTURUniChannelCSKMCMLDecoder(c::Carrier, nmc::Int64, opt::Vector{NLoptimizer}, init::Vector{Float64})
    @assert length(opt) == 2 "2 optimizers are needed for the MCML decoder of UTURUniChannelCSK."
    @assert length(init) == 2 "The init field of the MCML decoder of UTURUniChannelCSK must be a vector of length 2."
    new(c, nmc, opt, init)
  end
end

UTURUniChannelCSKMCMLDecoder(c::Carrier, nmc::Int64, opt::NLoptimizer, init::Vector{Float64}) =
  UTURUniChannelCSKMCMLDecoder(c, nmc, [opt, opt], init)
UTURUniChannelCSKMCMLDecoder(c::Carrier, nmc::Int64, opt::Vector{NLoptimizer}, init::Float64) =
  UTURUniChannelCSKMCMLDecoder(c, nmc, opt, [init, init])
UTURUniChannelCSKMCMLDecoder(c::Carrier, nmc::Int64, opt::NLoptimizer, init::Float64) =
  UTURUniChannelCSKMCMLDecoder(c, nmc, [opt, opt], [init, init])

# Default optimizer for MCML decoder of non-coherent UTURUniChannelCSK
uturunichannelcsk_default_opt = NLoptimizer(alg=:LD_SLSQP, lb=0.0, ub=100.0, absftol=NaN, relftol=NaN, absxtol=1e-32,
  relxtol=NaN, nfeval=-1, maxtime=-1.0)

UTURUniChannelCSKMCMLDecoder(; carrier::Carrier=LogisticCarrier(5), nmc::Int64=50,
  opt::Union(NLoptimizer, Vector{NLoptimizer})=uturunichannelcsk_default_opt,
  init::Union(Float64, Vector{Float64})=1.0) =
  UTURUniChannelCSKMCMLDecoder(carrier, nmc, opt, init)

# Wrapper for MCML decoder construction of any systems that supports MCML decoding
mcml_types = (:UTURUniChannelCSK,)

function mcml_decoder(; system::Symbol=:UTURUniChannelCSK, args...)
  @assert in(system, mcml_types) "MCML decoder not defined for $system."

  if system == :UTURUniChannelCSK
    UTURUniChannelCSKMCMLDecoder(; args...)
  end
end


text:
using Parameters
using IterTools

abstract type AbstractRate end
export AbstractRate

abstract type AbstractFalloffRate <: AbstractRate end
export AbstractFalloffRate

@with_kw struct Arrhenius{N<:Real,K<:Real,Q<:Real,P<:AbstractRateUncertainty} <: AbstractRate
        A::N
        n::K
        Ea::Q
        unc::P = EmptyRateUncertainty()
end
@inline (arr::Arrhenius)(;T::Q,P::N=0.0,C::S=0.0,phi=0.0) where {Q<:Real,N<:Real,S<:Real} = @fastmath arr.A*T^arr.n*exp(-arr.Ea/(R*T))
@inline (arr::Arrhenius)(T::Q;P::N=0.0,C::S=0.0,phi=0.0) where {Q<:Real,N<:Real,S<:Real} = @fastmath arr.A*T^arr.n*exp(-arr.Ea/(R*T))
export Arrhenius

@with_kw struct StickingCoefficient{N<:Real,K<:Real,Q<:Real,P<:AbstractRateUncertainty} <: AbstractRate
        A::N
        n::K
        Ea::Q
        unc::P = EmptyRateUncertainty()
end
@inline (arr::StickingCoefficient)(;T::Q,P::N=0.0,C::S=0.0,phi=0.0) where {Q<:Real,N<:Real,S<:Real} = @fastmath min(arr.A*T^arr.n*exp(-arr.Ea/(R*T)),1.0)
@inline (arr::StickingCoefficient)(T::Q;P::N=0.0,C::S=0.0,phi=0.0) where {Q<:Real,N<:Real,S<:Real} = @fastmath min(arr.A*T^arr.n*exp(-arr.Ea/(R*T)),1.0)
export StickingCoefficient

@with_kw struct Arrheniusq{N<:Real,K<:Real,Q<:Real,P<:AbstractRateUncertainty,B} <: AbstractRate
        A::N
        n::K
        Ea::Q
        q::B = 0.0
        unc::P = EmptyRateUncertainty()
end
@inline (arr::Arrheniusq)(;T::Q,P::N=0.0,C::S=0.0,phi=0.0) where {Q<:Real,N<:Real,S<:Real} = @fastmath arr.A*T^arr.n*exp((-arr.Ea-arr.q*F*phi)/(R*T))
@inline (arr::Arrheniusq)(T::Q;P::N=0.0,C::S=0.0,phi=0.0) where {Q<:Real,N<:Real,S<:Real} = @fastmath arr.A*T^arr.n*exp((-arr.Ea-arr.q*F*phi)/(R*T))
export Arrheniusq

@with_kw struct PdepArrhenius{T<:Real,Q<:AbstractRateUncertainty,Z<:AbstractRate} <: AbstractRate
    Ps::Array{T,1}
    arrs::Array{Z,1}
    unc::Q = EmptyRateUncertainty()
end
PdepArrhenius(Ps::Array{Q,1},arrs::Array{Z,1}) where {Q<:Real,Z<:AbstractRate} = PdepArrhenius(sort(Ps),arrs)

@inline function (parr::PdepArrhenius)(;T::Q=nothing,P::V=nothing,C::S=0.0,phi=0.0) where {Q<:Real,V<:Real,S<:Real}
    inds = getBoundingIndsSorted(P,parr.Ps)::Tuple{Int64,Int64}

    if inds[2] == -1
        return @inbounds parr.arrs[inds[1]](T=T)
    else
        @inbounds highk = parr.arrs[inds[2]](T=T)
        @inbounds lowk = parr.arrs[inds[1]](T=T)
        @inbounds Plow = parr.Ps[inds[1]]
        @inbounds Phigh = parr.Ps[inds[2]]
        return @inbounds @fastmath lowk*10^(log10(P/Plow)/log10(Phigh/Plow)*log10(highk/lowk))
    end
end
export PdepArrhenius

@with_kw struct MultiArrhenius{Q<:AbstractRateUncertainty} <: AbstractRate
    arrs::Array{Arrhenius,1}
    unc::Q = EmptyRateUncertainty()
end

@inline function (marr::MultiArrhenius)(;T::Q,P::R=0.0,C::S=0.0,phi=0.0) where {Q<:Real,R<:Real,S<:Real}
    out = 0.0
    for arr in marr.arrs
        @fastmath out += arr(T)
    end
    return out
end
export MultiArrhenius

@with_kw struct MultiPdepArrhenius{Q<:AbstractRateUncertainty} <: AbstractRate
    parrs::Array{PdepArrhenius,1}
    unc::Q = EmptyRateUncertainty()
end

@inline function (parr::MultiPdepArrhenius)(;T::Q,P::R=0.0,C::S=0.0,phi=0.0) where {Q<:Real,R<:Real,S<:Real}
    out = 0.0
    for pdar in parr.parrs
        @fastmath out += pdar(T=T,P=P)
    end
    return out
end
export MultiPdepArrhenius

@with_kw struct ThirdBody{N<:Integer,K<:AbstractFloat,Q<:AbstractRateUncertainty} <: AbstractFalloffRate
    arr::Arrhenius
    efficiencies::Dict{N,K} = Dict()
    nameefficiencies::Dict{String,K} = Dict{String,Float64}()
    unc::Q = EmptyRateUncertainty()
end

(tbarr::ThirdBody)(;T::Q=nothing,P::R=0.0,C::S=nothing,phi=0.0) where {Q<:Real,R<:Real,S<:Real} = C*(tbarr.arr(T))
export ThirdBody

@with_kw struct Lindemann{N<:Integer,K<:AbstractFloat,Q<:AbstractRateUncertainty} <: AbstractFalloffRate
    arrhigh::Arrhenius
    arrlow::Arrhenius
    efficiencies::Dict{N,K} = Dict()
    nameefficiencies::Dict{String,K} = Dict{String,Float64}()
    unc::Q = EmptyRateUncertainty()
end

@inline function (lnd::Lindemann)(;T::Q=nothing,P::R=0.0,C::S=nothing,phi=0.0) where {Q<:Real,R<:Real,S<:Real}
    k0 = lnd.arrlow(T=T)
    kinf = lnd.arrhigh(T=T)
    @fastmath Pr = k0*C/kinf
    return @fastmath kinf*Pr/(1.0+Pr)
end
export Lindemann

@with_kw struct Troe{P<:Real,Q<:Real,F<:Real,L<:Real,N<:Integer,K<:AbstractFloat,R<:AbstractRateUncertainty} <: AbstractFalloffRate
    arrhigh::Arrhenius
    arrlow::Arrhenius
    a::P
    T3::Q
    T1::F
    T2::L
    efficiencies::Dict{N,K} = Dict()
    nameefficiencies::Dict{String,K} = Dict{String,Float64}()
    unc::R = EmptyRateUncertainty()
end

@inline function (tr::Troe)(;T::Q,P::R=0.0,C::S=nothing,phi=0.0) where {Q<:Real,R<:Real,S<:Real}
    k0 = tr.arrlow(T=T)
    kinf = tr.arrhigh(T=T)
    @fastmath Pr = k0*C/kinf

    if tr.T1 == 0.0 && tr.T3 == 0.0
        F = 1.0
    else
        @fastmath Fcent = (1-tr.a)*exp(-T/tr.T3)+tr.a*exp(-T/tr.T1)
        if tr.T2 != 0.0
            @fastmath Fcent += exp(-tr.T2/T)
        end
        d = 0.14
        @fastmath n = 0.75-1.27*log10(Fcent)
        @fastmath c = -0.4-0.67*log10(Fcent)
        @fastmath F = 10^(log10(Fcent)/(1+((log10(Pr)+c)/(n-d*(log10(Pr))))^2))
    end
    return @fastmath kinf*(Pr/(1+Pr))*F
end
export Troe

@with_kw struct Chebyshev{T<:Real,Q<:Real,S<:Real,V<:Real,B<:Real} <: AbstractRate
    coefs::Array{T,2}
    Tmin::Q
    Tmax::S
    Pmin::V
    Pmax::B
end

@inline function evalChebyshevPolynomial(ch::Chebyshev,n::N,x::Q) where {N<:Integer,Q<:Real}
    """
    evaluate the nth order Chebyshev Polynomial at x
    """
    if n==0
        return 1.0
    elseif n == 1
        return x
    else
        T = 0.0
        T0 = 1.0
        T1 = x
        for i in 1:(n-1)
            @fastmath T = 2*x*T1-T0
            T0 = T1
            T1 = T
        end
        return T
    end
end
export evalChebyshevPolynomial

@inline function getredtemp(ch::Chebyshev,T::N) where {N<:Real}
    """
    return a reduced temperature corresponding to the given tempeprature
    for the Chebyshev polynomial, maps the inverse of temperautre onto [-1,1]
    """
    return @fastmath (2.0/T-1.0/ch.Tmin-1.0/ch.Tmax)/(1.0/ch.Tmax-1.0/ch.Tmin)
end
export getredtemp

@inline function getredpress(ch::Chebyshev,P::N) where {N<:Real}
    """
    return a reduced pressure corresponding to the given temperature
    for the Chebyshev polynomial maps the logarithm of pressure onto [-1,1]
    """
    return @fastmath (2.0*log10(P)-log10(ch.Pmin)-log10(ch.Pmax))/(log10(ch.Pmax)-log10(ch.Pmin))
end
export getredpress

@inline function (ch::Chebyshev)(;T::N,P::Q=0.0,C::B=0.0,phi=0.0) where {N<:Real,B<:Real,Q<:Real}
    k = 0.0
    Tred = getredtemp(ch,T)
    Pred = getredpress(ch,P)
    Tlen,Plen = size(ch.coefs)
    @simd for i = 1:Tlen
        @simd for j = 1:Plen
            @fastmath @inbounds k += ch.coefs[i,j]*evalChebyshevPolynomial(ch,i-1,Tred)*evalChebyshevPolynomial(ch,j-1,Pred)
        end
    end
    return @fastmath 10.0^k
end
export Chebyshev

@inline function getkineticstype(kin::B) where {B<:AbstractRate}
    return extracttypename(typeof(kin).name)
end

@inline function getkineticstype(kin::PdepArrhenius)
    if occursin("AbstractRate", string(typeof(kin).parameters[3])) || occursin("MultiArrhenius", string(typeof(kin).parameters[3])) #no optimized function so don't put kinetics name first
        return ("AbstRate",extracttypename(typeof(kin).name),trunc.(kin.Ps,digits=3))
    else #optimized function available so put kinetics name first
        return (extracttypename(typeof(kin).name),trunc.(kin.Ps,digits=3))
    end
end

@inline function getkineticstype(kin::Chebyshev)
    return (extracttypename(typeof(kin).name),kin.Tmin,kin.Tmax,kin.Pmin,kin.Pmax,size(kin.coefs)) #different opt functions, but always can do
end
export getkineticstype

@inline extracttypename(typ::Symbol) = string(typ)
@inline extracttypename(typ) = string(typ.name)

@inline function _calcdkdCeff(tbarr::ThirdBody,T::Float64,Ceff::Float64)
    return @fastmath tbarr.arr(T)
end

@inline function _calcdkdCeff(lnd::Lindemann,T::Float64,Ceff::Float64)
    k0 = lnd.arrlow(T=T)
    kinf = lnd.arrhigh(T=T)
    @fastmath Pr = k0*Ceff/kinf
    return @fastmath k0/((1.0+Pr)*(1.0+Pr))
end

@inline function _calcdkdCeff(tr::Troe,T::Float64,Ceff::Float64)
    k0 = tr.arrlow(T=T)
    kinf = tr.arrhigh(T=T)
    @fastmath Pr = k0*Ceff/kinf
    if tr.T1 == 0.0 && tr.T3 == 0.0
        return @fastmath k0/((1.0+Pr)*(1.0+Pr))
    else
        @fastmath Fcent = (1-tr.a)*exp(-T/tr.T3)+tr.a*exp(-T/tr.T1)
        if tr.T2 !== 0.0
            @fastmath Fcent += exp(-tr.T2/T)
        end
        d = 0.14
        @fastmath n = 0.75-1.27*log10(Fcent)
        @fastmath c = -0.4-0.67*log10(Fcent)
        @fastmath x = (log10(Pr)+c)/(n-d*log10(Pr))
        @fastmath F = 10.0^(log10(Fcent)/(1.0+x*x))
        @fastmath dFdCeff = -log(10.0)*F*log10(Fcent)/((1.0+x*x)*(1.0+x*x))*2.0*x*log10(ℯ)/Pr*k0/kinf/(n-d*log10(Pr))*(1.0+d*x)
        return @fastmath k0/((1.0+Pr)*(1.0+Pr))*F + kinf*Pr/(1.0+Pr)*dFdCeff
    end
end


text:
export request_guild_members,
    update_voice_status,
    update_status,
    update_presence

const conn_properties = Dict(
    "\$os"      => string(Sys.KERNEL),
    "\$browser" => "Discord.jl",
    "\$device"  => "Discord.jl",
)

const OPCODES = Dict(
    0 =>  :DISPATCH,
    1 =>  :HEARTBEAT,
    2 =>  :IDENTIFY,
    3 =>  :STATUS_UPDATE,
    4 =>  :VOICE_STATUS_UPDATE,
    6 =>  :RESUME,
    7 =>  :RECONNECT,
    8 =>  :REQUEST_GUILD_MEMBERS,
    9 =>  :INVALID_SESSION,
    10 => :HELLO,
    11 => :HEARTBEAT_ACK,
)

struct Empty <: Exception end

# Connection.

"""
    open(c::Client; delay::Period=Second(7))

Connect a [`Client`](@ref) to the Discord gateway.

The `delay` keyword is the time between shards connecting. It can be increased from its
default if you are using multiple shards and frequently experiencing invalid sessions upon
 connection.
"""
function Base.open(c::Client; resume::Bool=false, delay::Period=Second(7))
    isopen(c) && throw(ArgumentError("Client is already connected"))
    c.ready = false

    # Clients can only identify once per 5 seconds.
    resume || sleep(c.shard * delay)

    @debug "Requesting gateway URL" logkws(c; conn=undef)...
    resp = try
        HTTP.get("$DISCORD_API/v$(c.version)/gateway")
    catch e
        kws = logkws(c; conn=undef, exception=(e, catch_backtrace()))
        @error "Getting gateway URL failed" kws...
        rethrow(e)
    end

    data = JSON3.read(String(resp.body))
    url = "$(data["url"])?v=$(c.version)&encoding=json"
    c.conn.v += 1
    @debug "Connecting to gateway" logkws(c; conn=c.conn.v, url=url)...
    c.conn.io = opentrick(HTTP.WebSockets.open, url)

    @debug "Receiving HELLO" logkws(c)...
    data, e = readjson(c.conn.io)
    op = get(OPCODES, data[:op], data[:op])

    if op !== :HELLO
        msg = "Expected opcode HELLO, received $op"
        @error msg logkws(c)...
        error(msg)
    end
    hello(c, data)

    data = if resume
        Dict("op" => 6, "d" => Dict(
            "token" => c.token,
            "intents" => c.intents,
            "session_id" => c.state.session_id,
            "seq" => c.hb_seq,
        ))
    else
        d = Dict("op" => 2, "s" => c.hb_seq, "d" => Dict(
            "token" => c.token,
            "intents" => c.intents,
            "properties" => conn_properties,
        ))
        isempty(c.presence) || (d["d"]["presence"] = c.presence)
        c.shards > 1 && (d["d"]["shard"] = [c.shard, c.shards])
        d
    end

    op = resume ? :RESUME : :IDENTIFY
    @debug "Writing $op" logkws(c)...
    try
        writejson(c.conn.io, data)
    catch e
        kws = logkws(c; exception=(e, catch_backtrace()))
        @error "Writing $op failed" kws...
        rethrow(e)
    end

    @debug "Starting background maintenance tasks" logkws(c)...
    @spawn heartbeat_loop(c)
    @spawn read_loop(c)

    c.ready = true
end

"""
    isopen(c::Client) -> Bool

Determine whether the [`Client`](@ref) is connected to the gateway.
"""
Base.isopen(c::Client) = c.ready && c.conn.io !== nothing && isopen(c.conn.io)

"""
    close(c::Client)

Disconnect the [`Client`](@ref) from the gateway.
"""
function Base.close(c::Client; statuscode::Int=1000, zombie::Bool=false)
    isopen(c) || return
    c.ready = false
    if zombie
        # It seems that Discord doesn't send a closing frame for zombie connections
        # (which makes sense). However, close waits for one forever (see HTTP.jl#350).
        @async close(c.conn.io; statuscode=statuscode)
        sleep(1)
    else
        close(c.conn.io; statuscode=statuscode)
    end
end

"""
    wait(c::Client)

Wait for an open [`Client`](@ref) to close.
"""
function Base.wait(c::Client)
    while isopen(c)
        wait(c.conn.io.cond)
        # This is an arbitrary amount of time to wait,
        # but we want to wait long enough to potentially reconnect.
        sleep(10)
    end
end

# Gateway commands.

"""
    request_guild_members(
        c::Client,
        guilds::Union{Integer, Vector{<:Integer};
        query::AbstractString="",
        limit::Int=0,
    ) -> Bool

Request offline guild members of one or more [`Guild`](@ref)s. [`on_guild_members_chunk!`](@ref)
events are sent by the gateway in response.
More details [here](https://discordapp.com/developers/docs/topics/gateway#request-guild-members).
"""
function request_guild_members(
    c::Client,
    guild::Integer;
    query::AbstractString="",
    limit::Int=0,
)
    return request_guild_members(c, [guild]; query=query, limit=limit)
end

function request_guild_members(
    c::Client,
    guilds::Vector{<:Integer};
    query::AbstractString="",
    limit::Int=0,
)
    e, bt = trywritejson(c.conn.io, Dict("op" => 8, "d" => Dict(
        "guild_id" => guilds,
        "query" => query,
        "limit" => limit,
    )))
    show_gateway_error(c, e, bt)
    return e === nothing
end

"""
    update_voice_state(
        c::Client,
        guild::Integer,
        channel::Nullable{Integer},
        mute::Bool,
        deaf::Bool,
    ) -> Bool

Join, move, or disconnect from a voice channel. A `VoiceStateUpdate` event is sent
by the gateway in response.
More details [here](https://discordapp.com/developers/docs/topics/gateway#update-voice-state).
"""
function update_voice_state(
    c::Client,
    guild::Integer,
    channel::Nullable{Integer},
    mute::Bool,
    deaf::Bool,
)
    e, bt = trywritejson(c.conn.io, Dict("op" => 4, "d" => Dict(
        "guild_id" => guild,
        "channel_id" => channel,
        "self_mute" => mute,
        "self_deaf" => deaf,
    )))
    show_gateway_error(c, e, bt)
    return e === nothing
end

"""
    update_status(
        c::Client,
        since::Nullable{Int},
        activity::Nullable{Activity},
        status::Union{PresenceStatus, AbstractString},
        afk::Bool,
    ) -> Bool

Indicate a presence or status update. A `PresenceUpdate` event is sent by the
gateway in response.
More details [here](https://discordapp.com/developers/docs/topics/gateway#update-status).
"""
function update_status(
    c::Client,
    since::Nullable{Int},
    game::Union{Dict, NamedTuple, Activity, Nothing},
    status::Union{Int, AbstractString},
    afk::Bool,
)
    # Update defaults for future calls to set_game.
    c.presence["since"] = since
    c.presence["game"] = game
    c.presence["status"] = status
    c.presence["afk"] = afk

    e, bt = trywritejson(c.conn.io, Dict("op" => 3, "d" => Dict(
        "since" => since,
        "game" => game,
        "status" => status,
        "afk" => afk,
    )))
    show_gateway_error(c, e, bt)
    return e === nothing
end

# Client maintenance.

# Continuously send the heartbeat.
function heartbeat_loop(c::Client)
    v = c.conn.v
    try
        sleep(rand(1:round(Int, c.hb_interval / 1000)))
        heartbeat(c) || (isopen(c) && @error "Writing HEARTBEAT failed" logkws(c)...)

        while c.conn.v == v && isopen(c)
            sleep(c.hb_interval / 1000)
            if c.last_hb > c.last_ack && isopen(c) && c.conn.v == v
                @debug "Encountered zombie connection" logkws(c)...
                reconnect(c; zombie=true)
            elseif !heartbeat(c) && c.conn.v == v && isopen(c)
                @error "Writing HEARTBEAT failed" logkws(c)...
            end
        end
        @debug "Heartbeat loop exited" logkws(c; conn=v)...
    catch e
        kws = logkws(c; conn=v, exception=(e, catch_backtrace()))
        @warn "Heartbeat loop exited unexpectedly" kws...
    end
end

# Continuously read and respond to messages.
function read_loop(c::Client)
    v = c.conn.v
    try
        while c.conn.v == v && isopen(c)
            data, e = readjson(c.conn.io)
            if e !== nothing
                if c.conn.v == v
                    handle_read_exception(c, e)
                else
                    @debug "Read failed, but the connection is outdated" logkws(c; error=e)...
                end
            elseif haskey(HANDLERS, data[:op])
                HANDLERS[data[:op]](c, data)
            else
                @warn "Unkown opcode" logkws(c; op=data[:op])...
            end
        end
        @debug "Read loop exited" logkws(c; conn=v)...
    catch e
        kws = logkws(c; conn=v, exception=(e, catch_backtrace()))
        @warn "Read loop exited unexpectedly" kws...
        c.ready && reconnect(c; zombie=true)
    end
end

# Dispatch an event to its handlers.
function dispatch(c::Client, data::Dict)
    T = get(EVENT_TYPES, data[:t], :UnknownEvent)
    handle(c::Client, Symbol("On"*String(T))::Symbol, data[:d]::Dict)
end
dispatch(c::Client; kwargs...) = dispatch(c::Client, Dict(kwargs))

# Send a heartbeat.
function heartbeat(c::Client, ::Dict=Dict())
    isopen(c) || return false
    e, bt = trywritejson(c.conn.io, Dict("op" => 1, "d" => c.hb_seq))
    show_gateway_error(c, e, bt)
    ok = e === nothing
    ok && (c.last_hb = now())
    return ok
end

# Reconnect to the gateway.
function reconnect(c::Client, ::Dict=Dict(); resume::Bool=true, zombie::Bool=false)
    @info "Reconnecting" logkws(c; resume=resume, zombie=zombie)...
    try
        close(c; zombie=zombie, statuscode=resume ? 4000 : 1000)
    catch ex
        @warn "Unable to close existing connection" ex
    end
    open(c; resume=resume)
end

# React to an invalid session.
function invalid_session(c::Client, data::Dict)
    @warn "Received INVALID_SESSION" logkws(c; resumable=data[:d])...
    sleep(rand(1:5))
    reconnect(c; resume=data[:d])
end

# React to a hello message.
hello(c::Client, data::Dict) = c.hb_interval = data[:d][:heartbeat_interval]

# React to a heartbeack ack.
heartbeat_ack(c::Client, ::Dict) = c.last_ack = now()

# Gateway opcodes => handler function.
const HANDLERS = Dict(
    0   => dispatch,
    1   => heartbeat,
    7   => reconnect,
    9   => invalid_session,
    10  => hello,
    11  => heartbeat_ack,
)

# Error handling.

const CLOSE_CODES = Dict(
    1000 => :NORMAL,
    4000 => :UNKNOWN_ERROR,
    4001 => :UNKNOWN_OPCODE,
    4002 => :DECODE_ERROR,           # Probably a library bug.
    4003 => :NOT_AUTHENTICATED,      # Probably a library bug.
    4004 => :AUTHENTICATION_FAILED,
    4005 => :ALREADY_AUTHENTICATED,
    4007 => :INVALID_SEQ,            # Probably a library bug.
    4008 => :RATE_LIMITED,           # Probably a library bug.
    4009 => :SESSION_TIMEOUT,
    4010 => :INVALID_SHARD,
    4011 => :SHARDING_REQUIRED,
)

# Deal with an error from reading a message.
function handle_read_exception(c::Client, e::Exception)
    @debug "Handling a $(typeof(e))" logkws(c; error=e)...
    c.ready && handle_specific_exception(c, e)
end

handle_specific_exception(::Client, ::Empty) = nothing
handle_specific_exception(c::Client, ::EOFError) = reconnect(c)
function handle_specific_exception(c::Client, e::HTTP.WebSockets.WebSocketError)
    err = get(CLOSE_CODES, e.status, :UNKNOWN_ERROR)
    if err === :NORMAL
        close(c)
    elseif err === :AUTHENTICATION_FAILED
        @error "Authentication failed" logkws(c)...
        close(c)
    elseif err === :INVALID_SHARD
        @error "Invalid shard" logkws(c)...
        close(c)
    elseif err === :SHARDING_REQUIRED
        @error "Sharding required" logkws(c)...
        close(c)
    else
        @debug "Gateway connection was closed" logkws(c; code=e.status, error=err)...
        reconnect(c)
    end
end
function handle_specific_exception(c::Client, e::Exception)
    @error sprint(showerror, e) logkws(c)...
    isopen(c) || reconnect(c)
end

# Read a JSON message.
function readjson(io)
    data = readavailable(io)
    if isempty(data)
        nothing, Empty()
    else
        k , v = JSON3.read(String(data), Dict), nothing
        symk = symbolize(k)
        return symk, v
    end
end

# Write a JSON message.
writejson(::Nothing, body) = error("Tried to write to an uninitialized connection")
writejson(io, body) = write(io, jsonify(body))

# Write a JSON message, but don't throw an exception.
function trywritejson(io, body)
    return try
        writejson(io, body)
        nothing, nothing
    catch e
        e, catch_backtrace()
    end
end

# Display a gateway error.
show_gateway_error(c::Client, ::Nothing, ::Nothing) = nothing
function show_gateway_error(c::Client, e::Exception, bt)
    kws = logkws(c; exception=(e, bt))
    if isopen(c)
        @error "Gateway error" kws...
    else
        @debug "Gateway error" kws...
    end
end


text:
include("tools.jl")
include("loadFiles.jl")

xf = XLSX.open_empty_template()

# Testing class matches
#counter = 1
# Iterate over all classes:
classFile   = "/Users/cfranken/GDrive/work/Caltech/OptionRepWork/TA2021/GPSClassList_2021.xlsx"
tabs =  ["Division", "Geology", "Geophysics", "Geobiology", "Geochemistry", "Planetary Science", "ESE", "Special Courses"]
data_per_option = []
for ii in eachindex(tabs)
    println(tabs[ii])
    df_classes  = DataFrame(XLSX.readtable(classFile, tabs[ii])...)
    df_classes."Matched"   .= ""
    df_classes."Unmatched" .= ""
    for class in df_classes."Class Name"
        foundEntry = false
        for row in eachrow(df_faculty)
            for course in keys(FacTA_class)
                    #filteredClass = dropmissing(df_faculty,FacTA_class[course])
                if ismissing(row[FacTA_class[course]]) == false
                    # Look for match:
                    if class == row[FacTA_class[course]]
                        foundEntry = true
                        #println(row[FacTA_class[course]])
                        find_matched_students(df_classes, df_students, row, course)
                    end
                end
            end
        end
        if !foundEntry
            find_matched_students(df_classes, df_students, class)
            #println(class, " ", !foundEntry)
        end
    end

    # Export to Excel:
    if xf[1].name == "Sheet1"
        # first sheet already exists in template file
        sheet = xf[1]
        XLSX.rename!(sheet, tabs[ii])
        XLSX.writetable!(sheet, collect(DataFrames.eachcol(df_classes)), DataFrames.names(df_classes))
    else
        println("Adding new tab ",tabs[ii] )
        sheet = XLSX.addsheet!(xf, tabs[ii])
        XLSX.writetable!(sheet, collect(DataFrames.eachcol(df_classes)), DataFrames.names(df_classes))
    end
    #push!(data_per_option,df_classes)
end

outFile = "/Users/cfranken/GDrive/work/Caltech/OptionRepWork/TA2021/GPS_TA_2021_matchedList.xlsx"
XLSX.writexlsx(outFile, xf, overwrite=true)

max_stars_repo_name: cfranken/TA-matching

max_issues_repo_name: cfranken/TA-matching

max_forks_repo_name: cfranken/TA-matching


text:
using FFTW
using DoubleFloats
using Random
using LinearAlgebra
using SemiLagrangian:
    PrepareFftBig,
    fftbig,
    fftbig!,
    fft,
    fft!,
    fftgen,
    ifftgen,
    fftgen!,
    ifftgen!,
    fftgenall,
    fftgenall!,
    ifftgenall,
    ifftgenall!

function getfalse(tab)
    for i = 1:size(tab, 1), j = 1:size(tab, 2)
        if !tab[i, j]
            return i, j
        end
    end
    return 0, 0
end

function testfftbig(s, T::DataType, seed_val)

    Random.seed!(seed_val)
    tab = zeros(Complex{T}, 1, s)
    tab .= rand(T, 1, s)
    if T == Float64
        tabfftref = fft(tab, 2)
    else
        tab2 = zeros(Complex{Float64}, 1, s)
        tab2 .= tab
        tabfftref = fft(tab2, 2)
    end

    tab_test = copy(tab)

    p = PrepareFftBig(s, real(tab[1, 1]), dims = (2,))

    fftbig!(p, tab_test)

    @test isapprox(tabfftref, tab_test, atol = 1e-15, rtol = 1e-15)

    tol = (T == BigFloat) ? 1e-50 : 1e-15

    fftbig!(p, tab_test, flag_inv = true)

    @test getfalse(isapprox.(tab, tab_test, atol = tol, rtol = tol)) == (0, 0)

    @test isapprox(tab, tab_test, atol = tol, rtol = tol)

end
function testfftbig2(s, T::DataType, seed_val, nb_v; dims = (1,))

    Random.seed!(seed_val)
    sizedims = dims[1] == 1 ? (s, nb_v) : (nb_v, s)
    tab = zeros(Complex{T}, sizedims)
    tab .= rand(T, sizedims)
    if T == Float64
        tabfftref = fft(tab, dims)
    else
        tab2 = zeros(Complex{Float64}, sizedims)
        tab2 .= tab
        tabfftref = fft(tab2, dims)
    end

    tab_test = copy(tab)

    p = PrepareFftBig(s, one(T), dims = dims)

    tab_test2 = fftbig(p, tab_test)

    @test isapprox(tabfftref, tab_test2, atol = 1e-15, rtol = 1e-15)

    fftbig!(p, tab_test)

    @test isapprox(tabfftref, tab_test, atol = 1e-15, rtol = 1e-15)

    tol = (T == BigFloat) ? 1e-50 : 1e-15

    tab_test3 = fftbig(p, tab_test, flag_inv = true)
    @test isapprox(tab, tab_test3, atol = tol, rtol = tol)

    fftbig!(p, tab_test, flag_inv = true)

    @test isapprox(tab, tab_test, atol = tol, rtol = tol)

end

function testfftbig2bis(s, T::DataType, seed_val, nb_v; dims = (1,))

    Random.seed!(seed_val)
    otherdim = dims[1] == 1 ? 2 : 1
    sizedims = dims[1] == 1 ? (s, nb_v) : (nb_v, s)
    tab = zeros(Complex{T}, sizedims)
    tab .= rand(T, sizedims)
    if T == Float64
        tabfftref = fft(tab, numdim)
    else
        tab2 = zeros(Complex{Float64}, sizedims)
        tab2 .= tab
        tabfftref = fft(tab2, dims)
    end

    tab_test = copy(tab)

    p = PrepareFftBig(s, one(T), numdims = 1, dims = (1,))

    tab_test2 = Array{Complex{T},2}(undef, sizedims)
    for i = 1:nb_v
        s_in = selectdim(tab_test, otherdim, i)
        s_out = selectdim(tab_test2, otherdim, i)
        s_out .= fftbig(p, s_in)
    end

    @test isapprox(tabfftref, tab_test2, atol = 1e-15, rtol = 1e-15)

    for i = 1:nb_v
        sel = selectdim(tab_test, otherdim, i)
        fftbig!(p, sel)
    end

    @test isapprox(tabfftref, tab_test, atol = 1e-15, rtol = 1e-15)

    tol = (T == BigFloat) ? 1e-50 : 1e-15

    tab_test3 = Array{Complex{T},2}(undef, sizedims)
    for i = 1:nb_v
        s_in = selectdim(tab_test, otherdim, i)
        s_out = selectdim(tab_test3, otherdim, i)
        s_out .= fftbig(p, s_in, flag_inv = true)
    end
    @test isapprox(tab, tab_test3, atol = tol, rtol = tol)

    for i = 1:nb_v
        sel = selectdim(tab_test, otherdim, i)
        fftbig!(p, sel, flag_inv = true)
    end

    @test isapprox(tab, tab_test, atol = tol, rtol = tol)

end

function testfftbigprec(s, seed_v)

    @time @testset "test fftbig changing precision s=$s seed=$seed_v" begin
        Random.seed!(seed_v)
        for k = 1:10
            setprecision(k * 256) do
                p = PrepareFftBig(s, dims = (1,))
                tab = rand(BigFloat, s, 5)
                tabRef = Complex.(tab)
                tabRes = fftgen(p, tab)
                tabRes2 = ifftgen(p, tabRes)
                #                println("k=$k norm=$(norm(tabRef-tabRes2))")
                #                @test isapprox(tabRef, tabRes2, atol=1e-78^k)
                #                @test isapprox(real.(tabRef), real.(tabRes2), atol=1e-78^k)
                @test isapprox(tabRef, tabRes2, atol = 1e-75^k)
                @test isapprox(real.(tabRef), real.(tabRes2), atol = 1e-75^k)
            end
        end
    end

end

tab_decl = [
    [8, Float64, 12345678],
    [8, BigFloat, 9876],
    [2^8, Float64, 1928],
    [2^10, BigFloat, 5656],
]

for t in tab_decl
    s = t[1]
    type = t[2]
    seed_v = t[3]
    @time @testset "test fftbig for value size=$s type=$type" begin
        testfftbig(s, type, seed_v)
    end
end
tab_decl2 = [
    [8, Float64, 4556789, 4],
    [8, BigFloat, 4563, 4],
    [2^10, Float64, 9900771, 4],
    [2^9, BigFloat, 125609, 4],
]

for t in tab_decl2
    s = t[1]
    type = t[2]
    seed_v = t[3]
    nb_v = t[4]
    @time @testset "test fftbig for vector size=$s type=$type nb_v=$nb_v" begin
        testfftbig2(s, type, seed_v, nb_v)
    end
end

tab_decl3 = [8965, 1919191, 56188827, 9017555]
for sd in tab_decl3
    testfftbigprec(32, sd)
end

@time @testset "testfftgen" begin
    s = 128
    #s = 2048
    f = zeros(Complex{Float64}, 100, s)
    f .= rand(100, s)
    f_0 = copy(f)
    f2 = zeros(Complex{BigFloat}, 100, s)
    f2 .= f
    f2_0 = copy(f2)
    p = PrepareFftBig(s, 0.0, dims = (2,))
    p2 = PrepareFftBig(s, big"0.0", dims = (2,))
    fftgen!(p, f)
    fftgen!(p2, f2)
    println("1norm(f-f2)=$(norm(f-f2))")
    @test isapprox(f, f2, atol = 1e-11)
    ifftgen!(p, f)
    ifftgen!(p2, f2)
    println("2norm(f-f2)=$(norm(f-f2))")
    @test isapprox(f, f2, atol = 1e-11)
    println("norm(f-f_0)=$(norm(f-f_0))")
    @test isapprox(f, f_0, atol = 1e-11)
    println("norm(f2-f2_0)=$(norm(f2-f2_0))")
    @test isapprox(f2, f2_0, atol = 1e-70)
end
@time @testset "testfftgen2" begin
    s = 128
    # s = 2048
    f = zeros(Complex{Float64}, s, 100)
    f .= rand(s, 100)
    f_0 = copy(f)
    f2 = zeros(Complex{BigFloat}, s, 100)
    f2 .= f
    f2_0 = copy(f2)
    p = PrepareFftBig(s, 0.0, numdims = 1)
    p2 = PrepareFftBig(s, big"0.0", numdims = 1)
    for i = 1:100
        fftgen!(p, f[:, i])
        fftgen!(p2, f2[:, i])
    end
    println("1norm(f-f2)=$(norm(f-f2))")
    @test isapprox(f, f2, atol = 1e-11)
    for i = 1:100
        ifftgen!(p, f[:, i])
        ifftgen!(p2, f2[:, i])
    end
    println("2norm(f-f2)=$(norm(f-f2))")
    @test isapprox(f, f2, atol = 1e-11)
    println("norm(f-f_0)=$(norm(f-f_0))")
    @test isapprox(f, f_0, atol = 1e-11)
    println("norm(f2-f2_0)=$(norm(f2-f2_0))")
    @test isapprox(f2, f2_0, atol = 1e-70)
end
@time @testset "testfftgen3" begin
    s = 128
    # s = 2048
    f = zeros(Complex{Float64}, 100, s)
    f .= rand(100, s)
    f_0 = copy(f)
    f2 = zeros(Complex{BigFloat}, 100, s)
    f2 .= f
    f2_0 = copy(f2)
    p = PrepareFftBig(s, 0.0, dims = (1,))
    p2 = PrepareFftBig(s, big"0.0", dims = (1,))
    for i = 1:100
        fftgen!(p, f[i, :])
        fftgen!(p2, f2[i, :])
    end
    println("1norm(f-f2)=$(norm(f-f2))")
    @test isapprox(f, f2, atol = 1e-11)
    for i = 1:100
        ifftgen!(p, f[i, :])
        ifftgen!(p2, f2[i, :])
    end
    println("2norm(f-f2)=$(norm(f-f2))")
    @test isapprox(f, f2, atol = 1e-11)
    println("norm(f-f_0)=$(norm(f-f_0))")
    @test isapprox(f, f_0, atol = 1e-11)
    println("norm(f2-f2_0)=$(norm(f2-f2_0))")
    @test isapprox(f2, f2_0, atol = 1e-70)
end
@testset "performance fft" begin
    Base.GC.gc()
    @time testfftbig2(2^7, BigFloat, 12344321, 50, dims = (2,))
    Base.GC.gc()
    @time testfftbig2(2^7, BigFloat, 12344321, 50, dims = (1,))
    Base.GC.gc()
    @time testfftbig2bis(2^7, BigFloat, 12344321, 50, dims = (2,))
    Base.GC.gc()
    @time testfftbig2bis(2^7, BigFloat, 12344321, 50, dims = (1,))
end

function testfftbigmult(s, T::DataType, seed_val; dims = (1,))

    Random.seed!(seed_val)
    tab = zeros(Complex{T}, s)
    tab .= rand(T, s)
    if T == Float64
        tabfftref = fft(tab, dims)
    else
        tab2 = zeros(Complex{Float64}, s)
        tab2 .= tab
        tabfftref = fft(tab2, dims)
    end

    tab_test = copy(tab)

    p = PrepareFftBig(s[collect(dims)], one(T), dims = dims)

    tab_test2 = fftbig(p, tab_test)

    @test isapprox(tabfftref, tab_test2, atol = 1e-15, rtol = 1e-15)

    fftbig!(p, tab_test)

    @test isapprox(tabfftref, tab_test, atol = 1e-15, rtol = 1e-15)

    tol = (T == BigFloat) ? 1e-50 : 1e-15

    tab_test3 = fftbig(p, tab_test, flag_inv = true)
    @test isapprox(tab, tab_test3, atol = tol, rtol = tol)

    fftbig!(p, tab_test, flag_inv = true)

    @test isapprox(tab, tab_test, atol = tol, rtol = tol)

end
@testset "multi-dimension test" begin
    @time testfftbigmult((3, 32, 5, 16), BigFloat, 15413; dims = (2, 4))
    @time testfftbigmult((3, 32, 16, 8), BigFloat, 44444, dims = (2, 3, 4))
    @time testfftbigmult((4, 32, 16, 8), BigFloat, 44544, dims = (1, 2, 3, 4))
end

function testfftallbig(s, T::DataType, seed_val)

    Random.seed!(seed_val)
    tab = zeros(Complex{T}, s)
    tab .= rand(T, s)
    dims = ntuple(x -> x, size(s, 1))
    if T == Float64
        tabfftref = fft(tab, dims)
    else
        tab2 = zeros(Complex{Float64}, s)
        tab2 .= tab
        tabfftref = fft(tab2, dims)
    end

    tab_test = copy(tab)

    p = PrepareFftBig(s[collect(dims)], one(T), dims = dims)

    tab_test2 = fftgenall(p, tab_test)

    @test isapprox(tabfftref, tab_test2, atol = 1e-15, rtol = 1e-15)

    fftgenall!(p, tab_test)

    @test isapprox(tabfftref, tab_test, atol = 1e-15, rtol = 1e-15)

    tol = (T == BigFloat) ? 1e-50 : (T == Double64) ? 1e-30 : 1e-15

    tab_test3 = ifftgenall(p, tab_test)
    @test isapprox(tab, tab_test3, atol = tol, rtol = tol)

    ifftgenall!(p, tab_test)

    @test isapprox(tab, tab_test, atol = tol, rtol = tol)

end

@testset "all-dimension test" begin
    @time testfftallbig((4, 32, 4, 16), BigFloat, 15416)
    @time testfftallbig((4, 32, 16, 8), BigFloat, 44488)
    @time testfftallbig((8, 32, 16, 8), BigFloat, 44588)
    @time testfftallbig((4, 32, 4, 16), Double64, 15416)
    @time testfftallbig((4, 32, 16, 8), Double64, 44488)
    @time testfftallbig((8, 32, 16, 8), Double64, 44588)
end


text:
import GeometryTypes, Chipmunk, LinearAlgebra
const gt = GeometryTypes
const Cp = Chipmunk
const la = LinearAlgebra

"""
    physic(ws::WorldState, what::Symbol, index::Int,
        new_pos::AbstractVector{<:AbstractFloat})

Handle physic. Objects can't overlapp.

returns the adjusted position.

# ToDo
- maybe: prevent from leaving the field? - No, thats not physic
- currently object can go through objects if the they are not touching each
  other at the new position
- handle collisions
- trigger ball movements
- replace by an physic engine (maybe https://github.com/klowrey/MuJoCo.jl)
"""
function physic(ws::WorldState, player::Object{<:Number}, new_pos::AbstractVector{<:Number})
    # initialization

    current_player = gt.Circle(gt.Point2(new_pos), get_size(player))

    other_player = [gt.Circle(gt.Point2(ps), get_size(ps)) for ps in vcat(ws.myPlayers, ws.opponents, ws.obstacles) if ps!=player]

    # handling
    touching_player = map(x -> is_touching(current_player, x), other_player)
    if !any(touching_player)
        return new_pos
    elseif sum(touching_player) == 1
        # REMINDER: at least two robots are touching - handle collisions here
        touchers = other_player[touching_player]
        toucher = touchers[1]
        return toucher.center .+ la.normalize(vector(toucher.center, new_pos)).*(toucher.r + current_player.r)
    elseif sum(touching_player) == 2
        touchers = other_player[touching_player]
        t1, t2 = touchers[1:2]
        t12 = vector(t1.center, t2.center)./2
        t12_n = normal2d(t12)
        d = t1.center'*t12_n
        if new_pos'*t12_n - d <= 0
            t12_n .*= -1
        end
        return t1.center .+ t12 .+ la.normalize(t12_n).*sqrt(4*max(t1.r, t2.r)^2 - distance(t1.center, t2.center)^2/4)
    else
        @warn "physic - There are more than 2 ($(other_player[touching_player])) obstacles touching a player at new position $(new_pos)"
    end
    return new_pos
end

"""
    update_game!(ws::WorldState, dt::Number, config::Config)

Update procedure for the world `ws` after the time `dt`.
"""
function update_game!(ws::WorldState, dt::Number, config::Config)

    # for player in vcat(ws.myPlayers::Vector{Robot{Float64}}, ws.opponents::Vector{Robot{Float64}})::Vector{Robot{Float64}} # TODO: tooks too many memory
    #     apply_order!(player)
    # end
    for myPlayer in ws.myPlayers::Vector{Robot{Float64}}
        apply_order!(myPlayer)
    end
    for opponent in ws.opponents::Vector{Robot{Float64}}
        apply_order!(opponent)
    end
    # apply_order!.(ws.myPlayers)
    # apply_order!.(ws.opponents)
    Cp.step(config.space, dt)
    get_strategy(config).run_strategies(config, dt, :step)
    return
end

# function physic_step(config::Config, dt::Number)
#     Cp.step(config.space, dt)
#     return
# end

"""
    getvalid_playerpos(config::Config)

Searches for a position for a new player to be inserted in the arena.
"""
function getvalid_playerpos(config::Config, player_size::Number)
    start_pos = [player_size - config.field_size[1]/2, config.field_size[2]/2 - player_size]
    while any([distance(start_pos, pos) <= 2*player_size for pos in vcat(config.ws.myPlayers, config.ws.opponents, config.ws.obstacles)])
        start_pos[1] += 3*player_size
    end
    start_pos
end

"""
    add_myPlayer!(config::Config)

Adds an player (myPlayer) with default values to the arena.
"""
function add_myPlayer!(config::Config)
    new_robot = Robot(getvalid_playerpos(config, get_size(NaoV5([0.0, 0.0], 0.0))), 0.0)
    add_object!(config, new_robot)
    push!(config.ws.myPlayers, new_robot) # HACK: adds a NaoV5
    # config.ws.all = vcat(config.ws.myPlayers,config.ws.opponents,config.ws.obstacles)
    # config.ws.player = vcat(config.ws.myPlayers, config.ws.opponents)
end

"""
    add_opponent!(config::Config)

Adds an player (opponent) with default values to the arena.
"""
function add_opponent!(config::Config)
    new_robot = Robot(getvalid_playerpos(config, get_size(NaoV5([0.0, 0.0], 0.0))), Float64(π))
    add_object!(config, new_robot)
    push!(config.ws.opponents, new_robot)  # HACK: adds a NaoV5
    # config.ws.all = vcat(config.ws.myPlayers,config.ws.opponents,config.ws.obstacles)
    # config.ws.player = vcat(config.ws.myPlayers, config.ws.opponents)
end