using IICGP
using Cambrian
using CartesianGeneticProgramming
using Test

import Cambrian.mutate # function extension

function test_fitness_matrix(e::DualCGPGAEvo)
    esize = (length(e.encoder_sympop), length(e.controller_sympop))
    @test size(e.fitness_matrix) == esize
    nrows, ncols = size(e.fitness_matrix)
    # 1. Test at least 1 eval per row col
    for r in eachrow(e.fitness_matrix)
        @test maximum(r) > -Inf
    end
    for c in eachcol(e.fitness_matrix)
        @test maximum(c) > -Inf
    end
    # 2. Test n_eval eval
    @test length(filter(x->x>-Inf, e.fitness_matrix)) == e.n_eval
    # 3. Test n_elites elites are selected
    @test sum(e.elites_matrix) == e.n_elite
    # 4. Test elites are evaluated and have higher scores
    for i in eachindex(e.elites_matrix)
        if e.elites_matrix[i]
            @test e.fitness_matrix[i] > -Inf
            for j in eachindex(e.fitness_matrix)
                if !e.elites_matrix[j]
                    @test e.fitness_matrix[i] >= e.fitness_matrix[j]
                end
            end
        end
    end
    # 5. Test all pairs in eval_matrix are evaluated
    for i in eachindex(e.elites_matrix)
        if e.elites_matrix[i]
            @test e.fitness_matrix[i] > -Inf
        end
    end
end

function test_ind_fitnesses(e::DualCGPGAEvo)
    # Test that individual's fitness is set according to the fitness matrix
    for i in eachindex(e.encoder_sympop)
        @test length(e.encoder_sympop[i].fitness) == e.config.d_fitness
        @test e.encoder_sympop[i].fitness[1] > -Inf
        @test e.encoder_sympop[i].fitness[1] == maximum(e.fitness_matrix[i,:])
    end
    for j in eachindex(e.controller_sympop)
        @test length(e.controller_sympop[j].fitness) == e.config.d_fitness
        @test e.controller_sympop[j].fitness[1] > -Inf
        @test e.controller_sympop[j].fitness[1] == maximum(e.fitness_matrix[:,j])
    end
end

cfg_filename = string(@__DIR__, "/dualcgpga_test.yaml")
game = "gravitar"
mcfg, ecfg, ccfg, reducer, bootstrap = IICGP.dualcgp_config(cfg_filename, game)
logid = "2021-testlogid"
resdir = dirname(@__DIR__)
function mutate(ind::CGPInd, ind_type::String)
    if ind_type == "encoder"
        return goldman_mutate(ecfg, ind, init_function=IPCGPInd)
    elseif ind_type == "controller"
        return goldman_mutate(ccfg, ind)
    end
end
fit(e::CGPInd, c::CGPInd, seed::Int64) = [e.chromosome[1] * c.chromosome[1]]
n_iter = 4

@testset "CGP GA Evaluation" begin
    evo = IICGP.DualCGPGAEvo(mcfg, ecfg, ccfg, fit, logid, resdir)
    for iter in 1:n_iter
        evaluate(evo)
        test_fitness_matrix(evo)
        test_ind_fitnesses(evo)
        populate(evo)
    end
end


using Tables
using Recommenders: Movielens1M, Movielens100k, download, load_dataset
using Test

@testset "Movielens100k" begin
    ml100k = Movielens100k()
    download(ml100k, usecache = false, removezip = true, unzip = true)
    rating, user, movie = load_dataset(ml100k)

    @test size(rating) == (100000,)
    @test Tables.columnnames(rating) == [:userid, :movieid, :rating, :timestamp]

    @test size(user) == (943,)
    @test Tables.columnnames(user) == [:userid, :age, :gender, :occupation, :zipcode]

    @test size(movie) == (1682,)
    @test Tables.columnnames(movie) == [
        :movieid,
        :movie_title,
        :release_date,
        :video_release_date,
        :IMDbURL,
        :unknown,
        :Action,
        :Adventure,
        :Animation,
        :Childrens,
        :Comedy,
        :Crime,
        :Documentary,
        :Drama,
        :Fantasy,
        :FilmNoir,
        :Horror,
        :Musical,
        :Mystery,
        :Romance,
        :SciFi,
        :Thriller,
        :War,
        :Western,
    ]
end

@testset "Movielent1M" begin
    ml1m = Movielens1M()
    download(ml1m, usecache = false, removezip = true, unzip = true)
    rating, user, movie = load_dataset(ml1m)

    @test size(rating) == (1000209,)
    @test Tables.columnnames(rating) == [:userid, :movieid, :rating, :timestamp]

    @test size(user) == (6040,)
    @test Tables.columnnames(user) == [:userid, :gender, :age, :occupation, :zipcode]
    @test min(user.userid...) == 1
    @test max(user.userid...) == 6040

    @test size(movie) == (3883,)
    @test Tables.columnnames(movie) == [:movieid, :title, :genres]
    @test min(movie.movieid...) == 1
    @test max(movie.movieid...) == 3952
end


using Oceananigans.Fields: ComputedField
using Statistics

import Oceananigans.Fields: AveragedField

"""
    AveragedField(op::AbstractOperation; dims, data=nothing, operand_data=nothing,
                  recompute_safely=false, recompute_operand_safely=false)

Forms a `ComputedField` to store the result of computing `op`, and returns an
`AveragedField` whose operand is the new `ComputedField`, representing an average over
`dims`.

If `data` is not provided, memory is allocated to store the result of the average.
See `AveragedField(field::AbstractField)`.

The keyword argument `operand_data` can be used to specify memory or scratch space
for the new `ComputedField` data.
"""
function AveragedField(op::AbstractOperation; dims, data=nothing, operand_data=nothing,
                       recompute_safely=false, recompute_operand_safely=false)

    computed = ComputedField(op, data=operand_data, recompute_safely=recompute_operand_safely)

    return AveragedField(computed, dims=dims, data=data, recompute_safely=recompute_safely)
end

"""
    mean(op::AbstractOperation; kwargs...)

Returns an Oceananigans.AveragedField representing the an average over `op`eration.
See `Oceananigans.AbstractField`.
"""
Statistics.mean(op::AbstractOperation; kwargs...) = AveragedField(op; kwargs...)


# inflation.jl - Estructura básica para cómputo de estimadores de inflación interanual

"""
    abstract type InflationFunction <: Function

Tipo abstracto para representar las funciones de inflación que operan sobre
[`CountryStructure`](@ref) y [`VarCPIBase`](@ref). Permiten computar la medida
de ritmo inflacionario interanual, el índice de precios dado por la metodología
y las variaciones intermensuales del índice de precios.
"""
abstract type InflationFunction <: Function end

# La mayoría de funciones de inflación devuelven una medida
"""
    num_measures(::InflationFunction)

Devuelve la cantidad de medidas devueltas por la función de inflación. Las
funciones de [`EnsembleFunction`](@ref) pueden computar varias medidas de
inflación simultáneamente.
"""
num_measures(::InflationFunction) = 1

# Funciones de inflación deben extender el método measure_name para devolver su nombre
"""
    measure_name(inflfn::InflationFunction)

Este método permite obtener el nombre convencional de una medida de inflación.
"""
measure_name(inflfn::InflationFunction) =
    error("Se debe extender el método `measure_name` para la función de inflación " * string(nameof(inflfn)))

# Funciones de inflación pueden extender opcionalmente un método measure_tag
# para indicar en archivos de resultados. Si no, se tomará por defecto el nombre
# del tipo de la función de inflación
"""
    measure_tag(inflfn::InflationFunction)

Obtiene una etiqueta de la medida de inflación. Se puede utilizar para guardar
como parámetro en archivos de resultados de evaluación.
"""
measure_tag(inflfn::InflationFunction) = string(nameof(inflfn))

# Método para obtener parámetros
"""
    params(inflfn::InflationFunction)
Método para obtener parámetros de la función de inflación. Devuelve una tupla
con el conjunto de parámetros utilizado por la función de inflación `inflfn`.
Este método debe redefinirse en las nuevas medidas de inflación si estas están
parametrizadas.
"""
params(inflfn::InflationFunction) = getproperty.(Ref(inflfn), propertynames(inflfn))

## Tipos para resultados, utilizados para el despacho de métodos
"Tipo abstracto para manejar el despacho de las funciones de inflación"
abstract type CPIResult end
"Tipo concreto único para obtener el índice de una función de inflación"
struct CPIIndex <: CPIResult end
"""
Tipo concreto único para obtener la variación intermensual de una función de inflación
"""
struct CPIVarInterm <: CPIResult end

## Esquema general de cómputo (programación genérica con tipos abstractos):
# - La función sobre CountryStructure devuelve la inflación interanual sobre
#   todas las bases que componen
# - Esta llama a la función de inflación que recibe `CPIIndex`.
# - Y esta a su vez, llama a la función de inflación que recibe `CPIVarInterm`.
# - Finalmente, se llama al método que opera sobre objetos `VarCPIBase` para
#   obtener la variación intermensual resumen. De tal manera que la mayoría de
#   funciones solamente requieren definir su operación sobre los contenedores
#   `VarCPIBase` y devolver una variación intermensual resumen.

function (inflfn::InflationFunction)(cs::CountryStructure)
    cpi_index = inflfn(cs, CPIIndex())
    varinteran(cpi_index)
end

function (inflfn::InflationFunction)(cs::CountryStructure, ::CPIIndex)
    v_interm = inflfn(cs, CPIVarInterm())
    capitalize!(v_interm, 100) # v_interm -> cpi_index
    v_interm
end

function (inflfn::InflationFunction)(cs::CountryStructure, ::CPIVarInterm)
    # Acá se llama a inflfn(base), en donde base es de tipo VarCPIBase. Esta
    # es la función que debe definirse para cualquier medida de inflación.
    v_interm = mapfoldl(inflfn, vcat, cs.base)
    v_interm
end

# Funciones de inflación deben extender el método que opera sobre VarCPIBase
# para computar la variación intermensual resumen
function (inflfn::InflationFunction)(::VarCPIBase)
    error("Se debe extender un método para computar la variación intermensual resumen de la medida de inflación " * string(nameof(inflfn)))
end


#=
# a customized function to convert number to formatted number
function number_to_string(number::Number; digit::Int=6)
    if digit==0
        return @sprintf("%0.0f", number)
    elseif digit==1
        return @sprintf("%0.1f", number)
    elseif digit==2
        return @sprintf("%0.2f", number)
    elseif digit==3
        return @sprintf("%0.3f", number)
    elseif digit==4
        return @sprintf("%0.4f", number)
    elseif digit==5
        return @sprintf("%0.5f", number)
    else
        return @sprintf("%0.6f", number)
    end
end

