text:
"""
    $(SIGNATURES)

Perform the nonlinear numerical operations to approximate the convolution with a particular user defined likelihood function (conditional), which as been prepared in the `frl` object.  This function uses root finding to enforce a non-linear function constraint.

Notes:
- remember this is a deepcopy of original sfidx, since we are generating a proposal distribution and not directly replacing the existing variable belief estimate

Future work:
- once Threads.@threads have been optmized JuliaLang/julia#19967, also see area4 branch
- improve handling of n and particleidx, especially considering future multithreading support
"""
function approxConvOnElements!( ccwl::Union{CommonConvWrapper{F},
                                            CommonConvWrapper{Mixture{N_,F,S,T}}},
                                elements::Union{Vector{Int}, UnitRange{Int}},
                                ::Type{<:MultiThreaded},
                                _slack=nothing ) where {N_,F<:AbstractRelative,S,T}
  #
  Threads.@threads for n in elements
    # ccwl.thrid_ = Threads.threadid()
    ccwl.cpt[Threads.threadid()].particleidx = n

    # ccall(:jl_, Nothing, (Any,), "starting loop, thrid_=$(Threads.threadid()), partidx=$(ccwl.cpt[Threads.threadid()].particleidx)")
    _solveCCWNumeric!( ccwl, _slack=_slack)
  end
  nothing
end

function approxConvOnElements!( ccwl::Union{CommonConvWrapper{F},
                                            CommonConvWrapper{Mixture{N_,F,S,T}}},
                                elements::Union{Vector{Int}, UnitRange{Int}},
                                ::Type{<:SingleThreaded},
                                _slack=nothing ) where {N_,F<:AbstractRelative,S,T}
  #
  for n in elements
    ccwl.cpt[Threads.threadid()].particleidx = n
    _solveCCWNumeric!( ccwl, _slack=_slack)
  end
  nothing
end

function approxConvOnElements!( ccwl::Union{CommonConvWrapper{F},
                                            CommonConvWrapper{Mixture{N_,F,S,T}}},
                                elements::Union{Vector{Int}, UnitRange{Int}},
                                _slack=nothing )  where {N_,F<:AbstractRelative,S,T}
  #
  approxConvOnElements!(ccwl, elements, ccwl.threadmodel, _slack)
end

"""
    $SIGNATURES

Control the amount of entropy to add to null-hypothesis in multihypo case.

Notes:
- Basically calculating the covariance (with a bunch of assumptions TODO, fix)
- FIXME, Currently only supports Euclidean domains.
- FIXME, allow particle subpopulations instead of just all of a variable
"""
function calcVariableDistanceExpectedFractional(ccwl::CommonConvWrapper,
                                                sfidx::Integer,
                                                certainidx::AbstractVector{<:Integer};
                                                kappa::Real=3.0  )
  #
  if sfidx in certainidx
    # msst_ = sqrt(calcCovarianceBasic(getManifold(ccwl.vartypes[sfidx]), ccwl.params[sfidx]))
    msst_ = calcStdBasicSpread(ccwl.vartypes[sfidx](), ccwl.params[sfidx])
    return kappa*msst_
  end
  # @assert !(sfidx in certainidx) "null hypo distance does not work for sfidx in certainidx"

  # get mean of all fractional variables
  # ccwl.params::Vector{Vector{P}}
  uncertainidx = setdiff(1:length(ccwl.params), certainidx)
  dists = zeros(length(uncertainidx)+length(certainidx))

  dims = manifold_dimension(getManifold(ccwl.vartypes[sfidx]))

  uncMeans = zeros(dims, length(uncertainidx))
  for (count,i) in enumerate(uncertainidx)
    u = mean(getManifold(ccwl.vartypes[i]), ccwl.params[i])
    uncMeans[:,count] .= getCoordinates(ccwl.vartypes[i], u)
  end
  count = 0

  refMean = getCoordinates(ccwl.vartypes[sfidx], mean(getManifold(ccwl.vartypes[sfidx]), ccwl.params[sfidx]))

  # calc for uncertain and certain
  for i in uncertainidx
    count += 1
    dists[count] = norm(refMean - uncMeans[:,count])
  end
  # also check distance to certainidx for general scale reference (workaround heuristic)
  for cidx in certainidx
    count += 1
    cerMeanPnt = mean(getManifold(ccwl.vartypes[cidx]), ccwl.params[cidx])
    cerMean = getCoordinates(ccwl.vartypes[cidx], cerMeanPnt)
    dists[count] = norm(refMean[1:dims] - cerMean[1:dims])
  end

  push!(dists, 1e-2)
  return kappa*maximum(dists)
end

# Add entrypy on a point in `points` on manifold M, only on dimIdx if in p
function addEntropyOnManifold!( M::ManifoldsBase.AbstractManifold,
                                    points::Union{<:AbstractVector{<:Real},SubArray},
                                    dimIdx::AbstractVector,
                                    spreadDist::Real,
                                    p::Union{Colon, <:AbstractVector}=: )
  #
  if length(points) == 0
    return nothing
  end

  # preallocate
  T = number_eltype(points[1])
  Xc = zeros(T, manifold_dimension(M))
  X = get_vector(M, points[1], Xc, DefaultOrthogonalBasis())

  for idx in 1:length(points)
    # build tangent coordinate random
    for dim in dimIdx
      if (p === :) || dim in p
        Xc[dim] = spreadDist*(rand()-0.5)
      end
    end
    # update tangent vector X
    get_vector!(M, X, points[idx], Xc, DefaultOrthogonalBasis())
    #update point
    exp!(M, points[idx], points[idx], X)
    # points[idx] = exp(M, points[idx], X)

  end
  #
  nothing
end

"""
    $(SIGNATURES)

Common function to compute across a single user defined multi-hypothesis ambiguity per factor.
This function dispatches both `AbstractRelativeRoots` and `AbstractRelativeMinimize` factors.

DevNotes
- Future combo with `_calcIPCRelative`
"""
function computeAcrossHypothesis!(ccwl::Union{<:CommonConvWrapper{F},
                                              <:CommonConvWrapper{Mixture{N_,F,S,T}}},
                                  hyporecipe::NamedTuple,
                                  sfidx::Int,
                                  maxlen::Int,
                                  mani::ManifoldsBase.AbstractManifold; # maniAddOps::Tuple;
                                  spreadNH::Real=5.0,
                                  inflateCycles::Int=3,
                                  skipSolve::Bool=false,
                                  testshuffle::Bool=false,
                                  _slack=nothing ) where {N_,F<:AbstractRelative,S,T}
  #
  count = 0
  # transition to new hyporecipe approach
  allelements = hyporecipe.allelements
  activehypo  = hyporecipe.activehypo
  certainidx  = hyporecipe.certainidx

  cpt_ = ccwl.cpt[Threads.threadid()]

  @assert ccwl.certainhypo == hyporecipe.certainidx "expected hyporecipe.certainidx to be the same as cached in ccw"
  for (hypoidx, vars) in activehypo
    count += 1

    # now do hypothesis specific
    if sfidx in certainidx && hypoidx != 0 || hypoidx in certainidx || hypoidx == sfidx
      # hypo case hypoidx, sfidx = $hypoidx, $sfidx
      for i in 1:Threads.nthreads()  ccwl.cpt[i].activehypo = vars; end

      addEntr = view(ccwl.params[sfidx], allelements[count])
      # dynamic estimate with user requested speadNH of how much noise to inject (inflation or nullhypo)
      spreadDist = calcVariableDistanceExpectedFractional(ccwl, sfidx, certainidx, kappa=ccwl.inflation)

      # do proposal inflation step, see #1051
      # consider duplicate convolution approximations for inflation off-zero
      # ultimately set by dfg.params.inflateCycles
      for iflc in 1:inflateCycles
        addEntropyOnManifold!(mani, addEntr, 1:getDimension(mani), spreadDist, ccwl.partialDims)
        # no calculate new proposal belief on kernels `allelements[count]`
          _checkErrorCCWNumerics(ccwl, testshuffle)
        skipSolve ? @warn("skipping numerical solve operation") : approxConvOnElements!(ccwl, allelements[count], _slack)
      end
    elseif hypoidx != sfidx && hypoidx != 0
      # snap together case
      # multihypo, take other value case
      # sfidx=2, hypoidx=3:  2 should take a value from 3
      # sfidx=3, hypoidx=2:  3 should take a value from 2
      # DEBUG sfidx=2, hypoidx=1 -- bad when do something like multihypo=[0.5;0.5] -- issue 424
      # ccwl.params[sfidx][:,allelements[count]] = view(ccwl.params[hypoidx],:,allelements[count])
        # NOTE make alternative case only operate as null hypo
        addEntr = view(ccwl.params[sfidx], allelements[count])
        # dynamic estimate with user requested speadNH of how much noise to inject (inflation or nullhypo)
        spreadDist = calcVariableDistanceExpectedFractional(ccwl, sfidx, certainidx, kappa=spreadNH)
        addEntropyOnManifold!(mani, addEntr, 1:getDimension(mani), spreadDist)

    elseif hypoidx == 0
      # basically do nothing since the factor is not active for these allelements[count]
      # inject more entropy in nullhypo case
      # add noise (entropy) to spread out search in convolution proposals
      addEntr = view(ccwl.params[sfidx], allelements[count])
      # dynamic estimate with user requested speadNH of how much noise to inject (inflation or nullhypo)
      spreadDist = calcVariableDistanceExpectedFractional(ccwl, sfidx, certainidx, kappa=spreadNH)
      # # make spread (1σ) equal to mean distance of other fractionals
      addEntropyOnManifold!(mani, addEntr, 1:getDimension(mani), spreadDist)
    else
      error("computeAcrossHypothesis -- not dealing with multi-hypothesis case correctly")
    end
  end
  nothing
end
  # elseif hypoidx == sfidx
  #   # multihypo, do conv case, hypoidx == sfidx
  #   ah = sort(union([sfidx;], certainidx))
  #   @assert norm(ah - vars) < 1e-10
  #   for i in 1:Threads.nthreads()  ccwl.cpt[i].activehypo = ah; end
  #   approxConvOnElements!(ccwl, allelements[count])

# TODO what about nullhypo in recipe (when .mhidx[smpid]==0)?
# TODO figure out how best to combine with computeAcrossHypothesis!
function _calcIPCRelative(Xi::AbstractVector{<:DFGVariable},
                          ccwl::CommonConvWrapper,
                          hyporecipe::NamedTuple,
                          sfidx::Integer,
                          smpid::Integer=findfirst(x->x != 0, hyporecipe.mhidx)
                          )
  #
  @assert hyporecipe.activehypo[1][1] === 0 "expected 0-hypo case in hyporecipe.activehypo, to get variable hypo mask for relative partial propagation calculations."
  @assert hyporecipe.mhidx[smpid] !== 0 "_calcIPCRelative does not yet handle nullhypo gradients, try alternative hypo (smpid=$smpid), available hypos are hyporecipe.mhidx=$(hyporecipe.mhidx)"

  # select only the active variables in case of multihypo
  # @show smpid
  # @show hyporecipe.mhidx
  # @show hyporecipe.activehypo
  # NOTE +1 bc first element in .activehypo is nullhypo case, e.g. `(0,[1;])`
  _selhypo = hyporecipe.mhidx[smpid] + 1
  activehypo = hyporecipe.activehypo[_selhypo]
  activeids = activehypo[2]
  # solvefor index without the fractional variables
  active_mask = (x->x in activeids).(1:length(Xi))
  sfidx_active = sum(active_mask[1:sfidx])

  # build a view to the decision variable memory
  activeParams = view(ccwl.params, activeids)
  activeVars = Xi[active_mask]

  # assume gradients are just done for the first sample values
  # error("Possible issue, a factor has one manifold and attached variables have different manifolds.  Make sure the plumbing respects that.")
  @show typeof(ccwl.usrfnc!)
  @show sfidx
  # @show getLabel.(Xi)
  @show getLabel.(activeVars)
  @show getVariableType.(activeVars)
  # @show _getindextuple(ccwl.measurement, smpid)
  meas_pts = tuple((_getindextuple(ccwl.measurement, smpid))..., (getindex.(activeParams, smpid))...)
  # @show meas_pts
  #
  ipc = if ccwl._gradients === nothing
    ones(getDimension(activeVars[sfidx_active]))
  else
    ipc_ = Pair[]
    # get infoPerCoord from all variables
    for (vid,var) in enumerate(activeVars)
      # set all other variables infoPerCoord values
      getLabel(var) != getLabel(activeVars[sfidx_active]) ? nothing : continue
      push!(ipc_, vid=>ones(getDimension(var)))
    end
    # update the gradients at current point estimates
    # meas_pts =
    ccwl._gradients(meas_pts...)
    # do perturbation check
    # @show ipc_
    allipc = calcPerturbationFromVariable(ccwl._gradients, ipc_)
    allipc[sfidx_active]
  end

  @show ipc
  # FIXME REMOVE, overwrite with defauls during dev
  # fill!(ipc, 1.0)

  return ipc
end

