text:
const __bodyfunction__ = Dict{Method,Any}()

# Find keyword "body functions" (the function that contains the body
# as written by the developer, called after all missing keyword-arguments
# have been assigned values), in a manner that doesn't depend on
# gensymmed names.
# `mnokw` is the method that gets called when you invoke it without
# supplying any keywords.
function __lookup_kwbody__(mnokw::Method)
    function getsym(arg)
        isa(arg, Symbol) && return arg
        @assert isa(arg, GlobalRef)
        return arg.name
    end

    f = get(__bodyfunction__, mnokw, nothing)
    if f === nothing
        fmod = mnokw.module
        # The lowered code for `mnokw` should look like
        #   %1 = mkw(kwvalues..., #self#, args...)
        #        return %1
        # where `mkw` is the name of the "active" keyword body-function.
        ast = Base.uncompressed_ast(mnokw)
        if isa(ast, Core.CodeInfo) && length(ast.code) >= 2
            callexpr = ast.code[end-1]
            if isa(callexpr, Expr) && callexpr.head == :call
                fsym = callexpr.args[1]
                if isa(fsym, Symbol)
                    f = getfield(fmod, fsym)
                elseif isa(fsym, GlobalRef)
                    if fsym.mod === Core && fsym.name === :_apply
                        f = getfield(mnokw.module, getsym(callexpr.args[2]))
                    elseif fsym.mod === Core && fsym.name === :_apply_iterate
                        f = getfield(mnokw.module, getsym(callexpr.args[3]))
                    else
                        f = getfield(fsym.mod, fsym.name)
                    end
                else
                    f = missing
                end
            else
                f = missing
            end
        else
            f = missing
        end
        __bodyfunction__[mnokw] = f
    end
    return f
end