# there is an option to save the figure
function visualize_struct_tree(tree::Tree, save::Bool=false)
    # clear plot first
    figure("Tree Visualization",figsize=(16,9), dpi=120)
    clf()

    # display table header
    text(-1.00, tree.h+0.05, L"$P_\mathrm{water}$"            , color=:red   , ha="right", va="bottom", fontsize=8)
    text( 1.00, tree.h+0.05, L"$Q_\mathrm{water}$"            , color=:blue  , ha="left" , va="bottom", fontsize=8)
    text( 1.40, tree.h+0.05, L"$\overline{\mathrm{PAR_{sl}}}$", color=:orange, ha="left" , va="bottom", fontsize=8)
    text( 1.60, tree.h+0.05, L"$\mathrm{PAR_{sh}}$"           , color=:orange, ha="left" , va="bottom", fontsize=8)

    # plot the root system
    for i in 1:length(tree.roots.root_list)
        rooti = tree.roots.root_list[i]

        lx = [         0,          0]
        ly = [rooti.z_hi, rooti.z_lo]
        plot(lx, ly, color=:brown)

        lx = [-rooti.r_layer,          0, rooti.r_layer]
        ly = [ rooti.z_lo   , rooti.z_hi, rooti.z_lo   ]
        plot(lx, ly, color=:brown)

        text(-rooti.r_layer, rooti.z_lo, number_to_string(rooti.p_ups , digit=3), color=:red , ha="right", va="top"   , fontsize=8)
        text(-rooti.r_layer, rooti.z_lo, number_to_string(rooti.p_rhiz, digit=3), color=:red , ha="left" , va="bottom", fontsize=8)
        text( rooti.r_layer, rooti.z_lo, number_to_string(rooti.q     , digit=6), color=:blue, ha="left" , va="top"   , fontsize=8)
    end

    # plot the trunk
    lx = [0, 0, 0]
    ly = [0, tree.trunk.z_lo, tree.trunk.z_hi]
    plot(lx, ly, color=:black)
    text(0, tree.trunk.z_lo    , number_to_string(tree.trunk.p_ups, digit=3), color=:red , ha="right", va="bottom", fontsize=8)
    text(0, tree.trunk.z_hi    , number_to_string(tree.trunk.p_dos, digit=3), color=:red , ha="right", va="top"   , fontsize=8)
    text(0, tree.trunk.z_hi*0.5, number_to_string(tree.trunk.q    , digit=6), color=:blue, ha="left" , va="top"   , fontsize=8)

    # plot the canopy stem and leaf system
    for i in 1:length(tree.branch.branch_list)
        branchi = tree.branch.branch_list[i]
        canopyi = tree.canopy.canopy_list[i]

        # plot the branch vertically
        lx = [           0,            0]
        ly = [branchi.z_lo, branchi.z_hi]
        plot(lx, ly, color=:green)

        # plot the branch horizentally
        lx = [-branchi.r_layer,            0, branchi.r_layer]
        ly = [ branchi.z_hi   , branchi.z_lo, branchi.z_hi   ]
        plot(lx, ly, color=:green)

        # label the stem-leaf joint pressure and flow rate through each branch
        text(-branchi.r_layer, branchi.z_hi, number_to_string(branchi.p_dos, digit=3), color=:red , ha="right", va="top", fontsize=8)
        text( branchi.r_layer, branchi.z_hi, number_to_string(branchi.q    , digit=6), color=:blue, ha="left" , va="top", fontsize=8)

        # calculate and display mean PAR for sunlit leaves and shaded leaves
        par_sl = sum(canopyi.par_list[1:end-1] .* canopyi.la_list[1:end-1]) / sum(canopyi.la_list[1:end-1])
        par_sh = canopyi.par_list[end]
        text(1.40, branchi.z_hi, number_to_string(par_sl, digit=1), color=:orange, ha="left", va="top", fontsize=8)
        text(1.60, branchi.z_hi, number_to_string(par_sh, digit=1), color=:orange, ha="left", va="top", fontsize=8)
    end

    # plot the ground
    plot([-1.2,4], [0,0], "k-", color=:black, linewidth=0.2)

    # set the title and label
    title("Tree system")
    xlabel("Distance from the tree base (m)", fontsize=16)
    ylabel("z (m)"                          , fontsize=16)

    # save the figure
    if save
        savefig("zzz_tree.pdf", bbox_inches="tight")
    end
end
=#


function solve()
    x = parse(Int, readline())
    1 - x
end

println(solve())


#############################################################################
#This file contains the implementation of the the functions that create the
#multiresolution ordering and sparsity pattern from an input consisting of
#a set of points
#############################################################################

#############################################################################
#Implementation of the Heap
#############################################################################

#HeapNode containing the global ID and the distance squared to the points added
mutable struct HeapNode
  Id::Int64
  dist2::Float64
end

#Heap containing its size (for convenience), an array of HeapNodes,
#and and array mapping the Id's of the nodes to their position in the heap.
struct Heap
  N::Int64
  nodes::Array{HeapNode,1}
  lookup::Array{Int64,1}
end

#Funktion swapping the nodes a and b, as identified by their respective heap ID,
#in the heap. Presently implemented by swapping the two values of the nodes.
function _swap!( h::Heap, aheapInd::Int64, bheapInd::Int64 )
  #Assinging the corresponding new values to the lookup table
  h.lookup[ h.nodes[aheapInd].Id ] = bheapInd
  h.lookup[ h.nodes[bheapInd].Id ] = aheapInd
  #Introducing temporary variables for the swapping.
  tempDist2::Float64 = h.nodes[ aheapInd ].dist2
  tempId::Int64 = h.nodes[ aheapInd ].Id
  #Swapping the entries of the corresponding node structs
  h.nodes[ aheapInd ].dist2 = h.nodes[ bheapInd ].dist2
  h.nodes[ aheapInd ].Id = h.nodes[ bheapInd ].Id
  h.nodes[ bheapInd ].dist2 = tempDist2
  h.nodes[ bheapInd ].Id = tempId
end

#Compares the element with heap Index hInd to its two children and if it is smaller
#than one of them, swaps it with the bigger one.
#The output is the the new hindex of the node, if a swap occured, and -1, otherwise.
function _moveDown!( h::Heap, hInd::Int64 )
  #Saving the squared distance of the node that is being swapped down.
  dist2::Float64 = h.nodes[ hInd ].dist2
  #If the both children exist:
  if 2 * hInd + 1 <= h.N
    #If the left child is larger:
    if h.nodes[ 2 * hInd ].dist2 >= h.nodes[ 2 * hInd + 1 ].dist2
      #Check if the Child is larger than the parent:
      if h.nodes[2*hInd ].dist2 >= dist2
        _swap!( h, hInd, 2 * hInd )
        return 2 * hInd
      else
        #No swap occuring:
        return -1
      end
    #If the left child is larger:
    else
      #Check if the Child is larger than the parent:
      if h.nodes[ 2*hInd + 1 ].dist2 >= dist2
        _swap!( h, hInd, 2 * hInd + 1 )
        return 2 * hInd + 1
      else
        #No swap occuring:
        return -1
      end
    end
  #If only one child exists:
  elseif 2 * hInd <= h.N
    #Check if the Child is larger than the parent:
    if h.nodes[ 2 * hInd ].dist2 > dist2
      _swap!( h, hInd, 2 * hInd )
      return 2 * hInd
    end
  end
  #No swap occuring:
  return -1
end

function topId( h::Heap )
  return h.nodes[1].Id
end

function topDist2( h::Heap )
  return h.nodes[1].dist2
end

#Updates (decreases) an element of the heap and restores the heap property.
function update!( h::Heap, Id::Int64, newDist2::Float64 )
  tempInd::Int64 = h.lookup[ Id ]
  h.nodes[ tempInd ].dist2 = newDist2
  while ( tempInd <= h.N ) && ( tempInd >= 0 )
    tempInd = _moveDown!( h, tempInd )
  end
end

#############################################################################
#Introducing the "daycare", which keeps track of the descendants of every node
#############################################################################

mutable struct Daycare
  NParents::Int64
  NChildren::Int64
  NBuffer::Int64

  #This array gives for any parent node the corresponding ID
  IdParents::Array{Int64, 1}
  firstChildren::Array{Int64, 1}
  children::Array{Int64, 1}
  #This array has $N$ entries, that given node Id, the corresponding entry in the list
  #of parents
  lookup::Array{Int64, 1}
end

function newparent( dc::Daycare, IdParent::Int64 )
  dc.NParents = dc.NParents + 1
  dc.IdParents[ dc.NParents ] = IdParent
  dc.firstChildren[ dc.NParents ] = dc.NChildren + 1
  dc.firstChildren[ dc.NParents + 1 ] = dc.NChildren + 1
  dc.lookup[ IdParent ] = dc.NParents
end

function newson( dc::Daycare, IdSon::Int64 )
  if dc.NChildren >= dc.NBuffer
    dc.NBuffer = 2 * dc.NBuffer
    resize!( dc.children, dc.NBuffer )
  end
  dc.NChildren = dc.NChildren + 1
  dc.firstChildren[ dc.NParents + 1 ] = dc.firstChildren[ dc.NParents + 1 ] + 1
  dc.children[ dc.NChildren ] = IdSon
end

#############################################################################
#Implementation of the ordering algorithm
#############################################################################

#############################################################################
#Version without levels
#############################################################################
function _determineChildren!( h::Heap,
                             dc::Daycare,
                             parents::Array{Int64, 1},
                             x::Array{Float64, 2},
                             ID::Int64 )
#Function to determine the children of a node in the ordering
#Inputs:
# h:
#   Heap to keep track of the elements and their distance to the points added
#   the ordering
# dc:
#   The "daycare" keeping track of the nodes and their children
# parents:
#   An array with N elements that in its i-th position contains the Id of the point
#   with Id i.
# x:
#   A d times N array containing the positions of the data points
# ID:
#   The ID of the point, the children of which are being determined

  #caching the Id of the father node
  father::Int64 = parents[ ID ]
  #caching the point
  y::Array{Float64, 1} = x[ :, ID ]
  #adding the a new node to the dc
  newparent( dc, ID )
  #storing the ambient dimension
  d::Int64 = size( x, 1 )

  #caching the distance of the newest elemnt
  pivotDist2::Float64 = topDist2( h )

  #We need to add the children to the new parent
  for index = dc.firstChildren[ dc.lookup[ father ] ]  : ( dc.firstChildren[ dc.lookup[ father ] + 1 ] - 1 )
    j::Int64 = dc.children[ index ]
    dist2::Float64 = dist2eval( x, j , y, d )

    if (dist2 <= pivotDist2)&&( h.nodes[ h.lookup[ j ] ].dist2 >= 0. )
      if dist2 < h.nodes[ h.lookup[ j ] ].dist2
        update!( h, j, dist2 )
      end
      newson( dc, j )
      if sqrt.( h.nodes[ h.lookup[ j ] ].dist2 ) .+ sqrt.( dist2 ) <= sqrt.( pivotDist2 )
        parents[ j ] = ID
      end
    end
  end

  #updating the newest element to distance -1, to indicate that it has been
  #included in the hierarchy already (important in case multiple measurements
  #have the same position
  update!( h, ID, -1. )

end

#function sortPoints( x::Array{Float64, 2}, boundDist::Array{Float64, 1} )
##Function to sort the points in a multicscale way
##Input:
## x:
##   A d times N array containing the measurement points
#  Nbuffer0::Int64 = 10
#  N::Int64 = size( x, 2 )
#  parents::Array{Int64, 1} = Array{Int64, 1}(N)
#  #Create heap:
#  h::Heap = Heap( N, Array{HeapNode, 1}( N ), 1 : 1 : N )
#  for i = 1 : N
#    h.nodes[ i ] = HeapNode( i, 1000000000000 )
#  end
#  #Create daycare
#  dc::Daycare = Daycare( 0, 0, Nbuffer0, Array{Int64, 1}(N), Array{Int64, 1}(N+1),  Array{Int64, 1}(Nbuffer0), Array{Int64, 1}(N))
#  #TODO remove ( only debugging )
#  dc.IdParents .= 0
#  dc.firstChildren .= 0
#  dc.children .= 0
#  dc.lookup .= 0
#  parents .= 0
#  #distances will contain as an i-th entry the distance upon elimination of the i-th
#  #degree of freedom
#  distances::Array{Float64} = zeros( N )
#
#
#  #Setting the first element:
#  #rootID::Int64 = randperm( N )[ 1 ]
#  rootID::Int64 = 1;
#  rootPoint::Array{Float64, 1} = x[ :, rootID ]
#  parents .= rootID
#  newparent( dc, rootID )
#  for i = 1 : N
#    newson( dc, i )
#    distances[1] = max( distances[1], sqrt.( dist2eval( x, i, rootPoint, size( x, 1 ) ) ) )
#    update!( h, i, dist2eval( x, i, rootPoint, size( x, 1 ) ) )
#  end
#
#  #updating the elements
#  for iter = 2 : N
#    pivotId::Int64 = topId( h )
#    distances[ iter ] = sqrt( topDist2( h ) )
#    _determineChildren!( h, dc, parents, x, pivotId )
#  end
#
#  #Output "P, revP, levels"
#  #Adding a vector with the distances, at which the
#
#  return dc.IdParents, dc.lookup, distances
#end

#Evaluates the distance of the point y in d-dimensional space to the jth point
#of the point cloud x
function dist2eval( x::Array{Float64, 2}, j::Int64, y::Array{Float64, 1}, d::Int64 )
  retVal::Float64 = 0.0;
  for i::Int64 = 1 : d
    retVal += ( x[ i, j ] - y[ i ] )^2
  end
  return retVal
end

#############################################################################
#Implementation of the sparsity pattern
#############################################################################

function _determineChildren!( revP::Array{Int64, 1}, distances::Array{Float64, 1}, dc::Daycare, parents::Array{Int64, 1}, x::Array{Float64, 2}, ID::Int64, rho::Float64 )
  #caches the father node
  father::Int64 = parents[ ID ]
  #caches the pivot point
  y::Array{Float64, 1} = x[ :, ID ]
  #Adds the new parent (row) to be added to the sparsity pattern
  newparent( dc, ID )
  #caches the ambient dimension
  d::Int64 = size( x, 1 )
  #caches the distance coresponding to the pivot
  pivotDist::Float64 = distances[ revP[ ID ] ]
  for index = dc.firstChildren[ dc.lookup[ father ] ]  : ( dc.firstChildren[ dc.lookup[ father ] + 1 ] - 1 )
    j::Int64 = dc.children[ index ]
    dist2::Float64 = dist2eval( x, j , y, d )

    if ( revP[ ID ] <= revP[ j ] ) && ( dist2 <= rho^2 * ( pivotDist.^2 ) )
      newson( dc, j )
      if sqrt.( dist2 ) + rho * distances[ revP[ j ] ] <= rho * pivotDist
        parents[ j ] = ID
      end
    end
  end
