include("meta.jl")

#@enum Result res_mem res_madd res_madd_clamp res_zero res_one res_m_one res_two res_1_fifth res_m_6_fifths res_exp2
@constants result_selections begin
	res_maxval # 0
	res_madd   # 1
	res_mem    # 2
	res_ext    # 3
	res_one    # even
	res_m_one  # odd
	res_two    # even
	res_m_two  # odd
	res_lfsr   # even
	res_eps    # odd
	res_1_fifth
	res_m_6_fifths
	res_zero
	res_exp2
end

const res_const = res_maxval

const const_sel_bits = 5
const result_sel_bits = 4
@flags control_flags 4 begin
# Data path control flags
	# register write enable bits
	T_WE
	F1_WE
	F2_WE
	SIGN_WE   # store sign(f2) into dp.positive
	OVERFLOW_WE # store overflow flag from madd result
	OUTPUT_WE # should probably not be handled by data path
	BIAS_WE
	# other control flags
	C_CLAMP
	C_STORED_SIGN
	C_TRUNC
	C_DITHER

# Non-data path control flags
	C_M_WE
	C_CONDITIONAL_M_WE
	C_MEM_ADDR
	C_INST_DONE
end

const res_madd_clamp = res_madd | C_CLAMP;
const res_stored_sign = res_one | C_STORED_SIGN;

const A_WE = T_WE | F1_WE | F2_WE | C_TRUNC

const MEM_READ = C_MEM_ADDR
const IMM_WE = C_M_WE
const MEM_WE = C_M_WE | C_MEM_ADDR

struct DPControl
	reg_we::Int
	res::Int
end
DPControl(reg_we, res) = DPControl(reg_we, res, 0)

const IC_N_ZERO = -13  # When n == IC_N_ZERO in IContext, the result is scale = 0

struct IContext
	addr::Int
	n::Int
end
IContext(addr) = IContext(addr, 0)
IContext() = IContext(0)

struct CycleControl
	cdp::DPControl
	cn::Int
end

control(flags::Int, res::Int) = CycleControl(DPControl(flags, res), flags)
get_res(c::Int) = c & ((1 << result_sel_bits) - 1)
get_flags(c::Int) = c & (-1 << result_sel_bits)

get_bits(c::CycleControl) = c.cdp.res | c.cn
get_res(c::CycleControl) = get_res(c.cdp.res)
get_flags(c::CycleControl) = get_flags(get_bits(c))

@enum Opcode begin
	inst_nop

	inst_acc_imm_to_mem
	inst_macc_to_mem
	inst_wadd
	inst_lpfilter

	inst_pulse_exp2
	inst_pulse
	inst_triangle
	inst_sina2
	inst_sin

	inst_lfsr

	inst_output
	inst_output_a
	inst_dither_update

	inst_imm
	inst_acc_to_imm
end

struct Instruction
	op::Opcode
	context::IContext
end
Instruction(op::Opcode) = Instruction(op, IContext())

const microcode = [
	inst_nop => [control(0, res_mem)],
	inst_imm => [control(A_WE, res_mem)], # a = imm
	inst_acc_to_imm => [ # a = (imm += 2^-n*a + dither)
		control(F1_WE, res_exp2),
		control(T_WE,  res_mem),
		control(A_WE | IMM_WE | OVERFLOW_WE | C_DITHER, res_madd)
	],
	inst_acc_imm_to_mem => [ # a = (mem += 2^-n*imm + dither)
		control(F1_WE, res_exp2),
		control(F2_WE, res_mem),
		control(T_WE | MEM_READ, res_mem),
		control(A_WE | MEM_WE | OVERFLOW_WE | C_DITHER, res_madd)
	],
	inst_wadd => [ # a = 4^-n*(imm*a) + mem (n can be < 0)
		# f2 = imm*a
		control(T_WE, res_zero),
		control(F1_WE, res_mem),
		control(F2_WE, res_madd),
		# f2 = 2^-n*f2
		control(F1_WE, res_exp2),
		control(F2_WE, res_madd),
		# a = 2^-n*f2 + mem
		control(T_WE | MEM_READ, res_mem),
		control(A_WE, res_madd)
	],
	inst_macc_to_mem => [ # mem = clamp(imm*a + mem) -- keeps value of a!
		control(F1_WE, res_mem),
		control(T_WE | MEM_READ, res_mem),
		control(MEM_WE, res_madd_clamp),
		control(F1_WE, res_one), # restore a from f2...
		control(T_WE, res_zero), #
		control(A_WE, res_madd)  #
	],
	inst_lpfilter =>  [ # a = mem = clamp(mem + 2^-n*(imm - mem) + dither)
		# f2 = imm-mem
		control(T_WE, res_mem),
		control(F1_WE, res_m_one),
		control(F2_WE | MEM_READ, res_mem),
		control(F2_WE, res_madd_clamp),
		# mem += 2^-n*f2 + dither (clamp)
		control(T_WE | MEM_READ, res_mem),
		control(F1_WE, res_exp2),
		control(A_WE | MEM_WE, res_madd_clamp | C_DITHER)
	],
	inst_sina2 => [ # a = -sina2(a)
		# a = 2a + 1/2
		control(F1_WE | SIGN_WE, res_two),
		control(T_WE, res_one),
		control(F1_WE | F2_WE | C_TRUNC, res_madd),
		# a = a*a - 1
		control(T_WE, res_m_one),
		control(F2_WE, res_madd),
		# a *= sign(a_in)
		control(F1_WE, res_stored_sign),
		control(T_WE, res_zero),
		control(A_WE, res_madd_clamp),
	],
	inst_sin => [ # a = sina4(a)
		# a = 2a + 1/2
		control(F1_WE | SIGN_WE, res_two),
		control(T_WE, res_one),
		control(F1_WE | F2_WE | C_TRUNC, res_madd),
		# f1 = a*a
		control(T_WE, res_zero),
		control(F1_WE, res_madd),
		# f2 = -6/5 + 1/5*f1
		control(T_WE, res_m_6_fifths),
		control(F2_WE, res_1_fifth),
		control(F2_WE, res_madd),
		# a = M + f1*f2
		control(T_WE, res_one),
		control(F2_WE, res_madd),
		# a *= sign(a_in)
		control(F1_WE, res_stored_sign),
		control(T_WE, res_zero),
		control(A_WE, res_madd_clamp),
	],
	inst_output => [ # output imm; a = imm = 0
		control(OUTPUT_WE, res_mem),
		control(A_WE | IMM_WE, res_zero)
	],
	inst_dither_update => [ # bias = bit_reverse(imm); a = (imm += epsilon)
		control(T_WE | BIAS_WE, res_mem),
		control(F1_WE, res_one),
		control(F2_WE, res_eps),
		control(A_WE | IMM_WE, res_madd)
	],
	inst_output_a => [ # output a
		control(F1_WE | F2_WE, res_zero),
		control(A_WE | OUTPUT_WE, res_madd)
	],
	inst_pulse => [ # a = a >= imm ? maxval : minval
		control(F1_WE, res_m_one),
		control(F2_WE, res_mem),
		control(F2_WE, res_madd), # f2 = a - imm
		control(SIGN_WE, res_zero), # s = sign(a - imm), TODO: Merge with previous cycle when sign_we reads from result!
		control(A_WE, res_maxval | C_STORED_SIGN)
	],
	inst_pulse_exp2 => [ # a = a >= -1 + 2^-n ? maxval : minval
		# Prepare
		control(F1_WE | F2_WE, res_one),
		control(T_WE, res_madd),
		# Same as inst_pulse except f2 = 2^-n instead of f2 = imm
		# TODO: Merge with inst_pulse when 2^-n can represent imm
		control(F1_WE, res_m_one),
		control(F2_WE, res_exp2),
		control(F2_WE, res_madd), # f2 = a - imm
		control(SIGN_WE, res_zero), # s = sign(a - imm), TODO: Merge with previous cycle when sign_we reads from result!
		control(A_WE, res_maxval | C_STORED_SIGN)
	],
	inst_triangle => [ # a = triangle(a)
		# a += 2^-n -- use to get a += 0.5
		control(F1_WE, res_one),
		control(F2_WE, res_exp2),
		control(A_WE, res_madd | C_TRUNC),

		control(T_WE | SIGN_WE, res_m_one),
		control(F1_WE, res_two | C_STORED_SIGN),
		control(A_WE, res_madd_clamp)
	],
	inst_lfsr => [ # a = imm = lfsr(a)
		control(F1_WE, res_two),
		control(F2_WE, res_mem),
		control(T_WE, res_lfsr),
		control(A_WE | IMM_WE | C_CONDITIONAL_M_WE, res_madd)
	],
]
const opcode_programs = Dict(microcode)

const instructions = [
	"nop"            => Instruction(inst_nop,            IContext(0,  IC_N_ZERO)),

	"enable"         => Instruction(inst_nop,            IContext(0,  IC_N_ZERO+2)),
	"disable"        => Instruction(inst_nop,            IContext(0,  IC_N_ZERO+3)),

	"phase_update"   => Instruction(inst_acc_imm_to_mem, IContext(0,  IC_N_ZERO)),
	"contribute"     => Instruction(inst_macc_to_mem,    IContext(0,  IC_N_ZERO)),
	"madd_scale2"    => Instruction(inst_wadd,           IContext(0,  IC_N_ZERO)),
	"approach"       => Instruction(inst_lpfilter,       IContext(0,  IC_N_ZERO)),

	"square"         => Instruction(inst_pulse_exp2,     IContext(0,  0)),
	"pulse"          => Instruction(inst_pulse_exp2,     IContext(0,  IC_N_ZERO)),
	"pulse_imm"      => Instruction(inst_pulse,          IContext(0,  IC_N_ZERO)),
	"sawtooth"       => Instruction(inst_nop,            IContext(0,  IC_N_ZERO)), # synonym for nop
	"triangle"       => Instruction(inst_triangle,       IContext(0, -1)),
	"sina2"          => Instruction(inst_sina2,          IContext(0,  IC_N_ZERO)),
	"sin"            => Instruction(inst_sin,            IContext(0,  IC_N_ZERO)),

	"noise_update"   => Instruction(inst_lfsr,           IContext(0,  IC_N_ZERO)),

	"output"         => Instruction(inst_output,         IContext(0,  IC_N_ZERO)),
	"output_left"    => Instruction(inst_output,         IContext(0,  IC_N_ZERO)),
	"output_right"   => Instruction(inst_output,         IContext(0,  IC_N_ZERO+1)),
	"output_a"       => Instruction(inst_output_a,       IContext(0,  IC_N_ZERO)),
	"output_a_left"  => Instruction(inst_output_a,       IContext(0,  IC_N_ZERO)),
	"output_a_right" => Instruction(inst_output_a,       IContext(0,  IC_N_ZERO+1)),

	"loop_update"    => Instruction(inst_dither_update,  IContext(0,  IC_N_ZERO)),
]


import StatsBase: mode

mutable struct NcDim
    name::String
    dimlen::Int
    vals::Union{AbstractArray,Nothing}
    atts::Dict
end

"""
    NcDim(name::AbstractString, vals::AbstractArray, atts::Dict = Dict())

Represents a NetCDF dimension of name `name` optionally holding the dimension values.
"""
NcDim(name::AbstractString, vals::AbstractArray, atts::Dict = Dict()) =
    NcDim(name, length(vals), vals, atts)

function Ipaper.names(dims::Vector{NcDim})
    map(x -> x.name, dims)
end

function Base.getindex(dims::Vector{NcDim}, name::AbstractString)
    names = Ipaper.names(dims)
    ind = indexin([name], names)[1]
    dims[ind]
end

# function Base.getindex(dims::Vector{NcDim}, name::Vector{AbstractString})
#     names = Ipaper.names(dims)
#     ind = indexin(name, names)
#     print(ind)
#     dims[ind]
# end
# Base.getindex(dims::Vector{NcDim}, name::AbstractString) = Base.getindex(dims, [name])
function nc_dim_shape(ds::NCdata, name = "time")
    n = ds.dim[name]
    vals = 1:n
    NcDim(name, n, vals, Dict())
    # NcDim(name, n, nothing, Dict()) # TODO
end

function nc_dim(ds::NCdata, name = "time")
    if haskey(ds, name)
        try
            x = ds[name]
            NcDim(name, x.var[:], Dict(x.attrib))
        catch
            nc_dim_shape(ds, name)
        end
    else
        nc_dim_shape(ds, name)
    end
end

function nc_dim(file::NCfiles, name = "time")
    nc_open(file) do ds
        nc_dim(ds, name)
    end
end

function nc_dims(ds::NCdata)
    items = keys(ds.dim) #|> reverse
    map(x -> nc_dim(ds, x), items)
end

function nc_dims(file::NCfiles)
    nc_open(file) do ds
        nc_dims(ds)
    end
end

function nc_dimsize(file::NCfiles)
    nc_open(file) do ds
        var = nc_bands(ds)[1]
        size(ds[var])
    end
end

"""
    nc_cellsize(ds::NCdata)
"""
function nc_cellsize(ds::NCdata)
    diflon = nc_dim(ds, "lon").vals |> diff
    diflat = nc_dim(ds, "lat").vals |> diff

    cell_x = mode(diflon)
    cell_y = mode(diflat)
    # regular or complex grid
    regular = length(unique(diflon)) == 1 && length(unique(diflat)) == 1
    cell_x, cell_y, regular
end

function nc_cellsize(file::AbstractString)
    nc_open(file) do ds
        nc_cellsize(ds)
    end
end

function nc_cellsize(files::Vector{<:AbstractString})
    n = length(files)
    cell_x = zeros(n)
    cell_y = zeros(n)
    regular = zeros(Bool, n)
    for i = 1:n
        cell_x[i], cell_y[i], regular[i] = nc_cellsize(files[i])
    end
    cell_x, cell_y, regular
end

function ncvar_dim(ds::NCdata, varname::Union{String, Nothing} = nothing; varid = 1)
    if varname === nothing
        varname = nc_bands(ds)[varid]
    end

    dims = nc_dims(ds)
    ids = ds[varname].var.dimids
    ids = collect(ids) .+ 1
    dims[ids]
end

function ncvar_dim(file::NCfiles, varname::Union{String,Nothing} = nothing; kwargs...)
    nc_open(file) do ds
        ncvar_dim(ds, varname; kwargs...)
    end
