function GHmsjPolynomials(arg0::jdouble, arg1::jdouble, arg2::jdouble, arg3::jdouble, arg4::jint)
    return GHmsjPolynomials((jdouble, jdouble, jdouble, jdouble, jint), arg0, arg1, arg2, arg3, arg4)
end

function get_gmsj(obj::GHmsjPolynomials, arg0::jint, arg1::jint, arg2::jint)
    return jcall(obj, "getGmsj", jdouble, (jint, jint, jint), arg0, arg1, arg2)
end

function get_hmsj(obj::GHmsjPolynomials, arg0::jint, arg1::jint, arg2::jint)
    return jcall(obj, "getHmsj", jdouble, (jint, jint, jint), arg0, arg1, arg2)
end

function getd_gmsd_alpha(obj::GHmsjPolynomials, arg0::jint, arg1::jint, arg2::jint)
    return jcall(obj, "getdGmsdAlpha", jdouble, (jint, jint, jint), arg0, arg1, arg2)
end

function getd_gmsd_beta(obj::GHmsjPolynomials, arg0::jint, arg1::jint, arg2::jint)
    return jcall(obj, "getdGmsdBeta", jdouble, (jint, jint, jint), arg0, arg1, arg2)
end

function getd_gmsdh(obj::GHmsjPolynomials, arg0::jint, arg1::jint, arg2::jint)
    return jcall(obj, "getdGmsdh", jdouble, (jint, jint, jint), arg0, arg1, arg2)
end

function getd_gmsdk(obj::GHmsjPolynomials, arg0::jint, arg1::jint, arg2::jint)
    return jcall(obj, "getdGmsdk", jdouble, (jint, jint, jint), arg0, arg1, arg2)
end

function getd_hmsd_alpha(obj::GHmsjPolynomials, arg0::jint, arg1::jint, arg2::jint)
    return jcall(obj, "getdHmsdAlpha", jdouble, (jint, jint, jint), arg0, arg1, arg2)
end

function getd_hmsd_beta(obj::GHmsjPolynomials, arg0::jint, arg1::jint, arg2::jint)
    return jcall(obj, "getdHmsdBeta", jdouble, (jint, jint, jint), arg0, arg1, arg2)
end

function getd_hmsdh(obj::GHmsjPolynomials, arg0::jint, arg1::jint, arg2::jint)
    return jcall(obj, "getdHmsdh", jdouble, (jint, jint, jint), arg0, arg1, arg2)
end

function getd_hmsdk(obj::GHmsjPolynomials, arg0::jint, arg1::jint, arg2::jint)
    return jcall(obj, "getdHmsdk", jdouble, (jint, jint, jint), arg0, arg1, arg2)
end


module Chunk

using SimpleHttpIO

export IOChunked,
       done,
       size,
       data,
       block,
       iterator,
       ChunkIterator

const N = SimpleHttpIO.N
const N_TYPE = SimpleHttpIO.N_TYPE

type IOChunked <: AbstractIOSocket
    io::IO
    env::Associative
end

IOChunked(io::IO; kwargs...) = IOChunked(io, Dict{Any, Any}(kwargs))

write(io::IOChunked, data) = (isempty(data) ? 0 : writeln(io, hex(sizeof(data))) + writeln(io, data))
done(io::IOChunked) = writeln(io, hex(0)) + writeln(io)

type ChunkException <: Exception
    info
end

function size(io::IOChunked, loop=true)
    # read first char
    byte = readbyte(io)

    if byte == '\r'
        byte = readbyte(io)
        byte == '\n' || throw(ChunkException([:keys => ("parse_error", "unexpected_byte"),
                              :msg => "Expected \\n byte after \\r.", :byte => byte]))
        N
    else
        d = UTF8String(append!(BYTE_TYPE[byte], readlinebare(io)))
        # parse hex data
        s = parseint(d, 16)

        if loop
            while s == 0
                # read next block
                s = size(io, false)
            end
        end

        s
    end
end

function data(io::IOChunked, size::Integer)
    d = readbytes(io, size)
    # read CRLF
    readbytes(io, 2)
    # return data
    d
end

function block(io::IOChunked)
    s = size(io)
    if s != N
        data(io, s)
    end
    N
end

type ChunkIterator
    io::IOChunked
end

const iterator = ChunkIterator

Base.start(r::ChunkIterator) = size(r.io)
Base.done(r::ChunkIterator, state) = state == N
Base.next(r::ChunkIterator, state) = begin
    d = data(r.io, state)
    new_state = size(r.io)
    d, new_state
end

end


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

## Broadcast styles
import Base.Broadcast
using Base.Broadcast: DefaultArrayStyle, Broadcasted, tail

struct StructuredMatrixStyle{T} <: Broadcast.AbstractArrayStyle{2} end
StructuredMatrixStyle{T}(::Val{2}) where {T} = StructuredMatrixStyle{T}()
StructuredMatrixStyle{T}(::Val{N}) where {T,N} = Broadcast.DefaultArrayStyle{N}()

const StructuredMatrix = Union{Diagonal,Bidiagonal,SymTridiagonal,Tridiagonal,LowerTriangular,UnitLowerTriangular,UpperTriangular,UnitUpperTriangular}
Broadcast.BroadcastStyle(::Type{T}) where {T<:StructuredMatrix} = StructuredMatrixStyle{T}()

# Promotion of broadcasts between structured matrices. This is slightly unusual
# as we define them symmetrically. This allows us to have a fallback to DefaultArrayStyle{2}().
# Diagonal can cavort with all the other structured matrix types.
# Bidiagonal doesn't know if it's upper or lower, so it becomes Tridiagonal
Broadcast.BroadcastStyle(::StructuredMatrixStyle{<:Diagonal}, ::StructuredMatrixStyle{<:Diagonal}) =
    StructuredMatrixStyle{Diagonal}()
Broadcast.BroadcastStyle(::StructuredMatrixStyle{<:Diagonal}, ::StructuredMatrixStyle{<:Union{Bidiagonal,SymTridiagonal,Tridiagonal}}) =
    StructuredMatrixStyle{Tridiagonal}()
Broadcast.BroadcastStyle(::StructuredMatrixStyle{<:Diagonal}, ::StructuredMatrixStyle{<:Union{LowerTriangular,UnitLowerTriangular}}) =
    StructuredMatrixStyle{LowerTriangular}()
Broadcast.BroadcastStyle(::StructuredMatrixStyle{<:Diagonal}, ::StructuredMatrixStyle{<:Union{UpperTriangular,UnitUpperTriangular}}) =
    StructuredMatrixStyle{UpperTriangular}()

Broadcast.BroadcastStyle(::StructuredMatrixStyle{<:Bidiagonal}, ::StructuredMatrixStyle{<:Union{Diagonal,Bidiagonal,SymTridiagonal,Tridiagonal}}) =
    StructuredMatrixStyle{Tridiagonal}()
Broadcast.BroadcastStyle(::StructuredMatrixStyle{<:SymTridiagonal}, ::StructuredMatrixStyle{<:Union{Diagonal,Bidiagonal,SymTridiagonal,Tridiagonal}}) =
    StructuredMatrixStyle{Tridiagonal}()
Broadcast.BroadcastStyle(::StructuredMatrixStyle{<:Tridiagonal}, ::StructuredMatrixStyle{<:Union{Diagonal,Bidiagonal,SymTridiagonal,Tridiagonal}}) =
    StructuredMatrixStyle{Tridiagonal}()

Broadcast.BroadcastStyle(::StructuredMatrixStyle{<:LowerTriangular}, ::StructuredMatrixStyle{<:Union{Diagonal,LowerTriangular,UnitLowerTriangular}}) =
    StructuredMatrixStyle{LowerTriangular}()
Broadcast.BroadcastStyle(::StructuredMatrixStyle{<:UpperTriangular}, ::StructuredMatrixStyle{<:Union{Diagonal,UpperTriangular,UnitUpperTriangular}}) =
    StructuredMatrixStyle{UpperTriangular}()
Broadcast.BroadcastStyle(::StructuredMatrixStyle{<:UnitLowerTriangular}, ::StructuredMatrixStyle{<:Union{Diagonal,LowerTriangular,UnitLowerTriangular}}) =
    StructuredMatrixStyle{LowerTriangular}()
Broadcast.BroadcastStyle(::StructuredMatrixStyle{<:UnitUpperTriangular}, ::StructuredMatrixStyle{<:Union{Diagonal,UpperTriangular,UnitUpperTriangular}}) =
    StructuredMatrixStyle{UpperTriangular}()

Broadcast.BroadcastStyle(::StructuredMatrixStyle{<:Union{LowerTriangular,UnitLowerTriangular}}, ::StructuredMatrixStyle{<:Union{UpperTriangular,UnitUpperTriangular}}) =
    StructuredMatrixStyle{Matrix}()
Broadcast.BroadcastStyle(::StructuredMatrixStyle{<:Union{UpperTriangular,UnitUpperTriangular}}, ::StructuredMatrixStyle{<:Union{LowerTriangular,UnitLowerTriangular}}) =
    StructuredMatrixStyle{Matrix}()

# Make sure that `StructuredMatrixStyle{<:Matrix}` doesn't ever end up falling
# through and give back `DefaultArrayStyle{2}`
Broadcast.BroadcastStyle(T::StructuredMatrixStyle{<:Matrix}, ::StructuredMatrixStyle) = T
Broadcast.BroadcastStyle(::StructuredMatrixStyle, T::StructuredMatrixStyle{<:Matrix}) = T
Broadcast.BroadcastStyle(T::StructuredMatrixStyle{<:Matrix}, ::StructuredMatrixStyle{<:Matrix}) = T

# All other combinations fall back to the default style
Broadcast.BroadcastStyle(::StructuredMatrixStyle, ::StructuredMatrixStyle) = DefaultArrayStyle{2}()

# And a definition akin to similar using the structured type:
structured_broadcast_alloc(bc, ::Type{<:Diagonal}, ::Type{ElType}, n) where {ElType} =
    Diagonal(Array{ElType}(undef, n))
# Bidiagonal is tricky as we need to know if it's upper or lower. The promotion
# system will return Tridiagonal when there's more than one Bidiagonal, but when
# there's only one, we need to make figure out upper or lower
merge_uplos(::Nothing, ::Nothing) = nothing
merge_uplos(a, ::Nothing) = a
merge_uplos(::Nothing, b) = b
merge_uplos(a, b) = a == b ? a : 'T'

find_uplo(a::Bidiagonal) = a.uplo
find_uplo(a) = nothing
find_uplo(bc::Broadcasted) = mapreduce(find_uplo, merge_uplos, bc.args, init=nothing)

function structured_broadcast_alloc(bc, ::Type{<:Bidiagonal}, ::Type{ElType}, n) where {ElType}
    uplo = find_uplo(bc)
    if uplo == 'T'
        return Tridiagonal(Array{ElType}(undef, n-1), Array{ElType}(undef, n), Array{ElType}(undef, n-1))
    end
    return Bidiagonal(Array{ElType}(undef, n),Array{ElType}(undef, n-1), uplo)
end
structured_broadcast_alloc(bc, ::Type{<:SymTridiagonal}, ::Type{ElType}, n) where {ElType} =
    SymTridiagonal(Array{ElType}(undef, n),Array{ElType}(undef, n-1))
structured_broadcast_alloc(bc, ::Type{<:Tridiagonal}, ::Type{ElType}, n) where {ElType} =
    Tridiagonal(Array{ElType}(undef, n-1),Array{ElType}(undef, n),Array{ElType}(undef, n-1))
structured_broadcast_alloc(bc, ::Type{<:LowerTriangular}, ::Type{ElType}, n) where {ElType} =
    LowerTriangular(Array{ElType}(undef, n, n))
structured_broadcast_alloc(bc, ::Type{<:UpperTriangular}, ::Type{ElType}, n) where {ElType} =
    UpperTriangular(Array{ElType}(undef, n, n))
structured_broadcast_alloc(bc, ::Type{<:UnitLowerTriangular}, ::Type{ElType}, n) where {ElType} =
    UnitLowerTriangular(Array{ElType}(undef, n, n))
structured_broadcast_alloc(bc, ::Type{<:UnitUpperTriangular}, ::Type{ElType}, n) where {ElType} =
    UnitUpperTriangular(Array{ElType}(undef, n, n))
structured_broadcast_alloc(bc, ::Type{<:Matrix}, ::Type{ElType}, n) where {ElType} =
    Matrix(Array{ElType}(undef, n, n))

# A _very_ limited list of structure-preserving functions known at compile-time. This list is
# derived from the formerly-implemented `broadcast` methods in 0.6. Note that this must
# preserve both zeros and ones (for Unit***erTriangular) and symmetry (for SymTridiagonal)
const TypeFuncs = Union{typeof(round),typeof(trunc),typeof(floor),typeof(ceil)}
isstructurepreserving(bc::Broadcasted) = isstructurepreserving(bc.f, bc.args...)
isstructurepreserving(::Union{typeof(abs),typeof(big)}, ::StructuredMatrix) = true
isstructurepreserving(::TypeFuncs, ::StructuredMatrix) = true
isstructurepreserving(::TypeFuncs, ::Ref{<:Type}, ::StructuredMatrix) = true
function isstructurepreserving(::typeof(Base.literal_pow), ::Ref{typeof(^)}, ::StructuredMatrix, ::Ref{Val{N}}) where N
    return N isa Integer && N > 0
end
isstructurepreserving(f, args...) = false

