mutable struct DynamicGenerator{
    M <: Machine,
    S <: Shaft,
    A <: AVR,
    TG <: TurbineGov,
    P <: PSS,
} <: DynamicInjection
    static_injector::Union{Nothing, Generator}
    ω_ref::Float64
    machine::M
    shaft::S
    avr::A
    prime_mover::TG
    pss::P
    n_states::Int64
    states::Vector{Symbol}
    ext::Dict{String, Any}
    internal::InfrastructureSystemsInternal
end

function DynamicGenerator(
    static_injector::Generator,
    ω_ref::Float64,
    machine::M,
    shaft::S,
    avr::A,
    prime_mover::TG,
    pss::P,
    ext::Dict{String, Any} = Dict{String, Any}(),
) where {M <: Machine, S <: Shaft, A <: AVR, TG <: TurbineGov, P <: PSS}

    n_states = (
        machine.n_states +
        shaft.n_states +
        avr.n_states +
        prime_mover.n_states +
        pss.n_states
    )
    states = vcat(machine.states, shaft.states, avr.states, prime_mover.states, pss.states)

    return DynamicGenerator{M, S, A, TG, P}(
        static_injector,
        ω_ref,
        machine,
        shaft,
        avr,
        prime_mover,
        pss,
        n_states,
        states,
        ext,
        InfrastructureSystemsInternal(),
    )
end

function DynamicGenerator(;
    static_injector::Generator,
    ω_ref::Float64,
    machine::M,
    shaft::S,
    avr::A,
    prime_mover::TG,
    pss::P,
    ext::Dict{String, Any} = Dict{String, Any}(),
) where {M <: Machine, S <: Shaft, A <: AVR, TG <: TurbineGov, P <: PSS}
    DynamicGenerator(static_injector, ω_ref, machine, shaft, avr, prime_mover, pss, ext)
end

IS.get_name(device::DynamicGenerator) = get_name(device.static_injector)
get_bus(device::DynamicGenerator) = get_bus(device.static_injector)
get_Sbase(device::DynamicGenerator) = device.machine.s_rated
get_states(device::DynamicGenerator) = device.states
get_n_states(device::DynamicGenerator) = device.n_states
get_ω_ref(device::DynamicGenerator) = device.ω_ref
get_V_ref(device::DynamicGenerator) = get_voltage(get_bus(device))
get_P_ref(device::DynamicGenerator) = get_activepower(device.static_injector)
get_Q_ref(device::DynamicGenerator) = get_reactivepower(device.static_injector)
get_basepower(device::DynamicGenerator) = get_basepower(device.static_injector)
get_machine(device::DynamicGenerator) = device.machine
get_shaft(device::DynamicGenerator) = device.shaft
get_avr(device::DynamicGenerator) = device.avr
get_prime_mover(device::DynamicGenerator) = device.prime_mover
get_pss(device::DynamicGenerator) = device.pss
get_static_injector(device::DynamicGenerator) = device.static_injector
get_ext(device::DynamicGenerator) = device.ext


using SMLMData

"""
    connect1DS!(smld::SMLMData.SMLD2D, dataset::Int; maxframegap::Int = 5)

Define the "ideal" frame-connection result for simulated `smld` with one dataset.

# Description
This is a helper function which defines the ideal FC result for a single
dataset.  The user should probably not bother using this, and instead should
call defineidealFC().

# Inputs
- `smld`: SMLMData.SMLD2D with smld.connectID populated to indicate emitter ID.
- `dataset`: Dataset number to be connected.
- `maxframegap`: Maximum frame gap allowed between localizations connected in
                 the "ideal" result.
"""
function connect1DS!(smld::SMLMData.SMLD2D, dataset::Int; maxframegap::Int = 5)
    # Loop through associated localizations and combine them as appropriate.
    currentDS = smld.datasetnum .== dataset
    smld_current = smld[currentDS]
    maxID = maximum(smld.connectID)
    smld_current.connectID .+= maxID # recompressed later
    for ii in unique(smld_current.connectID)
        # Determine which localizations belong to the ii-th emitter.
        currentinds = findall(smld_current.connectID .== ii)

        # Sort these localizations with respect to their frame.
        framenum = smld_current.framenum[currentinds]
        sortinds = sortperm(framenum)
        sortedframes = framenum[sortinds]
        sorted_currentinds = currentinds[sortinds]

        # If all of these localizations are within the framegap, no action is
        # needed (they already share the same connectID).
        framediff = diff(sortedframes)
        if all(framediff .<= maxframegap)
            continue
        end

        # Determine which localizations we can combine.
        for ff = 1:length(framediff)
            if framediff[ff] <= maxframegap
                # Connect these localizations.
                smld_current.connectID[sorted_currentinds[ff+1]] =
                    smld_current.connectID[sorted_currentinds[ff]]
            else
                # Localization ff+1 should be a new blinking event.
                maxID += 1
                smld_current.connectID[sorted_currentinds[ff+1]] = maxID
            end
        end
    end

    # Update smld.connectID for the current dataset.
    smld.connectID[currentDS] = smld_current.connectID

    return smld
end

function connect1DS(smld::SMLMData.SMLD2D, dataset::Int; maxframegap::Int = 5)
    return connect1DS!(deepcopy(smld))
end

"""
    smld, smld_combined = defineidealFC!(smld::SMLMData.SMLD2D;
        maxframegap::Int = 5)

Define the "ideal" frame-connection result for a simulated `smld`.

# Description
This function defines the "ideal" frame connection result from a simulation.
That is to say, for a simulated SMLD2D structure `smld` with `connectID` field
populated to indicate emitter membership of localizations, this function will
generate an "ideal" FC result which combines all blinking events that appeared
with frame gaps less than `maxframegap` of one another.  Note that for very
high duty cycles, multiple blinking events might be mistakingly combined by
this method (i.e., if the emitter blinks back on within `maxframegap` frames
of its previous blink).  Note that localizations are not allowed to be
connected across datasets.

# Inputs
-`smld`: SMLMData.SMLD2D with smld.connectID populated to indicate emitter ID.
-`maxframegap`: Maximum frame gap allowed between localizations connected in
                the "ideal" result.

# Outputs
-`smld_out`: Ideal frame-connection result on input `smld` with localizations
             combined as appropriate.
-`smld_connected`: Copy of the input `smld` with smld_connected.connectID
                   modified to reflect blinking event ID (i.e., `smld_out` is
                   generated as smld_out = combinelocalizations(smld_connected))
"""
function defineidealFC!(smld::SMLMData.SMLD2D; maxframegap::Int = 5)
    # Loop through datasets and combine localizations as appropriate.
    for ii in unique(smld.datasetnum)
        connect1DS!(smld, ii; maxframegap = maxframegap)
    end
    compress_connectID!(smld.connectID)

    return smld, combinelocalizations(smld)
end

function defineidealFC(smld::SMLMData.SMLD2D; maxframegap::Int = 5)
    return defineidealFC!(deepcopy(smld); maxframegap = maxframegap)
end


__precompile__()

module OrthographicProjection

	using Common
	import Common.AABB, Common.LAR, Common.Points
	import Common.getmodel
	using FileManager
	using Printf
	using Images

	#
	include("struct.jl")
	include("trie_traversal.jl")
	include("saves.jl")
	include("main.jl")
	include("process_point.jl")
	include("util.jl")

	export Common, FileManager
end # module


using Adapt
using Dates: AbstractTime, DateTime, Nanosecond, Millisecond
using Oceananigans.Utils: prettytime

import Base: show
import Oceananigans: short_show

"""
    Clock{T<:Number}

Keeps track of the current `time`, `iteration` number, and time-stepping `stage`.
`stage` is updated only for multi-stage time-stepping methods.
The `time::T` can be either a number or a `DateTime` object.
"""
mutable struct Clock{T}
         time :: T
    iteration :: Int
        stage :: Int

    @doc """
        Clock{T}(time, iteration, stage=1)

    Returns a `Clock` with time of type `T`, initialized to the first stage.
    """
    function Clock{T}(time, iteration=0, stage=1) where T
        return new{T}(time, iteration, stage)
    end
end

"""
    Clock(; time, iteration=0, stage=1)

Returns a `Clock` initialized to the zeroth iteration and first time step stage.
"""
Clock(; time, iteration=0, stage=1) = Clock{typeof(time)}(time, iteration, stage)

short_show(clock::Clock) = string("Clock(time=$(prettytime(clock.time)), iteration=$(clock.iteration))")

Base.show(io::IO, c::Clock{T}) where T =
    println(io, "Clock{$T}: time = $(prettytime(c.time)), iteration = $(c.iteration), stage = $(c.stage)")

next_time(clock, Δt) = clock.time + Δt
next_time(clock::Clock{<:AbstractTime}, Δt) = clock.time + Nanosecond(round(Int, 1e9 * Δt))

tick_time!(clock, Δt) = clock.time += Δt
tick_time!(clock::Clock{<:AbstractTime}, Δt) = clock.time += Nanosecond(round(Int, 1e9 * Δt))

# Convert the time to units of clock.time (assumed to be seconds if using DateTime or TimeDate).
unit_time(t) = t
unit_time(t::Millisecond) = t.value / 1_000
unit_time(t::Nanosecond) = t.value / 1_000_000_000

# Convert to a base Julia type (a float or DateTime). Mainly used by NetCDFOutputWriter.
float_or_date_time(t) = t
float_or_date_time(t::AbstractTime) = DateTime(t)

function tick!(clock, Δt; stage=false)

    tick_time!(clock, Δt)

    if stage # tick a stage update
        clock.stage += 1
    else # tick an iteration and reset stage
        clock.iteration += 1
        clock.stage = 1
    end

    return nothing
end

"Adapt `Clock` to work on the GPU via CUDAnative and CUDAdrv."
Adapt.adapt_structure(to, clock::Clock) = (time=clock.time, iteration=clock.iteration, stage=clock.stage)


@POMDP_require solve(solver::DeepQLearningSolver, problem::Union{MDP,POMDP}) begin
    P = typeof(problem)
    S = state_type(problem)
    A = action_type(problem)
    @req n_actions(::P)
    @req initial_state(::P, ::AbstractRNG)
    @req convert_o(::Type{Vector{Float64}}, ::S, ::P)
    @req action_index(::P, ::A)

end


# const getf_scheduler = Libdl.dlsym(polo_lib, :getf_scheduler)
# const getx_scheduler = Libdl.dlsym(polo_lib, :getx_scheduler)

struct Scheduler <: ParameterServer
    fval::Float64
    x::Vector{Float64}
    psopts::ParameterServerOptions

    function (::Type{Scheduler})(; kw...)
        scheduler =  new(0., Vector{Float64}(), ParameterServerOptions(; kw...))
    end
end

function initialize!(scheduler::Scheduler, x₀::AbstractVector)
    resize!(scheduler.x,length(x₀))
    scheduler.x .= x₀
end
getx(scheduler::Scheduler) = scheduler.x

function (proxgrad::ProxGradient{Scheduler})(x₀::AbstractVector,termination::AbstractTermination,logger::AbstractLogger)
    initialize!(proxgrad.execution, x₀)
    xbegin = pointer(x₀, 1)
    xend = pointer(x₀, length(x₀) + 1)
    termination_c = @cfunction(POLO.termination_wrapper, Cint,
                               (Cint, Cdouble, Ptr{Cdouble}, Ptr{Cdouble}, Ptr{Cdouble}, Ptr{Nothing}))
    log_c = @cfunction(POLO.log_wrapper, Nothing,
                       (Cint, Cdouble, Ptr{Cdouble}, Ptr{Cdouble}, Ptr{Cdouble}, Ptr{Nothing}))
    return ccall(POLO.run_scheduler, Nothing,
                 (Ptr{Nothing},Ptr{Cdouble}, Ptr{Cdouble},
                  Ptr{Nothing}, Any,
                  Ptr{Nothing}, Any),
                 proxgrad,
                 xbegin, xend,
                 termination_c, termination,
                 log_c, logger)
end
function (proxgrad::ProxGradient{Scheduler})(x₀::AbstractVector)
    termination = POLO.Utility.MaxIteration(100)
    return proxgrad(x₀,termination,POLO.Utility.ProgressLogger.Value(termination))
end

paramserver_handle(::Scheduler) = POLO.proxgradient_scheduler
execution_handle(::Scheduler) = POLO.run_scheduler
delete_handle(::Scheduler) = POLO.delete_proxgradient_scheduler
# getf_handle(::Scheduler) = getf_scheduler
# getx_handle(::Scheduler) = getx_scheduler


mkpath("problems")
function write_file(problem; folder_name="MMKP/problems")
	path = joinpath(folder_name, problem.id["id"]) * ".mps"
	write_to_file(problem.model, path)
end
write_file.(problems)


using EcosystemCore

g = Grass(1,5)
s = Sheep(2,1,1,1,1)
w = Wolf(3,1,1,1,1)
world = World([g,s,w])


using PyCall

np = pyimport("numpy")

P = pyimport("numpy.polynomial")
@pydef mutable struct Doubler <: P.Polynomial
    function __init__(self, x=10)
        self.x = x
    end
    my_method(self, arg1::Number) = arg1 + 20
    x2.get(self) = self.x * 2
    function x2.set!(self, new_val)
        self.x = new_val / 2
    end
end

@show Doubler().x2

@pydef mutable struct MyClass
    function __init__(self, x)
        self.x = x
    end
end

#=
py"""
class MyClass():
    def __init__(self, x):
        self.x = x
    def edit(self, v):
        self.x[0] = v
        return self.x
"""
=#

jo = [1 2 3; 4 5 6]
mc = MyClass(jo)
x = PyArray(PyObject(mc.edit(-10)))
@show x
x[2]=-200
@show x
@show mc.x


stuff = IIF.localProduct(fg, :x5)
stuff[2]

