text:
# update_variable_selection_global.jl

export βδ_x_modify_γ, δ_x_modify_γ, lNXmat_lωNXvec;

function ldens_y(model::Model_BNP_WMReg_Joint, γ_cand::BitArray{1})

    lNy = zeros(typeof(model.y[1]), model.n)
    γcand_indx = findall(γ_cand) # currently assumes global γ

    for i = 1:model.n
        μ = deepcopy(model.state.μ_y[model.state.S[i]])
        for k in γcand_indx
            μ -= model.state.β_y[model.state.S[i], k] * (model.X[i,k] - model.state.μ_x[model.state.S[i], k])
        end
        lNy[i] += logpdf(Normal(μ, sqrt(model.state.δ_y[model.state.S[i]])), model.y[i])
    end

    return lNy
end

## single component versions of these functions are found in update_eta_Met.jl
## This is the version that inflates variances
function βδ_x_modify_γ(β_x::Union{Array{Array{T, 2}, 1}, Array{Any,1}}, δ_x::Array{T, 2},
                        γ::BitArray{1}, γδc::Array{T, 1}) where T <: Real
    modify_indx = findall(.!(γ))
    K = length(γ)

    βout = deepcopy(β_x) # vector of H by (k in (K-1):1) matrices
    δout = deepcopy(δ_x) # H by K matrix

    for k in modify_indx # this works even if no modifications are necessary
        δout[:,k] .+= γδc[k]
    end

    for k = 1:(K-1)
        if !γ[k]
            βout[k] *= 0.0
        else
            modify2 = intersect((k+1):K, modify_indx)
            βout[k][:,(modify2 .- k)] *= 0.0
        end
    end

    return βout, δout
end
function δ_x_modify_γ(δ_x::Array{T, 2},
                      γ::BitArray{1}, γδc::Array{T, 1}) where T <: Real
    modify_indx = findall(.!(γ))
    δout = deepcopy(δ_x)

    for k in modify_indx # this works even if no modifications are necessary
        δout[:,k] .+= γδc[k]
    end
    return δout
end

## single component versions of these functions are found in update_eta_Met.jl
## This is the version that subsets
function βδ_x_modify_γ(β_x::Union{Array{Array{T, 2}, 1}, Array{Any,1}}, δ_x::Array{T, 2},
                        γ::BitArray{1}, γδc::Float64) where T <: Real

    γδc == Inf || throw("A single variance inflation should be equal to Inf")

    γindx = findall(γ)
    nγ = length(γindx)
    nγ > 1 || throw("βδ_x_modify_γ requires more than one selected variable.")

    βout = [ deepcopy(β_x[γindx[k]][:, (γindx[(k+1):nγ] .- γindx[k]) ])  for k = 1:(nγ-1) ] # vector of H by (nγ-1):1 matrices
    δout = deepcopy(δ_x[:,γindx]) # H nows and sum(gamma) cols

    return βout, δout
end
function δ_x_modify_γ(δ_x::Array{T, 2},
                      γ::BitArray{1}, γδc::Float64) where T <: Real
    γδc == Inf || throw("A single variance inflation should be equal to Inf")
    γindx = findall(γ)
    δout = deepcopy(δ_x[:,γindx]) # H nows and sum(gamma) cols
    return δout
end

## Just subset this in the subset method
function β_y_modify_γ(β_y::Array{T, 2}, γ::BitArray{1}) where T <: Real
    modify_indx = findall(.!(γ))
    βout = deepcopy(β_y)

    for k in modify_indx # this works even if no modifications are necessary
        βout[:,k] *= 0.0
    end
    return βout
end
function β_y_modify_γ(β_y::Array{T, 1}, γ::BitArray{1}) where T <: Real
    modify_indx = findall(.!(γ))
    βout = deepcopy(β_y)

    for k in modify_indx # this works even if no modifications are necessary
        βout[k] *= 0.0
    end
    return βout
end