_iszero(n::Number) = iszero(n)
_iszero(x) = x == 0
fzeropreserving(bc) = (v = fzero(bc); !ismissing(v) && _iszero(v))
# Like sparse matrices, we assume that the zero-preservation property of a broadcasted
# expression is stable.  We can test the zero-preservability by applying the function
# in cases where all other arguments are known scalars against a zero from the structured
# matrix. If any non-structured matrix argument is not a known scalar, we give up.
fzero(x::Number) = x
fzero(::Type{T}) where T = T
fzero(r::Ref) = r[]
fzero(t::Tuple{Any}) = t[1]
fzero(S::StructuredMatrix) = zero(eltype(S))
fzero(x) = missing
function fzero(bc::Broadcast.Broadcasted)
    args = map(fzero, bc.args)
    return any(ismissing, args) ? missing : bc.f(args...)
end

function Base.similar(bc::Broadcasted{StructuredMatrixStyle{T}}, ::Type{ElType}) where {T,ElType}
    inds = axes(bc)
    if isstructurepreserving(bc) || (fzeropreserving(bc) && !(T <: Union{SymTridiagonal,UnitLowerTriangular,UnitUpperTriangular}))
        return structured_broadcast_alloc(bc, T, ElType, length(inds[1]))
    end
    return similar(convert(Broadcasted{DefaultArrayStyle{ndims(bc)}}, bc), ElType)
end

function copyto!(dest::Diagonal, bc::Broadcasted{<:StructuredMatrixStyle})
    !isstructurepreserving(bc) && !fzeropreserving(bc) && return copyto!(dest, convert(Broadcasted{Nothing}, bc))
    axs = axes(dest)
    axes(bc) == axs || Broadcast.throwdm(axes(bc), axs)
    for i in axs[1]
        dest.diag[i] = Broadcast._broadcast_getindex(bc, CartesianIndex(i, i))
    end
    return dest
end

function copyto!(dest::Bidiagonal, bc::Broadcasted{<:StructuredMatrixStyle})
    !isstructurepreserving(bc) && !fzeropreserving(bc) && return copyto!(dest, convert(Broadcasted{Nothing}, bc))
    axs = axes(dest)
    axes(bc) == axs || Broadcast.throwdm(axes(bc), axs)
    for i in axs[1]
        dest.dv[i] = Broadcast._broadcast_getindex(bc, CartesianIndex(i, i))
    end
    if dest.uplo == 'U'
        for i = 1:size(dest, 1)-1
            dest.ev[i] = Broadcast._broadcast_getindex(bc, CartesianIndex(i, i+1))
        end
    else
        for i = 1:size(dest, 1)-1
            dest.ev[i] = Broadcast._broadcast_getindex(bc, CartesianIndex(i+1, i))
        end
    end
    return dest
end

function copyto!(dest::SymTridiagonal, bc::Broadcasted{<:StructuredMatrixStyle})
    !isstructurepreserving(bc) && !fzeropreserving(bc) && return copyto!(dest, convert(Broadcasted{Nothing}, bc))
    axs = axes(dest)
    axes(bc) == axs || Broadcast.throwdm(axes(bc), axs)
    for i in axs[1]
        dest.dv[i] = Broadcast._broadcast_getindex(bc, CartesianIndex(i, i))
    end
    for i = 1:size(dest, 1)-1
        v = Broadcast._broadcast_getindex(bc, CartesianIndex(i, i+1))
        v == Broadcast._broadcast_getindex(bc, CartesianIndex(i+1, i)) || throw(ArgumentError("broadcasted assignment breaks symmetry between locations ($i, $(i+1)) and ($(i+1), $i)"))
        dest.ev[i] = v
    end
    return dest
end

function copyto!(dest::Tridiagonal, bc::Broadcasted{<:StructuredMatrixStyle})
    !isstructurepreserving(bc) && !fzeropreserving(bc) && return copyto!(dest, convert(Broadcasted{Nothing}, bc))
    axs = axes(dest)
    axes(bc) == axs || Broadcast.throwdm(axes(bc), axs)
    for i in axs[1]
        dest.d[i] = Broadcast._broadcast_getindex(bc, CartesianIndex(i, i))
    end
    for i = 1:size(dest, 1)-1
        dest.du[i] = Broadcast._broadcast_getindex(bc, CartesianIndex(i, i+1))
        dest.dl[i] = Broadcast._broadcast_getindex(bc, CartesianIndex(i+1, i))
    end
    return dest
end

function copyto!(dest::LowerTriangular, bc::Broadcasted{<:StructuredMatrixStyle})
    !isstructurepreserving(bc) && !fzeropreserving(bc) && return copyto!(dest, convert(Broadcasted{Nothing}, bc))
    axs = axes(dest)
    axes(bc) == axs || Broadcast.throwdm(axes(bc), axs)
    for j in axs[2]
        for i in j:axs[1][end]
            dest.data[i,j] = Broadcast._broadcast_getindex(bc, CartesianIndex(i, j))
        end
    end
    return dest
end

function copyto!(dest::UpperTriangular, bc::Broadcasted{<:StructuredMatrixStyle})
    !isstructurepreserving(bc) && !fzeropreserving(bc) && return copyto!(dest, convert(Broadcasted{Nothing}, bc))
    axs = axes(dest)
    axes(bc) == axs || Broadcast.throwdm(axes(bc), axs)
    for j in axs[2]
        for i in 1:j
            dest.data[i,j] = Broadcast._broadcast_getindex(bc, CartesianIndex(i, j))
        end
    end
    return dest
end

# We can also implement `map` and its promotion in terms of broadcast with a stricter dimension check
function map(f, A::StructuredMatrix, Bs::StructuredMatrix...)
    sz = size(A)
    all(map(B->size(B)==sz, Bs)) || throw(DimensionMismatch("dimensions must match"))
    return f.(A, Bs...)
end


function Identity()
    return Identity(())
end

function value(obj::Identity, arg0::DerivativeStructure)
    return jcall(obj, "value", DerivativeStructure, (DerivativeStructure,), arg0)
end

function value(obj::Identity, arg0::jdouble)
    return jcall(obj, "value", jdouble, (jdouble,), arg0)
end


using CUDA
using FailureRepresentation
using FailureRepresentation.SystemUnderTest
using FailureRepresentation.Autoencoder
using Flux

sut = SystemUnderTest.load("models/sut.bson")
autoencoder = Autoencoder.load("models/autoencoder.bson")
encoder = Autoencoder.load("models/encoder.bson", :encoder)


@testset "IO" begin
  for (l, r) in [ (Dense(20, 30, relu), rand(Float32, 20, 2))
                , (Pointwise(tanh), rand(Float32, 15, 24, 2, 5))
                , (Conv(3, 5), rand(Float32, 7, 7, 3, 12))
                , (Deconv(5, 3), rand(Float32, 8, 8, 5, 4))
                , (Pool(), rand(Float32, 5, 5, 5, 5))
                , (Dropout(0.7, elu), rand(Float32, 7,7,7,7))
                , (Batchnorm(10), rand(Float32, 10, 5)) ]

    ld = l |> Jtac.decompose |> Jtac.compose
    @test typeof(l) == typeof(ld)
    @test all(l(r) .== ld(r))

  end

  for G in [TicTacToe, MetaTac]

    feats = [ConstantFeature([0.7, 0.5, 1.]), ConstantFeature([-5.])]
    chain = @chain G Conv(10) Deconv(7) Batchnorm() Pool() Dense(25) Dropout(0.7)
    model = NeuralModel(G, chain, feats)
    name  = tempname()

    save_model(name, model)
    model2 = load_model(name)
    @test typeof(model) == typeof(model2)

    game = random_turn!(G())
    @test model(game) == model2(game)

    player = MCTSPlayer(model, power = 10)
    dataset = record_self(player, 1, augment = true)

    name = tempname()
    save_dataset(name, dataset)
    ds = load_dataset(name)
    @test all(representation(ds.games) .== representation(dataset.games))
    @test all(ds.label .== dataset.label)
    @test all(ds.flabel .== dataset.flabel)

  end

end


using Transformers
using Transformers.Basic

# BertAbs transformer model for abstractive summarization
# from the "Text Summarization with Pretrained Encoders" paper
# by Liu et al. (2019) as described on page 6.
function BertAbs(
    bert_model::TransformerModel{<:CompositeEmbedding,<:Bert,<:Any},
    vocab_size::Integer
)::Translator
    return Translator(
        TransformersModel(
            AbsEmbed(768, vocab_size),
            bert_model.transformers,
            Decoder(768, 8, 96, 2048, 6, pdrop=0.1),
            Generator(768, vocab_size)
        ),
        AbsSearch()
    )
    return
end

# TransformerAbs transformer model for abstractive summarization
# from the "Text Summarization with Pretrained Encoders" paper
# by Liu et al. (2019) as described on pages 6-7.
function TransformerAbs(vocab_size::Integer)::Translator
    return Translator(
        TransformersModel(
            AbsEmbed(768, vocab_size),
            # FIXME The paper mentions a hidden size of 512.
            768, 8, 96, 2048, 6,
            vocab_size,
            pdrop=0.1,
        ),
        AbsSearch()
    )
end

# Tiny version of `TransformerAbs` for testing training on devices with
# less GPU memory.
function TransformerAbsTiny(vocab_size::Integer)::Translator
    return Translator(
        TransformersModel(
            AbsEmbed(8, vocab_size),
            8, 4, 2, 16, 2,
            vocab_size,
            pdrop=0.1,
        ),
        AbsSearch()
    )
end

# Beam search used in the abstractive summarization models
# from the "Text Summarization with Pretrained Encoders" paper
# by Liu et al. (2019) as described on page 6.
function AbsSearch()
    return BeamSearch(
        5,
        # This parameter is not described in the paper.
        32,
        # The length normalization parameter α should be
        # tuned on the development set from 0.6 to 1.0.
        length_normalization=0.8
    )
end

# Word embeddings used in the abstractive summarization models
# from the "Text Summarization with Pretrained Encoders" paper
# by Liu et al. (2019) as described on page 4.
# TODO It's not clear how the random embedding is
# "added to" BERT embeddings.
function AbsEmbed(size::Integer, vocab_size::Integer)
    return WordPositionEmbed(
        size,
        vocab_size,
        # Not mentioned in the paper, but CNN and Daily Mail
        # documents are limited to 2000 tokens, XSum documents
        # are shorter then CNN / Daily Mail on average.
        # Our guess is that no document would be longer
        # than 4096 tokens.
        4096,
        trainable=true
    )
end


###############################################################################
#
# Query MODIS ungridded data folder
#
###############################################################################
"""
    query_RAW(dt::AbstractUngriddedData,
              year::Int,
              days::Int) where {FT<:AbstractFloat}
    query_RAW(dt::MOD15A2Hv006LAI,
              year::Int) where {FT<:AbstractFloat}

Prepare parameters (file name and etc) to work on, given
- `dt` [`AbstractUngriddedData`](@ref) type ungridded data type
- `year` AD year using format XXXX
- `days` How many days per cycle

Note that `query_RAW(dt, year)` function returns the follwings
- Folder in on Fluo server that contains files to process
- File name pattern, such as "MOD15A2H.AYYYYDOY.*.hdf"
- File name prefix, such as "MOD15A2H.A"
- Band name to process, such as "Lai_500m"
- Cache folder location to store generated CSV files
- Scaling and offset array [a,b] as in y=ax+b
- Mask of minimum and maximum values
"""
function query_RAW(
            dt::AbstractMODIS500m{FT},
            year::Int,
            days::Int
) where {FT<:AbstractFloat}
    # Querry the file information using data type
    _folder, _naming, _pref, _band, _cache, _mm = query_RAW(dt, year);

    # set time information for the gridding process
    date_start = DateTime("$(year)-01-01");
    date_stop  = DateTime("$(year)-12-31");
    date_dday  = Day(days);
    date_list  = date_start:date_dday:date_stop;

    # generate file list to process
    @info "Processing data for year $(year)";
    @info "Please wait while fetching file names...";
    params = [];
    for layer in eachindex(date_list)
        date_temp = date_list[layer];
        for _date in date_temp:Day(1):date_temp+Day(days-1)
            file_dict = ["YYYY" => lpad(Dates.year(_date)     , 4, "0"),
                         "DOY"  => lpad(Dates.dayofyear(_date), 3, "0")];
            file_temp = reduce(replace, file_dict, init=_naming);
            for _path in glob(file_temp, _folder)
                push!(params, [dt, layer, _path, _pref, _band, _cache, _mm]);
            end
        end
    end

    return params
end

function query_RAW(dt::MOD09A1v006NIRv{FT}, year::Int) where {FT<:AbstractFloat}
    _folder = joinpath("$(MODIS_HOME)/MOD09A1.006/original", string(year));
    _naming = "MOD09A1.AYYYYDOY.*.hdf";
    _prefix = "MOD09A1.A";
    _cache  = joinpath("$(MODIS_HOME)/MOD09A1.006/cache", string(year));
    _band   = ["sur_refl_b01", "sur_refl_b02"];

    return _folder, _naming, _prefix, _band, _cache, FT[-0.01,1.7]
end

function query_RAW(dt::MOD15A2Hv006LAI{FT}, year::Int) where {FT<:AbstractFloat}
    _folder = joinpath("$(MODIS_HOME)/MOD15A2H.006/original", string(year));
    _naming = "MOD15A2H.AYYYYDOY.*.hdf";
    _prefix = "MOD15A2H.A";
    _cache  = joinpath("$(MODIS_HOME)/MOD15A2H.006/cache", string(year));
    _band   = "Lai_500m";

    return _folder, _naming, _prefix, _band, _cache, FT[0,15]
end


"""
    `calculated_attributes!(trait::Type{<: AbstractPlot}, plot)`
trait version of calculated_attributes
"""
calculated_attributes!(trait, plot) = nothing

"""
    `calculated_attributes!(plot::AbstractPlot)`
Fill in values that can only be calculated when we have all other attributes filled
"""
calculated_attributes!(plot::T) where T = calculated_attributes!(T, plot)

