############################################################
# judiPDEfull ################################################
############################################################

# Authors: Philipp Witte (pwitte@eos.ubc.ca), Henryk Modzelewski (hmodzelewski@eos.ubc.ca)
# Date: January 2017

export judiPDEfull, judiPDEfullException, subsample

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

# Type for linear operator representing  Pr*A(m)^-1*Ps,
# i.e. it includes source and receiver projections

struct judiPDEfull{DDT<:Number,RDT<:Number} <: joAbstractLinearOperator{DDT,RDT}
    name::String
    m::Integer
    n::Integer
    info::Info
    model::Model
    srcGeometry::Geometry
    recGeometry::Geometry
    options::Options
    fop::Function              # forward
    fop_T::Union{Function, Nothing}  # transpose
end

mutable struct judiPDEfullException <: Exception
    msg :: String
end

############################################################
## Constructor

function judiModeling(info::Info,model::Model, srcGeometry::Geometry, recGeometry::Geometry; options=Options(), DDT::DataType=Float32, RDT::DataType=DDT)
# JOLI wrapper for nonlinear forward modeling
    (DDT == Float32 && RDT == Float32) || throw(judiPDEfullException("Domain and range types not supported"))

    # Determine dimensions
    if typeof(recGeometry) == GeometryOOC
        m = sum(recGeometry.nsamples)
    else
        m = 0
        for j=1:info.nsrc m += length(recGeometry.xloc[j])*recGeometry.nt[j] end

    end
    if typeof(srcGeometry) == GeometryOOC
        n = sum(srcGeometry.nsamples)
    else
        n = 0
        for j=1:info.nsrc n += length(srcGeometry.xloc[j])*srcGeometry.nt[j] end
    end

    if info.nsrc > 1
        srcnum = 1:info.nsrc
    else
        srcnum = 1
    end

    return F = judiPDEfull{Float32,Float32}("Proj*F*Proj'", m, n, info, model, srcGeometry, recGeometry, options,
                              src -> time_modeling(model, srcGeometry, src.data, recGeometry, nothing, nothing, srcnum, 'F', 1, options),
                              rec -> time_modeling(model, srcGeometry, nothing, recGeometry, rec.data, nothing, srcnum, 'F', -1, options),
                              )
end

############################################################
## overloaded Base functions

# conj(judiPDEfull)
conj(A::judiPDEfull{DDT,RDT}) where {DDT,RDT} =
    judiPDEfull{DDT,RDT}("conj("*A.name*")",A.m,A.n,A.info,A.model,A.srcGeometry,A.recGeometry,A.options,
        A.fop,
        A.fop_T
        )

# transpose(judiPDEfull)
transpose(A::judiPDEfull{DDT,RDT}) where {DDT,RDT} =
    judiPDEfull{DDT,RDT}("Proj*F'*Proj'",A.n,A.m,A.info,A.model,A.srcGeometry,A.recGeometry,A.options,
        A.fop_T,
        A.fop
        )

# adjoint(judiPDEfull)
adjoint(A::judiPDEfull{DDT,RDT}) where {DDT,RDT} =
    judiPDEfull{DDT,RDT}("Proj*F'*Proj'",A.n,A.m,A.info,A.model,A.srcGeometry,A.recGeometry,A.options,
        A.fop_T,
        A.fop
        )

############################################################
## overloaded Base *(...judiPDEfull...)

# *(judiPDEfull,judiVector)
function *(A::judiPDEfull{ADDT,ARDT},v::judiVector{vDT}) where {ADDT,ARDT,vDT}
    A.n == size(v,1) || throw(judiPDEfullException("shape mismatch"))
    if compareGeometry(A.srcGeometry,v.geometry) == false && compareGeometry(A.recGeometry,v.geometry) == false
        throw(judiPDEfullException("geometry mismatch"))
    end
    jo_check_type_match(ADDT,vDT,join(["DDT for *(judiPDEfull,judiVector):",A.name,typeof(A),vDT]," / "))
    V = A.fop(v)
    jo_check_type_match(ARDT,eltype(V),join(["RDT from *(judiPDEfull,judiVector):",A.name,typeof(A),eltype(V)]," / "))
    return V
end

# *(num,judiPDEfull)
function *(a::Number,A::judiPDEfull{ADDT,ARDT}) where {ADDT,ARDT}
    return judiPDEfull{ADDT,ARDT}("(N*"*A.name*")",A.m,A.n,A.info,A.model,A.srcGeometry,A.recGeometry,A.options,
        v1 -> jo_convert(ARDT,a*A.fop(v1),false),
        v2 -> jo_convert(ADDT,a*A.fop_T(v2),false)
        )
end

function A_mul_B!(x::judiVector,A::judiPDEfull,y::judiVector)
    z = A*y
    x.data = z.data
end

function Ac_mul_B!(x::judiVector,A::judiPDEfull,y::judiVector)
    z = adjoint(A)*y
    x.data = z.data
end

############################################################
## overloaded Bases +(...judiPDEfull...), -(...judiPDEfull...)

# +(judiPDEfull,num)
function +(A::judiPDEfull{ADDT,ARDT},b::Number) where {ADDT,ARDT}
    return judiPDE{ADDT,ARDT}("("*A.name*"+N)",A.m,A.n,A.info,A.model,A.srcGeometry,A.recGeometry,A.options,
        v1 -> A.fop(v1)+joConstants(A.m,A.n,b;DDT=ADDT,RDT=ARDT)*v1,
        v2 -> A.fop_T(v2)+joConstants(A.m,A.n,b;DDT=ADDT,RDT=ARDT)*v2
        )
end

# -(judiPDEfull,num)
function -(A::judiPDEfull{ADDT,ARDT},b::Number) where {ADDT,ARDT}
    return judiPDE{ADDT,ARDT}("("*A.name*"-N)",A.m,A.n,A.info,A.model,A.srcGeometry,A.recGeometry,A.options,
        v1 -> A.fop(v1)-joConstants(A.m,A.n,b;DDT=ADDT,RDT=ARDT)*v1,
        v2 -> A.fop_T(v2)-joConstants(A.m,A.n,b;DDT=ADDT,RDT=ARDT)*v2
        )
end

# -(judiPDEfull)
-(A::judiPDEfull{DDT,RDT}) where {DDT,RDT} =
    judiPDEfull{DDT,RDT}("(-"*A.name*")",A.m,A.n,A.info,A.model,A.srcGeometry,A.recGeometry,A.options,
        v1 -> -A.fop(v1),
        v2 -> -A.fop_T(v2)
        )

############################################################
## Additional overloaded functions

# Subsample Modeling operator
function subsample(F::judiPDEfull{ADDT,ARDT}, srcnum) where {ADDT,ARDT}

    srcGeometry = subsample(F.srcGeometry,srcnum)       # Geometry of subsampled data container
    recGeometry = subsample(F.recGeometry,srcnum)

    info = Info(F.info.n, length(srcnum), F.info.nt[srcnum])
    return judiModeling(info, F.model, srcGeometry, recGeometry; options=F.options, DDT=ADDT, RDT=ARDT)
end

getindex(F::judiPDEfull,a) = subsample(F,a)


module PipsNLP

using JuMP, Plasmo
using Libdl
using MPI
using Distributed
using DataStructures

export pipsnlp_solve, distribute_optigraph, retrieve_solution

include("pips_nlp_data.jl")
include("distribute.jl")
include("pipsnlp_interface.jl")

using .PipsNlpInterface

end # module


# A modified next reaction method Heaps version of mnrm_v1
struct MNRM <: AbstractAggregatorAlgorithm end
mutable struct MNRMJumpAggregation{T,S,F1,F2,RNG,DEPGR,PQ} <: AbstractSSAJumpAggregator
  next_jump::Int
  prev_jump::Int
  next_jump_time::T
  end_time::T
  cur_rates::Vector{T}
  sum_rate::T
  ma_jumps::S
  rates::F1
  affects!::F2
  save_positions::Tuple{Bool,Bool}
  rng::RNG
  dep_gr::DEPGR
  pq::PQ
end
function MNRMJumpAggregation(nj::Int, njt::T, et::T, crs::Vector{T}, sr::T, maj::S, rs::F1, affs!::F2, sps::Tuple{Bool,Bool}, rng::RNG; num_specs, dep_graph=nothing, kwargs...) where {T,S,F1,F2,RNG}

    # a dependency graph is needed and must be provided if there are constant rate jumps
    if dep_graph === nothing
        if (get_num_majumps(maj) == 0) || !isempty(rs)
            error("To use ConstantRateJumps with the Next Reaction Method (NRM) algorithm a dependency graph must be supplied.")
        else
            dg = make_dependency_graph(num_specs, maj)
        end
    else
        dg = dep_graph

        # make sure each jump depends on itself
        add_self_dependencies!(dg)
    end
    pq = MutableBinaryHeap{T, DataStructures.FasterForward}()
    MNRMJumpAggregation{T,S,F1,F2,RNG,typeof(dg), typeof(pq)}(nj, nj, njt, et, crs, sr, maj, rs, affs!, sps, rng, dg, pq)
end

function aggregate(aggregator::MNRM, u, p, t, end_time, constant_jumps, ma_jumps, save_positions, rng; kwargs...)

    # handle constant jumps using tuples
    rates, affects! = get_jump_info_fwrappers(u, p, t, constant_jumps)
    build_jump_aggregation(MNRMJumpAggregation, u, p, t, end_time, ma_jumps, rates, affects!, save_positions, rng; num_specs = length(u), kwargs...)
end

function initialize!(p::MNRMJumpAggregation, integrator, u, params, t)
    fill_rates_and_get_times!(p, u, params, t)
    generate_jumps!(p, integrator, u, params, t)
    nothing
end

@inline function execute_jumps!(p::MNRMJumpAggregation, integrator, u, params, t)
    update_state!(p, integrator, u)
    update_internal_times!(p, u, params, t)
    nothing
end
# calculate the next jump / jump time
function generate_jumps!(p::MNRMJumpAggregation, integrator, u, params, t)
    ttnj, p.next_jump = top_with_handle(p.pq)
    @fastmath p.next_jump_time = t + ttnj
    # TODO how to update the whole thing
    for rx in eachindex(p.cur_rates)
        update!(p.pq, rx, p.pq[rx] - ttnj)
    end
    nothing
end

@inline function update_internal_times!(p::MNRMJumpAggregation, u, params, t)
    @unpack pq, cur_rates, rates, ma_jumps, rng = p
    dep_rxs = p.dep_gr[p.next_jump]
    num_majumps = get_num_majumps(ma_jumps)
    @inbounds for rx in dep_rxs
        oldrate = cur_rates[rx]
        @inbounds cur_rates[rx] = calculate_jump_rate(ma_jumps,num_majumps,rates,u,params,t,rx)
        if rx != p.next_jump && oldrate > zero(oldrate)
            if cur_rates[rx] > zero(eltype(cur_rates))
                update!(pq, rx, pq[rx]*oldrate/cur_rates[rx])
            else
                update!(pq, rx, typemax(t))
            end
        else
            if cur_rates[rx] > zero(eltype(cur_rates))
                update!(pq, rx, randexp(rng)/cur_rates[rx])
            else
                update!(pq, rx, typemax(t))
            end
        end
    end
    nothing
end

# fill the propensity rates
# reevaulate all rates, recalculate all jump times, and reinit the priority queue
function fill_rates_and_get_times!(p::MNRMJumpAggregation, u, params, t)

    # mass action jumps
    majumps   = p.ma_jumps
    cur_rates = p.cur_rates
    pqdata = Vector{typeof(t)}(undef,length(cur_rates))
    @inbounds for i in 1:get_num_majumps(majumps)
        cur_rates[i] = evalrxrate(u, i, majumps)
        pqdata[i] = randexp(p.rng) / cur_rates[i]
    end

    # constant rates
    rates = p.rates
    idx   = get_num_majumps(majumps) + 1
    @inbounds for rate in rates
        cur_rates[idx] = rate(u, params, t)
        pqdata[idx] = randexp(p.rng) / cur_rates[idx]
        idx += 1
    end

    # setup a new indexed priority queue to storing rx times
    p.pq = MutableBinaryHeap{typeof(t),DataStructures.FasterForward}(pqdata)
    nothing
end

# Change below


module MappedArrays

using Base: @propagate_inbounds

export mappedarray

abstract AbstractMappedArray{T,N} <: AbstractArray{T,N}

immutable ReadonlyMappedArray{T,N,A<:AbstractArray,F} <: AbstractMappedArray{T,N}
    f::F
    data::A
end
immutable MappedArray{T,N,A<:AbstractArray,F,Finv} <: AbstractMappedArray{T,N}
    f::F
    finv::Finv
    data::A
end

"""
     mappedarray(f, A)

creates a view of the array `A` that applies `f` to every element of
`A`. The view is read-only (you can get values but not set them).
"""
mappedarray{T,N}(f, data::AbstractArray{T,N}) = ReadonlyMappedArray{typeof(f(one(T))),N,typeof(data),typeof(f)}(f, data)

"""
    mappedarray((f, finv), A)

creates a view of the array `A` that applies `f` to every element of
`A`. The inverse function, `finv`, allows one to also set values of
the view and, correspondingly, the values in `A`.
"""
function mappedarray{T,N}(f_finv::Tuple{Any,Any}, data::AbstractArray{T,N})
    f, finv = f_finv
    MappedArray{typeof(f(one(T))),N,typeof(data),typeof(f),typeof(finv)}(f, finv, data)
end

Base.parent(A::AbstractMappedArray) = A.data
Base.size(A::AbstractMappedArray) = size(A.data)
parenttype{T,N,A,F}(::Type{ReadonlyMappedArray{T,N,A,F}}) = A
parenttype{T,N,A,F,Finv}(::Type{MappedArray{T,N,A,F,Finv}}) = A
Base.linearindexing{MA<:AbstractMappedArray}(::Type{MA}) = Base.linearindexing(parenttype(MA))