plotKDE([getBelief(fg, :x5);stuff[2]], dims=[1;2], levels=3, c=["red";"green";["blue" for i in 1:10]])
pts = KDE.getPoints(stuff[2][1])
Gadfly.plot(x=pts[1,:],y=pts[2,:], Geom.hexbin) |> SVG("/tmp/test.svg", 35cm, 25cm)

#
# setVal!(fg, :l1, predL1)
#
#
# L1 = getVal(fg, :l1)
# pl = plotKDE(kde!(L1))
# # pl.coord = Gadfly.Coord.Cartesian(xmin=-200, xmax=100, ymin=-100, ymax=200);
# pl

# Look at sas2d.dbg
# @show length(sas2d.dbg)
# plot(x=sas2d.dbg.azi_smpls, Geom.histogram)

#
# n=200; pl = plot(x=linspace(0,2pi,length(sas2d.dbg.beams[n])), y=sas2d.dbg.beams[n], Geom.path())
#
#
# n=200; pl = plot(x=linspace(0,2pi,length(sas2d.dbg.beams_cut[n])), y=sas2d.dbg.beams_cut[n], Geom.path())
#

avg_beam_cut = zeros(length(sas2d.dbg.beams_cut[1]))
for i in 1:N
  avg_beam_cut[:] = avg_beam_cut + sas2d.dbg.beams_cut[i]
end
avg_beam_cut ./= sum(avg_beam_cut)

avg_beam = zeros(length(sas2d.dbg.beams[1]))
for i in 1:N
  avg_beam[:] = avg_beam + sas2d.dbg.beams[i]
end
avg_beam ./= sum(avg_beam)

circ_avg_beam = zeros(2,length(avg_beam))

count = 0
for th in linspace(0,2pi,length(avg_beam))
  count += 1
  circ_avg_beam[:,count] = 7500*R(th)*[avg_beam[count];0.0]
end

pl = plot(x=linspace(0,2pi,length(avg_beam)), y=avg_beam, Geom.path(), Coord.Cartesian(ymin=0))
pl = plot(x=linspace(0,2pi,length(avg_beam_cut)), y=avg_beam_cut, Geom.path(), Coord.Cartesian(ymin=0))

# not the median cut beam
plPica = plot(x=circ_avg_beam[1,:], y=circ_avg_beam[2,:], Geom.path(), Theme(default_color=colorant"magenta", line_width=2pt))

plPica.layers = union(plPica.layers, pl.layers)

plPica

0


using Fst

a = Wfst()
add_arc!(a, 0, 1, "x", "x", 1.0)
add_arc!(a, 1, 2, "x", "x", 1.0)
add_arc!(a, 1, 3, "x", "x", 1.0)
add_arc!(a, 3, 4, "x", "x", 1.0)
add_arc!(a, 4, 3, "x", "x", 1.0)
add_arc!(a, 1, 5, "x", "x", 1.0)
add_arc!(a, 5, 1, "x", "x", 1.0)
add_arc!(a, 6, 1, "x", "x", 1.0)
add_arc!(a, 6, 7, "x", "x", 1.0)
add_arc!(a, 7, 6, "x", "x", 1.0)
add_initial_state!(a, 0, 1.0)
add_final_state!(a, 2, 1.0)
create_pdf(a, "a.pdf")
println(a)
create_pdf(accessible(a), "A(a).pdf")
create_pdf(coaccessible(accessible(a)), "Co(A(a)).pdf")
create_pdf(coaccessible(accessible(a)), "trim(a).pdf")


using DataFrames, CSV
include("../src/sdistance.jl") # 必ずダブルクオーテーション
include("../test/draw.jl")
include("../src/SignedDistanceFunction.jl")
import .Sdistance: benchmark_floodfill, benchmark_singlecurves_isinside #,signedDistance2D
using .SignedDistanceFunction
import .Draw: parformance_graphs

@enum ExecuteKinds _multicurves _singlecurve _singlecurve_floodfill

function _exe(kinds)
    _execute_times = 2
    _phi = []
    init_N = 100
    increment_N = 200
    runtime = zeros(_execute_times + 1, 2)
    N = [init_N + increment_N * item for item = 0:_execute_times]
    if kinds == 1
        for i = 0:_execute_times
            runtime[i+1, 1] = benchmark_floodfill(init_N + increment_N * i, "./test/mock_csv_data/multiple_curves.csv")
            runtime[i+1, 2] = benchmark_floodfill(init_N + increment_N * i, "./test/mock_csv_data/multiple_curves.csv", false)
        end
        parformance_graphs(N, runtime, "multiple_curves", ["Parallel processing", "Normal processing"])
    elseif kinds == 2
        for i = 0:_execute_times
            runtime[i+1, 1] = benchmark_floodfill(init_N + increment_N * i, "./test/mock_csv_data/interface.csv")
            runtime[i+1, 2] = benchmark_floodfill(init_N + increment_N * i, "./test/mock_csv_data/interface.csv", false)
        end
        parformance_graphs(N, runtime, "interface_floodfill", ["Parallel processing", "Normal processing"])
    elseif kinds == 3
        for i = 0:_execute_times
            runtime[i+1, 1] = benchmark_singlecurves_isinside(init_N + increment_N * i, "./test/mock_csv_data/interface.csv")
            runtime[i+1, 2] = benchmark_singlecurves_isinside(init_N + increment_N * i, "./test/mock_csv_data/interface.csv", false)
        end
        parformance_graphs(N, runtime, "the jordan curve", ["Parallel processing", "Normal processing"])
    end
end

# _exe(1)
# _exe(2)
_exe(3)

#==
Jabelic test it and get it as below.

N2300:  300+200i, ARG = 10
 [300, 500, 700, 900, 1100, 1300, 1500, 1700, 1900, 2100, 2300]
 [2.727736517 1.549037993; 2.430711433 4.540753791;
 4.87217261 8.72733049; 6.970006918 11.296165896;
 10.919117877 16.429877231; 15.524662846 28.576650596;
 20.630861499 38.540759007; 26.182433943 51.252256581;
 33.966169503 64.128344396; 40.489381557 75.419365612;
 50.053971199 91.594363467]

300+200i, ARG = 3, interface  multi-thread, single-thread
 [2.402844242 1.564786665; 2.415855251 4.372297246; 4.636160664 9.164722692; 7.243630318 11.635953353]
signedDistance2D, 300+200i, ARG = 3, interface, multi-thread only. jordan_curve(using isinside), multi_curves(using floodfill)
 [2.126074484 13.096371426; 2.496373027 125.23088408; 5.242287246 596.261015286; 7.987274914 1743.300632586]

1-3-500, interface, inpolygon
[100, 300, 500]
[1.248799153 0.530269039; 2.28104107 4.735589021; 6.293578857 11.423713058]
32

1-3-500, interface, Floodfill
[100, 300, 500][1.291998074 0.123935482; 3.388728651 5.878153884; 69.895109677 84.980423578]
32

1-3-500, multi_curves, Floodfill
[100, 300, 500][2.400877896 2.741995989; 38.038218511 92.339324457; 476.64809553 833.91326708]
32

1-3-500, multi_curves, Floodfill
[100, 300, 500][0.5009443733333333 0.891537955; 12.114947880666668 28.633708910333336; 133.44351538533337 261.82990826500003]

 ==#


mutable struct H5Seis
  seis::PyCall.PyObject
  # types must be compatible with h5geo seis IO data
  ind::Array{UInt64,1}
  data::Array{Float32,2}
end


#########
# Trial #
#########

mutable struct Trial
    params::Parameters
    times::Vector{Float64}
    gctimes::Vector{Float64}
    memory::Int
    allocs::Int
end

Trial(params::Parameters) = Trial(params, Float64[], Float64[], typemax(Int), typemax(Int))

function Base.:(==)(a::Trial, b::Trial)
    return a.params == b.params &&
           a.times == b.times &&
           a.gctimes == b.gctimes &&
           a.memory == b.memory &&
           a.allocs == b.allocs
end

Base.copy(t::Trial) = Trial(copy(t.params), copy(t.times), copy(t.gctimes), t.memory, t.allocs)

function Base.push!(t::Trial, time, gctime, memory, allocs)
    push!(t.times, time)
    push!(t.gctimes, gctime)
    memory < t.memory && (t.memory = memory)
    allocs < t.allocs && (t.allocs = allocs)
    return t
end

function Base.deleteat!(t::Trial, i)
    deleteat!(t.times, i)
    deleteat!(t.gctimes, i)
    return t
end

Base.length(t::Trial) = length(t.times)
Base.getindex(t::Trial, i::Number) = push!(Trial(t.params), t.times[i], t.gctimes[i], t.memory, t.allocs)
Base.getindex(t::Trial, i) = Trial(t.params, t.times[i], t.gctimes[i], t.memory, t.allocs)
Base.lastindex(t::Trial) = length(t)

function Base.sort!(t::Trial)
    inds = sortperm(t.times)
    t.times = t.times[inds]
    t.gctimes = t.gctimes[inds]
    return t
end

Base.sort(t::Trial) = sort!(copy(t))

Base.time(t::Trial) = time(minimum(t))
gctime(t::Trial) = gctime(minimum(t))
memory(t::Trial) = t.memory
allocs(t::Trial) = t.allocs
params(t::Trial) = t.params

# returns the index of the first outlier in `values`, if any outliers are detected.
# `values` is assumed to be sorted from least to greatest, and assumed to be right-skewed.
function skewcutoff(values)
    current_values = copy(values)
    while mean(current_values) > median(current_values)
        deleteat!(current_values, length(current_values))
    end
    return length(current_values) + 1
end

skewcutoff(t::Trial) = skewcutoff(t.times)

function rmskew!(t::Trial)
    sort!(t)
    i = skewcutoff(t)
    i <= length(t) && deleteat!(t, i:length(t))
    return t
end

function rmskew(t::Trial)
    st = sort(t)
    return st[1:(skewcutoff(st) - 1)]
end

trim(t::Trial, percentage = 0.1) = t[1:max(1, floor(Int, length(t) - (length(t) * percentage)))]

#################
# TrialEstimate #
#################

mutable struct TrialEstimate
    params::Parameters
    time::Float64
    gctime::Float64
    memory::Int
    allocs::Int
end

function TrialEstimate(trial::Trial, t, gct)
    return TrialEstimate(params(trial), t, gct, memory(trial), allocs(trial))
end

function Base.:(==)(a::TrialEstimate, b::TrialEstimate)
    return a.params == b.params &&
           a.time == b.time &&
           a.gctime == b.gctime &&
           a.memory == b.memory &&
           a.allocs == b.allocs
end

Base.copy(t::TrialEstimate) = TrialEstimate(copy(t.params), t.time, t.gctime, t.memory, t.allocs)

function Base.minimum(trial::Trial)
    i = argmin(trial.times)
    return TrialEstimate(trial, trial.times[i], trial.gctimes[i])
end

function Base.maximum(trial::Trial)
    i = argmax(trial.times)
    return TrialEstimate(trial, trial.times[i], trial.gctimes[i])
end

Statistics.median(trial::Trial) = TrialEstimate(trial, median(trial.times), median(trial.gctimes))
Statistics.mean(trial::Trial) = TrialEstimate(trial, mean(trial.times), mean(trial.gctimes))
Statistics.std(trial::Trial) = TrialEstimate(trial, std(trial.times), std(trial.gctimes))

Base.isless(a::TrialEstimate, b::TrialEstimate) = isless(time(a), time(b))

Base.time(t::TrialEstimate) = t.time
gctime(t::TrialEstimate) = t.gctime
memory(t::TrialEstimate) = t.memory
allocs(t::TrialEstimate) = t.allocs
params(t::TrialEstimate) = t.params

##############
# TrialRatio #
##############

mutable struct TrialRatio
    params::Parameters
    time::Float64
    gctime::Float64
    memory::Float64
    allocs::Float64
end

function Base.:(==)(a::TrialRatio, b::TrialRatio)
    return a.params == b.params &&
           a.time == b.time &&
           a.gctime == b.gctime &&
           a.memory == b.memory &&
           a.allocs == b.allocs
end

Base.copy(t::TrialRatio) = TrialRatio(copy(t.params), t.time, t.gctime, t.memory, t.allocs)

Base.time(t::TrialRatio) = t.time
gctime(t::TrialRatio) = t.gctime
memory(t::TrialRatio) = t.memory
allocs(t::TrialRatio) = t.allocs
params(t::TrialRatio) = t.params

function ratio(a::Real, b::Real)
    if a == b # so that ratio(0.0, 0.0) returns 1.0
        return one(Float64)
    end
    return Float64(a / b)
end

function ratio(a::TrialEstimate, b::TrialEstimate)
    ttol = max(params(a).time_tolerance, params(b).time_tolerance)
    mtol = max(params(a).memory_tolerance, params(b).memory_tolerance)
    p = Parameters(params(a); time_tolerance = ttol, memory_tolerance = mtol)
    return TrialRatio(p, ratio(time(a), time(b)), ratio(gctime(a), gctime(b)),
                      ratio(memory(a), memory(b)), ratio(allocs(a), allocs(b)))
end

gcratio(t::TrialEstimate) =  ratio(gctime(t), time(t))

##################
# TrialJudgement #
##################

struct TrialJudgement
    ratio::TrialRatio
    time::Symbol
    memory::Symbol
end

function TrialJudgement(r::TrialRatio)
    ttol = params(r).time_tolerance
    mtol = params(r).memory_tolerance
    return TrialJudgement(r, judge(time(r), ttol), judge(memory(r), mtol))
end

function Base.:(==)(a::TrialJudgement, b::TrialJudgement)
    return a.ratio == b.ratio &&
           a.time == b.time &&
           a.memory == b.memory
end

Base.copy(t::TrialJudgement) = TrialJudgement(copy(t.params), t.time, t.memory)

Base.time(t::TrialJudgement) = t.time
memory(t::TrialJudgement) = t.memory
ratio(t::TrialJudgement) = t.ratio
params(t::TrialJudgement) = params(ratio(t))

