text:
export  MonteCarloPolicyEvaluation,
        HookeJeevesPolicySearch,
        optimize,
        GeneticPolicySearch,
        CrossEntropyPolicySearch,
        optimize_dist,
        EvolutionStrategies,
        evolution_strategy_weights,
        IsotropicEvolutionStrategies

# --------------------------- POLICY SEARCH ---------------------------
#
#                      PART 1: POLICY EVALUATION
#
# 1.  Monte Carlo Policy Evaluation
#
# Return the mean discounted reward of m random trajectories,
# each calculated by first sampling the initial state distribution,
# and then executing rollout from that state, using policy π.

"""
    struct MonteCarloPolicyEvaluation
        𝒫 # problem
        b # initial state distribution
        d # depth
        m # number of samples
    end
"""
struct MonteCarloPolicyEvaluation
    𝒫 # problem
    b # initial state distribution
    d # depth
    m # number of samples
end

@doc raw"""
    function (U::MonteCarloPolicyEvaluation)(π)

Return the mean discounted reward of m random trajectories, each calculated by first sampling the initial state distribution, and then executing rollout from that state, using policy π.

$U(\pi) = E_{\tau}[R(\tau)] = \sum_{\tau} p(\tau)R(\tau) \approx \frac{1}{m} \sum_{i=1}^{m} R(\tau^{(i)})$

Returns: Ũ(π)

Complexity: 𝒪(m × d)
"""
function (U::MonteCarloPolicyEvaluation)(π)
    R(π) = rollout(U.𝒫, rand(U.b), π, U.d) # 𝒪(d)
    return mean(R(π) for i = 1:U.m)
end

"""
    (U::MonteCarloPolicyEvaluation)(π, θ) = U(s->π(θ, s))

Parameterized version where policy adjusted by parameter vector `θ`.

Returns: Ũ(π₍θ₎)

Complexity: 𝒪(m × d)
"""
(U::MonteCarloPolicyEvaluation)(π, θ) = U(s->π(θ, s))

#                   PART 2: SEARCH
#
# 2.  Local Search (Hooke-Jeeves)
#
# Start w/initial parameterization and move from neighbor to neighbor
#   until convergence.
# θ: n-dimensional vector
# Takes step of ±α in each coordinate direction from θ.  If improvement found, moves to best neighbor.  Else stepsize reduced to α *= c for some c < 1.0.  Continue until α < ϵ for some ϵ > 0.

"""
    struct HookeJeevesPolicySearch
        θ # initial parameterization
        α # step size
        c # step size reduction factor
        ε # termination step size
    end
"""
struct HookeJeevesPolicySearch
    θ # initial parameterization
    α # step size
    c # step size reduction factor
    ε # termination step size
end

"""
    function optimize(M::HookeJeevesPolicySearch, π, U)

Returns: optimized policy parameterization `θ_best`.
"""
function optimize(M::HookeJeevesPolicySearch, π, U)
    θ, θ′, α, c, ε = copy(M.θ), similar(M.θ), M.α, M.c, M.ε
    u, n = U(π, θ), length(θ)
    while α > ε
        copyto!(θ′, θ) # θ′← θ
        best = (i=0, sgn=0, u=u)
        for i in 1:n
            for sgn in (-1,1)
                θ′[i] = θ[i] + sgn*α
                u′ = U(π, θ′)  # eval: Example: MonteCarloPolicyEvaluation
                if u′ > best.u
                    best = (i=i, sgn=sgn, u=u′)
                end
            end
            θ′[i] = θ[i]
        end
        if best.i != 0
            θ[best.i] += best.sgn*α
            u = best.u
        else
            α *= c
        end
    end
    return θ
end

# 3.  Genetic Policy Search

"""
    struct GeneticPolicySearch
        θs # initial population
        σ # initial standard devidation
        m_elite # number of elite samples
        k_max # number of iterations
    end
"""
struct GeneticPolicySearch
    θs # initial parameter sample population
    σ # initial standard deviation
    m_elite # number of elite samples
    k_max # number of iterations
end

"""
    function optimize(M::GeneticPolicySearch, π, U)

Returns: optimized policy parameterizaion `θ_best`.
"""
function optimize(M::GeneticPolicySearch, π, U)
    θs, σ = M.θs, M.σ
    n, m = length(first(θs)), length(θs)

    # iterate k_max times
    for k in 1:M.k_max
        # Estimate U(π) for each θ in sample population
        us = [U(π, θ) for θ in θs]
        # indicies of high-to-low ordering of value estimates
        sp = sortperm(us, rev=true)
        # save the top performer
        θ_best = θs[sp[1]]
        # helper func to sample uniformly one of the top m_elite performers
        rand_elite() = θs[sp[rand(1:M.m_elite)]]
        # Update sample population with m-1 samples of
        # ∟ top performers perturbed with added gaussian noise
        θs = [rand_elite() + σ.*randn(n) for i in 1:(m-1)]
        # also include the top, unperturbed performer
        push!(θs, θ_best)
    end

    # return the best performer from final iteration.
    return last(θs)
end

# 4.  Cross Entropy Method
#
# Train distribution which is used to generate approximatly optimal policy parameters.

"""
    struct CrossEntropyPolicySearch
        p # initial distribution
        m # number of samples
        m_elite # number of elite samples
        k_max # number of iterations
    end
"""
struct CrossEntropyPolicySearch
    p # initial distribution
    m # number of samples
    m_elite # number of elite samples
    k_max # number of iterations
end

"""
    function optimize_dist(M::CrossEntropyPolicySearch, π, U)

Train policy parameter distribution `p` for parameterized policy `π` using approximate value function U.

Returns: optimized distribution `p` ('p̃')

Complexity: 𝒪(k_max × m × d)
"""
function optimize_dist(M::CrossEntropyPolicySearch, π, U)
    p, m, m_elite, k_max = M.p, M.m, M.m_elite, M.k_max

    # iterate k_max times
    for k in 1:k_max
        # sample parameter distributionn p m times
        θs = rand(p, m)
        # Estimate policy values for each parameter sample
        us = [U(π, θs[:,i]) for i in 1:m]  # 𝒪(m × d)
        # Extract top m_elite performers
        θ_elite = θs[:,sortperm(us)[(m-m_elite+1):m]]
        # refit distribution to elite samples
        p = Distributions.fit(typeof(p), θ_elite)
    end
    return p
end

"""
    function optimize(M, π, U)

Policy parameter optimization over space of parameter distributions. For when M is a policy search type that lets policy parameters θ to come from a sampled distribution `p`, whose parameters are directly optimized to maximize E[U(θ)], θ ∼ p.

Returns: mode(p)

Complexity: depends on M
"""
function optimize(M, π, U)
    return Distributions.mode(optimize_dist(M, π, U))
end

# 5.  Evolutionary Strategies
#
# Update a search distribution parameterized by a vector ψ at
#   each iteration by taking a step in the direciton of the gradient of
#   the Expected Policy Utility function.
"""
    struct EvolutionStrategies
        D       # distribution constructor
        ψ       # initial distribution parameterization
        ∇logp   # log search likelihood gradient
                #   note: ∇ψlogp = ∇logp(ψ, θ): ∇logp is a function of (ψ, θ)
        m       # number of samples
        α       # step factor
        k_max   # number of iterations
    end
"""
struct EvolutionStrategies
    D       # distribution constructor (θ⁽ⁱ⁾ ∼ D(ψ))
    ψ       # initial distribution parameterization
    ∇logp   # log search likelihood gradient
            #   note: ∇ψlogp = ∇logp(ψ, θ): ∇logp is a function of (ψ, θ)
    m       # number of samples
    α       # step factor
    k_max   # number of iterations
end

"""
    function evolution_strategy_weights(m)

Complexity: 𝒪(m)
"""
function evolution_strategy_weights(m)
    ws = [max(0, log(m/2+1) - log(i)) for i in 1:m]
    ws ./= sum(ws)
    ws .-= 1/m

    return ws
end

"""
    function optimize_dist(M::EvolutionStrategies, π, U)
"""
function optimize_dist(M::EvolutionStrategies, π, U)
    D, ψ, m, ∇logp, α = M.D, M.ψ, M.m, M.∇logp, M.α
    ws = evolution_strategy_weights(m)
    for k in 1:M.k_max
        # get m samples θ⁽ⁱ⁾ ∼ D(ψ)
        θs = rand(D(ψ), m)
        # evaluate value of the m samples
        us = [U(π, θs[:,i]) for i in 1:m]
        # sort high-to-low
        sp = sortperm(us, rev=true)
        # ∇ == Est. gradient w.r.t ψ of E[U(θ)], θ ∼ D(ψ)
        ∇ = sum(w.*∇logp(ψ, θs[:,i]) for (w,i) in zip(ws,sp))
        # gradient ascent
        ψ += α.*∇
    end
    # return optimized distribution
    return D(ψ)
end

# 6.  Isotropic Evolutionary Strategies
#
# Same as Evolutionary Strategies, but use mirroring of samples to reduce
#   gradient variation.
#
# Also, assumes θ ∼ 𝒩(ψ, σ²I).  Uses evolution strategy weights for U(θ⁽ⁱ⁾)
"""
    struct IsotropicEvolutionStrategies
        ψ # initial mean
        σ # initial standard devidation
        m # number of samples
        α # step factor
        k_max # number of iterations
    end
"""
struct IsotropicEvolutionStrategies
    ψ # initial mean
    σ # initial standard devidation
    m # number of samples
    α # step factor
    k_max # number of iterations
end

"""
    function optimize_dist(M::IsotropicEvolutionStrategies, π, U)
"""
function optimize_dist(M::IsotropicEvolutionStrategies, π, U)
    ψ, σ, m, α, k_max = M.ψ, M.σ, M.m, M.α, M.k_max
    n = length(ψ)
    ws = evolution_strategy_weights(2*div(m,2))
    for k in 1:k_max
        εs = [randn(n) for i in 1:div(m,2)]
        append!(εs, -εs) # weight mirroring
        us = [U(π, ψ + σ.*ε) for ε in εs]
        sp = sortperm(us, rev=true)
        ∇ = sum(w.*εs[i] for (w,i) in zip(ws,sp)) / σ
        ψ += α.*∇
    end
    return MvNormal(ψ, σ)
end


text:
#predevelopment and consenting