end

function sortPoints( x::Array{Float64, 2} )
#Function to sort the points in a multicscale way
#Input:
# x:
#   A d times N array containing the measurement points
  Nbuffer0::Int64 = 10
  N::Int64 = size( x, 2 )
  parents::Array{Int64, 1} = Array{Int64, 1}(N)
  #Create heap:
  h::Heap = Heap( N, Array{HeapNode, 1}( N ), 1 : 1 : N )
  for i = 1 : N
    h.nodes[ i ] = HeapNode( i, 1000000000000 )
  end
  #Create daycare
  dc::Daycare = Daycare( 0, 0, Nbuffer0, Array{Int64, 1}(N), Array{Int64, 1}(N+1),  Array{Int64, 1}(Nbuffer0), Array{Int64, 1}(N))
  #TODO remove ( only debugging )
  dc.IdParents .= 0
  dc.firstChildren .= 0
  dc.children .= 0
  dc.lookup .= 0
  parents .= 0
  #distances will contain as an i-th entry the distance upon elimnation of the i-th
  #degree of freedom
  distances::Array{Float64} = zeros( N )

  #Setting the first element:
  #rootID::Int64 = randperm( N )[ 1 ]
  rootID::Int64 = 1;
  rootPoint::Array{Float64, 1} = x[ :, rootID ]
  parents .= rootID
  newparent( dc, rootID )
  for i = 1 : N
    newson( dc, i )
    distances[1] = max( distances[1], sqrt.( dist2eval( x, i, rootPoint, size( x, 1 ) ) ) )
    update!( h, i, dist2eval( x, i, rootPoint, size( x, 1 ) ) )
  end
  update!( h, rootID, -1. )

  #updating the elements
  for iter = 2 : N
    pivotId::Int64 = topId( h )
    distances[ iter ] = sqrt( topDist2( h ) )
    _determineChildren!( h, dc, parents, x, pivotId )
  end

  #Output "P, revP, levels"
  #Adding a vector with the distances, at which the
  return dc.IdParents, dc.lookup, distances
end

#############################################################################
#Implementation of the sparsity pattern
#############################################################################

function sparsityPattern( x::Array{Float64, 2}, P::Array{Int64, 1}, revP::Array{Int64, 1}, distances::Array{Float64, 1}, rho::Float64 )
  d::Int64 = size( x, 1 )
  N::Int64 = size( x, 2 )
  Nbuffer0::Int64 = 10

  parents::Array{Int64, 1} = Array{Int64, 1}(N)
  dc::Daycare = Daycare( 0, 0, Nbuffer0, Array{Int64, 1}(N), Array{Int64, 1}(N+1),  Array{Int64, 1}(Nbuffer0), Array{Int64, 1}(N))

  dc.IdParents .= 0
  dc.firstChildren .= 0
  dc.children .= 0
  dc.lookup .= 0
  parents .= 0

  parents .= P[ 1 ]
  newparent( dc, P[ 1 ] )
  for i = 1 : N
    newson( dc, i )
  end

  for k = 2 : N
    _determineChildren!( revP, distances, dc, parents, x, P[ k ], rho)
  end

  #Sort the jind according to their ordering:
  for i = 1 : N
    dc.children[ dc.firstChildren[ i ] : ( dc.firstChildren[ i + 1 ] - 1 ) ] =
      P[ sort!( revP[ dc.children[ dc.firstChildren[ i ] : ( dc.firstChildren[ i + 1 ] - 1 ) ] ] ) ]
  end

  return dc.firstChildren, revP[ dc.children[ 1 : dc.NChildren ] ]
end


using QuNet

# println("-----Running maxpaths.jl-----")
# include("maxpaths.jl")
# println("-----Running percolations.jl-----")
# include("percolations.jl")
# println("-----Running gridsize.jl-----")
# include("gridsize.jl")
# println("-----Running userpairs.jl-----")
# include("userpairs.jl")
# println("-----Running timedepth.jl-----")
# include("timedepth.jl")
println("Running bandwidth_userpairs.jl")
include("new_bandwidth_userpairs.jl")
println("-----Running bandwidth_memory.jl")
include("new_bandwidth_memory.jl")


# Digital signal plots

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

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

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

    ImPlot_PlotDigital(label_id, x, y, count, offset, stride * sizeof(T))
end

function PlotDigital(x::AbstractArray{T1}, y::AbstractArray{T2}; kwargs...) where {T1<:Real,T2<:Real}
    PlotDigital(promote(x,y)...; kwargs...)
end

# xfield, yfield should be propertynames of eltype(structvec)
function PlotDigital(
    structvec::Vector{T}, xfield::Symbol, yfield::Symbol;
    count::Integer = length(structvec), offset::Integer = 0,
    stride::Integer = 1, label_id::String = ""
) where T

    Tx = fieldtype(T, xfield)
    Ty = fieldtype(T, yfield)
    x_offset = fieldoffset(T, Base.fieldindex(T, xfield))
    y_offset = fieldoffset(T, Base.fieldindex(T, yfield))
    x_ptr = (pointer(structvec, 1) + x_offset) |> Ptr{Tx}
    y_ptr = (pointer(structvec, 1) + y_offset) |> Ptr{Ty}

    if !T.mutable
        # this is somewhat illegal and is used only to pass a pointer through AbstractArray argument into ccall
        x = unsafe_wrap(Vector{Tx}, x_ptr, size(structvec); own = false)
        y = unsafe_wrap(Vector{Ty}, y_ptr, size(structvec); own = false)
        stride = stride * sizeof(T)
    else # two new vectors every 1/60 second...
        x = Vector{Tx}(undef, length(structvec))
        y = Vector{Ty}(undef, length(structvec))
        for (i, val) in enumerate(structvec)
            x[i] = getproperty(val, xfield)
            y[i] = getproperty(val, yfield)
        end
        if Tx !== Ty
            x, y = promote(x, y)
        end
        stride = stride * sizeof(eltype(x))
    end

    ImPlot_PlotDigital(label_id, x, y, count, offset, stride)
end

PlotDigitalG(label_id, getter, data, count, offset=0) =
ImPlot_PlotDigitalG(label_id, getter, data, counter, offset)


"""
    BitCount

type used to hold bit counts and shift amounts
"""
const BitCount = Int16

"""
    BitField

The structured BitField.

fields:
    nbits
    shift
    maskof1s
    maskof0s
    name::Symbol
"""
struct BitField{U} <: AbstractBitField
    nbits::BitCount
    shift::BitCount
    maskof1s::U
    maskof0s::U
    name::Symbol
end

Base.eltype(::Type{BitField{U}}) where {U} = U
Base.eltype(x::BitField{U}) where {U} = U

name(x::BitField{U}) where {U<:UBits} = x.name
nbits(x::BitField{U}) where {U<:UBits} = x.nbits
shift(x::BitField{U}) where {U<:UBits} = x.shift
maskof1s(x::BitField{U}) where {U<:UBits} = x.maskof1s
maskof0s(x::BitField{U}) where {U<:UBits} = x.maskof0s

const BitField8 = BitField{UInt8}
const BitField16 = BitField{UInt16}
const BitField32 = BitField{UInt32}
const BitField64 = BitField{UInt64}
const BitField128 = BitField{UInt128}

BitField8(; name::Symbol, bitspan::Int, bitshift::Int) = BitField(UInt8, bitspan, bitshift, name)
BitField16(; name::Symbol, bitspan::Int, bitshift::Int) = BitField(UInt16, bitspan, bitshift, name)
BitField32(; name::Symbol, bitspan::Int, bitshift::Int) = BitField(UInt32, bitspan, bitshift, name)
BitField64(; name::Symbol, bitspan::Int, bitshift::Int) = BitField(UInt64, bitspan, bitshift, name)
BitField128(; name::Symbol, bitspan::Int, bitshift::Int) = BitField(UInt128, bitspan, bitshift, name)

function BitField(::Type{U}, bitspan::Int, bitshift::Int, name::Symbol) where {U<:UBits}
    span  = BitCount(bitspan)
    shift = BitCount(bitshift)
    validate(U, span, shift)

    maskof1s = onebits(U, span, shift)
    maskof0s = ~maskof1s
    return BitField{U}(span, shift, maskof1s, maskof0s, name)
end

BitField(::Type{U}, name::Symbol, bitspan::Int, bitshift::Int) where {U<:UBits} =
    BitField(U, bitspan, bitshift, name)

BitField(bitspan::Int, bitshift::Int, name::Symbol) = BitField(UInt64, bitspan, bitshift, name)

BitField(name::Symbol, bitspan::Int, bitshift::Int) = BitField(UInt64, bitspan, bitshift, name)

@inline function isolate(bitfield::BitField{U}, source::U) where {U<:UBits}
    return source & bitfield.maskof1s
end

@inline function filter(bitfield::BitField{U}, source::U) where {U<:UBits}
    return source & bitfield.maskof0s
end

@inline function Base.get(bitfield::BitField{U}, source::U) where {U<:UBits}
    return isolate(bitfield, source) >> bitfield.shift
end

@inline function Base.get(bitfield::BitField{U}, source::Base.RefValue{U}) where {U<:UBits}
    return isolate(bitfield, source[]) >> bitfield.shift
end

@inline function Base.get(bitfield::BitField{U}, source::ByRef{S,U}) where {S,U<:UBits}
    return isolate(bitfield, source.ref[]) >> bitfield.shift
end

@inline function put(bitfield::BitField{U}, value::U) where {U<:UBits}
    value << bitfield.shift
end

set(bitfield::BitField{U}, value::B, target::U) where {U<:UBits, B<:Union{UBits,IBits}} =
    set(bitfield, value%U, target)

set!(bitfield::BitField{U}, value::B, target::Base.RefValue{U})  where {U<:UBits, B<:Union{UBits,IBits}} =
    set!(bitfield, value%U, target)

set!(bitfield::BitField{U}, value::B, target::ByRef{S,U})  where {S, U<:UBits, B<:Union{UBits,IBits}} =
    set!(bitfield, value%U, target)

@inline function set(bitfield::BitField{U}, value::U, target::U)  where {U<:UBits}
    return filter(bitfield, target) | put(bitfield, value)
end

@inline function set!(bitfield::BitField{U}, value::U, target::Base.RefValue{U})  where {U<:UBits}
    target[] = set(bitfield, value, target[])
    return target
end

@inline function set!(bitfield::BitField{U}, value::U, target::ByRef{S, U})  where {S, U<:UBits}
    target.ref[] = set(bitfield, value, target.ref[])
    return target
end

function validate(::Type{U}, bitspan::Integer, bitshift::Integer) where {U<:UBits}
    if !(bitspan > 0 && bitspan + bitshift <= bitsof(U))
        if bitspan <= 0
            throw(ErrorException("bitspan ($bitspan) must be > 0"))
        else
            throw(ErrorException("bitspan + bitshift > totalbits: $bitspan + $bitshift > $(bitsizeof(U))"))
        end
    end
    return nothing
end

onebits(::Type{T}, bitspan::I, bitshift::I) where {T<:UBits, I<:Integer} =
    ~(~zero(T) << bitspan) << bitshift


module AirSeaFluxes

export simpleflux, bulkformulae
include("simpleflux.jl")
include("bulkformulae.jl")

end # module


mutable struct FirstReactionMethod <: CoupledSearchAlgorithm
  rates::Vector{Float64}
  total_rate::Float64
end

RatesCache(::FirstReactionMethod) = HasRates()

@inline search_jumps(algorithm::FirstReactionMethod) = search_jump_times(RatesCache(algorithm), algorithm.rates)

@inline update!(algorithm::FirstReactionMethod, state, model, j) = update_jump_rates!(algorithm, state, model)


#
# TimestepVector and TimestepMatrix
#

#
# a.  General
#

# Get a timestep array of type T with N dimensions. Time labels will match those from the time dimension in md
function get_timestep_array(md::ModelDef, T, N, ti, value)
	if isuniform(md)
		first, stepsize = first_and_step(md)
		last = last_period(md)
		first === nothing && @warn "get_timestep_array: first === nothing"
		last === nothing && @warn "get_timestep_array: last === nothing"
        return TimestepArray{FixedTimestep{first, stepsize, last}, T, N, ti}(value)
    else
        TIMES = (time_labels(md)...,)
        return TimestepArray{VariableTimestep{TIMES}, T, N, ti}(value)
    end
