text:
using ForwardDiff: value

"""
    AbstractObsScheme

Types inheriting from abstract type `AbstractObsScheme` define the scheme
according to which a stochastic process has been observed
"""
abstract type AbstractObsScheme end

"""
    PartObs <: AbstractObsScheme

Type acting as a flag for partially observed diffusions
"""
struct PartObs <: AbstractObsScheme end

"""
    FPT <: AbstractObsScheme

Observation scheme in which only first passage times are observed
"""
struct FPT <: AbstractObsScheme end

"""
    ParamUpdateType

Types inheriting from abstract type `ParamUpdateType` define the way in which
parameters are to be updated by the MCMC sampler
"""
abstract type ParamUpdateType end

"""
    ConjugateUpdt <: ParamUpdateType

Type acting as a flag for update from full conditional (conjugate to a prior)
"""
struct ConjugateUpdt <: ParamUpdateType end

"""
    MetropolisHastingsUpdt <: ParamUpdateType

Flag for performing update according to Metropolis Hastings step
"""
struct MetropolisHastingsUpdt <: ParamUpdateType end

"""
    setBlocking(𝔅::NoBlocking, ::Any, ::Any, ::Any, ::Any)

No blocking is to be done, do nothing
"""
setBlocking(𝔅::NoBlocking, ::Any, ::Any, ::Any, ::Any) = 𝔅

"""
    setBlocking(::ChequeredBlocking, blockingParams, P, WW, XX)

Blocking pattern is chosen to be a chequerboard.
"""
function setBlocking(::ChequeredBlocking, blockingParams, P, WW, XX)
    ChequeredBlocking(blockingParams..., P, WW, XX)
end

"""
    FPTInfo{S,T}

The struct
```
struct FPTInfo{S,T}
    condCoord::NTuple{N,S}
    upCrossing::NTuple{N,Bool}
    autoRenewed::NTuple{N,Bool}
    reset::NTuple{N,T}
end
```
serves as a container for the information regarding first passage time
observations. `condCoord` is an NTuple of coordinates that are conditioned on
the first passage time nature of the observations. `upCrossing` indicates
whether observations of the corresponding coordinate are up-crossings or
down-crossings. `autoRenewed` indicates whether process starts from the
renewed state (i.e. normally the process is unconstrained until it hits level
`reset` for the first time, however `autoRenewed` process is constrained on the
first passage time from the very beginnig). `reset` level is the level that
needs to be reached before the process starts to be conditioned on the first
passage time.
"""
struct FPTInfo{S,T,N}
    condCoord::NTuple{N,S}
    upCrossing::NTuple{N,Bool}
    autoRenewed::NTuple{N,Bool}
    reset::NTuple{N,T}

    FPTInfo(condCoord::NTuple{N,S}, upCrossing::NTuple{N,Bool},
            reset::NTuple{N,T},
            autoRenewed::NTuple{N,Bool} = Tuple(fill(false,length(condCoord)))
            ) where {S,T,N} = new{S,T,N}(condCoord, upCrossing,
                                         autoRenewed, reset)
end

"""
    checkSingleCoordFpt(XXᵒ, c, cidx, fpt)

Verify whether coordinate `c` (with index number `cidx`) of path `XXᵒ`.yy
adheres to the first passage time observation scheme specified by the object
`fpt`.
"""
function checkSingleCoordFpt(XXᵒ, c, cidx, fpt)
    k = length(XXᵒ.yy)
    thrsd = XXᵒ.yy[end][c]
    renewed = fpt.autoRenewed[cidx]
    s = fpt.upCrossing[cidx] ? 1 : -1
    for i in 1:k
        if !renewed && (s*XXᵒ.yy[i][c] <= s*fpt.reset[cidx])
            renewed = true
        elseif renewed && (s*XXᵒ.yy[i][c] > s*thrsd)
            return false
        end
    end
    return renewed
end

"""
    checkFpt(::PartObs, XXᵒ, fpt)

First passage time constrains are automatically satisfied for the partially
observed scheme
"""
checkFpt(::PartObs, XXᵒ, fpt) = true

"""
    checkFpt(::FPT, XXᵒ, fpt)

Verify whether path `XXᵒ`.yy adheres to the first passage time observation
scheme specified by the object `fpt`.
"""
function checkFpt(::FPT, XXᵒ, fpt)
    for (cidx, c) in enumerate(fpt.condCoord)
        if !checkSingleCoordFpt(XXᵒ, c, cidx, fpt)
            return false
        end
    end
    return true
end

"""
    checkFullPathFpt(::PartObs, ::Any, ::Any, ::Any)

First passage time constrains are automatically satisfied for the partially
observed scheme
"""
checkFullPathFpt(::PartObs, ::Any, ::Any, ::Any) = true

"""
    checkFullPathFpt(::PartObs, XXᵒ, m, fpt)

Verify whether all paths in the range `iRange`, i.e. `XXᵒ`[i].yy, i in `iRange`
adhere to the first passage time observation scheme specified by the object
`fpt`
"""
function checkFullPathFpt(::FPT, XXᵒ, iRange, fpt)
    for i in iRange
        if !checkFpt(FPT(), XXᵒ[i], fpt[i])
            return false
        end
    end
    return true
end

"""
    findProposalLaw(xx, tt, P˟, P̃, Ls, Σs; dt=1/5000, timeChange=true,
                    solver::ST=Ralston3())

Initialise the object with proposal law and all the necessary containers needed
for the simulation of the guided proposals
"""
function findProposalLaw(::Type{K}, xx, tt, P˟, P̃, Ls, Σs, τ; dt=1/5000,
                         solver::ST=Ralston3(),
                         changePt::ODEChangePt=NoChangePt()) where {K,ST}
    m = length(xx) - 1
    P = Array{ContinuousTimeProcess,1}(undef,m)
    for i in m:-1:1
        numPts = Int64(ceil((tt[i+1]-tt[i])/dt))+1
        t = τ(tt[i], tt[i+1]).( range(tt[i], stop=tt[i+1], length=numPts) )
        P[i] = ( (i==m) ? GuidPropBridge(K, t, P˟, P̃[i], Ls[i], xx[i+1], Σs[i];
                                         changePt=changePt, solver=ST()) :
                          GuidPropBridge(K, t, P˟, P̃[i], Ls[i], xx[i+1], Σs[i],
                                         P[i+1].H[1], P[i+1].Hν[1], P[i+1].c[1];
                                         changePt=changePt, solver=ST()) )
    end
    P
end

"""
    initialise(::ObsScheme, P, m, y::StartingPtPrior{T}, ::S, fpt)

Initialise the workspace for MCMC algorithm. Initialises containers for driving
Wiener processes `WWᵒ` & `WW`, for diffusion processes `XXᵒ` & `XX`, for
diffusion Law `Pᵒ` (parametetrised by proposal parameters) and defines the type
of Wiener process `Wnr`.
"""
function initialise(::ObsScheme, P, m, yPr::StartingPtPrior{T}, ::S,
                    fpt) where {ObsScheme <: AbstractObsScheme,T,S}
    y = startPt(yPr)
    Pᵒ = deepcopy(P)
    TW = typeof(sample([0], Wiener{S}()))
    TX = typeof(SamplePath([], zeros(T, 0)))
    XXᵒ = Vector{TX}(undef,m)
    WWᵒ = Vector{TW}(undef,m)
    Wnr = Wiener{S}()
    for i in 1:m
        WWᵒ[i] = Bridge.samplepath(P[i].tt, zero(S))
        sample!(WWᵒ[i], Wnr)
        XXᵒ[i] = solve(Euler(), y, WWᵒ[i], P[i])
        while !checkFpt(ObsScheme(), XXᵒ[i], fpt[i])
            sample!(WWᵒ[i], Wnr)
            solve!(Euler(), XXᵒ[i], y, WWᵒ[i], P[i])
        end
        y = XXᵒ[i].yy[end]
    end
    y = startPt(yPr)
    ll = logpdf(yPr, y)
    ll += pathLogLikhd(ObsScheme(), XXᵒ, P, 1:m, fpt, skipFPT=true)
    ll += lobslikelihood(P[1], y)

    XX = deepcopy(XXᵒ)
    WW = deepcopy(WWᵒ)
    # needed for proper initialisation of the Crank-Nicolson scheme
    yPr = invStartPt(y, yPr, P[1])

    Wnr, WWᵒ, WW, XXᵒ, XX, Pᵒ, ll, yPr
end

"""
    savePath!(Paths, XX, saveMe, skip)

If `saveMe` flag is true, then save the entire path spanning all segments in
`XX`. Only 1 in  every `skip` points is saved to reduce storage space.
"""
function savePath!(Paths, XX, saveMe, skip)
    if saveMe
        push!(Paths, collect(Iterators.flatten(XX[i].yy[1:skip:end-1]
                                               for i in 1:length(XX))))
    end
end

"""
    acceptSample(logThreshold, verbose=false)

Make a random MCMC decision for whether to accept a sample or reject it.
"""
function acceptSample(logThreshold, verbose=false)
    if rand(Exponential(1.0)) > -logThreshold # Reject if NaN
        verbose && print("\t ✓\n")
        return true
    else
        verbose && print("\t .\n")
        return false
    end
end

"""
    solveBackRec!(P, solver::ST=Ralston3()) where ST

Solve backward recursion to find H, Hν, c and Q, which together define r̃(t,x)
and p̃(x, 𝓓) under the auxiliary law, when no blocking is done
"""
function solveBackRec!(::NoBlocking, P, solver::ST=Ralston3()) where ST
    m = length(P)
    gpupdate!(P[m]; solver=ST())
    for i in (m-1):-1:1
        gpupdate!(P[i], P[i+1].H[1], P[i+1].Hν[1], P[i+1].c[1]; solver=ST())
    end
end

"""
    solveBackRec!(P, solver::ST=Ralston3()) where ST

Solve backward recursion to find H, Hν, c and Q, which together define r̃(t,x)
and p̃(x, 𝓓) under the auxiliary law, when blocking is done
"""
function solveBackRec!(𝔅::BlockingSchedule, P, solver::ST=Ralston3()) where ST
    for block in reverse(𝔅.blocks[𝔅.idx])
        gpupdate!(P[block[end]]; solver=ST())
        for i in reverse(block[1:end-1])
            gpupdate!(P[i], P[i+1].H[1], P[i+1].Hν[1], P[i+1].c[1]; solver=ST())
        end
    end
end

"""
    proposalStartPt(::BlockingSchedule, ::Val{1}, ::Any, yPr, P, ρ)

Set a new starting point for the proposal path when sampling the first block in
a blocking scheme.

...
# Arguments
- `::BlockingSchedule`: indicator that a blocking scheme is used
- `::Val{1}`: indicator that it's the first block, so starting point needs updating
- `yPr`: prior over the starting point
- `P`: diffusion law
- `ρ`: memory parameter in the Crank-Nicolson scheme
...
"""
function proposalStartPt(::BlockingSchedule, ::Val{1}, ::Any, yPr, P, ρ)
    proposalStartPt(NoBlocking(), nothing, nothing, yPr, P, ρ)
end

"""
    proposalStartPt(::BlockingSchedule, ::Any, y₀, yPr, ::Any, ::Any)

Default behaviour of dealing with a starting point in the blocking scheme is
to do nothing
"""
function proposalStartPt(::BlockingSchedule, ::Any, y₀, yPr, ::Any, ::Any)
    y₀, yPr
end

"""
    proposalStartPt(::NoBlocking, ::Any, y₀, yPr, P, ρ)

Set a new starting point for the proposal path when no blocking is done
...
# Arguments
- `::NoBlocking`: indicator that no blocking is done
- `yPr`: prior over the starting point
- `P`: diffusion law
- `ρ`: memory parameter in the Crank-Nicolson scheme
...
"""
function proposalStartPt(::NoBlocking, ::Any, ::Any, yPr, P, ρ)
    yPrᵒ = rand(yPr, ρ)
    y = startPt(yPrᵒ, P)
    y, yPrᵒ