"""
    image(x, y, image)
    image(image)

Plots an image on range `x, y` (defaults to dimensions).

"""
@recipe(Image, x, y, image) do scene
    Attributes(;
        default_theme(scene)...,
        colormap = [:black, :white],
        colorrange = automatic,
        interpolate = true,
        fxaa = false,
        lowclip = nothing,
        highclip = nothing,
        inspectable = theme(scene, :inspectable)
    )
end

"""
    heatmap(x, y, values)
    heatmap(values)

Plots a heatmap as an image on `x, y` (defaults to interpretation as dimensions).

"""
@recipe(Heatmap, x, y, values) do scene
    Attributes(;
        default_theme(scene)...,
        colormap = theme(scene, :colormap),
        colorrange = automatic,
        linewidth = 0.0,
        interpolate = false,
        levels = 1,
        fxaa = true,
        lowclip = nothing,
        highclip = nothing,
        inspectable = theme(scene, :inspectable)
    )
end

"""
    volume(volume_data)
    volume(x, y, z, volume_data)

Plots a volume, with optional physical dimensions `x, y, z`.
Available algorithms are:
* `:iso` => IsoValue
* `:absorption` => Absorption
* `:mip` => MaximumIntensityProjection
* `:absorptionrgba` => AbsorptionRGBA
* `:additive` => AdditiveRGBA
* `:indexedabsorption` => IndexedAbsorptionRGBA
"""
@recipe(Volume, x, y, z, volume) do scene
    Attributes(;
        default_theme(scene)...,
        algorithm = :mip,
        isovalue = 0.5,
        isorange = 0.05,
        color = nothing,
        colormap = theme(scene, :colormap),
        colorrange = (0, 1),
        fxaa = true,
        inspectable = theme(scene, :inspectable)
    )
end

"""
    surface(x, y, z)

Plots a surface, where `(x, y)`  define a grid whose heights are the entries in `z`.
`x` and `y` may be `Vectors` which define a regular grid, **or** `Matrices` which define an irregular grid.

"""
@recipe(Surface, x, y, z) do scene
    Attributes(;
        default_theme(scene)...,
        color = nothing,
        colormap = theme(scene, :colormap),
        colorrange = automatic,
        shading = true,
        fxaa = true,
        lowclip = nothing,
        highclip = nothing,
        invert_normals = false,
        inspectable = theme(scene, :inspectable)
    )
end

"""
    lines(positions)
    lines(x, y)
    lines(x, y, z)

Creates a connected line plot for each element in `(x, y, z)`, `(x, y)` or `positions`.

!!! tip
    You can separate segments by inserting `NaN`s.
"""
@recipe(Lines, positions) do scene
    Attributes(;
        default_theme(scene)...,
        linewidth = theme(scene, :linewidth),
        color = theme(scene, :linecolor),
        colormap = theme(scene, :colormap),
        colorrange = automatic,
        linestyle = nothing,
        fxaa = false,
        cycle = [:color],
        inspectable = theme(scene, :inspectable)
    )
end

"""
    linesegments(positions)
    linesegments(x, y)
    linesegments(x, y, z)

Plots a line for each pair of points in `(x, y, z)`, `(x, y)`, or `positions`.

"""
@recipe(LineSegments, positions) do scene
    default_theme(scene, Lines)
end

# alternatively, mesh3d? Or having only mesh instead of poly + mesh and figure out 2d/3d via dispatch
"""
    mesh(x, y, z)
    mesh(mesh_object)
    mesh(x, y, z, faces)
    mesh(xyz, faces)

Plots a 3D or 2D mesh. Supported `mesh_object`s include `Mesh` types from [GeometryBasics.jl](https://github.com/JuliaGeometry/GeometryBasics.jl).

"""
@recipe(Mesh, mesh) do scene
    Attributes(;
        default_theme(scene)...,
        color = :black,
        colormap = theme(scene, :colormap),
        colorrange = automatic,
        interpolate = false,
        shading = true,
        fxaa = true,
        inspectable = theme(scene, :inspectable),
        cycle = [:color => :patchcolor],
    )
end

"""
    scatter(positions)
    scatter(x, y)
    scatter(x, y, z)

Plots a marker for each element in `(x, y, z)`, `(x, y)`, or `positions`.

"""
@recipe(Scatter, positions) do scene
    Attributes(;
        default_theme(scene)...,
        color = theme(scene, :markercolor),
        colormap = theme(scene, :colormap),
        colorrange = automatic,
        marker = theme(scene, :marker),
        markersize = theme(scene, :markersize),

        strokecolor = theme(scene, :markerstrokecolor),
        strokewidth = theme(scene, :markerstrokewidth),
        glowcolor = (:black, 0.0),
        glowwidth = 0.0,

        rotations = Billboard(),
        marker_offset = automatic,
        transform_marker = false, # Applies the plots transformation to marker
        distancefield = nothing,
        uv_offset_width = (0.0, 0.0, 0.0, 0.0),
        markerspace = Pixel,
        fxaa = false,
        cycle = [:color],
        inspectable = theme(scene, :inspectable)
    )
end

"""
    meshscatter(positions)
    meshscatter(x, y)
    meshscatter(x, y, z)

Plots a mesh for each element in `(x, y, z)`, `(x, y)`, or `positions` (similar to `scatter`).
`markersize` is a scaling applied to the primitive passed as `marker`.

"""
@recipe(MeshScatter, positions) do scene
    Attributes(;
        default_theme(scene)...,
        color = :black,
        colormap = theme(scene, :colormap),
        colorrange = automatic,
        marker = :Sphere,
        markersize = 0.1,
        rotations = 0.0,
        # markerspace = relative,
        shading = true,
        fxaa = true,
        inspectable = theme(scene, :inspectable),
        cycle = [:color],
    )
end

"""
    text(string)

Plots a text.

"""
@recipe(Text, text) do scene
    Attributes(;
        default_theme(scene)...,
        color = theme(scene, :textcolor),
        font = theme(scene, :font),
        strokecolor = (:black, 0.0),
        strokewidth = 0,
        align = (:left, :bottom),
        rotation = 0.0,
        textsize = 20,
        position = (0.0, 0.0),
        justification = automatic,
        lineheight = 1.0,
        space = :screen, # or :data
        offset = (0.0, 0.0),
        inspectable = theme(scene, :inspectable)
    )
end


function FieldEventState(arg0::FieldEventDetector)
    return FieldEventState((FieldEventDetector,), arg0)
end

function do_event(obj::FieldEventState, arg0::FieldSpacecraftState)
    return jcall(obj, "doEvent", FieldEventState_EventOccurrence, (FieldSpacecraftState,), arg0)
end

function evaluate_step(obj::FieldEventState, arg0::FieldOrekitStepInterpolator)
    return jcall(obj, "evaluateStep", jboolean, (FieldOrekitStepInterpolator,), arg0)
end

function get_event_date(obj::FieldEventState)
    return jcall(obj, "getEventDate", FieldAbsoluteDate, ())
end

function get_event_detector(obj::FieldEventState)
    return jcall(obj, "getEventDetector", FieldEventDetector, ())
end

function get_pending_event(obj::FieldEventState)
    return jcall(obj, "getPendingEvent", jboolean, ())
end

function init(obj::FieldEventState, arg0::FieldSpacecraftState, arg1::FieldAbsoluteDate)
    return jcall(obj, "init", void, (FieldSpacecraftState, FieldAbsoluteDate), arg0, arg1)
end

function reinitialize_begin(obj::FieldEventState, arg0::FieldOrekitStepInterpolator)
    return jcall(obj, "reinitializeBegin", void, (FieldOrekitStepInterpolator,), arg0)
end

function try_advance(obj::FieldEventState, arg0::FieldSpacecraftState, arg1::FieldOrekitStepInterpolator)
    return jcall(obj, "tryAdvance", jboolean, (FieldSpacecraftState, FieldOrekitStepInterpolator), arg0, arg1)
end


module RunSettings
    #
    # This module contains things needed to control one run e.g. the output destination, number of households to use andd so on.
    #
    using Parameters

    using ScottishTaxBenefitModel
    using .Definitions
    using UUIDs

    export
        Settings,
        MT_Routing,
        uc_full,
        lmt_full,
        modelled_phase_in,

        IneqIncomeMeasure,
        TargetBCIncomes,

        bhc_net_income,
        eq_bhc_net_income,
        ahc_net_income,
        eq_ahc_net_income,
        DataIncomeSource,
        ds_hbai,
        ds_frs,

        ahc_hh,
        bhc_hh,
        total_bens,
        total_taxes

    @enum TargetBCIncomes ahc_hh bhc_hh total_bens total_taxes

    @enum MT_Routing uc_full lmt_full modelled_phase_in
    @enum IneqIncomeMeasure bhc_net_income eq_bhc_net_income ahc_net_income eq_ahc_net_income
    @enum DataIncomeSource ds_hbai ds_frs
    @with_kw mutable struct Settings
        uuid :: UUID = UUID("c2ae9c83-d24a-431c-b04f-74662d2ba07e")
        uid :: Int = 1 # placeholder for maybe a user somewhere
        run_name :: String = "default_run"
        start_year :: Int = 2015
        end_year :: Int = 2018
        scotland_full :: Bool = true
        weighted :: Bool = false
        data_dir :: String = MODEL_DATA_DIR
        household_name = "model_households_scotland"
        people_name    = "model_people_scotland"
        dump_frames :: Bool = false
        num_households :: Int = 0
        num_people :: Int = 0
        prices_file = "indexes.tab"
        to_y :: Int = 2022
        to_q :: Int = 2
        output_dir :: String = joinpath(tempdir(),"output")
        means_tested_routing :: MT_Routing = uc_full
        poverty_line :: Real = -1.0
        ineq_income_measure  :: IneqIncomeMeasure = eq_bhc_net_income
        growth :: Real = 0.02 # for time to exit poverty
        income_data_source :: DataIncomeSource = ds_hbai
        do_marginal_rates  :: Bool = false
        do_replacement_rates :: Bool = false
        replacement_rate_hours :: Int = 30
        # We jam on 68 here since we don't want changes to pension age
        # in the parameters to affect the numbers of people in
        # mr/rr calculations.
        mr_rr_upper_age :: Int = 68
        target_bc_income :: TargetBCIncomes = ahc_hh
        target_mr_rr_income :: TargetBCIncomes = ahc_hh
        mr_incr = 0.001
        requested_threads = 1
        impute_employer_pension = true
    end

end


module PkgTest

using ADMIXTURE, Tar, Test

@info "Download HAPMAP3 data"
download("http://dalexander.github.io/admixture/hapmap3-files.tar.gz",
         joinpath(pwd(), "hapmap3.tar"))
Tar.extract("hapmap3.tar", joinpath(pwd(), "hapmap3"))
HAPMAP3_FILE = "hapmap3/hapmap3"

@testset "help" begin
run(`$ADMIXTURE_EXE --help`)
end

@testset "hapmap3.bed (default)" begin
k = 3
P, Q = admixture(HAPMAP3_FILE * ".bed", k)
@test size(P) == (13928, 3)
@test size(Q) == (324, 3)
pfile = "hapmap3.$k.P"
qfile = "hapmap3.$k.Q"
@test isfile(qfile)
@test isfile(pfile)
rm(qfile, force = true)
rm(pfile, force = true)
end

@testset "hapmap3.bed (-j4, -B3, -c, -s)" begin
k = 3
P, Q = admixture(HAPMAP3_FILE * ".bed", k,
j=8, B=3, C=5, c=5, s=123)
@test size(P) == (13928, 3)
@test size(Q) == (324, 3)
pfile = "hapmap3.$k.P"
qfile = "hapmap3.$k.Q"
sfile = "hapmap3.$k.Q_se"
bfile = "hapmap3.$k.Q_bias"
@test isfile(qfile)
@test isfile(pfile)
@test isfile(sfile)
@test isfile(bfile)
rm(qfile, force = true)
rm(pfile, force = true)
rm(sfile, force = true)
rm(bfile, force = true)
end

@testset "hapmap3.bed (-l, -e)" begin
k, l, e = 3, 500, 0.1
P, Q = admixture(HAPMAP3_FILE * ".bed", k, l=l, e=e, j=8, C=5)
@test size(P) == (13928, 3)
@test size(Q) == (324, 3)
pfile = "hapmap3.$k.P.lambda.$l"
qfile = "hapmap3.$k.Q.lambda.$l"
@test isfile(qfile)
@test isfile(pfile)
rm(qfile, force = true)
rm(pfile, force = true)
end

@info "Clean up HAPMAP3 data"
rm("hapmap3.tar", force = true)
rm("hapmap3", recursive = true)

end # module PkgTest


#!/usr/bin/env julia

# Positive: number of ticks. Negative: Distance in hours between ticks
const NUMBER_OF_TICKS = -3

using Colors
# For color preview, look here: https://www.december.com/html/spec/colorsvg.html

include("src/structures.jl")

# if no size is given, this size is used for the plot
const DEFAULT_SIZE = (1400, 430)

# make nights slightly blue
const DRAW_NIGHT_BACKGROUND = true

