using Test
using YaoCompiler
using YaoLocations
using LinearAlgebra
using YaoArrayRegister
using YaoCompiler.Intrinsics

struct JLEmulationTarget <: NativeJuliaTarget end

for G in [:X, :Y, :Z, :H, :S, :T]
    @eval function Intrinsics.apply(r::ArrayReg, ::Intrinsics.$(Symbol(G, :Gate)), locs::Locations)
        instruct!(r, Val($(QuoteNode(G))), Tuple(locs))
        return
    end

    @eval function Intrinsics.apply(
        r::ArrayReg,
        ::Intrinsics.$(Symbol(G, :Gate)),
        locs::Locations,
        ctrl::CtrlLocations,
    )
        ctrl_locs = Tuple(ctrl.storage)
        ctrl_configs = ntuple(length(ctrl)) do k
            Int(ctrl.flags[k])
        end
        instruct!(r, Val($(QuoteNode(G))), Tuple(locs), ctrl_locs, ctrl_configs)
        return
    end
end

for axis in [:x, :y, :z]
    @eval function Intrinsics.apply(r::ArrayReg, op::Intrinsics.$(Symbol(:R, axis)), locs::Locations)
        instruct!(r, Val($(QuoteNode(Symbol(:R, axis)))), Tuple(locs), op.θ)
        return
    end

    @eval function Intrinsics.apply(
        r::ArrayReg,
        op::Intrinsics.$(Symbol(:R, axis)),
        locs::Locations,
        ctrl::CtrlLocations,
    )
        ctrl_locs = Tuple(ctrl.storage)
        ctrl_configs = ntuple(length(ctrl)) do k
            Int(ctrl.flags[k])
        end
        instruct!(r, Val($(QuoteNode(Symbol(:R, axis)))), Tuple(locs), ctrl_locs, ctrl_configs, op.θ)
        return
    end
end

function Intrinsics.apply(r::ArrayReg, op::Intrinsics.shift, locs::Locations)
    instruct!(r, Diagonal(ComplexF64[1.0, exp(im * op.θ)]), Tuple(locs))
end

@noinline function Intrinsics.measure(r::ArrayReg, locs::Locations)
    result = YaoArrayRegister.measure!(r, Tuple(locs))
    return MeasureResult{typeof(result)}(result)
end

@device function test_intrinsic(theta, phi)
    # syntax sugar
    1 => X
    @gate 2 => Z
    @ctrl 1 4 => Rx(theta)
    @ctrl 2 4 => Ry(phi)
    return
end

@device function test_location_map(theta)
    3:6 => test_intrinsic(theta, 2.5)
    @ctrl 1 3:6 => test_intrinsic(theta, 2.1)
    return
end

r = rand_state(10)
op = test_location_map(1.0)
f = YaoCompiler.compile(JLEmulationTarget(), Intrinsics.apply, Tuple{typeof(r),typeof(op)})

function location_map(r, theta)
    instruct!(r, Val(:X), (3,))
    instruct!(r, Val(:Z), (4,))
    instruct!(r, Val(:Rx), (6,), (3,), (1,), theta)
    instruct!(r, Val(:Ry), (6,), (4,), (1,), 2.5)

    instruct!(r, Val(:X), (3,), (1,), (1,))
    instruct!(r, Val(:Z), (4,), (1,), (1,))
    instruct!(r, Val(:Rx), (6,), (1, 3), (1, 1), theta)
    instruct!(r, Val(:Ry), (6,), (1, 4), (1, 1), 2.1)
    return
end

r1, r2 = copy(r), copy(r)
f(r1, op)
location_map(r2, 1.0)
@test r1 ≈ r2


using st16SseqJuliaTools
using ArgParse2
using Statistics
using DataStructures
using BioSequences
using BioAlignments

function julia_main()::Cint
    parser = ArgumentParser(prog = "",
                            description = "Filter out contigs based on length median",
                            epilog = "")

    add_argument!(parser, "-i", "--infasta", type = String, help = "Input fasta")
    add_argument!(parser, "-o", "--outfasta", type = String, help = "Output fasta")
    add_argument!(parser, "-e", "--length-cutoff", type = Float64, help = "Fraction from maximumlength - shorter contigs will be discarded", default=0.6)
    add_argument!(parser, "-d", "--identity-cutoff", type = Float64, help = "Aligned fraction identity cut off for containment", default=0.97)
    add_argument!(parser, "-v", "--coverage-cutoff", type = Float64, help = "Aligned part length fraction in the shorter sequence", default=0.6)
    add_argument!(parser, "-l", "--length-3endcut", type = Int64, help = "How many 3end bases should be removed before merging", default=20)
    add_argument!(parser,
    "--filter-length",
    action = "store_true",
    default = false,
    help = "Turn on length filtering based on maximumlength")
    add_argument!(parser,
    "--remove-contained",
    action = "store_true",
    default = false,
    help = "Turn on removal of contained sequences")

    args = parse_args(parser)
    seqs = st16SseqJuliaTools.fasta_to_dict(args.infasta)
    lens = map(x -> length(x),collect(values(seqs)))
    llimit = args.length_cutoff*maximum(lens)
    seqs = sort(seqs, by = x -> length(seqs[x]),rev = true)
    #remove some of 3end
    tmp = OrderedDict{String,BioSequences.LongSequence{BioSequences.DNAAlphabet{4}}}()
    for k in keys(seqs)
        tmp[k] = seqs[k][1:end-args.length_3endcut]
    end
    seqs = tmp

    if args.filter_length
        inict = length(seqs)
        seqs = filter(x -> length(x[2]) >= llimit, seqs)
        gotct = length(seqs)
        dct = inict - gotct
        println(stderr,"Removed $dct sequences using length cutoff")
    end
    #len_filtered = filter(x -> len_sorted[x] >= llimit, len_sorted)
    #st16SseqJuliaTools.dict_to_fasta(len_sorted,args.outfasta)
    ks = collect(keys(seqs))
    len_ks = length(ks)
    out = OrderedDict{String,BioSequences.LongSequence{BioSequences.DNAAlphabet{4}}}()
    out[ks[1]] = seqs[ks[1]] #longest sequence is always kept
    contained = fill(false,len_ks)
    if args.remove_contained
        for i in 1:len_ks
            for j in i:len_ks
                if i != j
                    identityf,coveragef =  check_pair(seqs[ks[i]], seqs[ks[j]])
                    if round(identityf,digits=2) >= args.identity_cutoff && round(coveragef,digits=2) >= args.coverage_cutoff
                        contained[j] = true
                        println("aaaa")
                    end
                end
            end
        end
        contained_nr = count(contained)
        println(stderr,"Removed $contained_nr due to containements")
        for i in 1:len_ks
            k = ks[i]
            cnted = contained[i]
            if !cnted
                out[k]=seqs[k]
            end
        end
        seqs = out
    end
    st16SseqJuliaTools.dict_to_fasta(seqs,args.outfasta)
   return 0

end

julia_main()


using BioSymbols
using BioSequences
using ArgParse
using Glob

using Rifraf

function parse_commandline()
    s = ArgParseSettings()
    @add_arg_table s begin
        "--multi-reference"
        help = "each sequence is followed by its reference"
        action = :store_true

        "--log-p"
        help = "log error probability"
        arg_type = Float64
        default = -1.0

        "--bandwidth"
        help = "alignment bandwidth. If < 0, choose dynamically."
        arg_type = Int
        default = -1

        "--errors"
        help = "comma-seperated reference error ratios - mm, codon ins, codon del"
        arg_type = String
        default = "10,0.00001,0.00001,1,1"

        "--verbose", "-v"
        help = "print progress"
        arg_type = Int
        default = 0

        # required arguments
        "input"
        help = "input fasta file, with sequence/reference alternating pairs"
        required = true

        "output"
        help = "output fasta file of corrected sequences"
        required = true
    end
    return parse_args(s)
end

function main()
    args = parse_commandline()

    score_args = map(x -> parse(Float64, x), split(args["errors"], ","))
    scores = Scores(ErrorModel(score_args...))

    input = args["input"]
    input_sequences = Rifraf.read_fasta_records(input)

    multi = args["multi-reference"]
    sequences = multi ? input_sequences[1:2:end-1] : input_sequences[2:end]
    references = multi ? input_sequences[2:2:end] : repeated(input_sequences[1])

    stream = open(FASTAWriter, args["output"])
    for (seq, ref) in zip(sequences, references)
        result = Rifraf.correct_shifts(DNASeq(seq.seq), DNASeq(ref.seq);
                                       log_p=args["log-p"],
                                       bandwidth=args["bandwidth"],
                                       scores=scores)
        record = Seq.FASTASeqRecord(seq.name, result)
        write(stream, record)
    end
    close(stream)
end

main()


using Oceananigans.Architectures

using Oceananigans.Grids: topology, validate_tupled_argument

struct MultiCPU{G, R, I, ρ, C, γ} <: AbstractCPUArchitecture
    distributed_grid :: G
          local_rank :: R
         local_index :: I
               ranks :: ρ
        connectivity :: C
        communicator :: γ
end

struct MultiGPU{G, R, I, ρ, C, γ} <: AbstractGPUArchitecture
    distributed_grid :: G
          local_rank :: R
         local_index :: I
               ranks :: ρ
        connectivity :: C
        communicator :: γ
end

const AbstractMultiArchitecture = Union{MultiCPU, MultiGPU}

child_architecture(::MultiCPU) = CPU()
child_architecture(::CPU) = CPU()

child_architecture(::MultiGPU) = GPU()
child_architecture(::GPU) = GPU()

#####
##### Converting between index and MPI rank taking k as the fast index
#####

index2rank(i, j, k, Rx, Ry, Rz) = (i-1)*Ry*Rz + (j-1)*Rz + (k-1)

function rank2index(r, Rx, Ry, Rz)
    i = div(r, Ry*Rz)
    r -= i*Ry*Rz
    j = div(r, Rz)
    k = mod(r, Rz)
    return i+1, j+1, k+1  # 1-based Julia
end

#####
##### Rank connectivity graph
#####

struct RankConnectivity{E, W, N, S, T, B}
      east :: E
      west :: W
     north :: N
     south :: S
       top :: T
    bottom :: B
end

RankConnectivity(; east, west, north, south, top, bottom) =
    RankConnectivity(east, west, north, south, top, bottom)

function increment_index(i, R, topo)
    R == 1 && return nothing
    if i+1 > R
        if topo == Periodic
            return 1
        else
            return nothing
        end
    else
        return i+1
    end
end

function decrement_index(i, R, topo)
    R == 1 && return nothing
    if i-1 < 1
        if topo == Periodic
            return R
        else
            return nothing
        end
    else
        return i-1
    end
end

function RankConnectivity(model_index, ranks, topology)
    i, j, k = model_index
    Rx, Ry, Rz = ranks
    TX, TY, TZ = topology

    i_east  = increment_index(i, Rx, TX)
    i_west  = decrement_index(i, Rx, TX)
    j_north = increment_index(j, Ry, TY)
    j_south = decrement_index(j, Ry, TY)
    k_top   = increment_index(k, Rz, TZ)
    k_bot   = decrement_index(k, Rz, TZ)

    r_east  = isnothing(i_east)  ? nothing : index2rank(i_east, j, k, Rx, Ry, Rz)
    r_west  = isnothing(i_west)  ? nothing : index2rank(i_west, j, k, Rx, Ry, Rz)
    r_north = isnothing(j_north) ? nothing : index2rank(i, j_north, k, Rx, Ry, Rz)
    r_south = isnothing(j_south) ? nothing : index2rank(i, j_south, k, Rx, Ry, Rz)
    r_top   = isnothing(k_top)   ? nothing : index2rank(i, j, k_top, Rx, Ry, Rz)
    r_bot   = isnothing(k_bot)   ? nothing : index2rank(i, j, k_bot, Rx, Ry, Rz)

    return RankConnectivity(east=r_east, west=r_west, north=r_north,
                            south=r_south, top=r_top, bottom=r_bot)
end

#####
##### Constructors
#####

function MultiArchitecture(MultiArch, grid, ranks, communicator)
    MPI.Initialized() || error("Must call MPI.Init() before constructing a MultiCPU.")

    validate_tupled_argument(ranks, Int, "ranks")

    Rx, Ry, Rz = ranks
    total_ranks = Rx*Ry*Rz

    mpi_ranks = MPI.Comm_size(communicator)
    local_rank   = MPI.Comm_rank(communicator)

    i, j, k = local_index = rank2index(local_rank, Rx, Ry, Rz)

    if total_ranks != mpi_ranks
        throw(ArgumentError("ranks=($Rx, $Ry, $Rz) [$total_ranks total] inconsistent " *
                            "with number of MPI ranks: $mpi_ranks."))
    end

    local_connectivity = RankConnectivity(local_index, ranks, topology(grid))

    return MultiArch(grid, local_rank, local_index, ranks, local_connectivity, communicator)
end

MultiCPU(; grid, ranks, communicator=MPI.COMM_WORLD) = MultiArchitecture(MultiCPU, grid, ranks, communicator)
MultiGPU(; grid, ranks, communicator=MPI.COMM_WORLD) = MultiArchitecture(MultiGPU, grid, ranks, communicator)

#####
##### Pretty printing
#####

function Base.show(io::IO, arch::MultiCPU)
    c = arch.connectivity
    print(io, "MultiCPU architecture (rank $(arch.local_rank)/$(prod(arch.ranks)-1)) [index $(arch.local_index) / $(arch.ranks)]\n",
              "└── connectivity:",
              isnothing(c.east) ? "" : " east=$(c.east)",
              isnothing(c.west) ? "" : " west=$(c.west)",
              isnothing(c.north) ? "" : " north=$(c.north)",
              isnothing(c.south) ? "" : " south=$(c.south)",
              isnothing(c.top) ? "" : " top=$(c.top)",
              isnothing(c.bottom) ? "" : " bottom=$(c.bottom)")
end