judge(a::TrialEstimate, b::TrialEstimate; kwargs...) = judge(ratio(a, b); kwargs...)

function judge(r::TrialRatio; kwargs...)
    newr = copy(r)
    newr.params = Parameters(params(r); kwargs...)
    return TrialJudgement(newr)
end

function judge(ratio::Real, tolerance::Float64)
    if isnan(ratio) || (ratio - tolerance) > 1.0
        return :regression
    elseif (ratio + tolerance) < 1.0
        return :improvement
    else
        return :invariant
    end
end

isimprovement(f, t::TrialJudgement) = f(t) == :improvement
isimprovement(t::TrialJudgement) = isimprovement(time, t) || isimprovement(memory, t)

isregression(f, t::TrialJudgement) = f(t) == :regression
isregression(t::TrialJudgement) = isregression(time, t) || isregression(memory, t)

isinvariant(f, t::TrialJudgement) = f(t) == :invariant
isinvariant(t::TrialJudgement) = isinvariant(time, t) && isinvariant(memory, t)

const colormap = (
    regression = :red,
    improvement = :green,
    invariant = :normal,
)

printtimejudge(io, t::TrialJudgement) =
    printstyled(io, time(t); color=colormap[time(t)])
printmemoryjudge(io, t::TrialJudgement) =
    printstyled(io, memory(t); color=colormap[memory(t)])

###################
# Pretty Printing #
###################

prettypercent(p) = string(@sprintf("%.2f", p * 100), "%")

function prettydiff(p)
    diff = p - 1.0
    return string(diff >= 0.0 ? "+" : "", @sprintf("%.2f", diff * 100), "%")
end

function prettytime(t)
    if t < 1e3
        value, units = t, "ns"
    elseif t < 1e6
        value, units = t / 1e3, "μs"
    elseif t < 1e9
        value, units = t / 1e6, "ms"
    else
        value, units = t / 1e9, "s"
    end
    return string(@sprintf("%.3f", value), " ", units)
end

function prettymemory(b)
    if b < 1024
        return string(b, " bytes")
    elseif b < 1024^2
        value, units = b / 1024, "KiB"
    elseif b < 1024^3
        value, units = b / 1024^2, "MiB"
    else
        value, units = b / 1024^3, "GiB"
    end
    return string(@sprintf("%.2f", value), " ", units)
end

function withtypename(f, io, t)
    needtype = get(io, :typeinfo, Nothing) !== typeof(t)
    if needtype
        print(io, nameof(typeof(t)), '(')
    end
    f()
    if needtype
        print(io, ')')
    end
end

function bindata(sorteddata, nbins, min, max)
    Δ = (max - min) / nbins
    bins = zeros(nbins)
    lastpos = 0
    for i ∈ 1:nbins
        pos = searchsortedlast(sorteddata, min + i * Δ)
        bins[i] = pos - lastpos
        lastpos = pos
    end
    bins
end
bindata(sorteddata, nbins) = bindata(sorteddata, nbins, first(sorteddata), last(sorteddata))

function asciihist(bins, height=1)
    histbars = ['▁', '▂', '▃', '▄', '▅', '▆', '▇', '█']
    if minimum(bins) == 0
        barheights = 2 .+ round.(Int, (height * length(histbars) - 2) * bins ./ maximum(bins))
        barheights[bins .== 0] .= 1
    else
        barheights = 1 .+ round.(Int, (height * length(histbars) - 1) * bins ./ maximum(bins))
    end
    heightmatrix = [min(length(histbars), barheights[b] - (h-1) * length(histbars))
                    for h in height:-1:1, b in 1:length(bins)]
    map(height -> if height < 1; ' ' else histbars[height] end, heightmatrix)
end

_summary(io, t, args...) = withtypename(() -> print(io, args...), io, t)

Base.summary(io::IO, t::Trial) = _summary(io, t, prettytime(time(t)))
Base.summary(io::IO, t::TrialEstimate) = _summary(io, t, prettytime(time(t)))
Base.summary(io::IO, t::TrialRatio) = _summary(io, t, prettypercent(time(t)))
Base.summary(io::IO, t::TrialJudgement) = withtypename(io, t) do
    print(io, prettydiff(time(ratio(t))), " => ")
    printtimejudge(io, t)
end

_show(io, t) =
    if get(io, :compact, true)
        summary(io, t)
    else
        show(io, MIME"text/plain"(), t)
    end

Base.show(io::IO, t::Trial) = _show(io, t)
Base.show(io::IO, t::TrialEstimate) = _show(io, t)
Base.show(io::IO, t::TrialRatio) = _show(io, t)
Base.show(io::IO, t::TrialJudgement) = _show(io, t)

function Base.show(io::IO, ::MIME"text/plain", t::Trial)

    pad = get(io, :pad, "")
    print(io, "BenchmarkTools.Trial: ", length(t), " sample", if length(t) > 1 "s" else "" end,
          " with ", t.params.evals, " evaluation", if t.params.evals > 1 "s" else "" end ,".\n")

    perm = sortperm(t.times)
    times = t.times[perm]
    gctimes = t.gctimes[perm]

    if length(t) > 1
        med = median(t)
        avg = mean(t)
        std = Statistics.std(t)
        min = minimum(t)
        max = maximum(t)

        medtime, medgc = prettytime(time(med)), prettypercent(gcratio(med))
        avgtime, avggc = prettytime(time(avg)), prettypercent(gcratio(avg))
        stdtime, stdgc = prettytime(time(std)), prettypercent(Statistics.std(gctimes ./ times))
        mintime, mingc = prettytime(time(min)), prettypercent(gcratio(min))
        maxtime, maxgc = prettytime(time(max)), prettypercent(gcratio(max))

        memorystr = string(prettymemory(memory(min)))
        allocsstr = string(allocs(min))
    elseif length(t) == 1
        print(io, pad, " Single result which took ")
        printstyled(io, prettytime(times[1]); color=:blue)
        print(io, " (", prettypercent(gctimes[1]/times[1]), " GC) ")
        print(io, "to evaluate,\n")
        print(io, pad, " with a memory estimate of ")
        printstyled(io, prettymemory(t.memory[1]); color=:yellow)
        print(io, ", over ")
        printstyled(io, t.allocs[1]; color=:yellow)
        print(io, " allocations.")
        return
    else
        print(io, pad, " No results.")
        return
    end

    lmaxtimewidth = maximum(length.((medtime, avgtime, mintime)))
    rmaxtimewidth = maximum(length.((stdtime, maxtime)))
    lmaxgcwidth = maximum(length.((medgc, avggc, mingc)))
    rmaxgcwidth = maximum(length.((stdgc, maxgc)))

    # Main stats

    print(io, pad, " Range ")
    printstyled(io, "("; color=:light_black)
    printstyled(io, "min"; color=:cyan, bold=true)
    print(io, " … ")
    printstyled(io, "max"; color=:magenta)
    printstyled(io, "):  "; color=:light_black)
    printstyled(io, lpad(mintime, lmaxtimewidth); color=:cyan, bold=true)
    print(io, " … ")
    printstyled(io, lpad(maxtime, rmaxtimewidth); color=:magenta)
    print(io, "  ")
    printstyled(io, "┊"; color=:light_black)
    print(io, " GC ")
    printstyled(io, "("; color=:light_black)
    print(io, "min … max")
    printstyled(io, "): "; color=:light_black)
    print(io, lpad(mingc, lmaxgcwidth), " … ", lpad(maxgc, rmaxgcwidth))

    print(io, "\n", pad, " Time  ")
    printstyled(io, "("; color=:light_black)
    printstyled(io, "median"; color=:blue, bold=true)
    printstyled(io, "):     "; color=:light_black)
    printstyled(io, lpad(medtime, lmaxtimewidth), rpad(" ", rmaxtimewidth + 5); color=:blue, bold=true)
    printstyled(io, "┊"; color=:light_black)
    print(io, " GC ")
    printstyled(io, "("; color=:light_black)
    print(io, "median")
    printstyled(io, "):    "; color=:light_black)
    print(io, lpad(medgc, lmaxgcwidth))

    print(io, "\n", pad, " Time  ")
    printstyled(io, "("; color=:light_black)
    printstyled(io, "mean"; color=:green, bold=true)
    print(io, " ± ")
    printstyled(io, "σ"; color=:green)
    printstyled(io, "):   "; color=:light_black)
    printstyled(io, lpad(avgtime, lmaxtimewidth); color=:green, bold=true)
    print(io, " ± ")
    printstyled(io, lpad(stdtime, rmaxtimewidth); color=:green)
    print(io, "  ")
    printstyled(io, "┊"; color=:light_black)
    print(io, " GC ")
    printstyled(io, "("; color=:light_black)
    print(io, "mean ± σ")
    printstyled(io, "):  "; color=:light_black)
    print(io, lpad(avggc, lmaxgcwidth), " ± ", lpad(stdgc, rmaxgcwidth))

    # Histogram

    histquantile = 0.99
    # The height and width of the printed histogram in characters.
    histheight = 2
    histwidth = 42 + lmaxtimewidth + rmaxtimewidth

    histtimes = times[1:round(Int, histquantile*end)]
    histmin = get(io, :histmin, first(histtimes))
    histmax = get(io, :histmax, last(histtimes))
    logbins = get(io, :logbins, nothing)
    bins = bindata(histtimes, histwidth - 1, histmin, histmax)
    append!(bins, [1, floor((1-histquantile) * length(times))])
    # if median size of (bins with >10% average data/bin) is less than 5% of max bin size, log the bin sizes
    if logbins === true || (logbins === nothing && median(filter(b -> b > 0.1 * length(times) / histwidth, bins)) / maximum(bins) < 0.05)
        bins, logbins = log.(1 .+ bins), true
    else
        logbins = false
    end
    hist = asciihist(bins, histheight)
    hist[:,end-1] .= ' '
    maxbin = maximum(bins)

    delta1 = (histmax - histmin) / (histwidth - 1)
    if delta1 > 0
        medpos = 1 + round(Int, (histtimes[length(times) ÷ 2] - histmin) / delta1)
        avgpos = 1 + round(Int, (mean(times) - histmin) / delta1)
    else
        medpos, avgpos = 1, 1
    end

    print(io, "\n")
    for r in axes(hist, 1)
        print(io, "\n", pad, "  ")
        for (i, bar) in enumerate(view(hist, r, :))
            color = :default
            if i == avgpos color = :green end
            if i == medpos color = :blue end
            printstyled(io, bar; color=color)
        end
    end

    remtrailingzeros(timestr) = replace(timestr, r"\.?0+ " => " ")
    minhisttime, maxhisttime = remtrailingzeros.(prettytime.(round.([histmin, histmax], sigdigits=3)))

    print(io, "\n", pad, "  ", minhisttime)
    caption = "Histogram: " * ( logbins ? "log(frequency)" : "frequency" ) * " by time"
    if logbins
        printstyled(io, " " ^ ((histwidth - length(caption)) ÷ 2 - length(minhisttime)); color=:light_black)
        printstyled(io, "Histogram: "; color=:light_black)
        printstyled(io, "log("; bold=true, color=:light_black)
        printstyled(io, "frequency"; color=:light_black)
        printstyled(io, ")"; bold=true, color=:light_black)
        printstyled(io, " by time"; color=:light_black)
    else
        printstyled(io, " " ^ ((histwidth - length(caption)) ÷ 2 - length(minhisttime)), caption; color=:light_black)
    end
    print(io, lpad(maxhisttime, ceil(Int, (histwidth - length(caption)) / 2) - 1), " ")
    printstyled(io, "<"; bold=true)

    # Memory info

    print(io, "\n\n", pad, " Memory estimate")
    printstyled(io, ": "; color=:light_black)
    printstyled(io, memorystr; color=:yellow)
    print(io, ", allocs estimate")
    printstyled(io, ": "; color=:light_black)
    printstyled(io, allocsstr; color=:yellow)
    print(io, ".")
end

function Base.show(io::IO, ::MIME"text/plain", t::TrialEstimate)
    println(io, "BenchmarkTools.TrialEstimate: ")
    pad = get(io, :pad, "")
    println(io, pad, "  time:             ", prettytime(time(t)))
    println(io, pad, "  gctime:           ", prettytime(gctime(t)), " (", prettypercent(gctime(t) / time(t)),")")
    println(io, pad, "  memory:           ", prettymemory(memory(t)))
    print(io,   pad, "  allocs:           ", allocs(t))
end

function Base.show(io::IO, ::MIME"text/plain", t::TrialRatio)
    println(io, "BenchmarkTools.TrialRatio: ")
    pad = get(io, :pad, "")
    println(io, pad, "  time:             ", time(t))
    println(io, pad, "  gctime:           ", gctime(t))
    println(io, pad, "  memory:           ", memory(t))
    print(io,   pad, "  allocs:           ", allocs(t))
end

function Base.show(io::IO, ::MIME"text/plain", t::TrialJudgement)
    println(io, "BenchmarkTools.TrialJudgement: ")
    pad = get(io, :pad, "")
    print(io, pad, "  time:   ", prettydiff(time(ratio(t))), " => ")
    printtimejudge(io, t)
    println(io, " (", prettypercent(params(t).time_tolerance), " tolerance)")
    print(io,   pad, "  memory: ", prettydiff(memory(ratio(t))), " => ")
    printmemoryjudge(io, t)
    println(io, " (", prettypercent(params(t).memory_tolerance), " tolerance)")
end


module Shapes

include("PartialSphere.jl")
include("Cylinder.jl")
include("Grid.jl")
include("PartialTorus.jl")

function choose(shape::Symbol)
    apps = Dict(
            :PartialSphere =>PartialSphere,
            :Cylinder => Cylinder,
            :Grid => Grid,
            :PartialTorus => PartialTorus,
        )
    if shape in keys(apps)
        return apps[shape].assemble
    else
        error("Error hint: shape \"$(string(shape))\" not recognized")
    end