function PCCost!(options::zefiroOptions,sizes::zefiroSizes,data::zefiroData,output::zefiroOutput)
    key = (options.postprocess ? "postprocessing" : "evaluating")
    println(key*" projectManagement cost")
    projectManagement!(options,sizes,data,output)
    println(key*" legalAuthorization cost")
    legalAuthorization!(options,sizes,data,output)
    println(key*" surveys cost")
    surveys!(options,sizes,data,output)
    println(key*" engineering cost")
    engineering!(options,sizes,data,output)
    println(key*" contingencies cost")
    contingencies!(options,sizes,data,output)
    nothing

end

function projectManagement!(options::zefiroOptions,sizes::zefiroSizes,data::zefiroData,output::zefiroOutput)

    if options.projectManagement == 0
        if options.postprocess
            output.projectManagement = 0.03*output.CAPEX
        else
            output.CAPEXrate .+= 0.03
        end
    else
        error("projectManagement mode not found.")
    end
    nothing

end

function legalAuthorization!(options::zefiroOptions,sizes::zefiroSizes,data::zefiroData,output::zefiroOutput)

    if options.legalAuthorization == 0
        if options.postprocess
            output.legalAuthorization = 0.0013*output.CAPEX
        else
            output.CAPEXrate .+= 0.0013
        end
    else
        error("legalAuthorization mode not found.")
    end
    nothing

end

function surveys!(options::zefiroOptions,sizes::zefiroSizes,data::zefiroData,output::zefiroOutput)
    if options.surveys == 0
        if options.postprocess
            nothing
        else
            output.CAPEX .+= data.surveysCost
            output.surveys .+= data.surveysCost
        end
    else
        error("surveys mode not found.")
    end
    nothing

end

function engineering!(options::zefiroOptions,sizes::zefiroSizes,data::zefiroData,output::zefiroOutput)
    if options.engineering == 0
        if options.postprocess
            nothing
        else
            output.CAPEX .+= transpose(data.engVerifCost .+ data.baseCost .+ data.engUnitCost .* data.IC)
            output.engineering .+= transpose(data.engVerifCost .+ data.baseCost .+ data.engUnitCost .* data.IC)
        end
    else
        error("engineering mode not found.")
    end
    nothing

end

function contingencies!(options::zefiroOptions,sizes::zefiroSizes,data::zefiroData,output::zefiroOutput)

    if options.contingencies == 0
        if options.postprocess
            output.contingencies = 0.1*output.CAPEX
        else
            output.CAPEXrate .+= 0.1
        end
    else
        error("contingencies mode not found.")
    end
    nothing

end

ext: jl

max_stars_repo_name: Thiago-NovaesB/zefiro

max_issues_repo_name: Thiago-NovaesB/zefiro

max_forks_repo_name: Thiago-NovaesB/zefiro


text:
@doc raw"""
    pFq(α, β; z)
Compute the generalized hypergeometric function, defined by
```math
\operatorname{pFq}(α, β; z) = \sum_{k=0}^\infty \dfrac{(\alpha_1)_k\cdots(\alpha_p)_k}{(\beta_1)_k\cdots(\beta_q)_k}\dfrac{z^k}{k!}.
```
"""
function pFq(α::AbstractVector, β::AbstractVector, z; kwds...)
    if length(α) == length(β) == 0
        return exp(z)
    elseif length(α) == 1 && length(β) == 0
        return exp(-α[1]*log1p(-z))
    elseif length(α) == 1 && length(β) == 1
        return _₁F₁(α[1], β[1], float(z); kwds...)
    elseif length(α) == 2 && length(β) == 1
        return _₂F₁(α[1], α[2], β[1], float(z); kwds...)
    elseif length(α) ≤ length(β)
        if real(z) ≥ 0
            return pFqmaclaurin(α, β, float(z); kwds...)
        else
            return pFqweniger(α, β, float(z); kwds...)
        end
    elseif length(α) == length(β) + 1
        if abs(z) ≤ ρ
            return pFqmaclaurin(α, β, float(z); kwds...)
        else
            return pFqweniger(α, β, float(z); kwds...)
        end
    else
        return pFqweniger(α, β, float(z); kwds...)
    end
end

"""
Compute the generalized hypergeometric function `pFq(α, β; z)` by continued fraction.
"""
function pFqcontinuedfraction(α::AbstractVector{S}, β::AbstractVector{U}, z::V) where {S, U, V}
    T = promote_type(S, U, V)
    numerator(i) = - z * prod(i .+ α) / prod(i .+ β) / (i + 1)
    denominator(i) = 1 - numerator(i)
    K = continuedfraction(denominator, numerator, 10eps(real(T))) - denominator(0)
    # iszero(K + 1) leads to Inf, when e.g. Float64s run out of digits
    return 1 + z * prod(α) / prod(β) / (1 + K)
end

"""
Compute the generalized hypergeometric function `₃F₂(a₁, a₂, a₃, b₁, b₂; z)`.
"""
function _₃F₂(a₁, a₂, a₃, b₁, b₂, z; kwds...)
    if abs(z) ≤ ρ
        _₃F₂maclaurin(a₁, a₂, a₃, b₁, b₂, float(z); kwds...)
    else
        pFqweniger([a₁, a₂, a₃], [b₁, b₂], float(z); kwds...)
    end
end
_₃F₂(a₁, b₁, z; kwds...) = _₃F₂(1, 1, a₁, 2, b₁, z; kwds...)


text:
nonparametricdistr_insts(ext::VecSpecExt) = [
    [(d, ext)
    for d in vcat(10, 500, 1000, 2500, 5000:5000:25000)] # includes compile run
    ]

insts = OrderedDict()
insts["nat"] = (nothing, vcat(
    nonparametricdistr_insts(VecNegGeom()),
    nonparametricdistr_insts(VecNegLog()),
    nonparametricdistr_insts(VecNegEntropy()),
    ))
insts["vecext"] = (nothing, vcat(
    nonparametricdistr_insts(VecNegGeomEFExp()),
    nonparametricdistr_insts(VecNegLogEF()),
    nonparametricdistr_insts(VecNegEntropyEF()),
    ))
return (NonparametricDistrJuMP, insts)


text:
"""
    MatrixList

Data type of storing a list of sparse matrices in CSC format.
"""
type MatrixList
    matrices::Array{SparseMatrixCSC, 1}
end
MatrixList() = MatrixList(SparseMatrixCSC[])

"""
    evolving_graph_to_matrices(g)

Convert an evolving graph `g` to a matrix list.

# Example

```jldoctest
julia> using EvolvingGraphs

julia> g = EvolvingGraph()
Directed EvolvingGraph 0 nodes, 0 static edges, 0 timestamps

julia> add_bunch_of_edges!(g, [(1,2,1), (2,4,1), (2,3,1), (3,4,2), (1,3,3)])
Directed EvolvingGraph 4 nodes, 5 static edges, 3 timestamps

julia> ml = evolving_graph_to_matrices(g)
MatrixList (3 matrices)

julia> sparse_adjacency_matrix(ml, 1)
4×4 SparseMatrixCSC{Float64,Int64} with 3 stored entries:
  [1, 2]  =  1.0
  [2, 3]  =  1.0
  [2, 4]  =  1.0

julia> sparse_adjacency_matrix(ml,2)
4×4 SparseMatrixCSC{Float64,Int64} with 1 stored entry:
  [4, 3]  =  1.0
```
"""
function evolving_graph_to_matrices(g::AbstractEvolvingGraph)
    ts = unique_timestamps(g)
    n = length(ts)
    matrices = Array{SparseMatrixCSC{Float64}}(n)

    for (i,t) = enumerate(ts)
        matrices[i] = sparse_adjacency_matrix(g,t)
    end
    MatrixList(matrices)
end

"""
    matrices(ml)

Return a list of matrices in MatrixList `ml`.
"""
matrices(ml::MatrixList) = ml.matrices

"""
    num_matrices(ml)

Return the number of matrices in MatrixList `ml`.
"""
num_matrices(ml::MatrixList) = length(ml.matrices)
sparse_adjacency_matrix(ml::MatrixList, i::Int) = ml.matrices[i]

length(ml::MatrixList) = length(ml.matrices)
Base.start(ml::MatrixList) = 1
Base.next(ml::MatrixList, state) = (ml.matrices[state], state+1)
Base.done(ml::MatrixList, state) = state > length(ml.matrices)

push!(ml::MatrixList, m::SparseMatrixCSC) = (push!(ml.matrices, m); ml)


text:
#=
Here I show the exact correspondence
my spiking implementation and code by
Litwin-Kumar / Auguste Schulz

E-I network
=#

push!(LOAD_PATH, abspath(@__DIR__,".."))

using LinearAlgebra,Statistics,StatsBase,Distributions
using Plots,NamedColors ; theme(:dark)
using SparseArrays
using SpikingRNNs; const global S = SpikingRNNs

function onesparsemat(w::Real)
  return sparse(fill(w,(1,1)))
end

##