@propagate_inbounds Base.getindex(A::AbstractMappedArray, i::Int...) = A.f(A.data[i...])
@propagate_inbounds Base.setindex!(A::MappedArray, val, i::Int...) = A.data[i...] = A.finv(val)

end # module


function PlotStairs(label_id, values::AbstractArray{T}, count::Integer, xscale::Real = 1.0, x0::Real = 0.0, offset::Integer = 0, stride::Integer = sizeof(T)) where {T<:ImPlotData}
    LibCImPlot.PlotStairs(label_id, values, count, xscale, x0, offset, stride)
end

function PlotStairs(label_id, values::AbstractArray{T}, count::Integer, xscale::Real = 1.0, x0::Real = 0.0, offset::Integer = 0, stride::Integer = sizeof(Float64)) where {T<:Real}
    LibCImPlot.PlotStairs(label_id, Float64.(values), count, xscale, x0, offset, stride)
end

function PlotStairs(label_id, x::AbstractArray{T}, y::AbstractArray{T}, count::Integer, offset::Integer = 0, stride::Integer = sizeof(T)) where {T<:ImPlotData}
    LibCImPlot.PlotStairs(label_id, x, y, count, offset, stride)
end

function PlotStairs(label_id, x::AbstractArray{T}, y::AbstractArray{T}, count::Integer, offset::Integer = 0, stride::Integer = sizeof(Float64)) where {T<:Real}
    LibCImPlot.PlotStairs(label_id, Float64.(x), Float64.(y), count, offset, stride)
end

function PlotStairs(y::AbstractArray{T};
    label_id::String="",
    count::Integer=length(y),
    xscale::Real = 1.0, x0::Real = 0.0,
    offset::Integer=0,
    stride::Integer=1
) where {T <: ImPlotData}

    LibCImPlot.PlotStairs(label_id, y, count, xscale, x0, offset, stride * sizeof(T))
end

function PlotStairs(x::AbstractArray{T}, y::AbstractArray{T};
    count::Integer = min(length(x), length(y)),
    offset::Integer = 0,
    stride::Integer = 1,
    label_id::String = ""
) where {T <: ImPlotData}

    LibCImPlot.PlotStairs(label_id, x, y, count, offset, stride * sizeof(T))
end


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

module DSFMT

import Base: copy, copy!, ==, hash
using Base.GMP.MPZ

export DSFMT_state, dsfmt_get_min_array_size, dsfmt_get_idstring,
       dsfmt_init_gen_rand, dsfmt_init_by_array, dsfmt_gv_init_by_array,
       dsfmt_fill_array_close_open!, dsfmt_fill_array_close1_open2!,
       win32_SystemFunction036!

"Mersenne Exponent"
const MEXP = 19937

"DSFMT internal state array size of N 128-bit integers."
const N = floor(Int, ((MEXP - 128) / 104 + 1))

"""
Julia DSFMT state representation size counted in 32-bit integers.

Size: (DSFMT state array of Int128 + 1)*4 + Int32 index + Int32 padding
"""
const JN32 = (N+1)*4+1+1

struct DSFMT_state
    val::Vector{Int32}

    function DSFMT_state(val::Vector{Int32} = zeros(Int32, JN32))
        length(val) == JN32 ||
            throw(DomainError(length(val), "Expected length: $JN32."))
        new(val)
    end
end

copy!(dst::DSFMT_state, src::DSFMT_state) = (copyto!(dst.val, src.val); dst)
copy(src::DSFMT_state) = DSFMT_state(copy(src.val))

==(s1::DSFMT_state, s2::DSFMT_state) = s1.val == s2.val

hash(s::DSFMT_state, h::UInt) = hash(s.val, h)

## wrapper functions

function dsfmt_get_idstring()
    idstring = ccall((:dsfmt_get_idstring,:libdSFMT),
                     Ptr{UInt8},
                     ())
    return unsafe_string(idstring)
end

function dsfmt_get_min_array_size()
    min_array_size = ccall((:dsfmt_get_min_array_size,:libdSFMT),
                           Int32,
                           ())
end

const dsfmt_min_array_size = dsfmt_get_min_array_size()

function dsfmt_init_gen_rand(s::DSFMT_state, seed::UInt32)
    ccall((:dsfmt_init_gen_rand,:libdSFMT),
          Cvoid,
          (Ptr{Cvoid}, UInt32,),
          s.val, seed)
end

function dsfmt_init_by_array(s::DSFMT_state, seed::Vector{UInt32})
    ccall((:dsfmt_init_by_array,:libdSFMT),
          Cvoid,
          (Ptr{Cvoid}, Ptr{UInt32}, Int32),
          s.val, seed, length(seed))
end

function dsfmt_gv_init_by_array(seed::Vector{UInt32})
    ccall((:dsfmt_gv_init_by_array,:libdSFMT),
          Cvoid,
          (Ptr{UInt32}, Int32),
          seed, length(seed))
end

function dsfmt_fill_array_close1_open2!(s::DSFMT_state, A::Ptr{Float64}, n::Int)
    @assert Csize_t(A) % 16 == 0 # the underlying C array must be 16-byte aligned
    @assert dsfmt_min_array_size <= n && iseven(n)
    ccall((:dsfmt_fill_array_close1_open2,:libdSFMT),
          Cvoid,
          (Ptr{Cvoid}, Ptr{Float64}, Int),
          s.val, A, n)
end

function dsfmt_fill_array_close_open!(s::DSFMT_state, A::Ptr{Float64}, n::Int)
    @assert Csize_t(A) % 16 == 0 # the underlying C array must be 16-byte aligned
    @assert dsfmt_min_array_size <= n && iseven(n)
    ccall((:dsfmt_fill_array_close_open,:libdSFMT),
          Cvoid,
          (Ptr{Cvoid}, Ptr{Float64}, Int),
          s.val, A, n)
end

# dSFMT jump poly calculation

"Represents a polynomial in GF(2)[X]"
struct GF2X
    z::BigInt
end

GF2X(s::AbstractString) = GF2X(parse(BigInt, reverse(s), base = 16))
Base.string(f::GF2X) = reverse(string(f.z, base = 16))
Base.:(==)(f::GF2X, g::GF2X) = f.z == g.z
Base.copy(f::GF2X) = GF2X(MPZ.set(f.z))

degree(f::GF2X) = Base.ndigits0z(f.z, 2) - 1
coeff(f::GF2X, i) = MPZ.tstbit(f.z, i)
setcoeff!(f::GF2X, i) = (MPZ.setbit!(f.z, i); f)

mulx!(f::GF2X, c::Int=1) = (MPZ.mul_2exp!(f.z, c), f)
xor!(f::GF2X, g::GF2X) = (MPZ.xor!(f.z, g.z); f)

# compute f*X mod m
function mulxmod!(f::GF2X, m::GF2X, deg=degree(m))::GF2X
    # precondition: degree(f) < degree(m)
    mulx!(f)
    degree(f) == deg && xor!(f, m)
    f
end

# cache for X^(2i) mod m
const _squares = Dict{GF2X, Vector{GF2X}}()

# compute f^2 mod m
function sqrmod!(f::GF2X, m::GF2X)::GF2X
    d = degree(m)-1
    0 <= degree(f) <= d || throw(DomainError("f must satisfy 0 <= degree(f) <= degree(m)-1"))
    sqrs = get!(_squares, m) do
        x2i = GF2X(1)
        GF2X[copy(mulxmod!(mulxmod!(x2i, m, d+1), m, d+1)) for i=1:d]
    end
    foldl(GF2X(0), filter(i->coeff(f, i), 0:degree(f))) do g, i
        i <= d÷2 ? # optimization for "simple" squares
            setcoeff!(g, 2i) :
            xor!(g, sqrs[i])
    end
end

# compute X^e mod m
function powxmod(e::BigInt, m::GF2X)::GF2X
    e < 0 && throw(DomainError("e must be >= 0"))
    foldl(GF2X(1), Base.ndigits0z(e, 2)-1:-1:0) do f, i
        MPZ.tstbit(e, i) ?
            mulxmod!(sqrmod!(f, m), m) :
            sqrmod!(f, m)
    end
end

"Cached jump polynomials for `MersenneTwister`."
const JumpPolys = Dict{BigInt,GF2X}()

const CharPoly = Ref{GF2X}()
# Ref because it can not be initialized at compile time

function __init__()
    CharPoly[] = GF2X(Poly19937)
    JumpPolys[big(10)^20] = GF2X(JPOLY1e20)
end

"""
    calc_jump(steps::Integer)

Compute the dSFMT jump polynomial for `steps` steps (by default for
the Mersenne-Twister with exponent 19937). Note that `steps` should be
less than the period (e.g. ``steps ≪ 2^19937-1``).
"""
function calc_jump(steps::Integer,
                   charpoly::GF2X=CharPoly[])::GF2X
    steps < 0 && throw(DomainError("jump steps must be < 0 (got $steps)"))
    get!(JumpPolys, steps) do
        powxmod(big(steps), charpoly)
    end
end

## dSFMT jump

function dsfmt_jump(s::DSFMT_state, jp::GF2X)
    degree(jp) < degree(CharPoly[]) || throw(DomainError("degree of jump polynomial must be < $(degree(CharPoly[])), got $(degree(jp))"))

    val = s.val
    nval = length(val)
    index = val[nval - 1]
    work = zeros(Int32, JN32)
    rwork = reinterpret(UInt64, work)
    dsfmt = Vector{UInt64}(undef, nval >> 1)
    ccall(:memcpy, Ptr{Cvoid}, (Ptr{UInt64}, Ptr{Int32}, Csize_t),
          dsfmt, val, (nval - 1) * sizeof(Int32))
    dsfmt[end] = UInt64(N*2)

    for i in 0:degree(jp)
        coeff(jp, i) && dsfmt_jump_add!(rwork, dsfmt)
        dsfmt_jump_next_state!(dsfmt)
    end

    rwork[end] = index
    return DSFMT_state(work)
end

function dsfmt_jump_add!(dest::AbstractVector{UInt64}, src::Vector{UInt64})
    dp = dest[end] >> 1
    sp = src[end] >> 1
    diff = ((sp - dp + N) % N)
    i = 1
    while i <= N-diff
        j = i*2-1
        p = j + diff*2
        dest[j]   ⊻= src[p]
        dest[j+1] ⊻= src[p+1]
        i += 1
    end
    while i <= N
        j = i*2-1
        p = j + (diff - N)*2
        dest[j]   ⊻= src[p]
        dest[j+1] ⊻= src[p+1]
        i += 1
    end
    dest[N*2+1] ⊻= src[N*2+1]
    dest[N*2+2] ⊻= src[N*2+2]
    return dest
end

function dsfmt_jump_next_state!(mts::Vector{UInt64})
    POS1 = 117
    SL1  = 19
    SR   = 12
    MSK1 = 0x000ffafffffffb3f
    MSK2 = 0x000ffdfffc90fffd

    idx = (mts[end] >> 1) % N

    a = idx*2+1
    b = ((idx + POS1) % N)*2+1
    u = N*2+1

    t0 = mts[a]
    t1 = mts[a+1]
    L0 = mts[u]
    L1 = mts[u+1]
    mts[u]   = xor(t0 << SL1, L1 >> 32, L1 << 32, mts[b])
    mts[u+1] = xor(t1 << SL1, L0 >> 32, L0 << 32, mts[b+1])
    mts[a]   = xor(mts[u]   >> SR, mts[u]   & MSK1, t0)
    mts[a+1] = xor(mts[u+1] >> SR, mts[u+1] & MSK2, t1)

    mts[end] = (mts[end] + 2) % (N*2)
    return mts
end