end

"""
    printInfo(verbose::Bool, it::Integer, ll, llᵒ, msg="update")

Print information to the console about current likelihood values

...
# Arguments
- `verbose`: flag for whether to print anything at all
- `it`: iteration of the Markov chain
- `ll`: likelihood of the previous, accepted sample
- `llᵒ`: likelihood of the proposal sample
- `msg`: message to start with
...
"""
function printInfo(verbose::Bool, it::Integer, ll, llᵒ, msg="update")
    verbose && print(msg, ": ", it, " ll ", round(ll, digits=3), " ",
                     round(llᵒ, digits=3), " diff_ll: ", round(llᵒ-ll,digits=3))
end

"""
    pathLogLikhd(::ObsScheme, XX, P, iRange, fpt; skipFPT=false)

Compute likelihood for path `XX` to be observed under `P`. Only segments with
index numbers in `iRange` are considered. `fpt` contains relevant info about
checks regarding adherence to first passage time pattern. `skipFPT` if set to
`true` can skip the step of checking adherence to fpt pattern (used for
conjugate updates, or any updates that keep `XX` unchanged)
"""
function pathLogLikhd(::ObsScheme, XX, P, iRange, fpt; skipFPT=false
                      ) where ObsScheme <: AbstractObsScheme
    ll = 0.0
    for i in iRange
        ll += llikelihood(LeftRule(), XX[i], P[i])
    end
    !skipFPT && (ll = checkFullPathFpt(ObsScheme(), XX, iRange, fpt) ? ll : -Inf)
    ll
end

"""
    swap!(A, Aᵒ, iRange)

Swap contents between containers A & Aᵒ in the index range iRange
"""
function swap!(A, Aᵒ, iRange)
    for i in iRange
        A[i], Aᵒ[i] = Aᵒ[i], A[i]
    end
end

"""
    swap!(A, Aᵒ, B, Bᵒ, iRange)

Swap contents between containers A & Aᵒ in the index range iRange, do the same
for containers B & Bᵒ
"""
function swap!(A, Aᵒ, B, Bᵒ, iRange)
    swap!(A, Aᵒ, iRange)
    swap!(B, Bᵒ, iRange)
end

"""
    crankNicolson!(yᵒ, y, ρ)

Preconditioned Crank Nicolson update with memory parameter `ρ`, previous vector
`y` and new vector `yᵒ`
"""
crankNicolson!(yᵒ, y, ρ) = (yᵒ .= √(1-ρ)*yᵒ + √(ρ)*y)

"""
    sampleSegment!(i, Wnr, WW, WWᵒ, P, y, XX, ρ)

Sample `i`th path segment using preconditioned Crank-Nicolson scheme
...
# Arguments
- `i`: index of the segment to be sampled
- `Wnr`: type of the Wiener process
- `WW`: containers with old Wiener paths
- `WWᵒ`: containers where proposal Wiener paths will be stored
- `P`: laws of the diffusion to be sampled
- `y`: starting point of the segment
- `XX`: containers for proposal diffusion path
- `ρ`: memory parameter for the Crank-Nicolson scheme
...
"""
function sampleSegment!(i, Wnr, WW, WWᵒ, P, y, XX, ρ)
    sample!(WWᵒ[i], Wnr)
    crankNicolson!(WWᵒ[i].yy, WW[i].yy, ρ)
    solve!(Euler(), XX[i], y, WWᵒ[i], P[i])
    XX[i].yy[end]
end

"""
    sampleSegments!(iRange, Wnr, WW, WWᵒ, P, y, XX, ρ)

Sample paths segments in index range `iRange` using preconditioned
Crank-Nicolson scheme
...
# Arguments
- `iRange`: range of indices of the segments that need to be sampled
- `Wnr`: type of the Wiener process
- `WW`: containers with old Wiener paths
- `WWᵒ`: containers where proposal Wiener paths will be stored
- `P`: laws of the diffusion to be sampled
- `y`: starting point of the segment
- `XX`: containers for proposal diffusion path
- `ρ`: memory parameter for the Crank-Nicolson scheme
...
"""
function sampleSegments!(iRange, Wnr, WW, WWᵒ, P, y, XX, ρ)
    for i in iRange
        y = sampleSegment!(i, Wnr, WW, WWᵒ, P, y, XX, ρ)
    end
end

"""
    impute!(::ObsScheme, 𝔅::NoBlocking, Wnr, yPr, WWᵒ, WW, XXᵒ, XX, P, ll, fpt;
            ρ=0.0, verbose=false, it=NaN, headStart=false) where
            ObsScheme <: AbstractObsScheme -> acceptedLogLikhd, acceptDecision

Imputation step of the MCMC scheme (without blocking).
...
# Arguments
- `::ObsScheme`: observation scheme---first-passage time or partial observations
- `Wnr`: type of the Wiener process
- `yPr`: prior over the starting point of the diffusion path
- `WWᵒ`: containers for proposal Wiener paths
- `WW`: containers with old Wiener paths
- `XXᵒ`: containers for proposal diffusion paths
- `XX`: containers with old diffusion paths
- `P`: laws of the diffusion path (proposal and target)
- `11`: log-likelihood of the old (previously accepted) diffusion path
- `fpt`: info about first-passage time conditioning
- `ρ`: memory parameter for the Crank-Nicolson scheme
- `verbose`: whether to print updates info while sampling
- `it`: iteration index of the MCMC algorithm
- `headStart`: flag for whether to 'ease into' fpt conditions
...
"""
function impute!(::ObsScheme, 𝔅::NoBlocking, Wnr, yPr, WWᵒ, WW, XXᵒ, XX, P, ll,
                 fpt; ρ=0.0, verbose=false, it=NaN, headStart=false,
                 solver::ST=Ralston3()) where
                 {ObsScheme <: AbstractObsScheme, ST}
    # sample proposal starting point
    yᵒ, yPrᵒ = proposalStartPt(𝔅, nothing, nothing, yPr, P[1], ρ)

    # sample proposal path
    m = length(WWᵒ)
    yᵗᵉᵐᵖ = copy(yᵒ)
    for i in 1:m
        sampleSegment!(i, Wnr, WW, WWᵒ, P, yᵗᵉᵐᵖ, XXᵒ, ρ)
        if headStart
            while !checkFpt(ObsScheme(), XXᵒ[i], fpt[i])
                sampleSegment!(i, Wnr, WW, WWᵒ, P, yᵗᵉᵐᵖ, XXᵒ, ρ)
            end
        end
        yᵗᵉᵐᵖ = XXᵒ[i].yy[end]
    end

    llᵒ = logpdf(yPrᵒ, yᵒ)
    llᵒ += pathLogLikhd(ObsScheme(), XXᵒ, P, 1:m, fpt)
    llᵒ += lobslikelihood(P[1], yᵒ)

    printInfo(verbose, it, value(ll), value(llᵒ), "impute")

    if acceptSample(llᵒ-ll, verbose)
        swap!(XX, XXᵒ, WW, WWᵒ, 1:m)
        return llᵒ, true, 𝔅, yPrᵒ
    else
        return ll, false, 𝔅, yPr
    end
end

#NOTE deprecated
"""
    swapXX!(𝔅::ChequeredBlocking, XX)

Swap containers between `XX` and `𝔅.XX`
"""
function swapXX!(𝔅::BlockingSchedule, XX)
    for block in 𝔅.blocks[𝔅.idx]
        swap!(XX, 𝔅.XX, block)
    end
end

#NOTE deprecated
"""
    swapXX!(𝔅::NoBlocking, XX)

nothing to do
"""
swapXX!(𝔅::NoBlocking, XX) = nothing

"""
    noiseFromPath!(𝔅::BlockingSchedule, XX, WW, P)

Compute driving Wiener noise `WW` from path `XX` drawn under law `P`
"""
function noiseFromPath!(𝔅::BlockingSchedule, XX, WW, P)
    for block in 𝔅.blocks[𝔅.idx]
        for i in block
            invSolve!(Euler(), XX[i], WW[i], P[i])
        end
    end
end

"""
    startPtLogPdf(::Val{1}, yPr::StartingPtPrior, y)

Compute the log-likelihood contribution of the starting point for a given prior
under a blocking scheme (intended to be used with a first block only)
"""
startPtLogPdf(::Val{1}, yPr::StartingPtPrior, y) = logpdf(yPr, y)

"""
    startPtLogPdf(::Any, yPr::StartingPtPrior, y)

Default contribution to log-likelihood from the startin point under blocking
"""
startPtLogPdf(::Any, yPr::StartingPtPrior, y) = 0.0

#NOTE deprecated
"""
    impute!(::ObsScheme, 𝔅::ChequeredBlocking, Wnr, y, WWᵒ, WW, XXᵒ, XX, P, ll,
            fpt; ρ=0.0, verbose=false, it=NaN, headStart=false) where
            ObsScheme <: AbstractObsScheme -> acceptedLogLikhd, acceptDecision

Imputation step of the MCMC scheme (without blocking).
...
# Arguments
- `::ObsScheme`: observation scheme---first-passage time or partial observations
- `𝔅`: object with relevant information about blocking
- `Wnr`: type of the Wiener process
- `yPr`: prior over the starting point of the diffusion path
- `WWᵒ`: containers for proposal Wiener paths
- `WW`: containers with old Wiener paths
- `XXᵒ`: containers for proposal diffusion paths
- `XX`: containers with old diffusion paths
- `P`: laws of the diffusion path (proposal and target)
- `11`: log-likelihood of the old (previously accepted) diffusion path
- `fpt`: info about first-passage time conditioning
- `ρ`: memory parameter for the Crank-Nicolson scheme
- `verbose`: whether to print updates info while sampling
- `it`: iteration index of the MCMC algorithm
- `headStart`: flag for whether to 'ease into' fpt conditions
...
"""
function impute!_deprecated(::ObsScheme, 𝔅::ChequeredBlocking, Wnr, yPr, WWᵒ, WW, XXᵒ, XX,
                 P, ll, fpt; ρ=0.0, verbose=false, it=NaN, headStart=false,
                 solver::ST=Ralston3()) where
                 {ObsScheme <: AbstractObsScheme, ST}
    θ = params(P[1].Target)             # current parameter
    𝔅 = next(𝔅, XX, θ)
    solveBackRec!(𝔅, 𝔅.P, ST())         # compute (H, Hν, c) for given blocks

    swapXX!(𝔅, XX)                      # move current path to object 𝔅
    noiseFromPath!(𝔅, 𝔅.XX, 𝔅.WW, 𝔅.P) # find noise WW that generates XX under 𝔅

    # compute white noise generating starting point under 𝔅
    yPr𝔅 = invStartPt(𝔅.XX[1].yy[1], yPr, 𝔅.P[1])

    for (blockIdx, block) in enumerate(𝔅.blocks[𝔅.idx])
        blockFlag = Val{block[1]}()
        y = 𝔅.XX[block[1]].yy[1]       # current starting point

        # set the starting point for the block
        yᵒ, yPrᵒ = proposalStartPt(𝔅, blockFlag, y, yPr𝔅, 𝔅.P[block[1]], ρ)

        # sample path in block
        sampleSegments!(block, Wnr, 𝔅.WW, 𝔅.WWᵒ, 𝔅.P, yᵒ, 𝔅.XXᵒ, ρ)
        setEndPtManually!(𝔅, blockIdx, block)

        # loglikelihoods
        llᵒ = startPtLogPdf(blockFlag, yPrᵒ, yᵒ)
        llᵒ += pathLogLikhd(ObsScheme(), 𝔅.XXᵒ, 𝔅.P, block, fpt)
        llᵒ += lobslikelihood(𝔅.P[block[1]], yᵒ)

        llPrev = startPtLogPdf(blockFlag, yPr𝔅, y)
        llPrev += pathLogLikhd(ObsScheme(), 𝔅.XX, 𝔅.P, block, fpt; skipFPT=true)
        llPrev += lobslikelihood(𝔅.P[block[1]], y)

        printInfo(verbose, it, value(llPrev), value(llᵒ), "impute")
        if acceptSample(llᵒ-llPrev, verbose)
            swap!(𝔅.XX, 𝔅.XXᵒ, block)
            registerAccpt!(𝔅, blockIdx, true)
            yPr𝔅 = yPrᵒ # can do something non-trivial only for the first block
        else
            registerAccpt!(𝔅, blockIdx, false)
        end
    end
    swapXX!(𝔅, XX) # move accepted path from object 𝔅 to general container XX
    noiseFromPath!(𝔅, XX, WW, P) # compute noise WW that generated XX under law P
    # compute white noise generating starting point under P
    y = XX[1].yy[1]
    yPr = invStartPt(y, yPr𝔅, P[1])

    ll = logpdf(yPr, y) # starting point contribution
    ll += pathLogLikhd(ObsScheme(), XX, P, 1:length(P), fpt; skipFPT=true)
    ll += lobslikelihood(P[1], y)

    # acceptance indicator does not matter for sampling with blocking
    return ll, true, 𝔅, yPr