"""
    $(SIGNATURES)

Multiple dispatch wrapper for `<:AbstractRelativeRoots` types, to prepare and execute the general approximate convolution with user defined factor residual functions.  This method also supports multihypothesis operations as one mechanism to introduce new modality into the proposal beliefs.

Planned changes will fold null hypothesis in as a standard feature and no longer appear as a separate `InferenceVariable`.
"""
function evalPotentialSpecific( Xi::AbstractVector{<:DFGVariable},
                                ccwl::CommonConvWrapper{T},
                                solvefor::Symbol,
                                T_::Type{<:AbstractRelative},
                                measurement::AbstractVector=Tuple[];
                                needFreshMeasurements::Bool=true,
                                solveKey::Symbol=:default,
                                N::Int= 0<length(measurement) ? length(measurement) : maximum(Npts.(getBelief.(Xi, solveKey))),
                                spreadNH::Real=3.0,
                                inflateCycles::Int=3,
                                nullSurplus::Real=0,
                                dbg::Bool=false,
                                skipSolve::Bool=false,
                                _slack=nothing  ) where {T <: AbstractFactor}
  #

  # Prep computation variables
  # NOTE #1025, should FMD be built here...
  sfidx, maxlen, mani = _updateCCW!(ccwl, Xi, solvefor, N; needFreshMeasurements, solveKey)
  # check for user desired measurement values
  if 0 < length(measurement)
    ccwl.measurement = measurement
  end

  # Check which variables have been initialized
  isinit = map(x->isInitialized(x), Xi)

  # assemble how hypotheses should be computed
  # nullSurplus see #1517
  runnullhypo = maximum((ccwl.nullhypo,nullSurplus))
  hyporecipe = _prepareHypoRecipe!(ccwl.hypotheses, maxlen, sfidx, length(Xi), isinit, runnullhypo )

  # get manifold add operations
  # TODO, make better use of dispatch, see JuliaRobotics/RoME.jl#244
  # addOps, d1, d2, d3 = buildHybridManifoldCallbacks(manis)
  mani = getManifold(getVariableType(Xi[sfidx]))

  # perform the numeric solutions on the indicated elements
  # FIXME consider repeat solve as workaround for inflation off-zero
  # NOTE alternate use of ccwl.certainidx to hyporecipe, certainidx = ccwl.certainhypo
  computeAcrossHypothesis!( ccwl, hyporecipe,
                            sfidx, maxlen, mani, spreadNH=spreadNH,
                            inflateCycles=inflateCycles, skipSolve=skipSolve,
                            _slack=_slack )
  #
  # FIXME do info per coord
  # ipc_ = _calcIPCRelative(Xi, ccwl, hyporecipe, sfidx)
  ipc = ones(getDimension(Xi[sfidx]))
  if isPartial(ccwl)
    # FIXME this is a workaround until better _calcIPCRelative can be used
    msk_ = setdiff(1:length(ipc), ccwl.usrfnc!.partial)
    for _i in msk_
      ipc[_i] = 0.0
    end
  end

  # return the found points, and info per coord
  return ccwl.params[ccwl.varidx], ipc
end

# TODO `measurement` might not be properly wired up yet
# TODO consider 1051 here to inflate proposals as general behaviour
function evalPotentialSpecific( Xi::AbstractVector{<:DFGVariable},
                                ccwl::CommonConvWrapper{T},
                                solvefor::Symbol,
                                T_::Type{<:AbstractPrior},
                                measurement::AbstractVector=Tuple[];
                                needFreshMeasurements::Bool=true,
                                solveKey::Symbol=:default,
                                N::Int=length(measurement),
                                dbg::Bool=false,
                                spreadNH::Real=3.0,
                                inflateCycles::Int=3,
                                nullSurplus::Real=0,
                                skipSolve::Bool=false,
                                _slack=nothing ) where {T <: AbstractFactor}
  #
  # setup the partial or complete decision variable dimensions for this ccwl object
  # NOTE perhaps deconv has changed the decision variable list, so placed here during consolidation phase
  _setCCWDecisionDimsConv!(ccwl)

  # FIXME, NEEDS TO BE CLEANED UP AND WORK ON MANIFOLDS PROPER
  fnc = ccwl.usrfnc!
  sfidx = findfirst(getLabel.(Xi) .== solvefor)
  # sfidx = 1 #  WHY HARDCODED TO 1??
  solveForPts = getVal(Xi[sfidx], solveKey=solveKey)
  nn = maximum([N; calcZDim(ccwl); length(solveForPts); length(ccwl.params[sfidx])])  # length(measurement[1])

  # FIXME better standardize in-place operations (considering solveKey)
  if needFreshMeasurements
    cf = CalcFactor( ccwl )
    newMeas = sampleFactor(cf, nn)
    ccwl.measurement = newMeas
  end

  # Check which variables have been initialized
  # TODO not sure why forcing to Bool vs BitVector
  isinit::Vector{Bool} = Xi .|> isInitialized .|> Bool
  # nullSurplus see #1517
  runnullhypo = maximum((ccwl.nullhypo,nullSurplus))
  hyporecipe = _prepareHypoRecipe!(ccwl.hypotheses, nn, sfidx, length(Xi), isinit, runnullhypo )

  # get solvefor manifolds, FIXME ON FIRE, upgrade to new Manifolds.jl
  mani = getManifold(Xi[sfidx])
  # two cases on how to use the measurement
  nhmask = hyporecipe.mhidx .== 0
  ahmask = hyporecipe.mhidx .== 1
  # generate nullhypo samples
  # inject lots of entropy in nullhypo case
  # make spread (1σ) equal to mean distance of other fractionals
  # FIXME better standardize in-place operations (considering solveKey)
  addEntr = if length(solveForPts) == nn
    deepcopy(solveForPts)
  else
    ret = typeof(solveForPts)(undef, nn)
    for i in 1:length(solveForPts)
      ret[i] = solveForPts[i]
    end
    for i in (length(solveForPts)+1):nn
      ret[i] = getPointIdentity(getVariableType(Xi[sfidx]))
    end
    ret
  end

  # view on elements marked for nullhypo
  addEntrNH = view(addEntr, nhmask)
  # spreadDist = spreadNH*sqrt(calcCovarianceBasic(mani, addEntr))
  spreadDist = spreadNH*calcStdBasicSpread(getVariableType(Xi[sfidx]), addEntr)
  # partials are treated differently
  ipc = if !isPartial(ccwl) #ccwl.partial
    # TODO for now require measurements to be coordinates too
    # @show typeof(ccwl.measurement[1])
    for m in (1:length(addEntr))[ahmask]
      # FIXME, selection for all measurement::Tuple elements
      # @info "check broadcast" ccwl.usrfnc! addEntr[m] ccwl.measurement[1][m]
      setPointsMani!(addEntr[m], ccwl.measurement[m])
      # addEntr[m] = ccwl.measurement[m][1]
    end
    # ongoing part of RoME.jl #244
    addEntropyOnManifold!(mani, addEntrNH, 1:getDimension(mani), spreadDist)
    # do info per coords
    ones(getDimension(Xi[sfidx]))
  else
    # FIXME but how to add partial factor info only on affected dimensions fro general manifold points?
    pvec = [fnc.partial...]

    if !hasmethod(getManifold, (typeof(fnc),))
      @debug "No method getManifold for $(typeof(fnc)), using getManifoldPartial"
    end

    # active hypo that receives the regular measurement information
    for m in (1:length(addEntr))[ahmask]
      # addEntr is no longer in coordinates, these are now general manifold points!!
      # for (i,dimnum) in enumerate(fnc.partial)
        # FIXME, need ability to replace partial points
        partialCoords = ccwl.partialDims

        #FIXME check if getManifold is defined otherwise fall back to getManifoldPartial, JT: I would like to standardize to getManifold
        if hasmethod(getManifold, (typeof(fnc),))
          Msrc = getManifold(fnc)
          # TODO workaround until partial manifold approach is standardized, see #1492
          asPartial = isPartial(fnc) && manifold_dimension(Msrc) < manifold_dimension(mani)
          setPointPartial!(mani, addEntr[m], Msrc, ccwl.measurement[m], partialCoords, asPartial)
        else
          Msrc, = getManifoldPartial(mani, partialCoords)
          setPointPartial!(mani, addEntr[m], Msrc, ccwl.measurement[m], partialCoords)
        end
        # addEntr[m][dimnum] = ccwl.measurement[1][m][i]
      # end
    end
    # null hypo mask that needs to be perturbed by "noise"
    addEntrNHp = view(addEntr, nhmask)
    # ongoing part of RoME.jl #244
    addEntropyOnManifold!(mani, addEntrNHp, 1:getDimension(mani), spreadDist, pvec)
    # do info per coords
    ipc_ = zeros(getDimension(Xi[sfidx]))
    ipc_[pvec] .= 1.0
    ipc_
  end

  # check partial is easy as this is a prior
  return addEntr, ipc
end

function evalPotentialSpecific( Xi::AbstractVector{<:DFGVariable},
                                ccwl::CommonConvWrapper{Mixture{N_,F,S,T}},
                                solvefor::Symbol,
                                measurement::AbstractVector=Tuple[];
                                kw... ) where {N_,F<:AbstractFactor,S,T}
  #
  evalPotentialSpecific(Xi,
                        ccwl,
                        solvefor,
                        F,
                        measurement;
                        kw... )
end

function evalPotentialSpecific( Xi::AbstractVector{<:DFGVariable},
                                ccwl::CommonConvWrapper{F},
                                solvefor::Symbol,
                                measurement::AbstractVector=Tuple[];
                                kw... ) where {F <: AbstractFactor}
  #
  evalPotentialSpecific(Xi,
                        ccwl,
                        solvefor,
                        F,
                        measurement;
                        kw... )
end

"""
    $(SIGNATURES)

Single entry point for evaluating factors from factor graph, using multiple dispatch to locate the correct `evalPotentialSpecific` function.
"""
function evalFactor(dfg::AbstractDFG,
                    fct::DFGFactor,
                    solvefor::Symbol,
                    measurement::AbstractVector=Tuple[];
                    needFreshMeasurements::Bool=true,
                    solveKey::Symbol=:default,
                    N::Int=length(measurement),
                    inflateCycles::Int=getSolverParams(dfg).inflateCycles,
                    nullSurplus::Real=0,
                    dbg::Bool=false,
                    skipSolve::Bool=false,
                    _slack=nothing  )
  #

  ccw = _getCCW(fct)
  # TODO -- this build up of Xi is excessive and could happen at addFactor time
  variablelist = getVariableOrder(fct)
  Xi = getVariable.(dfg, variablelist)

  # setup operational values before compute (likely to be refactored)
  for i in 1:Threads.nthreads()
    ccw.cpt[i].factormetadata.variablelist = variablelist
    ccw.cpt[i].factormetadata.solvefor = solvefor
  end

  return evalPotentialSpecific( Xi, ccw, solvefor, measurement; needFreshMeasurements,
                                solveKey, N, dbg, spreadNH=getSolverParams(dfg).spreadNH,
                                inflateCycles, nullSurplus, skipSolve, _slack  )
  #
end

"""
    $SIGNATURES

Perform factor evaluation to resolve the "solve for" variable of a factor.
This temporary function can be run without passing a factor graph object, but will internally allocate a new temporary new one.
Alternatively, the factor graph used for calculations can be passed in via the keyword `tfg`, hence the function name bang.

Notes
- `TypeParams_args::Vector{Tuple{InferenceVariable, P}}
- idea is please find best e.g. `b`, given `f(z,a,b,c)` either by roots or minimize (depends on definition of `f`)
- `sfidx::Int` is the solve for index, assuming `getVariableOrder(fct)`.

Example
```julia
B = _evalFactorTemporary!(EuclidDistance, (ContinuousScalar, ContinuousScalar), 2, ([10;],), ([0.],[9.5]) )
# should return `B = 10`
```

Related

[`evalFactor`](@ref), [`calcFactorResidual`](@ref), [`testFactorResidualBinary`](@ref), [`solveFactorParameteric`](@ref), [`approxConvBelief`](@ref)
"""
function _evalFactorTemporary!( fct::AbstractFactor,
                                varTypes::Tuple,
                                sfidx::Int,  # solve for index, assuming variable order for fct
                                measurement::AbstractVector,
                                pts::Tuple;
                                tfg::AbstractDFG=initfg(),
                                solveKey::Symbol=:default,
                                newFactor::Bool=true,
                                _slack=nothing,
                                buildgraphkw... )
  #

  # build up a temporary graph in dfg
  _, _dfgfct = IIF._buildGraphByFactorAndTypes!(fct, varTypes, pts; dfg=tfg, solveKey=solveKey, newFactor=newFactor, buildgraphkw...)

  # get label convention for which variable to solve for
  solvefor = getVariableOrder(_dfgfct)[sfidx]

  # do the factor evaluation
  sfPts, _ = evalFactor(tfg, _dfgfct, solvefor, measurement, needFreshMeasurements=false, solveKey=solveKey, inflateCycles=1, _slack=_slack )

  return sfPts
end

#


text:
@testset "PaddedString" begin

end

ext: jl


text:
# Julia wrapper for header: cpl_vsi.h
# Automatically generated using Clang.jl

"""
    VSIFOpen(const char * pszFilename,
             const char * pszAccess) -> FILE *
"""
function vsifopen()
    aftercare(ccall((:VSIFOpen, libgdal), Ptr{Cint}, ()))
end

"""
    VSIFClose(FILE * fp) -> int
"""
function vsifclose(arg1)
    aftercare(ccall((:VSIFClose, libgdal), Cint, (Ptr{Cint},), arg1))
end

"""
    VSIFSeek(FILE * fp,
             long nOffset,
             int nWhence) -> int
"""
function vsifseek(arg1, arg2, arg3)
    aftercare(ccall((:VSIFSeek, libgdal), Cint, (Ptr{Cint}, Clong, Cint), arg1, arg2, arg3))