const machine = (function ()

    re = Automa.RegExp

    cat = Automa.RegExp.cat
    rep = Automa.RegExp.rep
    rep1 = Automa.RegExp.rep1
    opt = Automa.RegExp.opt

    newline = let
        lf = re"\n"
        lf.actions[:enter] = [:countline]

        cat(opt('\r'), lf)
    end

    record = let
        space = re"[\t ]+"

        header = let
            h1 = re"[A-Za-z0-9_\-]*"
            # h1 = re"[!-~]*"
            h2 = re"[0-9\.]*"
            h3 = cat(opt("-"),re"[0-9]*")
            h4 = re"[A-Za-z0-9_\-]*"
            # h4 = re"[!-~]*"

            cat(h1, space, h2,space, h3, space, h4)
        end
        header.actions[:enter] = [:mark]
        header.actions[:exit] = [:record_header]

        pfm = let
            position = re"[0-9]*"
            position.actions[:enter] = [:record_pfm_position]

            frequency = re"[0-9]*"
            frequency.actions[:enter] = [:mark]
            frequency.actions[:exit] = [:record_pfm_frequency]

            nucleotide = re"[ACGT]"
            nucleotide.actions[:enter] = [:mark]
            nucleotide.actions[:exit] = [:record_pfm_nucleotide]

            rep1(cat(position, space, frequency, space, frequency, space, frequency, space, frequency, space, nucleotide, newline))
        end

        cat("DE", space, header, newline, pfm, "XX", newline)
    end
    record.actions[:exit] = [:record]

    stamp = rep(cat(record, rep(newline)))

    Automa.compile(stamp)
end)()

const actions = Dict(
    :mark => :(@mark),
    :countline => :(linenum += 1),

    :record_header => quote
            str = String(data[@markpos():p - 1])
            record.header = str
            @debug "header" str
        end,
    :record_pfm_position => quote
            posnum += 1
            @debug posnum
        end,
    :record_pfm_frequency => quote
            str = String(data[@markpos():p - 1])
            push!(frequencies, parse(Int64, str))
            @debug "frequency" str
        end,
    :record_pfm_nucleotide => quote
            str = String(data[@markpos():p - 1])
            sequence = sequence * LongDNASeq(str)
            @debug "nucleotide" str
        end,
    :record => quote
            record.frequencies = reshape(frequencies, 4, :)
            record.sequence = sequence
            found = true
            @escape
        end
    )

initcode = quote

    found = false

    posnum = 0
    frequencies = Int64[]
    sequence = LongDNASeq()

    initialize!(record)

    cs, linenum = state
end

loopcode = quote

    if cs < 0
        throw(ArgumentError("malformed file at line $(linenum)"))
    end
    found && @goto __return__
end

returncode = :(return cs, linenum, found)

context = Automa.CodeGenContext(generator = :goto, checkbounds = false, loopunroll = 8)

Automa.Stream.generate_reader(
    :readrecord!,
    machine,
    arguments = (:(record::Record), :(state::Tuple{Int,Int})),
    actions = actions,
    context = context,
    initcode = initcode,
    loopcode = loopcode,
    returncode = returncode
) |> eval


using DocOpt

doc="""Training (Single) Gaussian Mixture model for global variance.

Usage:
    train_gv.jl [options] <tgt_dir> <dst_jld>
    train_gv.jl --version
    train_gv.jl -h | --help

Options:
    -h --help           show this message
    --add_delta         add delta feature
    --n_components=MIX  number of mixtures [default: 1]
    --n_iter=ITER       number of iterations [default: 200]
    --n_init=N          number of initialization [default: 2]
    --max=MAX           maximum number of data we use [default: 100]
    --min_covar=MIN     minimum covariance [default: 1.0e-7]
"""

using VoiceConversion
using HDF5, JLD
using PyCall

@pyimport sklearn.mixture as mixture

using Logging
@Logging.configure(level=DEBUG, output=STDOUT)