end

# Return the index position of the time dimension in the datumdef or parameter. If there is no time dimension, return nothing
get_time_index_position(dims::Union{Nothing, Array{Symbol}}) = findfirst(isequal(:time), dims)
get_time_index_position(obj::Union{AbstractDatumDef, ArrayModelParameter}) = get_time_index_position(dim_names(obj))

function get_time_index_position(obj::AbstractCompositeComponentDef, comp_name::Symbol, datum_name::Symbol)
	get_time_index_position(dim_names(compdef(obj, comp_name), datum_name))
end

const AnyIndex = Union{Int, Vector{Int}, Tuple, Colon, OrdinalRange}

# Helper function for getindex; throws a MissingException if data is missing, otherwise returns data
function _missing_data_check(data, t)
	if data === missing
		throw(MissingException("Cannot get index; data is missing. You may have tried to access a value in timestep $t that has not yet been computed."))
	else
		return data
	end
end

# Helper function for getindex; throws an error if the TimestepIndex index is out of range of the TimestepArray
function _index_bounds_check(data, dim, t)
	if size(data, dim) < t
		error("TimestepIndex index $t extends beyond bounds of TimestepArray dimension $dim")
	end
end

# Helper function for getindex; throws an error if you index into a N-dimensional TimestepArray with only one index
# if N > 1; Note that Julia does allow this and returns the column-major value, but this could produce unexpected
# behavior for users in this case so we do not allow it for now
function _single_index_check(data, idxs)
	num_idxs = length(idxs)
	num_dims = length(size(data))
	if num_idxs < num_dims
		error("Not enough indices provided to index into TimestepArray, $num_idxs provided, $num_dims required")
	end
end

# Helper macro used by connector
macro allow_missing(expr)
	let e = gensym("e")
		retexpr = quote
			try
				$expr
			catch $e
				if $e isa MissingException
					missing
				else
					rethrow($e)
				end
			end
		end
		return esc(retexpr)
	end
end

# Helper functions for TimestepValue type
function _get_time_value_position(times::Union{Tuple, Array}, ts::TimestepValue{T}) where T
	t = findfirst(isequal.(ts.value, times))
	if t === nothing
		error("cannot use TimestepValue with value $(ts.value), value is not in the TimestepArray")
	end

	t_offset = t + ts.offset
	if t_offset > length(times)
		error("cannot get TimestepValue offset of $(ts.offset) from value $(ts.value), offset is after the end of the TimestepArray")
	end
	return t_offset
end

# Helper function to get the array of indices from an Array{TimestepIndex,1} or Array{TimestepValue, 1}
function _get_ts_indices(ts_array::Array{TimestepIndex, 1})
    return [ts.index for ts in ts_array]
end

function _get_ts_indices(ts_array::Array{TimestepValue{T}, 1}, times::Union{Tuple, Array}) where T
	return [_get_time_value_position(times, ts) for ts in ts_array]
end

# Base.firstindex and Base.lastindex
function Base.firstindex(arr::TimestepArray{T_TS, T, N, ti}) where {T_TS, T, N, ti}
	if ti == 1
		return TimestepIndex(1)
	else
		return 1
	end
end

function Base.lastindex(arr::TimestepArray{T_TS, T, N, ti}) where {T_TS, T, N, ti}
	if ti == length(size(arr.data))
		return TimestepIndex(length(arr.data))
	else
		return length(arr.data)
	end
end

function Base.lastindex(arr::TimestepArray{T_TS, T, N, ti}, dim::Int) where {T_TS, T, N, ti}
	if ti == dim
		return TimestepIndex(size(arr.data, dim))
	else
		return size(arr.data, dim)
	end
end

function Base.firstindex(arr::TimestepArray{T_TS, T, N, ti}, dim::Int) where {T_TS, T, N, ti}
	if ti == dim
		return TimestepIndex(1)
	else
		return 1
	end
end

# add axes methos copied from abstarctarray.jl:56
function Base.axes(A::TimestepArray{T_TS, T, N, ti}, d::Int) where {T_TS, T, N, ti}
	_d_lessthan_N = d <= N;
	if d == ti
		if _d_lessthan_N
			return Tuple(TimestepIndex.(1:size(A,d)))
		else
			return TimestepIndex(1)
		end
	else
		if _d_lessthan_N
			if _d_lessthan_N
				return 1:size(A,d)
			else
				return 1
			end
		end
	end
end

#
# b. TimestepVector
#

function Base.length(v::TimestepVector)
	return length(v.data)
end

function Base.getindex(v::TimestepVector, ts::FixedTimestep{FIRST, STEP, LAST}) where {FIRST, STEP, LAST}
	data = v.data[ts.t]
	_missing_data_check(data, ts.t)
end

function Base.getindex(v::TimestepVector, ts::VariableTimestep{TIMES}) where {TIMES}
	data = v.data[ts.t]
	_missing_data_check(data, ts.t)
end

function Base.getindex(v::TimestepVector{FixedTimestep{FIRST, STEP, LAST}, T_data}, ts::TimestepValue{T_time}) where {T_data, FIRST, STEP, LAST, T_time}
	t = _get_time_value_position([FIRST:STEP:LAST...], ts)
	v.data isa SubArray ? view_offset = v.data.offset1 : view_offset = 0
	t = t - view_offset
	data = v.data[t]
	_missing_data_check(data, t)
end

function Base.getindex(v::TimestepVector{VariableTimestep{TIMES}, T_data}, ts::TimestepValue{T_time}) where {T_data, TIMES, T_time}
	t = _get_time_value_position(TIMES, ts)
	v.data isa SubArray ? view_offset = v.data.offset1 : view_offset = 0
	t = t - view_offset
	data = v.data[t]
	_missing_data_check(data, t)
end

function Base.getindex(v::TimestepVector, ts::TimestepIndex)
	_index_bounds_check(v.data, 1, ts.index)
	data = v.data[ts.index]
	_missing_data_check(data, ts.index)
end

function Base.setindex!(v::TimestepVector, val, ts::FixedTimestep{FIRST, STEP, LAST}) where {FIRST, STEP, LAST}
	setindex!(v.data, val, ts.t)
end

function Base.setindex!(v::TimestepVector, val, ts::VariableTimestep{TIMES}) where {TIMES}
	setindex!(v.data, val, ts.t)
end

function Base.setindex!(v::TimestepVector{FixedTimestep{FIRST, STEP, LAST}, T_data}, val, ts::TimestepValue{T_time}) where {T_data, FIRST, STEP, LAST, T_time}
	t = _get_time_value_position([FIRST:STEP:LAST...], ts)
	v.data isa SubArray ? view_offset = v.data.offset1 : view_offset = 0
	t = t - view_offset
	setindex!(v.data, val, t)
end

function Base.setindex!(v::TimestepVector{VariableTimestep{TIMES}, T_data}, val, ts::TimestepValue{T_time}) where {T_data, TIMES, T_time}
	t = _get_time_value_position(TIMES, ts)
	v.data isa SubArray ? view_offset = v.data.offset1 : view_offset = 0
	t = t - view_offset
	setindex!(v.data, val, t)
end

function Base.setindex!(v::TimestepVector, val, ts::TimestepIndex)
	setindex!(v.data, val, ts.index)
end

#
# c. TimestepMatrix
#

function Base.getindex(mat::TimestepMatrix{FixedTimestep{M_FIRST, M_STEP, M_LAST}, T, 1}, ts::FixedTimestep{T_FIRST, T_STEP, T_LAST}, idx::AnyIndex) where {T, M_FIRST, M_STEP, M_LAST, T_FIRST, T_STEP, T_LAST}
	data = mat.data[ts.t, idx]
	_missing_data_check(data, ts.t)
end

function Base.getindex(mat::TimestepMatrix{VariableTimestep{M_TIMES}, T, 1}, ts::VariableTimestep{T_TIMES}, idx::AnyIndex) where {T, M_TIMES, T_TIMES}
	data = mat.data[ts.t, idx]
	_missing_data_check(data, ts.t)
end

function Base.getindex(mat::TimestepMatrix{FixedTimestep{M_FIRST, M_STEP, M_LAST}, T, 2}, idx::AnyIndex, ts::FixedTimestep{T_FIRST, T_STEP, T_LAST}) where {T, M_FIRST, M_STEP, M_LAST, T_FIRST, T_STEP, T_LAST}
	data = mat.data[idx, ts.t]
	_missing_data_check(data, ts.t)
end

function Base.getindex(mat::TimestepMatrix{VariableTimestep{M_TIMES}, T, 2}, idx::AnyIndex, ts::VariableTimestep{T_TIMES}) where {T, M_TIMES, T_TIMES}
	data = mat.data[idx, ts.t]
	_missing_data_check(data, ts.t)
end

function Base.getindex(mat::TimestepMatrix{FixedTimestep{FIRST, STEP, LAST}, T_data, 1}, ts::TimestepValue{T_time}, idx::AnyIndex) where {T_data, FIRST, STEP, LAST, T_time}
	t = _get_time_value_position([FIRST:STEP:LAST...], ts)
	mat.data isa SubArray ? view_offset = mat.data.indices[1][1] + 1 : view_offset = 0
	t = t - view_offset
	data = mat.data[t, idx]
	_missing_data_check(data, t)
end

function Base.getindex(mat::TimestepMatrix{VariableTimestep{TIMES}, T_data, 1}, ts::TimestepValue{T_time}, idx::AnyIndex) where {T_data, TIMES, T_time}
	t = _get_time_value_position(TIMES, ts)
	mat.data isa SubArray ? view_offset = mat.data.indices[1][1] + 1 : view_offset = 0
	t = t - view_offset
	data = mat.data[t, idx]
	_missing_data_check(data, t)
end

function Base.getindex(mat::TimestepMatrix, ts::TimestepIndex, idx::AnyIndex)
	t = ts.index
	_index_bounds_check(mat.data, 1, t)
	data = mat.data[t, idx]
	_missing_data_check(data, t)
end

function Base.getindex(mat::TimestepMatrix{FixedTimestep{FIRST, STEP, LAST}, T_data, 2}, idx::AnyIndex, ts::TimestepValue{T_time}) where {T_data, FIRST, STEP, LAST, T_time}
	t = _get_time_value_position([FIRST:STEP:LAST...], ts)
	mat.data isa SubArray ? view_offset = mat.data.indices[2][1] + 1 : view_offset = 0
	t = t - view_offset
	data = mat.data[idx, t]
	_missing_data_check(data, t)
end

function Base.getindex(mat::TimestepMatrix{VariableTimestep{TIMES}, T_data, 2}, idx::AnyIndex, ts::TimestepValue{T_time}) where {T_data, TIMES, T_time}
	t = _get_time_value_position(TIMES, ts)
	mat.data isa SubArray ? view_offset = mat.data.indices[2][1] + 1 : view_offset = 0
	t = t - view_offset
	data = mat.data[idx, t]
	_missing_data_check(data, t)
end

function Base.getindex(mat::TimestepMatrix, idx::AnyIndex, ts::TimestepIndex)
	t = ts.index
	_index_bounds_check(mat.data, 2, t)
	data = mat.data[idx, t]
	_missing_data_check(data, t)
end

function Base.setindex!(mat::TimestepMatrix{FixedTimestep{M_FIRST, M_STEP, M_LAST}, T, 1}, val, ts::FixedTimestep{T_FIRST, T_STEP, T_LAST}, idx::AnyIndex) where {T, M_FIRST, M_STEP, M_LAST, T_FIRST, T_STEP, T_LAST}
	setindex!(mat.data, val, ts.t, idx)
end

function Base.setindex!(mat::TimestepMatrix{VariableTimestep{M_TIMES}, T, 1}, val, ts::VariableTimestep{T_TIMES}, idx::AnyIndex) where {T, M_TIMES, T_TIMES}
	setindex!(mat.data, val, ts.t, idx)
end

function Base.setindex!(mat::TimestepMatrix{FixedTimestep{M_FIRST, M_STEP, M_LAST}, T, 2}, val, idx::AnyIndex, ts::FixedTimestep{T_FIRST, T_STEP, T_LAST}) where {T, M_FIRST, M_STEP, M_LAST, T_FIRST, T_STEP, T_LAST}
	setindex!(mat.data, val, idx, ts.t)
end

function Base.setindex!(mat::TimestepMatrix{VariableTimestep{M_TIMES}, T, 2}, val, idx::AnyIndex, ts::VariableTimestep{T_TIMES}) where {T, M_TIMES, T_TIMES}
	setindex!(mat.data, val, idx, ts.t)
end