# this is a bare-bone version of the Schulz code
function runsimulation_static_simplified(Ne::Integer,Ni::Integer,
  weights::Array{Float64,2},spiketimes::Array{<:Integer,2},
  v_start::Vector{Float64},input_constant::Vector{Float64}; dt = 0.1, T = 2000)

		# Naming convention
		# e corresponds to excitatory
		# i corresponds to inhibitory
		# x corresponds to external

		#membrane dynamics
		taue = 30 #e membrane time constant ms
		taui = 20 #i membrane time constant ms
		vreste = -60 #e resting potential mV
		vresti = -70 #i resting potential mV
		vpeak = 20 #cutoff for voltage.  when crossed, record a spike and reset mV
		eifslope = 2 #eif slope parameter mV
		C = 300 #capacitance pF
		erev = 0 #e synapse reversal potential mV
		irev = -75 #i synapse reversal potntial mV
		vth0 = -52 #initial spike voltage threshold mV
		thrchange = false # can be switched off to have vth constant at vth0
		ath = 10 #increase in threshold post spike mV
		tauth = 30 #threshold decay timescale ms
		vresete = vreste #reset potential mV
    vreseti = vresti
		taurefrac = 20 #absolute refractory period ms

		# total number of neurons
		Ncells = Ne+Ni

    # constant input
    in_const = input_constant

		# synaptic kernel
		tauerise = 1 #e synapse rise time
		tauedecay = 6 #e synapse decay time
		tauirise = .5 #i synapse rise time
		tauidecay = 2 #i synapse decay time

		# Initialisation of spike arrays ------------------------------
		# spike count and spike times
		# Scope problems - changing arrays are therefore defined here directly

		totalspikes = zeros(Int,Ncells)
		totsp::Int64 = 0; # total numberof spikes
		spmax::Int64 = size(spiketimes,1); # maximum recorded spikes

		# further arrays for storing inputs and synaptic integration
		forwardInputsE = zeros(Float64,Ncells) #sum of all incoming weights from excitatpry inputs both external and EE and IE
		forwardInputsI = zeros(Float64,Ncells) #sum of all incoming weights from inhibitory inputs both II and EI
		forwardInputsEPrev = zeros(Float64,Ncells) #as above, for previous timestep
		forwardInputsIPrev = zeros(Float64,Ncells)

		xerise = zeros(Float64,Ncells) #auxiliary variables for E/I currents (difference of exponentials)
		xedecay = zeros(Float64,Ncells)
		xirise = zeros(Float64,Ncells)
		xidecay = zeros(Float64,Ncells)

		v = zeros(Float64,Ncells) #membrane voltage

		# initialisation of membrane potentials and poisson inputs
    copy!(v,v_start)
		# for cc = 1:Ncells
		# 	v[cc] = vreset # + (vth0-vreset)*rand()
		# end

		vth = vth0*ones(Float64,Ncells) #adaptive threshold
		lastSpike = -100*ones(Ncells) #last time the neuron spiked

		# ---------------------------------- set up storing avg weights -----------------

		# total number of simulation steps, steps when to normalise, and save
		Nsteps = round(Int,T/dt)

		# true time
		t::Float64 = 0.0

		# bool counter if a neuron has just had a spike
		spiked = zeros(Bool,Ncells)
  v_out = Matrix{Float64}(undef,Ncells,Nsteps)
	@time	for tt = 1:Nsteps

				if mod(tt,Nsteps/100) == 1  #print percent complete
					print("\r",round(Int,100*tt/Nsteps))
				end

				forwardInputsE[:] .= 0.
				forwardInputsI[:] .= 0.
				t = dt*tt

				fill!(spiked,zero(Bool)) # reset spike bool without new memory allocation

				for cc = 1:Ncells
					xerise[cc] += -dt*xerise[cc]/tauerise + forwardInputsEPrev[cc]
					xedecay[cc] += -dt*xedecay[cc]/tauedecay + forwardInputsEPrev[cc]
					xirise[cc] += -dt*xirise[cc]/tauirise + forwardInputsIPrev[cc]
					xidecay[cc] += -dt*xidecay[cc]/tauidecay + forwardInputsIPrev[cc]

					if cc <= Ne # excitatory
						if thrchange
						vth[cc] += dt*(vth0 - vth[cc])/tauth;
						end
					end

					if t > (lastSpike[cc] + taurefrac) #not in refractory period
						# update membrane voltage

						ge = (xedecay[cc] - xerise[cc])/(tauedecay - tauerise);
						gi = (xidecay[cc] - xirise[cc])/(tauidecay - tauirise);

						if cc <= Ne #excitatory neuron (eif), has adaptation
							dv = (vreste - v[cc] + eifslope*exp((v[cc]-vth[cc])/eifslope))/taue +
                  ge*(erev-v[cc])/C + gi*(irev-v[cc])/C +
                  in_const[cc]/taue
							v[cc] += dt*dv
							if v[cc] > vpeak
								spiked[cc] = true
							end
						else
							dv = (vresti - v[cc])/taui +
                 ge*(erev-v[cc])/C + gi*(irev-v[cc])/C +
                  in_const[cc]/taui
							v[cc] += dt*dv
							if v[cc] > vth0
								spiked[cc] = true
							end
						end

						if spiked[cc] #spike occurred
							spiked[cc] = true;
							v[cc] = cc<=Ne ? vresete : vreseti;
							lastSpike[cc] = t;
							totalspikes[cc] += 1;
							totsp += 1;
							if totsp < spmax
								spiketimes[totsp,1] = tt; # time index as a sparse way to save spiketimes
								spiketimes[totsp,2] = cc; # cell id
							elseif totsp == spmax
								spiketimes[totsp,1] = tt; # time index
								spiketimes[totsp,2] = cc; # cell id

								totsp = 0 # reset counter total number of spikes
							end

							if cc <= Ne && thrchange # only change for excitatory cells and when thrchange == true
								vth[cc] = vth0 + ath;
							end

							#loop over synaptic projections
							for dd = 1:Ncells # postsynaptic cells dd  - cc presynaptic cells
								if cc <= Ne #excitatory synapse
									forwardInputsE[dd] += weights[cc,dd];
								else #inhibitory synapse
									forwardInputsI[dd] += weights[cc,dd];
								end
							end

						end #end if(spiked)
					end #end if(not refractory)
        v_out[cc,tt] = v[cc]
			end #end loop over cells

			forwardInputsEPrev = copy(forwardInputsE)
			forwardInputsIPrev = copy(forwardInputsI)

		end # tt loop over time
	print("\r")

	println("simulation finished")

	return totalspikes,v_out

end

##
const Ttot=1.0
const Ne = 10
const Ni = 10
const Ntot = Ne+Ni
const all_in_e = (60.0-50.0)
const all_in_i = (70.0-50.0)
const all_w = 20.0

weights = let _w =  fill(all_w,(Ntot,Ntot))
   _w .*= rand(size(_w)...) .< 0.3
   _w[diagind(_w)] .= 0.0
   _w
end

v_start = let
  (vmin,vmax) = (-60.0,-51.9)
  rand(Uniform(vmin,vmax),Ntot)
end

vtest = let dt=0.1,
  input = vcat(fill(all_in_e,Ne),fill(all_in_i,Ni))
  _weights = permutedims(weights)
  T=round(Integer,Ttot*1E3)
  spiketimes=Matrix{Int32}(undef,1000,2)
  runsimulation_static_simplified(Ne,Ni,_weights,spiketimes,
  v_start, input; dt=dt, T=T)[2]
end

_ = let neus=[1,3,5]
  plt = plot()
  ts = (1:size(vtest,2)).*0.1E-3
  for neu in neus
    plot!(plt,ts,vtest[neu,:];linewidth=2)
  end
  plt
end

##
# time in seconds, voltage in mV
dt = 0.1E-3
myτe = 30E-3 # seconds
myτi = 20E-3 # seconds
vreste = -60.0
vresti = -70.0
τrefr= 20E-3 # refractoriness
vth_e = 20.   # mV
vthexp = -52.0 # actual threshold for spike-generation
vth_i = vthexp
eifslope = 2.0
v_reset_e = vreste
v_reset_i = vresti
v_rev_e = 0.0
v_rev_i = -75.0
v_leak_e = vreste
v_leak_i = vresti
Cap = 300.0 #capacitance mF

in_const_e = (-50.0 - vreste)*Cap/myτe
in_const_i = (-50.0 - vresti)*Cap/myτi

# synaptic kernel
tauerise = 1E-3 #e synapse rise time
tauedecay = 6E-3 #e synapse decay time
#taueplus,taueminus  = tauerise, tauedecay
taueplus,taueminus  = tauedecay, tauerise
tauirise = 0.5E-3 #i synapse rise time
tauidecay = 2E-3 #i synapse decay time
tauiplus,tauiminus = tauidecay,tauirise

##
nt_e = let sker = S.SKExpDiff(taueplus,taueminus)
  sgen = S.SpikeGenEIF(vthexp,eifslope)
  S.NTLIFConductance(sker,sgen,myτe,Cap,
   vth_e,v_reset_e,vreste,τrefr,v_rev_e)
end
ps_e = S.PSLIFConductance(nt_e,Ne)

nt_i = let sker = S.SKExpDiff(tauiplus,tauiminus)
  sgen = S.SpikeGenNone()
  S.NTLIFConductance(sker,sgen,myτi,Cap,
   vth_i,v_reset_i,vresti,τrefr,v_rev_i)
end
ps_i = S.PSLIFConductance(nt_i,Ni)

# static inputs
ps_input_e = S.PSSimpleInput(S.InputSimpleOffset(in_const_e))
ps_input_i = S.PSSimpleInput(S.InputSimpleOffset(in_const_i))

## connections
conn_ee = let w_ee = weights[1:Ne,1:Ne]
  S.ConnGeneralIF2(sparse(w_ee))
end
conn_ii = let w_ii = weights[Ne+1:end,Ne+1:end]
  S.ConnGeneralIF2(sparse(w_ii))
end
conn_ei = let w_ei = weights[1:Ne,Ne+1:end]
  S.ConnGeneralIF2(sparse(w_ei))
end
conn_ie = let w_ie = weights[Ne+1:end,1:Ne]
  S.ConnGeneralIF2(sparse(w_ie))
end

# input populations
pop_e = S.Population(ps_e,(S.FakeConnection(),ps_input_e),
   (conn_ee,ps_e),(conn_ei,ps_i) )
pop_i = S.Population(ps_i,(S.FakeConnection(),ps_input_i),
   (conn_ii,ps_i),(conn_ie,ps_e) )

##
# that's it, let's make the network
myntw = S.RecurrentNetwork(dt,pop_e,pop_i)

# record spiketimes and internal potential
krec = 1
rec_state_e = S.RecStateNow(ps_e,krec,dt,Ttot)
rec_spikes_e = S.RecSpikes(ps_e,100.0,Ttot)
rec_state_i = S.RecStateNow(ps_i,krec,dt,Ttot)
rec_spikes_i = S.RecSpikes(ps_i,100.0,Ttot)

## Run

times = (0:myntw.dt:Ttot)
nt = length(times)
# clean up
S.reset!.([rec_state_e,rec_spikes_e])
S.reset!.([rec_state_i,rec_spikes_i])
S.reset!.([ps_e,ps_i])
S.reset!.([conn_ee,conn_ii,conn_ie,conn_ei])
# initial conditions
ps_e.state_now .= v_start[1:Ne]
ps_i.state_now .= v_start[Ne+1:end]

@time begin
  for (k,t) in enumerate(times)
    rec_state_e(t,k,myntw)
    rec_spikes_e(t,k,myntw)
    rec_state_i(t,k,myntw)
    rec_spikes_i(t,k,myntw)
    S.dynamics_step!(t,myntw)
  end
end

S.add_fake_spikes!(0.5vth_e,rec_state_e,rec_spikes_e)
S.add_fake_spikes!(0.0,rec_state_i,rec_spikes_i)
##

_ = let plt=plot()
  neu = 1
  plot!(plt,rec_state_e.times,rec_state_e.state_now[neu,:];linewidth=2,leg=false)
  ts = (1:size(vtest,2)).*0.1E-3
  plot!(plt,ts,vtest[neu,:];linewidth=2,linestyle=:dash)