"Jump polynomial for 10^20 steps for dSFMT with exponent 19937"
const JPOLY1e20 = "e172e20c5d2de26b567c0cace9e7c6cc4407bd5ffcd22ca59d37b73d54fdbd937cd3abc6f502e8c186dbd4f1a06b9e2b894f31be77424f94dddfd5a45888a84ca66eeeb242eefe6764ed859dafccae7a6a635b3a63fe9dfbbd5f2d3f2610d39388f53060e84edae75be4f4f2272c0f1f26d1231836ad040ab091550f8a3a5423fb3ab83e068fe2684057f15691c4dc757a3aee4bca8595bf1ad03500d9620a5dbe3b2d64380694895d2f379ca928238293ea267ce14236d5be816a61f018fe4f6bc3c9865f5d4d4186e320ab653d1f3c035ae83e2ad725648a67d3480331e763a1dcdfb5711b56796170b124f5febd723a664a2deefbfa9999d922a108b0e683582ae8d3baacb5bb56683405ea9e6e0d71ddb24b2229c72bb9d07061f2d1fa097ade823b607a2029d6e121ae09d93de01a154199e8e6a6e77c970bda72ba8079b2b3a15dd494a3188b1d94a25ae108a8a5bd0b050e6ce64a365a21420e07fdeebecae02eb68a4304b59283055d22c27d680ea35952834d828c9b9b9dd1a886b4f7fe82fe8f2a962e1e5390e563dc281c799aee2a441b7a813facb6ff5e94c059710dcfe7e6b1635e21ae0dc878dd5f7cc0e1101a74452495a67d23a2672c939f32c81d4a2611073990e92a084cc3a62fd42ee566f29d963a9cc5100ccd0a200f49ce0a74fa891efa1b974d342b7fedf9269e40d9b34e3c59c3d37201aecd5a04f4ae3d0c9a68c7ab78c662390e4cf36cb63ea3539c442efd0bf4aace4b8c8bde93c3d84b4d6290adfae1c5e3fcd457b6f3159e501f17b72ff6bc13d6bf61fbdafabefd16ac1dae0bca667e4e16a2b800732f1d0a9274c8a4c6cccd2db62fc275dc308c31c11cd6fda78de2f81f0e542b76b42b2cc09ed8f965d94c714c9918064f53af5379cfbbc31edf9cbce694f63a75f122048de6e57b094908f749661456813a908027f5d8397ab7962bf75ac779a3e1b7ae3fbc93397a67b486bb849befff1de6162ef2819715a88f41881e366ace692a900796a2806393898dd1750ac2b4ca3d34ca48942322fb6375f0c9a00c9701048ee8d7d7a17e11739177a7ad5027556e85835daf8594d84a97fe6621c0fce1495ae6ab8676cdc992d247acf5a4e5ec8c4755fde28117228d2c3ecf89edb91e93d949e2174924572265e36d176d082ed1be884e51d885ba3cda175c51edcee5042eaf519d292aa05aa4185b03858d710a9d0880b3d4e5111f858a52fe352cbe0a24f06a3d977ae2eb85e2a03a68131d0ab91dac4941067cf90ecd0fce156bcd40b8968cd4aa11e0b4353b14508d79d13ac00af4a4d452496b7f2393699889aa1e508427dbf0be3db91d955feb51e559af57640c6b3f9d5f95609852c28f9462a9869dd93acbdb1aafb2381ebb886a0b3fcec278f8bb0f62c23e157e49b89245b0881268ce594acbddd3605b9eaa77c9ff513e0dbad514914136d96fe2843fe2b4e886a0b718a9b8d1132132110618d0d3595da284cd2a4c9d09386199e4f4d7723983d3a374b51cf20dac5cabb4ff7e7197c2ebd9318463409baa583d6a6115c1b768282ff37b0fe152c97671e400d5ccba7d6875df0bf95c5d91257fedb124de393f31908d0e36251326aa29dd5be86291c80b4bf78f419ec151eeaeff643a58b48ab35ad2cd2c0b77b1965966ef3db6b6373cb2c4b590cef2f16f4d6f62f13a6cbf1a481565b5935edd4e76f7b6a8fd0d74bc336b40a803aec38125c006c877dfdcdb9ba2b7aecab5cafe6076e024c73e3567adf97f607a71d180402c22a20a8388f517484cc4198f97c2fe4f3407e0dc577e61f0f71354aa601cf4e3e42e1edd8722d50f5af3441f68caa568cc1c3a19956c1233f265bb47236afab24ee42b27b0042b90693d77c1923147360ae6503f6ba6abbc9dd52a7b4c36a3b6b55f6a80cfa7f101dd9f1bfc7d7eaf09a5d636b510228f245bfb37b4625025d2c911435cdf6f878113753e0804ab8ecab870ad733b9728d7636b17578b41239393e7de47cbce871137d2b61729dda67b2b84cd3363aad64c5dd5bd172f1f091305b1ff78982abe7dab1588036d097cf497e300e6c78a926048febd1b9462c07f5868928357b74297c87f503056b89f786d22a538b6702e290bca04639a0f1d0939b67f409e5e58e472a6a07fa543e2531c2567ec73c41f6769b6ba94c5aa0a030d006f5b6b1c5fb218b86a8f63a48bc867466f20f699859e87956f48a182d26ed451861dd21201ecc7239037ada67319bdf0849c387c73a110af798b4c5f9018bc97993e060ea2a2937fa2eb095d65ec07009fc407a350f1d6fb3c98a0a5f204be985b0cb6962f0eb7844a179c4598a92ea32d2d706c800034d2e960ded5b476d77073316b933fb3e6ba2f4f24a3b73a1e4d8ed1491d757ecf56fd72465dac0000736744d28d29073091587c8bccad302f7054e8a32bb8724974d9f3e449fc70b2a41f0008b548f717ac0a2c3a6580bfb50774933a578ad6acdcb89940bb406ea540893f097d8a88d1609ed605f25499de939083a0c8a7c6db462df5dfa06c298dd233e249433a54267d5cdc22e5524705b7d6b16b96bb2cb83e00cef62e21d91528a74cf95bfd1d391590c93f4058e9bb02656fd087a5b63d738d1c3b5cf533fd59c81cf9136bfcd3e955c19daf9906ef175791fde6a1d98155d7881e241c3522551cf9fcae42e1e46929ea39fd00943446823f9755085ccc8456a3090b73a3031a201d9c704a4ad4868dd9b6d06205560013973f60d637de2f18354bf4523d9d81dc2a7e78cd42c586364bbe0ed86fde0f081f801c1a4abb830839b7796d9a01f141bec8bd93144104c6dc59170162c0a5a639eb63a0a164970de50eb2e04f027394b26ed48d341f7851994df79d7cd663672a556f25e5e16a3adbe1003d631de938fabfed234df12b5ff3027f4a2da823834cb098e0f977a4eb9614579d5c7a1d400a1a933a657aef8ea1a66743d73b0cf37a7d64e9a63e4c7b09945f0db750b311b39783fb5ea216616751967d480a630d3da7c89d1c7beae20369137e96734a4cfedca56a7887f076fe4fe97534ad3e4f74d1a81750581a5ea214b440c7f30331ab86c257534c71175d1e731303a48b01c589fda4fb0d4368b4dd63d91204cb6fc389b2202aa94391907bfb72902a4031f5589ed5f391c2ce92aa998c200ba3c77d8bd747b9d0a29fa85cda3949a6d2bd0c3402e68f98fd451aa27b6c2dfd170e004577cbdb25e3a1b9852e9f66a370789c47bfce722446dade1b32ceae71ee0e1d96edf7ed08a93e3690056f46c3d8e63f88e53673ee71d72cfedbeba493ee91333120e09e9ce9f9c9a7a400f814ea618b1de48f9805e092f4e20f301fbb65caa83735a2a5c89befe4bce4116dca3688e1e14c6f09a945671dedbb5c0ba526842b6cae31d8b5ff978bae928a17a75c134630dd9de988f6ad3d89a071b33775a9660a40b48ec61ad3f93ac81cb1c65d8b0bab5c214786abd13cc10a8ea2e2a370e86e2fa1a372d83c9697b5e37b281e51507685f714fdaebe49ffc93a5582e1936eaee8e4140a4b72"

"Characteristic polynomial used by dSFMT with exponent 19937"
const Poly19937 = "10455110544511444054554514541dbbb0a20820a288a00a80a82208280b73237d2ff2ab5e2fcac914510101df18802aaaa08a022820a02aaa202ae822c364b91fb08598fe4410154041f028a266586e59cf12ce316b3a56af45cb49d02c7059cb89d2d81b0d288badbb25fa4e53b280aa88a22aa82abe7fc0645b7d7a4650c1dec48f21224e3d0e6e04c062c2273ef0d8361415c724dbc8f79118d5fac429f35dc460f6007e54c3966c28808e4c9308cc46e2e0e153bd922223796d4101af867e16e6641e6831e06ebbd2beaf52b2b47076dbf3a3e36c6d14d19dbf5d4b2b44b4d3aa6e1ea102578d765f08e1cb0749220075b8aae81c6e99692a56b35ddd4cf91b1034f1398c98e2d4ac8dbed09bc73ede098514cf3ffdf45cbb59335e3ec42f5f6a5672acc4ca8daa02a2502350ac0485f8b36f27d7a1d4d4b22fc7601e22a4f7c6ba53782b335837a21a068e8fcf3fdebb28860157301519cdea328b1ef4b8d5bc512ce65ff33298c34cc98ea1558f7b6d4db594f4bcab58d9f7bcf5cc59e259216de13f77569bbcee1c8abd55de985b7129e611d156c08cafa022ad7a2206a34a61e5c4c91e75495112003ec03c182a0155d9221382d0b30f45a00d6c19740f9ecebb448de78dfc0342f14166f54afdb97d00ac1a390355ce2aa9de1b3c919d8dd188fc9451ce9c86fa560a2da9dcaa681efd21fe5b6055f8e35a833e5704936245d06e387bf9956251bf0a252628be7a3cb0edab4aaaf141e6d7a9929cc03afa76ca433016d880def9e4cf10f7274e58aa35c372b7b7fb210fe0dc1a6b8445e7774ad6001b9aa1f2a01a790ee849e7ac810e0a646e733a7121bd727a9b593e0c9f5306dff5105af5967e3cee81069100d7e91a9c266e64c9e073a6e527765308879b22ca062668f9a564da6314dcad51405f160e8259582b7c06c313c84b0acba44958c052e6be540a7688e240232bee40b990dc48ee07d560786ca34e7df1bbdd2bca38a30c548ec57e91906b8417ff0da68db9c154d8ad83b06a6fc2b2e14ca5fbe7bd50382949c9b1f6e8540d9d43b35fa76a6ded27c2f17095a4f330626c5e86e8ff88ae53e05a434356a04a1ddae43b6e2ab883719360fbece72b6090ab17414ca7006e49d183813422c808fde53a30f872254bb554653aef86855f95a9361782100de2402efd749cc8cf6a837066c1c40c0160e415d8119e53a09877f1160ef8b99b2839e9b8c09b1e461e906041344c8fc2ef0a8eda04e319da41e001e60bc64dcdfc0593dff0f4b390580e1cd5b3c16204e77df10217791f99de49fcdc9160b793fd980bff7ae0cbd570425eb439352e5032e03797461376b5fb92aa156ad64935cc201a162f10f04b6d2d20a0415ae16f299e98baa86466f6f517f05f430254884a4010ac196540b0644e3c274323d4f0206780d38175f1e41fcb65bd387be073abee61b99d43f6b9106953ae4f6906e6ac0741e26af05fa9150c4f380558668aa667e404e3784b839d631e35af015024dbfc3dce4685574cd1e58eb72c70011090a2a876b65e34cb6cf297d24cb61ccca5a9706f34ae1f66345998f850de4e48d77cdf6e00fb0d2210ec9fb53fed02a781f7dfcdb609b9f955504f450e4b7b623cb0f5ba1ea09d92cd8d14f7e827b4580855f3a7dc2e5a45817df9e26adb5934f6026f745cb0f65e71c590bd954d1abc3826379719b7c6f4a0cbe6eb22a903b98bef785bb96efe2fc96988722c91f3e59d28d8244c8bfb59ee26082d82cad937ee70f178b44082533308ca24f236d8b91ca7af5b3d865c90d61410e1ffe39be6433a12ef2932e101b4bb34befa76748e0364a96f06e7932f44297fd5f85765b662c3ade19d9a9d9479f6de20b6b753d3dcbadf63e344578b98af85b4c4c63be9d154864f5d341f210f3503a60efec38ee59069499d0049aedfaef9264a7ce9de460a01e5437254fc68dfaebaa5e0e791380806bc149aedbc1d771457770937a5d606fc5ae728993783e6c45e1f5e1b9492a32f9df46a01020792a3803af04837a3905e7cc6ae68c512cb58f4facb457476729bac1ac89a7a32dc6857edbd6624ebeffed9d4e84a2f4ded9759962635aac94ca72d039c14af6e932fc84c25de28688acab0f41ae931a0f35b9c4195483d902f95e0d3e745e08f334cf5062da9fbd18fbb9efeb275a50a8a479939aa3e376821a030f0d241a4c4f6e3298a43a7f2166db483946c5ca623087b6252e27b09805d92a834ad914b9555f9c36150bb17d7e85f6b1a10b41a5c36a1fd0fdc354ec91720b245b0abcde6b38fdd33f471aa6ba1dbb4f178c3c6d8140a1f0700e072b0f9aafe1208abc94eea750b84e74ba6781b48c398ddfd2842e5447a11767e4f467864e4447382379010669f07870339ca42ac290063b40eaba1daa63cf6cd70c5a600c018f6933dc8bec000a521344cad7320ba121dd21f4723d38a8d3ab8c1f0f6c5a6995b796af79407ae1a7ce6d78922677d9990dd2a588f8a3323d80e9a69417f5224565afa60b64604e7316229514dcb7282b4e638981a5751d114da1ac9bf31f0e2fff5213f2020f9f2f31a8fd0c614e80c1a056d4b1af3ded2f696578f56427962ca54f4a28a502a0ac59013416abd81515fb1956ccb494c05ef61cab48474b6b1609cc5a3871a5111f8bf0a76b378f0e646687829e30f5762156da66c1b1c7abc0eb84e6ff2b9f5b22d34540ab97d643e8dd2e35f6e9e4fc2c30d8af88b2caab7bd5d4a6cf967e8ef79967c1ae85bf7d410a79f4630f13fdc6507d339909b81a29d84741103371310e5b4e279758431df627553b6826fc4c98e5fb6551315b0bd811b7b0f357198210dd99ccd8fba2904114c3e0b344eba43832b3c507e8b6b586e4ab3dc7a2ec71e150c54a13eca2340328d0b3e419ab2ba862ee93fc239fb46d907628055e105318e7fa52f9a83deb0e3cb02c62b8817702ead02a315f76aa1d08860cc5214a867808e33f8e075241956f148f876f3bc66566773610c9c5935b559c0ac47d84b6bfb112f59842be58df51055cf9180264f53f7795d4c934718bc65f359e34a8d230408854685b59c3a9f4d73a229bb465d4da3165404c6786c767299f57dfa85a83492fb4f61386441c928224cd88a7f4b36f245b7aa2b5c97b545ac4db8afe9a1a87e27b57d94c2bbffdb6e88f812aa27e0908048812086c2a72289d7bf136b3a1042a44d1913d39caec24ffda22814706f080b6cbe00e9cd442ccdcb600a436c0daeacbc5482021ba8a06c1fedbb333793557d5175b9313799ff91dfa620380a9e2a10132f0818bba72072e359726e2bd1f2ec98e0face32e0f88ee2c6f7cef7c2fbceffe8d3ccdff97b7ff71d861ba8b98237ccfb00176ee02206ccc08026cee082a88a8a349a1c9016983ea10789272105032f89b3113fd9b75b35c884622ec884622ee8aee2aaa2aeae86868c868ea68ea6862e0624062ea22aa66ee66ee44ee64ce64ce64ce646464c4808081808080a0a0828282828282822222a8898888888888aaaa2aaaaaaaaaaaaa2000000000000000008"

end # module


module ROOTHistograms

using ROOT, Histograms