function Base.setindex!(mat::TimestepMatrix{FixedTimestep{FIRST, STEP, LAST}, T_data, 1}, val, ts::TimestepValue{T_time}, idx::AnyIndex) where {T_data, FIRST, STEP, LAST, T_time}
	t = _get_time_value_position([FIRST:STEP:LAST...], ts)
	mat.data isa SubArray ? view_offset = mat.data.indices[1][1] + 1 : view_offset = 0
	t = t - view_offset
	setindex!(mat.data, val, t, idx)
end

function Base.setindex!(mat::TimestepMatrix{VariableTimestep{TIMES}, T_data, 1}, val, ts::TimestepValue{T_time}, idx::AnyIndex) where {T_data, TIMES, T_time}
	t = _get_time_value_position(TIMES, ts)
	mat.data isa SubArray ? view_offset = mat.data.indices[1][1] + 1 : view_offset = 0
	t = t - view_offset
	setindex!(mat.data, val, t, idx)
end

function Base.setindex!(mat::TimestepMatrix{FixedTimestep{FIRST, STEP, LAST}, T_data, 2}, val, idx::AnyIndex, ts::TimestepValue{T_time}) where {T_data, FIRST, STEP, LAST, T_time}
	t = _get_time_value_position([FIRST:STEP:LAST...], ts)
	mat.data isa SubArray ? view_offset = mat.data.indices[2][1] + 1 : view_offset = 0
	t = t - view_offset
	setindex!(mat.data, val, idx, t)
end

function Base.setindex!(mat::TimestepMatrix{VariableTimestep{TIMES}, T_data, 2}, val, idx::AnyIndex, ts::TimestepValue{T_time}) where {T_data, TIMES, T_time}
	t = _get_time_value_position(TIMES, ts)
	mat.data isa SubArray ? view_offset = mat.data.indices[2][1] + 1 : view_offset = 0
	t = t - view_offset
	setindex!(mat.data, val, idx, t)
end

function Base.setindex!(mat::TimestepMatrix, val, idx::AnyIndex, ts::TimestepIndex)
	setindex!(mat.data, val, idx, ts.index)
end

function Base.setindex!(mat::TimestepMatrix, val, ts::TimestepIndex, idx::AnyIndex)
	setindex!(mat.data, val, ts.index, idx)
end

#
# TimestepArray methods
#

# _dotview_helper TODOs - we should add options for if the arg is a TimestepValue
# or an array of TimestepIndexes or TimestepValues
function _dotview_helper(arg)
	if arg isa AbstractTimestep
		return arg.t
	elseif arg isa TimestepIndex
		return arg.index
	else
		return arg
	end
end

function Base.dotview(v::TimestepArray, args...)
	# convert any timesteps to their underlying index
	args = map(_dotview_helper, args)
	Base.dotview(v.data, args...)
end

Base.fill!(obj::TimestepArray, value) = fill!(obj.data, value)

Base.size(obj::TimestepArray) = size(obj.data)

Base.size(obj::TimestepArray, i::Int) = size(obj.data, i)

Base.ndims(obj::TimestepArray{T_ts, T, N, ti}) where {T_ts, T, N, ti} = N

Base.eltype(obj::TimestepArray{T_ts, T, N, ti}) where {T_ts, T, N, ti} = T

first_period(obj::TimestepArray{FixedTimestep{FIRST,STEP, LAST}, T, N, ti}) where {FIRST, STEP, LAST, T, N, ti} = FIRST
first_period(obj::TimestepArray{VariableTimestep{TIMES}, T, N, ti}) where {TIMES, T, N, ti} = TIMES[1]

last_period(obj::TimestepArray{FixedTimestep{FIRST, STEP, LAST}, T, N, ti}) where {FIRST, STEP, LAST, T, N, ti} = (FIRST + (size(obj, 1) - 1) * STEP)
last_period(obj::TimestepArray{VariableTimestep{TIMES}, T, N, ti}) where {TIMES, T, N, ti} = TIMES[end]

time_labels(obj::TimestepArray{FixedTimestep{FIRST, STEP, LAST}, T, N, ti}) where {FIRST, STEP, LAST, T, N, ti} = collect(FIRST:STEP:(FIRST + (size(obj, 1) - 1) * STEP))
time_labels(obj::TimestepArray{VariableTimestep{TIMES}, T, N, ti}) where {TIMES, T, N, ti} = collect(TIMES)

split_indices(idxs, ti) = idxs[1:ti - 1], idxs[ti], idxs[ti + 1:end]

function Base.getindex(arr::TimestepArray{FixedTimestep{A_FIRST, A_STEP, A_LAST}, T, N, ti}, idxs::Union{FixedTimestep{T_FIRST, T_STEP, T_LAST}, AnyIndex}...) where {A_FIRST, A_STEP, A_LAST, T_FIRST, T_STEP, T_LAST, T, N, ti}
	idxs1, ts, idxs2 = split_indices(idxs, ti)
	return arr.data[idxs1..., ts.t, idxs2...]
end

function Base.getindex(arr::TimestepArray{VariableTimestep{A_TIMES}, T, N, ti}, idxs::Union{VariableTimestep{T_TIMES}, AnyIndex}...) where {A_TIMES, T_TIMES, T, N, ti}
	idxs1, ts, idxs2 = split_indices(idxs, ti)
	return arr.data[idxs1..., ts.t, idxs2...]
end

function Base.getindex(arr::TimestepArray{FixedTimestep{FIRST, STEP, LAST}, T_data, N, ti}, idxs::Union{TimestepValue{T_time}, AnyIndex}...) where {T_data, N, ti, FIRST, STEP, LAST, T_time}
	_single_index_check(arr.data, idxs)
	idxs1, ts, idxs2 = split_indices(idxs, ti)
	t = _get_time_value_position([FIRST:STEP:LAST...], ts)
	arr.data isa SubArray ? view_offset = arr.data.indices[ti][1] + 1 : view_offset = 0
	t = t - view_offset
	return arr.data[idxs1..., t, idxs2...]
end

function Base.getindex(arr::TimestepArray{VariableTimestep{TIMES}, T_data, N, ti}, idxs::Union{TimestepValue{T_time}, AnyIndex}...) where {T_data, N, ti, TIMES, T_time}
	_single_index_check(arr.data, idxs)
	idxs1, ts, idxs2 = split_indices(idxs, ti)
	t = _get_time_value_position(TIMES, ts)
	arr.data isa SubArray ? view_offset = arr.data.indices[ti][1] + 1 : view_offset = 0
	t = t - view_offset
	return arr.data[idxs1..., t, idxs2...]
end

function Base.getindex(arr::TimestepArray{FixedTimestep{FIRST, STEP, LAST}, T, N, ti}, idxs::Union{TimestepIndex, AnyIndex}...) where {T, N, ti, FIRST, STEP, LAST}
	_single_index_check(arr.data, idxs)
	idxs1, ts, idxs2 = split_indices(idxs, ti)
	t = ts.index
	_index_bounds_check(arr.data, ti, t)
	return arr.data[idxs1..., t, idxs2...]
end

function Base.getindex(arr::TimestepArray{VariableTimestep{TIMES}, T, N, ti}, idxs::Union{TimestepIndex, AnyIndex}...) where {T, N, ti, TIMES}
	_single_index_check(arr.data, idxs)
	idxs1, ts, idxs2 = split_indices(idxs, ti)
	t = ts.index
	_index_bounds_check(arr.data, ti, t)
	return arr.data[idxs1..., t, idxs2...]
end

function Base.setindex!(arr::TimestepArray{FixedTimestep{A_FIRST, A_STEP, A_LAST}, T, N, ti}, val, idxs::Union{FixedTimestep{T_FIRST, T_STEP, T_LAST}, AnyIndex}...) where {A_FIRST, A_STEP, A_LAST, T_FIRST, T_STEP, T_LAST, T, N, ti}
	idxs1, ts, idxs2 = split_indices(idxs, ti)
	setindex!(arr.data, val, idxs1..., ts.t, idxs2...)
end

function Base.setindex!(arr::TimestepArray{VariableTimestep{A_TIMES}, T, N, ti}, val, idxs::Union{VariableTimestep{T_TIMES}, AnyIndex}...) where {A_TIMES, T_TIMES, T, N, ti}
	idxs1, ts, idxs2 = split_indices(idxs, ti)
	setindex!(arr.data, val, idxs1..., ts.t, idxs2...)
end

function Base.setindex!(arr::TimestepArray{FixedTimestep{FIRST, STEP, LAST}, T_data, N, ti}, val, idxs::Union{TimestepValue{T_time}, AnyIndex}...) where {T_data, N, ti, FIRST, STEP, LAST, T_time}
	_single_index_check(arr.data, idxs)
	idxs1, ts, idxs2 = split_indices(idxs, ti)
	t = _get_time_value_position([FIRST:STEP:LAST...], ts)
	arr.data isa SubArray ? view_offset = arr.data.indices[ti][1] + 1 : view_offset = 0
	t = t - view_offset
	setindex!(arr.data, val, idxs1..., t, idxs2...)
end

function Base.setindex!(arr::TimestepArray{VariableTimestep{TIMES}, T_data, N, ti}, val, idxs::Union{TimestepValue{T_time}, AnyIndex}...) where {T_data, N, ti, TIMES, T_time}
	_single_index_check(arr.data, idxs)
	idxs1, ts, idxs2 = split_indices(idxs, ti)
	t = _get_time_value_position(TIMES, ts)
	arr.data isa SubArray ? view_offset = arr.data.indices[ti][1] + 1 : view_offset = 0
	t = t - view_offset
	setindex!(arr.data, val, idxs1..., t, idxs2...)
end

function Base.setindex!(arr::TimestepArray{FixedTimestep{FIRST, STEP, LAST}, T, N, ti}, val, idxs::Union{TimestepIndex, AnyIndex}...) where {FIRST, STEP, LAST, T, N, ti}
	idxs1, ts, idxs2 = split_indices(idxs, ti)
	setindex!(arr.data, val, idxs1..., ts.index, idxs2...)
end

function Base.setindex!(arr::TimestepArray{VariableTimestep{TIMES}, T, N, ti}, val, idxs::Union{TimestepIndex, AnyIndex}...) where {TIMES, T, N, ti}
	idxs1, ts, idxs2 = split_indices(idxs, ti)
	setindex!(arr.data, val, idxs1..., ts.index, idxs2...)
end

# Indexing with arrays of TimestepIndexes or TimestepValues
function Base.getindex(arr::TimestepArray{TS, T, N, ti}, idxs::Union{Array{TimestepIndex,1}, AnyIndex}...) where {TS, T, N, ti}
	idxs1, ts_array, idxs2 = split_indices(idxs, ti)
	ts_idxs = _get_ts_indices(ts_array)
	return arr.data[idxs1..., ts_idxs, idxs2...]
end

function Base.getindex(arr::TimestepArray{FixedTimestep{FIRST, STEP, LAST}, T_data, N, ti}, idxs::Union{Array{TimestepValue{T_time},1}, AnyIndex}...) where {T_data, N, ti, FIRST, STEP, LAST, T_time}
	idxs1, ts_array, idxs2 = split_indices(idxs, ti)
	ts_idxs = _get_ts_indices(ts_array, [FIRST:STEP:LAST...])
	arr.data isa SubArray ? view_offset = arr.data.indices[ti][1] + 1 : view_offset = 0
	ts_idxs = ts_idxs .- view_offset
	return arr.data[idxs1..., ts_idxs, idxs2...]
end

function Base.getindex(arr::TimestepArray{VariableTimestep{TIMES}, T_data, N, ti}, idxs::Union{Array{TimestepValue{T_time},1}, AnyIndex}...) where {T_data, N, ti, TIMES, T_time}
	idxs1, ts_array, idxs2 = split_indices(idxs, ti)
	ts_idxs = _get_ts_indices(ts_array, TIMES)
	arr.data isa SubArray ? view_offset = arr.data.indices[ti][1] + 1 : view_offset = 0
	ts_idxs = ts_idxs .- view_offset
	return arr.data[idxs1..., ts_idxs, idxs2...]
end

function Base.setindex!(arr::TimestepArray{TS, T, N, ti}, vals, idxs::Union{Array{TimestepIndex,1}, AnyIndex}...) where {TS, T, N, ti}
	idxs1, ts_array, idxs2 = split_indices(idxs, ti)
	ts_idxs = _get_ts_indices(ts_array)
	setindex!(arr.data, vals, idxs1..., ts_idxs, idxs2...)
end