function _precompile_()
    ccall(:jl_generating_output, Cint, ()) == 1 || return nothing
    Base.precompile(Tuple{Core.kwftype(typeof(Type)),NamedTuple{(:label, :blank),Tuple{Symbol,Bool}},Type{EmptyLayout}})
    Base.precompile(Tuple{Core.kwftype(typeof(Type)),NamedTuple{(:label, :width, :height),Tuple{Symbol,Symbol,Length{:pct,Float64}}},Type{EmptyLayout}})
    Base.precompile(Tuple{Core.kwftype(typeof(Type)),NamedTuple{(:parent,),Tuple{GridLayout}},Type{Subplot},GRBackend})
    Base.precompile(Tuple{Core.kwftype(typeof(Type)),NamedTuple{(:parent,),Tuple{GridLayout}},Type{Subplot},PlotlyBackend})
    Base.precompile(Tuple{Core.kwftype(typeof(Type)),NamedTuple{(:parent,),Tuple{Subplot{GRBackend}}},Type{Subplot},GRBackend})
    Base.precompile(Tuple{Core.kwftype(typeof(Type)),NamedTuple{(:parent,),Tuple{Subplot{PlotlyBackend}}},Type{Subplot},PlotlyBackend})
    Base.precompile(Tuple{Core.kwftype(typeof(_make_hist)),NamedTuple{(:normed, :weights),Tuple{Bool,Array{Int64,1}}},typeof(_make_hist),Tuple{Array{Float64,1}},Symbol})
    Base.precompile(Tuple{Core.kwftype(typeof(_make_hist)),NamedTuple{(:normed, :weights),Tuple{Bool,Nothing}},typeof(_make_hist),Tuple{Array{Float64,1},Array{Float64,1}},Int64})
    Base.precompile(Tuple{Core.kwftype(typeof(_make_hist)),NamedTuple{(:normed, :weights),Tuple{Bool,Nothing}},typeof(_make_hist),Tuple{Array{Float64,1},Array{Float64,1}},Tuple{Int64,Int64}})
    Base.precompile(Tuple{Core.kwftype(typeof(_make_hist)),NamedTuple{(:normed, :weights),Tuple{Bool,Nothing}},typeof(_make_hist),Tuple{Array{Float64,1}},Symbol})
    Base.precompile(Tuple{Core.kwftype(typeof(attr!)),NamedTuple{(:flip,),Tuple{Bool}},typeof(attr!),Axis})
    Base.precompile(Tuple{Core.kwftype(typeof(attr!)),NamedTuple{(:formatter,),Tuple{typeof(datetimeformatter)}},typeof(attr!),Axis})
    Base.precompile(Tuple{Core.kwftype(typeof(attr!)),NamedTuple{(:grid, :lims),Tuple{Bool,Tuple{Float64,Float64}}},typeof(attr!),Axis})
    Base.precompile(Tuple{Core.kwftype(typeof(attr!)),NamedTuple{(:grid, :lims, :flip),Tuple{Bool,Tuple{Float64,Float64},Bool}},typeof(attr!),Axis})
    Base.precompile(Tuple{Core.kwftype(typeof(attr!)),NamedTuple{(:grid,),Tuple{Bool}},typeof(attr!),Axis})
    Base.precompile(Tuple{Core.kwftype(typeof(attr!)),NamedTuple{(:gridlinewidth, :grid, :gridalpha, :gridstyle, :foreground_color_grid),Tuple{Int64,Bool,Float64,Symbol,RGBA{Float64}}},typeof(attr!),Axis})
    Base.precompile(Tuple{Core.kwftype(typeof(attr!)),NamedTuple{(:guide,),Tuple{String}},typeof(attr!),Axis})
    Base.precompile(Tuple{Core.kwftype(typeof(attr!)),NamedTuple{(:lims, :flip, :ticks, :guide),Tuple{Tuple{Int64,Int64},Bool,StepRange{Int64,Int64},String}},typeof(attr!),Axis})
    Base.precompile(Tuple{Core.kwftype(typeof(attr!)),NamedTuple{(:lims,),Tuple{Tuple{Float64,Float64}}},typeof(attr!),Axis})
    Base.precompile(Tuple{Core.kwftype(typeof(attr!)),NamedTuple{(:lims,),Tuple{Tuple{Int64,Float64}}},typeof(attr!),Axis})
    Base.precompile(Tuple{Core.kwftype(typeof(attr!)),NamedTuple{(:lims,),Tuple{Tuple{Int64,Int64}}},typeof(attr!),Axis})
    Base.precompile(Tuple{Core.kwftype(typeof(attr!)),NamedTuple{(:rotation,),Tuple{Int64}},typeof(attr!),Axis})
    Base.precompile(Tuple{Core.kwftype(typeof(attr!)),NamedTuple{(:scale, :guide),Tuple{Symbol,String}},typeof(attr!),Axis})
    Base.precompile(Tuple{Core.kwftype(typeof(attr!)),NamedTuple{(:ticks,),Tuple{UnitRange{Int64}}},typeof(attr!),Axis})
    Base.precompile(Tuple{Core.kwftype(typeof(default)),NamedTuple{(:titlefont, :legendfontsize, :guidefont, :tickfont, :guide, :framestyle, :yminorgrid),Tuple{Tuple{Int64,String},Int64,Tuple{Int64,Symbol},Tuple{Int64,Symbol},String,Symbol,Bool}},typeof(default)})
    Base.precompile(Tuple{Core.kwftype(typeof(gr_polyline)),NamedTuple{(:arrowside, :arrowstyle),Tuple{Symbol,Symbol}},typeof(gr_polyline),Array{Int64,1},Array{Float64,1}})
    Base.precompile(Tuple{Core.kwftype(typeof(gr_polyline)),NamedTuple{(:arrowside, :arrowstyle),Tuple{Symbol,Symbol}},typeof(gr_polyline),StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}},Array{Float64,1}})
    Base.precompile(Tuple{Core.kwftype(typeof(gr_polyline)),NamedTuple{(:arrowside, :arrowstyle),Tuple{Symbol,Symbol}},typeof(gr_polyline),StepRange{Int64,Int64},Array{Float64,1}})
    Base.precompile(Tuple{Core.kwftype(typeof(gr_polyline)),NamedTuple{(:arrowside, :arrowstyle),Tuple{Symbol,Symbol}},typeof(gr_polyline),UnitRange{Int64},Array{Float64,1}})
    Base.precompile(Tuple{Core.kwftype(typeof(gr_polyline)),NamedTuple{(:arrowside, :arrowstyle),Tuple{Symbol,Symbol}},typeof(gr_polyline),UnitRange{Int64},UnitRange{Int64}})
    Base.precompile(Tuple{Core.kwftype(typeof(heatmap)),Any,typeof(heatmap),Any,Vararg{Any,N} where N})
    Base.precompile(Tuple{Core.kwftype(typeof(hline!)),Any,typeof(hline!),Any})
    Base.precompile(Tuple{Core.kwftype(typeof(lens!)),Any,typeof(lens!),Any,Vararg{Any,N} where N})
    Base.precompile(Tuple{Core.kwftype(typeof(plot!)),NamedTuple{(:alpha, :seriestype),Tuple{Float64,Symbol}},typeof(plot!),Array{GeometryBasics.Point{2,Float64},1}})
    Base.precompile(Tuple{Core.kwftype(typeof(plot!)),NamedTuple{(:alpha, :seriestype),Tuple{Float64,Symbol}},typeof(plot!),Plot{GRBackend},Array{GeometryBasics.Point{2,Float64},1}})
    Base.precompile(Tuple{Core.kwftype(typeof(plot!)),NamedTuple{(:annotation,),Tuple{Array{Tuple{Int64,Float64,PlotText},1}}},typeof(plot!)})
    Base.precompile(Tuple{Core.kwftype(typeof(plot!)),NamedTuple{(:line, :seriestype),Tuple{Tuple{Int64,Symbol,Float64,Array{Symbol,2}},Symbol}},typeof(plot!),Array{Float64,2}})
    Base.precompile(Tuple{Core.kwftype(typeof(plot!)),NamedTuple{(:lw, :color),Tuple{Int64,Symbol}},typeof(plot!),Function,Float64,Vararg{Any,N} where N})
    Base.precompile(Tuple{Core.kwftype(typeof(plot!)),NamedTuple{(:lw, :color),Tuple{Int64,Symbol}},typeof(plot!),Plot{GRBackend},Function,Vararg{Any,N} where N})
    Base.precompile(Tuple{Core.kwftype(typeof(plot!)),NamedTuple{(:marker, :series_annotations, :seriestype),Tuple{Tuple{Int64,Float64,Symbol},Array{Any,1},Symbol}},typeof(plot!),Plot{GRBackend},StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}},Vararg{Any,N} where N})
    Base.precompile(Tuple{Core.kwftype(typeof(plot!)),NamedTuple{(:marker, :series_annotations, :seriestype),Tuple{Tuple{Int64,Float64,Symbol},Array{Any,1},Symbol}},typeof(plot!),Plot{PlotlyBackend},StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}},Vararg{Any,N} where N})
    Base.precompile(Tuple{Core.kwftype(typeof(plot!)),NamedTuple{(:marker, :series_annotations, :seriestype),Tuple{Tuple{Int64,Float64,Symbol},Array{Any,1},Symbol}},typeof(plot!),StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}},Array{Float64,1}})
    Base.precompile(Tuple{Core.kwftype(typeof(plot!)),NamedTuple{(:markersize, :c, :seriestype),Tuple{Int64,Symbol,Symbol}},typeof(plot!),Array{Float64,1}})
    Base.precompile(Tuple{Core.kwftype(typeof(plot!)),NamedTuple{(:seriestype, :inset),Tuple{Symbol,Tuple{Int64,BoundingBox{Tuple{Length{:w,Float64},Length{:h,Float64}},Tuple{Length{:w,Float64},Length{:h,Float64}}}}}},typeof(plot!),Array{Int64,1},Array{Float64,1}})
    Base.precompile(Tuple{Core.kwftype(typeof(plot!)),NamedTuple{(:seriestype, :inset),Tuple{Symbol,Tuple{Int64,BoundingBox{Tuple{Length{:w,Float64},Length{:h,Float64}},Tuple{Length{:w,Float64},Length{:h,Float64}}}}}},typeof(plot!),Plot{GRBackend},Array{Int64,1},Vararg{Any,N} where N})
    Base.precompile(Tuple{Core.kwftype(typeof(plot!)),NamedTuple{(:seriestype, :inset),Tuple{Symbol,Tuple{Int64,BoundingBox{Tuple{Length{:w,Float64},Length{:h,Float64}},Tuple{Length{:w,Float64},Length{:h,Float64}}}}}},typeof(plot!),Plot{PlotlyBackend},Array{Int64,1},Vararg{Any,N} where N})
    Base.precompile(Tuple{Core.kwftype(typeof(plot!)),NamedTuple{(:seriestype,),Tuple{Symbol}},typeof(plot!),Array{Int64,1}})
    Base.precompile(Tuple{Core.kwftype(typeof(plot!)),NamedTuple{(:title,),Tuple{String}},typeof(plot!)})
    Base.precompile(Tuple{Core.kwftype(typeof(plot!)),NamedTuple{(:w,),Tuple{Int64}},typeof(plot!),Array{Float64,1},Array{Float64,1},Vararg{Any,N} where N})
    Base.precompile(Tuple{Core.kwftype(typeof(plot!)),NamedTuple{(:w,),Tuple{Int64}},typeof(plot!),Plot{GRBackend},Array{Float64,1},Vararg{Any,N} where N})
    Base.precompile(Tuple{Core.kwftype(typeof(plot!)),NamedTuple{(:xgrid,),Tuple{Tuple{Symbol,Symbol,Int64,Symbol,Float64}}},typeof(plot!),Plot{GRBackend}})
    Base.precompile(Tuple{Core.kwftype(typeof(plot!)),NamedTuple{(:yaxis,),Tuple{Tuple{String,Symbol}}},typeof(plot!)})
    Base.precompile(Tuple{Core.kwftype(typeof(plot!)),NamedTuple{(:zcolor, :m, :ms, :lab, :seriestype),Tuple{Array{Float64,1},Tuple{Symbol,Float64,Stroke},Array{Float64,1},String,Symbol}},typeof(plot!),Array{Float64,1}})
    Base.precompile(Tuple{Core.kwftype(typeof(plot!)),NamedTuple{(:zcolor, :m, :ms, :lab, :seriestype),Tuple{Array{Float64,1},Tuple{Symbol,Float64,Stroke},Array{Float64,1},String,Symbol}},typeof(plot!),Plot{GRBackend},Array{Float64,1}})
    Base.precompile(Tuple{Core.kwftype(typeof(plot!)),NamedTuple{(:zcolor, :m, :ms, :lab, :seriestype),Tuple{Array{Float64,1},Tuple{Symbol,Float64,Stroke},Array{Float64,1},String,Symbol}},typeof(plot!),Plot{PlotlyBackend},Array{Float64,1}})
    Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:annotations, :leg),Tuple{Tuple{Int64,Float64,PlotText},Bool}},typeof(plot),Array{Float64,1}})
    Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:arrow,),Tuple{Int64}},typeof(plot),Array{Float64,1},Array{Float64,1}})
    Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:aspect_ratio, :seriestype),Tuple{Int64,Symbol}},typeof(plot),Array{String,1},Array{String,1},Vararg{Any,N} where N})
    Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:bins, :weights, :seriestype),Tuple{Symbol,Array{Int64,1},Symbol}},typeof(plot),Array{Float64,1}})
    Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:color, :line, :marker),Tuple{Array{Symbol,2},Tuple{Symbol,Int64},Tuple{Array{Symbol,2},Int64,Float64,Stroke}}},typeof(plot),Array{Array{T,1} where T,1}})
    Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:connections, :seriestype),Tuple{Tuple{Array{Int64,1},Array{Int64,1},Array{Int64,1}},Symbol}},typeof(plot),Array{Int64,1},Array{Int64,1},Vararg{Array{Int64,1},N} where N})
    Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:fill, :seriestype),Tuple{Bool,Symbol}},typeof(plot),StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}},StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}},Vararg{Any,N} where N})
    Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:framestyle, :title, :color, :layout, :label, :markerstrokewidth, :ticks, :seriestype),Tuple{Array{Symbol,2},Array{String,2},Base.ReshapedArray{Int64,2,UnitRange{Int64},Tuple{}},Int64,String,Int64,UnitRange{Int64},Symbol}},typeof(plot),Array{Array{Float64,1},1},Array{Array{Float64,1},1}})
    Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:grid, :title),Tuple{Tuple{Symbol,Symbol,Symbol,Int64,Float64},String}},typeof(plot),Array{Float64,1}})
    Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:lab, :w, :palette, :fill, :α),Tuple{String,Int64,PlotUtils.ContinuousColorGradient,Int64,Float64}},typeof(plot),StepRange{Int64,Int64},Array{Float64,2}})
    Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:label, :title, :xlabel, :linewidth, :legend),Tuple{Array{String,2},String,String,Int64,Symbol}},typeof(plot),Array{Function,1},Float64,Vararg{Float64,N} where N})
    Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:label,),Tuple{Array{String,2}}},typeof(plot),Array{AbstractArray{Float64,1},1}})
    Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:layout, :group, :linetype, :linecolor),Tuple{GridLayout,Array{String,1},Array{Symbol,2},Symbol}},typeof(plot),Array{Float64,1}})
    Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:layout, :label, :fillrange, :fillalpha),Tuple{Tuple{Int64,Int64},String,Int64,Float64}},typeof(plot),Plot{GRBackend},Plot{GRBackend},Vararg{Plot{GRBackend},N} where N})
    Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:layout, :label, :fillrange, :fillalpha),Tuple{Tuple{Int64,Int64},String,Int64,Float64}},typeof(plot),Plot{PlotlyBackend},Plot{PlotlyBackend},Vararg{Plot{PlotlyBackend},N} where N})
    Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:layout, :link),Tuple{Int64,Symbol}},typeof(plot),Plot{GRBackend},Plot{GRBackend}})
    Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:layout, :link),Tuple{Int64,Symbol}},typeof(plot),Plot{PlotlyBackend},Plot{PlotlyBackend}})
    Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:layout, :palette, :bg_inside),Tuple{Int64,Array{PlotUtils.ContinuousColorGradient,2},Array{Symbol,2}}},typeof(plot),Array{Float64,2}})
    Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:layout, :t, :leg, :ticks, :border),Tuple{GridLayout,Array{Symbol,2},Bool,Nothing,Symbol}},typeof(plot),Array{Float64,2}})
    Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:layout, :title, :titlelocation, :left_margin, :bottom_margin, :xrotation),Tuple{GridLayout,Array{String,2},Symbol,Array{Length{:mm,Float64},2},Length{:mm,Float64},Int64}},typeof(plot),Array{Float64,2}})
    Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:layout, :xlims),Tuple{GridLayout,Tuple{Int64,Float64}}},typeof(plot),Plot{GRBackend},Plot{GRBackend},Vararg{Plot{GRBackend},N} where N})
    Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:legend,),Tuple{Bool}},typeof(plot),Plot{GRBackend},Plot{GRBackend},Vararg{Plot{GRBackend},N} where N})
    Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:legend,),Tuple{Bool}},typeof(plot),Plot{PlotlyBackend},Plot{PlotlyBackend},Vararg{Plot{PlotlyBackend},N} where N})
    Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:legend,),Tuple{Symbol}},typeof(plot),Array{Tuple{Int64,Real},1}})
    Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:legend,),Tuple{Symbol}},typeof(plot),Plot{GRBackend},Plot{GRBackend},Vararg{Plot{GRBackend},N} where N})
    Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:legend,),Tuple{Symbol}},typeof(plot),Plot{PlotlyBackend},Plot{PlotlyBackend},Vararg{Plot{PlotlyBackend},N} where N})
    Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:line, :lab, :ms),Tuple{Tuple{Array{Symbol,2},Int64},Array{String,2},Int64}},typeof(plot),Array{Array{T,1} where T,1},Array{Float64,2}})
    Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:line, :label, :legendtitle),Tuple{Tuple{Int64,Array{Symbol,2}},Array{String,2},String}},typeof(plot),Array{Float64,2}})
    Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:line, :leg, :fill),Tuple{Int64,Bool,Tuple{Int64,Symbol}}},typeof(plot),Function,Function,Vararg{Any,N} where N})
    Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:line, :marker, :bg, :fg, :xlim, :ylim, :leg),Tuple{Tuple{Int64,Symbol,Symbol},Tuple{Shape,Int64,RGBA{Float64}},Symbol,Symbol,Tuple{Int64,Int64},Tuple{Int64,Int64},Bool}},typeof(plot),StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}},Array{Float64,1}})
    Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:line_z, :linewidth, :legend),Tuple{StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}},Int64,Bool}},typeof(plot),Array{Float64,1},Array{Float64,1}})
    Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:m, :lab, :bg, :xlim, :ylim, :seriestype),Tuple{Tuple{Int64,Symbol},Array{String,2},Symbol,Tuple{Int64,Int64},Tuple{Int64,Int64},Symbol}},typeof(plot),StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}},Array{Float64,2}})
    Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:marker,),Tuple{Bool}},typeof(plot),Array{Union{Missing, Int64},1}})
    Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:marker_z, :color, :legend, :seriestype),Tuple{typeof(+),Symbol,Bool,Symbol}},typeof(plot),Array{Float64,1},Array{Float64,1}})
    Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:markershape, :markersize, :marker_z, :line_z, :linewidth),Tuple{Array{Symbol,2},Array{Int64,2},Array{Int64,2},Array{Int64,2},Array{Int64,2}}},typeof(plot),Array{Float64,2}})
    Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:nbins, :seriestype),Tuple{Int64,Symbol}},typeof(plot),Array{Float64,1},Array{Float64,1}})
    Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:nbins, :show_empty_bins, :normed, :aspect_ratio, :seriestype),Tuple{Tuple{Int64,Int64},Bool,Bool,Int64,Symbol}},typeof(plot),Array{Complex{Float64},1}})
    Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:proj, :m),Tuple{Symbol,Int64}},typeof(plot),StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}},Array{Float64,1}})
    Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:projection, :seriestype),Tuple{Symbol,Symbol}},typeof(plot),StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}},UnitRange{Int64},Vararg{Any,N} where N})
    Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:quiver, :seriestype),Tuple{Tuple{Array{Float64,1},Array{Float64,1},Array{Float64,1}},Symbol}},typeof(plot),Array{Float64,1},Array{Float64,1},Vararg{Array{Float64,1},N} where N})
    Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:reg, :fill),Tuple{Bool,Tuple{Int64,Symbol}}},typeof(plot),Array{Float64,1}})
    Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:ribbon,),Tuple{Int64}},typeof(plot),UnitRange{Int64}})
    Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:ribbon,),Tuple{Tuple{LinRange{Float64},LinRange{Float64}}}},typeof(plot),UnitRange{Int64}})
    Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:ribbon,),Tuple{typeof(sqrt)}},typeof(plot),UnitRange{Int64}})
    Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:seriestype, :markershape, :markersize, :color),Tuple{Array{Symbol,2},Array{Symbol,1},Int64,Array{Symbol,1}}},typeof(plot),Array{Float64,2}})
    Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:seriestype,),Tuple{Symbol}},typeof(plot),Array{DateTime,1},UnitRange{Int64},Vararg{Any,N} where N})
    Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:seriestype,),Tuple{Symbol}},typeof(plot),Array{OHLC,1}})
    Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:st, :xlabel, :ylabel, :zlabel),Tuple{Symbol,String,String,String}},typeof(plot),Array{Float64,1},Array{Float64,1},Vararg{Array{Float64,1},N} where N})
    Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:title, :l, :seriestype),Tuple{String,Float64,Symbol}},typeof(plot),Array{String,1},Array{Float64,1}})
    Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:title,),Tuple{Array{String,2}}},typeof(plot),Plot{GRBackend},Plot{GRBackend}})
    Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:title,),Tuple{Array{String,2}}},typeof(plot),Plot{PlotlyBackend},Plot{PlotlyBackend}})
    Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:title,),Tuple{String}},typeof(plot),Plot{GRBackend}})
    Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:title,),Tuple{String}},typeof(plot),Plot{PlotlyBackend}})
    Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:w,),Tuple{Int64}},typeof(plot),Array{Float64,2}})
    Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:xaxis, :background_color, :leg),Tuple{Tuple{String,Tuple{Int64,Int64},StepRange{Int64,Int64},Symbol},RGB{Float64},Bool}},typeof(plot),Array{Float64,2}})
    Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:yflip, :aspect_ratio),Tuple{Bool,Symbol}},typeof(plot),Array{Float64,1},Array{Int64,1},Vararg{Any,N} where N})
    Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:zcolor, :m, :leg, :cbar, :w),Tuple{StepRange{Int64,Int64},Tuple{Int64,Float64,Symbol,Stroke},Bool,Bool,Int64}},typeof(plot),Array{Float64,1},Array{Float64,1},Vararg{Any,N} where N})
    Base.precompile(Tuple{Core.kwftype(typeof(portfoliocomposition)),Any,typeof(portfoliocomposition),Any,Vararg{Any,N} where N})
    Base.precompile(Tuple{Core.kwftype(typeof(scatter!)),Any,typeof(scatter!),Any,Vararg{Any,N} where N})
    Base.precompile(Tuple{Core.kwftype(typeof(scatter!)),Any,typeof(scatter!),Any})
    Base.precompile(Tuple{Core.kwftype(typeof(test_examples)),NamedTuple{(:skip, :disp),Tuple{Array{Int64,1},Bool}},typeof(test_examples),Symbol})
    Base.precompile(Tuple{Core.kwftype(typeof(test_examples)),NamedTuple{(:skip,),Tuple{Array{Int64,1}}},typeof(test_examples),Symbol})
    Base.precompile(Tuple{Type{GridLayout},Int64,Vararg{Int64,N} where N})
    Base.precompile(Tuple{Type{Shape},Array{Tuple{Float64,Float64},1}})
    Base.precompile(Tuple{typeof(RecipesBase.apply_recipe),AbstractDict{Symbol,Any},AbstractArray{OHLC,1}})
    Base.precompile(Tuple{typeof(RecipesBase.apply_recipe),AbstractDict{Symbol,Any},Array{Complex{Float64},1}})
    Base.precompile(Tuple{typeof(RecipesBase.apply_recipe),AbstractDict{Symbol,Any},PortfolioComposition})
    Base.precompile(Tuple{typeof(RecipesBase.apply_recipe),AbstractDict{Symbol,Any},Type{Val{:barbins}},Any,Any,Any})
    Base.precompile(Tuple{typeof(RecipesBase.apply_recipe),AbstractDict{Symbol,Any},Type{Val{:barhist}},Any,Any,Any})
    Base.precompile(Tuple{typeof(RecipesBase.apply_recipe),AbstractDict{Symbol,Any},Type{Val{:bar}},Any,Any,Any})
    Base.precompile(Tuple{typeof(RecipesBase.apply_recipe),AbstractDict{Symbol,Any},Type{Val{:histogram2d}},Any,Any,Any})
    Base.precompile(Tuple{typeof(RecipesBase.apply_recipe),AbstractDict{Symbol,Any},Type{Val{:hline}},Any,Any,Any})
    Base.precompile(Tuple{typeof(RecipesBase.apply_recipe),AbstractDict{Symbol,Any},Type{Val{:pie}},Any,Any,Any})
    Base.precompile(Tuple{typeof(RecipesBase.apply_recipe),AbstractDict{Symbol,Any},Type{Val{:quiver}},Any,Any,Any})
    Base.precompile(Tuple{typeof(RecipesBase.apply_recipe),AbstractDict{Symbol,Any},Type{Val{:spy}},Any,Any,Any})
    Base.precompile(Tuple{typeof(RecipesBase.apply_recipe),AbstractDict{Symbol,Any},Type{Val{:sticks}},Any,Any,Any})
    Base.precompile(Tuple{typeof(RecipesBase.apply_recipe),AbstractDict{Symbol,Any},Type{Val{:xerror}},Any,Any,Any})
    Base.precompile(Tuple{typeof(RecipesPipeline.add_series!),Plot{GRBackend},DefaultsDict})
    Base.precompile(Tuple{typeof(RecipesPipeline.add_series!),Plot{PlotlyBackend},DefaultsDict})
    Base.precompile(Tuple{typeof(RecipesPipeline.plot_setup!),Plot{GRBackend},Dict{Symbol,Any},Array{Dict{Symbol,Any},1}})
    Base.precompile(Tuple{typeof(RecipesPipeline.plot_setup!),Plot{PlotlyBackend},Dict{Symbol,Any},Array{Dict{Symbol,Any},1}})
    Base.precompile(Tuple{typeof(RecipesPipeline.preprocess_attributes!),Plot{GRBackend},DefaultsDict})
    Base.precompile(Tuple{typeof(RecipesPipeline.process_userrecipe!),Plot{GRBackend},Array{Dict{Symbol,Any},1},Dict{Symbol,Any}})
    Base.precompile(Tuple{typeof(RecipesPipeline.process_userrecipe!),Plot{PlotlyBackend},Array{Dict{Symbol,Any},1},Dict{Symbol,Any}})
    Base.precompile(Tuple{typeof(RecipesPipeline.warn_on_recipe_aliases!),Plot{GRBackend},DefaultsDict,Symbol,Any})
    Base.precompile(Tuple{typeof(RecipesPipeline.warn_on_recipe_aliases!),Plot{GRBackend},Dict{Symbol,Any},Symbol,Any})
    Base.precompile(Tuple{typeof(RecipesPipeline.warn_on_recipe_aliases!),Plot{PlotlyBackend},DefaultsDict,Symbol,Any})
    Base.precompile(Tuple{typeof(RecipesPipeline.warn_on_recipe_aliases!),Plot{PlotlyBackend},Dict{Symbol,Any},Symbol,Any})
    Base.precompile(Tuple{typeof(_bin_centers),StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}}})
    Base.precompile(Tuple{typeof(_cbar_unique),Array{Int64,1},String})
    Base.precompile(Tuple{typeof(_cbar_unique),Array{Nothing,1},String})
    Base.precompile(Tuple{typeof(_cbar_unique),Array{PlotUtils.ContinuousColorGradient,1},String})
    Base.precompile(Tuple{typeof(_cbar_unique),Array{StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}},1},String})
    Base.precompile(Tuple{typeof(_cbar_unique),Array{Symbol,1},String})
    Base.precompile(Tuple{typeof(_cycle),Array{Float64,1},Array{Int64,1}})
    Base.precompile(Tuple{typeof(_cycle),Array{Float64,1},StepRange{Int64,Int64}})
    Base.precompile(Tuple{typeof(_cycle),Array{Float64,1},UnitRange{Int64}})
    Base.precompile(Tuple{typeof(_cycle),Base.OneTo{Int64},Array{Int64,1}})
    Base.precompile(Tuple{typeof(_cycle),StepRange{Int64,Int64},Array{Int64,1}})
    Base.precompile(Tuple{typeof(_do_plot_show),Plot{GRBackend},Bool})
    Base.precompile(Tuple{typeof(_do_plot_show),Plot{PlotlyBackend},Bool})
    Base.precompile(Tuple{typeof(_heatmap_edges),Array{Float64,1},Bool,Bool})
    Base.precompile(Tuple{typeof(_plot!),Plot,Any,Any})
    Base.precompile(Tuple{typeof(_preprocess_barlike),DefaultsDict,Base.OneTo{Int64},Array{Float64,1}})
    Base.precompile(Tuple{typeof(_preprocess_binlike),DefaultsDict,StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}},Array{Float64,1}})
    Base.precompile(Tuple{typeof(_replace_markershape),Array{Symbol,1}})
    Base.precompile(Tuple{typeof(_update_min_padding!),GridLayout})
    Base.precompile(Tuple{typeof(_update_plot_args),Plot{GRBackend},DefaultsDict})
    Base.precompile(Tuple{typeof(_update_subplot_args),Plot{GRBackend},Subplot{GRBackend},Dict{Symbol,Any},Int64,Bool})
    Base.precompile(Tuple{typeof(_update_subplot_args),Plot{PlotlyBackend},Subplot{PlotlyBackend},Dict{Symbol,Any},Int64,Bool})
    Base.precompile(Tuple{typeof(backend),PlotlyBackend})
    Base.precompile(Tuple{typeof(bbox),Float64,Float64,Float64,Float64})
    Base.precompile(Tuple{typeof(bbox),Length{:mm,Float64},Length{:mm,Float64},Length{:mm,Float64},Length{:mm,Float64}})
    Base.precompile(Tuple{typeof(build_layout),GridLayout,Int64})
    Base.precompile(Tuple{typeof(contour),Any,Vararg{Any,N} where N})
    Base.precompile(Tuple{typeof(convert_to_polar),StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}},Array{Float64,1},Tuple{Int64,Float64}})
    Base.precompile(Tuple{typeof(create_grid),Expr})
    Base.precompile(Tuple{typeof(error_coords),Array{Float64,1},Array{Float64,1},Array{Float64,1}})
    Base.precompile(Tuple{typeof(error_zipit),Tuple{Array{Float64,1},Array{Float64,1},Array{Float64,1}}})
    Base.precompile(Tuple{typeof(fakedata),Int64,Vararg{Int64,N} where N})
    Base.precompile(Tuple{typeof(font),String,Vararg{Any,N} where N})
    Base.precompile(Tuple{typeof(get_clims),Subplot{GRBackend},Series,Function})
    Base.precompile(Tuple{typeof(get_minor_ticks),Subplot{GRBackend},Axis,Tuple{Array{Float64,1},Array{String,1}}})
    Base.precompile(Tuple{typeof(get_minor_ticks),Subplot{GRBackend},Axis,Tuple{Array{Int64,1},Array{String,1}}})
    Base.precompile(Tuple{typeof(get_series_color),Array{Symbol,1},Subplot{GRBackend},Int64,Symbol})
    Base.precompile(Tuple{typeof(get_series_color),Array{Symbol,1},Subplot{PlotlyBackend},Int64,Symbol})
    Base.precompile(Tuple{typeof(get_xy),Array{OHLC,1}})
    Base.precompile(Tuple{typeof(get_xy),OHLC{Float64},Int64,Float64})
    Base.precompile(Tuple{typeof(gr_add_legend),Subplot{GRBackend},NamedTuple{(:w, :h, :dy, :leftw, :textw, :rightw, :xoffset, :yoffset, :width_factor),NTuple{9,Float64}},Array{Float64,1}})
    Base.precompile(Tuple{typeof(gr_add_legend),Subplot{GRBackend},NamedTuple{(:w, :h, :dy, :leftw, :textw, :rightw, :xoffset, :yoffset, :width_factor),Tuple{Int64,Float64,Float64,Float64,Int64,Float64,Float64,Float64,Float64}},Array{Float64,1}})
    Base.precompile(Tuple{typeof(gr_display),Subplot{GRBackend},Length{:mm,Float64},Length{:mm,Float64},Array{Float64,1}})
    Base.precompile(Tuple{typeof(gr_draw_colorbar),GRColorbar,Subplot{GRBackend},Tuple{Float64,Float64},Array{Float64,1}})
    Base.precompile(Tuple{typeof(gr_draw_contour),Series,StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}},StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}},Array{Float64,2},Tuple{Float64,Float64}})
    Base.precompile(Tuple{typeof(gr_draw_grid),Subplot{GRBackend},Axis,Segments{Tuple{Float64,Float64}}})
    Base.precompile(Tuple{typeof(gr_draw_heatmap),Series,Array{Float64,1},Array{Float64,1},Array{Float64,2},Tuple{Float64,Float64}})
    Base.precompile(Tuple{typeof(gr_draw_markers),Series,Array{Float64,1},Array{Float64,1},Tuple{Float64,Float64}})
    Base.precompile(Tuple{typeof(gr_draw_markers),Series,Array{Int64,1},Array{Float64,1},Tuple{Float64,Float64}})
    Base.precompile(Tuple{typeof(gr_draw_markers),Series,Base.OneTo{Int64},Array{Float64,1},Tuple{Float64,Float64}})
    Base.precompile(Tuple{typeof(gr_draw_markers),Series,StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}},Array{Float64,1},Tuple{Float64,Float64}})
    Base.precompile(Tuple{typeof(gr_draw_segments),Series,Array{Float64,1},Array{Float64,1},Int64,Tuple{Float64,Float64}})
    Base.precompile(Tuple{typeof(gr_draw_segments),Series,Array{Float64,1},Array{Float64,1},Nothing,Tuple{Float64,Float64}})
    Base.precompile(Tuple{typeof(gr_draw_segments),Series,Array{Int64,1},Array{Int64,1},Nothing,Tuple{Float64,Float64}})
    Base.precompile(Tuple{typeof(gr_draw_segments),Series,Base.OneTo{Int64},Array{Float64,1},Int64,Tuple{Float64,Float64}})
    Base.precompile(Tuple{typeof(gr_draw_segments),Series,Base.OneTo{Int64},Array{Float64,1},Nothing,Tuple{Float64,Float64}})
    Base.precompile(Tuple{typeof(gr_draw_segments),Series,Base.OneTo{Int64},UnitRange{Int64},Tuple{Array{Float64,1},Array{Float64,1}},Tuple{Float64,Float64}})
    Base.precompile(Tuple{typeof(gr_draw_segments),Series,StepRange{Int64,Int64},Array{Float64,1},Int64,Tuple{Float64,Float64}})
    Base.precompile(Tuple{typeof(gr_draw_surface),Series,Array{Float64,1},Array{Float64,1},Array{Float64,1},Tuple{Float64,Float64}})
    Base.precompile(Tuple{typeof(gr_draw_surface),Series,Array{Float64,1},Array{Float64,1},Array{Float64,2},Tuple{Float64,Float64}})
    Base.precompile(Tuple{typeof(gr_get_ticks_size),Tuple{Array{Float64,1},Array{Any,1}},Int64})
    Base.precompile(Tuple{typeof(gr_get_ticks_size),Tuple{Array{Float64,1},Array{String,1}},Int64})
    Base.precompile(Tuple{typeof(gr_get_ticks_size),Tuple{Array{Int64,1},Array{String,1}},Int64})
    Base.precompile(Tuple{typeof(gr_label_ticks),Subplot{GRBackend},Symbol,Tuple{Array{Float64,1},Array{String,1}}})
    Base.precompile(Tuple{typeof(gr_label_ticks_3d),Subplot{GRBackend},Symbol,Tuple{Array{Float64,1},Array{String,1}}})
    Base.precompile(Tuple{typeof(gr_polaraxes),Int64,Float64,Subplot{GRBackend}})
    Base.precompile(Tuple{typeof(gr_polyline),Array{Float64,1},Array{Float64,1},Function})
    Base.precompile(Tuple{typeof(gr_set_gradient),PlotUtils.ContinuousColorGradient})
    Base.precompile(Tuple{typeof(heatmap_edges),Array{Float64,1},Symbol})
    Base.precompile(Tuple{typeof(heatmap_edges),Base.OneTo{Int64},Symbol})
    Base.precompile(Tuple{typeof(heatmap_edges),StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}},Symbol})
    Base.precompile(Tuple{typeof(heatmap_edges),UnitRange{Int64},Symbol})
    Base.precompile(Tuple{typeof(ignorenan_minimum),Array{Int64,1}})
    Base.precompile(Tuple{typeof(layout_args),Int64})
    Base.precompile(Tuple{typeof(make_fillrange_side),UnitRange{Int64},LinRange{Float64}})
    Base.precompile(Tuple{typeof(optimal_ticks_and_labels),Subplot{GRBackend},Axis,StepRange{Int64,Int64}})
    Base.precompile(Tuple{typeof(optimal_ticks_and_labels),Subplot{GRBackend},Axis,UnitRange{Int64}})
    Base.precompile(Tuple{typeof(plot!),Any})
    Base.precompile(Tuple{typeof(plot),Any,Any})
    Base.precompile(Tuple{typeof(plot),Any})
    Base.precompile(Tuple{typeof(processGridArg!),DefaultsDict,Bool,Symbol})
    Base.precompile(Tuple{typeof(processGridArg!),Dict{Symbol,Any},Symbol,Symbol})
    Base.precompile(Tuple{typeof(processLineArg),Dict{Symbol,Any},Array{Symbol,2}})
    Base.precompile(Tuple{typeof(processLineArg),Dict{Symbol,Any},Int64})
    Base.precompile(Tuple{typeof(processLineArg),Dict{Symbol,Any},Symbol})
    Base.precompile(Tuple{typeof(processMarkerArg),Dict{Symbol,Any},Array{Symbol,2}})
    Base.precompile(Tuple{typeof(processMarkerArg),Dict{Symbol,Any},RGBA{Float64}})
    Base.precompile(Tuple{typeof(processMarkerArg),Dict{Symbol,Any},Shape})
    Base.precompile(Tuple{typeof(processMarkerArg),Dict{Symbol,Any},Stroke})
    Base.precompile(Tuple{typeof(processMarkerArg),Dict{Symbol,Any},Symbol})
    Base.precompile(Tuple{typeof(process_annotation),Subplot{GRBackend},Int64,Float64,PlotText})
    Base.precompile(Tuple{typeof(process_annotation),Subplot{PlotlyBackend},Int64,Float64,PlotText})
    Base.precompile(Tuple{typeof(process_axis_arg!),Dict{Symbol,Any},StepRange{Int64,Int64},Symbol})
    Base.precompile(Tuple{typeof(process_axis_arg!),Dict{Symbol,Any},Symbol,Symbol})
    Base.precompile(Tuple{typeof(process_axis_arg!),Dict{Symbol,Any},Tuple{Int64,Int64},Symbol})
    Base.precompile(Tuple{typeof(push!),Plot{GRBackend},Float64,Array{Float64,1}})
    Base.precompile(Tuple{typeof(push!),Segments{Tuple{Float64,Float64,Float64}},Tuple{Float64,Int64,Int64},Tuple{Float64,Int64,Int64}})
    Base.precompile(Tuple{typeof(quiver_using_arrows),DefaultsDict})
    Base.precompile(Tuple{typeof(quiver_using_hack),DefaultsDict})
    Base.precompile(Tuple{typeof(reset_axis_defaults_byletter!)})
    Base.precompile(Tuple{typeof(slice_arg),Array{Int64,2},Int64})
    Base.precompile(Tuple{typeof(slice_arg),Array{Length{:mm,Float64},2},Int64})
    Base.precompile(Tuple{typeof(slice_arg),Array{PlotUtils.ContinuousColorGradient,2},Int64})
    Base.precompile(Tuple{typeof(slice_arg),Array{RGBA{Float64},2},Int64})
    Base.precompile(Tuple{typeof(slice_arg),Array{String,2},Int64})
    Base.precompile(Tuple{typeof(slice_arg),Array{Symbol,2},Int64})
    Base.precompile(Tuple{typeof(slice_arg),Base.ReshapedArray{Int64,2,UnitRange{Int64},Tuple{}},Int64})
    Base.precompile(Tuple{typeof(spy),Any})
    Base.precompile(Tuple{typeof(straightline_data),Tuple{Float64,Float64},Tuple{Float64,Float64},Array{Float64,1},Array{Float64,1},Int64})
    Base.precompile(Tuple{typeof(stroke),Int64,Vararg{Any,N} where N})
    Base.precompile(Tuple{typeof(text),String,Int64,Symbol,Vararg{Symbol,N} where N})
    Base.precompile(Tuple{typeof(text),String,Symbol,Int64,Vararg{Any,N} where N})
    Base.precompile(Tuple{typeof(text),String,Symbol})
    Base.precompile(Tuple{typeof(title!),AbstractString})
    Base.precompile(Tuple{typeof(unzip),Array{GeometryBasics.Point{2,Float64},1}})
    Base.precompile(Tuple{typeof(vline!),Any})
    Base.precompile(Tuple{typeof(xgrid!),Plot{GRBackend},Symbol,Vararg{Any,N} where N})
    Base.precompile(Tuple{typeof(xlims),Subplot{PlotlyBackend}})
    Base.precompile(Tuple{typeof(yaxis!),Any,Any})
    let fbody = try __lookup_kwbody__(which(gr_polyline, (Array{Float64,1},Array{Float64,1},typeof(GR.fillarea),))) catch missing end
        if !ismissing(fbody)
            precompile(fbody, (Symbol,Symbol,typeof(gr_polyline),Array{Float64,1},Array{Float64,1},typeof(GR.fillarea),))
        end
    end
    let fbody = try __lookup_kwbody__(which(plot!, ())) catch missing end
        if !ismissing(fbody)
            precompile(fbody, (Any,typeof(plot!),))
        end
    end
    let fbody = try __lookup_kwbody__(which(plot!, (Any,))) catch missing end
        if !ismissing(fbody)
            precompile(fbody, (Any,typeof(plot!),Any,))
        end
    end
    let fbody = try __lookup_kwbody__(which(plot!, (Any,Vararg{Any,N} where N,))) catch missing end
        if !ismissing(fbody)
            precompile(fbody, (Any,typeof(plot!),Any,Vararg{Any,N} where N,))
        end
    end
    let fbody = try __lookup_kwbody__(which(plot, (Any,))) catch missing end
        if !ismissing(fbody)
            precompile(fbody, (Any,typeof(plot),Any,))
        end
    end
    let fbody = try __lookup_kwbody__(which(plot, (Any,Vararg{Any,N} where N,))) catch missing end
        if !ismissing(fbody)
            precompile(fbody, (Any,typeof(plot),Any,Vararg{Any,N} where N,))
        end
    end
    let fbody = try __lookup_kwbody__(which(plot, (Plot,))) catch missing end
        if !ismissing(fbody)
            precompile(fbody, (Any,typeof(plot),Plot,))
        end
    end
    let fbody = try __lookup_kwbody__(which(plot, (Plot,Plot,))) catch missing end
        if !ismissing(fbody)
            precompile(fbody, (Any,typeof(plot),Plot,Plot,))
        end
    end
    let fbody = try __lookup_kwbody__(which(plot, (Plot,Plot,Vararg{Plot,N} where N,))) catch missing end
        if !ismissing(fbody)
            precompile(fbody, (Any,typeof(plot),Plot,Plot,Vararg{Plot,N} where N,))
        end
    end
    let fbody = try __lookup_kwbody__(which(text, (String,Int64,Vararg{Any,N} where N,))) catch missing end
        if !ismissing(fbody)
            precompile(fbody, (Base.Iterators.Pairs{Union{},Union{},Tuple{},NamedTuple{(),Tuple{}}},typeof(text),String,Int64,Vararg{Any,N} where N,))
        end
    end
    let fbody = try __lookup_kwbody__(which(text, (String,Symbol,Vararg{Any,N} where N,))) catch missing end
        if !ismissing(fbody)
            precompile(fbody, (Base.Iterators.Pairs{Union{},Union{},Tuple{},NamedTuple{(),Tuple{}}},typeof(text),String,Symbol,Vararg{Any,N} where N,))
        end
    end
    let fbody = try __lookup_kwbody__(which(title!, (AbstractString,))) catch missing end
        if !ismissing(fbody)
            precompile(fbody, (Any,typeof(title!),AbstractString,))
        end
    end
    let fbody = try __lookup_kwbody__(which(vline!, (Any,))) catch missing end
        if !ismissing(fbody)
            precompile(fbody, (Any,typeof(vline!),Any,))
        end
    end
    let fbody = try __lookup_kwbody__(which(yaxis!, (Any,Vararg{Any,N} where N,))) catch missing end
        if !ismissing(fbody)
            precompile(fbody, (Any,typeof(yaxis!),Any,Vararg{Any,N} where N,))
        end
    end
