text:
function Base.isequal(t1::Table, t2::Table)
    return (t1.schema == t2.schema) && isequal(t1.data, t2.data)
end

# shallow-copy
function Base.copy(t::Table)
    new_schema = copy(t.schema)
    new_data = copy(t.data)
    return Table(new_schema, new_data)
end

function Base.deepcopy(t::Table)
    new_schema = deepcopy(t.schema)
    new_data = deepcopy(t.data)
    return Table(new_schema, new_data)
end

function Table(schema::Schema)
    cols = ncol(schema)
    data = Vector{Any}(undef, cols)
    @inbounds for c in 1:cols
        col_type = schema.types[c]
        col_data = _create_table_column(col_type, 0)
        data[c] = col_data
    end
    return Table(schema, data)
end

# DataFrame like creation
# Table(a=[1,2], b=[3,missing])
function Table(; kwargs...)
    n = length(kwargs)

    column_names = Vector{Symbol}(undef, n)
    column_types = Vector{Type}(undef, n)
    data = Vector{Any}(undef, n)

    for (i, p) in enumerate(kwargs)
        name, column_data = p
        @assert isa(column_data, Vector) "$(i)th argument is not a vector: $column_data."
        column_names[i] = name
        column_types[i] = eltype(column_data)
        data[i] = column_data
    end

    return Table(Schema(column_names, column_types), data)
end

function Table(schema::Schema, matrix::Array{T, 2}) where {T}
    rows, cols = size(matrix)
    tb = Table(schema, rows)
    for c in 1:cols
        for r in 1:rows
            tb[r,c] = matrix[r,c]
        end
    end
    return tb
end

_create_table_column(::Type{Union{Missing, T}}, rows::Int) where {T} = Vector{Union{Missing, T}}(missing, rows)
_create_table_column(::Type{String}, rows::Int) = fill("", rows)
_create_table_column(::Type{T}, rows::Int) where {T<:Number} = zeros(T, rows)
_create_table_column(::Type{Date}, rows::Int) = fill(Date(0), rows)
_create_table_column(::Type{T}, rows::Int) where {T} = error("Method not implemented for type $T")

# Creates a table with number of rows = rows.
# Table data is not initialized.
function Table(schema::Schema, rows::Int)
    cols = length(names(schema))
    data = Vector{Any}(undef, cols)
    @inbounds for c in 1:cols
        col_type = schema.types[c]
        col_data = _create_table_column(col_type, rows)
        data[c] = col_data
    end
    Table(schema, data)
end

function Table(schema::Schema, df::DataFrame)
    rows, cols = size(df)
    @assert cols == length(schema.names) "number of columns in DataFrame ($cols) does not match schema ($(length(schema.names)))."
    data = Vector{Any}(undef, cols)

    @inbounds for (c, df_column) in enumerate(DataFrames.eachcol(df))
        @assert eltype(df_column) == schema.types[c] "Type mismatch between schema ($(schema.types[c])) and DataFrame ($(eltype(df_column))) for column $c."
        data[c] = copy(df_column)
    end

    Table(schema, data)
end

function Table(types::Vector{T}, df::DataFrame) where {T<:Type}
    head = names(df)
    schema = Schema(head, types)
    return Table(schema, df)
end

function Table(df::DataFrame)
    rows, cols = size(df)
    col_names = copy(names(df))
    col_types = Vector{Type}(undef, cols)

    data = Vector{Any}(undef, cols)

    @inbounds for (c, df_column) in enumerate(DataFrames.eachcol(df))
        col_types[c] = eltype(df_column)
        data[c] = copy(df_column)
    end

    return Table(Schema(col_names, col_types), data)
end

DataFrames.names(t::Table) = names(t.schema)

function DataFrames.rename!(tb::Table, new_header::Vector)
    new_header_as_sym = new_header_as_sym = [ Symbol(x) for x in new_header]
    DataFrames.rename!(tb.schema, new_header_as_sym)
end

"""
    column_index(tb::Table, colname::Symbol)

Returns the index of the column `colname`.
Returns `nothing` if the column is not found.
"""
column_index(tb::Table, colname::Symbol) = findfirst(c->c==colname, names(tb))

function Base.getindex(tb::Table, colname::Symbol)
    index = column_index(tb, colname)
    @assert index != nothing "Couldn´t find column $colname"
    return tb.data[index]
end

Base.getindex(tb::Table, r::Int, c::Int) = tb.data[c][r]
Base.getindex(tb::Table, colname::AbstractString) = getindex(tb, Symbol(colname))
Base.getindex(tb::Table, ::Colon, c::Int) = tb.data[c]
Base.getindex(tb::Table, r::Int, ::Colon) = error("Not supported. Try Tables.TableRow(tb, $r)[:]")
Base.getindex(tb::Table, r::Int, colname::Symbol) = getindex(tb, colname)[r]
Base.getindex(tb::Table, colname::Symbol, r::Int) = getindex(tb, r, colname)

"""
    _column_index__create_column(tb::Table, column_description::Pair{Symbol, DataType})

Returns the index of the column `colname`.
If the column is not found, creates a new column and returns its index.
"""
function _column_index__create_column(tb::Table, column_description::Pair{Symbol, T}) where {T<:Type}
    colname = column_description[1]
    coltype = column_description[2]

    index = column_index(tb, colname)

    # Creates a new column if couldn´t find it
    if index == nothing
        r, c = size(tb)
        index = c + 1
        push!(tb.schema, column_description)
        col_data = _create_table_column(coltype, r)
        push!(tb.data, col_data)
    end
    return index
end

function Base.setindex!(tb::Table, value, r::Int, c::Int)
    tb.data[c][r] = value
end

function Base.setindex!(tb::Table, value, colname::Symbol, r::Int)
    index = _column_index__create_column(tb, colname => typeof(value))
    tb.data[index][r] = value
end

function Base.setindex!(tb::Table, value::Vector{T}, colname::Symbol) where {T}
    index = _column_index__create_column(tb, colname => T)
    r = nrow(tb)
    @assert length(value) == r
    @assert T == tb.schema.types[index]
    tb.data[index] = value
end

function Base.setindex!(tb::Table, value, colname::Symbol)
    index = _column_index__create_column(tb, colname => typeof(value))
    fill!(tb.data[index], value)
end

Base.isempty(tb::Table) = isempty(tb.data)

function Base.size(tb::Table)
    isempty(tb) && return (0, 0)
    length(tb.data[1]), length(names(tb))
end

function DataFrames.nrow(tb::Table)
    r, c = size(tb)
    return r
end

function DataFrames.ncol(tb::Table)
    r, c = size(tb)
    return c
end

function Base.show(io::IO, table::Table)
    r, c = size(table)
    println(io, "Table [$r, $c]")
end

function Base.append!(tb::Table, rows::Table)
    @assert tb.schema == rows.schema "Schemas don't match"
    for c in 1:ncol(tb)
        append!(tb.data[c], rows.data[c])
    end
    return tb
end

# append row to table
function Base.push!(tb::Table, row::Array{T,1}) where {T}
    # Check number of columns
    cols = length(row)
    @assert cols == ncol(tb) "Number of columns doesn't match vector size."

    tb_tmp = Table(tb.schema, 1)

    # Set values
    for i = 1:cols
        tb_tmp[1, i] = row[i]
    end
    return append!(tb, tb_tmp)
end

Base.append!(tb::Table, row::Array{T,1}) where {T} = push!(tb, row)

function Base.append!(tb::Table, data::Array{T,2}) where {T}
    # Check number of columns
    cols = ncol(tb)
    data_rows, data_cols = size(data)
    @assert cols == data_cols "Number of columns doesn't match"

    tb_tmp = Table(tb.schema, data)
    return append!(tb, tb_tmp)
end

function Base.vcat(tb1::Table, tb2::Table)
    @assert tb1.schema == tb2.schema "Schemas don't match"
    tb1_copy = deepcopy(tb1)
    tb2_copy = deepcopy(tb2)
    return append!(tb1_copy, tb2_copy)
end

function Base.vcat(tb::Table, data::Array{T,1}) where {T}
    if isempty(data)
        return deepcopy(tb)
    else
        tb_copy = deepcopy(tb)
        return push!(tb_copy, data)
    end
end

function Base.vcat(tb::Table, data::Array{T,2}) where {T}
    if isempty(data)
        return deepcopy(tb)
    else
        tb_copy = deepcopy(tb)
        return append!(tb_copy, data)
    end
end


text:
@testset "Indexed" begin
    v = rand(10)

    a, b = 4, 8
    c, d = 5, 9

    M = randn(a, b)
    N = rand(c, d)

    p = rand(1:a, 6)
    q = rand(1:c, 6)

    r = rand(1:b, 10)
    t = rand(1:d, 10)

    kronprod = N ⊗ M

    @test_throws DimensionMismatch kronprod[p, rand(1:b, 11), r,t]
    @test_throws BoundsError kronprod[p, -q, r,t]
    @test_throws BoundsError kronprod[p, q.+b, r,t]
    @test_throws BoundsError kronprod[p,q,r,t.+d]

    ikp = kronprod[p,q,r,t]

    @test eltype(ikp) == Float64

    subsystem = kron(N, M)[a * (q .- 1) .+ p, b * (t .- 1) .+ r]
    @test subsystem ≈ collect(ikp)
    @test subsystem ≈ ikp

    @test (N, M) == getmatrices(ikp)

    # result shortcut
    #u = genvectrick(M, N, v, p, q, r, t)
    u = ikp * v
    # result naive
    unaive = kron(N, M)[a * (q .- 1) .+ p, b * (t .- 1) .+ r] * v
    @test all(u .≈ unaive)

    @test_throws DimensionMismatch ikp * rand(8)
end

ext: jl


text:
module RegistryCI

# import GitCommand

include("TagBot/TagBot.jl")
include("AutoMerge/AutoMerge.jl")
include("registry_testing.jl")
include("utils.jl")

end # module


text:
### A Pluto.jl notebook ###
# v0.16.0

using Markdown
using InteractiveUtils

# This Pluto notebook uses @bind for interactivity. When running this notebook outside of Pluto, the following 'mock version' of @bind gives bound variables a default value (instead of an error).
macro bind(def, element)
    quote
        local el = $(esc(element))
        global $(esc(def)) = Core.applicable(Base.get, el) ? Base.get(el) : missing
        el
    end
end

# ╔═╡ f4653ca6-5cba-11eb-0935-4d963ea430c8
begin
	using Pkg
	Pkg.add(["Plots", "PlutoUI","StatsBase", "DataStructures", "Unitful", "PhysicalConstants", "UnitfulEquivalences", "DataFrames", "CSV", "Dates", "TimeZones", "StatsPlots", "ImageFiltering", "OffsetArrays"])

	using Plots
	plotly()
	using PlutoUI
	using StatsBase
	using DataStructures
	using Unitful
	using PhysicalConstants.CODATA2018: c_0, h, ħ
	using UnitfulEquivalences
	using DataFrames
	using CSV
	using TimeZones
	using Dates
	using StatsPlots
	using ImageFiltering
	using OffsetArrays
end

# ╔═╡ cd4fe1e2-5cbb-11eb-392d-37cd89200e04
md"""
__Author__: Kristjan Laht, B59990
"""

# ╔═╡ e0f2ceba-5cba-11eb-1f14-ad4ef1619c20
md"""
# Functions in physics
1. In the figure below you see a graph of some function. It is a square function.
"""

# ╔═╡ b9290aee-5cba-11eb-16e7-c92402c6f6b8
md"""
### 2. Physical example

This function could be the function of velocity with regards to time with a constant _jerk_.

$$v(t) = \frac{jt^2}{2} + a_0t + v_0$$
$$1 \frac{m}{s} = \frac{1 \frac{m}{s^3} \cdot (1 s)^2}{2} + 1 \frac{m}{s^2} \cdot 1s + 1 \frac{m}{s}$$

* _$v$_ velocity - it's the first deriative of position, showing the change of change of postion.
* __$v_0$__ starting velocity
* __$a_0$__ starting acceleration. acceleration is the first deriative of velocity and the second deriative of position.
* __$j$__ jerk - the third deriative of position. constantly changing velocity.
* __$t$__ time
"""

# ╔═╡ cc76810a-5cba-11eb-3784-e9ccb5bc8ce2
md"""
### 3. Integral

$$v(t) = \frac{jt^2}{2} + a_0t + v_0$$
$$ds = v\ dt =  \left( \frac{jt^2}{2} + a_0t + v_0 \right) dt$$
$$\int_{s_0}^s ds = \int_0^t \left( \frac{jt^2}{2} + a_0t + v_0 \right) dt$$
$$\int_{s_0}^s ds = \int_0^t \left( \frac{jt^2}{2} + a_0t + v_0 \right) dt$$
$$s-s_0 = \frac{jt^3}{6} + \frac{a_0t^2}{2} + v_0t $$
$$s = \frac{jt^3}{6} + \frac{a_0t^2}{2} + v_0t + s_0$$

I chose to derive this integral with the limits of $(s_0, s)$ to get the positional displacement in familiar terms. (where $s_0$ is the starting positional displacement and $s$ is the final displacement)

The integral of the constant jerk velocity function with regards to time is the positional displacement physical quantity.
"""