import ROOT.to_root
function to_root{T, E}(h::ErrorHistogram{T, 2, E}, name="hist")
    nbins_x = nbins(h, 1)
    nbins_y = nbins(h, 2)

    conts, errs = contents(h), errors(h)

    #remove overflow bins
    e1 = convert(Vector{Float64}, h.edges[1][2:end-1])
    e2 = convert(Vector{Float64}, h.edges[2][2:end-1])

    hi = TH2D(name, name, Int32(nbins_x - 3), pointer(e1), Int32(nbins_y - 3), pointer(e2))
    for i=0:nbins_x-2
        for j=0:nbins_y-2
            SetBinContent(hi, Int32(i), Int32(j), conts[i + 1, j + 1])
            SetBinError(hi, Int32(i), Int32(j), errs[i + 1, j + 1])
        end
    end
    SetEntries(hi, sum(entries(h)))
    return hi
end

function to_root{T, E}(h::ErrorHistogram{T, 1, E}, name="hist")

    #remove underflow low and overflow high
    _edges = collect(edges(h)[1])[2:end-1]
    hi = TH1D(name, name, Int32(nbins(h)[1] - 2), pointer(_edges))
    for i=0:GetNbinsX(hi)+1
        SetBinContent(hi, Int32(i), contents(h)[i + 1])
        SetBinError(hi, Int32(i), errors(h)[i + 1])
    end
    SetEntries(hi, sum(entries(h)))

    # # println(h)
    # # Print(hi, "ALL")
    # conts, errs, ents, _edges2 = get_hist_bins(hi)
    #
    # if !all(conts .==  contents(h))
    #     warn("mismatch in contents: \n", hcat(conts, contents(h))|>string, "\n")
    # end
    # if !all(errs .==  errors(h))
    #     warn("mismatch in errors: \n", hcat(errs, errors(h))|>string, "\n")
    # end
    # # if !all(ents .==  entries(h))
    # #     warn("mismatch in entries: \n", hcat(ents, entries(h))|>string, "\n")
    # # end
    # if !all(_edges2 .==  _edges)
    #     warn("mismatch in edges: \n", hcat(_edges, edges(h)[1])|>string, "\n")
    # end
    return hi
end

function get_hist_bins(h::Union{TH1D, TH1A, TH1}; error_type=:contents)
    assert(h.p != C_NULL)
    nb = Int64(GetNbinsX(h))
    nb>0 || error("nbins = $nb")

    #+3 = underflow, overflow, superfluous extra bin
    conts = zeros(Float64, nb+2)
    errs = zeros(Float64, nb+2)
    ents = zeros(Float64, nb+2)

    #underflow low, overflow low, overflow high
    edges = zeros(Float64, nb+3)
    for n=0:nb+1
        #println("$n ", GetBinLowEdge(h, Int32(n)), " ", GetBinContent(h, Int32(n)), " ", GetBinError(h, Int32(n)))
        conts[n+1] = GetBinContent(h, Int32(n))
        errs[n+1] = GetBinError(h, Int32(n))
        #entries[n+1] = GetEntries(h) * conts[n+1]
        edges[n+1] = GetBinLowEdge(h, Int32(n))
    end

    #this works for histograms for which the bin errors have been manually set
    #to non-Poisson, GetEntries is meaningless
    if error_type == :errors
        ents = (conts ./ errs).^2
        ents[isnan(ents)] = 0
        ents[ents .== Inf] = 1
        #println(hcat(conts, errs, ents, conts./sqrt(ents)))
    end

    #this works for Poisson bin errors
    if error_type == :contents
        ents = conts ./ sum(conts) .* GetEntries(h)
        ents[isnan(ents)] = 0
    end
    #ents = int(round(ents))

    edges[1] = -Inf
    edges[nb+2] = edges[nb+1] + GetBinWidth(h, Int32(nb))
    edges[nb+3] = Inf

    if error_type != :errors
        if GetEntries(h)>0
            @assert abs(sum(ents) - GetEntries(h))/sum(ents)<0.0001 string("entries unequal ", sum(ents), "!=", GetEntries(h))
        end
    end
    if Integral(h)>0
        @assert abs(sum(conts) - Integral(h, Int32(0), Int32(nb+1)))/sum(conts)<0.0001 string("contents unequal ", sum(conts), "!=", Integral(h, Int32(0), Int32(nb+1)))
    end
    # if (abs(sum(conts) - Integral(h)) > 100000 * eps(Float64))
    #     warn(
    #         GetName(h)|>bytestring,
    #         " integral mismatch: $(sum(conts)) != $(Integral(h))"
    #     )
    # end
    return conts, errs, ents, edges
end

function load_with_errors(f::TDirectoryA, k::ASCIIString; kwargs...)
    th = root_cast(TH1, Get(f, k))
    #println(th)
    conts, errs, ents, edgs = get_hist_bins(th; kwargs...)
    h = Histogram(ents, conts, edgs)
    return h
end

function from_root(o::TH1A)
    assert(o.p != C_NULL)
    conts, errs, ents, edgs = get_hist_bins(o)
    h = ErrorHistogram{Float64, 1, Tuple{Vector{Float64}}}(
        (edgs, ), conts, errs.^2, :left
    )
    # for i=1:nbins(h)
    #     @assert abs(errors(h)[i] - errs[i])<0.0001 string("errors unequal $(hcat(errors(h), errs))")
    # end
    return h
end

function from_root(o::TH2A)
    assert(o.p != C_NULL)
    nx, ny = size(o)
    #println("nx=$nx ny=$ny")
    conts = zeros(nx+2, ny+2)
    errs = zeros(nx+2, ny+2)
    edgs = (zeros(Float64, nx+3), zeros(Float64, ny+3))
    for x=0:nx+1
        edgs[1][x+1] = GetBinLowEdgeX(o, Int32(x))
    end
    for y=0:ny+1
        edgs[2][y+1] = GetBinLowEdgeY(o, Int32(y))
    end
    edgs[1][1] = -Inf
    edgs[1][end] = Inf
    edgs[2][1] = -Inf
    edgs[2][end] = Inf
    for x=0:nx+1
        for y=0:ny+1
            conts[x+1, y+1] =
                GetBinContent(root_cast(TH1, o), Int32(x), Int32(y))
            errs[x+1, y+1] =
                GetBinError(root_cast(TH1, o), Int32(x), Int32(y))
        end
    end

    return ErrorHistogram{Float64, 2, Tuple{Vector{Float64}, Vector{Float64}}}(
        edgs, conts, errs.^2, :left
    )
end

import Base.size
Base.size(o::TH2D) = (GetNbinsX(root_cast(TH1, o)), GetNbinsY(root_cast(TH1, o)))

function load_hists_from_file(fn, hfilter=(name->true))
    tf = TFile(fn)
    @assert tf.p != C_NULL

    kl = GetListOfKeys(tf)
    @assert kl.p != C_NULL

    key_iterator = TListIter(kl.p)
    #kl = GetListOfKeys(tf)
    #objs = GetList(tf)

    ret = Dict()

    tic()
    for i=1:length(kl)
        #(i % 10000 == 0) && (println("$(i)/$(length(kl)) ", toq());tic())
        _k = Next(key_iterator)
        @assert _k != C_NULL
        const k = TKey(_k)
        const n = GetName(k) |> bytestring
        hfilter(n) || continue
        const o = ReadObj(k) |> to_root
        ret[n] = from_root(o)
    end

    Close(tf)
    return ret
end

# function recurse_write(tf, hd::Associative, pref="")
#     if pref != ""
#         println("making $pref")
#         d = ROOT.mkdir(tf, pref)
#         @show d
#         if ROOT.root_pointer(d) == C_NULL
#             d2 =  root_cast(TDirectory, Get(tf, pref))
#             @show d2
#         end
#         d = root_cast(TDirectory, d)
#         assert(d.p != C_NULL)
#     else
#         d = tf
#     end
#     Cd(tf, pref)
#     for (k, v) in hd
#         s = length(pref)>0 ? "$pref/$k" : string(k)
#         recurse_write(d, v, s)
#     end
#     pref!= "" && Cd(tf, "..")
# end

# function recurse_write(tf::TDirectoryA, v::ErrorHistogram, pref="")
#     th = to_root(v)
#     @show tf
#     SetDirectory(th, ROOT.root_pointer(tf))
#     Write(th)
# end

function write_hists_to_file(fn, hd::Associative; verbose=false)
    tf = TFile(convert(ASCIIString, fn), "RECREATE")
    Cd(tf, "")
    for (objpath, obj) in hd
        spl = rsearch(objpath, "/")
        dirname = objpath[1:spl.start-1]
        objname = objpath[spl.start+1:end]
        if length(dirname) > 0
            verbose && println("dir=$dirname obj=$objname")
            d = Get(tf, dirname)
            if d.p == C_NULL
                verbose && println("making $dirname")
                d = ROOT.mkdir(tf, dirname)
            end
            d = root_cast(TDirectory, Get(tf, dirname))
            assert(d.p != C_NULL)
        else
            d = tf
        end
        verbose && println("TDirectory=$d")
        robj = to_root(obj, objname)
        SetDirectory(robj, convert(Ptr{TDirectory}, d.p))
    end
    nb = Write(tf)
    verbose && println("Wrote $nb bytes")
    Close(tf)
end

include("pyplot.jl")

export to_root, get_hist_bins, load_hists_from_file, from_root, write_hists_to_file
export load_with_errors

end


# ---------------------------------------------------------------------------
# This is experimental, and currently unused
# ---------------------------------------------------------------------------

export
    Operator,
    OpGraph,
    InputNode,
    OutputNode,
    Learnable,
    Constant,
    AbstractTransformation,
    op,
    add_op,
    UNARY,
    BINARY,
    ELEMENTWISE,
    AGGREGATOR,
    @op

"""
The idea here is that an Operator wraps an arbitrary function, and there are static query methods
which define how to apply the function to input(s) and how to update the output.

In addition, we should be able to define (as part of the type, and thus at compile time) inversion
functions, deriviatives, etc.

some core traits:
- is_mappable: one input, function applied elementwise, input size matches output size
    examples: sin, exp, log, sigmoid, tanh
- is_operator: two (or more?) inputs, output dimension/size is function of inputs
    examples: * (matrix multiply)
- is_element_operator: two (or more?) inputs, applied elementwise, input sizes match output size
    examples: +, -, .*, ./, max
- is_aggregator: one input, output dimension is less than input dimension
    examples: sum, product
"""

@enum OpType UNARY BINARY ELEMENTWISE AGGREGATOR

const _op_types = Dict{Symbol, Tuple{Function,OpType}}(
    :sigmoid => (sigmoid, UNARY),
    :tanh    => (tanh, UNARY),
    :*       => (*, BINARY),
    :+       => (+, ELEMENTWISE),
    :minimum => (minimum, AGGREGATOR),
)

op_function(s::Symbol) = _op_types[s][1]
op_type(s::Symbol)     = _op_types[s][2]

function add_op(s::Symbol, f::Function, t::OpType)
    _op_types[s] = (f, t)
end

is_op(s::Symbol) = haskey(_op_types, s)

# -------------------------------------------------------

# wrap a function call (static transformation) and input/output dimensions in a type signature
immutable Operator{OP,F,I,O} <: AbstractTransformation{I,O}
    f::Function
end

# convenience to instantiate an Operator
op(f::Function, I::Int, O::Int) = Operator{op_type(Symbol(f)), Symbol(f), I, O}(f)
op(s::Symbol, I::Int, O::Int) = Operator{op_type(s), s, I, O}(op_function(s))

(o::Operator)(args...) = (o.f)(args...)

LearnBase.transform!(y, o::Operator{UNARY}, args...) = map!(o.f, y, args...)
LearnBase.transform!(y, o::Operator{ELEMENTWISE}, args...) = map!(o.f, y, args...)
LearnBase.transform!(y, o::Operator{BINARY}, args...) = y[:] = o.f(args...)
LearnBase.transform!(y, o::Operator{AGGREGATOR}, args...) = y[:] = o.f(args...)

function Base.show{OP,F,I,O}(io::IO, o::Operator{OP,F,I,O})
    print(io, "Op{$OP, $F, $I, $O}")
end

is_unary{OPTYPE}(o::Operator{OPTYPE})       = OPTYPE == UNARY
is_binary{OPTYPE}(o::Operator{OPTYPE})      = OPTYPE == BINARY
is_elementwise{OPTYPE}(o::Operator{OPTYPE}) = OPTYPE == ELEMENTWISE
is_aggregator{OPTYPE}(o::Operator{OPTYPE})  = OPTYPE == AGGREGATOR

# -------------------------------------------------------

# const _mappables = [:sin, :exp, :log, :sigmoid, :tanh, :sqrt, :^]
# const _operators = [:*, :/, :\]
# const _element_ops = [:+, :-, :.*, :./, :max, :min, :.^]
# const _aggregators = [:sum, :product, :minimum, :maximum]

# function is_op(s::Symbol)
#     for v in (_mappables, _operators, _element_ops, _aggregators)
#         if s in v
#             return true
#         end
#     end
#     false
# end
# const _all_ops = vcat(_mappables, _operators, _element_ops, _aggregators)

# @generated function is_mappable{F,I,O}(o::Operator{F,I,O})
#     if F in _mappables
#         I==O || error("Mappables input and output dimensions should match")
#         :(true)
#     else
#         :(false)
#     end
# end
# @generated function is_operator{F,I,O}(o::Operator{F,I,O})
#     F in _operators ? :(true) : :(false)
# end
# @generated function is_element_operator{F,I,O}(o::Operator{F,I,O})
#     F in _element_ops ? :(true) : :(false)
# end
# @generated function is_aggregator{F,I,O}(o::Operator{F,I,O})
#     F in _aggregators ? :(true) : :(false)
# end

# -------------------------------------------------------

# -------------------------------------------------------

# input and output nodes allow for easy connectivity
immutable InputNode{I} <: AbstractTransformation{I,I} end
immutable OutputNode{O} <: AbstractTransformation{O,O} end