end


text:
include("cfd.jl")

function check_pressure(sys::System)
    for index in CartesianIndices(sys.P)
        i, j = Tuple(index)
        if sys.P[i, j] < 0
            return error("Negative Pressure [i, j] = [", i, ", ",
                         j,"]")
        end
    end
end

function check_saturation(sys::System, p::Parameters)
    for k = 1 : 2
        for j = 2 : p.ny - 1
            for i = 2 : p.nx - 1
                if sys.s[i, j, k] < 0
                    return error("Negative saturation, component ",
                                 k, " [x, y] = [", i, ", ", j, "]")
                end
                if sys.s[i, j, k] > 1
                    return error("Saturation > 1, component ",
                                 k, " [x, y] = [", i, ", ", j, "]")
                end
            end
        end
    end
end

function check_density(sys::System, p::Parameters)
    for k = 1 : 2
        for j = 2 : p.ny - 1
            for i = 2 : p.nx - 1
                if sys.ρ[i, j, k] < 0
                    return error("Negative density, component ",
                                 k, " [x, y] = [", i, ", ", j, "]")
                end
            end
        end
    end
end

function check_bc(bc::BoundaryCondition, var::String)
    for k = 1 : 2
        for w = 1 : 4
            for x = 1 : min(nx, ny)
                if (bc.a[x, w, k]^2 + bc.b[x, w, k]^2) == 0
                    return error("Wrong ", var, " bc at k = ", k,
                                 " w = ", w, " x = ", x)
                end
            end
        end
    end