# List of all commands and keywords to generate graphs for
global QUERY = Array{QWidget,1}([

    QPlot(
        "Network",
        days = 3,
        data = [
            QGroup(
                "kB",
                log = true,
                min = 1,
                data = [QStack([Sysstat("txpck/s"), Sysstat("rxpck/s")], ctxs = [])],
            ),
            QGroup("%", min = 0, max = 100, ctxs = [], data = [Sysstat("%ifutil")]),
        ],
    ),

    QPlot(
        "I/O",
        days = 3,
        data = [
            QGroup(
                "packets/s",
                log = true,
                data = [
                    QStack([
                        QStyle(Sysstat("wtps"), Dict("color" => :red)),
                        QStyle(Sysstat("rtps"), Dict("color" => :royalblue)),
                    ]),
                    # QStyle(Sysstat("tps"), Dict("color" => :navy))
                ],
            ),
            QGroup(
                "blocks/s",
                data = [QStyle(
                    QStack([
                        QStyle(Sysstat("bread/s"), Dict("color" => :navy)),
                        QStyle(Sysstat("bwrtn/s"), Dict("color" => :maroon)),
                    ]),
                    Dict("fillalpha" => 0.4),
                )],
            ),
        ],
    ),

    QPlot(
        "CPU Long",
        days = 30,
        data = [QGroup(
            "%",
            min = 0,
            max = 100,
            data = [
                QStack([
                    Sysstat("%steal"),
                    Sysstat("%nice"),
                    Sysstat("%iowait"),
                    Sysstat("%system"),
                    Sysstat("%user"),
                ]),
                QStyle(Sysstat("%memused"), Dict("color" => :red)),
            ],
        )],
    ),

    QPlot(
        "CPU",
        days = 3,
        data = [QGroup(
            "%",
            min = 0,
            max = 100,
            data = [
                QStack([
                    Sysstat("%steal"),
                    Sysstat("%nice"),
                    Sysstat("%iowait"),
                    Sysstat("%system"),
                    Sysstat("%user"),
                ]),
                QStyle(Sysstat("%memused"), Dict("color" => :red)),
            ],
        )],
    ),

    QPlot(
        "RAM",
        days = 3,
        data = [
            QGroup(
                "kB",
                data = [
                    Sysstat("kbdirty"),
                    QStack([
                        #Sysstat("kbswpused"), # TODO: I want this!
                        Sysstat("kbinact"),
                        Sysstat("kbactive"),
                        QStyle(
                            Sysstat("kbavail"),
                            Dict("color" => :lightblue, "fillrange" => nothing),
                        ),
                    ]),
                ],
            ),
            QGroup(
                "%",
                min = 0,
                data = [
                #QStyle(Sysstat("%memused"), Dict("color"=>:darkmagenta)),
                QStyle(Sysstat("%commit"), Dict("color" => :red))],
            ),
        ],
    ),

    QPlot(
        "Handles",
        days = 3,
        data = [
            QGroup(
                "Number",
                min = 0,
                max = 50,
                data = [QStack([QStyle(
                    Sysstat("pty-nr"),
                    Dict("color" => :darkmagenta, "fillalpha" => 0.3),
                )])],
            ),
            QGroup(
                "Number",
                min = 0,
                data = [
                    QStack([Sysstat("file-nr"), Sysstat("inode-nr")]),
                    Sysstat("dentunusd"),
                ],
            ),
        ],
    ),

    QPlot(
        "Task Creation & Context Switches",
        days = 3,
        data = [
            QGroup("Number / s", min = 0, data = [QStack([Sysstat("cswch/s")])]),
            QGroup("Number / s", min = 0, data = [QStack([Sysstat("proc/s")])]),
        ],
    ),

    QPlot(
        "Network failures",
        days = 3,
        data = [
            QGroup(
                "Number / s",
                min = 0,
                ctxs = [],
                data = [
                    QStack([
                        Sysstat("coll/s"),
                        Sysstat("rxerr/s"),
                        Sysstat("txerr/s"),
                        Sysstat("rxdrop/s"),
                        Sysstat("txdrop/s"),
                        Sysstat("txcarr/s"),
                        Sysstat("rxfram/s"),
                        Sysstat("rxfifo/s"),
                        Sysstat("txfifo/s"),
                    ]),
                ],
            ),
        ],
    ),

    QPlot(
        "Sockets",
        days = 3,
        data = [
            QGroup(
                "Number",
                min = 0,
                data = [
                    Sysstat("totsck"),
                    QStack([Sysstat("tcpsck"), Sysstat("udpsck"), Sysstat("rawsck")]),
                    Sysstat("tcp-tw"),
                ],
            ),
            QGroup("Number", min = 0, data = [QStack([Sysstat("ip-frag")])]),
        ],
    ),

    QPlot(
        "Disk I/O",
        days = 3,
        data = [
            QGroup(
                "kB",
                log = true,
                data = [QStack([Sysstat("wkB/s"), Sysstat("rkB/s")], ctxs = [])],
            ),
            QGroup("%", min = 0, max = 100, ctxs = [], data = [QStack([Sysstat("%util")])]),
        ],
    ),

    #=
    QPlot(
        "AWS UsageQuantity",
        days = 30,
        data = [QGroup("X",
            min = 0,
            data = [
                QStack([
                    Aws("UsageQuantity AWS Lambda"),
                    Aws("UsageQuantity AWS Key Management Service"),
                    Aws("UsageQuantity AWS Cost Explorer"),
                    Aws("UsageQuantity AWS Budgets"),
                    Aws("UsageQuantity Tax"),
                    Aws("UsageQuantity AmazonCloudWatch"),
                    Aws("UsageQuantity Amazon Simple Storage Service"),
                    Aws("UsageQuantity Amazon Simple Notification Service"),
                    Aws("UsageQuantity Amazon DynamoDB"),
                ]),
            ],
        )],
    ),

    QPlot(
        "AWS BlendedCost",
        days = 30,
        data = [QGroup("X",
            min = 0,
            max = 0.2, # TODO: "minmax" - scale it down if there isn't much so if the value increases it stands out immediately!
            data = [
                QStack([
                    Aws("BlendedCost AWS Lambda"),
                    Aws("BlendedCost AWS Key Management Service"),
                    Aws("BlendedCost AWS Cost Explorer"),
                    Aws("BlendedCost AWS Budgets"),
                    Aws("BlendedCost Tax"),
                    Aws("BlendedCost AmazonCloudWatch"),
                    Aws("BlendedCost Amazon Simple Storage Service"),
                    Aws("BlendedCost Amazon Simple Notification Service"),
                    Aws("BlendedCost Amazon DynamoDB"),
                ]),
            ],
        )],
    ),

    =#
])

# Color Theme
# See https://github.com/JuliaPlots/PlotThemes.jl
global PLOT_THEME = :default # :wong

# Show the last n days in the plot.
const SHOW_DAYS = 3

const MAKE_OVERVIEW = true
const OVERVIEW_WIDTH = DEFAULT_SIZE[1] ÷ 3
const OVERVIEW_HEIGHT = DEFAULT_SIZE[2] ÷ 3

# Sysstat Corruption detection
const SYSSTAT_CORRUPTION_THRESHOLD = 1_000_000_000_000
const SYSSTAT_CORRUPTION_MARGIN = 4

const USE_PNGQUANT = true


"""
    Trim{N} <: AbstractModel

Trimmed aircraft controller model with state variables corresponding to control
inputs which are used to trim the aircraft and inputs corresponding to the total
forces and moments on the aircraft ``F_x, F_y, F_z, M_x, M_y, M_z``.
"""
struct Trim{N} <: AbstractModel
    state_indices::NTuple{N,Int}
    force_indices::NTuple{N,Int}
end

"""
    Trim(state_indices::NTuple{N}, force_indices::NTuple{N})

Initialize an object of type [`Trim`](@ref). Use the state variables
corresponding to `state_indices` to trim the forces/moments corresponding to
`force_indices`.
"""
Trim(state_indices, force_indices)

Trim() = Trim((1,2,3,4,5,6), (1,2,3,4,5,6))

# --- Traits --- #

number_of_states(::Type{Trim{N}}) where N = N
number_of_inputs(::Type{Trim{N}}) where N = N
number_of_parameters(::Type{<:Trim}) = 0

inplaceness(::Type{<:Trim}) = OutOfPlace()

rate_jacobian_type(::Type{<:Trim}) = Zeros()
state_jacobian_type(::Type{<:Trim}) = Zeros()
input_jacobian_type(::Type{<:Trim}) = Identity()
parameter_jacobian_type(::Type{<:Trim}) = Empty()
time_gradient_type(::Type{<:Trim}) = Zeros()

# --- Methods --- #

get_residual(::Trim, dx, x, y, p, t) = y

# --- Performance Overloads --- #

# NOTE: No performance overloads are needed since the jacobians are fully
# defined by their provided types

# --- Convenience Methods --- #

set_states!(x, model::Trim; delta) = x .= delta

function set_inputs!(y, model::Trim; Fx, Fy, Fz, Mx, My, Mz)

    y[1] = Fx
    y[2] = Fy
    y[3] = Fz
    y[4] = Mx
    y[5] = My
    y[6] = Mz

    return y
end

set_parameters!(p, model::Trim) = p

separate_states(model::Trim, x) = (delta = x,)

function separate_inputs(model::Trim, y)

    return (Fx = y[1], Fy = y[2], Fz = y[3], Mx = y[4], My = y[5], Mz = y[6])
end

separate_parameters(model::Trim, p) = ()

# --- Internal Methods for Couplings --- #


#=AIHelp.jl --
functions which will help out the AI=#
module AIHelp
global board_max =4
function bishop(board,x::Int64,y::Int64) #Returns an array of Tuples(currentX,currentY,newX,newY) of legal coordinates to move
  #Bishop is correct
  #board_max = 9
  legalC = Array(Tuple{Int64,Int64,Int64,Int64},0)
  team = board[(x,y)][2]

  for i = 1:min(x-1,y-1)
    if !( (x-i,y-i) in keys(board))  #Empty square ahead
      push!(legalC,(x,y,x-i,y-i))
    elseif (!( get(board,(x-i,y-i),('x','x'))[2] == 'w' ) && team == 'w') || (!( get(board,(x-i,y-i),('x','x'))[2] == 'b' ) && team == 'b') #Enemy piece ahead
      push!(legalC,(x,y,x-i,y-i))
      break
    else #Friendly piece ahead
      break
    end
  end

  for i = 1:min(board_max-x,board_max-y)
    if !( (x+i,y+i) in keys(board) )
      push!(legalC,(x,y,x+i,y+i))
    elseif (!( get(board,(x+i,y+i),('x','x'))[2] == 'w' ) && team == 'w') || (!( get(board,(x+i,y+i),('x','x'))[2] == 'b' ) && team == 'b')
      push!(legalC,(x,y,x+i,y+i))
      break
    else
      break
    end
  end

  for i = 1:min(board_max-x,y-1)
    if !( (x+i,y-i) in keys(board) )
      push!(legalC,(x,y,x+i,y-i))
    elseif (!( get(board,(x+i,y-i),('x','x'))[2] == 'w' ) && team == 'w') || (!( get(board,(x+i,y-i),('x','x'))[2] == 'b' ) && team == 'b')
      push!(legalC,(x,y,x+i,y-i))
      break
    else
      break
    end
  end

  for i = 1:min(x-1,board_max-y)
    if !( (x-i,y+i) in keys(board) )
      push!(legalC,(x,y,x-i,y+i))
    elseif (!( get(board,(x-i,y+i),('x','x'))[2] == 'w' ) && team == 'w') || (!( get(board,(x-i,y+i),('x','x'))[2] == 'b' ) && team == 'b')
      push!(legalC,(x,y,x-i,y+i))
      break
    else
      break
    end
  end
  return legalC
end

function goldGeneral(board,x::Int64,y::Int64)
  #board_max = 9
  team = board[(x,y)][2]
  legalC = Array(Tuple{Int64,Int64,Int64,Int64},0)
  if team == 'w'
    potential=[(x,y,x,y+1),(x,y,x,y-1),(x,y,x-1,y+1),(x,y,x-1,y),(x,y,x+1,y),(x,y,x+1,y+1)]
    for i in eachindex(potential)
      if potential[i][3]<=board_max && potential[i][3]>0 && potential[i][4]<=board_max && potential[i][4]>0 #Makes sure move is not out of bounds
        if !(get(board,(potential[i][3],potential[i][4]),('x','x'))[2] == 'w') #Check that there's no friendly piece in the path
          push!(legalC,potential[i])
        end
      end
    end
  else #team == 'b'
      potential=[(x,y,x,y+1),(x,y,x,y-1),(x,y,x-1,y-1),(x,y,x-1,y),(x,y,x+1,y),(x,y,x+1,y-1)]
      for i in eachindex(potential)
        if potential[i][3]<=board_max && potential[i][3]>0 && potential[i][4]<=board_max && potential[i][4]>0 #Makes sure move is not out of bounds
          if !(get(board,(potential[i][3],potential[i][4]),('x','x'))[2] == 'b') #Check that there's no friendly piece in the path
            push!(legalC,potential[i])
          end
        end
      end
  end
  return legalC
end

function king(board,x::Int64,y::Int64)
  #board_max = 9
  team = board[(x,y)][2]
  legalC = Array(Tuple{Int64,Int64,Int64,Int64},0)
  if team == 'w'
    potential=[(x,y,x+1,y+1),(x,y,x,y-1),(x,y,x,y+1),(x,y,x-1,y),(x,y,x+1,y),(x,y,x-1,y-1),(x,y,x+1,y-1),(x,y,x-1,y+1)]
    for i in eachindex(potential)
      if potential[i][3]<=board_max && potential[i][3]>0 && potential[i][4]<=board_max && potential[i][4]>0 #Makes sure move is not out of bounds
        if !(get(board,(potential[i][3],potential[i][4]),('x','x'))[2] == 'w') #Check that there's no friendly piece in the path
          push!(legalC,potential[i])
        end
      end
    end
  else #Team == 'b'
    potential=[(x,y,x+1,y+1),(x,y,x,y-1),(x,y,x,y+1),(x,y,x-1,y),(x,y,x+1,y),(x,y,x-1,y-1),(x,y,x+1,y-1),(x,y,x-1,y+1)]
    for i in eachindex(potential)
      if potential[i][3]<=board_max && potential[i][3]>0 && potential[i][4]<=board_max && potential[i][4]>0 #Makes sure move is not out of bounds
        if !(get(board,(potential[i][3],potential[i][4]),('x','x')) == 'b') #Check that there's no friendly piece in the path
          push!(legalC,potential[i])
        end
      end
    end
  end

  return legalC