function Base.setindex!(arr::TimestepArray{FixedTimestep{FIRST, STEP, LAST}, T_data, N, ti}, vals, idxs::Union{Array{TimestepValue{T_time},1}, AnyIndex}...) where {T_data, N, ti, FIRST, STEP, LAST, T_time}
	idxs1, ts_array, idxs2 = split_indices(idxs, ti)
	ts_idxs = _get_ts_indices(ts_array, [FIRST:STEP:LAST...])
	arr.data isa SubArray ? view_offset = arr.data.indices[ti][1] + 1 : view_offset = 0
	ts_idxs = ts_idxs .- view_offset
	setindex!(arr.data, vals, idxs1..., ts_idxs, idxs2...)
end

function Base.setindex!(arr::TimestepArray{VariableTimestep{TIMES}, T_data, N, ti}, vals, idxs::Union{Array{TimestepValue{T_time},1}, AnyIndex}...) where {T_data, N, ti, TIMES, T_time}
	idxs1, ts_array, idxs2 = split_indices(idxs, ti)
	ts_idxs = _get_ts_indices(ts_array, TIMES)
	arr.data isa SubArray ? view_offset = arr.data.indices[ti][1] + 1 : view_offset = 0
	ts_idxs = ts_idxs .- view_offset
	setindex!(arr.data, vals, idxs1..., ts_idxs, idxs2...)
end

"""
	hasvalue(arr::TimestepArray, ts::FixedTimestep)

Return `true` or `false`, `true` if the TimestepArray `arr` contains the Timestep `ts`.
"""
function hasvalue(arr::TimestepArray{FixedTimestep{A_FIRST, A_STEP, A_LAST}, T, N, ti}, ts::FixedTimestep{T_FIRST, T_STEP, T_LAST}) where {T, N, ti, A_FIRST, A_STEP, A_LAST, T_FIRST, T_STEP, T_LAST}
	return A_FIRST <= gettime(ts) <= last_period(arr)
end
"""
	hasvalue(arr::TimestepArray, ts::VariableTimestep)

Return `true` or `false`, `true` if the TimestepArray `arr` contains the Timestep `ts`.
"""
function hasvalue(arr::TimestepArray{VariableTimestep{A_TIMES}, T, N, ti}, ts::VariableTimestep{T_TIMES}) where {T, N, ti, A_TIMES, T_TIMES}
	return A_TIMES[1] <= gettime(ts) <= last_period(arr)
end

"""
	hasvalue(arr::TimestepArray, ts::FixedTimestep, idxs::Int...)

Return `true` or `false`, `true` if the TimestepArray `arr` contains the Timestep `ts` within
indices `idxs`. Used when Array and Timestep have different FIRST, validating all dimensions.
"""
function hasvalue(arr::TimestepArray{FixedTimestep{A_FIRST, A_STEP, A_LAST}, T, N, ti},
	ts::FixedTimestep{T_FIRST, T_STEP, T_LAST},
	idxs::Int...) where {T, N, ti, A_FIRST, A_STEP, A_LAST, T_FIRST, T_STEP, T_LAST}
	return A_FIRST <= gettime(ts) <= last_period(arr) && all([1 <= idx <= size(arr, i) for (i, idx) in enumerate(idxs)])
end

"""
	hasvalue(arr::TimestepArray, ts::VariableTimestep, idxs::Int...)

Return `true` or `false`, `true` if the TimestepArray `arr` contains the Timestep `ts` within
indices `idxs`. Used when Array and Timestep have different TIMES, validating all dimensions.
"""
function hasvalue(arr::TimestepArray{VariableTimestep{A_TIMES}, T, N, ti},
	ts::VariableTimestep{T_TIMES},
	idxs::Int...) where {T, N, ti, A_TIMES, T_TIMES}

	return A_TIMES[1] <= gettime(ts) <= last_period(arr) && all([1 <= idx <= size(arr, i) for (i, idx) in enumerate(idxs)])
end

##
## DEPRECATIONS - Should move from warning --> error --> removal
##

# -- throw errors --

const AnyIndex_NonColon = Union{Int, Vector{Int}, Tuple, OrdinalRange}

# Helper function for getindex; throws an error if one indexes into a TimestepArray with an integer
function _throw_int_getindex_error()
	error("Indexing with getindex into a TimestepArray with Integer(s) is deprecated, please index with a TimestepIndex(index::Int) instead ie. instead of t[2] use t[TimestepIndex(2)]")
end

# Helper function for setindex; throws an error if one indexes into a TimestepArray with an integer
function _throw_int_setindex_error()
	error("Indexing with setindex into a TimestepArray with Integer(s) is deprecated, please index with a TimestepIndex(index::Int) instead ie. instead of t[2] use t[TimestepIndex(2)]")
end

# int indexing version supports old-style components and internal functions, not
# part of the public API

function Base.getindex(v::TimestepVector, i::AnyIndex_NonColon)
	_throw_int_getindex_error()
end

function Base.setindex!(v::TimestepVector, val, i::AnyIndex_NonColon)
	_throw_int_setindex_error()
end

# int indexing version supports old-style components and internal functions, not
# part of the public API

function Base.getindex(mat::TimestepMatrix, idx1::AnyIndex_NonColon, idx2::AnyIndex_NonColon)
	_throw_int_getindex_error()
end

function Base.setindex!(mat::TimestepMatrix, val, idx1::Int, idx2::Int)
	_throw_int_setindex_error()
end

# Colon support - this allows the time dimension to be indexed with a colon

function Base.getindex(arr::TimestepArray{FixedTimestep{FIRST, STEP, LAST}, T, N, ti}, idxs::AnyIndex...) where {FIRST, STEP, LAST, T, N, ti}
	isa(idxs[ti], AnyIndex_NonColon) ? _throw_int_getindex_error() : nothing
	return arr.data[idxs...]
end

function Base.getindex(arr::TimestepArray{VariableTimestep{TIMES}, T, N, ti}, idxs::AnyIndex...) where {TIMES, T, N, ti}
	isa(idxs[ti], AnyIndex_NonColon) ? _throw_int_getindex_error() : nothing
	return arr.data[idxs...]
end

function Base.setindex!(arr::TimestepArray{FixedTimestep{FIRST, STEP, LAST}, T, N, ti}, val, idxs::AnyIndex...) where {FIRST, STEP, LAST, T, N, ti}
	isa(idxs[ti], AnyIndex_NonColon) ? _throw_int_setindex_error() : nothing
	setindex!(arr.data, val, idxs...)
end

function Base.setindex!(arr::TimestepArray{VariableTimestep{TIMES}, T, N, ti}, val, idxs::AnyIndex...) where {TIMES, T, N, ti}
	isa(idxs[ti], AnyIndex_NonColon) ? _throw_int_setindex_error() : nothing
	setindex!(arr.data, val, idxs...)
end


# For numbers of a magnitude less than ~1000
function almostEqual(iterable1, iterable2, allowedError=0.0000000000001)
    if size(iterable1, 1) != size(iterable2, 1)
        s1 = size(iterable1, 1)
        s2  = size(iterable2, 1)
        println("Size mismatch: $s1 vs $s2")
        println(iterable1)
        println(iterable2)
        return false
    end

    if ndims(iterable1) >= 2
        return matrixAlmostEqual(iterable1, iterable2, allowedError)
    end

    for i in 1:size(iterable1, 1)
        if abs(iterable1[i] - iterable2[i]) > allowedError
            error = iterable1[i] - iterable2[i]
            println("Error: mismatch of $error at position $i")
            println(iterable1)
            println(iterable2)
            return false
        end
    end

    return true
end

function matrixAlmostEqual(m1, m2, allowedError=0.0000000000001)
    s1 = size(m1, 1)

    if size(m1) != size(m2)
        return false
    end

    for i in 1:s1
        if almostEqual(m1[i,:], m2[i,:], allowedError) != true
            return false
        end
    end

    return true
end


module TestCLBLAS
using Base.Test

using OpenCL: cl
import OpenCL.cl.CLArray
using CLBLAS

CLBLAS.setup()
dev, ctx, q = cl.create_compute_context()
const testtypes = (Float32, Float64)
include("test_l1.jl")
include("test_l2.jl")
include("test_l3.jl")

end


println("**********Testing FeatureVector**********")

facts("Creating a FeatureVector") do
  dict1 = ["word" => 4, "another" => 3]
  fv1 = FeatureVector(dict1)
  fv2 = FeatureVector()

  @fact fv1.total => 7
  @fact fv2.total => 0
  @fact fv1.map => dict1
  @fact typeof(fv1.map) => typeof(dict1)
  @fact typeof(fv2.map) => typeof(Dict{Any,Number}())
end

facts("Get value of FeatureVector given key") do
  dict1 = ["word" => 4, "another" => 3]
  fv1 = FeatureVector(dict1)

  @fact fv1.total => 7
  @fact fv1["word"] => fv1.map["word"]
end

facts("Sanitize Dictionary values of 0") do
  dict = ["word" => 4, "another" => 3, "help"=>0]
  fv = FeatureVector(dict)
  @fact fv.total => 7
  fv["the"] = 1
  @fact fv.total => 8
  fv["the"] = 0
  @fact fv.total => 7

  @fact fv.map => ["word"=>4,"another"=>3]
  add!(fv,(-1*fv))
  @fact isempty(fv) => true
  @fact fv.total => 0
end

facts("Set value of FeatureVector given key") do
  dict1 = ["word" => 4, "another" => 3]
  fv1 = FeatureVector(dict1)
  value = fv1["word"]

  fv1["word"] = 7

  @fact (fv1["word"] != dict1["word"]) => true
  @fact fv1["word"] => 7
  @fact dict1["word"] => 4
end

facts("Check to see if a key exists in a FeatureVector") do
  dict1 = ["word" => 4, "another" => 3]
  fv1 = FeatureVector(dict1)

  @fact haskey(fv1, "word") => true
  @fact haskey(fv1, "another") => true
  @fact haskey(fv1, "nope") => false
end

facts("Get keys of FeatureVector") do
  dict1 = ["word" => 4, "another" => 3]
  fv1 = FeatureVector(dict1)

  @fact keys(fv1) => Base.keys(fv1.map)
end

facts("Get values of FeatureVector") do
  dict1 = ["word" => 4, "another" => 3]
  fv1 = FeatureVector(dict1)

  @fact values(fv1) => Base.values(fv1.map)
end

facts("Check isempty on a FeatureVector") do
  fv1 = FeatureVector()

  @fact isempty(fv1) => true

  fv1["the"] = 3

  @fact isempty(fv1) => false

end

facts("Make copy of a FeatureVector") do
  dict1 = ["word" => 4, "another" => 3]
  fv1 = FeatureVector(dict1)
  fv2 = copy(fv1)

  for key in keys(fv1)
          @fact fv1[key] => fv2[key]
  end
  @fact fv2 == fv1 => false
  @fact typeof(fv1) => typeof(fv2)
  @fact fv2.total => fv1.total
end

facts("Find common type of a FeatureVector and an empty FeatureVector") do
  dict1 = ["word" => 4, "∂" => 3]
  fv1 = FeatureVector(dict1)
  fv2 = FeatureVector()

  @fact find_common_type(fv1,fv2) => (UTF8String,Int64)
  @fact find_common_type(fv2,fv1) => (UTF8String,Int64)
end

facts("Find common type of two FeatureVector") do
  dict1 = ["word" => 4, "another" => 3]
  dict2 = ["word" => .2, "∂" => .8]
  fv1 = FeatureVector(dict1)
  fv2 = FeatureVector(dict2)

  @fact find_common_type(fv1,fv2) => (UTF8String,Float64)
end

facts("Adding two FeatureVectors") do
  dict1 = ["word" => 4, "another" => 3]
  dict2 = ["word" => 2, "∂" => 3]
  fv1 = FeatureVector(dict1)
  fv2 = FeatureVector(dict2)
  dict3 = ["word"=>6,"∂"=>3,"another"=>3]
  fv3 = (fv1 + fv2)
  fv4 = (fv2 + fv1)
  for key in Base.keys(dict3)
    @fact fv3[key] => dict3[key]
  end
  for key in Base.keys(dict3)
    @fact fv4[key] => dict3[key]
  end
  @fact fv3.total => (fv1.total + fv2.total)
  @fact fv4.total => (fv1.total + fv2.total)
end

facts("Subtracting two FeatureVectors") do
  dict1 = ["word" => 4, "another" => 3]
  dict2 = ["word" => 2, "∂" => 3]
  fv1 = FeatureVector(dict1)
  fv2 = FeatureVector(dict2)
  dict3 = ["word"=>2,"∂"=>-3,"another"=>3]
  fv3 = (fv1 - fv2)
  dict4 = ["word"=>-2,"∂"=>3,"another"=>-3]
  fv4 = (fv2 - fv1)
  for key in Base.keys(dict3)
    @fact fv3[key] => dict3[key]
  end
  for key in Base.keys(dict4)
    @fact fv4[key] => dict4[key]
  end
  @fact fv3.total => (fv1.total - fv2.total)
  @fact fv4.total => (fv2.total - fv1.total)