end

function check_bc(bc::BoundaryConditions{T}) where T<:AbstractFloat
    check_bc(bc.u, "u")
    check_bc(bc.v, "v")
    check_bc(bc.ρ, "ρ")
    check_bc(bc.s, "s")
    check_bc(bc.P, "P")
end

function check_continuity(syswork::System, sysnext::System,
        p::Parameters, t::Integer)
    flux_init_work, flux_final_work = get_total_flux(syswork, p)
    content_work = get_content_inside(syswork, p)
    flux_init_next, flux_final_next = get_total_flux(sysnext, p)
    content_next = get_content_inside(sysnext, p)

    influx_work  = flux_init_work  * p.Δx * p.Δt
    outflux_work = flux_final_work * p.Δx * p.Δt
    inside_work  = content_next    * p.Δx * p.Δy

    influx_next  = flux_init_next  * p.Δx * p.Δt
    outflux_next = flux_final_next * p.Δx * p.Δt
    inside_next  = content_next    * p.Δx * p.Δy

    mass_work = influx_work - outflux_work + inside_work
    mass_next = influx_next - outflux_next + inside_next
    # println(mass_work, mass_next)
    diff = abs.(mass_work - mass_next)

    if t == 1
        io = open("../dump/mass.txt", "w")
        write(io, "m1 m2\n")
    else
        io = open("../dump/mass.txt", "a+")
    end
    write(io, string(mass_work[1]), " ", string(mass_work[2]), "\n")

    # eps = 0.001
    # if any(x -> x > eps, abs.(mass_work - mass_next))
    #     return error("Continuity equation breaks ", diff)
    # end
end

ext: jl

max_stars_repo_name: sofiabelen/Ideal-Gas-Compressible-Fluid

max_issues_repo_name: sofiabelen/Ideal-Gas-Compressible-Fluid