end

_ = let plt=plot()
  neu = 1
  plot!(plt,rec_state_i.times,rec_state_i.state_now[neu,:];linewidth=2,leg=false)
  ts = (1:size(vtest,2)).*0.1E-3
  plot!(plt,ts,vtest[Ne+neu,:];linewidth=2,linestyle=:dash)
end


text:
function act_2_deck(cur_T_bar::AbstractSymbol=symbols(:T_bar); cur_kwargs...)
  cur_dict = Dict(
    :deck => :act_2,
    :T_bar => cur_T_bar,
    :is_pulsed => false,
    :H => 1.22,
    :Q => 25.0,
    :epsilon => 0.25,
    :kappa_95 => 1.964,
    :delta_95 => 0.42,
    :nu_n => 0.41,
    :nu_T => 1.15,
    :Z_eff => 2.12,
    :f_D => 0.74,

    :A => 2.5,
    :l_i => 0.60275,
    # :rho_m => 0.0,

    :N_G => 1.3,
    :eta_T => 0.44,
    :eta_RF => 0.4,

    :max_beta_N => 0.026,
    :max_q_95 => 2.5,

    :max_P_E => 4000.0,

    :max_P_W => 1.46,
    :max_q_DV => 10.0,

    :B_CS => 12.77,
    :k => 0.333,
    :sigma_CS => 660,
    :sigma_TF => 660,
    :J_CS => 50,
    :J_TF => 230,
    :C_ejima => 0.3,
    :tau_FT => 1.6e9,

    :eta_CD => 0.256,
    :wave_theta => 135.0,
    :eta_LH => 0.6
  )

  merge!(cur_dict, Dict(cur_kwargs))

  BaseReactor(cur_dict)
end


text:
# This file is auto-generated by AWSMetadata.jl
using AWS
using AWS.AWSServices: sagemaker_runtime

using AWS.Compat
using AWS.UUIDs
"""
    InvokeEndpoint()

After you deploy a model into production using Amazon SageMaker hosting services, your client applications use this API to get inferences from the model hosted at the specified endpoint.  For an overview of Amazon SageMaker, see How It Works.  Amazon SageMaker strips all POST headers except those supported by the API. Amazon SageMaker might add additional headers. You should not rely on the behavior of headers outside those enumerated in the request syntax.  Calls to InvokeEndpoint are authenticated by using AWS Signature Version 4. For information, see Authenticating Requests (AWS Signature Version 4) in the Amazon S3 API Reference. A customer's model containers must respond to requests within 60 seconds. The model itself can have a maximum processing time of 60 seconds before responding to the /invocations. If your model is going to take 50-60 seconds of processing time, the SDK socket timeout should be set to be 70 seconds.  Endpoints are scoped to an individual account, and are not public. The URL does not contain the account ID, but Amazon SageMaker determines the account ID from the authentication token that is supplied by the caller.

# Required Parameters
- `Body`: Provides input data, in the format specified in the ContentType request header. Amazon SageMaker passes all of the data in the body to the model.  For information about the format of the request body, see Common Data Formats-Inference.
- `EndpointName`: The name of the endpoint that you specified when you created the endpoint using the CreateEndpoint API.

# Optional Parameters
- `Accept`: The desired MIME type of the inference in the response.
- `Content-Type`: The MIME type of the input data in the request body.
- `X-Amzn-SageMaker-Custom-Attributes`: Provides additional information about a request for an inference submitted to a model hosted at an Amazon SageMaker endpoint. The information is an opaque value that is forwarded verbatim. You could use this value, for example, to provide an ID that you can use to track a request or to provide other metadata that a service endpoint was programmed to process. The value must consist of no more than 1024 visible US-ASCII characters as specified in Section 3.3.6. Field Value Components of the Hypertext Transfer Protocol (HTTP/1.1). This feature is currently supported in the AWS SDKs but not in the Amazon SageMaker Python SDK.
- `X-Amzn-SageMaker-Target-Model`: The model to request for inference when invoking a multi-model endpoint.
- `X-Amzn-SageMaker-Target-Variant`: Specify the production variant to send the inference request to when invoking an endpoint that is running two or more variants. Note that this parameter overrides the default behavior for the endpoint, which is to distribute the invocation traffic based on the variant weights.
"""

invoke_endpoint(Body, EndpointName; aws_config::AWSConfig=global_aws_config()) = sagemaker_runtime("POST", "/endpoints/$(EndpointName)/invocations", Dict{String, Any}("Body"=>Body); aws_config=aws_config)
invoke_endpoint(Body, EndpointName, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = sagemaker_runtime("POST", "/endpoints/$(EndpointName)/invocations", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("Body"=>Body), args)); aws_config=aws_config)


text:
module Maxwell2D
export advance_fields!

function advance_fields!()
end

end


text:
using DiffEqJump, DiffEqBase
using Test, LinearAlgebra
using StableRNGs
rng = StableRNG(12345)

function regular_rate(out,u,p,t)
    out[1] = (0.1/1000.0)*u[1]*u[2]
    out[2] = 0.01u[2]
end

function regular_c(dc,u,p,t,mark)
    dc[1,1] = -1
    dc[2,1] = 1
    dc[2,2] = -1
    dc[3,2] = 1
end

dc = zeros(3,2)

rj = RegularJump(regular_rate,regular_c,dc;constant_c=true)
jumps = JumpSet(rj)

prob = DiscreteProblem([999.0,1.0,0.0],(0.0,250.0))
jump_prob = JumpProblem(prob,Direct(),rj; rng=rng)
sol = solve(jump_prob,SimpleTauLeaping();dt=1.0)

const _dc = zeros(3, 2)
dc[1,1] = -1
dc[2,1] = 1
dc[2,2] = -1
dc[3,2] = 1

function regular_c(du,u,p,t,counts,mark)
    mul!(du,dc,counts)
end

rj = RegularJump(regular_rate,regular_c,2)
jumps = JumpSet(rj)
prob = DiscreteProblem([999,1,0],(0.0,250.0))
jump_prob = JumpProblem(prob,Direct(),rj; rng=rng)
sol = solve(jump_prob,SimpleTauLeaping();dt=1.0)


text:
module GradChecker

export ngradient, gradcheck

using LinearAlgebra

function ngradient(f, x, is = 1:length(x); δ = 1e-2)
    Δ = zero(x)
    for i in is
        tmp = x[i]
        x[i] = tmp - δ / 2
        y1 = f(x)
        x[i] = tmp + δ / 2
        y2 = f(x)
        x[i] = tmp
        Δ[i] = (y2 - y1) / δ
    end
    return Δ
end

function gradcheck(f, θ, is = 1:length(θ))
    g = f'(θ)[is]
    g′ = ngradient(f, θ, is)[is]
    err = norm(g .- g′) / norm(g)
    return g, g′, err
end

end


text:
struct LineIndex{T}
    buf::Vector{UInt8}
    filestart::Int
    fileend::Int
    lineindex::Vector{Int}
    names::Vector{Symbol}
    lookup::Dict
    rowtype
    columntypes::OrderedDict
    structtype::Union{Nothing, DataType}
    nworkers::Int
end

function LineIndex(buf::Vector{UInt8}, filestart::Int = 0, skip::Int = 0, nrows::Int = typemax(Int), structtype = nothing, schemafrom = 1:10, nworkers::Int = 1)
    fileend = lastindex(buf)
    filestart = skip > 0 ? skiprows(buf, fileend, skip, filestart) : filestart
    if filestart == fileend
        @warn "Skipped all lines"
        return LineIndex{Missing}(buf, filestart, fileend, Int[0], Symbol[], Dict{Int, Symbol}(),  UnionAll, OrderedDict{Symbol, Union{DataType, UnionAll}}(), nothing, nworkers)
    end
    if nworkers > 1
        if nrows < typemax(Int)
            @warn "Cannot index a subset of rows with multiple threads"
            lineindex = indexrows(buf, fileend, nrows, filestart)
        else
            lineindex = tindexrows(buf, fileend, nworkers)
        end
    else
        lineindex = indexrows(buf, fileend, nrows, filestart)
    end
    row = parserow(@inbounds(@view(buf[rowindex(lineindex, 1)])), structtype)
    rowtype = typeof(row)
    isarray = rowtype <: JSON3.Array
    if isarray
        rowtype = JSON3.Array{T, SubArray{UInt8,1,Array{UInt8,1},Tuple{UnitRange{Int}},true},Array{UInt64,1}} where T
        names = Symbol.(row)
        lineindex = lineindex[2:end]
        row = parserow(@inbounds(@view(buf[rowindex(lineindex, 1)])), structtype)
        lookup = Dict(names .=> 1:length(names))
        typelookup = OrderedDict{Symbol, DataType}() # TODO Implement checking
    else
        typelookup = OrderedDict()
        lookup = Dict{Int, Symbol}()
        checkrows = (length(lineindex)-1) > last(schemafrom) ? schemafrom : first(schemafrom):(length(lineindex)-1)
        names = Symbol[]
        for i in checkrows # todo make input
            crow = parserow(@inbounds(@view(buf[rowindex(lineindex, i)])), structtype)
            colindex = 0
            cnames = propertynames(crow)
            cvals = [getproperty(crow, name) for name in cnames]
            for (k, v) in zip(cnames, cvals)
                colindex += 1
                if haskey(typelookup, k)
                    typelookup[k] = promote_type(typelookup[k], typeof(v))
                else
                    push!(names, k)
                    lookup[colindex] = k
                    typelookup[k] = typeof(v)
                end
            end
        end
    end
    return LineIndex{rowtype}(buf, filestart, fileend, lineindex, names, lookup, rowtype, typelookup, structtype, nworkers)
end

"""
    LineIndex(path::String; filestart::Int = 0, skip::Int = 0, nrows::Int = typemax(Int), structtype = nothing, schemafrom::UnitRange{Int} = 1:10, nworkers::Int = 1)

Create an index of a JSONLines file at `path`
* Keyword Arguments:
    * `filestart=0`: Number of bytes to skip before reading the file
    * `skip=0`: Number of rows to skip before parsing
    * `nrows=typemax(Int)`: Maximum number of rows to index
    * `structtype=nothing`: `StructType` passed to `JSON3.read` for each row
    * `schemafrom=1:10`: Rows to parse initially to determine columnnames and columntypes
    * `nworkers=1`: Number of threads to use for operations on the `LineIndex`
"""
LineIndex(path::String; filestart::Int = 0, skip::Int = 0, nrows::Int = typemax(Int), structtype = nothing, schemafrom::UnitRange{Int} = 1:10, nworkers::Int = 1) = LineIndex(Mmap.mmap(path), filestart, skip, nrows, structtype, schemafrom, nworkers)