# ╔═╡ ccc56cde-5cba-11eb-0601-bf2f29d0639a
md"""

### 4. Property of Nature

The square function describes the concept of compound growth. How even a linearly changing variable, when summed up over time, changes polynominally. This can happen when measuring speed and the jerk increases linearly.

"""

# ╔═╡ ccf312c4-5cba-11eb-24dc-c53f2185596b
square(a,x,c) = a*x^2 + x;

# ╔═╡ 282bfa7a-5cbb-11eb-3e20-818cbc5cadbb
@bind j Slider(2:50, show_value=true)

# ╔═╡ 72d21bfc-5cd1-11eb-2a88-5b7e0e16a951
@bind v_0 Slider(0:50, show_value=true)

# ╔═╡ 327c1456-5cbb-11eb-2537-29ee89edf2be
@bind s_0 Slider(-500:500, show_value=true);

# ╔═╡ 2aa87dd4-5cbb-11eb-3465-fbfff6973a43
x = 0:1:50;

# ╔═╡ e42deb9e-5cba-11eb-05bc-f99846852e79
begin
	plot(x,square.(j, x, v_0), xlabel="t (second)", ylabel="v (m/s)", label="v(t)")
	plot!([0], [v_0], seriestype = :scatter, label="v₀")
end

# ╔═╡ 687a0c52-5cbb-11eb-3b69-d3f0ba61397d
md"""
# Second function
#### 1.
In the figure below you see a graph of some function. It is an exponential decay function.
"""

# ╔═╡ 47a12e5c-5cc3-11eb-38c0-13546d4e6b2a
md"""
### 3. Derivative

The derivative of the discharge function is:

$$\frac{dV}{dt} = -\frac{V_0}{RC} e^{ \frac{-t}{RC}}$$

Which, if we move the $$C$$ to the other side, gets us the __current__ in the capacitor:

$$I(t) = C \frac{dV}{dt} = -\frac{V_0}{R} e^{\frac{-t}{RC}}$$

This only makes sense with regards to positive time t ∈ (0;∞) and positive voltage V ∈ (0;∞).

"""

# ╔═╡ d9b2a2f6-5da5-11eb-137a-1b28fc1e8851
md"""
### 4. Property of Nature

The energy stored inside a charged capactitor is potential energy. That is, it only exists as a relation to some other state (a discharged capacitor, perhaps). An ideal capacitor can be modeled with a constant *capacitance* $$C = \frac{Q}{V}$$. Therefore the voltage at any single time point is $$V(t) = \frac{Q(t)}{C}$$. That is, the voltage is linearly related to the charge stored inside the capacitor. Since the energy stored is potential energy, by way of positive charge on one plate and negative charge on the other plate, that energy/charge "wants" to settle back down onto a balanced equilibrium. The size of that "want" is proportional to how much imbalance there is.

That means that the discharge starts out fast, but slows down as the amount of imbalance drops off.
"""

# ╔═╡ 83c2566e-5cbd-11eb-073f-1d4a3d5518b8
exponential_decay(A, b, x) = A / exp(b*x)

# ╔═╡ 444988e0-5cbf-11eb-2406-f169032118cf
@bind V_0 Slider(0.2:0.2:4, show_value=true, default=3.4)

# ╔═╡ 5925a4d6-5cbf-11eb-3eeb-53518a7955eb
@bind RC Slider(0.2:0.2:10, show_value=true, default=5)

# ╔═╡ 68c1c22c-5cbb-11eb-3eb0-330e89f3f39d
begin
	plot(x, exponential_decay.(V_0, 1/RC, x), ylabel="V (volt)", xlabel="t (second)", label="V₀ e^(-t/RC)")
	plot!([0], [V_0], seriestype = :scatter, label="V₀")
end

# ╔═╡ a3077b76-5cc4-11eb-040f-251e086b20e3
RC_circuit_url = "https://upload.wikimedia.org/wikipedia/commons/a/a4/Discharging_capacitor.svg"

# ╔═╡ 1a449718-5cc1-11eb-1946-f114a25dc73d
md"""
### 2. Physical example
An example of exponential decay is the voltage of a simple __resistor-capacitor__ circuit, where the capacitor starts out charged and once the circuit is closed, the capacitor discharges its energy through the resistor.

$(Resource(RC_circuit_url, :width => 150))

$$V(t) = \frac{V_0}{ e^{\frac{t}{RC}}}$$

* __V(t)__ - function of voltage (V) over time (t)
* __V₀__ - voltage of capacitor at t = 0
* __t__ - time (in seconds)
* __R__ - resistance of resistor (in Ω)
* __C__ - capacitance of capaciotr (in farads)
* __e__ - the base of the natural logarithm

RC is the __exponential time constant__ τ.

"""

# ╔═╡ Cell order:
# ╟─f4653ca6-5cba-11eb-0935-4d963ea430c8
# ╟─cd4fe1e2-5cbb-11eb-392d-37cd89200e04
# ╟─e0f2ceba-5cba-11eb-1f14-ad4ef1619c20
# ╠═e42deb9e-5cba-11eb-05bc-f99846852e79
# ╟─b9290aee-5cba-11eb-16e7-c92402c6f6b8
# ╟─cc76810a-5cba-11eb-3784-e9ccb5bc8ce2
# ╟─ccc56cde-5cba-11eb-0601-bf2f29d0639a
# ╠═ccf312c4-5cba-11eb-24dc-c53f2185596b
# ╠═282bfa7a-5cbb-11eb-3e20-818cbc5cadbb
# ╠═72d21bfc-5cd1-11eb-2a88-5b7e0e16a951
# ╟─327c1456-5cbb-11eb-2537-29ee89edf2be
# ╠═2aa87dd4-5cbb-11eb-3465-fbfff6973a43
# ╟─687a0c52-5cbb-11eb-3b69-d3f0ba61397d
# ╠═68c1c22c-5cbb-11eb-3eb0-330e89f3f39d
# ╟─1a449718-5cc1-11eb-1946-f114a25dc73d
# ╟─47a12e5c-5cc3-11eb-38c0-13546d4e6b2a
# ╟─d9b2a2f6-5da5-11eb-137a-1b28fc1e8851
# ╠═83c2566e-5cbd-11eb-073f-1d4a3d5518b8
# ╠═444988e0-5cbf-11eb-2406-f169032118cf
# ╠═5925a4d6-5cbf-11eb-3eeb-53518a7955eb
# ╟─a3077b76-5cc4-11eb-040f-251e086b20e3


text:
using Makie
using Bridge
using Bridge: increment
using StaticArrays
using Trajectories
using Colors
import Trajectories: Trajectory
using Random
Trajectory(X::SamplePath) = Trajectory(X.tt, X.yy)
RECORD = false
const ℝ = SVector
const parametrisation = :regular
include("../src/fitzHughNagumo.jl")
P = FitzhughDiffusion(0.1, 0.0, 1.5, 0.8, 0.3)
#P = FitzhughDiffusion(10.0, -8.0, 15.0, 0.0, 3.0)
Wnr = Wiener()
x0 = ℝ{2}(-0.5, -0.6)
L = @SMatrix [1. 0.]

dt = 5e-4
T = 30.0
tt = 0.0:dt:T

function simulateSegment(::S, tt, x0, P, W) where S
    WW = Bridge.samplepath(tt, zero(S))
    sample!(WW, Wnr)
    X = Trajectory(solve(Euler(), x0, WW, P))
    X, X.x[end]
end

X, _ = simulateSegment(0.0, tt, x0, P, Wnr)