max_forks_repo_name: sofiabelen/Two-Phase-Filtration


text:
# ---
# title: Geographic data
# cover: assets/geographic.png
# description: Antarctic coastline.
# author: "[Pietro Vertechi](https://github.com/piever)"
# ---

using AlgebraOfGraphics, CairoMakie
using Shapefile, ZipFile
using Downloads: download
set_aog_theme!() #src

# Antarctic coastline. Data from the SCAR Antarctic Digital Database[^1].
#
# [^1]: Gerrish, L., Fretwell, P., & Cooper, P. (2021). Medium resolution vector polygons of the Antarctic coastline (7.4) [Data set]. UK Polar Data Centre, Natural Environment Research Council, UK Research & Innovation. https://doi.org/10.5285/747e63e-9d93-49c2-bafc-cf3d3f8e5afa

## Download, extract, and load shapefile
t = mktempdir() do dir
    url = "https://data.bas.ac.uk/download/7be3ab29-7caa-46b8-a355-2e3233796e86"
    r = ZipFile.Reader(seekstart(download(url, IOBuffer())))
    for f in r.files
        open(joinpath(dir, f.name), write = true) do io
            write(io, read(f, String));
        end
    end
    Shapefile.Table(joinpath(dir, "add_coastline_medium_res_polygon_v7_4.shp"))
end

## Draw map
plt = geodata(t) * mapping(:geometry, color = :surface) * visual(Poly)
fg = draw(plt; axis=(aspect=1,))

# save cover image #src
mkpath("assets") #src
save("assets/geographic.png", fg) #src


text:
# MonoidAlternative
# =================

# for convenience we forward Monoid definitions from the wrapped type
TypeClasses.neutral(::Type{Const{T}}) where {T} = Const(TypeClasses.neutral(T))
TypeClasses.combine(a::Const, b::Const) = Const(a.value ⊕ b.value)

# we support the special value Const(nothing) by defining Monoid on Nothing
TypeClasses.neutral(::Type{Nothing}) = nothing
TypeClasses.combine(::Nothing, ::Nothing) = nothing

# Const denotes failure, possibly trying again, hence we take the second attempt.
TypeClasses.orelse(a::Const, b::Const) = b

# FunctorApplicativeMonad
# =======================

TypeClasses.ap(f::Const, a::Const) = f  # short cycling behaviour
TypeClasses.flatmap(f, a::Const) = a

# FlipTypes
# =========

TypeClasses.flip_types(a::Const) = TypeClasses.map(Const, a.value)


text:
using MuZero

function MuZero.getconfig(::Type{Tac})
    config = make_board_game_config(
        Tac,
        network_type=Incremental,
        max_moves=9,
        root_dirichlet_alpha=0.3,
        action_space_size=9, # Number of all possible actions in the game
        support_size = 10, # Value and reward are scaled (with almost sqrt) and encoded on a vector with a range of -support_size to support_size. Choose it so that support_size <= sqrt(max(abs(discounted reward)))
        observation_shape = (3, 3, 3),
        stacked_observations = 0, # Number of previous observations and previous actions to add to the current observation
        hidden_state_size = 32, # The 1-dimensional size of the encoded game state
        checkpoint_interval = 1000, #  Number of training steps before using test model is updated.
        buffer_capacity = 100,
        num_actors = 2,
        weight_decay = 1.0f-1,
        lr_init=8e-4,
        save_interval = 1000, # Number of training steps before using play model is updated.
        num_unroll_steps = 2,
    )
    config.max_games = 55_000
    config.batch_size = 16
    config.num_simulations = 100
    config.training_steps = 200001
    config
end

function tacschedule!(l::AbstractLearner)
    step = get_steps(l)
    opt = l.optimizer
    setonce = (at, value) -> begin
        if step > at && opt[3].eta > value
            opt[3].eta = value
            println("Change learning rate to: $value")
        end
    end
    setonce(0, 8e-4)
    setonce(10000, 1e-4)
    setonce(20000, 9e-5)
    setonce(23000, 4e-5)
    setonce(25000, 3e-5)
    setonce(33000, 1e-5)
    setonce(140000, 1e-5)
end


text:
export Experiment

using Dates
using ReinforcementLearningBase
using ReinforcementLearningCore
using .ReinforcementLearningEnvironments
using Flux
using BSON
using TensorBoardLogger
using Logging
using Random

function RLCore.Experiment(
    ::Val{:JuliaRL},
    ::Val{:BasicDQN},
    ::Val{:CartPole},
    ::Nothing;
    save_dir = nothing,
    seed = 123,
)
    if isnothing(save_dir)
        t = Dates.format(now(), "yyyy_mm_dd_HH_MM_SS")
        save_dir = joinpath(pwd(), "checkpoints", "JuliaRL_BasicDQN_CartPole_$(t)")
    end

    lg = TBLogger(joinpath(save_dir, "tb_log"), min_level = Logging.Info)
    rng = MersenneTwister(seed)

    env = CartPoleEnv(; T = Float32, rng = rng)
    ns, na = length(get_state(env)), length(get_actions(env))
    agent = Agent(
        policy = QBasedPolicy(
            learner = BasicDQNLearner(
                approximator = NeuralNetworkApproximator(
                    model = Chain(
                        Dense(ns, 128, relu; initW = glorot_uniform(rng)),
                        Dense(128, 128, relu; initW = glorot_uniform(rng)),
                        Dense(128, na; initW = glorot_uniform(rng)),
                    ) |> cpu,
                    optimizer = ADAM(),
                ),
                batch_size = 32,
                min_replay_history = 100,
                loss_func = huber_loss,
                rng = rng,
            ),
            explorer = EpsilonGreedyExplorer(
                kind = :exp,
                ϵ_stable = 0.01,
                decay_steps = 500,
                rng = rng,
            ),
        ),
        trajectory = CircularCompactSARTSATrajectory(
            capacity = 1000,
            state_type = Float32,
            state_size = (ns,),
        ),
    )

    stop_condition = StopAfterStep(10000)

    total_reward_per_episode = TotalRewardPerEpisode()
    time_per_step = TimePerStep()
    hook = ComposedHook(
        total_reward_per_episode,
        time_per_step,
        DoEveryNStep() do t, agent, env
            with_logger(lg) do
                @info "training" loss = agent.policy.learner.loss
            end
        end,
        DoEveryNEpisode() do t, agent, env
            with_logger(lg) do
                @info "training" reward = total_reward_per_episode.rewards[end]
            end
        end,
        DoEveryNStep(10000) do t, agent, env
            RLCore.save(save_dir, agent)
            BSON.@save joinpath(save_dir, "stats.bson") total_reward_per_episode time_per_step
        end,
    )

    description = """
    This experiment uses three dense layers to approximate the Q value.
    The testing environment is CartPoleEnv.

    Agent and statistic info will be saved to: `$save_dir`
    You can also view the tensorboard logs with `tensorboard --logdir $(joinpath(save_dir, "tb_log"))`
    To load the agent and statistic info:
    ```
    agent = RLCore.load("$save_dir", Agent)
    BSON.@load joinpath("$save_dir", "stats.bson") total_reward_per_episode time_per_step
    ```
    """

    Experiment(agent, env, stop_condition, hook, description)
end

function RLCore.Experiment(
    ::Val{:JuliaRL},
    ::Val{:DQN},
    ::Val{:CartPole},
    ::Nothing;
    save_dir = nothing,
    seed = 123,
)
    if isnothing(save_dir)
        t = Dates.format(now(), "yyyy_mm_dd_HH_MM_SS")
        save_dir = joinpath(pwd(), "checkpoints", "JuliaRL_DQN_CartPole_$(t)")
    end

    lg = TBLogger(joinpath(save_dir, "tb_log"), min_level = Logging.Info)
    rng = MersenneTwister(seed)

    env = CartPoleEnv(; T = Float32, rng = rng)
    ns, na = length(get_state(env)), length(get_actions(env))

    agent = Agent(
        policy = QBasedPolicy(
            learner = DQNLearner(
                approximator = NeuralNetworkApproximator(
                    model = Chain(
                        Dense(ns, 128, relu; initW = glorot_uniform(rng)),
                        Dense(128, 128, relu; initW = glorot_uniform(rng)),
                        Dense(128, na; initW = glorot_uniform(rng)),
                    ) |> cpu,
                    optimizer = ADAM(),
                ),
                target_approximator = NeuralNetworkApproximator(
                    model = Chain(
                        Dense(ns, 128, relu; initW = glorot_uniform(rng)),
                        Dense(128, 128, relu; initW = glorot_uniform(rng)),
                        Dense(128, na; initW = glorot_uniform(rng)),
                    ) |> cpu,
                ),
                loss_func = huber_loss,
                stack_size = nothing,
                batch_size = 32,
                update_horizon = 1,
                min_replay_history = 100,
                update_freq = 1,
                target_update_freq = 100,
                rng = rng,
            ),
            explorer = EpsilonGreedyExplorer(
                kind = :exp,
                ϵ_stable = 0.01,
                decay_steps = 500,
                rng = rng,
            ),
        ),
        trajectory = CircularCompactSARTSATrajectory(
            capacity = 1000,
            state_type = Float32,
            state_size = (ns,),
        ),
    )

    stop_condition = StopAfterStep(10_000)

    total_reward_per_episode = TotalRewardPerEpisode()
    time_per_step = TimePerStep()
    hook = ComposedHook(
        total_reward_per_episode,
        time_per_step,
        DoEveryNStep() do t, agent, env
            if agent.policy.learner.update_step % agent.policy.learner.update_freq == 0
                with_logger(lg) do
                    @info "training" loss = agent.policy.learner.loss
                end
            end
        end,
        DoEveryNStep(10000) do t, agent, env
            RLCore.save(save_dir, agent)
            BSON.@save joinpath(save_dir, "stats.bson") total_reward_per_episode time_per_step
        end,
    )

    description = """
    This experiment uses the `DQNLearner` method with three dense layers to approximate the Q value.
    The testing environment is CartPoleEnv.

    Agent and statistic info will be saved to: `$save_dir`
    You can also view the tensorboard logs with `tensorboard --logdir $(joinpath(save_dir, "tb_log"))`
    To load the agent and statistic info:

    ```
    agent = RLCore.load("$save_dir", Agent)
    BSON.@load joinpath("$save_dir", "stats.bson") total_reward_per_episode time_per_step
    ```
    """

    Experiment(agent, env, stop_condition, hook, description)
end

function RLCore.Experiment(
    ::Val{:JuliaRL},
    ::Val{:PrioritizedDQN},
    ::Val{:CartPole},
    ::Nothing;
    save_dir = nothing,
    seed = 123,
)
    if isnothing(save_dir)
        t = Dates.format(now(), "yyyy_mm_dd_HH_MM_SS")
        save_dir = joinpath(pwd(), "checkpoints", "JuliaRL_PrioritizedDQN_CartPole_$(t)")
    end

    lg = TBLogger(joinpath(save_dir, "tb_log"), min_level = Logging.Info)
    rng = MersenneTwister(seed)

    env = CartPoleEnv(; T = Float32, rng = rng)
    ns, na = length(get_state(env)), length(get_actions(env))

    agent = Agent(
        policy = QBasedPolicy(
            learner = PrioritizedDQNLearner(
                approximator = NeuralNetworkApproximator(
                    model = Chain(
                        Dense(ns, 128, relu; initW = glorot_uniform(rng)),
                        Dense(128, 128, relu; initW = glorot_uniform(rng)),
                        Dense(128, na; initW = glorot_uniform(rng)),
                    ) |> cpu,
                    optimizer = ADAM(),
                ),
                target_approximator = NeuralNetworkApproximator(
                    model = Chain(
                        Dense(ns, 128, relu; initW = glorot_uniform(rng)),
                        Dense(128, 128, relu; initW = glorot_uniform(rng)),
                        Dense(128, na; initW = glorot_uniform(rng)),
                    ) |> cpu,
                    optimizer = ADAM(),
                ),
                loss_func = huber_loss_unreduced,
                stack_size = nothing,
                batch_size = 32,
                update_horizon = 1,
                min_replay_history = 100,
                update_freq = 1,
                target_update_freq = 100,
                rng = rng,
            ),
            explorer = EpsilonGreedyExplorer(
                kind = :exp,
                ϵ_stable = 0.01,
                decay_steps = 500,
                rng = rng,
            ),
        ),
        trajectory = CircularCompactPSARTSATrajectory(
            capacity = 1000,
            state_type = Float32,
            state_size = (ns,),
        ),
    )

    stop_condition = StopAfterStep(10_000)

    total_reward_per_episode = TotalRewardPerEpisode()
    time_per_step = TimePerStep()
    hook = ComposedHook(
        total_reward_per_episode,
        time_per_step,
        DoEveryNStep() do t, agent, env
            if agent.policy.learner.update_step % agent.policy.learner.update_freq == 0
                with_logger(lg) do
                    @info "training" loss = agent.policy.learner.loss
                end
            end
        end,
        DoEveryNStep(10000) do t, agent, env
            RLCore.save(save_dir, agent)
            BSON.@save joinpath(save_dir, "stats.bson") total_reward_per_episode time_per_step
        end,
    )

    description = """
    This experiment uses the `PrioritizedDQNLearner` method with three dense layers to approximate the Q value.
    The testing environment is CartPoleEnv.

    Agent and statistic info will be saved to: `$save_dir`
    You can also view the tensorboard logs with `tensorboard --logdir $(joinpath(save_dir, "tb_log"))`
    To load the agent and statistic info:

    ```
    agent = RLCore.load("$save_dir", Agent)
    BSON.@load joinpath("$save_dir", "stats.bson") total_reward_per_episode time_per_step
    ```
    """

    Experiment(agent, env, stop_condition, hook, description)
