text:
#*******************************************************************************
#
#
#                Aplicação do split-operator em um sistema 2D
#               com condições de contorno tipo hardwall.
#
#*******************************************************************************
using PyPlot

# constantes físicas
Ry = 13.6056917253e3; # meV
a0 = 0.0529177208319; # nm
h2m = (Ry*a0^2); #  massa efetiva GaAs
hbar = 0.65821188926; # meV.ps

################################################################################
                    # Grid Bidimensional (Espaço real)
################################################################################
Lx = 1000; # Tamanho da dimensão "x" em nanometros (nm)
Ly = 400; # Tamanho da dimensão "y" em nanometros (nm)
Nx = div(Lx,2) # número de pontos na dimensão x
Ny = div(Ly,2) # número de pontos na dimensão y
# Nx = 2*Lx;
# Ny = 2*Ly;

x = linspace(-Lx/2, Lx/2, Nx+1)[1:end-1];
y = linspace(-Ly/2, Ly/2, Ny+1)[1:end-1];
dx = x[2] - x[1];
dy = y[2] - y[1];
X = kron(x', ones(Ny));
Y = kron(ones(Nx)', y);

################################################################################
                    # Espaço-k (na dimensãolongitudinal à tira)
################################################################################
kx = fftshift(linspace(-pi/dx, pi/dx, Nx+1)[1:end-1]); # Valores possíveis de k_x
k0x_max = pi/(2*dx);
k0y_max = pi/(2*dy);

################################################################################
                    # Condição Inicial: Pacote Gaussiano
################################################################################
x_0 = 0; #  centro do pacote
y_0 = 0; #  centro do pacote
sigma_x = 50.0; # largura longitudinal
sigma_y = 50.0; # largura transversal
k0_x = 0.1 * k0x_max; # momento inicial
k0_y = 0; # momento inicial
# Norm = ((pi^2)*(sigma_x*sigma_y)^2)^(-1/4); # Fator para normalização
Norm = 1 ; # Fator para pacote NÃO normalizado

# pacote de ondas na representação de posições:
Ψ_0 = Norm * exp(-(X-x_0).^2/(2*sigma_x^2)-(Y-y_0).^2/(2*sigma_y^2)
                    + 1im*(k0_x*X + k0_y*Y))
# fft!(Ψ_0,2)
#
# contourf(X,Y,fftshift(abs(Ψ_0).^2,2))
# ylim(-200,200)
################################################################################
                        # Hamiltoniano
################################################################################

# Potencial
α, β, γ = 5, 0.01, 0;
V_degrau = α./(e.^(-β*(X-γ))+1);

# pcolormesh(x,y,V_degrau)

# Cinética:
# matrizes Tridiagonais (Ny x Ny)
d2dy2 = 1/(dy^2) * SymTridiagonal(-2*ones(Ny), ones(Ny-1));
# ddy = 1/(2*dy) * Tridiagonal(-1 * ones(Ny-1), zeros(Ny), ones(Ny-1));

T_y  = - h2m * full(d2dy2)
T_kx =   h2m * kx

function psi_dt(ψ,Δt)
  # espaço de posições 'x'
  psi_spinor = ψ;
  psi_spinor = [exp(-1im*V_degrau[j,i]*Δt/(2*hbar))*psi_spinor[j,i] for j=1:Ny, i=1:Nx ];
  # representação mista (kx,y)
  psi_spinor = fft(psi_spinor,2);

  for i = 1:Nx
    psi_spinor[:,i] = expm(-1im*(T_kx[i] + T_y)*Δt/hbar)*psi_spinor[:,i] ;
  end

  # espaço de posições 'x'
  psi_spinor = ifft(psi_spinor,2);
  psi_spinor = [exp(-1im*V_degrau[j,i]*Δt/(2*hbar))*psi_spinor[j,i] for j=1:Ny, i=1:Nx ];

  return psi_spinor;
end

function psit(Ψ, t_final, N_steps = 20)
  tspan = linspace(0, t_final, N_steps);
  delta_t = tspan[2]-tspan[1];

  for i = 1:N_steps
    psi_t = psi_dt(Ψ,delta_t);
    Ψ = psi_t;
  end
  Ψ
end

function psit_livre(Ψ,Δt)
  fft!(Ψ,2)
  psi = copy(Ψ);
  for i = 1:Nx
    psi[:,i] = expm(-1im*(T_kx[i] + T_y)*Δt/hbar)*Ψ[:,i] ;
  end
  ifft(psi,2)
end

# ψ_1 = psit(Ψ_0, 0.01,5)
ψ_1 = psit_livre(Ψ_0, 0.01)
pcolormesh(x,y,abs(ψ_1).^2)
ylim(-200,200)
# plot(x,10.0^(-1)*V_x, linewidth=2.0);
# # ylim(-0.05,1.05);
# # xlim(-20,20)
# title("Gaussian distribution");
# xlabel(L"$x$");
# ylabel(L"$|\psi(x)|^2$");

max_stars_repo_name: mhlmedeiros/Split-Operator

max_issues_repo_name: mhlmedeiros/Split-Operator

max_forks_repo_name: mhlmedeiros/Split-Operator


text:
using BasisFunctions: hasderivative, hasantiderivative

P = 80

@testset "$(rpad("Lagrange polynomials",P))" begin

    ξ = [-1.0, +1.0]
    b = Lagrange(ξ)
    blob = LagrangeLobattoLegendre(2)
    bgau = LagrangeGaussLegendre(2)
    blch = LagrangeLobattoChebyshev(2)
    bgch = LagrangeGaussChebyshev(2)

    BasisFunctions.Test.test_generic_dict_interface(LagrangeGaussLegendre(10))

    @testset "$(rpad("Basic functionality",P))" begin
        @test nodes(b) == ξ
        @test nnodes(b) == 2
        @test degree(b) == 1
        @test support(b) == LagrangeInterval{eltype(ξ)}()
        @test nodes(blob) == nodes(b)
        @test nodes(blch) == nodes(b)
        @test nodes(bgau) == [-0.5773502691896258, 0.5773502691896258]
        @test nodes(bgch) == [-0.7071067811865475, 0.7071067811865475]
        @test nodes(blob) == nodes(similar(blob, eltype(ξ), 2))
        @test nodes(bgau) == nodes(similar(bgau, eltype(ξ), 2))
    end

    @testset "$(rpad("Basis evaluation",P))" begin
        @test b[1](-1.0) == 1.0
        @test b[1]( 0.0) == 0.5
        @test b[1](+1.0) == 0.0

        @test b[2](-1.0) == 0.0
        @test b[2]( 0.0) == 0.5
        @test b[2](+1.0) == 1.0
    end

    @testset "$(rpad("Basis derivative",P))" begin
        @test eval_element_derivative(b, 1, -1.0) == -0.5
        @test eval_element_derivative(b, 1,  0.0) == -0.5
        @test eval_element_derivative(b, 1, +1.0) == -0.5

        @test eval_element_derivative(b, 2, -1.0) == +0.5
        @test eval_element_derivative(b, 2,  0.0) == +0.5
        @test eval_element_derivative(b, 2, +1.0) == +0.5
    end

    @testset "$(rpad("Basis antiderivative",P))" begin
        # @test moment(b, 1) == 1.0
        # @test moment(b, 2) == 1.0

        @test eval_element_antiderivative(b, 1, -1.0) == 0.0
        @test eval_element_antiderivative(b, 1,  0.0) == 0.75
        @test eval_element_antiderivative(b, 1, +1.0) == 1.0

        @test eval_element_antiderivative(b, 2, -1.0) == 0.0
        @test eval_element_antiderivative(b, 2,  0.0) == 0.25
        @test eval_element_antiderivative(b, 2, +1.0) == 1.0
    end

end

ext: jl


text:
module Compress

import Pkg
import UUIDs

include("compress_main.jl")
include("recompress.jl")

end # module


text:
#**************************************************************************************
# Config.jl
# =============== part of the GeoEfficiency.jl package.
#
# this file contain the configuration parameters. the values introduced here hid any other value.
#
#**************************************************************************************

using Compat
using Compat.Dates

#------------------------ about ------------------------------

function about()
	printstyled(
	"""
	\n
	\t *************************************************
	\t **            -=) GeoEfficiency (=-            **
	\t **  Accurate Geometrical Efficiency Calculator **
	\t **   First Created on Fri Aug 14 20:12:01 2015 **
	\t *************************************************

	\t Author:        Mohamed Krar,  @e-mail: DrKrar@gmail.com
	\t Auth_Profile:  https://www.researchgate.net/profile/Mohamed_Krar3
	\t Repository:    https://github.com/DrKrar/GeoEfficiency.jl/
	\t Version:       v"0.9.2-DEV" - ($(Date(now()) - Date("2018-06-08")) old master)
	\t Documentation: http://geoefficiencyjl.readthedocs.org
	\n"""
	, color=:green)
end

#------------------ ---- set_global_Consts -----------------------------

##Physics_Model.jl##

##Input_Interface.jl##
const dataFolder    = "GeoEfficiency"
const dataDir       = joinpath(homedir(), dataFolder)

##Calculations.jl##
using QuadGK; const integrate = QuadGK.quadgk
const relativeError = 1.0E-4
const absoluteError = eps(1.0)

##Output_Interface.jl##
const resultsFolder = "results"


text:
import FastTransforms
import QuadratureRules: scale_weights, shift_nodes

@testset "$(rpad("Clenshaw-Curtis",80))" begin

    @test_throws ErrorException ClenshawCurtisQuadrature(1)

    for s in 2:10
        @test ClenshawCurtisQuadrature(s) == ClenshawCurtisQuadrature(Float64, s)

        @test sum(weights(GaussChebyshevQuadrature(s))) ≈ 1

        μ = FastTransforms.chebyshevmoments1(Float64, s)
        b = scale_weights(reverse(FastTransforms.clenshawcurtisweights(μ)))
        c = shift_nodes(reverse(FastTransforms.clenshawcurtisnodes(Float64, s)))

        @test nodes(ClenshawCurtisQuadrature(s)) ≈ c
        @test weights(ClenshawCurtisQuadrature(s)) ≈ b
    end

end


text:
# This file is a part of JuliaFEM.
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md

using JuliaFEM
using JuliaFEM.Testing
importall Base
import JuliaFEM: get_basis, get_dbasis, get_integration_points

type MyQuad4 <: AbstractElement
end

function get_basis(element::Element{MyQuad4}, ip, time)
    1/4*[(1-ip[1])*(1-ip[2]) (1+ip[1])*(1-ip[2]) (1+ip[1])*(1+ip[2]) (1-ip[1])*(1+ip[2])]
end

function get_dbasis(element::Element{MyQuad4}, ip, time)
    1/4*[-(1-ip[2])    (1-ip[2])   (1+ip[2])  -(1+ip[2])
         -(1-ip[1])   -(1+ip[1])   (1+ip[1])   (1-ip[1])]
end

function get_integration_points(element::MyQuad4)
    [
        (1.0, 1.0/sqrt(3.0)*[-1, -1]),
        (1.0, 1.0/sqrt(3.0)*[ 1, -1]),
        (1.0, 1.0/sqrt(3.0)*[ 1,  1]),
        (1.0, 1.0/sqrt(3.0)*[-1,  1])
    ]
end

function length(element::Element{MyQuad4})
    return 4
end

function size(element::Element{MyQuad4})
    return (2, 4)
end

@testset "test new element" begin
    el = Element(MyQuad4)
    el["geometry"] = Vector{Float64}[[0.0,0.0], [1.0,0.0], [1.0,1.0], [0.0,1.0]]
    el["displacement"] = Vector{Float64}[[0.0,0.0], [0.0,0.0], [1.0,0.0], [0.0,0.0]]
    @test isapprox(el("geometry", [0.0, 0.0], 0.0), [0.5, 0.5])
    @test isapprox(el("displacement", [0.0, 0.0], 0.0), [0.25, 0.0])
    el["temperature thermal conductivity"] = 6.0
    dim = length(el)
    K = zeros(dim, dim)
    A = 0.0
    time = 0.0
    for ip in get_integration_points(el)
        dN = el(ip, time, Val{:Grad})
        detJ = el(ip, time, Val{:detJ})
        w = ip.weight*detJ
        c = el("temperature thermal conductivity", ip, time)
        K += w*c*dN'*dN
        A += w
    end
    @test isapprox(A, 1.0)
    K_expected = [
        4.0  -1.0  -2.0  -1.0
       -1.0   4.0  -1.0  -2.0
       -2.0  -1.0   4.0  -1.0
       -1.0  -2.0  -1.0   4.0]
    @test isapprox(K, K_expected)
end


text:
include("../../src/AlphaShapes.jl")
using LinearAlgebraicRepresentation, ViewerGL
Lar = LinearAlgebraicRepresentation
GL =  ViewerGL

include("./OBJ/cat.jl")
## Equivalent to the following instructions:
#V = Lar.obj2lar("./examples/examples3D/OBJ/cat.obj")[1];
#VS = AlphaStructures.matrixPerturbation(V);
#DT = AlphaStructures.delaunayWall(V);

filtration = AlphaShapes.alphaFilter(VS, DT);
filter_key = sort(unique(values(filtration)))

VV, EV, FV, TV = AlphaShapes.alphaSimplex(VS, filtration, 0.9)
GL.VIEW(
	[
		GL.GLGrid(VS, EV, GL.COLORS[1], 0.6) # White
		GL.GLGrid(VS, FV, GL.COLORS[2], 0.3) # Red
		GL.GLGrid(VS, TV, GL.COLORS[3], 0.3) # Green
	]
);

#
# Arlecchino's Cat
#

granular = 7
reduced_filter = filter_key[sort(abs.(rand(Int, granular).%length(filter_key)))]
reduced_filter = [reduced_filter; max(filter_key...)]
points = [[p] for p in VV]

for α in reduced_filter
	@show α
	VV,EV,FV,TV = AlphaShapes.alphaSimplex(VS, filtration, α)
	GL.VIEW(
		GL.GLExplode(
			VS,
			[[[t] for t in TV]; [[f] for f in FV]; [[e] for e in EV]],
			1., 1., 1.,	# Explode Ratio
			99, 1		# Colors
		)
	)
end

#
# Appearing Colors
#

for i = 2 : length(reduced_filter)
	VV0, EV0, FV0, TV0 = AlphaShapes.alphaSimplex(VS, filtration, reduced_filter[i-1])
	VV,  EV,  FV,  TV = AlphaShapes.alphaSimplex(VS, filtration, reduced_filter[i])
	EV0mesh = GL.GLGrid(VS, EV0)
	FV0mesh = GL.GLGrid(VS, FV0)
	TV0mesh = GL.GLGrid(VS, TV0)
	EVmesh = GL.GLGrid(VS, setdiff(EV, EV0), GL.COLORS[2], 1)
	FVmesh = GL.GLGrid(VS, setdiff(FV, FV0), GL.COLORS[7], 1)
	TVmesh = GL.GLGrid(VS, setdiff(TV, TV0), GL.COLORS[12], 1)
	GL.VIEW([EV0mesh; FV0mesh; TV0mesh; EVmesh; FVmesh; TVmesh])
end


text:
using MarketData

facts("arima builds models") do

  context("works when it supposed to") do
      @fact length(cl.values) => 500
  end

  context("fails when it's supposed to") do
      @fact_throws cl.value
  end
end


text:
"""
    string_deserializer(format::Symbol)::Function

Retrieves string deserializer for downloaded OpenStreetMap data.

# Arguments
- `format::Symbol`: Format of OpenStreetMap darta `:xml`, `:osm` or `:json`.

# Return
- `Function`: Either LightXML or JSON parser.
"""
function string_deserializer(format::Symbol)::Function
    if format == :xml || format == :osm
        return LightXML.parse_string
    elseif format == :json
        return JSON.parse
    else
        throw(ArgumentError("String deserializer for $format format does not exist"))
    end
end

"""
    has_extension(filename)

Returns `true` if `filename` has an extension.
"""
has_extension(filename) = length(split(filename, '.')) > 1

"""
    check_valid_filename(filename)

Check that `filename` ends in ".json", ".osm" or ".xml",
"""
function check_valid_filename(filename)
    split_name = split(filename, '.')
    if !has_extension(filename) || !in(split_name[end], ("json", "osm", "xml"))
        err_msg = "File $filename does not have an extension of .json, .osm or .xml"
        throw(ArgumentError(err_msg))
    end
    return true
end

"""
    get_extension(filename)

Return extension of `filename`.
"""
get_extension(filename) = split(filename, '.')[end]

"""
    file_deserializer(file_path)::Function

Retrieves file deserializer for downloaded OpenStreetMap data.

# Arguments
- `file_path`: File path of OSM data.

# Return
- `Function`: Either LightXML or JSON parser.
"""
function file_deserializer(file_path)::Function
    check_valid_filename(file_path)
    format = Symbol(get_extension(file_path))
    if format == :xml || format == :osm
        return LightXML.parse_file
    elseif format == :json
        return JSON.parsefile
    else
        throw(ArgumentError("File deserializer for $format format does not exist"))
    end
end

"""
    validate_save_location(save_to_file_location, download_format)

Check the extension of `save_to_file_location` and do the following:

- If it is a valid download format but not the download format used,
then error.
- If the extension matches `download_format`, return `save_to_file_location`
- If there is a different extension, append correct extension and return. This
allows users to have periods in their file names if they wanted to
- If no extension, add the correct extension and return
"""
function validate_save_location(save_to_file_location, download_format)
    valid_formats = ("osm", "xml", "json")
    if has_extension(save_to_file_location)
        extension = get_extension(save_to_file_location)
        if extension == string(download_format)
            # File extension matches download format, all good
            return save_to_file_location
        elseif extension in valid_formats
            # File extension is a different download format, error
            throw(ArgumentError("Extension of save location $save_to_file_location does not match download format $download_format"))
        end
    end
    return save_to_file_location *= "." * string(download_format)
end

"""
    tryparse_string_to_number(str::AbstractString)::Union{Number,AbstractString}

Attempts to parse a stringified number as an Integer or Float.
"""
function tryparse_string_to_number(str::AbstractString)::Union{Number,AbstractString}
    result = tryparse(Int, str)
    if !(result isa Nothing)
        return result
    end

    result = tryparse(Float64, str)
    if !(result isa Nothing)
        return result
    end

    return str
end

"""
    remove_non_numeric(str::AbstractString)::Number

Removes any non numeric characters from a string, then converts it to a number.
"""
function remove_non_numeric(str::AbstractString)::Number
    numeric_only = replace(str, r"[^\d\.]" => "")
    return isempty(numeric_only) ? 0 : tryparse_string_to_number(numeric_only)
end

"""
    remove_sub_string_after(str::AbstractString, after::AbstractString)::AbstractString

Removes all characters in a string that occurs `after` some input match pattern.
"""
function remove_sub_string_after(str::AbstractString, after::AbstractString)::AbstractString
    regex = Regex("$after.*\$")
    return replace(str, regex => "")
end

"""
    remove_numeric(str::AbstractString)::AbstractString

Removes numeric characters from a string.
"""
function remove_numeric(str::AbstractString)::AbstractString
    return replace(str, r"[\d\.]" => "")
end

"""
    xml_to_dict(root_node::Union{XMLNode,XMLElement}, attributes_to_exclude::Set=Set())::AbstractDict

Parses a LightXML object to a dictionary.

# Arguments
- `root_node::Union{XMLNode,XMLElement}`: LightXML object to parse.
- `attributes_to_exclude::Set=Set()`: Set of tags to ignore when parsing.

# Return
- `AbstractDict`: XML parsed as a dictionary.
"""
function xml_to_dict(root_node::Union{XMLNode,XMLElement}, attributes_to_exclude::Set=Set())::AbstractDict
    result = Dict{String, Any}()

    for a in attributes(root_node)
        if !(name(a) in attributes_to_exclude)
            result[name(a)] = tryparse_string_to_number(value(a))
        end
    end

    if has_children(root_node)
        for c in child_elements(root_node)
            key = name(c)

            if key in attributes_to_exclude
                continue
            end

            if haskey(result, name(c))
                push!(result[key]::Vector{Dict{String, Any}}, xml_to_dict(c, attributes_to_exclude))
            else
                result[key] = [xml_to_dict(c, attributes_to_exclude)]
            end
        end
    end

    return result
end

"""
Returns the first and last element of an array.
"""
trailing_elements(array::AbstractArray)::AbstractArray = [array[1], array[end]]

"""
Returns the common trailing element (first or last element) of two arrays if it exists.
"""
function first_common_trailing_element(a1::AbstractArray{T}, a2::AbstractArray{T})::T where T <: Any
    intersection = intersect(trailing_elements(a1), trailing_elements(a2))
    return length(intersection) >= 1 ? intersection[1] : throw(ArgumentError("No common trailinging elements between $a1 and $a2"))
end

"""
Joins two arrays into a single array on a common trailing element.
"""
function join_two_arrays_on_common_trailing_elements(a1::AbstractArray{T}, a2::AbstractArray{T})::AbstractArray{T} where T <: Any
    el = first_common_trailing_element(a1, a2)

    if el == a1[1] == a2[1]
        return [reverse(a1)..., a2[2:end]...]
    elseif el == a1[1] == a2[end]
        return [reverse(a1)..., reverse(a2)[2:end]...]
    elseif el == a1[end] == a2[1]
        return [a1..., a2[2:end]...]
    elseif el == a1[end] == a2[end]
        return [a1..., reverse(a2)[2:end]...]
    end
end

"""
Joins an array of arrays into a single array, on common trailing elements.
"""
function join_arrays_on_common_trailing_elements(arrays::AbstractVector{T}...)::AbstractVector{T} where T <: Any
    # Can we make this type stable? IE for input of Vector{Vector{Int}} a return of Vector{Int} can be detected by @code_warntype
    current = arrays[1]
    others = setdiff(arrays, [current])

    if !isempty(others)
        for (i, other) in enumerate(others)
            try
                current = join_two_arrays_on_common_trailing_elements(current, other)
                deleteat!(others, i)
                return join_arrays_on_common_trailing_elements(current, others...)
            catch
                continue
            end
        end
        throw(ErrorException("Could not join $current on $others"))
    else
        return current
    end
end

"""
    delete_from_dict!(dict::AbstractDict, items_to_delete::Union{AbstractArray,AbstractSet}, how::Symbol=:on_key)

Deletes key-value pairs from a dictionary.

# Arguments
- `dict::AbstractDict`: Any dictionary to delete from.
- `items_to_delete::Union{AbstractArray,AbstractSet}`: List of items to delete, either keys or values.
- `how::Symbol=:on_key`: To delete `:on_key` or `:on_value`.

# Return
- `AbstractDict`: Filtered dictionary.
"""
function delete_from_dict!(dict::AbstractDict, items_to_delete::Union{AbstractArray,AbstractSet}, how::Symbol=:on_key)
    if how == :on_key
        for k in items_to_delete
            delete!(dict, k)
        end
    elseif how == :on_value
        for (k, v) in dict
            if v in items_to_delete
                delete!(dict, k)
            end
        end
    else
        throw(ErrorException("Choose `how` as either `:on_key` or `on_value`"))
    end
end

"""
    flatten(array::AbstractArray)::AbstractArray

Flattens an array of arrays.
"""
function flatten(array::AbstractArray)::AbstractArray
    flattened = collect(Iterators.flatten(array))
    if any(x -> typeof(x) <: AbstractArray, flattened)
        return flatten(flattened)
    end
    return flattened
end


text:
using QuantumSpatialSearch
using LinearAlgebra, SparseArrays
using Test

include("rtn_test.jl")
include("grover_test.jl")

max_stars_repo_name: matteoacrossi/QuantumSpatialSearch

max_issues_repo_name: matteoacrossi/QuantumSpatialSearch

max_forks_repo_name: matteoacrossi/QuantumSpatialSearch


text:
#################################
# Current Conditions API        #
#################################
# Get Current Conditions data for a specific location.

# Contains a centralized function to make calls to the API.
include("request.jl")

function get_currentcondition(apikey::String, locationKey::String, historical::Int64, language::String, details::Bool)
    # Returns current conditions data for a specific location. Current conditions searches require a location key. Please use the Locations API to obtain the location key for your desired location. By default, a truncated version of the current conditions data is returned. The full object can be obtained by passing "details=true" into the url string.

    url = "http://dataservice.accuweather.com/currentconditions/v1/"

    # Add required parameters to the URL
    url = url * string(locationKey)
    if historical != Nothing url = url * "/historical/" * historical end
    # Accepted values: 6 24
    #   Example: If "timespan" is set to "day" and "time" to 10, it gets the forecast for 15 days.

    url = url * "?apikey=" * apikey

    # Add optional parameters to the URL
    if language != Nothing url = url * "&language=" * language end
    if details == true url = url * "&details=true" end

    return make_API_call(url)
end

function get_currentcondition_topcities(apikey::String, group::Int64, language::String)
    # Returns current conditions data for the top 50, 100, or 150 cities worldwide, based on rank.

    url = "http://dataservice.accuweather.com/currentconditions/v1/topcities/"

    # Add required parameters to the URL
    url = url * group
    url = url * "?apikey=" * apikey

    # Add optional parameters to the URL
    if language != Nothing url = url * "&language=" * language end

    return make_API_call(url)
end


text:
using Documenter, Polyhedra

makedocs(
    sitename = "Polyhedra",
    # See https://github.com/JuliaDocs/Documenter.jl/issues/868
    format = Documenter.HTML(prettyurls = get(ENV, "CI", nothing) == "true"),
    # See https://github.com/JuliaOpt/JuMP.jl/issues/1576
    strict = true,
    pages = [
        "Index" => "index.md",
        "Installation" => "installation.md",
        "Representation" => "representation.md",
        "Polyhedron" => "polyhedron.md",
        "Plot" => "plot.md",
        "Containment/Redundancy" => "redundancy.md",
        "Projection/Elimination" => "projection.md",
        "Optimization" => "optimization.md",
        "Utilities" => "utilities.md"
    ],
    # The following ensures that we only include the docstrings from
    # this module for functions define in Base that we overwrite.
    modules = [Polyhedra]
)

deploydocs(
    repo   = "github.com/JuliaPolyhedra/Polyhedra.jl.git",
)


text:
# looking for missing values

julia> v = [1, 2, missing, 4]
4-element Array{Union{Missing, Int64},1}:
 1
 2
  missing
 4

julia> ismissing.(v)
4-element BitArray{1}:
 false
 false
  true
 false

julia>


text:
using HTTP, JSON, JuMP
import Xpress
include("REopt.jl")
import REopt as reoptjl
include("GHPGHX.jl")
using .GHPGHX

function job(req::HTTP.Request)
    d = JSON.parse(String(req.body))
    timeout = pop!(d, "timeout_seconds")
    tol = pop!(d, "tolerance")
    m = REopt.xpress_model(timeout, tol)
    @info "Starting REopt with timeout of $(timeout) seconds..."
	error_response = Dict()
	results = Dict()
	try
    	results = REopt.reopt(m, d)
	catch e
		@error "Something went wrong in the Julia code!" exception=(e, catch_backtrace())
		error_response["error"] = sprint(showerror, e)
	end
    optimizer = backend(m)
	finalize(optimizer)
	GC.gc()
    Xpress.postsolve(optimizer.inner)
	if isempty(error_response)
    	@info "REopt model solved with status $(results["status"])."
    	return HTTP.Response(200, JSON.json(results))
	else
		@info "An error occured in the Julia code."
		return HTTP.Response(500, JSON.json(error_response))
	end
end

function reopt(req::HTTP.Request)
    d = JSON.parse(String(req.body))
	settings = d["Settings"]
	timeout_seconds = -pop!(settings, "timeout_seconds")
	optimality_tolerance = pop!(settings, "optimality_tolerance")
	ms = nothing
	if get(settings, "run_bau", true)
		m1 = direct_model(
			Xpress.Optimizer(
				MAXTIME = timeout_seconds,
				MIPRELSTOP = optimality_tolerance,
				OUTPUTLOG = 0
			)
		)
		m2 = direct_model(
			Xpress.Optimizer(
				MAXTIME = timeout_seconds,
				MIPRELSTOP = optimality_tolerance,
				OUTPUTLOG = 0
			)
		)
		ms = [m1, m2]
	else
		ms = direct_model(
			Xpress.Optimizer(
				MAXTIME = timeout_seconds,
				MIPRELSTOP = optimality_tolerance,
				OUTPUTLOG = 0
			)
		)
	end
    @info "Starting REopt..."
    error_response = Dict()
    results = Dict()
    try
        results = reoptjl.run_reopt(ms, d)
    catch e
        @error "Something went wrong in the Julia code!" exception=(e, catch_backtrace())
        error_response["error"] = sprint(showerror, e)
    end
	if typeof(ms) <: AbstractArray
		finalize(backend(ms[1]))
		finalize(backend(ms[2]))
	else
		finalize(backend(ms))
	end
    GC.gc()
    if isempty(error_response)
        @info "REopt model solved with status $(results["status"])."
        return HTTP.Response(200, JSON.json(results))
    else
        @info "An error occured in the Julia code."
        return HTTP.Response(500, JSON.json(error_response))
    end
end

function ghpghx(req::HTTP.Request)
    inputs_dict = JSON.parse(String(req.body))
    @info "Starting GHPGHX" #with timeout of $(timeout) seconds..."
    results, inputs_params = GHPGHX.ghp_model(inputs_dict)
    # Create a dictionary of the results data needed for REopt
    ghpghx_results = GHPGHX.get_ghpghx_results_for_reopt(results, inputs_params)
    @info "GHPGHX model solved" #with status $(results["status"])."
    return HTTP.Response(200, JSON.json(ghpghx_results))
end

function health(req::HTTP.Request)
    return HTTP.Response(200, JSON.json(Dict("Julia-api"=>"healthy!")))
end

# define REST endpoints to dispatch to "service" functions
const ROUTER = HTTP.Router()

HTTP.@register(ROUTER, "POST", "/job", job)
HTTP.@register(ROUTER, "POST", "/reopt", reopt)
HTTP.@register(ROUTER, "POST", "/ghpghx", ghpghx)
HTTP.@register(ROUTER, "GET", "/health", health)
HTTP.serve(ROUTER, "0.0.0.0", 8081, reuseaddr=true)


text:
# slab ocean ODE
function ocn_rhs!(du, u, (parameters, F_accumulated), t)
    """
    Slab layer equation
        d(T_sfc)/dt = - (F_accumulated) / (h_ocn * ρ_ocn * c_ocn)
        where
            F_accumulated = F_integrated / Δt_coupler
    """
    @unpack ocn_h, ocn_ρ, ocn_c = parameters
    @unpack T_sfc = du

    @. T_sfc = (-F_accumulated) / (ocn_h * ocn_ρ * ocn_c)
end

# set up domain
function hspace_1D(xlim = (-π, π), npoly = 0, helem = 10)
    FT = Float64

    domain = Domains.IntervalDomain(Geometry.XPoint{FT}(xlim[1]) .. Geometry.XPoint{FT}(xlim[2]), periodic = true)
    mesh = Meshes.IntervalMesh(domain; nelems = helem)
    topology = Topologies.IntervalTopology(mesh)

    # Finite Volume Approximation: Gauss-Lobatto with 1pt per element
    quad = Spaces.Quadratures.GL{npoly + 1}()
    space = Spaces.SpectralElementSpace1D(topology, quad)

    return space
end

# init simulation
function ocn_init(; xmin = -1000, xmax = 1000, helem = 20, npoly = 0)

    # construct domain spaces - get only surface layer (NB: z should be zero, not z = first central height)
    space = hspace_1D((xmin, xmax), npoly, helem)
    coords = Fields.coordinate_field(space)
    domain = space

    # initial condition
    T_sfc = map(coords) do coord
        T_sfc = 267.0
    end

    # prognostic variable
    Y = Fields.FieldVector(T_sfc = T_sfc)

    return Y, domain
end


text:
"""
    Nghttp2 Julia bindings.
"""

"""
    Items:
[x] add basic unit test, server, client
[x] unit test to submit_response with payload > 16 KB
[ ] verify trailers are sent at the end with request is send with multiple packages
[ ] add unit test with invalid response
[x] return an error if failure occurs in Http2Stream
[ ] nghttp2_on_stream_close_callback, close stream on error
[ ] ensure sessions are destroyed
[x] submit request should return correct stream
[x] create a new request stream

    - nghttp2_xxx functions returns error code, except _new functions.

        session reading loop
            read and dispatch

            single Http2Session
                read()

            multiple entries from read stream
                until it is available

    Multiple Http2Streams are reading from a single Http2Session.

        ┌─────────────────┐
        │Http2Stream::read│─ ─ ─
        └─────────────────┘     │        session.read_lock
        ┌─────────────────┐      ─ ─ ▶    ╔════╗      ┌─────────────┐
        │Http2Stream::read│───────────────╣Lock╠─────▶│Session::read│
        └─────────────────┘      ─ ─ ▶    ╚════╝      └─────────────┘
        ┌─────────────────┐     │
        │Http2Stream::read│─ ─ ─
        └─────────────────┘

"""

module Nghttp2

export Http2ClientSession, Http2ServerSession, Http2Stream, Http2ProtocolError
export send, recv, try_recv, submit_request, submit_response, read, eof, bytesavailable, close, isopen
export nghttp2_version

using nghttp2_jll
using BitFlags
using Sockets

const Option{T} = Union{Nothing,T} where {T}

"""
    Error codes used by Nghttp2 library.
"""
@enum(Nghttp2Error::Int32,
    # Invalid argument passed.
    NGHTTP2_ERR_INVALID_ARGUMENT = -501,
    # Out of buffer space.
    NGHTTP2_ERR_BUFFER_ERROR = -502,
    # The specified protocol version is not supported.
    NGHTTP2_ERR_UNSUPPORTED_VERSION = -503,
    # Used as a return value from nghttp2_send_callback, nghttp2_recv_callback and nghttp2_send_data_callback to indicate that the operation would block.
    NGHTTP2_ERR_WOULDBLOCK = -504,
    # General protocol error.
    NGHTTP2_ERR_PROTO = -505,
    # The frame is invalid.
    NGHTTP2_ERR_INVALID_FRAME = -506,
    # The peer performed a shutdown on the connection.
    NGHTTP2_ERR_EOF = -507,
    # Used as a return value from nghttp2_data_source_read_callback() to indicate that data transfer is postponed. See nghttp2_data_source_read_callback() for details.
    NGHTTP2_ERR_DEFERRED = -508,
    # Stream ID has reached the maximum value. Therefore no stream ID is available.
    NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE = -509,
    # The stream is already closed; or the stream ID is invalid.
    NGHTTP2_ERR_STREAM_CLOSED = -510,
    # RST_STREAM has been added to the outbound queue. The stream is in closing state.
    NGHTTP2_ERR_STREAM_CLOSING = -511,
    # The transmission is not allowed for this stream (e.g., a frame with END_STREAM flag set has already sent).
    NGHTTP2_ERR_STREAM_SHUT_WR = -512,
    # The stream ID is invalid.
    NGHTTP2_ERR_INVALID_STREAM_ID = -513,
    # The state of the stream is not valid (e.g., DATA cannot be sent to the stream if response HEADERS has not been sent).
    NGHTTP2_ERR_INVALID_STREAM_STATE = -514,
    # Another DATA frame has already been deferred.
    NGHTTP2_ERR_DEFERRED_DATA_EXIST = -515,
    # Starting new stream is not allowed (e.g., GOAWAY has been sent and/or received).
    NGHTTP2_ERR_START_STREAM_NOT_ALLOWED = -516,
    # GOAWAY has already been sent.
    NGHTTP2_ERR_GOAWAY_ALREADY_SENT = -517,
    # The received frame contains the invalid header block.
    NGHTTP2_ERR_INVALID_HEADER_BLOCK = -518,
    # Indicates that the context is not suitable to perform the requested operation.
    NGHTTP2_ERR_INVALID_STATE = -519,
    # The user callback function failed due to the temporal error.
    NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE = -521,
    # The length of the frame is invalid, either too large or too small.
    NGHTTP2_ERR_FRAME_SIZE_ERROR = -522,
    # Header block inflate/deflate error.
    NGHTTP2_ERR_HEADER_COMP = -523,
    # Flow control error.
    NGHTTP2_ERR_FLOW_CONTROL = -524,
    # Insufficient buffer size given to function.
    NGHTTP2_ERR_INSUFF_BUFSIZE = -525,
    # Callback was paused by the application.
    NGHTTP2_ERR_PAUSE = -526,
    # There are too many in-flight SETTING frame and no more transmission of SETTINGS is allowed.
    NGHTTP2_ERR_TOO_MANY_INFLIGHT_SETTINGS = -527,
    # The server push is disabled.
    NGHTTP2_ERR_PUSH_DISABLED = -528,
    # DATA or HEADERS frame for a given stream has been already submitted and has not been fully processed yet.
    NGHTTP2_ERR_DATA_EXIST = -529,
    # The current session is closing due to a connection error or nghttp2_session_terminate_session() is called.
    NGHTTP2_ERR_SESSION_CLOSING = -530,
    # Invalid HTTP header field was received and stream is going to be closed.
    NGHTTP2_ERR_HTTP_HEADER = -531,
    # Violation in HTTP messaging rule.
    NGHTTP2_ERR_HTTP_MESSAGING = -532,
    # Stream was refused.
    NGHTTP2_ERR_REFUSED_STREAM = -533,
    # Unexpected internal error, but recovered.
    NGHTTP2_ERR_INTERNAL = -534,
    # Indicates that a processing was canceled.
    NGHTTP2_ERR_CANCEL = -535,
    # When a local endpoint expects to receive SETTINGS frame, it receives an other type of frame.
    NGHTTP2_ERR_SETTINGS_EXPECTED = -536,
    # When a local endpoint receives too many settings entries in a single SETTINGS frame.
    NGHTTP2_ERR_TOO_MANY_SETTINGS = -537,
    # The errors < nghttp2_error.NGHTTP2_ERR_FATAL mean that the library is under unexpected condition and processing was terminated.
    NGHTTP2_ERR_FATAL = -900,
    # Out of memory. This is a fatal error.
    NGHTTP2_ERR_NOMEM = -901,
    # The user callback function failed. This is a fatal error.
    NGHTTP2_ERR_CALLBACK_FAILURE = -902,
    # Invalid client magic (see NGHTTP2_CLIENT_MAGIC) was received and further processing is not possible.
    NGHTTP2_ERR_BAD_CLIENT_MAGIC = -903,
    # Possible flooding by peer was detected in this HTTP/2 session.
    NGHTTP2_ERR_FLOODED = -904)

"""
    The frame types in HTTP/2 specification.
"""
@enum(Nghttp2FrameType::UInt8,
    # The DATA frame.
    NGHTTP2_DATA = 0,
    # The HEADERS frame.
    NGHTTP2_HEADERS = 0x01,
    # The PRIORITY frame.
    NGHTTP2_PRIORITY = 0x02,
    # The RST_STREAM frame.
    NGHTTP2_RST_STREAM = 0x03,
    # The SETTINGS frame.
    NGHTTP2_SETTINGS = 0x04,
    # The PUSH_PROMISE frame.
    NGHTTP2_PUSH_PROMISE = 0x05,
    # The PING frame.
    NGHTTP2_PING = 0x06,
    # The GOAWAY frame.
    NGHTTP2_GOAWAY = 0x07,
    # The WINDOW_UPDATE frame.
    NGHTTP2_WINDOW_UPDATE = 0x08,
    # The CONTINUATION frame.  This frame type won't be passed to any
    # callbacks because the library processes this frame type and its
    # preceding HEADERS/PUSH_PROMISE as a single frame.
    NGHTTP2_CONTINUATION = 0x09,
    # The ALTSVC frame, which is defined in `RFC 7383
    # <https://tools.ietf.org/html/rfc7838#section-4>`_.
    NGHTTP2_ALTSVC = 0x0a,
    # The ORIGIN frame, which is defined by `RFC 8336
    # <https://tools.ietf.org/html/rfc8336>`_.
    NGHTTP2_ORIGIN = 0x0c)

"""
    The category of HEADERS, which indicates the role of the frame.  In
    HTTP/2 spec, request, response, push response and other arbitrary
    headers (e.g., trailer fields) are all called just HEADERS.  To
    give the application the role of incoming HEADERS frame, we define
    several categories.
"""
@enum(Nghttp2FrameHeadersCategory::UInt32,
    # The HEADERS frame is opening new stream, which is analogous to SYN_STREAM in SPDY.
    NGHTTP2_HCAT_REQUEST = 0,
    # The HEADERS frame is the first response headers, which is
    # analogous to SYN_REPLY in SPDY.
    NGHTTP2_HCAT_RESPONSE = 1,
    # The HEADERS frame is the first headers sent against reserved stream.
    NGHTTP2_HCAT_PUSH_RESPONSE = 2,
    # The HEADERS frame which does not apply for the above categories,
    # which is analogous to HEADERS in SPDY.  If non-final response
    # (e.g., status 1xx) is used, final response HEADERS frame will be
    # categorized here.
    NGHTTP2_HCAT_HEADERS = 3)

"""
    The flags for HTTP/2 frames.
    This enum defines all flags for all frames.
"""
@enum(Nghttp2FrameFlags::UInt8,
    # No flag set.
    NGHTTP2_FLAG_NONE = 0,
    # The END_STREAM flag.
    # The ACK flag.
    NGHTTP2_FLAG_END_STREAM = 0x01,
    # The END_HEADERS flag.
    NGHTTP2_FLAG_END_HEADERS = 0x04,
    # The PADDED flag.
    NGHTTP2_FLAG_PADDED = 0x08,
    # The PRIORITY flag.
    NGHTTP2_FLAG_PRIORITY = 0x20)

const NGHTTP2_FLAG_ACK = NGHTTP2_FLAG_END_STREAM

"""
    The status codes for the RST_STREAM and GOAWAY frames.
"""
@enum(Nghttp2ErrorCode::UInt32,
    # No errors.
    NGHTTP2_NO_ERROR = 0x0,
    # PROTOCOL_ERROR.
    NGHTTP2_PROTOCOL_ERROR = 0x1,
    # INTERNAL_ERROR.
    NGHTTP2_INTERNAL_ERROR = 0x2,
    # FLOW_CONTROL_ERROR.
    NGHTTP2_FLOW_CONTROL_ERROR = 0x3,
    # SETTINGS_TIMEOUT.
    NGHTTP2_SETTINGS_TIMEOUT = 0x4,
    # STREAM_CLOSED.
    NGHTTP2_STREAM_CLOSED = 0x5,
    # FRAME_SIZE_ERROR.
    NGHTTP2_FRAME_SIZE_ERROR = 0x6,
    # REFUSED_STREAM.
    NGHTTP2_REFUSED_STREAM = 0x7,
    # CANCEL.
    NGHTTP2_CANCEL = 0x8,
    # COMPRESSION_ERROR.
    NGHTTP2_COMPRESSION_ERROR = 0x9,
    #CONNECT_ERROR.
    NGHTTP2_CONNECT_ERROR = 0xa,
    # ENHANCE_YOUR_CALM.
    NGHTTP2_ENHANCE_YOUR_CALM = 0xb,
    # INADEQUATE_SECURITY.
    NGHTTP2_INADEQUATE_SECURITY = 0xc,
    # HTTP_1_1_REQUIRED.
    NGHTTP2_HTTP_1_1_REQUIRED = 0xd)

"""
    The flags for header field name/value pair.
"""
@enum(Nghttp2NvFlags::UInt8,
    # No flag set.
    NGHTTP2_NV_FLAG_NONE = 0,
    # Indicates that this name/value pair must not be indexed ("Literal
    # Header Field never Indexed" representation must be used in HPACK
    # encoding).  Other implementation calls this bit as "sensitive".
    NGHTTP2_NV_FLAG_NO_INDEX = 0x01,
    # This flag is set solely by application.  If this flag is set, the
    # library does not make a copy of header field name.  This could
    # improve performance.
    NGHTTP2_NV_FLAG_NO_COPY_NAME = 0x02,
    # This flag is set solely by application.  If this flag is set, the
    # library does not make a copy of header field value.  This could
    # improve performance.
    NGHTTP2_NV_FLAG_NO_COPY_VALUE = 0x04)

"""
    The SETTINGS ID.
"""
@enum(Nghttp2SettingsId::UInt32,
    # SETTINGS_HEADER_TABLE_SIZE
    NGHTTP2_SETTINGS_HEADER_TABLE_SIZE = 0x01,
    # SETTINGS_ENABLE_PUSH, client only option.
    NGHTTP2_SETTINGS_ENABLE_PUSH = 0x02,
    # SETTINGS_MAX_CONCURRENT_STREAMS
    NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS = 0x03,
    # SETTINGS_INITIAL_WINDOW_SIZE
    NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE = 0x04,
    # SETTINGS_MAX_FRAME_SIZE
    NGHTTP2_SETTINGS_MAX_FRAME_SIZE = 0x05,
    # SETTINGS_MAX_HEADER_LIST_SIZE
    NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE = 0x06)

"""
    The flags used to set in |data_flags| output parameter in :type:`nghttp2_data_source_read_callback`.
"""
@bitflag Nghttp2DataFlags::UInt32 begin
    # No flag set.
    NGHTTP2_DATA_FLAG_NONE = 0
    # Indicates EOF was sensed.
    NGHTTP2_DATA_FLAG_EOF = 0x01
    # Indicates that END_STREAM flag must not be set even if
    # NGHTTP2_DATA_FLAG_EOF is set.  Usually this flag is used to send
    # trailer fields with `nghttp2_submit_request()` or
    # `nghttp2_submit_response()`.
    NGHTTP2_DATA_FLAG_NO_END_STREAM = 0x02
    # Indicates that application will send complete DATA frame in
    # :type:`nghttp2_send_data_callback`.
    NGHTTP2_DATA_FLAG_NO_COPY = 0x04
end

"""
    The SETTINGS ID/Value pair.
"""
struct SettingsEntry
    settings_id::Nghttp2SettingsId
    value::UInt32
end

const DEFAULT_SERVER_SETTINGS = Vector{SettingsEntry}(
[
    SettingsEntry(NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100)
])

const DEFAULT_CLIENT_SETTINGS = Vector{SettingsEntry}(
[
    SettingsEntry(NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100),
    SettingsEntry(NGHTTP2_SETTINGS_ENABLE_PUSH, 1),
])

"""
    The name/value pair, which mainly used to represent header fields.
    It creates a copy of the strings using malloc.
    String pointers can be safely passed to C function.
"""
struct NVPair
    name::Ptr{Cchar}
    value::Ptr{Cchar}
    namelen::Csize_t
    valuelen::Csize_t
    flags::UInt8

    NVPair(nothing) = new(C_NULL, C_NULL, 0, 0, UInt8(0))

    NVPair(nv_pair::Pair{String,String}) = NVPair(nv_pair.first, nv_pair.second, UInt8(0))

    function NVPair(name::String, value::String, flags::UInt8)
        name_len = length(name)
        value_len = length(value)

        # Reserve 2 bytes for the string terminator
        name_ptr = Ptr{UInt8}(Libc.calloc(name_len + value_len + 2, 1))

        value_ptr = name_ptr + name_len + 1

        GC.@preserve name unsafe_copyto!(name_ptr, pointer(name), name_len)
        GC.@preserve value unsafe_copyto!(value_ptr, pointer(value), value_len)

        return nv_pair = new(name_ptr, value_ptr, name_len, value_len, flags)
    end
end

free(nv_pair::NVPair) = Libc.free(nv_pair.name)

"""
    Helper functions to convert vector of string pairs to vector of NVPair.
"""
const NVPairs = Vector{NVPair}

const StringPairs = Vector{Pair{String,String}}

function convert_to_nvpairs(input::StringPairs)
    nv_pairs = NVPairs()

    foreach(pair -> push!(nv_pairs, NVPair(pair)), input)

    finalizer(free, nv_pairs)

    return nv_pairs
end

free(nv_pairs::NVPairs) =
    for i in 1:length(nv_pairs)
        free(nv_pairs[i])
        nv_pairs[i] = NVPair(nothing)
    end

"""
    Nghttp2 library options.
"""
mutable struct Nghttp2Option
    ptr::Ptr{Cvoid}

    """
        Creates an instance of Nghttp2Options.
    """
    function Nghttp2Option()::Nghttp2Option
        nghttp2_option = new(C_NULL)
        result = ccall(
            (:nghttp2_option_new, libnghttp2),
            Cint,
            (Ref{Nghttp2Option},),
            nghttp2_option)
        if result != 0
            throw(Http2ProtocolError(Nghttp2Error(result)))
        end

        finalizer(free, nghttp2_option)
        return nghttp2_option
    end
end

function free(nghttp2_option::Nghttp2Option)
    ccall(
        (:nghttp2_option_del, libnghttp2),
        Cvoid,
        (Nghttp2Option,),
        nghttp2_option)

    nghttp2_option.ptr = C_NULL
    return nothing
end

mutable struct Nghttp2Session
    ptr::Ptr{Cvoid}
end

mutable struct Nghttp2Frame
    ptr::Ptr{Cvoid}
end

"""
    The frame header.
"""
struct Nghttp2FrameHeader
    # The length field of this frame, excluding frame header.
    length::Csize_t
    # The stream identifier (aka, stream ID)
    stream_id::Int32
    # The type of this frame.  See `nghttp2_frame_type`.
    type::Nghttp2FrameType
    # The flags.
    flags::UInt8
    # Reserved bit in frame header.  Currently, this is always set to 0
    # and application should not expect something useful in here.
    reserved::UInt8
end

"""
    The structure to specify stream dependency.
"""
struct Nghttp2PrioritySpec
    # The stream ID of the stream to depend on. Specifying 0 makes stream not depend any other stream.
    stream_id::Int32
    # The weight of this dependency.
    weight::Int32
    # Nonzero means exclusive dependency.
    exclusive::UInt8
end

"""
    The HEADERS frame.
"""
struct Nghttp2HeadersFrame
    # The frame header.
    frame_header::Nghttp2FrameHeader
    # The length of the padding in this frame. This includes PAD_HIGH and PAD_LOW.
    pad_len::Csize_t
    # The priority specification
    pri_spec::Nghttp2PrioritySpec
    # The name/value pairs.
    nva::Ptr{Cvoid}
    # The number of name/value pairs in |nva|.
    nvlen::Csize_t
    # The category of this HEADERS frame.
    cat::Nghttp2FrameHeadersCategory
end

"""
    Nghttp2 library information.
"""
struct Nghttp2Info
    age::Cint
    version_num::Cint
    version_str::Cstring
    proto_str::Cstring
end

mutable struct DataSource
    send_stream::IO
    trailer::NVPairs
end

mutable struct DataProvider
    data_source::Ptr{DataSource}
    read_callback::Ptr{Cvoid}
end

"""
    Http2ProtocolError.
"""
mutable struct Http2ProtocolError <: Exception
    lib_error_code::Nghttp2Error
    msg::String

    Http2ProtocolError(lib_error_code::Nghttp2Error, msg::String) = new(lib_error_code, msg)

    function Http2ProtocolError(lib_error_code::Nghttp2Error)
        str_error = ccall(
            (:nghttp2_strerror, libnghttp2),
            Cstring,
            (Nghttp2Error,),
            lib_error_code)

        return new(lib_error_code, unsafe_string(str_error))
    end
end

abstract type AbstractSession end

"""
    Library definition.
"""
mutable struct Http2Stream <: IO
    session::AbstractSession
    stream_id::Int32
    buffer::IOBuffer
    headers::Dict{String,String}
    lock::ReentrantLock
    eof::Bool

    Http2Stream(session::AbstractSession, stream_id::Int32) =
        return new(
            session,
            stream_id,
            PipeBuffer(),
            Dict{String,String}(),
            ReentrantLock(),
            false)
end

"""
    Tests whether an HTTP2 stream is at end-of-file.
"""
function Base.eof(http2_stream::Http2Stream)::Bool
    lock(http2_stream.lock) do
        return http2_stream.eof && eof(http2_stream.buffer)
    end
end

"""
    Returns number of bytes available for reading before a read from this stream will block.
"""
function Base.bytesavailable(http2_stream::Http2Stream)
    lock(http2_stream.lock) do
        return bytesavailable(http2_stream.buffer)
    end
end

"""
    Determines whether the underlying session IO is not yet closed. Even if the stream is closed, it may still have data to read in its buffer; use eof to check for the ability to read data.
"""
function Base.isopen(http2_stream::Http2Stream)
    return isopen(http2_stream.session.io)
end

"""
    Ensures there are requested number of bytes in the Http2Stream.
"""
function ensure_in_buffer(http2_stream::Http2Stream, nb::Integer)
    should_read = true

    # Process HTTP2 stack until there is no more available data in HTTP2 stream.
    while should_read
        lock(http2_stream.lock) do
            if bytesavailable(http2_stream.buffer) >= nb || http2_stream.eof
                should_read = false
            end
        end

        if should_read && internal_read!(http2_stream.session)
            continue
        end

        # Read failed
        break
    end

    # Throw exception if session is in error state.
    if has_error(http2_stream.session)
        throw(http2_stream.session.exception)
    end
end

"""
    Reads available data from the HTTP2 stream.
"""
function Base.read(http2_stream::Http2Stream)::Vector{UInt8}
    ensure_in_buffer(http2_stream, 1)

    lock(http2_stream.lock) do
        result_buffer = read(http2_stream.buffer)
        return result_buffer
    end
end

"""
    Reads at most nb bytes from from the HTTP2 stream.
"""
function Base.read(http2_stream::Http2Stream, nb::Integer)::Vector{UInt8}
    ensure_in_buffer(http2_stream, nb)

    lock(http2_stream.lock) do
        if bytesavailable(http2_stream.buffer) < nb
            throw(EOFError())
        end

        result_buffer = read(http2_stream.buffer, nb)
        return result_buffer
    end
end

function Base.read(http2_stream::Http2Stream, ::Type{UInt8})::UInt8
    return read(http2_stream, Core.sizeof(UInt8))[begin + 0]
end

function Base.unsafe_read(http2_stream::Http2Stream, p::Ptr{UInt8}, nb::UInt)
    ensure_in_buffer(http2_stream, nb)

    lock(http2_stream.lock) do
        if bytesavailable(http2_stream.buffer) < nb
            throw(EOFError())
        end

        result_buffer = read(http2_stream.buffer, nb)
        GC.@preserve result_buffer unsafe_copyto!(p, pointer(result_buffer), nb)
    end
end

"""
    Writes the data to the Http2 stream.
"""
function Base.write(http2_stream::Http2Stream, out_buffer::Vector{UInt8})
    lock(http2_stream.lock) do
        # Write the data to the steam.
        return write(http2_stream.buffer, out_buffer)
    end
end

"""
    Internal HTTP2 session.
"""
mutable struct Session <: AbstractSession
    io::IO
    nghttp2_session::Nghttp2Session
    recv_streams::Dict{Int32,Http2Stream}
    recv_streams_id::Set{Int32}
    exception::Option{Exception}
    lock::ReentrantLock
    read_lock::ReentrantLock

    function Session(io::IO, nghttp2_session::Nghttp2Session)
        return new(
            io,
            nghttp2_session,
            Dict{Int32,Http2Stream}(),
            Set{Int32}(),
            nothing,
            ReentrantLock(),
            ReentrantLock())
    end
end

"""
    Retrieves the Session object from the nghttp2_session data.
    The Session object must be pinned.
"""
function session_from_data(user_data::Ptr{Cvoid})::Session
    session::Session = unsafe_pointer_to_objref(user_data)
    return session
end

"""
    Sets the session object in Nghttp2Session structure.
    The Session object must be pinned.
"""
function session_set_data(session::Session)
    return ccall(
        (:nghttp2_session_set_user_data, libnghttp2),
        Cvoid,
        (Nghttp2Session, Ptr{Cvoid}),
        session.nghttp2_session,
        pointer_from_objref(session))
end

function nghttp2_option_set_no_auto_window_update(nghttp2_option::Nghttp2Option, value::Cint)
    return ccall(
        (:nghttp2_option_set_no_auto_window_update, libnghttp2),
        Cvoid,
        (Nghttp2Option, Cint),
        nghttp2_option,
        value)
end

"""
    Session callbacks.
"""
mutable struct Nghttp2SessionCallbacks
    ptr::Ptr{Cvoid}

    function Nghttp2SessionCallbacks()::Nghttp2SessionCallbacks
        callbacks = new(C_NULL)
        result = ccall(
            (:nghttp2_session_callbacks_new, libnghttp2),
            Cint,
            (Ref{Nghttp2SessionCallbacks},),
            callbacks)
        if (result != 0)
            throw(Http2ProtocolError(Nghttp2Error(result)))
        end
        finalizer(free, callbacks)

        ccall(
            (:nghttp2_session_callbacks_set_on_frame_recv_callback, libnghttp2),
            Cvoid,
            (Nghttp2SessionCallbacks, Ptr{Cvoid}),
            callbacks,
            NGHTTP2_CALLBACKS.x.on_frame_recv_callback_ptr)

        ccall(
            (:nghttp2_session_callbacks_set_recv_callback, libnghttp2),
            Cvoid,
            (Nghttp2SessionCallbacks, Ptr{Cvoid}),
            callbacks,
            NGHTTP2_CALLBACKS.x.on_recv_callback_ptr)

        ccall(
            (:nghttp2_session_callbacks_set_on_begin_headers_callback, libnghttp2),
            Cvoid,
            (Nghttp2SessionCallbacks, Ptr{Cvoid}),
            callbacks,
            NGHTTP2_CALLBACKS.x.on_begin_headers_callback_ptr)

        ccall(
            (:nghttp2_session_callbacks_set_on_header_callback, libnghttp2),
            Cvoid,
            (Nghttp2SessionCallbacks, Ptr{Cvoid}),
            callbacks,
            NGHTTP2_CALLBACKS.x.on_header_recv_callback_ptr)

        ccall(
            (:nghttp2_session_callbacks_set_on_data_chunk_recv_callback, libnghttp2),
            Cvoid,
            (Nghttp2SessionCallbacks, Ptr{Cvoid}),
            callbacks,
            NGHTTP2_CALLBACKS.x.on_data_chunk_recv_callback_ptr)

        ccall(
            (:nghttp2_session_callbacks_set_send_callback, libnghttp2),
            Cvoid,
            (Nghttp2SessionCallbacks, Ptr{Cvoid}),
            callbacks,
            NGHTTP2_CALLBACKS.x.on_send_callback_ptr)

        ccall(
            (:nghttp2_session_callbacks_set_error_callback2, libnghttp2),
            Cvoid,
            (Nghttp2SessionCallbacks, Ptr{Cvoid}),
            callbacks,
            NGHTTP2_CALLBACKS.x.on_error_callback_ptr)

        ccall(
            (:nghttp2_session_callbacks_set_on_stream_close_callback, libnghttp2),
            Cvoid,
            (Nghttp2SessionCallbacks, Ptr{Cvoid}),
            callbacks,
            NGHTTP2_CALLBACKS.x.on_stream_close_callback_ptr)

        return callbacks
    end
end

function free(nghttp2_callbacks::Nghttp2SessionCallbacks)
    ccall(
        (:nghttp2_session_callbacks_del, libnghttp2),
        Cvoid,
        (Nghttp2SessionCallbacks,),
        nghttp2_callbacks)

    nghttp2_callbacks.ptr = C_NULL
    return nothing
end

"""
    Server session.

    Creates a new server session and stores the session object in the lookup dictionary.
"""
function server_session_new(io::IO)::Session
    nghttp2_session::Nghttp2Session = Nghttp2Session(C_NULL)

    nghttp2_session_callbacks = Nghttp2SessionCallbacks()

    result = ccall(
        (:nghttp2_session_server_new, libnghttp2),
        Cint,
        (Ref{Nghttp2Session}, Nghttp2SessionCallbacks, Ptr{Cvoid}),
        nghttp2_session,
        nghttp2_session_callbacks,
        C_NULL)
    if (result != 0)
        throw(Http2ProtocolError(Nghttp2Error(result)))
    end
    finalizer(free, nghttp2_session)

    session = Session(io, nghttp2_session)

    finalize(nghttp2_session_callbacks)

    return session
end

"""
    Client session.

    Creates a new client session.
"""
function client_session_new(io::IO)::Session
    nghttp2_session::Nghttp2Session = Nghttp2Session(C_NULL)

    nghttp2_session_callbacks = Nghttp2SessionCallbacks()

    result = ccall(
        (:nghttp2_session_client_new, libnghttp2),
        Cint,
        (Ref{Nghttp2Session}, Nghttp2SessionCallbacks, Ptr{Cvoid}),
        nghttp2_session,
        nghttp2_session_callbacks,
        C_NULL)
    if (result != 0)
        throw(Http2ProtocolError(Nghttp2Error(result)))
    end
    finalizer(free, nghttp2_session)

    session = Session(io, nghttp2_session)

    finalize(nghttp2_session_callbacks)

    return session
end

"""
    Nghttp2Session.
"""
function is_nghttp2_server_session(nghttp2_session::Nghttp2Session)::Bool
    result = ccall(
        (:nghttp2_session_check_server_session, libnghttp2),
        Cint,
        (Nghttp2Session,),
        nghttp2_session)

    return result != 0
end

function free(nghttp2_session::Nghttp2Session)
    ccall(
        (:nghttp2_session_del, libnghttp2),
        Cvoid,
        (Nghttp2Session,),
        nghttp2_session)

    nghttp2_session.ptr = C_NULL
    return nothing
end

function nghttp2_session_terminate_session(nghttp2_session::Nghttp2Session, error_code::UInt32)
    return ccall(
        (:nghttp2_session_terminate_session, libnghttp2),
        Cint,
        (Nghttp2Session, UInt32),
        nghttp2_session, error_code)
end

function nghttp2_submit_shutdown_notice(nghttp2_session::Nghttp2Session)
    return ccall(
        (:nghttp2_submit_shutdown_notice, libnghttp2),
        Cint,
        (Nghttp2Session,),
        nghttp2_session)
end

function nghttp2_session_send(nghttp2_session::Nghttp2Session)
    return ccall(
        (:nghttp2_session_send, libnghttp2),
        Cint,
        (Nghttp2Session,),
        nghttp2_session)
end

function nghttp2_session_submit_settings(nghttp2_session::Nghttp2Session, settings::Vector{SettingsEntry})
    return ccall(
        (:nghttp2_submit_settings, libnghttp2),
        Cint,
        (Nghttp2Session, UInt8, Ptr{Cvoid}, Csize_t),
        nghttp2_session,
        NGHTTP2_FLAG_NONE,
        pointer(settings),
        length(settings))
end

function nghttp2_submit_goaway(nghttp2_session::Nghttp2Session)
    return ccall(
        (:nghttp2_submit_goaway, libnghttp2),
        Cint,
        (Nghttp2Session, UInt8, Cint, UInt32, Ptr{Cvoid}, Csize_t),
        nghttp2_session,
        NGHTTP2_FLAG_NONE,
        0,
        NGHTTP2_NO_ERROR,
        C_NULL, 0)
end

function nghttp2_session_mem_recv(nghttp2_session::Nghttp2Session, input_data::Vector{UInt8})
    return ccall(
        (:nghttp2_session_mem_recv, libnghttp2),
        Cssize_t,
        (Nghttp2Session, Ptr{UInt8}, Csize_t),
        nghttp2_session,
        input_data,
        length(input_data))
end

function nghttp2_session_recv(nghttp2_session::Nghttp2Session)
    return ccall(
        (:nghttp2_session_recv, libnghttp2),
        Cint,
        (Nghttp2Session,),
        nghttp2_session)
end

function nghttp2_submit_window_update(nghttp2_session::Nghttp2Session, stream_id::Int32, window_size_increment::Int32)
    return ccall(
        (:nghttp2_submit_window_update, libnghttp2),
        Cint,
        (Nghttp2Session, UInt8, Cint, Cint),
        nghttp2_session,
        NGHTTP2_FLAG_NONE,
        stream_id,
        window_size_increment)
end

"""
    Errors.
"""
nghttp2_error_to_string(error::Nghttp2Error) =
    unsafe_string(
        ccall(
            (:nghttp2_strerror, libnghttp2),
            Cstring,
            (Cint,),
            error))

nghttp2_version() =
    unsafe_load(
        ccall(
            (:nghttp2_version, libnghttp2),
            Ptr{Nghttp2Info},
            (Cint,),
            0))

function Base.show(io::IO, nghttp2_info::Nghttp2Info)
    return println(
        io,
        """NGHttp2 lib: $(nghttp2_info.version_num)
path: $(libnghttp2)
version: $(unsafe_string(nghttp2_info.version_str))
protocol: $(unsafe_string(nghttp2_info.proto_str))""")
end

function Base.show(io::IO, nghttp2_frame_header::Nghttp2FrameHeader)
    return println(
        io,
        """Frame:
length: $(nghttp2_frame_header.length)
stream_id: $(nghttp2_frame_header.stream_id)
type: $(Nghttp2FrameType(nghttp2_frame_header.type))
flags: $(nghttp2_frame_header.flags)""")
end

function Base.show(io::IO, nv_pair::NVPair)
    local nv_pair_str::String
    if (nv_pair.name == C_NULL)
        nv_pair_str = "nothing"
    else
        nv_pair_str = "NVPair: { name: '$(unsafe_string(nv_pair.name)) $(nv_pair.name)', value: '$(unsafe_string(nv_pair.value)) $(nv_pair.value)' }"
    end

    return println(io, "NVPair: { $nv_pair_str }")
end

"""
    Callback implementation.
"""
function on_recv_callback(
    nghttp2_session::Nghttp2Session,
    buf::Ptr{UInt8},
    len::Csize_t,
    flags::Cint,
    user_data::Ptr{Cvoid})::Cssize_t
    # Get the server session object.
    session = session_from_data(user_data)

    result::Cssize_t = 0
    return result
end

function on_frame_recv_callback(nghttp2_session::Nghttp2Session, frame::Nghttp2Frame, user_data::Ptr{Cvoid})::Cint
    # Get the server session object.
    session = session_from_data(user_data)

    frame_header = unsafe_load(Ptr{Nghttp2FrameHeader}(frame.ptr))

    stream_id = frame_header.stream_id
    last_frame = stream_id != 0 && frame_header.flags & UInt8(NGHTTP2_FLAG_END_STREAM) != 0

    if last_frame
        # Last frame in the stream detected, mark the stream as EOF.
        local http2_stream::Http2Stream

        lock(session.lock) do
            return http2_stream = session.recv_streams[frame_header.stream_id]
        end

        lock(http2_stream.lock) do
            return http2_stream.eof = true
        end
    end

    result::Cint = 0
    return result
end

function on_begin_headers_callback(nghttp2_session::Nghttp2Session, frame::Nghttp2Frame, user_data::Ptr{Cvoid})::Cint
    # Get the server session object.
    session = session_from_data(user_data)

    frame_header = unsafe_load(Ptr{Nghttp2FrameHeader}(frame.ptr))

    # Create a new stream.
    lock(session.lock) do
        if !haskey(session.recv_streams, frame_header.stream_id)
            session.recv_streams[frame_header.stream_id] = Http2Stream(session, frame_header.stream_id)
            push!(session.recv_streams_id, frame_header.stream_id)
        end
    end

    result::Cint = 0
    return result
end

function on_header_recv_callback(
    nghttp2_session::Nghttp2Session,
    frame::Nghttp2Frame,
    name::Ptr{UInt8},
    namelen::Csize_t,
    value::Ptr{UInt8},
    valuelen::Csize_t,
    flags::UInt8,
    user_data::Ptr{Cvoid})::Cint
    # Get the server session object.
    session = session_from_data(user_data)

    frame_header = unsafe_load(Ptr{Nghttp2FrameHeader}(frame.ptr))

    # Copy from received buffer to the local data.
    header_name = Vector{UInt8}(undef, namelen)
    GC.@preserve header_name unsafe_copyto!(pointer(header_name), name, namelen)

    header_value = Vector{UInt8}(undef, valuelen)
    GC.@preserve header_value unsafe_copyto!(pointer(header_value), value, valuelen)

    # Store the header in the session stream.
    local recv_stream::Http2Stream

    lock(session.lock) do
        return recv_stream = session.recv_streams[frame_header.stream_id]
    end

    lock(recv_stream.lock) do
        return recv_stream.headers[String(header_name)] = String(header_value)
    end

    result::Cint = 0
    return result
end

function on_data_chunk_recv_callback(
    nghttp2_session::Nghttp2Session,
    flags::UInt8,
    stream_id::Cint,
    buf::Ptr{UInt8},
    len::Csize_t,
    user_data::Ptr{Cvoid})::Cint
    # Get the server session object.
    session = session_from_data(user_data)

    # Copy from received buffer to the local data.
    data = Vector{UInt8}(undef, len)
    GC.@preserve data unsafe_copyto!(pointer(data), buf, len)

    # Write received data to the received stream buffer.
    local http2_stream::Http2Stream

    lock(session.lock) do
        return http2_stream = session.recv_streams[stream_id]
    end

    write(http2_stream, data)

    result::Cint = 0
    return result
end

function on_send_callback(
    nghttp2_session::Nghttp2Session,
    data::Ptr{UInt8},
    length::Csize_t,
    flags::Cint,
    user_data::Ptr{Cvoid})::Csize_t
    # Get the server session object.
    session = session_from_data(user_data)

    # Copy send data to the buffer.
    out_buffer = Vector{UInt8}(undef, length)

    GC.@preserve out_buffer unsafe_copyto!(pointer(out_buffer), data, length)

    try
        write(session.io, out_buffer)
    catch ex
        lock(session.lock) do
            return session.exception = ex
        end
        return Int(NGHTTP2_ERR_CALLBACK_FAILURE) % Csize_t
    end

    return length
end

function on_error_callback(
    nghttp2_session::Nghttp2Session,
    lib_error_code::Cint,
    msg::Ptr{UInt8},
    len::Csize_t,
    user_data::Ptr{Cvoid})::Cint
    # Get the server session object.
    session = session_from_data(user_data)
    println("on_error_callback session:$(session.nghttp2_session) nghtt2_error_code: $(lib_error_code)")

    # Create HTTP2 error object, include Nghttp2 error.
    http2_protocol_error = Http2ProtocolError(Nghttp2Error(lib_error_code), unsafe_string(msg))

    lock(session.lock) do
        return session.exception = http2_protocol_error
    end

    @show session.exception

    result::Cint = 0
    return result
end

function on_data_source_read_callback(
    nghttp2_session::Nghttp2Session,
    stream_id::Cint,
    buf::Ptr{UInt8},
    buf_length::Csize_t,
    data_flags::Ptr{UInt32},
    data_source::Ptr{Ptr{IOBuffer}},
    user_data::Ptr{Cvoid})::Cssize_t
    # Get DataSource object.
    data_source = unsafe_load(data_source)
    data_source = unsafe_pointer_to_objref(data_source)

    # TODO on_data_source_read_callback here
    in_buffer = read(data_source.send_stream, buf_length)
    in_length = length(in_buffer)

    GC.@preserve in_buffer unsafe_copyto!(buf, pointer(in_buffer), in_length)

    source_stream_eof = eof(data_source.send_stream)
    source_has_trailer = source_stream_eof && length(data_source.trailer) != 0

    send_data_flags::Nghttp2DataFlags = NGHTTP2_DATA_FLAG_NONE
    if source_stream_eof
        send_data_flags |= NGHTTP2_DATA_FLAG_EOF
    end

    if source_has_trailer
        send_data_flags |= NGHTTP2_DATA_FLAG_NO_END_STREAM

        # Submit the trailer.
        result = ccall(
            (:nghttp2_submit_trailer, libnghttp2),
            Cint,
            (Nghttp2Session, Int32, Ptr{Cvoid}, Csize_t),
            nghttp2_session,
            stream_id,
            pointer(data_source.trailer),
            length(data_source.trailer))
    end

    unsafe_store!(data_flags, UInt32(send_data_flags))

    result::Cssize_t = in_length
    return result
end

function on_stream_close_callback(
    nghttp2_session::Nghttp2Session,
    stream_id::Cint,
    error_code::UInt32,
    user_data::Ptr{Cvoid})::Cint
    # Get the server session object.
    session = session_from_data(user_data)

    result::Cint = 0
    return result
end

"""
    Nghttp2 callbacks.
"""
struct Nghttp2Callbacks
    on_recv_callback_ptr::Ptr{Nothing}
    on_frame_recv_callback_ptr::Ptr{Nothing}
    on_begin_headers_callback_ptr::Ptr{Nothing}
    on_header_recv_callback_ptr::Ptr{Nothing}
    on_data_chunk_recv_callback_ptr::Ptr{Nothing}
    on_send_callback_ptr::Ptr{Nothing}
    on_error_callback_ptr::Ptr{Nothing}
    on_data_source_read_callback_ptr::Ptr{Nothing}
    on_stream_close_callback_ptr::Ptr{Nothing}

    function Nghttp2Callbacks()
        on_recv_callback_ptr = @cfunction on_recv_callback Cssize_t (
            Nghttp2Session,
            Ptr{UInt8},
            Csize_t,
            Cint,
            Ptr{Cvoid})

        on_frame_recv_callback_ptr = @cfunction on_frame_recv_callback Cint (
            Nghttp2Session,
            Nghttp2Frame,
            Ptr{Cvoid})

        on_begin_headers_callback_ptr = @cfunction on_begin_headers_callback Cint (
            Nghttp2Session,
            Nghttp2Frame,
            Ptr{Cvoid})

        on_header_recv_callback_ptr = @cfunction on_header_recv_callback Cint (
            Nghttp2Session,
            Nghttp2Frame,
            Ptr{UInt8},
            Csize_t,
            Ptr{UInt8},
            Csize_t,
            UInt8,
            Ptr{Cvoid})

        on_data_chunk_recv_callback_ptr = @cfunction on_data_chunk_recv_callback Cint (
            Nghttp2Session,
            UInt8,
            Cint,
            Ptr{UInt8},
            Csize_t,
            Ptr{Cvoid})

        on_send_callback_ptr = @cfunction on_send_callback Csize_t (
            Nghttp2Session,
            Ptr{UInt8},
            Csize_t,
            Cint,
            Ptr{Cvoid})

        on_error_callback_ptr = @cfunction on_error_callback Cint (
            Nghttp2Session,
            Cint,
            Ptr{UInt8},
            Csize_t,
            Ptr{Cvoid})

        on_data_source_read_callback_ptr = @cfunction on_data_source_read_callback Cssize_t (
            Nghttp2Session,
            Cint,
            Ptr{UInt8},
            Csize_t,
            Ptr{UInt32},
            Ptr{Ptr{IOBuffer}},
            Ptr{Cvoid})

        on_stream_close_callback_ptr = @cfunction on_stream_close_callback Cint (
            Nghttp2Session,
            Cint,
            UInt32,
            Ptr{Cvoid})

        return new(
            on_recv_callback_ptr,
            on_frame_recv_callback_ptr,
            on_begin_headers_callback_ptr,
            on_header_recv_callback_ptr,
            on_data_chunk_recv_callback_ptr,
            on_send_callback_ptr,
            on_error_callback_ptr,
            on_data_source_read_callback_ptr,
            on_stream_close_callback_ptr)
    end
end

"""
    HTTP2 session.
"""
function submit_settings(session::Session, settings::Vector{SettingsEntry})
    GC.@preserve session begin
        session_set_data(session)

        result = nghttp2_session_submit_settings(session.nghttp2_session, settings)
        if result != 0
            throw(Http2ProtocolError(Nghttp2Error(result)))
        end

        result = nghttp2_session_send(session.nghttp2_session)
        if result != 0
            throw(Http2ProtocolError(Nghttp2Error(result)))
        end
    end
end

"""
    Returns true, if session is in error state.
"""
function has_error(session::Session)::Bool
    lock(session.lock) do
        return !isnothing(session.exception)
    end
end

function set_error(session::Session, http2_protocol_error::Http2ProtocolError)
    lock(session.lock) do
        if !isnothing(session.exception)
            session.exception = http2_protocol_error
        end
    end
end

"""
    Reads from the session input IO and sends it to HTTP2 stack.
    Returns true if there is more data available.
    Ensure only one task is reading from the session.
"""
function internal_read!(session::Session)::Bool
    # Return if there are errors.
    if has_error(session)
        return false
    end

    lock(session.read_lock) do
        if bytesavailable(session.io) != 0 || (isreadable(session.io) && !eof(session.io))
            available_bytes = bytesavailable(session.io)
            input_data = read(session.io, available_bytes)

            GC.@preserve session begin
                session_set_data(session)

                result = nghttp2_session_mem_recv(session.nghttp2_session, input_data)
                if result < 0
                    set_error(session, Http2ProtocolError(Nghttp2Error(result)))
                end

                result = nghttp2_session_send(session.nghttp2_session)
                if result < 0
                    set_error(session, Http2ProtocolError(Nghttp2Error(result)))
                end
            end

            return true
        end
    end

    return false
end

"""
    Receives next Http2Stream from the session.
"""
function Sockets.recv(session::Session)::Option{Http2Stream}
    should_read = true

    while should_read
        lock(session.lock) do
            # Throw exception if errors occurred.
            if !isnothing(session.exception)
                throw(session.exception)
            end

            # Break, if there is no data available in the session's IO and
            # there are no more HTTP2 streams to return.
            if !isempty(session.recv_streams_id) || eof(session.io)
                should_read = false
            end
        end

        if should_read
            # Process received data through the HTTP2 stack.
            internal_read!(session)
            continue
        end

        break
    end

    lock(session.lock) do
        # If available, return a new HTTP2 stream.
        if !isempty(session.recv_streams_id)
            recv_stream_id = pop!(session.recv_streams_id)
            recv_stream = session.recv_streams[recv_stream_id]

            return recv_stream
        end

        eof(session.io)

        return nothing
    end
end

"""
    Receives expected Http2Stream from the session.
"""
function Sockets.recv(session::Session, stream_id::Int32)::Option{Http2Stream}
    should_read = true

    while should_read
        lock(session.lock) do
            # Throw exception if errors occurred.
            if !isnothing(session.exception)
                throw(session.exception)
            end

            # Break, if there is no data available in the session's IO and there are no more HTTP2 streams to return.
            if stream_id in session.recv_streams_id || !isreadable(session.io) || eof(session.io)
                should_read = false
            end
        end

        if should_read
            # Process received data through the HTTP2 stack.
            internal_read!(session)
            continue
        end

        break
    end

    lock(session.lock) do
        # If available, return a new HTTP2 stream.
        if stream_id in session.recv_streams_id
            delete!(session.recv_streams_id, stream_id)
            recv_stream = session.recv_streams[stream_id]

            return recv_stream
        end

        if isempty(session.recv_streams_id)
            eof(session.io)
        end

        return nothing
    end
end

"""
    Returns available Http2Streams.
    If there are no active Http2Streams, returns nothing.
"""
function try_recv(session::Session)::Option{Http2Stream}
    lock(session.lock) do
        # Throw exception if errors occurred.
        if !isnothing(session.exception)
            throw(session.exception)
        end

        # If available, return Http2Stream.
        if !isempty(session.recv_streams_id)
            recv_stream_id = pop!(session.recv_streams_id)
            recv_stream = session.recv_streams[recv_stream_id]

            return recv_stream
        end

        return nothing
    end
end

"""
    Sends the data in IO stream via HTTP2 session.
"""
function send(
    session::Session,
    stream_id::Int32,
    send_buffer::IO,
    header::StringPairs=StringPairs(),
    trailer::StringPairs=StringPairs())
    headers::NVPairs = convert_to_nvpairs(header)
    trailers::NVPairs = convert_to_nvpairs(trailer)

    GC.@preserve session send_buffer headers trailers begin
        session_set_data(session)

        data_source = DataSource(send_buffer, trailers)

        GC.@preserve data_source begin
            data_provider = DataProvider(
                pointer_from_objref(data_source),
                NGHTTP2_CALLBACKS.x.on_data_source_read_callback_ptr)

            GC.@preserve data_provider begin
                # send headers, data, and trailers
                result = ccall(
                    (:nghttp2_submit_response, libnghttp2),
                    Cint,
                    (Nghttp2Session, Int32, Ptr{Cvoid}, Csize_t, Ptr{Cvoid}),
                    session.nghttp2_session,
                    stream_id,
                    pointer(headers),
                    length(headers),
                    pointer_from_objref(data_provider))
                if result < 0
                    set_error(session, Http2ProtocolError(Nghttp2Error(result)))
                end

                result = nghttp2_session_send(session.nghttp2_session)
                if result < 0
                    set_error(session, Http2ProtocolError(Nghttp2Error(result)))
                end

                while !eof(send_buffer) && !has_error(session)
                    internal_read!(session)
                end
            end
        end
    end

    # Release headers and trailers after sending the frame.
    finalize(headers)
    finalize(trailers)

    # Throw if error occurred.
    if has_error(session)
        throw(session.exception)
    end
end

function send(session::Session, send_buffer::IO, header::StringPairs=StringPairs(), trailer::StringPairs=StringPairs())
    headers::NVPairs = convert_to_nvpairs(header)
    trailers::NVPairs = convert_to_nvpairs(trailer)

    GC.@preserve session send_buffer headers trailers begin
        session_set_data(session)

        data_source = DataSource(send_buffer, trailers)

        GC.@preserve data_source begin
            data_provider = DataProvider(
                pointer_from_objref(data_source),
                NGHTTP2_CALLBACKS.x.on_data_source_read_callback_ptr)

            GC.@preserve data_provider begin
                # send headers, data, and trailers
                stream_id = ccall(
                    (:nghttp2_submit_request, libnghttp2),
                    Cint,
                    (Nghttp2Session, Ptr{Cvoid}, Ptr{Cvoid}, Csize_t, Ptr{Cvoid}),
                    session.nghttp2_session,
                    C_NULL,
                    pointer(headers),
                    length(headers),
                    pointer_from_objref(data_provider))
                if stream_id < 0
                    throw(Http2ProtocolError(Nghttp2Error(stream_id)))
                end

                result = nghttp2_session_send(session.nghttp2_session)
                if result < 0
                    set_error(session, Http2ProtocolError(Nghttp2Error(result)))
                end

                while !eof(send_buffer) && !has_error(session)
                    internal_read!(session)
                end
            end
        end
    end

    # Release headers and trailers after sending the frame.
    finalize(headers)
    finalize(trailers)

    # Throw if error occurred.
    if has_error(session)
        throw(session.exception)
    end

    return stream_id
end

function is_server_session(session::Session)::Bool
    return is_nghttp2_server_session(session.nghttp2_session)
end

"""
    Public API.

    Wrapper classes around Http2Session.
"""

"""
    Http2 client session.
"""
struct Http2ClientSession
    session::Session
end

function open(io::IO)::Http2ClientSession
    session = client_session_new(io)
    result = submit_settings(session, DEFAULT_CLIENT_SETTINGS)

    return Http2ClientSession(session)
end

"""
    Submit a request.
"""
function submit_request(http2_client_session::Http2ClientSession, io::IO)
    return submit_request(http2_client_session, io, StringPairs(), StringPairs())
end

function submit_request(http2_client_session::Http2ClientSession, io::IO, header::StringPairs)
    return submit_request(http2_client_session, io, header, StringPairs())
end

function submit_request(
    http2_client_session::Http2ClientSession,
    io::IO,
    header::StringPairs,
    trailer::StringPairs)::Option{Http2Stream}
    # Send the request.
    response_stream_id = send(http2_client_session.session, io, header, trailer)

    response_stream = recv(http2_client_session.session, response_stream_id)

    return response_stream
end

"""
    Http2 server session.
"""
struct Http2ServerSession
    session::Session
end

function from_accepted(io::IO)::Http2ServerSession
    session = server_session_new(io)
    result = submit_settings(session, DEFAULT_SERVER_SETTINGS)

    return Http2ServerSession(session)
end

function Sockets.recv(http2_server_session::Http2ServerSession)
    return recv(http2_server_session.session)
end

function Base.close(http2_server_session::Http2ServerSession)
    result = nghttp2_submit_shutdown_notice(http2_server_session.session.nghttp2_session)
    if result != 0
        throw(Http2ProtocolError(Nghttp2Error(result)))
    end

    result = nghttp2_session_send(http2_server_session.session.nghttp2_session)
    if result != 0
        throw(Http2ProtocolError(Nghttp2Error(result)))
    end

    result = nghttp2_submit_goaway(http2_server_session.session.nghttp2_session)
    if result != 0
        throw(Http2ProtocolError(Nghttp2Error(result)))
    end

    result = nghttp2_session_send(http2_server_session.session.nghttp2_session)
    if result != 0
        throw(Http2ProtocolError(Nghttp2Error(result)))
    end

    return close(http2_server_session.session.io)
end

"""
    Http2 stream.
"""
function submit_response(
    http2_stream::Http2Stream,
    io::IO,
    header::StringPairs=StringPairs(),
    trailer::StringPairs=StringPairs())
    return send(
        http2_stream.session,
        http2_stream.stream_id,
        io,
        header,
        trailer)
end

"""
    Runme.
"""

const NGHTTP2_CALLBACKS = Ref{Nghttp2Callbacks}()

"""
    Initialize the module.
"""
function __init__()
    println("$(@__MODULE__)::__init")
    NGHTTP2_CALLBACKS.x = Nghttp2Callbacks()
    return nothing
end

end # module Nghttp2

max_stars_repo_name: grlap/nghttpjl

max_issues_repo_name: grlap/nghttpjl

max_forks_repo_name: grlap/nghttpjl


text:
@testset "GeometricBM" begin

using DiffEqNoiseProcess, DiffEqBase, Test, Statistics

μ = 1.0
σ = 2.0

W = GeometricBrownianMotionProcess(μ,σ,0.0,1.0,1.0)
dt = 0.1
calculate_step!(W,dt,nothing,nothing)
for i in 1:10
  accept_step!(W,dt,nothing,nothing)
end

W = GeometricBrownianMotionProcess(μ,σ,0.0,1.0,1.0)
prob = NoiseProblem(W,(0.0,1.0))
sol = solve(prob;dt=0.1)

W = GeometricBrownianMotionProcess(μ,σ,0//1,1.0,1.0)
prob_rational = NoiseProblem(W,(0,1))
dt = 1//10
sol = solve(prob_rational;dt=dt)

dt = dt/100
for t in dt:dt:1-dt
  sol(t)
end

t = 1.0
u0 = 1.0
expected_mean = u0*exp(μ*t)
expected_variance = u0^2*exp(2μ*t)*(exp(σ^2*t)-1)
ensemble_prob = EnsembleProblem(prob;output_func = (sol,i)-> (sol[end],false))
sol = solve(ensemble_prob;dt=0.1,trajectories=40000)
@test abs(mean(sol) - expected_mean) < 0.4
#abs(var(sol) - expected_variance) < 0.4 # Converges slowly

W = GeometricBrownianMotionProcess!(μ,σ,0.0,ones(2),ones(2))
prob = NoiseProblem(W,(0.0,1.0))
sol = solve(prob;dt=0.1)

end


text:
using MathOptFormat, Test

const MOI = MathOptFormat.MOI
const MOIU = MOI.Utilities

@testset "MathOptFormat tests" begin
    @testset "$(file)" for file in ["CBF", "LP", "MOF", "MPS"]
        include("$(file)/$(file).jl")
    end

    @testset "Copying options" begin
        models = [
            MathOptFormat.CBF.Model,
            MathOptFormat.LP.Model,
            MathOptFormat.MOF.Model,
            MathOptFormat.MPS.Model
        ]
        for src in models
            model_src = src()
            for dest in models
                model_dest = dest()
                MOI.copy_to(model_dest, model_src)
                @test !isempty(sprint(io -> MOI.write_to_file(model_dest, io)))
            end
            model_dest = MOIU.MockOptimizer(MOIU.Model{Float64}())
            MOI.copy_to(model_dest, model_src)
        end
    end

    @testset "Calling gzip_open" begin
        @test_throws ArgumentError MathOptFormat.gzip_open((x) -> nothing,
                                                           "dummy.gz", "a")
        @test_throws ArgumentError MathOptFormat.gzip_open((x) -> nothing,
                                                           "dummy.gz", "r+")
        @test_throws ArgumentError MathOptFormat.gzip_open((x) -> nothing,
                                                           "dummy.gz", "w+")
        @test_throws ArgumentError MathOptFormat.gzip_open((x) -> nothing,
                                                           "dummy.gz", "a+")

    end
end


text:
# A development file for testing implementation of non-stationary rouwenhorst method (à la FGP2019)
# built on existing function for stationary processes from package QuantEcon

# Citations
# FGP2019: Fella, Gallipoli, Pan, 2019
# KV2010: Kaplan, Violante, 2010

using QuantEcon, Plots, Random, Statistics

function simulate_PTincome(N, T, σ_u, σ_ε::Real, ρ::Real, σ_0)
# Simulates persistent-transitory, non-stationary income process as per FGP2019
# Only gaussian persistent shock is implemented
# Inputs are standard deviations
# σ_0 is the standard deviation of η_0 (denoted simply as σ in FGP2019)
    Y = zeros(Float64, N, T)
    η_t1 = σ_0 .* randn(N)

    for t in 1:T
        η_t = ρ * η_t1 + σ_ε .* randn(N)
        Y[:,t] = exp.( η_t + σ_u .* randn(N) )
        η_t1 = copy(η_t)
    end
    return Y'
end

### Check simulation
σ_ε = sqrt(0.0161)    #variance is σ_η^2=0.0161 in FGP2019
σ_u = 0. #sqrt(0.063)   #variance is σ_u^2=0.063 in FGP2019
σ_η0 = 0.             # In FGP2019
ρ = 0.9            # ρ ∈ {0.95,0.98,1} in FGP2019 section 4.1.1
N = 10000
T = 20
Random.seed!(1234)
Y = simulate_PTincome(N, T, σ_u, σ_ε, ρ, σ_η0)
plot(1:T,Y[:,1])
plot(var(Y,dims=2))
histogram(Y[1,:], bins = 100)
histogram!(Y[end,:], bins = 100, alpha = 0.5)
histogram!(Y[end-1,:], bins = 100, alpha = 0.5)

### Test rouwenhorst_ns
σ_ε = sqrt(0.0161)    #variance is σ_η^2=0.0161 in FGP2019
σ_u = sqrt(0.063)   #variance is σ_u^2=0.063 in FGP2019
σ_η0 = 0.             # In FGP2019
ρ = 0.98            # ρ ∈ {0.95,0.98,1} in FGP2019 section 4.1.1
T = 15
test = rouwenhorst_ns(19, T, 0.9, σ_ε, σ_η0)
test[1].state_values
test[1].p
heatmap(test[15].p)

# Compare to stationary
test_stat = rouwenhorst(19, 0.9, σ_ε)
heatmap(test_stat.p)
maximum(test[15].p - test_stat.p)

# Difference due to initial variance? This is constrained for stationary case
test = rouwenhorst_ns(19, T, 0.9, σ_ε, σ_ε/sqrt(1-ρ^2))
maximum(test[15].p - test_stat.p)
# virtually identical at steady state
heatmap(test[5].p)

### Markov Chain simulation for non-stationary

# Check that my simulation function gives same results as QuantEcon's for
# stationary process
T = 25
N = 100000
ρ = 0.9
mc_qe = rouwenhorst(19, ρ, σ_ε)

# QuantEcon's MC simulation
# simulate(mc, T, init = 10) |> plot
Y_mc_qe = zeros(T+1,N)
[Y_mc_qe[:,i] = simulate(mc_qe, T+1, init = 10) for i in 1:N]
plot(0:T, var(Y_mc_qe, dims = 2), label="QE MC", xlabel="t", ylabel="Variance", legend=:bottomright)

# DGP simulation
Y = log.(simulate_PTincome(N, T, 0, σ_ε, ρ, 0.))
plot!(1:T, var(Y, dims = 2), label="DGP")

# My MC simulation with QE MC
mcs = fill(mc_qe,T)
Y_mc_qe1 = zeros(length(mcs)+1,N)
[Y_mc_qe1[:,i] = simulate_ns(mcs, 10) for i in 1:N]
plot!(0:T, var(Y_mc_qe1, dims = 2), label="MC")

# Compare to theoretical variance
expected_vars = function(σ, ρ, σ_y0, T)
    var_y = zeros(T)
    for t in 1:T
        var_y[t] = ρ^2 * σ_y0^2 + σ^2
        σ_y0 = sqrt(var_y[t])
    end
    return var_y
end
scatter!(1:T, expected_vars(σ_ε,ρ,0.,T), label = "Theory")

# My MC simulation with my MC
mcs_ns = rouwenhorst_ns(19, T, ρ, σ_ε, 0.)
Y_mc_ns = zeros(length(mcs_ns)+1,N)
[Y_mc_ns[:,i] = simulate_ns(mcs_ns, 10) for i in 1:N]
plot!(0:T, var(Y_mc_ns, dims = 2), label="MC all mine")

# Means
plot(mean(Y, dims=2))
plot!(mean(Y_mc_qe, dims = 2))
plot!(mean(Y_mc_ns, dims = 2))

# Check for ρ=1
ρ=1
T=30
scatter(1:T, expected_vars(σ_ε,ρ,0.,T), label = "Theory", legend=:bottomright)

# DGP simulation
Y = log.(simulate_PTincome(N, T, 0, σ_ε, ρ, 0.))
plot!(1:T, var(Y, dims = 2), label="DGP")

# My MC simulation with my MC
mcs_ns = rouwenhorst_ns(19, T, ρ, σ_ε, 0.)
Y_mc_ns = zeros(length(mcs_ns)+1,N)
[Y_mc_ns[:,i] = simulate_ns(mcs_ns, 10) for i in 1:N]
plot!(0:T, var(Y_mc_ns, dims = 2), label="MC all mine")

### Now let's try to discretize the transitory-persistent process!!!
σ_ε = sqrt(0.0161)    #variance is σ_η^2=0.0161 in FGP2019
σ_u = sqrt(0.063)   #variance is σ_u^2=0.063 in FGP2019
σ_η0 = 0.             # In FGP2019
ρ = 1.            # ρ ∈ {0.95,0.98,1} in FGP2019 section 4.1.1
N = 200000
T = 40
n = 11

# Simulate DGP
Random.seed!(1234)
Y = simulate_PTincome(N, T, σ_u, σ_ε, ρ, σ_η0)
plot(1:T, var(Y, dims = 2), label="DGP", legend=:bottomright)

# Markov chain for persistent component
mcs_pers = rouwenhorst_ns(n, T, ρ, σ_ε, σ_η0)
η = zeros(length(mcs_FGP)+1,N)
[η[:,i] = simulate_ns(mcs_pers, round(Int,n/2)) for i in 1:N]
# plot!(0:T, var(η, dims = 2), label="eta_FGP")

# Markov simulation with actual transitory component
u = σ_u*randn(size(η))
Y_m = exp.(η + u)
plot!(0:T, var(Y_m, dims = 2), label="Y_m_FGP")

# Markov simulation with discretized transitory component
# Transitory component
mc_trans = rouwenhorst(n, 0., σ_u)
u_mc = zeros(T+1,N)
[u_mc[:,i] = simulate(mc_trans, T+1, init = round(Int,n/2)) for i in 1:N]
# plot!(0:T, var(u_mc, dims = 2), label="u_mc", xlabel="t", ylabel="Variance", legend=:bottomright)

# Put together
Y_mm = exp.(η + u_mc)
plot!(0:T, var(Y_mm, dims = 2), label="Y_mm_FGP")
xlims!(1.,35.)
ylims!(0.,1.8)

Y_mm = Y_mm[2:end,:]
sim_error = (var(Y_mm, dims=2)-var(Y,dims=2))./var(Y,dims=2)*100
sim_error[end]
sim_error = (mean(Y_mm, dims=2)-mean(Y,dims=2))./mean(Y,dims=2)*100
sim_error[end]
# These compare quite well with FPG2019 Table 1

### Need to include initial variance in simulate_ns argument if non-zero!

### Then we calculate expectations!!!

# PROBLEMS
# NONE

stop

# Boneyard
# function simulate_AR1(N, T, σ_ε, ρ, σ_0)
# # η_t = ρ_t *η_{t-1} + ε_t, ε_t ~ N(0,σ_εt)
# # σ_0 is the standard deviation of η_0 (denoted simply as σ in FGP2019)
#     Y = zeros(Float64, N, T-1)
#     η_t1 = sqrt(σ_0) .* randn(N)
#
#     for t in 1:(T-1)
#         η_t = η_t1 + sqrt(σ_ε) .* randn(N)
#         η_t1 = copy(η_t)
#     end
#     return Y
# end

# function rouwenhorst(N::Integer, ρ::Real, σ::Real, μ::Real=0.0; stationary::Bool=true)
# # Note: μ is not implemented for non-stationary case
#     if stationary
#         return rouwenhorst(N, ρ, σ, μ)
#     else
#         return rouwenhorst_ns(N, T, repeat(ρ,T), repeat(σ,T))
# end

# Y[:,2]
# test = function()
#     pY = fill(plot(1:T-1,Y[:,1]),10)
#     for i in 1:10
#         pY[i] = plot(1:T-1,Y[:,i])
#     end
#     return pY
# end
# test()
# display(pY[2])
#
# plot()
# for i in 1:10
#     display(plot!(1:T+1,Y_mc_qe[:,i]))
# end
# plot()
# for i in 1:10
#     display(plot!(1:T+1,Y_mc_qe1[:,i]))
# end
#
#
# #######
# plot()
# for i in 100:110
#     plot!(Y[:,i], legend=:none) |>display
#     plot!(Y_m[:,i], line=:dash) |>display
#     # plot!(η[:,i]) |>display
# end

# @doc doc"""
# Simulate one sample path of the non-stationary vector of Markov chains `mcs`.
# The resulting vector has the state values of `mcs` as elements.
#
# ### Arguments
#
# - `mcs::Vector{MarkovChain}` : Vector of MarkovChains.
# - `;init::Int` : Index of initial state
#
# ### Returns
#
# - `X::Vector` : Vector containing the sample path, with length
#   length(mcs)+1 (includes initial state)
# """
# function simulate_ns(mcs::Vector, init::Int)
#     ind_0 = init
#     ind = zeros(Int, length(mcs))
#     X = zeros(Real, length(mcs))
#
#     for t in eachindex(mcs)
#         X[t] = simulate(mcs[t], 2, init = ind_0)[2]
#         ind[t] = findmin( abs.(mcs[t].state_values .- X[t]) )[2]
#         if t < length(mcs)
#             ind_0 = ind[t] #findmin( abs.(mcs[t].state_values .- X[t]) )[2]
#         end
#     end
#     ind = [init; ind]
#     X = [mcs[1].state_values[init]; X]
# end


text:
"""
**Hose.jl** is for the situations where pipe is just not flexible enough.
"""
module Hose

export @hose

const PLACEHOLDER = :_

rewrite_apply(ff::Symbol, target) = :($ff($target)) # Function/macro application

function rewrite_apply(ff::Expr, target)
    if ff.head == :call
        insert!(ff.args, 2, target)
        ff
    elseif ff.head == :macrocall
        insert!(ff.args, 3, target)
        ff
    else
        :($ff($target))
    end
end

rewrite(ff::Symbol, target) = ifelse(ff == PLACEHOLDER, target,
                                     rewrite_apply(ff, target))

function rewrite(ff::Expr, target)
    replace(arg::Any) = arg  # For most things should be an identity.
    replace(arg::Symbol) = ifelse(arg == PLACEHOLDER, target, arg) # Placeholder symbol should get replaced.
    function replace(arg::Expr)
        rep = copy(arg)
        rep.args = map(replace,rep.args)
        rep
    end # Placeholder symbol should get replaced in expression arguments.

    rep_args = map(replace, ff.args)
    if ff.args != rep_args
        ff.args = rep_args # Placeholder subsitution
        ff
    else # No subsitution was done (no placeholder symbol found)
        rewrite_apply(ff, target) # Apply to a function/macro that is being returned by ff (ff could be a function call or something more complex).
    end
end

funnel(ee::Any) = ee # Identity for first (left most) input.

function funnel(ee::Expr)
    if (ee.args[1] == :|>) # If ee is a call to |>
        target = funnel(ee.args[2]) # Process left hand side
        rewrite(ee.args[3], target) # Rewrite |> right hand side using left hand side
    else
        ee # Not in a piping situtation
    end
end

"""
```julia
    @hose(expression)
```
The `@hose` macro processes its argument so that you pipe the left hand side (LHS) into functions of several arguments, macros, blocks, just index the LHS etc. The placeholder symbol for the LHS on the RHS is the underscore (`_`).

Examples:
```julia
    @hose a |> b(x, _) # produces b(x, a)
    @hose a |> _[b] # produces a[b]
    @hose a |> @testmacro _ b # equivalent of @testmacro(a, b)
```

Also behaves like [Lazy.jl](https://github.com/MikeInnes/Lazy.jl) `@>` macro:
```julia
    @hose a |> b(x) # produces b(a, x)
    @hose a |> @testmacro # produces equivalent of @testmacro(a)
```

Standard piping works as well. For other examples see `test/runtests.jl`.
"""
macro hose(ee)
    esc(funnel(ee))
end

end


text:
include("simplematrixsos.jl")
include("choi.jl")
include("sosdemo9.jl")
include("sosdemo10.jl")


text:
#!/usr/bin/env julia
#=
Common structures
del2z <delta.z@aliyun.com>
=#
import Base

abstract type Model end

# Euler's formula for complex number
mutable struct Polar <: Number
    r::Real
    # -180 ~ 180
    θ::Integer
    Polar(r::Real, θ::Integer) = new(r, θ % 181)
end

function Polar(z::Complex)
    @assert(!isnan(z) && !isinf(z), "Unbounded complex number.")
    Polar(abs(z), round(Int, angle(z) / π * 180))
end

Base.complex(z::Polar) = complex(z.r * cos(z.θ / 180 * π), z.r * sin(z.θ / 180 * π))
Base.show(io::IO, z::Polar) = print(io, z.r, " * exp(", z.θ, "im)")