end

function lance(board,x::Int64,y::Int64)
  #board_max = 9
  team = board[(x,y)][2]
  legalC = Array(Tuple{Int64,Int64,Int64,Int64},0)
  if team == 'w'
    for i = 1:(board_max-y-1)
      if !( (x,y+i) in keys(board) ) #Empty square ahead
        push!(legalC,(x,y,x,y+i))
      elseif (!(get(board,(x,y+i),('x','x'))[2] == 'w' ) && team == 'w') || (!( get(board,(x,y+i),('x','x'))[2] == 'b' ) && team == 'b') #Enemy piece ahead
        push!(legalC,(x,y,x,y+i))
        break
      else #Friendly piece ahead
        break
      end
    end
  else #team == 'b'
    for i = 1:y-1
      if !( (x,y-i) in keys(board) ) #Empty square ahead
        push!(legalC,(x,y,x,y-i))
      elseif (!( get(board,(x,y-i),('x','x'))[2] == 'w' ) && team == 'w') || (!( get(board,(x,y-i),('x','x'))[2] == 'b' ) && team == 'b') #Enemy piece ahead
        push!(legalC,(x,y,x,y-i))
        break
      else #Friendly piece ahead
        break
      end
    end
  end
  return legalC
end
function knight(board,x::Int64,y::Int64)
  #board_max = 9
  team = board[(x,y)][2]
  legalC = Array(Tuple{Int64,Int64,Int64,Int64},0)
  if team == 'w'
    potential=[(x,y,x+1,y+2),(x,y,x-1,y+2)]
    for i in eachindex(potential)
      if potential[i][3]<=board_max && potential[i][3]>0 && potential[i][4]<=board_max && potential[i][4]>0 #Makes sure move is not out of bounds
        if !(get(board,(potential[i][3],potential[i][4]),('x','x'))[2] == 'w') #Check that there's no friendly piece in the path
          push!(legalC,potential[i])
        end
      end
    end
  else #team == 'b'
      potential=[(x,y,x+1,y-2),(x,y,x-1,y-2)]
      for i in eachindex(potential)
        if potential[i][3]<=board_max && potential[i][3]>0 && potential[i][4]<=board_max && potential[i][4]>0 #Makes sure move is not out of bounds
          if !(get(board,(potential[i][3],potential[i][4]),('x','x'))[2] == 'b') #Check that there's no friendly piece in the path
            push!(legalC,potential[i])
          end
        end
      end
  end
  return legalC
end

function pawn(board,x::Int64,y::Int64)
  team = board[(x,y)][2]
  legalC = Array(Tuple{Int64,Int64,Int64,Int64},0)
  if team == 'w' && !( get(board,(x,y+1),('x','x'))[2] == 'w')
    push!(legalC,(x,y,x,y+1))
  elseif team == 'b' && !( get(board,(x,y-1),('x','x'))[2] == 'b')
    push!(legalC,(x,y,x,y-1))
  end
  return legalC
end

function rook(board,x::Int64,y::Int64)
  #board_max = 9
  legalC = Array(Tuple{Int64,Int64,Int64,Int64},0)
  team = board[(x,y)][2]

  for i = 1:(y-1)
    if !( (x,y-i) in keys(board) ) #Empty square ahead
      push!(legalC,(x,y,x,y-i))
    elseif (!(get(board,(x,y-1),('x','x'))[2] == 'w') && team == 'w') || (!(get(board,(x,y-1),('x','x'))[2] == 'b') && team == 'b') #Enemy piece ahead
      push!(legalC,(x,y,x,y-i))
      break
    else #Friendly piece ahead
      break
    end
  end

  for i = 1:(board_max-x)
    if !( (x+i,y) in keys(board) )
      push!(legalC,(x,y,x+i,y))
    elseif (!(get(board,(x,y+i),('x','x'))[2] == 'w') && team == 'w') || (!(get(board,(x,y+i),('x','x'))[2] == 'b') && team == 'b')
      push!(legalC,(x,y,x+i,y))
      break
    else
      break
    end
  end

  for i = 1:(x-1)
    if !( (x-i,y) in keys(board) )
      push!(legalC,(x,y,x-i,y))
    elseif (!(get(board,(x-i,y),('x','x'))[2] == 'w') && team == 'w') || (!(get(board,(x-i,y),('x','x'))[2] == 'b') && team == 'b')
      push!(legalC,(x,y,x-i,y))
      break
    else
      break
    end
  end

  for i = 1:(board_max-y)
    if !( (x,y+i) in keys(board) )
      push!(legalC,(x,y,x,y+i))
    elseif (!(get(board,(x,y+i),('x','x'))[2] == 'w') && team == 'w') || (!(get(board,(x,y+i),('x','x'))[2] == 'b') && team == 'b')
      push!(legalC,(x,y,x,y+i))
      break
    else
      break
    end
  end

  return legalC
end
function silverGeneral(board,x::Int64,y::Int64)
  #board_max = 9
  team = board[(x,y)][2]
  legalC = Array(Tuple{Int64,Int64,Int64,Int64},0)
  if team == 'w'
    potential=[(x,y,x,y+1),(x,y,x+1,y-1),(x,y,x-1,y+1),(x,y,x-1,y-1),(x,y,x+1,y+1)]
    for i in eachindex(potential)
      if potential[i][3]<=board_max && potential[i][3]>0 && potential[i][4]<=board_max && potential[i][4]>0 #Makes sure move is not out of bounds
        if !( get(board,(potential[i][3],potential[i][4]),('x','x'))[2] == 'w' ) #Check that there's no friendly piece in the path
          push!(legalC,potential[i])
        end
      end
    end
  else #team == 'b'
      potential=[(x,y,x,y-1),(x,y,x-1,y-1),(x,y,x-1,y+1),(x,y,x+1,y+1),(x,y,x+1,y-1)]
      for i in eachindex(potential)
        if potential[i][3]<=board_max && potential[i][3]>0 && potential[i][4]<=board_max && potential[i][4]>0 #Makes sure move is not out of bounds
          if !( get(board,(potential[i][3],potential[i][4]),('x','x'))[2] == 'b' ) #Check that there's no friendly piece in the path
            push!(legalC,potential[i])
          end
        end
      end
  end
  return legalC
end

function reverseChariot(board,x::Int64,y::Int64)
  #board_max = 9
  team = board[(x,y)][2]
  legalC = Array(Tuple{Int64,Int64,Int64,Int64},0)

  for i = 1:(y-1)
    if !( (x,y-i) in keys(board) ) #Empty square ahead
      push!(legalC,(x,y,x,y-i))
    elseif (!(get(board,(x,y-1),('x','x'))[2] == 'w') && team == 'w') || (!(get(board,(x,y-1),('x','x'))[2] == 'b') && team == 'b') #Enemy piece ahead
      push!(legalC,(x,y,x,y-i))
      break
    else #Friendly piece ahead
      break
    end
  end

  for i = 1:(board_max-y)
    if !( (x,y+i) in keys(board) )
      push!(legalC,(x,y,x,y+i))
    elseif (!(get(board,(x,y+i),('x','x'))[2] == 'w') && team == 'w') || (!(get(board,(x,y+i),('x','x'))[2] == 'b') && team == 'b')
      push!(legalC,(x,y,x,y+i))
      break
    else
      break
    end
  end

  return legalC
end
function copperGeneral(board,x::Int64,y::Int64)
  #board_max = 9
  team = board[(x,y)][2]
  legalC = Array(Tuple{Int64,Int64,Int64,Int64},0)
  if team == 'w'
    potential=[(x,y,x,y+1),(x,y,x,y-1),(x,y,x-1,y+1),(x,y,x+1,y+1)]
    for i in eachindex(potential)
      if potential[i][3]<=board_max && potential[i][3]>0 && potential[i][4]<=board_max && potential[i][4]>0 #Makes sure move is not out of bounds
        if !(get(board,(potential[i][3],potential[i][4]),('x','x'))[2] == 'w') #Check that there's no friendly piece in the path
          push!(legalC,potential[i])
        end
      end
    end
  else #team == 'b'
      potential=[(x,y,x,y+1),(x,y,x,y-1),(x,y,x-1,y-1),(x,y,x+1,y-1)]
      for i in eachindex(potential)
        if potential[i][3]<=board_max && potential[i][3]>0 && potential[i][4]<=board_max && potential[i][4]>0 #Makes sure move is not out of bounds
          if !(get(board,(potential[i][3],potential[i][4]),('x','x'))[2] == 'b') #Check that there's no friendly piece in the path
            push!(legalC,potential[i])
          end
        end
      end
  end
  return legalC
end
function dragonKing(board,x::Int64,y::Int64)
  #board_max = 9
  legalC = Array(Tuple{Int64,Int64,Int64,Int64},0)
  team = board[(x,y)][2]

  for i = 1:(y-1)
    if !( (x,y-i) in keys(board) ) #Empty square ahead
      push!(legalC,(x,y,x,y-i))
    elseif (!(get(board,(x,y-1),('x','x'))[2] == 'w') && team == 'w') || (!(get(board,(x,y-1),('x','x'))[2] == 'b') && team == 'b') #Enemy piece ahead
      push!(legalC,(x,y,x,y-i))
      break
    else #Friendly piece ahead
      break
    end
  end

  for i = 1:(board_max-x)
    if !( (x+i,y) in keys(board) )
      push!(legalC,(x,y,x+i,y))
    elseif (!(get(board,(x,y+i),('x','x'))[2] == 'w') && team == 'w') || (!(get(board,(x,y+i),('x','x'))[2] == 'b') && team == 'b')
      push!(legalC,(x,y,x+i,y))
      break
    else
      break
    end
  end

  for i = 1:(x-1)
    if !( (x-i,y) in keys(board) )
      push!(legalC,(x,y,x-i,y))
    elseif (!(get(board,(x-i,y),('x','x'))[2] == 'w') && team == 'w') || (!(get(board,(x-i,y),('x','x'))[2] == 'b') && team == 'b')
      push!(legalC,(x,y,x-i,y))
      break
    else
      break
    end
  end

  for i = 1:(board_max-y)
    if !( (x,y+i) in keys(board) )
      push!(legalC,(x,y,x,y+i))
    elseif (!(get(board,(x,y+i),('x','x'))[2] == 'w') && team == 'w') || (!(get(board,(x,y+i),('x','x'))[2] == 'b') && team == 'b')
      push!(legalC,(x,y,x,y+i))
      break
    else
      break
    end
  end

  if team == 'w'
    potential=[(x,y,x+1,y+1),(x,y,x-1,y-1),(x,y,x+1,y-1),(x,y,x-1,y+1)]
    for i in eachindex(potential)
      if potential[i][3]<=board_max && potential[i][3]>0 && potential[i][4]<=board_max && potential[i][4]>0 #Makes sure move is not out of bounds
        if !(get(board,(potential[i][3],potential[i][4]),('x','x'))[2] == 'w') #Check that there's no friendly piece in the path
          push!(legalC,potential[i])
        end
      end
    end
  else #Team == 'b'
    potential=[(x,y,x+1,y+1),(x,y,x-1,y-1),(x,y,x+1,y-1),(x,y,x-1,y+1)]
    for i in eachindex(potential)
      if potential[i][3]<=board_max && potential[i][3]>0 && potential[i][4]<=board_max && potential[i][4]>0 #Makes sure move is not out of bounds
        if !(get(board,(potential[i][3],potential[i][4]),('x','x')) == 'b') #Check that there's no friendly piece in the path
          push!(legalC,potential[i])
        end
      end
    end
  end

  return legalC
end
function drunkElephant(board,x::Int64,y::Int64)
  #board_max = 9
  team = board[(x,y)][2]
  legalC = Array(Tuple{Int64,Int64,Int64,Int64},0)
  if team == 'w'
    potential=[(x,y,x+1,y+1),(x,y,x,y+1),(x,y,x-1,y),(x,y,x+1,y),(x,y,x-1,y-1),(x,y,x+1,y-1),(x,y,x-1,y+1)]
    for i in eachindex(potential)
      if potential[i][3]<=board_max && potential[i][3]>0 && potential[i][4]<=board_max && potential[i][4]>0 #Makes sure move is not out of bounds
        if !(get(board,(potential[i][3],potential[i][4]),('x','x'))[2] == 'w') #Check that there's no friendly piece in the path
          push!(legalC,potential[i])
        end
      end
    end
  else #Team == 'b'
    potential=[(x,y,x+1,y+1),(x,y,x,y-1),(x,y,x-1,y),(x,y,x+1,y),(x,y,x-1,y-1),(x,y,x+1,y-1),(x,y,x-1,y+1)]
    for i in eachindex(potential)
      if potential[i][3]<=board_max && potential[i][3]>0 && potential[i][4]<=board_max && potential[i][4]>0 #Makes sure move is not out of bounds
        if !(get(board,(potential[i][3],potential[i][4]),('x','x')) == 'b') #Check that there's no friendly piece in the path
          push!(legalC,potential[i])
        end
      end
    end
  end

  return legalC