end

facts("Multiply a FeatureVector by a scalar") do
  dict1 = ["word" => 4, "another" => 3]
  dict2 = ["word" => .4, "∂" => .3]
  fv1 = FeatureVector(dict1)
  fv2 = FeatureVector(dict2)
  fv3 = FeatureVector()

  fv1 = fv1 * 2
  fv2 = fv2 * 2
  fv4 = fv3 * 2

  @fact fv1["word"] => 8
  @fact fv1["another"] => 6
  @fact fv1.total => 14
  @fact fv2["word"] => .8
  @fact fv2["∂"] => .6
  @fact fv2.total => 1.4
  @fact fv3 => fv4
  @fact fv3.total => 0
  @fact fv4.total => 0
end

facts("Divide a FeatureVector by a scalar") do
  dict1 = ["word" => 4, "another" => 3]
  dict2 = ["word" => .4, "∂" => .3]
  fv1 = FeatureVector(dict1)
  fv2 = FeatureVector(dict2)
  fv3 = FeatureVector()

  fv1 = fv1 / 2
  fv2 = fv2 / 2
  fv4 = fv3 / 2

  @fact fv1["word"] => 2
  @fact fv1["another"] => 1.5
  @fact fv1.total => 3.5
  @fact fv2["word"] => .2
  @fact fv2["∂"] => .15
  @fact fv2.total => .35
  @fact fv3 => fv4
  @fact fv3.total => 0
  @fact fv4.total => 0
end

facts("Rationalize a FeatureVector by a scalar") do
  dict1 = ["word" => 4, "another" => 3]
  fv1 = FeatureVector(dict1)
  fv2 = FeatureVector()

  fv1 = fv1 // 2
  fv3 = fv2 // 2

  @fact fv1["word"] => 2//1
  @fact fv1["another"] => 3//2
  @fact fv1.total => 7//2
  @fact fv2 => fv3
  @fact fv2.total => 0
  @fact fv3.total => 0
end

facts("Find dist_cos between two FeatureVectors") do
  fv = FeatureVector()
  fw = FeatureVector(Dict(["x"=>0, "y"=>0]))
  fv1 = FeatureVector(Dict(["x"=>1, "y"=>0]))
  fv2 = FeatureVector(Dict(["x"=>0, "y"=>1]))
  fv3 = FeatureVector(Dict(["x"=>1, "y"=>1]))
  fv4 = FeatureVector(Dict(["x"=>1.0, "y"=>sqrt(3)]))

  @fact dist_cos(fv3,fv3) => 0
  @fact isnan(dist_cos(fw,fv3)) => true
  @fact isnan(dist_cos(fv,fv1)) => true
  @fact 1 - cos(90*pi/180) - 1e-15 <= dist_cos(fv1,fv2) <= 1 - cos(90*pi/180) + 1e-15 => true
  @fact 1 - cos(45*pi/180) - 1e-15 <= dist_cos(fv2,fv3) <= 1 - cos(45*pi/180) + 1e-15 => true
  @fact 1 - cos(45*pi/180) - 1e-15 <= dist_cos(fv1,fv3) <= 1 - cos(45*pi/180) + 1e-15 => true
  @fact 1 - cos(60*pi/180) - 1e-15 <= dist_cos(fv1,fv4) <= 1 - cos(60*pi/180) + 1e-15 => true
  @fact 1 - cos(30*pi/180) - 1e-15 <= dist_cos(fv2,fv4) <= 1 - cos(30*pi/180) + 1e-15 => true
  @fact 1 - cos(0*pi/180) - 1e-15 <= dist_cos(fv1,fv1) <= 1 - cos(0*pi/180) + 1e-15 => true
end

facts("Find dist_zero between two FeatureVectors") do
  fv = FeatureVector()
  fv1 = FeatureVector(Dict(["x"=>2,"y"=>7]))
  fv2 = FeatureVector(Dict(["x"=>0,"y"=>4]))
  fv3 = FeatureVector(Dict(["x"=>3,"y"=>0]))
  fv4 = FeatureVector(Dict(["x"=>0,"y"=>0]))

  @fact dist_zero(fv,fv4) => 0
  @fact dist_zero(fv1,fv1) => 0
  @fact dist_zero(fv1,fv) == dist_zero(fv1,fv4) == 2 => true
  @fact dist_zero(fv2,fv) == dist_zero(fv3,fv4) == 1 => true
  @fact dist_zero(fv1,fv2) == dist_zero(fv1,fv3) == 1 => true
  @fact dist_zero(fv2,fv3) => 2
end

facts("Find dist_taxicab between two FeatureVectors") do
  fv = FeatureVector()
  fv1 = FeatureVector(Dict(["x"=>2,"y"=>2]))
  fv2 = FeatureVector(Dict(["x"=>0,"y"=>2]))
  fv3 = FeatureVector(Dict(["x"=>2,"y"=>0]))
  fv4 = FeatureVector(Dict(["x"=>0,"y"=>0]))

  @fact dist_taxicab(fv,fv4,false) => 0
  @fact dist_taxicab(fv1,fv1,false) => 0
  @fact dist_taxicab(fv1,fv,false) == dist_taxicab(fv1,fv4,false) == 4 => true
  @fact dist_taxicab(fv2,fv,false) == dist_taxicab(fv3,fv4,false) == 2 => true
  @fact dist_taxicab(fv1,fv2,false) == dist_taxicab(fv1,fv3,false) == 2 => true
  @fact dist_taxicab(fv2,fv3,false) => 4
end

facts("Find dist_euclidean between two FeatureVectors") do
  fv = FeatureVector()
  fv1 = FeatureVector(Dict(["x"=>2,"y"=>2]))
  fv2 = FeatureVector(Dict(["x"=>0,"y"=>2]))
  fv3 = FeatureVector(Dict(["x"=>2,"y"=>0]))
  fv4 = FeatureVector(Dict(["x"=>0,"y"=>0]))

  @fact dist_euclidean(fv,fv4,false) => 0
  @fact dist_euclidean(fv1,fv1,false) => 0
  @fact dist_euclidean(fv1,fv,false) == dist_euclidean(fv1,fv4,false) == sqrt(8) => true
  @fact dist_euclidean(fv2,fv,false) == dist_euclidean(fv3,fv4,false) == 2 => true
  @fact dist_euclidean(fv1,fv2,false) == dist_euclidean(fv1,fv3,false) == 2 => true
  @fact dist_euclidean(fv2,fv3,false) => sqrt(8)
end

facts("Find dist_infinite between two FeatureVectors") do
  fv = FeatureVector()
  fv1 = FeatureVector(Dict(["x"=>10,"y"=>5]))
  fv2 = FeatureVector(Dict(["x"=>0,"y"=>5]))
  fv3 = FeatureVector(Dict(["x"=>10,"y"=>0]))
  fv4 = FeatureVector(Dict(["x"=>0,"y"=>0]))

  @fact dist_infinite(fv,fv4,false) => 0
  @fact dist_infinite(fv1,fv,false) == dist_infinite(fv1,fv4,false) == 10 => true
  @fact dist_infinite(fv1,fv1,false) => 0
  @fact dist_infinite(fv2,fv,false) => 5
  @fact dist_infinite(fv3,fv4,false) => 10
  @fact dist_infinite(fv1,fv2,false) => 10
  @fact dist_infinite(fv1,fv3,false) => 5
  @fact dist_infinite(fv2,fv3,false) => 10
end


# test evolving graph
g = EvolvingGraph{Node{Int}, String}()
add_edge!(g, 1, 2, "t1")
add_edge!(g, 2, 3, "t2")
add_edge!(g, 4, 2, "t2")
add_edge!(g, 4, 2, "t1")
add_edge!(g, 2, 1, "t3")

@test issorted(g) == false

g1 = sort_timestamps(g)

@test issorted(g1) == true
@test num_timestamps(g1) == 3

slice_timestamps!(g1, "t1", "t2")

@test num_timestamps(g1) == 2

p = sortperm(g.timestamps)

for e in edges(g1)
    @test (e in edges(g)) == true
end


function gencodeface(filename::String, f, xdg, udg, odg, wdg, uhg, nlg, tau, uinf, param, time)

foldername = "app";
opufile = "opu" * filename;
cpufile = "cpu" * filename;
gpufile = "gpu" * filename;

nbc = size(f,2);

stropu = "";
strgpu = "";
for k = 1:nbc
    str1, str2 = gencodebou(filename * string(k), f[:,k], xdg, udg, odg, wdg, uhg, nlg, tau, uinf, param, time);
    stropu = stropu * str1;
    strgpu = strgpu * str2;
end

tmp = "template <typename T> void " * opufile;
tmp = tmp * "(T *f, T *xdg, T *udg, T *odg, T *wdg, T *uhg, T *nlg, T *tau, T *uinf, T *param, T time, int modelnumber, int ib, int ng, int nc, int ncu, int nd, int ncx, int nco, int ncw)\n";
tmp = tmp * "{\n";
for k = 1:nbc
    if k == 1
        tmp = tmp * "\tif (ib == " * string(k) * ")\n";
    else
        tmp = tmp * "\telse if (ib == " * string(k) * ")\n";
    end
    tmp = tmp * "\t\t" * opufile * string(k) * "(f, xdg, udg, odg, wdg, uhg, nlg, tau, uinf, param, time, modelnumber, ng, nc, ncu, nd, ncx, nco, ncw);\n";
end
tmp = tmp * "}\n\n";

tmp = tmp * "template void " * opufile;
tmp = tmp * "(double *, double *, double *, double *, double *, double *, double *, double *, double *, double *, double, int, int, int, int, int, int, int, int, int);\n";
tmp = tmp * "template void " * opufile;
tmp = tmp * "(float *, float *, float *, float *, float *, float *, float *, float *, float *, float *, float, int, int, int, int, int, int, int, int, int);\n";
stropu = stropu * tmp;

ioopu = open(foldername * "/" * opufile * ".cpp", "w");
write(ioopu, stropu);
close(ioopu);

tmp = "template <typename T> void " * gpufile;
tmp = tmp * "(T *f, T *xdg, T *udg, T *odg, T *wdg, T *uhg, T *nlg, T *tau, T *uinf, T *param, T time, int modelnumber, int ib, int ng, int nc, int ncu, int nd, int ncx, int nco, int ncw)\n";
tmp = tmp * "{\n";
tmp = tmp * "\tint blockDim = 256;\n";
tmp = tmp * "\tint gridDim = (ng + blockDim - 1) / blockDim;\n";
tmp = tmp * "\tgridDim = (gridDim>1024)? 1024 : gridDim;\n";
for k = 1:nbc
    if k == 1
        tmp = tmp * "\tif (ib == " * string(k) * ")\n";
    elseif k<nbc
        tmp = tmp * "\telse if (ib == " * string(k) * ")\n";
    else
        tmp = tmp * "\telse if (ib == " * string(k) * ")\n";
    end
    tmp = tmp * "\t\tkernel"  * gpufile * string(k) * "<<<gridDim, blockDim>>>(f, xdg, udg, odg, wdg, uhg, nlg, tau, uinf, param, time, modelnumber, ng, nc, ncu, nd, ncx, nco, ncw);\n";
end
tmp = tmp * "}\n\n";

tmp = tmp * "template void " * gpufile;
tmp = tmp * "(double *, double *, double *, double *, double *, double *, double *, double *, double *, double *, double, int, int, int, int, int, int, int, int, int);\n";
tmp = tmp * "template void " * gpufile;
tmp = tmp * "(float *, float *, float *, float *, float *, float *, float *, float *, float *, float *, float, int, int, int, int, int, int, int, int, int);\n";
strgpu = strgpu * tmp;

iogpu = open(foldername * "/" * gpufile * ".cu", "w");
write(iogpu, strgpu);
close(iogpu);

strcpu = replace(stropu, "opu" => "cpu");
strcpu = replace(strcpu, "for (int i = 0; i <ng; i++) {" => "#pragma omp parallel for\n\tfor (int i = 0; i <ng; i++) {");

iocpu = open(foldername * "/" * cpufile * ".cpp", "w");
write(iocpu, strcpu);
close(iocpu);

end


__precompile__()