end

end


"""
Materialize a `Projection` into a `DataFrame` by generating each output column
in sequence.
"""
function materialize(node::Projection)
    out = DataFrames.DataFrame()
    # TODO: Perfomanance optimization
    #    If there are no cross-dependencies, generate the output in parallel.
    for e in node.expressions
        out[e.alias] = generate_column(node.source, e)
    end
    out
end


# This file is part of Kpax3. License is MIT.

function readposteriork(fileroot::String)
  fp = string(fileroot, "_posterior_k.csv")
  d = DelimitedFiles.readdlm(fp, ',', String, header=true)

  len = size(d[1], 1)

  k = zeros(Int, len)
  pk = zeros(Float64, len)

  for i in 1:len
    k[i] = parse(Int, d[1][i, 1])
    pk[i] = parse(Float64, d[1][i, 2])
  end

  (k, pk)
end

function readposteriorP(fileroot::String)
  fp = string(fileroot, "_posterior_R.csv")
  d = DelimitedFiles.readdlm(fp, ',', String, header=true)

  len = size(d[1], 1)
  n = convert(Int, (1 + sqrt(1 + 8 * len)) / 2)

  id = fill("", n)
  P = ones(Float64, n, n)

  id[1] = d[1][1, 1]
  idx = 0
  for j in 1:(n - 1)
    id[j + 1] = d[1][j, 2]

    for i in (j + 1):n
      idx += 1
      P[i, j] = P[j, i] = parse(Float64, d[1][idx, 3])
    end
  end

  (id, P)
end

function readposteriorC(fileroot::String)
  fp = string(fileroot, "_posterior_C.csv")
  d = DelimitedFiles.readdlm(fp, ',', String, header=true)

  len = size(d[1], 1)

  site = zeros(Int, len)
  aa = fill("", len)
  freq = zeros(Float64, len)
  C = zeros(Float64, 3, len)

  for b in 1:len
    site[b] = parse(Int, d[1][b, 1])
    aa[b] = d[1][b, 2]
    freq[b] = parse(Float64, d[1][b, 3])
    C[1, b] = parse(Float64, d[1][b, 4])
    C[2, b] = parse(Float64, d[1][b, 5])
    C[3, b] = parse(Float64, d[1][b, 6])
  end

  (site, aa, freq, C)
end


using Test, AFK

_eltype = Float32
_test_atype = (AFK.gpu() >= 0 ? AFK.KnetArray{_eltype} : Array{_eltype})

_INPUTdim = 10; _OUTPUTdim = 2;

@time include("primitive.jl")


module ITensorNetworks

using ITensors

include("ising_classical_2d.jl")
include("tensor_networks.jl")
include("boundary_mps_projectors.jl")

end


# This file was generated, do not modify it. # hide
Normal() ≪ Lebesgue(ℝ)


import ..SamplingConstraints: SamplingConstraint

"""
    ConstrainedResampling(constraint::NTuple{N, Union{SamplingConstraint, Vector{<:SamplingConstraint}}})

Indicates that resampling should be performed with constraints on the uncertain values.
"""
struct ConstrainedResampling{N} <: AbstractUncertainDataResampling where N
    constraints::NTuple{N, Union{SamplingConstraint, Vector{<:SamplingConstraint}}}
end

ConstrainedResampling(constraints::Union{SamplingConstraint, Vector{<:SamplingConstraint}}...) =
    ConstrainedResampling(constraints)

ConstrainedResampling(constraints::Vector{Union{SamplingConstraint, Vector{<:SamplingConstraint}}}) =
    ConstrainedResampling((constraints...))

Base.length(resampling::ConstrainedResampling) = length(resampling.constraints)
Base.getindex(resampling::ConstrainedResampling, i) = resampling.constraints[i]
Base.firstindex(resampling::ConstrainedResampling) = 1
Base.lastindex(resampling::ConstrainedResampling) = length(resampling)

function summarise(resampling::ConstrainedResampling)
    _type = typeof(resampling)
    constraint_types = [typeof(c) for c in resampling.constraints]

    strs = ["$constraint" for  constraint in constraint_types]
    return "$_type" * "(" * join(strs, ", ") * ")"
end

Base.show(io::IO, resampling::ConstrainedResampling) = print(io, summarise(resampling))

export ConstrainedResampling


const SOLUTION_MAX_PRINT_LENGTH = 10

abstract type OptimizationAlgorithm end

solver_step!(alg::OptimizationAlgorithm) = error("solver_step! not implemented for $(typeof(alg))")
hessian(alg::OptimizationAlgorithm, args...) = error("hessian not implemented for $(typeof(alg))")
linesearch(alg::OptimizationAlgorithm, args...) = error("linesearch not implemented for $(typeof(alg))")
update!(::OptimizerResult, alg::OptimizationAlgorithm) = error("update! not implemented for $(typeof(alg))")
OptimizerState(alg::OptimizationAlgorithm, args...) = error("OptimizerState not implemented for $(typeof(alg))")

struct Optimizer{ALG <: NonlinearMethod,
                 OBJ <: MultivariateObjective,
                 OPT <: Options,
                 RES <: OptimizerResult,
                 AST <: OptimizationAlgorithm} <: NonlinearSolver
    algorithm::ALG
    objective::OBJ
    config::OPT
    result::RES
    state::AST
end

function Optimizer(x::VT, objective::MultivariateObjective; algorithm = BFGS(), linesearch = Bisection(), config = Options()) where {XT, VT <: AbstractVector{XT}}
    y = value(objective, x)
    result = OptimizerResult(x, y)
    astate = OptimizerState(algorithm, objective, x, y; linesearch = linesearch)

    Optimizer{typeof(algorithm), typeof(objective), typeof(config), typeof(result), typeof(astate)}(algorithm, objective, config, result, astate)
end

function Optimizer(x::AbstractVector, F::Function; ∇F! = nothing, kwargs...)
    G = GradientParameters(∇F!, F, x)
    objective = MultivariateObjective(F, G, x)
    Optimizer(x, objective; kwargs...)
end

config(opt::Optimizer) = opt.config
result(opt::Optimizer) = opt.result
status(opt::Optimizer) = opt.result.status
state(opt::Optimizer) = opt.state
objective(opt::Optimizer) = opt.objective
algorithm(opt::Optimizer) = opt.algorithm
linesearch(opt::Optimizer) = linesearch(opt.state)

Base.minimum(opt::Optimizer) = minimum(result(opt))
minimizer(opt::Optimizer) = minimizer(result(opt))

function Base.show(io::IO, opt::Optimizer)
    c = config(opt)
    s = status(opt)

    @printf io "\n"
    @printf io " * Algorithm: %s \n" algorithm(opt)
    @printf io "\n"
    @printf io " * Linesearch: %s\n" linesearch(opt)
    @printf io "\n"
    @printf io " * Iterations\n"
    @printf io "\n"
    @printf io "    n = %i\n" iterations(s)
    @printf io "\n"
    @printf io " * Convergence measures\n"
    @printf io "\n"
    @printf io "    |x - x'|               = %.2e %s %.1e\n"  x_abschange(s) x_abschange(s) ≤ x_abstol(c) ? "≤" : "≰" x_abstol(c)
    @printf io "    |x - x'|/|x'|          = %.2e %s %.1e\n"  x_relchange(s) x_relchange(s) ≤ x_reltol(c) ? "≤" : "≰" x_reltol(c)
    @printf io "    |f(x) - f(x')|         = %.2e %s %.1e\n"  f_abschange(s) f_abschange(s) ≤ f_abstol(c) ? "≤" : "≰" f_abstol(c)
    @printf io "    |f(x) - f(x')|/|f(x')| = %.2e %s %.1e\n"  f_relchange(s) f_relchange(s) ≤ f_reltol(c) ? "≤" : "≰" f_reltol(c)
    @printf io "    |g(x)|                 = %.2e %s %.1e\n"  g_residual(s)  g_residual(s)  ≤ g_restol(c) ? "≤" : "≰" g_restol(c)
    @printf io "\n"

    @printf io " * Candidate solution\n"
    @printf io "\n"
    length(minimizer(opt)) > SOLUTION_MAX_PRINT_LENGTH || @printf io  "    Final solution value:     [%s]\n" join([@sprintf "%e" x for x in minimizer(opt)], ", ")
    @printf io "    Final objective value:     %e\n" minimum(opt)
    @printf io "\n"

end

check_gradient(opt::Optimizer) = check_gradient(gradient(objective(opt)))
print_gradient(opt::Optimizer) = print_gradient(gradient(objective(opt)))
print_status(opt::Optimizer) = print_status(status(opt), config(opt))

assess_convergence(opt::Optimizer) = assess_convergence(status(opt), config(opt))
meets_stopping_criteria(opt::Optimizer) = meets_stopping_criteria(status(opt), config(opt))

function initialize!(opt::Optimizer, x::AbstractVector)
    clear!(objective(opt))
    initialize!(result(opt), x, value!(objective(opt), x), gradient!(objective(opt), x))
    initialize!(state(opt), x)
end

"compute objective and gradient at new solution and update result"
function update!(opt::Optimizer, x::AbstractVector)
    update!(result(opt), x, value!(objective(opt), x), gradient!(objective(opt), x))
end

function solve!(x, opt::Optimizer)
    initialize!(opt, x)

    while !meets_stopping_criteria(opt)
        next_iteration!(result(opt))
        solver_step!(x, state(opt))
        update!(opt, x)
    end

    warn_iteration_number(status(opt), config(opt))
    print_status(status(opt), config(opt))

    return x
end


"""
    has_particles(P)
Determine whether or no the object `P` has some kind of particles inside it. This function examins fields of `P` recursively and looks inside arrays etc.
"""
function has_particles(P)
    P isa AbstractParticles && (return true)
    if P isa AbstractArray
        length(P) < 1 && (return eltype(P) <: AbstractParticles)
        return has_particles(P[1])
    end
    any(fieldnames(typeof(P))) do n
        fp = getfield(P,n)
        if fp isa Union{AbstractArray, Tuple}
            length(fp) < 1 && (return eltype(fp) <: AbstractParticles)
            return has_particles(fp[1]) # Specials can occur inside arrays or tuples
        else
            return has_particles(fp)
        end
    end
end

"""
    has_mutable_particles(P)
Similar to `has_particles`, but only returns true if the found particles are mutable, i.e., are not `StaticParticles`
"""
function has_mutable_particles(P)
    P isa Particles && (return true)
    P isa StaticParticles && (return false)
    P isa AbstractArray && (return has_mutable_particles(P[1]))
    all(fieldnames(typeof(P))) do n
        fp = getfield(P,n)
        if fp isa Union{AbstractArray, Tuple}
            length(fp) < 1 && (return eltype(fp) <: Particles)
            return has_mutable_particles(fp[1])
        else
            return has_mutable_particles(fp)
        end
    end
end
"""
    nakedtypeof(x)
Returns the type of `x` with type parameters removed. Uses internals and should ideally not be used at all. Do not use inside generated function.
"""
nakedtypeof(x::Type) = x.name.wrapper
nakedtypeof(x) = nakedtypeof(typeof(x))

"""
    build_mutable_container(P)
Recursively visits all fields of `P` and replaces all instances of `StaticParticles` with `Particles`
"""
function build_mutable_container(P)
    has_mutable_particles(P) && (return P)
    replace_particles(P, replacer=P->Particles(Vector(P.particles)))
end

"""
    make_scalar(P)

Replaces all fields of `P` that are particles with `Particles(1)`
"""
function make_scalar(P)
    replace_particles(P, replacer=P->Particles([pmean(P)]))
end

"""
    restore_scalar(P, N)

Replaces all fields of `P` that are `Particles(1)` with `Particles(N)`
"""
function restore_scalar(P, N)
    replace_particles(P, replacer = P->Particles(N))
end

"""
    make_static(P)
Replaces all mutable particles inside `P` with `StaticParticles`.
"""
function make_static(P)
    !has_mutable_particles(P) && (return P)
    replace_particles(P, replacer = P->StaticParticles(P.particles))
end

"""
    build_container(P)
Recursively visits all fields of `P` and replaces all instances of `AbstractParticles{T,N}` with `::T`
"""
build_container(P) = replace_particles(P)

"""
    mean_object(x)
Returns an object similar to `x`, but where all internal instances of `Particles` are replaced with their mean. The generalization of this function is `replace_particles`.
"""
mean_object(p::AbstractParticles) = pmean(p)
mean_object(p::AbstractArray{<:AbstractParticles}) = pmean.(p)
mean_object(P) = replace_particles(P; replacer = P->pmean(P))

"""
    replace_particles(x; condition=P->P isa AbstractParticles,replacer = P->vecindex(P, 1))

This function recursively scans through the structure `x`, every time a field that matches `condition` is found, `replacer` is called on that field and the result is used instead of `P`. See function `mean_object`, which uses this function to replace all instances of `Particles` with their mean.
"""
function replace_particles(P; condition::F1=P->P isa AbstractParticles,replacer::F2 = P->vecindex(P, 1)) where {F1,F2}
    # @show typeof(P)
    condition(P) && (return replacer(P))
    has_particles(P) || (return P) # No need to carry on
    if P isa AbstractArray # Special handling for arrays
        return map(P->replace_particles(P;condition,replacer), P)
    end
    P isa Complex && condition(real(P)) && (return complex(replacer(real(P)), replacer(imag(P))))
    P isa Number && (return P)
    fields = map(fieldnames(typeof(P))) do n
        f = getfield(P,n)
        has_particles(f) || (return f)
        # @show typeof(f), n
        replace_particles(f; condition,replacer)
    end
    T = nakedtypeof(P)

    try
        if T <: NamedTuple # Special case required for NamedTuple
            return (;Pair.(keys(P), fields)...) # The semicolon is required to create named tuple
        else
            return T(fields...)
        end
    catch e
        if has_mutable_particles(P)
            @error("Failed to create a `$T` by calling it with its fields in order. For this to work, `$T` must have a constructor that accepts all fields in the order they appear in the struct and accept that the fields that contained particles are replaced by 0. Try defining a meaningful constructor that accepts arguments with the type signature \n`$(T)$(typeof.(fields))`\nThe error thrown by `$T` was ")
        else
            mutable_fields = build_mutable_container.(fields)
            @error("Failed to create a `$T` by calling it with its fields in order. For this to work, `$T` must have a constructor that accepts all fields in the order they appear in the struct and accept that the fields that contained particles are replaced by 0. Try defining a meaningful constructor that accepts arguments with the following two type signatures \n`$(T)$(typeof.(fields))`\n`$(T)$(typeof.(mutable_fields))`\nThe error thrown by `$T` was ")
        end
        rethrow(e)
    end