end
function ferociousLeopard(board,x::Int64,y::Int64)
  #board_max = 9
  team = board[(x,y)][2]
  legalC = Array(Tuple{Int64,Int64,Int64,Int64},0)
  if team == 'w'
    potential=[(x,y,x+1,y+1),(x,y,x,y-1),(x,y,x,y+1),(x,y,x-1,y-1),(x,y,x+1,y-1),(x,y,x-1,y+1)]
    for i in eachindex(potential)
      if potential[i][3]<=board_max && potential[i][3]>0 && potential[i][4]<=board_max && potential[i][4]>0 #Makes sure move is not out of bounds
        if !(get(board,(potential[i][3],potential[i][4]),('x','x'))[2] == 'w') #Check that there's no friendly piece in the path
          push!(legalC,potential[i])
        end
      end
    end
  else #Team == 'b'
    potential=[(x,y,x+1,y+1),(x,y,x,y-1),(x,y,x,y+1),(x,y,x-1,y-1),(x,y,x+1,y-1),(x,y,x-1,y+1)]
    for i in eachindex(potential)
      if potential[i][3]<=board_max && potential[i][3]>0 && potential[i][4]<=board_max && potential[i][4]>0 #Makes sure move is not out of bounds
        if !(get(board,(potential[i][3],potential[i][4]),('x','x')) == 'b') #Check that there's no friendly piece in the path
          push!(legalC,potential[i])
        end
      end
    end
  end

  return legalC
end
function dragonHorse(board,x::Int64,y::Int64)
  #board_max = 9
  legalC = Array(Tuple{Int64,Int64,Int64,Int64},0)
  team = board[(x,y)][2]

  for i = 1:min(x-1,y-1)
    if !( (x-i,y-i) in keys(board))  #Empty square ahead
      push!(legalC,(x,y,x-i,y-i))
    elseif (!( get(board,(x-i,y-i),('x','x'))[2] == 'w' ) && team == 'w') || (!( get(board,(x-i,y-i),('x','x'))[2] == 'b' ) && team == 'b') #Enemy piece ahead
      push!(legalC,(x,y,x-i,y-i))
      break
    else #Friendly piece ahead
      break
    end
  end

  for i = 1:min(board_max-x,board_max-y)
    if !( (x+i,y+i) in keys(board) )
      push!(legalC,(x,y,x+i,y+i))
    elseif (!( get(board,(x+i,y+i),('x','x'))[2] == 'w' ) && team == 'w') || (!( get(board,(x+i,y+i),('x','x'))[2] == 'b' ) && team == 'b')
      push!(legalC,(x,y,x+i,y+i))
      break
    else
      break
    end
  end

  for i = 1:min(board_max-x,y-1)
    if !( (x+i,y-i) in keys(board) )
      push!(legalC,(x,y,x+i,y-i))
    elseif (!( get(board,(x+i,y-i),('x','x'))[2] == 'w' ) && team == 'w') || (!( get(board,(x+i,y-i),('x','x'))[2] == 'b' ) && team == 'b')
      push!(legalC,(x,y,x+i,y-i))
      break
    else
      break
    end
  end

  for i = 1:min(x-1,board_max-y)
    if !( (x-i,y+i) in keys(board) )
      push!(legalC,(x,y,x-i,y+i))
    elseif (!( get(board,(x-i,y+i),('x','x'))[2] == 'w' ) && team == 'w') || (!( get(board,(x-i,y+i),('x','x'))[2] == 'b' ) && team == 'b')
      push!(legalC,(x,y,x-i,y+i))
      break
    else
      break
    end
  end

  if team == 'w'
    potential=[(x,y,x,y-1),(x,y,x,y+1),(x,y,x-1,y),(x,y,x+1,y)]
    for i in eachindex(potential)
      if potential[i][3]<=board_max && potential[i][3]>0 && potential[i][4]<=board_max && potential[i][4]>0 #Makes sure move is not out of bounds
        if !(get(board,(potential[i][3],potential[i][4]),('x','x'))[2] == 'w') #Check that there's no friendly piece in the path
          push!(legalC,potential[i])
        end
      end
    end
  else #Team == 'b'
    potential=[(x,y,x,y-1),(x,y,x,y+1),(x,y,x-1,y),(x,y,x+1,y)]
    for i in eachindex(potential)
      if potential[i][3]<=board_max && potential[i][3]>0 && potential[i][4]<=board_max && potential[i][4]>0 #Makes sure move is not out of bounds
        if !(get(board,(potential[i][3],potential[i][4]),('x','x')) == 'b') #Check that there's no friendly piece in the path
          push!(legalC,potential[i])
        end
      end
    end
  end

  return legalC
end
function lion(board,x::Int64,y::Int64)
end
function kirin(board,x::Int64,y::Int64)
  #board_max = 9
  team = board[(x,y)][2]
  legalC = Array(Tuple{Int64,Int64,Int64,Int64},0)
  if team == 'w'
    potential=[(x,y,x+1,y+1),(x,y,x,y-2),(x,y,x,y+2),(x,y,x-2,y),(x,y,x+2,y),(x,y,x-1,y-1),(x,y,x+1,y-1),(x,y,x-1,y+1)]
    for i in eachindex(potential)
      if potential[i][3]<=board_max && potential[i][3]>0 && potential[i][4]<=board_max && potential[i][4]>0 #Makes sure move is not out of bounds
        if !(get(board,(potential[i][3],potential[i][4]),('x','x'))[2] == 'w') #Check that there's no friendly piece in the path
          push!(legalC,potential[i])
        end
      end
    end
  else #Team == 'b'
    potential=[(x,y,x+1,y+1),(x,y,x,y-2),(x,y,x,y+2),(x,y,x-2,y),(x,y,x+2,y),(x,y,x-1,y-1),(x,y,x+1,y-1),(x,y,x-1,y+1)]
    for i in eachindex(potential)
      if potential[i][3]<=board_max && potential[i][3]>0 && potential[i][4]<=board_max && potential[i][4]>0 #Makes sure move is not out of bounds
        if !(get(board,(potential[i][3],potential[i][4]),('x','x')) == 'b') #Check that there's no friendly piece in the path
          push!(legalC,potential[i])
        end
      end
    end
  end

  return legalC
end
function sideMover(board,x::Int64,y::Int64)
  #board_max = 9
  legalC = Array(Tuple{Int64,Int64,Int64,Int64},0)
  team = board[(x,y)][2]

  for i = 1:(board_max-x)
    if !( (x+i,y) in keys(board) )
      push!(legalC,(x,y,x+i,y))
    elseif (!(get(board,(x,y+i),('x','x'))[2] == 'w') && team == 'w') || (!(get(board,(x,y+i),('x','x'))[2] == 'b') && team == 'b')
      push!(legalC,(x,y,x+i,y))
      break
    else
      break
    end
  end

  for i = 1:(x-1)
    if !( (x-i,y) in keys(board) )
      push!(legalC,(x,y,x-i,y))
    elseif (!(get(board,(x-i,y),('x','x'))[2] == 'w') && team == 'w') || (!(get(board,(x-i,y),('x','x'))[2] == 'b') && team == 'b')
      push!(legalC,(x,y,x-i,y))
      break
    else
      break
    end
  end

  if team == 'w'
    potential=[(x,y,x,y-1),(x,y,x,y+1)]
    for i in eachindex(potential)
      if potential[i][3]<=board_max && potential[i][3]>0 && potential[i][4]<=board_max && potential[i][4]>0 #Makes sure move is not out of bounds
        if !(get(board,(potential[i][3],potential[i][4]),('x','x'))[2] == 'w') #Check that there's no friendly piece in the path
          push!(legalC,potential[i])
        end
      end
    end
  else #Team == 'b'
    potential=[(x,y,x,y-1),(x,y,x,y+1)]
    for i in eachindex(potential)
      if potential[i][3]<=board_max && potential[i][3]>0 && potential[i][4]<=board_max && potential[i][4]>0 #Makes sure move is not out of bounds
        if !(get(board,(potential[i][3],potential[i][4]),('x','x')) == 'b') #Check that there's no friendly piece in the path
          push!(legalC,potential[i])
        end
      end
    end
  end
  return legalC

end
function goBetween(board,x::Int64,y::Int64)
  #board_max = 9
  legalC = Array(Tuple{Int64,Int64,Int64,Int64},0)
  team = board[(x,y)][2]
  if team == 'w'
    potential=[(x,y,x,y-1),(x,y,x,y+1)]
    for i in eachindex(potential)
      if potential[i][3]<=board_max && potential[i][3]>0 && potential[i][4]<=board_max && potential[i][4]>0 #Makes sure move is not out of bounds
        if !(get(board,(potential[i][3],potential[i][4]),('x','x'))[2] == 'w') #Check that there's no friendly piece in the path
          push!(legalC,potential[i])
        end
      end
    end
  else #Team == 'b'
    potential=[(x,y,x,y-1),(x,y,x,y+1)]
    for i in eachindex(potential)
      if potential[i][3]<=board_max && potential[i][3]>0 && potential[i][4]<=board_max && potential[i][4]>0 #Makes sure move is not out of bounds
        if !(get(board,(potential[i][3],potential[i][4]),('x','x')) == 'b') #Check that there's no friendly piece in the path
          push!(legalC,potential[i])
        end
      end
    end
  end
  return legalC
end
function blindTiger(board,x::Int64,y::Int64)
  #board_max = 9
  team = board[(x,y)][2]
  legalC = Array(Tuple{Int64,Int64,Int64,Int64},0)
  if team == 'w'
    potential=[(x,y,x+1,y+1),(x,y,x,y-1),(x,y,x-1,y),(x,y,x+1,y),(x,y,x-1,y-1),(x,y,x+1,y-1),(x,y,x-1,y+1)]
    for i in eachindex(potential)
      if potential[i][3]<=board_max && potential[i][3]>0 && potential[i][4]<=board_max && potential[i][4]>0 #Makes sure move is not out of bounds
        if !(get(board,(potential[i][3],potential[i][4]),('x','x'))[2] == 'w') #Check that there's no friendly piece in the path
          push!(legalC,potential[i])
        end
      end
    end
  else #Team == 'b'
    potential=[(x,y,x+1,y+1),(x,y,x,y+1),(x,y,x-1,y),(x,y,x+1,y),(x,y,x-1,y-1),(x,y,x+1,y-1),(x,y,x-1,y+1)]
    for i in eachindex(potential)
      if potential[i][3]<=board_max && potential[i][3]>0 && potential[i][4]<=board_max && potential[i][4]>0 #Makes sure move is not out of bounds
        if !(get(board,(potential[i][3],potential[i][4]),('x','x')) == 'b') #Check that there's no friendly piece in the path
          push!(legalC,potential[i])
        end
      end
    end
  end

  return legalC
end
function queen(board,x::Int64,y::Int64)
  #board_max = 9
  legalC = Array(Tuple{Int64,Int64,Int64,Int64},0)
  team = board[(x,y)][2]

  for i = 1:min(x-1,y-1)
    if !( (x-i,y-i) in keys(board))  #Empty square ahead
      push!(legalC,(x,y,x-i,y-i))
    elseif (!( get(board,(x-i,y-i),('x','x'))[2] == 'w' ) && team == 'w') || (!( get(board,(x-i,y-i),('x','x'))[2] == 'b' ) && team == 'b') #Enemy piece ahead
      push!(legalC,(x,y,x-i,y-i))
      break
    else #Friendly piece ahead
      break
    end
  end

  for i = 1:min(board_max-x,board_max-y)
    if !( (x+i,y+i) in keys(board) )
      push!(legalC,(x,y,x+i,y+i))
    elseif (!( get(board,(x+i,y+i),('x','x'))[2] == 'w' ) && team == 'w') || (!( get(board,(x+i,y+i),('x','x'))[2] == 'b' ) && team == 'b')
      push!(legalC,(x,y,x+i,y+i))
      break
    else
      break
    end
  end

  for i = 1:min(board_max-x,y-1)
    if !( (x+i,y-i) in keys(board) )
      push!(legalC,(x,y,x+i,y-i))
    elseif (!( get(board,(x+i,y-i),('x','x'))[2] == 'w' ) && team == 'w') || (!( get(board,(x+i,y-i),('x','x'))[2] == 'b' ) && team == 'b')
      push!(legalC,(x,y,x+i,y-i))
      break
    else
      break
    end
  end

  for i = 1:min(x-1,board_max-y)
    if !( (x-i,y+i) in keys(board) )
      push!(legalC,(x,y,x-i,y+i))
    elseif (!( get(board,(x-i,y+i),('x','x'))[2] == 'w' ) && team == 'w') || (!( get(board,(x-i,y+i),('x','x'))[2] == 'b' ) && team == 'b')
      push!(legalC,(x,y,x-i,y+i))
      break
    else
      break
    end
  end

    for i = 1:(y-1)
      if !( (x,y-i) in keys(board) ) #Empty square ahead
        push!(legalC,(x,y,x,y-i))
      elseif (!(get(board,(x,y-1),('x','x'))[2] == 'w') && team == 'w') || (!(get(board,(x,y-1),('x','x'))[2] == 'b') && team == 'b') #Enemy piece ahead
        push!(legalC,(x,y,x,y-i))
        break
      else #Friendly piece ahead
        break
      end
    end

    for i = 1:(board_max-x)
      if !( (x+i,y) in keys(board) )
        push!(legalC,(x,y,x+i,y))
      elseif (!(get(board,(x,y+i),('x','x'))[2] == 'w') && team == 'w') || (!(get(board,(x,y+i),('x','x'))[2] == 'b') && team == 'b')
        push!(legalC,(x,y,x+i,y))
        break
      else
        break
      end
    end

    for i = 1:(x-1)
      if !( (x-i,y) in keys(board) )
        push!(legalC,(x,y,x-i,y))
      elseif (!(get(board,(x-i,y),('x','x'))[2] == 'w') && team == 'w') || (!(get(board,(x-i,y),('x','x'))[2] == 'b') && team == 'b')
        push!(legalC,(x,y,x-i,y))
        break
      else
        break
      end
    end

    for i = 1:(board_max-y)
      if !( (x,y+i) in keys(board) )
        push!(legalC,(x,y,x,y+i))
      elseif (!(get(board,(x,y+i),('x','x'))[2] == 'w') && team == 'w') || (!(get(board,(x,y+i),('x','x'))[2] == 'b') && team == 'b')
        push!(legalC,(x,y,x,y+i))
        break
      else
        break
      end
    end

    return legalC