k = 250
n = 2500
pts = rand(X.x, n)
xraw = [copy(pts) for i in 1:k]
x = [Node(xraw[i]) for i in 1:k]
col = [Node(RGBA{Float32}(0.0, 0.0, 0.0, 0.0)) for i in 1:k]
c = 1
ms = 0.02
i = 1;
R = 1.5
limits = FRect(-R, -R, 2R, 2R)
p = Scene(resolution=(800,800), limits=limits, backgroundcolor = RGB{Float32}(0.04, 0.11, 0.22))
for i in randperm(k)
    scatter!(p, x[i], color = col[i], markersize = ms, #=show_axis = true,limits=limits,=#  glowwidth = 0.005, glowcolor = :white)
end
#update_cam!(p, limits)
axis = p[Axis]
axis[:grid, :linewidth] =  (1, 1)
axis[:grid, :linecolor] = (RGBA{Float32}(0.5, 0.7, 1.0, 0.5),RGBA{Float32}(0.5, 0.7, 1.0, 0.5))
axis[:names][:textsize] = (0.0,0.0)
axis[:ticks, :textcolor] = (RGBA{Float32}(0.5, 0.7, 1.0, 0.8),RGBA{Float32}(0.5, 0.7, 1.0, 0.8))
r = range(-R, 2R, length=100)
lines!(p, [x for x in Point2f0.(r, r - r.^3) if x in limits], color = RGBA{Float32}(0.5, 0.7, 1.0, 0.8))
lines!(p, [x for x in Point2f0.(r, P.γ*r .+ P.β) if x in limits], color = RGBA{Float32}(0.5, 0.7, 1.0, 0.8))

display(p)

rebirth(α, R) = x -> (rand() > α  ? x : (2rand(typeof(x)) .- 1)*R)
function update!(t, x, y, dt, P, W, jump)
    for i in eachindex(x)
        y[i] = jump(x[i])
        y[i] = y[i] + b(t, y[i], P)*dt + σ(t, y[i], P)*rand(increment(dt, W))
    end
    y
end

update!(x, y, dt, P, W, jump = indentity) = update!(NaN, x, y, dt, P, W, jump)
sleep(1)
N = 4000
jump = rebirth(0.0001, R)
for i in 1:N
    global c
    cnew = mod1(c+1, k)
    update!(x[c][], x[cnew][], dt, P, Wnr, jump)
    c = cnew
    x[c][] = x[cnew][]

    for i in 0:k-1
        f = (i + 5*(i!=0))/(k+5)
        col[mod1(c-i, k)][] = RGBA{Float32}(0.5, 0.7, 1.0, (1-sqrt(f))/3)
    end
    sleep(1e-10)
end

if RECORD
record(p, "output/fitzhugh.mp4", 1:(N÷10)) do i
    global c
    cnew = mod1(c+1, k)
    update!(x[c][], x[cnew][], dt, P, Wnr, jump)
    c = cnew
    x[c][] = x[cnew][]

    for i in 0:k-1
        f = (i + 5*(i!=0))/(k+5)
        col[mod1(c-i, k)][] = RGBA{Float32}(0.5, 0.7, 1.0, (1-sqrt(f))/3)
    end
    yield()
end
end


text:
module MyPkg

PROJECT_ROOT = abspath(".")
Settings = []

import Dance

#=
    STEP 1
    If apart from `static_dir` as defined in `routes.jl` there are dirs to ignore adding to LOAD_PATH
        => add to `ignore_dirs`
    By default all other sub-dirs of project structure will be added
=#
ignore_dirs = [""]
if Dance.populate_settings(PROJECT_ROOT)
    global Settings
    Settings = Dance.Configuration.Settings
    for item in Dance.populate_load_path(PROJECT_ROOT; ignore_dirs=ignore_dirs)
        push!(LOAD_PATH, item)
    end
end

#=
    STEP 2
    Import modules here below, not above
    (STEP 1 populated `LOAD_PATH`)
=#
function main(args)
    if Dance.populate_router(PROJECT_ROOT)
        include(Settings[:routes_filename]*".jl")

        ## STEP 3: Add custom scripts here that need be run before launching Dance ##
        ##############################################

        start_server::Bool = true
        if length(args)>0
            if args[1]=="repl"
                start_server = false
            end
        end
        Dance.launch(start_server)
    end
end

end


text:
show_option(str::String) = string(replace(str, [' ', '\\'] => s -> "\\$s"))
show_option(bool::Bool) = ifelse(bool, 't', 'f')
show_option(num::Real) = num

# values containing spaces may not work correctly on PostgreSQL versions before 9.6
const CONNECTION_OPTION_DEFAULTS = Dict{String, String}(
    "DateStyle" => "ISO,YMD",
    "IntervalStyle" => "iso_8601",
    "TimeZone" => DEFAULT_CLIENT_TIME_ZONE[],
)

function _connection_parameter_dict(;
    client_encoding::String="UTF8",
    application_name::String="LibPQ.jl",
    connection_options::Dict{String, String}=Dict{String, String}(),
)
    keep_option((k, v)) = !(k == "TimeZone" && v == "")

    Dict{String, String}(
        "client_encoding" => client_encoding,
        "application_name" => application_name,
        "options" => join(
            imap(Iterators.filter(keep_option, connection_options)) do (k, v)
                "-c $k=$(show_option(v))"
            end,
            " ",
        ),
    )
end

const CONNECTION_PARAMETER_DEFAULTS = _connection_parameter_dict(
    connection_options=CONNECTION_OPTION_DEFAULTS
)

# https://www.postgresql.org/docs/10/libpq-connect.html#LIBPQ-PQCONNECTSTARTPARAMS
const CONNECTION_STATUS_MESSAGES = Dict{libpq_c.ConnStatusType, String}(
    libpq_c.CONNECTION_OK => "Connection is ready",
    libpq_c.CONNECTION_BAD => "Connection failed",
    libpq_c.CONNECTION_STARTED => "Waiting for connection to be made",
    libpq_c.CONNECTION_MADE => "Connection OK; waiting to send",
    libpq_c.CONNECTION_AWAITING_RESPONSE => "Waiting for a response from the server",
    libpq_c.CONNECTION_AUTH_OK => "Received authentication; waiting for backend start-up to finish",
    libpq_c.CONNECTION_SETENV => "Negotiating environment-driven parameter settings",
    libpq_c.CONNECTION_SSL_STARTUP => "Negotiating SSL encryption",
    libpq_c.CONNECTION_CHECK_WRITABLE => "Checking if connection is able to handle write transactions",
    libpq_c.CONNECTION_CONSUME => "Consuming any remaining response messages on connection",
    libpq_c.CONNECTION_GSS_STARTUP => "Negotiating GSSAPI",
)

"A connection to a PostgreSQL database."
mutable struct Connection
    "A pointer to a libpq PGconn object (C_NULL if closed)"
    conn::Ptr{libpq_c.PGconn}

    "libpq client encoding (string encoding of returned data)"
    encoding::String

    "Integer counter for generating connection-level unique identifiers"
    uid_counter::UInt

    "Connection-level type correspondence map"
    type_map::PQTypeMap

    "Connection-level conversion functions"
    func_map::PQConversions

    "True if the connection is closed and the PGconn object has been cleaned up"
    closed::Atomic{Bool}

    "Semaphore for thread-safety (not thread-safe until Julia 1.2)"
    semaphore::Semaphore

    "Current AsyncResult, if active"
    async_result  # ::Union{AsyncResult, Nothing}, would be a circular reference

    function Connection(
        conn::Ptr,
        closed=false;
        type_map::AbstractDict=PQTypeMap(),
        conversions::AbstractDict=PQConversions(),
    )
        return new(
            conn,
            "UTF8",
            0,
            PQTypeMap(type_map),
            PQConversions(conversions),
            Atomic{Bool}(closed),
            Semaphore(1),
            nothing,
        )
    end
end

Base.broadcastable(c::Connection) = Ref(c)

"""
    handle_new_connection(jl_conn::Connection; throw_error=true) -> Connection

Check status and handle errors for newly-created connections.
Also set the client encoding ([23.3. Character Set Support](https://www.postgresql.org/docs/10/multibyte.html))
to `jl_conn.encoding`.

If `throw_error` is `true`, an error will be thrown if the connection's status is
`CONNECTION_BAD` and the PGconn object will be cleaned up.
Otherwise, a warning will be shown and the user should call `close` or `reset!` on the
returned `Connection`.
"""
function handle_new_connection(jl_conn::Connection; throw_error::Bool=true)
    conn_status = status(jl_conn)
    if conn_status != libpq_c.CONNECTION_OK
        if conn_status == libpq_c.CONNECTION_BAD
            if jl_conn.conn == C_NULL
                err = Errors.JLConnectionError(
                    "libpq could not allocate memory for the connection struct"
                )
            else
                err = Errors.PQConnectionError(jl_conn)
            end
        else
            # all other states happen during the connection process
            err = Errors.JLConnectionError("Connection process timed out")
        end

        if throw_error
            close(jl_conn)
            error(LOGGER, err)
        else
            warn(LOGGER, err)
        end
    else
        debug(LOGGER, "Connection established: $(jl_conn.conn)")
        # if connection is successful, set client_encoding
        reset_encoding!(jl_conn)
    end

    # keep a reference to `closed` so it's not cleaned up before the connection is
    let closed = jl_conn.closed, conn_ptr = jl_conn.conn
        finalizer(jl_conn) do _
            # finalizers can't task swtich, but they can schedule tasks
            @async begin
                if !atomic_cas!(closed, false, true)
                    debug(LOGGER, "Closing connection $(conn_ptr) in finalizer")
                    # we don't need to acquire a lock, because if anyone else is holding a
                    # lock on the connection (using lock(::Connection)) then it won't be
                    # cleaned up by the gc yet
                    libpq_c.PQfinish(conn_ptr)
                end
            end

            return
        end
    end

    return jl_conn
end

# Perform the connection loop as specified in
# https://www.postgresql.org/docs/10/libpq-connect.html#LIBPQ-PQCONNECTSTARTPARAMS
# NOTE: this uses the non-blocking interface but this function does block!
# If the connection times out, handle_new_connection will log/throw the error
function _connect_nonblocking(keywords, values, expand_dbname; timeout::Real=0)
    timer = Timer(timeout)
    conn = libpq_c.PQconnectStartParams(keywords, values, expand_dbname)
    return _connect_poll(conn, timer, timeout)
end

function _connect_poll(conn::Ptr{libpq_c.PGconn}, timer::Timer, timeout::Real)
    if timeout == 0
        timeout = -1  # disables timeout for poll_fd
    end

    c_state = libpq_c.PQstatus(conn)

    # This is also true when `conn == C_NULL`
    c_state == libpq_c.CONNECTION_BAD && return conn

    # Log initial state
    msg = get(CONNECTION_STATUS_MESSAGES, c_state, "Connecting")
    debug(LOGGER, "Connection $conn: $msg")

    p_state = libpq_c.PGRES_POLLING_WRITING
    while p_state != libpq_c.PGRES_POLLING_FAILED && p_state != libpq_c.PGRES_POLLING_OK
        # PostgreSQL docs say we can't assume that the socket will remain the same while
        # connecting
        try
            wait_write = p_state == libpq_c.PGRES_POLLING_WRITING
            wait_read = p_state == libpq_c.PGRES_POLLING_READING

            if wait_write || wait_read
                event = poll_fd(
                    socket(conn), timeout; writable=wait_write, readable=wait_read
                )

                if event.timedout
                    debug(LOGGER, "Connection $conn: timed out while waiting for socket")
                end
            end
        catch err
            # catch the scenario when the socket is closed while we're waiting for it
            # the loop may repeat in order to get to the libpq_c.PGRES_POLLING_FAILED state
            if err isa Base.IOError && err.code == -9  # EBADF
                debug(() -> sprint(showerror, err), LOGGER)
                # handle_new_connection should see CONNECTION_BAD and report:
                # "could not receive data from server: Bad file descriptor"
            else
                rethrow(err)
            end
        end

        p_state = libpq_c.PQconnectPoll(conn)

        # Only print connection status when it changes
        new_c_state = libpq_c.PQstatus(conn)
        if new_c_state != c_state
            c_state = new_c_state
            msg = get(CONNECTION_STATUS_MESSAGES, c_state, "Connecting")
            debug(LOGGER, "Connection $conn: $msg")
        end

        # connection timed out
        timeout > 0 && !isopen(timer) && break
    end

    return conn
end

"""
    Connection(
        str::AbstractString;
        throw_error::Bool=true,
        connect_timeout::Real=0,
        type_map::AbstractDict=LibPQ.PQTypeMap(),
        conversions::AbstractDict=LibPQ.PQConversions(),
        options::Dict{String, String}=LibPQ.CONNECTION_OPTION_DEFAULTS,
    ) -> Connection

Create a `Connection` from a connection string as specified in the PostgreSQL
documentation ([33.1.1. Connection Strings](https://www.postgresql.org/docs/10/libpq-connect.html#LIBPQ-CONNSTRING)).

If `connect_timeout > 0` and that amount of seconds elapses without succesfully connecting,
give up and log throw an error (depending on `throw_error`).

For information on the `type_map` and `conversions` arguments, see [Type Conversions](@ref typeconv).

See [`handle_new_connection`](@ref) for information on the `throw_error` argument.

## PostgreSQL Connection Options

For a list of available options for the `options` argument, see [Server Configuration](https://www.postgresql.org/docs/10/runtime-config.html).

The default connection options are:

$(join(map((k, v) for (k, v) in CONNECTION_OPTION_DEFAULTS if k != "TimeZone") do (k, v)
    "* `$(repr(k)) => $(repr(v))`"
end, "\n"))
* `"TimeZone" => $(repr(DEFAULT_CLIENT_TIME_ZONE[]))`, or the `PGTZ` environment variable.
  Will use the server time zone if option is set to `""`.

Note that these default connection options may be different than the defaults used by the
server, which are the defaults used by `psql` and other LibPQ clients.
To use the defaults provided by the server, use
`options = Dict{String, String}("TimeZone" => "")`.
"""
function Connection(
    str::AbstractString;
    throw_error::Bool=true,
    connect_timeout::Real=0,
    options::Dict{String, String}=CONNECTION_OPTION_DEFAULTS,
    kwargs...
)
    if options === CONNECTION_OPTION_DEFAULTS
        # avoid allocating another dict in the common case
        connection_parameters = CONNECTION_PARAMETER_DEFAULTS
    else
        connection_parameters = _connection_parameter_dict(connection_options=options)
    end

    ci_array = conninfo(str)

    keywords = String[]
    values = String[]

    for (k, v) in connection_parameters
        push!(keywords, k)
        push!(values, v)
    end

    for ci in ci_array
        if !ismissing(ci.val)
            push!(keywords, ci.keyword)
            push!(values, ci.val)
        end
    end

    # Make the connection
    debug(LOGGER, "Connecting to $str")
    jl_conn = Connection(
        _connect_nonblocking(keywords, values, false; timeout=connect_timeout); kwargs...
    )

    # If password needed and not entered, prompt the user
    if libpq_c.PQconnectionNeedsPassword(jl_conn.conn) == 1
        push!(keywords, "password")
        user = unsafe_string(libpq_c.PQuser(jl_conn.conn))
        # close this connection; will open another one below with the user-provided password
        close(jl_conn)
        prompt = "Enter password for PostgreSQL user $user:"
        pass = Base.getpass(prompt)
        push!(values, read(pass, String))
        Base.shred!(pass)
        return handle_new_connection(
            Connection(
                _connect_nonblocking(keywords, values, false; timeout=connect_timeout);
                kwargs...
            );
            throw_error=throw_error,
        )
    else
        return handle_new_connection(
            jl_conn;
            throw_error=throw_error,
        )
    end
end

# AbstractLock primitives:
# https://github.com/JuliaLang/julia/blob/master/base/condition.jl#L18
Base.lock(conn::Connection) = acquire(conn.semaphore)
Base.unlock(conn::Connection) = release(conn.semaphore)
Base.islocked(conn::Connection) = conn.semaphore.curr_cnt >= conn.semaphore.sem_size

# AbstractLock convention:
function Base.lock(f, conn::Connection)
    lock(conn)
    try
        return f()
    finally
        unlock(conn)
    end
end

"""
    Connection(f, args...; kwargs...) -> Connection

A utility method to support `do` syntax.
Constructs the `Connection`, calls `f` on it, then closes it.
"""
function Connection(f::Base.Callable, args...; kwargs...)
    jl_conn = Connection(args...; kwargs...)

    try
        return f(jl_conn)
    finally
        close(jl_conn)
    end
end

"""
    server_version(jl_conn::Connection) -> VersionNumber

Get the PostgreSQL version of the server.

See [33.2. Connection Status Functions](https://www.postgresql.org/docs/10/libpq-status.html#LIBPQ-PQSERVERVERSION)
for information on the integer returned by `PQserverVersion` that is parsed by this
function.

See [`@pqv_str`](@ref) for information on how this packages represents PostgreSQL version
numbers.
"""
function server_version(jl_conn::Connection)
    version_int = libpq_c.PQserverVersion(jl_conn.conn)

    first_major = version_int ÷ 10000
    version = if first_major >= 10
        # new style (only major-minor)
        return VersionNumber(first_major, 0, version_int % 100)
    else
        # old style (major-major-minor)
        return VersionNumber(first_major, (version_int % 10000) ÷ 100, version_int % 100)
    end

    return version
end

"""
    @pqv_str -> VersionNumber

Parse a PostgreSQL version.

!!! note

    As of version 10.0, PostgreSQL moved from a three-part version number (where the first
    two parts represent the major version and the third represents the minor version) to a
    two-part major-minor version number.
    In LibPQ.jl, we represent this using the first two `VersionNumber` components as the
    major version and the third as the minor version.

    ## Examples

    ```jldoctest
    julia> using LibPQ: @pqv_str

    julia> pqv"10.1" == v"10.0.1"
    true

    julia> pqv"9.2.5" == v"9.2.5"
    true
    ```
"""
macro pqv_str(str)
    _pqv_str(str)
end

function _pqv_str(str)
    splitted = map(x -> parse(Int, x)::Int, split(str, '.'))

    if isempty(splitted)
        throw(ArgumentError("PostgreSQL version must contain at least one integer"))
    end

    version = if splitted[1] >= 10
        if length(splitted) == 1
            VersionNumber(splitted[1])
        elseif length(splitted) == 2
            VersionNumber(splitted[1], 0, splitted[2])
        else
            throw(ArgumentError(
                "PostgreSQL versions only have two components starting at version 10"
            ))
        end
    else
        if length(splitted) > 3
            throw(ArgumentError(
                "PostgreSQL versions cannot have more than three components"
            ))
        end

        VersionNumber(splitted...)
    end

    return version
end

"""
    encoding(jl_conn::Connection) -> String

Return the client encoding name for the current connection (see
[Table 23.1. PostgreSQL Character Sets](https://www.postgresql.org/docs/10/multibyte.html#CHARSET-TABLE)
for possible values).

Currently all Julia connections are set to use `UTF8` as this makes conversion to and from
`String` straightforward.

See also: [`set_encoding!`](@ref), [`reset_encoding!`](@ref)
"""
function encoding(jl_conn::Connection)
    encoding_id::Cint = libpq_c.PQclientEncoding(jl_conn.conn)

    if encoding_id == -1
        error(LOGGER, Errors.JLConnectionError(
            "libpq could not retrieve the connection's client encoding. " *
            "Something is wrong with the connection."
        ))
    end

    return unsafe_string(libpq_c.pg_encoding_to_char(encoding_id))
end

"""
    set_encoding!(jl_conn::Connection, encoding::String)

Set the client encoding for the current connection (see
[Table 23.1. PostgreSQL Character Sets](https://www.postgresql.org/docs/10/multibyte.html#CHARSET-TABLE)
for possible values).

Currently all Julia connections are set to use `UTF8` as this makes conversion to and from
`String` straighforward.
Other encodings are not explicitly handled by this package and will probably be very buggy.

See also: [`encoding`](@ref), [`reset_encoding!`](@ref)
"""
function set_encoding!(jl_conn::Connection, encoding::String)
    lock(jl_conn) do
        status = libpq_c.PQsetClientEncoding(jl_conn.conn, encoding)

        if status == -1
            error(LOGGER, Errors.JLConnectionError(
                "libpq could not set the connection's client encoding to $encoding"
            ))
        else
            jl_conn.encoding = encoding
        end
    end

    return nothing
end

"""
    reset_encoding!(jl_conn::Connection, encoding::String)

Reset the client encoding for the current connection to `jl_conn.encoding`.

See also: [`encoding`](@ref), [`set_encoding!`](@ref)
"""
function reset_encoding!(jl_conn::Connection)
    if encoding(jl_conn) != jl_conn.encoding
        set_encoding!(jl_conn, jl_conn.encoding)
    end
end

"""
    unique_id(jl_conn::Connection, prefix::AbstractString="") -> String

Return a valid PostgreSQL identifier that is unique for the current connection.
This is mostly used to create names for prepared statements.
"""
function unique_id(jl_conn::Connection, prefix::AbstractString="")
    lock(jl_conn) do
        id_number = jl_conn.uid_counter
        jl_conn.uid_counter += 1

        return "__libpq_$(prefix)_$(id_number)__"
    end
end

"""
    status(jl_conn::Connection) -> libpq_c.ConnStatusType

Return the status of the PostgreSQL database connection according to libpq.
Only `CONNECTION_OK` and `CONNECTION_BAD` are valid for blocking connections, and only
blocking connections are supported right now.

See also: [`error_message`](@ref)
"""
status(jl_conn::Connection) = libpq_c.PQstatus(jl_conn.conn)

"""
    transaction_status(jl_conn::Connection) -> libpq_c.PGTransactionStatusType

Return the PostgreSQL database server's current in-transaction status for the connection.
See [](https://www.postgresql.org/docs/10/libpq-status.html#LIBPQ-PQTRANSACTIONSTATUS)
for information on the meaning of the possible return values.
"""
transaction_status(jl_conn::Connection) = libpq_c.PQtransactionStatus(jl_conn.conn)

"""
    close(jl_conn::Connection)

Close the PostgreSQL database connection and free the memory used by the `PGconn` object.
This function calls [`PQfinish`](https://www.postgresql.org/docs/10/libpq-connect.html#LIBPQ-PQFINISH),
but only if `jl_conn.closed` is `false`, to avoid a double-free.
"""
function Base.close(jl_conn::Connection)
    if !atomic_cas!(jl_conn.closed, false, true)
        debug(LOGGER, "Closing connection $(jl_conn.conn)")
        async_result = jl_conn.async_result
        async_result === nothing || cancel(async_result)
        lock(jl_conn) do
            libpq_c.PQfinish(jl_conn.conn)
            jl_conn.conn = C_NULL
        end
    else
        debug(LOGGER, "Tried to close a closed connection; doing nothing")
    end
    return nothing
end

"""
    isopen(jl_conn::Connection) -> Bool

Check whether a connection is open.
"""
Base.isopen(jl_conn::Connection) = !jl_conn.closed[]

"""
    reset!(jl_conn::Connection; throw_error=true)

Reset the communication to the PostgreSQL server.
The `PGconn` object will be recreated using identical connection parameters.

See [`handle_new_connection`](@ref) for information on the `throw_error` argument.

!!! note

    This function can be called on a connection with status `CONNECTION_BAD`, for example,
    but cannot be called on a connection that has been closed.
"""
function reset!(jl_conn::Connection; throw_error::Bool=true)
    if !atomic_cas!(jl_conn.closed, false, true)
        debug(LOGGER, "Closing connection $(jl_conn.conn)")
        async_result = jl_conn.async_result
        async_result === nothing || cancel(async_result)
        lock(jl_conn) do
            jl_conn.closed[] = false
            debug(LOGGER, "Resetting connection $(jl_conn.conn)")
            libpq_c.PQreset(jl_conn.conn)
        end

        handle_new_connection(jl_conn; throw_error=throw_error)
    else
        error(LOGGER, Errors.JLConnectionError(
            "Cannot reset a connection that has been closed"
        ))
    end

    return nothing
end

"""
    error_message(jl_conn::Connection) -> String

Return the error message most recently generated by an operation on the connection.
Includes a trailing newline.
"""
error_message(jl_conn::Connection) = unsafe_string(libpq_c.PQerrorMessage(jl_conn.conn))

"""
Indicator for how to display a PostgreSQL connection option (`PQconninfoOption`).

Possible values are:

* `Normal` (libpq: ""): display as is
* `Password` (libpq: "*"): hide the value of this field
* `Debug` (libpq: "D"): don't show by default
"""
@enum ConninfoDisplay Normal Password Debug

"""
    parse(::Type{ConninfoDisplay}, str::AbstractString) -> ConninfoDisplay

Parse a `ConninfoDisplay` from a string. See [`ConninfoDisplay`](@ref).
"""
function Base.parse(::Type{ConninfoDisplay}, str::AbstractString)::ConninfoDisplay
    if length(str) < 1
        Normal
    elseif first(str) == '*'
        Password
    elseif first(str) == 'D'
        Debug
    else
        error(LOGGER, Errors.JLConnectionError(
            "Unexpected dispchar '$str' in PQconninfoOption"
        ))
    end
end

"A Julia representation of a PostgreSQL connection option (`PQconninfoOption`)."
struct ConnectionOption
    "The name of the option"
    keyword::String

    "The name of the fallback environment variable for this option"
    envvar::Union{String, Missing}

    "The PostgreSQL compiled-in default for this option"
    compiled::Union{String, Missing}

    "The value of the option if set"
    val::Union{String, Missing}

    "The label of the option for display"
    label::String

    "Indicator for how to display the option (see [`ConninfoDisplay`](@ref))"
    disptype::ConninfoDisplay

    "The size of field to provide for entry of the option value (not used here)"
    dispsize::Int
end

"""
    ConnectionOption(pq_opt::libpq_c.PQconninfoOption) -> ConnectionOption

Construct a `ConnectionOption` from a `libpg_c.PQconninfoOption`.
"""
function ConnectionOption(pq_opt::libpq_c.PQconninfoOption)
    return ConnectionOption(
        unsafe_string(pq_opt.keyword),
        unsafe_string_or_null(pq_opt.envvar),
        unsafe_string_or_null(pq_opt.compiled),
        unsafe_string_or_null(pq_opt.val),
        unsafe_string(pq_opt.label),
        parse(ConninfoDisplay, unsafe_string(pq_opt.dispchar)),
        pq_opt.dispsize,
    )
end

"""
    conninfo(jl_conn::Connection) -> Vector{ConnectionOption}

Get all connection options for a connection.
"""
function conninfo(jl_conn::Connection)
    ci_ptr = libpq_c.PQconninfo(jl_conn.conn)

    if ci_ptr == C_NULL
        if !isopen(jl_conn)
            error(LOGGER, Errors.JLConnectionError(
                "Cannot get connection info as the connection is closed."
            ))
        else
            error(LOGGER, Errors.JLConnectionError(
                "libpq could not allocate memory for connection info"
            ))
        end
    end

    ci_array = conninfo(ci_ptr)
    libpq_c.PQconninfoFree(ci_ptr)
    return ci_array
end

function conninfo(ci_ptr::Ptr{libpq_c.PQconninfoOption})
    ci_array = Vector{ConnectionOption}()

    # ci_ptr is an array of PQconninfoOptions terminated by a PQconninfoOption with the
    # keyword field set to C_NULL
    ci_opt_idx = 1
    ci_opt = unsafe_load(ci_ptr, ci_opt_idx)
    while ci_opt.keyword != C_NULL
        push!(ci_array, ConnectionOption(ci_opt))

        ci_opt_idx += 1
        ci_opt = unsafe_load(ci_ptr, ci_opt_idx)
    end

    return ci_array
end

"""
    conninfo(str::AbstractString) -> Vector{ConnectionOption}

Parse connection options from a connection string (either a URI or key-value pairs).
"""
function conninfo(str::AbstractString)
    err_ref = Ref{Ptr{UInt8}}(C_NULL)
    ci_ptr = libpq_c.PQconninfoParse(str, err_ref)

    if ci_ptr == C_NULL && err_ref[] == C_NULL
        error(LOGGER, JLConnectionError(
            "libpq could not allocate memory for connection info"
        ))
    end

    if err_ref[] != C_NULL
        err_msg = unsafe_string(err_ref[])
        libpq_c.PQfreemem(err_ref[])
        error(LOGGER, Errors.ConninfoParseError(err_msg))
    end

    ci_array = conninfo(ci_ptr)
    libpq_c.PQconninfoFree(ci_ptr)
    return ci_array
end

"""
    show(io::IO, jl_conn::Connection)

Display a [`Connection`](@ref) by showing the connection status and each connection option.
"""
function Base.show(io::IO, jl_conn::Connection)
    if !isopen(jl_conn)
        print(io, "PostgreSQL connection (closed)")
        return nothing
    end

    print(io, "PostgreSQL connection ($(status(jl_conn))) with parameters:")
    for ci_opt in conninfo(jl_conn)
        if !ismissing(ci_opt.val) && ci_opt.disptype != Debug
            print(io, "\n  ", ci_opt.keyword, " = ")

            if ci_opt.disptype == Password
                print(io, "*" ^ ci_opt.dispsize)
            else
                print(io, ci_opt.val)
            end
        end
    end
end

function socket(conn::Ptr{libpq_c.PGconn})
    socket_int = libpq_c.PQsocket(conn)
    @static if Sys.iswindows()
        return Base.WindowsRawSocket(Ptr{Cvoid}(Int(socket_int)))
    else
        return RawFD(socket_int)
    end
end

socket(jl_conn::Connection) = socket(jl_conn.conn)


text:
module MyGenieExample

using Logging, LoggingExtras

function main()
  Base.eval(Main, :(const UserApp = MyGenieExample))

  include(joinpath("..", "genie.jl"))

  Base.eval(Main, :(const Genie = MyGenieExample.Genie))
  Base.eval(Main, :(using Genie))
end; main()

end


text:
#module Compologicircuits

using Catlab.WiringDiagrams
using Catlab.CategoricalAlgebra
using Catlab.Theories
using Catlab.Syntax
using Catlab.GAT
using Catlab.Present
using Catlab.Programs
using Catlab.Graphics
import Catlab.Graphics: Graphviz
import Catlab.Theories: dom, codom, id, compose, otimes, braid, munit, mcopy, delete, pair, proj1, proj2, ⊗, ⋅, σ

# Helper function to show graphviz diagrams
show_diagram(d::WiringDiagram) = to_graphviz(d,
    orientation=LeftToRight,
    labels=false, label_attr=:xlabel,
    node_attrs=Graphviz.Attributes(
        :fontname => "Courier",
    ),
    edge_attrs=Graphviz.Attributes(
        :fontname => "Courier",
    )
)

# The skeleton of n-dimensional bool spaces which are the input/output types of logic circuits
struct CircuitDom
    n::Int
end

# Implement a logic circuit as a function from Vector{Bool} to Vector{Bool}
struct Circuit
    dom::CircuitDom
    codom::CircuitDom
    impl::Function # Vector{Bool} of size dom -> Vector{Bool} of size codom
end

# Convenience constructor
Circuit(nins::Int, nouts::Int, impl) = Circuit(CircuitDom(nins), CircuitDom(nouts), impl)

# Apply a circuit to the specified input vector
apply(circuit::Circuit, input::Vector{Bool})::Vector{Bool} = circuit.impl(input)

# Basic logic gate implementations
iNOT = Circuit(1, 1, x->map(b->!b, x))

iAND = Circuit(2, 1, x->[x[1] && x[2]])

iOR = Circuit(2, 1, x->[x[1] || x[2]])

#iδ = Circuit(1, 2, x->[x[1], x[1]])

# Make circuits compositional by implementing them as an SMC instance
@instance CartesianCategory{CircuitDom, Circuit} begin
    id(A::CircuitDom) = Circuit(A,A, x->x)
    dom(f::Circuit) = f.dom
    codom(f::Circuit) = f.codom

    # Circuit composition is just function composition
    compose(f::Circuit, g::Circuit) = begin
        @assert(f.codom == g.dom)
        return Circuit(f.dom, g.codom, x->g.impl(f.impl(x)))
    end

    otimes(A::CircuitDom, B::CircuitDom) = CircuitDom(A.n + B.n)
    # Monoidal product of circuits runs both circuits (TODO: run them in parallel to improve performance) and concatenates the results
    otimes(f::Circuit, g::Circuit) = begin
        impl = x -> vcat(f.impl(x[1:f.dom.n]), g.impl(x[f.dom.n + 1:end]))
        return Circuit(f.dom.n + g.dom.n, f.codom.n + g.codom.n, impl)
    end

    # A symmetric braiding just swaps the A and B parts of the input vector
    braid(A::CircuitDom, B::CircuitDom) = begin
        impl = x -> vcat(x[A.n+1:end], x[1:A.n])
        n = A.n + B.n
        Circuit(n, n, impl)
    end

    # Monoidal unit is a 0-dimensional bool space (i.e. a point or empty list)
    munit(::Type{CircuitDom}) = CircuitDom(0)

    # Stuff for Cartesian Category
    mcopy(A::CircuitDom) = Circuit(A.n, A.n+A.n, x->vcat(x,x))
    delete(A::CircuitDom) = Circuit(A.n, 0, x->Bool[])

    pair(f::Circuit, g::Circuit) = begin
        @assert(f.dom == g.dom)
        return Circuit(f.dom.n, f.codom.n+g.codom.n, x->vcat(f.impl(x), g.impl(x)))
    end
    proj1(A::CircuitDom, B::CircuitDom) = Circuit(A.n+B.n, A.n, x->x[1:A.n])
    proj2(A::CircuitDom, B::CircuitDom) = Circuit(A.n+B.n, B.n, x->x[A.n+1:end])
end

# Syntactic representation of logic circuits

@present Circuits(FreeCartesianCategory) begin
    B::Ob
    NOT::Hom(B,B)
    AND::Hom(B⊗B,B)
    OR::Hom(B⊗B,B)
    XOR::Hom(B⊗B,B)
    FA::Hom(B⊗B⊗B,B⊗B)
end

# Functor from circuit diagrams to circuit implementations
gens = Dict(
    Circuits[:B]=>CircuitDom(1),
    Circuits[:NOT]=>iNOT,
    Circuits[:AND]=>iAND,
    Circuits[:OR]=>iOR
)
Impl(expr) = functor((CircuitDom, Circuit), expr, generators=gens)

# Make some circuits

XOR = @program Circuits (x::B, y::B) begin
    xnandy = NOT(AND(x,y))
    xory = OR(x,y)
    return AND(xnandy,xory)
end

XOR_expr = to_hom_expr(FreeCartesianCategory, XOR)

iXOR = Impl(XOR_expr)

# Print XOR truth table

print_xor() = for i in Iterators.product([false, true], [false, true])
    input = collect(i)
    output = apply(iXOR, input)
    println("XOR on $input = $output")
end

full_adder = @program Circuits (x::B, y::B, cin::B) begin
    z = XOR(x,y)
    s = XOR(z,cin)
    cout = OR(AND(z,cin),AND(x,y))
    return (s, cout)
end

AND = to_wiring_diagram(Circuits[:AND])
NOT = to_wiring_diagram(Circuits[:NOT])
OR = to_wiring_diagram(Circuits[:OR])
total_full_adder = ocompose(full_adder, [XOR, XOR, AND, AND, OR])
iFA = Impl(to_hom_expr(FreeCartesianCategory, total_full_adder))

# Print Full Adder truth table

print_fa() = for i in Iterators.product([false, true], [false, true], [false, true])
    input = collect(i)
    output = apply(iFA, input)
    println("FA on $input = $output")
end

four_bit_adder = @program Circuits (cin::B, x1::B, x2::B, x3::B, x4::B, y1::B, y2::B, y3::B, y4::B) begin
    s1, cout1 = FA(x4,y4,cin)
    s2, cout2 = FA(x3,y3,cout1)
    s3, cout3 = FA(x2,y2,cout2)
    s4, cout = FA(x1,y1,cout3)
    return (cout, s4, s3, s2, s1)
end

total_four_bit_adder = ocompose(four_bit_adder, [total_full_adder, total_full_adder, total_full_adder, total_full_adder])
tfba_expr = to_hom_expr(FreeCartesianCategory, total_four_bit_adder)
i4bitadder = Impl(tfba_expr)

sum = apply(i4bitadder,
    Bool[0,      #carry in
        0,1,1,1, #x
        0,1,1,0] #y
)

### Perform sub-circuit matching.
# The theory of directed wiring diagrams without outer ports.
@present TheoryNotOpenWiringDiagram(FreeSchema) begin
  Box::Ob
  (InPort, OutPort)::Ob
  Wire::Ob
  #(InWire, OutWire)::Ob
  PortValue::AttrType
  WireValue::AttrType
  BoxValue::AttrType
  BoxType::AttrType

  # To which in/out port is a wire attached?
  src::Hom(Wire, OutPort)
  tgt::Hom(Wire, InPort)

  in_port_type::Attr(InPort, PortValue)
  out_port_type::Attr(OutPort, PortValue)

  value::Attr(Box, BoxValue)
  box_type::Attr(Box, BoxType)
  wire_value::Attr(Wire, WireValue)
  #in_wire_value::Attr(InWire, WireValue)
  #out_wire_value::Attr(OutWire, WireValue)

  #in_tgt::Hom(InWire, InPort)
  #out_src::Hom(OutWire, OutPort)

  # To which box is an in/out port attached?
  in_port_box::Hom(InPort, Box)
  out_port_box::Hom(OutPort, Box)
end
F = @finfunctor TheoryNotOpenWiringDiagram DirectedWiringDiagrams.TheoryAttributedWiringDiagram begin
  Box => Box
  InPort => InPort
  OutPort => OutPort
  Wire => Wire
  #InWire => InWire
  #OutWire => OutWire

  PortValue => PortValue
  WireValue => WireValue
  BoxValue => BoxValue
  BoxType => BoxType

  src => src
  tgt => tgt

  in_port_type => in_port_type
  out_port_type => out_port_type

  value => value
  box_type => box_type
  wire_value => wire_value
  #in_wire_value => in_wire_value
  #out_wire_value => out_wire_value

  in_tgt => in_tgt
  out_src => out_src

  in_port_box => in_port_box
  out_port_box => out_port_box
end

@acset_type NOWDACSet(TheoryNotOpenWiringDiagram)

deltaF = DeltaMigration(F, NOWDACSet{Any, Any, Any, DataType})

cAND = deltaF(AND.diagram)
cOR = deltaF(OR.diagram)
cNOT = deltaF(NOT.diagram)
cXOR = deltaF(XOR.diagram)

## Pick out AND's in AND.
println("There are ", length(homomorphisms(cAND, cAND, monic=true)), " monic homomorphisms from AND to AND.")

## Pick out OR's in AND.
println("There are ", length(homomorphisms(cOR, cAND, monic=true)), " monic homomorphisms from OR to AND.")

# Pick out AND's in XOR.
println("There are ", length(homomorphisms(cAND, cXOR, monic=true)), " monic homomorphisms from AND to XOR.")

# Pick out circuits in total_four_bit_adder that can be replaced by FA's.
println("There are ", length(homomorphisms(deltaF(total_full_adder.diagram), deltaF(total_four_bit_adder.diagram), monic=true)), " monic homomorphisms from total_full_adder to total_four_bit_adder.")

#end


text:
const OneToInfCumsum = InfiniteArrays.RangeCumsum{Int,OneToInf{Int}}
const OneToCumsum = InfiniteArrays.RangeCumsum{Int,OneTo{Int}}

BlockArrays.sortedunion(::AbstractVector{<:PosInfinity}, ::AbstractVector{<:PosInfinity}) = [∞]
function BlockArrays.sortedunion(::AbstractVector{<:PosInfinity}, b)
    @assert isinf(length(b))
    b
end

function BlockArrays.sortedunion(b, ::AbstractVector{<:PosInfinity})
    @assert isinf(length(b))
    b
end
BlockArrays.sortedunion(a::OneToInfCumsum, ::OneToInfCumsum) = a
BlockArrays.sortedunion(a::OneToCumsum, ::OneToCumsum) = a
function BlockArrays.sortedunion(a::RangeCumsum{<:Any,<:AbstractRange}, b::RangeCumsum{<:Any,<:AbstractRange})
    @assert a == b
    a
end

function BlockArrays.sortedunion(a::Vcat{Int,1,<:Tuple{Union{Int,AbstractVector{Int}},<:AbstractRange}},
                                 b::Vcat{Int,1,<:Tuple{Union{Int,AbstractVector{Int}},<:AbstractRange}})
    @assert a == b # TODO: generailse? Not sure how to do so in a type stable fashion
    a
end

sizes_from_blocks(A::AbstractVector, ::Tuple{OneToInf{Int}}) = (map(length,A),)
length(::BlockedUnitRange{<:InfRanges}) = ℵ₀

const OneToInfBlocks = BlockedUnitRange{OneToInfCumsum}
const OneToBlocks = BlockedUnitRange{OneToCumsum}

axes(a::OneToInfBlocks) = (a,)
axes(a::OneToBlocks) = (a,)

LazyBandedMatrices.unitblocks(a::OneToInf) = blockedrange(Ones{Int}(length(a)))

BlockArrays.dimlength(start, ::Infinity) = ℵ₀

function copy(bc::Broadcasted{<:BroadcastStyle,<:Any,typeof(*),<:Tuple{Ones{T,1,Tuple{OneToInfBlocks}},AbstractArray{V,N}}}) where {N,T,V}
    a,b = bc.args
    @assert bc.axes == axes(b)
    convert(AbstractArray{promote_type(T,V),N}, b)
end

function copy(bc::Broadcasted{<:BroadcastStyle,<:Any,typeof(*),<:Tuple{AbstractArray{T,N},Ones{V,1,Tuple{OneToInfBlocks}}}}) where {N,T,V}
    a,b = bc.args
    @assert bc.axes == axes(a)
    convert(AbstractArray{promote_type(T,V),N}, a)
end

_block_interlace_axes(::Int, ax::Tuple{BlockedUnitRange{OneToInf{Int}}}...) = (blockedrange(Fill(length(ax), ∞)),)

_block_interlace_axes(nbc::Int, ax::NTuple{2,BlockedUnitRange{OneToInf{Int}}}...) =
    (blockedrange(Fill(length(ax) ÷ nbc, ∞)),blockedrange(Fill(mod1(length(ax),nbc), ∞)))

include("infblocktridiagonal.jl")

#######
# block broadcasted
######

BroadcastStyle(::Type{<:SubArray{T,N,Arr,<:NTuple{N,BlockSlice{BlockRange{1,Tuple{II}}}},false}}) where {T,N,Arr<:BlockArray,II<:InfRanges} =
    LazyArrayStyle{N}()

# TODO: generalise following
BroadcastStyle(::Type{<:BlockArray{T,N,<:AbstractArray{<:AbstractArray{T,N},N},<:NTuple{N,BlockedUnitRange{<:InfRanges}}}}) where {T,N} = LazyArrayStyle{N}()
BroadcastStyle(::Type{<:PseudoBlockArray{T,N,<:AbstractArray{T,N},<:NTuple{N,BlockedUnitRange{<:InfRanges}}}}) where {T,N} = LazyArrayStyle{N}()
BroadcastStyle(::Type{<:BlockArray{T,N,<:AbstractArray{<:AbstractArray{T,N},N},<:NTuple{N,BlockedUnitRange{<:RangeCumsum{Int,<:InfRanges}}}}}) where {T,N} = LazyArrayStyle{N}()
BroadcastStyle(::Type{<:PseudoBlockArray{T,N,<:AbstractArray{T,N},<:NTuple{N,BlockedUnitRange{<:RangeCumsum{Int,<:InfRanges}}}}}) where {T,N} = LazyArrayStyle{N}()

###
# KronTrav
###

_krontrav_axes(A::NTuple{N,OneToInf{Int}}, B::NTuple{N,OneToInf{Int}}) where N =
     @. blockedrange(oneto(length(A)))


text:
using HTTP

url = "https://raw.githubusercontent.com/jbrownlee/Datasets/master/shampoo.csv"

HTTP.get()
# load dataset
def parser(x):
	return datetime.strptime('190'+x, '%Y-%m')

series = read_csv(
    'shampoo-sales.csv',
    header=0, parse_dates=[0], index_col=0, squeeze=True, date_parser=parser)

# split data into train and test
X = series.values
train, test = X[0:-12], X[-12:]

# walk-forward validation
history = [x for x in train]
predictions = list()
for i in range(len(test)):
	# make prediction
	predictions.append(history[-1])
	# observation
	history.append(test[i])
# report performance
rmse = sqrt(mean_squared_error(test, predictions))
print('RMSE: %.3f' % rmse)
# line plot of observed vs predicted
pyplot.plot(test)
pyplot.plot(predictions)
pyplot.show()


text:
mutable struct BDM3DBasis{T,M,P} <: Space{T}
    geo::M
    fns::Vector{Vector{Shape{T}}}
    pos::Vector{P}
end

refspace(s::BDM3DBasis{T}) where {T} = BDM3DRefSpace{T}()

function brezzidouglasmarini3d(mesh, faces)
    T = coordtype(mesh)
    P = vertextype(mesh)
    num_faces = numcells(faces)

    C = connectivity(faces, mesh, identity)

    #println(C)
    rows = rowvals(C)
    vals = nonzeros(C)

    #println(rows)

    fns = Vector{Vector{Shape{T}}}(undef, 3*num_faces)
    pos = Vector{P}(undef, 3*num_faces)

    #println(cells(faces))

    for (i,face) in enumerate(cells(faces))

        fns[3*(i-1)+1] = Vector{Shape{T}}()
        fns[3*(i-1)+2] = Vector{Shape{T}}()
        fns[3*(i-1)+3] = Vector{Shape{T}}()

        pos[3*(i-1)+1] = cartesian(center(chart(faces,face)))
        pos[3*(i-1)+2] = cartesian(center(chart(faces,face)))
        pos[3*(i-1)+3] = cartesian(center(chart(faces,face)))

        #println( cartesian(center(chart(faces,face))))
        #println(face)

        # add shape function for each cell to the basis
        for k in nzrange(C,i)

            #Cell index
            j = rows[k]
            #face index (sign gives local orientation)
            s = vals[k]

            cell = cells(mesh)[j]

            #local face index
            lf = abs(s);
            #local vertex index
            lv1 = mod1(lf+1,4)
            lv2 = mod1(lf+2,4)
            lv3 = mod1(lf+3,4)

            #println("\t",lv1," ",lv2," ",lv3)
            #println("\t=>",cell[lv1]," ",cell[lv2]," ",cell[lv3])
            #global vertex index
            gv1 = cell[lv1]
            gv2 = cell[lv2]
            gv3 = cell[lv3]

            if (gv1 < gv2) && (gv2 < gv3)
                push!(fns[3*(i-1)+1], Shape{T}(j, 3*(abs(s)-1)+1, sign(s)))
                push!(fns[3*(i-1)+2], Shape{T}(j, 3*(abs(s)-1)+2, sign(s)))
                push!(fns[3*(i-1)+3], Shape{T}(j, 3*(abs(s)-1)+3, sign(s)))
                #println("Case 1")
            elseif (gv1 < gv3) && (gv3 < gv2)
                push!(fns[3*(i-1)+1], Shape{T}(j, 3*(abs(s)-1)+1, sign(s)))
                push!(fns[3*(i-1)+2], Shape{T}(j, 3*(abs(s)-1)+3, sign(s)))
                push!(fns[3*(i-1)+3], Shape{T}(j, 3*(abs(s)-1)+2, sign(s)))
                #println("Case 2")
            elseif (gv2 < gv1) && (gv1 < gv3)
                push!(fns[3*(i-1)+1], Shape{T}(j, 3*(abs(s)-1)+2, sign(s)))
                push!(fns[3*(i-1)+2], Shape{T}(j, 3*(abs(s)-1)+1, sign(s)))
                push!(fns[3*(i-1)+3], Shape{T}(j, 3*(abs(s)-1)+3, sign(s)))
                #println("Case 3")
            elseif (gv2 < gv3) && (gv3 < gv1)
                push!(fns[3*(i-1)+1], Shape{T}(j, 3*(abs(s)-1)+2, sign(s)))
                push!(fns[3*(i-1)+2], Shape{T}(j, 3*(abs(s)-1)+3, sign(s)))
                push!(fns[3*(i-1)+3], Shape{T}(j, 3*(abs(s)-1)+1, sign(s)))
                #println("Case 4")
            elseif (gv3 < gv2) && (gv2 < gv1)
                push!(fns[3*(i-1)+1], Shape{T}(j, 3*(abs(s)-1)+3, sign(s)))
                push!(fns[3*(i-1)+2], Shape{T}(j, 3*(abs(s)-1)+2, sign(s)))
                push!(fns[3*(i-1)+3], Shape{T}(j, 3*(abs(s)-1)+1, sign(s)))
                #println("Case 5")
            elseif (gv3 < gv1) && (gv1 < gv2)
                push!(fns[3*(i-1)+1], Shape{T}(j, 3*(abs(s)-1)+3, sign(s)))
                push!(fns[3*(i-1)+2], Shape{T}(j, 3*(abs(s)-1)+1, sign(s)))
                push!(fns[3*(i-1)+3], Shape{T}(j, 3*(abs(s)-1)+2, sign(s)))
                #println("Case 6")
            end

            #println(s)

            #push!(fns[3*(i-1)+1], Shape{T}(j, 3*(abs(s)-1)+1, sign(s)))
            #push!(fns[3*(i-1)+2], Shape{T}(j, 3*(abs(s)-1)+2, sign(s)))
            #push!(fns[3*(i-1)+3], Shape{T}(j, 3*(abs(s)-1)+3, sign(s)))
        end

    end

    BDM3DBasis(mesh, fns, pos)
end

function brezzidouglasmarini3d(mesh)
    faces = skeleton(mesh, 2)
    brezzidouglasmarini3d(mesh, faces)
end


text:
module Books

using SearchLight, SearchLight.Validation, BooksValidator

export Book

mutable struct Book <: AbstractModel
  ### INTERNALS
  _table_name::String
  _id::String
  _serializable::Vector{Symbol}

  ### FIELDS
  id::DbId
  title::String
  author::String
  cover::String

  ### VALIDATION
  # validator::ModelValidator

  ### CALLBACKS
  # before_save::Function
  # after_save::Function
  # on_save::Function
  # on_find::Function
  # after_find::Function

  ### SCOPES
  # scopes::Dict{Symbol,Vector{SearchLight.SQLWhereEntity}}

  ### constructor
  Book(;
    ### FIELDS
    id = DbId(),
    title = "",
    author = "",
    cover = "",

    ### VALIDATION
    # validator = ModelValidator([
    #   ValidationRule(:title, BooksValidator.not_empty)
    # ]),

    ### CALLBACKS
    # before_save = (m::Todo) -> begin
    #   @info "Before save"
    # end,
    # after_save = (m::Todo) -> begin
    #   @info "After save"
    # end,
    # on_save = (m::Todo, field::Symbol, value::Any) -> begin
    #   @info "On save"
    # end,
    # on_find = (m::Todo, field::Symbol, value::Any) -> begin
    #   @info "On find"
    # end,
    # after_find = (m::Todo) -> begin
    #   @info "After find"
    # end,

    ### SCOPES
    # scopes = Dict{Symbol,Vector{SearchLight.SQLWhereEntity}}()

  ) = new("books", "id", Symbol[],                                                ### INTERNALS
          id, title, author, cover                                                             ### FIELDS
          # validator,                                                                  ### VALIDATION
          # before_save, after_save, on_save, on_find, after_find                       ### CALLBACKS
          # scopes                                                                      ### SCOPES
          )
end

function seed()
  BillGatesBooks = [
    ("The Best We Could Do", "Thi Bui"),
    ("Evicted: Poverty and Profit in the American City", "Matthew Desmond"),
    ("Believe Me: A Memoir of Love, Death, and Jazz Chickens", "Eddie Izzard"),
    ("The Sympathizer!", "Viet Thanh Nguyen"),
    ("Energy and Civilization, A History", "Vaclav Smil")
  ]
  for b in BillGatesBooks
    Book(title = b[1], author = b[2]) |> SearchLight.save!
  end
end

end

max_stars_repo_name: tetsugps/julia

max_issues_repo_name: tetsugps/julia

max_forks_repo_name: tetsugps/julia


text:
using DiffEqFlux, Optimization, OrdinaryDiffEq, Test

#A desired MWE for now, not a test yet.
function f(du,u,p,t)
    y₁,y₂,y₃ = u
    k₁,k₂,k₃ = p
    du[1] = -k₁*y₁ + k₃*y₂*y₃
    du[2] =  k₁*y₁ - k₃*y₂*y₃ - k₂*y₂^2
    du[3] =  y₁ + y₂ + y₃ - 1
    nothing
end
u₀ = [1.0, 0, 0]
M = [1. 0  0
     0  1. 0
     0  0  0]
tspan = (0.0,1.0)
p = [0.04,3e7,1e4]
func = ODEFunction(f,mass_matrix=M)
prob = ODEProblem(func,u₀,tspan,p)
sol = solve(prob,Rodas5(),saveat=0.1)

dudt2 = FastChain(FastDense(3,64,tanh),FastDense(64,2))
ndae = NeuralODEMM(dudt2, (u,p,t) -> [u[1] + u[2] + u[3] - 1], tspan, M, Rodas5(autodiff=false),saveat=0.1)
ndae(u₀)

function predict_n_dae(p)
    ndae(u₀,p)
end
function loss(p)
    pred = predict_n_dae(p)
    loss = sum(abs2,Array(sol) .- pred)
    loss,pred
end

cb = function (p,l,pred) #callback function to observe training
  display(l)
  return false
end

l1 = first(loss(ndae.p))
optfunc = Optimization.OptimizationFunction((x, p) -> loss(x), Optimization.AutoZygote())
optprob = Optimization.OptimizationProblem(optfunc, ndae.p)
res = Optimization.solve(optprob, BFGS(initial_stepnorm = 0.001), callback = cb, maxiters = 100)
@test res.minimum < l1


text:
# workspace()

# include("./types.jl")
# include("./tree.jl")

# Return the optimal weight to add to a tree that will maximally decrease the
# objective. line 12 ~16 in algorithm
function ComputeEta(wgtd_error::Float64, tree_size::Float64, alpha::Float64,
                    beta::Float64, lambda::Float64,
                    the_normalizer::Float64, num_examples::Int64, num_features::Int64)
  wgtd_error = max(wgtd_error, kTolerance)  # Helps with division by zero.
  error_term = (1. - wgtd_error) * exp(alpha) - wgtd_error * exp(-alpha)
  complexity_penalty = ComplexityPenalty(Int64(tree_size), beta, lambda,
                                  the_normalizer, num_examples, num_features)
  ratio = complexity_penalty / wgtd_error
  eta = 0.
  if abs(error_term) <= 2.0 * complexity_penalty
    eta = -alpha;
  elseif error_term > 2.0 * complexity_penalty
    eta = log(-ratio + sqrt(ratio * ratio + (1 - wgtd_error)/wgtd_error))
  else
    eta = log(ratio + sqrt(ratio * ratio + (1 - wgtd_error)/wgtd_error))
  end
  return eta
end

# Either add a new tree to model or update the weight of an existing tree in
# model. The tree and weight are selected via approximate coordinate descent on
# the objective, where the "approximate" indicates that we do not search all
# trees but instead grow trees greedily.
function AddTreeToModel(examples::Vector{Example}, model::Model, loss_type::String,
                        beta::Float64, lambda::Float64, tree_depth::Int64)
# NB(wg): we return (examples, model)
# TODO(usyed): examples is passed by non-const reference because the example
# weights need to be changed. This is bad style.
# NB(wg):weights of examples  should be changed, the Vector should do this, need check
  # Initialize normalizer
  normalizer = 0. # NB:static float, why static?
  if isempty(model)
    if loss_type == "e"
      normalizer = exp(1) * length(examples)
    elseif loss_type == "l"
      normalizer = length(examples) / (log(2) * (1 + exp(-1)))
    end
  end
  # by yy: for distribution is initialized with uniform distribution
  # if isempty(model)
  #   normlizer = 1.
  # end

  # InitializeTreeData(examples, normalizer) # NB(need change)
  num_features = length(examples[1].values) # length of feature, num_examples should >= 1
  num_examples = length(examples)
  the_normalizer = normalizer

  best_old_tree_idx = -1
  best_wgtd_error = wgtd_error = gradient = best_gradient = 0.0

  # Find best old tree
  old_tree_is_best = false
  for i in 1:length(model)
    alpha = model[i].weight
    if abs(alpha) < kTolerance
      continue  # Skip zeroed-out weights.
    end
    old_tree = model[i].tree
    wgtd_error = EvaluateTreeWgtd(examples, old_tree)
    sign_edge = (wgtd_error >= 0.5) ? 1 : -1
    gradient = Gradient(wgtd_error, length(old_tree), alpha, sign_edge, beta, lambda,
                        the_normalizer, num_examples, num_features)
    if abs(gradient) >= abs(best_gradient)
      best_gradient = gradient
      best_wgtd_error = wgtd_error
      best_old_tree_idx = i
      old_tree_is_best = true
    end
  end
  # println("successfully find the best old tree!")

  # Find best new tree
  new_tree = TrainTree(examples, beta, lambda, tree_depth, normalizer)
  wgtd_error = EvaluateTreeWgtd(examples, new_tree)
  gradient = Gradient(wgtd_error, length(new_tree), 0., -1, beta, lambda,
                      the_normalizer, num_examples, num_features)
  # println("Complete calculate the gradient !")
  if isempty(model) || abs(gradient) > abs(best_gradient)
    best_gradient = gradient
    best_wgtd_error = wgtd_error
    old_tree_is_best = false
  end
  # println("successfully find the best new tree!")

  # Update model weights
  alpha = 0.
  tree = Tree() # vector of Nodes
  if old_tree_is_best
    alpha = model[best_old_tree_idx].weight
    tree = model[best_old_tree_idx].tree
  else
    alpha = 0.
    tree = new_tree
  end
  eta = ComputeEta(best_wgtd_error, Float64(length(tree)), alpha, beta, lambda,
                  the_normalizer, num_examples, num_features)
  if old_tree_is_best
    println("before update, weight is ", model[best_old_tree_idx].weight)
    model[best_old_tree_idx].weight += eta
    println("after update, weight is ", model[best_old_tree_idx].weight)
  else
    push!(model, classifier(eta, new_tree))
    println("the weight of new classifier is ", eta)
  end

  # Update examples weights and compute normalizer
  old_normalizer = normalizer
  normalizer = 0.
  for example in examples
    u = eta * example.label * ClassifyExampleTree(example, tree); # ??
    if loss_type == "e"
      example.weight *= exp(-u)
    elseif loss_type == "l"
      z = (1 - log(2) * example.weight * old_normalizer) /
                      (log(2) * example.weight * old_normalizer)
      example.weight = 1 / (log(2) * (1 + z * exp(u)))
    end
    normalizer += example.weight
  end

  # Renormalize example weights
  # TODO(usyed): Two loops is inefficient.
  for example in examples
    example.weight /= normalizer
  end
  return (examples, model)
end

# Classify example with model.
function ClassifyExampleModel(example::Example, model::Model)
  score = 0.
  for wgtd_tree in model
    score += wgtd_tree.weight * ClassifyExampleTree(example, wgtd_tree.tree)
  end
  if score < 0.
    return -1
  else
    return 1
  end
end

# Compute the error of model on examples. Also compute the number of trees in
# model and their average size.
# return (error, avg_tree_size, num_trees)
function EvaluateModel(examples::Vector{Example}, model::Model)
# error: pointer float
# avg_tree_size: pointer float
# num_trees: pointer int
  incorrect = 0.
  for example in examples
    if example.label != ClassifyExampleModel(example, model)
      incorrect += 1
    end
  end
  num_trees = 0.
  sum_tree_size = 0.
  println("the size of model is ", length(model))
  for  wgtd_tree in model
    if abs(wgtd_tree.weight) >= kTolerance
      num_trees += 1
      sum_tree_size += length(wgtd_tree.tree)
    end
    # num_trees += 1
    # sum_tree_size += length(wgtd_tree.tree)
  end
  error = incorrect / length(examples)
  avg_tree_size = sum_tree_size / num_trees
  return (error, avg_tree_size, num_trees)
end


text:
# Copyright 2017-2018 Gandalf Software, Inc. (Scott Paul Jones)
# Licensed under MIT License, see LICENSE.md

EncodingStyle(::Type{<:Union{UTF8_CSEs,UTF16CSE}}) = MultiCodeUnitEncoding()
EncodingStyle(::Type{<:CSE}) = SingleCodeUnitEncoding()

EncodingStyle(v::AbstractString) = EncodingStyle(typeof(v))
EncodingStyle(::Type{T}) where {T<:AbstractString} = EncodingStyle(cse(T))
EncodingStyle(::Type{<:SubString{T}}) where {T<:AbstractString} = EncodingStyle(T)

################################################################################

CharSetStyle(::Type{<:CSE}) = CharSetUnicodePlus()
CharSetStyle(::Type{<:Union{Text1CSE, Text2CSE, Text4CSE}}) = CharSetUnknown()
CharSetStyle(::Type{BinaryCSE})    = CharSetBinary()
CharSetStyle(::Type{ASCIICSE})     = CharSetASCIICompat()
CharSetStyle(::Type{<:Latin_CSEs}) = CharSetISOCompat()
CharSetStyle(::Type{<:UCS2_CSEs})  = CharSetBMPCompat()

CharSetStyle(::Type{<:AbstractString}) = CharSetUnicodePlus()
CharSetStyle(::Type{<:AbstractChar})   = CharSetUnicode()
CharSetStyle(::Type{Char})      = CharSetUnicodePlus() # Encodes invalid characters also
CharSetStyle(::Type{UInt8})     = CharSetBinary()
CharSetStyle(::Type{UInt16})    = CharSetUnknown()
CharSetStyle(::Type{UInt32})    = CharSetUnknown()

CharSetStyle(::T) where {T<:AbstractString} = CharSetStyle(cse(T))
CharSetStyle(::T) where {T<:AbstractChar}   = CharSetStyle(T)

################################################################################

CompareStyle(::Type{<:CSE}, ::Type{<:CSE}) = CodePointCompare()

CompareStyle(::Type{C}, ::Type{C}) where {C<:CSE} =
    ByteCompare()
CompareStyle(::Type{C}, ::Type{C}) where {C<:Union{Word_CSEs,Quad_CSEs}} =
    WordCompare()

# This is important because of invalid sequences
CompareStyle(::Type{C}, ::Type{C}) where {C<:RawUTF8CSE} =
    CodePointCompare()

CompareStyle(::Type{UTF16CSE},    ::Type{UTF16CSE})    = UTF16Compare()
CompareStyle(::Type{UTF16CSE},    ::Type{<:UCS2_CSEs}) = UTF16Compare()
CompareStyle(::Type{<:UCS2_CSEs}, ::Type{UTF16CSE})    = UTF16Compare()

CompareStyle(::Type{ASCIICSE}, ::Type{<:Union{Binary_CSEs,Latin_CSEs,UTF8_CSEs}}) =
    ByteCompare()
CompareStyle(::Type{ASCIICSE}, ::Type{<:Union{Word_CSEs,Quad_CSEs}}) =
    WidenCompare()

CompareStyle(::Type{<:Latin_CSEs}, ::Type{<:Latin_CSEs}) =
    ByteCompare()
CompareStyle(::Type{<:Latin_CSEs}, ::Type{UTF8_CSEs}) =
    ASCIICompare()
CompareStyle(::Type{<:Latin_CSEs}, ::Type{<:Union{Word_CSEs,Quad_CSEs}}) =
    WidenCompare()

CompareStyle(::Type{<:UCS2_CSEs}, ::Type{<:Union{ASCIICSE,Binary_CSEs,Latin_CSEs,Quad_CSEs}}) =
    WidenCompare()
CompareStyle(::Type{<:UCS2_CSEs},  ::Type{<:UCS2_CSEs}) =
    WordCompare()

CompareStyle(::Type{<:UTF32_CSEs},
             ::Type{<:Union{ASCIICSE,Binary_CSEs,Latin_CSEs,Text2CSE,UCS2_CSEs}}) =
    WidenCompare()
CompareStyle(::Type{<:UTF32_CSEs},  ::Type{<:UTF32_CSEs}) =
    WordCompare()

CompareStyle(::Type{S}, ::Type{T}) where {S<:AbstractString, T<:AbstractString} =
    CompareStyle(cse(S), cse(T))
CompareStyle(::Type{T}, ::Type{T}) where {T<:AbstractString} = ByteCompare()

CompareStyle(A::AbstractString, B::AbstractString) = CompareStyle(typeof(A), typeof(B))


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

@doc raw"""SelfSubjectAccessReviewSpec is a description of the access request.  Exactly one of ResourceAuthorizationAttributes and NonResourceAuthorizationAttributes must be set

    IoK8sApiAuthorizationV1beta1SelfSubjectAccessReviewSpec(;
        nonResourceAttributes=nothing,
        resourceAttributes=nothing,
    )

    - nonResourceAttributes::IoK8sApiAuthorizationV1beta1NonResourceAttributes : NonResourceAttributes describes information for a non-resource access request
    - resourceAttributes::IoK8sApiAuthorizationV1beta1ResourceAttributes : ResourceAuthorizationAttributes describes information for a resource access request
"""
mutable struct IoK8sApiAuthorizationV1beta1SelfSubjectAccessReviewSpec <: SwaggerModel
    nonResourceAttributes::Any # spec type: Union{ Nothing, IoK8sApiAuthorizationV1beta1NonResourceAttributes } # spec name: nonResourceAttributes
    resourceAttributes::Any # spec type: Union{ Nothing, IoK8sApiAuthorizationV1beta1ResourceAttributes } # spec name: resourceAttributes

    function IoK8sApiAuthorizationV1beta1SelfSubjectAccessReviewSpec(;nonResourceAttributes=nothing, resourceAttributes=nothing)
        o = new()
        validate_property(IoK8sApiAuthorizationV1beta1SelfSubjectAccessReviewSpec, Symbol("nonResourceAttributes"), nonResourceAttributes)
        setfield!(o, Symbol("nonResourceAttributes"), nonResourceAttributes)
        validate_property(IoK8sApiAuthorizationV1beta1SelfSubjectAccessReviewSpec, Symbol("resourceAttributes"), resourceAttributes)
        setfield!(o, Symbol("resourceAttributes"), resourceAttributes)
        o
    end
end # type IoK8sApiAuthorizationV1beta1SelfSubjectAccessReviewSpec

const _property_map_IoK8sApiAuthorizationV1beta1SelfSubjectAccessReviewSpec = Dict{Symbol,Symbol}(Symbol("nonResourceAttributes")=>Symbol("nonResourceAttributes"), Symbol("resourceAttributes")=>Symbol("resourceAttributes"))
const _property_types_IoK8sApiAuthorizationV1beta1SelfSubjectAccessReviewSpec = Dict{Symbol,String}(Symbol("nonResourceAttributes")=>"IoK8sApiAuthorizationV1beta1NonResourceAttributes", Symbol("resourceAttributes")=>"IoK8sApiAuthorizationV1beta1ResourceAttributes")
Base.propertynames(::Type{ IoK8sApiAuthorizationV1beta1SelfSubjectAccessReviewSpec }) = collect(keys(_property_map_IoK8sApiAuthorizationV1beta1SelfSubjectAccessReviewSpec))
Swagger.property_type(::Type{ IoK8sApiAuthorizationV1beta1SelfSubjectAccessReviewSpec }, name::Symbol) = Union{Nothing,eval(Base.Meta.parse(_property_types_IoK8sApiAuthorizationV1beta1SelfSubjectAccessReviewSpec[name]))}
Swagger.field_name(::Type{ IoK8sApiAuthorizationV1beta1SelfSubjectAccessReviewSpec }, property_name::Symbol) =  _property_map_IoK8sApiAuthorizationV1beta1SelfSubjectAccessReviewSpec[property_name]

function check_required(o::IoK8sApiAuthorizationV1beta1SelfSubjectAccessReviewSpec)
    true
end

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


text:
function get_module_2_excersise_1_author_name()
    return "PUT YOUR NAME HERE"
end

function get_odd_numbers(number_of_values)
    odd_numbers = zeros(number_of_values)
    # implement your loop here
    return odd_numbers
end

function get_powers_of_three(number_of_values)
    powers_of_three = zeros(number_of_values)
    # implement your loop here
    return powers_of_three
end


text:
@testset "Distance" begin
    g4 = PathDiGraph(5)
    adjmx1 = [0 1 0; 1 0 1; 0 1 0] # graph
    adjmx2 = [0 1 0; 1 0 1; 1 1 0] # digraph
    a1 = Graph(adjmx1)
    a2 = DiGraph(adjmx2)
    distmx1 = [Inf 2.0 Inf; 2.0 Inf 4.2; Inf 4.2 Inf]
    distmx2 = [Inf 2.0 Inf; 3.2 Inf 4.2; 5.5 6.1 Inf]

    for g in testdigraphs(g4)
        @test_throws ErrorException eccentricity(g)
    end
    for g in testgraphs(a1)
        z = @inferred(eccentricity(g, distmx1))
        @test z == [6.2, 4.2, 6.2]
        @test @inferred(diameter(z)) == diameter(g, distmx1) == 6.2
        @test @inferred(periphery(z)) == periphery(g, distmx1) == [1,3]
        @test @inferred(radius(z)) == radius(g, distmx1) == 4.2
        @test @inferred(center(z)) == center(g, distmx1) == [2]
    end

    for g in testdigraphs(a2)
        z = @inferred(eccentricity(g, distmx2))
        @test z == [6.2, 4.2, 6.1]
        @test @inferred(diameter(z)) == diameter(g, distmx2) == 6.2
        @test @inferred(periphery(z)) == periphery(g, distmx2) == [1]
        @test @inferred(radius(z)) == radius(g, distmx2) == 4.2
        @test @inferred(center(z)) == center(g, distmx2) == [2]
    end
    @test size(LightGraphs.DefaultDistance()) == (typemax(Int), typemax(Int))
    d = @inferred(LightGraphs.DefaultDistance(3))
    @test size(d) == (3, 3)
    @test d[1,1] == getindex(d, 1, 1) == 1
    @test d[1:2, 1:2] == LightGraphs.DefaultDistance(2)
    @test d == transpose(d) == ctranspose(d)
end

max_stars_repo_name: AsileBcd/JohnsonCycles

max_issues_repo_name: AsileBcd/JohnsonCycles

max_forks_repo_name: AsileBcd/JohnsonCycles


text:
export get_config
import Cambrian.get_config

"overrides Cambrian.get_config(::Dict), converts function names to functions and tracks arity"
function get_config(config::Dict)
    # parse all function names, assign to function value
    two_arity = falses(length(config["functions"]))
    functions = Array{Function}(undef, length(config["functions"]))
    for i in eachindex(config["functions"])
        fname = config["functions"][i]
        if CGPFunctions.arity[fname] == 2
            two_arity[i] = true
        end
        functions[i] = eval(Meta.parse(string("CGPFunctions.", fname)))
    end
    config["two_arity"] = two_arity
    config["functions"] = functions
    (; (Symbol(k)=>v for (k, v) in config)...)
end


text:
module GitHubActions

export
    GitHubActionsLogger,
    add_path,
    end_group,
    get_input,
    get_state,
    group,
    log_debug,
    log_error,
    log_warning,
    save_state,
    set_command_echo,
    set_env,
    set_failed,
    set_output,
    set_secret,
    start_group

using Logging: Logging, AbstractLogger, Debug, Info, Warn, Error

using JSON: json

const CMD_MARKER = "::"

"""
    MissingInputError(k)

Indicates that a required input was not provided.
The input's name is stored in `k`.
"""
struct MissingInputError <: Exception
    k::String
end

Base.showerror(io::IO, e::MissingInputError) =
    print(io, "Input required and not supplied: $(e.k)")

cmd_value(::Nothing) = ""
cmd_value(s::AbstractString) = s
cmd_value(x) = json(x)

function esc_data(val)
    s = cmd_value(val)
    s = replace(s, '%' => "%25")
    s = replace(s, '\r' => "%0D")
    s = replace(s, '\n' => "%0A")
    return s
end

function esc_prop(val)
    s = cmd_value(val)
    s = replace(s, '%' => "%25")
    s = replace(s, '\r' => "%0D")
    s = replace(s, '\n' => "%0A")
    s = replace(s, ':' => "%3A")
    s = replace(s, ',' => "%2C")
    return s
end

format_props(props) =
    join(map(p -> string(p.first, "=", esc_prop(p.second)), collect(pairs(props))), ',')

function command(cmd, props, val)
    s = CMD_MARKER * cmd
    isempty(props) || (s *= ' ' * format_props(props))
    s *= CMD_MARKER * esc_data(val)
    println(s)
end

fail() = exit(1)

add_to_file(k, v) = open(f -> println(f, v), ENV[k], "a")

"""
    end_group()

End a group that was started with [`start_group`](@ref).
"""
end_group() = command("endgroup", (), "")

"""
    get_state(k)

Get the state variable with name `k`.
"""
get_state(k) = get(ENV, "STATE_$k", "")

"""
    log_debug(msg)

Log a debug message.
See also [`GitHubActionsLogger`](@ref).
"""
log_debug(msg) = command("debug", (), msg)

"""
    log_error(msg)

Log an error message.
See also [`GitHubActionsLogger`](@ref).
"""
log_error(msg) = command("error", (), msg)

"""
    log_warning(msg)

Log a warning message.
See also [`GitHubActionsLogger`](@ref).
"""
log_warning(msg) = command("warning", (), msg)

"""
    save_state(k, v)

Save value `v` with name `k` to state.
"""
save_state(k, v) = command("save-state", (name=k,), v)

"""
    set_command_echo(enable)

Enable or disable command echoing.
"""
set_command_echo(enable) = command("echo", (), enable ? "on" : "off")

"""
    set_output(k, v)

Set the output with name `k` to value `v`.
"""
set_output(k, v) = command("set-output", (name=k,), v)

"""
    set_secret(v)

Mask the value `v` in logs.
"""
set_secret(v) = command("add-mask", (), v)

"""
    start_group(name)

Start a foldable group called `name`.
"""
start_group(name) = command("group", (), name)

"""
    add_path(v)

Add `v` to the system `PATH`.
"""
function add_path(v)
    sep = @static Sys.iswindows() ? ';' : ':'
    ENV["PATH"] = v * sep * ENV["PATH"]
    add_to_file("GITHUB_PATH", v)
end

"""
    get_input(k; required=false)

Get an input.
If `required` is set and the input is not, a [`MissingInputError`](@ref) is thrown.
"""
function get_input(k; required=false)
    val = get(ENV, "INPUT_" * uppercase(replace(k, ' ' => '_')), "")
    required && isempty(val) && throw(MissingInputError(k))
    return string(strip(val))
end

"""
    group(f, name)

Run `f` inside of a foldable group.
See also [`start_group`](@ref) and [`end_group`](@ref).
"""
function group(f, name)
    start_group(name)
    return try f() finally end_group() end
end

"""
    set_env(k, v)

Set environment variable `k` to value `v`.
"""
function set_env(k, v)
    val = cmd_value(v)
    ENV[k] = val
    delimiter = "EOF"
    while occursin(delimiter, val)
        delimiter *= "EOF"
    end
    add_to_file("GITHUB_ENV", join(["$k<<$delimiter", val, delimiter], "\n"))
end

"""
    set_failed(msg)

Error with `msg`, and set the process exit code to `1`.
"""
function set_failed(msg)
    atexit(fail)
    log_error(msg)
end

"""
    GitHubActionsLogger()

A logger that prints to standard output in the format expected by GitHub Actions.
"""
struct GitHubActionsLogger <: AbstractLogger end

Logging.catch_exceptions(::GitHubActionsLogger) = true
Logging.min_enabled_level(::GitHubActionsLogger) = Debug
Logging.shouldlog(::GitHubActionsLogger, args...) = true

function Logging.handle_message(
    ::GitHubActionsLogger,
    level, msg, _module, group, id, file, line;
    location=nothing, kwargs...,
)
    file, line = something(location, (file, line))

    workspace = get(ENV, "GITHUB_WORKSPACE", nothing)
    if workspace !== nothing
        # In order for inline annotations to work correctly:
        file = relpath(file, workspace)
    end
    message = string(msg)
    for (k, v) in kwargs
        result = sprint(Logging.showvalue, v)
        message *= "\n  $k = " * if occursin('\n', result)
            replace("\n" * result, '\n' => "\n    ")
        else
            result
        end
    end
    if level === Info
        println(message)
    else
        cmd = if level === Debug
            "debug"
        elseif level === Warn
            "warning"
        elseif level === Error
            "error"
        end
        command(cmd, (file=file, line=line), message)
    end
end

end


text:
struct GradientDescent{T1} <: QuasiNewton{T1}
    approx::T1
end
# might want to clean this up
update!(A::UniformScaling, s, y, scheme::GradientDescent{<:InverseApprox}) = A
update!(A, s, y, scheme::GradientDescent{<:DirectApprox}) = A
update(A::UniformScaling, s, y, scheme::GradientDescent{<:InverseApprox}) = A
update(A, s, y, scheme::GradientDescent{<:DirectApprox}) = A


text:
using ProjAssistant
@quickactivate "Chemostat_Rath2017"

@time begin

    using ProgressMeter
    using Base.Threads

    import Chemostat
    import Chemostat.MetNets
    import Chemostat.MetLP
    import Chemostat.MetEP
    const Ch = Chemostat

    import Chemostat_Rath2017
    const ChR = Chemostat_Rath2017
    const Rd = ChR.RathData
    const H1 = ChR.Human1
    const HG = H1.HumanGEM
    const AG = H1.HartGEMs

    using Plots

    using Statistics
end

## ---------------------------------------------------------------------
# DESCRIPTION
# This is a numeric test of MaxEnt EP at beta 0

## ---------------------------------------------------------------------
let
    tissue = "GBM"
    for modelid in ["fva_base", "fva_scaled"]

        model = AG.load_model(modelid, tissue; uncompress = true)
        model = MetNets.force_dims(model)
        objider = HG.HUMAN_BIOMASS_IDER
        M, N = size(model)
        objidx = MetNets.rxnindex(model, objider)

        # maxent
        save_frec = 5
        function oniter(it, epmodel)
            !iszero(rem(it, save_frec)) && return

            epout_ = MetEP.produce_epout(epmodel; drop_epfields = true)
            sdat(AG, epout_,
                "maxent_ep_beta0", "epout", (;modelid, tissue, it), ".jls";
                verbose = false
            )

        end

        try
            @info("MAxEnt", model = size(model))
            epmodel = MetEP.EPModel(model)
            model = nothing; GC.gc(); # free mem
            MetEP.converge_ep!(epmodel; oniter,
                maxiter = 2000, # Test
                verbose = true
            )
        catch err
            @error("At MaxEnt ", err)
        end
    end

end


text:
@testset "OneMax" begin

    # Initial population
    N = 100
    initpop = (() -> BitArray(rand(Bool, N)))

    function Evolutionary.trace!(record::Dict{String,Any}, objfun, state, population, method::GA, options)
        idx = sortperm(state.fitpop)
        record["fitpop"] = state.fitpop[idx[1:5]]
    end

    res = Evolutionary.optimize(
        x -> -sum(x),                 # Function to MINIMIZE
        initpop,
        GA(
            selection = uniformranking(3),
            mutation =  flip,
            crossover = twopoint,
            mutationRate = 0.6,
            crossoverRate = 0.2,
            populationSize = 100,
        ),
        Evolutionary.Options(successive_f_tol = 20,iterations = 1500, store_trace=true));
    println("GA:UR(3):FLP:SP (OneMax: 1/sum) => F: $(minimum(res)), C: $(Evolutionary.iterations(res))")
    @test sum(Evolutionary.minimizer(res)) >= N-1
    @test abs(minimum(res)) >= N-1
    @test Evolutionary.trace(res)[end].metadata["fitpop"][1] == minimum(res)

end