end

"""
particletypetuple(p::AbstractParticles{T,N}) = (T,N,ParticleType)
"""
particletypetuple(p::Particles{T,N}) where {T,N} = (T,N,Particles)
particletypetuple(::Type{Particles{T,N}}) where {T,N} = (T,N,Particles)
particletypetuple(p::StaticParticles{T,N}) where {T,N} = (T,N,StaticParticles)
particletypetuple(::Type{StaticParticles{T,N}}) where {T,N} = (T,N,StaticParticles)
particletypetuple(a::AbstractArray) = particletypetuple(eltype(a))

"""
    particle_paths(P)

Figure out all paths down through fields of `P` that lead to an instace of `<: AbstractParticles`. The returned structure is a list where each list element is a tuple. The tuple looks like this: (path, particletypetuple, particlenumber)
`path in turn looks like this (:fieldname, fieldtype, size)
"""
function particle_paths(P, allpaths=[], path=[])
    T = typeof(P)
    if T <: AbstractParticles
        push!(allpaths, (path,particletypetuple(T)...))
        return
    end
    if T <: AbstractArray
        particle_paths(P[1], allpaths, [path; (:input, T, size(P))])
    end
    if T <: Tuple
        particle_paths(P[1], allpaths, [path; (:input, T, length(P))])
    end
    for n in fieldnames(T)
        fp = getfield(P,n)
        FT = typeof(fp)
        if FT <: AbstractArray
            particle_paths(fp[1], allpaths, [path; (n, FT, size(fp))])
        elseif FT <: Tuple
                particle_paths(fp[1], allpaths, [path; (n, FT, length(fp))])
        else
            particle_paths(fp, allpaths, [path; (n, FT, ())])
        end
    end
    ntuple(i->allpaths[i], length(allpaths))
end

"""
    vecpartind2vec!(v, pv, j)

Extract particle `j` into vector `v`

# Arguments:
- `v`: vector
- `pv`: vector of `Particles`
- `j`: index
"""
function vecpartind2vec!(v, pv, j)
    for i in eachindex(v)
        v[i] = vecindex(pv[i], j)
    end
end

"""
    vec2vecpartind!(pv, v, j)

Extract particle `j` from vector `v` into particles

# Arguments:
- `v`: vector
- `pv`: vector of `Particles`
- `j`: index
"""
function vec2vecpartind!(pv, v, j)
    for i in eachindex(v)
        pv[i].particles[j] = v[i]
    end
end

"""
    s1 = get_buffer_setter(paths)
Returns a function that is to be used to update work buffer inside `Workspace`
This function is `@eval`ed and can cause world-age problems unless called with `invokelatest`.
"""
function get_buffer_setter(paths)
    setbufex = map(paths) do p # for each encountered particle
        getbufex = :(input)
        setbufex = :(simple_input)
        for (i,(fn,ft,fs)) in enumerate(p[1][1:end-1]) # p[1] is a tuple vector where the first element in each tuple is the fieldname
            if ft <: AbstractArray
                # Here we should recursively branch down into all the elements, but this seems very complicated so we'll just access element 1 for now
                getbufex = :($(getbufex).$(fn)[1])
                setbufex = :($(setbufex).$(fn)[1])
            else # It was no array, regular field
                getbufex = :($(getbufex).$(fn))
                setbufex = :($(setbufex).$(fn))
            end
        end
        (fn,ft,fs) = p[1][end] # The last element, we've reached the particles
        if ft <: AbstractArray
            setbufex = :(vecpartind2vec!($(setbufex).$(fn), $(getbufex).$(fn), partind))
            # getbufex = :(getindex.($(getbufex).$(fn), partind))

        else # It was no array, regular field
            getbufex = :($(getbufex).$(fn)[partind])
            setbufex = :($(getbufex).$(fn)[partind] = $getbufex)
        end
        setbufex = MacroTools.postwalk(setbufex) do x
            @capture(x, y_.input) && (return y)
            x
        end
        setbufex
    end
    # setbufex,getbufex = getindex.(setbufex, 1),getindex.(getbufex, 2)
    setbufex = Expr(:block, setbufex...)
    # getbufex = Expr(:block, getbufex...)
    @eval setbuffun = (input,simple_input,partind)-> $setbufex
    setbuffun

end

"""
    get_result_setter(result)
See `get_buffer_setter`.
"""
function get_result_setter(result)
    paths = particle_paths(result)
    setresex = map(paths) do p # for each encountered particle
        getresex = :(result)
        setresex = :(simple_result)
        for (fn,ft,fs) in p[1][1:end-1] # p[1] is a tuple vector where the first element in each tuple is the fieldname
            if ft <: Union{AbstractArray, Tuple}
                # Here we should recursively branch down into all the elements, but this seems very complicated so we'll just access element 1 for now
                getresex = :($(getresex).$(fn)[1])
                setresex = :($(setresex).$(fn)[1])
            else # It was no array, regular field
                getresex = :($(getresex).$(fn))
                setresex = :($(setresex).$(fn))
            end
        end
        (fn,ft,fs) = p[1][end] # The last element, we've reached the particles
        if ft <: AbstractArray
            setresex = :(vec2vecpartind!($(getresex).$(fn), $(setresex).$(fn), partind))
            # getresex = :(getindex.($(getresex).$(fn), partind))

        else # It was no array, regular field
            getresex = :($(getresex).$(fn)[partind])
            setresex = :($(getresex).$(fn)[partind] = $getbufex)
        end
        setresex = MacroTools.postwalk(setresex) do x
            @capture(x, y_.input) && (return y)
            x
        end
        setresex
    end
    setresex = Expr(:block, setresex...)

    @eval setresfun = (result,simple_result,partind)-> $setresex
    setresfun
end

##
# We create a two-stage process with the outer function `withbuffer` and an inner macro with the same name. The reason is that the function generates an expression at *runtime* and this should ideally be compiled into the body of the function without a runtime call to eval. The macro allows us to do this

"""
    struct Workspace{T1, T2, T3, T4, T5, T6}

# Arguments:
- `simple_input`: Input object `f` will be called with, does not contain any particles
- `simple_result`: Simple output from `f` without particles
- `result`: Complete output of `f` including particles
- `buffersetter`: Helper function to shift data between objects
- `resultsetter`: Helper function to shift data between objects
- `f`: Function to call
- `N`: Number of particles
"""
struct Workspace{T1,T2,T3,T4,T5,T6}
    simple_input::T1
    simple_result::T2
    result::T3
    buffersetter::T4
    resultsetter::T5
    f::T6
    N::Int
end

"""
    Workspace(f, input)
Create a `Workspace` object for inputs of type `typeof(input)`. Useful if `input` is a structure with fields of type `<: AbstractParticles` (can be deeply nested). See also `with_workspace`.
"""
function Workspace(f,input)
    paths = particle_paths(input)
    buffersetter = get_buffer_setter(paths)
    @assert all(n == paths[1][3] for n in getindex.(paths,3))
    simple_input = build_container(input)
    N = paths[1][3]
    Base.invokelatest(buffersetter,input,simple_input,1)
    simple_result = f(simple_input) # We first to index 1 to peek at the result
    result = @unsafe restore_scalar(build_mutable_container(f(make_scalar(input))), N) # Heuristic, see what the result is if called with particles and unsafe_comparisons TODO: If the reason the workspace approach is used is that the function f fails for different reason than comparinsons, this will fail here. Maybe Particles{1} can act as constant and be propagated through
    resultsetter = get_result_setter(result)
    Workspace(simple_input,simple_result,result,buffersetter, resultsetter,f,N)
end

"""
    with_workspace(f,P)

In some cases, defining a primitive function which particles are to be propagate through is not possible but allowing unsafe comparisons are not acceptable. One such case is functions that internally calculate eigenvalues of uncertain matrices. The eigenvalue calculation makes use of comparison operators. If the uncertainty is large, eigenvalues might change place in the sorted list of returned eigenvalues, completely ruining downstream computations. For this we recommend, in order of preference
1. Use `@bymap` detailed [in the documentation](https://github.com/baggepinnen/MonteCarloMeasurements.jl#monte-carlo-simulation-by-mappmap). Applicable if all uncertain values appears as arguments to your entry function.
2. Create a `Workspace` object and call it using your entry function. Applicable if uncertain parameters appear nested in an object that is an argument to your entry function:
```julia
# desired computation: y = f(obj), obj contains uncertain parameters inside
y = with_workspace(f, obj)
# or equivalently
w = Workspace(f, obj)
use_invokelatest = true # Set this to false to gain 0.1-1 ms, at the expense of world-age problems if w is created and used in the same function.
w(obj, use_invokelatest)
```
"""
with_workspace(f,P) = Workspace(f,P)(P, true)

function (w::Workspace)(input)
    simple_input,simple_result,result,buffersetter,resultsetter,N,f = w.simple_input,w.simple_result,w.result,w.buffersetter,w.resultsetter,w.N,w.f
    for partind = 1:N
        buffersetter(input,simple_input, partind)
        simple_result = f(simple_input)
        Base.invokelatest(resultsetter, result,simple_result, partind)
    end
    has_mutable_particles(input) ? result : make_static(result)
end

function (w::Workspace)(input, invlatest::Bool)
    invlatest || w(input)
    simple_input,simple_result,result,buffersetter,resultsetter,N,f = w.simple_input,w.simple_result,w.result,w.buffersetter,w.resultsetter,w.N,w.f

    for partind = 1:N
        Base.invokelatest(buffersetter, input,simple_input, partind)
        simple_result = f(simple_input)
        Base.invokelatest(resultsetter, result,simple_result, partind)
    end
    has_mutable_particles(input) ? result : make_static(result)
end

"""
    array_of_structs(f, arg)

Exectues `f` on each instance of `arg` represented by internal particles of `arg`. This is useful as a last resort if all other methods to propagate particles through `f` fails. The function returns an array (length = num. particles) of structs rather than particles, each struct is the result of `f(replace_particles(arg, p->p[i]))`.
"""
function array_of_structs(f, arg)
    N = particle_paths(arg)[end][end-1]
    map(1:N) do i
        arg_i = replace_particles(arg, replacer=p->vecindex(p, i))
        f(arg_i)
    end
end

# macro withbuffer(f,P,simple_input,setters,setters2,N)
#     quote
#         $(esc(:(partind = 1))) # Because we need the actual name partind
#         $(esc(setters))($(esc(P)),$(esc(simple_input)), $(esc(:partind)))
#         $(esc(:simple_result)) = $(esc(f))($(esc(simple_input))) # We first to index 1 to peek at the result
#         result = @unsafe build_mutable_container($(esc(f))($(esc(P)))) # Heuristic, see what the result is if called with particles and unsafe_comparisons
#         $(esc(setters2))(result,$(esc(:simple_result)), $(esc(:partind)))
#         for $(esc(:partind)) = 2:$(esc(N))
#             $(esc(setters))($(esc(P)),$(esc(simple_input)), $(esc(:partind)))
#             $(esc(:simple_result)) = $(esc(f))($(esc(simple_input)))
#             $(esc(setters2))(result,$(esc(:simple_result)), $(esc(:partind)))
#         end
#         result
#     end
# end
#
# @generated function withbufferg(f,P,simple_input,N,setters,setters2)
#     ex = Expr(:block)
#     push!(ex.args, quote
#         partind = 1 # Because we need the actual name partind
#     end)
#     push!(ex.args, :(setters(P,simple_input,partind)))
#     push!(ex.args, quote
#         simple_result = f(simple_input) # We first to index 1 to peek at the result
#         result = @unsafe f(P)
#     end) #build_container(paths, results[1])
#     push!(ex.args, :(setters2(result,simple_result,partind)))
#     loopex = Expr(:block, :(setters(P,simple_input,partind)))
#     push!(loopex.args, :(simple_result = f(simple_input)))
#     push!(loopex.args, :(setters2(result,simple_result,partind)))
#     push!(ex.args, quote
#         for partind = 2:N
#             $loopex
#         end
#     end)
#     push!(ex.args, :result)
#     ex
# end
#


function fill_refs!(refs::AbstractArray, X::AbstractArray,
                    breaks::AbstractVector, extend::Bool, nullok::Bool)
    n = length(breaks)
    lower = first(breaks)
    upper = last(breaks)

    @inbounds for i in eachindex(X)
        x = X[i]

        if extend && x == upper
            refs[i] = n-1
        elseif !extend && !(lower <= x < upper)
            throw(ArgumentError("value $x (at index $i) does not fall inside the breaks: adapt them manually, or pass extend=true"))
        else
            refs[i] = searchsortedlast(breaks, x)
        end
    end
end