end
function verticalMover(board,x::Int64,y::Int64)
  #board_max = 9
  legalC = Array(Tuple{Int64,Int64,Int64,Int64},0)
  team = board[(x,y)][2]

  for i = 1:(y-1)
    if !( (x,y-i) in keys(board) ) #Empty square ahead
      push!(legalC,(x,y,x,y-i))
    elseif (!(get(board,(x,y-1),('x','x'))[2] == 'w') && team == 'w') || (!(get(board,(x,y-1),('x','x'))[2] == 'b') && team == 'b') #Enemy piece ahead
      push!(legalC,(x,y,x,y-i))
      break
    else #Friendly piece ahead
      break
    end
  end

  for i = 1:(board_max-y)
    if !( (x,y+i) in keys(board) )
      push!(legalC,(x,y,x,y+i))
    elseif (!(get(board,(x,y+i),('x','x'))[2] == 'w') && team == 'w') || (!(get(board,(x,y+i),('x','x'))[2] == 'b') && team == 'b')
      push!(legalC,(x,y,x,y+i))
      break
    else
      break
    end
  end
  if team == 'w'
    potential=[(x,y,x-1,y),(x,y,x+1,y)]
    for i in eachindex(potential)
      if potential[i][3]<=board_max && potential[i][3]>0 && potential[i][4]<=board_max && potential[i][4]>0 #Makes sure move is not out of bounds
        if !(get(board,(potential[i][3],potential[i][4]),('x','x'))[2] == 'w') #Check that there's no friendly piece in the path
          push!(legalC,potential[i])
        end
      end
    end
  else #Team == 'b'
    potential=[(x,y,x-1,y),(x,y,x+1,y)]
    for i in eachindex(potential)
      if potential[i][3]<=board_max && potential[i][3]>0 && potential[i][4]<=board_max && potential[i][4]>0 #Makes sure move is not out of bounds
        if !(get(board,(potential[i][3],potential[i][4]),('x','x')) == 'b') #Check that there's no friendly piece in the path
          push!(legalC,potential[i])
        end
      end
    end
  end
  return legalC
end
function phoenix(board,x::Int64,y::Int64)
  #board_max = 9
  team = board[(x,y)][2]
  legalC = Array(Tuple{Int64,Int64,Int64,Int64},0)
  if team == 'w'
    potential=[(x,y,x+2,y+2),(x,y,x,y-1),(x,y,x,y+1),(x,y,x-1,y),(x,y,x+1,y),(x,y,x-2,y-2),(x,y,x+2,y-2),(x,y,x-2,y+2)]
    for i in eachindex(potential)
      if potential[i][3]<=board_max && potential[i][3]>0 && potential[i][4]<=board_max && potential[i][4]>0 #Makes sure move is not out of bounds
        if !(get(board,(potential[i][3],potential[i][4]),("x",'x'))[2] == 'w') #Check that there's no friendly piece in the path
          push!(legalC,potential[i])
        end
      end
    end
  else #Team == 'b'
    potential=[(x,y,x+2,y+2),(x,y,x,y-1),(x,y,x,y+1),(x,y,x-1,y),(x,y,x+1,y),(x,y,x-2,y-2),(x,y,x+2,y-2),(x,y,x-2,y+2)]
    for i in eachindex(potential)
      if potential[i][3]<=board_max && potential[i][3]>0 && potential[i][4]<=board_max && potential[i][4]>0 #Makes sure move is not out of bounds
        if !(get(board,(potential[i][3],potential[i][4]),('x','x')) == 'b') #Check that there's no friendly piece in the path
          push!(legalC,potential[i])
        end
      end
    end
  end

  return legalC
end

function legalMoves(board,x,y)
  target = board[(x,y)]
  unit = target[1]
#  println("$unit at $x , $y")
  funDict = Dict("bishop" => bishop, "gold general" => goldGeneral, "king" => king, "lance" => lance, "knight" => knight, "pawn" => pawn, "rook" => rook, "silver general" => silverGeneral,
  "phoenix" => phoenix, "vertical mover" => verticalMover, "go-between" => goBetween, "queen" => queen, "lion" => lion, "dragon king" => dragonKing, "dragon horse"=> dragonHorse, "side mover" => sideMover,
  "kirin" => kirin, "blind tiger"=> blindTiger, "reverse chariot" => reverseChariot, "drunk elephant" => drunkElephant, "ferocious leopard" => ferociousLeopard, "blind tiger" => blindTiger)
  funDict[unit](board,x,y)
end

function legalMovesPlayer(board,team,shogiType)
  board_max_dict = Dict("standard" => 9, "chu" => 12)
  global board_max = 4 #board_max_dict[shogiType]
  legalC = Array(Tuple{Int64,Int64,Int64,Int64},0) #Initialize an array of tuples for coordinates
   currentTeam = team #Define currentTeam (either 'b' or 'w)
   #board_max = 9
  #=for x in 1:board_max
    for y in 1:board_max
      if board[(x,y)][2] == currentTeam
        append!(legalC,legalMoves(board,x,y))
      end
    end
  end=#
  for (x,y) in keys(board)
    if board[(x,y)][2] == currentTeam
      append!(legalC,legalMoves(board,x,y))
    end
  end
  return legalC
end
export legalMovesPlayer
end #End module "AI"


function get_rop_sections()
    return ["MODIFICATION CODE", "BUS VOLTAGE", "ADJUSTABLE BUS SHUNTS",
            "BUS LOADS", "ADJUSTABLE BUS LOAD", "GENERATOR DISPATCH",
            "ACTIVE POWER DISPATCH", "GENERATOR RESERVE",
            "GENERATION REACTIVE CAP", "ADJUSTABLE BRANCH",
            "PIECEWISE LINEAR COST", "PIECEWISE QUADRATIC COST",
            "POLYNOMIAL EXPONENTIAL COST", "PERIOD RESERVES",
            "BRANCH FLOWS", "INTERFACE FLOWS", "LINEAR CONSTRAINT DEP"]
end

function get_rop_section_info()
    generator_dispatch = [["BUS", 1, Int], ["GENID", 2, String],
                          ["DSPTBL", 4, Int]]
    active_power_dispatch = [["TBL", 1, Int], ["CTBL", 7, Int]]
    piecewise_linear_cost = [["LTBL", 1, Int], ["NPARIS", 3, Int]]

    section_info = Dict{String,Array}(
        "MODIFICATION CODE" => [],
        "BUS VOLTAGE" => [],
        "ADJUSTABLE BUS SHUNTS" => [],
        "BUS LOADS" => [],
        "ADJUSTABLE BUS LOAD" => [],
        "GENERATOR DISPATCH" => generator_dispatch,
        "ACTIVE POWER DISPATCH" => active_power_dispatch,
        "GENERATOR RESERVE" => [],
        "GENERATION REACTIVE CAP" => [],
        "ADJUSTABLE BRANCH" => [],
        "PIECEWISE LINEAR COST" => piecewise_linear_cost,
        "PIECEWISE QUADRATIC COST" => [],
        "POLYNOMIAL EXPONENTIAL COST" => [],
        "PERIOD RESERVES" => [],
        "BRANCH FLOWS" => [],
        "INTERFACE FLOWS" => [],
        "LINEAR CONSTRAINT DEP" => [])

    return section_info
end

function idx_rop()
    ROP_BUS = 1
    ROP_ID = 2
    ROP_NPAIRS = 3
    ROP_PAIRSTART = 4

    return ROP_BUS, ROP_ID, ROP_NPAIRS, ROP_PAIRSTART
end

function rop_join_tables(ropdata::Dict{String,Array})
    gendspt = ropdata["GENERATOR DISPATCH"]
    acpdspt = ropdata["ACTIVE POWER DISPATCH"]
    pwlcost = ropdata["PIECEWISE LINEAR COST"]

    ndata = size(gendspt, 1)
    data = Array{Any}(undef, ndata, 4)

    for i=1:ndata
        dsptbl = gendspt[i, 3]

        if dsptbl <= ndata && dsptbl == acpdspt[dsptbl, 1]
            acp_row = dsptbl
        else
            acp_row = findfirst(dsptbl .== acpdspt[:, 1])
        end

        if acpdspt[acp_row, 2] == pwlcost[acp_row, 1]
            pwl_row = acp_row
        else
            pwl_row = findfirst(acpdspt[acp_row, 2] .== pwlcost[:, 1])
        end

        data[i, 1] = gendspt[i, 1]
        data[i, 2] = gendspt[i, 2]
        data[i, 3] = pwlcost[pwl_row, 2]
        data[i, 4] = pwlcost[pwl_row, 3]
    end

    return data
end

function rop_pwl(lines::Array{String}, pos::Int, info::Array;
                 delim=",")
    NPAIRS_LOC = 2
    start = last = pos
    ndata = npairs = tot_npairs = 0

    line = strip(lines[last])
    while line[1] ∉ [ '0', 'Q' ]
        fields = split(line, delim)
        npairs = parse(info[2][3], fields[info[2][2]])
        tot_npairs += npairs
        ndata += 1
        last += 1 + npairs
        line = strip(lines[last])
    end

    data = Array{Any}(undef, ndata, length(info)+1)
    pairs = Array{Float64}(undef, tot_npairs, 2)

    offset = start
    npairs = tot_npairs = 0
    for i=1:ndata
        fields = split(lines[offset], delim)

        for j=1:length(info)
            data[i,j] = parse(info[j][3], fields[info[j][2]])
        end

        offset += 1
        npairs = data[i,NPAIRS_LOC]
        data[i,end] = tot_npairs
        for j=1:npairs
            tot_npairs += 1
            fields = split(lines[offset], delim)
            pairs[tot_npairs,1] = parse(Float64, fields[1])
            pairs[tot_npairs,2] = parse(Float64, fields[2])
            offset += 1
        end
    end

    pos = last + 1
    return pos, data, pairs
end

function parse_rop(filename::AbstractString; delim=",")
    f = open(filename, "r")
    lines = readlines(f)
    close(f)

    sections = get_rop_sections()
    section_info = get_rop_section_info()
    pos = 1

    ropdata = Dict{String,Array}()
    for s in sections
        info = section_info[s]

        if isempty(info)
            pos = parse_skipsection(lines, pos) + 1
            continue
        end

        if s == "PIECEWISE LINEAR COST"
            pos, data, pairs = rop_pwl(lines, pos, info; delim=delim)
            ropdata["PAIRS"] = pairs
        else
            pos, data = parse_data(lines, pos, info; delim=delim)
        end

        ropdata[s] = data
    end

    # Perform join operation
    if haskey(ropdata, "PIECEWISE LINEAR COST")
        data = rop_join_tables(ropdata)
        ropdata["JOINED GENERATOR DISPATCH"] = data
    end

    return ropdata
end


@everywhere using SeisDownload
using Dates

#==================================================#
# Input Parameters
MAX_MEM_PER_CPU = 2.0 # [GB] maximum allocated memory for one cpu
DownloadType = "Noise" # Choise of "Noise" or "Earthquake"

network     = ["BP"]
station     = ["LCCB", "MMNB"]
#station = ["CCRB","EADB","FROB","GHIB","JCNB","JCSB","LCCB","MMNB","SCYB","SMNB","VARB","VCAB"]

location    = [""]
channel     = ["BP1"]
datacenter  = "FDSN" #Data center
src         = "NCEDC"

# Time info for Noise case
starttime   = DateTime(2004,9,25,0,0,0)
endtime     = DateTime(2004,9,25,12,0,0)

IsLocationBox = false
method  = "FDSN" # Method to download data.
datasource = "NCEDC" # currently, only one src can be specified.

DL_time_unit = 3600 * 2 #3600 * 24 # Download tiem unit [s] more than one day is better to avoid artifacts of response removal

IsResponseRemove = true #whether instrumental response is removed or not
pre_filt    = (0.001, 0.002, 10.0, 20.0) #prefilter tuple used obspy remove_response: taper between f1 and f2, f3 and f4 with obspy

fodir       = "./dataset"
foname      = "BPnetwork" # data is saved at ./dataset/$foname.jld2
#==================================================#

# store metadata in Dictionary
# This can be customized by users

stationlist       = String[]
stationmethod    = String[]
stationsrc        = String[]
for i=1:length(network)
    for j=1:length(station)
        for k=1:length(location)
            for l=1:length(channel)
                stationname = join([network[i], station[j], location[k], channel[l]], ".")
                push!(stationlist, stationname)

                #Here should be improved for multiple seismic network; we have to make
                #proper conbination of request station and data server.
                push!(stationmethod, method)
                push!(stationsrc, datasource)
            end
        end
    end
end

stationinfo = Dict(["stationlist" => stationlist, "stationmethod" => stationmethod, "stationsrc" => stationsrc])

mkpath(fodir)
fopath=joinpath(fodir, foname*".jld2")

#if lat-log box or not
IsLocationBox ? reg=locationbox : reg=[]
IsResponseRemove ? pre_filt = pre_filt : pre_filt = []

InputDictionary = Dict([
        "DownloadType"=> DownloadType,
        "stationinfo" => stationinfo,
        "starttime"   => starttime,
        "endtime"     => endtime,
        "DL_time_unit"=> DL_time_unit,
        "IsLocationBox"   => IsLocationBox,
        "reg"             => reg,
        "IsResponseRemove"=> IsResponseRemove,
        "pre_filt"        => pre_filt,
        "fopath"          => fopath
    ])

# mass request with input Dictionary
seisdownload(InputDictionary, MAX_MEM_PER_CPU=float(MAX_MEM_PER_CPU))


############################################################
# judiPDEextended ##########################################
############################################################

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

export judiPDEextended, judiPDEextendedException, subsample

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

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

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