# a Learnable has no inputs, but produces an output (the parameters)
immutable Learnable{O} <: AbstractTransformation{0,O}
    name::Symbol
end

immutable Constant{O} <: AbstractTransformation{0,O}
    value::Float64
end

# an OpGraph is a lightweight representation of a directed graph of AbstractTransformations
type OpGraph{I,O} <: AbstractTransformation{I,O}
    nodes::Vector{AbstractTransformation}
    edges::Vector{NTuple{2,Int}}
    names::Vector{String}
end

# op_type(g::OpGraph) =

using PlotRecipes
@recipe function f(g::OpGraph)
    arrow --> arrow()
    markersize --> 50
    markeralpha --> 0.2
    linealpha --> 0.4
    linewidth --> 2
    names --> g.names
    func --> :tree
    root --> :left
    PlotRecipes.GraphPlot((Int[e[1] for e=g.edges], Int[e[2] for e=g.edges]))
end

# -------------------------------------------------------

"""
We want to build an op graph, where each node has input(s) and output(s)

```
# single input, single output
# w and b are assumed 'learnable parameters'
@op affine(x) = x * w + b

# single input, single output
# no learnable parameters
@op sigmoid(x) = 1 / (1 + exp(-x))

# this shouldn't need a special definiton... use the built-in method
# no learnable parameters
@op tanh(x) = tanh(x)

# 2 inputs, 2 outputs
w is a learnable parameter, and can be fused to avoid recomputing
@op f(x, y) = (w * (x-y), w * (x+y))
```

The point of this design is that we should be able to define simple functions
composed of previously defined components, and the `@op` macro will build
a connected graph of transformations.  Some notes:

- If all components and operations inside an op graph are differentiable, then so is the graph
    - macro should build `deriv` method (and others in a similar way)
- If a variable appears in the body, but not in the function signature, we assume it
    is a `LearnableParameter`, and include it (with gradient state objects) in the internals
    of the op graph (or its sub-components).
"""

const _input_index = 1
const _output_index = 2

# NOTE: in the add_item_to_graph methods, we want to:
#   - increase the numnodes
#   - call add_edge! for each connection

# remember that numnodes is also the index of the last-added node

function add_item_to_graph!(block::Expr, input_idx::Int, variables, numnodes::Base.RefValue{Int}, item, parent_idx)
    @show "EXPR",item input_idx variables numnodes[]
    dump(item, maxdepth=20)

    if isa(item, Symbol)
        _add_symbol_to_graph!(block, numnodes, item, parent_idx, variables)
    elseif isa(item, Number)
        _add_const_to_graph!(block, numnodes, item, parent_idx)

    elseif item.head == :call && is_op(item.args[1])
        # node_indices = Int[]
        func = shift!(item.args)

        add_node!(block, numnodes, func, :operation)
        this_idx = numnodes[]
        for x in item.args
            if isa(x, Symbol)
                _add_symbol_to_graph!(block, numnodes, x, this_idx, variables)
                # if x in variables
                #     # TODO: we should have multiple input nodes, one for each variable
                #     # connect the input node to this op
                #     add_edge!(block, _input_index, this_idx)
                # else
                #     # TODO: this is a Learnable... add a node and connect it to this op
                #     add_node!(block, numnodes, x, :learnable)
                #     add_edge!(block, numnodes[], this_idx)
                # end
            elseif isa(x, Number)
                _add_const_to_graph!(block, numnodes, x, this_idx)

            else
                # this is another op... build it
                add_item_to_graph!(block, input_idx, variables, numnodes, x, this_idx)
            end
        end

        # now connect this op to its parent
        add_edge!(block, this_idx, parent_idx)

    elseif item.head == :line
        # if it's a line number block, pass through to wrapped arg
        # add_item_to_graph!(block, input_idx, variables, numnodes, item.args[1])
        info("line")

    else
        error("OpGraph parse error: $item")
    end
    return
end

function _add_symbol_to_graph!(block, numnodes, x, parent_idx, variables)
    if x in variables
        # TODO: we should have multiple input nodes, one for each variable
        # connect the input node to this op
        add_edge!(block, _input_index, parent_idx)
    else
        # TODO: this is a Learnable... add a node and connect it to this op
        add_node!(block, numnodes, x, :learnable)
        add_edge!(block, numnodes[], parent_idx)
    end
end

function _add_const_to_graph!(block, numnodes, x, parent_idx)
    add_node!(block, numnodes, x, :constant)
    add_edge!(block, numnodes[], parent_idx)
end

# function add_item_to_graph!(block::Expr, input_idx::Int, variables, numnodes::Base.RefValue{Int}, item::Symbol, opidx)
#     @show "SYM",item input_idx variables numnodes[]
#     if item in variables
#         # TODO: we should have multiple input nodes, one for each variable
#         add_edge!(block, _input_index, opidx)
#     else
#         # TODO: this is a Learnable
#         add_node!(block, numnodes, item, :learnable)
#         add_edge!(block, numnodes[], opidx)
#     end
#     return opidx
# end

function add_node!(block::Expr, numnodes, node, nodetype::Symbol)
    nodesym = quot(node)
    nodeexpr = if nodetype == :learnable
        :(Learnable{1}($nodesym))
    elseif nodetype == :constant
        :(Constant{1}($node))
    elseif nodetype == :operation
        :(op($nodesym, 1, 1))
    else
        error("how do I add this node? $node $nodetype")
    end
    push!(block.args, esc(:(push!(g.nodes, $nodeexpr))))
    push!(block.args, esc(:(push!(g.names, string($nodesym)))))
    numnodes[] += 1
end

function add_edge!(block::Expr, i::Int, j::Int)
    push!(block.args, esc(:(push!(g.edges, ($i, $j)))))
end

function _op_macro(funcexpr::Expr, inout::NTuple{2,Int} = (1,1))
    dump(funcexpr; maxdepth=20)
    @show inout typeof(inout)

    func_signature, func_body = funcexpr.args

    if !(funcexpr.head in (:(=), :function))
        error("Must wrap a valid function call!")
    end
    if !(isa(func_signature, Expr) && func_signature.head == :call)
        error("Expected `func_signature = ...` with func_signature as a call Expr... got: $func_signature")
    end

    func_name = func_signature.args[1]
    variables = func_signature.args[2:end]
    @show func_name variables

    # TODO: we want to build an OpGraph as a function of Variable, Learnable, and Op components
    # To simplify, we'll assume that:
    #   - Expr --> Op
    #   - Symbol --> haskey(_ops, k) ? Variable : Learnable

    # Variables are not learnable, and they are NOT part of the graph.  They represent the inputs
    # to the OpGraph.

    I,O = inout

    block = Expr(:block)
    push!(block.args, esc(:(
        g = OpGraph{$I, $O}(
            AbstractTransformation[InputNode{$I}(), OutputNode{$O}()],
            NTuple{2,Int}[],
            String["Input", "Output"]
        )
    )))
    @show block

    # graph_nodes = [InputNode(), OutputNode()]
    # graph_edges = []
    numnodes = Ref(2) # input and output
    for item in func_body.args
        opidx = add_item_to_graph!(block, _input_index, variables, numnodes, item, 2)

        # # connect this node to the output node, since it is returned from the function
        # add_edge!(block, opidx, _output_index)
    end

    # # add this to the operators list
    # push!(_operators, func_name)
    push!(block.args, esc(:(Transformations.add_op($(quot(func_name)), g, Transformations.op_type(g)))))

    push!(block.args, esc(:(g)))
    @show block
    block
end

macro op(args...)
    _op_macro(args...)
end

#####
# below this point is old code:

# Base.call(o::Operator, input) = map(o.f, input)
# transform(o::Operator, input) = map(o.f, input)

# # operator(f::Function, I, O) = Operator{Symbol(f), I, O}(f)

# # same dimensions
# function transform!{T,F,I,O}(output::AbstractArray{T,O}, o::Operator{F,I,O}, input::AbstractArray{T,I})
#     map!(o.f, output, input)
# end
# function transform!{T,F,I,O}(output::AbstractArray{T,O}, o::Operator{F,I,O}, input::AbstractArray{T,I})
#     map!(o.f, output, input)
# end

# # @generated function transform!{T,F,I,O}(output::AbstractArray{T,O}, o::Operator{F,I,O}, input::AbstractArray{T,I})
# #     @show T, F, O, I

# #     :(for (i,j) in zip(eachindex(output), eachindex(input))
# #         output[i] = (o.f)(input[j])
# #     end)
# # end


import StatsBase: sample
using Random, KernelDensity

function sample(rng::AbstractRNG, chn::MCMCChains.AbstractChains, n;
    replace=true, ordered=false)
  indxs = sample(rng,
    range(chn),
    n,
    replace=replace, ordered=ordered)
  return Chains(chn.value[indxs, :, :],
    names(chn),
    chn.name_map,
    info = chn.info)
end

function sample(chn::MCMCChains.AbstractChains, n;
    replace=true, ordered=false)
  indxs = sample(Random.GLOBAL_RNG,
    range(chn),
    n,
    replace=replace, ordered=ordered)
  return Chains(chn.value[indxs, :, :],
    names(chn),
    chn.name_map,
    info = chn.info)
end

function sample(rng::AbstractRNG, chn::MCMCChains.AbstractChains,
    wv::AbstractWeights, n)
  indxs = sample(rng,
    range(chn),
    wv,
    n)
  return Chains(chn.value[indxs, :, :],
    names(chn),
    chn.name_map,
    info = chn.info)
end

function sample(chn::MCMCChains.AbstractChains,
    wv::AbstractWeights, n)
  indxs = sample(Random.GLOBAL_RNG,
    range(chn),
    wv,
    n)
    return Chains(chn.value[indxs, :, :],
      names(chn),
      chn.name_map,
      info = chn.info)
end


include("runtests_preamble.jl")
include("run_script.jl")

@testset "Examples" begin
    #####
    ##### Free convection example
    #####

    free_convection_filepath = joinpath(@__DIR__, "..", "examples", "free_convection.jl")

    replace_strings = [
        ("size=(32, 32, 32)", "size=(1, 1, 1)"),
        ("TimeInterval(4hour)", "TimeInterval(2minute)"),
        ("AveragedTimeInterval(1hour, window=15minute)", "AveragedTimeInterval(2minute, window=1minute)"),
        ("IterationInterval(100)", "IterationInterval(1)"),
        ("stop_time=8hour", "stop_time=1")
    ]

    @test run_script(replace_strings, "free_convection", free_convection_filepath)

    push!(replace_strings, ("CPU()", "GPU()"))
    @test_skip run_script(replace_strings, "free_convection", free_convection_filepath)
end


export
       Creation,
       Matrices,
       Graph,
       connect!,
       disconnect,
       remove_vertex!,
       reindex!,
       random_graph,
       target_vertex,
       cycles,
    number_of_neighbors

using LightGraphs

import LightGraphs: nv, ne, has_edge, is_directed

import Base.length
import Base.empty!
import Base.iterate
"""
```julia
type Edge
```

"""
mutable struct Edge{T}
  next_v1::Union{Nothing,Edge{T}}
  prev_v1::Union{Nothing,Edge{T}}
  next_v2::Union{Nothing,Edge{T}}
  prev_v2::Union{Nothing,Edge{T}}
  v1
  v2
  weight::T
end
function weight_type(edge::Edge{T}) where T
    return T
end
mutable struct Vertex{T, EdgeType}
   id::Integer
   data::T
   edges::Union{Nothing,Edge{EdgeType}}
   number_of_edges::Integer
   degree::Float64
   connections::Set{Integer}
   lock::Threads.SpinLock
end
function weight_type(v::Vertex{V,E}) where V where E
    return E
end
function Vertex(id::Integer,d::DataType = Any, val=nothing, weight_type::DataType=Float64)
    return Vertex{d, weight_type}(id,val,nothing,0,0, Set{Integer}(),Threads.SpinLock())
end
function Edge(v1::Vertex,v2::Vertex,w::Number)
    return Edge{weight_type(v1)}(nothing,nothing,nothing,nothing,v1,v2,convert(weight_type(v1),w))
end
struct Graph
  vertices::Vector{Vertex}
  is_dirty::Bool
end
struct EdgeIterator
   e::Union{Nothing,Edge}
   i::Integer
end
function empty!(g::Graph)
  for i=1:length(g.vertices)
    g.vertices[i].edges = nothing
    g.vertices[i].number_of_edges = 0
    g.vertices[i].degree = 0
  end
end
"""
```julia
Graph(n_vertices::Integer=0; vertex_type::DataType  = Any ,initial_value=nothing, weight_type::DataType = Float64)
```
Construct an undirected weighted grpah of `n_vertices` vertices.
"""
function Graph(n_vertices::Integer=0; vertex_type::DataType  = Any ,initial_value=nothing, weight_type::DataType = Float64)
   vertices = Vector{Vertex{vertex_type, weight_type}}(undef, n_vertices)
   for i=1:n_vertices
       vertices[i] = Vertex(i,vertex_type,initial_value, weight_type)
   end
   return Graph(vertices,false)
end

function is_directed(g::Graph)
    return false
end
"""
```julia
nv(g::Graph)
```
Return the number of vertices of `g`.
"""
function nv(g::Graph)
    return length(g.vertices)
end
function ne(g::Graph)
    return sum([v.number_of_edges for v in g.vertices])
end
function has_edge(gr::Graph, i::Integer, k::Integer)
    return (k in gr.vertices[i].connections)
end
function insert!(v::Vertex,e::Edge)
    if (e.v1 == v)
        #El siguiente del nuevo es el primero de la lista original
        e.next_v1 = v.edges
        #El anterior del primero de la lista original es ahora el nuevo
        if (v.edges != nothing)
          if (v.edges.v1 == v)
              v.edges.prev_v1 = e
         else
              v.edges.prev_v2 = e
          end
        end
        v.edges = e
    else
        e.next_v2 = v.edges
        if (v.edges != nothing)
          if (v.edges.v1 == v)
              v.edges.prev_v1 = e
          else
            v.edges.prev_v2 = e
          end
        end
        v.edges = e
    end