function fill_refs!{T<:Nullable}(refs::AbstractArray, X::AbstractArray{T},
                                  breaks::AbstractVector, extend::Bool, nullok::Bool)
    n = length(breaks)
    lower = first(breaks)
    upper = last(breaks)

    @inbounds for i in eachindex(X)
        isnull(X[i]) && continue

        x = unsafe_get(X[i])

        if extend && x == upper
            refs[i] = n-1
        elseif !extend && !(lower <= x < upper)
            nullok || throw(ArgumentError("value $x (at index $i) does not fall inside the breaks: adapt them manually, or pass extend=true or nullok=true"))
            refs[i] = 0
        else
            refs[i] = searchsortedlast(breaks, x)
        end
    end
end

function fill_refs!(refs::AbstractArray, X::NullableArray,
                    breaks::AbstractVector, extend::Bool, nullok::Bool)
    n = length(breaks)
    lower = first(breaks)
    upper = last(breaks)

    @inbounds for i in eachindex(X)
        X.isnull[i] && continue

        x = X.values[i]

        if extend && x == upper
            refs[i] = n-1
        elseif !extend && !(lower <= x < upper)
            nullok || throw(ArgumentError("value $x (at index $i) does not fall inside the breaks: adapt them manually, or pass extend=true or nullok=true"))
            refs[i] = 0
        else
            refs[i] = searchsortedlast(breaks, x)
        end
    end
end

_extrema(X::Any) = extrema(X)
# NullableArrays provide a more efficient version with higher precedence
_extrema{T<:Nullable}(X::AbstractArray{T}) = (minimum(X), maximum(X))

unwrap(x::Any) = x
unwrap(x::Nullable) = x.value

"""
    cut(x::AbstractArray, breaks::AbstractVector;
        extend::Bool=false, labels::AbstractVector=[])

Cut a numeric array into intervals and return an ordered `CategoricalArray` indicating
the interval into which each entry falls. Intervals are of the form `[lower, upper)`,
i.e. the lower bound is included and the upper bound is excluded.

# Arguments
* `extend::Bool=false`: when `false`, an error is raised if some values in `x` fall
  outside of the breaks; when `true`, breaks are automatically added to include all
  values in `x`, and the upper bound is included in the last interval.
* `labels::AbstractVector=[]`: a vector of strings giving the names to use for the
  intervals; if empty, default labels are used.

    cut(x::AbstractArray{<:Nullable}, breaks::AbstractVector;
        extend::Bool=false, labels::AbstractVector=[]), nullok::Bool=false)

For nullable arrays, return a `NullableCategoricalArray`. If `nullok=true`, values outside
of breaks result in null values.
"""
function cut{T, N, U<:AbstractString}(x::AbstractArray{T, N}, breaks::AbstractVector;
                                      extend::Bool=false, labels::AbstractVector{U}=String[],
                                      nullok::Bool=false)
    if !issorted(breaks)
        breaks = sort(breaks)
    end

    if extend
        min_x, max_x = _extrema(x)
        if !isnull(min_x) && breaks[1] > unwrap(min_x)
            unshift!(breaks, unwrap(min_x))
        end
        if !isnull(max_x) && breaks[end] < unwrap(max_x)
            push!(breaks, unwrap(max_x))
        end
    end

    refs = Array{DefaultRefType, N}(size(x))
    try
        fill_refs!(refs, x, breaks, extend, nullok)
    catch err
        # So that the error appears to come from cut() itself,
        # since it refers to its keyword arguments
        if isa(err, ArgumentError)
            throw(err)
        else
            rethrow(err)
        end
    end

    n = length(breaks)
    if isempty(labels)
        from = map(x -> sprint(showcompact, x), breaks[1:n-1])
        to = map(x -> sprint(showcompact, x), breaks[2:n])
        levs = Vector{String}(n-1)
        for i in 1:n-2
            levs[i] = string("[", from[i], ", ", to[i], ")")
        end
        if extend
            levs[end] = string("[", from[end], ", ", to[end], "]")
        else
            levs[end] = string("[", from[end], ", ", to[end], ")")
        end
    else
        length(labels) == n-1 || throw(ArgumentError("labels must be of length $(n-1), but got length $(length(labels))"))
        levs = copy(labels)
    end

    pool = CategoricalPool(levs, true)
    if T <: Nullable
        NullableCategoricalArray{String, N, DefaultRefType}(refs, pool)
    else
        CategoricalArray{String, N, DefaultRefType}(refs, pool)
    end
end

"""
    cut(x::AbstractArray, ngroups::Integer;
        labels::AbstractVector=String[])

Cut a numeric array into `ngroups` quantiles, determined using
[`quantile`](@ref).
"""
cut{U<:AbstractString}(x::AbstractArray, ngroups::Integer;
                       labels::AbstractVector{U}=String[]) =
    cut(x, quantile(x, (1:ngroups-1)/ngroups); extend=true, labels=labels)


function Base.iterate(layer::AbstractFeatureLayer, state::Int=0)
    layer.ptr == C_NULL && return nothing
    state == 0 && resetreading!(layer)
    ptr = GDAL.ogr_l_getnextfeature(layer.ptr)
    if ptr == C_NULL
        resetreading!(layer)
        return nothing
    else
        return (Feature(ptr), state+1)
    end
end

Base.eltype(layer::AbstractFeatureLayer) = Feature

Base.length(layer::AbstractFeatureLayer) = nfeature(layer, true)

struct BlockIterator
    rows::Cint
    cols::Cint
    ni::Cint
    nj::Cint
    n::Cint
    xbsize::Cint
    ybsize::Cint
end

function blocks(raster::AbstractRasterBand)
    (xbsize, ybsize) = blocksize(raster)
    rows = height(raster)
    cols = width(raster)
    ni = ceil(Cint, rows / ybsize)
    nj = ceil(Cint, cols / xbsize)
    BlockIterator(rows, cols, ni, nj, ni * nj, xbsize, ybsize)
end

function Base.iterate(obj::BlockIterator, iter::Int=0)
    iter == obj.n && return nothing
    j = floor(Int, iter / obj.ni)
    i = iter % obj.ni
    nrows = if (i + 1) * obj.ybsize < obj.rows
        obj.ybsize
    else
        obj.rows - i * obj.ybsize
    end
    ncols = if (j + 1) * obj.xbsize < obj.cols
        obj.xbsize
    else
        obj.cols - j * obj.xbsize
    end
    (((i, j), (nrows, ncols)), iter+1)
end

struct WindowIterator
    blockiter::BlockIterator
end
Base.size(i::WindowIterator) = (i.blockiter.ni, i.blockiter.nj)
Base.length(i::WindowIterator) = i.blockiter.n
Base.IteratorSize(::Type{WindowIterator}) = Base.HasShape{2}()
Base.IteratorEltype(::Type{WindowIterator}) = Base.HasEltype()
Base.eltype(::WindowIterator) = Tuple{UnitRange{Int}, UnitRange{Int}}

windows(raster::AbstractRasterBand) = WindowIterator(blocks(raster))

function Base.iterate(obj::WindowIterator, iter::Int=0)
    handle = obj.blockiter
    next = Base.iterate(handle, iter)
    next == nothing && return nothing
    (((i, j), (nrows, ncols)), iter) = next
    (((1:ncols) .+ j * handle.xbsize, (1:nrows) .+ i * handle.ybsize), iter)
end

mutable struct BufferIterator{T <: Real}
    raster::AbstractRasterBand
    w::WindowIterator
    buffer::Array{T, 2}
end

function bufferwindows(raster::AbstractRasterBand)
    BufferIterator(
        raster,
        windows(raster),
        Array{pixeltype(raster)}(undef, blocksize(raster)...)
    )
end

function Base.iterate(obj::BufferIterator, iter::Int=0)
    next = Base.iterate(obj.w, iter)
    next == nothing && return nothing
    ((cols, rows), iter) = next
    rasterio!(obj.raster, obj.buffer, rows, cols)
    return (obj.buffer[1:length(cols), 1:length(rows)], iter)
end


module test_82_AllExportedFunctions

import ModiaResult
ModiaResult.@usingModiaPlot

# Test the Figure operations
println("\n... test test_82_AllExportedFunctions.jl:\n")

t = range(0.0, stop=10.0, length=100)
result = Dict{String,Any}("time" => t, "phi" => sin.(t))
info   = ModiaResult.resultInfo(result)
ModiaResult.printResultInfo(result)

plot(result, "phi", figure=2)

showFigure(2)
saveFigure(2, "test_saveFigure.png")
closeFigure(2)
closeAllFigures()

end


struct NullInvariants end
Base.getindex(::NullInvariants, i...) = error("Invariants were indexed but the invariants are `nothing`. You likely forgot to pass invariants to the Equation!")
Base.iterate(::NullInvariants) = error("Invariants were indexed but the invariants are `nothing`. You likely forgot to pass invariants to the Equation!")

struct NullParameters end
Base.getindex(::NullParameters, i...) = error("Parameters were indexed but the parameters are `nothing`. You likely forgot to pass parameters to the Equation!")
Base.iterate(::NullParameters) = error("Parameters were indexed but the parameters are `nothing`. You likely forgot to pass parameters to the Equation!")

struct NullPeriodicity end
Base.getindex(::NullPeriodicity, i...) = error("Periodicity was indexed but the periodicity is `nothing`. You likely forgot to pass periodicity to the Equation!")
Base.iterate(::NullPeriodicity) = error("Periodicity was indexed but the periodicity is `nothing`. You likely forgot to pass periodicity to the Equation!")

const OptionalArray{arrayType} = Union{Nothing, arrayType} where {arrayType <: AbstractArray}

const OptionalAbstractArray = Union{Nothing, AbstractArray}
const OptionalCallable      = Union{Nothing, Callable}
const OptionalFunction      = Union{Nothing, Function}
const OptionalNamedTuple    = Union{Nothing, NamedTuple}
const OptionalTuple         = Union{Nothing, Tuple}

const OptionalInvariants    = Union{NamedTuple, NullInvariants}
const OptionalParameters    = Union{NamedTuple, NullParameters}
const OptionalPeriodicity   = Union{AbstractArray, NullPeriodicity}

const AbstractData = Union{Number, AbstractArray{<:Number}}

const State{DT <: Number} = AbstractArray{DT}
const StateVector{DT,VT} = VT where {DT, VT <: AbstractVector{<:State{DT}}}

Base.zero(X::ST) where {DT, VT, ST <: StateVector{DT,VT}} = VT[zero(x) for x in X]

SolutionVector{DT} = Union{Vector{DT}, Vector{TwicePrecision{DT}}}


# This script contains the plotting of timer output
using Plots

function plotTimerOutput(archs, dims, to::TimerOutput, modelName)

    x = fill(0, length(dims))
    y = fill(0.0, length(dims))

    # Build a list of architectures in a single string, to include in the title
    archList = ""
    for arch in archs
        if arch != archs[end]
            archList *= arch*" vs. "
        else
            archList *= arch*" "
        end
    end

    # Make the plot title
    plotTitle = "Performance comparison for " * archList * "for "* modelName * " model"

    # Start the empty plot with title
    p = plot(title=plotTitle)

    # Create placeholder for the reference time (used for computing the speed-up)
    refTime = 0.0

    # For now allow up to 5 different architectures.
    # When more are needed, expand this list with more markers.
    mark = [(:circle), (:square), (:cross), (:pentagon), (:hexagon)]

    # Now loop over all architecture and grid resolutions to build up the plot
    archIdx = 0
    for arch in archs
        archIdx += 1
        dimIdx = 0
        for dim in dims
            dimIdx += 1
            dimstr = string(dim)
            time = TimerOutputs.time(to[arch][dimstr])

            # Determine total number of cells for this resolution
            numcells = 1
            for idim in dim
                numcells *=idim
            end
            x[dimIdx] = numcells
            # Time was given in nano-seconds: convert to seconds
            y[dimIdx] = Float64(time)/1.0e9
        end
        if arch == archs[1]
            refTime = copy(y)
        end
        p = plot!(x,y, marker=mark[archIdx], size=(800,800), xlabel="# of cells", ylabel="Time (s)", label=arch, legend=(0.2, 0.9), grid=true)
    end

    @show SpeedUp = refTime./y
    p = plot!(x,SpeedUp, marker=:diamond, size=(800,800), xlabel="# of cells", ylabel="Time (s)", label="Architectural speed up", legend=(0.2, 0.9), grid=true)

    #display(p)
    figureTitle = "performance_plot_" * modelName * ".png"
    #savefig(p, figureTitle)

end


"""
    save_input_mat(case)

Read the original case.mat file, replace relevant parameters from Case struct,
save a new input.mat file with parameters as they're passed to solver.
"""
function save_input_mat(
    case::Case, storage::Storage, inputfolder::String, outputfolder::String
)
    # MATPOWER column indices
    gen_PMAX = 9
    gen_PMIN = 10
    gen_RAMP_30 = 19
    gencost_MODEL = 1
    gencost_NCOST = 4
    gencost_COST = 5

    # Read original
    case_mat_file = MAT.matopen(joinpath(inputfolder, "case.mat"))
    mpc = read(case_mat_file, "mpc")
    mdi = Dict("mpc" => mpc)

    # Save modifications to gen
    mpc["gen"][:, gen_PMIN] = case.gen_pmin
    mpc["gen"][:, gen_RAMP_30] = case.gen_ramp30
    # Save modifications to gencost table
    mpc["gencost"] = case.gencost
    mpc["gencost_orig"] = case.gencost_orig

    # Save storage details in mpc.[gencost, genfuel, gencost] and in 'Storage'
    if size(storage.gen, 1) > 0
        num_storage = size(storage.gen, 1)
        # Add fuel types for storage 'generators'. ESS = energy storage system
        mpc["genfuel"] = [mpc["genfuel"]; repeat(["ess"], num_storage)]
        # Save storage modifications to gen table
        mpc["gen"] = [mpc["gen"]; storage.gen]
        # Save storage modifications to gencost
        #@show case.gencost[:, gencost_MODEL]
        segment_indices = findall(case.gencost[:, gencost_MODEL] .== 1)
        segment_orders = case.gencost[segment_indices, gencost_NCOST]
        num_segments = convert(Int, maximum(segment_orders)) - 1
        storage_gencost = zeros(num_storage, (6 + 2 * num_segments))
        # Storage is specified by two points, PMIN and PMAX, both with cost 0
        storage_gencost[:, gencost_MODEL] .= 1
        storage_gencost[:, gencost_NCOST] .= 2
        storage_gencost[:, gencost_COST] = storage.gen[:, gen_PMIN]
        storage_gencost[:, gencost_COST + 2] = storage.gen[:, gen_PMAX]
        mpc["gencost"] = [mpc["gencost"]; storage_gencost]
        # Save addition of 'iess' field (index, energy storage systems) to mpc
        num_gen = length(case.genid)
        # Mimic the array that MATPOWER/MOST would create
        iess = collect((num_gen + 1):(num_gen + num_storage))
        mpc["iess"] = iess
        # Build new struct for data in 'Storage'
        input_mat_storage = Dict(
            String(s) => storage.sd_table[!, s] for s in DataFrames.names(storage.sd_table)
        )
        mdi["Storage"] = input_mat_storage
    end

    output_path = joinpath(outputfolder, "input.mat")
    MAT.matwrite(output_path, Dict("mdi" => mdi); compress=true)
    return nothing