## Materialize
"""
    materialize(lines::LineIndex, rows::Union{UnitRange{Int}, Vector{Int}} = 1:length(lines))

Return a `Vector{NamedTuple}` of the `rows` selected. Similar to `Tables.rowtable`
"""
function materialize(lines::LineIndex, rows::Union{UnitRange{Int}, Vector{Int}} = 1:length(lines))
    if lines.nworkers > 1
        return _tmaterialize(lines.buf, lines.lineindex, rows, tuple(columnnames(lines)...), lines.structtype, lines.nworkers)
    else
        return _materialize(lines.buf, lines.lineindex, rows, tuple(columnnames(lines)...), lines.structtype)
    end
end

"""
    materialize(lines::LineIndex,  f::Function, rows::Union{UnitRange{Int}, Vector{Int}} = 1:length(lines); eltype = T where T)

Apply `f` to `rows` selected. `eltype` of result can be specified as keyword argument.
"""
function materialize(lines::LineIndex,  f::Function, rows::Union{UnitRange{Int}, Vector{Int}} = 1:length(lines); eltype = T where T)
    if lines.nworkers > 1
        return _ftmaterialize(lines.buf, f, eltype, lines.lineindex, rows, lines.structtype, lines.nworkers)
    else
        return _fmaterialize(lines.buf, f, eltype, lines.lineindex, rows, lines.structtype)
    end
end

## Columnwise
"""
    columnwise(lines::LineIndex; coltypes = lines.columntypes)

Parse `lines` to columnwise vectors. Similar to `Tables.columntable`
"""
function columnwise(lines::LineIndex; coltypes = lines.columntypes)
    _columnwise(lines, coltypes)
end

"""
    gettypes(lines::LineIndex, rows = 1:5)

Infer types of columns in `lines` based on `rows` selected. Returns `Dict` of types.
"""
function gettypes(lines::LineIndex, rows = 1:5)
    vals = lines[rows]
    lookup = Dict{Symbol, DataType}()
    for row in vals
        cnames = propertynames(row)
        cvals = [getproperty(row, name) for name in cnames]
        for (k, v) in zip(cnames, cvals)
            if haskey(lookup, k)
                lookup[k] = promote_type(lookup[k], typeof(v))
            else
                lookup[k] = typeof(v)
            end
        end
    end
    return lookup
end

"""
    settypes!(lines::LineIndex, rows::Union{UnitRange{Int}, Vector{Int}} = 1:5)

Infer types of columns in `lines` based on `rows` selected. Overwrites existing types.
"""
function settypes!(lines::LineIndex, rows::Union{UnitRange{Int}, Vector{Int}} = 1:5)
    newtypes = gettypes(lines, rows)
    for (k, v) in newtypes
        lines.columntypes[k] = v
    end
end

"""
    columntypes(lines::LineIndex)

Returns current value of columntypes of lines.
"""
columntypes(lines::LineIndex) = lines.columntypes

"""
    settype!(lines::LineIndex, p::Union{Pair{Symbol, DataType},Pair{Symbol, UnionAll}, Pair{Symbol, Union}})

Set a single columntype using `:col => Type`.
"""
function settype!(lines::LineIndex, p::Union{Pair{Symbol, DataType},Pair{Symbol, UnionAll}, Pair{Symbol, Union}})
    lines.columntypes[p[1]] = p[2]
end

"""
    settypes!(lines::LineIndex, d::Union{Dict{Symbol, DataType}, Dict{Symbol, UnionAll}, Dict{Symbol, Union}})

Manually set types for columns. `d` is a `Dict` in which keys are `Symbol`s corresponding to columnnames and values are the datatypes.
"""
function settypes!(lines::LineIndex, d::Union{Dict{Symbol, DataType}, Dict{Symbol, UnionAll}, Dict{Symbol, Union}})
    for (k, v) in d
        settype!(lines, k => v)
    end
end

## Filter
"""
    filter(f::Function, lines::LineIndex)

Return rows of `lines` for which `f` evaluates to `true`
"""
function Base.filter(f::Function, lines::LineIndex)
    if lines.nworkers > 1
        return _tfilter(lines.buf, f, lines.rowtype, lines.lineindex, lines.structtype, lines.nworkers)
    else
        return _filter(lines.buf, f, lines.rowtype, lines.lineindex, lines.structtype)
    end
end

## Find
"""
    findall(f::Function, lines::LineIndex)

Return indices of `lines` for which `f` evaluates to `true`
"""
function Base.findall(f::Function, lines::LineIndex)
    if lines.nworkers > 1
        return _tfindall(lines.buf, f, lines.lineindex, lines.nworkers, lines.structtype)
    else
        return _findall(lines, f)
    end
end

"""
    findnext(f::Function, lines::LineIndex, i::Int)

Return index of next row for which `f` returns `true` starting at row `i`
"""
function Base.findnext(f::Function, lines::LineIndex, i::Int)
    return _findnext(lines, f, i)
end

"""
    findnext(f::Function, lines::LineIndex, i::Int)

Return index of previous row for which `f` returns `true` starting at row `i`
"""
function Base.findprev(f::Function, lines::LineIndex, i::Int)
    return _findprev(lines, f, i)
end

"""
    findfirst(f::Function, lines::LineIndex)

Return index of first row for which `f` returns `true`
"""
Base.findfirst(f::Function, lines::LineIndex) = _findnext(lines, f, 1)

"""
    findlast(f::Function, lines::LineIndex)

Return index of last row for which `f` returns `true`
"""
Base.findlast(f::Function, lines::LineIndex) = _findprev(lines, f, length(lines))

## Iteration interface
function Base.iterate(lines::LineIndex, state::Int = 1; parsed::Bool = true)
    state > length(lines) && (return nothing)
    ret = @inbounds(@view(lines.buf[rowindex(lines.lineindex, state)]))
    return (parsed ? parserow(ret, lines.structtype) : ret, state + 1)
end

function Base.length(lines::LineIndex)
    return length(lines.lineindex) - 1
end

function Base.eltype(lines::LineIndex{T}) where T
    return T
end

Base.IteratorSize(::Type{LineIndex}) = Base.HasLength()

## Indexing  interface
Base.size(lines::LineIndex) = (length(lines), length(columnnames(lines)))

@inline function Base.getindex(lines::LineIndex, i::Int; parsed::Bool = true)
    @boundscheck checkbounds(lines, i)
    ret = @inbounds(@view(lines.buf[rowindex(lines.lineindex, i)]))
    return parsed ? parserow(ret, lines.structtype) : ret
end

## Int, Symbol
Base.getindex(lines::LineIndex, i::Int, col::Symbol) = getproperty(lines[i], col)
Base.getindex(lines::LineIndex{T}, i::Int, col::Symbol) where T <: JSON3.Array = lines[i][lines.lookup[col]]

## Int, Int
Base.getindex(lines::LineIndex, I::Vararg{Int, 2}) = getproperty(lines[I[1]], lines.lookup[I[2]])
Base.getindex(lines::LineIndex{T}, I::Vararg{Int,2}) where T <: JSON3.Array = lines[I[1]][I[2]]

## UnitRange
@inline function Base.getindex(lines::LineIndex, r::UnitRange{Int})
    @boundscheck checkbounds(lines, last(r))
    return parserows(lines.buf, lines.lineindex, r, lines.rowtype, lines.structtype, lines.nworkers)
end

## UnitRange, Symbol
function Base.getindex(lines::LineIndex, r::UnitRange{Int}, col::Symbol)
    return promote_type.(materialize(lines, x->x[col], r))
end
function Base.getindex(lines::LineIndex{T}, r::UnitRange{Int}, col::Symbol) where T <: JSON3.Array
    rows = getindex(lines, r)
    return [row[lines.lookup[col]] for row in rows]
end

## UnitRang, Int
Base.getindex(lines::LineIndex, r::UnitRange{Int}, col::Int) = getindex(lines, r, lines.lookup[col])
Base.getindex(lines::LineIndex{T}, r::UnitRange{Int}, col::Int) where T <: JSON3.Array = [l[col] for l in getindex(lines, r)]

## Vector
@inline function Base.getindex(lines::LineIndex, r::Vector{Int})
    @boundscheck checkbounds(lines, maximum(r))
    return parserows(lines.buf, lines.lineindex, r, lines.rowtype, lines.structtype, lines.nworkers)
end

## Vector, Symbol
function Base.getindex(lines::LineIndex, r::Vector{Int}, col::Symbol)
    rows = getindex(lines, r)
    return @. getproperty(rows, col)
end
function Base.getindex(lines::LineIndex{T}, r::Vector{Int}, col::Symbol) where T <: JSON3.Array
    rows = getindex(lines, r)
    return [row[lines.lookup[col]] for row in rows]
end

## Vector, Int
Base.getindex(lines::LineIndex, r::Vector{Int}, i::Int) = getindex(lines, r, lines.lookup[i])
Base.getindex(lines::LineIndex{T}, r::Vector{Int}, col::Int) where T <: JSON3.Array = [l[col] for l in getindex(lines, r)]

## Colon, Symbol
Base.getindex(lines::LineIndex, c::Colon, col::Symbol) = lines[1:end, col]

## Symbol
Base.getindex(lines::LineIndex, col::Symbol) = lines[:, col]

##
Base.IndexStyle(::Type{LineIndex{T}}) where T = Base.IndexLinear()
Base.firstindex(lines::LineIndex) = 1
Base.lastindex(lines::LineIndex) = length(lines)
Base.lastindex(lines::LineIndex, i::Int) = length(lines)

Base.checkbounds(lines::LineIndex, i::Int) = i > length(lines) && throw(BoundsError(lines,i))
function Base.summary(io::IO, lines::LineIndex)
    print(io, Base.dims2string(size(lines)), " ")
    Base.showarg(io, lines, true)
end

## Show
function Base.summary(lines::LineIndex)
    io = IOBuffer()
    summary(io, lines)
    String(take!(io))
end

Base.show(io::IO, ::MIME"text/plain", lines::LineIndex{Missing}) = summary(io, lines)