end

function set_previous(v::Vertex,e::Edge,prev::Union{Nothing,Edge})
    if (e.v1 == v)
        e.prev_v1=prev
    else
        e.prev_v2=prev
    end
end

function set_next(v::Vertex,e::Edge,next::Union{Nothing,Edge})
    if (e.v1 == v)
        e.next_v1=next
    else
        e.next_v2=next
    end
end
function linked_list_connect(v::Vertex,e::Edge,next::Union{Nothing,Edge})
    set_next(v,e,next)
    if (next != nothing)
        set_previous(v,next,e)
    end
end
function remove!(v::Vertex,e::Edge)
    if (e.v1 == v)
        prev = e.prev_v1
        if (prev == nothing)
           v.edges = e.next_v1
           if e.next_v1 != nothing
               set_previous(v,e.next_v1,nothing)
            end
           return
        else
           linked_list_connect(v,prev,e.next_v1)
        end
    else
        prev = e.prev_v2
        if (prev == nothing)
           v.edges = e.next_v2
           if e.next_v2 != nothing
              set_previous(v,e.next_v2,nothing)
           end
           return
        else
           linked_list_connect(v,prev,e.next_v2)
        end
    end
end
"""
```julia
function connect!(g::Graph, i::Integer, neighbors::Vector, weigths::Vector)
```
"""
function connect!(g::Graph, i::Integer, neighbors::Vector, w::Vector)
    for j=1:length(neighbors)
        connect!(g,i,neighbors[j],w[j])
    end
end

"""
```julia
connect!(g::Graph,i::Integer,j::Integer,w::Number)
```
Connect the vertex `i` with the vertex `j` with weight `w`.
"""
function connect!(g::Graph,i::Integer,j::Integer,w::Number)
    if (i==j)
        return
    end
    if i>nv(g) || j>nv(g)
      throw("Invalid vertex")
    end
    if (w<=0)
        return
    end
    vertex_j = g.vertices[j]
    vertex_i = g.vertices[i]

     if !(vertex_j.id in vertex_i.connections)
      edge = Edge(g.vertices[i],g.vertices[j],w)
      lock(vertex_i.lock)
      lock(vertex_j.lock)
      insert!(g.vertices[i],edge)
      insert!(g.vertices[j],edge)
      push!(vertex_i.connections, vertex_j.id)
      push!(vertex_j.connections, vertex_i.id)
      unlock(vertex_j.lock)
      unlock(vertex_i.lock)
      g.vertices[i].number_of_edges=g.vertices[i].number_of_edges+1
      g.vertices[j].number_of_edges=g.vertices[j].number_of_edges+1
      g.vertices[i].degree = g.vertices[i].degree + w
      g.vertices[j].degree = g.vertices[j].degree + w
    end

end

"""
```julia
target_vertex(e::Edge,v::Vertex)
```
Given an edge `e` and a vertex `v` returns the other vertex different from `v`
"""
function target_vertex(e::Edge,v::Vertex)
    if (e.v1 == v)
        return e.v2
    elseif (e.v2 == v)
        return e.v1
    else
        return Nothing
    end
end
"""
```julia
length(v::Vertex)
```
Return the number of edges connected to a given vertex.
"""
function length(v::Vertex)
    return v.number_of_edges
end
function iterate(v::Vertex)
    if v.edges == nothing
        return nothing
    else
        edge = v.edges
        next_edge = edge.v1 == v ? edge.next_v1 : edge.next_v2
        return (edge, next_edge)
    end
end

function iterate(v::Vertex, state)
    edge = state
    if edge == nothing
        return nothing
    else
        next_edge = edge.v1 == v ? edge.next_v1 : edge.next_v2
        return (edge, next_edge)
    end
end

import Base.show
function show(io::IO, e::Edge)
  println(string(e.v1.id," -(",e.weight, ")-> ",e.v2.id))
end
function show(io::IO, g::Graph)
  for vertex in g.vertices
         println(vertex)
         for e in vertex
           println(e)
       end
   end
end

function show(io::IO, v::Vertex)
    repr = "Vertex $(v.id) ("
    if v.data != nothing
      repr *= "data=$(v.data), "
    end
    repr *= "n_edges=$(v.number_of_edges), "
    repr *= "degree=$(v.degree))"
    println(repr)
end
function find_edge(p, v::Vertex)
     for e in v
         if (p(e))
           return e
         end
      end
      return nothing
end
"""
```julia
disconnect(g::Graph,i::Integer,j::Integer)
```
Removes the edge that connects the `i`-th vertex to the `j`-th vertex.
"""
function disconnect(g::Graph,i::Integer,j::Integer)
   vertex_i = g.vertices[i]
   vertex_j = g.vertices[j]
   edge = find_edge(e->(e.v1 == vertex_j || e.v2==vertex_j),g.vertices[i])
   remove!(vertex_i,edge)
   remove!(vertex_j,edge)
   delete!(vertex_i.connections, vertex_j.id)
   delete!(vertex_j.connections, vertex_i.id)
   vertex_i.degree = vertex_i.degree - edge.weight
   vertex_j.degree = vertex_j.degree - edge.weight
   g.is_dirty = true
end
function update_connections!(g::Graph)

   for i=1:nv(g)
       empty!(g.vertices[i].connections)
       for e in g.vertices[i]
            v_j = target_vertex(e, g.vertices[i])
            push!(g.vertices[i].connections, v_j.id)
       end
   end
end

function reindex!(g::Graph)
   for i=1:nv(g)
       g.vertices[i].id = i
   end
   update_connections!(g)
   g.is_dirty = false
end
"""
```julia
remove_vertex!(g::Graph,i::Integer)
```
Remove the `i`-th vertex.
"""
function remove_vertex!(g::Graph,i::Integer)
    if i>nv(g)
      throw("No se puede eliminar")
    end
    if (g.is_dirty)
      reindex!(g)
    end
    vertex_i = g.vertices[i]
    for e in vertex_i
      if e.v1 == vertex_i
            remove!(e.v2,e)
            e.v2.degree = e.v2.degree - e.weight
            e.v2.number_of_edges = e.v2.number_of_edges-1
        else
            remove!(e.v1,e)
            e.v1.number_of_edges = e.v1.number_of_edges-1
            e.v1.degree = e.v1.degree - e.weight
        end
    end
    deleteat!(g.vertices,i)
    g.is_dirty=true
end
function add_vertex!(g::Graph, datatype = Any, data=nothing)
   if g.is_dirty
      reindex!(g)
   end
   new_id = nv(g) +1
   vertex = Vertex(new_id,datatype, data)
   push!(g.vertices,vertex)
   return vertex
end
function connect(e::Edge, v::Vertex)
   return e.v1 == v || e.v2 == v
end
function number_of_neighbors(g::Graph)
    number = zeros(Int,nv(g))
    for i=1:length(g.vertices)
        n=0
        for e in g.vertices[i]
            n= n +1
        end
        number[i] = n
    end
    return number

end
"""
```
function random_graph(iterations::Integer; probs=[0.4,0.4,0.2], weight=()->5, debug=false)
```
Create a random graphs. `probs` is an array of probabilities. The function create a vertex with probability `probs[1]`, connect two vertices with probability `probs[2]` and delete a vertex with probability `probs[2]`. The weight of the edges is given by `weight`
"""
function random_graph(iterations::Integer; probs=[0.4,0.4,0.2], weight=()->5, debug=false)
  g= Graph()
  for i=1:iterations
    action = sum(rand() .>= cumsum(probs)) +1
    nog = nv(g)
    if action==1
        add_vertex!(g)
        if debug
           println("add_vertex!(g)")
        end
    elseif action == 2
        if (nog >= 2)
           v1 = rand(1:nog)
           v2 = rand(1:nog)
           if (v1!=v2)
             if (debug)
               println("connect!(g,$(v1),$(v2),5)")
             end
             connect!(g,v1,v2,weight())
           end
        end
     elseif action == 3
       if (nog > 0)
          v1 = rand(1:nog)
          if (debug)
            println("remove_vertex!(g,$(v1))")
          end
          remove_vertex!(g,v1)
        end
     end
  end
  return g
end
struct TargetVertexAndWeight
    vertex_id::Integer
    edge_weight::Float64
end
struct Triangle
    edge_1::TargetVertexAndWeight
    edge_2::TargetVertexAndWeight
end
function Triangle()
    return Triangle((-1,-1.0),(-1,-1.0))
end
import Base.hash
import Base.isequal

function hash(a::Triangle, h::UInt)
    if a.edge_1.vertex_id < a.edge_2.vertex_id
        hash(a.edge_1.vertex_id, hash(a.edge_2.vertex_id, hash(:Triangle, h)))
    else
        hash(a.edge_2.vertex_id, hash(a.edge_1.vertex_id, hash(:Triangle, h)))
    end

end
isequal(a::Triangle, b::Triangle) = Base.isequal(hash(a), hash(b))
function compute_cycles(vertice_inicio::Vertex, vertice::Vertex,
                                        visitados::Vector{Bool}, ciclos::Set{Triangle},
                                        cant_aristas::Integer, peso_aristas::TargetVertexAndWeight)
   if  !visitados[vertice.id]
     visitados[vertice.id] = true
     cant_aristas = cant_aristas+1
     for edge in vertice
          vertice_destino = target_vertex(edge, vertice)
          if cant_aristas == 1
               peso_aristas.vertex_id  = vertice_destino.id
               peso_aristas.edge_weight  = edge.weight
          end
          if vertice_destino == vertice_inicio && cant_aristas == 3

               push!(ciclos, Triangle( deepcopy(peso_aristas), TargetVertexAndWeight(vertice.id, edge.weight)))
               visitados[vertice.id]=false
               return  #Si encontre un ciclo, no voy a encontrar otro
          else
               if !visitados[vertice_destino.id]
                    if cant_aristas < 3
                         compute_cycles(vertice_inicio, vertice_destino, visitados, ciclos,
                                        cant_aristas,peso_aristas)

                    end
               end
          end
     end
     visitados[vertice.id]=false
   end

end

function cycles(g::Graph)
    visitados = fill(false,length(g.vertices))
    ciclos_vertices = []
    for i=1:length(g.vertices)
        v = g.vertices[i]
        peso_aristas = TargetVertexAndWeight(-1,-1.0)
        ciclos = Set{Triangle}()
        compute_cycles(v,v, visitados,ciclos,0,peso_aristas)
        push!(ciclos_vertices,ciclos)
    end
    return ciclos_vertices
end

import LinearAlgebra.issymmetric
import LinearAlgebra.diag
issymmetric(::SpectralClustering.Graph) = true
diag(g::SpectralClustering.Graph) = [g.vertices[i].degree for i=1:length(g.vertices)]

include("Creation.jl")
include("Matrices.jl")
include("Plot.jl")


using .Links
using .ProductDistributions
BayesianTools.Links.link(p::ProductDistribution, x::Vector) = map(BayesianTools.Links.link, p, x)
BayesianTools.Links.invlink(p::ProductDistribution, x::Vector) = map(BayesianTools.Links.invlink, p, x)
BayesianTools.Links.logjacobian(p::ProductDistribution, x::Vector) = map(BayesianTools.Links.logjacobian, p, x)


# Working towards implementing Harrison's TightBinding methods, as presented in:
#
# Walter A. Harrison
# Electronic structure and the properties of solids

module HarrisonSolidStateTable

include("PeriodicTable.jl") # basic atom, A->Z data
include("SolidStateTable.jl")

end


using Ipopt
using Compat
using Compat.Test

@testset "C API" begin
    # First of all, test that hs071 example works
    include("hs071_test.jl")

    # Test setting some options
    # String option
    println("\nString option")
    addOption(prob, "hessian_approximation", "exact")
    @test_throws ErrorException addOption(prob, "hessian_approximation", "badoption")
    println("\nInt option")
    # Int option
    addOption(prob, "file_print_level", 3) == nothing
    @test_throws ErrorException addOption(prob, "file_print_level", -1)
    # Double option
    println("\nFloat option")
    addOption(prob, "derivative_test_tol", 0.5)
    @test_throws ErrorException addOption(prob, "derivative_test_tol", -1.0)

    # Test opening an output file
    openOutputFile(prob, "blah.txt", 5)
    Ipopt.freeProblem(prob) # Needed before the `rm` on Windows.
    # unlink the output file
    rm("blah.txt")

    # Test that the ipopt binary works
    # See https://github.com/JuliaOpt/Ipopt.jl/issues/119 for discussion of the
    # known failure on Windows and Julia 0.7.
    if !(Compat.Sys.iswindows() && VERSION >= v"0.7-")
        @test success(`$(Ipopt.amplexe) -v`)
    end
end

@testset "MathProgBase" begin
    include("MPBWrapper.jl")
end

@testset "MathOptInterface" begin
    include("MOIWrapper.jl")
end


############################################################################################
############################################################################################
############################################################################################
# Diagnostics
@testset "Sampling - Diagnostics: " begin
    diag_ℓobjective = Float16(1.0)
    diag_temperature = Float32(0.5)
    diag_tmp = BaseDiagnostics(diag_ℓobjective, diag_temperature, nothing, 1)
    BaytesCore.get_prediction(diag_tmp)
    @test untemper(diag_tmp) == diag_ℓobjective*(1/diag_temperature)
end

############################################################################################
############################################################################################
############################################################################################
# Logging
@testset "Sampling - Logging: " begin
    logging_pr = ProgressReport(;bar=true, log=ConsoleLog())
    logging_sd = SampleDefault()
end

############################################################################################
############################################################################################
############################################################################################
# Resample
@testset "Sampling - Resample: " begin
    struct Resamplingmethod1 <: ResamplingMethod end
    resample_bool = false
    resample_tune = ResampleTune(Resamplingmethod1(), Updater(resample_bool))
    update!(resample_tune)
    @test resample_tune.update.current != resample_bool
    init!(resample_tune, resample_bool)
    @test resample_tune.update.current == resample_bool