module DiffEqWebBase
  using OrdinaryDiffEq, DiffEqBase

  import DiffEqBase: build_solution

  immutable QuickODEProblem{uType,tType,isinplace} <: AbstractODEProblem{uType,tType,isinplace}
    f::Function
    u0::uType
    tspan::Tuple{tType,tType}
    callback::C
  end

  function QuickODEProblem(f,u0,tspan)
    callback = CallbackSet()
    QuickODEProblem{typeof(u0),eltype(tspan),true,typeof(callback)}(f,u0,tspan,callback)
  end

  immutable QuickODESolution{uType,tType,rateType,P,A} <: AbstractODESolution
    u::uType
    t::tType
    k::rateType
    prob::P
    alg::A
    interp::OrdinaryDiffEq.InterpolationData
    dense::Bool
    tslocation::Int
  end
  (sol::QuickODESolution)(t) = sol.interp(t)

  function build_solution{uType,tType,isinplace}(
          prob::QuickODEProblem{uType,tType,isinplace},
          alg,t,u;dense=false,
          k=[],interp = (tvals) -> nothing,kwargs...)
    QuickODESolution(u,t,k,prob,alg,interp,dense,0)
  end

  type QuickSDEProblem{uType,tType,isinplace,NoiseClass,F3,C,ND} <: AbstractSDEProblem{uType,tType,isinplace,NoiseClass,ND}
    f::Function
    g::Function
    u0::uType
    tspan::Tuple{tType,tType}
    noise::DiffEqBase.NoiseProcess{NoiseClass,F3}
    callback::C
    noise_rate_prototype::ND
  end

  function QuickSDEProblem(f,g,u0,tspan)
    noise = INPLACE_WHITE_NOISE
    callback = CallbackSet()
    noise_rate_prototype = nothing
    QuickSDEProblem{typeof(u0),eltype(tspan),true,true,:White,typeof(noise.noise_func),typeof(callback),typeof(noise_rate_prototype)}(f,g,u0,tspan,noise,callback,noise_rate_prototype)
  end

  export QuickODEProblem, QuickODESolution,QuickSDEProblem
end


using ChainRulesTestUtils
using LinearAlgebra
using Zygote: ZygoteRuleConfig, _pullback

# issue 897

test_rrule(ZygoteRuleConfig(), x->sum(sin, Diagonal(x)), ones(2); rrule_f=rrule_via_ad, check_inferred=false)
test_rrule(ZygoteRuleConfig(), x->sum(sin, Diagonal(x)), rand(3); rrule_f=rrule_via_ad, check_inferred=false)

@testset "adjoints of Iterators.product" begin
    y, back = _pullback(Iterators.product, 1:5, 1:3, 1:2)
    @test back(collect(y)) == (nothing, [6.0, 12.0, 18.0, 24.0, 30.0], [10.0, 20.0, 30.0], [15.0, 30.0])
    @test back([(nothing, j, k) for i in 1:5, j in 1:3, k in 1:2]) == (nothing, nothing, [10.0, 20.0, 30.0], [15.0, 30.0])
    @test back([(i, nothing, k) for i in 1:5, j in 1:3, k in 1:2]) == (nothing, [6.0, 12.0, 18.0, 24.0, 30.0], nothing, [15.0, 30.0])
    @test back([(i, j, nothing) for i in 1:5, j in 1:3, k in 1:2]) == (nothing, [6.0, 12.0, 18.0, 24.0, 30.0], [10.0, 20.0, 30.0], nothing)

    # This was wrong before https://github.com/FluxML/Zygote.jl/pull/1170
    @test gradient(x -> sum([y[2] * y[3] for y in Iterators.product(x, x, x, x)]), [1,2,3,4])[1] ≈ [320, 320, 320, 320]
    @test gradient(x -> sum(y[2] * y[3] for y in Iterators.product(x, x, x, x)), [1,2,3,4])[1] ≈ [320, 320, 320, 320]
end


module Inlining

using Mixtape
import Mixtape: CompilationContext, transform, allow
using CodeInfoTools
using CodeInfoTools: var, get_slot, walk

# This is just a fallback stub. We intercept this in inference.
invert(ret, f, args...)  = f(args...)

struct Mix <: CompilationContext end

# Allow the transform on our Target module.
allow(ctx::Mix, fn::typeof(invert), args...) = true

function transform(mix::Mix, src, sig)
    if !(sig[3] <: Function) ||
        sig[3] === Core.IntrinsicFunction
        return src
    end # If target is not a function, just return src.
    b = CodeInfoTools.Builder(src)
    forward = sig[3].instance
    argtypes = sig[4 : end]
    forward = CodeInfoTools.code_info(forward, Tuple{argtypes...})
    submap = Dict()
    for (ind, a) in enumerate(forward.slotnames[2 : end])
        push!(b, Expr(:call, Base.getindex, Core.SlotNumber(4), ind))
        setindex!(submap, var(ind), get_slot(forward, a))
    end

    # TODO: this should probably go in reverse.
    for (v, st) in enumerate(forward.code)
        if st isa Expr
            ex = Expr(:call, invert, Int, walk(v -> get(submap, v, v), st).args...) # TODO: need to get return type here I think.
        else
            ex = walk(v -> get(submap, v, v), st)
        end
        setindex!(submap, push!(b, ex), var(v))
    end

    println("Resultant IR for $(sig):")
    display(b)
    return CodeInfoTools.finish(b)
end

function foo(x)
    return x + 5
end

Mixtape.@load_abi()
ret = call(invert, 10, foo, 5; ctx = Mix())
display(ret)

end # module


abstract NUTSState{F<:VariateForm} <: HMCSamplerState{F}

type MuvNUTSState <: NUTSState{Multivariate}
  pstateplus::ParameterState{Continuous, Multivariate}
  pstateminus::ParameterState{Continuous, Multivariate}
  pstateprime::ParameterState{Continuous, Multivariate}
  pstatedprime::ParameterState{Continuous, Multivariate}
  tune::MCTunerState
  ratio::Real
  a::Real
  momentum::RealVector
  momentumplus::RealVector
  momentumminus::RealVector
  momentumprime::RealVector
  oldhamiltonian::Real
  newhamiltonian::Real
  u::Real
  v::Integer
  j::Integer
  n::Integer
  nprime::Integer
  ndprime::Integer
  s::Bool
  sprime::Bool
  sdprime::Bool
  update::Bool
  count::Integer

  function MuvNUTSState(
    pstateplus::ParameterState{Continuous, Multivariate},
    pstateminus::ParameterState{Continuous, Multivariate},
    pstateprime::ParameterState{Continuous, Multivariate},
    pstatedprime::ParameterState{Continuous, Multivariate},
    tune::MCTunerState,
    ratio::Real,
    a::Real,
    momentum::RealVector,
    momentumplus::RealVector,
    momentumminus::RealVector,
    momentumprime::RealVector,
    oldhamiltonian::Real,
    newhamiltonian::Real,
    u::Real,
    v::Integer,
    j::Integer,
    n::Integer,
    nprime::Integer,
    ndprime::Integer,
    s::Bool,
    sprime::Bool,
    sdprime::Bool,
    update::Bool,
    count::Integer
  )
    if !isnan(ratio)
      @assert ratio > 0 "Acceptance ratio should be positive"
    end
    if !isnan(a)
      @assert 0 <= a <= 1 "Acceptance probability should be in [0, 1]"
    end
    @assert count >= 0 "Number of iterations (count) should be non-negative"
    new(
      pstateplus,
      pstateminus,
      pstateprime,
      pstatedprime,
      tune,
      ratio,
      a,
      momentum,
      momentumplus,
      momentumminus,
      momentumprime,
      oldhamiltonian,
      newhamiltonian,
      u,
      v,
      j,
      n,
      nprime,
      ndprime,
      s,
      sprime,
      sdprime,
      update,
      count
    )
  end
end

MuvNUTSState(pstate::ParameterState{Continuous, Multivariate}, tune::MCTunerState=BasicMCTune()) =
  MuvNUTSState(
    pstate,
    pstate,
    pstate,
    pstate,
    tune,
    NaN,
    NaN,
    Array(eltype(pstate), pstate.size),
    Array(eltype(pstate), pstate.size),
    Array(eltype(pstate), pstate.size),
    Array(eltype(pstate), pstate.size),
    NaN,
    NaN,
    NaN,
    0,
    0,
    0,
    0,
    0,
    true,
    true,
    true,
    true,
    0
  )

immutable NUTS <: HMCSampler
  leapstep::Real
  maxδ::Integer
  maxndoublings::Integer

  function NUTS(leapstep::Real, maxδ::Integer, maxndoublings::Integer)
    @assert leapstep > 0 "Leapfrog step is not positive"
    @assert maxδ > 0 "maxδ is not positive"
    @assert maxndoublings > 0 "Maximum number of doublings is not positive"
    new(leapstep, maxδ, maxndoublings)
  end
end

NUTS(leapstep::Real=0.1; maxδ::Integer=1000, maxndoublings::Integer=5) = NUTS(leapstep, maxδ, maxndoublings)

function initialize!{F<:VariateForm}(
  pstate::ParameterState{Continuous, F},
  parameter::Parameter{Continuous, F},
  sampler::NUTS,
  outopts::Dict
)
  parameter.uptogradlogtarget!(pstate)
  @assert isfinite(pstate.logtarget) "Log-target not finite: initial value out of support"
  @assert all(isfinite(pstate.gradlogtarget)) "Gradient of log-target not finite: initial values out of support"

  if !isempty(outopts[:diagnostics])
    pstate.diagnostickeys = copy(outopts[:diagnostics])
    pstate.diagnosticvalues = Array(Any, length(pstate.diagnostickeys))
  end
end

sampler_state(
  parameter::Parameter{Continuous, Multivariate},
  sampler::NUTS,
  tuner::MCTuner,
  pstate::ParameterState{Continuous, Multivariate},
  vstate::VariableStateVector
) =
  MuvNUTSState(generate_empty(pstate), tuner_state(parameter, sampler, tuner))

uturn(positionplus::RealVector, positionminus::RealVector, momentumplus::RealVector, momentumminus::RealVector) =
  dot(positionplus-positionminus, momentumplus) < 0. || dot(positionplus-positionminus, momentumminus) < 0.

function build_tree!(
  sstate::NUTSState{Multivariate},
  pstate::ParameterState{Continuous, Multivariate},
  momentum::RealVector,
  u::Real,
  v::Real,
  j::Integer,
  step::Real,
  logtarget!::Function,
  gradlogtarget!::Function,
  sampler::NUTS
)
  if j == 0 # Base case: take one leapfrog step in the direction v
    local hamiltonianprime::Real

    leapfrog!(sstate.pstateprime, sstate.momentumprime, pstate, momentum, v*sstate.tune.step, gradlogtarget!)
    logtarget!(sstate.pstateprime)
    hamiltonianprime = hamiltonian(sstate.pstateprime.logtarget, sstate.momentumprime)

    sstate.nprime = Int(u <= hamiltonianprime)
    sstate.sprime = u < sampler.maxδ+hamiltonianprime

    return sstate.pstateprime,
      sstate.momentumprime,
      sstate.pstateprime,
      sstate.momentumprime,
      sstate.pstateprime,
      sstate.nprime,
      sstate.sprime
  else # Recursion: implicitly build the left and right subtrees
    sstate.pstateminus,
    sstate.momentumminus,
    sstate.pstateplus,
    sstate.momentumplus,
    sstate.pstateprime,
    sstate.nprime,
    sstate.sprime =
      build_tree!(sstate, pstate, momentum, u, v, j-1, step, logtarget!, gradlogtarget!, sampler)

    if sstate.sprime
      if v == -1
        sstate.pstateminus, sstate.momentumminus, _, _, sstate.pstatedprime, sstate.ndprime, sstate.sdprime =
          build_tree!(
            sstate,
            sstate.pstateminus,
            sstate.momentumminus,
            u,
            v,
            j-1,
            step,
            logtarget!,
            gradlogtarget!,
            sampler
          )
      else
        _, _, sstate.pstateplus, sstate.momentumplus, sstate.pstatedprime, sstate.ndprime, sstate.sdprime =
          build_tree!(
            sstate,
            sstate.pstateplus,
            sstate.momentumplus,
            u,
            v,
            j-1,
            step,
            logtarget!,
            gradlogtarget!,
            sampler
          )
      end

      if rand() <= sstate.ndprime/(sstate.ndprime+sstate.nprime)
        sstate.pstateprime.value = copy(sstate.pstatedprime.value)
        sstate.pstateprime.gradlogtarget = copy(sstate.pstatedprime.gradlogtarget)
      end

      sstate.nprime += sstate.ndprime

      sstate.sprime =
        sstate.sdprime &&
        !uturn(sstate.pstateplus.value, sstate.pstateminus.value, sstate.momentumplus, sstate.momentumminus)
    end

    return sstate.pstateminus,
      sstate.momentumminus,
      sstate.pstateplus,
      sstate.momentumplus,
      sstate.pstateprime,
      sstate.nprime,
      sstate.sprime
  end
end