mutable struct judiPDEextendedException <: Exception
    msg :: String
end

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

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

    # Determine dimensions
    m = n_samples(recGeometry, info)
    n = info.n * info.nsrc

    srcnum = 1:info.nsrc

    return F = judiPDEextended{Float32,Float32}("Proj*F*Proj'", m, n, info, model, wavelet, recGeometry, options,
        w -> extended_source_modeling(model, wavelet, recGeometry, nothing, process_input_data(w, model,info),
                                      nothing, srcnum, 'F', 1, options),
        rec -> extended_source_modeling(model, wavelet, recGeometry, process_input_data(rec,recGeometry,info),
                                        nothing, nothing, srcnum, 'F', -1, options),
        )
end

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

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

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

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

############################################################
## overloaded Base *(...judiPDEextended...)

# *(judiPDEextended,judiWeights)
function *(A::judiPDEextended{ADDT,ARDT},v::judiWeights{vDT}) where {ADDT,ARDT,vDT}
    A.n == size(v,1) || throw(judiPDEextendedException("Shape mismatch: A:$(size(A)), v: $(size(v))"))
    jo_check_type_match(ADDT,vDT,join(["DDT for *(judiPDEextended,judiWeights):",A.name,typeof(A),vDT]," / "))
    V = A.fop(v)
    jo_check_type_match(ARDT,eltype(V),join(["RDT from *(judiPDEextended,judiWeights):",A.name,typeof(A),eltype(V)]," / "))
    return V
end

# *(judiPDEextended,judiVector)
function *(A::judiPDEextended{ADDT,ARDT},v::judiVector{vDT, AT}) where {ADDT,ARDT,vDT, AT}
    A.n == size(v,1) || throw(judiPDEextendedException("Shape mismatch: A:$(size(A)), v: $(size(v))"))
    jo_check_type_match(ADDT,vDT,join(["DDT for *(judiPDEextended,judiWeights):",A.name,typeof(A),vDT]," / "))
    V = A.fop(v)
    jo_check_type_match(ARDT,eltype(V),join(["RDT from *(judiPDEextended,judiWeights):",A.name,typeof(A),eltype(V)]," / "))
    return V
end

# *(judiPDEextended,vec)
function *(A::judiPDEextended{ADDT,ARDT},v::AbstractVector{vDT}) where {ADDT,ARDT,vDT}
    A.n == size(v,1) || throw(judiPDEextendedException("Shape mismatch: A:$(size(A)), v: $(size(v))"))
    jo_check_type_match(ADDT,vDT,join(["DDT for *(judiPDEextended,AbstractVector):",A.name,typeof(A),vDT]," / "))
    V = A.fop(v)
    jo_check_type_match(ARDT, eltype(V),join(["RDT from *(judiPDEextended,AbstractVector):",A.name,typeof(A),eltype(V)]," / "))
    return V
end

*(A::judiPDEextended{ADDT,ARDT}, v::AbstractMatrix{vDT}) where {ADDT,ARDT,vDT} = *(A, vec(v))

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

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

# Subsample Modeling operator
function subsample(F::judiPDEextended{ADDT,ARDT}, srcnum) where {ADDT,ARDT}
    recGeometry = subsample(F.recGeometry,srcnum)
    info = Info(F.info.n, length(srcnum), F.info.nt[srcnum])
    return judiPDEextended(info, F.model, F.wavelet[srcnum], recGeometry; options=F.options, DDT=ADDT, RDT=ARDT)
end

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


using WAV

# create a CMD command that twill extract information from a WAV file given as a full path string.
function command(file; TIME_STEP = 0.01)
    # read wav file
    x,fs = WAV.wavread(file)
    x = x / maximum(x)

    # save wav file with WAVE_FORMAT_PCM/16bits
    WAV.wavwrite(x, raw"extraFiles\Temp.wav",
                 Fs=fs,
                 compression=WAVE_FORMAT_PCM,
                 nbits = 16) # this doesn't work with other files than test.wav in WavFiles

    # .exe praat file
    path_PraatExecutable = raw".\extraFiles\praatcon.exe"
    # praat script filemyF
    path_PraatScript = raw".\extraFiles\AutoRhythmPitch.praat"

    temp = raw" Temp.wav "
    features = raw"Temp.features "

    cmd = path_PraatExecutable * " " * path_PraatScript * temp  * features * raw".\ " * string(TIME_STEP)

    return cmd
end


const param_details = Array[[:librarygenome_num_genes, :num_genes, Int64, :both, :screen],
    [:screen_num_bottlenecks, :num_bottlenecks, Int64, :growth, :screen],
    [:screen_type, :screen_type, AbstractString, :both, :screen],
    [:screenrepresentation_selection, :bottleneck_representation, Int64, :both, :screen],
    [:libraryguides_frac_high_quality, :frac_hq, Float64, :both, :library],
    [:screenrepresentation_transfection, :representation, Int64, :both, :screen],
    [:screen_std_noise, :σ, Float64, :facs, :screen],
    [:librarygenome_num_guides_per_gene, :coverage, Int64, :both, :screen],
    [:librarygenome_frac_decreasing_genes, :frac_dec_genes, Float64, :both, :library],
    [:libraryguides_crispr_type, :crisprtype, AbstractString, :both, :library],
    [:libraryguides_mean_high_quality_kd, :mean_kd, Float64, :both, :library],
    [:screen_bin_size, :bin_info, Float64, :facs, :screen],
    [:librarygenome_frac_increasing_genes, :frac_inc_genes, Float64, :both, :library],
    [:screenrepresentation_sequencing, :seq_depth, Int64, :both, :screen],
    [:screen_num_runs, :num_runs, Int64, :both, :library]]

# TODO: This is shitty. A reworking of this might be nice at some point
function Base.parse(data::Dict{Any, Any})

    info("Parsing config")

    input_dict = unravel(data)

    param_table = DataFrame(permutedims(hcat(param_details...), [2, 1]))
    names!(param_table, [:yaml_name, :sim_name, :type, :relevance, :usage])

    input_mat = [Any[key, value] for (key, value) in input_dict]
    input_table = DataFrame(permutedims(hcat(input_mat...), [2, 1]))
    names!(input_table, [:yaml_name, :input])

    combined = join(param_table, input_table, on=:yaml_name)

    # get screen type
    screentype = Symbol(lowercase(combined[combined[:yaml_name] .== :screen_type, :input][1]))

    # get crispr type
    crisprtype = Symbol(lowercase(combined[combined[:yaml_name] .== :libraryguides_crispr_type, :input][1]))

    screen = screentype == :facs ? FacsScreen() : GrowthScreen()

    screen_fields = combined[(combined[:usage] .== :screen) .& BitArray(map(x->x in [screentype, :both], combined[:relevance])), :]
    deleterows!(screen_fields, find(screen_fields[:yaml_name] .== :screen_type)[1])

    lib_vals = Dict()
    curr_field, curr_type = :none, Int64

    try
        for row in eachrow(screen_fields)
            curr_field, curr_type = row[:yaml_name], row[:type]
            if row[:sim_name] == :bin_info
                p::Float64 = row[:input]
                input_val = Dict{Symbol, Tuple{Float64, Float64}}(:bin1 => (0, p), :bin2 => (1-p, 1))
            else
                input_val = convert(curr_type, row[:input])
            end
            setfield!(screen, row[:sim_name], input_val)
        end

        for row in eachrow(combined[combined[:usage] .== :library, :])
            curr_field, curr_type = row[:yaml_name], row[:type]
            (row[:sim_name] == :crisprtype) && continue
            lib_vals[row[:sim_name]] = row[:input]::curr_type
        end
    catch e
        if isa(e, TypeError) || isa(e, InexactError)
            println("ERROR: $curr_field must have a type of $curr_type")
            exit(1)
        end
        rethrow(e)
    end

    if crisprtype == :crispri
        cas9_behavior = CRISPRi()
        knockdown_dist = Dict{Symbol, Tuple{Float64, Sampleable}}(
            :high => (lib_vals[:frac_hq], TruncatedNormal(lib_vals[:mean_kd], 0.1, 0, 1)),
            :low => (1-lib_vals[:frac_hq], TruncatedNormal(0.05, 0.07, 0, 1))
        )
    elseif crisprtype == :crisprn
        cas9_behavior = CRISPRn()
        knockdown_dist = Dict{Symbol, Tuple{Float64, Sampleable}}(
            :high => (lib_vals[:frac_hq], Delta(1.0)),
            :low => (1-lib_vals[:frac_hq], TruncatedNormal(0.05, 0.07, 0, 1))
        )
    else
        error("CRISPR type must be either CRISPRi or CRISPRn")
    end

    frac_inc_genes, frac_dec_genes = lib_vals[:frac_inc_genes], lib_vals[:frac_dec_genes]
    max_phenotype_dists = Dict{Symbol, Tuple{Float64, Sampleable}}(
        :inactive => (1-frac_inc_genes-frac_dec_genes-0.2, Delta(0.0)),
        :negcontrol => (0.2, Delta(0.0)),
        :increasing => (frac_inc_genes, TruncatedNormal(0.1, 0.1, 0.025, 1)),
        :decreasing => (frac_dec_genes, TruncatedNormal(-0.55, 0.2, -1, -0.1))
    )
    lib = Library(max_phenotype_dists, knockdown_dist, cas9_behavior)

    (screen, lib, lib_vals[:num_runs])
end

"""
unravel(data::Dict)

Hacky code that unravels the dictionary returned from the YAML
parser and flattens it.
"""
function unravel(data::Dict)
    new_data = Dict{Symbol, Any}()
    for (k1, v1) in data
        if typeof(v1) <: Dict
            for (k2, v2) in unravel(v1)
                new_data[Symbol(k1, k2)] = v2
            end
        elseif typeof(v1) <: Array && typeof(v1[1]) <: Dict
            for item in v1
                for (k2, v2) in unravel(item)
                    new_data[Symbol(k1, k2)] = v2
                end
            end
        else
            new_data[Symbol("_", replace(k1, "-", "_"))] = v1
        end
    end
    new_data
end


##### Get prognostic variable list

prognostic_vars(::TotalEnergyModel) = (Energy(),)
prognostic_vars(::θModel) = (ρθ_liq_ice(),)

prognostic_vars(::DryModel) = ()
prognostic_vars(::EquilMoist) = (TotalMoisture(),)
prognostic_vars(::NonEquilMoist) =
    (TotalMoisture(), LiquidMoisture(), IceMoisture())

prognostic_vars(::NoPrecipitation) = ()
prognostic_vars(::RainModel) = (Rain(),)
prognostic_vars(::RainSnowModel) = (Rain(), Snow())

prognostic_vars(::NoTracers) = ()
prognostic_vars(::NTracers{N}) where {N} = (Tracers{N}(),)

prognostic_vars(atmos::AtmosModel) = prognostic_vars(atmos.physics)

prognostic_vars(m::AtmosPhysics) = (
    Mass(),
    Momentum(),
    prognostic_vars(energy_model(m))...,
    prognostic_vars(moisture_model(m))...,
    prognostic_vars(precipitation_model(m))...,
    prognostic_vars(tracer_model(m))...,
    prognostic_vars(turbconv_model(m))...,
)

get_prog_state(state, ::Mass) = (state, :ρ)
get_prog_state(state, ::Momentum) = (state, :ρu)
get_prog_state(state, ::Energy) = (state.energy, :ρe)
get_prog_state(state, ::ρθ_liq_ice) = (state.energy, :ρθ_liq_ice)
get_prog_state(state, ::TotalMoisture) = (state.moisture, :ρq_tot)
get_prog_state(state, ::LiquidMoisture) = (state.moisture, :ρq_liq)
get_prog_state(state, ::IceMoisture) = (state.moisture, :ρq_ice)
get_prog_state(state, ::Rain) = (state.precipitation, :ρq_rai)
get_prog_state(state, ::Snow) = (state.precipitation, :ρq_sno)
get_prog_state(state, ::Tracers{N}) where {N} = (state.tracers, :ρχ)

get_specific_state(state, ::Mass) = (state, :ρ)
get_specific_state(state, ::Momentum) = (state, :u)
get_specific_state(state, ::Energy) = (state.energy, :e)
get_specific_state(state, ::ρθ_liq_ice) = (state.energy, :θ_liq_ice)
get_specific_state(state, ::TotalMoisture) = (state.moisture, :q_tot)
get_specific_state(state, ::LiquidMoisture) = (state.moisture, :q_liq)
get_specific_state(state, ::IceMoisture) = (state.moisture, :q_ice)
get_specific_state(state, ::Rain) = (state.precipitation, :q_rai)
get_specific_state(state, ::Snow) = (state.precipitation, :q_sno)
get_specific_state(state, ::Tracers{N}) where {N} = (state.tracers, :χ)

prognostic_vars(m::AtmosLinearModel) = (Mass(), Momentum(), Energy())


module CUDNN

using CUDA

include("../libcudnn-5/libcudnn.jl")
include("../libcudnn-5/libcudnn_types.jl")

@windows? (
  begin
    const libcudnn = Libdl.find_library(["cudnn64_5"])
  end : begin
    const libcudnn = Libdl.find_library(["libcudnn"])
  end)
isempty(libcudnn) && throw("CUDNN library cannot be found.")

function checkstatus(status)
  if status != CUDNN_STATUS_SUCCESS
    Base.show_backtrace(STDOUT, backtrace())
    throw(bytestring(cudnnGetErrorString(status)))
  end
end

datatype(::Type{Float32}) = CUDNN_DATA_FLOAT
datatype(::Type{Float64}) = CUDNN_DATA_DOUBLE
datatype(::Type{Float16}) = CUDNN_DATA_HALF

include("handle.jl")
include("tensor.jl")
include("activation.jl")
include("convolution.jl")
include("filter.jl")
include("softmax.jl")

end