end

############################################################################################
############################################################################################
############################################################################################
# Shuffle

@testset "Sampling - Shuffle: " begin
    # Parameter
    shuffle_ndata = 10
    shuffle_nparticles = 5
    shuffle_lookback = 2
    shuffle_ancestors = [5, 4, 4, 3, 1]
    shuffle_anestorbuffer = zeros(Int64, shuffle_nparticles)

    shuffle_val = repeat(1:shuffle_nparticles, 1, shuffle_ndata)
    shuffle_valbuffer = zeros(Int64, shuffle_nparticles)

    shuffle_allancestors = repeat(1:shuffle_nparticles, 1, shuffle_ndata)
    for iter in 1:shuffle_nparticles
        shuffle_allancestors[iter, end] = shuffle_ancestors[iter]
    end

    # Basic shuffle! for last dimension
    shuffle_tmp = deepcopy(shuffle_val)
    shuffle!(shuffle_tmp, shuffle_anestorbuffer, shuffle_ancestors)
    @test sum( vec(sum(shuffle_tmp, dims=1)) .== sum(shuffle_ancestors) ) == shuffle_ndata

    # Shuffle_forward!
    shuffle_tmp = deepcopy(shuffle_val)
    shuffle_forward!(
        shuffle_tmp,
        shuffle_valbuffer,
        shuffle_allancestors,
        shuffle_anestorbuffer,
        shuffle_lookback, shuffle_ndata
    )
    @test shuffle_allancestors[1, shuffle_ndata] == shuffle_allancestors[end, shuffle_ndata-1]
    @test shuffle_tmp[1, shuffle_ndata] == shuffle_tmp[end, shuffle_ndata-shuffle_lookback]

    # Shuffle backward
    shuffle_tmp = deepcopy(shuffle_val)
    shuffle_backward!(
        shuffle_tmp,
        shuffle_valbuffer,
        shuffle_allancestors,
        shuffle_anestorbuffer
    )

end

############################################################################################
############################################################################################
############################################################################################
# Statistics
@testset "Sampling - Statistics: " begin
    accept_rate = 1.0
    accept_stat = AcceptStatistic(_RNG, log(accept_rate))
    @test accept_stat.accepted == true
end

############################################################################################
############################################################################################
############################################################################################
# Weights

@testset "Sampling - Weights: " begin
    weights_Nweights = 10
    weights_param = ParameterWeights(weights_Nweights)
    update!(weights_param)
    weights_param(log.(1:weights_Nweights))
    @test logsumexp((weights_param.ℓweightsₙ)) ≈ 0.0
    weightedincrement(weights_param)
    @test computeESS(weights_param) ≈ computeESS(exp.(weights_param.ℓweightsₙ))
    draw!(_RNG, weights_param)
end


using DataFrames
using CSV

cd("/Users/kiristern/Documents/GitHub/SDM/")

predators = CSV.read("data/preds/originals/Nasua_nasua2.csv")
predators = select(predators, [:species, :decimalLatitude, :decimalLongitude, :year])

# select only ursus arctos from imported all predators csv file
#df = predators[predators.species .== "Ursus arctos", :]

p1 = CSV.read("data/prey_class/Diplopoda2.csv")
p1 = select(p1,[:class, :decimalLatitude, :decimalLongitude, :year])
rename!(p1, :class => :species)

#create one dataframe with predators/prey dataframes
pred_prey = vcat(predators, p1)

pred_prey = dropmissing!(pred_prey, [:decimalLatitude, :decimalLongitude, :year])

CSV.write("data/pred_prey/Nasua_nasua.csv", pred_prey)

show(by(pred_prey, :species, :species => length), allcols=true)


# BONDGRAPH
struct BondGraph <: g.AbstractGraph{Int64}
    type::Symbol
    name::Symbol
    nodes::Vector{T} where {T<:AbstractNode}
    bonds::Vector{Bond}
end
BondGraph(name = :BG) = BondGraph(:BG, Symbol(name), AbstractNode[], Bond[])

# PROPERTIES
type(bg::BondGraph) = bg.type

name(bg::BondGraph) = bg.name

nodes(bg::BondGraph) = bg.nodes

bonds(bg::BondGraph) = bg.bonds

# AbstractNode properties
parameters(bg::BondGraph) = [p for c in components(bg) for p in parameters(c)]

states(bg::BondGraph) = [x for c in components(bg) for x in states(c)]

function equations(bg::BondGraph; simplify_eqs = true)
    isempty(bg.nodes) && return Equation[]
    sys = ODESystem(bg; simplify_eqs = simplify_eqs)
    return equations(sys)
end

# Filtering
components(bg::BondGraph) = filter(x -> x isa Component, bg.nodes)
junctions(bg::BondGraph) = filter(x -> x isa Junction, bg.nodes)

getnodes(bg::BondGraph, T::DataType) = filter(x -> x isa T, bg.nodes)
getnodes(bg::BondGraph, t::Symbol) = filter(x -> type(x) == t, bg.nodes)
getnodes(bg::BondGraph, n) = filter(x -> name(x) == Symbol(n), bg.nodes)

getbonds(bg::BondGraph, t::Tuple) = getbonds(bg, t[1], t[2])
getbonds(bg::BondGraph, n1::AbstractNode, n2::AbstractNode) = filter(b -> n1 in b && n2 in b, bg.bonds)

# Base functions
show(io::IO, bg::BondGraph) = print(io, "BondGraph $(bg.type):$(bg.name) ($(g.nv(bg)) Nodes, $(g.ne(bg)) Bonds)")

# Easier referencing systems using a.b notation
function getproperty(bg::BondGraph, sym::Symbol)
    # Calling getfield explicitly avoids using "a.b" and causing a StackOverflowError
    allnodes = getfield(bg, :nodes)
    names = [getfield(n, :name) for n in allnodes]
    symnodes = allnodes[names.==sym]
    if isempty(symnodes)
        return getfield(bg, sym)
    elseif length(symnodes) == 1
        return symnodes[1]
    else
        return symnodes
    end
end

# BONDGRAPH NODE
struct BondGraphNode <: AbstractNode
    bondgraph::BondGraph
    type::Symbol
    name::Symbol
    exposed::Vector{SourceSensor}
    freeports::Vector{Bool}
    vertex::RefValue{Int}
end
function BondGraphNode(bg::BondGraph, name = name(bg); vertex::Int = 0, deepcopy = false)
    _bg = deepcopy ? deepcopy(bg) : bg

    exposed_ports = getnodes(_bg, SourceSensor)
    freeports = fill(true, length(exposed_ports))

    BondGraphNode(_bg, :BG, Symbol(name), exposed_ports, freeports, Ref(vertex))
end

# Easier referencing systems using a.b notation
function getproperty(bgn::BondGraphNode, sym::Symbol)
    bg = getfield(bgn, :bondgraph)
    try
        return getproperty(bg, sym)
    catch
        return getfield(bgn, sym)
    end
end

exposed(bgn::BondGraphNode) = bgn.exposed


using HorizonSideRobots

function krest(r::Robot)
    for side in [Nord,West,Sud,Ost]
        movekrest(r,side)
        reversekrest(r,side)
    end
    putmarker!(r)
end

function movekrest(r,side)
    while !isborder(r,side)
        move!(r,side)
        putmarker!(r)
    end
end

function reversekrest(r,side)
    while ismarker(r)==1
        move!(r,HorizonSide((Int(side)+2)%4))
    end
end

krest(r)


"""
    AbstractTabular{N}
    AbstractSeries === AbstractTabular{1}
    AbstractTable === AbstractTabular{2}

`AbstractTabular{N}`s are flexible, `N`-dimensional storage wrappers, designed to provide
a uniform interface for working with data in various (possibly nested) data structures.

Objects of this abstract type can be seen as an extension (or combination) of both
`AbstractArray` and `Associative`. An `AbstractTabular{N}` object behaves as a generic
mapping from `N` indices to it's elements..

Unlike `AbstractArray`, no assumptions about the indices or element type is made - excepting
that the indices for a given dimension must be unique, and amongst dimensions they are
"Cartesian" (e.g. a 2D tabular object must be rectangular in shape, not a generic nested
or ragged array).

The `AbstractTabular{N}` interface requires the following functions (WIP):

 - `indices(tabular)` returns a length-`N` tuple of the indices for each dimension.
 - `getindex(tabular, inds...)` fetches an element given the `N` indices.
 - `setindex!(tabular, value, inds...)` sets an element to `value` given the `N` indices.
"""
abstract type AbstractTabular{N}; end

const AbstractTable = AbstractTabular{2}
const AbstractSeries = AbstractTabular{1}

ndims(t::AbstractTabular{N}) where {N} = N
ndims(t::Type{<:AbstractTabular{N}}) where {N} = N

@inline size(t::AbstractTabular) = map(length, indices(t))
@inline size(t::AbstractTabular, i) = length(indices(t)[i])

function summary(t::AbstractSeries)
    string("s Tabular{$N}")
end

function summary(t::AbstractTable)
    string(join(size(t), "×"), " Table")
end

function summary(t::AbstractTabular{N}) where {N}
    string(join(size(t), "×"), " Tabular{$N}")
end

"""
    Series(data...)
    Table(data...)
    Tabular{N}(data...)

Wraps input `data` in a `Tabular` of the specified dimensionality.
Indexing behavior will depend on the data type, and assumes a nested
data access pattern. The immediate data container `data` is indexed by
the final index value, such that (like `Base.Array`) the data "closest"
in memory is represented by the first index, and "furthest" in memory
by the final index. By default, multidimensional arrays will preserve
their index order.

If you wish the indices applied in a different order, see `PermutedDimsTabular`
or consider using `transpose(table)` / `table'`.
"""
struct Tabular{N, D} <: AbstractTabular{N}
    data::D
end

const Table = Tabular{2}
const Series = Tabular{1}

@inline Tabular{N}(x::D) where {N,D} = Tabular{N,D}(x)
@inline Tabular{N}(x...) where {N} = Tabular{N}(x)
@inline Tabular{N}(x::Pair) where {N} = Tabular{N}((x,))

get(t::Tabular) = t.data


module DiskTracer

# package code goes here
export constants,
    model

# These statements just straight up dump the source code directly here, making DiskJockey.jl
# act as one giant file.
include("constants.jl")
include("model.jl")
include("geometry.jl")
include("trace.jl")
include("trace_simple.jl")

end # module


#===================================================================================================
Abstract types and common interfaces.
===================================================================================================#
"""
    Evolver

Abstract type of all DE core.
"""
abstract type Evolver end

"""
    Options

Abstract type of all DE options.
"""
abstract type Options end

abstract type Controller end

"""
    DEEvolver <: Evolver

An abstract normal (single-population) DE evolution type
"""
abstract type DEEvolver <: Evolver end

"""
    DEOptions <: Options

Abstract type of options for a DE, particually, a single-population one. See also [`DEEvolver`](@ref).
"""
abstract type DEOptions <: Options end
abstract type DEController <: Controller end

_throw_unimplemented_error() = error("Unimplemented")

# all subtypes of `DEEvolver` should have the following fields
"""
    pop(e::DEEvolver)

Get the population of a DE, which is typically a matrix.
"""
pop(e::DEEvolver) = e.pop

"""
    fitness(e::DEEvolver)

Get the fitness of the population, which is generally a vector for single-objective DE or a matrix
for multi-objective DE.
"""
fitness(e::DEEvolver) = e.fitness

"""
    senses(e::DEEvolver)

Get the senses of optimization. In traditional evolutionary algorithms, the fitness of an individual
is going to be maximized. If minimization is required instead, then set a negative sense.
"""
senses(e::DEEvolver) = e.senses

"""
    bounds(e::DEEvolver)

Get the bound constraints,typically a ``n x 2`` matrix for `n` variables.
"""
bounds(e::DEEvolver) = e.bounds

"""
    options(e::DEEvolver)

Get the options of this DE.
"""
options(e::DEEvolver) = e.options

"""
    nindividuals(e::DEEvolver)

Number of individuals in the population.
"""
function nindividuals(e::DEEvolver)
    return size(pop(e), 2)
end

"""
    ndims(de::DEEvolver)

Number of variables (dimension) of the DE.
"""
Base.ndims(de::DEEvolver) = size(pop(de), 1)

"""
    nobjectives(e::Evolver)

Number of objectives.
"""
function nobjectives(e::DEEvolver)
    f = fitness(e)
    if ndims(f) == 1
        return 1
    end
    return size(f, 1)
end

"""
    best_individual(e::Evolver; all=false)

Get individuals that have the best fitness. If `all` is `true`, then all such individuals are returned;
otherwise, only one of them is fetched.
"""
function best_individual(e::Evolver; all=false)
    _throw_unimplemented_error()
end

"""
    best_fitness(e::Evolver)

Get the best fitness among the individuals of the evolver.
"""
function best_fitness(e::Evolver)
    _throw_unimplemented_error()
end


using  FortranFiles: FString
import Random

struct CodegenTask
   jtype :: DataType
   ftype :: String
   sz    :: Dims
   var   :: String
end

Base.size(task::CodegenTask) = sizeof(task.jtype) * prod(task.sz)

function fdecl(task::CodegenTask)
   if task.sz == (1,)
      code = "$(task.ftype) :: $(task.var)"
   else
      code = "$(task.ftype), allocatable :: $(task.var)("
      code *= join( (":" for _ in task.sz), "," ) * ")"
   end
   return code
end

function fmkdata(task::CodegenTask)
   codes = String[]
   if task.sz != (1,)
      dims = join( ("$dim" for dim in task.sz), "," )
      push!(codes, "allocate($(task.var)($dims))")
   end
   push!(codes, "call random($(task.var))")
   return codes
end

function fwrdata(tasks::Vector{CodegenTask})
   vars = join((task.var for task in tasks), ",")
   code = "write(lun) $(vars)"
   return code
end