end

"""
    VSIFTell(FILE * fp) -> long
"""
function vsiftell(arg1)
    aftercare(ccall((:VSIFTell, libgdal), Clong, (Ptr{Cint},), arg1))
end

"""
    VSIRewind(FILE * fp) -> void
"""
function vsirewind(arg1)
    aftercare(ccall((:VSIRewind, libgdal), Cvoid, (Ptr{Cint},), arg1))
end

"""
    VSIFFlush(FILE * fp) -> void
"""
function vsifflush(arg1)
    aftercare(ccall((:VSIFFlush, libgdal), Cvoid, (Ptr{Cint},), arg1))
end

"""
    VSIFRead(void * pBuffer,
             size_t nSize,
             size_t nCount,
             FILE * fp) -> size_t
"""
function vsifread()
    aftercare(ccall((:VSIFRead, libgdal), Cint, ()))
end

"""
    VSIFWrite(const void * pBuffer,
              size_t nSize,
              size_t nCount,
              FILE * fp) -> size_t
"""
function vsifwrite()
    aftercare(ccall((:VSIFWrite, libgdal), Cint, ()))
end

"""
    VSIFGets(char * pszBuffer,
             int nBufferSize,
             FILE * fp) -> char *
"""
function vsifgets(arg1, arg2, arg3)
    aftercare(ccall((:VSIFGets, libgdal), Cstring, (Cstring, Cint, Ptr{Cint}), arg1, arg2, arg3), false)
end

"""
    VSIFPuts(const char * pszString,
             FILE * fp) -> int
"""
function vsifputs(arg1, arg2)
    aftercare(ccall((:VSIFPuts, libgdal), Cint, (Cstring, Ptr{Cint}), arg1, arg2))
end

"""
    VSIFGetc(FILE * fp) -> int
"""
function vsifgetc(arg1)
    aftercare(ccall((:VSIFGetc, libgdal), Cint, (Ptr{Cint},), arg1))
end

"""
    VSIFPutc(int nChar,
             FILE * fp) -> int
"""
function vsifputc(arg1, arg2)
    aftercare(ccall((:VSIFPutc, libgdal), Cint, (Cint, Ptr{Cint}), arg1, arg2))
end

"""
    VSIUngetc(int c,
              FILE * fp) -> int
"""
function vsiungetc(arg1, arg2)
    aftercare(ccall((:VSIUngetc, libgdal), Cint, (Cint, Ptr{Cint}), arg1, arg2))
end

"""
    VSIFEof(FILE * fp) -> int
"""
function vsifeof(arg1)
    aftercare(ccall((:VSIFEof, libgdal), Cint, (Ptr{Cint},), arg1))
end

"""
    VSIStat(const char * pszFilename,
            VSIStatBuf * pStatBuf) -> int
"""
function vsistat(arg1, arg2)
    aftercare(ccall((:VSIStat, libgdal), Cint, (Cstring, Ptr{VSIStatBuf}), arg1, arg2))
end

"""
    VSIFOpenL(const char * pszFilename,
              const char * pszAccess) -> VSILFILE *

Open file.

### Parameters
* **pszFilename**: the file to open. UTF-8 encoded.
* **pszAccess**: access requested (i.e. "r", "r+", "w")

### Returns
NULL on failure, or the file handle.
"""
function vsifopenl(arg1, arg2)
    aftercare(ccall((:VSIFOpenL, libgdal), Ptr{VSILFILE}, (Cstring, Cstring), arg1, arg2))
end

"""
    VSIFOpenExL(const char * pszFilename,
                const char * pszAccess,
                int bSetError) -> VSILFILE *

Open file.

### Parameters
* **pszFilename**: the file to open. UTF-8 encoded.
* **pszAccess**: access requested (i.e. "r", "r+", "w")
* **bSetError**: flag determining whether or not this open call should set VSIErrors on failure.

### Returns
NULL on failure, or the file handle.
"""
function vsifopenexl(arg1, arg2, arg3)
    aftercare(ccall((:VSIFOpenExL, libgdal), Ptr{VSILFILE}, (Cstring, Cstring, Cint), arg1, arg2, arg3))
end

"""
    VSIFCloseL(VSILFILE * fp) -> int

Close file.

### Parameters
* **fp**: file handle opened with VSIFOpenL(). Passing a nullptr produces undefined behavior.

### Returns
0 on success or -1 on failure.
"""
function vsifclosel(arg1)
    aftercare(ccall((:VSIFCloseL, libgdal), Cint, (Ptr{VSILFILE},), arg1))
end

"""
    VSIFSeekL(VSILFILE * fp,
              vsi_l_offset nOffset,
              int nWhence) -> int

Seek to requested offset.

### Parameters
* **fp**: file handle opened with VSIFOpenL().
* **nOffset**: offset in bytes.
* **nWhence**: one of SEEK_SET, SEEK_CUR or SEEK_END.

### Returns
0 on success or -1 one failure.
"""
function vsifseekl(arg1, arg2, arg3)
    aftercare(ccall((:VSIFSeekL, libgdal), Cint, (Ptr{VSILFILE}, vsi_l_offset, Cint), arg1, arg2, arg3))
end

"""
    VSIFTellL(VSILFILE * fp) -> vsi_l_offset

Tell current file offset.

### Parameters
* **fp**: file handle opened with VSIFOpenL().

### Returns
file offset in bytes.
"""
function vsiftelll(arg1)
    aftercare(ccall((:VSIFTellL, libgdal), vsi_l_offset, (Ptr{VSILFILE},), arg1))
end

"""
    VSIRewindL(VSILFILE * fp) -> void

Rewind the file pointer to the beginning of the file.

### Parameters
* **fp**: file handle opened with VSIFOpenL().
"""
function vsirewindl(arg1)
    aftercare(ccall((:VSIRewindL, libgdal), Cvoid, (Ptr{VSILFILE},), arg1))
end

"""
    VSIFReadL(void * pBuffer,
              size_t nSize,
              size_t nCount,
              VSILFILE * fp) -> size_t

Read bytes from file.

### Parameters
* **pBuffer**: the buffer into which the data should be read (at least nCount * nSize bytes in size.
* **nSize**: size of objects to read in bytes.
* **nCount**: number of objects to read.
* **fp**: file handle opened with VSIFOpenL().

### Returns
number of objects successfully read.
"""
function vsifreadl()
    aftercare(ccall((:VSIFReadL, libgdal), Cint, ()))
end

"""
    VSIFReadMultiRangeL(int nRanges,
                        void ** ppData,
                        const vsi_l_offset * panOffsets,
                        const size_t * panSizes,
                        VSILFILE * fp) -> int

Read several ranges of bytes from file.

### Parameters
* **nRanges**: number of ranges to read.
* **ppData**: array of nRanges buffer into which the data should be read (ppData[i] must be at list panSizes[i] bytes).
* **panOffsets**: array of nRanges offsets at which the data should be read.
* **panSizes**: array of nRanges sizes of objects to read (in bytes).
* **fp**: file handle opened with VSIFOpenL().

### Returns
0 in case of success, -1 otherwise.
"""
function vsifreadmultirangel(nRanges, ppData, panOffsets, panSizes, arg1)
    aftercare(ccall((:VSIFReadMultiRangeL, libgdal), Cint, (Cint, Ptr{Ptr{Cvoid}}, Ptr{vsi_l_offset}, Ptr{Cint}, Ptr{VSILFILE}), nRanges, ppData, panOffsets, panSizes, arg1))
end

"""
    VSIFWriteL(const void * pBuffer,
               size_t nSize,
               size_t nCount,
               VSILFILE * fp) -> size_t

Write bytes to file.

### Parameters
* **pBuffer**: the buffer from which the data should be written (at least nCount * nSize bytes in size.
* **nSize**: size of objects to read in bytes.
* **nCount**: number of objects to read.
* **fp**: file handle opened with VSIFOpenL().

### Returns
number of objects successfully written.
"""
function vsifwritel()
    aftercare(ccall((:VSIFWriteL, libgdal), Cint, ()))
end

"""
    VSIFEofL(VSILFILE * fp) -> int

Test for end of file.

### Parameters
* **fp**: file handle opened with VSIFOpenL().

### Returns
TRUE if at EOF else FALSE.
"""
function vsifeofl(arg1)
    aftercare(ccall((:VSIFEofL, libgdal), Cint, (Ptr{VSILFILE},), arg1))
end

"""
    VSIFTruncateL(VSILFILE * fp,
                  vsi_l_offset nNewSize) -> int

Truncate/expand the file to the specified size.

### Parameters
* **fp**: file handle opened with VSIFOpenL().
* **nNewSize**: new size in bytes.

### Returns
0 on success
"""
function vsiftruncatel(arg1, arg2)
    aftercare(ccall((:VSIFTruncateL, libgdal), Cint, (Ptr{VSILFILE}, vsi_l_offset), arg1, arg2))
end

"""
    VSIFFlushL(VSILFILE * fp) -> int

Flush pending writes to disk.

### Parameters
* **fp**: file handle opened with VSIFOpenL().

### Returns
0 on success or -1 on error.
"""
function vsifflushl(arg1)
    aftercare(ccall((:VSIFFlushL, libgdal), Cint, (Ptr{VSILFILE},), arg1))
end

"""
    VSIFPutcL(int nChar,
              VSILFILE * fp) -> int

Write a single byte to the file.

### Parameters
* **nChar**: character to write.
* **fp**: file handle opened with VSIFOpenL().

### Returns
1 in case of success, 0 on error.
"""
function vsifputcl(arg1, arg2)
    aftercare(ccall((:VSIFPutcL, libgdal), Cint, (Cint, Ptr{VSILFILE}), arg1, arg2))
end

"""
    VSIFGetRangeStatusL(VSILFILE * fp,
                        vsi_l_offset nOffset,
                        vsi_l_offset nLength) -> VSIRangeStatus

Return if a given file range contains data or holes filled with zeroes.

### Parameters
* **fp**: file handle opened with VSIFOpenL().
* **nOffset**: offset of the start of the extent.
* **nLength**: extent length.

### Returns
extent status: VSI_RANGE_STATUS_UNKNOWN, VSI_RANGE_STATUS_DATA or VSI_RANGE_STATUS_HOLE
"""
function vsifgetrangestatusl(fp, nStart, nLength)
    aftercare(ccall((:VSIFGetRangeStatusL, libgdal), VSIRangeStatus, (Ptr{VSILFILE}, vsi_l_offset, vsi_l_offset), fp, nStart, nLength))
end

"""
    VSIIngestFile(VSILFILE * fp,
                  const char * pszFilename,
                  GByte ** ppabyRet,
                  vsi_l_offset * pnSize,
                  GIntBig nMaxSize) -> int

Ingest a file into memory.

### Parameters
* **fp**: file handle opened with VSIFOpenL().
* **pszFilename**: filename.
* **ppabyRet**: pointer to the target buffer. *ppabyRet must be freed with VSIFree()
* **pnSize**: pointer to variable to store the file size. May be NULL.
* **nMaxSize**: maximum size of file allowed. If no limit, set to a negative value.

### Returns
TRUE in case of success.
"""
function vsiingestfile(fp, pszFilename, ppabyRet, pnSize, nMaxSize)
    aftercare(ccall((:VSIIngestFile, libgdal), Cint, (Ptr{VSILFILE}, Cstring, Ptr{Ptr{GByte}}, Ptr{vsi_l_offset}, GIntBig), fp, pszFilename, ppabyRet, pnSize, nMaxSize))
end

"""
    VSIOverwriteFile(VSILFILE * fpTarget,
                     const char * pszSourceFilename) -> int

Overwrite an existing file with content from another one.

### Parameters
* **fpTarget**: file handle opened with VSIFOpenL() with "rb+" flag.
* **pszSourceFilename**: source filename

### Returns
TRUE in case of success.
"""
function vsioverwritefile(fpTarget, pszSourceFilename)
    aftercare(ccall((:VSIOverwriteFile, libgdal), Cint, (Ptr{VSILFILE}, Cstring), fpTarget, pszSourceFilename))
end

"""
    VSIStatL(const char * pszFilename,
             VSIStatBufL * psStatBuf) -> int

Get filesystem object info.

### Parameters
* **pszFilename**: the path of the filesystem object to be queried. UTF-8 encoded.
* **psStatBuf**: the structure to load with information.

### Returns
0 on success or -1 on an error.
"""
function vsistatl(arg1, arg2)
    aftercare(ccall((:VSIStatL, libgdal), Cint, (Cstring, Ptr{VSIStatBufL}), arg1, arg2))
end

"""
    VSIStatExL(const char * pszFilename,
               VSIStatBufL * psStatBuf,
               int nFlags) -> int

Get filesystem object info.

### Parameters
* **pszFilename**: the path of the filesystem object to be queried. UTF-8 encoded.
* **psStatBuf**: the structure to load with information.
* **nFlags**: 0 to get all information, or VSI_STAT_EXISTS_FLAG, VSI_STAT_NATURE_FLAG or VSI_STAT_SIZE_FLAG, or a combination of those to get partial info.

### Returns
0 on success or -1 on an error.
"""
function vsistatexl(pszFilename, psStatBuf, nFlags)
    aftercare(ccall((:VSIStatExL, libgdal), Cint, (Cstring, Ptr{VSIStatBufL}, Cint), pszFilename, psStatBuf, nFlags))
end