let
    args = docopt(doc, version=v"0.0.1")

    nmax = parse(Int, args["--max"])
    add_delta = args["--add_delta"]

    n_components  = parse(Int, args["--n_components"])
    n_iter = parse(Int, args["--n_iter"])
    n_init = parse(Int, args["--n_init"])
    min_covar = parse(Float64, args["--min_covar"])

    dataset = GVDataset(args["<tgt_dir>"], add_delta=add_delta, nmax=nmax)

    gmm = mixture.GMM(n_components=n_components,
                      covariance_type="full",
                      n_iter=n_iter,
                      n_init=n_init,
                      min_covar=min_covar
                      )

    @show gmm

    # pass transposed matrix because python is row-major language
    elapsed = @elapsed gmm[:fit](dataset.X')
    info("Elapsed time in training is $(elapsed) sec.")

    # save transposed parameters because julia is column-major language
    # convert means
    py_means = gmm[:means_]
    means = py_means'

    # convert covar tensor
    py_covars = gmm[:covars_] # shape: (n_components, order, order)
    order = size(py_covars, 2)
    covars = Array(eltype(py_covars), order, order, n_components)
    for m=1:n_components
        covars[:,:,m] = reshape(py_covars[m,:,:], order, order)
    end

    save(args["<dst_jld>"],
         "description", "Parameters of Gaussian Mixture Model for GV",
         "n_components", n_components,
         "weights", gmm[:weights_],
         "means", means,
         "covars", covars
         )
end

main()


abstract type TagBase end

struct PayloadGroup
    range::NTuple{2,Int32}
    ratio::Float64
end

PayloadGroup(range::Vector{<:Integer}, ratio::T) where {T} =
    PayloadGroup((range[1], range[2]), get_ratio(ratio))

struct DataStreamConfig
    name::String
    payload_groups::Vector{PayloadGroup}
end

struct DataSetConfig
    name::String
    basename::String
    datagen_backend_tag::TagBase
    io_backend_tag::TagBase
    nsteps::Int32
    compute_seconds::Float64
    streams::Vector{DataStreamConfig}
end

abstract type DataObject end

mutable struct DataVector <: DataObject
    vec::Vector{Float64}
end

DataVector() = DataVector(Float64[])

mutable struct DataSet
    cfg::DataSetConfig
    curr_step::Int32
    timestamp::Float64
    streams::Vector{DataObject}
end

DataSet(cfg::DataSetConfig) = DataSet(cfg, 1, time(), Float64[])


#!/usr/bin/env julia

using GZip

"""
Remove variants from exonic (coding) or masked low resolution mapping regions of the genome
"""

wigf = ARGS[1]   # .wig file
bedf = ARGS[2]   # .bed file

function read(file)
    if ismatch(r".gz$", file)   # Gzipped .gz files
		f = GZip.open(file)
		lines = readlines(f)
		close(f)
    else
		f = open(file)
		lines = readlines(f)
		close(f)
	end
	return lines
end

"""
Expanding the dashed intervals into single nucleotide positions
example:
Y:2688071-2688073 =>
                    Y:2688071
                    Y:2688072
                    Y:2688073
"""
function expand(locci)
    list = []
    pos = split(locci, ':')
        chr = pos[1]
        region = split(pos[2], '-')
        n = parse(Int64, region[1])
        while n <= parse(Int64, region[end])
             out = "$chr" * ":" * "$n"
            push!(list, out)
            n +=  1
        end
    return list
end

"""
Read .wig file

track graphType=bar windowingFunction=none autoScale=off viewLimits=0:1
variableStep chrom=Y
2649597	1
2649610	1
2649664	1
"""
function readwig(fh)
    pos = []
    lines = read(fh)
    header = lines[1:2]
    lines  = lines[3:end]
    chr = split(chomp(header[2]), '=')
    for line in lines
        cols = split(line)
        out = chr[2] * ":" * cols[1]
        push!(pos, out)
    end
    chr = chr[2]
    unique(pos)
    return chr, header, pos
end

"""
Read .bed file

chr1	67000041	67000051	NM_032291_cds_0_0_chr1_67000042_f	0	+
chr1	67091529	67091593	NM_032291_cds_1_0_chr1_67091530_f	0	+
chr1	67098752	67098777	NM_032291_cds_2_0_chr1_67098753_f	0	+
"""
function readBED(bed_file, chr::AbstractString=chr)
    pos = []
    bed = read(bed_file)
    for line in bed[5265:5999]
        bed = split(line, '\t')
        chrm = replace(bed[1], "chr", "")
        if chrm == chr
            start, last = bed[2], bed[3]
            chroms = "$chrm" * ":" * "$start" * "-" * "$last"
            region = expand(chroms)
            append!(pos, region)
        end
    end
    pos = unique(pos)
    return pos
end

function printWIG(wigf, bedf)
    chr, header, ln = readwig(wigf)
    bed = readBED(bedf, chr)
    ncDNA = setdiff(Set(ln), Set(bed))

    for line in header
        println(chomp(line))
    end

   for i in ncDNA
       pattern = chr * ":"
       println(replace(i, pattern, ""), ' ', 1)
   end
end

printWIG(wigf, bedf)


######################################################################
# gif.jl
#
# This file makes gifs of simulations
######################################################################

"""
Creates a gif.

`gif(m, x, f, p, num_steps=10, filename="out.gif")`

where
* `m` is a `SearchDomain`
* `x` is a `Vehicle`
* `f` is a subtype of `AbstractFilter`
* `p` is a subtype of `Policy`
"""
function gif(m::SearchDomain,
             x::Vehicle,
             f::AbstractFilter,
             p::Policy,
             tc::TerminationCondition=StepThreshold(10),
             filename="out.gif";
             seconds_per_step=0.5,
             show_mean=false,
             show_cov=false,
             show_path=false
            )

    frames = Frames(MIME("image/png"), fps=20)
    path_x = Float64[]
    path_y = Float64[]

    # Plot the original scene
    plot(m, f, x)
    title("\$t\$ = 0 s")
    push!(frames, gcf())
    push!(path_x, x.x)
    push!(path_y, x.y)
    close()

    # 20 is the fps
    frames_per_step = round(Int, seconds_per_step * 20)

    step_count = 0
    while !is_complete(f, tc, step_count)
        old_pose = (x.x, x.y, x.heading)
        o = observe(m,x)
        update!(f, x, o)
        a = action(m, x, o, f, p)
        act!(m,x,a)

        move_target!(m)

        new_pose = (x.x, x.y, x.heading)
        # Plot everything in between old pose and new pose
        dx = (new_pose[1] - old_pose[1]) / frames_per_step
        dy = (new_pose[2] - old_pose[2]) / frames_per_step
        dh = a[3] / frames_per_step
        for j = 1:frames_per_step
            #figure()
            # determine intermediate pose
            new_h = mod(old_pose[3] + dh, 360.0)
            old_pose = (old_pose[1] + dx, old_pose[2] + dy, new_h)

            push!(path_x, old_pose[1])
            push!(path_y, old_pose[2])
            plot(m, f, old_pose;show_mean=show_mean, show_cov=show_cov)
            if show_path
                plot(path_x, path_y)
            end
            title("\$t\$ = $step_count s")
            push!(frames, gcf())
            close()
        end
        step_count += 1
    end
    #write(filename, frames)
    write("temp.mp4", frames)
    write("temp.gif", frames)
end

function gif(m::SearchDomain,
             xarr::Vector{Vehicle},
             farr,
             parr,
             num_steps::Int=10,
             filename="out.gif";
             seconds_per_step=0.5,
             show_mean=false,
             show_cov=false,
             show_path=false,
             alpha=1.0
            )

	num_vehicles = length(xarr)
	obsarr = zeros(num_vehicles)
	new_poses = Array(Pose, num_vehicles)
	old_poses = Array(Pose, num_vehicles)
	diffs = Array(Pose, num_vehicles)

	frames = Frames(MIME("image/png"), fps=20)

	# Set up the paths
	x_paths = Array(Vector{Float64}, num_vehicles)
	y_paths = Array(Vector{Float64}, num_vehicles)
	for (xi,x) in enumerate(xarr)
		x_paths[xi] = [x.x]
		y_paths[xi] = [x.y]
	end

	# Plot the original scene
	plot(m, farr, xarr)
	push!(frames, gcf())
	close()

	# 20 is the fps
	frames_per_step = round(Int, seconds_per_step * 20)

	colors = ["b", "r"]
	if num_vehicles == 1
		colors = ["b"]
	elseif num_vehicles == 2
		colors = ["b", "r"]
	elseif num_vehicles == 3
		colors = ["b", "r", "g"]
	elseif num_vehicles == 4
		colors = ["b", "r", "g", "k"]
	end

	for i = 1:num_steps
		for (xi,x) in enumerate(xarr)
			f = farr[xi]
			p = parr[xi]
			old_pose = (x.x, x.y, x.heading)
			old_poses[xi] = old_pose
			o = observe(m,x)
			update!(f, x, o)
			a = action(m, x, o, f, p)
			act!(m,x,a)
			new_pose = (x.x, x.y, x.heading)
			new_poses[xi] = new_pose
			# Plot everything in between old pose and new pose
			dx = (new_pose[1] - old_pose[1]) / frames_per_step
			dy = (new_pose[2] - old_pose[2]) / frames_per_step
			dh = a[3] / frames_per_step
			diffs[xi] = (dx,dy,dh)
		end
		for j = 1:frames_per_step
			#figure()
			# determine intermediate pose
			for (xi, x) in enumerate(xarr)
				dx = diffs[xi][1]
				dy = diffs[xi][2]
				dh = diffs[xi][3]
				old_pose = old_poses[xi]
				new_h = mod(old_pose[3] + dh, 360.0)
				old_pose = (old_pose[1] + dx, old_pose[2] + dy, new_h)
				old_poses[xi] = (old_pose)

				push!(x_paths[xi], old_pose[1])
				push!(y_paths[xi], old_pose[2])
			end
			plot(m, farr, old_poses; show_mean=show_mean, show_cov=show_cov, alpha=alpha)
			if show_path
				for (xi, x) in enumerate(xarr)
					plot(x_paths[xi], y_paths[xi], colors[xi])
				end
			end
			push!(frames, gcf())
			close()
		end
	end
	write(filename, frames)
end

# TODO: find a place for this stuff.... probably not here

# really, this is for white sands results
function simgif(j::LocTuple, actions::Vector{Pose}, observations::Vector{Float64}, b::Vector{Matrix{Float64}}, m::SearchDomain, x::Vehicle; show_mean=false, show_cov=false, show_path=false)
	# always start the vehicle in the center
	# NO, don't do that
	#x.x = m.length / 2.0
	#x.y = m.length / 2.0
	theta!(m, j[1], j[2])

	num_vehicles = 1
	new_poses = Array(Pose, num_vehicles)
	old_poses = Array(Pose, num_vehicles)
	diffs = Array(Pose, num_vehicles)

	frames = Frames(MIME("image/png"), fps=20)

	# Set up the paths
	x_path = [x.x]
	y_path = [x.y]

	# Plot the original scene
	#plot(m, b[1], x, show_mean=show_mean, show_cov=show_cov, obs=observations[1])
	plot(m, b[1], x, show_mean=show_mean, show_cov=show_cov)
	push!(frames, gcf())
	close()

	# 20 is the fps
	seconds_per_step = 2.0
	frames_per_step = round(Int, seconds_per_step * 20)

	# warn user if jammer is not where it should be
	#if abs(dx) > x.x || abs(dy) > x.y
	#	println("WARNING: jammer outside search domain.")
	#end
	colors = ["b", "r"]
	if num_vehicles == 1
		colors = ["b"]
	elseif num_vehicles == 2
		colors = ["b", "r"]
	elseif num_vehicles == 3
		colors = ["b", "r", "g"]
	elseif num_vehicles == 4
		colors = ["b", "r", "g", "k"]
	end

	# loop through all observations...
	num_obs = length(observations)
	for (oi,o) in enumerate(observations)
		println("oi = ", oi)
		xi = 1
		old_pose = (x.x, x.y, x.heading)
		old_poses[xi] = old_pose

		a = actions[oi]
		act!(m, x, actions[oi])
		new_pose = (x.x, x.y, x.heading)
		new_poses[xi] = new_pose
		# Plot everything in between old pose and new pose
		dx = (new_pose[1] - old_pose[1]) / frames_per_step
		dy = (new_pose[2] - old_pose[2]) / frames_per_step
		dh = a[3] / frames_per_step
		diffs = (dx, dy, dh)

		#plot(m, b[oi], x, show_mean=true, show_cov=true)
		#println("oi = ", oi)
		FEBOL.plot(m, b[oi], x, show_mean=show_mean, show_cov=show_cov)
		for j = 1:frames_per_step
			xi = 1
			figure()
			# determine intermediate pose
			dx, dy, dh = diffs
			old_pose = old_poses[xi]
			new_h = mod(old_pose[3] + dh, 360.0)
			old_pose = (old_pose[1] + dx, old_pose[2] + dy, new_h)
			old_poses[xi] = (old_pose)
			push!(x_path, old_pose[1])
			push!(y_path, old_pose[2])
			#plot(m, farr, old_poses; show_mean=show_mean, show_cov=show_cov, alpha=alpha)
			#FEBOL.plot(m, b[oi], old_pose, show_mean=show_mean, show_cov=show_cov, obs=o)
			FEBOL.plot(m, b[oi], old_pose, show_mean=show_mean, show_cov=show_cov)
			if show_path
				plot(x_path, y_path, colors[1])
			end

			push!(frames, gcf())
			close()
		end
		#savefig("temp_$(oi).png", format="png")
		#hold(false)
		#act!(m, x, actions[oi])
	end
	filename="temp.gif"
	write(filename, frames)
end

# really, this is for white sands results
# This shouldn't be so hard...
export simgif2
function simgif2(states::Vector{Pose}, beliefs::Vector{Matrix{Float64}}, m::SearchDomain, x::Vehicle; show_mean=false, show_cov=false, show_path=false, j::LocTuple=m.theta)

	theta!(m, j[1], j[2])
	frames_per_second = 4
	frames = Frames(MIME("image/png"), fps=frames_per_second)

	# Set up the paths
	x_path = [x.x]
	y_path = [x.y]

	# Plot the original scene
	plot(m, beliefs[1], x, show_mean=show_mean, show_cov=show_cov,obs=0)
	push!(frames, gcf())
	close()

	# 20 is the fps
	seconds_per_step = 1.0
	frames_per_step = round(Int, seconds_per_step * frames_per_second)

	colors = ["b"]

	# loop through all beliefs...
	num_b = length(beliefs)
	s = states[1]
	for bi = 2:(num_b+1)
		println("bi = ", bi)
		sp = states[num_b]
		if bi < (num_b + 1)
			sp = states[bi]
		end

		# Plot everything in between old pose and new pose
		dx = (sp[1] - s[1]) / frames_per_step
		dy = (sp[2] - s[2]) / frames_per_step
		dh = fit_180(sp[3] - s[3]) / frames_per_step
		diffs = (dx, dy, dh)

		#plot(m, b[oi], x, show_mean=true, show_cov=true)
		#println("oi = ", oi)
		#FEBOL.plot(m, beliefs[bi-1], x,show_mean=show_mean, show_cov=show_cov)
		for j = 1:frames_per_step
			figure()
			# determine intermediate pose
			dx, dy, dh = diffs
			new_h = mod(s[3] + dh, 360.0)
			s = (s[1] + dx, s[2] + dy, new_h)

			push!(x_path, s[1])
			push!(y_path, s[2])

			FEBOL.plot(m, beliefs[bi-1], s, show_mean=show_mean, show_cov=show_cov, obs=bi-1)
			if show_path
				# changed by LD for rss submission
				#plot(x_path, y_path, colors[1])
				plot(x_path, y_path, "k")
			end

			push!(frames, gcf())
			close()
		end
	end
	#filename="temp.mp4"
	filename="temp.gif"
	write(filename, frames)
end


for O in (:Adjoint, :Transpose,:Cholesky, :Hessenberg, :LDLt, :LQ, :LU, :QR)
    @eval LinearAlgebra.$O(::Type{T}) where {T} = LinearAlgebra.$O{T, Array{T,1}}
end

for O in (:BunchKaufman, :GeneralizedSVD, :Schur)
    @eval LinearAlgebra.$O(::Type{T}) where {T} = LinearAlgebra.$O{T, Array{T,2}}
end


function FieldElevationDetector(arg0::Field, arg1::TopocentricFrame)
    return FieldElevationDetector((Field, TopocentricFrame), arg0, arg1)
end

function FieldElevationDetector(arg0::RealFieldElement, arg1::RealFieldElement, arg2::TopocentricFrame)
    return FieldElevationDetector((RealFieldElement, RealFieldElement, TopocentricFrame), arg0, arg1, arg2)
end

function g(obj::FieldElevationDetector, arg0::FieldSpacecraftState)
    return jcall(obj, "g", RealFieldElement, (FieldSpacecraftState,), arg0)
end

function get_elevation_mask(obj::FieldElevationDetector)
    return jcall(obj, "getElevationMask", ElevationMask, ())
end

function get_min_elevation(obj::FieldElevationDetector)
    return jcall(obj, "getMinElevation", jdouble, ())
end

function get_refraction_model(obj::FieldElevationDetector)
    return jcall(obj, "getRefractionModel", AtmosphericRefractionModel, ())
end

function get_topocentric_frame(obj::FieldElevationDetector)
    return jcall(obj, "getTopocentricFrame", TopocentricFrame, ())
end

function with_constant_elevation(obj::FieldElevationDetector, arg0::jdouble)
    return jcall(obj, "withConstantElevation", FieldElevationDetector, (jdouble,), arg0)
end

function with_elevation_mask(obj::FieldElevationDetector, arg0::ElevationMask)
    return jcall(obj, "withElevationMask", FieldElevationDetector, (ElevationMask,), arg0)
end

function with_refraction(obj::FieldElevationDetector, arg0::AtmosphericRefractionModel)
    return jcall(obj, "withRefraction", FieldElevationDetector, (AtmosphericRefractionModel,), arg0)
end


module handle_validvalues

export parse_validvalues, get_datatype, value_is_valid

using CategoricalArrays
using DataAPI
using Dates

"Returns: The type of each valid value (after validvalues has been parsed)."
get_datatype(validvalues::DataType) = validvalues
get_datatype(validvalues::Set)      = eltype(validvalues)
get_datatype(validvalues::T) where {T <: AbstractRange} = typeof(validvalues[1])

"Returns: True if value is in validvalues"
value_is_valid(value::T, validvalues::DataType) where {T <: CategoricalValue} = typeof(DataAPI.unwrap(value)) == validvalues
value_is_valid(value::T, validvalues::DataType) where {T <: CategoricalValue{String, <:Integer}} = typeof(DataAPI.unwrap(value)) == validvalues
value_is_valid(value::T, validvalues)           where {T <: CategoricalValue} = value_is_valid(DataAPI.unwrap(value), validvalues)
value_is_valid(value::T, validvalues)           where {T <: CategoricalValue{String, <:Integer}} = value_is_valid(DataAPI.unwrap(value), validvalues)
value_is_valid(value, validvalues::DataType) = typeof(value) == validvalues
value_is_valid(value, validvalues) = typeof(value) == eltype(validvalues) && in(value, validvalues)

"""
Returns: An instance of ColumnSchema.validvalues.

validvalues could be a:
- DataType. E.g., "Int64"
- Range of a Core type. E.g., "1:10"
- Range of a non-Core type, represented with a tuple: "(val1, stepsize, val2)"
"""
function parse_validvalues(parser::Function, returntype::DataType, validvalues::String)
    validvalues[1] == '(' && return parse_noncore_range(parser, returntype, validvalues)  # Range of a non-Core type

    # DataType
    try
        eval(Meta.parse(validvalues)) == returntype && return returntype  # "Int" becomes Int64
    catch e
    end

    # Range of a Core type
    vals = strip.(String.(split(validvalues, ':')))
    val1 = parser(vals[1])
    val2 = parser(vals[2])
    length(vals) == 2 && return val1:val2       # Step-size not supplied
    val3 = parser(vals[3])
    length(vals) == 3 && return val1:val2:val3  # Step-size supplied

    # Else error
    error("Cannot parse validvalues: $(validvalues)")
end

function parse_validvalues(parser::Function, returntype::DataType, validvalues::T) where {T <: Vector}
    eltype(validvalues) == returntype ? validvalues : parser.(validvalues)
end

"""
Parse String to a range of values with non-Core type.

The format is: "(start, stepsize, stop)"

Example: (2017-10-01 09:00+10:00, Day(1), 2017-12-08 23:00+10:00), where the entries have type TimeZones.ZonedDateTime.
"""
function parse_noncore_range(parser::Function, returntype::DataType, validvalues::String)
    @assert length(validvalues) >= 5  # "(a,b)" contains 5 characters
    @assert validvalues[1] == '('
    @assert validvalues[end] == ')'
    validvalues = strip.(String.(split(validvalues[2:(end-1)], ",")))
    length(validvalues) != 3 && error("Range of non-Base type requires 3 elements. It has $(length(validvalues)).")
    val1 = parser(validvalues[1])
    val2 = returntype <: Dates.TimeType ? eval(Meta.parse(validvalues[2])) : parser(validvalues[2])
    val3 = parser(validvalues[3])
    val1:val2:val3
end

end


# Define simple generic types and methods for testing

sprintcompact(x) = sprint(show, x; context=:compact=>true)

struct TestSharpness <: TreatmentSharpness end
struct TestParaCondition <: ParallelCondition end
struct TestParaStrength <: ParallelStrength end

struct TestTreatment <: AbstractTreatment
    time::Symbol
    ref::Int
end

ttreat(time::Term, ref::ConstantTerm) = TestTreatment(time.sym, ref.n)

struct TestParallel{C,S} <: AbstractParallel{C,S}
    e::Int
end

TestParallel(e::Int) = TestParallel{ParallelCondition,ParallelStrength}(e)
tpara(c::ConstantTerm) = TestParallel{ParallelCondition,ParallelStrength}(c.n)

teststep(tr::AbstractTreatment, pr::AbstractParallel) =
    (str=sprint(show, tr), spr=sprint(show, pr))
const TestStep = StatsStep{:TestStep, typeof(teststep), true}
required(::TestStep) = (:tr, :pr)

testnextstep(::AbstractTreatment, str::String) = (next="next"*str,)
const TestNextStep = StatsStep{:TestNextStep, typeof(testnextstep), true}
required(::TestNextStep) = (:tr, :str)

const TestDID = DiffinDiffsEstimator{:TestDID, Tuple{TestStep,TestNextStep}}
const NotImplemented = DiffinDiffsEstimator{:NotImplemented, Tuple{}}

const TR = TestTreatment(:t, 0)
const PR = TestParallel(0)

struct TestResult{TR} <: DIDResult{TR}
    coef::Vector{Float64}
    vcov::Matrix{Float64}
    vce::Nothing
    tr::AbstractTreatment
    nobs::Int
    dof_residual::Int
    yname::String
    coefnames::Vector{String}
    coefinds::Dict{String, Int}
    treatcells::VecColumnTable
    weightname::Symbol
    extra1::Int
    extra2::Symbol
    extra3::String
    extra4::Vector{Float64}
    extra5::Vector{String}
    extra6::Matrix{Float64}
    extra7::Vector{Symbol}
    extra8::Nothing
    extra9::Array{Float64,3}
end

@fieldequal TestResult

function TestResult(n1::Int, n2::Int)
    N = n1*(n2+1)
    coef = collect(Float64, 1:N)
    tr = TR
    tnames = ["rel: $a & c: $b" for a in 1:n1 for b in 1:n2]
    cnames = vcat(tnames, ["c"*string(i) for i in n1*n2+1:N])
    cinds = Dict(cnames .=> 1:N)
    tcells = VecColumnTable((rel=repeat(1:n1, inner=n2), c=repeat(1:n2, outer=n1)))
    return TestResult{typeof(tr)}(coef, coef.*coef', nothing, tr, N, N-1, "y", cnames, cinds,
        tcells, :w, 1, :a, "a", [1.0], ["a"], [1.0 2.0], [:a], nothing, ones(1,1,1))
end

struct TestResultBARE{TR} <: DIDResult{TR}
    coef::Vector{Float64}
    vcov::Matrix{Float64}
    vce::Nothing
    tr::AbstractTreatment
    nobs::Int
    yname::String
    coefnames::Vector{String}
    treatcells::VecColumnTable
    weightname::Symbol
end

@fieldequal TestResultBARE

function TestResultBARE(n1::Int, n2::Int)
    N = n1*n2
    coef = collect(Float64, 1:N)
    tr = dynamic(:time, (-1,))
    tnames = ["rel: $a & c: $b" for a in 1:n1 for b in 1:n2]
    tcells = VecColumnTable((rel=repeat(1:n1, inner=n2), c=repeat(1:n2, outer=n1)))
    return TestResultBARE{typeof(tr)}(coef, coef.*coef', nothing, tr, N, "y", tnames, tcells, :w)
end

function result(::Type{TestDID}, nt::NamedTuple)
    return merge(nt, (result=TestResult(2, 2),))
end


module JuSwalbe
    import Base.Threads.@spawn
    using Pkg
    using DelimitedFiles, JSON, DrWatson, Images, Parameters, BSON, Glob, Distributions
    using Revise

    include("minimalsetup.jl")
    include("macroscopic_typs.jl")
    include("distribution_types.jl")
    include("readinput.jl")
    include("equilibriumcalculation.jl")
    include("calculatepressure.jl")
    include("calcmoments.jl")
    include("forcing.jl")
    include("collide.jl")
    include("readwritedata.jl")
    # include("startrun.jl")

    # IO related
    export readinput, findargument, minimalsetup1d, minimalsetup2d, simplemoment2d, simpleTwovector, zeroTwovector, simpledistD2Q9, simplemoment1d
    # File IO
    export savecheckpoint, savecheckpoints, loadcheckpoint, height2file, velocity2file, velocityandheight2file
    # Pressure related
    export Δh, Π, pressure, ∇p
    # Distributions functions and moments
    export calc_equilibrium_distribution, calculatemoments, dist2array, collisionBGK, streamdistperiodic!, streamdistperiodic
    # Forces
    export computeslip, computethermalcapillarywaves

    # Full simulations
    # export runsimulation_1D, runsimulation_2D

end # module


function (semmg)(par)
    for i in 1:length(semmg.vec)
        F += semmg.vec[i](par)
    end
end

function (semmg)(par)
    for i in 1:length(semmg.imply_vec)
        semmg.imply_vec[i](par, semmg)
    end
    for i in 1:length(semmg.loss_vec)
        F += semmg.loss_vec[i](par, semmg)
    end
end

function (semmg)(par)
    semmg.imply_column(par, semmg)
    F = semmg.loss_column(par, semmg)
end

abstract type loss_column end

struct ML <: loss_column
    vec::Vector{loss}
end

(ML)(par)

struct ML_superparallel <: loss_column

end

struct WLS_superparallel <: loss_column

end

function (WLS_superparallel)(par, model)
    for i in 1:length(imply_column)
        A[i] = model.imply_column[i].imp_cov
    end
    do A end
end

sem_fit(model)
sem_fit_superparallel_ML(model)
sem_fit_superparallel_WLS(model)
ML_superparallel(model, par)

function (semdef)(par)
    for i in 1:length(semdef.vec)
        F += semdef.vec[i](par, semdef.defvars[i])
    end
end

function (semfiml)(par)
    for i in 1:length(semfiml.vec)
        F += semfiml.vec[i](par, semfiml.filter[i])
    end
end

function (semfiml)(par, defvars)
    imply(par)
    delete(semfiml.imply)
    for loss(par, semfiml) end

    for i in 1:length(semfiml.vec)
        F += semfiml.vec[i](par, semfiml.filter[i], defvars)
    end
end

function (semel)(par)
    semel.imply(par, semel)
    F = semel.loss(par, semel)
    return F
end

function (semel)(par, filter)
    semel.imply(par, semel)
    F = semel.loss(par, semel, filter)
    return F
end

function (sematom)(par, defvars)
    semel.imply(par, semel, defvars)
    F = semel.loss(par, sematom)
    return F
end

function (semel)(par, defvars, filter)
    semel.imply(par, semel, defvars)
    F = semel.loss(par, semel, filter)
    return F
end

test

Threads.nthreads()

A = rand(100,100)

using BenchmarkTools, LinearAlgebra

@benchmark inv(A)

BLAS.get_config()

using MKL

@benchmark inv(A)

BLAS.get_config()


using KMarkovGenSeq

function main()
    cnt, pred_cls, des = query_freq("data/reads.fa", "data/genomes", 9, nothing, false)
    len = length(des)
    println("Total query sequences: $cnt")
    counter = fill(0, len)
    for cls in pred_cls
        counter[cls] += 1
    end
    println()
    for threshold in [1, 5, 10, 50]
        println("Number of groups containing at least $threshold query sequences: ", count(>=(threshold), counter))
    end
    for i in eachindex(counter)
        if counter[i] >= 10
            println(des[i], "contains\t", counter[i], "\tquery sequences")
        end
    end
    println()
    @show(counter)
end

main()

"""
Total query sequences: 1876

Number of groups containing at least 1 query sequences: 10
Number of groups containing at least 5 query sequences: 10
Number of groups containing at least 10 query sequences: 10
Number of groups containing at least 50 query sequences: 8

Baumannia cicadellinicola str. Hc (Homalodisca coagulata), complete genomecontains      298     query sequences
Psychromonas ingrahamii 37 chromosome, complete genomecontains  22      query sequences
Sphingomonas wittichii RW1 chromosome, complete genomecontains  81      query sequences
Roseiflexus castenholzii DSM 13941 chromosome, complete genomecontains  375     query sequences
Hydrogenobaculum sp. Y04AAS1 chromosome, complete genomecontains        104     query sequences
Alteromonas macleodii str. 'Deep ecotype' chromosome, complete genomecontains   279     query sequences
Denitrovibrio acetiphilus DSM 12809 chromosome, complete genomecontains 111     query sequences
Frankia symbiont of Datisca glomerata chromosome, complete genomecontains       23      query sequences
Candidatus Midichloria mitochondrii IricVA chromosome, complete genomecontains  348     query sequences
Corynebacterium variabile DSM 44702 chromosome, complete genomecontains 235     query sequences

counter = [298, 22, 81, 375, 104, 279, 111, 23, 348, 235]
"""


_dst_eltype(any, default) = default
_dst_eltype(::Type{T}, default) where {T<:Number} = T
_dst_eltype(::Type{Bool}, default) = default

@testset "convertlabelview binary" begin
    @test_throws MethodError convertlabelview(LabelEnc.MarginBased, [1.,0,1], LabelEnc.ZeroOne())
    @test_throws MethodError convertlabelview(LabelEnc.OneOfK(2), [1.,0,1], LabelEnc.ZeroOne())
    @test_throws MethodError convertlabelview(LabelEnc.Indices(3), [1.,0,1], LabelEnc.ZeroOne())
    @test_throws MethodError convertlabelview(LabelEnc.ZeroOne(), [1,2,3], LabelEnc.Indices(Int,3))
    @test_throws MethodError convertlabelview(LabelEnc.FuzzyBinary(), [1.,0,1], LabelEnc.ZeroOne())
    @test_throws MethodError convertlabelview(LabelEnc.MarginBased(), Any[1.,0,1], LabelEnc.FuzzyBinary())
    @test_throws MethodError convertlabelview(LabelEnc.FuzzyBinary(), [1.,0,1], LabelEnc.ZeroOne())
    @test_throws MethodError convertlabelview(LabelEnc.FuzzyBinary(), [1.,0,1])
    for (src_lm, src_x) in (
            (LabelEnc.TrueFalse(),[true,false,true,false,false,true]),
            (LabelEnc.ZeroOne(Int32),Int32[1,0,1,0,0,1]),
            (LabelEnc.ZeroOne(Int64),Int64[1,0,1,0,0,1]),
            (LabelEnc.ZeroOne(Float32),Float32[1,0,1,0,0,1]),
            (LabelEnc.ZeroOne(),Float64[1,0,1,0,0,1]),
            (LabelEnc.MarginBased(Int32),Int32[1,-1,1,-1,-1,1]),
            (LabelEnc.MarginBased(Int64),Int64[1,-1,1,-1,-1,1]),
            (LabelEnc.MarginBased(Float32),Float32[1,-1,1,-1,-1,1]),
            (LabelEnc.MarginBased(),Float64[1,-1,1,-1,-1,1]),
            (LabelEnc.Indices(Float64,2),Float64[1,2,1,2,2,1]),
            (LabelEnc.OneVsRest(:yes),[:yes,:no,:yes,:maybe,:no,:yes]),
            (LabelEnc.NativeLabels([:a,:b]),[:a,:b,:a,:b,:b,:a]),
        )
        for (dst_lm, dst_x) in (
                (LabelEnc.TrueFalse(),[true,false,true,false,false,true]),
                (LabelEnc.ZeroOne(Int32),Int32[1,0,1,0,0,1]),
                (LabelEnc.ZeroOne(Int64),Int64[1,0,1,0,0,1]),
                (LabelEnc.ZeroOne(Float32),Float32[1,0,1,0,0,1]),
                (LabelEnc.ZeroOne(),Float64[1,0,1,0,0,1]),
                (LabelEnc.MarginBased(Int32),Int32[1,-1,1,-1,-1,1]),
                (LabelEnc.MarginBased(Int64),Int64[1,-1,1,-1,-1,1]),
                (LabelEnc.MarginBased(Float32),Float32[1,-1,1,-1,-1,1]),
                (LabelEnc.MarginBased(),Float64[1,-1,1,-1,-1,1]),
                (LabelEnc.Indices(Float64,2),Float64[1,2,1,2,2,1]),
                (LabelEnc.OneVsRest(:yes),[:yes,:not_yes,:yes,:not_yes,:not_yes,:yes]),
                (LabelEnc.NativeLabels([:a,:b]),[:a,:b,:a,:b,:b,:a]),
            )
            c_src_x = copy(src_x)
            res = @inferred(convertlabelview(dst_lm, c_src_x, src_lm))
            @test res == dst_x
            if typeof(src_lm) <: LabelEnc.OneVsRest
                @test typeof(res) <: MappedArrays.ReadonlyMappedArray
            else
                res2 = convertlabelview(dst_lm, c_src_x)
                @test res == res2
                @test typeof(res) <: MappedArrays.MappedArray
                @test c_src_x[1] == poslabel(src_lm)
                res[1] = neglabel(dst_lm)
                @test c_src_x[1] == neglabel(src_lm)
                @test c_src_x[2] == neglabel(src_lm)
                res[2] = poslabel(dst_lm)
                @test c_src_x[2] == poslabel(src_lm)
            end
        end
    end
end

println("<HEARTBEAT>")

@testset "convertlabelview multiclass" begin
    for (src_lm, src_x) in (
            (LabelEnc.Indices(Int32,3),Int32[1,2,3,2,2,1]),
            (LabelEnc.Indices(Float64,3),Float64[1,2,3,2,2,1]),
            (LabelEnc.NativeLabels([:a,:b,:c]),[:a,:b,:c,:b,:b,:a]),
        )
        for (dst_lm, dst_x) in (
                (LabelEnc.Indices(UInt8,3),UInt8[1,2,3,2,2,1]),
                (LabelEnc.Indices(Float64,3),Float64[1,2,3,2,2,1]),
                (LabelEnc.NativeLabels(["a","b","c"]),["a","b","c","b","b","a"]),
            )
            c_src_x = copy(src_x)
            res = @inferred(convertlabelview(dst_lm, c_src_x, src_lm))
            @test res == dst_x
            res2 = convertlabelview(dst_lm, c_src_x)
            @test res == res2
            @test typeof(res) <: MappedArrays.MappedArray
            @test c_src_x[1] == ind2label(1, src_lm)
            res[1] = ind2label(2, dst_lm)
            @test c_src_x[1] == ind2label(2, src_lm)
            @test c_src_x[3] == ind2label(3, src_lm)
            res[3] = ind2label(1, dst_lm)
            @test c_src_x[3] == ind2label(1, src_lm)
        end
    end
end

println("<HEARTBEAT>")

@testset "convert binary" begin
    @test convertlabel(LabelEnc.MarginBased, UInt8[1,0,1], LabelEnc.ZeroOne()) == [0x1,0xff,0x1]
    for (src_lm, src_x) in (
            (LabelEnc.FuzzyBinary(),Any[true,0,1,-1,false,3]),
            (LabelEnc.FuzzyBinary(),Int32[1,0,1,0,0,1]),
            (LabelEnc.FuzzyBinary(),Float64[1,-1,1,-1,-1,1]),
            (LabelEnc.TrueFalse(),BitArray([true,false,true,false,false,true])),
            (LabelEnc.ZeroOne(),Int32[1,0,1,0,0,1]),
            (LabelEnc.ZeroOne(),Int64[1,0,1,0,0,1]),
            (LabelEnc.ZeroOne(),Float32[1,0,1,0,0,1]),
            (LabelEnc.ZeroOne(),Float64[1,0,1,0,0,1]),
            (LabelEnc.MarginBased(),Int32[1,-1,1,-1,-1,1]),
            (LabelEnc.MarginBased(),Int64[1,-1,1,-1,-1,1]),
            (LabelEnc.MarginBased(),Float32[1,-1,1,-1,-1,1]),
            (LabelEnc.MarginBased(),Float64[1,-1,1,-1,-1,1]),
            (LabelEnc.Indices(2),Float64[1,2,1,2,2,1]),
            (LabelEnc.OneVsRest(:yes),[:yes,:no,:yes,:maybe,:no,:yes]),
            (LabelEnc.NativeLabels([:a,:b]),[:a,:b,:a,:b,:b,:a]),
            ([:a,:b],[:a,:b,:a,:b,:b,:a]),
            (LabelEnc.OneOfK(Bool,2),BitArray(Bool[1 0 1 0 0 1; 0 1 0 1 1 0])),
            (LabelEnc.OneOfK(Int8,2),Int8[1 0 1 0 0 1; 0 1 0 1 1 0]),
        )
        for (dst_lm, dst_x) in (
                (LabelEnc.TrueFalse,BitArray([true,false,true,false,false,true])),
                (LabelEnc.TrueFalse(),BitArray([true,false,true,false,false,true])),
                (LabelEnc.ZeroOne,(_dst_eltype(eltype(src_x),Float64))[1,0,1,0,0,1]),
                (LabelEnc.ZeroOne(UInt8),UInt8[1,0,1,0,0,1]),
                (LabelEnc.ZeroOne(Int),Int[1,0,1,0,0,1]),
                (LabelEnc.ZeroOne(),[1.,0,1,0,0,1]),
                (LabelEnc.MarginBased,(_dst_eltype(eltype(src_x),Float64))[1,-1,1,-1,-1,1]),
                (LabelEnc.MarginBased(Float32),Float32[1,-1,1,-1,-1,1]),
                (LabelEnc.MarginBased(Int),Int[1,-1,1,-1,-1,1]),
                (LabelEnc.MarginBased(),[1,-1.,1,-1,-1,1]),
                (LabelEnc.Indices,(_dst_eltype(eltype(src_x),Int))[1,2,1,2,2,1]),
                (LabelEnc.Indices{UInt8},UInt8[1,2,1,2,2,1]),
                (LabelEnc.Indices(2),Int[1,2,1,2,2,1]),
                (LabelEnc.Indices(Float32,2),Float32[1,2,1,2,2,1]),
                (LabelEnc.OneVsRest(:yes),[:yes,:not_yes,:yes,:not_yes,:not_yes,:yes]),
                (LabelEnc.NativeLabels([:a,:b]),[:a,:b,:a,:b,:b,:a]),
                ([:a,:b],[:a,:b,:a,:b,:b,:a]),
                (LabelEnc.OneOfK,(_dst_eltype(eltype(src_x),Int))[1 0 1 0 0 1; 0 1 0 1 1 0]),
                (LabelEnc.OneOfK{UInt8},UInt8[1 0 1 0 0 1; 0 1 0 1 1 0]),
                (LabelEnc.OneOfK{Float32},Float32[1 0 1 0 0 1; 0 1 0 1 1 0]),
                (LabelEnc.OneOfK(Bool,2),BitArray(Bool[1 0 1 0 0 1; 0 1 0 1 1 0])),
            )
            @testset "($src_lm) $src_x -> ($dst_lm) $dst_x" begin
                if !(typeof(dst_lm) <: Vector)
                    @test @inferred(islabelenc(dst_x, dst_lm)) == true
                end
                if !(typeof(src_lm)<:LabelEnc.FuzzyBinary) && !xor((typeof(src_lm) <: LabelEnc.OneVsRest),(typeof(dst_lm) <: LabelEnc.OneVsRest))
                    res = if typeof(dst_lm) <: Type || typeof(dst_lm) <: Array
                        convertlabel(dst_lm, src_x)
                    else
                        @inferred convertlabel(dst_lm, src_x)
                    end
                    @test typeof(res) <: typeof(dst_x)
                    @test res == dst_x
                end
                res = if typeof(src_lm) <: Vector && typeof(dst_lm) <: Type && (dst_lm <: LabelEnc.Indices || dst_lm <: LabelEnc.OneOfK)
                    # in this situation K can not be inferred
                    # this is because we neither specify in src or dst the number of labels at compile time
                    convertlabel(dst_lm, src_x, src_lm)
                elseif typeof(src_lm) <: Vector && typeof(dst_lm) <: Vector
                    # same here
                    convertlabel(dst_lm, src_x, src_lm)
                else
                    @inferred convertlabel(dst_lm, src_x, src_lm)
                end
                @test typeof(res) <: typeof(dst_x)
                @test res == dst_x
            end
        end
        println("<HEARTBEAT>")
    end
end

@testset "convert multiclass" begin
    for (src_lm, src_x) in (
            (LabelEnc.Indices(3),[1,2,3,2,3,1]),
            (LabelEnc.Indices(Float64,3),Float64[1,2,3,2,3,1]),
            (LabelEnc.NativeLabels([:a,:b,:c]),[:a,:b,:c,:b,:c,:a]),
            ([:a,:b,:c],[:a,:b,:c,:b,:c,:a]),
            (LabelEnc.OneOfK(Bool,3),BitArray(Bool[1 0 0 0 0 1; 0 1 0 1 0 0; 0 0 1 0 1 0])),
            (LabelEnc.OneOfK(Int8,3),Int8[1 0 0 0 0 1; 0 1 0 1 0 0; 0 0 1 0 1 0]),
        )
        for (dst_lm, dst_x) in (
                (LabelEnc.Indices,(_dst_eltype(eltype(src_x),Int))[1,2,3,2,3,1]),
                (LabelEnc.Indices{UInt8},UInt8[1,2,3,2,3,1]),
                (LabelEnc.Indices(3),Int[1,2,3,2,3,1]),
                (LabelEnc.Indices(Float32,3),Float32[1,2,3,2,3,1]),
                (LabelEnc.NativeLabels([:x,:y,:z]),[:x,:y,:z,:y,:z,:x]),
                ([:x,:y,:z],[:x,:y,:z,:y,:z,:x]),
                (LabelEnc.OneOfK,(_dst_eltype(eltype(src_x),Int))[1 0 0 0 0 1; 0 1 0 1 0 0; 0 0 1 0 1 0]),
                (LabelEnc.OneOfK{UInt8},UInt8[1 0 0 0 0 1; 0 1 0 1 0 0; 0 0 1 0 1 0]),
                (LabelEnc.OneOfK{Float32},Float32[1 0 0 0 0 1; 0 1 0 1 0 0; 0 0 1 0 1 0]),
                (LabelEnc.OneOfK(Bool,3),BitArray(Bool[1 0 0 0 0 1; 0 1 0 1 0 0; 0 0 1 0 1 0])),
            )
            @testset "($src_lm) $src_x -> ($dst_lm) $dst_x" begin
                if !(typeof(dst_lm) <: Vector)
                    @test @inferred(islabelenc(dst_x, dst_lm)) == true
                end
                res = if typeof(dst_lm) <: Type || typeof(dst_lm) <: Array
                    convertlabel(dst_lm, src_x)
                else
                    @inferred convertlabel(dst_lm, src_x)
                end
                @test typeof(res) <: typeof(dst_x)
                @test res == dst_x
                res = if typeof(src_lm) <: Vector && typeof(dst_lm) <: Type && (dst_lm <: LabelEnc.Indices || dst_lm <: LabelEnc.OneOfK)
                    # in this situation K can not be inferred
                    # this is because we neither specify in src or dst the number of labels at compile time
                    convertlabel(dst_lm, src_x, src_lm)
                elseif typeof(src_lm) <: Vector && typeof(dst_lm) <: Vector
                    # same here
                    convertlabel(dst_lm, src_x, src_lm)
                else
                    @inferred convertlabel(dst_lm, src_x, src_lm)
                end
                @test typeof(res) <: typeof(dst_x)
                @test res == dst_x
            end
        end
    end
end

println("<HEARTBEAT>")

@testset "binary OneOfK with and without ObsDim" begin
    x = [1 0 1 0 0 1; 0 1 0 1 1 0]
    xt = x'
    # From OneOfK
    for (dst_lm, dst_x) in (
            (LabelEnc.TrueFalse,BitArray([true,false,true,false,false,true])),
            (LabelEnc.TrueFalse(),BitArray([true,false,true,false,false,true])),
            (LabelEnc.ZeroOne,(_dst_eltype(eltype(x),Float64))[1,0,1,0,0,1]),
            (LabelEnc.ZeroOne(UInt8),UInt8[1,0,1,0,0,1]),
            (LabelEnc.ZeroOne(Int),Int[1,0,1,0,0,1]),
            (LabelEnc.ZeroOne(),[1.,0,1,0,0,1]),
            (LabelEnc.MarginBased,(_dst_eltype(eltype(x),Float64))[1,-1,1,-1,-1,1]),
            (LabelEnc.MarginBased(Float32),Float32[1,-1,1,-1,-1,1]),
            (LabelEnc.MarginBased(Int),Int[1,-1,1,-1,-1,1]),
            (LabelEnc.MarginBased(),[1,-1.,1,-1,-1,1]),
            (LabelEnc.Indices,(_dst_eltype(eltype(x),Int))[1,2,1,2,2,1]),
            (LabelEnc.Indices{UInt8},UInt8[1,2,1,2,2,1]),
            (LabelEnc.Indices(2),Int[1,2,1,2,2,1]),
            (LabelEnc.Indices(Float32,2),Float32[1,2,1,2,2,1]),
            (LabelEnc.OneVsRest(:yes),[:yes,:not_yes,:yes,:not_yes,:not_yes,:yes]),
            (LabelEnc.NativeLabels([:a,:b]),[:a,:b,:a,:b,:b,:a]),
            ([:a,:b],[:a,:b,:a,:b,:b,:a]),
        )
        @testset "$x -> ($dst_lm) $dst_x" begin
            res = if typeof(dst_lm) <: Type && (dst_lm <: LabelEnc.Indices || dst_lm <: LabelEnc.OneOfK)
                convertlabel(dst_lm, x)
            else
                @inferred convertlabel(dst_lm, x)
            end
            @test typeof(res) <: typeof(dst_x)
            @test res == dst_x

            res = @inferred convertlabel(dst_lm, x, LabelEnc.OneOfK(2))
            @test typeof(res) <: typeof(dst_x)
            @test res == dst_x

            # positional obsdim
            res = @inferred convertlabel(dst_lm, x, LabelEnc.OneOfK(2), ObsDim.Constant(2))
            @test typeof(res) <: typeof(dst_x)
            @test res == dst_x
            res = @inferred convertlabel(dst_lm, x, LabelEnc.OneOfK(2), ObsDim.Last())
            @test typeof(res) <: typeof(dst_x)
            @test res == dst_x
            res = @inferred convertlabel(dst_lm, xt, LabelEnc.OneOfK(2), ObsDim.First())
            @test typeof(res) <: typeof(dst_x)
            @test res == dst_x

            # kw obsdim
            res = convertlabel(dst_lm, x, obsdim=2)
            @test typeof(res) <: typeof(dst_x)
            @test res == dst_x
            res = convertlabel(dst_lm, x, LabelEnc.OneOfK(2), obsdim=:last)
            @test typeof(res) <: typeof(dst_x)
            @test res == dst_x
            res = convertlabel(dst_lm, xt, obsdim=1)
            @test typeof(res) <: typeof(dst_x)
            @test res == dst_x
            res = convertlabel(dst_lm, xt, LabelEnc.OneOfK(2), obsdim=1)
            @test typeof(res) <: typeof(dst_x)
            @test res == dst_x
        end
    end
    println("<HEARTBEAT>")
    # To OneOfK
    for (src_lm, src_x) in (
            (LabelEnc.TrueFalse(),BitArray([true,false,true,false,false,true])),
            (LabelEnc.ZeroOne(UInt8),UInt8[1,0,1,0,0,1]),
            (LabelEnc.ZeroOne(Int),Int[1,0,1,0,0,1]),
            (LabelEnc.ZeroOne(),[1.,0,1,0,0,1]),
            (LabelEnc.MarginBased(Float32),Float32[1,-1,1,-1,-1,1]),
            (LabelEnc.MarginBased(Int),Int[1,-1,1,-1,-1,1]),
            (LabelEnc.MarginBased(),[1,-1.,1,-1,-1,1]),
            (LabelEnc.Indices(2),Int[1,2,1,2,2,1]),
            (LabelEnc.Indices(Float32,2),Float32[1,2,1,2,2,1]),
            (LabelEnc.OneVsRest(:yes),[:yes,:not_yes,:yes,:not_yes,:not_yes,:yes]),
            (LabelEnc.NativeLabels([:a,:b]),[:a,:b,:a,:b,:b,:a]),
        )
        for (dst_lm, dst_x) in (
                (LabelEnc.OneOfK,Array{_dst_eltype(eltype(src_x),Int)}(x)),
                (LabelEnc.OneOfK{Float32},Array{Float32}(x)),
                (LabelEnc.OneOfK(Bool,2),BitArray(x)),
             )
            @testset "($src_lm) $src_x -> ($dst_lm) $dst_x" begin
                res = @inferred convertlabel(dst_lm, src_x, src_lm)
                @test typeof(res) <: typeof(dst_x)
                @test res == dst_x

                # positional obsdim
                res = @inferred convertlabel(dst_lm, src_x, src_lm, ObsDim.Constant(2))
                @test typeof(res) <: typeof(dst_x)
                @test res == dst_x
                res = @inferred convertlabel(dst_lm, src_x, src_lm, ObsDim.Last())
                @test typeof(res) <: typeof(dst_x)
                @test res == dst_x
                res = @inferred convertlabel(dst_lm, src_x, src_lm, ObsDim.First())
                # TODO in julia 0.7 we take transposition seriously:
                #   typeof(res) == BitArray{2}
                #   typeof(dst_x') == LinearAlgebra.Adjoint{Bool,BitArray{2}}

                @test typeof(res) <: typeof(dst_x)
                @test res == dst_x'

                # kw obsdim
                res = convertlabel(dst_lm, src_x, obsdim=:last)
                @test typeof(res) <: typeof(dst_x)
                @test res == dst_x
                res = convertlabel(dst_lm, src_x, src_lm, obsdim=:last)
                @test typeof(res) <: typeof(dst_x)
                @test res == dst_x
                res = convertlabel(dst_lm, src_x, obsdim=1)
                @test typeof(res) <: typeof(dst_x)
                @test res == dst_x'
                res = convertlabel(dst_lm, src_x, src_lm, obsdim=1)
                @test typeof(res) <: typeof(dst_x)
                @test res == dst_x'
            end
        end
    end
end

println("<HEARTBEAT>")

@testset "multiclass OneOfK with and without ObsDim" begin
    x = [1 0 0 0 0 1; 0 1 0 1 0 0; 0 0 1 0 1 0]
    xt = x'
    # From OneOfK
    for (dst_lm, dst_x) in (
            (LabelEnc.Indices(3),[1,2,3,2,3,1]),
            (LabelEnc.Indices(Float64,3),Float64[1,2,3,2,3,1]),
            (LabelEnc.NativeLabels([:a,:b,:c]),[:a,:b,:c,:b,:c,:a]),
            ([:a,:b,:c],[:a,:b,:c,:b,:c,:a]),
        )
        @testset "$x -> ($dst_lm) $dst_x" begin
            res = if typeof(dst_lm) <: Type && (dst_lm <: LabelEnc.Indices || dst_lm <: LabelEnc.OneOfK)
                convertlabel(dst_lm, x)
            else
                @inferred convertlabel(dst_lm, x)
            end
            @test typeof(res) <: typeof(dst_x)
            @test res == dst_x

            res = @inferred convertlabel(dst_lm, x, LabelEnc.OneOfK(3))
            @test typeof(res) <: typeof(dst_x)
            @test res == dst_x

            # positional obsdim
            res = @inferred convertlabel(dst_lm, x, LabelEnc.OneOfK(3), ObsDim.Constant(2))
            @test typeof(res) <: typeof(dst_x)
            @test res == dst_x
            res = @inferred convertlabel(dst_lm, x, LabelEnc.OneOfK(3), ObsDim.Last())
            @test typeof(res) <: typeof(dst_x)
            @test res == dst_x
            res = @inferred convertlabel(dst_lm, xt, LabelEnc.OneOfK(3), ObsDim.First())
            @test typeof(res) <: typeof(dst_x)
            @test res == dst_x

            # kw obsdim
            res = convertlabel(dst_lm, x, obsdim=2)
            @test typeof(res) <: typeof(dst_x)
            @test res == dst_x
            res = convertlabel(dst_lm, x, LabelEnc.OneOfK(3), obsdim=:last)
            @test typeof(res) <: typeof(dst_x)
            @test res == dst_x
            res = convertlabel(dst_lm, xt, obsdim=1)
            @test typeof(res) <: typeof(dst_x)
            @test res == dst_x
            res = convertlabel(dst_lm, xt, LabelEnc.OneOfK(3), obsdim=1)
            @test typeof(res) <: typeof(dst_x)
            @test res == dst_x
        end
    end
    # To OneOfK
    @testset "implicit NativeLabels" begin
        res = @inferred convertlabel(LabelEnc.OneOfK(Int,3), [:a,:b,:c,:b,:c,:a], [:a,:b,:c], ObsDim.Constant(1))
        @test typeof(res) <: typeof(x)
        @test res == x'
        res = convertlabel(LabelEnc.OneOfK, [:a,:b,:c,:b,:c,:a], [:a,:b,:c], ObsDim.Constant(1))
        @test typeof(res) <: typeof(x)
        @test res == x'
        res = convertlabel(LabelEnc.OneOfK{Float64}, [:a,:b,:c,:b,:c,:a], [:a,:b,:c], ObsDim.Constant(1))
        @test typeof(res) <: Matrix{Float64}
        @test res == x'
        res = convertlabel(LabelEnc.OneOfK{Float64}, [:a,:b,:c,:b,:c,:a], [:a,:b,:c], obsdim=1)
        @test typeof(res) <: Matrix{Float64}
        @test res == x'
    end
    for (src_lm, src_x) in (
            (LabelEnc.Indices(3),[1,2,3,2,3,1]),
            (LabelEnc.Indices(Float64,3),Float64[1,2,3,2,3,1]),
            (LabelEnc.NativeLabels([:a,:b,:c]),[:a,:b,:c,:b,:c,:a]),
        )
        for (dst_lm, dst_x) in (
                (LabelEnc.OneOfK,Array{(_dst_eltype(eltype(src_x),Int))}(x)),
                (LabelEnc.OneOfK{Float32},Array{Float32}(x)),
                (LabelEnc.OneOfK(Bool,3),BitArray(x)),
             )
            @testset "($src_lm) $src_x -> ($dst_lm) $dst_x" begin
                res = @inferred convertlabel(dst_lm, src_x, src_lm)
                @test typeof(res) <: typeof(dst_x)
                @test res == dst_x

                # positional obsdim
                res = @inferred convertlabel(dst_lm, src_x, src_lm, ObsDim.Constant(2))
                @test typeof(res) <: typeof(dst_x)
                @test res == dst_x
                res = @inferred convertlabel(dst_lm, src_x, src_lm, ObsDim.Last())
                @test typeof(res) <: typeof(dst_x)
                @test res == dst_x
                res = @inferred convertlabel(dst_lm, src_x, src_lm, ObsDim.First())
                @test typeof(res) <: typeof(dst_x)
                @test res == dst_x'

                # kw obsdim
                res = convertlabel(dst_lm, src_x, obsdim=:last)
                @test typeof(res) <: typeof(dst_x)
                @test res == dst_x
                res = convertlabel(dst_lm, src_x, src_lm, obsdim=:last)
                @test typeof(res) <: typeof(dst_x)
                @test res == dst_x
                res = convertlabel(dst_lm, src_x, obsdim=1)
                @test typeof(res) <: typeof(dst_x)
                @test res == dst_x'
                res = convertlabel(dst_lm, src_x, src_lm, obsdim=1)
                @test typeof(res) <: typeof(dst_x)
                @test res == dst_x'
            end
        end
    end
end

@testset "special NativeLabels-Indices examples" begin
    enc = LabelEnc.NativeLabels(["a","b","c","d"])
    @test @inferred(convertlabel(LabelEnc.Indices, "c", enc)) == 3

    csub = SubString("c",1,1)
    @test @inferred(convertlabel(LabelEnc.Indices, csub, enc)) == 3
end


const FILE_80 = """

 Couenne (/home/bgulcan/.julia/v0.6/AmplNLWriter/.solverdata/tmpMSxFvq.nl Mar  6 2018): Infeasible

Options
3
0
1
0
140
0
110
0
objno 0 220
"""

@testset "Test sol file parsing" begin
    @testset "Issue #80" begin
        io = IOBuffer()
        write(io, FILE_80)
        seekstart(io)
        model = AmplNLWriter.AmplNLMathProgModel("", String[], "")
        model.ncon = 140
        model.nvar = 110
        @test !AmplNLWriter.read_sol(io, model)
        @test model.solve_result_num == 220
        @test all(model.solution .=== NaN)
    end
end


push!(LOAD_PATH, "./models")
push!(LOAD_PATH, ".")
push!(LOAD_PATH, "./SparseInverseProblems/src")
using SparseInverseProblemsMod
using SuperResModels
using TestCases
using Utils
using PyCall
@pyimport numpy as np
include("1d_parameters.jl")

# To see the progress in pmap
# To use PmapProgressMeter you need to clone it manually
# do in Julia: Pkg.clone("https://github.com/slundberg/PmapProgressMeter.jl")
# if this is no longer existent, uncoment the alternative pmap in this code.
@everywhere using ProgressMeter
@everywhere using PmapProgressMeter

model_static = SuperResModels.Fourier1d(f_c, x_max, filter_x, num_x)
model_dynamic = SuperResModels.DynamicFourier1d(model_static, v_max, tau, K, num_v*K)
if K*tau != 1.0
    warning(" The function match_points < Utils considers K*tau = 1 to set distance in Omega, change this if it's not the case")
end

@everywhere begin
    model_static = $model_static
    model_dynamic = $model_dynamic
    x_max = $x_max
    v_max = $v_max
    minWeights = $minWeights
    maxWeights = $maxWeights
    K = $K
    tau = $tau
    dataFolder = $dataFolder
    test_case = $test_case
    noises_data = $noises_data
    noises_position = $noises_position
    bins = $bins
    density = $density
    cases = $cases
end

results = pmap(x -> begin sleep(1) ; Utils.generate_and_reconstruct_all(model_static, model_dynamic, bins, density, algIter, test_case, noises_data, noises_position, cases) end, Progress(num_trials), 1:num_trials)
# results = pmap(x -> Utils.generate_and_reconstruct_all(model_static, model_dynamic, bins, density, test_case, noises_data, noises_position, cases), 1:num_trials)
results_array = vcat([result[3] for result in results]...)
separations_array = vcat([result[1] for result in results]...)
separation_dyn_array = vcat([result[2] for result in results]...)

#
now_str = string(now())
now_str = replace(now_str, ":", "-")
now_str = replace(now_str, ".", "-")

# Save results
saveFolder = dataFolder*"/"*now_str
mkdir(saveFolder)
cp("1d_parameters.jl", string(saveFolder, "/1d_parameters.jl"))
cd(saveFolder)

println(" Simulations finished, we save the files into folder :/" *saveFolder )

np.save("datanoise", noises_data)
np.save("positionnoise.npy", noises_position)
np.save("separationDynamic.npy", separation_dyn_array)
np.save("separations.npy", separations_array)
np.save("results.npy", results_array)


#=
Copyright (c) 2015, Intel Corporation

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright notice,
      this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of Intel Corporation nor the names of its contributors
      may be used to endorse or promote products derived from this software
      without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
=#

include("../../src/Sparso.jl")
include("../../src/simple-show.jl")
using Sparso

set_options(SA_ENABLE, SA_VERBOSE, SA_USE_SPMP, SA_CONTEXT, SA_REORDER, SA_REPLACE_CALLS)

#include("./pcg-symgs.jl")
include("utils.jl")

# TEMPORARY: this should be removed in future once structure analysis can
# recognize that L and U are part of A
# Copied from pcg-symgs.jl to avoid affecting other tests if it were changed.
function pcg_symgs(x, A, b, tol, maxiter)
    # These are the only 3 properties we need to discover automatically.
    # We also require SA_CONST_STRUCTURED for A, but that has already been discovered.
#    set_matrix_property(:L, SA_LOWER_OF, :A)
#    set_matrix_property(:U, SA_UPPER_OF, :A)
    set_matrix_property(Dict(
        :A => SA_SYMM_STRUCTURED | SA_SYMM_VALUED,
        :U => SA_CONST_VALUED
        )
    )

    total_time = time()

    trsv_time = 0.
    spmv_time = 0.
    blas1_time = 0.

    L = tril(A)
    U  = SparseMatrixCSC{Cdouble, Cint}(spdiagm(1./diag(A))*triu(A))

    spmv_time -= time()
    r = b - A * x
    spmv_time += time()

    blas1_time -= time()
    normr0 = norm(r)
    z = copy(r)
    blas1_time += time()

    rel_err = 1

    trsv_time -= time()
    Base.SparseMatrix.fwdTriSolve!(L, z)
    Base.SparseMatrix.bwdTriSolve!(U, z)
    trsv_time += time()

    blas1_time -= time()
    p = copy(z) #NOTE: do not write "p=z"! That would make p and z aliased (the same variable)
    rz = dot(r, z)
    blas1_time += time()

    # For statistics only. We get these values so that L, U and A won't be live out of
    # the loop -- to save reverse-reordering of them.
    nnzL = nnz(L)
    nnzU = nnz(U)
    sizeL1 = size(L, 1)
    sizeL2 = size(L, 2)
    nnzA = nnz(A)
    sizeA1 = size(A, 1)
    sizeA2 = size(A, 2)

    k = 1

    while k <= maxiter
        old_rz = rz

        spmv_time -= time()
        Ap = A*p # Ap = Sparso.SpMV(A, p) # This takes most time. Compiler can reorder A to make faster
        spmv_time += time()

        blas1_time -= time()
        alpha = old_rz / dot(p, Ap)
        x += alpha * p
        r -= alpha * Ap
        rel_err = norm(r)/normr0
        blas1_time += time()

        if rel_err < tol
            break
        end

        blas1_time -= time()
        z = copy(r)
        blas1_time += time()

        trsv_time -= time()
        Base.SparseMatrix.fwdTriSolve!(L, z)
          # Could have written as z=L\z if \ is specialized for triangular
        Base.SparseMatrix.bwdTriSolve!(U, z)
          # Could have wrriten as z=U\z if \ is specialized for triangular
        trsv_time += time()

        blas1_time -= time()
        rz = dot(r, z)
        beta = rz/old_rz
        p = z + beta * p
        blas1_time += time()

        k += 1
    end

    total_time = time() - total_time
    println("total = $(total_time)s trsv_time = $(trsv_time)s ($((12.*(nnzL   + nnzU  ) + 2.*8*(sizeL1     + sizeL2    ))*k/trsv_time/1e9) gbps) spmv_time = $(spmv_time)s ($((12.*nnzA   + 8.*(sizeA1     + sizeA2    ))*(k + 1)/spmv_time/1e9) gbps) blas1_time = $blas1_time")
#   println("total = $(total_time)s trsv_time = $(trsv_time)s ($((12.*(nnz(L) + nnz(U)) + 2.*8*(size(L, 1) + size(L, 2)))*k/trsv_time/1e9) gbps) spmv_time = $(spmv_time)s ($((12.*nnz(A) + 8.*(size(A, 1) + size(A, 2)))*(k + 1)/spmv_time/1e9) gbps) blas1_time = $blas1_time")

    return x, k, rel_err
end

if length(ARGS) == 0
    m = 10
    A = generate_symmetric_nonzerodiagonal_sparse_matrix(m)
else
    A = matrix_market_read(ARGS[1], true, true)
    m = size(A, 1)
end
x       = zeros(Float64, m)
b       = ones(Float64, m)
tol     = 1e-7
maxiter = 20000

x, k, rel_err = pcg_symgs(x, A, b, tol, maxiter)
x = zeros(Float64, m)
println("Original: ")
x, k, rel_err = pcg_symgs(x, A, b, tol, maxiter)
println("\tOriginal k=", k)
println("\tOriginal rel_err=", rel_err)

A2 = copy(A) # workaround that we change A in-place
@acc x, k, rel_err = pcg_symgs(x, A2, b, tol, maxiter)
println("\nAccelerated: ")
x = zeros(Float64, m)
@acc x, k, rel_err = pcg_symgs(x, A, b, tol, maxiter)
println("\tAccelerated k=", k)
println("\tAccelerated rel_err=", rel_err)


function guess()
    number = dec(rand(1:10))
    print("Guess my number! ")
    while readline() != number
        print("Nope, try again... ")
    end
    println("Well guessed!")
end

guess()


# License for this file: MIT (expat)
# Copyright 2017-2018, DLR Institute of System Dynamics and Control
#
# This file is part of module
#   ModiaMath.Result (ModiaMath/Result/_module.jl)
#

#---------------------------- Acessing the time series results (various options)
appendUnit(name, unit) = unit == "" ? string(name) : string(name, " [", unit, "]")

"""
    indexOftrailingDot(name::AbstractString)::Int

Return the index of the trailing "." of name. If there is no ".xxx", zero is returned
"""
function indexOftrailingDot(name::AbstractString)::Int
   i = first(something(findlast(".", name), 0:-1))
   return i > 0 && i < length(name) ? i : 0
end

"""
    getStringDictResult(model, res)

Return dictionary of result, from raw result data structure `raw`.
"""
function getStringDictResult(model, res::RawResult)
    nt     = res.nt
    data   = res.data
    result = Dict{AbstractString,Any}()
    for (i, name) in enumerate(res.names)
        result[name] = @view data[1:nt,i]
    end

    return result, nt
end

const StringDictAnyResult = Dict{AbstractString,Any}
resultHeading(result::StringDictAnyResult) = ""
variablesDependingOnStruct(state::Any) = 0

function getSignal(seriesDict::StringDictAnyResult, name::AbstractString)
    hasSignal = false
    if haskey(seriesDict, name)
        sig       = seriesDict[name]
        keyName   = name
        hasSignal = true
    else
        # Handle signal arrays, auch as  a.b.c[3]
        if name[end] == ']'
            indexRange = something(findlast("[", name), 0:-1)
            i = indexRange[1]
            @assert(i >= 2)
            keyName = name[1:i-1]

            if haskey(seriesDict, keyName)
                sig2 = seriesDict[keyName]

                # Build AST and evaluate: sig = sig2[:,...]
                ex1 = Meta.parse(name[i:end])
                ex2 = :( $sig2[:] )
                append!(ex2.args, ex1.args)
                sig = @eval $ex2
                if ndims(sig) != 1
                    @warn "ModiaMath.plot: argument name (= $name) does not characterize one array element\nIndex ranges are not yet supported."
                    return (nothing, keyName, name)
                end
                hasSignal = true
            end

        elseif (i = indexOftrailingDot(name)) > 0
            # Handle vector of structs, such as a.b.c and a.b is the key and c is the field in struct a.b[i]
            keyName   = name[1:i-1]
            fieldName = Symbol(name[i+1:end])
            if haskey(seriesDict, keyName)
                sig3 = seriesDict[keyName]
                if typeof(sig3) <: AbstractVector && length(sig3) > 0 && isstructtype(typeof(sig3[1]))
                    # Signal is a vector of structs and has at least one element
                    if isdefined(sig3[1], fieldName) && fieldtype(typeof(sig3[1]), fieldName) <: Number
                        # Struct has fieldName and fieldName is a number
                        sig = zeros(length(sig3))
                        for i in 1:length(sig3)
                           sig[i] = getfield(sig3[i], fieldName)
                        end
                        hasSignal = true
                    else
                        # Check whether dependent variables are defined
                        vdict = variablesDependingOnStruct(sig3[1])
                        key   = name[i+1:end]
                        if typeof(vdict) != Int
                            if haskey(vdict, key)
                                # Dependent variable fieldName is defined
                                sig = zeros(length(sig3))
                                fc  = vdict[key]
                                for i in 1:length(sig3)
                                    sig[i] = fc(sig3[i])
                                end
                                hasSignal = true
                            end
                        end
                    end
                end
            end
        end
    end

    if hasSignal
        return (sig, keyName, name)

    else
        @warn "ModiaMath.plot: argument name (= $name) is not correct or does not identify a signal in the result."
        return (nothing, name, name)
    end
end

"""
    (xsig, xsigLegend, ysig, ysigLegend) =
           ModiaMath.resultTimeSeries(result, name, xLabel::Bool, xAxis)

For a desired `result` data structure, this function has to be provided
to return the x-vector (`xsig`), the y-vector (`ysig`) and the legend
of the x-vector (`xsigLegend`), and of the y-vector (`ysigLegend`) as Strings, given
the key of the y-vector (`name`) and the key of the x-vector (`xAxis`).
If `xLabel=false` the legend of the x-vector should be an empty string (`""`).
"""
function resultTimeSeries(result::StringDictAnyResult, name, xLabel::Bool, xAxis)
    # Get x-axis signal
    (xsig, xkeyName, xName) = getSignal(result, string(xAxis))
    if xsig == nothing
        return (nothing, nothing, nothing, nothing)
    end
    if ndims(xsig) != 1
        @warn "ModiaMath.plot: argument xAxis (= $xAxis) does not characterize a signal vector."
        return (nothing, nothing, nothing, nothing)
    end
    xsigLegend = xLabel ? appendUnit(string(xAxis), string(unit(xsig[1]))) : ""
    xsig       = convert.(Float64, ustrip.(xsig))

    # Get y-axis signals
    (ysig, ykeyName, yName) = getSignal(result, string(name))

    if ysig == nothing
        return (nothing, nothing, nothing, nothing)
    end

    if ndims(ysig) == 0
        # ysig is a scalar
        # Construct a constant time series with two points at the first and the last value of the time vector
        ysigLegend = [appendUnit(yName, string(unit(ysig)))]
        xsig = [xsig[1], xsig[end]]
        ysig = [ysig   , ysig     ]
    elseif ndims(ysig) == 1
        # ysig is a vector
        ysigLegend = [appendUnit(yName, string(unit(ysig[1])))]
    elseif ndims(ysig) == 2
        # ysig is a matrix
        nLegend = size(ysig,2)
        ysigLegend = Array{String}(undef, nLegend)

        for i = 1:nLegend
            ysigLegend[i] = appendUnit(ykeyName*"["*string(i)*"]", string(unit(ysig[1,i])))
        end
    else
        @warn "ModiaMath.plot: Variable $ykeyName is not plotted because variables with more as one dimension not yet supported."
        return (nothing, nothing, nothing, nothing)
    end

    ysig = convert.(Float64, ustrip.(ysig))

    return (xsig, xsigLegend, ysig, ysigLegend)
end

sizeToString(value::Any)    = string( size(value) )
sizeToString(value::Number) = ""

"""
    table = resultTable(result)

Return the variables stored in `result` in form of a DataFrames table
(which can then be printed/showed in various forms).

`Base.show(io, result)` is defined to print `resultTable(result)`,
in case result is of type ModiaMath.ResultWithVariables.

# Examples
```julia
import ModiaMath
using  Unitful

t = range(0.0, stop=10.0, length=100)
result = Dict{AbstractString,Any}()
result["time"] = t * u"s";
result["phi"]  = sin.(t)u"rad";

# Print table of the variables that are stored in result
println("result variables = ", ModiaMath.resultTable(result))

# Results in
result variables =
│ Row │ name   │ elType  │ sizeOrValue   │ unit   │
│     │ String │ String  │ String        │ String │
├─────┼────────┼─────────┼───────────────┼────────┤
│ 1   │ phi    │ Float64 │ (100,)        │ rad    │
│ 2   │ time   │ Float64 │ (100,)        │ s      │

```

"""
function resultTable(result::StringDictAnyResult)
    resultTable = DataFrames.DataFrame(name=String[], elType=String[], sizeOrValue=String[], unit=String[])

    for key in sort( collect( keys(result) ) )
        value = result[key]

        # Determine unit as string (if columns have different units, provide unit per column)
        strippedValue = ustrip.(value) # Strip units from value
        tvalue = typeof( strippedValue )
        tsize  = ndims(value) > 0 ? sizeToString( strippedValue ) : string( strippedValue )
        if tvalue <: Number
            tunit = string( unit(value) )
        elseif tvalue <: AbstractVector || (tvalue <: AbstractMatrix && size(value,2) == 1)
            tunit = string( unit( value[1] ) )
        elseif tvalue <: AbstractMatrix
            tunit = string( unit( value[1] ) )
            columnsHaveSameUnit = true
            for i in 2:size(value,2)
                if string( unit( value[1,i] ) ) != tunit
                    columnsHaveSameUnit = false
                    break
                end
            end
            if !columnsHaveSameUnit
                tunit = "[" * tunit
                for i in 2:size(value,2)
                     tunit = tunit * ", " * string( unit( value[1,i] ) )
                end
                tunit = tunit * "]"
            end
        else
            tunit = "???"
        end

        push!(resultTable, [key, string( typeof(strippedValue[1]) ), tsize, tunit] )
    end
    return resultTable
end

"""
    mutable struct ResultWithVariables

Struct that is generated as a result of ModiaMath.simulate!(...),
if the model is a Modia3D.AbstractComponentWithVariables struct.
"""
mutable struct ResultWithVariables
    "Dictionary of time series (elements can be Float64/Int64/Bool Vectors or Matrices)"
    series::StringDictAnyResult

    "Dictionary of variables (elements are <: ModiaMath.AbstractVariable with required fields value and unit)"
    var::Dict{Symbol,Any}

    "String used as optional heading of a plot window"
    resultHeading::String

    ResultWithVariables(series, var, resultHeading="") = new(series, var, resultHeading)
end

resultHeading(result::ResultWithVariables) = result.resultHeading

function resultTimeSeries(result::ResultWithVariables, name, xLabel::Bool, xAxis)
    seriesDict = result.series

    # Get x-axis signal
    (xsig, xkeyName, xName) = getSignal(seriesDict, string(xAxis))
    if xsig == nothing
        return (nothing, nothing, nothing, nothing)
    end
    if ndims(xsig) != 1
        @warn "ModiaMath.plot: argument xAxis (= $xAxis) does not characterize a signal vector."
        return (nothing, nothing, nothing, nothing)
    end
    xsigLegend = xLabel ? appendUnit(xName, result.var[Symbol(xkeyName)].unit) : ""

    # Get y-axis signal(s)
    (ysig, ykeyName, yNameAsString) = getSignal(seriesDict, string(name))
    symbol_ykeyName = Symbol(ykeyName)

    if ysig == nothing
        return (nothing, nothing, nothing, nothing)
    end

    yvar = result.var[symbol_ykeyName]
    if ndims(ysig) == 0
        # ysig is a scalar
        # Construct a constant time series with two points at the first and the last value of the time vector
        ysigLegend = [appendUnit(yNameAsString, yvar.unit)]
        xsig = [xsig[1], xsig[end]]
        ysig = [ysig   , ysig     ]

    elseif ndims(ysig) == 1
        # ysig is a vector
        ysigLegend = [appendUnit(yNameAsString, yvar.unit)]

    else
        # sig has more as one dimension
        ysigLegend = Array{String}(undef, length(yvar.value))
        for i in eachindex(yvar.value)
            ysigLegend[i] = appendUnit(ModiaMath.indexToString(symbol_ykeyName, yvar.value, i), yvar.unit)
        end
    end

    return (xsig, xsigLegend, ysig, ysigLegend)
end

function resultTable(result::ResultWithVariables)
    resultTable = DataFrames.DataFrame(name=String[], elType=String[], sizeOrValue=String[], unit=String[], info=String[])
    series      = result.series
    vars        = result.var

    for key in sort( collect( keys(series) ) )
        value = series[key]
        var   = vars[Symbol(key)]
        tsize = ndims(value) > 0 ? sizeToString( value ) : string( value )
        push!(resultTable, [key, string( typeof(value[1]) ), tsize, var.unit, var.info] )
    end
    return resultTable
end

function Base.show(io::IO, result::ResultWithVariables)
    show(io, resultTable(result), summary=false, splitcols=true)
end

getHeading(result, heading) = heading != "" ? heading : resultHeading(result)


import LazyArrays

#=
The macro option "lazy" always produces things like sum(@__dot__(lazy(x+y)))

TensorCast.lazy duplicates the behaviour of LazyArrays.lazy before this PR:
https://github.com/JuliaArrays/LazyArrays.jl/pull/31

Eventually it will be OK not to materialize the BroadcastArray before summing:
https://github.com/JuliaLang/julia/pull/31020
=#

if VERSION < v"2.0.0"

    lazy(::Any) = throw(ArgumentError("function `lazy` exists only for its effect on broadcasting"))

    struct LazyCast{T}
        value::T
    end

    Broadcast.broadcasted(::typeof(lazy), x) = LazyCast(x)

    Broadcast.materialize(x::LazyCast) = LazyArrays.BroadcastArray(x.value)

else

    lazy(x) = LazyArrays.lazy(x)

end


import ..SingleStackUtils: single_stack_diagnostics, NodalStack
using ..ODESolvers

# Convenience wrapper
single_stack_diagnostics(solver_config; kwargs...) = single_stack_diagnostics(
    solver_config.dg.grid,
    solver_config.dg.balance_law,
    gettime(solver_config.solver),
    solver_config.dg.direction;
    prognostic = solver_config.Q,
    auxiliary = solver_config.dg.state_auxiliary,
    diffusive = solver_config.dg.state_gradient_flux,
    hyperdiffusive = solver_config.dg.states_higher_order[2],
    kwargs...,
)

# Convenience wrapper
NodalStack(solver_config; kwargs...) = NodalStack(
    solver_config.dg.balance_law,
    solver_config.dg.grid;
    prognostic = solver_config.Q,
    auxiliary = solver_config.dg.state_auxiliary,
    diffusive = solver_config.dg.state_gradient_flux,
    hyperdiffusive = solver_config.dg.states_higher_order[2],
    kwargs...,
)


example_filepath(example_name, examples_dir="../examples/") =
    joinpath(examples_dir, example_name * ".jl")

@testset "Examples" begin
    @info "Testing examples..."

    @testset "One-dimensional diffusion" begin
        @info "  Testing one-dimensional diffusion example"

        replace_strings = [
            ("size = (1, 1, 128)", "size = (1, 1, 16)"),
            ("stop_iteration = 1000", "stop_iteration = 1"),
            ("simulation.stop_iteration += 10000", "simulation.stop_iteration += 100"),
            ("mp4(", "# mp4(")
        ]

        @test run_script(replace_strings, "one_dimensional_diffusion", example_filepath("one_dimensional_diffusion"))
    end

    @testset "Two-dimensional turbulence example" begin
        @info "  Testing two-dimensional turbulence example"

        replace_strings = [
            ("size=(128, 128, 1)", "size=(16, 16, 1)"),
            ("for i=1:100", "for i=1:1"),
            ("stop_iteration += 10", "stop_iteration += 1"),
            ("mp4(", "# mp4(")
        ]

        @test run_script(replace_strings, "two_dimensional_turbulence", example_filepath("two_dimensional_turbulence"))
    end

    for arch in archs
        @testset "Wind and convection-driven mixing example [$(typeof(arch))]" begin
            @info "  Testing wind and convection-driven mixing example [$(typeof(arch))]"

            replace_strings = [
                ("Nz = 32", "Nz = 16"),
                ("iteration_interval=10", "iteration_interval=1"),
                ("for i in 1:100", "for i in 1:1"),
                ("stop_iteration += 10", "stop_iteration += 1"),
                ("mp4(", "# mp4(")
            ]

            if arch isa GPU
                push!(replace_strings, ("architecture = CPU()", "architecture = GPU()"))
            end

            @test run_script(replace_strings, "ocean_wind_mixing_and_convection",
                             example_filepath("ocean_wind_mixing_and_convection"),
                             string(typeof(arch)))

            rm("ocean_wind_mixing_and_convection.jld2", force=true)
        end
    end

    @testset "Ocean convection with plankton example" begin
        @info "  Testing ocean convection with plankton example"

        replace_strings = [
            ("Nz = 128", "Nz = 16"),
            ("iteration_interval=100", "iteration_interval=1"),
            ("for i = 1:100", "for i = 1:1"),
            ("stop_iteration += 100", "stop_iteration += 1"),
            ("mp4(", "# mp4(")
        ]

        @test run_script(replace_strings, "ocean_convection_with_plankton",
                         example_filepath("ocean_convection_with_plankton"))
    end

    @testset "Internal wave example" begin
        @info "  Testing internal wave example"

        replace_strings = [
            ("Nx = 128", "Nx = 16"),
            ("iteration_interval = 20", "iteration_interval = 1"),
            ("for i=0:100", "for i=1:1"),
            ("stop_iteration += 20", "stop_iteration += 1"),
            ("mp4(", "# mp4(")
        ]

        @test run_script(replace_strings, "internal_wave", example_filepath("internal_wave"))
    end

    @testset "Eady turbulence" begin
        @info "  Testing Eady turbulence example"

        replace_strings = [
            ("Nh = 64", "Nh = 16"),
            ("Nz = 32", "Nz = 16"),
            ("end_time = 3day", "end_time = 1"),
            # Get rid of anything PyPlot/PyCall related
            ("using PyPlot, PyCall", ""),
            ("GridSpec =", "#"),
            ("fig =", "#"),
            ("gs =", "#"),
            ("fig.add_subplot", "#"),
            ("gcf()", "#"),
            ("makeplot!", "#makeplot!"),
            ("function #makeplot!(axs, model)", "function makeplot!(axs, model)")
        ]

        @test run_script(replace_strings, "eady_turbulence", example_filepath("eady_turbulence"))
    end
end


@generated function vrangeincr(::Val{W}, i::I, ::Val{O}) where {W,I<:Integer,O}
    bytes = I === Int ? min(8, VectorizationBase.prevpow2(VectorizationBase.REGISTER_SIZE ÷ W)) : sizeof(I)
    # bytes = min(8, VectorizationBase.prevpow2(VectorizationBase.REGISTER_SIZE ÷ W))
    bits = 8bytes
    jtypesym = Symbol(:Int, bits)
    iexpr = bytes == sizeof(I) ? :i : Expr(:call, :%, :i, jtypesym)
    typ = "i$(bits)"
    vtyp = "<$W x $typ>"
    rangevec = join(("$typ $(w+O)" for w ∈ 0:W-1), ", ")
    instrs = String[]
    push!(instrs, "%ie = insertelement $vtyp undef, $typ %0, i32 0")
    push!(instrs, "%v = shufflevector $vtyp %ie, $vtyp undef, <$W x i32> zeroinitializer")
    push!(instrs, "%res = add $vtyp %v, <$rangevec>")
    push!(instrs, "ret $vtyp %res")
    quote
        $(Expr(:meta,:inline))
        Base.llvmcall(
            $(join(instrs,"\n")), Vec{$W,$jtypesym}, Tuple{$jtypesym}, $iexpr
        )
    end
end
@generated function vrangeincr(::Val{W}, i::T, ::Val{O}) where {W,T<:FloatingTypes,O}
    typ = llvmtype(T)
    vtyp = "<$W x $typ>"
    rangevec = join(("$typ $(w+O).0" for w ∈ 0:W-1), ", ")
    instrs = String[]
    push!(instrs, "%ie = insertelement $vtyp undef, $typ %0, i32 0")
    push!(instrs, "%v = shufflevector $vtyp %ie, $vtyp undef, <$W x i32> zeroinitializer")
    push!(instrs, "%res = fadd $vtyp %v, <$rangevec>")
    push!(instrs, "ret $vtyp %res")
    quote
        $(Expr(:meta,:inline))
        Base.llvmcall(
            $(join(instrs,"\n")), Vec{$W,$T}, Tuple{$T}, i
        )
    end
end
@generated function vrangemul(::Val{W}, i::I, ::Val{O}) where {W,I<:Integer,O}
    bytes = I === Int ? min(8, VectorizationBase.prevpow2(VectorizationBase.REGISTER_SIZE ÷ W)) : sizeof(I)
    bits = 8bytes
    jtypesym = Symbol(:Int, bits)
    iexpr = bytes == sizeof(I) ? :i : Expr(:call, :%, :i, jtypesym)
    typ = "i$(bits)"
    vtyp = "<$W x $typ>"
    rangevec = join(("$typ $(w+O)" for w ∈ 0:W-1), ", ")
    instrs = String[]
    push!(instrs, "%ie = insertelement $vtyp undef, $typ %0, i32 0")
    push!(instrs, "%v = shufflevector $vtyp %ie, $vtyp undef, <$W x i32> zeroinitializer")
    push!(instrs, "%res = mul $vtyp %v, <$rangevec>")
    push!(instrs, "ret $vtyp %res")
    quote
        $(Expr(:meta,:inline))
        Base.llvmcall(
            $(join(instrs,"\n")), Vec{$W,$jtypesym}, Tuple{$jtypesym}, $iexpr
        )
    end
end
@generated function vrangemul(::Val{W}, i::T, ::Val{O}) where {W,T<:FloatingTypes,O}
    typ = llvmtype(T)
    vtyp = "<$W x $typ>"
    rangevec = join(("$typ $(w+O).0" for w ∈ 0:W-1), ", ")
    instrs = String[]
    push!(instrs, "%ie = insertelement $vtyp undef, $typ %0, i32 0")
    push!(instrs, "%v = shufflevector $vtyp %ie, $vtyp undef, <$W x i32> zeroinitializer")
    push!(instrs, "%res = fmul fast $vtyp %v, <$rangevec>")
    push!(instrs, "ret $vtyp %res")
    quote
        $(Expr(:meta,:inline))
        Base.llvmcall(
            $(join(instrs,"\n")), Vec{$W,$T}, Tuple{$T}, i
        )
    end
end
@inline svrangeincr(::Val{W}, i, ::Val{O}) where {W,O} = SVec(vrangeincr(Val{W}(), i, Val{O}()))
@inline svrangemul(::Val{W}, i, ::Val{O}) where {W,O} = SVec(vrangemul(Val{W}(), i, Val{O}()))

@inline vrange(i::_MM{W}) where {W} = vrangeincr(Val{W}(), i.i, Val{0}())
@inline svrange(i::_MM{W}) where {W} = SVec(vrangeincr(Val{W}(), i.i, Val{0}()))
@inline Base.:(+)(i::_MM{W}, j::AbstractSIMDVector{W}) where {W} = vadd(vrange(i), j)
@inline Base.:(+)(i::AbstractSIMDVector{W}, j::_MM{W}) where {W} = vadd(i, vrange(j))
@inline Base.:(*)(i::_MM{W}, j::AbstractSIMDVector{W}) where {W} = vmul(vrange(i), j)
@inline Base.:(*)(i::AbstractSIMDVector{W}, j::_MM{W}) where {W} = vmul(i, vrange(j))

@inline vrange(::Val{W}) where {W} = vrange(Val{W}(), Float64)
@inline svrange(::Val{W}) where {W} = svrange(Val{W}(), Float64)

@inline vrange(i::_MM{W}, ::Type{T}) where {W,T} = vrangeincr(Val{W}(), T(i.i), Val{0}())
@inline vrange(i::_MM{W}, ::Type{T}) where {W,T <: Integer} = vrangeincr(Val{W}(), i.i % T, Val{0}())
@inline svrange(i::_MM, ::Type{T}) where {T} = SVec(vrange(i, T))

@inline Base.:(<<)(i::_MM, j::Integer) = svrange(i) << j
@inline Base.:(>>)(i::_MM, j::Integer) = svrange(i) >> j
@inline Base.:(>>>)(i::_MM, j::Integer) = svrange(i) >>> j

@inline Base.:(*)(i::_MM{W}, j::T) where {W,T} = vmul(svrange(i), j)
@inline Base.:(*)(j::T, i::_MM{W}) where {W,T} = vmul(svrange(i), j)
@inline vconvert(::Type{Vec{W,T}}, i::_MM{W}) where {W,T} = vrange(i, T)
@inline vconvert(::Type{SVec{W,T}}, i::_MM{W}) where {W,T} = svrange(i, T)


@with_kw mutable struct UrbanDriver <: DriverModel{LonAccelDirection}
    a::LonAccelDirection = LonAccelDirection(0., 1)
    navigator::RouteFollowingIDM = RouteFollowingIDM()
    intersection_driver::DriverModel = StopIntersectionDriver()
    crosswalk_drivers::Vector{CrosswalkDriver} = [CrosswalkDriver()]

    debug::Bool = false
end

function AutomotiveDrivingModels.reset_hidden_state!(model::UrbanDriver)
    reset_hidden_state!(model.navigator)
    reset_hidden_state!(model.intersection_driver)
    for driver in model.crosswalk_drivers
        reset_hidden_state!(driver)
    end
    model
end

function AutomotiveDrivingModels.observe!(model::UrbanDriver, scene::Scene, roadway::Roadway, egoid::Int)
    AutomotiveDrivingModels.observe!(model.navigator, scene, roadway, egoid)
    AutomotiveDrivingModels.observe!(model.intersection_driver, scene, roadway, egoid)
    for driver in model.crosswalk_drivers
        AutomotiveDrivingModels.observe!(driver, scene, roadway, egoid)
    end
    a_lon_crosswalks = minimum([driver.a.a_lon for driver in model.crosswalk_drivers])
    # println("intersection driver ", model.intersection_driver.a)
    # println("crosswalk driver ", model.crosswalk_driver.a)
    # take the more conservative of the three actions
    a_lon = min(model.intersection_driver.a.a_lon, a_lon_crosswalks)
    # a_lon = model.intersection_driver.a.a_lon
    # i = indmin([model.intersection_driver.a.a_lon, model.crosswalk_drivers[1].a.a_lon, model.crosswalk_drivers[2].a.a_lon, model.crosswalk_drivers[3].a.a_lon])
    # println(egoid, " getting minimum acceleration from ", i)
    if model.debug
        println("inter ", model.intersection_driver.a.a_lon)
        println("crosswalk 1 ", model.crosswalk_drivers[1].a.a_lon)
    end
    model.a = LonAccelDirection(a_lon, model.navigator.dir)
    return model
end

get_name(::UrbanDriver) = "UrbanDriver"
Base.rand(model::UrbanDriver) = model.a

function action_space(model::UrbanDriver)
    return (LonAccelDirection(model.a.a_lon-model.navigator.σ, model.navigator.dir), LonAccelDirection(model.a.a_lon, model.navigator.dir), LonAccelDirection(model.a.a_lon + model.navigator.σ, model.navigator.dir))
end

function get_distribution(model::UrbanDriver)
    actions = action_space(model)
    probs = (1/3, 1/3, 1/3)
    return actions, probs
end


# Shortcuts for now
export reals, bools, integers, unit

bools(θ) = unit(θ) > 0.5
bound(θ, a, b, c) = unit(θ, T) * b - a + a
reals(θ, T = Float64) = (unit(θ, T) - 0.5) * 100.0
integers(θ) = rand(Int64)

const ℝ = reals
const 𝔹 = bools
const ℤ = integers

function choose(branches, θ)
  return 1
end