function jwrdata(tasks::Vector{CodegenTask})
   vars   = join((task.var for task in tasks), ", ")
   nbytes = sum(size(task) for task in tasks)
   codes  = [ "$(vars) = popfirst!(data)",
              "nwritten = write(f, $(vars))",
              "@test nwritten == $(nbytes)" ]
   return codes
end

function jrddata(tasks::Vector{CodegenTask})
   if isempty(tasks)
      return [ "read(f)" ]
   end
   specs = String[]
   tests = String[]
   for task in tasks
      spec = "$(task.jtype)"
      typ  = "$(task.jtype)"
      if task.sz != (1,)
         dims = join( ("$dim" for dim in task.sz), "," )
         mixdims = join( ( rand(["$dim", "Int64($dim)", "Int32($dim)", "Int16($dim)"]) for dim in task.sz), "," )
         if length(task.sz) == 1
            spec = rand([ "Array{$(typ)}(undef, $(dims))", "($spec, $(mixdims))" ])
         else
            spec = rand([ "Array{$(typ)}(undef, $(dims))", "($spec, $(mixdims))", "($spec, ($(mixdims)))" ])
         end
         typ = "Array{$(typ),$(length(task.sz))}"
      end
      push!(specs, spec)
      push!(tests, "@test typeof($(task.var)) == $(typ)")
      push!(tests, "@test sizeof($(task.var)) == $(size(task))")
   end
   vars = join((task.var for task in tasks), ", ")
   vartup = (length(tasks)==1) ? vars : "($(vars))"
   specstr = join(specs, ", ")
   codes = [ "$(vars) = read(f, $(specstr))",
             tests...,
             "push!(data, $(vartup))" ]
   return codes
end

function jfrdata(tasks::Vector{CodegenTask})
   if isempty(tasks)
      return [ "@fread f" ]
   end
   specs = String[]
   tests = String[]
   decls = String[]
   for task in tasks
      typ = "$(task.jtype)"
      if task.sz == (1,)
         spec = "$(task.var)::$(typ)"
      else
         dims = join( ("$dim" for dim in task.sz), "," )
         choice = rand(1:3)
         if choice==1
            # read into preallocated array
            decl = "$(task.var) = Array{$(typ)}(undef, $(dims))"
            push!(decls, decl)
            spec = "$(task.var)"
         elseif choice==2
            # read into freshly constructed array
            spec = "$(task.var)::Array{$(typ)}(undef, $(dims))"
         else
            # ditto, using short syntax (T,n,m) or (T,(n,m))
            spec = ( (length(task.sz)==1) || rand(Bool) ) ?
                   "$(task.var)::($(typ),$(dims))" :
                   "$(task.var)::($(typ),($(dims)))"
         end
         typ = "Array{$(typ),$(length(task.sz))}"
      end
      push!(specs, spec)
      push!(tests, "@test typeof($(task.var)) == $(typ)")
      push!(tests, "@test sizeof($(task.var)) == $(size(task))")
   end
   vars = join((task.var for task in tasks), ", ")
   vartup = (length(tasks)==1) ? vars : "($(vars))"
   specstr = join(specs, " ")
   codes = [ decls...,
             "@fread f $(specstr)",
             tests...,
             "push!(data, $(vartup))" ]
   return codes
end

function gencode(nscalar=5, narray=3, nstrlen=3; seed=1)
# will generate (8+nstrlen)*(nscalar+3*narray) CodegenTasks
   @static if VERSION >= v"0.7.0-beta2.171"
      Random.seed!(seed)
   else
      Random.srand(seed)
   end

   numtypes = [
      ( Int8 ,      "integer(kind=int8)"   ),
      ( Int16,      "integer(kind=int16)"  ),
      ( Int32,      "integer(kind=int32)"  ),
      ( Int64,      "integer(kind=int64)"  ),
      ( Float32,    "real(kind=real32)"    ),
      ( Float64,    "real(kind=real64)"    ),
      ( ComplexF32, "complex(kind=real32)" ),
      ( ComplexF64, "complex(kind=real64)" ) ]
   strtypes = [
      ( FString{n}, "character(len=$n)" ) for n in rand(1:200,nstrlen) ]
   types = vcat(numtypes, strtypes)

   dims0 = [ ( 1, ) for i=1:nscalar ]
   dims1 = [ ( rand(1:10_000), ) for i=1:narray ]
   dims2 = [ ( rand(1:100), rand(1:100) ) for i=1:narray ]
   dims3 = [ ( rand(1:10), rand(1:10), rand(1:10) ) for i=1:narray ]
   dims  = vcat(dims0, dims1, dims2, dims3)

   fdeclf = open("fdecl.f90", "w")
   fwrf = open("fwrite.f90", "w")
   jwrf = open("jwrite.jl", "w")
   jrdf = open("jread.jl", "w")
   jskf = open("jskip.jl", "w")
   jfrf = open("jfread.jl", "w")
   print(jwrf, "function writedata(f::FortranFile, data)\n")
   print(jrdf, "function readdata(f::FortranFile)\n   data = Any[]\n   @test eof(f) == false\n")
   print(jskf, "function skipdata(f::FortranFile)\n   data = Any[]\n")
   print(jfrf, "function freaddata(f::FortranFile)\n   data = Any[]\n")

   tasks = CodegenTask[]
   itask = 1
   for (jt,ft) in types, sz in dims
      var = "var$(itask)"
      push!( tasks, CodegenTask(jt, ft, sz, var) )
      itask += 1
   end

   for task in tasks
      println(fdeclf, "   ", fdecl(task))
      for code in fmkdata(task)
         println(fwrf, "   ", code)
      end
   end

   Random.shuffle!(tasks)
   taskgroups = Vector{CodegenTask}[]
   itask = 1
   while itask <= length(tasks)
      jtask = min(itask + rand(0:3), length(tasks))
      push!( taskgroups, tasks[itask:jtask] )
      itask = jtask + 1
   end

   for tg in taskgroups
      println(fwrf, "   ", fwrdata(tg))
      for line in jwrdata(tg); println(jwrf, "   ", line); end
      for line in jrddata(tg); println(jrdf, "   ", line); end
      for line in jrddata(tg[1:rand(0:end-1)]); println(jskf, "   ", line); end
      for line in jfrdata(tg); println(jfrf, "   ", line); end
   end

   print(jwrf, "end\n")
   print(jrdf, "   @test eof(f) == true\n   return data\nend\n")
   print(jskf, "   return data\nend\n")
   print(jfrf, "   return data\nend\n")
   close(fwrf)
   close(jwrf)
   close(jrdf)
   close(jskf)
   close(jfrf)
end

gencode(1,1,2,seed=12345678)


module Interface

import Distributions: sample, Sampleable
import Random: GLOBAL_RNG, AbstractRNG

export AbstractSampler,
       AbstractTransition,
       sample_init!,
       sample_end!,
       sample,
       step!

"""
    AbstractSampler

The `AbstractSampler` type is intended to be inherited from when
implementing a custom sampler. Any persistent state information should be
saved in a subtype of `AbstractSampler`.

When defining a new sampler, you should also overload the function
`transition_type`, which tells the `sample` function what type of parameter
it should expect to receive.
"""
abstract type AbstractSampler end

"""
    AbstractTransition

The `AbstractTransition` type describes the results of a single step
of a given sampler. As an example, one implementation of an
`AbstractTransition` might include be a vector of parameters sampled from
a prior distribution.

Transition types should store a single draw from any sampler, since the
interface will sample `N` times, and store the results of each step in an
array of type `Array{Transition<:AbstractTransition, 1}`. If you were
using a sampler that returned a `NamedTuple` after each step, your
implementation might look like:

```
struct MyTransition <: AbstractTransition
    draw :: NamedTuple
end
```
"""
abstract type AbstractTransition end

"""
    sample(
        ℓ::ModelType,
        s::SamplerType,
        N::Integer;
        kwargs...)

    sample(
        rng::AbstractRNG,
        ℓ::ModelType,
        s::SamplerType,
        N::Integer;
        kwargs...)

A generic interface for samplers.
"""
function sample(
    ℓ::ModelType,
    s::SamplerType,
    N::Integer;
    kwargs...
) where {ModelType<:Sampleable, SamplerType<:AbstractSampler}
    return sample(GLOBAL_RNG, ℓ, s, N)
end

"""
    sample(
        rng::AbstractRNG,
        ℓ::ModelType,
        s::SamplerType,
        N::Integer;
        kwargs...
    )

`sample` returns an `MCMCChains.Chains` object containing `N` samples from a given model and
sampler.
"""
function sample(
    rng::AbstractRNG,
    ℓ::ModelType,
    s::SamplerType,
    N::Integer;
    kwargs...
) where {ModelType<:Sampleable, SamplerType<:AbstractSampler}
    # Perform any necessary setup.
    sample_init!(rng, ℓ, s, N; kwargs...)

    # Preallocate the TransitionType vector.
    ts = transitions_init(rng, ℓ, s, N; kwargs...)

    # Step through the sampler.
    for i=1:N
        if i == 1
            ts[i] = step!(rng, ℓ, s, N; kwargs...)
        else
            ts[i] = step!(rng, ℓ, s, N, ts[i-1]; kwargs...)
        end
    end

    # Wrap up the sampler, if necessary.
    sample_end!(rng, ℓ, s, N, ts; kwargs...)

    return Chains(ts)
end

"""
    sample_init!(
        rng::AbstractRNG,
        ℓ::ModelType,
        s::SamplerType,
        N::Integer;
        kwargs...
    )

Performs whatever initial setup is required for your sampler.
"""
function sample_init!(
    rng::AbstractRNG,
    ℓ::ModelType,
    s::SamplerType,
    N::Integer;
    kwargs...
) where {ModelType<:Sampleable, SamplerType<:AbstractSampler}
    # Do nothing.
    @warn "No sample_init! function has been implemented for objects
           of types $(typeof(ℓ)) and $(typeof(s))"
end

"""
    sample_end!(
        rng::AbstractRNG,
        ℓ::ModelType,
        s::SamplerType,
        N::Integer,
        ts::Vector{TransitionType};
        kwargs...
    )

Performs whatever finalizing the sampler requires.
"""
function sample_end!(
    rng::AbstractRNG,
    ℓ::ModelType,
    s::SamplerType,
    N::Integer,
    ts::Vector{TransitionType};
    kwargs...
) where {
    ModelType<:Sampleable,
    SamplerType<:AbstractSampler,
    TransitionType<:AbstractTransition
}
    # Do nothing.
    @warn "No sample_end! function has been implemented for objects
           of types $(typeof(ℓ)) and $(typeof(s))"
end

"""
    step!(
        rng::AbstractRNG,
        ℓ::ModelType,
        s::SamplerType,
        N::Integer;
        kwargs...
    )

Returns a single `AbstractTransition` drawn using the model and sampler type.
This is a unique step function called the first time a sampler runs.
"""
function step!(
    rng::AbstractRNG,
    ℓ::ModelType,
    s::SamplerType,
    N::Integer;
    kwargs...
) where {ModelType<:Sampleable, SamplerType<:AbstractSampler}
    # Do nothing.
    @warn "No step! function has been implemented for objects
           of types $(typeof(ℓ)) and $(typeof(s))"
end

"""
    step!(
        rng::AbstractRNG,
        ℓ::ModelType,
        s::SamplerType,
        N::Integer,
        t::TransitionType;
        kwargs...
    )

Returns a single `AbstractTransition` drawn using the model and sampler type.
"""
function step!(
    rng::AbstractRNG,
    ℓ::ModelType,
    s::SamplerType,
    N::Integer,
    t::TransitionType;
    kwargs...
) where {ModelType<:Sampleable,
    SamplerType<:AbstractSampler,
    TransitionType<:AbstractTransition
}
    # Do nothing.
    @warn "No step! function has been implemented for objects
           of types $(typeof(ℓ)) and $(typeof(s))"
end

"""
    transitions_init(
        rng::AbstractRNG,
        ℓ::ModelType,
        s::SamplerType,
        N::Integer;
        kwargs...
    )

Generates a vector of `AbstractTransition` types of length `N`.
"""
function transitions_init(
    rng::AbstractRNG,
    ℓ::ModelType,
    s::SamplerType,
    N::Integer;
    kwargs...
) where {ModelType<:Sampleable, SamplerType<:AbstractSampler}
    @warn "No transitions_init function has been implemented
           for objects of types $(typeof(ℓ)) and $(typeof(s))"
    return Vector(undef, N)
end

end # module Interface


mutable struct joint_angles_t <: LCMType
    utime::Int64
    robot_name::String
    num_joints::Int32
    joint_name::Vector{String}
    joint_position::Vector{Float64}
end

@lcmtypesetup(joint_angles_t,
    joint_name => (num_joints,),
    joint_position => (num_joints,)
)


using MeasureTheory
using Test

@testset "MeasureTheory.jl" begin
    # Write your own tests here.
end


# http://www.neos-server.org/neos/NEOS-API.html
module NEOS

warn("All models submitted to NEOS become part of the public domain. For more see http://www.neos-server.org")

using LightXML
using Requests
using Codecs
using Libz

importall MathProgBase.SolverInterface

include("NEOSServer.jl")
include("NEOSSolverInterface.jl")
include("writer.jl")

include("solvers/CPLEX.jl")
include("solvers/MOSEK.jl")
include("solvers/SYMPHONY.jl")
include("solvers/XpressMP.jl")

export NEOSServer,
	NEOSCPLEXSolver, NEOSMOSEKSolver, NEOSSYMPHONYSolver, NEOSXpressMPSolver,

	addparameter!, addemail!,

 	# NEOS API functions
	neosHelp, emailHelp, welcome, version, ping, printQueue,
	listAllSolvers, listCategories,
	getSolverTemplate,
	listSolversInCategory,
	submitJob,
	getJobStatus, killJob, getFinalResults, getFinalResultsNonBlocking,
	getJobInfo,
	getIntermediateResults, getIntermediateResultsNonBlocking
end