"""
    VSIIsCaseSensitiveFS(const char * pszFilename) -> int

Returns if the filenames of the filesystem are case sensitive.

### Parameters
* **pszFilename**: the path of the filesystem object to be tested. UTF-8 encoded.

### Returns
TRUE if the filenames of the filesystem are case sensitive.
"""
function vsiiscasesensitivefs(pszFilename)
    aftercare(ccall((:VSIIsCaseSensitiveFS, libgdal), Cint, (Cstring,), pszFilename))
end

"""
    VSISupportsSparseFiles(const char * pszPath) -> int

Returns if the filesystem supports sparse files.

### Parameters
* **pszPath**: the path of the filesystem object to be tested. UTF-8 encoded.

### Returns
TRUE if the file system is known to support sparse files. FALSE may be returned both in cases where it is known to not support them, or when it is unknown.
"""
function vsisupportssparsefiles(pszPath)
    aftercare(ccall((:VSISupportsSparseFiles, libgdal), Cint, (Cstring,), pszPath))
end

"""
    VSIHasOptimizedReadMultiRange(const char * pszPath) -> int

Returns if the filesystem supports efficient multi-range reading.

### Parameters
* **pszPath**: the path of the filesystem object to be tested. UTF-8 encoded.

### Returns
TRUE if the file system is known to have an efficient multi-range reading.
"""
function vsihasoptimizedreadmultirange(pszPath)
    aftercare(ccall((:VSIHasOptimizedReadMultiRange, libgdal), Cint, (Cstring,), pszPath))
end

"""
    VSIGetActualURL(const char * pszFilename) -> const char *

Returns the actual URL of a supplied filename.

### Parameters
* **pszFilename**: the path of the filesystem object. UTF-8 encoded.

### Returns
the actual URL corresponding to the supplied filename, or NULL. Should not be freed.
"""
function vsigetactualurl(pszFilename)
    aftercare(ccall((:VSIGetActualURL, libgdal), Cstring, (Cstring,), pszFilename), false)
end

"""
    VSIGetSignedURL(const char * pszFilename,
                    CSLConstList papszOptions) -> char *

Returns a signed URL of a supplied filename.

### Parameters
* **pszFilename**: the path of the filesystem object. UTF-8 encoded.
* **papszOptions**: list of options, or NULL. Depend on file system handler. For /vsis3/, /vsigs/, /vsiaz/ and /vsioss/, the following options are supported:

START_DATE=YYMMDDTHHMMSSZ: date and time in UTC following ISO 8601 standard, corresponding to the start of validity of the URL. If not specified, current date time.

EXPIRATION_DELAY=number_of_seconds: number between 1 and 604800 (seven days) for the validity of the signed URL. Defaults to 3600 (one hour)

VERB=GET/HEAD/DELETE/PUT/POST: HTTP VERB for which the request will be used. Default to GET.

### Returns
a signed URL, or NULL. Should be freed with CPLFree().
"""
function vsigetsignedurl(pszFilename, papszOptions)
    aftercare(ccall((:VSIGetSignedURL, libgdal), Cstring, (Cstring, CSLConstList), pszFilename, papszOptions), false)
end

"""
    VSIGetFileSystemOptions(const char * pszFilename) -> const char *

Return the list of options associated with a virtual file system handler as a serialized XML string.

### Parameters
* **pszFilename**: a filename, or prefix of a virtual file system handler.

### Returns
a string, which must not be freed, or NULL if no options is declared.
"""
function vsigetfilesystemoptions(pszFilename)
    aftercare(ccall((:VSIGetFileSystemOptions, libgdal), Cstring, (Cstring,), pszFilename), false)
end

"""
    VSIGetFileSystemsPrefixes(void) -> char **

Return the list of prefixes for virtual file system handlers currently registered.

### Returns
a NULL terminated list of prefixes. Must be freed with CSLDestroy()
"""
function vsigetfilesystemsprefixes()
    aftercare(ccall((:VSIGetFileSystemsPrefixes, libgdal), Ptr{Cstring}, ()))
end

"""
    VSIFGetNativeFileDescriptorL(VSILFILE * fp) -> void *

Returns the "native" file descriptor for the virtual handle.

### Parameters
* **fp**: file handle opened with VSIFOpenL().

### Returns
the native file descriptor, or NULL.
"""
function vsifgetnativefiledescriptorl(arg1)
    aftercare(ccall((:VSIFGetNativeFileDescriptorL, libgdal), Ptr{Cvoid}, (Ptr{VSILFILE},), arg1))
end

"""
    VSIGetFileMetadata(const char * pszFilename,
                       const char * pszDomain,
                       CSLConstList papszOptions) -> char **

Get metadata on files.

### Parameters
* **pszFilename**: the path of the filesystem object to be queried. UTF-8 encoded.
* **pszDomain**: Metadata domain to query. Depends on the file system. The following are supported:

HEADERS: to get HTTP headers for network-like filesystems (/vsicurl/, /vsis3/, etc)

TAGS: specific to /vsis3/: to get S3 Object tagging information
* **papszOptions**: Unused. Should be set to NULL.

### Returns
a NULL-terminated list of key=value strings, to be freed with CSLDestroy() or NULL in case of error / empty list.
"""
function vsigetfilemetadata(pszFilename, pszDomain, papszOptions)
    aftercare(ccall((:VSIGetFileMetadata, libgdal), Ptr{Cstring}, (Cstring, Cstring, CSLConstList), pszFilename, pszDomain, papszOptions))
end

"""
    VSISetFileMetadata(const char * pszFilename,
                       CSLConstList papszMetadata,
                       const char * pszDomain,
                       CSLConstList papszOptions) -> int

Set metadata on files.

### Parameters
* **pszFilename**: the path of the filesystem object to be queried. UTF-8 encoded.
* **papszMetadata**: NULL-terminated list of key=value strings.
* **pszDomain**: Metadata domain to set. Depends on the file system. The following are supported:

HEADERS: to set HTTP header

TAGS: to set S3 Object tagging information
* **papszOptions**: Unused. Should be set to NULL.

### Returns
TRUE in case of success.
"""
function vsisetfilemetadata(pszFilename, papszMetadata, pszDomain, papszOptions)
    aftercare(ccall((:VSISetFileMetadata, libgdal), Cint, (Cstring, CSLConstList, Cstring, CSLConstList), pszFilename, papszMetadata, pszDomain, papszOptions))
end

"""
    VSICalloc(size_t nCount,
              size_t nSize) -> void *

Analog of calloc().
"""
function vsicalloc()
    aftercare(ccall((:VSICalloc, libgdal), Ptr{Cvoid}, ()))
end

"""
    VSIMalloc(size_t nSize) -> void *

Analog of malloc().
"""
function vsimalloc()
    aftercare(ccall((:VSIMalloc, libgdal), Ptr{Cvoid}, ()))
end

"""
    VSIFree(void * pData) -> void

Analog of free() for data allocated with VSIMalloc(), VSICalloc(), VSIRealloc()
"""
function vsifree(arg1)
    aftercare(ccall((:VSIFree, libgdal), Cvoid, (Ptr{Cvoid},), arg1))
end

"""
    VSIRealloc(void * pData,
               size_t nNewSize) -> void *

Analog of realloc().
"""
function vsirealloc(arg1, size_t)
    aftercare(ccall((:VSIRealloc, libgdal), Ptr{Cvoid}, (Ptr{Cvoid}, Cint), arg1, size_t))
end

"""
    VSIStrdup(const char * pszString) -> char *

Analog of strdup().
"""
function vsistrdup(arg1)
    aftercare(ccall((:VSIStrdup, libgdal), Cstring, (Cstring,), arg1), false)
end

"""
    VSIMallocAligned(size_t nAlignment,
                     size_t nSize) -> void *

Allocates a buffer with an alignment constraint.

### Parameters
* **nAlignment**: Must be a power of 2, multiple of sizeof(void*), and lesser than 256.
* **nSize**: Size of the buffer to allocate.

### Returns
a buffer aligned on nAlignment and of size nSize, or NULL
"""
function vsimallocaligned(nAlignment, nSize)
    aftercare(ccall((:VSIMallocAligned, libgdal), Ptr{Cvoid}, (Cint, Cint), nAlignment, nSize))
end

"""
    VSIMallocAlignedAuto(size_t nSize) -> void *

Allocates a buffer with an alignment constraint such that it can be used by the most demanding vector instruction set on that platform.

### Parameters
* **nSize**: Size of the buffer to allocate.

### Returns
an aligned buffer of size nSize, or NULL
"""
function vsimallocalignedauto(nSize)
    aftercare(ccall((:VSIMallocAlignedAuto, libgdal), Ptr{Cvoid}, (Cint,), nSize))
end

"""
    VSIFreeAligned(void * ptr) -> void

Free a buffer allocated with VSIMallocAligned().

### Parameters
* **ptr**: Buffer to free.
"""
function vsifreealigned(ptr)
    aftercare(ccall((:VSIFreeAligned, libgdal), Cvoid, (Ptr{Cvoid},), ptr))
end

"""
    VSIMallocAlignedAutoVerbose(size_t nSize,
                                const char * pszFile,
                                int nLine) -> void *

See VSIMallocAlignedAuto()
"""
function vsimallocalignedautoverbose(nSize, pszFile, nLine)
    aftercare(ccall((:VSIMallocAlignedAutoVerbose, libgdal), Ptr{Cvoid}, (Cint, Cstring, Cint), nSize, pszFile, nLine))
end

"""
    VSIMalloc2(size_t nSize1,
               size_t nSize2) -> void *

VSIMalloc2 allocates (nSize1 * nSize2) bytes.
"""
function vsimalloc2(nSize1, nSize2)
    aftercare(ccall((:VSIMalloc2, libgdal), Ptr{Cvoid}, (Cint, Cint), nSize1, nSize2))
end

"""
    VSIMalloc3(size_t nSize1,
               size_t nSize2,
               size_t nSize3) -> void *

VSIMalloc3 allocates (nSize1 * nSize2 * nSize3) bytes.
"""
function vsimalloc3(nSize1, nSize2, nSize3)
    aftercare(ccall((:VSIMalloc3, libgdal), Ptr{Cvoid}, (Cint, Cint, Cint), nSize1, nSize2, nSize3))
end

"""
    VSIMallocVerbose(size_t nSize,
                     const char * pszFile,
                     int nLine) -> void *

VSIMallocVerbose.
"""
function vsimallocverbose(nSize, pszFile, nLine)
    aftercare(ccall((:VSIMallocVerbose, libgdal), Ptr{Cvoid}, (Cint, Cstring, Cint), nSize, pszFile, nLine))
end

"""
    VSIMalloc2Verbose(size_t nSize1,
                      size_t nSize2,
                      const char * pszFile,
                      int nLine) -> void *

VSIMalloc2Verbose.
"""
function vsimalloc2verbose(nSize1, nSize2, pszFile, nLine)
    aftercare(ccall((:VSIMalloc2Verbose, libgdal), Ptr{Cvoid}, (Cint, Cint, Cstring, Cint), nSize1, nSize2, pszFile, nLine))
end

"""
    VSIMalloc3Verbose(size_t nSize1,
                      size_t nSize2,
                      size_t nSize3,
                      const char * pszFile,
                      int nLine) -> void *

VSIMalloc3Verbose.
"""
function vsimalloc3verbose(nSize1, nSize2, nSize3, pszFile, nLine)
    aftercare(ccall((:VSIMalloc3Verbose, libgdal), Ptr{Cvoid}, (Cint, Cint, Cint, Cstring, Cint), nSize1, nSize2, nSize3, pszFile, nLine))
end

"""
    VSICallocVerbose(size_t nCount,
                     size_t nSize,
                     const char * pszFile,
                     int nLine) -> void *

VSICallocVerbose.
"""
function vsicallocverbose(nCount, nSize, pszFile, nLine)
    aftercare(ccall((:VSICallocVerbose, libgdal), Ptr{Cvoid}, (Cint, Cint, Cstring, Cint), nCount, nSize, pszFile, nLine))
end

"""
    VSIReallocVerbose(void * pOldPtr,
                      size_t nNewSize,
                      const char * pszFile,
                      int nLine) -> void *

VSIReallocVerbose.
"""
function vsireallocverbose(pOldPtr, nNewSize, pszFile, nLine)
    aftercare(ccall((:VSIReallocVerbose, libgdal), Ptr{Cvoid}, (Ptr{Cvoid}, Cint, Cstring, Cint), pOldPtr, nNewSize, pszFile, nLine))
end

"""
    VSIStrdupVerbose(const char * pszStr,
                     const char * pszFile,
                     int nLine) -> char *

VSIStrdupVerbose.
"""
function vsistrdupverbose(pszStr, pszFile, nLine)
    aftercare(ccall((:VSIStrdupVerbose, libgdal), Cstring, (Cstring, Cstring, Cint), pszStr, pszFile, nLine), false)
end

"""
    CPLGetPhysicalRAM(void) -> GIntBig

Return the total physical RAM in bytes.

### Returns
the total physical RAM in bytes (or 0 in case of failure).
"""
function cplgetphysicalram()
    aftercare(ccall((:CPLGetPhysicalRAM, libgdal), GIntBig, ()))
end

"""
    CPLGetUsablePhysicalRAM(void) -> GIntBig

Return the total physical RAM, usable by a process, in bytes.

### Returns
the total physical RAM, usable by a process, in bytes (or 0 in case of failure).
"""
function cplgetusablephysicalram()
    aftercare(ccall((:CPLGetUsablePhysicalRAM, libgdal), GIntBig, ()))
end