function Base.show(io::IO,  ::MIME"text/plain", lines::LineIndex)
    summary(io,lines)
    try
    print(io, ":\n")
    scrsz = displaysize(io)[1] -4
    if scrsz <= 0
        print(" \t \u22ee")
        return
    end
    if length(lines) < scrsz
        for i in 1:length(lines)
            print(materialize(lines, i:i)[1])
            i!=length(lines) && print('\n')
        end
    end
    halfd = div(scrsz,2)
    uphalfd = scrsz - halfd
    if length(lines) > scrsz
        for i in 1:(uphalfd-1)
            print(materialize(lines, i:i)[1])
            print('\n')
        end
        print(" \t \u22ee \n")
        for i in 2:halfd
            print(materialize(lines, (lastindex(lines)-halfd + i):(lastindex(lines)-halfd + i))[1])
            i != halfd && print('\n')
        end
    end
    catch e
    end
end

function Base.show(io::IO,  ::MIME"text/plain", lines::LineIndex{T}) where T <: JSON3.Array
    summary(io,lines)
    try
    print(io, ":\n")
    scrsz = displaysize(io)[1] -2
    if scrsz -2 <= 0
        print("  \u22ee      \u22f1  ")
        return
    end
    halfd = div(scrsz,2)
    uphalfd = scrsz - halfd
    if length(lines) <= scrsz
        x = Matrix(undef, length(lines) + 1, size(lines)[2])
        x[1, :] = columnnames(lines)
        for i in 2:length(lines)+1
            x[i, :] = lines[i-1]
        end
        Base.print_matrix(io, x, "    ", "\t ")
        return nothing
    end
    indices = [1:uphalfd-2..., lastindex(lines) - halfd .+ collect(2:halfd)...]
    ll = lines[indices]
    x = Matrix(undef, scrsz-1, size(lines)[2])
    x[1, :] = columnnames(lines)
    for i in 2:(uphalfd-1)
        vals = ll[i-1]
        x[i, :] = vals
    end
    for i in 2:(halfd)
        vals =  ll[end-halfd+i]
        x[i+uphalfd-1, :] = vals
    end
    Base.print_matrix(io, x, "  ", "  ")
    catch e
    end
end

"""
    columnnames(lines::LineIndex)

Returns the columnnames of the LineIndex
"""
columnnames(lines::LineIndex) = lines.names

## Tables.jl interface

Tables.isrowtable(lines::LineIndex) = true
Tables.rowtable(lines::LineIndex) = Tables.rowtable(materialize(lines))
Tables.columntable(lines::LineIndex) = Tables.columntable(columnwise(lines))
#Tables.getcolumn(row::JSON3.Object, ::Type{T}, i::Int, nm::Symbol) where T = T(row[nm])


text:
using Test
using Random
using UnitTestDesign

### argmin_rand
ar_cases = [
    [[1,2,3,1,4], [1,4]],
    [[-5, -7, -6, -3], [2]],
    [[0, -1, -2, -2], [3, 4]],
    [[1,1,1,1], [1,2,3,4]]
]
ar_rng = MersenneTwister(342234)
for ar_case in ar_cases
    values = Set{Int}()
    for i in 1:100
        idx = UnitTestDesign.argmin_rand(ar_rng, ar_case[1])
        push!(values, idx)
    end
    res = sort([x for x in values])
    @test res == ar_case[2]
end

n_way = 2
M = 50
rng = MersenneTwister(97072343)
arity = [5, 4, 3, 2, 2]
cover = UnitTestDesign.n_way_coverage_filter(arity, n_way, x -> x[3] == 1 && x[4] != 1, [], M, rng)
nn = UnitTestDesign.n_way_coverage([4,4,4,4,4,4,4,4,4], 2, M, rng)
@test length(nn) > 20
@test length(nn) < 100