## Calculate lNX and lωNXvec under different variable selection methods
function lNXmat_lωNXvec(model::Model_BNP_WMReg_Joint, γ::BitArray{1})

    γindx = findall(γ)
    nγ = length(γindx)

    if model.state.γδc == Inf # subset method for variable selection

        if nγ == 0
            lNX = zeros(Float64, model.n, model.H)
            lωNX_vec = zeros(Float64, model.n)
        elseif nγ == 1
            lNX = lNXmat(model.X[:,γindx],
                         model.state.μ_x[:,γindx], model.state.δ_x[:,γindx])
            lωNX_vec = lωNXvec(model.state.lω, lNX)
        elseif nγ > 1 && model.Σx_type == :full
            βγ_x, δγ_x = βδ_x_modify_γ(model.state.β_x, model.state.δ_x,
                                       γ, model.state.γδc)
            lNX = lNXmat(model.X[:,γindx],
                         model.state.μ_x[:,γindx], βγ_x, δγ_x) # n by H matrix
            lωNX_vec = lωNXvec(model.state.lω, lNX)
        elseif nγ > 1 && model.Σx_type == :diag
            lNX = lNXmat_Σdiag(model.X[:,γindx],
                model.state.μ_x[:,γindx], model.state.δ_x[:,γindx]) # n by H matrix
            lωNX_vec = lωNXvec(model.state.lω, lNX)
        end

    elseif isnothing(model.state.γδc) # integration method for variable selection

        if nγ == 0
            lNX = zeros(Float64, model.n, model.H)
            lωNX_vec = zeros(Float64, model.n)
        elseif nγ == 1
            if model.K > 1 && model.Σx_type == :full
                σ2xs = [ sqfChol_to_Σ( [ model.state.β_x[k][h,:] for k = 1:(model.K-1) ], model.state.δ_x[h,:] ).mat[γindx, γindx][1] for h = 1:model.H ]
            elseif model.K > 1 && model.Σx_type == :diag
                σ2xs = deepcopy(model.state.δ_x[:,γindx])
            elseif model.K == 1
                σ2xs = deepcopy(model.state.δ_x[:,1])
            end
            lNX = lNXmat(model.X[:,γindx], model.state.μ_x[:,γindx], σ2xs)
            lωNX_vec = lωNXvec(model.state.lω, lNX)
        elseif nγ > 1 && model.Σx_type == :full
            Σxs = [ PDMat( sqfChol_to_Σ( [ model.state.β_x[k][h,:] for k = 1:(model.K-1) ], model.state.δ_x[h,:] ).mat[γindx, γindx] ) for h = 1:model.H ]
            lNX = hcat( [ logpdf(MultivariateNormal(model.state.μ_x[h,γindx], Σxs[h]), Matrix(model.X[:,γindx]')) for h = 1:model.H ]... ) # n by H matrix
            lωNX_vec = lωNXvec(model.state.lω, lNX)
        elseif nγ > 1 && model.Σx_type == :diag
            lNX = lNXmat_Σdiag(model.X[:,γindx],
                model.state.μ_x[:,γindx], model.state.δ_x[:,γindx]) # n by H matrix
            lωNX_vec = lωNXvec(model.state.lω, lNX)
        end

    else # variance-inflation method for variable selection

        if model.K > 1 && model.Σx_type == :full
            βγ_x, δγ_x = βδ_x_modify_γ(model.state.β_x, model.state.δ_x,
                                       γ, model.state.γδc)
            lNX = lNXmat(model.X, model.state.μ_x, βγ_x, δγ_x)
        elseif model.K == 1 && model.Σx_type == :full
            δγ_x = δ_x_modify_γ(model.state.δ_x, γ, model.state.γδc)
            lNX = lNXmat(vec(model.X), vec(model.state.μ_x), vec(δγ_x))
        end
        lωNX_vec = lωNXvec(model.state.lω, lNX)
    end

    return lNX, lωNX_vec
end

# function update_γ_k!(model::Model_BNP_WMReg_Joint, lNy_old::Array{T,1}, k::Int) where T <: Real

#     γ_alt = deepcopy(model.state.γ)
#     γ_alt[k] = !γ_alt[k]

#     lNy_alt = ldens_y(model, γ_alt)
#     lNX_alt, lωNX_vec_alt = lNXmat_lωNXvec(model, γ_alt)

#     lNx_alt = [ deepcopy(lNX_alt[i, model.state.S[i]]) for i = 1:model.n ]
#     lNx_old = [ deepcopy(model.state.lNX[i, model.state.S[i]]) for i = 1:model.n ]

#     la = log(model.state.π_γ[k])
#     lb = log(1.0 - model.state.π_γ[k])

#     if model.state.γ[k]
#         la +=  sum( lNy_old + lNx_old - model.state.lωNX_vec )
#         lb +=  sum( lNy_alt + lNx_alt - lωNX_vec_alt )
#         ldenom = BayesInference.logsumexp([la, lb])
#         lprob_switch = lb - ldenom
#         lfc_on = la - ldenom
#     else
#         la += sum( lNy_alt + lNx_alt - lωNX_vec_alt )
#         lb += sum( lNy_old + lNx_old - model.state.lωNX_vec )
#         lprob_switch = la - BayesInference.logsumexp([la, lb])
#         lfc_on = deepcopy(lprob_switch)
#     end

#     switch = log(rand()) < lprob_switch

#     if switch
#         model.state.γ[k] = !model.state.γ[k]
#         model.state.lNX = deepcopy(lNX_alt)
#         model.state.lωNX_vec = deepcopy(lωNX_vec_alt)
#         lNy_old = deepcopy(lNy_alt)
#     end

#     return lfc_on
# end

function update_γ_block!(model::Model_BNP_WMReg_Joint, up_indx::Array{Int,1}) where T <: Real

    ## Propose deterministic switch of uniformly selected indices
    K_upd = min(3, length(up_indx))
    k_upd = sample(StatsBase.Weights( (0.5).^collect(1:K_upd) ))
    switch_indx = sample(up_indx, k_upd, replace=false)

    γ_alt = deepcopy(model.state.γ)
    γ_alt[switch_indx] = .!γ_alt[switch_indx]

    ## Calculate lmargy under both scenarios
    Λβ0star_ηy = model.state.Λ0star_ηy * model.state.β0star_ηy
    βΛβ0star_ηy = PDMats.quad(model.state.Λ0star_ηy, model.state.β0star_ηy)

    indx_h_vec = [ findall(model.state.S .== h) for h = 1:model.H ]
    n_h_vec = [ length(indx_h_vec[h]) for h = 1:model.H ]

    D_vec = [ construct_Dh(h, model.X[indx_h_vec[h],:], model.state.μ_x[h,:], model.state.γ ) for h = 1:model.H ]

    ## Calculate Lam1, a1, b1 for all h
    a1_vec = 0.5 .* ( model.state.ν_δy .+ n_h_vec )
    Λ1star_ηy_vec = [ n_h_vec[h] > 0 ? get_Λ1star_ηy_h(D_vec[h], model.state.Λ0star_ηy) : deepcopy(model.state.Λ0star_ηy) for h = 1:model.H ]
    β1star00_vec = [ n_h_vec[h] > 0 ? (Λβ0star_ηy + D_vec[h]'model.y[indx_h_vec[h]]) : deepcopy(Λβ0star_ηy) for h = 1:model.H ]
    β1star_ηy_vec = [ n_h_vec[h] > 0 ? Λ1star_ηy_vec[h] \ β1star00_vec[h] : deepcopy(model.state.β0star_ηy) for h = 1:model.H ]
    b1_vec = [ n_h_vec[h] > 0 ? get_b1_δy_h(model.state.ν_δy, model.state.s0_δy, model.y[indx_h_vec[h]], βΛβ0star_ηy, Λ1star_ηy_vec[h], β1star_ηy_vec[h]) : 0.0  for h = 1:model.H ]

    lmargy_old = [ n_h_vec[h] > 0 ? lmargy(Λ1star_ηy_vec[h], a1_vec[h], b1_vec[h]) : 0.0 for h = 1:model.H ] # unoccupied components don't contribute

    D_alt_vec = [ construct_Dh(h, model.X[indx_h_vec[h],:], model.state.μ_x[h,:], γ_alt ) for h = 1:model.H ] # could be more efficient, but, meh.
    Λ1star_ηy_alt_vec = [ n_h_vec[h] > 0 ? get_Λ1star_ηy_h(D_alt_vec[h], model.state.Λ0star_ηy) : deepcopy(model.state.Λ0star_ηy) for h = 1:model.H ]
    β1star00_alt_vec = [ n_h_vec[h] > 0 ? (Λβ0star_ηy + D_alt_vec[h]'model.y[indx_h_vec[h]]) : deepcopy(Λβ0star_ηy) for h = 1:model.H ]
    β1star_ηy_alt_vec = [ n_h_vec[h] > 0 ? Λ1star_ηy_alt_vec[h] \ β1star00_alt_vec[h] : deepcopy(model.state.β0star_ηy) for h = 1:model.H ]
    b1_alt_vec = [ n_h_vec[h] > 0 ? get_b1_δy_h(model.state.ν_δy, model.state.s0_δy, model.y[indx_h_vec[h]], βΛβ0star_ηy, Λ1star_ηy_alt_vec[h], β1star_ηy_alt_vec[h]) : 0.0  for h = 1:model.H ]

    lmargy_alt = [ n_h_vec[h] > 0 ? lmargy(Λ1star_ηy_alt_vec[h], a1_vec[h], b1_alt_vec[h]) : 0.0 for h = 1:model.H ]

    ## Calculate lNX under alternate scenario
    lNX_alt, lωNX_vec_alt = lNXmat_lωNXvec(model, γ_alt)

    ## Calculate lNx under both scenarios
    lNx_alt = [ deepcopy(lNX_alt[i, model.state.S[i]]) for i = 1:model.n ]
    lNx_old = [ deepcopy(model.state.lNX[i, model.state.S[i]]) for i = 1:model.n ]

    ## Metropolis step
    lp_old = sum( log.(model.state.π_γ[switch_indx][ findall( model.state.γ[switch_indx] ) ]) ) +
             sum( log.( 1.0 .- model.state.π_γ[switch_indx][ findall( .!model.state.γ[switch_indx] ) ]) )
    lp_old += (sum(lNx_old) - sum(model.state.lωNX_vec) + sum(lmargy_old) )

    lp_alt = sum( log.(model.state.π_γ[switch_indx][ findall( γ_alt[switch_indx] ) ]) ) +
             sum( log.( 1.0 .- model.state.π_γ[switch_indx][ findall( .!γ_alt[switch_indx] ) ]) )
    lp_alt += (sum(lNx_alt) - sum(lωNX_vec_alt) + sum(lmargy_alt) )

    switch = log(rand()) < ( lp_alt - lp_old )

    if switch
        model.state.γ = γ_alt
        model.state.lNX = deepcopy(lNX_alt)
        model.state.lωNX_vec = deepcopy(lωNX_vec_alt)
    end

    return nothing
end

# function update_γ_global!(model::Model_BNP_WMReg_Joint)

#     ## calculate lNy
#     lNy = ldens_y(model, model.state.γ)

#     up_indx = findall( [ model.state.π_γ[k] < 1.0 && model.state.π_γ[k] > 0.0 for k = 1:model.K ] )
#     lfc_on = deepcopy(model.state.π_γ)

#     ## loop through k
#     for k in up_indx
#         lfc_on[k] = update_γ_k!(model, lNy, k)
#     end

#     return lfc_on
# end

function update_γ_global!(model::Model_BNP_WMReg_Joint)

    up_indx = findall( [ model.state.π_γ[k] < 1.0 && model.state.π_γ[k] > 0.0 for k = 1:model.K ] )

    if length(up_indx) > 0
        update_γ_block!(model, up_indx)
    end

    return zeros(model.K)
end

function update_γ!(model::Model_BNP_WMReg_Joint)

    if model.γ_type == :global

        lfc_on = update_γ_global!(model)

    elseif model.γ_type == :local

        lfc_on = update_γ_local!(model)

    end

    return lfc_on
end

### Functions for Zanella & Roberts (2019) tempered Gibbs algorithm. (doesn't work embedded in larger Gibbs sampler)
# function get_lp_γ(model::Model_BNP_WMReg_Joint, up_indx::Vector{Int}, κ_γ::T) where T <: Real
#
#     n_up = length(up_indx)
#     lp_out = Vector{T}(undef, n_up)
#     lfc_on = log.(deepcopy(model.state.π_γ))
#
#     lNX_out = [ Matrix{T}(undef, model.n, model.H) for ii = 1:n_up ]
#     lωNX_vec_out = [ Vector{T}(undef, model.n) for ii = 1:n_up ]
#
#     lNy_old = ldens_y(model, model.state.γ)
#     lNx_old = [ deepcopy(model.state.lNX[i, model.state.S[i]]) for i = 1:model.n ]
#
#     lκ_γ = log(κ_γ)
#     # lκ_γ = log(1000.0)
#     ln_up = log( float(n_up) )
#     l2 = log(2.0)
#
#     for ii = 1:n_up
#         k = up_indx[ii]
#
#         γ_alt = deepcopy(model.state.γ)
#         γ_alt[k] = !γ_alt[k]
#
#         lNy_alt = ldens_y(model, γ_alt)
#         lNX_out[ii], lωNX_vec_out[ii] = lNXmat_lωNXvec(model, γ_alt)
#
#         lNx_alt = [ deepcopy(lNX_out[ii][i, model.state.S[i]]) for i = 1:model.n ]
#
#         lfc_part_on = log(model.state.π_γ[k]) # log prior probability of γ = 1
#         lfc_part_off = log(1.0 - model.state.π_γ[k])
#
#         if model.state.γ[k]
#             lfc_part_on +=  sum( lNy_old + lNx_old - model.state.lωNX_vec )
#             lfc_part_off +=  sum( lNy_alt + lNx_alt - lωNX_vec_out[ii] )
#             lfc_denom = BayesInference.logsumexp([lfc_part_on, lfc_part_off])
#             lfc_on[k] = lfc_part_on - lfc_denom
#
#             lp_numer = BayesInference.logsumexp( [ lfc_on[k], lκ_γ - ln_up ] )
#             lp_out[ii] = lp_numer - ( l2 + lfc_on[k] )
#         else
#             lfc_part_on += sum( lNy_alt + lNx_alt - lωNX_vec_out[ii] )
#             lfc_part_off += sum( lNy_old + lNx_old - model.state.lωNX_vec )
#             lfc_denom = BayesInference.logsumexp([lfc_part_on, lfc_part_off])
#             lfc_on[k] = lfc_part_on - lfc_denom
#             lfc_off = lfc_part_off - lfc_denom
#
#             lp_numer = BayesInference.logsumexp( [ lfc_on[k], lκ_γ - ln_up ] )
#             lp_out[ii] = lp_numer - ( l2 + lfc_off )
#         end
#
#     end
#
#     return lp_out, lNX_out, lωNX_vec_out, lfc_on
# end
#
# function update_γ!(model::Model_BNP_WMReg_Joint) ## Zanella & Roberts (2019) tempered Gibbs algorithm.
#
#     up_indx = findall( [ model.state.π_γ[k] < 1.0 && model.state.π_γ[k] > 0.0 for k = 1:model.K ] )
#     d = length(up_indx)
#
#     if d > 0
#         κ_γ = sum(model.state.π_γ[up_indx]) # apriori expected number of active lags (that aren't fixed on or off)
#
#         ## get weights
#         lp, lNX_alt, lωNX_vec_alt, lfc_on = get_lp_γ(model, up_indx, κ_γ)
#         lsump = BayesInference.logsumexp(lp)
#         lp .-= lsump
#
#         ## select which indicator to switch
#         switch_indx = StatsBase.sample(1:d, Weights(exp.(lp)))
#
#         ## switch the selected indicator
#         model.state.γ[up_indx[switch_indx]] = !model.state.γ[up_indx[switch_indx]]
#
#         ## assign model state quantities
#         model.state.lNX = lNX_alt[switch_indx]
#         model.state.lωNX_vec = lωNX_vec_alt[switch_indx]
#
#         ## assign sample log importance weight
#         model.state.lwimp = -lsump
#     end
#
#     return lfc_on
# end


text:
# This file is auto-generated by AWSMetadata.jl
using AWS
using AWS.AWSServices: firehose
using AWS.Compat
using AWS.UUIDs

"""
    create_delivery_stream(delivery_stream_name)
    create_delivery_stream(delivery_stream_name, params::Dict{String,<:Any})

Creates a Kinesis Data Firehose delivery stream. By default, you can create up to 50
delivery streams per AWS Region. This is an asynchronous operation that immediately
returns. The initial status of the delivery stream is CREATING. After the delivery stream
is created, its status is ACTIVE and it now accepts data. If the delivery stream creation
fails, the status transitions to CREATING_FAILED. Attempts to send data to a delivery
stream that is not in the ACTIVE state cause an exception. To check the state of a delivery
stream, use DescribeDeliveryStream. If the status of a delivery stream is CREATING_FAILED,
this status doesn't change, and you can't invoke CreateDeliveryStream again on it. However,
you can invoke the DeleteDeliveryStream operation to delete it. A Kinesis Data Firehose
delivery stream can be configured to receive records directly from providers using
PutRecord or PutRecordBatch, or it can be configured to use an existing Kinesis stream as
its source. To specify a Kinesis data stream as input, set the DeliveryStreamType parameter
to KinesisStreamAsSource, and provide the Kinesis stream Amazon Resource Name (ARN) and
role ARN in the KinesisStreamSourceConfiguration parameter. To create a delivery stream
with server-side encryption (SSE) enabled, include
DeliveryStreamEncryptionConfigurationInput in your request. This is optional. You can also
invoke StartDeliveryStreamEncryption to turn on SSE for an existing delivery stream that
doesn't have SSE enabled. A delivery stream is configured with a single destination: Amazon
S3, Amazon ES, Amazon Redshift, or Splunk. You must specify only one of the following
destination configuration parameters: ExtendedS3DestinationConfiguration,
S3DestinationConfiguration, ElasticsearchDestinationConfiguration,
RedshiftDestinationConfiguration, or SplunkDestinationConfiguration. When you specify
S3DestinationConfiguration, you can also provide the following optional values:
BufferingHints, EncryptionConfiguration, and CompressionFormat. By default, if no
BufferingHints value is provided, Kinesis Data Firehose buffers data up to 5 MB or for 5
minutes, whichever condition is satisfied first. BufferingHints is a hint, so there are
some cases where the service cannot adhere to these conditions strictly. For example,
record boundaries might be such that the size is a little over or under the configured
buffering size. By default, no encryption is performed. We strongly recommend that you
enable encryption to ensure secure data storage in Amazon S3. A few notes about Amazon
Redshift as a destination:   An Amazon Redshift destination requires an S3 bucket as
intermediate location. Kinesis Data Firehose first delivers data to Amazon S3 and then uses
COPY syntax to load data into an Amazon Redshift table. This is specified in the
RedshiftDestinationConfiguration.S3Configuration parameter.   The compression formats
SNAPPY or ZIP cannot be specified in RedshiftDestinationConfiguration.S3Configuration
because the Amazon Redshift COPY operation that reads from the S3 bucket doesn't support
these compression formats.   We strongly recommend that you use the user name and password
you provide exclusively with Kinesis Data Firehose, and that the permissions for the
account are restricted for Amazon Redshift INSERT permissions.   Kinesis Data Firehose
assumes the IAM role that is configured as part of the destination. The role should allow
the Kinesis Data Firehose principal to assume the role, and the role should have
permissions that allow the service to deliver the data. For more information, see Grant
Kinesis Data Firehose Access to an Amazon S3 Destination in the Amazon Kinesis Data
Firehose Developer Guide.

# Arguments
- `delivery_stream_name`: The name of the delivery stream. This name must be unique per AWS
  account in the same AWS Region. If the delivery streams are in different accounts or
  different Regions, you can have multiple delivery streams with the same name.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"AmazonopensearchserviceDestinationConfiguration"`:
- `"DeliveryStreamEncryptionConfigurationInput"`: Used to specify the type and Amazon
  Resource Name (ARN) of the KMS key needed for Server-Side Encryption (SSE).
- `"DeliveryStreamType"`: The delivery stream type. This parameter can be one of the
  following values:    DirectPut: Provider applications access the delivery stream directly.
    KinesisStreamAsSource: The delivery stream uses a Kinesis data stream as a source.
- `"ElasticsearchDestinationConfiguration"`: The destination in Amazon ES. You can specify
  only one destination.
- `"ExtendedS3DestinationConfiguration"`: The destination in Amazon S3. You can specify
  only one destination.
- `"HttpEndpointDestinationConfiguration"`: Enables configuring Kinesis Firehose to deliver
  data to any HTTP endpoint destination. You can specify only one destination.
- `"KinesisStreamSourceConfiguration"`: When a Kinesis data stream is used as the source
  for the delivery stream, a KinesisStreamSourceConfiguration containing the Kinesis data
  stream Amazon Resource Name (ARN) and the role ARN for the source stream.
- `"RedshiftDestinationConfiguration"`: The destination in Amazon Redshift. You can specify
  only one destination.
- `"S3DestinationConfiguration"`: [Deprecated] The destination in Amazon S3. You can
  specify only one destination.
- `"SplunkDestinationConfiguration"`: The destination in Splunk. You can specify only one
  destination.
- `"Tags"`: A set of tags to assign to the delivery stream. A tag is a key-value pair that
  you can define and assign to AWS resources. Tags are metadata. For example, you can add
  friendly names and descriptions or other types of information that can help you distinguish
  the delivery stream. For more information about tags, see Using Cost Allocation Tags in the
  AWS Billing and Cost Management User Guide. You can specify up to 50 tags when creating a
  delivery stream.
"""
function create_delivery_stream(
    DeliveryStreamName; aws_config::AbstractAWSConfig=global_aws_config()
)
    return firehose(
        "CreateDeliveryStream",
        Dict{String,Any}("DeliveryStreamName" => DeliveryStreamName);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function create_delivery_stream(
    DeliveryStreamName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return firehose(
        "CreateDeliveryStream",
        Dict{String,Any}(
            mergewith(
                _merge, Dict{String,Any}("DeliveryStreamName" => DeliveryStreamName), params
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    delete_delivery_stream(delivery_stream_name)
    delete_delivery_stream(delivery_stream_name, params::Dict{String,<:Any})

Deletes a delivery stream and its data. To check the state of a delivery stream, use
DescribeDeliveryStream. You can delete a delivery stream only if it is in one of the
following states: ACTIVE, DELETING, CREATING_FAILED, or DELETING_FAILED. You can't delete a
delivery stream that is in the CREATING state. While the deletion request is in process,
the delivery stream is in the DELETING state. While the delivery stream is in the DELETING
state, the service might continue to accept records, but it doesn't make any guarantees
with respect to delivering the data. Therefore, as a best practice, first stop any
applications that are sending records before you delete a delivery stream.

# Arguments
- `delivery_stream_name`: The name of the delivery stream.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"AllowForceDelete"`: Set this to true if you want to delete the delivery stream even if
  Kinesis Data Firehose is unable to retire the grant for the CMK. Kinesis Data Firehose
  might be unable to retire the grant due to a customer error, such as when the CMK or the
  grant are in an invalid state. If you force deletion, you can then use the RevokeGrant
  operation to revoke the grant you gave to Kinesis Data Firehose. If a failure to retire the
  grant happens due to an AWS KMS issue, Kinesis Data Firehose keeps retrying the delete
  operation. The default value is false.
"""
function delete_delivery_stream(
    DeliveryStreamName; aws_config::AbstractAWSConfig=global_aws_config()
)
    return firehose(
        "DeleteDeliveryStream",
        Dict{String,Any}("DeliveryStreamName" => DeliveryStreamName);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function delete_delivery_stream(
    DeliveryStreamName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return firehose(
        "DeleteDeliveryStream",
        Dict{String,Any}(
            mergewith(
                _merge, Dict{String,Any}("DeliveryStreamName" => DeliveryStreamName), params
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    describe_delivery_stream(delivery_stream_name)
    describe_delivery_stream(delivery_stream_name, params::Dict{String,<:Any})

Describes the specified delivery stream and its status. For example, after your delivery
stream is created, call DescribeDeliveryStream to see whether the delivery stream is ACTIVE
and therefore ready for data to be sent to it.  If the status of a delivery stream is
CREATING_FAILED, this status doesn't change, and you can't invoke CreateDeliveryStream
again on it. However, you can invoke the DeleteDeliveryStream operation to delete it. If
the status is DELETING_FAILED, you can force deletion by invoking DeleteDeliveryStream
again but with DeleteDeliveryStreamInputAllowForceDelete set to true.

# Arguments
- `delivery_stream_name`: The name of the delivery stream.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"ExclusiveStartDestinationId"`: The ID of the destination to start returning the
  destination information. Kinesis Data Firehose supports one destination per delivery stream.
- `"Limit"`: The limit on the number of destinations to return. You can have one
  destination per delivery stream.
"""
function describe_delivery_stream(
    DeliveryStreamName; aws_config::AbstractAWSConfig=global_aws_config()
)
    return firehose(
        "DescribeDeliveryStream",
        Dict{String,Any}("DeliveryStreamName" => DeliveryStreamName);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function describe_delivery_stream(
    DeliveryStreamName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return firehose(
        "DescribeDeliveryStream",
        Dict{String,Any}(
            mergewith(
                _merge, Dict{String,Any}("DeliveryStreamName" => DeliveryStreamName), params
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    list_delivery_streams()
    list_delivery_streams(params::Dict{String,<:Any})

Lists your delivery streams in alphabetical order of their names. The number of delivery
streams might be too large to return using a single call to ListDeliveryStreams. You can
limit the number of delivery streams returned, using the Limit parameter. To determine
whether there are more delivery streams to list, check the value of HasMoreDeliveryStreams
in the output. If there are more delivery streams to list, you can request them by calling
this operation again and setting the ExclusiveStartDeliveryStreamName parameter to the name
of the last delivery stream returned in the last call.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"DeliveryStreamType"`: The delivery stream type. This can be one of the following
  values:    DirectPut: Provider applications access the delivery stream directly.
  KinesisStreamAsSource: The delivery stream uses a Kinesis data stream as a source.   This
  parameter is optional. If this parameter is omitted, delivery streams of all types are
  returned.
- `"ExclusiveStartDeliveryStreamName"`: The list of delivery streams returned by this call
  to ListDeliveryStreams will start with the delivery stream whose name comes alphabetically
  immediately after the name you specify in ExclusiveStartDeliveryStreamName.
- `"Limit"`: The maximum number of delivery streams to list. The default value is 10.
"""
function list_delivery_streams(; aws_config::AbstractAWSConfig=global_aws_config())
    return firehose(
        "ListDeliveryStreams"; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end
function list_delivery_streams(
    params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return firehose(
        "ListDeliveryStreams",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    list_tags_for_delivery_stream(delivery_stream_name)
    list_tags_for_delivery_stream(delivery_stream_name, params::Dict{String,<:Any})

Lists the tags for the specified delivery stream. This operation has a limit of five
transactions per second per account.

# Arguments
- `delivery_stream_name`: The name of the delivery stream whose tags you want to list.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"ExclusiveStartTagKey"`: The key to use as the starting point for the list of tags. If
  you set this parameter, ListTagsForDeliveryStream gets all tags that occur after
  ExclusiveStartTagKey.
- `"Limit"`: The number of tags to return. If this number is less than the total number of
  tags associated with the delivery stream, HasMoreTags is set to true in the response. To
  list additional tags, set ExclusiveStartTagKey to the last key in the response.
"""
function list_tags_for_delivery_stream(
    DeliveryStreamName; aws_config::AbstractAWSConfig=global_aws_config()
)
    return firehose(
        "ListTagsForDeliveryStream",
        Dict{String,Any}("DeliveryStreamName" => DeliveryStreamName);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function list_tags_for_delivery_stream(
    DeliveryStreamName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return firehose(
        "ListTagsForDeliveryStream",
        Dict{String,Any}(
            mergewith(
                _merge, Dict{String,Any}("DeliveryStreamName" => DeliveryStreamName), params
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    put_record(delivery_stream_name, record)
    put_record(delivery_stream_name, record, params::Dict{String,<:Any})

Writes a single data record into an Amazon Kinesis Data Firehose delivery stream. To write
multiple data records into a delivery stream, use PutRecordBatch. Applications using these
operations are referred to as producers. By default, each delivery stream can take in up to
2,000 transactions per second, 5,000 records per second, or 5 MB per second. If you use
PutRecord and PutRecordBatch, the limits are an aggregate across these two operations for
each delivery stream. For more information about limits and how to request an increase, see
Amazon Kinesis Data Firehose Limits.  You must specify the name of the delivery stream and
the data record when using PutRecord. The data record consists of a data blob that can be
up to 1,000 KiB in size, and any kind of data. For example, it can be a segment from a log
file, geographic location data, website clickstream data, and so on. Kinesis Data Firehose
buffers records before delivering them to the destination. To disambiguate the data blobs
at the destination, a common solution is to use delimiters in the data, such as a newline
(n) or some other character unique within the data. This allows the consumer application to
parse individual data items when reading the data from the destination. The PutRecord
operation returns a RecordId, which is a unique string assigned to each record. Producer
applications can use this ID for purposes such as auditability and investigation. If the
PutRecord operation throws a ServiceUnavailableException, back off and retry. If the
exception persists, it is possible that the throughput limits have been exceeded for the
delivery stream.  Data records sent to Kinesis Data Firehose are stored for 24 hours from
the time they are added to a delivery stream as it tries to send the records to the
destination. If the destination is unreachable for more than 24 hours, the data is no
longer available.  Don't concatenate two or more base64 strings to form the data fields of
your records. Instead, concatenate the raw data, then perform base64 encoding.

# Arguments
- `delivery_stream_name`: The name of the delivery stream.
- `record`: The record.

"""
function put_record(
    DeliveryStreamName, Record; aws_config::AbstractAWSConfig=global_aws_config()
)
    return firehose(
        "PutRecord",
        Dict{String,Any}("DeliveryStreamName" => DeliveryStreamName, "Record" => Record);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function put_record(
    DeliveryStreamName,
    Record,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return firehose(
        "PutRecord",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}(
                    "DeliveryStreamName" => DeliveryStreamName, "Record" => Record
                ),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    put_record_batch(delivery_stream_name, records)
    put_record_batch(delivery_stream_name, records, params::Dict{String,<:Any})

Writes multiple data records into a delivery stream in a single call, which can achieve
higher throughput per producer than when writing single records. To write single data
records into a delivery stream, use PutRecord. Applications using these operations are
referred to as producers. For information about service quota, see Amazon Kinesis Data
Firehose Quota. Each PutRecordBatch request supports up to 500 records. Each record in the
request can be as large as 1,000 KB (before base64 encoding), up to a limit of 4 MB for the
entire request. These limits cannot be changed. You must specify the name of the delivery
stream and the data record when using PutRecord. The data record consists of a data blob
that can be up to 1,000 KB in size, and any kind of data. For example, it could be a
segment from a log file, geographic location data, website clickstream data, and so on.
Kinesis Data Firehose buffers records before delivering them to the destination. To
disambiguate the data blobs at the destination, a common solution is to use delimiters in
the data, such as a newline (n) or some other character unique within the data. This allows
the consumer application to parse individual data items when reading the data from the
destination. The PutRecordBatch response includes a count of failed records,
FailedPutCount, and an array of responses, RequestResponses. Even if the PutRecordBatch
call succeeds, the value of FailedPutCount may be greater than 0, indicating that there are
records for which the operation didn't succeed. Each entry in the RequestResponses array
provides additional information about the processed record. It directly correlates with a
record in the request array using the same ordering, from the top to the bottom. The
response array always includes the same number of records as the request array.
RequestResponses includes both successfully and unsuccessfully processed records. Kinesis
Data Firehose tries to process all records in each PutRecordBatch request. A single record
failure does not stop the processing of subsequent records.  A successfully processed
record includes a RecordId value, which is unique for the record. An unsuccessfully
processed record includes ErrorCode and ErrorMessage values. ErrorCode reflects the type of
error, and is one of the following values: ServiceUnavailableException or InternalFailure.
ErrorMessage provides more detailed information about the error. If there is an internal
server error or a timeout, the write might have completed or it might have failed. If
FailedPutCount is greater than 0, retry the request, resending only those records that
might have failed processing. This minimizes the possible duplicate records and also
reduces the total bytes sent (and corresponding charges). We recommend that you handle any
duplicates at the destination. If PutRecordBatch throws ServiceUnavailableException, back
off and retry. If the exception persists, it is possible that the throughput limits have
been exceeded for the delivery stream. Data records sent to Kinesis Data Firehose are
stored for 24 hours from the time they are added to a delivery stream as it attempts to
send the records to the destination. If the destination is unreachable for more than 24
hours, the data is no longer available.  Don't concatenate two or more base64 strings to
form the data fields of your records. Instead, concatenate the raw data, then perform
base64 encoding.

# Arguments
- `delivery_stream_name`: The name of the delivery stream.
- `records`: One or more records.

"""
function put_record_batch(
    DeliveryStreamName, Records; aws_config::AbstractAWSConfig=global_aws_config()
)
    return firehose(
        "PutRecordBatch",
        Dict{String,Any}("DeliveryStreamName" => DeliveryStreamName, "Records" => Records);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function put_record_batch(
    DeliveryStreamName,
    Records,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return firehose(
        "PutRecordBatch",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}(
                    "DeliveryStreamName" => DeliveryStreamName, "Records" => Records
                ),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    start_delivery_stream_encryption(delivery_stream_name)
    start_delivery_stream_encryption(delivery_stream_name, params::Dict{String,<:Any})

Enables server-side encryption (SSE) for the delivery stream.  This operation is
asynchronous. It returns immediately. When you invoke it, Kinesis Data Firehose first sets
the encryption status of the stream to ENABLING, and then to ENABLED. The encryption status
of a delivery stream is the Status property in DeliveryStreamEncryptionConfiguration. If
the operation fails, the encryption status changes to ENABLING_FAILED. You can continue to
read and write data to your delivery stream while the encryption status is ENABLING, but
the data is not encrypted. It can take up to 5 seconds after the encryption status changes
to ENABLED before all records written to the delivery stream are encrypted. To find out
whether a record or a batch of records was encrypted, check the response elements
PutRecordOutputEncrypted and PutRecordBatchOutputEncrypted, respectively. To check the
encryption status of a delivery stream, use DescribeDeliveryStream. Even if encryption is
currently enabled for a delivery stream, you can still invoke this operation on it to
change the ARN of the CMK or both its type and ARN. If you invoke this method to change the
CMK, and the old CMK is of type CUSTOMER_MANAGED_CMK, Kinesis Data Firehose schedules the
grant it had on the old CMK for retirement. If the new CMK is of type CUSTOMER_MANAGED_CMK,
Kinesis Data Firehose creates a grant that enables it to use the new CMK to encrypt and
decrypt data and to manage the grant. If a delivery stream already has encryption enabled
and then you invoke this operation to change the ARN of the CMK or both its type and ARN
and you get ENABLING_FAILED, this only means that the attempt to change the CMK failed. In
this case, encryption remains enabled with the old CMK. If the encryption status of your
delivery stream is ENABLING_FAILED, you can invoke this operation again with a valid CMK.
The CMK must be enabled and the key policy mustn't explicitly deny the permission for
Kinesis Data Firehose to invoke KMS encrypt and decrypt operations. You can enable SSE for
a delivery stream only if it's a delivery stream that uses DirectPut as its source.  The
StartDeliveryStreamEncryption and StopDeliveryStreamEncryption operations have a combined
limit of 25 calls per delivery stream per 24 hours. For example, you reach the limit if you
call StartDeliveryStreamEncryption 13 times and StopDeliveryStreamEncryption 12 times for
the same delivery stream in a 24-hour period.

# Arguments
- `delivery_stream_name`: The name of the delivery stream for which you want to enable
  server-side encryption (SSE).

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"DeliveryStreamEncryptionConfigurationInput"`: Used to specify the type and Amazon
  Resource Name (ARN) of the KMS key needed for Server-Side Encryption (SSE).
"""
function start_delivery_stream_encryption(
    DeliveryStreamName; aws_config::AbstractAWSConfig=global_aws_config()
)
    return firehose(
        "StartDeliveryStreamEncryption",
        Dict{String,Any}("DeliveryStreamName" => DeliveryStreamName);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function start_delivery_stream_encryption(
    DeliveryStreamName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return firehose(
        "StartDeliveryStreamEncryption",
        Dict{String,Any}(
            mergewith(
                _merge, Dict{String,Any}("DeliveryStreamName" => DeliveryStreamName), params
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    stop_delivery_stream_encryption(delivery_stream_name)
    stop_delivery_stream_encryption(delivery_stream_name, params::Dict{String,<:Any})

Disables server-side encryption (SSE) for the delivery stream.  This operation is
asynchronous. It returns immediately. When you invoke it, Kinesis Data Firehose first sets
the encryption status of the stream to DISABLING, and then to DISABLED. You can continue to
read and write data to your stream while its status is DISABLING. It can take up to 5
seconds after the encryption status changes to DISABLED before all records written to the
delivery stream are no longer subject to encryption. To find out whether a record or a
batch of records was encrypted, check the response elements PutRecordOutputEncrypted and
PutRecordBatchOutputEncrypted, respectively. To check the encryption state of a delivery
stream, use DescribeDeliveryStream.  If SSE is enabled using a customer managed CMK and
then you invoke StopDeliveryStreamEncryption, Kinesis Data Firehose schedules the related
KMS grant for retirement and then retires it after it ensures that it is finished
delivering records to the destination. The StartDeliveryStreamEncryption and
StopDeliveryStreamEncryption operations have a combined limit of 25 calls per delivery
stream per 24 hours. For example, you reach the limit if you call
StartDeliveryStreamEncryption 13 times and StopDeliveryStreamEncryption 12 times for the
same delivery stream in a 24-hour period.

# Arguments
- `delivery_stream_name`: The name of the delivery stream for which you want to disable
  server-side encryption (SSE).

"""
function stop_delivery_stream_encryption(
    DeliveryStreamName; aws_config::AbstractAWSConfig=global_aws_config()
)
    return firehose(
        "StopDeliveryStreamEncryption",
        Dict{String,Any}("DeliveryStreamName" => DeliveryStreamName);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function stop_delivery_stream_encryption(
    DeliveryStreamName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return firehose(
        "StopDeliveryStreamEncryption",
        Dict{String,Any}(
            mergewith(
                _merge, Dict{String,Any}("DeliveryStreamName" => DeliveryStreamName), params
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    tag_delivery_stream(delivery_stream_name, tags)
    tag_delivery_stream(delivery_stream_name, tags, params::Dict{String,<:Any})

Adds or updates tags for the specified delivery stream. A tag is a key-value pair that you
can define and assign to AWS resources. If you specify a tag that already exists, the tag
value is replaced with the value that you specify in the request. Tags are metadata. For
example, you can add friendly names and descriptions or other types of information that can
help you distinguish the delivery stream. For more information about tags, see Using Cost
Allocation Tags in the AWS Billing and Cost Management User Guide.  Each delivery stream
can have up to 50 tags.  This operation has a limit of five transactions per second per
account.

# Arguments
- `delivery_stream_name`: The name of the delivery stream to which you want to add the tags.
- `tags`: A set of key-value pairs to use to create the tags.

"""
function tag_delivery_stream(
    DeliveryStreamName, Tags; aws_config::AbstractAWSConfig=global_aws_config()
)
    return firehose(
        "TagDeliveryStream",
        Dict{String,Any}("DeliveryStreamName" => DeliveryStreamName, "Tags" => Tags);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function tag_delivery_stream(
    DeliveryStreamName,
    Tags,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return firehose(
        "TagDeliveryStream",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}(
                    "DeliveryStreamName" => DeliveryStreamName, "Tags" => Tags
                ),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    untag_delivery_stream(delivery_stream_name, tag_keys)
    untag_delivery_stream(delivery_stream_name, tag_keys, params::Dict{String,<:Any})

Removes tags from the specified delivery stream. Removed tags are deleted, and you can't
recover them after this operation successfully completes. If you specify a tag that doesn't
exist, the operation ignores it. This operation has a limit of five transactions per second
per account.

# Arguments
- `delivery_stream_name`: The name of the delivery stream.
- `tag_keys`: A list of tag keys. Each corresponding tag is removed from the delivery
  stream.

"""
function untag_delivery_stream(
    DeliveryStreamName, TagKeys; aws_config::AbstractAWSConfig=global_aws_config()
)
    return firehose(
        "UntagDeliveryStream",
        Dict{String,Any}("DeliveryStreamName" => DeliveryStreamName, "TagKeys" => TagKeys);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function untag_delivery_stream(
    DeliveryStreamName,
    TagKeys,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return firehose(
        "UntagDeliveryStream",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}(
                    "DeliveryStreamName" => DeliveryStreamName, "TagKeys" => TagKeys
                ),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    update_destination(current_delivery_stream_version_id, delivery_stream_name, destination_id)
    update_destination(current_delivery_stream_version_id, delivery_stream_name, destination_id, params::Dict{String,<:Any})

Updates the specified destination of the specified delivery stream. Use this operation to
change the destination type (for example, to replace the Amazon S3 destination with Amazon
Redshift) or change the parameters associated with a destination (for example, to change
the bucket name of the Amazon S3 destination). The update might not occur immediately. The
target delivery stream remains active while the configurations are updated, so data writes
to the delivery stream can continue during this process. The updated configurations are
usually effective within a few minutes. Switching between Amazon ES and other services is
not supported. For an Amazon ES destination, you can only update to another Amazon ES
destination. If the destination type is the same, Kinesis Data Firehose merges the
configuration parameters specified with the destination configuration that already exists
on the delivery stream. If any of the parameters are not specified in the call, the
existing values are retained. For example, in the Amazon S3 destination, if
EncryptionConfiguration is not specified, then the existing EncryptionConfiguration is
maintained on the destination. If the destination type is not the same, for example,
changing the destination from Amazon S3 to Amazon Redshift, Kinesis Data Firehose does not
merge any parameters. In this case, all parameters must be specified. Kinesis Data Firehose
uses CurrentDeliveryStreamVersionId to avoid race conditions and conflicting merges. This
is a required field, and the service updates the configuration only if the existing
configuration has a version ID that matches. After the update is applied successfully, the
version ID is updated, and can be retrieved using DescribeDeliveryStream. Use the new
version ID to set CurrentDeliveryStreamVersionId in the next call.

# Arguments
- `current_delivery_stream_version_id`: Obtain this value from the VersionId result of
  DeliveryStreamDescription. This value is required, and helps the service perform
  conditional operations. For example, if there is an interleaving update and this value is
  null, then the update destination fails. After the update is successful, the VersionId
  value is updated. The service then performs a merge of the old configuration with the new
  configuration.
- `delivery_stream_name`: The name of the delivery stream.
- `destination_id`: The ID of the destination.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"AmazonopensearchserviceDestinationUpdate"`:
- `"ElasticsearchDestinationUpdate"`: Describes an update for a destination in Amazon ES.
- `"ExtendedS3DestinationUpdate"`: Describes an update for a destination in Amazon S3.
- `"HttpEndpointDestinationUpdate"`: Describes an update to the specified HTTP endpoint
  destination.
- `"RedshiftDestinationUpdate"`: Describes an update for a destination in Amazon Redshift.
- `"S3DestinationUpdate"`: [Deprecated] Describes an update for a destination in Amazon S3.
- `"SplunkDestinationUpdate"`: Describes an update for a destination in Splunk.
"""
function update_destination(
    CurrentDeliveryStreamVersionId,
    DeliveryStreamName,
    DestinationId;
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return firehose(
        "UpdateDestination",
        Dict{String,Any}(
            "CurrentDeliveryStreamVersionId" => CurrentDeliveryStreamVersionId,
            "DeliveryStreamName" => DeliveryStreamName,
            "DestinationId" => DestinationId,
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function update_destination(
    CurrentDeliveryStreamVersionId,
    DeliveryStreamName,
    DestinationId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return firehose(
        "UpdateDestination",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}(
                    "CurrentDeliveryStreamVersionId" => CurrentDeliveryStreamVersionId,
                    "DeliveryStreamName" => DeliveryStreamName,
                    "DestinationId" => DestinationId,
                ),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

ext: jl


text:
using Hashids

h = Hashid("salt")

# Testing against single random positive ints
for i in 1:1000
    test_id = abs(rand(Int))
    hashed_id = encode(h,test_id)
    unhashed_id = first(decode(h, hashed_id))
    @assert unhashed_id == test_id
end

# Test against multi-arity version
for i in 1:100
    test_ids = abs.(rand(Int,10))
    hashed_ids = encode(h,test_ids...)
    unhashed_ids = decode(h, hashed_ids)
    @assert unhashed_ids == test_ids
end


text:
using Mux, WebSockets, JSON, Lazy, Mustache

export Page, id, active

include("api.jl")

# Content

mutable struct Page
  id::Int
  sock::WebSocket
  handlers::Dict{String, Any}
  cb::Future

  function Page(init = nothing)
    serve()
    p = new(gen_id())
    p.handlers = Dict()
    p.cb = Future()
    init == nothing || (p.handlers["init"] = init)
    enable_callbacks!(p)
    pool[p.id] = WeakRef(p)
    finalizer(p) do p
      delete!(pool, p.id)
    end
    return p
  end
end

include("config.jl")

id(p::Page) = p.id
active(p::Page) = isdefined(p, :sock) && isopen(p.sock) && isopen(p.sock.socket)
handlers(p::Page) = p.handlers

function Base.wait(p::Page)
  wait(p.cb)
  return p
end

function msg(p::Page, m)
  active(p) || wait(p)
  write(p.sock, json(m))
end

const pool = Dict{Int, WeakRef}()

function gen_id()
  i = 1
  while haskey(pool, i) i += 1 end
  return i
end

include("server.jl")

@init for r in ["blink.js", "blink.css", "reset.css", "spinner.css"]
  resource(resolve_blink_asset("res", r))
end


text:
@testset "12.integer-to-roman.jl" begin
    @test all(roman_to_integer(int_to_roman(i)) == i for i in 1:3999)
end


text:
# Note that this script can accept some limited command-line arguments, run
# `julia build_tarballs.jl --help` to see a usage message.
using BinaryBuilder, Pkg

name = "ngraph"
version = v"0.0.1"

# Collection of sources required to complete build
sources = [
    GitSource("https://github.com/NervanaSystems/ngraph.git", "81ca5be950bb62b97c5f6f96616c9de5b39ebf45"),
    GitSource("https://github.com/Kitware/Cmake.git", "dee2eff2cfb4d0743c5b2c3468e2b9227baff102")
]

# Bash recipe for building across all platforms
script = raw"""
cd $WORKSPACE/srcdir
cd Cmake
mkdir build
cd build
cmake .. -DCMAKE_INSTALL_PREFIX=$prefix -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TARGET_TOOLCHAIN} -DCMAKE_BUILD_TYPE=Release
make -j$(nproc)
make install
cd ${WORKSPACE}/srcdir/ngraph
mkdir build
cd build
${WORKSPACE}/destdir/bin/cmake ..     -DCMAKE_INSTALL_PREFIX=$prefix     -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TARGET_TOOLCHAIN}     -DCMAKE_BUILD_TYPE=Release     -DNGRAPH_TBB_ENABLE=false     -DNGRAPH_CPU_CODEGEN_ENABLE=true
export PATH="${PATH}:$(pwd)/src/resource"
make -j$(nproc)
make install
exit
"""

# These are the platforms we will build for by default, unless further
# platforms are passed in on the command line
platforms = [
    Linux(:x86_64, libc=:glibc)
]

# The products that we will ensure are always built
products = [
    LibraryProduct("libdnnl", :libdnnl),
    LibraryProduct("libinterpreter_backend", :libinterpreter_backend),
    LibraryProduct("libnop_backend", :libnop_backend),
    LibraryProduct("libiomp5", :libiomp5),
    LibraryProduct("libmklml_intel", :libmklml_intel),
    LibraryProduct("libcodegen", :libcodegen),
    LibraryProduct("libeval_backend", :libeval_backend),
    LibraryProduct("libcpu_backend", :libcpu_backend),
    LibraryProduct("libngraph", :libngraph)
]

# Dependencies that must be installed before this package can be built
dependencies = [
    Dependency(PackageSpec(name="OpenSSL_jll", uuid="458c3c95-2e84-50aa-8efc-19380b2a3a95"))
]

# Build the tarballs, and possibly a `build.jl` as well.
build_tarballs(ARGS, name, version, sources, script, platforms, products, dependencies; preferred_gcc_version = v"8.1.0")


text:
################################################################################

# if CUDAapi.has_cuda()
  # # @info "CUDA is on"
  # import CuArrays
  # CuArrays.allowscalar(false)
# end

################################################################################

"""

    modelTrain!(model, inputAr;
    nnParams)

# Description
Train autoencoder.

# Arguments
`inputAr` array to train on.

`model` neural network architecture.

arguments passed as `nnParams` with `Parameters::@with_kw`

See also: [`buildAutoencoder`](@ref)
"""
function modelTrain!(model, inputAr; nnParams)
  args = nnParams()

  @info("Loading data...")
  trainAr = args.device.(inputAr)

  @info("Training model...")
  loss(χ) = mse(model(χ), χ)

  # training
  evalcb = throttle(() -> @show(loss(trainAr[1])), args.throttle)
  opt = ADAM(args.η)

  @epochs args.epochs train!(loss, params(model), zip(trainAr), opt, cb = evalcb)

  return model
end

################################################################################

max_stars_repo_name: DanielRivasMD/MindReader

max_issues_repo_name: DanielRivasMD/MindReader

max_forks_repo_name: DanielRivasMD/MindReader


text:
# Model m2.1s

using Pkg, DrWatson

begin
    using Distributions
    using StanQuap
    using StatisticalRethinking
end

# Define the Stan language model

stan2_1 = "
// Inferring a Rate
data {
  int N;
  int<lower=0> k[N];
  int<lower=1> n[N];
}
parameters {
  real<lower=0,upper=1> theta;
}
model {
  // Prior Distribution for Rate Theta
  theta ~ beta(1, 1);

  // Observed Counts
  k ~ binomial(n, theta);
}
";

# Use 16 observations
# Input data for cmdstan

N = 15
d = Binomial(9, 0.66)
k = rand(d, N)
n = repeat([9], N)
data = (N = N, n = n, k = k);
init = (theta = 0.5,);

# Sample using cmdstan

q2_1s, m2_1s, o2_1s = stan_quap("m2.1", stan2_1; data, init);

# Describe the draws

if !isnothing(m2_1s)
  part2_1s = read_samples(m2_1s, :particles)
end

if q2_1s.converged
  quap2_1s_df = sample(q2_1s)
  precis(quap2_1s_df)
end


text:
# Draw Primitives
function mark(gr::Graph, x::Real, y::Real, z::Real, mark::String)
    cmgl.mark(gr.ptr, x, y, z, mark)
end

function mark(gr::Graph, x::Real, y::Real, mark::String)
    cmgl.mark(gr.ptr, x, y, 0., mark)
end

function mark(gr::Graph, p::AbstractArray, mark::String)
    if length(mark) == 3
        cmgl.mark(gr.ptr, p..., mark)
    elseif length(mark) == 2
        cmgl.mark(gr.ptr, p..., 0., mark)
    else
        error("Lengths of the input vectors must match (either 2 or 3 dimensional)")
    end
end

# Ball function with color seems to be not implemented by MathGL Capi
function ball(gr::Graph, x::Real, y::Real)
    cmgl.ball(gr.ptr, x, y, 0.)
end

function ball(gr::Graph, x::Real, y::Real, z::Real)
    cmgl.ball(gr.ptr, x, y, z)
end

function ball(gr::Graph, p::AbstractArray, ball::String)
    if length(ball) == 3
        cmgl.ball(gr.ptr, p...)
    elseif length(ball) == 2
        cmgl.ball(gr.ptr, p..., 0.)
    else
        error("Lengths of the input vectors must match (either 2 or 3 dimensional)")
    end
end

function errbox(gr::Graph, x::Real, y::Real, z::Real, ex::Real, ey::Real, ez::Real; stl::String="")
    cmgl.error_box(gr.ptr, x, y, z, ex, ey, ez, stl)
end

function errbox(gr::Graph, x::Real, y::Real, ex::Real, ey::Real; stl::String="")
    cmgl.error_box(gr.ptr, x, y, 0., ex, ey, NaN, stl)
end

function errbox(gr::Graph, p::AbstractArray, ep::AbstractArray; stl::String="")
    if length(p) == length(ep) == 2
        cmgl.error_box(gr.ptr, p..., 0., ep..., NaN, stl)
    elseif length(p) == length(ep) == 3
        cmgl.error_box(gr.ptr, p..., ep..., stl=stl)
    else
        error("Lengths of the input vectors must match (either 2 or 3 dimensional)")
    end
end

function line(gr::Graph, x1::Real, y1::Real, z1::Real, x2::Real, y2::Real, z2::Real; stl::String="B", num::Int=2)
    cmgl.line(gr.ptr, x1, y1, z1, x2, y2, z2, stl, num)
end

function line(gr::Graph, x1::Real, y1::Real, x2::Real, y2::Real; stl::String="B", num::Int=2)
    cmgl.line(gr.ptr, x1, y1, 0., x2, y2, 0., stl, num)
end

function line(gr::Graph, p1::AbstractArray, p2::AbstractArray; stl::String="B", num::Int=2)
    if length(p1) == length(p2) == 2
        cmgl.line(gr.ptr, p1..., 0., p2..., 0., stl=stl, num=num)
    elseif length(p1) == length(p2) == 3
        cmgl.line(gr.ptr, p1..., p2..., stl=stl, num=num)
    else
        error("Lengths of the input vectors must match (either 2 or 3 dimensional)")
    end
end

function curve(gr::Graph, x1::Real, y1::Real, z1::Real, dx1::Real, dy1::Real, dz1::Real, x2::Real, y2::Real, z2::Real, dx2::Real, dy2::Real, dz2::Real; stl::String="B", num::Int=100)
    cmgl.curve(gr.ptr, x1, y1, z1, dx1, dy1, dz1, x2, y2, z2, dx2, dy2, dz2, stl, num)
end

function curve(gr::Graph, x1::Real, y1::Real, dx1::Real, dy1::Real, x2::Real, y2::Real, dx2::Real, dy2::Real; stl::String="B", num::Int=100)
    cmgl.curve(gr.ptr, x1, y1, 0., dx1, dy1, 0., x2, y2, 0., dx2, dy2, 0., stl, num)
end

function curve(gr::Graph, p1::AbstractArray, dp1::AbstractArray, p2::AbstractArray, dp2::AbstractArray; stl::String="B", num::Int=100)
    if length(p1) == length(dp1) == length(p2) == length(dp2) == 2
        cmgl.curve(gr.ptr, p1..., 0., dp1..., 0., p2..., 0., dp2..., 0., stl=stl, num=num)
    elseif length(p1) == length(dp1) == length(p2) == length(dp2) == 3
        cmgl.curve(gr.ptr, p1..., dp1..., p2..., dp2..., stl=stl, num=num)
    else
        error("Lengths of the input vectors must match (either 2 or 3 dimensional)")
    end
end

function face(gr::Graph, x1::Real, y1::Real, z1::Real, x2::Real, y2::Real, z2::Real, x3::Real, y3::Real, z3::Real, x4::Real, y4::Real, z4::Real; stl::String="w")
    cmgl.face(gr.ptr, x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4, stl)
end

function face(gr::Graph, x1::Real, y1::Real, x2::Real, y2::Real, x3::Real, y3::Real, x4::Real, y4::Real; stl::String="w")
    cmgl.face(gr.ptr, x1, y1, 0., x2, y2, 0., x3, y3, 0., x4, y4, 0., stl)
end

function face(gr::Graph, p1::AbstractArray, p2::AbstractArray, p3::AbstractArray, p4::AbstractArray; stl::String="w")
    if length(p1) == length(p2) == length(p3) == length(p4) == 2
        cmgl.face(gr.ptr, p1..., 0., p2..., 0., p3..., 0., p4..., 0., stl=stl)
    elseif length(p1) == length(p2) == length(p3) == length(p4) == 3
        cmgl.face(gr.ptr, p1..., p2..., p3..., p4..., stl=stl)
    else
        error("Lengths of the input vectors must match (either 2 or 3 dimensional)")
    end
end

# TODO: facex, facey, facez
#=function facex(gr::Graph, x1::Int, y1::Int, z1::Int, x2::Int, y2::Int, z2::Int, x3::Int, y3::Int, z3::Int, x4::Int, y4::Int, z4::Int; stl::String="w") =#
    #=cmgl.face(gr.ptr, x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4, stl)=#
#=end=#

function sphere(gr::Graph, x0::Real, y0::Real, z0::Real, r::Real; stl::String="r")
    cmgl.sphere(gr.ptr, x0, y0, z0, r, stl)
end

function sphere(gr::Graph, x0::Real, y0::Real, r::Real; stl::String="r")
    cmgl.sphere(gr.ptr, x0, y0, 0., r, stl)
end

function sphere(gr::Graph, p::AbstractArray, r::Real; stl="r")
    if length(p) == 2
        cmgl.sphere(gr.ptr, p..., 0., r, stl)
    elseif length(p) == 3
        cmgl.sphere(gr.ptr, p..., r, stl)
    else
        error("Lengths of the input vectors must match (either 2 or 3 dimensional)")
    end
end

function drop(gr::Graph, x0::Real, y0::Real, z0::Real, dx::Real, dy::Real, dz::Real, r::Real; stl::String="r", sh::Real=1., asp::Real=1.)
    cmgl.drop(gr.ptr, x0, y0, z0, dx, dy, dz, r, stl, sh, asp)
end

function drop(gr::Graph, x0::Real, y0::Real, dx::Real, dy::Real, r::Real; stl="r", sh=1., asp=1.)
    cmgl.drop(gr.ptr, x0, y0, 0., dx, dy, 0., r, stl, sh, asp)
end

function drop(gr::Graph, p::AbstractArray, d::AbstractArray, r::Real; stl::String="r", sh::Real=1., asp::Real=1.)
    if length(p) == length(d) == 2
        cmgl.sphere(gr.ptr, p..., 0., d..., 0., r, stl)
    elseif length(p) == length(d) == 3
        cmgl.sphere(gr.ptr, p..., d..., r, stl)
    else
        error("Lengths of the input vectors must match (either 2 or 3 dimensional)")
    end
end

function cone(gr::Graph, x1::Real, y1::Real, z1::Real, x2::Real, y2::Real, z2::Real, r1::Real; r2::Real=-1., stl::String="B")
    cmgl.cone(gr.ptr, x1, y1, z1, x2,y2, z2, r1, r2, stl)
end

function cone(gr::Graph, p1::AbstractArray, p2::AbstractArray, r1::Real; r2::Real=-1., stl::String="B")
    if length(p1) == length(p2) == 3
        cmgl.cone(gr.ptr, x1, y1, z1, x2,y2, z2, r1, r2, stl)
    else
        error("Lengths of the input vectors must match and be 3 dimensional")
    end
end

#TODO: Get to know why there is no C function for circle
function circle(gr::Graph, x0::Real, y0::Real, z0::Real, r::Real; stl::String="r")
    cmgl.ellipse(gr.ptr, x0, y0, z0, x0, y0, z0, r, stl)
end

function circle(gr::Graph, x0::Real, y0::Real, r::Real; stl::String="r")
    cmgl.ellipse(gr.ptr, x0, y0, 0., x0, y0, 0., r, stl)
end

function circle(gr::Graph, p0::AbstractArray, r::Real; stl::String="r")
    if length(p0) == 3
        cmgl.ellipse(gr.ptr, p0..., p0..., r, stl)
    elseif length(p0) == 2
        cmgl.ellipse(gr.ptr, p0..., 0., p0..., 0., r, stl)
    else
        error("Lengths of the input vectors must match (either 2 or 3 dimensional)")
    end
end

function ellipse(gr::Graph, x1::Real, y1::Real, z1::Real, x2::Real, y2::Real, z2::Real, r::Real; stl::String="r")
    cmgl.ellipse(gr.ptr, x1, y1, z1, x2, y2, z2, r, stl)
end

function ellipse(gr::Graph, x1::Real, y1::Real, x2::Real, y2::Real, r::Real; stl::String="r")
    cmgl.ellipse(gr.ptr, x1, y1, 0., x2, y2, 0., r, stl)
end

function ellipse(gr::Graph, p1::AbstractArray, p2::AbstractArray, r::Real; stl::String=r)
    if length(p1) == length(p2) == 3
        cmgl.ellipse(gr.ptr, p1..., p2..., r, stl)
    elseif length(p1) == length(p2) == 2
        cmgl.ellipse(gr.ptr, p1..., 0., p2..., 0., r, stl)
    else
        error("Lengths of the input vectors must match (either 2 or 3 dimensional)")
    end
end

function rhomb(gr::Graph, x1::Real, y1::Real, z1::Real, x2::Real, y2::Real, z2::Real, r::Real; stl::String="r")
    cmgl.rhomb(gr.ptr, x1, y1, z1, x2, y2, z2, r, stl)
end

function rhomb(gr::Graph, x1::Real, y1::Real, x2::Real, y2::Real, r::Real; stl::String="r")
    cmgl.rhomb(gr.ptr, x1, y1, 0., x2, y2, 0., r, stl)
end

function rhomb(gr::Graph, p1::AbstractArray, p2::AbstractArray, r::Real; stl::String=r)
    if length(p1) == length(p2) == 3
        cmgl.rhomb(gr.ptr, p1..., p2..., r, stl)
    elseif length(p1) == length(p2) == 2
        cmgl.rhomb(gr.ptr, p1..., 0., p2..., 0., r, stl)
    else
        error("Lengths of the input vectors must match (either 2 or 3 dimensional)")
    end
end

# Arc for 3 dimensions
function arc(gr::Graph, x0::Real, y0::Real, z0::Real, x1::Real, y1::Real, z1::Real, a::Real; stl::String="r")
    cmgl.arc_ext(gr.ptr, x0, y0, z0, 0., 0., 1., x1, y1, z1, a, stl)
end

function arc(gr::Graph, x0::Real, y0::Real, z0::Real, xa::Real, ya::Real, za::Real, x1::Real, y1::Real, z1::Real, a::Real; stl::String="r")
    cmgl.arc_ext(gr.ptr, x0, y0, z0, xa, ya, za, x1, y1, z1, a, stl)
end

# 2d Arc function
function arc(gr::Graph, x0::Real, y0::Real, x1::Real, z1::Real, a::Real; stl::String="r")
    cmgl.arc(gr.ptr, x0, y0, x1, y1, a, stl)
end

function arc(gr::Graph, p0::AbstractArray, p1::AbstractArray, a::Real; stl::String="r")
    if length(p0) == length(p1) == 3
        cmgl.arc_ext(gr.ptr, p0..., 0., 0., 1., p1..., a, stl)
    elseif length(p1) == length(p2) == 2
        cmgl.arc(gr.ptr, p0..., p1..., a, stl)
    else
        error("Lengths of the input vectors must match (either 2 or 3 dimensional)")
    end
end

function arc(gr::Graph, p0::AbstractArray, pa::AbstractArray, p1::AbstractArray, a::Real; stl::String="r")
    if length(p0) == length(p1) == length(pa) == 3
        cmgl.arc_ext(gr.ptr, p0..., pa..., p1..., a, stl)
    else
        error("Lengths of the input vectors must match and be 3 dimensional)")
    end
end

function polygon(gr::Graph, x0::Real, y0::Real, z0::Real, x1::Real, y1::Real, z1::Real, num::Int; stl::String="r")
    cmgl.polygon(gr.ptr, x0, y0, z0, x1, y1, z1, num, stl)
end

function polygon(gr::Graph, x0::Real, y0::Real, x1::Real, y1::Real, num::Int; stl::String="r")
    cmgl.polygon(gr.ptr, x0, y0, 0., x1, y1, 0., num, stl)
end

function polygon(gr::Graph, p0::AbstractArray, p1::AbstractArray, num::Int; stl::String="r")
    if length(p0) == length(p1) == length(pa) == 3
        cmgl.polygon(gr.ptr, p0..., p1..., num, stl)
    elseif length(p0) == length(p1) == length(pa) == 2
        cmgl.polygon(gr.ptr, p0..., 0., p1..., 0., num, stl)
    else
        error("Lengths of the input vectors must match and be 3 dimensional)")
    end
end


text:
import Pkg
Pkg.activate(dirname(Base.current_project()))
Pkg.precompile()

using ODINN
using Test
using Flux
using JLD, PyCallJLD

@testset "SIA PDE simulations" begin include("PDE_solve.jl") end
@testset "SIA UDE training" begin include("UDE_train.jl") end


text:
@inline function indmin3(a,b,c,i,j)
    if a <= b
        if a <= c
            return 1,i-1,j-1
        else
            return 3,i,j-1
        end
    else
        if b <= c
            return 2,i-1,j
        else
            return 3,i,j-1
        end
    end
end

"""
    imin, imax = radiuslimits(r, n::Int, m::Int)
    imin, imax = radiuslimits(r, seq1, seq2)
"""
function radiuslimits(r, n::Int, m::Int)
    d = abs(m-n)
    if m >= n
        imin, imax = max.((1:n) .- r,1), min.((1:n) .+ (r+d), m)
    else
        imin, imax = max.((1:n) .- (r+d),1), min.((1:n) .+ r, m)
    end

    imin, imax
end

radiuslimits(r,seq1, seq2) = radiuslimits(r, lastlength(seq1), lastlength(seq2))

"""
    inds = align_signals(s::AbstractVector{<:AbstractArray}, master = argmax(length.(s)); method = :dtw, output=:indices)

Compute a set of indices such that `s[i][inds[i]]` is optimally aligned to `s[master]`. All elements of `inds` will have the same length.

# Arguments:
- `s`: A vector of signals to align
- `master`: Index of the signal used as reference. All the other signals will be aligned to this one.
- `method`: `:dtw` uses the warping paths from dtw between `s[master]` and `s[i]`. `:xcorr` uses `DSP.finddelay` which internally computes the cross correlation between signals, which often results in a slight misalignment.
- `output`: The default is to output the aligning indices, alternatively, the aligned `:signals` themselves can be outputted.
"""
function align_signals(s::AbstractVector{<:AbstractArray}, master::Integer=argmax(length.(s)); method=:dtw, output=:indices, postprocess=nothing)
    inds = [1:lastlength(s) for s in s]
    # find delays to align with master
    d = map(s) do si
        si === s[master] && (return 0)
        if method ∈ (:xcorr, :crosscorr, :dsp)
            SlidingDistancesBase.DSP.finddelay(s[master], si) # suboptimal because xcorr does not do exactly what we want
        elseif method ∈ (:dtw, :DTW)
            d,i1,i2 = dtw(si, s[master], postprocess=postprocess)
            round(Int, median(i2-i1))
        else
            throw(ArgumentError("Unknown method"))
        end
    end

    # find left and right (virtual) zero padding
    lp = maximum(d)
    rp = maximum(length(s[master]) .- (length.(s) .+ d))

    # New window length
    wl = length(inds[master]) - lp - rp

    # trim individual index sets to fit into new master window
    for i in eachindex(inds)
        start = max(1, 1+lp-d[i])
        stop = min(length(s[i]),start+wl-1)
        inds[i] = start : stop
    end
    @assert all(length.(inds) .== length(inds[1]))
    if output === :indices
        return inds
    else
        return [s[!,i] for (s,i) in zip(s,inds)]
    end
end


text:
module Memoize
export @memoize
using Compat

macro memoize(args...)
    if length(args) == 1
        dicttype = :(ObjectIdDict)
        ex = args[1]
    elseif length(args) == 2
        (dicttype, ex) = args
    else
        error("Memoize accepts at most two arguments")
    end

    if !isa(ex,Expr) || (ex.head != :function && ex.head != @compat(Symbol("="))) ||
       isempty(ex.args) || ex.args[1].head != :call || isempty(ex.args[1].args)
        error("@memoize must be applied to a method definition")
    end
    f = ex.args[1].args[1]
    ex.args[1].args[1] = u = @compat(Symbol(string(f,"_unmemoized")))

    args = ex.args[1].args[2:end]

    # Extract keywords from AST
    kws = Any[]
    vals = copy(args)
    if length(vals) > 0 && isa(vals[1], Expr) && vals[1].head == :parameters
        kws = shift!(vals).args
    end

    # Set up arguments for tuple to encode keywords
    tup = Array(Any, length(kws)+length(vals))
    i = 1
    for val in vals
        tup[i] = if isa(val, Expr)
                if val.head == :... || val.head == :kw
                    val.args[1]
                elseif val.head == :(::)
                    val
                else
                    error("@memoize did not understand method syntax $val")
                end
            else
                val
            end
        i += 1
    end

    for kw in kws
        if isa(kw, Expr) && (kw.head == :kw || kw.head == :...)
            tup[i] = kw.args[1]
        else
            error("@memoize did not understand method syntax")
        end
        i += 1
    end

    # Set up identity arguments to pass to unmemoized function
    identargs = Array(Any, (length(kws) > 0)+length(vals))
    i = (length(kws) > 0) + 1
    for val in vals
        if isa(val, Expr)
            if val.head == :kw
                val = val.args[1]
            end
            if isa(val, Expr) && val.head == :(::)
                val = val.args[1]
            end
        end
        identargs[i] = val
        i += 1
    end
    if length(kws) > 0
        identkws = map(kws) do kw
            if kw.head == :kw
                key = kw.args[1]
                if isa(key, Expr) && key.head == :(::)
                    key = key.args[1]
                end
                Expr(:kw, key, key)
            else
                kw
            end
        end
        identargs[1] = Expr(:parameters, identkws...)
    end

    # Generate function
    # This construction is bizarre, but it was the only thing I could devise
    # that passes the tests included with this package and also frees the cache
    # when a function is reloaded. Improvements are welcome.
    quote
        $(esc(ex))
        isdef = false
        try
            $(esc(f))
            isdef = true
        end
        if isdef
            for i = 1
                $(esc(quote
                    local fcache
                    const fcache = ($dicttype)()
                    $(f)($(args...),) =
                        haskey(fcache, ($(tup...),)) ? fcache[($(tup...),)] :
                        (fcache[($(tup...),)] = $(u)($(identargs...),))
                end))
            end
        else
            $(esc(quote
                const $(f) = let
                    local fcache, $f
                    const fcache = ($dicttype)()
                    $(f)($(args...),) =
                        haskey(fcache, ($(tup...),)) ? fcache[($(tup...),)] :
                        (fcache[($(tup...),)] = $(u)($(identargs...),))
                end
            end))
        end
    end
end
end


text:
module TestExamples2DPart1

using Test
using Trixi

include("test_trixi.jl")

# pathof(Trixi) returns /path/to/Trixi/src/Trixi.jl, dirname gives the parent directory
EXAMPLES_DIR = joinpath(pathof(Trixi) |> dirname |> dirname, "examples", "tree_2d_dgsem")

# Start with a clean environment: remove Trixi output directory if it exists
outdir = "out"
isdir(outdir) && rm(outdir, recursive=true)

@testset "2D-Part1" begin

# Run basic tests
@testset "Examples 2D" begin
  # Linear advection
  include("test_examples_2d_advection.jl")

  # Hyperbolic diffusion
  include("test_examples_2d_hypdiff.jl")

  # Compressible Euler
  include("test_examples_2d_euler.jl")
end

# Coverage test for all initial conditions
@testset "Tests for initial conditions" begin
  # Linear scalar advection
  @trixi_testset "elixir_advection_extended.jl with initial_condition_sin_sin" begin
    @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_extended.jl"),
      l2   = [0.0001424424872539405],
      linf = [0.0007260692243253875],
      maxiters = 1,
      initial_condition = Trixi.initial_condition_sin_sin)
  end

  @trixi_testset "elixir_advection_extended.jl with initial_condition_constant" begin
    @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_extended.jl"),
      l2   = [3.2933000250376106e-16],
      linf = [6.661338147750939e-16],
      maxiters = 1,
      initial_condition = initial_condition_constant)
  end

  @trixi_testset "elixir_advection_extended.jl with initial_condition_linear_x_y" begin
    @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_extended.jl"),
      l2   = [2.478798286796091e-16],
      linf = [7.105427357601002e-15],
      maxiters = 1,
      initial_condition = Trixi.initial_condition_linear_x_y,
      boundary_conditions = Trixi.boundary_condition_linear_x_y,
      periodicity=false)
  end

  @trixi_testset "elixir_advection_extended.jl with initial_condition_linear_x" begin
    @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_extended.jl"),
      l2   = [1.475643203742897e-16],
      linf = [1.5543122344752192e-15],
      maxiters = 1,
      initial_condition = Trixi.initial_condition_linear_x,
      boundary_conditions = Trixi.boundary_condition_linear_x,
      periodicity=false)
  end

  @trixi_testset "elixir_advection_extended.jl with initial_condition_linear_y" begin
    @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_extended.jl"),
      l2   = [1.5465148503676022e-16],
      linf = [3.6637359812630166e-15],
      maxiters = 1,
      initial_condition = Trixi.initial_condition_linear_y,
      boundary_conditions = Trixi.boundary_condition_linear_y,
      periodicity=false)
  end

  # Compressible Euler
  @trixi_testset "elixir_euler_vortex.jl one step with initial_condition_density_pulse" begin
    @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_vortex.jl"),
      l2   = [0.003489659044164644, 0.0034896590441646494, 0.0034896590441646502, 0.003489659044164646],
      linf = [0.04761180654650543, 0.04761180654650565, 0.047611806546505875, 0.04761180654650454],
      maxiters = 1,
      initial_condition = initial_condition_density_pulse)
  end

  @trixi_testset "elixir_euler_vortex.jl one step with initial_condition_pressure_pulse" begin
    @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_vortex.jl"),
      l2   = [0.00021747693728234874, 0.0022010142997830533, 0.0022010142997830485, 0.010855273768135729],
      linf = [0.005451116856088789, 0.03126448432601536, 0.03126448432601536, 0.14844305553724624],
      maxiters = 1,
      initial_condition = Trixi.initial_condition_pressure_pulse)
  end

  @trixi_testset "elixir_euler_vortex.jl one step with initial_condition_density_pressure_pulse" begin
    @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_vortex.jl"),
      l2   = [0.003473649182284682, 0.005490887132955628, 0.005490887132955635, 0.015625074774949926],
      linf = [0.046582178207169145, 0.07332265196082899, 0.07332265196082921, 0.2107979471941368],
      maxiters = 1,
      initial_condition = Trixi.initial_condition_density_pressure_pulse)
  end

  @trixi_testset "elixir_euler_vortex.jl one step with initial_condition_constant" begin
    @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_vortex.jl"),
      l2   = [7.89034964747135e-17, 8.095575651413758e-17, 1.0847287658433571e-16, 1.2897732640029767e-15],
      linf = [2.220446049250313e-16, 3.191891195797325e-16, 4.163336342344337e-16, 3.552713678800501e-15],
      maxiters = 1,
      initial_condition = initial_condition_constant)
  end

  @trixi_testset "elixir_euler_sedov_blast_wave.jl one step" begin
    @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_sedov_blast_wave.jl"),
      l2   = [0.0021037031798961914, 0.010667428589443025, 0.01066742858944302, 0.10738893384136498],
      linf = [0.11854059147646778, 0.7407961272348982, 0.7407961272348981, 3.92623931433345],
      maxiters=1)
  end

  @trixi_testset "elixir_euler_sedov_blast_wave.jl one step with initial_condition_medium_sedov_blast_wave" begin
    @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_sedov_blast_wave.jl"),
      l2   = [0.0021025532272874827, 0.010661548568022292, 0.010661548568022284, 0.10734939168392313],
      linf = [0.11848345578926645, 0.7404217490990809, 0.7404217490990809, 3.9247328712525973],
      maxiters=1, initial_condition=initial_condition_medium_sedov_blast_wave)
  end
end

@testset "Displaying components 2D" begin
  @test_nowarn include(joinpath(EXAMPLES_DIR, "elixir_advection_amr.jl"))

  # test both short and long printing formats
  @test_nowarn show(mesh); println()
  @test_nowarn println(mesh)
  @test_nowarn display(mesh)

  @test_nowarn show(equations); println()
  @test_nowarn println(equations)
  @test_nowarn display(equations)

  @test_nowarn show(solver); println()
  @test_nowarn println(solver)
  @test_nowarn display(solver)

  @test_nowarn show(solver.basis); println()
  @test_nowarn println(solver.basis)
  @test_nowarn display(solver.basis)

  @test_nowarn show(solver.mortar); println()
  @test_nowarn println(solver.mortar)
  @test_nowarn display(solver.mortar)

  @test_nowarn show(semi); println()
  @test_nowarn println(semi)
  @test_nowarn display(semi)

  @test_nowarn show(summary_callback); println()
  @test_nowarn println(summary_callback)
  @test_nowarn display(summary_callback)

  @test_nowarn show(amr_controller); println()
  @test_nowarn println(amr_controller)
  @test_nowarn display(amr_controller)

  @test_nowarn show(amr_callback); println()
  @test_nowarn println(amr_callback)
  @test_nowarn display(amr_callback)

  @test_nowarn show(stepsize_callback); println()
  @test_nowarn println(stepsize_callback)
  @test_nowarn display(stepsize_callback)

  @test_nowarn show(save_solution); println()
  @test_nowarn println(save_solution)
  @test_nowarn display(save_solution)

  @test_nowarn show(analysis_callback); println()
  @test_nowarn println(analysis_callback)
  @test_nowarn display(analysis_callback)

  @test_nowarn show(alive_callback); println()
  @test_nowarn println(alive_callback)
  @test_nowarn display(alive_callback)

  @test_nowarn println(callbacks)
end

# Clean up afterwards: delete Trixi output directory
@test_nowarn rm(outdir, recursive=true)

end # 2D-Part1

end #module


text:
const RESERVED = Set(["local", "global", "export", "let",
    "for", "struct", "while", "const", "continue", "import",
    "function", "if", "else", "try", "begin", "break", "catch",
    "return", "using", "baremodule", "macro", "finally",
    "module", "elseif", "end", "quote", "do"])

normalizename(name::Symbol) = name
function normalizename(name::String)
    uname = strip(Unicode.normalize(name))
    id = Base.isidentifier(uname) ? uname : map(c->Base.is_id_char(c) ? c : '_', uname)
    cleansed = string((!Base.is_id_start_char(id[1]) || id in RESERVED) ? "_" : "", id)
    return Symbol(replace(cleansed, r"(_)\1+"=>"_"))
end

function makeunique(names)
    set = Set(names)
    length(set) == length(names) && return names
    nms = Symbol[]
    for nm in names
        if nm in nms
            k = 1
            newnm = Symbol("$(nm)_$k")
            while newnm in set || newnm in nms
                k += 1
                newnm = Symbol("$(nm)_$k")
            end
            nm = newnm
        end
        push!(nms, nm)
    end
    return nms
end

function skiptoheader!(parsinglayers, io, row, header)
    while row < header
        while !eof(io)
            r = Parsers.parse(parsinglayers, io, Tuple{Ptr{UInt8}, Int})
            (r.code & Parsers.DELIMITED) > 0 && break
        end
        row += 1
    end
    return row
end

function countfields(io, parsinglayers)
    rows = 0
    result = Parsers.Result(Tuple{Ptr{UInt8}, Int})
    while !eof(io)
        result.code = Parsers.SUCCESS
        Parsers.parse!(parsinglayers, io, result)
        Parsers.ok(result.code) || throw(Error(result, rows+1, 1))
        rows += 1
        (result.code & Parsers.DELIMITED) > 0 && continue
        (newline(result.code) || eof(io)) && break
    end
    return rows
end

function datalayout_transpose(header, parsinglayers, io, datarow, footerskip, normalizenames)
    if isa(header, Integer) && header > 0
        # skip to header column to read column names
        row = skiptoheader!(parsinglayers, io, 1, header)
        # io now at start of 1st header cell
        columnnames = [Parsers.parse(parsinglayers, io, String).result::String]
        columnpositions = [position(io)]
        datapos = position(io)
        rows = countfields(io, parsinglayers)

        # we're now done w/ column 1, if EOF we're done, otherwise, parse column 2's column name
        cols = 1
        while !eof(io)
            # skip to header column to read column names
            row = skiptoheader!(parsinglayers, io, 1, header)
            cols += 1
            push!(columnnames, Parsers.parse(parsinglayers, io, String).result::String)
            push!(columnpositions, position(io))
            readline!(parsinglayers, io)
        end
        seek(io, datapos)
    elseif isa(header, AbstractRange)
        # column names span several columns
        throw(ArgumentError("not implemented for transposed csv files"))
    elseif eof(io)
        # emtpy file, use column names if provided
        datapos = position(io)
        columnpositions = Int[]
        columnnames = header
    else
        # column names provided explicitly or should be generated, they don't exist in data
        # skip to datarow
        row = skiptoheader!(parsinglayers, io, 1, datarow)
        # io now at start of 1st data cell
        columnnames = [isa(header, Integer) || isempty(header) ? "Column1" : header[1]]
        columnpositions = [position(io)]
        datapos = position(io)
        rows = countfields(io, parsinglayers)
        # we're now done w/ column 1, if EOF we're done, otherwise, parse column 2's column name
        cols = 1
        while !eof(io)
            # skip to datarow column
            row = skiptoheader!(parsinglayers, io, 1, datarow)
            cols += 1
            push!(columnnames, isa(header, Integer) || isempty(header) ? "Column$cols" : header[cols])
            push!(columnpositions, position(io))
            readline!(parsinglayers, io)
        end
        seek(io, datapos)
    end
    rows = rows - footerskip # rows now equals the actual number of rows in the dataset
    return rows, makeunique(map(x->normalizenames ? normalizename(x) : Symbol(x), columnnames)), columnpositions
end

function datalayout(header::Integer, parsinglayers, io, datarow, normalizenames)
    # default header = 1
    if header <= 0
        # no header row in dataset; skip to data to figure out # of columns
        skipto!(parsinglayers, io, 1, datarow)
        datapos = position(io)
        row_vals = readsplitline(parsinglayers, io)
        seek(io, datapos)
        columnnames = [Symbol("Column$i") for i = eachindex(row_vals)]
    else
        skipto!(parsinglayers, io, 1, header)
        columnnames = makeunique([ismissing(x) ? Symbol("Column$i") : (normalizenames ? normalizename(x) : Symbol(x)) for (i, x) in enumerate(readsplitline(parsinglayers, io))])
        datarow != header+1 && skipto!(parsinglayers, io, header+1, datarow)
        datapos = position(io)
    end
    return columnnames, datapos
end

function datalayout(header::AbstractRange, parsinglayers, io, datarow, normalizenames)
    skipto!(parsinglayers, io, 1, first(header))
    columnnames = [x for x in readsplitline(parsinglayers, io)]
    for row = first(header):(last(header)-1)
        for (i,c) in enumerate([x for x in readsplitline(parsinglayers, io)])
            columnnames[i] *= "_" * c
        end
    end
    datarow != last(header)+1 && skipto!(parsinglayers, io, last(header)+1, datarow)
    datapos = position(io)
    return makeunique([normalizenames ? normalizename(nm) : Symbol(nm) for nm in columnnames]), datapos
end

function datalayout(header::Vector, parsinglayers, io, datarow, normalizenames)
    skipto!(parsinglayers, io, 1, datarow)
    datapos = position(io)
    if eof(io)
        columnnames = makeunique([normalizenames ? normalizename(nm) : Symbol(nm) for nm in header])
    else
        row_vals = readsplitline(parsinglayers, io)
        seek(io, datapos)
        if isempty(header)
            columnnames = [Symbol("Column$i") for i in eachindex(row_vals)]
        else
            length(header) == length(row_vals) || throw(ArgumentError("The length of provided header ($(length(header))) doesn't match the number of columns at row $datarow ($(length(row_vals)))"))
            columnnames = makeunique([normalizenames ? normalizename(nm) : Symbol(nm) for nm in header])
        end
    end
    return columnnames, datapos
end

const READLINE_RESULT = Parsers.Result(Tuple{Ptr{UInt8}, Int})
# readline! is used for implementation of skipto!
function readline!(layers, io::IO)
    eof(io) && return
    while true
        READLINE_RESULT.code = Parsers.SUCCESS
        res = Parsers.parse!(layers, io, READLINE_RESULT)
        Parsers.ok(res.code) || throw(Parsers.Error(res))
        (newline(res.code) || eof(io)) && break
    end
    return
end

function skipto!(layers, io::IO, cur, dest)
    cur >= dest && return
    for _ = 1:(dest-cur)
        readline!(layers, io)
    end
    return
end

const READSPLITLINE_RESULT = Parsers.Result(String)
const DELIM_NEWLINE = Parsers.DELIMITED | Parsers.NEWLINE

readsplitline(io::IO; delim=",") = readsplitline(Parsers.Delimited(Parsers.Quoted(), delim; newline=true), io)
function readsplitline(layers::Parsers.Delimited, io::IO)
    vals = Union{String, Missing}[]
    eof(io) && return vals
    col = 1
    result = READSPLITLINE_RESULT
    while true
        result.code = Parsers.SUCCESS
        Parsers.parse!(layers, io, result)
        # @debug "readsplitline!: result=$result"
        Parsers.ok(result.code) || throw(Error(Parsers.Error(io, result), 1, col))
        # @show result
        push!(vals, result.result)
        col += 1
        (result.code & Parsers.DELIMITED) > 0 && continue
        (newline(result.code) || eof(io)) && break
    end
    return vals
end

consumecommentedline!(layers, io, ::Nothing) = nothing
function consumecommentedline!(layers, io, comment::Parsers.Trie)
    while Parsers.match!(comment, io, READLINE_RESULT, false)
        readline!(layers, io)
    end
end

function rowpositions(io::IO, q::UInt8, e::UInt8, limit::Nothing, layers, comment)
    nl = Int64[position(io)] # we always start at the beginning of the first data row
    b = 0x00
    while !eof(io)
        b = Parsers.readbyte(io)
        if b === q
            while !eof(io)
                b = Parsers.readbyte(io)
                if b === e
                    if eof(io)
                        break
                    elseif e === q && Parsers.peekbyte(io) !== q
                        break
                    end
                    b = Parsers.readbyte(io)
                elseif b === q
                    break
                end
            end
        elseif b === UInt8('\n')
            consumecommentedline!(layers, io, comment)
            !eof(io) && push!(nl, position(io))
        elseif b === UInt8('\r')
            !eof(io) && Parsers.peekbyte(io) === UInt8('\n') && Parsers.readbyte(io)
            consumecommentedline!(layers, io, comment)
            !eof(io) && push!(nl, position(io))
        end
    end
    return nl
end

function rowpositions(io::IO, q::UInt8, e::UInt8, limit::Int, layers, comment)
    nl = Vector{Int64}(undef, limit)
    limit == 0 && return nl
    nl[1] = position(io) # we always start at the beginning of the first data row
    b = 0x00
    i = 2
    while !eof(io) && i <= limit
        b = Parsers.readbyte(io)
        if b === q
            while !eof(io)
                b = Parsers.readbyte(io)
                if b === e
                    if eof(io)
                        break
                    elseif e === q && Parsers.peekbyte(io) !== q
                        break
                    end
                    b = Parsers.readbyte(io)
                elseif b === q
                    break
                end
            end
        elseif b === UInt8('\n')
            consumecommentedline!(layers, io, comment)
            !eof(io) && setindex!(nl, position(io), i)
            i += 1
        elseif b === UInt8('\r')
            !eof(io) && Parsers.peekbyte(io) === UInt8('\n') && Parsers.readbyte(io)
            consumecommentedline!(layers, io, comment)
            !eof(io) && setindex!(nl, position(io), i)
            i += 1
        end
    end
    (i-1) < limit && resize!(nl, i-1)
    return nl
end


text:
"""
nz, nx is the size of window, oz, ox is the starting index
temporarily is added as explosive source
"""
type WSrcCube
     oz :: Int64
     ox :: Int64
     nz :: Int64
     nx :: Int64
     ext:: Int64
     iflag :: Int64
     ot :: Float64
     dt :: Float64
     nt :: Int64
     p  :: Array{Float64, 3}
end

function InitWSrcCube(oz::Int64, ox::Int64, nz::Int64, nx::Int64, ext::Int64, iflag::Int64, ot::Float64, dt::Float64, nt::Int64)
    wsc = WSrcCube(oz, ox, nz, nx, ext, iflag, ot, dt, nt, zeros(nz, nx, nt))
    return wsc
end

function CopyWsc(wsc::WSrcCube)
   wsc1 = WSrcCube(wsc.oz, wsc.ox, wsc.nz, wsc.nx, wsc.ext, wsc.iflag, wsc.ot, wsc.dt, wsc.nt, copy(wsc.p))
   return wsc1
end

function l112norm(wsc::WSrcCube)
    (nz, nx, nt) = size(wsc.p)
    l = 0.0
    for ix = 1 : nx
        for iz = 1 : nz
            tmp = 0.0
            for it = 1 : nt
                tmp = tmp + wsc.p[iz,ix,it]*wsc.p[iz,ix,it]
            end
            l = l + sqrt(tmp)
        end
    end
    return l
end

function EngDisWsc(wsc::WSrcCube)
    (nz, nx, nt) = size(wsc.p)
    l = zeros(nz, nx)
    for ix = 1 : nx
        for iz = 1 : nz
            tmp = 0.0
            for it = 1 : nt
                tmp = tmp + wsc.p[iz,ix,it]*wsc.p[iz,ix,it]
            end
            l[iz,ix] = sqrt(tmp)
        end
    end
    return l
end

"""
convert explosive sources to WSrcCube
"""
function Srcs2Wsc(oz::Int64, ox::Int64, nz::Int64, nx::Int64, srcs::Array{Source,1})
    for is = 1 : length(srcs)
        if !srcs[is].Tzz_flag || !srcs[is].Txx_flag
           error("non explosive sources, can not convert srcs to WSrcCube")
        end
    end
    ext = srcs[1].ext; iflag = srcs[1].iflag;
    dt = srcs[1].dt
    (tl, tu) = SourcesTimeRange(srcs)
    nt = floor(Int64, (tu-tl)/dt) + 1
    wsc = InitWSrcCube(oz, ox, nz, nx, ext, iflag, tl, dt, nt)
    for it = 1 : nt
        for is = 1 : length(srcs)
            if srcs[is].ot <= tl+(it-1)*dt <=srcs[is].ot+(srcs[is].nt-1)*dt
               iz = srcs[is].isz; iz = iz - oz + 1;
               ix = srcs[is].isx; ix = ix - ox + 1;
               indt = it + round(Int64, (tl-srcs[is].ot)/dt)
               wsc.p[iz, ix, it] = wsc.p[iz, ix, it] + srcs[is].Tzz[indt]
            end
        end
    end
    return wsc
end

"""
sources is added as explosive sources
"""
function AddWsc2spt!(spt::SnapShot, wsc::WSrcCube)
    nz=spt.nz; nx=spt.nx; ext=spt.ext; iflag=spt.iflag;
    if iflag == 1
       zupper = ext
       Nz  = nz + 2*ext
    elseif iflag == 2
       zupper = 0
       Nz  = nz +   ext
    end
    Nx = nx + 2*ext
    it = spt.it
    dt = spt.dt
    indt = it - round(Int64, wsc.ot/dt)
    tmp  = zeros(Nz, Nx)
    izl  = zupper + wsc.oz; izu = zupper + wsc.oz + wsc.nz-1
    ixl  = ext    + wsc.ox; ixu = ext    + wsc.ox + wsc.nx-1
    tmp[izl:izu, ixl:ixu] = 1/2*wsc.p[:,:,indt]
    tmp = vec(tmp)
    spt.Tzzz[:] = spt.Tzzz[:] + tmp[:]
    spt.Tzzx[:] = spt.Tzzx[:] + tmp[:]
    spt.Txxz[:] = spt.Txxz[:] + tmp[:]
    spt.Txxx[:] = spt.Txxx[:] + tmp[:]
    return nothing
end

function spt2Wsc!(wsc::WSrcCube, spt::SnapShot)
    nz=spt.nz; nx=spt.nx; ext=spt.ext; iflag=spt.iflag;
    if iflag == 1
       zupper = ext
       Nz  = nz + 2*ext
    elseif iflag == 2
       zupper = 0
       Nz  = nz +   ext
    end
    Nx = nx + 2*ext
    it = spt.it
    dt = spt.dt
    indt = it - round(Int64, wsc.ot/dt)
    izl  = zupper + wsc.oz; izu = zupper + wsc.oz + wsc.nz-1
    ixl  = ext    + wsc.ox; ixu = ext    + wsc.ox + wsc.nx-1
    tmp  = 1/2*(spt.Tzzz + spt.Tzzx + spt.Txxz + spt.Txxx)
    tmp  = reshape(tmp, Nz, Nx)
    wsc.p[:, :, indt] = tmp[izl:izu, ixl:ixu]
    return nothing
end

max_stars_repo_name: WneleiGao/ElasticWave