"""
    VSIReadDir(const char * pszPath) -> char **

Read names in a directory.

### Parameters
* **pszPath**: the relative, or absolute path of a directory to read. UTF-8 encoded.

### Returns
The list of entries in the directory, or NULL if the directory doesn't exist. Filenames are returned in UTF-8 encoding.
"""
function vsireaddir(arg1)
    aftercare(ccall((:VSIReadDir, libgdal), Ptr{Cstring}, (Cstring,), arg1))
end

"""
    VSIReadDirRecursive(const char * pszPathIn) -> char **

Read names in a directory recursively.

### Parameters
* **pszPathIn**: the relative, or absolute path of a directory to read. UTF-8 encoded.

### Returns
The list of entries in the directory and subdirectories or NULL if the directory doesn't exist. Filenames are returned in UTF-8 encoding.
"""
function vsireaddirrecursive(pszPath)
    aftercare(ccall((:VSIReadDirRecursive, libgdal), Ptr{Cstring}, (Cstring,), pszPath))
end

"""
    VSIReadDirEx(const char * pszPath,
                 int nMaxFiles) -> char **

Read names in a directory.

### Parameters
* **pszPath**: the relative, or absolute path of a directory to read. UTF-8 encoded.
* **nMaxFiles**: maximum number of files after which to stop, or 0 for no limit.

### Returns
The list of entries in the directory, or NULL if the directory doesn't exist. Filenames are returned in UTF-8 encoding.
"""
function vsireaddirex(pszPath, nMaxFiles)
    aftercare(ccall((:VSIReadDirEx, libgdal), Ptr{Cstring}, (Cstring, Cint), pszPath, nMaxFiles))
end

"""
    VSISiblingFiles(const char * pszFilename) -> char **

Return related filenames.

### Parameters
* **pszFilename**: the path of a filename to inspect UTF-8 encoded.

### Returns
The list of entries, relative to the directory, of all sidecar files available or NULL if the list is not known. Filenames are returned in UTF-8 encoding. Most implementations will return NULL, and a subsequent ReadDir will list all files available in the file's directory. This function will be overridden by VSI FilesystemHandlers that wish to force e.g. an empty list to avoid opening non-existant files on slow filesystems. The return value shall be destroyed with CSLDestroy()
"""
function vsisiblingfiles(pszPath)
    aftercare(ccall((:VSISiblingFiles, libgdal), Ptr{Cstring}, (Cstring,), pszPath))
end

"""
    VSIOpenDir(const char * pszPath,
               int nRecurseDepth,
               const char *const * papszOptions) -> VSIDIR *

Open a directory to read its entries.

### Parameters
* **pszPath**: the relative, or absolute path of a directory to read. UTF-8 encoded.
* **nRecurseDepth**: 0 means do not recurse in subdirectories, 1 means recurse only in the first level of subdirectories, etc. -1 means unlimited recursion level
* **papszOptions**: NULL terminated list of options, or NULL.

### Returns
a handle, or NULL in case of error
"""
function vsiopendir(pszPath, nRecurseDepth, papszOptions)
    aftercare(ccall((:VSIOpenDir, libgdal), Ptr{VSIDIR}, (Cstring, Cint, Ptr{Cstring}), pszPath, nRecurseDepth, papszOptions))
end

"""
    VSIGetNextDirEntry(VSIDIR * dir) -> const VSIDIREntry *

Return the next entry of the directory.

### Parameters
* **dir**: Directory handled returned by VSIOpenDir(). Must not be NULL.

### Returns
a entry, or NULL if there is no more entry in the directory. This return value must not be freed.
"""
function vsigetnextdirentry(dir)
    aftercare(ccall((:VSIGetNextDirEntry, libgdal), Ptr{VSIDIREntry}, (Ptr{VSIDIR},), dir))
end

"""
    VSICloseDir(VSIDIR * dir) -> void

Close a directory.

### Parameters
* **dir**: Directory handled returned by VSIOpenDir().
"""
function vsiclosedir(dir)
    aftercare(ccall((:VSICloseDir, libgdal), Cvoid, (Ptr{VSIDIR},), dir))
end

"""
    VSIMkdir(const char * pszPathname,
             long mode) -> int

Create a directory.

### Parameters
* **pszPathname**: the path to the directory to create. UTF-8 encoded.
* **mode**: the permissions mode.

### Returns
0 on success or -1 on an error.
"""
function vsimkdir(pszPathname, mode)
    aftercare(ccall((:VSIMkdir, libgdal), Cint, (Cstring, Clong), pszPathname, mode))
end

"""
    VSIMkdirRecursive(const char * pszPathname,
                      long mode) -> int

Create a directory and all its ancestors.

### Parameters
* **pszPathname**: the path to the directory to create. UTF-8 encoded.
* **mode**: the permissions mode.

### Returns
0 on success or -1 on an error.
"""
function vsimkdirrecursive(pszPathname, mode)
    aftercare(ccall((:VSIMkdirRecursive, libgdal), Cint, (Cstring, Clong), pszPathname, mode))
end

"""
    VSIRmdir(const char * pszDirname) -> int

Delete a directory.

### Parameters
* **pszDirname**: the path of the directory to be deleted. UTF-8 encoded.

### Returns
0 on success or -1 on an error.
"""
function vsirmdir(pszDirname)
    aftercare(ccall((:VSIRmdir, libgdal), Cint, (Cstring,), pszDirname))
end

"""
    VSIRmdirRecursive(const char * pszDirname) -> int

Delete a directory recursively.

### Returns
0 on success or -1 on an error.
"""
function vsirmdirrecursive(pszDirname)
    aftercare(ccall((:VSIRmdirRecursive, libgdal), Cint, (Cstring,), pszDirname))
end

"""
    VSIUnlink(const char * pszFilename) -> int

Delete a file.

### Parameters
* **pszFilename**: the path of the file to be deleted. UTF-8 encoded.

### Returns
0 on success or -1 on an error.
"""
function vsiunlink(pszFilename)
    aftercare(ccall((:VSIUnlink, libgdal), Cint, (Cstring,), pszFilename))
end

"""
    VSIUnlinkBatch(CSLConstList papszFiles) -> int *

Delete several files, possibly in a batch.

### Parameters
* **papszFiles**: NULL terminated list of files. UTF-8 encoded.

### Returns
an array of size CSLCount(papszFiles), whose values are TRUE or FALSE depending on the success of deletion of the corresponding file. The array should be freed with VSIFree(). NULL might be return in case of a more general error (for example, files belonging to different file system handlers)
"""
function vsiunlinkbatch(papszFiles)
    aftercare(ccall((:VSIUnlinkBatch, libgdal), Ptr{Cint}, (CSLConstList,), papszFiles))
end

"""
    VSIRename(const char * oldpath,
              const char * newpath) -> int

Rename a file.

### Parameters
* **oldpath**: the name of the file to be renamed. UTF-8 encoded.
* **newpath**: the name the file should be given. UTF-8 encoded.

### Returns
0 on success or -1 on an error.
"""
function vsirename(oldpath, newpath)
    aftercare(ccall((:VSIRename, libgdal), Cint, (Cstring, Cstring), oldpath, newpath))
end

"""
    VSISync(const char * pszSource,
            const char * pszTarget,
            const char *const * papszOptions,
            GDALProgressFunc pProgressFunc,
            void * pProgressData,
            char *** ppapszOutputs) -> int

Synchronize a source file/directory with a target file/directory.

### Parameters
* **pszSource**: Source file or directory. UTF-8 encoded.
* **pszTarget**: Target file or directory. UTF-8 encoded.
* **papszOptions**: Null terminated list of options, or NULL. Currently accepted options are:

RECURSIVE=NO (the default is YES)

SYNC_STRATEGY=TIMESTAMP/ETAG/OVERWRITE. Determines which criterion is used to determine if a target file must be replaced when it already exists and has the same file size as the source. Only applies for a source or target being a network filesystem.
The default is TIMESTAMP (similarly to how 'aws s3 sync' works), that is to say that for an upload operation, a remote file is replaced if it has a different size or if it is older than the source. For a download operation, a local file is replaced if it has a different size or if it is newer than the remote file.
The ETAG strategy assumes that the ETag metadata of the remote file is the MD5Sum of the file content, which is only true in the case of /vsis3/ for files not using KMS server side encryption and uploaded in a single PUT operation (so smaller than 50 MB given the default used by GDAL). Only to be used for /vsis3/, /vsigs/ or other filesystems using a MD5Sum as ETAG.
The OVERWRITE strategy (GDAL >= 3.2) will always overwrite the target file with the source one.

NUM_THREADS=integer. Number of threads to use for parallel file copying. Only use for when /vsis3/, /vsigs/ or /vsiaz/ is in source or target. Since GDAL 3.1

CHUNK_SIZE=integer. Maximum size of chunk (in bytes) to use to split large objects when downloading them from /vsis3/, /vsigs/ or /vsiaz/ to local file system, or for upload to /vsis3/ or /vsiaz/ from local file system. Only used if NUM_THREADS > 1. For upload to /vsis3/, this chunk size must be set at least to 5 MB. Since GDAL 3.1
* **pProgressFunc**: Progress callback, or NULL.
* **pProgressData**: User data of progress callback, or NULL.
* **ppapszOutputs**: Unused. Should be set to NULL for now.

### Returns
TRUE on success or FALSE on an error.
"""
function vsisync(pszSource, pszTarget, papszOptions, pProgressFunc, pProgressData, ppapszOutputs)
    aftercare(ccall((:VSISync, libgdal), Cint, (Cstring, Cstring, Ptr{Cstring}, GDALProgressFunc, Ptr{Cvoid}, Ptr{Ptr{Cstring}}), pszSource, pszTarget, papszOptions, pProgressFunc, pProgressData, ppapszOutputs))
end

"""
    VSIStrerror(int nErrno) -> char *

Return the error string corresponding to the error number.
"""
function vsistrerror(arg1)
    aftercare(ccall((:VSIStrerror, libgdal), Cstring, (Cint,), arg1), false)
end

"""
    VSIGetDiskFreeSpace(const char * pszDirname) -> GIntBig

Return free disk space available on the filesystem.

### Parameters
* **pszDirname**: a directory of the filesystem to query.

### Returns
The free space in bytes. Or -1 in case of error.
"""
function vsigetdiskfreespace(pszDirname)
    aftercare(ccall((:VSIGetDiskFreeSpace, libgdal), GIntBig, (Cstring,), pszDirname))
end

"""
    VSINetworkStatsReset(void) -> void

Clear network related statistics.
"""
function vsinetworkstatsreset()
    aftercare(ccall((:VSINetworkStatsReset, libgdal), Cvoid, ()))
end

"""
    VSINetworkStatsGetAsSerializedJSON(char ** papszOptions) -> char *

Return network related statistics, as a JSON serialized object.

### Parameters
* **papszOptions**: Unused.

### Returns
a JSON serialized string to free with VSIFree(), or nullptr
"""
function vsinetworkstatsgetasserializedjson(papszOptions)
    aftercare(ccall((:VSINetworkStatsGetAsSerializedJSON, libgdal), Cstring, (Ptr{Cstring},), papszOptions), false)
end

"""
    VSIInstallMemFileHandler() -> void

Install "memory" file system handler.
"""
function vsiinstallmemfilehandler()
    aftercare(ccall((:VSIInstallMemFileHandler, libgdal), Cvoid, ()))
end

function vsiinstalllargefilehandler()
    aftercare(ccall((:VSIInstallLargeFileHandler, libgdal), Cvoid, ()))
end

"""
    VSIInstallSubFileHandler() -> void

Install /vsisubfile/ virtual file handler.
"""
function vsiinstallsubfilehandler()
    aftercare(ccall((:VSIInstallSubFileHandler, libgdal), Cvoid, ()))
end

"""
    VSIInstallCurlFileHandler(void) -> void

Install /vsicurl/ HTTP/FTP file system handler (requires libcurl)
"""
function vsiinstallcurlfilehandler()
    aftercare(ccall((:VSIInstallCurlFileHandler, libgdal), Cvoid, ()))
end

"""
    VSICurlClearCache(void) -> void

Clean local cache associated with /vsicurl/ (and related file systems)
"""
function vsicurlclearcache()
    aftercare(ccall((:VSICurlClearCache, libgdal), Cvoid, ()))
end

"""
    VSICurlPartialClearCache(const char * pszFilenamePrefix) -> void

Clean local cache associated with /vsicurl/ (and related file systems) for a given filename (and its subfiles and subdirectories if it is a directory)

### Parameters
* **pszFilenamePrefix**: Filename prefix
"""
function vsicurlpartialclearcache(pszFilenamePrefix)
    aftercare(ccall((:VSICurlPartialClearCache, libgdal), Cvoid, (Cstring,), pszFilenamePrefix))
end

"""
    VSIInstallCurlStreamingFileHandler(void) -> void

Install /vsicurl_streaming/ HTTP/FTP file system handler (requires libcurl).
"""
function vsiinstallcurlstreamingfilehandler()
    aftercare(ccall((:VSIInstallCurlStreamingFileHandler, libgdal), Cvoid, ()))
end

"""
    VSIInstallS3FileHandler(void) -> void

Install /vsis3/ Amazon S3 file system handler (requires libcurl)
"""
function vsiinstalls3filehandler()
    aftercare(ccall((:VSIInstallS3FileHandler, libgdal), Cvoid, ()))
end