end

"""
    impute!(::ObsScheme, 𝔅::ChequeredBlocking, Wnr, y, WWᵒ, WW, XXᵒ, XX, P, ll,
            fpt; ρ=0.0, verbose=false, it=NaN, headStart=false) where
            ObsScheme <: AbstractObsScheme -> acceptedLogLikhd, acceptDecision

Imputation step of the MCMC scheme (without blocking).
...
# Arguments
- `::ObsScheme`: observation scheme---first-passage time or partial observations
- `𝔅`: object with relevant information about blocking
- `Wnr`: type of the Wiener process
- `yPr`: prior over the starting point of the diffusion path
- `WWᵒ`: containers for proposal Wiener paths
- `WW`: containers with old Wiener paths
- `XXᵒ`: containers for proposal diffusion paths
- `XX`: containers with old diffusion paths
- `P`: laws of the diffusion path (proposal and target)
- `11`: log-likelihood of the old (previously accepted) diffusion path
- `fpt`: info about first-passage time conditioning
- `ρ`: memory parameter for the Crank-Nicolson scheme
- `verbose`: whether to print updates info while sampling
- `it`: iteration index of the MCMC algorithm
- `headStart`: flag for whether to 'ease into' fpt conditions
...
"""
function impute!(::ObsScheme, 𝔅::ChequeredBlocking, Wnr, yPr, WWᵒ, WW, XXᵒ, XX,
                 P, ll, fpt; ρ=0.0, verbose=false, it=NaN, headStart=false,
                 solver::ST=Ralston3()
                 ) where {ObsScheme <: AbstractObsScheme, ST}
    θ = params(𝔅.P[1].Target)             # current parameter
    𝔅 = next(𝔅, 𝔅.XX, θ)
    solveBackRec!(𝔅, 𝔅.P, ST())         # compute (H, Hν, c) for given blocks
    noiseFromPath!(𝔅, 𝔅.XX, 𝔅.WW, 𝔅.P) # find noise WW that generates XX under 𝔅.P

    # compute white noise generating starting point under 𝔅
    yPr = invStartPt(𝔅.XX[1].yy[1], yPr, 𝔅.P[1])

    ll_total = 0.0
    for (blockIdx, block) in enumerate(𝔅.blocks[𝔅.idx])
        blockFlag = Val{block[1]}()
        y = 𝔅.XX[block[1]].yy[1]       # accepted starting point

        # proposal starting point for the block (can be non-y only for the first block)
        yᵒ, yPrᵒ = proposalStartPt(𝔅, blockFlag, y, yPr, 𝔅.P[block[1]], ρ)

        # sample path in block
        sampleSegments!(block, Wnr, 𝔅.WW, 𝔅.WWᵒ, 𝔅.P , yᵒ, 𝔅.XXᵒ, ρ)
        setEndPtManually!(𝔅, blockIdx, block)

        # starting point, path and observations contribution
        llᵒ = startPtLogPdf(blockFlag, yPrᵒ, yᵒ)
        llᵒ += pathLogLikhd(ObsScheme(), 𝔅.XXᵒ, 𝔅.P, block, fpt)
        llᵒ += lobslikelihood(𝔅.P[block[1]], yᵒ)

        llPrev = startPtLogPdf(blockFlag, yPr, y)
        llPrev += pathLogLikhd(ObsScheme(), 𝔅.XX, 𝔅.P, block, fpt; skipFPT=true)
        llPrev += lobslikelihood(𝔅.P[block[1]], y)

        printInfo(verbose, it, value(llPrev), value(llᵒ), "impute")
        if acceptSample(llᵒ-llPrev, verbose)
            swap!(𝔅.XX, 𝔅.XXᵒ, block)
            registerAccpt!(𝔅, blockIdx, true)
            yPr = yPrᵒ # can do something non-trivial only for the first block
            ll_total += llᵒ
        else
            registerAccpt!(𝔅, blockIdx, false)
            ll_total += llPrev
        end
    end
    # acceptance indicator does not matter for sampling with blocking
    return ll_total, true, 𝔅, yPr
end

"""
    updateLaws!(Ps, θᵒ)

Set new parameter `θᵒ` for the laws in vector `Ps`
"""
function updateLaws!(Ps, θᵒ)
    m = length(Ps)
    for i in 1:m
        Ps[i] = GuidPropBridge(Ps[i], θᵒ)
    end
end

"""
    updateTargetLaws!(𝔅::NoBlocking, θᵒ)

Nothing to do
"""
updateTargetLaws!(𝔅::NoBlocking, θᵒ) = nothing

"""
    updateTargetLaws!(𝔅::BlockingSchedule, θᵒ)

Set new parameter `θᵒ` for the target laws in blocking object `𝔅`
"""
function updateTargetLaws!(𝔅::BlockingSchedule, θᵒ)
    for block in 𝔅.blocks[𝔅.idx]
        for i in block
            𝔅.P[i] = GuidPropBridge(𝔅.P[i], θᵒ)
        end
    end
end

"""
    updateProposalLaws!(𝔅::BlockingSchedule, θᵒ)

Set new parameter `θᵒ` for the proposal laws inside blocking object `𝔅`
"""
function updateProposalLaws!(𝔅::BlockingSchedule, θᵒ)
    for block in 𝔅.blocks[𝔅.idx]
        for i in block
            𝔅.Pᵒ[i] = GuidPropBridge(𝔅.Pᵒ[i], θᵒ)
        end
    end
end

"""
    findPathFromWiener!(XX, y, WW, P, iRange)

Find path `XX` (that starts from `y`) that is generated under law `P` from the
Wiener process `WW`. Only segments with indices in range `iRange` are considered
"""
function findPathFromWiener!(XX, y, WW, P, iRange)
    for i in iRange
        solve!(Euler(), XX[i], y, WW[i], P[i])
        y = XX[i].yy[end]
    end
end

"""
    priorKernelContrib(tKern, priors, θ, θᵒ)

Contribution to the log-likelihood ratio from transition kernel `tKernel` and
`priors`.
"""
function priorKernelContrib(tKern, priors, θ, θᵒ)
    llr = logpdf(tKern, θᵒ, θ) - logpdf(tKern, θ, θᵒ)
    for prior in priors
        llr += logpdf(prior, θᵒ) - logpdf(prior, θ)
    end
    llr
end

"""
    setEndPtManually!(𝔅::BlockingSchedule, blockIdx, block)

Manually set the end-point of the proposal path under blocking so that it agrees
with the end-point of the previously accepted path. If it is the last block,
then do nothing
"""
function setEndPtManually!(𝔅::BlockingSchedule, blockIdx, block)
    if blockIdx < length(𝔅.blocks[𝔅.idx])
        𝔅.XXᵒ[block[end]].yy[end] = 𝔅.XX[block[end]].yy[end]
    end
end

"""
    updateParam!(::ObsScheme, ::MetropolisHastingsUpdt, tKern, θ, ::UpdtIdx,
                 yPr, WW, Pᵒ, P, XXᵒ, XX, ll, prior, fpt, recomputeODEs;
                 solver::ST=Ralston3(), verbose=false,
                 it=NaN) where {ObsScheme <: AbstractObsScheme, ST, UpdtIdx}
                 -> acceptedLogLikhd, acceptDecision
Update parameters
...
# Arguments
- `::ObsScheme`: observation scheme---first-passage time or partial observations
- `::MetropolisHastingsUpdt()`: type of the parameter update
- `tKern`: transition kernel
- `θ`: current value of the parameter
- `updtIdx`: object declaring indices of the updated parameter
- `yPr`: prior over the starting point of the diffusion path
- `WW`: containers with Wiener paths
- `Pᵒ`: container for the laws of the diffusion path with new parametrisation
- `P`: laws of the diffusion path with old parametrisation
- `XXᵒ`: containers for proposal diffusion paths
- `XX`: containers with old diffusion paths
- `11`: likelihood of the old (previously accepted) parametrisation
- `priors`: list of priors
- `fpt`: info about first-passage time conditioning
- `recomputeODEs`: whether auxiliary law depends on the updated params
- `verbose`: whether to print updates info while sampling
- `it`: iteration index of the MCMC algorithm
...
"""
function updateParam!(::ObsScheme, ::MetropolisHastingsUpdt, 𝔅::NoBlocking,
                      tKern, θ, ::UpdtIdx, yPr, WW, Pᵒ, P, XXᵒ, XX, ll, priors,
                      fpt, recomputeODEs; solver::ST=Ralston3(), verbose=false,
                      it=NaN) where {ObsScheme <: AbstractObsScheme, ST, UpdtIdx}
    m = length(WW)
    θᵒ = rand(tKern, θ, UpdtIdx())               # sample new parameter
    updateLaws!(Pᵒ, θᵒ)
    recomputeODEs && solveBackRec!(NoBlocking(), Pᵒ, ST()) # compute (H, Hν, c)

    # find white noise which for a given θᵒ gives a correct starting point
    y = XX[1].yy[1]
    yPrᵒ = invStartPt(y, yPr, Pᵒ[1])

    findPathFromWiener!(XXᵒ, y, WW, Pᵒ, 1:m)

    llᵒ = logpdf(yPrᵒ, y)
    llᵒ += pathLogLikhd(ObsScheme(), XXᵒ, Pᵒ, 1:m, fpt)
    llᵒ += lobslikelihood(Pᵒ[1], y)

    printInfo(verbose, it, ll, llᵒ)

    llr = ( llᵒ - ll + priorKernelContrib(tKern, priors, θ, θᵒ))

    # Accept / reject
    if acceptSample(llr, verbose)
        swap!(XX, XXᵒ, P, Pᵒ, 1:m)
        return llᵒ, true, θᵒ, yPrᵒ
    else
        return ll, false, θ, yPr
    end
end