### n_way_coverage_multi
rng = MersenneTwister(789607)
M = 50
arity = [2,3,4,2,2,3]
n_way = 2
high_indices = [1,3,4,5]
mwc = UnitTestDesign.multi_way_coverage(arity, Dict(3 => [high_indices]), n_way)
@test maximum(sum(mwc .!= 0, dims = 1)) == 3
mc = UnitTestDesign.MatrixCoverage(mwc, size(mwc, 1), arity)
cov = UnitTestDesign.n_way_coverage_multi(mc, x->false, zeros(length(arity), 0), M, rng)
cov_mat = vcat([cv' for cv in cov]...)
cov_high = cov_mat[:, high_indices]
singles = unique(sort(cov_mat[:, high_indices], dims = 1), dims = 1)

cov_high_list = [cov_high[idx, :] for idx in 1:size(cov_high, 1)]
accumulated = UnitTestDesign.tuples_in_trials(cov_high_list, 3)
tuple_cnt = UnitTestDesign.coverage_by_tuple(cov_high_list, 3)
# This is it. The total number of 3-way tuples should match straight
# count of combinations of the four items for 3-way.
@test tuple_cnt == UnitTestDesign.total_combinations(arity[high_indices], 3)

### n_way_coverage
rng = Random.MersenneTwister(9234724)
arity = [2,3,2,3]
n_way = 2

M = 50
cover = UnitTestDesign.n_way_coverage(arity, n_way, M, rng)
tuple_cnt = UnitTestDesign.coverage_by_tuple(cover, n_way)
@test tuple_cnt == UnitTestDesign.total_combinations(arity, n_way)

n_way = 3
cover = UnitTestDesign.n_way_coverage(arity, n_way, M, rng)
tuple_cnt = UnitTestDesign.coverage_by_tuple(cover, n_way)
@test tuple_cnt == UnitTestDesign.total_combinations(arity, n_way)

cover = UnitTestDesign.n_way_coverage_init(arity, n_way, zeros(length(arity), 0), M, rng)
tuple_cnt = UnitTestDesign.coverage_by_tuple(cover, n_way)
@test tuple_cnt == UnitTestDesign.total_combinations(arity, n_way)

rng = Random.MersenneTwister(9234724)
arity = [2,3,2,3]
n_way = 3

M = 50
seed = [
    1 1 1 1;
    1 3 1 1;
    2 2 2 2;
    2 1 2 1
]
cover = UnitTestDesign.n_way_coverage_init(arity, n_way, seed, M, rng)
tuple_cnt = UnitTestDesign.coverage_by_tuple(cover, n_way)
# It is possible for this to fail randomly.
for sti in 1:size(seed, 2)
    @test seed[:, sti] in cover
end


text:
module REPL

using Base.Meta
using ..Terminals
using ..LineEdit
using ..REPLCompletions

export
    BasicREPL,
    LineEditREPL,
    StreamREPL

import Base:
    AsyncStream,
    Display,
    display,
    writemime

import ..LineEdit:
    CompletionProvider,
    HistoryProvider,
    add_history,
    complete_line,
    history_next,
    history_next_prefix,
    history_prev,
    history_prev_prefix,
    history_search

abstract AbstractREPL

answer_color(::AbstractREPL) = ""

type REPLBackend
    repl_channel::RemoteRef
    response_channel::RemoteRef
    ans
end

function eval_user_input(ast::ANY, backend::REPLBackend)
    iserr, lasterr, bt = false, (), nothing
    while true
        try
            if iserr
                put!(backend.response_channel, (lasterr, bt))
                iserr, lasterr = false, ()
            else
                ast = expand(ast)
                ans = backend.ans
                # note: value wrapped in a non-syntax value to avoid evaluating
                # possibly-invalid syntax (issue #6763).
                eval(Main, :(ans = $({ans})[1]))
                value = eval(Main, ast)
                backend.ans = value
                put!(backend.response_channel, (value, nothing))
            end
            break
        catch err
            if iserr
                println("SYSTEM ERROR: Failed to report error to REPL frontend")
                println(err)
            end
            iserr, lasterr = true, err
            bt = catch_backtrace()
        end
    end
end

function parse_input_line(s::String)
    # s = bytestring(s)
    # (expr, pos) = parse(s, 1)
    # (ex, pos) = ccall(:jl_parse_string, Any,
    #                   (Ptr{Uint8},Int32,Int32),
    #                   s, int32(pos)-1, 1)
    # if !is(ex,())
    #     throw(ParseError("extra input after end of expression"))
    # end
    # expr
    ccall(:jl_parse_input_line, Any, (Ptr{Uint8},), s)
end

function start_repl_backend(repl_channel::RemoteRef, response_channel::RemoteRef)
    backend = REPLBackend(repl_channel, response_channel, nothing)
    @async begin
        # include looks at this to determine the relative include path
        # nothing means cwd
        while true
            tls = task_local_storage()
            tls[:SOURCE_PATH] = nothing
            ast, show_value = take!(backend.repl_channel)
            if show_value == -1
                # exit flag
                break
            end
            eval_user_input(ast, backend)
        end
    end
    backend
end

function display_error(io::IO, er, bt)
    Base.with_output_color(:red, io) do io
        print(io, "ERROR: ")
        Base.showerror(io, er, bt)
    end
end

immutable REPLDisplay{R<:AbstractREPL} <: Display
    repl::R
end
function display(d::REPLDisplay, ::MIME"text/plain", x)
    io = outstream(d.repl)
    write(io, answer_color(d.repl))
    writemime(io, MIME("text/plain"), x)
    println(io)
end
display(d::REPLDisplay, x) = display(d, MIME("text/plain"), x)

function print_response(repl::AbstractREPL, val::ANY, bt, show_value::Bool, have_color::Bool)
    repl.waserror = bt !== nothing
    print_response(outstream(repl), val, bt, show_value, have_color)
end
function print_response(errio::IO, val::ANY, bt, show_value::Bool, have_color::Bool)
    while true
        try
            if bt !== nothing
                display_error(errio, val, bt)
                println(errio)
                iserr, lasterr = false, ()
            else
                if val !== nothing && show_value
                    try
                        display(val)
                    catch err
                        println(errio, "Error showing value of type ", typeof(val), ":")
                        rethrow(err)
                    end
                end
            end
            break
        catch err
            if bt !== nothing
                println(errio, "SYSTEM: show(lasterr) caused an error")
                break
            end
            val = err
            bt = catch_backtrace()
        end
    end
end

# A reference to a backend
immutable REPLBackendRef
    repl_channel::RemoteRef
    response_channel::RemoteRef
end

function run_repl(repl::AbstractREPL)
    repl_channel = RemoteRef()
    response_channel = RemoteRef()
    start_repl_backend(repl_channel, response_channel)
    run_frontend(repl, REPLBackendRef(repl_channel,response_channel))
end

## BasicREPL ##

type BasicREPL <: AbstractREPL
    terminal::TextTerminal
    waserror::Bool
    BasicREPL(t) = new(t,false)
end

outstream(r::BasicREPL) = r.terminal

function run_frontend(repl::BasicREPL, backend::REPLBackendRef)
    d = REPLDisplay(repl)
    pushdisplay(d)
    repl_channel, response_channel = backend.repl_channel, backend.response_channel
    while true
        write(repl.terminal, "julia> ")
        line = ""
        ast = nothing
        while true
            line *= readline(repl.terminal)
            ast = Base.parse_input_line(line)
            (isa(ast,Expr) && ast.head == :incomplete) || break
        end
        if !isempty(line)
            put!(repl_channel, (ast, 1))
            val, bt = take!(response_channel)
            if !ends_with_semicolon(line)
                print_response(repl, val, bt, true, false)
            end
        end
        write(repl.terminal, '\n')
    end
    # terminate backend
    put!(repl_channel, (nothing, -1))
    popdisplay(d)
end

## LineEditREPL ##

type LineEditREPL <: AbstractREPL
    t::TextTerminal
    prompt_color::String
    input_color::String
    answer_color::String
    shell_color::String
    help_color::String
    no_history_file::Bool
    in_shell::Bool
    in_help::Bool
    consecutive_returns::Int
    waserror::Bool
    interface
    backendref::REPLBackendRef
    LineEditREPL(t,prompt_color,input_color,answer_color,shell_color,help_color,no_history_file,in_shell,in_help) =
        new(t,prompt_color,input_color,answer_color,shell_color,help_color,no_history_file,in_shell,in_help,0,false)
end
outstream(r::LineEditREPL) = r.t

LineEditREPL(t::TextTerminal) =  LineEditREPL(t, julia_green,
                                              Base.input_color(),
                                              Base.answer_color(),
                                              Base.text_colors[:red],
                                              Base.text_colors[:yellow],
                                              false, false, false)

type REPLCompletionProvider <: CompletionProvider
    r::LineEditREPL
end

type ShellCompletionProvider <: CompletionProvider
    r::LineEditREPL
end

bytestring_beforecursor(buf::IOBuffer) = bytestring(pointer(buf.data), buf.ptr-1)

function complete_line(c::REPLCompletionProvider, s)
    partial = bytestring_beforecursor(s.input_buffer)
    ret, range, should_complete = completions(partial, endof(partial))
    return ret, partial[range], should_complete
end

function complete_line(c::ShellCompletionProvider, s)
    # First parse everything up to the current position
    partial = bytestring_beforecursor(s.input_buffer)
    ret, range, should_complete = shell_completions(partial, endof(partial))
    return ret, partial[range], should_complete
end

type REPLHistoryProvider <: HistoryProvider
    history::Array{String,1}
    history_file
    cur_idx::Int
    last_idx::Int
    last_buffer::IOBuffer
    last_mode
    mode_mapping
    modes::Array{Symbol,1}
end
REPLHistoryProvider(mode_mapping) =
    REPLHistoryProvider(String[], nothing, 0, -1, IOBuffer(),
                        nothing, mode_mapping, Uint8[])

const invalid_history_message = """
Invalid history format. If you have a ~/.julia_history file left over from an older version of Julia, try renaming or deleting it.
"""

function hist_from_file(hp, file)
    hp.history_file = file
    seek(file, 0)
    while !eof(file)
        mode = :julia
        line = utf8(readline(file))
        line[1] == '#' || error(invalid_history_message)
        while true
            m = match(r"^#\s*(\w+)\s*:\s*(.*?)\s*$", line)
            m == nothing && break
            if m.captures[1] == "mode"
                mode = symbol(m.captures[2])
            end
            line = utf8(readline(file))
        end
        line[1] == '\t' || error(invalid_history_message)
        lines = UTF8String[]
        while true
            push!(lines, chomp(line[2:end]))
            eof(file) && break
            Base.peek(file) == '\t' || break
            line = utf8(readline(file))
        end
        push!(hp.modes, mode)
        push!(hp.history, join(lines, '\n'))
    end
    seekend(file)
    hp
end

function mode_idx(hist::REPLHistoryProvider, mode)
    c = :julia
    for (k,v) in hist.mode_mapping
        v == mode && (c = k)
    end
    return c
end

function add_history(hist::REPLHistoryProvider, s)
    # bytestring copies
    str = bytestring(s.input_buffer)
    if isempty(strip(str)) || # Do not add empty strings to the history
       (length(hist.history) > 0 && str == hist.history[end]) # Do not add consecutive duplicate entries
        return
    end
    push!(hist.history, str)
    mode = mode_idx(hist, LineEdit.mode(s))
    push!(hist.modes, mode)
    hist.history_file == nothing && return
    println(hist.history_file, "# mode: $mode")
    println(hist.history_file, "# time: $(strftime("%F %T %Z", time()))")
    println(hist.history_file, replace(str, r"^"ms, "\t"))
    flush(hist.history_file)
end

function history_move(s::LineEdit.MIState, hist::REPLHistoryProvider, idx::Int)
    max_idx = length(hist.history) + 1
    @assert 1 <= hist.cur_idx <= max_idx
    (1 <= idx <= max_idx) || return false
    idx != hist.cur_idx || return false

    # save the current line
    if hist.cur_idx == max_idx
        hist.last_mode = LineEdit.mode(s)
        hist.last_buffer = copy(LineEdit.buffer(s))
    else
        hist.history[hist.cur_idx] = LineEdit.input_string(s)
        hist.modes[hist.cur_idx] = mode_idx(hist, LineEdit.mode(s))
    end

    # load the saved line
    if idx == max_idx
        LineEdit.transition(s, hist.last_mode)
        LineEdit.replace_line(s, hist.last_buffer)
        hist.last_mode = nothing
        hist.last_buffer = IOBuffer()
    else
        LineEdit.transition(s, hist.mode_mapping[hist.modes[idx]])
        LineEdit.replace_line(s, hist.history[idx])
    end

    hist.cur_idx = idx
    true
end

function history_prev(s::LineEdit.MIState, hist::REPLHistoryProvider)
    hist.last_idx = -1
    if history_move(s, hist, hist.cur_idx-1)
        LineEdit.move_input_start(s)
        LineEdit.move_line_end(s)
        LineEdit.refresh_line(s)
    else
        Terminals.beep(LineEdit.terminal(s))
    end
end

function history_next(s::LineEdit.MIState, hist::REPLHistoryProvider)
    cur_idx = hist.cur_idx
    if 0 < hist.last_idx
        # issue #6312
        cur_idx = hist.last_idx
        hist.last_idx = -1
    end
    if history_move(s, hist, cur_idx+1)
        LineEdit.move_input_end(s)
        LineEdit.refresh_line(s)
    else
        Terminals.beep(LineEdit.terminal(s))
    end
end

function history_move_prefix(s::LineEdit.MIState,
                             hist::REPLHistoryProvider,
                             backwards::Bool)
    buf = LineEdit.buffer(s)
    pos = position(buf)
    prefix = bytestring_beforecursor(buf)
    allbuf = bytestring(buf)
    idxs = backwards ? ((hist.cur_idx-1):-1:1) : ((hist.cur_idx+1):length(hist.history))
    for idx in idxs
        if beginswith(hist.history[idx], prefix) && hist.history[idx] != allbuf
            history_move(s, hist, idx)
            seek(LineEdit.buffer(s), pos)
            LineEdit.refresh_line(s)
            return
        end
    end
    Terminals.beep(LineEdit.terminal(s))
end
history_next_prefix(s::LineEdit.MIState, hist::REPLHistoryProvider) =
    hist.cur_idx == length(hist.history) ?
        history_next(s, hist) : history_move_prefix(s, hist, false)
history_prev_prefix(s::LineEdit.MIState, hist::REPLHistoryProvider) =
    history_move_prefix(s, hist, true)

function history_search(hist::REPLHistoryProvider, query_buffer::IOBuffer, response_buffer::IOBuffer,
                        backwards::Bool=false, skip_current::Bool=false)

    qpos = position(query_buffer)
    qpos > 0 || return true
    searchdata = bytestring_beforecursor(query_buffer)
    response_str = bytestring(response_buffer)

    # Alright, first try to see if the current match still works
    a = position(response_buffer) + 1
    b = a + sizeof(searchdata) - 1
    if !skip_current && (0 < a <= b <= response_buffer.size) &&
       searchdata == bytestring(response_buffer.data[a:b])
        return true
    end

    searchfunc,delta = backwards ? (rsearch,-1) : (search,1)

    # Start searching
    # First the current response buffer
    if 1 <= a+delta <= length(response_str)
        match = searchfunc(response_str, searchdata, a+delta)
        if match != 0:-1
            seek(response_buffer, first(match)-1)
            return true
        end
    end

    # Now search all the other buffers
    idxs = backwards ? ((hist.cur_idx-1):-1:1) : ((hist.cur_idx+1):length(hist.history))
    for idx in idxs
        h = hist.history[idx]
        match = searchfunc(h, searchdata)
        if match != 0:-1 && h != response_str
            truncate(response_buffer, 0)
            write(response_buffer, h)
            seek(response_buffer, first(match)-1)
            hist.cur_idx = idx
            return true
        end
    end

    return false
end

function history_reset_state(hist::REPLHistoryProvider)
    hist.last_idx = hist.cur_idx
    hist.cur_idx = length(hist.history) + 1
end
LineEdit.reset_state(hist::REPLHistoryProvider) = history_reset_state(hist)

const julia_green = "\033[1m\033[32m"

function return_callback(repl, s)
    if position(s.input_buffer) != 0 && eof(s.input_buffer) &&
        (seek(s.input_buffer, position(s.input_buffer)-1); read(s.input_buffer, Uint8) == '\n')
        repl.consecutive_returns += 1
    else
        repl.consecutive_returns = 0
    end
    ast = parse_input_line(bytestring(s.input_buffer))
    if repl.consecutive_returns > 1 || !isa(ast, Expr) || (ast.head != :continue && ast.head != :incomplete)
        return true
    else
        return false
    end
end

function find_hist_file()
    filename = ".julia_history"
    if isfile(filename)
        return filename
    elseif haskey(ENV, "JULIA_HISTORY")
        return ENV["JULIA_HISTORY"]
    else
        return joinpath(homedir(), filename)
    end
end

backend(r::AbstractREPL) = r.backendref

send_to_backend(ast, backend::REPLBackendRef) = send_to_backend(ast, backend.repl_channel, backend.response_channel)
function send_to_backend(ast, req, rep)
    put!(req, (ast, 1))
    val, bt = take!(rep)
end

have_color(s) = true

function respond(f, repl, main)
    (s,buf,ok)->begin
        if !ok
            return transition(s, :abort)
        end
        line = takebuf_string(buf)
        if !isempty(line)
            reset(repl)
            val, bt = send_to_backend(f(line), backend(repl))
            if !ends_with_semicolon(line) || bt !== nothing
                print_response(repl, val, bt, true, have_color(s))
            end
        end
        println(repl.t)
        reset_state(s)
        transition(s, main)
    end
end

function reset(repl::LineEditREPL)
    raw!(repl.t, false)
    print(repl.t,Base.text_colors[:normal])
end

function setup_interface(repl::LineEditREPL; extra_repl_keymap = Dict{Any,Any}[])
    ###
    #
    # This function returns the main interface that describes the REPL
    # functionality, it is called internally by functions that setup a
    # Terminal-based REPL frontend, but if you want to customize your REPL
    # or embed the REPL in another interface, you may call this function
    # directly and append it to your interface.
    #
    # Usage:
    #
    # repl_channel,response_channel = RemoteRef(),RemoteRef()
    # start_repl_backend(repl_channel, response_channel)
    # setup_interface(REPLDisplay(t),repl_channel,response_channel)
    #
    ###

    ###
    # We setup the interface in two stages.
    # First, we set up all components (prompt,rsearch,shell,help)
    # Second, we create keymaps with appropriate transitions between them
    #   and assign them to the components
    #
    ###

    ############################### Stage I ################################

    # This will provide completions for REPL and help mode
    replc = REPLCompletionProvider(repl)

    # Set up the main Julia prompt
    julia_prompt = Prompt("julia> ";
        # Copy colors from the prompt object
        prompt_prefix = repl.prompt_color,
        prompt_suffix = repl.input_color,
        keymap_func_data = repl,
        complete = replc,
        on_enter = s->return_callback(repl, s))

    julia_prompt.on_done = respond(Base.parse_input_line, repl, julia_prompt)

    # Setup help mode
    help_mode = Prompt(" help> ",
        prompt_prefix = repl.help_color,
        prompt_suffix = repl.input_color,
        keymap_func_data = repl,
        complete = replc,
        # When we're done transform the entered line into a call to help("$line")
        on_done = respond(repl, julia_prompt) do line
            parse("Base.@help $line", raise=false)
        end)

    # Set up shell mode
    shell_mode = Prompt("shell> ";
        prompt_prefix = repl.shell_color,
        prompt_suffix = repl.input_color,
        keymap_func_data = repl,
        complete = ShellCompletionProvider(repl),
        # Transform "foo bar baz" into `foo bar baz` (shell quoting)
        # and pass into Base.repl_cmd for processing (handles `ls` and `cd`
        # special)
        on_done = respond(repl, julia_prompt) do line
            Expr(:call, :(Base.repl_cmd), macroexpand(Expr(:macrocall, symbol("@cmd"),line)))
        end)

    ################################# Stage II #############################

    # Setup history
    # We will have a unified history for all REPL modes
    hp = REPLHistoryProvider((Symbol=>Any)[:julia => julia_prompt,
                                           :shell => shell_mode,
                                           :help  => help_mode])
    if !repl.no_history_file
        f = open(find_hist_file(), true, true, true, false, false)
        finalizer(replc, replc->close(f))
        hist_from_file(hp, f)
    end
    history_reset_state(hp)
    julia_prompt.hist = hp
    shell_mode.hist = hp
    help_mode.hist = hp

    hkp, hkeymap = LineEdit.setup_search_keymap(hp)

    # Canonicalize user keymap input
    if isa(extra_repl_keymap, Dict)
        extra_repl_keymap = [extra_repl_keymap]
    end

    const repl_keymap = {
        ';' => function (s)
            if isempty(s) || position(LineEdit.buffer(s)) == 0
                buf = copy(LineEdit.buffer(s))
                transition(s, shell_mode)
                LineEdit.state(s, shell_mode).input_buffer = buf
                LineEdit.refresh_line(s)
            else
                edit_insert(s, ';')
            end
        end,
        '?' => function (s)
            if isempty(s) || position(LineEdit.buffer(s)) == 0
                buf = copy(LineEdit.buffer(s))
                transition(s, help_mode)
                LineEdit.state(s, help_mode).input_buffer = buf
                LineEdit.refresh_line(s)
            else
                edit_insert(s, '?')
            end
        end,

        # Bracketed Paste Mode
        "\e[200~" => s->begin
            ps = LineEdit.state(s, LineEdit.mode(s))
            input = readuntil(ps.terminal, "\e[201~")[1:(end-6)]
            input = replace(input, '\r', '\n')
            if position(LineEdit.buffer(s)) == 0
                indent = Base.indentation(input)[1]
                input = Base.unindent(lstrip(input), indent)
            end
            buf = copy(LineEdit.buffer(s))
            edit_insert(buf,input)
            string = takebuf_string(buf)
            curspos = position(LineEdit.buffer(s))
            pos = 0
            inputsz = sizeof(input)
            sz = sizeof(string)
            while pos <= sz
                oldpos = pos
                ast, pos = Base.parse(string, pos, raise=false)
                if isa(ast, Expr) && ast.head == :error
                    # Insert all the remaining text as one line (might be empty)
                    LineEdit.replace_line(s, strip(bytestring(string.data[max(oldpos, 1):end])))
                    seek(LineEdit.buffer(s), max(curspos-oldpos+inputsz, 0))
                    LineEdit.refresh_line(s)
                    break
                end
                # Get the line and strip leading and trailing whitespace
                line = strip(bytestring(string.data[max(oldpos, 1):min(pos-1, sz)]))
                isempty(line) && continue
                LineEdit.replace_line(s, line)
                if oldpos <= curspos
                    seek(LineEdit.buffer(s),curspos-oldpos+inputsz)
                end
                LineEdit.refresh_line(s)
                (pos > sz && last(string) != '\n') && break
                if !isa(ast, Expr) || (ast.head != :continue && ast.head != :incomplete)
                    LineEdit.commit_line(s)
                    # This is slightly ugly but ok for now
                    terminal = LineEdit.terminal(s)
                    stop_reading(terminal)
                    raw!(terminal, false) && disable_bracketed_paste(terminal)
                    LineEdit.mode(s).on_done(s, LineEdit.buffer(s), true)
                    raw!(terminal, true) && enable_bracketed_paste(terminal)
                    start_reading(terminal)
                else
                    break
                end
            end
        end,
    }

    a = Dict{Any,Any}[hkeymap, repl_keymap, LineEdit.history_keymap(hp), LineEdit.default_keymap, LineEdit.escape_defaults]
    prepend!(a, extra_repl_keymap)

    julia_prompt.keymap_func = @eval @LineEdit.keymap $(a)

    const mode_keymap = {
        '\b' => function (s)
            if isempty(s) || position(LineEdit.buffer(s)) == 0
                buf = copy(LineEdit.buffer(s))
                transition(s, julia_prompt)
                LineEdit.state(s, julia_prompt).input_buffer = buf
                LineEdit.refresh_line(s)
            else
                LineEdit.edit_backspace(s)
            end
        end,
        "^C" => function (s)
            LineEdit.move_input_end(s)
            LineEdit.refresh_line(s)
            print(LineEdit.terminal(s), "^C\n\n")
            transition(s, julia_prompt)
            transition(s, :reset)
            LineEdit.refresh_line(s)
        end
    }

    b = Dict{Any,Any}[hkeymap, mode_keymap, LineEdit.history_keymap(hp), LineEdit.default_keymap, LineEdit.escape_defaults]

    shell_mode.keymap_func = help_mode.keymap_func = @eval @LineEdit.keymap $(b)

    ModalInterface([julia_prompt, shell_mode, help_mode,hkp])
end

function run_frontend(repl::LineEditREPL, backend)
    d = REPLDisplay(repl)
    pushdisplay(d)
    if !isdefined(repl,:interface)
        interface = repl.interface = setup_interface(repl)
    else
        interface = repl.interface
    end
    repl.backendref = backend
    run_interface(repl.t, interface)
    popdisplay(d)
end

if isdefined(Base, :banner_color)
    banner(io, t) = banner(io, hascolor(t))
    banner(io, x::Bool) = print(io, x ? Base.banner_color : Base.banner_plain)
else
    banner(io,t) = Base.banner(io)
end

## StreamREPL ##

type StreamREPL <: AbstractREPL
    stream::IO
    prompt_color::String
    input_color::String
    answer_color::String
    waserror::Bool
    StreamREPL(stream,pc,ic,ac) = new(stream,pc,ic,ac,false)
end

outstream(s::StreamREPL) = s.stream

StreamREPL(stream::AsyncStream) = StreamREPL(stream, julia_green, Base.text_colors[:white], Base.answer_color())

answer_color(r::LineEditREPL) = r.answer_color
answer_color(r::StreamREPL) = r.answer_color

function run_repl(stream::AsyncStream)
    repl =
    @async begin
        repl_channel = RemoteRef()
        response_channel = RemoteRef()
        start_repl_backend(repl_channel, response_channel)
        StreamREPL_frontend(repl, repl_channel, response_channel)
    end
    repl
end

function ends_with_semicolon(line)
    match = rsearch(line, ';')
    if match != 0
        for c in line[(match+1):end]
            isspace(c) || return c == '#'
        end
        return true
    end
    return false
end

function run_frontend(repl::StreamREPL, backend::REPLBackendRef)
    have_color = true
    banner(repl.stream, have_color)
    d = REPLDisplay(repl)
    pushdisplay(d)
    repl_channel, response_channel = backend.repl_channel, backend.response_channel
    while repl.stream.open
        if have_color
            print(repl.stream,repl.prompt_color)
        end
        print(repl.stream, "julia> ")
        if have_color
            print(repl.stream, repl.input_color)
        end
        line = readline(repl.stream)
        if !isempty(line)
            ast = Base.parse_input_line(line)
            if have_color
                print(repl.stream, Base.color_normal)
            end
            put!(repl_channel, (ast, 1))
            val, bt = take!(response_channel)
            if !ends_with_semicolon(line)
                print_response(repl, val, bt, true, have_color)
            end
        end
    end
    # Terminate Backend
    put!(repl_channel, (nothing, -1))
    popdisplay(d)
end

function start_repl_server(port)
    listen(port) do server, status
        client = accept(server)
        run_repl(client)
    end
end

end # module

max_stars_repo_name: vchuravy/julia

max_issues_repo_name: vchuravy/julia

max_forks_repo_name: vchuravy/julia