"""
    VSIInstallS3StreamingFileHandler(void) -> void

Install /vsis3_streaming/ Amazon S3 file system handler (requires libcurl).
"""
function vsiinstalls3streamingfilehandler()
    aftercare(ccall((:VSIInstallS3StreamingFileHandler, libgdal), Cvoid, ()))
end

"""
    VSIInstallGSFileHandler(void) -> void

Install /vsigs/ Google Cloud Storage file system handler (requires libcurl)
"""
function vsiinstallgsfilehandler()
    aftercare(ccall((:VSIInstallGSFileHandler, libgdal), Cvoid, ()))
end

"""
    VSIInstallGSStreamingFileHandler(void) -> void

Install /vsigs_streaming/ Google Cloud Storage file system handler (requires libcurl)
"""
function vsiinstallgsstreamingfilehandler()
    aftercare(ccall((:VSIInstallGSStreamingFileHandler, libgdal), Cvoid, ()))
end

"""
    VSIInstallAzureFileHandler(void) -> void

Install /vsiaz/ Microsoft Azure Blob file system handler (requires libcurl)
"""
function vsiinstallazurefilehandler()
    aftercare(ccall((:VSIInstallAzureFileHandler, libgdal), Cvoid, ()))
end

"""
    VSIInstallAzureStreamingFileHandler(void) -> void

Install /vsiaz_streaming/ Microsoft Azure Blob file system handler (requires libcurl)
"""
function vsiinstallazurestreamingfilehandler()
    aftercare(ccall((:VSIInstallAzureStreamingFileHandler, libgdal), Cvoid, ()))
end

"""
    VSIInstallOSSFileHandler(void) -> void

Install /vsioss/ Alibaba Cloud Object Storage Service (OSS) file system handler (requires libcurl)
"""
function vsiinstallossfilehandler()
    aftercare(ccall((:VSIInstallOSSFileHandler, libgdal), Cvoid, ()))
end

"""
    VSIInstallOSSStreamingFileHandler(void) -> void

Install /vsioss_streaming/ Alibaba Cloud Object Storage Service (OSS) file system handler (requires libcurl)
"""
function vsiinstallossstreamingfilehandler()
    aftercare(ccall((:VSIInstallOSSStreamingFileHandler, libgdal), Cvoid, ()))
end

"""
    VSIInstallSwiftFileHandler(void) -> void

Install /vsiswift/ OpenStack Swif Object Storage (Swift) file system handler (requires libcurl)
"""
function vsiinstallswiftfilehandler()
    aftercare(ccall((:VSIInstallSwiftFileHandler, libgdal), Cvoid, ()))
end

"""
    VSIInstallSwiftStreamingFileHandler(void) -> void

Install /vsiswift_streamin/ OpenStack Swif Object Storage (Swift) file system handler (requires libcurl)
"""
function vsiinstallswiftstreamingfilehandler()
    aftercare(ccall((:VSIInstallSwiftStreamingFileHandler, libgdal), Cvoid, ()))
end

"""
    VSIInstallGZipFileHandler() -> void

Install GZip file system handler.
"""
function vsiinstallgzipfilehandler()
    aftercare(ccall((:VSIInstallGZipFileHandler, libgdal), Cvoid, ()))
end

"""
    VSIInstallZipFileHandler() -> void

Install ZIP file system handler.
"""
function vsiinstallzipfilehandler()
    aftercare(ccall((:VSIInstallZipFileHandler, libgdal), Cvoid, ()))
end

"""
    VSIInstallStdinHandler() -> void

Install /vsistdin/ file system handler.
"""
function vsiinstallstdinhandler()
    aftercare(ccall((:VSIInstallStdinHandler, libgdal), Cvoid, ()))
end

"""
    VSIInstallHdfsHandler() -> void

Install /vsihdfs/ file system handler (requires JVM and HDFS support)
"""
function vsiinstallhdfshandler()
    aftercare(ccall((:VSIInstallHdfsHandler, libgdal), Cvoid, ()))
end

"""
    VSIInstallWebHdfsHandler(void) -> void

Install /vsiwebhdfs/ WebHDFS (Hadoop File System) REST API file system handler (requires libcurl)
"""
function vsiinstallwebhdfshandler()
    aftercare(ccall((:VSIInstallWebHdfsHandler, libgdal), Cvoid, ()))
end

"""
    VSIInstallStdoutHandler() -> void

Install /vsistdout/ file system handler.
"""
function vsiinstallstdouthandler()
    aftercare(ccall((:VSIInstallStdoutHandler, libgdal), Cvoid, ()))
end

"""
    VSIInstallSparseFileHandler() -> void

Install /vsisparse/ virtual file handler.
"""
function vsiinstallsparsefilehandler()
    aftercare(ccall((:VSIInstallSparseFileHandler, libgdal), Cvoid, ()))
end

"""
    VSIInstallTarFileHandler(void) -> void

Install /vsitar/ file system handler.
"""
function vsiinstalltarfilehandler()
    aftercare(ccall((:VSIInstallTarFileHandler, libgdal), Cvoid, ()))
end

"""
    VSIInstallCryptFileHandler() -> void

Install /vsicrypt/ encrypted file system handler (requires libcrypto++)
"""
function vsiinstallcryptfilehandler()
    aftercare(ccall((:VSIInstallCryptFileHandler, libgdal), Cvoid, ()))
end

"""
    VSISetCryptKey(const GByte * pabyKey,
                   int nKeySize) -> void

Installs the encryption/decryption key.

### Parameters
* **pabyKey**: key. Might be NULL to clear previously set key.
* **nKeySize**: length of the key in bytes. Might be 0 to clear previously set key.
"""
function vsisetcryptkey(pabyKey, nKeySize)
    aftercare(ccall((:VSISetCryptKey, libgdal), Cvoid, (Ptr{GByte}, Cint), pabyKey, nKeySize))
end

function vsicleanupfilemanager()
    aftercare(ccall((:VSICleanupFileManager, libgdal), Cvoid, ()))
end

"""
    VSIFileFromMemBuffer(const char * pszFilename,
                         GByte * pabyData,
                         vsi_l_offset nDataLength,
                         int bTakeOwnership) -> VSILFILE *

Create memory "file" from a buffer.

### Parameters
* **pszFilename**: the filename to be created.
* **pabyData**: the data buffer for the file.
* **nDataLength**: the length of buffer in bytes.
* **bTakeOwnership**: TRUE to transfer "ownership" of buffer or FALSE.

### Returns
open file handle on created file (see VSIFOpenL()).
"""
function vsifilefrommembuffer(pszFilename, pabyData, nDataLength, bTakeOwnership)
    aftercare(ccall((:VSIFileFromMemBuffer, libgdal), Ptr{VSILFILE}, (Cstring, Ptr{GByte}, vsi_l_offset, Cint), pszFilename, pabyData, nDataLength, bTakeOwnership))
end

"""
    VSIGetMemFileBuffer(const char * pszFilename,
                        vsi_l_offset * pnDataLength,
                        int bUnlinkAndSeize) -> GByte *

Fetch buffer underlying memory file.

### Parameters
* **pszFilename**: the name of the file to grab the buffer of.
* **pnDataLength**: (file) length returned in this variable.
* **bUnlinkAndSeize**: TRUE to remove the file, or FALSE to leave unaltered.

### Returns
pointer to memory buffer or NULL on failure.
"""
function vsigetmemfilebuffer(pszFilename, pnDataLength, bUnlinkAndSeize)
    aftercare(ccall((:VSIGetMemFileBuffer, libgdal), Ptr{GByte}, (Cstring, Ptr{vsi_l_offset}, Cint), pszFilename, pnDataLength, bUnlinkAndSeize))
end

"""
    VSIStdoutSetRedirection(VSIWriteFunction pFct,
                            FILE * stream) -> void

Set an alternative write function and output file handle instead of fwrite() / stdout.

### Parameters
* **pFct**: Function with same signature as fwrite()
* **stream**: File handle on which to output. Passed to pFct.
"""
function vsistdoutsetredirection(pFct, stream)
    aftercare(ccall((:VSIStdoutSetRedirection, libgdal), Cvoid, (Cint, Ptr{Cint}), pFct, stream))
end

"""
    VSIAllocFilesystemPluginCallbacksStruct(void) -> VSIFilesystemPluginCallbacksStruct *

return a VSIFilesystemPluginCallbacksStruct to be populated at runtime with handler callbacks
"""
function vsiallocfilesystemplugincallbacksstruct()
    aftercare(ccall((:VSIAllocFilesystemPluginCallbacksStruct, libgdal), Ptr{VSIFilesystemPluginCallbacksStruct}, ()))
end

"""
    VSIFreeFilesystemPluginCallbacksStruct(VSIFilesystemPluginCallbacksStruct * poCb) -> void

free resources allocated by VSIAllocFilesystemPluginCallbacksStruct
"""
function vsifreefilesystemplugincallbacksstruct(poCb)
    aftercare(ccall((:VSIFreeFilesystemPluginCallbacksStruct, libgdal), Cvoid, (Ptr{VSIFilesystemPluginCallbacksStruct},), poCb))
end

"""
    VSIInstallPluginHandler(const char * pszPrefix,
                            const VSIFilesystemPluginCallbacksStruct * poCb) -> int

register a handler on the given prefix.
"""
function vsiinstallpluginhandler(pszPrefix, poCb)
    aftercare(ccall((:VSIInstallPluginHandler, libgdal), Cint, (Cstring, Ptr{VSIFilesystemPluginCallbacksStruct}), pszPrefix, poCb))
end

"""
    VSITime(unsigned long * pnTimeToSet) -> unsigned long
"""
function vsitime(arg1)
    aftercare(ccall((:VSITime, libgdal), Culong, (Ptr{Culong},), arg1))
end

"""
    VSICTime(unsigned long nTime) -> const char *
"""
function vsictime(arg1)
    aftercare(ccall((:VSICTime, libgdal), Cstring, (Culong,), arg1), false)
end

"""
    VSIGMTime(const time_t * pnTime,
              struct tm * poBrokenTime) -> struct tm *
"""
function vsigmtime(pnTime, poBrokenTime)
    aftercare(ccall((:VSIGMTime, libgdal), Ptr{Ctm}, (Ptr{Cint}, Ptr{Ctm}), pnTime, poBrokenTime))
end

"""
    VSILocalTime(const time_t * pnTime,
                 struct tm * poBrokenTime) -> struct tm *
"""
function vsilocaltime(pnTime, poBrokenTime)
    aftercare(ccall((:VSILocalTime, libgdal), Ptr{Ctm}, (Ptr{Cint}, Ptr{Ctm}), pnTime, poBrokenTime))
end


text:
module PmmZeroDrag
using DifferentialEquations, ...Types
export timeOfFlight!
function QE(target::Target2D)
    θ = 1/2 *asin(g0*target.position[1]/v0^2)
    return θ
end

function timeOfFlight!(p::Projectile2D, target::Target2D) #PmmZEroDrag
    #t = 0.0
    ρ = target.position[1]
    #θ = QE(target)
    #println(θ)
    #p.velocity_m[1] = v0*cos(θ)
    #p.velocity_m[2] = v0*sin(θ)
    flight_time = 0.0

    function f(du,u,p,t)
        du[1] = u[3]#vx
        du[2] = u[4]#vy
        du[3] =  0 #ax no forces in this direction
        du[4] = g0 #ax
    end
    x0 = p.position[1]
    y0 = p.position[2]
    v0x = p.velocity_m[1]
    v0y = p.velocity_m[2]
    u0 = [x0,y0,v0x,v0y]
    tspan = (0.0,tmax)
    prob = ODEProblem(f,u0,tspan)
    #sol = solve(prob, Tsit5(), reltol=1e-8, abstol=1e-8)
    integrator = init(prob, Tsit5(), reltol=1e-8, abstol=1e-8)
    oldx = integrator.u[1]
    oldy = integrator.u[2]
    oldu = integrator.u[3]
    oldv = integrator.u[4]
    while ρ > p.position[1]
        oldx = integrator.u[1]
        oldy = integrator.u[2]
        oldu = integrator.u[3]
        oldv = integrator.u[4]
    step!(integrator,dt,true)
    p.position[1]= integrator.u[1]

    end
    p.position[1] = oldx
    p.position[2] = oldy
    p.velocity_m[1] = oldu
    p.velocity_m[2] = oldv
    flight_time = integrator.t-dt
    p.yaw = atan(p.velocity_m[2]/p.velocity_m[1])

    return flight_time
end

end


text:
# This file was generated by the Julia Swagger Code Generator
# Do not modify this file directly. Modify the swagger specification instead.

@doc raw"""describes the attributes of a scale subresource

    IoK8sApiExtensionsV1beta1ScaleSpec(;
        replicas=nothing,
    )

    - replicas::Int32 : desired number of instances for the scaled object.
"""
mutable struct IoK8sApiExtensionsV1beta1ScaleSpec <: SwaggerModel
    replicas::Any # spec type: Union{ Nothing, Int32 } # spec name: replicas

    function IoK8sApiExtensionsV1beta1ScaleSpec(;replicas=nothing)
        o = new()
        validate_property(IoK8sApiExtensionsV1beta1ScaleSpec, Symbol("replicas"), replicas)
        setfield!(o, Symbol("replicas"), replicas)
        o
    end
end # type IoK8sApiExtensionsV1beta1ScaleSpec