"""
    updateParam!(::ObsScheme, ::MetropolisHastingsUpdt, tKern, θ, ::UpdtIdx,
                 yPr, WW, Pᵒ, P, XXᵒ, XX, ll, prior, fpt, recomputeODEs;
                 solver::ST=Ralston3(), verbose=false,
                 it=NaN) where {ObsScheme <: AbstractObsScheme, ST, UpdtIdx}
                 -> acceptedLogLikhd, acceptDecision
Update parameters
...
# Arguments
- `::ObsScheme`: observation scheme---first-passage time or partial observations
- `::MetropolisHastingsUpdt()`: type of the parameter update
- `tKern`: transition kernel
- `θ`: current value of the parameter
- `updtIdx`: object declaring indices of the updated parameter
- `yPr`: prior over the starting point of the diffusion path
- `WW`: containers with Wiener paths
- `Pᵒ`: container for the laws of the diffusion path with new parametrisation
- `P`: laws of the diffusion path with old parametrisation
- `XXᵒ`: containers for proposal diffusion paths
- `XX`: containers with old diffusion paths
- `11`: likelihood of the old (previously accepted) parametrisation
- `priors`: list of priors
- `fpt`: info about first-passage time conditioning
- `recomputeODEs`: whether auxiliary law depends on the updated params
- `verbose`: whether to print updates info while sampling
- `it`: iteration index of the MCMC algorithm
...
"""
function updateParam!(::ObsScheme, ::MetropolisHastingsUpdt,
                      𝔅::ChequeredBlocking, tKern, θ, ::UpdtIdx,
                      yPr, WW, Pᵒ, P, XXᵒ, XX, ll, priors, fpt, recomputeODEs;
                      solver::ST=Ralston3(), verbose=false,
                      it=NaN) where {ObsScheme <: AbstractObsScheme, ST, UpdtIdx}
    m = length(WW)
    θᵒ = rand(tKern, θ, UpdtIdx())               # sample new parameter
    updateProposalLaws!(𝔅, θᵒ)                   # update law `Pᵒ` accordingly
    solveBackRec!(𝔅, 𝔅.Pᵒ, ST())                 # compute (H, Hν, c)

    llᵒ = logpdf(yPr, 𝔅.XX[1].yy[1])
    for (blockIdx, block) in enumerate(𝔅.blocks[𝔅.idx])
        y = 𝔅.XX[block[1]].yy[1]
        findPathFromWiener!(𝔅.XXᵒ, y, 𝔅.WW, 𝔅.Pᵒ, block)
        setEndPtManually!(𝔅, blockIdx, block)

        # Compute log-likelihood ratio
        llᵒ += pathLogLikhd(ObsScheme(), 𝔅.XXᵒ, 𝔅.Pᵒ, block, fpt)
        llᵒ += lobslikelihood(𝔅.Pᵒ[block[1]], y)
    end
    printInfo(verbose, it, ll, llᵒ)

    llr = ( llᵒ - ll + priorKernelContrib(tKern, priors, θ, θᵒ))

    # Accept / reject
    if acceptSample(llr, verbose)
        swap!(𝔅.XX, 𝔅.XXᵒ, 𝔅.P, 𝔅.Pᵒ, 1:m)
        return llᵒ, true, θᵒ, yPr
    else
        return ll, false, θ, yPr
    end
end

fetchTargetLaw(𝔅::NoBlocking, P) = P[1].Target

fetchTargetLaw(𝔅::BlockingSchedule, P) = 𝔅.P[1].Target

"""
    updateParam!(::PartObs, ::ConjugateUpdt, tKern, θ, ::UpdtIdx, yPr, WW, Pᵒ,
                 P, XXᵒ, XX, ll, priors, fpt, recomputeODEs;
                 solver::ST=Ralston3(), verbose=false, it=NaN
                 ) -> acceptedLogLikhd, acceptDecision
Update parameters
see the definition of  updateParam!(…, ::MetropolisHastingsUpdt, …) for the
explanation of the arguments.
"""
function updateParam!(::ObsScheme, ::ConjugateUpdt, 𝔅::NoBlocking,
                      tKern, θ, ::UpdtIdx, yPr, WW, Pᵒ, P, XXᵒ, XX, ll, priors,
                      fpt, recomputeODEs; solver::ST=Ralston3(), verbose=false,
                      it=NaN) where {ObsScheme <: AbstractObsScheme, ST, UpdtIdx}
    m = length(P)
    ϑ = conjugateDraw(θ, XX, P[1].Target, priors[1], UpdtIdx())   # sample new parameter
    θᵒ = moveToProperPlace(ϑ, θ, UpdtIdx())     # align so that dimensions agree

    updateLaws!(P, θᵒ)
    recomputeODEs && solveBackRec!(NoBlocking(), P, ST()) # compute (H, Hν, c)

    for i in 1:m    # compute wiener path WW that generates XX
        invSolve!(Euler(), XX[i], WW[i], P[i])
    end
    # compute white noise that generates starting point
    y = XX[1].yy[1]
    yPr = invStartPt(y, yPr, P[1])

    llᵒ = logpdf(yPr, y)
    llᵒ += pathLogLikhd(ObsScheme(), XX, P, 1:m, fpt; skipFPT=true)
    llᵒ += lobslikelihood(P[1], y)
    printInfo(verbose, it, value(ll), value(llᵒ))
    return llᵒ, true, θᵒ, yPr
end

"""
    updateParam!(::PartObs, ::ConjugateUpdt, tKern, θ, ::UpdtIdx, yPr, WW, Pᵒ,
                 P, XXᵒ, XX, ll, priors, fpt, recomputeODEs;
                 solver::ST=Ralston3(), verbose=false, it=NaN
                 ) -> acceptedLogLikhd, acceptDecision
Update parameters
see the definition of  updateParam!(…, ::MetropolisHastingsUpdt, …) for the
explanation of the arguments.
"""
function updateParam!(::ObsScheme, ::ConjugateUpdt, 𝔅::BlockingSchedule,
                      tKern, θ, ::UpdtIdx, yPr, WW, Pᵒ, P, XXᵒ, XX, ll, priors,
                      fpt, recomputeODEs; solver::ST=Ralston3(), verbose=false,
                      it=NaN) where {ObsScheme <: AbstractObsScheme, ST, UpdtIdx}
    m = length(P)
    ϑ = conjugateDraw(θ, 𝔅.XX, 𝔅.P[1].Target, priors[1], UpdtIdx())   # sample new parameter
    θᵒ = moveToProperPlace(ϑ, θ, UpdtIdx())     # align so that dimensions agree

    updateTargetLaws!(𝔅, θᵒ)
    recomputeODEs && solveBackRec!(𝔅, 𝔅.P, ST())
    for i in 1:m    # compute wiener path WW that generates XX
        invSolve!(Euler(), 𝔅.XX[i], 𝔅.WW[i], 𝔅.P[i])
    end
    # compute white noise that generates starting point
    y = 𝔅.XX[1].yy[1]
    yPr = invStartPt(y, yPr, 𝔅.P[1])
    llᵒ = logpdf(yPr, y)
    for block in 𝔅.blocks[𝔅.idx]
        llᵒ += pathLogLikhd(ObsScheme(), 𝔅.XX, 𝔅.P, block, fpt; skipFPT=true)
        llᵒ += lobslikelihood(𝔅.P[block[1]], 𝔅.XX[block[1]].yy[1])
    end
    printInfo(verbose, it, value(ll), value(llᵒ))
    return llᵒ, true, θᵒ, yPr
end

"""
    mcmc(::ObsScheme, obs, obsTimes, yPr::StartingPtPrior, w, P˟, P̃, Ls, Σs,
         numSteps, tKernel, priors; fpt=fill(NaN, length(obsTimes)-1), ρ=0.0,
         dt=1/5000, timeChange=true, saveIter=NaN, verbIter=NaN,
         updtCoord=(Val((true,)),), paramUpdt=true, skipForSave=1,
         updtType=(MetropolisHastingsUpdt(),), solver::ST=Ralston3(), warmUp=0)

Gibbs sampler alternately imputing unobserved parts of the path and updating
unknown coordinates of the parameter vector (the latter only if paramUpdt==true)
...
# Arguments
- `::ObsScheme`: observation scheme---first-passage time or partial observations
- `obs`: vector with observations
- `obsTimes`: times of the observations
- `yPr`: prior over the starting point of the diffusion path
- `w`: dummy variable whose type must agree with the type of the Wiener process
- `P˟`: law of the target diffusion (with initial θ₀ set)
- `P̃`: law of the auxiliary process (with initial θ₀ set)
- `Ls`: vector of observation operators (one per each observation)
- `Σs`: vector of covariance matrices of the noise (one per each observaiton)
- `numSteps`: number of mcmc iterations
- `tKernel`: transition kernel (also with initial θ₀ set)
- `priors`: a list of lists of priors
- `τ`: time-change transformation
- `fpt`: info about first-passage time conditioning
- `ρ`: memory parameter for the Crank-Nicolson scheme
- `dt`: time-distance for the path imputation
- `saveIter`: save path `XX` once every `saveIter` many iterations
- `verbIter`: print out progress info once every `verbIter` many iterations
- `updtCoord`: list of objects declaring indices of to-be-updated parameters
- `paramUpdt`: flag for whether to update parameters at all
- `skipForSave`: when saving paths, save only one in every `skipForSave` points
- `updtType`: list of types of updates to cycle through
- `solver`: numerical solver used for computing backward ODEs
- `warmUp`: number of steps for which no parameter update is to be made
...
"""
function mcmc(::Type{K}, ::ObsScheme, obs, obsTimes, yPr::StartingPtPrior, w,
              P˟, P̃, Ls, Σs, numSteps, tKernel, priors, τ;
              fpt=fill(NaN, length(obsTimes)-1), ρ=0.0, dt=1/5000, saveIter=NaN,
              verbIter=NaN, updtCoord=(Val((true,)),), paramUpdt=true,
              skipForSave=1, updtType=(MetropolisHastingsUpdt(),),
              blocking::Blocking=NoBlocking(),
              blockingParams=([], 0.1, NoChangePt()),
              solver::ST=Ralston3(), changePt::CP=NoChangePt(), warmUp=0
              ) where {K, ObsScheme <: AbstractObsScheme, ST, Blocking, CP}
    P = findProposalLaw( K, obs, obsTimes, P˟, P̃, Ls, Σs, τ; dt=dt, solver=ST(),
                         changePt=CP(getChangePt(blockingParams[3])) )
    m = length(obs)-1
    updtLen = length(updtCoord)
    Wnr, WWᵒ, WW, XXᵒ, XX, Pᵒ, ll, yPr = initialise(ObsScheme(), P, m, yPr, w,
                                                    fpt)
    Paths = []
    accImpCounter = 0
    accUpdtCounter = [0 for i in 1:updtLen]
    θ = params(P˟)
    θchain = Vector{typeof(θ)}(undef, (numSteps-warmUp)*updtLen+1)
    θchain[1] = copy(θ)
    recomputeODEs = [any([e in dependsOnParams(P[1].Pt) for e
                         in idx(uc)]) for uc in updtCoord]

    updtStepCounter = 1
    𝔅 = setBlocking(blocking, blockingParams, P, WW, XX)
    display(𝔅)
    for i in 1:numSteps
        verbose = (i % verbIter == 0)
        i > warmUp && savePath!(Paths, blocking == NoBlocking() ? XX : 𝔅.XX,
                                (i % saveIter == 0), skipForSave)
        ll, acc, 𝔅, yPr = impute!(ObsScheme(), 𝔅, Wnr, yPr, WWᵒ, WW, XXᵒ, XX,
                                  P, ll, fpt, ρ=ρ, verbose=verbose, it=i,
                                  solver=ST())
        accImpCounter += 1*acc
        if paramUpdt && i > warmUp
            for j in 1:updtLen
                (ll, acc, θ,
                 yPr) = updateParam!(ObsScheme(), updtType[j], 𝔅, tKernel, θ,
                                     updtCoord[j], yPr, WW, Pᵒ, P, XXᵒ, XX, ll,
                                     priors[j], fpt, recomputeODEs[j];
                                     solver=ST(), verbose=verbose, it=i)
                accUpdtCounter[j] += 1*acc
                updtStepCounter += 1
                θchain[updtStepCounter] = copy(θ)
                verbose && print("\n")
            end
            verbose && print("------------------------------------------------",
                             "------\n")
        end
    end
    displayAcceptanceRate(𝔅)
    Time = collect(Iterators.flatten(p.tt[1:skipForSave:end-1] for p in P))
    θchain, accImpCounter/numSteps, accUpdtCounter./numSteps, Paths, Time
end


text:
# syntax: proto3
using ProtoBuf
import ProtoBuf.meta