end

function RLCore.Experiment(
    ::Val{:JuliaRL},
    ::Val{:Rainbow},
    ::Val{:CartPole},
    ::Nothing;
    save_dir = nothing,
    seed = 123,
)
    if isnothing(save_dir)
        t = Dates.format(now(), "yyyy_mm_dd_HH_MM_SS")
        save_dir = joinpath(pwd(), "checkpoints", "JuliaRL_Rainbow_CartPole_$(t)")
    end

    lg = TBLogger(joinpath(save_dir, "tb_log"), min_level = Logging.Info)
    rng = MersenneTwister(seed)

    env = CartPoleEnv(; T = Float32, rng = rng)
    ns, na = length(get_state(env)), length(get_actions(env))

    n_atoms = 51
    agent = Agent(
        policy = QBasedPolicy(
            learner = RainbowLearner(
                approximator = NeuralNetworkApproximator(
                    model = Chain(
                        Dense(ns, 128, relu; initW = glorot_uniform(rng)),
                        Dense(128, 128, relu; initW = glorot_uniform(rng)),
                        Dense(128, na * n_atoms; initW = glorot_uniform(rng)),
                    ) |> cpu,
                    optimizer = ADAM(0.0005),
                ),
                target_approximator = NeuralNetworkApproximator(
                    model = Chain(
                        Dense(ns, 128, relu; initW = glorot_uniform(rng)),
                        Dense(128, 128, relu; initW = glorot_uniform(rng)),
                        Dense(128, na * n_atoms; initW = glorot_uniform(rng)),
                    ) |> cpu,
                    optimizer = ADAM(0.0005),
                ),
                n_actions = na,
                n_atoms = n_atoms,
                Vₘₐₓ = 200.0f0,
                Vₘᵢₙ = 0.0f0,
                update_freq = 1,
                γ = 0.99f0,
                update_horizon = 1,
                batch_size = 32,
                stack_size = nothing,
                min_replay_history = 100,
                loss_func = logitcrossentropy_unreduced,
                target_update_freq = 100,
                rng = rng,
            ),
            explorer = EpsilonGreedyExplorer(
                kind = :exp,
                ϵ_stable = 0.01,
                decay_steps = 500,
                rng = rng,
            ),
        ),
        trajectory = CircularCompactPSARTSATrajectory(
            capacity = 1000,
            state_type = Float32,
            state_size = (ns,),
        ),
    )

    stop_condition = StopAfterStep(10_000)

    total_reward_per_episode = TotalRewardPerEpisode()
    time_per_step = TimePerStep()
    hook = ComposedHook(
        total_reward_per_episode,
        time_per_step,
        DoEveryNStep() do t, agent, env
            if agent.policy.learner.update_step % agent.policy.learner.update_freq == 0
                with_logger(lg) do
                    @info "training" loss = agent.policy.learner.loss
                end
            end
        end,
        DoEveryNStep(10000) do t, agent, env
            RLCore.save(save_dir, agent)
            BSON.@save joinpath(save_dir, "stats.bson") total_reward_per_episode time_per_step
        end,
    )

    description = """
    This experiment uses the `RainbowLearner` method with three dense layers to approximate the distributed Q value.
    The testing environment is CartPoleEnv.

    Agent and statistic info will be saved to: `$save_dir`
    You can also view the tensorboard logs with `tensorboard --logdir $(joinpath(save_dir, "tb_log"))`
    To load the agent and statistic info:

    ```
    agent = RLCore.load("$save_dir", Agent)
    BSON.@load joinpath("$save_dir", "stats.bson") total_reward_per_episode time_per_step
    ```
    """

    Experiment(agent, env, stop_condition, hook, description)
end

function RLCore.Experiment(
    ::Val{:JuliaRL},
    ::Val{:IQN},
    ::Val{:CartPole},
    ::Nothing;
    save_dir = nothing,
    seed = 123,
)
    if isnothing(save_dir)
        t = Dates.format(now(), "yyyy_mm_dd_HH_MM_SS")
        save_dir = joinpath(pwd(), "checkpoints", "JuliaRL_IQN_CartPole_$(t)")
    end

    lg = TBLogger(joinpath(save_dir, "tb_log"), min_level = Logging.Info)
    rng = MersenneTwister(seed)

    env = CartPoleEnv(; T = Float32, rng = rng)
    ns, na = length(get_state(env)), length(get_actions(env))

    init = glorot_uniform(rng)
    Nₑₘ = 16
    n_hidden = 64
    κ = 1.0f0

    nn_creator() =
        ImplicitQuantileNet(
            ψ = Dense(ns, n_hidden, relu; initW = init),
            ϕ = Dense(Nₑₘ, n_hidden, relu; initW = init),
            header = Dense(n_hidden, na; initW = init),
        ) |> cpu

    agent = Agent(
        policy = QBasedPolicy(
            learner = IQNLearner(
                approximator = NeuralNetworkApproximator(
                    model = nn_creator(),
                    optimizer = ADAM(0.001),
                ),
                target_approximator = NeuralNetworkApproximator(model = nn_creator()),
                κ = κ,
                N = 8,
                N′ = 8,
                Nₑₘ = Nₑₘ,
                K = 32,
                γ = 0.99f0,
                stack_size = nothing,
                batch_size = 32,
                update_horizon = 1,
                min_replay_history = 100,
                update_freq = 1,
                target_update_freq = 100,
                default_priority = 1.0f2,
                rng = rng,
                device_rng = rng,
            ),
            explorer = EpsilonGreedyExplorer(
                kind = :exp,
                ϵ_stable = 0.01,
                decay_steps = 500,
                rng = rng,
            ),
        ),
        trajectory = CircularCompactPSARTSATrajectory(
            capacity = 1000,
            state_type = Float32,
            state_size = (ns,),
        ),
    )

    stop_condition = StopAfterStep(10_000)

    total_reward_per_episode = TotalRewardPerEpisode()
    time_per_step = TimePerStep()
    hook = ComposedHook(
        total_reward_per_episode,
        time_per_step,
        DoEveryNStep() do t, agent, env
            if agent.policy.learner.update_step % agent.policy.learner.update_freq == 0
                with_logger(lg) do
                    @info "training" loss = agent.policy.learner.loss
                end
            end
        end,
        DoEveryNStep(10000) do t, agent, env
            RLCore.save(save_dir, agent)
            BSON.@save joinpath(save_dir, "stats.bson") total_reward_per_episode time_per_step
        end,
    )

    description = """
    This experiment uses the `IQNLearner` method with a `ImplicitQuantileNet`.
    The testing environment is CartPoleEnv.

    Agent and statistic info will be saved to: `$save_dir`
    You can also view the tensorboard logs with `tensorboard --logdir $(joinpath(save_dir, "tb_log"))`
    To load the agent and statistic info:

    ```
    agent = RLCore.load("$save_dir", Agent)
    BSON.@load joinpath("$save_dir", "stats.bson") total_reward_per_episode time_per_step
    ```
    """

    Experiment(agent, env, stop_condition, hook, description)
end

function RLCore.Experiment(
    ::Val{:JuliaRL},
    ::Val{:A2C},
    ::Val{:CartPole},
    ::Nothing;
    save_dir = nothing,
    seed = 123,
)
    rng = MersenneTwister(seed)
    N_ENV = 16
    UPDATE_FREQ = 10
    env = MultiThreadEnv([
        CartPoleEnv(; T = Float32, rng = MersenneTwister(hash(seed + i))) for i in 1:N_ENV
    ])
    ns, na = length(get_state(env[1])), length(get_actions(env[1]))
    RLBase.reset!(env, is_force = true)
    agent = Agent(
        policy = QBasedPolicy(
            learner = A2CLearner(
                approximator = ActorCritic(
                    actor = Chain(
                        Dense(ns, 256, relu; initW = glorot_uniform(rng)),
                        Dense(256, na; initW = glorot_uniform(rng)),
                    ),
                    critic = Chain(
                        Dense(ns, 256, relu; initW = glorot_uniform(rng)),
                        Dense(256, 1; initW = glorot_uniform(rng)),
                    ),
                    optimizer = ADAM(1e-3),
                ) |> cpu,
                γ = 0.99f0,
                actor_loss_weight = 1.0f0,
                critic_loss_weight = 0.5f0,
                entropy_loss_weight = 0.001f0,
            ),
            explorer = BatchExplorer(GumbelSoftmaxExplorer()),#= seed = nothing =#
        ),
        trajectory = CircularCompactSARTSATrajectory(;
            capacity = UPDATE_FREQ,
            state_type = Float32,
            state_size = (ns, N_ENV),
            action_type = Int,
            action_size = (N_ENV,),
            reward_type = Float32,
            reward_size = (N_ENV,),
            terminal_type = Bool,
            terminal_size = (N_ENV,),
        ),
    )

    hook = TotalBatchRewardPerEpisode(N_ENV)
    stop_condition = StopAfterStep(100000)
    Experiment(agent, env, stop_condition, hook, "# A2C with CartPole")
end

function RLCore.Experiment(
    ::Val{:JuliaRL},
    ::Val{:A2CGAE},
    ::Val{:CartPole},
    ::Nothing;
    save_dir = nothing,
    seed = 123,
)
    rng = MersenneTwister(seed)
    N_ENV = 16
    UPDATE_FREQ = 10
    env = MultiThreadEnv([
        CartPoleEnv(; T = Float32, rng = MersenneTwister(hash(seed + i))) for i in 1:N_ENV
    ])
    ns, na = length(get_state(env[1])), length(get_actions(env[1]))
    RLBase.reset!(env, is_force = true)
    agent = Agent(
        policy = QBasedPolicy(
            learner = A2CGAELearner(
                approximator = ActorCritic(
                    actor = NeuralNetworkApproximator(
                        model = Chain(
                            Dense(ns, 256, relu; initW = glorot_uniform(rng)),
                            Dense(256, na; initW = glorot_uniform(rng)),
                        ),
                        optimizer = ADAM(1e-3),
                    ),
                    critic = NeuralNetworkApproximator(
                        model = Chain(
                            Dense(ns, 256, relu; initW = glorot_uniform(rng)),
                            Dense(256, 1; initW = glorot_uniform(rng)),
                        ),
                        optimizer = ADAM(1e-3),
                    ),
                ) |> cpu,
                γ = 0.99f0,
                λ = 0.97f0,
                actor_loss_weight = 1.0f0,
                critic_loss_weight = 0.5f0,
                entropy_loss_weight = 0.001f0,
            ),
            explorer = BatchExplorer(GumbelSoftmaxExplorer(;)), #= seed = nothing =#
        ),
        trajectory = CircularCompactSARTSATrajectory(;
            capacity = UPDATE_FREQ,
            state_type = Float32,
            state_size = (ns, N_ENV),
            action_type = Int,
            action_size = (N_ENV,),
            reward_type = Float32,
            reward_size = (N_ENV,),
            terminal_type = Bool,
            terminal_size = (N_ENV,),
        ),
    )
    Experiment(
        agent,
        env,
        StopAfterStep(100000),
        TotalBatchRewardPerEpisode(N_ENV),
        "# A2CGAE with CartPole",
    )
end