const _property_map_IoK8sApiExtensionsV1beta1ScaleSpec = Dict{Symbol,Symbol}(Symbol("replicas")=>Symbol("replicas"))
const _property_types_IoK8sApiExtensionsV1beta1ScaleSpec = Dict{Symbol,String}(Symbol("replicas")=>"Int32")
Base.propertynames(::Type{ IoK8sApiExtensionsV1beta1ScaleSpec }) = collect(keys(_property_map_IoK8sApiExtensionsV1beta1ScaleSpec))
Swagger.property_type(::Type{ IoK8sApiExtensionsV1beta1ScaleSpec }, name::Symbol) = Union{Nothing,eval(Base.Meta.parse(_property_types_IoK8sApiExtensionsV1beta1ScaleSpec[name]))}
Swagger.field_name(::Type{ IoK8sApiExtensionsV1beta1ScaleSpec }, property_name::Symbol) =  _property_map_IoK8sApiExtensionsV1beta1ScaleSpec[property_name]

function check_required(o::IoK8sApiExtensionsV1beta1ScaleSpec)
    true
end

function validate_property(::Type{ IoK8sApiExtensionsV1beta1ScaleSpec }, name::Symbol, val)
end


text:
#=
Created on Monday 12 August 2019
Last update: -

@author: Michiel Stock
michielfmstock@gmail.com

Efficient ways of storing Kronecker powers.
=#

"""
Efficient way of storing Kronecker powers, e.g.

K = A ⊗ A ⊗ ... ⊗ A.
"""
struct KroneckerPower{T,TA<:AbstractMatrix{T}} <: AbstractKroneckerProduct{T}
   A::TA
   pow::Int
   function KroneckerPower(A::AbstractMatrix, pow::Integer)
      @assert pow ≥ 2 "KroneckerPower only makes sense for powers greater than 1"
      return new{eltype(A), typeof(A)}(A, Int(pow))
    end
end

"""
    kronecker(A::AbstractMatrix, pow::Int)

Kronecker power, computes `A ⊗ A ⊗ ... ⊗ A`. Returns a lazy `KroneckerPower`
type.
"""
kronecker(A::AbstractMatrix, pow::Integer) = KroneckerPower(A, pow)

"""
    ⊗(A::AbstractMatrix, pow::Int)

Kronecker power, computes `A ⊗ A ⊗ ... ⊗ A`. Returns a lazy `KroneckerPower`
type.
"""
⊗(A::AbstractMatrix, pow::Integer) = kronecker(A, pow)

getallfactors(K::KroneckerPower) = ntuple(_ -> K.A, K.pow)

getmatrices(K::KroneckerPower) = (K.pow == 2 ? K.A : KroneckerPower(K.A, K.pow-1), K.A)

order(K::KroneckerPower) = K.pow
Base.size(K::KroneckerPower) = size(K.A).^K.pow
issquare(K::KroneckerPower) = issquare(K.A)

# SCALAR EQUIVALENTS FOR AbstractKroneckerProduct

"""
    det(K::KroneckerPower)

Compute the determinant of a Kronecker power.
"""
function LinearAlgebra.det(K::KroneckerPower)
    checksquare(K.A)
    A, pow = K.A, K.pow
    n = size(A, 1)
    return det(K.A)^(n * pow)
end

"""
    logdet(K::KroneckerPower)

Compute the logarithm of the determinant of a Kronecker power.
"""
function LinearAlgebra.logdet(K::KroneckerPower)
    checksquare(K.A)
    A, pow = K.A, K.pow
    n = size(A, 1)
    return n * pow * logdet(K.A)
end

"""
    tr(K::KroneckerPower)

Compute the trace of a Kronecker power.
"""
function LinearAlgebra.tr(K::KroneckerPower)
    checksquare(K.A)
    return tr(K.A)^K.pow
end

# Matrix operations

"""
    inv(K::KroneckerPower)

Compute the inverse of a Kronecker power.
"""
function Base.inv(K::KroneckerPower)
    checksquare(K.A)
    return KroneckerPower(inv(K.A), K.pow)
end

"""
    pinv(K::KroneckerPower)

Compute the Moore-Penrose pseudo-inverse of a Kronecker power.
"""
function LinearAlgebra.pinv(K::KroneckerPower)
    return KroneckerPower(pinv(K.A), K.pow)
end

function LinearAlgebra.diag(K::KroneckerPower)
    if issquare(K.A)
        d = reshape(diag(K.A), :, 1)
        return vec(reduce(kron, fill(d, order(K))))
    end
    return K[diagind(K)]
end