mutable struct CodeDef_Trace <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function CodeDef_Trace(; kwargs...)
        obj = new(meta(CodeDef_Trace), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct CodeDef_Trace
const __meta_CodeDef_Trace = Ref{ProtoMeta}()
function meta(::Type{CodeDef_Trace})
    ProtoBuf.metalock() do
        if !isassigned(__meta_CodeDef_Trace)
            __meta_CodeDef_Trace[] = target = ProtoMeta(CodeDef_Trace)
            fnum = Int[1,6,2,3,7,4,8,5]
            allflds = Pair{Symbol,Union{Type,String}}[:file => AbstractString, :file_id => Int64, :lineno => Int32, :_function => AbstractString, :function_id => Int64, :line => AbstractString, :line_id => Int64, :func_start_line => Int32]
            meta(target, CodeDef_Trace, allflds, ProtoBuf.DEF_REQ, fnum, ProtoBuf.DEF_VAL, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_CodeDef_Trace[]
    end
end
function Base.getproperty(obj::CodeDef_Trace, name::Symbol)
    if name === :file
        return (obj.__protobuf_jl_internal_values[name])::AbstractString
    elseif name === :file_id
        return (obj.__protobuf_jl_internal_values[name])::Int64
    elseif name === :lineno
        return (obj.__protobuf_jl_internal_values[name])::Int32
    elseif name === :_function
        return (obj.__protobuf_jl_internal_values[name])::AbstractString
    elseif name === :function_id
        return (obj.__protobuf_jl_internal_values[name])::Int64
    elseif name === :line
        return (obj.__protobuf_jl_internal_values[name])::AbstractString
    elseif name === :line_id
        return (obj.__protobuf_jl_internal_values[name])::Int64
    elseif name === :func_start_line
        return (obj.__protobuf_jl_internal_values[name])::Int32
    else
        getfield(obj, name)
    end
end

mutable struct CodeDef <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function CodeDef(; kwargs...)
        obj = new(meta(CodeDef), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct CodeDef
const __meta_CodeDef = Ref{ProtoMeta}()
function meta(::Type{CodeDef})
    ProtoBuf.metalock() do
        if !isassigned(__meta_CodeDef)
            __meta_CodeDef[] = target = ProtoMeta(CodeDef)
            allflds = Pair{Symbol,Union{Type,String}}[:traces => Base.Vector{CodeDef_Trace}]
            meta(target, CodeDef, allflds, ProtoBuf.DEF_REQ, ProtoBuf.DEF_FNUM, ProtoBuf.DEF_VAL, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_CodeDef[]
    end
end
function Base.getproperty(obj::CodeDef, name::Symbol)
    if name === :traces
        return (obj.__protobuf_jl_internal_values[name])::Base.Vector{CodeDef_Trace}
    else
        getfield(obj, name)
    end
end

mutable struct OpLogEntry <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function OpLogEntry(; kwargs...)
        obj = new(meta(OpLogEntry), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct OpLogEntry
const __meta_OpLogEntry = Ref{ProtoMeta}()
function meta(::Type{OpLogEntry})
    ProtoBuf.metalock() do
        if !isassigned(__meta_OpLogEntry)
            __meta_OpLogEntry[] = target = ProtoMeta(OpLogEntry)
            allflds = Pair{Symbol,Union{Type,String}}[:name => AbstractString, :float_ops => Int64, :types => Base.Vector{AbstractString}, :code_def => CodeDef]
            meta(target, OpLogEntry, allflds, ProtoBuf.DEF_REQ, ProtoBuf.DEF_FNUM, ProtoBuf.DEF_VAL, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_OpLogEntry[]
    end
end
function Base.getproperty(obj::OpLogEntry, name::Symbol)
    if name === :name
        return (obj.__protobuf_jl_internal_values[name])::AbstractString
    elseif name === :float_ops
        return (obj.__protobuf_jl_internal_values[name])::Int64
    elseif name === :types
        return (obj.__protobuf_jl_internal_values[name])::Base.Vector{AbstractString}
    elseif name === :code_def
        return (obj.__protobuf_jl_internal_values[name])::CodeDef
    else
        getfield(obj, name)
    end
end

mutable struct OpLogProto_IdToStringEntry <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function OpLogProto_IdToStringEntry(; kwargs...)
        obj = new(meta(OpLogProto_IdToStringEntry), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct OpLogProto_IdToStringEntry (mapentry)
const __meta_OpLogProto_IdToStringEntry = Ref{ProtoMeta}()
function meta(::Type{OpLogProto_IdToStringEntry})
    ProtoBuf.metalock() do
        if !isassigned(__meta_OpLogProto_IdToStringEntry)
            __meta_OpLogProto_IdToStringEntry[] = target = ProtoMeta(OpLogProto_IdToStringEntry)
            allflds = Pair{Symbol,Union{Type,String}}[:key => Int64, :value => AbstractString]
            meta(target, OpLogProto_IdToStringEntry, allflds, ProtoBuf.DEF_REQ, ProtoBuf.DEF_FNUM, ProtoBuf.DEF_VAL, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_OpLogProto_IdToStringEntry[]
    end
end
function Base.getproperty(obj::OpLogProto_IdToStringEntry, name::Symbol)
    if name === :key
        return (obj.__protobuf_jl_internal_values[name])::Int64
    elseif name === :value
        return (obj.__protobuf_jl_internal_values[name])::AbstractString
    else
        getfield(obj, name)
    end
end

mutable struct OpLogProto <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function OpLogProto(; kwargs...)
        obj = new(meta(OpLogProto), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct OpLogProto
const __meta_OpLogProto = Ref{ProtoMeta}()
function meta(::Type{OpLogProto})
    ProtoBuf.metalock() do
        if !isassigned(__meta_OpLogProto)
            __meta_OpLogProto[] = target = ProtoMeta(OpLogProto)
            allflds = Pair{Symbol,Union{Type,String}}[:log_entries => Base.Vector{OpLogEntry}, :id_to_string => Base.Dict{Int64,AbstractString}]
            meta(target, OpLogProto, allflds, ProtoBuf.DEF_REQ, ProtoBuf.DEF_FNUM, ProtoBuf.DEF_VAL, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_OpLogProto[]
    end
end
function Base.getproperty(obj::OpLogProto, name::Symbol)
    if name === :log_entries
        return (obj.__protobuf_jl_internal_values[name])::Base.Vector{OpLogEntry}
    elseif name === :id_to_string
        return (obj.__protobuf_jl_internal_values[name])::Base.Dict{Int64,AbstractString}
    else
        getfield(obj, name)
    end
end

mutable struct Tuple <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function Tuple(; kwargs...)
        obj = new(meta(Tuple), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct Tuple
const __meta_Tuple = Ref{ProtoMeta}()
function meta(::Type{Tuple})
    ProtoBuf.metalock() do
        if !isassigned(__meta_Tuple)
            __meta_Tuple[] = target = ProtoMeta(Tuple)
            pack = Symbol[:int64_values]
            allflds = Pair{Symbol,Union{Type,String}}[:int64_values => Base.Vector{Int64}]
            meta(target, Tuple, allflds, ProtoBuf.DEF_REQ, ProtoBuf.DEF_FNUM, ProtoBuf.DEF_VAL, pack, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_Tuple[]
    end
end
function Base.getproperty(obj::Tuple, name::Symbol)
    if name === :int64_values
        return (obj.__protobuf_jl_internal_values[name])::Base.Vector{Int64}
    else
        getfield(obj, name)
    end
end

mutable struct ProfileNode_OutputShapesEntry <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function ProfileNode_OutputShapesEntry(; kwargs...)
        obj = new(meta(ProfileNode_OutputShapesEntry), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct ProfileNode_OutputShapesEntry (mapentry)
const __meta_ProfileNode_OutputShapesEntry = Ref{ProtoMeta}()
function meta(::Type{ProfileNode_OutputShapesEntry})
    ProtoBuf.metalock() do
        if !isassigned(__meta_ProfileNode_OutputShapesEntry)
            __meta_ProfileNode_OutputShapesEntry[] = target = ProtoMeta(ProfileNode_OutputShapesEntry)
            allflds = Pair{Symbol,Union{Type,String}}[:key => Int32, :value => Tuple]
            meta(target, ProfileNode_OutputShapesEntry, allflds, ProtoBuf.DEF_REQ, ProtoBuf.DEF_FNUM, ProtoBuf.DEF_VAL, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_ProfileNode_OutputShapesEntry[]
    end
end
function Base.getproperty(obj::ProfileNode_OutputShapesEntry, name::Symbol)
    if name === :key
        return (obj.__protobuf_jl_internal_values[name])::Int32
    elseif name === :value
        return (obj.__protobuf_jl_internal_values[name])::Tuple
    else
        getfield(obj, name)
    end
end

mutable struct ProfileNode_InputShapesEntry <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function ProfileNode_InputShapesEntry(; kwargs...)
        obj = new(meta(ProfileNode_InputShapesEntry), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct ProfileNode_InputShapesEntry (mapentry)
const __meta_ProfileNode_InputShapesEntry = Ref{ProtoMeta}()
function meta(::Type{ProfileNode_InputShapesEntry})
    ProtoBuf.metalock() do
        if !isassigned(__meta_ProfileNode_InputShapesEntry)
            __meta_ProfileNode_InputShapesEntry[] = target = ProtoMeta(ProfileNode_InputShapesEntry)
            allflds = Pair{Symbol,Union{Type,String}}[:key => Int32, :value => Tuple]
            meta(target, ProfileNode_InputShapesEntry, allflds, ProtoBuf.DEF_REQ, ProtoBuf.DEF_FNUM, ProtoBuf.DEF_VAL, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_ProfileNode_InputShapesEntry[]
    end
end
function Base.getproperty(obj::ProfileNode_InputShapesEntry, name::Symbol)
    if name === :key
        return (obj.__protobuf_jl_internal_values[name])::Int32
    elseif name === :value
        return (obj.__protobuf_jl_internal_values[name])::Tuple
    else
        getfield(obj, name)
    end
end

mutable struct ExecTime <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function ExecTime(; kwargs...)
        obj = new(meta(ExecTime), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct ExecTime
const __meta_ExecTime = Ref{ProtoMeta}()
function meta(::Type{ExecTime})
    ProtoBuf.metalock() do
        if !isassigned(__meta_ExecTime)
            __meta_ExecTime[] = target = ProtoMeta(ExecTime)
            allflds = Pair{Symbol,Union{Type,String}}[:times => Base.Vector{Tuple}]
            meta(target, ExecTime, allflds, ProtoBuf.DEF_REQ, ProtoBuf.DEF_FNUM, ProtoBuf.DEF_VAL, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_ExecTime[]
    end
end
function Base.getproperty(obj::ExecTime, name::Symbol)
    if name === :times
        return (obj.__protobuf_jl_internal_values[name])::Base.Vector{Tuple}
    else
        getfield(obj, name)
    end
end

mutable struct ExecProfile_AcceleratorExecsEntry <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function ExecProfile_AcceleratorExecsEntry(; kwargs...)
        obj = new(meta(ExecProfile_AcceleratorExecsEntry), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct ExecProfile_AcceleratorExecsEntry (mapentry)
const __meta_ExecProfile_AcceleratorExecsEntry = Ref{ProtoMeta}()
function meta(::Type{ExecProfile_AcceleratorExecsEntry})
    ProtoBuf.metalock() do
        if !isassigned(__meta_ExecProfile_AcceleratorExecsEntry)
            __meta_ExecProfile_AcceleratorExecsEntry[] = target = ProtoMeta(ExecProfile_AcceleratorExecsEntry)
            allflds = Pair{Symbol,Union{Type,String}}[:key => AbstractString, :value => ExecTime]
            meta(target, ExecProfile_AcceleratorExecsEntry, allflds, ProtoBuf.DEF_REQ, ProtoBuf.DEF_FNUM, ProtoBuf.DEF_VAL, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_ExecProfile_AcceleratorExecsEntry[]
    end
end
function Base.getproperty(obj::ExecProfile_AcceleratorExecsEntry, name::Symbol)
    if name === :key
        return (obj.__protobuf_jl_internal_values[name])::AbstractString
    elseif name === :value
        return (obj.__protobuf_jl_internal_values[name])::ExecTime
    else
        getfield(obj, name)
    end
end

mutable struct ExecProfile_CpuExecsEntry <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function ExecProfile_CpuExecsEntry(; kwargs...)
        obj = new(meta(ExecProfile_CpuExecsEntry), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct ExecProfile_CpuExecsEntry (mapentry)
const __meta_ExecProfile_CpuExecsEntry = Ref{ProtoMeta}()
function meta(::Type{ExecProfile_CpuExecsEntry})
    ProtoBuf.metalock() do
        if !isassigned(__meta_ExecProfile_CpuExecsEntry)
            __meta_ExecProfile_CpuExecsEntry[] = target = ProtoMeta(ExecProfile_CpuExecsEntry)
            allflds = Pair{Symbol,Union{Type,String}}[:key => AbstractString, :value => ExecTime]
            meta(target, ExecProfile_CpuExecsEntry, allflds, ProtoBuf.DEF_REQ, ProtoBuf.DEF_FNUM, ProtoBuf.DEF_VAL, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_ExecProfile_CpuExecsEntry[]
    end
end
function Base.getproperty(obj::ExecProfile_CpuExecsEntry, name::Symbol)
    if name === :key
        return (obj.__protobuf_jl_internal_values[name])::AbstractString
    elseif name === :value
        return (obj.__protobuf_jl_internal_values[name])::ExecTime
    else
        getfield(obj, name)
    end
end

mutable struct Memory <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function Memory(; kwargs...)
        obj = new(meta(Memory), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct Memory
const __meta_Memory = Ref{ProtoMeta}()
function meta(::Type{Memory})
    ProtoBuf.metalock() do
        if !isassigned(__meta_Memory)
            __meta_Memory[] = target = ProtoMeta(Memory)
            allflds = Pair{Symbol,Union{Type,String}}[:bytes => Int64, :ptr => UInt64]
            meta(target, Memory, allflds, ProtoBuf.DEF_REQ, ProtoBuf.DEF_FNUM, ProtoBuf.DEF_VAL, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_Memory[]
    end
end
function Base.getproperty(obj::Memory, name::Symbol)
    if name === :bytes
        return (obj.__protobuf_jl_internal_values[name])::Int64
    elseif name === :ptr
        return (obj.__protobuf_jl_internal_values[name])::UInt64
    else
        getfield(obj, name)
    end
end

mutable struct ExecMemory_OutputMemoryEntry <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function ExecMemory_OutputMemoryEntry(; kwargs...)
        obj = new(meta(ExecMemory_OutputMemoryEntry), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct ExecMemory_OutputMemoryEntry (mapentry)
const __meta_ExecMemory_OutputMemoryEntry = Ref{ProtoMeta}()
function meta(::Type{ExecMemory_OutputMemoryEntry})
    ProtoBuf.metalock() do
        if !isassigned(__meta_ExecMemory_OutputMemoryEntry)
            __meta_ExecMemory_OutputMemoryEntry[] = target = ProtoMeta(ExecMemory_OutputMemoryEntry)
            allflds = Pair{Symbol,Union{Type,String}}[:key => Int32, :value => Memory]
            meta(target, ExecMemory_OutputMemoryEntry, allflds, ProtoBuf.DEF_REQ, ProtoBuf.DEF_FNUM, ProtoBuf.DEF_VAL, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_ExecMemory_OutputMemoryEntry[]
    end
end
function Base.getproperty(obj::ExecMemory_OutputMemoryEntry, name::Symbol)
    if name === :key
        return (obj.__protobuf_jl_internal_values[name])::Int32
    elseif name === :value
        return (obj.__protobuf_jl_internal_values[name])::Memory
    else
        getfield(obj, name)
    end
end

mutable struct ExecMemory <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function ExecMemory(; kwargs...)
        obj = new(meta(ExecMemory), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct ExecMemory
const __meta_ExecMemory = Ref{ProtoMeta}()
function meta(::Type{ExecMemory})
    ProtoBuf.metalock() do
        if !isassigned(__meta_ExecMemory)
            __meta_ExecMemory[] = target = ProtoMeta(ExecMemory)
            allflds = Pair{Symbol,Union{Type,String}}[:memory_micros => Int64, :host_temp_bytes => Int64, :host_persistent_bytes => Int64, :accelerator_temp_bytes => Int64, :accelerator_persistent_bytes => Int64, :requested_bytes => Int64, :peak_bytes => Int64, :residual_bytes => Int64, :output_bytes => Int64, :allocator_bytes_in_use => Int64, :output_memory => Base.Dict{Int32,Memory}]
            meta(target, ExecMemory, allflds, ProtoBuf.DEF_REQ, ProtoBuf.DEF_FNUM, ProtoBuf.DEF_VAL, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_ExecMemory[]
    end
end
function Base.getproperty(obj::ExecMemory, name::Symbol)
    if name === :memory_micros
        return (obj.__protobuf_jl_internal_values[name])::Int64
    elseif name === :host_temp_bytes
        return (obj.__protobuf_jl_internal_values[name])::Int64
    elseif name === :host_persistent_bytes
        return (obj.__protobuf_jl_internal_values[name])::Int64
    elseif name === :accelerator_temp_bytes
        return (obj.__protobuf_jl_internal_values[name])::Int64
    elseif name === :accelerator_persistent_bytes
        return (obj.__protobuf_jl_internal_values[name])::Int64
    elseif name === :requested_bytes
        return (obj.__protobuf_jl_internal_values[name])::Int64
    elseif name === :peak_bytes
        return (obj.__protobuf_jl_internal_values[name])::Int64
    elseif name === :residual_bytes
        return (obj.__protobuf_jl_internal_values[name])::Int64
    elseif name === :output_bytes
        return (obj.__protobuf_jl_internal_values[name])::Int64
    elseif name === :allocator_bytes_in_use
        return (obj.__protobuf_jl_internal_values[name])::Int64
    elseif name === :output_memory
        return (obj.__protobuf_jl_internal_values[name])::Base.Dict{Int32,Memory}
    else
        getfield(obj, name)
    end
end

mutable struct ExecProfile <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function ExecProfile(; kwargs...)
        obj = new(meta(ExecProfile), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct ExecProfile
const __meta_ExecProfile = Ref{ProtoMeta}()
function meta(::Type{ExecProfile})
    ProtoBuf.metalock() do
        if !isassigned(__meta_ExecProfile)
            __meta_ExecProfile[] = target = ProtoMeta(ExecProfile)
            fnum = Int[1,2,3,4,5,7,11,6]
            allflds = Pair{Symbol,Union{Type,String}}[:run_count => Int64, :all_start_micros => Int64, :latest_end_micros => Int64, :accelerator_execs => Base.Dict{AbstractString,ExecTime}, :cpu_execs => Base.Dict{AbstractString,ExecTime}, :memory_execs => Base.Vector{ExecMemory}, :allocations => Base.Vector{AllocationRecord}, :devices => Base.Vector{AbstractString}]
            meta(target, ExecProfile, allflds, ProtoBuf.DEF_REQ, fnum, ProtoBuf.DEF_VAL, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_ExecProfile[]
    end
end
function Base.getproperty(obj::ExecProfile, name::Symbol)
    if name === :run_count
        return (obj.__protobuf_jl_internal_values[name])::Int64
    elseif name === :all_start_micros
        return (obj.__protobuf_jl_internal_values[name])::Int64
    elseif name === :latest_end_micros
        return (obj.__protobuf_jl_internal_values[name])::Int64
    elseif name === :accelerator_execs
        return (obj.__protobuf_jl_internal_values[name])::Base.Dict{AbstractString,ExecTime}
    elseif name === :cpu_execs
        return (obj.__protobuf_jl_internal_values[name])::Base.Dict{AbstractString,ExecTime}
    elseif name === :memory_execs
        return (obj.__protobuf_jl_internal_values[name])::Base.Vector{ExecMemory}
    elseif name === :allocations
        return (obj.__protobuf_jl_internal_values[name])::Base.Vector{AllocationRecord}
    elseif name === :devices
        return (obj.__protobuf_jl_internal_values[name])::Base.Vector{AbstractString}
    else
        getfield(obj, name)
    end
end

mutable struct ProfileNode_ExecsEntry <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function ProfileNode_ExecsEntry(; kwargs...)
        obj = new(meta(ProfileNode_ExecsEntry), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct ProfileNode_ExecsEntry (mapentry)
const __meta_ProfileNode_ExecsEntry = Ref{ProtoMeta}()
function meta(::Type{ProfileNode_ExecsEntry})
    ProtoBuf.metalock() do
        if !isassigned(__meta_ProfileNode_ExecsEntry)
            __meta_ProfileNode_ExecsEntry[] = target = ProtoMeta(ProfileNode_ExecsEntry)
            allflds = Pair{Symbol,Union{Type,String}}[:key => Int64, :value => ExecProfile]
            meta(target, ProfileNode_ExecsEntry, allflds, ProtoBuf.DEF_REQ, ProtoBuf.DEF_FNUM, ProtoBuf.DEF_VAL, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_ProfileNode_ExecsEntry[]
    end
end
function Base.getproperty(obj::ProfileNode_ExecsEntry, name::Symbol)
    if name === :key
        return (obj.__protobuf_jl_internal_values[name])::Int64
    elseif name === :value
        return (obj.__protobuf_jl_internal_values[name])::ExecProfile
    else
        getfield(obj, name)
    end
end

mutable struct ProfileNode_InputsEntry <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function ProfileNode_InputsEntry(; kwargs...)
        obj = new(meta(ProfileNode_InputsEntry), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct ProfileNode_InputsEntry (mapentry)
const __meta_ProfileNode_InputsEntry = Ref{ProtoMeta}()
function meta(::Type{ProfileNode_InputsEntry})
    ProtoBuf.metalock() do
        if !isassigned(__meta_ProfileNode_InputsEntry)
            __meta_ProfileNode_InputsEntry[] = target = ProtoMeta(ProfileNode_InputsEntry)
            allflds = Pair{Symbol,Union{Type,String}}[:key => Int32, :value => Int64]
            meta(target, ProfileNode_InputsEntry, allflds, ProtoBuf.DEF_REQ, ProtoBuf.DEF_FNUM, ProtoBuf.DEF_VAL, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_ProfileNode_InputsEntry[]
    end
end
function Base.getproperty(obj::ProfileNode_InputsEntry, name::Symbol)
    if name === :key
        return (obj.__protobuf_jl_internal_values[name])::Int32
    elseif name === :value
        return (obj.__protobuf_jl_internal_values[name])::Int64
    else
        getfield(obj, name)
    end
end

mutable struct ProfileNode_OutputsEntry <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function ProfileNode_OutputsEntry(; kwargs...)
        obj = new(meta(ProfileNode_OutputsEntry), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct ProfileNode_OutputsEntry (mapentry)
const __meta_ProfileNode_OutputsEntry = Ref{ProtoMeta}()
function meta(::Type{ProfileNode_OutputsEntry})
    ProtoBuf.metalock() do
        if !isassigned(__meta_ProfileNode_OutputsEntry)
            __meta_ProfileNode_OutputsEntry[] = target = ProtoMeta(ProfileNode_OutputsEntry)
            allflds = Pair{Symbol,Union{Type,String}}[:key => Int32, :value => Int64]
            meta(target, ProfileNode_OutputsEntry, allflds, ProtoBuf.DEF_REQ, ProtoBuf.DEF_FNUM, ProtoBuf.DEF_VAL, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_ProfileNode_OutputsEntry[]
    end
end
function Base.getproperty(obj::ProfileNode_OutputsEntry, name::Symbol)
    if name === :key
        return (obj.__protobuf_jl_internal_values[name])::Int32
    elseif name === :value
        return (obj.__protobuf_jl_internal_values[name])::Int64
    else
        getfield(obj, name)
    end
end

mutable struct ProfileNode_SrcOutputIndexEntry <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function ProfileNode_SrcOutputIndexEntry(; kwargs...)
        obj = new(meta(ProfileNode_SrcOutputIndexEntry), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct ProfileNode_SrcOutputIndexEntry (mapentry)
const __meta_ProfileNode_SrcOutputIndexEntry = Ref{ProtoMeta}()
function meta(::Type{ProfileNode_SrcOutputIndexEntry})
    ProtoBuf.metalock() do
        if !isassigned(__meta_ProfileNode_SrcOutputIndexEntry)
            __meta_ProfileNode_SrcOutputIndexEntry[] = target = ProtoMeta(ProfileNode_SrcOutputIndexEntry)
            allflds = Pair{Symbol,Union{Type,String}}[:key => Int64, :value => Int32]
            meta(target, ProfileNode_SrcOutputIndexEntry, allflds, ProtoBuf.DEF_REQ, ProtoBuf.DEF_FNUM, ProtoBuf.DEF_VAL, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_ProfileNode_SrcOutputIndexEntry[]
    end
end
function Base.getproperty(obj::ProfileNode_SrcOutputIndexEntry, name::Symbol)
    if name === :key
        return (obj.__protobuf_jl_internal_values[name])::Int64
    elseif name === :value
        return (obj.__protobuf_jl_internal_values[name])::Int32
    else
        getfield(obj, name)
    end
end

mutable struct ProfileNode_AttrsEntry <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function ProfileNode_AttrsEntry(; kwargs...)
        obj = new(meta(ProfileNode_AttrsEntry), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct ProfileNode_AttrsEntry (mapentry)
const __meta_ProfileNode_AttrsEntry = Ref{ProtoMeta}()
function meta(::Type{ProfileNode_AttrsEntry})
    ProtoBuf.metalock() do
        if !isassigned(__meta_ProfileNode_AttrsEntry)
            __meta_ProfileNode_AttrsEntry[] = target = ProtoMeta(ProfileNode_AttrsEntry)
            allflds = Pair{Symbol,Union{Type,String}}[:key => AbstractString, :value => AttrValue]
            meta(target, ProfileNode_AttrsEntry, allflds, ProtoBuf.DEF_REQ, ProtoBuf.DEF_FNUM, ProtoBuf.DEF_VAL, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_ProfileNode_AttrsEntry[]
    end
end
function Base.getproperty(obj::ProfileNode_AttrsEntry, name::Symbol)
    if name === :key
        return (obj.__protobuf_jl_internal_values[name])::AbstractString
    elseif name === :value
        return (obj.__protobuf_jl_internal_values[name])::AttrValue
    else
        getfield(obj, name)
    end
end

mutable struct ProfileNode <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function ProfileNode(; kwargs...)
        obj = new(meta(ProfileNode), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct ProfileNode
const __meta_ProfileNode = Ref{ProtoMeta}()
function meta(::Type{ProfileNode})
    ProtoBuf.metalock() do
        if !isassigned(__meta_ProfileNode)
            __meta_ProfileNode[] = target = ProtoMeta(ProfileNode)
            fnum = Int[1,9,13,2,16,3,15,14,4,5,6,7,8,10,11,12]
            pack = Symbol[:shape]
            allflds = Pair{Symbol,Union{Type,String}}[:name => AbstractString, :op => AbstractString, :id => Int64, :inputs => Base.Dict{Int32,Int64}, :input_shapes => Base.Dict{Int32,Tuple}, :outputs => Base.Dict{Int32,Int64}, :output_shapes => Base.Dict{Int32,Tuple}, :src_output_index => Base.Dict{Int64,Int32}, :shape => Base.Vector{Int64}, :op_types => Base.Vector{AbstractString}, :canonical_device => AbstractString, :host_device => AbstractString, :float_ops => Int64, :trace => CodeDef, :attrs => Base.Dict{AbstractString,AttrValue}, :execs => Base.Dict{Int64,ExecProfile}]
            meta(target, ProfileNode, allflds, ProtoBuf.DEF_REQ, fnum, ProtoBuf.DEF_VAL, pack, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_ProfileNode[]
    end
end
function Base.getproperty(obj::ProfileNode, name::Symbol)
    if name === :name
        return (obj.__protobuf_jl_internal_values[name])::AbstractString
    elseif name === :op
        return (obj.__protobuf_jl_internal_values[name])::AbstractString
    elseif name === :id
        return (obj.__protobuf_jl_internal_values[name])::Int64
    elseif name === :inputs
        return (obj.__protobuf_jl_internal_values[name])::Base.Dict{Int32,Int64}
    elseif name === :input_shapes
        return (obj.__protobuf_jl_internal_values[name])::Base.Dict{Int32,Tuple}
    elseif name === :outputs
        return (obj.__protobuf_jl_internal_values[name])::Base.Dict{Int32,Int64}
    elseif name === :output_shapes
        return (obj.__protobuf_jl_internal_values[name])::Base.Dict{Int32,Tuple}
    elseif name === :src_output_index
        return (obj.__protobuf_jl_internal_values[name])::Base.Dict{Int64,Int32}
    elseif name === :shape
        return (obj.__protobuf_jl_internal_values[name])::Base.Vector{Int64}
    elseif name === :op_types
        return (obj.__protobuf_jl_internal_values[name])::Base.Vector{AbstractString}
    elseif name === :canonical_device
        return (obj.__protobuf_jl_internal_values[name])::AbstractString
    elseif name === :host_device
        return (obj.__protobuf_jl_internal_values[name])::AbstractString
    elseif name === :float_ops
        return (obj.__protobuf_jl_internal_values[name])::Int64
    elseif name === :trace
        return (obj.__protobuf_jl_internal_values[name])::CodeDef
    elseif name === :attrs
        return (obj.__protobuf_jl_internal_values[name])::Base.Dict{AbstractString,AttrValue}
    elseif name === :execs
        return (obj.__protobuf_jl_internal_values[name])::Base.Dict{Int64,ExecProfile}
    else
        getfield(obj, name)
    end
end

mutable struct ProfileProto_NodesEntry <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function ProfileProto_NodesEntry(; kwargs...)
        obj = new(meta(ProfileProto_NodesEntry), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct ProfileProto_NodesEntry (mapentry)
const __meta_ProfileProto_NodesEntry = Ref{ProtoMeta}()
function meta(::Type{ProfileProto_NodesEntry})
    ProtoBuf.metalock() do
        if !isassigned(__meta_ProfileProto_NodesEntry)
            __meta_ProfileProto_NodesEntry[] = target = ProtoMeta(ProfileProto_NodesEntry)
            allflds = Pair{Symbol,Union{Type,String}}[:key => Int64, :value => ProfileNode]
            meta(target, ProfileProto_NodesEntry, allflds, ProtoBuf.DEF_REQ, ProtoBuf.DEF_FNUM, ProtoBuf.DEF_VAL, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_ProfileProto_NodesEntry[]
    end
end
function Base.getproperty(obj::ProfileProto_NodesEntry, name::Symbol)
    if name === :key
        return (obj.__protobuf_jl_internal_values[name])::Int64
    elseif name === :value
        return (obj.__protobuf_jl_internal_values[name])::ProfileNode
    else
        getfield(obj, name)
    end
end

mutable struct ProfileProto_IdToStringEntry <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function ProfileProto_IdToStringEntry(; kwargs...)
        obj = new(meta(ProfileProto_IdToStringEntry), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct ProfileProto_IdToStringEntry (mapentry)
const __meta_ProfileProto_IdToStringEntry = Ref{ProtoMeta}()
function meta(::Type{ProfileProto_IdToStringEntry})
    ProtoBuf.metalock() do
        if !isassigned(__meta_ProfileProto_IdToStringEntry)
            __meta_ProfileProto_IdToStringEntry[] = target = ProtoMeta(ProfileProto_IdToStringEntry)
            allflds = Pair{Symbol,Union{Type,String}}[:key => Int64, :value => AbstractString]
            meta(target, ProfileProto_IdToStringEntry, allflds, ProtoBuf.DEF_REQ, ProtoBuf.DEF_FNUM, ProtoBuf.DEF_VAL, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_ProfileProto_IdToStringEntry[]
    end
end
function Base.getproperty(obj::ProfileProto_IdToStringEntry, name::Symbol)
    if name === :key
        return (obj.__protobuf_jl_internal_values[name])::Int64
    elseif name === :value
        return (obj.__protobuf_jl_internal_values[name])::AbstractString
    else
        getfield(obj, name)
    end
end

mutable struct ProfileProto <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function ProfileProto(; kwargs...)
        obj = new(meta(ProfileProto), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct ProfileProto
const __meta_ProfileProto = Ref{ProtoMeta}()
function meta(::Type{ProfileProto})
    ProtoBuf.metalock() do
        if !isassigned(__meta_ProfileProto)
            __meta_ProfileProto[] = target = ProtoMeta(ProfileProto)
            fnum = Int[1,2,5,3,4]
            pack = Symbol[:steps]
            allflds = Pair{Symbol,Union{Type,String}}[:nodes => Base.Dict{Int64,ProfileNode}, :has_trace => Bool, :miss_accelerator_stream => Bool, :steps => Base.Vector{Int64}, :id_to_string => Base.Dict{Int64,AbstractString}]
            meta(target, ProfileProto, allflds, ProtoBuf.DEF_REQ, fnum, ProtoBuf.DEF_VAL, pack, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_ProfileProto[]
    end
end
function Base.getproperty(obj::ProfileProto, name::Symbol)
    if name === :nodes
        return (obj.__protobuf_jl_internal_values[name])::Base.Dict{Int64,ProfileNode}
    elseif name === :has_trace
        return (obj.__protobuf_jl_internal_values[name])::Bool
    elseif name === :miss_accelerator_stream
        return (obj.__protobuf_jl_internal_values[name])::Bool
    elseif name === :steps
        return (obj.__protobuf_jl_internal_values[name])::Base.Vector{Int64}
    elseif name === :id_to_string
        return (obj.__protobuf_jl_internal_values[name])::Base.Dict{Int64,AbstractString}
    else
        getfield(obj, name)
    end
end

export CodeDef_Trace, CodeDef, OpLogEntry, OpLogProto_IdToStringEntry, OpLogProto, ProfileProto_NodesEntry, ProfileProto_IdToStringEntry, ProfileProto, ProfileNode_InputsEntry, ProfileNode_InputShapesEntry, ProfileNode_OutputsEntry, ProfileNode_OutputShapesEntry, ProfileNode_SrcOutputIndexEntry, ProfileNode_AttrsEntry, ProfileNode_ExecsEntry, ProfileNode, ExecProfile_AcceleratorExecsEntry, ExecProfile_CpuExecsEntry, ExecProfile, ExecTime, ExecMemory_OutputMemoryEntry, ExecMemory, Tuple, Memory
# mapentries: "ProfileNode_AttrsEntry" => ("AbstractString", "AttrValue"), "ProfileNode_ExecsEntry" => ("Int64", "ExecProfile"), "ProfileNode_InputsEntry" => ("Int32", "Int64"), "ProfileNode_InputShapesEntry" => ("Int32", "Tuple"), "ExecProfile_CpuExecsEntry" => ("AbstractString", "ExecTime"), "ExecMemory_OutputMemoryEntry" => ("Int32", "Memory"), "ProfileNode_SrcOutputIndexEntry" => ("Int64", "Int32"), "ProfileProto_NodesEntry" => ("Int64", "ProfileNode"), "ExecProfile_AcceleratorExecsEntry" => ("AbstractString", "ExecTime"), "ProfileNode_OutputsEntry" => ("Int32", "Int64"), "OpLogProto_IdToStringEntry" => ("Int64", "AbstractString"), "ProfileNode_OutputShapesEntry" => ("Int32", "Tuple"), "ProfileProto_IdToStringEntry" => ("Int64", "AbstractString")

ext: jl


text:
using SevenDayStreamflowForecasts

using Test

@testset "SevenDayStreamflowForecasts" begin
    sdf = SDF()
    @testset "SDF()" begin
        nrows, ncols = size(sdf.sites)
        @test nrows > 0 # At least one site.
        @test ncols > 0 # At least one column.
    end

    @testset "get_forecasts()" begin
        site_ids = ["410730"]
        for awrc_id in site_ids
            data, header = get_forecasts(sdf, awrc_id)

            local nrows, ncols = size(data)
            @test nrows > 0 # At least one data point.
            @test ncols > 0 # At least one column.
            @test names(data)[1] == "DateTime"
            @test names(data)[2] == "Time"

            @test length(header) > 0 # At least one header line
        end
        data, header = get_forecasts(sdf, "invalid ID")
        @test isempty(data)
        @test isempty(header)
    end
    @testset "show()" begin
        show_str = repr(sdf)
        @test occursin("awrc_id", show_str)
        @test occursin("station_name", show_str)
    end
    @testset "close()" begin
        close!(sdf)
        @test isempty(sdf.sites)
    end
end


text:
# ------------------------------------------------------------ #
# dual(b)fft for input X::Array{Complex{D},Dim}) where D<:Dual #
# ------------------------------------------------------------ #
for (f,DIRECTION) in ((:fft,FFTW.FORWARD), (:bfft,FFTW.BACKWARD))
    f! = Symbol(f,"!")
    df, df! = Symbol("dual",f), Symbol("dual",f!)
    @eval function $df(X::Array{Complex{D},Dim}, region) where {D<:Dual,Dim}
        y = dual2array(X)
        $f!(y, region.+1)
        Y = array2dual(Complex{D}, y)
        return Y
    end
    @eval function $df!(X::Array{Complex{D},Dim}, region) where {D<:Dual,Dim}
        Y = $df(X, region)
        copy!(X, Y)
        return nothing
    end
end


text:
# methods for estimating dtw #
abstract type DTWMethod end

mutable struct ClassicDTW <: DTWMethod end

struct FastDTW <: DTWMethod
    radius::Int
end

# distance interface #
struct DTWDistance{M<:DTWMethod, D<:SemiMetric} <: SemiMetric
    method::M
    dist::D
    DTWDistance{M,D}(m::M,d::D) where  {M<:DTWMethod, D<:SemiMetric} = new(m,d)
end

function DTWDistance(
        d::D=SqEuclidean(),
        m::M=ClassicDTW()
    ) where {M<:DTWMethod,D<:SemiMetric}
    DTWDistance{M,D}(m,d)
end

function DTWDistance(
        m::M,
        d::D=SqEuclidean()
    ) where {M<:DTWMethod,D<:SemiMetric}
    DTWDistance{M,D}(m,d)
end

Distances.evaluate(d::DTWDistance{ClassicDTW}, x, y) = dtw(x,y,d.dist)[1]
Distances.evaluate(d::DTWDistance{FastDTW}, x, y) = fastdtw(x,y,d.method.radius,d.dist)[1]

distpath(d::DTWDistance{ClassicDTW}, x, y) = dtw(x,y,d.dist)
distpath(d::DTWDistance{ClassicDTW}, x, y, i2min::AbstractVector, i2max::AbstractVector) = dtw(x,y,i2min,i2max,d.dist)
distpath(d::DTWDistance{FastDTW}, x, y) = fastdtw(x,y,d.method.radius,d.dist)


text:
# To test a simplified 2 axis gimbal.
# Modeling the system as a cube and the gimbal placed on top.
# We need two revolute joints between the cube and gimbal.

include("../src/plotSol.jl")
include("../src/simulate.jl")
include("../src/OrientationConversion.jl")
clearconsole()

## Inertial Properties
mb = 0.755; mg = 0.215; mp = 0.02;

Ib =  [ 0.0009    0.0000   -0.0000;
        0.0000    0.0053    0.0000;
       -0.0000    0.0000    0.0054]#*(10^-3)

Ip =    [ 0.0029    0.0000    0.0000;
          0.0000    0.5824    0.0000;
          0.0000    0.0000    0.5805]*(10^-4)

Ig =    [ 0.1435    0.0000    0.0000;
          0.0000    0.0498    0.0000;
          0.0000    0.0000    0.1381]*(10^-3)

## Initialisation
InFrame = InertialFrameAsRB()
Body = RigidBody(mb,Ib,2)
Gimbal = RigidBody(mg,Ig,3)

x0Body = [zeros(3);[1;zeros(3)];zeros(3);zeros(4)]
initialiseRigidBody!(Body,x0Body)
x0Gimbal = [[0.0;0.0;0.143];[1;zeros(3)];zeros(3);zeros(4)]
initialiseRigidBody!(Gimbal,x0Gimbal)

## Joint Descriptions
# Joint 1 (Free) between the inertial frame and body.
j1 = Joint(InFrame,Body,zeros(3),zeros(3))

# Joint 2 (Revolute2) between the body and gimbal
axisZ = [0.0 0.0 1.0][:]
rjBody = [0.0 0.0 0.134][:]
rjGimbal = [0.0 0.0 -0.009][:]
j2 = Joint(Body,Gimbal,rjBody,rjGimbal,
     type = "Revolute",axis = axisZ, jointTorque = [0.0 0.0 0.0][:])

## Simulation
tEnd = 1.0
tSpan = 0.01
g = [0.0;0.0;0.0]
tSim, solFinal = simulate(tEnd,tSpan,j1,j2,g=g)#,extFVec = extFList)
@time simulate(tEnd,tSpan,j1,j2,g=g)
solBody = solFinal[1]
solGimbal = solFinal[2]

## Plotting
plotErrNorm(tSim,solBody.β)
plotErrNorm(tSim,solGimbal.β)
# Check if joint location has moved
jointLoc = Matrix{Float64}(undef,length(tSim),3)
ωBody = Matrix{Float64}(undef,length(tSim),3)
ωGimbal = Matrix{Float64}(undef,length(tSim),3)
ωGimbalInBody = Matrix{Float64}(undef,length(tSim),3)
ωRel = Matrix{Float64}(undef,length(tSim),3) # Relative
for i=1:length(tSim)
    Body.dcm = quat2dcm(solBody.β[i,:])
    Gimbal.dcm = quat2dcm(solGimbal.β[i,:])
    ωBody[i,:] = angVel(solBody.β[i,:],solBody.βdot[i,:])
    ωGimbal[i,:] = angVel(solGimbal.β[i,:],solGimbal.βdot[i,:])
    ωGimbalInBody[i,:] = quat2dcm(solBody.β[i,:])*
                         transpose(quat2dcm(solGimbal.β[i,:]))*
                         ωGimbal[i,:]
    ωRel[i,:] = ωGimbalInBody[i,:] - ωBody[i,:]
    jointLoc[i,:] = solBody.r[i,:] + transpose(Body.dcm)*rjBody -
                    solGimbal.r[i,:] - transpose(Gimbal.dcm)*rjGimbal
end
plotPos(tSim,jointLoc)
plotPos(tSim,solBody.r)
plotPos(tSim,solGimbal.r)
plotVel(tSim,solBody.v)
plotVel(tSim,solGimbal.v)
plotAngVel(tSim,ωBody)
plotAngVel(tSim,ωGimbal)
plotAngVel(tSim,ωGimbalInBody)
plotAngVel(tSim,ωRel)
# plotAngVel(tSim,ωCube - ωProp)

# # Check revJoint axis
# axisSol = Matrix{Float64}(undef,length(tSim),3)
# errAxis = Matrix{Float64}(undef,length(tSim),3)
# errAxisNorm = Vector{Float64}(undef,length(tSim))
# for i=1:length(tSim)
#     axisSol[i,:] = solProp.β[i,2:4]/norm(solProp.β[i,2:4],2)
#
#     # β1inv = [solQuad.β[i,1];solQuad.β[i,2:4]]
#     # βRev = quaternionProduct(solProp.β[i,:],β1inv)
#     # axisSol[i,:] = βRev[2:4]/norm(βRev[2:4])
#
#     # axisSol[i,:] = (solQuad.β[i,:])*solProp.β[i,2:4]/norm(solProp.β[i,2:4],2)
#
#     # axisSol[i,:] = axixRev(solQuad.β[i,:],solProp.β[i,:])
#     errAxis[i,:] = axisSol[i,:] - axis
#     errAxisNorm[i] = norm(axisSol[i,:],2) - 1.0
# end
# plot(tSim[2:end],errAxisNorm[2:end])
# plotPos(tSim,errAxis)

max_stars_repo_name: isrlab/Multibody-Dynamics

max_issues_repo_name: isrlab/Multibody-Dynamics

max_forks_repo_name: isrlab/Multibody-Dynamics


text:
using ValkyrieRobot
using ValkyrieRobot.BipedControlUtil
using Test
using RigidBodyDynamics: num_velocities

@testset "side" begin
    @test -left == right
    @test -right == left

    sides = [rand(Side) for i = 1 : 10000];
    @test isapprox(count(side -> side == left, sides) / length(sides), 0.5; atol = 0.05)

    @test flipsign_if_right(2., left) == 2.
    @test flipsign_if_right(2., right) == -2.
end

@testset "valkyrie" begin
    val = Valkyrie()
    @test num_velocities(val.mechanism) == 36
    meshdir = joinpath(dirname(ValkyrieRobot.urdfpath()), "urdf", "model", "meshes")

    num_obj = 0
    for (root, dirs, files) in walkdir(meshdir)
        for file in files
            if endswith(file, ".obj")
                num_obj += 1
            end
        end
    end
    @test num_obj == 60
end


text:
for (S,T) in ((:SafeInt8, :Int8), (:SafeInt16, :Int16), (:SafeInt32, :Int32),  (:SafeInt64, :Int64), (:SafeInt128, :Int128))
  @eval begin
     function parse(::Type{$S}, str::A) where {A<:AbstractString}
       (occursin('.', str) || occursin('e', str) || occursin('f', str)) && return Meta.parse(str)
       x = parse($T, str)
       return $S(x)
     end

    function tryparse(::Type{$S}, str::A; base=10) where {A<:AbstractString}
      x = tryparse($T, str, base=Int(base))
      return ifelse(isnothing(x), nothing, $S(x))
    end
  end
end

for (S,T) in ((:SafeUInt8, :UInt8), (:SafeUInt16, :UInt16), (:SafeUInt32, :Int32),  (:SafeUInt64, :UInt64), (:SafeUInt128, :UInt128))
  @eval begin
    function parse(::Type{$S}, str::A) where {A<:AbstractString}
      (occursin('.', str) || occursin('e', str) || occursin('f', str)) && return Meta.parse(str)
      x = parse($T, str)
      return $S(x)
    end

    function tryparse(::Type{$S}, str::A; base=10) where {A<:AbstractString}
      x = tryparse($T, str, base=Int(base))
      return ifelse(isnothing(x), nothing, $S(x))
    end
  end
end


text:
using DecomposedMDPSolver
using POMDPs, POMDPModels
using Test
using Random
using Flux

mdp = SimpleGridWorld(tprob = 1, discount = 1, rewards = Dict(GWPos(4,3)=>0., GWPos(4,6)=>0., GWPos(9,3)=>1.0, GWPos(8,8)=>1.0))
reward(mdp, GWPos(3,3), :up)
POMDPs.gen(mdp::SimpleGridWorld, s, a, rng = Random.GLOBAL_RNG) = (sp =rand(transition(mdp, s, a )), r=reward(mdp, s, a))
POMDPs.initialstate(mdp::SimpleGridWorld) = rand(initialstate_distribution(mdp))
Na = 1
Np = 10
solutions = [(x) -> 0.5*ones(Na) for i=1:Np ]

## Weights network
base = Chain(Dense(2, 32, relu), Dense(32, Na, sigmoid))
attn = Chain(Dense(2, 32, relu), Dense(32, Np+1), softmax)
a2t_model = A2TNetwork(base, attn, solutions)

action_prob(mdp, s, a) = 0.25
p = ISPolicy(a2t_model, mdp, action_prob, Random.GLOBAL_RNG)

S, G, R = sample_episodes(mdp, p, 10)
@test all(G .>= 0)
@test size(S, 1) == 2
@test length(G) == size(S, 2)
@test R > 0