end

"""Given a Results object and a filename, save a matfile with results data."""
function save_results(results::Results, filename::String; demand_scaling::Number=1.0)
    mdo_save = Dict(
        "results" => Dict("f" => results.f),
        "demand_scaling" => demand_scaling,
        "flow" => Dict(
            "mpc" => Dict(
                "bus" => Dict("LAM_P" => results.lmp),
                "gen" => Dict("PG" => results.pg),
                "branch" => Dict(
                    "PF" => results.pf,
                    "MU_SF" => results.congu,
                    "MU_ST" => results.congl,
                ),
            ),
        ),
    )
    # For DC lines, storage power/energy, load_shed, and flexible demand,
    # save only if nonempty
    if size(results.pf_dcline) != (0, 0)
        mdo_save["flow"]["mpc"]["dcline"] = Dict("PF_dcline" => results.pf_dcline)
    end
    if size(results.storage_pg) != (0, 0)
        mdo_save["flow"]["mpc"]["storage"] = Dict(
            "PG" => results.storage_pg, "Energy" => results.storage_e
        )
    end
    if size(results.load_shed) != (0, 0)
        mdo_save["flow"]["mpc"]["load_shed"] = Dict("load_shed" => results.load_shed)
    end
    if size(results.load_shift_dn) != (0, 0)
        mdo_save["flow"]["mpc"]["flexible_demand"] = Dict(
            "load_shift_up" => results.load_shift_up,
            "load_shift_dn" => results.load_shift_dn,
        )
    end
    return MAT.matwrite(filename, Dict("mdo_save" => mdo_save); compress=true)
end

"""
    redirect_stdout_stderr("stdout.log", "stderr.err") do
        run_scenario(; kwargs...)
    end

While executing a function, redirect stdout and stderr to files.
"""
function redirect_stdout_stderr(dofunc, stdoutfile, stderrfile)
    open(stdoutfile, "a") do out
        open(stderrfile, "a") do err
            redirect_stdout(out) do
                redirect_stderr(err) do
                    dofunc()
                end
            end
        end
    end
end


module Mathbox

export bar3d, vec3d, surf
using JSON
using Patchwork

include("patchworkpatch.jl")

const mbjsdir = joinpath(dirname(Base.source_path()), "js")
const mbjs = joinpath(mbjsdir, "mathbox-bundle.js") |> readstring
const mbjlinitjs = joinpath(mbjsdir, "mbjlinit.js") |> readstring

__init__() = begin
  try
      Patchwork.load_js_runtime()
      display("text/html", script_elem(mbjs, mbjlinitjs))
  catch e
      error(e)
  end
end

script_elem(jsstrs...) =
  Elem(:script, join(jsstrs,";\n")) & Dict(:type=>"text/javascript", :charset=>"utf-8")

plotdefaults = Dict(:height=>"500px", :wigglefactor=>0.0)

"""
Returns a div and a script which sets up a scene in the div with axes and a grid
"""
scene_setup_elem(divid, fulldivid, params) = begin
  divsetupjs = """
  var divid = "$divid"
  var fulldivid = "$fulldivid"
  """
  scene_setupjs = joinpath(mbjsdir, "mbscene_axes_setup.js") |> readstring
  [Elem(:div; id=fulldivid, style=Dict(:height=>params[:height])),
   script_elem(divsetupjs, scene_setupjs)]
end

scene_from_template{T<: Real}(
    data_matrices::Vector{Matrix{T}}, template::AbstractString;
    divid=randstring(12), params=plotdefaults, reinit_scene=false) = begin
  params = merge(plotdefaults, params)
  divid = string(divid)
  data_assignments = ""
  for (i,data) in enumerate(data_matrices)
    tdata = data'
    nrows, ncols = size(tdata)
    data_assignments *= """
      var data$i = $(tdata |> vec |> json)
      var drows$i = $nrows
      var dcols$i = $ncols
    """
  end
  template_jsstr = joinpath(mbjsdir, template) |> readstring
  fulldivid = "mathbox-div-"*divid
  pwelems = scene_setup_elem(divid, fulldivid, params)
  variable_assignments = """
$data_assignments
var divid = "$divid"
var mathbox = mbinstances[divid]
var view = mbinstances["view"+divid]
mbjlparams[divid] = $(params |> json)
"""
  script_contents = """
(function mbdiv$divid(){
  $variable_assignments
  $template_jsstr
})()
"""
  push!(pwelems, script_elem(script_contents))
  Elem(:div, pwelems)
end

bar3d{T <: Real}(data::Matrix{T}; divid=randstring(12), params=plotdefaults, reinit_scene=false) =
  scene_from_template(Matrix{T}[data], "mbbar3d_template.js"; divid=divid, params=params, reinit_scene=reinit_scene)

vec3d{T <: Real}(data::Matrix{T}; bases=zeros(data), divid=randstring(12), params=plotdefaults, reinit_scene=false) =
  scene_from_template(Matrix{T}[data, bases], "mbvec3d_template.js"; divid=divid, params=params, reinit_scene=reinit_scene)

surf{T<: Real}(data::Matrix{T}; divid=randstring(12), params=plotdefaults, reinit_scene=false) =
  scene_from_template(Matrix{T}[data], "mbsurf_template.js"; divid=divid, params=params, reinit_scene=reinit_scene)

end


@testset "SBML import and conversion" begin
    sbmlm = load_sbml_model(model_paths["ecoli_core_model.xml"])
    m = convert(CoreModel, sbmlm)

    @test size(stoichiometry(sbmlm)) == (92, 95)
    @test size(stoichiometry(m)) == (n_metabolites(sbmlm), n_reactions(sbmlm))
    @test length(m.S.nzval) == 380
    @test length.(bounds(sbmlm)) == (95, 95)
    @test length.(bounds(m)) == (95, 95)
    @test all([length(m.xl), length(m.xu), length(m.c)] .== 95)

    @test metabolites(m)[1:3] == ["M_succoa_c", "M_ac_c", "M_fru_b"]
    @test reactions(m)[1:3] == ["R_EX_fum_e", "R_ACONTb", "R_GLNS"]

    cm = convert(CoreModelCoupled, sbmlm)
    @test n_coupling_constraints(cm) == 0
end


# LibTOAST.jl: interface to the TOAST++ library
# Copyright (C) 2019 Samuel Powell

# Imports
import Base: string, print, show

# Export types
export Mesh

# Export methods
export delete, string, print, show, load, save,
  nodecount, elemcount, dimensions, boundingbox, fullsize, data, maxnodes, surface

# Type definitions
"""
    Mesh

A reference to a TOAST mesh, with cached sparsity structure for re-use in the
generation of, e.g., system matrices.
"""
mutable struct Mesh

  ptr::Cxx.CxxCore.CppPtr
  rowptr::Array{Cint,1}
  colidx::Array{Cint,1}

  function Mesh(ptr::Cxx.CxxCore.CppPtr)

    # Remove unused nodes
    if @cxx ptr->Shrink()
      warn("Removing unused nodes.");
    end

    # Perform initialisation
    _info("Initiliasing mesh.")
    @cxx ptr->Setup()

    # Compute sparsity pattern
    _info("Calculating sparsity pattern.")
    rowptr, colidx = _sparsity!(ptr)

    mesh = new(ptr, rowptr, colidx)
    finalizer(_mesh_delete, mesh)

    return mesh

  end

end

"""
    Mesh(vtxs, elem, eltp)

Construct a new mesh given a list of vertices, elements, and element types.

# Arguments
* `vtxs::Matrix{Float64}`: matrix of vertices (nvtx x dim)
* `elem::Matrix{Integer}`: element connectivity matrix (nels x mnnd)
* `eltp::Vector{Integer}`: a vector of element types (nels x 1)

...where `nvtx` is the number of vertices in the mesh, `dim` is the spatial
dimension, `nels` is the number of elements, `mnnd` is the maximium number of
nodes per element.
"""
function Mesh(vtxs::Matrix{Float64}, elem::Matrix{I}, eltp::Vector{I}) where {I <: Integer}
  meshptr = _mesh_new()
  _make!(meshptr,vtxs,elem,eltp)
  return Mesh(meshptr)
end

"""
    Mesh(filename)

Construct a new mesh from a specified file.
"""
function Mesh(fn::String)
  meshptr = _mesh_new()
  _load!(meshptr,fn)
  return Mesh(meshptr)
end

# Create new mesh pointer
_mesh_new() = @cxxnew TOAST_Mesh()

# Delete and deallocate mesh pointer
# TODO: Does Cxx.jl do this for me?
_mesh_delete(mesh) = finalize(mesh.ptr) # icxx"""delete $(mesh.ptr);"""

# Compute sparsity structure in zero-based CSR format and 1-based CSC.
function _sparsity!(ptr::Cxx.CxxCore.CppPtr)

  rowptr = Ptr{Int32}[0]
  colidx = Ptr{Int32}[0]
  nnzero = Int32[0]

  icxx"""
    $(ptr)->SparseRowStructure($(pointer(rowptr))[0], $(pointer(colidx))[0], $(pointer(nnzero))[0]);
  """

  nnd = @cxx ptr->nlen()

  _info("Number of vertices: $nnd, number of nonzeros: $(nnzero[1])")

  # Zero based rowptr and colidx, for resuse when building a system matrix
  rowptr = unsafe_wrap(Array, rowptr[1], nnd+1, own = true)
  colidx = unsafe_wrap(Array, colidx[1], nnzero[1], own = true)

  return rowptr, colidx

end

# Build a mesh from a matrix of vertices, elements, and element types
function _make!(meshptr::Cxx.CxxCore.CppPtr, node, elem, eltp)

  jnvtx = size(node,1)
  jnel = size(elem,1)
  jdim = size(node,2)
  jnnd0 = size(elem,2)

  # The following code fragment is modified from mtMesh.cc
  icxx"""
      int i, j, k;
      int nvtx = $jnvtx;
      int nel  = $jnel;
      int dim  = $jdim;
      int nnd0 = $jnnd0;
      double *vtx = $(pointer(node));
      int *idx = $(pointer(elem));
      int *etp = $(pointer(eltp));

      Mesh *mesh = $(meshptr);

      // Create node list
      mesh->nlist.New (nvtx);
      for (i = 0; i < nvtx; i++) {
        mesh->nlist[i].New(dim);
        mesh->nlist[i].SetBndTp (BND_NONE); // Unknown (yet)
      }
      for (j = k = 0; j < dim; j++) {
        for (i = 0; i < nvtx; i++) {
          mesh->nlist[i][j] = vtx[k++];
        }
      }

      // Create element list
      Element *el, **list = new Element*[nel];
      for (i = 0; i < nel; i++) {
        int eltp = (int)(etp[i]+0.5);
        switch (eltp) {
          case ELID_TRI3OLD:
            list[i] = new Triangle3old;
            break;
          case ELID_TET4:
            list[i] = new Tetrahedron4;
            break;
          case ELID_WDG6:
            list[i] = new Wedge6;
            break;
          case ELID_VOX8:
            list[i] = new Voxel8;
            break;
          case ELID_TRI6:
            list[i] = new Triangle6;
            break;
          case ELID_TET10:
            list[i] = new Tetrahedron10;
            break;
          case ELID_TRI6_IP:
            list[i] = new Triangle6_ip;
            break;
          case ELID_TRI10:
            list[i] = new Triangle10;
            break;
          case ELID_TRI10_IP:
            list[i] = new Triangle10_ip;
            break;
          case ELID_TET10_IP:
            list[i] = new Tetrahedron10_ip;
            break;
          case ELID_PIX4:
            list[i] = new Pixel4;
            break;
          case ELID_TRI3:
            list[i] = new Triangle3;
            break;
          case ELID_TRI3D3:
            list[i] = new Triangle3D3;
            break;
          case ELID_TRI3D6:
            list[i] = new Triangle3D6;
            break;
          default:
            printf("Element type not supported!\n");
            list[i] = 0;
            break;
        }
      }
      mesh->elist.SetList (nel, list);
      delete []list;

      for (j = k = 0; j < nnd0; j++) {
        for (i = 0; i < nel; i++) {
          if ((el = mesh->elist[i])) {
            if (j < el->nNode())
              el->Node[j] = (int)(idx[k]-0.5);
            }
          k++;
        }
      }
    """

  return nothing

end

# Method definitions
# data
# surf
# maxnodes

# String conversion
string(mesh::Mesh) = ("")

# Print method
print(io::IO, mesh::Mesh) = print(io, string(mesh))

# Show method
show(io::IO, mesh::Mesh) = print(io, mesh)