"""
    adjoint(K::KroneckerPower)

Compute the adjoint of a Kronecker power.
"""
function Base.adjoint(K::KroneckerPower)
    return KroneckerPower(K.A', K.pow)
end

"""
    transpose(K::KroneckerPower)

Compute the transpose of a Kronecker power.
"""
function Base.transpose(K::KroneckerPower)
    return KroneckerPower(transpose(K.A), K.pow)
end

"""
    conj(K::KroneckerPower)

Compute the conjugate of a Kronecker power.
"""
function Base.conj(K::KroneckerPower)
    return KroneckerPower(conj(K.A), K.pow)
end

# mixed-product property
function _mulmixed(K1::KroneckerPower, K2::KroneckerPower)
    if size(K1, 2) != size(K2, 1)
        throw(DimensionMismatch("Mismatch between K1 and K2"))
    end
    return KroneckerPower(K1.A * K2.A, K1.pow)
end
function Base.:*(K1::KroneckerPower, K2::KroneckerPower)
    K1.pow == K2.pow || throw(ArgumentError("multiplication is only defined if all terms have the same exponent"))
    _mulmixed(K1, K2)
end
const KronPowDiagonal = KroneckerPower{<:Any, <:Diagonal}
function Base.:*(K1::KronPowDiagonal, K2::KronPowDiagonal)
    K1.pow == K2.pow || throw(ArgumentError("multiplication is only defined if all terms have the same exponent"))
    _mulmixed(K1, K2)
end

for T in [:Diagonal, :UniformScaling]
    @eval Base.:+(K::KronPowDiagonal, D::$T) = Diagonal(K) + D
    @eval Base.:+(D::$T, K::KronPowDiagonal) = D + Diagonal(K)
    @eval Base.:-(K::KronPowDiagonal, D::$T) = Diagonal(K) - D
    @eval Base.:-(D::$T, K::KronPowDiagonal) = D - Diagonal(K)
end

"""
    lmul!(a::Number, K::KroneckerPower)

Scale an `KroneckerPower` `K` inplace by a factor `a` by rescaling the matrix the base
matrix with a factor `a^(1/N)`.

It is recommended to rewrite your Kronecker product rather as `copy(A) ⊗ (A ⊗ n - 1)`
(note the copy) for numerical stability. This will only modify the first matrix, leaving
the chain of Kronecker products alone.
"""
function LinearAlgebra.lmul!(a::Number, K::KroneckerPower)
    # will only work if eltype is some kind of float
    #eltype(K) <: AbstractFloat || throw(InexactError)
    n = order(K)
    @warn "Inplace scaling of a Kronecker product, consider rewriting as `copy(A) ⊗ (A ⊗ $n - 1)`"
    A = K.A
    lmul!(a^(1 / n), A)
end


text:
struct Packed{N}
    val::UInt64
end

function bitsize end

bitsize(::Type{Packed{N}}) where N = N

bitsize(::Type{T}) where T = sizeof(T)*8

S1 = @NamedTuple{ f1::Packed{1}, i1::Packed{7}, i2::Packed{4}, i3::Packed{12}}
S2 = @NamedTuple{ v1::UInt8, v2::UInt8, v3::UInt16, v4::UInt32}

primitive type PackedStruct{T<:NamedTuple} 64 end

PackedStruct{T}(v::UInt64) where T = reinterpret(PackedStruct{T},v)

s1 = PackedStruct{S1}(0x0102030405060708)

Base.@pure function Base.getproperty(x::PackedStruct{T},s::Symbol) where T
    shift = 0
    syms = T.parameters[1]
    types = Tuple(T.parameters[2].parameters)
    bits = bitsize(types[1])
    if syms[1]===s
        mask = (one(UInt64)<<bits) -one(UInt64)
        return types[1]((reinterpret(UInt64,x) >>> shift ) & mask)
    end

    shift = shift + bits
    bits = bitsize(types[2])
    if syms[2]===s
        mask = (one(UInt64)<<bits) -one(UInt64)
        return types[2]((reinterpret(UInt64,x) >>> shift ) & mask)
    end
    shift = shift + bits
    bits = bitsize(types[3])
    if syms[3]===s
        mask = (one(UInt64)<<bits) -one(UInt64)
        return types[3]((reinterpret(UInt64,x) >>> shift ) & mask)
    end
    shift = shift + bits
    bits = bitsize(types[4])
    if syms[4]===s
        mask = (one(UInt64)<<bits) -one(UInt64)
        return (reinterpret(UInt64,x) >>> shift ) & mask
    end
    shift = shift + bits
    bits = bitsize(types[5])
    if syms[5]===s
        mask = (one(UInt64)<<bits) -one(UInt64)
        return (reinterpret(UInt64,x) >>> shift ) & mask
    end
    shift = shift + bits
    bits = bitsize(types[6])
    if syms[6]===s
        mask = (one(UInt64)<<bits) -one(UInt64)
        return (reinterpret(UInt64,x) >>> shift ) & mask
    end
    shift = shift + bits
    bits = bitsize(types[7])
    if syms[7]===s
        mask = (one(UInt64)<<bits) -one(UInt64)
        return (reinterpret(UInt64,x) >>> shift ) & mask
    end
    shift = shift + bits
    bits = bitsize(types[8])
    if syms[8]===s
        mask = (one(UInt64)<<bits) -one(UInt64)
        return (reinterpret(UInt64,x) >>> shift ) & mask
    end
end
#=
show(s1.f1)
println()
show(s1.i1)
println()
show(s1.i2)
println()
show(s1.i3)
println()
=#

s2 = PackedStruct{S2}(0x0102030405060708)
show(s2.v1)
println()
show(s2.v2)
println()
show(s2.v3)
println()
show(s2.v4)
println()


text:
function tomakie!(sc::AbstractScene, layout::MakieLayout.GridLayout, args...; attrs...)
    # TODO create a finalizer for a Tuple{Scene, Layout, Vector{LAxis}}
    RecipesPipeline.recipe_pipeline!(sc, Dict{Symbol, Any}(attrs), args)
end


text:
using MarketData

facts("readwrite parses csv file correctly") do

  context("1-d values array works") do
      @fact typeof(cl.values) => Array{Float64,1}
  end

  context("1-d values array works") do
      @fact typeof(ohlc.values) => Array{Float64,2}
  end

  context("timestamp parses to correct type") do
      @fact typeof(cl.timestamp) => Array{Date,1}
  end
end


text:
using Distributed
@everywhere using Random, Test
include("utils.jl")

const tutorialtests = [
   "line"
]

const samplertests = [
  "amm",
  "amwg",
  "bhmc",
  "bia",
  "bmc3",
  "bmg",
  "hmc",
  "mala",
  "nuts",
  "rwm",
  "slice",
  "slicesimplex"
]

const mcmctests = [
  "discretediag",
  "readcoda"
]

const extensiontests = [
  "newunivardist",
  "newmultivardist"
]

const treetests = [
  "basics",
  "consensus",
  "ladderize",
  "pruning",
  "spr"
]

const parsertests = [
  "newick"
]

println("Running tests:")
#
@testset "All tests" begin
@testset "Tutorial" begin
  @everywhere Random.seed!(123)
  for t in tutorialtests
    @runtest "mcmc/" t
end
end

@testset "Samplertest" begin
  @everywhere Random.seed!(123)
for t in samplertests
    @runtest "samplers/" t
  end
end
@testset "mcmctests" begin
  for t in mcmctests
    @runtest "mcmc/" t
  end
end
#
@testset "extensions" begin
for t in extensiontests
  @everywhere Random.seed!(123)
    @runtest "mcmc/" t
  end
end

@testset "treetests" begin
for t in treetests
  @everywhere Random.seed!(123)
    @runtest "Tree/" t
  end
end

@testset "parsers" begin
for t in parsertests
  @everywhere Random.seed!(123)
    @runtest "parsers/" t
  end
end

end
#all sets test set


text:
using Test
@isdefined(TestHelpers) || include("TestHelpers.jl")

TestHelpers.@timed_testset "vendor/" begin
    include("vendor/ConditionalJuMP.jl")
end


text:
@testset "Kaleidoscope" begin

include(joinpath(@__DIR__, "..", "examples", "Kaleidoscope", "Kaleidoscope.jl"))

@testset "recursion" begin
    program = """
        def fib(x) {
            if x < 3 then
                1
            else
                fib(x-1) + fib(x-2)
        }

        def entry() {
            fib(10)
        }
    """

    LLVM.Context() do ctx
        m = Kaleidoscope.generate_IR(program, ctx)
        Kaleidoscope.optimize!(m)
        v = Kaleidoscope.run(m, "entry")
        @test v == 55.0
    end
end

@testset "loops" begin
    program = """
    def fib(x) {
        var a = 1, b = 1
        for i = 3, i < x, 1.0 {
            var c = a + b
            a = b
            b = c
        }
        b
    }

    def entry() {
        fib(10)
    }
    """

    LLVM.Context() do ctx
        m = Kaleidoscope.generate_IR(program, ctx)
        Kaleidoscope.optimize!(m)
        mktemp() do path, io
            Kaleidoscope.write_objectfile(m, path)
        end
        v = Kaleidoscope.run(m, "entry")
        @test v == 55.0
    end
end

@testset "global vars" begin
    program = """
    var x = 5
    var y = 3
    def foo(a b) a + b + x + y

    def entry() foo(2, 3)
    """

    LLVM.Context() do ctx
        m = Kaleidoscope.generate_IR(program, ctx)
        Kaleidoscope.optimize!(m)
        v = Kaleidoscope.run(m, "entry")
        @test v == 13
    end
end

end


text:
include("../src/interface.jl")
using LinearAlgebra
using Plots
using Plots.PlotMeasures

function fctmain(n_tau, prec)
    setprecision(prec)
    u0 = BigFloat.([-34 // 100, 78 // 100, 67 // 100, -56 // 10])
    B =
        BigFloat.([
            12//100 -78//100 91//100 34//100
            -45//100 56//100 3//100 54//100
            -67//100 09//100 18//100 89//100
            -91//100 -56//100 11//100 -56//100
        ])
    alpha = BigFloat.([12 // 100, -98 // 100, 45 // 100, 26 // 100])
    beta = BigFloat.([-4 // 100, 48 // 100, 23 // 100, -87 // 100])
    println("u0=$u0")
    println("B=$B")
    println("alpha=$alpha")
    println("beta=$beta")
    fct = (u, p, t) -> B * u + t * p[1] + p[2]
    t_max = big"1.0"
    epsilon = big"0.015"
    A = [0 0 1 0; 0 0 0 0; -1 0 0 0; 0 0 0 0]
    prob = HiOscODEProblem(fct, u0, (big"0.0", t_max), (alpha, beta), A, epsilon, B)
    #    nbmaxtest=6
    nbmaxtest = 12
    ordmax = 17
    debord = 3
    pasord = 2
    y = ones(Float64, nbmaxtest, div(ordmax - debord, pasord) + 1)
    x = zeros(Float64, nbmaxtest)
    ind = 1
    for order = debord:pasord:ordmax
        nb = 100
        indc = 1
        labels = Array{String,2}(undef, 1, div(order - debord, pasord) + 1)
        ordprep = min(order + 2, 10)
        ordprep = order + 2
        println("preparation ordre $ordprep")
        par_u0 = missing
        while indc <= nbmaxtest
            println("u0=$u0")
            println("B=$B")
            println("alpha=$alpha")
            println("beta=$beta")
            @time res = solve(
                prob,
                nb_tau = n_tau,
                order = order,
                order_prep = ordprep,
                nb_t = nb,
                par_u0 = par_u0,
                dense = false,
            )
            par_u0 = res.par_u0
            sol = res[end]
            solref = getexactsol(res.par_u0.parphi, u0, t_max)
            println("solref=$solref")
            println("nb=$nb sol=$sol")
            diff = solref - sol
            x[indc] = 1.0 / nb
            println("nb=$nb dt=$(1.0/nb) normInf=$(norm(diff,Inf)) norm2=$(norm(diff))")
            resnorm = Float64(norm(diff, Inf))
            y[indc, ind] = (resnorm < 1) ? resnorm : NaN
            println("result=$y")
            println("log2(y)=$(log2.(y))")
            nb *= 2
            indc += 1
        end
        for i = debord:pasord:order
            labels[1, div((i - debord), pasord)+1] = " order=$i "
        end
        yv = y[:, 1:ind]
        p = Plots.plot(
            x,
            yv,
            xlabel = "Δt",
            xaxis = :log,
            ylabel = "error",
            yaxis = :log,
            legend = :bottomright,
            label = labels,
            marker = 2,
        )
        pp = Plots.plot(
            x,
            yv,
            xlabel = "Δt",
            xaxis = :log,
            ylabel = "error",
            yaxis = :log,
            legend = :bottomright,
            label = labels,
            marker = 2,
            bottom_margin = 30px,
            left_margin = 60px,
        )
        prec_v = precision(BigFloat)
        eps_v = convert(Float32, epsilon)
        Plots.savefig(
            p,
            "out/res9p_$(prec_v)_$(eps_v)_$(order)_$(ordprep)_$(n_tau)_exact.pdf",
        )
        Plots.savefig(
            p,
            "out/res9p_$(prec_v)_$(eps_v)_$(order)_$(ordprep)_$(n_tau)_exact.png",
        )
        Plots.savefig(
            pp,
            "out/res9pp_$(prec_v)_$(eps_v)_$(order)_$(ordprep)_$(n_tau)_exact.pdf",
        )
        Plots.savefig(
            pp,
            "out/res9pp_$(prec_v)_$(eps_v)_$(order)_$(ordprep)_$(n_tau)_exact.png",
        )
        ind += 1
    end
end

# testODESolver()

# for i=3:9
#     fctMain(2^i)
# end
fctmain(32, 512)


text:
using Crux
using Test
using POMDPs
import POMDPPolicies:FunctionPolicy
using Random
using Flux
using POMDPModels
using POMDPGym
using Distributions

mdp = GridWorldMDP()
pomdp = TigerPOMDP()

s = rand(initialstate(pomdp))
o = rand(initialobs(pomdp, s))

function POMDPs.gen(pomdp::TigerPOMDP, s, a)
    sp = rand(transition(pomdp, s, a ))
    o = rand(observation(pomdp, a, sp))
    r = reward(pomdp, s, a)
    (sp = sp, o=o, r=r)
end

π_explore = ϵGreedyPolicy(Crux.LinearDecaySchedule(1., 0.1, Int(100/2)), actions(mdp))

s1 = Sampler(mdp, PolicyParams(π=FunctionPolicy((s) -> [:up]), space=DiscreteSpace(actions(mdp))), max_steps=500)
s2 = Sampler(mdp, PolicyParams(π=DistributionPolicy(ObjectCategorical([:up, :down, :left, :right])), space=DiscreteSpace(actions(mdp)), π_explore = π_explore), max_steps=50, )
s3 = Sampler(pomdp, PolicyParams(π=FunctionPolicy((s)-> 0), space=DiscreteSpace(actions(pomdp))))

## Steps! function
data = steps!(s1, Nsteps = 10)
@test size(data[:s], 2) == 10
@test all([data[:a][:,i] == Bool[1,0,0,0] for i=1:length(data)])
@test sum(data[:done]) > 0 || s1.episode_length == 10

data = steps!(s2, Nsteps = 10, explore = true)
@test size(data[:s], 2) == 10
@test !all(data[:a] .== 1)
@test sum(data[:done]) > 0 || s2.episode_length == 10

data = steps!(s2, Nsteps = 100)
@test s2.episode_length < 100

data = steps!(s3, Nsteps = 100)
@test size(data[:s], 2) == 100

## episodes! function
data, eps = episodes!(s2, Neps=10, return_episodes=true)

@test length(eps) == 10

for e in eps
    @test e[1] < e[2]
    @test data[:done][e[2]] == 1f0 || e[2] - e[1] == s2.max_steps - 1
end

## positive returns only:
#TODO for GAIL

## Trajecory metrics
for e in eps
    ur =  undiscounted_return(data, e...)
    @test ur in [-10., 10., 0., 3., -5.]
    @test discounted_return(data, e..., discount(mdp)) ≈ ur*discount(mdp)^(e[2] - e[1])
    @test failure(data, e...) == (undiscounted_return(data, e...) < 0)
end

@test undiscounted_return(s1) < 3.
@test discounted_return(s1) < 3
@test failure(s1) < 1.

## Generalized Advantage Estimation
b = ExperienceBuffer(ContinuousSpace(2), DiscreteSpace(4, Int), 100, [:advantage, :return])
d = Dict(:s => 3*ones(2,3), :a => fill(1, 4, 3), :sp => 5*ones(2,3), :r => 6*ones(1,3), :done => ones(1,3), :return=>ones(1,3), :advantage=>ones(1,3))
push!(b, d)
push!(b, d)
#TODO: For policy gradient
fill_gae!(b, 1:5, ContinuousNetwork(Chain((s)->zeros(1, size(s, 2))), 1), 0.9f0, 0.7f0)
fill_returns!(b, 1:5, 0.7f0)

## Test sampling with a vector of mdps
mdps = [mdp, mdp, mdp]

samplers = Sampler(mdps, PolicyParams(π=FunctionPolicy((s) -> [:up]), space=DiscreteSpace(actions(mdp))))
@test length(samplers) == 3

data = steps!(samplers, Nsteps = 5)
data[:s]
@test size(data[:s],2) == 15

## Test episodes
mdp = GridWorldMDP()
s1 = Sampler(mdp, PolicyParams(π=FunctionPolicy((s) -> [:up]), space=DiscreteSpace(actions(mdp))), required_columns =[:episode_end], max_steps = 500,)
data, eps = episodes!(s1, Neps = 10, return_episodes = true,)
buffer = ExperienceBuffer(data)
eps2 = Crux.episodes(buffer)

@test all(eps .== eps2)

## Test the adversarial MDP stuff
amdp = AdditiveAdversarialMDP(ContinuumWorldMDP(), MvNormal([0,0], [1,1]))
G() = GaussianPolicy(ContinuousNetwork(Chain(Dense(4,2))), zeros(2))

s = Sampler(amdp, G(), adversary=PolicyParams(G()))
@test :x in s.required_columns

d = steps!(s, Nsteps=100)
size(d[:x]) == size(d[:a])


text:
export
    FixedTimeModel

"""
    abstract type FixedTimeModel{I,O} <: Model{I,O}

A dynamic model described only for fixed time delta.  Must implement
`get_initial`, `get_transition`, and `get_dt`.
These can depend on the current time.
"""
abstract type FixedTimeModel{I,J,O} <: Model{I,J,O} end

# get_initial(::FixedTimeModel) = error("Not implemented")
# get_transition(::FixedTimeModel) = error("Not implemented")
# get_dt(::FixedTimeModel) = error("Not implemented")

make_initial(m::FixedTimeModel, t=0) = get_initial(m, t)

function make_transition(m::FixedTimeModel, parenttimes, time)
    dt = get_dt(m)
    # Allow edges from the same time step, or intertemporal edges from the previous time step at distance dt
    if all(t -> time - t == dt || time == t, parenttimes)
        return get_transition(m, time)
    else
        error("make_transition called on FixedTimeModel with incorrect dt")
    end
end

# is_fixed(::FixedTimeModel) = true


text:
using Random
using LinearAlgebra, Krylov
using Plots
using RandomizedLasso
const RL = RandomizedLasso

## Compare "best" vs random preconditioner on random example
# Data
n, r = 1000, 500
A = randn(n, r)
A = A*A'
μ = 1e-2
xtrue = randn(n)
b = A*xtrue
D, V = eigen(A)

function true_preconditioner(k, D, V, μ)
    return (D[k+1] + μ)*V*Diagonal(1.0 ./ (D .+ μ))*V' + (I - V*V')
end
function cg_iters(A, b, P)
    _, stats = cg(A, b; history=true, M = P)
    !stats.solved && @warn "Did not correctly solve CG!!!"
    return length(stats.residuals)
end

nys_iters = zeros(length(ks))
true_iters = zeros(length(ks))
nopc_iters = ones(length(ks)) * cg_iters(A, b, I)
for (ind, (k, r)) in enumerate(zip(ks, rs))
    Anys = RL.NystromApprox(A, k, r)
    P = RL.RandomizedNystromPreconditionerInverse(Anys, μ)
    nys_iters[ind] = cg_iters(A, b, P)
    true_iters[ind] = cg_iters(A, b, true_preconditioner(k, D, V, μ))
    r % 100 == 0 && @info "Finished with r = $r"
end

plt_sketch_error = plot(ks[1:60],
    [nys_iters[1:60], true_iters[1:60], nopc_iters[1:60]],
    dpi=300,
    lw=3,
    label=["Nystrom Preconditioner" "True Preconditioner" "No Preconditioner"],
    ylabel="CG Iters",
    xlabel="Rank k",
    title="Convergence vs Preconditioner Rank",
    legend=:left
)
savefig(plt_sketch_error, joinpath(@__DIR__, "figs/cg_iters.pdf"))

## Real Dataset
using CSV, DataFrames
file = CSV.read("/Users/theodiamandis/Downloads/file7b5323e77330.csv", DataFrame)
M = Matrix(file)
b = M[:, 1]
A = M[:, 2:end]
m, n = size(A)
b = 1/m * A'*b
A = 1/m * A' * A

μ = 1e-3
_, stats = cg(A+μ*I, b; history=true)
npc_res = stats.residuals
nys_res = Vector{Float64}[]
rs = [10, 50, 100, 250, 500, 1000]
for r in rs
    k = Int(round(.9r))
    Anys = RL.NystromApprox(A, k, r)
    P = RL.RandomizedNystromPreconditionerInverse(Anys, μ)
    _, stats = cg(A+μ*I, b; history=true, M=P)
    push!(nys_res, stats.residuals)
    @info "Finished with r = $r"
end

plt_cg_real = plot(
    npc_res,
    dpi=300,
    lw=2,
    label="No Preconditioner",
    ylabel="residual",
    xlabel="iteration",
    title="Convergence of CG",
    legend=:topright,
    yaxis=:log
)
for (ind, y) in enumerate(nys_res)
    plot!(plt_cg_real, y, label="Nystrom, r = $(rs[ind])", lw=2)
end
savefig(plt_cg_real, joinpath(@__DIR__, "figs/cg_real_res.pdf"))
RL.deff(A, μ)


text:
module SqState

    const PROJECT_PATH = @__DIR__

    include("read.jl")
    include("utils.jl")
    include("polynomial.jl")
    include("wigner.jl")

end