function RLCore.Experiment(
    ::Val{:JuliaRL},
    ::Val{:DDPG},
    ::Val{:Pendulum},
    ::Nothing;
    seed = 123,
)
    rng = MersenneTwister(seed)
    inner_env = PendulumEnv(T = Float32, rng = rng)
    action_space = get_actions(inner_env)
    low = action_space.low
    high = action_space.high
    ns = length(get_state(inner_env))

    env = inner_env |> ActionTransformedEnv(x -> low + (x + 1) * 0.5 * (high - low))
    init = glorot_uniform(rng)

    create_actor() = Chain(
        Dense(ns, 30, relu; initW = init),
        Dense(30, 30, relu; initW = init),
        Dense(30, 1, tanh; initW = init),
    )

    create_critic() = Chain(
        Dense(ns + 1, 30, relu; initW = init),
        Dense(30, 30, relu; initW = init),
        Dense(30, 1; initW = init),
    )

    agent = Agent(
        policy = DDPGPolicy(
            behavior_actor = NeuralNetworkApproximator(
                model = create_actor(),
                optimizer = ADAM(),
            ),
            behavior_critic = NeuralNetworkApproximator(
                model = create_critic(),
                optimizer = ADAM(),
            ),
            target_actor = NeuralNetworkApproximator(
                model = create_actor(),
                optimizer = ADAM(),
            ),
            target_critic = NeuralNetworkApproximator(
                model = create_critic(),
                optimizer = ADAM(),
            ),
            γ = 0.99f0,
            ρ = 0.995f0,
            batch_size = 64,
            start_steps = 1000,
            start_policy = RandomPolicy(ContinuousSpace(-1.0, 1.0); rng = rng),
            update_after = 1000,
            update_every = 1,
            act_limit = 1.0,
            act_noise = 0.1,
            rng = rng,
        ),
        trajectory = CircularCompactSARTSATrajectory(
            capacity = 10000,
            state_type = Float32,
            state_size = (ns,),
            action_type = Float32,
        ),
    )

    description = """
    # Play Pendulum with DDPG
    """

    Experiment(agent, env, StopAfterStep(10000), TotalRewardPerEpisode(), description)
end

function RLCore.Experiment(
    ::Val{:JuliaRL},
    ::Val{:PPO},
    ::Val{:CartPole},
    ::Nothing;
    seed = 123,
)
    rng = MersenneTwister(seed)
    N_ENV = 8
    UPDATE_FREQ = 16
    env = MultiThreadEnv([
        CartPoleEnv(; T = Float32, rng = MersenneTwister(hash(seed + i))) for i in 1:N_ENV
    ])
    ns, na = length(get_state(env[1])), length(get_actions(env[1]))
    RLBase.reset!(env, is_force = true)
    agent = Agent(
        policy = QBasedPolicy(
            learner = PPOLearner(
                approximator = ActorCritic(
                    actor = NeuralNetworkApproximator(
                        model = Chain(
                            Dense(ns, 256, relu; initW = glorot_uniform(rng)),
                            Dense(256, na; initW = glorot_uniform(rng)),
                        ),
                        optimizer = ADAM(1e-3),
                    ),
                    critic = NeuralNetworkApproximator(
                        model = Chain(
                            Dense(ns, 256, relu; initW = glorot_uniform(rng)),
                            Dense(256, 1; initW = glorot_uniform(rng)),
                        ),
                        optimizer = ADAM(1e-3),
                    ),
                ) |> cpu,
                γ = 0.99f0,
                λ = 0.95f0,
                clip_range = 0.1f0,
                max_grad_norm = 0.5f0,
                n_epochs = 4,
                n_microbatches = 4,
                actor_loss_weight = 1.0f0,
                critic_loss_weight = 0.5f0,
                entropy_loss_weight = 0.001f0,
            ),
            explorer = BatchExplorer(GumbelSoftmaxExplorer(; rng = rng)),
        ),
        trajectory = PPOTrajectory(;
            capacity = 32,
            state_type = Float32,
            state_size = (ns, N_ENV),
            action_type = Int,
            action_size = (N_ENV,),
            action_log_prob_type = Float32,
            action_log_prob_size = (N_ENV,),
            reward_type = Float32,
            reward_size = (N_ENV,),
            terminal_type = Bool,
            terminal_size = (N_ENV,),
        ),
    )
    Experiment(
        agent,
        env,
        StopAfterStep(100000),
        TotalBatchRewardPerEpisode(N_ENV),
        "# PPO with CartPole",
    )
end

function RLCore.Experiment(
    ::Val{:JuliaRL},
    ::Val{:BasicDQN},
    ::Val{:MountainCar},
    ::Nothing;
    save_dir = nothing,
    seed = 123,
)
    rng = MersenneTwister(seed)
    if isnothing(save_dir)
        t = Dates.format(now(), "yyyy_mm_dd_HH_MM_SS")
        save_dir = joinpath(pwd(), "checkpoints", "JuliaRL_BasicDQN_MountainCar_$(t)")
    end

    lg = TBLogger(joinpath(save_dir, "tb_log"), min_level = Logging.Info)

    env = MountainCarEnv(; T = Float32, max_steps = 5000, rng = rng)
    ns, na = length(get_state(env)), length(get_actions(env))
    agent = Agent(
        policy = QBasedPolicy(
            learner = BasicDQNLearner(
                approximator = NeuralNetworkApproximator(
                    model = Chain(
                        Dense(ns, 64, relu; initW = glorot_uniform(rng)),
                        Dense(64, 64, relu; initW = glorot_uniform(rng)),
                        Dense(64, na; initW = glorot_uniform(rng)),
                    ) |> cpu,
                    optimizer = ADAM(),
                ),
                batch_size = 32,
                min_replay_history = 100,
                loss_func = huber_loss,
                rng = rng,
            ),
            explorer = EpsilonGreedyExplorer(
                kind = :exp,
                ϵ_stable = 0.01,
                decay_steps = 500,
                rng = rng,
            ),
        ),
        trajectory = CircularCompactSARTSATrajectory(
            capacity = 50000,
            state_type = Float32,
            state_size = (ns,),
        ),
    )

    stop_condition = StopAfterStep(40000)

    total_reward_per_episode = TotalRewardPerEpisode()
    time_per_step = TimePerStep()
    hook = ComposedHook(
        total_reward_per_episode,
        time_per_step,
        DoEveryNStep() do t, agent, env
            with_logger(lg) do
                @info "training" loss = agent.policy.learner.loss
            end
        end,
        DoEveryNEpisode() do t, agent, env
            with_logger(lg) do
                @info "training" reward = total_reward_per_episode.rewards[end]
            end
        end,
        DoEveryNStep(10000) do t, agent, env
            RLCore.save(save_dir, agent)
            BSON.@save joinpath(save_dir, "stats.bson") total_reward_per_episode time_per_step
        end,
    )

    description = """
    This experiment uses three dense layers to approximate the Q value.
    The testing environment is MountainCarEnv.

    Agent and statistic info will be saved to: `$save_dir`
    You can also view the tensorboard logs with `tensorboard --logdir $(joinpath(save_dir, "tb_log"))`
    To load the agent and statistic info:
    ```
    agent = RLCore.load("$save_dir", Agent)
    BSON.@load joinpath("$save_dir", "stats.bson") total_reward_per_episode time_per_step
    ```
    """

    Experiment(agent, env, stop_condition, hook, description)
end

function RLCore.Experiment(
    ::Val{:JuliaRL},
    ::Val{:DQN},
    ::Val{:MountainCar},
    ::Nothing;
    save_dir = nothing,
    seed = 123,
)
    if isnothing(save_dir)
        t = Dates.format(now(), "yyyy_mm_dd_HH_MM_SS")
        save_dir = joinpath(pwd(), "checkpoints", "JuliaRL_DQN_MountainCar_$(t)")
    end

    lg = TBLogger(joinpath(save_dir, "tb_log"), min_level = Logging.Info)
    rng = MersenneTwister(seed)

    env = MountainCarEnv(; T = Float32, max_steps = 5000, rng = rng)
    ns, na = length(get_state(env)), length(get_actions(env))

    agent = Agent(
        policy = QBasedPolicy(
            learner = DQNLearner(
                approximator = NeuralNetworkApproximator(
                    model = Chain(
                        Dense(ns, 64, relu; initW = glorot_uniform(rng)),
                        Dense(64, 64, relu; initW = glorot_uniform(rng)),
                        Dense(64, na; initW = glorot_uniform(rng)),
                    ) |> cpu,
                    optimizer = ADAM(),
                ),
                target_approximator = NeuralNetworkApproximator(
                    model = Chain(
                        Dense(ns, 64, relu; initW = glorot_uniform(rng)),
                        Dense(64, 64, relu; initW = glorot_uniform(rng)),
                        Dense(64, na; initW = glorot_uniform(rng)),
                    ) |> cpu,
                    optimizer = ADAM(),
                ),
                loss_func = huber_loss,
                stack_size = nothing,
                batch_size = 32,
                update_horizon = 1,
                min_replay_history = 100,
                update_freq = 1,
                target_update_freq = 100,
                rng = rng,
            ),
            explorer = EpsilonGreedyExplorer(
                kind = :exp,
                ϵ_stable = 0.01,
                decay_steps = 500,
                rng = rng,
            ),
        ),
        trajectory = CircularCompactSARTSATrajectory(
            capacity = 50000,
            state_type = Float32,
            state_size = (ns,),
        ),
    )

    stop_condition = StopAfterStep(40_000)

    total_reward_per_episode = TotalRewardPerEpisode()
    time_per_step = TimePerStep()
    hook = ComposedHook(
        total_reward_per_episode,
        time_per_step,
        DoEveryNStep() do t, agent, env
            if agent.policy.learner.update_step % agent.policy.learner.update_freq == 0
                with_logger(lg) do
                    @info "training" loss = agent.policy.learner.loss
                end
            end
        end,
        DoEveryNEpisode() do t, agent, env
            with_logger(lg) do
                @info "training" reward = total_reward_per_episode.rewards[end]
            end
        end,
        DoEveryNStep(10000) do t, agent, env
            RLCore.save(save_dir, agent)
            BSON.@save joinpath(save_dir, "stats.bson") total_reward_per_episode time_per_step
        end,
    )

    description = """
    This experiment uses the `DQNLearner` method with three dense layers to approximate the Q value.
    The testing environment is MountainCarEnv.

    Agent and statistic info will be saved to: `$save_dir`
    You can also view the tensorboard logs with `tensorboard --logdir $(joinpath(save_dir, "tb_log"))`
    To load the agent and statistic info:

    ```
    agent = RLCore.load("$save_dir", Agent)
    BSON.@load joinpath("$save_dir", "stats.bson") total_reward_per_episode time_per_step
    ```
    """

    Experiment(agent, env, stop_condition, hook, description)
end

function RLCore.Experiment(
    ::Val{:JuliaRL},
    ::Val{:SAC},
    ::Val{:Pendulum},
    ::Nothing;
    seed = 123,
)
    rng = MersenneTwister(seed)
    inner_env = PendulumEnv(T = Float32, rng = rng)
    action_space = get_actions(inner_env)
    low = action_space.low
    high = action_space.high
    ns = length(get_state(inner_env))

    env = inner_env |> ActionTransformedEnv(x -> low + (x + 1) * 0.5 * (high - low))
    init = glorot_uniform(rng)

    create_policy_net() = NeuralNetworkApproximator(
        model = SACPolicyNetwork(
            Chain(Dense(ns, 30, relu), Dense(30, 30, relu)),
            Chain(Dense(30, 1, initW = init)),
            Chain(Dense(
                30,
                1,
                x -> min(max(x, typeof(x)(-20)), typeof(x)(2)),
                initW = init,
            )),
        ),
        optimizer = ADAM(0.003),
    )

    create_q_net() = NeuralNetworkApproximator(
        model = Chain(
            Dense(ns + 1, 30, relu; initW = init),
            Dense(30, 30, relu; initW = init),
            Dense(30, 1; initW = init),
        ),
        optimizer = ADAM(0.003),
    )

    agent = Agent(
        policy = SACPolicy(
            policy = create_policy_net(),
            qnetwork1 = create_q_net(),
            qnetwork2 = create_q_net(),
            target_qnetwork1 = create_q_net(),
            target_qnetwork2 = create_q_net(),
            γ = 0.99f0,
            ρ = 0.995f0,
            α = 0.2f0,
            batch_size = 64,
            start_steps = 1000,
            start_policy = RandomPolicy(ContinuousSpace(-1.0, 1.0); rng = rng),
            update_after = 1000,
            update_every = 1,
            rng = rng,
        ),
        trajectory = CircularCompactSARTSATrajectory(
            capacity = 10000,
            state_type = Float32,
            state_size = (ns,),
            action_type = Float32,
        ),
    )

    description = """
    # Play Pendulum with SAC
    """

    Experiment(agent, env, StopAfterStep(10000), TotalRewardPerEpisode(), description)
end


text:
using Test

@testset "UniformResourceIdentifiers" begin
    include("chapter3_2_test.jl")
end