# Load a mesh from the specified filename
function _load!(ptr::Cxx.CxxCore.CppPtr, fn::String)

  # TODO: We would like to do something like this:
  # ifs = @cxx std::ifstream(pointer(filename))
  # icxx"""$(ifs) >> *$(obj.meshptr);"""

  # @show ptr
  # Check if file exists
  if !isfile(fn)
    error("File not found ", fn)
  end

  # Load the mesh
  icxx"""
    ifstream ifs($(pointer(fn)));
    ifs >> *($(ptr));
    ifs.close();
  """

  _info("Loaded $(stat(fn).size) bytes from $(fn).")

  return

end

"""
    save(mesh, filename)

Save mesh to the specified filename.
"""
function save(mesh::Mesh, fn::String)

  # Output the mesh
  icxx"""
    ofstream ofs($(pointer(fn)));
    ofs << *($(mesh.ptr));
    ofs.close();
  """

  _info("Mesh written to $(fn), $(stat(fn).size) bytes.")

  return nothing

end

"""
    nodecount(mesh)

Return the number of nodes in the mesh.
"""
nodecount(mesh::Mesh) = @cxx mesh.ptr->nlen()

"""
    elemcount(mesh)

Return the number of elements in the mesh.
"""
elemcount(mesh::Mesh) = @cxx mesh.ptr->elen()

"""
    dimensions(mesh)

Return the number of spatial dimensions of the mesh.
"""
dimensions(mesh::Mesh) = @cxx mesh.ptr->Dimension()

"""
    maxnodes(mesh)

Return the maximum number of nodes in any element of the mesh.
"""
function maxnodes(mesh::Mesh)

  meshptr = mesh.ptr

  mnnd = icxx"""
    int elen = $meshptr->elen();
    int mnnd = $meshptr->elist[0]->nNode();
    for (int i = 0; i < elen; i++)
      mnnd = ::max (mnnd, $meshptr->elist[i]->nNode());
    return mnnd;
  """

  return mnnd

end

"""
  data(mesh)

Return matrices defining the vertices, element connectivity, and element types
of the specified mesh.
"""
function data(mesh::Mesh)

  meshptr = mesh.ptr

  ndim = dimensions(mesh)
  nvtx = nodecount(mesh)
  vtx = Array{Float64,2}(undef, nvtx, ndim)
  vtxptr = pointer(vtx)

  # for i in 0:(ndim-1), j in 0:(nvtx-1)
  #   icxx"""
  #     *$(vtxptr)++ = $(meshptr)->nlist[$j][$i];
  #     """
  # end

  icxx"""
    // vertex coordinate list
    for (int i = 0; i < $ndim; i++)
      for (int j = 0; j < $nvtx; j++)
            *$(vtxptr)++ = $(meshptr)->nlist[j][i];
  """

  mnnd = maxnodes(mesh)
  nele = elemcount(mesh)
  ele = Array{Cint}(undef, nele, mnnd)
  eleptr = pointer(ele)

  icxx"""
    // element index list
    // (1-based; value 0 indicates unused matrix entry)
    for (int i = 0; i < $mnnd; i++)
        for (int j = 0; j < $nele; j++)
            if (i < $(meshptr)->elist[j]->nNode())
                *$(eleptr)++ = $(meshptr)->elist[j]->Node[i]+1;
            else
                *$(eleptr)++ = 0;
  """

  elt = Vector{Cint}(undef, nele)
  eltptr = pointer(elt)

  icxx"""
    // element type list
    for (int i = 0; i < $nele; i++)
        *$(eltptr)++ = $(meshptr)->elist[i]->Type();
  """

  return vtx, ele, elt

end

"""
    surface(mesh)

Extract vertices and element connectivity of the surface of the mesh.
"""
function surface(mesh::Mesh)

  meshptr = mesh.ptr

  nlen = nodecount(mesh)
  ndim = dimensions(mesh)
  nbnd = icxx"""$(meshptr)->nlist.NumberOf(BND_ANY);"""

  vtx = Array{Float64}(undef, nbnd, ndim)

  icxx"""
    for (int i = 0; i < $(ndim); i++)
      for (int j = 0; j < $(nlen); j++)
        if ($(meshptr)->nlist[j].isBnd())
          *$(pointer(vtx))++ = $(meshptr)->nlist[j][i];
  """

  bndidx = Array{Cint}(undef, nlen)
  nnd = [Cint(0)]
  nface = [Cint(0)]

  icxx"""
    int  *bndellist, *bndsdlist;

    for (int j = 0, k = 0; j < $(nlen); j++)
      $(pointer(bndidx))[j] = ($(meshptr)->nlist[j].isBnd() ? k++ : -1);

    // NB: This assumes all elements contain the same number of vertices
    *$(pointer(nnd)) = 0;
    *$(pointer(nface)) = $(meshptr)->BoundaryList (&bndellist, &bndsdlist);
    for (int j = 0; j < *$(pointer(nface)); j++)
      *$(pointer(nnd)) = ::max (*$(pointer(nnd)), $(meshptr)->elist[bndellist[j]]->nSideNode(bndsdlist[j]));

    delete[] bndellist;
    delete[] bndsdlist;
  """

  idx = Array{Cint}(undef, nnd[1], nface[1])

  icxx"""
    int sd, nn, nd, bn, *bndellist, *bndsdlist;

    $(meshptr)->BoundaryList (&bndellist, &bndsdlist);

    for (int i = 0; i < *$(pointer(nnd)); i++)
      for (int j = 0; j < *$(pointer(nface)); j++) {
        Element *pel = $(meshptr)->elist[bndellist[j]];
        sd = bndsdlist[j];
        nn = pel->nSideNode (sd);
        if (i < nn) {
          nd = pel->Node[pel->SideNode (sd, i)];
          bn = $(pointer(bndidx))[nd]+1;
        } else bn = 0;
        *$(pointer(idx))++ = bn;
      }
  """

  return vtx, idx

end

"""
    boundingbox(mesh)

Return the bounding box of the mesh in a ``2 \times dim`` matrix.
"""
function boundingbox(mesh::Mesh)

  dim = dimensions(mesh)
  bb = Array{Cdouble}(undef, 2,dim)

  icxx"""
    double *jptr = $(pointer(bb));

    Point pmin($dim), pmax($dim);
    $(mesh.ptr)->BoundingBox(pmin, pmax);

    for(int i=0; i<$dim; i++) {
      *jptr++ = pmin[i];
      *jptr++ = pmax[i];
    }
  """

  return bb

end

"""
    size(mesh)

Compute the total area (2D) for volume (3D) of the mesh.
"""
fullsize(mesh::Mesh) = @cxx mesh.ptr->FullSize()


"""
$(TYPEDEF)

Direction observation information of a `Point2` variable.
"""
mutable struct PriorPoint2{T} <: IncrementalInference.FunctorSingleton where {T <: IIF.SamplableBelief}
  Z::T
  # W::Array{Float64,1} # TODO, deprecate the weight parameter
  PriorPoint2{T}() where T = new()
  PriorPoint2{T}(dist::T) where {T <: IIF.SamplableBelief} = new{T}(dist)
end
PriorPoint2(z::T) where {T <: IIF.SamplableBelief} = PriorPoint2{T}(z)
function PriorPoint2D(mu, cov, W)
  @warn "PriorPoint2D(mu, cov, W) is deprecated, use PriorPoint{T}(T(...)) instead -- e.g. PriorPoint2{MvNormal}(MvNormal(...) or any other Distributions.Distribution type instead."
  PriorPoint2{MvNormal{Float64}}(MvNormal(mu, cov))
end
function getSample(p2::PriorPoint2, N::Int=1)
  return (rand(p2.Z, N),)
end

"""
$(TYPEDEF)
"""
mutable struct Point2Point2{D <: IIF.SamplableBelief} <: FunctorPairwise
    Zij::D
    Point2Point2{T}() where T = new{T}()
    Point2Point2{T}(x::T) where {T <: IIF.SamplableBelief} = new{T}(x)
end
Point2Point2(x::T) where {T <: IIF.SamplableBelief} = Point2Point2{T}(x)
function getSample(pp2::Point2Point2, N::Int=1)
  return (rand(pp2.Zij,N),  )
end
function (pp2r::Point2Point2{T})(
            res::Array{Float64},
            userdata::FactorMetadata,
            idx::Int,
            meas::Tuple,
            xi::Array{Float64,2},
            xj::Array{Float64,2} ) where T
  #
  res[1]  = meas[1][1,idx] - (xj[1,idx] - xi[1,idx])
  res[2]  = meas[1][2,idx] - (xj[2,idx] - xi[2,idx])
  nothing
end

"""
$(TYPEDEF)
"""
mutable struct Point2Point2WorldBearing{T} <: IncrementalInference.FunctorPairwise where {T <: IIF.SamplableBelief}
    Z::T
    rangemodel::Rayleigh
    # zDim::Tuple{Int, Int}
    Point2Point2WorldBearing{T}() where T = new{T}()
    Point2Point2WorldBearing{T}(x::T) where {T <: IIF.SamplableBelief} = new{T}(x, Rayleigh(100))
end
Point2Point2WorldBearing(x::T) where {T <: IIF.SamplableBelief} = Point2Point2WorldBearing{T}(x)
function getSample(pp2::Point2Point2WorldBearing, N::Int=1)
  sp = Array{Float64,2}(undef, 2,N)
  sp[1,:] = rand(pp2.Z,N)
  sp[2,:] = rand(pp2.rangemodel,N)
  return (sp, )
end
function (pp2r::Point2Point2WorldBearing)(
          res::Array{Float64},
          userdata::FactorMetadata,
          idx::Int,
          meas::Tuple,
          pi::Array{Float64,2},
          pj::Array{Float64,2} )
  #
  # noisy bearing measurement
  z, r = meas[1][1,idx], meas[1][2,idx]
  dx, dy = pj[1,idx]-pi[1,idx], pj[2,idx]-pi[2,idx]
  res[1] = z - atan(dy,dx)
  res[2] = r - norm([dx; dy])
  nothing
end

"""
$(TYPEDEF)

Will be deprecated, use `addFactor!(.., nullhypo=)` instead (work in progress)
"""
mutable struct PriorPoint2DensityNH <: IncrementalInference.FunctorSingletonNH
  belief::BallTreeDensity
  nullhypothesis::Distributions.Categorical
  PriorPoint2DensityNH() = new()
  PriorPoint2DensityNH(belief, p::Distributions.Categorical) = new(belief, p)
  PriorPoint2DensityNH(belief, p::Vector{Float64}) = new(belief, Distributions.Categorical(p))
end
function getSample(p2::PriorPoint2DensityNH, N::Int=1)
  return (rand(p2.belief, N), )
end

"""
$(TYPEDEF)

Will be deprecated, use `addFactor!(.., nullhypo=)` instead (work in progress)
"""
mutable struct PackedPriorPoint2DensityNH <: IncrementalInference.PackedInferenceType
    rpts::Vector{Float64} # 0rotations, 1translation in each column
    rbw::Vector{Float64}
    dims::Int
    nh::Vector{Float64}
    PackedPriorPoint2DensityNH() = new()
    PackedPriorPoint2DensityNH(x1,x2,x3, x4) = new(x1, x2, x3, x4)
end
function convert(::Type{PriorPoint2DensityNH}, d::PackedPriorPoint2DensityNH)
  return PriorPoint2DensityNH(
            kde!(EasyMessage( reshapeVec2Mat(d.rpts, d.dims), d.rbw, (:Euclid, :Euclid))),
            Distributions.Categorical(d.nh)  )
end
function convert(::Type{PackedPriorPoint2DensityNH}, d::PriorPoint2DensityNH)
  return PackedPriorPoint2DensityNH( getPoints(d.belief)[:], getBW(d.belief)[:,1], Ndim(d.belief), d.nullhypothesis.p )
end

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

"""
$(TYPEDEF)

Serialization type for `PriorPoint2`.
"""
mutable struct PackedPriorPoint2  <: IncrementalInference.PackedInferenceType
    str::String
    PackedPriorPoint2() = new()
    PackedPriorPoint2(x::String) = new(x)
end

function convert(::Type{PriorPoint2}, d::PackedPriorPoint2)
  # Cov = reshapeVec2Mat(d.vecCov, d.dimc)
  distr = extractdistribution(d.str)
  return PriorPoint2{typeof(distr)}(distr)
end
function convert(::Type{PackedPriorPoint2}, d::PriorPoint2)
  # v2 = d.mv.Σ.mat[:];
  return PackedPriorPoint2(string(d.Z))
end

"""
$(TYPEDEF)

Serialization type for `Point2Point2WorldBearing`.
"""
mutable struct PackedPoint2Point2WorldBearing  <: IncrementalInference.PackedInferenceType
    str::String
    # NOTE Not storing rangemodel which may cause inconsistencies if the implementation parameters change
    PackedPoint2Point2WorldBearing() = new()
    PackedPoint2Point2WorldBearing(x::String) = new(x)
end
function convert(::Type{PackedPoint2Point2WorldBearing}, d::Point2Point2WorldBearing)
  return PackedPoint2Point2WorldBearing( string(d.Z) )
end
function convert(::Type{Point2Point2WorldBearing}, d::PackedPoint2Point2WorldBearing)
  return Point2Point2WorldBearing( extractdistribution(d.str) )
end

"""
$(TYPEDEF)

Serialization type for `Point2Point2`.
"""
mutable struct PackedPoint2Point2 <: IncrementalInference.PackedInferenceType
    # mu::Vector{Float64}
    # sigma::Vector{Float64}
    # sdim::Int
    str::String
    PackedPoint2Point2() = new()
    PackedPoint2Point2(s::AS) where {AS <: AbstractString} = new(s)
end
function convert(::Type{Point2Point2}, d::PackedPoint2Point2)
  return Point2Point2( extractdistribution(d.str) )
end
function convert(::Type{PackedPoint2Point2}, d::Point2Point2)
  return PackedPoint2Point2( string(d.Zij) )
end