end


using SVG
using Test

function wtest()
    z = IOBuffer()
    write(z, Svg([Circle(0,1,1), Line(0,1,1,2), Polyline([0,1,2], [3,4,5])]), 10, 10)
    seek(z, 0)
    read(z, String) == "<svg width=\"10\" height=\"10\">\n<circle cx=\"0\" cy=\"1\" r=\"1\" style=\"fill:none;stroke:black;stroke-width:1.0\" />\n<line x1=\"0\" y1=\"1\" x2=\"1\" y2=\"2\" style=\"fill:none;stroke:black;stroke-width:1.0\" />\n<polyline points=\"0,3 1,4 2,5 \" style=\"fill:none;stroke:black;stroke-width:1.0\" />\n</svg>\n"
end

@testset "SVG.jl" begin
#==
   @test bounds(Circle(0,1,1)) == (xmin=-1, xmax=1, ymin=0, ymax=2)
   @test bounds(Line(0,1,1,2)) == (xmin=0, xmax=1, ymin=1, ymax=2)
   @test bounds(Polyline([0,1,2], [3,4,5])) == (xmin=0, xmax=2, ymin=3, ymax=5)
   @test bounds(Polyline([(0,3), (1,4), (2,5)]) == (xmin=0, xmax=2, ymin=3, ymax=5)
   @test bounds(Svg([Circle(0,1,1), Line(0,1,1,2), Polyline([0,1,2], [3,4,5])])) == (xmin=-1, xmax=2, ymin=0, ymax=5)
==#
   @test Polyline([0,1,2], [3,4,5]) == Polyline([(0,3), (1,4), (2,5)])
   @test wtest()

end


using SDFResultViewer
using Unitful
using Test

@testset "SDFResultViewer.jl" begin
    λ = 800u"nm"
    dir = pwd()
    file = "0002.sdf"
    times = get_times(dir, λ)
    @test length(times) == 1

    m_Lx = mean_Lx(dir, λ)
    @test length(m_Lx) == 1

    Lx, y, z = Lx_slice(joinpath(dir, file), λ, 0)
    @test length(Lx) > 0
    @test length(Lx) == length(y) == length(z)
end


"""Evaluates a set of SCM configurations for a single parameter vector."""

using ArgParse
using CalibrateEDMF
using CalibrateEDMF.Pipeline
src_dir = dirname(pathof(CalibrateEDMF))
using CalibrateEDMF.HelperFuncs
using JLD2

# Read iteration number of ensemble to be recovered
s = ArgParseSettings()
@add_arg_table s begin
    "--version"
    help = "Calibration process number"
    arg_type = Int
    "--job_dir"
    help = "Job output directory"
    arg_type = String
    default = "output"
    "--mode"
    help = "Forward model evaluation mode: `train` or `validation`"
    arg_type = String
    default = "train"
end
parsed_args = parse_args(ARGS, s)
outdir_path = parsed_args["job_dir"]
include(joinpath(outdir_path, "config.jl"))

config = get_config()
version = parsed_args["version"]
mode = parsed_args["mode"]
versioned_model_eval(version, outdir_path, mode, config)


using DataFrames
using Gadfly

function savetableimg(A::BenchmarkTable, title::String, filename::String,
                        relproc::Int, indp = 1:size(A, 2))
    #require("DataFrames")
    #require("Gadfly")
    x = A.cfgs
    nn = size(A, 1)
    np = size(A, 2)
    dfa = Array(DataFrames.DataFrame, length(indp))

    for i = 1:length(indp)
        proc = indp[i]
        y = Array(Float64, nn)
        for j = 1:nn
            rel = get(A[j, relproc], BenchmarkLite.Gps())
            y[j] = rel/get(A[j, proc], BenchmarkLite.Gps())
        end
        dfa[i] = DataFrames.DataFrame(x=x, y=y, method=string(rtable.procs[proc]))
    end
    df = vcat(dfa...)

    p = Gadfly.plot(df, x="x", y="y", color="method",
            Scale.x_log10, Scale.y_log10, Theme(line_width=0.7mm, default_point_size=0.5mm), Geom.line, Geom.point,
            Guide.XLabel("Array length [n]"), Guide.YLabel("Rel. [s / G ops.] to FAO"),
            Guide.title(title))
    Gadfly.draw(SVG(string(filename,".svg"), 16cm, 10cm), p)
    Gadfly.draw(PNG(string(filename,".png"), 16cm, 10cm), p)
    return nothing
end


using NBody

function main()
    while !eof(STDIN)
        str = readline(STDIN)
        nb = NBody.read_nbody_json(str)

        dump_nb(nb)
    end
end

function dump_nb(nb)
    for b in nb.bodies
        map(x-> @printf("%24.16f",x), b.pos)
        println()
    end
end

main()


include(joinpath("gammaReg", "chosenVariables_inverse_test.jl"))
include(joinpath("gammaReg", "chosenVariables_log_test.jl"))
include(joinpath("gammaReg", "research_inverse_test.jl"))
include(joinpath("gammaReg", "research_inverse_test.jl"))


module Order

## notions of element ordering ##

export # not exported by Base
    Ordering, Forward, Reverse, Lexicographic,
    By, Lt, Perm,
    ReverseOrdering, ForwardOrdering, LexicographicOrdering,
    DirectOrdering,
    lt, uint_mapping, ord, ordtype

abstract Ordering

immutable ForwardOrdering <: Ordering end
immutable ReverseOrdering{Fwd<:Ordering} <: Ordering
    fwd::Fwd
end

ReverseOrdering(rev::ReverseOrdering) = rev.fwd
ReverseOrdering{Fwd}(fwd::Fwd) = ReverseOrdering{Fwd}(fwd)

typealias DirectOrdering Union(ForwardOrdering,ReverseOrdering{ForwardOrdering})

const Forward = ForwardOrdering()
const Reverse = ReverseOrdering(Forward)

immutable LexicographicOrdering <: Ordering end
const Lexicographic = LexicographicOrdering()

immutable By <: Ordering
    by::Function
end

immutable Lt <: Ordering
    lt::Function
end

immutable Perm{O<:Ordering,V<:AbstractVector} <: Ordering
    order::O
    data::V
end
Perm{O<:Ordering,V<:AbstractVector}(o::O,v::V) = Perm{O,V}(o,v)

lt(o::ForwardOrdering,       a, b) = isless(a,b)
lt(o::ReverseOrdering,       a, b) = lt(o.fwd,b,a)
lt(o::By,                    a, b) = isless(o.by(a),o.by(b))
lt(o::Lt,                    a, b) = o.lt(a,b)
lt(o::LexicographicOrdering, a, b) = lexcmp(a,b) < 0

function lt(p::Perm, a::Int, b::Int)
    lt(p.order, p.data[a], p.data[b]) ? true :
    lt(p.order, p.data[b], p.data[a]) ? false : a < b
end
function lt(p::Perm{LexicographicOrdering}, a::Int, b::Int)
    c = lexcmp(p.data[a], p.data[b])
    c != 0 ? c < 0 : a < b
end

# Map a bits-type to an unsigned int, maintaining sort order
uint_mapping(::ForwardOrdering, x::Unsigned) = x
uint_mapping(::ForwardOrdering, x::Int8)     = uint8  (x $ typemin(Int8))
uint_mapping(::ForwardOrdering, x::Int16)    = uint16 (x $ typemin(Int16))
uint_mapping(::ForwardOrdering, x::Int32)    = uint32 (x $ typemin(Int32))
uint_mapping(::ForwardOrdering, x::Int64)    = uint64 (x $ typemin(Int64))
uint_mapping(::ForwardOrdering, x::Int128)   = uint128(x $ typemin(Int128))
uint_mapping(::ForwardOrdering, x::Float32)  = (y = reinterpret(Int32, x); uint32(y < 0 ? ~y : (y $ typemin(Int32))))
uint_mapping(::ForwardOrdering, x::Float64)  = (y = reinterpret(Int64, x); uint64(y < 0 ? ~y : (y $ typemin(Int64))))

uint_mapping{Fwd}(::ReverseOrdering{Fwd}, x) = ~uint_mapping(Fwd, x)
#uint_mapping{T<:Real}(::ReverseOrdering{ForwardOrdering}, x::T) = ~uint_mapping(Forward, x)  ## Manually inlined
uint_mapping(::ReverseOrdering{ForwardOrdering}, x::Unsigned) = ~x
uint_mapping(::ReverseOrdering{ForwardOrdering}, x::Int8)     = ~uint8  (x $ typemin(Int8))
uint_mapping(::ReverseOrdering{ForwardOrdering}, x::Int16)    = ~uint16 (x $ typemin(Int16))
uint_mapping(::ReverseOrdering{ForwardOrdering}, x::Int32)    = ~uint32 (x $ typemin(Int32))
uint_mapping(::ReverseOrdering{ForwardOrdering}, x::Int64)    = ~uint64 (x $ typemin(Int64))
uint_mapping(::ReverseOrdering{ForwardOrdering}, x::Int128)   = ~uint128(x $ typemin(Int128))
uint_mapping(::ReverseOrdering{ForwardOrdering}, x::Float32)  = (y = reinterpret(Int32, x); uint32(y < 0 ? y : ~(y $ typemin(Int32))))
uint_mapping(::ReverseOrdering{ForwardOrdering}, x::Float64)  = (y = reinterpret(Int64, x); uint64(y < 0 ? y : ~(y $ typemin(Int64))))

uint_mapping(o::By,   x     ) = uint_mapping(Forward, o.by(x))
uint_mapping(o::Perm, i::Int) = uint_mapping(o.order, o.data[i])
uint_mapping(o::Lt,   x     ) = error("uint_mapping does not work with general Lt Orderings")

ordtype(o::ReverseOrdering, vs::AbstractArray) = ordtype(o.fwd, vs)
ordtype(o::Perm,            vs::AbstractArray) = ordtype(o.order, o.data)
# TODO: here, we really want the return type of o.by, without calling it
ordtype(o::By,              vs::AbstractArray) = try typeof(o.by(vs[1])) catch Any end
ordtype(o::Ordering,        vs::AbstractArray) = eltype(vs)

function ord(lt::Function, by::Function, rev::Bool, order::Ordering=Forward)
    order == Forward || order == Lexicographic ||
        Base.warn_once("the `order` keyword is deprecated, use `lt`, `by` and `rev` instead")
    o = (lt===isless) & (by===identity) ? order  :
        (lt===isless) & (by!==identity) ? By(by) :
        (lt!==isless) & (by===identity) ? Lt(lt) :
                                          Lt((x,y)->lt(by(x),by(y)))
    rev ? ReverseOrdering(o) : o
end

end


export @timedone, fasthash, cache, dictcache
export loadedmodules, reloadmodules

macro timedone(a, ex)
    quote
        local t = time()
        println($a, " ... ")
        flush(stdout)
        r = $ex
        local elapsed = round((time()-t)*100)/100
        println("    done, $elapsed sec!")
        flush(stdout)
        r
    end
end

# OPTIM remove collect:
fasthash(a::Array{T}) where {T<:Number} = bytes2hex(eltype(a)!=UInt8 ? sha256(collect(reinterpret(UInt8, vec(a)))) : sha256(a))

fasthash(a::Number) = fasthash(Any[[a], "__julianumber"])
fasthash(a::Char) = fasthash(Any[string(a), "__juliachar"])
fasthash(a::AbstractString) = bytes2hex(sha256(a))
fasthash(a::Symbol) = fasthash(Any[string(a),"__juliasymbol"])
fasthash(a::Tuple) = fasthash(Any[a..., "__juliatuple"])
fasthash(f::Function) = fasthash(Any[string(f), "__juliafunction"])
fasthash(a::UnitRange) = fasthash(Any[a.start, a.stop, "__juliaunitrange"])
fasthash(a::AbstractRange) = fasthash(Any[a.start, a.step, a.stop, "__juliarange"])
fasthash(a::StepRangeLen) = fasthash(Any[a.ref, a.step, a.len, a.offset, "__juliafloatrange"])
fasthash(a::Bool) = fasthash("$(a)_juliabool")
fasthash(a) = fasthash(repr(a))

function fasthash(a::Array)
    if length(a)>100000 && isa(a, DenseArray) && eltype(a)<:Number
        fasthash(Any[a[1:911:end],sum(a)])
    else
        r = Base.map(fasthash, a)
        fasthash(join(r))
    end
end
function fasthash(a::Dict)
    K = collect(keys(a))
    ind = sortperm(Base.map(string, K))
    return fasthash([(k, fasthash(a[k])) for k in K[ind]])
end
fasthash(f::Function, args, version) = fasthash(Any[string(f), args..., version])

cache(f::FD.Callable, f2::FD.Callable, args...) = error("cache(f,f2,...) not supported")
function cache(f::FD.Callable, args...; version = "0 none set", kargs...)
    mkpath("zzz")
    h = hash(fasthash([f; args; version; kargs]))
    filename = "zzz/$(string(f))-$h.jls"

    prefix = ""
    if existsfile(filename)
        try
            return @timedone "reloading $(string(f))" open(deserialize, filename)
        catch
        end
        println("  reloading failed")
        prefix = "re"
    end

    @timedone "$(prefix)computing $(string(f))" begin
        r = f(args...; kargs...)
        open(s->serialize(s,r),filename,"w")
        r
    end
end
cache(a,f::FD.Callable,args...; kargs...) = cache(f, Any[a, args...]...; kargs...)

function dictcache(f, args...; version = "0 none set", filepath = "cache.dictfile")
    dictopen(filepath) do cache
        h = fhash(f, args, version)
        key = tuple(string(f), version, h)
        #@show "before" key
        if haskey(cache, key...)
             @timedone "reloading $(string(f))" cache[key...]
        else
            @timedone "computing $(string(f))" begin
              r = f(args...)
              cache[key...] = r
              #@show keys(cache, key[1:end-1])
              @assert in(key[end], keys(cache, key[1:end-1]...))
              r
            end
        end
        #@show "after" key keys(cache, string(f), version)
    end
end

function loadedmodules()
    a = names(Main)
    r = Any[]
    for x in a
        #try
            if typeof(eval(Main,x))==Module && !in(x,Any[:Main, :Core])
                push!(r, fullname(eval(Main,x))[1])
            end
        #catch e
        #    Base.display_error(e)
        #end
    end
    r
end

function reloadmodules()
    for x in vcat(:__anon__, sort(loadedmodules())) # HACK to make sure HDF5 is loaded before JLD
        if in(x, Any[:MAT_HDF5, :MAT_v5, :IPythonDisplay, :IJulia, :ReactiveContexts])
            try
				a = "@everywhere using $x"
				println("trying: $a")
                eval(Main, parse(a))
            catch e
				showerror(e)
            end
        end
    end
end


module TestStaticArrays

using Mutabilities
using Mutabilities: isfrozen, isfrozenindex, isfrozenvalue
using StaticArrays
using Test

@testset "isfrozen" begin
    @test isfrozen(SVector(1))
    @test isfrozenindex(SVector(1))
    @test isfrozenvalue(SVector(1))

    @test !isfrozen(MVector(1))
    @test isfrozenindex(MVector(1))
    @test !isfrozenvalue(MVector(1))
end

@testset "melt" begin
    @test typeof(melt(SVector(1))) <: Vector{Int}
    @test typeof(melt(MVector(1))) <: Vector{Int}
    @test typeof(melt(SMatrix{1,1}(1))) <: MMatrix
    @test typeof(melt(MMatrix{1,1}(1))) <: MMatrix
end

@testset "meltvalue" begin
    @test typeof(meltvalue(SVector(1))) <: MVector
    @test typeof(meltvalue(MVector(1))) <: MVector
    @test typeof(meltvalue(SMatrix{1,1}(1))) <: MMatrix
    @test typeof(meltvalue(MMatrix{1,1}(1))) <: MMatrix
end

@testset "meltindex" begin
    @test typeof(meltindex(SVector(1))) <: Vector{Int}
    @test typeof(meltindex(MVector(1))) <: Vector{Int}
    @test typeof(meltindex(SMatrix{1,1}(1))) <: MMatrix
    @test typeof(meltindex(MMatrix{1,1}(1))) <: MMatrix
end

end  # module


#################################################
# Parser to PowerModels Dict
# Ref: http://www.pserc.cornell.edu/matpower/manual.pdf
#################################################

function gen_dict(; name = nothing,
    ncost = 2, # Number of terms to interpolate in cost funcion
    qc1max= 0, # Maximum reactive power output at PC1 (MVAr)
    pg= 0, # Active Generation (Not Used by PowerModels,but expected)
    model= 2, # Cost model, 1 = piecewise linear, 2 = polynomial
    shutdown= 0, # Shutdown Cost
    startup= 0, # Startup Cost
    qc2max= 0, # Maximum reactive power output at PC2 (MVAr)
    ramp_agc= 0,
    qg= 0,  # Reactive Generation (Not Used by PowerModels,but expected)
    gen_bus= 2, # Bus Of Generator
    pmax= 1, # Maximum Real Power Generation (u.u) (Will be multi by mbase)
    ramp_10= 0,
    vg= 1, # Voltage magnitude setpoint (p.u.)
    mbase= 100, # Total MVA base of machine
    pc2= 0, # Upper real power output of PQ capability curve (MW)
    index= 1, # Index
    cost= [     # Cost terms
        2000,
        0
    ],
    qmax= 999, # Maximun Reacitive
    gen_status= 1, # Status Generator (On 1, OFF 0)
    qmin= -999, # Minimum Reactive
    qc1min= 0, # Minimum reactive power output at PC1 (MVAr)
    qc2min= 0, # Minimum reactive power output at PC2 (MVAr)
    pc1= 0, # Lower real power output of PQ capability curve (MW)
    ramp_q= 0,
    ramp_30= 0,
    pmin= 0, # Minimum Real Power Generation (u.u) (Will be multi by mbase)
    apf= 0 #[not] Area participation factor
    )
    gen = Dict("ncost"=> ncost,
        "qc1max"=> qc1max,
        "pg"=> pg,
        "model"=> model,
        "shutdown"=> shutdown,
        "startup"=> startup,
        "qc2max"=> qc2max,
        "ramp_agc"=> ramp_agc,
        "qg"=> qg,
        "gen_bus"=> gen_bus,
        "pmax"=> pmax,
        "ramp_10"=> ramp_10,
        "vg"=> vg,
        "mbase"=> mbase,
        "pc2"=> pc2,
        "index"=> index,
        "cost"=> cost,
        "qmax"=> qmax,
        "gen_status"=> gen_status,
        "qmin"=> qmin,
        "qc1min"=> qc1min,
        "qc2min"=> qc2min,
        "pc1"=> pc1,
        "ramp_q"=> ramp_q,
        "ramp_30"=> ramp_30,
        "pmin"=> pmin,
        "apf"=> apf
    )

    if name != nothing
        gen["name"] = name
    end
    return gen
end

function branch_dict(; name = nothing,
        br_r= 0.065, # Resistance (p.u.)
        rate_a= 1, #  MVA rating A (long term rating), set to 0 for unlimited
        shift= 0, #  Transformer phase shift angle (degrees), positive ⇒ delay
        br_x= 1, #  Reactance (p.u.)
        g_to= 0,
        g_fr= 0,
        b_fr= 0.225,
        f_bus= 1, # “from” bus number
        br_status= 1, # Initial branch status, 1 = in-service, 0 = out-of-service
        t_bus= 3, # “to” bus number
        b_to= 0.225,
        index= 1, # Index
        angmin= -1.0472, # minimum angle difference, θf − θt (rad)
        angmax= 1.0472, # maximum angle difference, θf − θt (rad)
        transformer= false, # Bool to indicate if it is transformer
        tap= 1) #  transformer off nominal turns ratio

    branch = Dict{String,Any}("br_r"=> br_r,
        "rate_a"=> rate_a,
        "shift"=> shift,
        "br_x"=> br_x,
        "g_to"=> g_to,
        "g_fr"=> g_fr,
        "b_fr"=> b_fr,
        "f_bus"=> f_bus,
        "br_status"=> br_status,
        "t_bus"=> t_bus,
        "b_to"=> b_to,
        "index"=> index,
        "angmin"=> angmin,
        "angmax"=> angmax,
        "transformer"=> transformer,
        "tap"=> tap
    )

    if name != nothing
        branch["name"] = name
    end
    return branch
end

function bus_dict(; name = nothing,
        zone= 1, #  loss zone (positive integer)
        bus_i= 1, # bus number (positive integer)
        bus_type= 1,  # bus type (1 = PQ, 2 = PV, 3 = ref, 4 = isolated)
        vmax= 1.1, # maximum voltage magnitude (p.u.)
        area= 1, # area number (positive integer)
        vmin= 0.9, # minimum voltage magnitude (p.u.)
        index= 1, # index
        va= 0, # voltage angle (degrees)
        vm= 1, # voltage magnitude (p.u.)
        base_kv= 0) # base voltage (kV)

    bus = Dict{String,Any}("zone"=> zone,
        "bus_i"=> bus_i,
        "bus_type"=> bus_type,
        "vmax"=> vmax,
        "area"=> area,
        "vmin"=> vmin,
        "index"=> index,
        "va"=> va,
        "vm"=> vm,
        "base_kv"=> base_kv
    )
    if name != nothing
        bus["name"] = name
    end
    return bus
end

function load_dict(;
        load_bus= 1, # bus where load is present
        status= 1, # status (ON: 1, OFF:0)
        pd= 1, # reactive power demand
        qd= 0.12*pd, # real power demand
        index= 1) # index

    lod = Dict{String,Any}( "load_bus"=> load_bus,
        "status"=> status,
        "qd"=> qd,
        "pd"=> pd,
        "index"=> index)
end

function _powermodels_dict_(; bus::Dict{String,Dict{String,Any}}= Dict{String,Dict{String,Any}}(),
                source_type::String= "matpower",
                name = "CaseX",
                cost_deficit = 1000,
                dcline::Dict{String,Any}= Dict{String,Any}(),
                source_version::Dict{String,Any}= Dict(
                    "major"=> 2,
                    "minor"=> 0,
                    "patch"=> 0,
                    "prerelease"=> [],
                    "build"=> []),
                gen::Dict{String,Dict{String,Any}}= Dict{String,Dict{String,Any}}(),
                branch::Dict{String,Dict{String,Any}}= Dict{String,Dict{String,Any}}(),
                storage::Dict{String,Dict{String,Any}}= Dict{String,Dict{String,Any}}(),
                baseMVA::Float64= 100.0,
                per_unit::Bool= true,
                shunt::Dict{String,Dict{String,Any}}= Dict{String,Dict{String,Any}}(),
                load::Dict{String,Dict{String,Any}}= Dict{String,Dict{String,Any}}())

    pm = Dict(  "name" => name,
        "cost_deficit" =>cost_deficit,
        "bus"=> bus,
        "source_type"=> source_type,
        "dcline"=> dcline,
        "source_version"=> source_version,
        "gen"=> gen,
        "branch"=> branch,
        "storage"=> storage,
        "baseMVA"=> baseMVA,
        "per_unit"=> per_unit,
        "shunt"=> shunt,
        "load"=> load
    )
    return pm

end

function powermodels_dict(;bus::Array{Dict{String,Any}}= Array{Dict{String,Any}}(undef,0),
    source_type::String= "matpower",
    name = "CaseX",
    cost_deficit = 1000,
    dcline::Dict{String,Any}= Dict{String,Any}(),
    source_version::Dict{String,Any}= Dict(
    "major"=> 2,
    "minor"=> 0,
    "patch"=> 0,
    "prerelease"=> [],
    "build"=> []),
    gen::Array{Dict{String,Any}}= Array{Dict{String,Any}}(undef,0),
    branch::Array{Dict{String,Any}}= Array{Dict{String,Any}}(undef,0),
    storage::Array{Dict{String,Any}}= Array{Dict{String,Any}}(undef,0),
    baseMVA::Float64= 100.0,
    per_unit::Bool= true,
    shunt::Array{Dict{String,Any}}= Array{Dict{String,Any}}(undef,0),
    load::Array{Dict{String,Any}}= Array{Dict{String,Any}}(undef,0))

    pm = _powermodels_dict_(bus= Dict(Pair.(string.(collect(1:size(bus,1))) , bus)),
        source_type= source_type,
        name= name,
        cost_deficit = cost_deficit,
        dcline= dcline,
        source_version= source_version,
        gen= Dict(Pair.(string.(collect(1:size(gen,1))),gen)),
        branch= Dict(Pair.(string.(collect(1:size(branch,1))),branch)),
        storage= Dict(Pair.(string.(collect(1:size(storage,1))),storage)),
        baseMVA= baseMVA,
        per_unit= per_unit,
        shunt= Dict(Pair.(string.(collect(1:size(shunt,1))),shunt)),
        load= Dict(Pair.(string.(collect(1:size(load,1))),load)))

end


using GPMLj, Test, Random, PyCall

Random.seed!(123)

@testset "MeanFunctions" begin

    @testset "Additive" begin
        identity(X) = X
        mf = gpflow.mean_functions.Additive(identity, identity)
        @test typeof(mf)<:gpflow.AbstractMeanFunction
        @test typeof(mf)<:gpflow.mean_functions.Additive
        @test typeof(mf.o)<:PyObject
        temp = mf.o
        @test instantiate!(mf) == temp
    end

    @testset "Constant" begin
        mf = gpflow.mean_functions.Constant()
        @test typeof(mf)<:gpflow.AbstractMeanFunction
        @test typeof(mf)<:gpflow.mean_functions.Constant
        @test typeof(mf.o)<:PyObject
        temp = mf.o
        @test instantiate!(mf) == temp
    end

    @testset "Identity" begin
        mf = gpflow.mean_functions.Identity()
        @test typeof(mf)<:gpflow.AbstractMeanFunction
        @test typeof(mf)<:gpflow.mean_functions.Identity
        @test typeof(mf.o)<:PyObject
        temp = mf.o
        @test instantiate!(mf) == temp
    end

    @testset "Linear" begin
        mf = gpflow.mean_functions.Linear()
        @test typeof(mf)<:gpflow.AbstractMeanFunction
        @test typeof(mf)<:gpflow.mean_functions.Linear
        @test typeof(mf.o)<:PyObject
        temp = mf.o
        @test instantiate!(mf) == temp
    end

    @testset "MeanFunction" begin
        mf = gpflow.mean_functions.MeanFunction()
        @test typeof(mf)<:gpflow.AbstractMeanFunction
        @test typeof(mf)<:gpflow.mean_functions.MeanFunction
        @test typeof(mf.o)<:PyObject
        temp = mf.o
        @test instantiate!(mf) == temp
    end

    @testset "SwitchedMeanFunction" begin
        mf1 = gpflow.mean_functions.Linear()
        mf2 = gpflow.mean_functions.Linear()
        mf3 = gpflow.mean_functions.Linear()
        mf = gpflow.mean_functions.SwitchedMeanFunction([mf1, mf2, mf3])
        @test typeof(mf)<:gpflow.AbstractMeanFunction
        @test typeof(mf)<:gpflow.mean_functions.SwitchedMeanFunction
        @test typeof(mf.o)<:PyObject
        temp = mf.o
        @test instantiate!(mf) == temp
    end

end #module


module OutputRelaxationmod

using OutputALASmod

type OutputRelaxation

 solved::Int64 #renvoi 0 si réussi
 solve_message::AbstractString #message de sortie
 xtab::Array #tableau des itérés
 inner_output_alas::Any #en fait une liste de OutputALAS

 rtab::Array{Float64,1}
 stab::Array{Float64,1}
 ttab::Array{Float64,1}
 prectab::Array{Float64,1}

 realisabilite::Array{Float64,1}
 objtab::Array{Float64,1}

end

#Initialisation:
function OutputRelaxation(x0::Vector,realisabilite::Float64,obj::Float64)
 solved=0
 solve_message="Success"
 xtab=Array{Float64,2}
 xtab=collect(x0)
 inner_output=[]
 rtab=[]
 stab=[]
 ttab=[]
 prectab=[]

   if length(x0)<=4
    print_with_color(:green, "j: 0 (r,s,t)=(-,-,-) eps=- xj=$(x0) f(xj)=$(obj) \|c(xj)\|=$(realisabilite)\n\n")
   else
    print_with_color(:green, "j: 0 (r,s,t)=(-,-,-) eps=- f(xj)=$(obj) \|c(xj)\|=$(realisabilite)\n\n")
   end

 return OutputRelaxation(solved,solve_message,xtab,inner_output,rtab,stab,ttab,prectab,[realisabilite],[obj])
end

#Après une itération :
function UpdateOR(or::OutputRelaxation,xk::Vector,stat::Int64,r::Float64,s::Float64,t::Float64,prec::Float64,realisabilite::Float64,outputalas::OutputALASmod.OutputALAS,obj::Float64)
 if stat==0
  or.solved=0
  or.solve_message="Success"
 else #stat !=0
  or.solved=-1
  or.solve_message="Fail"
 end

 or.xtab=[or.xtab xk]
 or.inner_output_alas=[or.inner_output_alas;outputalas]
 or.rtab=[or.rtab;r]
 or.stab=[or.stab;s]
 or.ttab=[or.ttab;t]
 or.prectab=[or.prectab;prec]
 or.realisabilite=[or.realisabilite;realisabilite]
 or.objtab=[or.objtab;obj]

Print(or,length(xk),1,j=length(or.rtab))

 return or
end

function UpdateOR(or::OutputRelaxation,xk::Vector,stat::Int64,r::Float64,s::Float64,t::Float64,prec::Float64,realisabilite::Float64,output,obj::Float64)
 if stat==0
  or.solved=0
  or.solve_message="?"
 else #stat !=0
  or.solved=-1
  or.solve_message="Fail"
 end

 or.xtab=[or.xtab xk]
 or.inner_output_alas=[or.inner_output_alas;output]
 or.rtab=[or.rtab;r]
 or.stab=[or.stab;s]
 or.ttab=[or.ttab;t]
 or.prectab=[or.prectab;prec]
 or.realisabilite=[or.realisabilite;realisabilite]
 or.objtab=[or.objtab;obj]

 return or
end

function UpdateFinalOR(or::OutputRelaxation,mess::String)
 or.solve_message=mess
 return or
end

function Print(or::OutputRelaxation,n::Int64,verbose::Int64;j::Int64=0)
 if verbose>0
  println("")
  if j==0
   if n<=4
    print_with_color(:green, "j: 0 (r,s,t)=(-,-,-) eps=- xj=$(or.xtab[1:n,j]) f(xj)=$(or.objtab[j]) \|c(xj)\|=$(or.realisabilite[j])\n\n")
   else
    print_with_color(:green, "j: 0 (r,s,t)=(-,-,-) eps=- f(xj)=$(or.objtab[j]) \|c(xj)\|=$(or.realisabilite[j])\n\n")
   end
  else
   while j<= length(or.rtab)
    if n<=4
     print_with_color(:green, "j: $j (r,s,t)=($(or.rtab[j]),$(or.stab[j]),$(or.ttab[j])) eps=$(or.prectab[j]) \n xj=$(or.xtab[1:n,j+1]) f(xj)=$(or.objtab[j+1]) \|c(xj)\|=$(or.realisabilite[j+1])\n\n")
    else
     print_with_color(:green, "j: $j (r,s,t)=($(or.rtab[j]),$(or.stab[j]),$(or.ttab[j])) eps=$(or.prectab[j]) \n f(xj)=$(or.objtab[j+1]) \|c(xj)\|=$(or.realisabilite[j+1])\n\n")
    end
    OutputALASmod.Print(or.inner_output_alas[j],n,verbose)
    j+=1
   end
  end
 end

 return true
end

#end of module
end


module KmerAnalysisMakie

export
    spectracn

using Observables
using AbstractPlotting

import KmerAnalysis: KmerFrequencySpectra

"""
    _find_plottable_subset(spec::KmerFrequencySpectra{1})

Utility method that automatically finds the highest k-mer frequency in a spectra
with a non-zero count, and returns a view of the spectra data that only goes up
to that detected frequeny.

Currently this is useful to have Makie primitives like `scatter` and `barplot`
not plot a large portion of the graph where there's basically nothing interesting.
"""
function _find_plottable_subset(spec::KmerFrequencySpectra{1})
    return view(spec.data, firstindex(spec.data):findprev(x -> x > 0, spec.data, lastindex(spec.data)))
end

"""
    _find_plottable_subset(spec::KmerFrequencySpectra{2})

Utility method that automatically finds the highest k-mer frequency in both of
a 2D spectras dimensions with a non-zero count, and returns a sub-view of the
spectra data that only goes up to those detected frequencies.

Currently this is useful to have Makie primitives like `heatmap` and `contour`
not plot a large portion of the graph where there's basically nothing interesting.
"""
function _find_plottable_subset(spec::KmerFrequencySpectra{2})
    mat = spec.data
    furthest_row = 0
    furthest_col = 0
    for col in 1:257
        for row in 1:257
            v = mat[row, col]
            furthest_row = ifelse((row > furthest_row) & !iszero(v), row, furthest_row)
            furthest_col = ifelse((col > furthest_col) & !iszero(v), col, furthest_col)
        end
    end
    return view(mat, 1:furthest_row, 1:furthest_col)
end

include(joinpath("type_recipes", "kmer_spectra.jl"))
include(joinpath("full_recipes", "spectracn.jl"))

end # module


using CSV
using DataFrames
using DelimitedFiles
using Glob
using Plots; pgfplotsx()

# Create plots
function plot_spmv_nthreads()

	# Dictionary to convert command line arguments to label parts
	# for all solvergroups
	labels = Dict(
		# kernels
		"ser" => "SparseArrays", "par" => "MtSpMV.jl", "mkl" => "MKLSparse.jl",
		# formats
		"csr" => ", CSR", "csc" => ", CSC",
	)

	# Linestyles and markershapes
	styles = [:solid, :dash, :dot, :xcross, :cross]

	# Iterate over systems
	for sys in ["sys1", "sys2"]

		# Iterate over matrix sizes
		for matsize in ["1e6", "1e7"]

			# Create all plots
			plt1 = plot(ylabel="Runtime in seconds", xlabel="Number of threads",
						legend=:topright, dpi=300, framestyle=:box)
			plt2 = plot(ylabel="Parallel speedup", xlabel="Number of threads",
						legend=:none, dpi=300, framestyle=:box)
			plts = [plt1, plt2]

			# Plot for all benchmark types
			for (j, metric) in enumerate(["runtimes", "speedups"])

				# Get folder with benchmarks
				folder = joinpath(sys, matsize)
				folder = joinpath(folder, readdir(folder)[1])

				# Get all csv files for metric in sorted order
				files_ser = glob(string(metric, "*ser*"), joinpath(folder, "csv"))
				files_par = glob(string(metric, "*par*"), joinpath(folder, "csv"))
				files_mkl = glob(string(metric, "*mkl*"), joinpath(folder, "csv"))
				files = vcat(reverse(files_par), reverse(files_mkl), reverse(files_ser))

				# Add results from all files to plots
				for (i, file) in enumerate(files)

					# Get benchmark results
				    df = DataFrame!(CSV.File(file, header=true))
				    x = convert(Vector, df[!, 1])
				    y = convert(Vector, df[!, 5])

				    # # Convert from seconds to milliseconds
				    # if metric == "runtimes"
				    # 	y = y .* 1000
				    # end

				    # Create label from filename
				    infos = file[findlast('/', file) + 1 : findlast('.', file) - 1]
				    infos = infos[findfirst('_', infos) + 1 : length(infos)]
				    infos = split(infos, "_")
				    label = ""
				    for (j, info) in enumerate(infos)
						label = string(label, labels[info])
					end

					# Add to plot
					if length(x) == 1
						scatter!(plts[j], x, y, label=label, markershape=styles[i])
					else
						plot!(plts[j], x, y, label=label, linestyle=styles[i])
					end

				end # for

				# Plot side by side and save
				width = 530
				plot(plts[1], plts[2], layout=(1,2), size=(width, width/2))
				filename = string("bench_spmv_nthreads_", sys, "_", matsize)
				savefig(string(filename, ".png"))
				savefig(string(filename, ".tex"))

			end # for

		end # for

	end # for

end # function


#ДАНО: На ограниченном внешней прямоугольной рамкой поле имеется ровно одна внутренняя перегородка в форме прямоугольника.
#Робот - в произвольной клетке поля между внешней и внутренней перегородками.
#РЕЗУЛЬТАТ: Робот - в исходном положении и по всему периметру внутренней перегородки поставлены маркеры.
function move_to_startplace!(r::Robot)
    x = 0
    y = 0
    while !(isborder(r, HorizonSide(2)) && isborder(r, HorizonSide(3)))
        while !isborder(r, HorizonSide(2))
            move!(r, HorizonSide(2))
            y+=1
        end
        while !isborder(r, HorizonSide(3))
            move!(r, HorizonSide(3))
            x+=1
        end
    end
    return (x, y)
end

function move_to_beginplace!(r::Robot, X, Y)
    x = X
    y = Y

    while x != 0 || y != 0
        while x != 0
            if x > 0
                if isborder(r, HorizonSide(1))
                    move!(r, HorizonSide(0))
                    y-=1
                end
                if !isborder(r, HorizonSide(1))
                    move!(r, HorizonSide(1))
                    x -= 1
                end
            elseif x < 0
                if isborder(r, HorizonSide(3))
                    move!(r, HorizonSide(0))
                    y-=1
                end
                if !isborder(r, HorizonSide(3))
                    move!(r, HorizonSide(3))
                    x += 1
                end
            end
        end
        while y != 0

            if y > 0
                if isborder(r, HorizonSide(0))
                    move!(r, HorizonSide(1))
                    x-=1
                end
                if !isborder(r, HorizonSide(0))
                    move!(r, HorizonSide(0))
                    y -= 1
                end
            elseif y < 0
                if isborder(r, HorizonSide(2))
                    move!(r, HorizonSide(1))
                    x-=1
                end
                if !isborder(r, HorizonSide(2))
                    move!(r, HorizonSide(2))
                    y += 1
                end
            end
        end
    end
end

function search_object!(r::Robot)
    x = 0
    y = 0
    i = 2
    check = false
    while !isborder(r, HorizonSide(0))
        move!(r, HorizonSide(0))
        y+=1
    end
    while !isborder(r, HorizonSide(1))
        move!(r, HorizonSide(1))
        x+=1
    end
    while !check
        if i % 2 == 0
            for _ in 1:(y-1)
                move!(r, HorizonSide(i))
                if isborder(r, HorizonSide((i+1)%4)) || isborder(r, HorizonSide(i))
                    check = true
                    return i
                end
            end
            y-=1
        end
        if i % 2 != 0
            for _ in 1:(x-1)
                move!(r, HorizonSide(i))
                if isborder(r, HorizonSide((i+1)%4)) || isborder(r, HorizonSide(i))
                    check = true
                    return i
                end
            end
            x-=1
        end
        i = (i + 1)%4
    end
    return ErrorException
end

function perimetr_around_object!(r::Robot, side)
    check = false

    while !check
        if ismarker(r)
            break
        end
        while isborder(r, HorizonSide((side+1)%4))
            putmarker!(r)
            move!(r, HorizonSide(side))
        end
        putmarker!(r)
        side = (side+1)%4
        move!(r, HorizonSide(side))
    end
end

function okolo_obyekta(r::Robot)
    x, y = move_to_startplace!(r)
    side = search_object!(r)
    perimetr_around_object!(r, side)
    move_to_startplace!(r)
    move_to_beginplace!(r, x, y)
end


using DataDeps

register(DataDep(
  "online-news-popularity",
  "https://archive.ics.uci.edu/ml/datasets/Online+News+Popularity",
  "https://archive.ics.uci.edu/ml/machine-learning-databases/00332/OnlineNewsPopularity.zip",
  "dba2ae526f62ccef6f2f8efb53b0268319f4d0d2719bb98d88958cfe31d58a22",
  post_fetch_method=(path -> begin
    unpack(path)
    new_path = joinpath(dirname(path), "OnlineNewsPopularity/OnlineNewsPopularity.csv")
    mv(new_path, path)

    UCIData.process_dataset(path,
        target_index=61,
        feature_indices=2:60,
        id_indices=1,
        skipto=2,
    )
  end),
))


using PeatUtils
using Base.Test
using GDAL

# reuse GDAL.jl test data
tifpath = joinpath(Pkg.dir("GDAL"), "test", "data", "utmsmall.tif")
tifpath_local = joinpath(dirname(@__FILE__), "data", "utmsmall.tif")

cp(tifpath, tifpath_local, remove_destination=true)

@test getnodata(tifpath) == -1.0e10

nodata = -9999.0
setnodata!(tifpath_local, nodata)
@test getnodata(tifpath_local) == nodata

# test pointsample()
let
    GDAL.allregister()
    dataset = GDAL.open(tifpath_local, GDAL.GA_ReadOnly)
    geotransform = zeros(6)
    GDAL.getgeotransform(dataset, geotransform)
    band = GDAL.getrasterband(dataset, 1)

    @test pointsample(band, geotransform, 443334.9, 3750015.9) === UInt8(189)
    @test pointsample(band, geotransform, 443167.9, 3749928.3) === UInt8(173)

    GDAL.close(dataset)
    GDAL.destroydrivermanager()
end

A, x_min, y_max, cellsize, epsg, nodata = read_raster(tifpath_local)

@test x_min === 440720.0
@test y_max === 3.75132e6
@test cellsize === 60.0
@test epsg === Nullable{Int64}(26711)
@test nodata === -9999.0

tifpath_local_out = joinpath(dirname(@__FILE__), "data", "utmsmall_out.tif")
write_raster(tifpath_local_out, A, x_min, y_max, cellsize; epsg=epsg)

ref_wkt_utm48n = """PROJCS["WGS 84 / UTM zone 48N",GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",105],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS["Easting",EAST],AXIS["Northing",NORTH],AUTHORITY["EPSG","32648"]]"""
ref_epsg_utm48n = Nullable(32648)
@test epsg2wkt(ref_epsg_utm48n) == ref_wkt_utm48n
@test wkt2epsg(ref_wkt_utm48n) === ref_epsg_utm48n

rm(tifpath_local)
rm(tifpath_local_out)

# test without projection information
# A, x_min, y_max, cellsize, epsg, nodata = read_raster("a.tif")
# @test isnull(epsg)
# @test isa(epsg, Nullable{Int})


using ResourceManagement
using DataFrames
A = DisciplineLabor()
B = DisciplineLabor()

dflabor = ReadLaborTracker("C:\\Users\\junqueg\\Documents\\My Documents\\15. Programming\\Projects\\ResourceManagement.jl\\src\\TEAM_PLANNED_FWD24_NOV.csv");
vh, pv = _getEmployeePlannedHours(dflabor, "HIGA ANTHONY", 24)

vh

sum(sum.(vh))

typeof(pv)
for p in pv
    @show p
end

p=unique(pv)
for project in p
    @show project
end

p[1]
p[2]
p[3]
p[4]

Tony = DisciplineLabor("430300", "Higa Anthony",  24);

V1, p1 = fetchAndWritePlannedHours!(dflabor, "HIGA ANTHONY", 24, Tony);

sum(getFwdPlannedHours(Tony, ""))

sum(getFwdPlannedHours(Tony, "153804"))

dfAvail = ReadAvailHours("src\\UTILREPORT_FWD_NOV.csv")
A = _getAvailMonthHours(dfAvail, 24)

for i in A
    @show i[1]
end

dfRev = ReadAvailHours("C:\\Users\\junqueg\\Documents\\My Documents\\15. Programming\\Projects\\ResourceManagement.jl\\src\\TEAM_ACTUALPLAN_REV18.csv");

Tony = DisciplineLabor("430300", "Higa Anthony",  24)
Julie = DisciplineLabor("430300", "BRIONES JULITA",  24)

fetchAndWritePlannedHours!(dfRev, "HIGA ANTHONY", 18, Tony, :plan);

fetchAndWritePlannedHours!(dfRev, "HIGA ANTHONY", 18, Tony, :rev);
fetchAndWritePlannedHours!(dflabor, "HIGA ANTHONY", 24, Tony, :fwd);
fetchAndWriteRevActualHours!(dfRev, "HIGA ANTHONY", 18, Tony);

Julie = fetchAndWritePlannedHours!(dfRev, "BRIONES JULITA", 18, Julie, :rev);
Julie = fetchAndWritePlannedHours!(dflabor, "BRIONES JULITA", 24, Julie, :fwd);
fetchAndWriteRevActualHours!(dfRev, "BRIONES JULITA", 18, Julie);

writeAvailableFwdHours!(Tony, dfAvail, 24);
writeAvailableFwdHours!(Julie, dfAvail, 24);

Team1 = Tony + Julie

take(n::Int) = xs -> xs[1:min(n,end)]

t = TeamDump(Team1, :TotalFwdPlannedHours)
TeamDump(Team1, :TotalRevPlannedHours)
TeamDump(Team1, :TotalRevActualHours)
TeamDump(Team1, :FwdUtilization)

#pipe examples
TeamDump(Team1, :FwdUtilization) |> values |> collect .|> mean
TeamDump(Team1, :TotalFwdPlannedHours) |> values |> collect .|> sum

A = TeamDump(Team1, :TotalFwdPlannedHours) |> values |>collect |>minimum |> take(6)  # collect hours and take the minimum monthly values from the resources and take the first 6

B = TeamDump(Team1, :TotalFwdPlannedHours) |> values |>collect

TeamDump(Team1, :TotalRevActualHours) |> values |> take(3)

"TT = combine(Tony.FwdHoursForecast, months .=>sum)

size(Tony.FwdHoursForecast)[2]

TT = Tony.FwdHoursForecast[:, 2: end]
months= names(Tony.FwdHoursForecast)[2:end]

Vector(TT[1,:])

Vector(TT)

Vector(TT)

combine(Tony.FwdHoursForecast, months .=>sum)

Tony.FwdHoursForecast.Project

filter = Tony.FwdHoursForecast.Project .== "153804"

df = Tony.FwdHoursForecast

v = combine(df[filter, :] , months .=>sum)
v

v = Vector(v[1, :])

sum(getFwdPlannedHours(Tony, "153804"))

Tony.FwdHoursForecast.Project
Julie.FwdHoursForecast.Project

Tr = DisciplineLabor("430300", "Higa Anthony",  24);

Tr.FwdHoursForecast.Project

Tony.FwdHoursForecast ∪ Julie.FwdHoursForecast
S1 = Set(Julie.FwdHoursForecast.Project)

S2 = Set(Tony.FwdHoursForecast.Project)

Tony.FwdHoursForecast.Project

S1 ∪ S2

Tr.FwdHoursForecast.Project = String.(S1 ∪ S2)

Tr.FwdHoursForecast[!,:Project] = String.(S1 ∪ S2)

String.(S1 ∪ S2)

Tr.FwdHoursForecast

Tony.FwdHoursForecast

names(Tony.FwdHoursForecast)
Tony.FwdHoursForecast[:,2] .+ Julie.FwdHoursForecast[:,2]

Tr.FwdHoursForecast = vcat(Tony.FwdHoursForecast, Julie.FwdHoursForecast)

Tr.FwdHoursForecast.Project

sum(getFwdPlannedHours(Julie))
getFwdPlannedHours(Tony)
dflabor

sum(getFwdPlannedHours(Tr))


using LinearAlgebra, LinearOperators, SparseArrays, Printf, Test

using MPCC

using JuMP
using NLPModelsJuMP
using NLPModels

include("problems.jl")
include("rosenbrock.jl")

printstyled("MPCCMeta tests... ")
include("test-mpcc-meta.jl")
printstyled("passed ✓ \n", color = :green)
printstyled("MPCCCounters tests... ")
include("test-mpcc-counters.jl")
printstyled("passed ✓ \n", color = :green)
printstyled("ADMPCC tests... ")
include("run_test_admpcc.jl")
printstyled("passed ✓ \n", color = :green)
printstyled("MPCCNLPs tests... ")
include("run_test_mpccnlps.jl")
printstyled("passed ✓ \n", color = :green)
printstyled("NLMPCC tests... ")
include("test-mpcc-nlmpcc.jl")
printstyled("passed ✓ \n", color = :green)
printstyled("Bilevel Programming MPCC tests... ")
include("run_test_bpmpcc.jl")
printstyled("passed ✓ \n", color = :green)
printstyled("Ampl MPCC tests... ")
include("run_test_mpccampl.jl")
printstyled("passed ✓ \n", color = :green)
printstyled("MPCCAtX tests... ")
include("test-mpcc-state.jl")
printstyled("passed ✓ \n", color = :green)
printstyled("MPCCStopping tests... ")
include("test-mpcc-stopping.jl")
printstyled("passed ✓ \n", color = :green)


module SparseDiffTools

export CGraph, add_vertex!, add_edge!, neighbors, vertices, has_edge, num_edges, has_vertex, rem_edge!, rem_vertex!,
colorGraph, num_vertices, max_degree_vertex, non_neighbors, length_common_neighbor, vertex_degree, contract!,
greedy_d1

include("custom_graph.jl")
include("contraction_algo.jl")
include("greedy_dist1_coloring.jl")

end # module


## header to provide surface and context
using Cairo
c = CairoRGBSurface(256,256);
cr = CairoContext(c);

save(cr);
set_source_rgb(cr,0.8,0.8,0.8);    # light gray
rectangle(cr,0.0,0.0,256.0,256.0); # background
fill(cr);
restore(cr);

save(cr);
## original example, following here

select_font_face(cr, "Sans", Cairo.FONT_SLANT_NORMAL,
                 Cairo.FONT_WEIGHT_BOLD);
set_font_size(cr, 90.0);

move_to(cr, 10.0, 135.0);
show_text(cr, "Hello");

move_to(cr, 70.0, 165.0);
text_path(cr, "void");
set_source_rgb(cr, 0.5, 0.5, 1);
fill_preserve(cr);
set_source_rgb(cr, 0, 0, 0);
set_line_width(cr, 2.56);
stroke(cr);

# draw helping lines
set_source_rgba(cr, 1, 0.2, 0.2, 0.6);
arc(cr, 10.0, 135.0, 5.12, 0, 2*pi);
close_path(cr);
arc(cr, 70.0, 165.0, 5.12, 0, 2*pi);
fill(cr);

## mark picture with current date
restore(cr);
move_to(cr,0.0,12.0);
set_source_rgb(cr, 0,0,0);
show_text(cr,Libc.strftime(time()));
write_to_png(c,"sample_text.png");


"""
HistoryReplayer uses the generative PMDP interface to reaplay requests in history h.

Usage:

"""
struct HistoryReplayer <: PMDP{State, Action}
    pp::PMDPProblem
    empty_product::Product
    empty_product_id::Int64
    h::AbstractVector{<:NamedTuple}
    request_timesteps::Array{Int64}

    function HistoryReplayer(m::PMDP, h::AbstractVector{<:NamedTuple})
        return new(m.pp, empty_product(m), empty_product_id(m), h, collect([s.t for s in h[:s]]))
    end
end

POMDPs.initialstate(hr::HistoryReplayer) = Deterministic(hr.h[1].s)

"""
Returns next requested product. If in given timestep one of the prodcuts has selling period end, update the product request probs.
"""
function sample_request(hr::HistoryReplayer, t::Timestep, rng::AbstractRNG)::Int64
    history_index = findfirst(x->x==t, hr.request_timesteps)
    if history_index === nothing
        iₚ = empty_product_id(hr)
    else
        iₚ = hr.h[history_index].s.iₚ
    end
    return iₚ
end

"""
Sample user budget Budget for product requested in state s.
"""
function sample_customer_budget(hr::HistoryReplayer, s::State, rng::AbstractRNG)::Action
    history_index = findfirst(x->x==s.t, hr.request_timesteps)
    if history_index === nothing
        budget = EMPTY_PRODUCT_USER_BUDGET
    else
        budget = hr.h[history_index].info.b
    end
    return budget
end


"""

A structure containing the
"""
mutable struct SubSolvers{Q<:MOI.AbstractOptimizer, S<:MOI.AbstractOptimizer, T<:ExtensionType}
    relaxed_optimizer::Q
    upper_optimizer::S
    ext::T
end
SubSolvers{Q,S,T}(; r::Q = Cbc.Optimizer(), u::S = Ipopt.Optimizer(), t::T = DefaultExt()) where {Q,S,T} = SubSolvers{Q,S,T}(r, u, t)
SubSolvers(; r::Q = Cbc.Optimizer(), u::S = Ipopt.Optimizer(), t::T = DefaultExt()) where {Q,S,T} = SubSolvers{Q,S,T}(r,u,t)

function _relaxed_optimizer(d::SubSolvers{Q,S,T}) where {Q <: MOI.AbstractOptimizer,
                                                         S <: MOI.AbstractOptimizer,
                                                         T <: ExtensionType}
    return d.relaxed_optimizer
end

function _upper_optimizer(d::SubSolvers{Q,S,T}) where {Q <: MOI.AbstractOptimizer,
                                                       S <: MOI.AbstractOptimizer,
                                                       T <: ExtensionType}
    return d.upper_optimizer
end

function _ext(d::SubSolvers{Q,S,T}) where {Q <: MOI.AbstractOptimizer,
                                                S <: MOI.AbstractOptimizer,
                                                T <: ExtensionType}
    return d.ext
end

function isempty(d::SubSolvers{Q,S,T}) where {Q,S,T}
    MOI.is_empty(d.relaxed_optimizer) && MOI.is_empty(d.upper_optimizer)
end
function MOI.empty!(d::SubSolvers{Q,S,T}) where {Q,S,T}
    MOI.empty!(d.relaxed_optimizer)
    MOI.empty!(d.upper_optimizer)
    return
end


import Boltzmann: split_evenly

if :unit in TEST_GROUPS
    @test split_evenly(10, 2) == [(1,2), (3,4), (5,6), (7,8), (9,10)]
    @test split_evenly(11, 2) == [(1,2), (3,4), (5,6), (7,8), (9,10), (11,11)]
    @test split_evenly(3, 5) == [(1,3)]
end

if :integration in TEST_GROUPS
    # Ensure that the test methods all work correctly
    df1 = Boltzmann.compare(GRBM(10, 5); n_obs=10)
    @test size(df1, 1) > 0

    # Try a comparison with 2 different contexts
    # in this case just use 2 different reporters
    context1 = Boltzmann.DefaultContext()
    context1[:reporter].log = true
    context2 = Boltzmann.DefaultContext()
    context2[:reporter] = Boltzmann.TextReporter()
    df2 = Boltzmann.compare(GRBM(10, 5), context1, context2; input_size=10, n_obs=10)
    @test size(df2, 1) > 0

    # Test the benchmark function (since we won't be running
    # it under coverage normally.
    df3 = Boltzmann.benchmark(GRBM(10, 5), input_size=10, n_obs=10, debug=true)
    @test size(df3, 1) > 0
end


using WGANGP
using Test
using Random

include("wgangp.jl")


function Base.getindex(
    tr::TableResidues,
    com::ConstraintSolverModel,
    vidx::Int,
    local_vidx::Int,
    val::Int,
)
    val_idx = com.search_space[vidx].init_val_to_index[val + com.search_space[vidx].offset]
    index_shift = tr.var_start[local_vidx] - 1 + val_idx
    return tr.values[index_shift]
end

function Base.setindex!(
    tr::TableResidues,
    residue::Int,
    com::ConstraintSolverModel,
    vidx::Int,
    local_vidx::Int,
    val::Int,
)
    val_idx = com.search_space[vidx].init_val_to_index[val + com.search_space[vidx].offset]
    index_shift = tr.var_start[local_vidx] - 1 + val_idx
    tr.values[index_shift] = residue
end


if !@isdefined(TabLevel)
    TabLevel = ""
end
println(TabLevel*"Open OpAdd.jl")
TabLevel=TabLevel*"    "

import Base.+
import Base.-

+(x::MajoranaOp,y::MajoranaOp) = OpAdd(x,y)
+(x::MajoranaSp,y::MajoranaOp) = OpAdd(x,y)
+(x::MajoranaOp,y::MajoranaSp) = OpAdd(x,y)
+(x::MajoranaSp,y::MajoranaSp) = OpAdd(x,y)

+(x::MajoranaOp,y::Number) = OpAdd(x,y)
+(x::MajoranaSp,y::Number) = OpAdd(x,y)
+(x::Number,y::MajoranaOp) = OpAdd(x,y)
+(x::Number,y::MajoranaSp) = OpAdd(x,y)

-(x::MajoranaOp,y::MajoranaOp) = x+(-1*y)
-(x::MajoranaSp,y::MajoranaOp) = x+(-1*y)
-(x::MajoranaOp,y::MajoranaSp) = x+(-1*y)
-(x::MajoranaSp,y::MajoranaSp) = x+(-1*y)

-(x::MajoranaOp,y::Number) = x+(-1*y)
-(x::MajoranaSp,y::Number) = x+(-1*y)
-(x::Number,y::MajoranaOp) = x+(-1*y)
-(x::Number,y::MajoranaSp) = x+(-1*y)

-(y::MajoranaOp) = OpScale(-1,y)
-(y::MajoranaSp) = OpScale(-1,y)

##### Methods to add majoranas
function OpAdd(a)
    OpAdd(0,a)
end

function OpAdd(b,a::Number)
     OpAdd(a,b)
end
function OpAdd(a::Number,b)
    if a == 0
        return OpCopy(b)
    else
        MajScalar(a) + b
    end
end

function OpAdd(a::Number,b::Number)
    OpScale(a+b)
end

function OpAdd(a_in::MajoranaSp,b::MajoranaSp)
    ##loop over the lists in the 'b' variable
    a=OpCopy(a_in)
    #println("the variables on the left -- a:")
    #OpPrintln(a)
    for bOP in b.SpList
        #println(".-.-.-.-.-.")
        #println("Adding  -- bOP:")
        #OpPrintln(bOP)
        a = OpAdd(a,bOP)
        #println("the new result -- a:")
        #OpPrintln(a)
        #println("a=$a")
    end
    return a
end

function OpAdd(a::MajoranaSp,b::MajoranaOp) OpAdd(b,a) end
function OpAdd(a::MajoranaOp,b_in::MajoranaSp)
    ##loop over the lists in the 'b' variable
    b=OpCopy(b_in)
    if a.scale==0
        return b
    end
    for indx in 1:length(b.SpList)
        localB=b.SpList[indx]
        if OpProp(a ,localB ) ###If poportional, spimply
            NewOp = OpAdd(a,localB)
            if NewOp == 0 ##If the weight is zero remove the term
                deleteat!(b.SpList,indx)
                if length(b.SpList)==1 ###We could have removed a term
                    return b.SpList[1] ##then return the only operator
                else ##Otherwise return all the terms
                    return b
                end
            else ##Update the scale
                b.SpList[indx]=NewOp
                return b
            end
        elseif a.OpList < localB.OpList
            ###If a  is smaller we insert a before b
            insert!(b.SpList,indx,a)
            return b
        end
        ###if it is bigger we loop to the next value to see its size
    end
    ###If we have looped over all the elements then we insert the element at the end
    push!(b.SpList,a)
    return b
end

function OpAdd(a::MajoranaOp,b::MajoranaOp;verbose=false)
    verbose && println("Adding a=$a\nand b=$b")
    if a.KetState != b.KetState
        error("a=$a and b=$b have different ket-states. Nonsensical addition")
    elseif a.scale==0
        verbose && println("Adding zero to b")
        return OpCopy(b) ###Adding zero
    elseif b.scale==0
        verbose && println("Adding zero to a")
        return OpCopy(a) ###Adding zero
    elseif OpProp(a,b) ##Check proåportionality
        verbose && println("States are Proportional")
        NewScale=a.scale+b.scale
        if NewScale == 0
            return  OpScale(0)
        else ###Just add the scales
            MajOpOut=OpReduce(a)
            MajOpOut.scale = a.scale+b.scale
            return MajOpOut
        end
    else ###
        verbose && println("States are not proprtional")
        verbose && println("a.OpList = $(a.OpList)")
        verbose && println("b.OpList = $(b.OpList)")
        if a.OpList > b.OpList ###If a is larger we insert a after b
            verbose && println("a > b")
            return MajoranaSp([OpReduce(b),OpReduce(a)],true)
        else  ###If a  is smaller we insert a before b
            verbose && println("b < a")
            return MajoranaSp([OpReduce(a),OpReduce(b)],true)
        end
    end
end

###

TabLevel=TabLevel[1:end-4]
println(TabLevel*"Close OpAdd.jl")


using Colors, FixedPointNumbers, JLD2
using Test
using ColorTypes: eltype_default, parametric3

@testset "Conversion" begin
    r8(x) = reinterpret(N0f8, x)

    # Promotions
    a, b = promote(RGB(1,0,0), Gray(0.8))
    @test isa(a, RGB{Float64}) && isa(b, RGB{Float64})
    a, b = promote(RGBA(1,0,0), Gray(0.8))
    @test isa(a, RGBA{Float64}) && isa(b, RGBA{Float64})
    a, b = promote(RGBA(1,0,0), GrayA(0.8))
    @test isa(a, RGBA{Float64}) && isa(b, RGBA{Float64})
    a, b = promote(RGB(1,0,0), GrayA(0.8))
    @test isa(a, RGBA{Float64}) && isa(b, RGBA{Float64})
    a, b = promote(RGB(1,0,0), AGray(0.8))
    @test isa(a, ARGB{Float64}) && isa(b, ARGB{Float64})

    # srgb_compand / invert_srgb_compand
    @test Colors.srgb_compand(0.5) ≈ 0.7353569830524494 atol=eps()
    @test Colors.invert_srgb_compand(0.7353569830524494) ≈ 0.5 atol=eps()
    # issue #351
    l_pow_x_y() = for i=1:1000; (i/1000)^(1/2.4) end
    l_exp_y_log_x() = for i=1:1000; exp(1/2.4*log(i/1000)) end
    l_pow_x_y(); t_pow_x_y = @elapsed l_pow_x_y()
    l_exp_y_log_x(); t_exp_y_log_x = @elapsed l_exp_y_log_x()
    if t_exp_y_log_x > t_pow_x_y
        @warn "Optimization in `[invert_]srgb_compand()` may have the opposite effect."
    end

    fractional_types = (RGB, BGR, RGB1, RGB4)  # types that support Fractional

    redF64 = RGB{Float64}(1,0,0)
    redF32 = RGB{Float32}(1,0,0)
    red24 = reinterpret(RGB24, 0x00ff0000)
    red32 = reinterpret(ARGB32, 0xffff0000)
    @testset "type check: RGB, RGB{$T}" for T in (Float64, Float32, N0f8)
        c = RGB(one(T), zero(T), zero(T))
        @test eltype(c) == T
        @test convert(RGB{Float64}, c) === redF64
        @test convert(RGB{T}, redF64) == c
    end
    @test RGB(1,0,0) == redF64
    @test RGB(UInt8(1), 0, 0) == redF64
    @test RGB(UInt8(1), UInt8(0), UInt8(0)) == redF64 # != colorant"#010000"
    @test convert(RGB, red24) == redF64

    @testset "type check: RGB24-->$Cto" for Cto in parametric3
        @test typeof(convert(Cto, red24)) == Cto{eltype_default(Cto)}
        @test typeof(convert(Cto{Float64}, red24)) == Cto{Float64}
    end

    @testset "type check: C{Float}-->C{Float}" for Cfrom in parametric3, Tfrom in (Float32, Float64)
        c = convert(Cfrom{Tfrom}, redF64)
        @test typeof(c) == Cfrom{Tfrom}
        @testset "$Cfrom{$Tfrom}-->$Cto" for Cto in parametric3
            c1 = convert(Cto, c)
            @test eltype(c1) == Tfrom
        end
        @testset "$Cfrom{$Tfrom}-->$Cto{$Tto}" for Cto in parametric3, Tto in (Float32, Float64)
            c2 = convert(Cto{Tto}, c)
            @test typeof(c2) == Cto{Tto}
        end
    end

    normed_types = (N0f8, N6f10, N4f12, N2f14, N0f16)
    # Test conversion from Normed types
    @testset "type check: C{Normed}-->C{Float}" for Cfrom in fractional_types, Tfrom in normed_types
        c = convert(Cfrom{Tfrom}, redF64)
        @test typeof(c) == Cfrom{Tfrom}
        @testset "$Cfrom{$Tfrom}-->$Cto" for Cto in parametric3
            eltype_default(Cto) <: FixedPoint && continue
            c1 = convert(Cto, c)
            @test eltype(c1) == eltype_default(Cto)
        end
        @testset "$Cfrom{$Tfrom}-->$Cto{$Tto}" for Cto in parametric3, Tto in (Float32, Float64)
            c2 = convert(Cto{Tto}, c)
            @test typeof(c2) == Cto{Tto}
        end
    end

    # Test conversion to Normed types
    @testset "type check: C{Float}-->C{Normed}" for Cfrom in parametric3, Tfrom in (Float32, Float64)
        c = convert(Cfrom{Tfrom}, redF64)
        @test typeof(c) == Cfrom{Tfrom}
        @testset "$Cfrom{$Tfrom}-->$Cto{$Tto}" for Cto in fractional_types, Tto in normed_types
            c2 = convert(Cto{Tto}, c)
            @test typeof(c2) == Cto{Tto}
        end
    end

    ac = RGBA(redF64)

    @test convert(RGB, ac) == RGB(1,0,0)
    @test convert(RGB{N0f8}, ac) == RGB{N0f8}(1,0,0)
    @test convert(RGBA{N0f8}, ac) == RGBA{N0f8}(1,0,0,1)
    @test convert(HSVA, ac) == HSVA{Float64}(convert(HSV, redF64), 1.0)
    @test convert(HSVA{Float32}, ac) == HSVA{Float32}(convert(HSV{Float32}, redF64), 1.0f0)
    @test convert(RGBA, redF64) == ac

    @test convert(ARGB32, ac) == reinterpret(ARGB32, 0xffff0000)
    @test convert(ARGB32, RGB(1,0,0)) == reinterpret(ARGB32, 0xffff0000)
    ac32 = convert(ARGB32, RGB(1,0,0), 0.5)
    @test ac32 == reinterpret(ARGB32, 0x80ff0000)
    @test convert(ARGB32, ac32) === ac32
    @test convert(ARGB32, BGRA(1,0,0,0.5)) == reinterpret(ARGB32, 0x80ff0000)
    @test reinterpret(UInt32, convert(ARGB32, ac)) == 0xffff0000
    @test convert(RGB24, RGB(r8(0xff),r8(0x00),r8(0x00))) == reinterpret(RGB24, 0x00ff0000)
    @test reinterpret(UInt32, convert(RGB24, RGB(r8(0xff),r8(0x00),r8(0x00)))) == 0x00ff0000
    redhsv = convert(HSV, redF64)
    @test convert(RGB24, red24) === red24
    @test convert(RGB24, redhsv) == reinterpret(RGB24, 0x00ff0000)
    @test_throws ArgumentError convert(RGB24,  RGB(0, 1.1, 0))
    @test_throws ArgumentError convert(ARGB32, RGBA(0, 1.1, 0, 0.8))
    @test_throws ArgumentError convert(ARGB32, RGBA(0, 0.8, 0, 1.1))

    @test convert(RGB{N0f8}, red24) == RGB{N0f8}(1,0,0)
    @test convert(RGBA{N0f8}, red32) == RGBA{N0f8}(1,0,0,1)
    @test convert(HSVA{Float64}, red32) == HSVA{Float64}(360, 1, 1, 1)

    @test_throws MethodError AlphaColor(RGB(1,0,0), r8(0xff))

    # whitepoint conversions
    @test isa(convert(XYZ, convert(Lab, redF64), Colors.WP_DEFAULT), XYZ{Float64})
    @test isa(convert(XYZ{Float32}, convert(Lab, redF64), Colors.WP_DEFAULT), XYZ{Float32})
    @test isa(convert(XYZ, convert(Luv, redF64), Colors.WP_DEFAULT), XYZ{Float64})
    @test isa(convert(XYZ{Float32}, convert(Luv, redF64), Colors.WP_DEFAULT), XYZ{Float32})
    @test isa(convert(Lab, convert(XYZ, redF64), Colors.WP_DEFAULT), Lab{Float64})
    @test isa(convert(Lab{Float32}, convert(XYZ, redF64), Colors.WP_DEFAULT), Lab{Float32})
    @test isa(convert(Luv, convert(XYZ, redF64), Colors.WP_DEFAULT), Luv{Float64})
    @test isa(convert(Luv{Float32}, convert(XYZ, redF64), Colors.WP_DEFAULT), Luv{Float32})

    #59
    @test Colors.xyz_to_uv(XYZ{Float64}(0.0, 0.0, 0.0)) === (0.0, 0.0)
    @test Colors.xyz_to_uv(XYZ{Float64}(0.0, 1.0, 0.0)) === (0.0, 0.6)
    @test Colors.xyz_to_uv(XYZ{Float64}(0.0, 1.0, 1.0)) === (0.0, 0.5)
    @test Colors.xyz_to_uv(XYZ{Float64}(1.0, 0.0, 1.0)) === (1.0, 0.0)
    @test Colors.xyz_to_uv(XYZ{Float64}(1.0, 0.0, 0.0)) === (4.0, 0.0)

    # ColorTypes.jl issue #40
    @test convert(HSL, RGB{N0f8}(0.678, 0.847, 0.902)) ≈ HSL{Float32}(194.73685f0,0.5327105f0,0.7901961f0) atol=100eps(Float32)

    # YIQ
    @test convert(YIQ, RGB(1,0,0)) == YIQ{Float32}(0.299, 0.595716, 0.211456)
    @test convert(YIQ, RGB(0,1,0)) == YIQ{Float32}(0.587, -0.274453, -0.522591)
    @test convert(YIQ, RGB(0,0,1)) == YIQ{Float32}(0.114, -0.321263, 0.311135)
    @test convert(RGB, YIQ(1.0,0.0,0.0)) == RGB(1,1,1)
    v = 0.5957
    @test convert(RGB, YIQ(0.0,1.0,0.0)) == RGB(0.9563*v,0,0)
    v = -0.5226
    @test convert(RGB, YIQ(0.0,0.0,-1.0)) == RGB(0,-0.6474*v,0)

    # Gray
    @test convert(Gray{N0f8}, Gray{N0f8}(0.1)) == Gray{N0f8}(0.1)
    @test convert(Gray{N0f8}, Gray(0.1))     == Gray{N0f8}(0.1)
    @test convert(Gray{N0f8}, Gray24(0.1))   == Gray{N0f8}(0.1)
    @test convert(Gray24, Gray{N0f8}(0.1))   == Gray24(0.1)

    @test convert(RGB{N0f8}, Gray{N0f8}(0.1)) == RGB{N0f8}(0.1,0.1,0.1)
    @test convert(RGB{N0f8}, Gray24(0.1))   == RGB{N0f8}(0.1,0.1,0.1)

    grayN0f16 = Gray{N0f16}(0.8)
    @test convert(RGB, grayN0f16) == RGB{N0f16}(0.8,0.8,0.8)
    @test convert(RGB{Float32}, grayN0f16) == RGB{Float32}(0.8,0.8,0.8)

    @testset "$C-->Gray" for C in parametric3
        c = convert(C, RGB(1,1,1))
        g1 = convert(Gray, c)
        @test isa(g1, Gray)
        @test gray(g1) ≈ 1 atol=0.01
        g2 = convert(Gray{Float64}, c)
        @test typeof(g2) == Gray{Float64}
        @test gray(g2) ≈ 1 atol=0.01
    end

    # Images issue #382
    @test convert(Gray, RGBA(1,1,1,1)) == Gray(N0f8(1))

    # https://github.com/timholy/Images.jl/pull/445#issuecomment-189866806
    @test convert(Gray, RGB{N0f8}(0.145,0.145,0.145)) == Gray{N0f8}(0.145)

    # More AbstractRGB
    r4 = RGB4(1,0,0)
    @test convert(RGB, r4) == RGB(1,0,0)
    @test convert(RGB{N0f8}, r4) == RGB{N0f8}(1,0,0)
    @test convert(RGB4{N0f8}, r4) == RGB4{N0f8}(1,0,0)
    @test convert(RGB4{Float32}, r4) == RGB4{Float32}(1,0,0)
    @test convert(BGR{Float32}, r4) == BGR{Float32}(1,0,0)

    # Issue #257
    c = RGB{Float16}(0.9473,0.962,0.9766)
    hsi = convert(HSI, c)
    @test hsi.i > 0.96 && hsi.h ≈ 210

    # Test accuracy of conversion
    csconv = jldopen(joinpath(dirname(@__FILE__), "test_conversions.jld2")) do file
        read(file, "csconv")
    end

    # Since `colordiff`(e.g. `DE_2000`) involves a color space conversion, it is
    # not suitable for evaluating the conversion itself. On the other hand,
    # since the tolerance varies from component to component, a homogeneous
    # error evaluation function (e.g. a simple sum of differences) is also not
    # appropriate. Therefore, a series of `diffnorm`, which returns the
    # normalized Euclidian distance, is defined as follows. They are just for
    # testing purposes as the cyclicity of hue is ignored.
    sqd(a, b, s=1.0) = ((float(a) - float(b))/s)^2
    function diffnorm(a::T, b::T) where {T<:Color3} # RGB,XYZ,xyY,LMS
        sqrt(sqd(comp1(a), comp1(b)) + sqd(comp2(a), comp2(b)) + sqd(comp3(a),comp3(b)))/sqrt(3)
    end
    function diffnorm(a::T, b::T) where {T<:Union{HSV,HSL,HSI}}
        sqrt(sqd(a.h, b.h, 360) + sqd(a.s, b.s) + sqd(comp3(a), comp3(b)))/sqrt(3)
    end
    function diffnorm(a::T, b::T) where {T<:Union{Lab,Luv}}
        sqrt(sqd(a.l, b.l, 100) + sqd(comp2(a), comp2(b), 200) + sqd(comp3(a), comp3(b), 200))/sqrt(3)
    end
    function diffnorm(a::T, b::T) where {T<:Union{LCHab,LCHuv}}
        sqrt(sqd(a.l, b.l, 100) + sqd(a.c, b.c, 100) + sqd(a.h, b.h, 360))/sqrt(3)
    end
    function diffnorm(a::T, b::T) where {T<:Union{DIN99,DIN99d,DIN99o}} # csconv has no DIN99 case
        sqrt(sqd(a.l, b.l, 100) + sqd(a.a, b.a, 100) + sqd(a.b, b.b, 100))/sqrt(3)
    end
    function diffnorm(a::T, b::T) where {T<:YIQ}
        sqrt(sqd(a.y, b.y) + sqd(a.i, b.i, 1.2) + sqd(a.q, b.q, 1.2))/sqrt(3)
    end
    function diffnorm(a::T, b::T) where {T<:YCbCr}
        sqrt(sqd(a.y, b.y, 219) + sqd(a.cb, b.cb, 224) + sqd(a.cr, b.cr, 224))/sqrt(3)
    end

    for C in ColorTypes.parametric3
        y = convert(C, RGB(1.0,1.0,0.0))
        b = convert(C, RGB(0.1,0.1,0.2))
        diffnorm(y, b) < 0.5 && @warn("`diffnorm` for $C may be broken")
    end

    function convcompare(from, to, tol; showfailure::Bool=false)
        errmax = 0.0
        for i = 1:length(from)
            t = to[i]
            f = convert(typeof(t), from[i])
            diff = diffnorm(t, f)
            if showfailure && diff>tol
                original = from[i]
                @show original f t diff
            end
            errmax = max(errmax, diff)
        end
        errmax > tol && @warn("Error on conversions from $(eltype(from)) to $(eltype(to)), relative error = $errmax")
        errmax <= tol
    end

    base_t(i, from_to) = base_color_type(eltype(csconv[i][from_to]))
    @testset "accuracy test: $(base_t(i,1))-->$(base_t(i,2)) (index $i)" for i = 1:length(csconv)
        f, t = csconv[i]
        @test convcompare(f, t, 2e-3, showfailure=false)
    end
end


using CLIMAParameters
using CLIMAParameters.Planet: planet_radius
using ..Diagnostics
using ..Mesh.Interpolation

"""
    DiagnosticsConfiguration

Container for all the `DiagnosticsGroup`s to be used for a simulation.
"""
mutable struct DiagnosticsConfiguration
    groups::Array{DiagnosticsGroup, 1}

    DiagnosticsConfiguration(
        groups::Array{DG, 1},
    ) where {DG <: DiagnosticsGroup} = new(groups)
end

function InterpolationConfiguration(
    ::StackedBrickTopology,
    driver_config,
    boundaries,
    axes,
)
    grid = driver_config.grid
    return InterpolationBrick(grid, boundaries, axes[1], axes[2], axes[3])
end

function InterpolationConfiguration(
    ::StackedCubedSphereTopology,
    driver_config,
    boundaries,
    axes;
    nr_toler = nothing,
)
    grid = driver_config.grid
    info = driver_config.config_info
    return InterpolationCubedSphere(
        grid,
        info.vert_range,
        info.nelem_horz,
        axes[1],
        axes[2],
        axes[3];
        nr_toler = nr_toler,
    )
end

"""
    InterpolationConfiguration(
        driver_config::DriverConfiguration,
        boundaries::Array,
        resolution = nothing;
        axes = nothing;
    )

Creates an `InterpolationTopology` (either an `InterpolationBrick` or an
`InterpolationCubedSphere`) to be used with a `DiagnosticsGroup`. Either
`resolution` is specified, in which case the axes are set up with
equi-distant points, or the `axes` may be specified directly (in
lat/lon/lvl or x/y/z order).
"""
function InterpolationConfiguration(
    driver_config::DriverConfiguration,
    boundaries::Array,
    resolution = nothing;
    axes = nothing,
)
    @assert isnothing(resolution) || isnothing(axes)
    if isnothing(axes)
        axes = (
            collect(range(
                boundaries[1, 1],
                boundaries[2, 1],
                step = resolution[1],
            )),
            collect(range(
                boundaries[1, 2],
                boundaries[2, 2],
                step = resolution[2],
            )),
            collect(range(
                boundaries[1, 3],
                boundaries[2, 3],
                step = resolution[3],
            )),
        )
    end

    return InterpolationConfiguration(
        driver_config.grid.topology,
        driver_config,
        boundaries,
        axes,
    )
end


# for each parameter sim_select

# generate

println(now())
using Debug
# Initialize simulation functions
fpath="../src/new_SimulationFunctions.jl" # Loads up DataFile , SolveBalances , Balances, Kinetics, Control
include(fpath)

# Establish simulation parameters
TSTART = 0.0;
Ts = 1.0; # 0.1
TSTOP = 150.0; # 250

# Initialize intial data dictionary
data_dictionary = DataFile(TSTART,TSTOP,Ts)
len_cpa = length(data_dictionary["CONTROL_PARAMETER_ARRAY"])
len_rca = length(data_dictionary["RATE_CONSTANT_ARRAY"])
len_sca = length(data_dictionary["SATURATION_CONSTANT_ARRAY"])
sca_indices,sca_values = [],[]
for i in 1:len_sca
  val = data_dictionary["SATURATION_CONSTANT_ARRAY"][i]
if val !=0
  push!(sca_indices,i)
  push!(sca_values,val)
end
end

sca_pack = zip(sca_indices,sca_values)

 # CPA, let's give access to gain AND power
 cpa_gain = data_dictionary["CONTROL_PARAMETER_ARRAY"][:,1] # Gain
 cpa_order= data_dictionary["CONTROL_PARAMETER_ARRAY"][:,2] # Power

 # get lengths of new arrays
 len_cpa_gain = length(cpa_gain)
 len_cpa_order = length(cpa_order)
 len_sca_values = length(sca_values)

 len_sca_prime = length(sca_values)
   A = len_rca + len_cpa_gain
   B = A + len_cpa_order
   C = B + len_sca_prime

# read parameter arrays resulting from POETs

PC = readdlm("PC.dat")

# PC is an array of parameter arrays
PC_results =[]
fraction = 1
popsize = Int(length(PC[1,:])*fraction)
# popsize = 2 # to debug
println("Model Population Size ", popsize)
for i in 1:popsize
  println("Regen ",i," of ",popsize)
  # Refactor data dictionary for model execution
  # need to specify implicit strucutre of parameter_array
  P = PC[:,i]
  data_dictionary["RATE_CONSTANT_ARRAY"] = P[1:len_rca]
  data_dictionary["CONTROL_PARAMETER_ARRAY"][:,1] = P[len_rca+1:A]
  data_dictionary["CONTROL_PARAMETER_ARRAY"][:,2] = P[A+1:B]
# tune VEGF/NFATc1 parameters
  control_parameter_array=data_dictionary["CONTROL_PARAMETER_ARRAY"]
  control_parameter_array[6,1]=0.001 # GAIN NFATc1 -> Ecadherin_Bcatenin
  control_parameter_array[6,2]=1.5 # ORDER
  data_dictionary["CONTROL_PARAMETER_ARRAY"]=control_parameter_array
  rate_constant_array=data_dictionary["RATE_CONSTANT_ARRAY"]
# degradation rates
  rate_constant_array[34]=25.0
  rate_constant_array[36]=100.0
  rate_constant_array[37]=100.0
# PKC->RAF activation rate
  rate_constant_array[11]=0.0
  data_dictionary["RATE_CONSTANT_ARRAY"]=rate_constant_array

  # Rebuild SCA
  sca_pack = zip(sca_indices,P[B+1:C]) # new values
  for (i,val) in sca_pack
  data_dictionary["SATURATION_CONSTANT_ARRAY"][i]=val
  end

  # Run to Steady State
 #println("running to SS")
  XSS = EstimateSteadyState(data_dictionary)
  initial_condition_array = vec(XSS);
  data_dictionary["INITIAL_CONDITION_ARRAY"] = initial_condition_array;
  xss_data_dict = deepcopy(data_dictionary)

  simulations = ["edit_file_none.jl","edit_file_tgfb.jl","edit_file_mapkin.jl","edit_file_ss.jl","edit_file_ssecad.jl","edit_file_ss_tgfb3ab.jl","edit_file_ss_dnlef1.jl","edit_file_tgfb3.jl","edit_file_dnsmad.jl","edit_file_tgfb3_dnlef1.jl","edit_file_tgfb_tgfb3ab.jl"]
# Need new simulations for TGFB, VEGF, TGFB+VEGF, VEGF+VIVIT, TGFB+VIVIT,
  simulations = ["edit_file_none.jl","edit_file_tgfb.jl","edit_file_vegf.jl","edit_file_tgfb_vegf.jl",
  "edit_file_tgfb_vegf_vi.jl","edit_file_tgfb_vi.jl","edit_file_vegf_vi.jl"]
  sim_data = [] # Keep data in RAM
  for sim in simulations
    ##### What data_dictionary should be loaded here??? clearly the active one
    active_data_dictionary = deepcopy(xss_data_dict)
    (TSIM,X) =  SolveModelGenericPOETS(TSTART,TSTOP,Ts,active_data_dictionary,string("../src/",sim))
    push!(sim_data,X)
    # now reset the data dictionary
  end
  push!(PC_results,sim_data)

end

function trapz{Tx<:Number, Ty<:Number}(x::Vector{Tx}, y::Vector{Ty})
    # Trapezoidal integration rule
    local n = length(x)
    if (length(y) != n)
        error("Vectors 'x', 'y' must be of same length")
    end
    r = zero(zero(Tx) + zero(Ty))
    if n == 1; return r; end
    for i in 2:n
        r += (x[i] - x[i-1]) * (y[i] + y[i-1])
    end
    #= correction -h^2/12 * (f'(b) - f'(a))
    ha = x[2] - x[1]
    he = x[end] - x[end-1]
    ra = (y[2] - y[1]) / ha
    re = (y[end] - y[end-1]) / he
    r/2 - ha*he/12 * (re - ra)
    =#
    return r/2
end
#@show "BYPASS ROBUSTNESS CALC"
function compute_robustness(BASE,PERT,TIME_B,TIME_P,K=0)

#base_auc = trapz(TIME_B,BASE)
#pert_auc = trapz(TIME_P,PERT)
#ROBUSTNESS = (pert_auc)/(K+base_auc)

return PERT[t48]
#return ROBUSTNESS
end

# # ok we will overwrite these indices to plot nfat and sp1

# ecadherin protein (unbound)
index_active_nfatc = 34
#index_active_nfatc = 16 # MRNA!!
# ecad_bcat index
#index_active_nfatc_bcat = 53
# vimentin protein
index_psp1 = 50 #AP1_SP1_P
#index_psp1 = 85 # MRNA
t0  = 1
t24 = 74
t48 = 149
t72 = 224
t96 = 298
ti = t0
tf = t48
TIME = collect(ti:tf)

# sim indices
# 1 edit_file_none.jl
# 2 edit_file_tgfb.jl
# 3 edit_file_vegf.jl
# 4 edit_file_tgfb_vegf.jl
# 5 edit_file_tgfb_vegf_vi.jl
# 6 edit_file_tgfb_vi.jl
# 7 edit_file_vegf_vi.jl

robustness_array = []

const K = 0 # offset for robustness calc
for state in PC_results # iterate over parameter sets

state_robustness_values = []

# calculate tgfb robustness
# sample from 0 to 48 hrs and integrate
# get time series for simulation
base_ = state[1]
pert_ = state[2]

# select species and time range from simulation
base_ecad = base_[ti:tf,index_active_nfatc] #+ base_[ti:tf,index_active_nfatc_bcat]
pert_ecad = pert_[ti:tf,index_active_nfatc] #+ pert_[ti:tf,index_active_nfatc_bcat]
base_vim  = base_[ti:tf,index_psp1]
pert_vim  = pert_[ti:tf,index_psp1]

# compute robustness
robustness_ecad = compute_robustness(base_ecad,pert_ecad,TIME,TIME,K)
robustness_vim  = compute_robustness(base_vim,pert_vim,TIME,TIME,K)
push!(state_robustness_values, robustness_ecad)
push!(state_robustness_values, robustness_vim)

# calculate vegf robustness
# sample from 0 to 48 hrs and integrate
base_ = state[1]
pert_ = state[3]

base_ecad = base_[ti:tf,index_active_nfatc] #+ base_[ti:tf,index_active_nfatc_bcat]
pert_ecad = pert_[ti:tf,index_active_nfatc] #+ pert_[ti:tf,index_active_nfatc_bcat]
base_vim  = base_[ti:tf,index_psp1]
pert_vim  = pert_[ti:tf,index_psp1]

robustness_ecad = compute_robustness(base_ecad,pert_ecad,TIME,TIME,K)
robustness_vim  = compute_robustness(base_vim,pert_vim,TIME,TIME,K)
push!(state_robustness_values, robustness_ecad)
push!(state_robustness_values, robustness_vim)

# calculate tgfb vegf robustness
# sample from 0 to 48 hrs and integrate
base_ = state[1]
pert_ = state[4]

base_ecad = base_[ti:tf,index_active_nfatc] #+ base_[ti:tf,index_active_nfatc_bcat]
pert_ecad = pert_[ti:tf,index_active_nfatc] #+ pert_[ti:tf,index_active_nfatc_bcat]
base_vim  = base_[ti:tf,index_psp1]
pert_vim  = pert_[ti:tf,index_psp1]

robustness_ecad = compute_robustness(base_ecad,pert_ecad,TIME,TIME,K)
robustness_vim  = compute_robustness(base_vim,pert_vim,TIME,TIME,K)
push!(state_robustness_values, robustness_ecad)
push!(state_robustness_values, robustness_vim)

# calculate tgfb vegf vi robustness
# sample from 0 to 48 hrs and integrate
base_ = state[1]
pert_ = state[5]

base_ecad = base_[ti:tf,index_active_nfatc] #+ base_[ti:tf,index_active_nfatc_bcat]
pert_ecad = pert_[ti:tf,index_active_nfatc] #+ pert_[ti:tf,index_active_nfatc_bcat]
base_vim  = base_[ti:tf,index_psp1]
pert_vim  = pert_[ti:tf,index_psp1]

robustness_ecad = compute_robustness(base_ecad,pert_ecad,TIME,TIME,K)
robustness_vim  = compute_robustness(base_vim,pert_vim,TIME,TIME,K)
push!(state_robustness_values, robustness_ecad)
push!(state_robustness_values, robustness_vim)

# calculate tgfb vi robustness
# sample from 0 to 48 hrs and integrate
base_ = state[1]
pert_ = state[6]

base_ecad = base_[ti:tf,index_active_nfatc] #+ base_[ti:tf,index_active_nfatc_bcat]
pert_ecad = pert_[ti:tf,index_active_nfatc] #+ pert_[ti:tf,index_active_nfatc_bcat]
base_vim  = base_[ti:tf,index_psp1]
pert_vim  = pert_[ti:tf,index_psp1]

robustness_ecad = compute_robustness(base_ecad,pert_ecad,TIME,TIME,K)
robustness_vim  = compute_robustness(base_vim,pert_vim,TIME,TIME,K)
push!(state_robustness_values, robustness_ecad)
push!(state_robustness_values, robustness_vim)

# calculate vegf vi robustness
# sample from 0 to 48 hrs and integrate
base_ = state[1]
pert_ = state[7]
# base_ = state[2]# test
# pert_ = state[2]

base_ecad = base_[ti:tf,index_active_nfatc] #+ base_[ti:tf,index_active_nfatc_bcat]
pert_ecad = pert_[ti:tf,index_active_nfatc] #+ pert_[ti:tf,index_active_nfatc_bcat]
base_vim  = base_[ti:tf,index_psp1]
pert_vim  = pert_[ti:tf,index_psp1]

robustness_ecad = compute_robustness(base_ecad,pert_ecad,TIME,TIME,K)
robustness_vim  = compute_robustness(base_vim,pert_vim,TIME,TIME,K)
push!(state_robustness_values, robustness_ecad)
push!(state_robustness_values, robustness_vim)

# let's report the base case
push!(state_robustness_values, base_ecad[t48])
push!(state_robustness_values, base_vim[t48])

push!(robustness_array, state_robustness_values)

end

out_dir = "../poets/results_poets/"

writedlm(string(out_dir,"active_nfat_ap_sp1_p_training_set",".dat"),robustness_array,',')
# execfile('/home/dbassen/Dropbox/pyprgs/in_out_line.py')
# execfile('/home/dbassen/Dropbox/pyprgs/in_out_csv.py')
# b = lin.reader("results_poets/Robustness_training_set.dat")
# csv.nestedToFlt([( str.replace('Any[' ,'').replace(']','')).split(',') for str in b]) # python

### END ###

# # TGFB
# robustness_tgfb_ecad = []
# robustness_tgfb_vim = []
# # VEGF
# robustness_vegf_ecad = []
# robustness_vegf_vim = []
# # VEGF + TGFB
# robustness_tgfbvegf_ecad = []
# robustness_tgfbvegf_vim = []
# # VEGF + TGFB + VIVIT
# robustness_tgfbvegfviv_ecad = []
# robustness_tgfbvegfviv_vim = []
# # TGFB + VI
# robustness_tgfbviv_ecad = []
# robustness_tgfbviv_vim = []
# # VEGF + VI
# robustness_vegfviv_ecad = []
# robustness_vegfviv_vim = []


using Fermi.HartreeFock

export RCCSD

abstract type RCCSDAlgorithm end

function get_rccsd_alg()
    implemented = [RCCSDa()]
    N = Options.get("cc_alg")
    try
        return implemented[N]
    catch BoundsError
        throw(FermiException("implementation number $N not available for RCCSD."))
    end
end

"""
    Fermi.CoupledCluster.RCCSD
    TODO

Fermi struct that holds information about RCCSD wavefunctions

# Fields

    CorrelationEnergy   CCSD correlation energy
    T1                  T1 amplitudes
    T2                  T2 amplitudes

_struct tree:_

**RCCSD** <: AbstractCCWavefunction <: AbstractCorrelatedWavefunction <: AbstractWavefunction
"""
struct RCCSD{T} <: AbstractCCWavefunction
    guessenergy::Float64
    energy::Float64
    correlation::T
    T1::AbstractArray{T,2}
    T2::AbstractArray{T,4}
    δE::T
    residue::T
end

function RCCSD()
    aoints = IntegralHelper{Float64}()
    #aoints = IntegralHelper{Float64}(eri_type=JKFIT())
    rhf = RHF(aoints)
    moints = IntegralHelper(orbitals=rhf.orbitals)
    RCCSD(moints, aoints)
end

function RCCSD(moints::IntegralHelper{T1,E1,O}, aoints::IntegralHelper{T2,E2,AtomicOrbitals}) where {T1<:AbstractFloat,
                                                                                T2<:AbstractFloat,O<:AbstractRestrictedOrbitals,
                                                                                E1<:AbstractERI,E2<:AbstractERI}
    mo_from_ao!(moints, aoints, "Fd","OOOO", "OOOV", "OOVV", "OVOV", "OVVV", "VVVV")
    RCCSD(moints)
end

function RCCSD(moints::IntegralHelper{T1,E1,O}, aoints::IntegralHelper{T2,E2,AtomicOrbitals}) where {T1<:AbstractFloat,T2<:AbstractFloat,
                                                                                E1<:AbstractDFERI,E2<:AbstractDFERI,O<:AbstractRestrictedOrbitals}
    mo_from_ao!(moints, aoints, "Fd","BOO", "BOV", "BVV")
    RCCSD(moints)
end

function RCCSD{Float64}(mol = Molecule(), ints = IntegralHelper{Float64}())

    # Compute Restricted Hartree-Fock
    refwfn = RHF(mol, ints)

    # Delete integrals that are not gonna be used anymore
    delete!(ints, "S", "T", "V", "JKERI")
    RCCSD{Float64}(refwfn, ints)
end

function RCCSD{Float32}(mol = Molecule(), ints = IntegralHelper{Float32}())

    # Note that using this method a new IntegralHelper object
    # is created within the RHF call. This is necessary becasue
    # RHF needs a double precision integral helper

    # Compute Restricted Hartree-Fock
    refwfn = RHF(mol)

    RCCSD{Float32}(refwfn, ints)
end

function RCCSD(moints::IntegralHelper{T,E,O}) where {T<:AbstractFloat,E<:AbstractERI,O<:AbstractRestrictedOrbitals}

    # Create zeroed guesses for amplitudes

    o = moints.molecule.Nα - Options.get("drop_occ")
    v = size(moints.orbitals.C,1) - Options.get("drop_vir") - moints.molecule.Nα

    output("Using MP2 guess")
    T1guess = moints["Fia"]
    T2guess = permutedims(moints["OVOV"], (1,3,2,4))

    # Orbital energies line
    if haskey(moints.cache, "D1")
        d = moints["D1"]
    else
        Fd = moints["Fd"]
        ndocc = moints.molecule.Nα
        frozen = Options.get("drop_occ")
        inac = Options.get("drop_vir")
        ϵo = Fd[(1+frozen:ndocc)]
        ϵv = Fd[(1+ndocc):end-inac]

        d = FermiMDArray([ϵo[i]-ϵv[a] for i=eachindex(ϵo), a=eachindex(ϵv)])
        moints["D1"] = d
    end

    if haskey(moints.cache, "D2")
        D = moints["D2"]
    else
        Fd = moints["Fd"]
        ndocc = moints.molecule.Nα
        frozen = Options.get("drop_occ")
        inac = Options.get("drop_vir")
        ϵo = Fd[(1+frozen:ndocc)]
        ϵv = Fd[(1+ndocc):end-inac]

        D = FermiMDArray([ϵo[i]+ϵo[j]-ϵv[a]-ϵv[b] for i=eachindex(ϵo), j=eachindex(ϵo), a=eachindex(ϵv), b=eachindex(ϵv)])
        moints["D2"] = D
    end

    T1guess ./= d
    T2guess ./= D

    RCCSD(moints, T1guess, T2guess, get_rccsd_alg())
end

# For each implementation a singleton type must be create
struct RCCSDa <: RCCSDAlgorithm end
include("RCCSDa.jl")


#
# Protein - Water
#

using ComplexMixtures ; const CM = ComplexMixtures
using PDBTools
using Plots
ENV["GKSwstype"] = "nul"

# Here we use the PDBTools package to read the pdb file (from http://github.com/m3g/PDBTools)
atoms = readPDB("../../test/data/NAMD/structure.pdb")

# The solute is a single protein molecule (infinte dilution case). In this case,
# use the option nmols=1
solute_indexes = select(atoms,"protein")
solute = CM.Selection( solute_indexes, nmols=1 )

# The solvent is Water, which has 3 atoms. Use the natomspermol to indicate how many
# atoms each molecule has, such that there is no ambiguity on how to split the coordinates
# of the selection into individual molecules.
solvent_indexes = select(atoms,"resname TIP3")
solvent = CM.Selection( solvent_indexes, natomspermol=3 )

# Initialize trajectroy data structure and open input stream
trajectory = CM.Trajectory("../../test/data/NAMD/trajectory.dcd",solute,solvent)

# Input options for the calcualtion
options = CM.Options(binstep=0.2)

# Run CM calculation, and get the resutls in the R structure
@time R = CM.mddf(trajectory,options)

CM.save(R,"example1.json")
CM.write(R,"example1.dat",solute,solvent)

plot(layout=(4,1))

x = [0,10]
y = [1, 1]

sp=1
plot!(ylabel="MDDF or RDF",subplot=sp)
plot!(R.d,R.mddf,subplot=sp,label="mddf")
plot!(R.d,R.rdf,subplot=sp,label="rdf")
plot!(x,y)
plot!(legend=:topright,subplot=sp)

sp=2
plot!(ylabel="KB",subplot=sp)
plot!(R.d,R.kb,subplot=sp,label="mddf")
plot!(R.d,R.kb_rdf,subplot=sp,label="rdf")
plot!(legend=:topright,subplot=sp)

sp=3
plot!(ylabel="atom contrib",subplot=sp)
for i in 1:solvent.natomspermol
  plot!(R.d,R.solvent_atom[:,i],subplot=sp,label="",linewidth=2)
end
plot!(legend=:topright,subplot=sp)

sp=4
plot!(ylabel="count",subplot=sp)
plot!(R.d,R.md_count,subplot=sp,label="R md")
plot!(R.d,R.md_count_random,subplot=sp,label="R rand")
plot!(legend=:topleft,subplot=sp)

plot!(size=(600,800))
savefig("./example2.pdf")