text:
export iauA2af
"""
Decompose radians into degrees, arcminutes, arcseconds, fraction.
This function is part of the International Astronomical Union's
SOFA (Standards Of Fundamental Astronomy) software collection.

Status:  vector/matrix support function.

Given:
- `ndp::Integer` resolution (Note 1)
- `angle::Real`  angle in radians

Returned:
- `sign::Char`    '+' or '-'
- `idmsf::Array{Int32, 1}`  degrees, arcminutes, arcseconds, fraction

Called:
   iauD2tf      decompose days to hms

Notes:
1. The argument ndp is interpreted as follows:
| ndp | resolution |
| :-: | :--------: |
| : | ...00,000,000 |
| -7 | 10,000,000 |
| -6 | 1,000,000 |
| -5 | 100,000 |
| -4 | 10,000 |
| -3 | 1,000 |
| -2 | 100 |
| -1 | 10 |
| 0 | 1 |
| 1 | 0.1 |
| 2 | 0.01 |
| 3 | 0.001 |
| : | 0.000... |

2. The largest positive useful value for ndp is determined by the size of angle, the format of doubles on the target platform, and the risk of overflowing idmsf[3].  On a typical platform, for angle up to 2pi, the available floating-point precision might correspond to ndp=12.  However, the practical limit is typically ndp=9, set by the capacity of a 32-bit int, or ndp=4 if int is only 16 bits.

3. The absolute value of angle may exceed 2pi.  In cases where it does not, it is up to the caller to test for and handle the case where angle is very nearly 2pi and rounds up to 360 degrees, by testing for idmsf[0]=360 and setting idmsf[0-3] to zero.

This revision:  2013 June 18

SOFA release 2018-01-30
"""
function iauA2af(ndp::Int, angle::Real)
    sign  = Array{UInt8}(undef, 1)
    idmsf = Array{Int32}(undef, 4)

    ccall((:iauA2af, libsofa_c), Cvoid,
        (Cint, Cdouble, Ptr{UInt8}, Ptr{Cint}),
        convert(Int32, ndp), convert(Float64, angle), sign, idmsf)

    return Char(sign[1]), idmsf
end


text:
##############################################################################
##
## TODO
##
##############################################################################

# - Read raw_tx to tx

##############################################################################
##
## Resources
##
##############################################################################

# - http://bitcoin.stackexchange.com/questions/2859/how-are-transaction-hashes-calculated

using Crypto
using HTTPClient
using JSON

const SIGHASH_ALL          = 0x00000001
const SIGHASH_NONE         = 0x00000002
const SIGHASH_SINGLE       = 0x00000003
const SIGHASH_ANYONECANPAY = 0x00000080

type OutPoint
  hash::Array{Uint8}          # 32-byte hash of the referenced transaction
  index::Uint32               # Index of specific output in tx. 1st output is 0

  function OutPoint(hash::String, index::Integer)
    OutPoint(Crypto.hex2oct(hash), uint32(index))
  end

  function OutPoint(hash::Array{Uint8}, index::Integer)
    if index < 0
      error("OutPoint cannot have negative index")
    end

    if length(hash) != 32
      error("OutPoint requres 32-byte hash of referenced transaction")
    end

    new(hash, uint32(index))
  end
end

function convert(::Type{Array{Uint8}}, outpoint::OutPoint)
  result = Array(Uint8, 0)

  append!(result, reverse(outpoint.hash))
  append!(result, reverse(Crypto.int2oct(outpoint.index)))

  return result
end

type Tx_Input
  previous_output::OutPoint             # Previous output tx, as OutPoint
  scriptSig::Array{Uint8}               # Script to confirm tx authorization
  sequence::Uint32                      # Tx version as defined by the sender

  function Tx_Input(previous_output::OutPoint, scriptSig::String; sequence = 0xffffffff)
    scriptSig = Crypto.hex2oct(scriptSig)
    Tx_Input(previous_output, scriptSig, sequence = sequence)
  end

  function Tx_Input(previous_output::OutPoint, scriptSig::Array{Uint8}; sequence = 0xffffffff)
    new(previous_output, scriptSig, uint32(sequence))
  end
end

function convert(::Type{Array{Uint8}}, tx_in::Tx_Input)
  result = Array(Uint8, 0)

  append!(result, bytearray(tx_in.previous_output))
  append!(result, reverse(to_varint(length(tx_in.scriptSig))))
  append!(result, tx_in.scriptSig)
  append!(result, reverse(Crypto.int2oct(tx_in.sequence)))
end

type Tx_Output
  # ERROR: does Uint64 exist on 32-bit OS?
  value::Uint64                         # Transaction value
  scriptPubKey::Array{Uint8}            # Script for claiming tx output

  # value: transaction value in Satoshi
  # scriptPubKey: script as hex string
  function Tx_Output(value, scriptPubKey::String)
    scriptPubKey = Crypto.hex2oct(scriptPubKey)
    Tx_Output(value, scriptPubKey)
  end

  # value: transaction value in Satoshi
  # scriptPubKey: script as Array of Uint8
  function Tx_Output(value, scriptPubKey::Array{Uint8})
    value = uint64(value)
    new(value, scriptPubKey)
  end
end

function convert(::Type{Array{Uint8}}, tx_out::Tx_Output)
  result = Array(Uint8, 0)

  # TODO: This is a really terrible way to get little
  # endian byte array
  append!(result, reverse(Crypto.int2oct(tx_out.value)))

  append!(result, reverse(to_varint(length(tx_out.scriptPubKey))))

  append!(result, tx_out.scriptPubKey)

  return result
end

type Tx
  version::Uint32             # Transaction data format version
  inputs::Array{Tx_Input}     # Array of transaction inputs
  outputs::Array{Tx_Output}   # Array of transaction outputs
  lock_time::Uint32           # Block num / time when tx is locked

  function Tx(inputs::Array{Tx_Input}, outputs::Array{Tx_Output}; version = 0x00000001, lock_time = 0x00000000)
    new(version, inputs, outputs, lock_time)
  end
end

function convert(::Type{Array{Uint8}}, tx::Tx)
  result = Array(Uint8, 0)

  # Add version
  append!(result, reverse(Crypto.int2oct(tx.version)))

  # Add number of inputs
  append!(result, reverse(to_varint(length(tx.inputs))))

  # Add inputs
  for input in tx.inputs
    append!(result, bytearray(input))
  end

  # Add number of outputs
  append!(result, reverse(to_varint(length(tx.outputs))))

  # Add outputs
  for output in tx.outputs
    append!(result, bytearray(output))
  end

  # Add lock_time
  append!(result, reverse(Crypto.int2oct(tx.lock_time)))

  return result
end

# Create transaction from previous OutPoints and outputs
function create_tx(keys::Array{Keys},
                   outpoints::Array{OutPoint},
                   addresses::Array,   # Should be array of Base58Check-encoded strings
                   amounts::Array;     # Should be array of Integers
                   hash_code = SIGHASH_ALL)

  if length(keys) != length(outpoints)
    error("Creating a transaction requires a private key for each input")
  end

  if length(addresses) != length(amounts)
    ereror("Creating a transation requires an amount for each address")
  end

  # Build input objects
  inputs = Array(Tx_Input, 0)
  for outpoint in outpoints
    prev_tx = get_tx(outpoint.hash)
    input = Tx_Input(outpoint, prev_tx.outputs[outpoint.index].scriptPubKey)
    append!(inputs, [input])
  end

  # Build output objects
  outputs = Array(Tx_Output, 0)
  for i in length(addresses)
    # First byte is network id, last four bytes are checksum
    address = decode58_to_array(addresses[i])[2:end-4]

    # Create scriptPubKey for pay2hash
    scriptPubKey = [OP_DUP, OP_HASH160, uint8(length(address)), address, OP_EQUALVERIFY, OP_CHECKSIG]

    # Create output object
    output = Tx_Output(amounts[i], scriptPubKey)
    append!(outputs, [output])
  end

  # Build transaction object
  tx = Tx(inputs, outputs)

  # Get transaction as byte array
  raw_tx = bytearray(tx)

  # Append hash code; see here: https://en.bitcoin.it/wiki/OP_CHECKSIG
  append!(raw_tx, reverse(Crypto.int2oct(hash_code)))

  # Double hash the transaction
  hash = Crypto.digest("SHA256", Crypto.digest("SHA256", raw_tx))

  # Sign the transaction using the private key
  for i in 1:length(keys)

    # Sign transation using private key
    signature = Crypto.ec_sign(hash, keys[i].priv_key)

    # Build scriptSig
    scriptSig = Array(Uint8, 0)

    # Append length of signature as little-endian varint hex
    append!(scriptSig, reverse(to_varint(length(signature))))

    # Append the signature as big-endian
    append!(scriptSig, signature)

    # Append hash_code byte
    append!(scriptSig, [uint8(hash_code)])

    # Append length of public key as little-endian varint hex
    append!(scriptSig, reverse(to_varint(length(keys[i].pub_key))))

    # Append public key
    append!(scriptSig, keys[i].pub_key)

    tx.inputs[i].scriptSig = scriptSig
  end

  return tx
end

function get_tx(hash::Array{Uint8})
  return get_tx(Crypto.oct2hex(hash))
end

function get_tx(hash::String)
  const TOSHI_API_TX_URL = "https://bitcoin.toshi.io/api/v0/transactions/"
  url = string(TOSHI_API_TX_URL, hash)

  # Get data
  # TODO: Add error handling to HTTP GET
  result = get(url)

  # Get body data
  result = result.body.data

  # Convert from hex array to ASCII string for JSON parsing
  result = join([char(x) for x in result])

  # Parse the JSON
  result = JSON.parse(result)

  # Parse json inputs into array of Tx_Input objects
  json_inputs = result["inputs"]
  inputs = Array(Tx_Input, 0)
  for input in json_inputs
    outpoint = OutPoint(input["previous_transaction_hash"], input["output_index"] + 1) # Julia is 1-index
    tx_input = Tx_Input(outpoint, input["script"])
    append!(inputs, [tx_input])
  end

  # Parse json outputs into array of Tx_Output objects
  json_outputs = result["outputs"]
  outputs = Array(Tx_Output, 0)
  for output in json_outputs
    tx_output = Tx_Output(output["amount"], output["script_hex"])
    append!(outputs, [tx_output])
  end

  return Tx(inputs, outputs)
end

function parse_tx(raw_tx::Array{Uint8})
  return 0
end

# https://en.bitcoin.it/wiki/OP_CHECKSIG
# https://github.com/aantonop/bitcoinbook/blob/develop/ch05.asciidoc
function verify_tx(tx::Tx)
  return 0
end


text:
struct Params
    # GENERAL PARAMETERS
    pathToInstance::String            # Path to the instance
    pathToOutput::String              # Path to the output solution
    # PARAMETERS OF THE ALGORITHM
    seed::Int
    maxTime::Int
    # DATASET INFORMATION
    datasetName::String
    datasetSize::Int
    A::Array{Int64}
    B::Array{Int64}
    solutionCost::Any
    solution::Any
end

# Constructor of struct Params
function Params(pathToInstance::String, pathToOutput::String, seed::Int, maxTime::Int)
    datasetName = split(pathToInstance, '\\')[end]
    sz, A, B = loadInstance(pathToInstance)

    solutionName = split(datasetName,'.')[1]*".sln"
    solution_path = joinpath(join(split(pathToInstance,'\\')[1:end-2],'\\'),"solutions",solutionName)

    if isfile(solution_path)
        sz_sol, cost, sol = loadSolution(solution_path)
        if sz != sz_sol
            throw("Size in instance file and solution file should be the same!")
        end
    else
        cost = NaN
        sol = NaN
    end
    params = Params(pathToInstance, pathToOutput, seed, maxTime, datasetName, sz, A, B, cost, sol)
    return params
end

function loadInstance(pathToInstance::String)
    """
    Loads instance file
    """
    s = open(pathToInstance) do file
        read(file, String)
    end
    s = split(strip(replace(s,"\n" => " ")), r"\s+")

    sz = parse(Int, s[1])
    if (length(s) != sz*sz*2+1)
        throw("Size of instance does not match the size specified!")
    end

    s = s[2:end]

    A = map(x->parse(Int64,x), reshape(s[1:sz^2],sz,sz))

    s = s[sz^2+1:end]

    B = map(x->parse(Int64,x), reshape(s[1:sz^2],sz,sz))

    return sz, A, B
end

function loadSolution(solution_path::String)
    """
    Loads solution file
    """
    s = open(solution_path) do file
        read(file, String)
    end
    s = replace(s,',' => ' ')
    elements = split(strip(s), r"\s+")
    sz = parse(Int, strip(elements[1]))
    cost = parse(Int, strip(elements[2]))
    sol = Array{Int64}(undef, sz)
    elements = elements[3:end]
    for row = 1:sz
        sol[row] = parse(Int64, elements[row])
    end
    return sz, cost, sol
end

max_stars_repo_name: BSAraujo/QAP-Metaheuristics

max_issues_repo_name: BSAraujo/QAP-Metaheuristics

max_forks_repo_name: BSAraujo/QAP-Metaheuristics