text:
# ### parametric function plot 3d
# https://jp.mathworks.com/matlabcentral/fileexchange/35263-matlab-plot-gallery-function-plot-3d?focused=7851891&tab=example

#md # ![FILE_NAME.png](images/FILE_NAME.png)

t = range(0, stop=10, length=1000)
x = cos.(t)
y = sin.(t)
z = sin.(5t)

plot(x, y, z)


text:
export openFile, closeFile, readFrames

const SFM_READ = int32(0x10)
const SFM_WRITE = int32(0x20)

type Sndfile
    filePtr::Ptr{Void}
    sfinfo::SF_INFO
end

function openFile(path::String)
    sfinfo = SF_INFO(0, 0, 0, 0, 0, 0)
    filePtr = ccall((:sf_open, sndfile), Ptr{Void},
                    (Ptr{Uint8}, Int32, Ptr{SF_INFO}),
                    path, SFM_READ, &sfinfo)
    if filePtr == C_NULL
        errmsg = ccall((:sf_strerror, sndfile), Ptr{Uint8}, (Ptr{Void},), filePtr)
        error(bytestring(errmsg))
    end

    return Sndfile(filePtr, sfinfo)
end

function closeFile(file::Sndfile)
    err = ccall((:sf_close, sndfile), Int32, (Ptr{Void},), file.filePtr)
    if err != 0
        error("Failed to close file")
    end
end

function openFile(f::Function, path::String)
    file = openFile(path)
    f(file)
    closeFile(file)
end

function readFrames(file::Sndfile, nframes::Integer, dtype::Type = Int16)
    arr = []
    if file.sfinfo.channels == 2
        arr = Array(dtype, 2, nframes)
    else
        arr = Array(dtype, nframes)
    end

    if dtype == Int16
        nread = ccall((:sf_readf_short, sndfile), Int64,
                        (Ptr{Void}, Ptr{Int16}, Int64),
                        file.filePtr, arr, nframes)
    elseif dtype == Int32
        nread = ccall((:sf_readf_int, sndfile), Int64,
                        (Ptr{Void}, Ptr{Int32}, Int64),
                        file.filePtr, arr, nframes)
    elseif dtype == Float32
        nread = ccall((:sf_readf_float, sndfile), Int64,
                        (Ptr{Void}, Ptr{Float32}, Int64),
                        file.filePtr, arr, nframes)
    elseif dtype == Float64
        nread = ccall((:sf_readf_double, sndfile), Int64,
                        (Ptr{Void}, Ptr{Float64}, Int64),
                        file.filePtr, arr, nframes)
    end

    if nread == 0
        return []
    end

    if file.sfinfo.channels == 2
        return arr[:, 1:nread]
    end

    return arr[1:nread]
end

ext: jl


text:
using Calculus
using Symbolics
using SparseArrays
using ForwardDiff
using ReverseDiff
using LinearAlgebra
using LightGraphs
using fastmaxent
using BenchmarkTools
using Latexify

# supporting functions - define the metric in terms of the adjacency matrix
"""using the adjacency matrix"""
function degree(i::Int,A::AbstractArray{T,2}) where T<:Real
    return sum(@view A[i,:]) - A[i,i]
end

"""using the symbolic representation"""
function degree(i::Int, A::Symbolics.Arr{Num, 2})
    return sum(@view A[i,:]) - A[i,i]
end

"""using the computed parameters"""
function degree(i, x::AbstractVector)
    res = - a(i,i,x)
    for j in eachindex(x)
        res += a(i,j,x)
    end
    return res
end

"""using the adjacency matrix"""
function ANND(i::Int, A::AbstractArray{T,2}) where T<:Real
    return sum(A[i,j] * degree(j,A) for j=1:size(A,1) if j≠i) / degree(i,A)
end

"""using the symbolic representation"""
function ANND(i::Int, A::Symbolics.Arr{Num, 2})
    return sum(A[i,j] * degree(j,A) for j=1:size(A,1) if j≠i) / degree(i,A)
end

"""using the computed parameters"""
function ANND(i::Int, x::Symbolics.Arr{Num, 1})
    @warn "ANND(i::Int, x::Symbolics.Arr{Num, 1}) is used!"
    res = zero(Num)
    for j in eachindex(x)
        @info "j: $(j), $(typeof(j)) - i: $(i), $(typeof(i))"
        if j[1] ≠ i
            res += a(i,j,x) * degree(j, x)
        end
    end
    return res / degree(i, x)
end

"""using the computed parameters"""
function ANND(i::Int, x::AbstractVector{T}) where T
    @warn "ANND(i::Int, x::AbstractVector{T}) where T is used!"
    res = zero(T)
    for j in eachindex(x)
        @info "j: $(j), $(typeof(j)) - i: $(i), $(typeof(i))"
        if j ≠ i
            res += a(i,j,x) * degree(j, x)
        end
    end
    return res / degree(i, x)
end

# second method: make use of Symbolics.jl (symbolic computations => sparsity pattern)
n = 7
@variables A[1:n, 1:n]
@variables x[1:n]
A_num = Array{Float64}(Symmetric(rand(Bool, n,n)))

i = 2

X = ANND(i, A) # X is the symbolic expression of our topological property
X_num = ANND(i,A_num) # X_num is the numerical version of our topological property
X_sym_to_num = eval(Symbolics.build_function(X, A))(A_num) # X_sym_to_num is the numerical evaluation of the symbolic expression

# compute each contribution ∂X/∂a_{t,s} and store it in an array
∂X∂a = zeros(Num,n,n)
for t = 1:n
    for s = 1:n
        ∂X∂a[t,s] = Symbolics.derivative(X, A[t,s])
    end
end

# you can eavluate the numerical value of each entry using
ΔxΔA = Symbolics.build_function(∂X∂a, A) # leads to two function definitions (with or without output arg)
ΔxΔA_exact = eval(ΔxΔA[1]) # this is the function
ΔxΔA_exact(A_num) # this is the results based on the numerical values

# third method: make use of ForwardDiff.jl (automated differentiation => numerical result)

# appropriate function that takes only one input argument
ANND_w(A::AbstractArray{R,2}) where {R<:Real} = ANND(i, A)
ANND_w(x::AbstractVector{R}) where {R<:Real} = ANND(i, x)
ForwardDiff.gradient(ANND_w, A_num)

# Challenges:
# 1. translate this to a closure for a single contribution, i.e. one ∂X/∂a_{ij}(a) at a time instead of ∇f(a) at once
# 2. inlude vector of parameters that allows to avoid computing matrix a as a whole

#@btime ΔxΔA_exact($A_num)
#@btime ForwardDiff.gradient($ANND_w, $A_num)
#@btime ReverseDiff.gradient($ANND_w, $A_num)

a_slow(x,i,j) = x[i]*x[j] / (1 + x[i]*x[j])
a_fast(x,i,j) = @inbounds f(x[i]*x[j])
f(x::T) where {T} = x / (one(T) + x)

# Include the entire method for an actual network

# supporting functions for computations
f(x::T) where {T} = x / (one(T) + x) # helper function UBCM]–
a(i,j,x::AbstractVector) = @inbounds f(x[i]*x[j]) # UBCM

# generate random graph
N = n
G = barabasi_albert(N, 2, 2, seed=-5)

model = UBCM(G)
model_c = UBCMCompact(G)
solve!(model)
solve!(model_c)
@info model.x
A_star = adjacency_matrix(G)                # observed adjacency matrix
d_star = LightGraphs.degree(G)              # observed degree sequence
ANND_star = map(i -> ANND(i, A_star), 1:N)  # observed ANND

A_exp = reshape([a(i,j,model.x) for j = 1:N for i in 1:N],N,:)  # expected adjacency_matrix
d_exp = map(x->degree(x, A_exp), 1:N)                           # expected degree sequence
ANND_exp = map(i -> ANND(i, A_exp), 1:N)                        # expected ANND

# variance for specific entry e.g. ANND (node number 2)
ΔA⁺Δa_exact = ΔxΔA_exact(A_exp)                                 # ∂ANND_i∂a_ij using analytical expression
ΔA⁺Δa_num = ForwardDiff.gradient(ANND_w, A_exp)                 # ∂ANND_i∂a_ij using autodiff (entire matrix A required)

# Trying vector based method
ΔMΔx = ForwardDiff.gradient(ANND_w, model.x)                    # gradient of M wrt computed parameters x
∂xi∂aij(i::Int, j::Int, x::Vector{T}) where T<: Real = (1 + x[i]*x[j])^2 / x[j]        # supposed form of ∂xi∂aij
∂M∂aij(ΔM::Vector, x::Vector, i::Int,j::Int) = ΔM[i] * ∂xi∂aij(i,j,x) + ΔM[j] * ∂xi∂aij(j,i,x) # unique contribution
# first problem stems from symmetrical case i = j (∂xi∂aij changes with factor 2, not main cause)
#
ΔA⁺Δa_piecewise = map(t -> t[1]==t[2] ? 0. : ∂M∂aij(ΔMΔx, model.x, t[1], t[2]), Base.product(1:N, 1:N));

## Run and compare the numerical method with the expected analytical result

# 1. Write M in function of aij & xi: OK
M_A = ANND(i, A) # M_A is the symbolic expression of our topological property using the matrix A
M_x = ANND(i, x) # M_x is the symbolic expression of our topological property using the vector x

#latexify(M_A)
# this is wrong, because there are other values that exist as well, it is better to solve the problem in the
# compact space in order to obtain the desired result. This reduces the matrix from N² to Ŋ² elements that
# can be reused :-)
#Symbolics.derivative(M_x, x[1]) / Symbolics.derivative(a(1,2,x), x[1]) + Symbolics.derivative(M_x, x[2]) / Symbolics.derivative(a(1,2,x), x[2])

##### Compact Vs extended model

model = UBCM(G)
model_c = UBCMCompact(G)
solve!(model)
solve!(model_c)
N_c = length(model_c.x0)

A_exp = reshape([i ≠ j ? a(i,j,model.x) : 0.0 for j = 1:N for i in 1:N],N,:)
A_exp_c = reshape([ a(i,j,model_c.x)  for j = 1:N_c for i in 1:N_c],N_c,:)

# Lets expand the matrix
nodemap = similar(model.x, Int)
for (key, val) in model_c.revmap
    for node in val
        nodemap[node] = key
    end
end

exp_ind(i,nodemap) = nodemap[i]
exp_ind_f(i,nodemap) = @inbounds nodemap[i]

A_exp_CL = reshape([A_exp_c[exp_ind(i, nodemap), exp_ind(j, nodemap)] for j in 1:N for i in 1:N], N,:)
ΔA⁺Δa_num_c = ForwardDiff.gradient(ANND_w, A_exp_c)

σ²ₐ(i,j,x) = (x[i]*x[j]) / (1 + x[i]*x[j])^2

σ²ₓ(A,x) = sum( σ²ₐ(i,j,x) * A[i,j]^2 for i in eachindex(x) for j in eachindex(x) if i≠j)

σ²ₓ(ΔA⁺Δa_num, model.x)

σ²ₓ_mat = zeros(length(model.x),length(model.x))
for i in eachindex(model.x)
    for j in eachindex(model.x)
        if i ≠ j
            σ²ₓ_mat[i,j] = σ²ₐ(i,j,model.x) * ΔA⁺Δa_num[i,j]^2
        end
    end
end
σ²ₓ_mat_c = zeros(length(model_c.x),length(model_c.x))
for i in eachindex(model_c.x)
    for j in eachindex(model_c.x)
        if i ≠ j
            σ²ₓ_mat_c[i,j] = σ²ₐ(i,j,model_c.x) * ΔA⁺Δa_num_c[i,j]^2
        end
    end
end

σ²ₓ(ΔA⁺Δa_num_c, model_c.x)

# first method: make use of Calculus.jl (expression) combined with symbolic differentiation

# - not trivial
# - text/symbol-based, so should work rather fast => hard to generate expressions

#=

function degree(i::Int,A::Array{String,2})
    res = IOBuffer()
    for j in 1:size(A,1)
        if j≠i
            write(res, " + $(A[i,j])")
        end
    end

    return String(take!(res))
end

function degree!(i::Int,A::Array{String,2}, res::IOBuffer)
    for j in 1:size(A,1)
        if j≠i
            write(res, " + $(A[i,j])")
        end
    end

    return String(take!(res))
end

n = 20
bin = IOBuffer()
A = rand(Bool,n,n)
As = reshape(["a_$(i)_$(j)" for j in 1:n for i in 1:n],n,:)
using BenchmarkTools

@btime degree(1,$As)
@btime degree!(1,$As,$bin)

d_i = degree(1, As)

@btime differentiate($d_i, :a_1_7)
=#

#=
struct CompactArray{T,N,A<:AbstractArray{T,N},J<:Integer,I<:AbstractArray{J,N} } <: AbstractArray{T,N}
    values::A
    index::I
end
Base.IndexStyle(::Type{CompactArray{T,N,A,J,I}}) where {T,N,A,J,I} = IndexStyle(A)
Base.show(io::IO,A::CompactArray{T,N}) where {T,N} = print(io, "CompactArray{$(T)}\n$(Base.print_matrix(io,[1 2;4 5]))")
Base.display(A::CompactArray{T,N}) where {T,N} = show(A)
Base.size(A::CompactArray{T,N}) where {T,N} = length(A.index) .* size(A.values)
Base.getindex(A::CompactArray, i::Int) = getindex(A.values,i)
Base.getindex(A::CompactArray{T,N}, I::Vararg{Int,N}) where {T,N} = getindex(A.values, I)
Base.setindex(A::CompactArray{T,N}, v::T, i::Int) where {T,N} = setindex(A.values, v, i)
Base.setindex!(A::CompactArray{T,N}, v, I::Vararg{Int,N}) where {T,N}  = setindex(A.values, v, I)
val = [1 2;3 4]
inds = [1;2;3;4;5]
CompactArray(val, inds)

Base.size(A::CompactArray{T,N}) where {T,N} = size(A.index)
Base.getindex(A::CompactArray, i::Int) = getindex(A.values,i)
Base.getindex(A::CompactArray{T,N}, I::Vararg{Int,N}) where {T,N} = getindex(A.values, I)
Base.setindex(A::CompactArray{T,N}, v::T, i::Int) where {T,N} = setindex(A.values, v, i)
Base.setindex!(A::CompactArray{T,N}, v, I::Vararg{Int,N}) where {T,N}  = setindex(A.values, v, I)
#=

struct CompactArray{T,N,I, A<:AbstractArray{T,N}, V<:AbstractVector{I}} <: AbstractArray{T,N}
    values::A
    index::V
    function CompactArray{T,N,I,A,V}(values::AbstractArray{T,N}, index::AbstractVector{I}) where {T,N,I,A,V}
        I <: Integer || error("index must have integer eltype")
        new{T,N,I,A,V}(values, index)
    end
end

const CompactVector{T,N,I,A<:AbstractVector,V} = IndirectArray{T,N,I,A,V}

#Base.size(A::CompactArray) = length(A.index) .* size(A.values)
Base.size(A::CompactArray{T,N,I,A,V}) where {T,N,I,A,V} = length(A.index) .* size(A.values)
Base.size(A::IndirectArray) = size(A.index)
Base.getindex(A::CompactArray, inds...) =
x = CompactArray(model_c.x, nodemap)

=#
=#

# Large model


text:
using MPI
using ClimateMachine
using Logging
using ClimateMachine.Mesh.Topologies
using ClimateMachine.Mesh.Grids
using ClimateMachine.DGMethods
using ClimateMachine.DGMethods.NumericalFluxes
using ClimateMachine.MPIStateArrays
using LinearAlgebra
using Printf
using Dates
using ClimateMachine.GenericCallbacks:
    EveryXWallTimeSeconds, EveryXSimulationSteps
using ClimateMachine.ODESolvers
using ClimateMachine.VTK: writevtk, writepvtu
using ClimateMachine.Mesh.Grids: min_node_distance

const output = parse(Bool, lowercase(get(ENV, "JULIA_CLIMA_OUTPUT", "false")))

if !@isdefined integration_testing
    const integration_testing = parse(
        Bool,
        lowercase(get(ENV, "JULIA_CLIMA_INTEGRATION_TESTING", "false")),
    )
end

include("advection_diffusion_model.jl")

struct DiffusionSphere <: AdvectionDiffusionProblem end

function init_velocity_diffusion!(
    problem::DiffusionSphere,
    aux::Vars,
    geom::LocalGeometry,
)
    FT = eltype(aux)
    IM = SMatrix{3, 3, FT}(I)
    ZM = zeros(SMatrix{3, 3, FT})
    μ = FT(1 / 10000)
    aux.diffusion.D = μ * hcat(IM, ZM)
    aux.hyperdiffusion.H = μ * hcat(ZM, IM)
end

function initial_condition!(problem::DiffusionSphere, state, aux, localgeo, t)
    coord = localgeo.coord
    x, y, z = coord
    r = norm(coord)
    θ = atan(sqrt(x^2 + y^2), z)
    φ = atan(y, x)
    @inbounds begin
        # m = 1 l = 2 spherical harmonic
        ρ₀ = cos(φ) * sin(θ) * cos(θ)
        l = 2
        c = l * (l + 1) / r^2
        μ = aux.diffusion.D[1]
        state.ρ = (ρ₀ * exp(-c * μ * t), ρ₀ * exp(-c^2 * μ * t))
    end
end

function do_output(mpicomm, vtkdir, vtkstep, dg, Q, Qe, model, testname)
    ## name of the file that this MPI rank will write
    filename = @sprintf(
        "%s/%s_mpirank%04d_step%04d",
        vtkdir,
        testname,
        MPI.Comm_rank(mpicomm),
        vtkstep
    )

    statenames = flattenednames(vars_state(model, Prognostic(), eltype(Q)))
    exactnames = statenames .* "_exact"

    writevtk(filename, Q, dg, statenames, Qe, exactnames)

    ## Generate the pvtu file for these vtk files
    if MPI.Comm_rank(mpicomm) == 0
        ## name of the pvtu file
        pvtuprefix = @sprintf("%s/%s_step%04d", vtkdir, testname, vtkstep)

        ## name of each of the ranks vtk files
        prefixes = ntuple(MPI.Comm_size(mpicomm)) do i
            @sprintf("%s_mpirank%04d_step%04d", testname, i - 1, vtkstep)
        end

        writepvtu(
            pvtuprefix,
            prefixes,
            (statenames..., exactnames...),
            eltype(Q),
        )

        @info "Done writing VTK: $pvtuprefix"
    end
end

function run(mpicomm, ArrayType, topl, N, timeend, FT, vtkdir, outputtime)

    grid = DiscontinuousSpectralElementGrid(
        topl,
        FloatType = FT,
        DeviceArray = ArrayType,
        polynomialorder = N,
        meshwarp = equiangular_cubed_sphere_warp,
    )

    dx = min_node_distance(grid)
    dt = 300 * dx^4
    @info "time step" dt
    dt = outputtime / ceil(Int64, outputtime / dt)

    model = AdvectionDiffusion{3}(
        DiffusionSphere(),
        advection = false,
        diffusion = true,
        hyperdiffusion = true,
        num_equations = 2,
    )
    dg = DGModel(
        model,
        grid,
        CentralNumericalFluxFirstOrder(),
        CentralNumericalFluxSecondOrder(),
        CentralNumericalFluxGradient(),
        diffusion_direction = HorizontalDirection(),
    )

    Q = init_ode_state(dg, FT(0))

    lsrk = LSRK54CarpenterKennedy(dg, Q; dt = dt, t0 = 0)

    eng0 = norm(Q, dims = (1, 3))
    @info @sprintf """Starting
    norm(Q₀) = %.16e""" eng0[1]

    # Set up the information callback
    starttime = Ref(now())
    cbinfo = EveryXWallTimeSeconds(60, mpicomm) do (s = false)
        if s
            starttime[] = now()
        else
            energy = norm(Q)
            @info @sprintf(
                """Update
                simtime = %.16e
                runtime = %s
                norm(Q) = %.16e""",
                gettime(lsrk),
                Dates.format(
                    convert(Dates.DateTime, Dates.now() - starttime[]),
                    Dates.dateformat"HH:MM:SS",
                ),
                energy
            )
        end
    end
    callbacks = (cbinfo,)
    if ~isnothing(vtkdir)
        # create vtk dir
        mkpath(vtkdir)

        vtkstep = 0
        # output initial step
        do_output(mpicomm, vtkdir, vtkstep, dg, Q, Q, model, "diffusion_sphere")

        # setup the output callback
        cbvtk = EveryXSimulationSteps(floor(outputtime / dt)) do
            vtkstep += 1
            Qe = init_ode_state(dg, gettime(lsrk))
            do_output(
                mpicomm,
                vtkdir,
                vtkstep,
                dg,
                Q,
                Qe,
                model,
                "diffusion_sphere",
            )
        end
        callbacks = (callbacks..., cbvtk)
    end

    solve!(Q, lsrk; timeend = timeend, callbacks = callbacks)

    # Print some end of the simulation information
    engf = norm(Q, dims = (1, 3))
    Qe = init_ode_state(dg, FT(timeend))

    engfe = norm(Qe, dims = (1, 3))
    errf = norm(Q .- Qe, dims = (1, 3))

    metrics = @. (engf, engf / eng0, engf - eng0, errf, errf / engfe)

    @info @sprintf """Finished
    Diffusion:
      norm(Q)                 = %.16e
      norm(Q) / norm(Q₀)      = %.16e
      norm(Q) - norm(Q₀)      = %.16e
      norm(Q - Qe)            = %.16e
      norm(Q - Qe) / norm(Qe) = %.16e
    HyperDiffusion:
      norm(Q)                 = %.16e
      norm(Q) / norm(Q₀)      = %.16e
      norm(Q) - norm(Q₀)      = %.16e
      norm(Q - Qe)            = %.16e
      norm(Q - Qe) / norm(Qe) = %.16e
      """ first.(metrics)... last.(metrics)...
    errf
end

using Test
let
    ClimateMachine.init()
    ArrayType = ClimateMachine.array_type()
    mpicomm = MPI.COMM_WORLD

    polynomialorder = 3
    base_num_elem = 4

    expected_result = Dict()
    expected_result[Diffusion, 1] = 2.6002775334282785e-06
    expected_result[Diffusion, 2] = 4.1602462623838931e-07
    expected_result[Diffusion, 3] = 5.8997889725858304e-08

    expected_result[HyperDiffusion, 1] = 9.3032674316702424e-05
    expected_result[HyperDiffusion, 2] = 1.0149377619104328e-05
    expected_result[HyperDiffusion, 3] = 1.2985297333025857e-06

    numlevels =
        integration_testing || ClimateMachine.Settings.integration_testing ? 3 :
        1
    @testset "$(@__FILE__)" begin
        for FT in (Float64,)
            result = Dict()
            for l in 1:numlevels
                Ne = 2^(l - 1) * base_num_elem
                vert_range = grid1d(1, 2, nelem = 1)
                topl = StackedCubedSphereTopology(
                    mpicomm,
                    Ne,
                    vert_range,
                    boundary = (0, 0),
                )

                timeend = FT(1)
                outputtime = FT(2)

                @info (ArrayType, FT)
                vtkdir =
                    output ?
                    "vtk_diffusion_sphere" *
                    "_poly$(polynomialorder)" *
                    "_$(ArrayType)_$(FT)" *
                    "_level$(l)" :
                    nothing
                result[l] = run(
                    mpicomm,
                    ArrayType,
                    topl,
                    polynomialorder,
                    timeend,
                    FT,
                    vtkdir,
                    outputtime,
                )
                @test result[l][1] ≈ expected_result[Diffusion, l]
                @test result[l][2] ≈ expected_result[HyperDiffusion, l]
            end
            @info begin
                msg = ""
                for l in 1:(numlevels - 1)
                    rate = @. log2(result[l]) - log2(result[l + 1])
                    msg *= @sprintf(
                        "\n  rates for level %d Diffusion = %e",
                        l,
                        rate[1]
                    )
                    msg *= @sprintf(", HyperDiffusion = %e\n", rate[2])
                end
                msg
            end
        end
    end
end

nothing


text:
using Gadfly

plot([sin, cos], 0, 25)


text:
# Base.CartesianIndices(A::MissingDataArray) = CartesianIndices(A.data)

function parameter_names(::Type{<:AbstractMissingDataArray{M}}, s::Symbol) where {M}
    ss = strip_hashtags(s) * "_missing_#"
    [ss * string(m) for m ∈ 1:M]
end
function parameter_names(A::AbstractMissingDataArray{M}, s::Symbol) where {M}
    out = Vector{String}(undef,M)
    # ci = CartesianIndices(A)
    inds = A.inds
    ss = strip_hashtags(s)*"["
    for m in 1:M
        cim = inds[m]
        ssm = ss * string(cim[1])
        for i in 2:length(cim)
            ssm *= "," * string(cim[i])
        end
        out[m] = ssm * "]"
    end
    out
end

PaddedMatrices.type_length(::AbstractMissingDataArray{M}) where {M} = M
PaddedMatrices.type_length(::Type{<:AbstractMissingDataArray{M}}) where {M} = M
PaddedMatrices.param_type_length(::AbstractMissingDataArray{M}) where {M} = M
PaddedMatrices.param_type_length(::Type{<:AbstractMissingDataArray{M}}) where {M} = M

"""
This function is not type stable.
"""
function maybe_missing(A::AA) where {T,N,AA <: AbstractArray{Union{Missing,T},N}}
    M = sum(ismissing, A)
    M == 0 && return convert(Array{T}, A)
    l, u = extrema(skipmissing(A))
    lb = l > 0 ? zero(T) : -typemax(T)
    ub = u < 0 ? zero(T) :  typemax(T)
    Threads.nthreads() > 1 ? convert(ThreadedMissingDataArray{M,Bounds(lb,ub)}, A) : convert(MissingDataArray{M,Bounds(lb,ub)}, A)
end
maybe_missing(A::AbstractArray) = A

function Base.convert(::Type{<:MissingDataArray{M}}, A::AbstractArray{Union{Missing,T}}) where {M,T}
    convert(MissingDataArray{M,Bounds(typemin(T),typemax(T))}, A)
end
function Base.convert(::Type{<:MissingDataArray{M,B}}, A::AA) where {M,B,T,N,AA <: AbstractArray{Union{Missing,T},N}}
#    M = sum(ismissing, A)
    #    M == 0 &&
    data = similar(A, Float64)
    ptr_A = Base.unsafe_convert(Ptr{T}, pointer(A)) # is this necessary?
    LoopVectorization.@vvectorize for i ∈ eachindex(A)
        data[i] = ptr_A[i]
    end
    MissingDataArray{M,B,T,N,typeof(data)}(
        data, findall(ismissing, A)
    )
end

function Base.convert(::Type{<:ThreadedMissingDataArray{M}}, A::AbstractArray{Union{Missing,T}}) where {M,T}
    convert(ThreadedMissingDataArray{M,Bounds(typemin(T),typemax(T))}, A)
end
function Base.convert(::Type{<:ThreadedMissingDataArray{M,B}}, A::AA) where {M,B,T,N,AA <: AbstractArray{Union{Missing,T},N}}
#    M = sum(ismissing, A)
    #    M == 0 &&
    data = similar(A, Float64)
    ptr_A = Base.unsafe_convert(Ptr{T}, pointer(A)) # is this necessary?
    LoopVectorization.@vvectorize for i ∈ eachindex(A)
        data[i] = ptr_A[i]
    end
    nthreads = Threads.nthreads()
    datav = Vector{tyepof(data)}(undef, nthreads)
    datav[1] = data
    Threads.@threads for n in 2:nthreads
        datav[n] = copy(data)
    end
    MissingDataArray{M,B,T,N,typeof(data)}(
        datav, findall(ismissing, A)
    )
end

function load_missing_as_vector!(
    first_pass, second_pass, out, ::Type{<:AbstractMissingDataArray{M,B,T}}, partial::Bool = false,
    m::Module = DistributionParameters, sptr::Union{Symbol,Nothing} = nothing, logjac::Bool = true, copyexport::Bool = false
) where {M,B,T}
    load_parameter!(
        first_pass, second_pass, out, RealVector{M,B,T}, partial, m, sptr, logjac, copyexport
    )
end

function load_parameter!(
    first_pass, second_pass, out, ::Type{MA}, partial::Bool = false,
    m::Module = DistributionParameters, sptr::Union{Symbol,Nothing} = nothing, logjac::Bool = true, copyexport::Bool = false
) where {M,B,T,MA <: AbstractMissingDataArray{M,B,T}}
    θ = Symbol("##θparameter##")
    ∂θ = Symbol("##∂θparameter##")
    out_missing = Symbol("##missing##", out)
    out_incomplete = Symbol("##incomplete##", out)
    out_incompleteinds = Symbol("##incompleteinds##", out)
    push!(first_pass, :($out_incompleteinds = $out_incomplete.inds))
    if MA <: ThreadedMissingDataArray
        push!(first_pass, :($out = $out_incomplete.data[Threads.threadid()]))
    else
        push!(first_pass, :($out = $out_incomplete.data))
    end
    seedout = Symbol("###seed###", out)
    seedoutextract = Symbol("###seed###extract", out)
    seedout_missing = Symbol("###seed###", out_missing)
    tempstackptr = sptr isa Symbol ? Symbol("##temp##", sptr) : sptr
    isym = gensym(:i)
    if partial
        push!(second_pass, :($seedoutextract = $m.DistributionParameters.extract($seedout)))
        if isunbounded(B) # no transformation
            # we fully handle the partial here
            # therefore, we set partial = false
            # to prevent load_transformations
            # from doing so as well.
            partial = false
            ptr_∂θ = gensym(:ptr_∂θ)
            ∂gather_quote = quote
                $ptr_∂θ = pointer($∂θ)
                @inbounds for $isym ∈ 1:$M
                    $m.VectorizationBase.store!($ptr_∂θ + ($isym - 1) * $(sizeof(T)), $seedoutextract[$out_incompleteinds[$isym]])
                end
                $∂θ += $M
            end
        else
            if sptr isa Symbol
                # Do we need to increment the stack pointer?
                seedout_init_quote = quote
                    $tempstackptr = $sptr
                    $seedout_missing = $m.PaddedMatrices.PtrVector{$M,$T,$M}(pointer($sptr, $T))
                    $sptr += $M
                end
            else
                seedout_init_quote = quote
                    $seedout_missing = $m.PaddedMatrices.FixedSizeVector{$M,$T,$M}(undef)
                end
            end
            ∂gather_quote = quote
                @inbounds for $isym ∈ 1:$M
                    $seedout_missing[$ism] = $seedoutextract[$out_incompleteinds[$isym]]
                end
            end
            push!(second_pass, seedout_init_quote)
        end
        push!(second_pass, ∂gather_quote)
    end
    load_transformations!(
        first_pass, second_pass, B, out_missing, Int[ M ],
        partial, logjac, sptr, m, θ, ∂θ, copyexport
    )
    scatter_quote = quote
        @inbounds for $isym ∈ 1:$M
            $out[$out_incompleteinds[$isym]] = $out_missing[$isym]
        end
    end
    push!(first_pass, scatter_quote)
    if partial && sptr isa Symbol
        push!(second_pass, :($sptr = $tempstackptr))
    end
    nothing
end


text:
export NoGate

using YaoBase, YaoArrayRegister, YaoBlocks
using LuxurySparse

struct NoGate <: PrimitiveBlock{1} end

YaoBlocks.apply!(r::AbstractRegister, x::NoGate) = r
YaoBase.instruct!(r::ArrayReg, x::NoGate, locs) = r
YaoBlocks.mat_matchreg(r::ArrayReg, x::NoGate) = x

YaoBlocks.mat(::Type{T}, x::NoGate) where T = IMatrix{2, T}()


text:
using StaticArrays
using LinearAlgebra
using Test
using Random
using ForwardDiff
const TO = TrajectoryOptimization

# Unit Quaternions
q = rand(UnitQuaternion)
q0 = rand(UnitQuaternion)
qval = SVector(q)
ϕ = @SVector zeros(3)

function mycost(q::SVector{4})
    return 1 - SVector(q0)'q
end
function mycostdiff(v::SVector{3})
    mycost(SVector(q*VectorPart(v)))
end

grad_q = ForwardDiff.gradient(mycost, qval)
hess_q = ForwardDiff.hessian(mycost, qval)
G = TO.∇differential(q)

@test ForwardDiff.gradient(mycostdiff, ϕ) ≈ G'grad_q
@test ForwardDiff.hessian(mycostdiff, ϕ) ≈ G'hess_q*G + TO.∇²differential(q, grad_q)
dq = SVector(q0)'SVector(q)
@test ForwardDiff.gradient(mycostdiff, ϕ) ≈ -G'SVector(q0)
@test ForwardDiff.gradient(mycostdiff, ϕ) ≈ -G'SVector(q0)
G'SVector(q0) ≈ Vmat()*Lmult(q)'SVector(q0)
@test ForwardDiff.hessian(mycostdiff, ϕ) ≈ I(3)*dq

# MRPs
g = rand(MRP)
gval = SVector(g)
g0 = rand(MRP)
dg = @SVector zeros(3)
function mycost(g::SVector{3})
    g = MRP(g)
    dg = g ⊖ g0
    return norm(dg)
end
mycostdiff(dg::SVector{3}) = mycost(SVector(g*MRP(dg)))
mycost(gval)
mycostdiff(dg)
G = TO.∇differential(g)
dfdp = ForwardDiff.gradient(mycost,gval)
dfdp2 = ForwardDiff.hessian(mycost,gval)

ForwardDiff.gradient(mycostdiff,dg) ≈ G'dfdp
ForwardDiff.hessian(mycostdiff,dg) ≈ G'dfdp2*G + TO.∇²differential(g,dfdp)

# State diff cost (Quaternion)
Random.seed!(1)
q = rand(UnitQuaternion)
q0 = rand(UnitQuaternion)
qval = SVector(q)
ϕ = @SVector zeros(3)

RMAP = CayleyMap
function mycost(q::SVector{4})
    q = UnitQuaternion{RMAP}(q)
    err = RMAP(q0\q)
    0.5*err'err
end
function mycostdiff(ϕ::SVector{3})
    mycost(SVector(q*RMAP(ϕ)))
end

mycostdiff(ϕ) ≈ mycost(qval)
grad_q = ForwardDiff.gradient(mycost, qval)
hess_q = ForwardDiff.hessian(mycost, qval)
dq = q0\q
err = CayleyMap(dq)
G = TO.∇differential(dq)
dmap = jacobian(CayleyMap,dq)
∇jac = TO.∇jacobian(CayleyMap, dq, err)
TO.∇²differential(q, grad_q)

@test grad_q ≈ Lmult(q0)*dmap'err
@test ForwardDiff.gradient(mycostdiff,ϕ) ≈ (dmap*G)'err
@test ForwardDiff.hessian(mycostdiff,ϕ) ≈ G'dmap'dmap*G + G'∇jac*G + TO.∇²differential(q,grad_q)

RMAP = MRPMap
@test mycostdiff(ϕ) ≈ mycost(qval)
grad_q = ForwardDiff.gradient(mycost, qval)
hess_q = ForwardDiff.hessian(mycost, qval)
dq = q0\q
err = RMAP(dq)
G = TO.∇differential(dq)
dmap = jacobian(RMAP,dq)
∇jac = TO.∇jacobian(RMAP, dq, err)

@test grad_q ≈ Lmult(q0)*dmap'err
@test ForwardDiff.gradient(mycostdiff,ϕ) ≈ (dmap*G)'err
@test ForwardDiff.hessian(mycostdiff,ϕ) ≈ G'dmap'dmap*G + G'∇jac*G + TO.∇²differential(q,grad_q)

RMAP = VectorPart
@test mycostdiff(ϕ) ≈ mycost(qval)
grad_q = ForwardDiff.gradient(mycost, qval)
hess_q = ForwardDiff.hessian(mycost, qval)
dq = q0\q
err = RMAP(dq)
G = TO.∇differential(dq)
dmap = jacobian(RMAP,dq)
∇jac = TO.∇jacobian(RMAP, dq, err)

@test grad_q ≈ Lmult(q0)*dmap'err
@test ForwardDiff.gradient(mycostdiff,ϕ) ≈ (dmap*G)'err
@test ForwardDiff.hessian(mycostdiff,ϕ) ≈ G'dmap'dmap*G + G'∇jac*G + TO.∇²differential(q,grad_q)

RMAP = ExponentialMap
@test mycostdiff(ϕ) ≈ mycost(qval)
grad_q = ForwardDiff.gradient(mycost, qval)
hess_q = ForwardDiff.hessian(mycost, qval)
dq = q0\q
err = RMAP(dq)
G = TO.∇differential(dq)
dmap = jacobian(RMAP,dq)
∇jac = TO.∇jacobian(RMAP, dq, err)

@test grad_q ≈ Lmult(q0)*dmap'err
ForwardDiff.gradient(mycostdiff,ϕ)
(dmap*G)'err
@test ForwardDiff.hessian(mycostdiff,ϕ) ≈ G'dmap'dmap*G + G'∇jac*G + TO.∇²differential(q,grad_q)

jacobian(ExponentialMap, q)
b = @SVector rand(3)
q0 = ExponentialMap(1e-7*@SVector ones(3))
TO.vecnorm(q0)
jacobian(ExponentialMap, q)'b ≈ TO.∇jacobian(ExponentialMap, q, b)
jacobian(ExponentialMap, q)'b
ForwardDiff.jacobian(x->jacobian(ExponentialMap, UnitQuaternion(x))'b, SVector(q0)) ≈ TO.∇jacobian(ExponentialMap, q0, b)
ForwardDiff.jacobian(x->jacobian(ExponentialMap, UnitQuaternion(x))'b, SVector(q)) ≈
    TO.∇jacobian(ExponentialMap, q, b)
@btime TO.∇jacobian(ExponentialMap, $q, $b)

# MRPs
p = rand(MRP)
p0 = rand(MRP)
pval = SVector(p)
ϕ = @SVector zeros(3)
Q = Diagonal(@SVector rand(3))

function mycost(p::SVector{3})
    p = MRP(p)
    err = SVector(p0\p)
    0.5*err'Q*err
end
function mycostdiff(ϕ::SVector{3})
    mycost(SVector(p*MRP(ϕ)))
end

grad_p = ForwardDiff.gradient(mycost, pval)
hess_p = ForwardDiff.hessian(mycost, pval)
err = SVector(p0\p)

grad_p ≈ TO.∇err(p0,p)'Q*err
hess_p ≈ TO.∇²err(p0,p,Q*err) + TO.∇err(p0,p)'Q*TO.∇err(p0,p)

grad_d = ForwardDiff.gradient(mycostdiff, ϕ)
hess_d = ForwardDiff.hessian(mycostdiff, ϕ)
@test grad_d ≈ TO.∇differential(p0\p)'Q*err
@test hess_d ≈ TO.∇differential(p0\p)'Q*TO.∇differential(p0\p) + TO.∇²differential(p0\p,Q*err)

# RPs
g = rand(RodriguesParam)
g0 = rand(RodriguesParam)
gz = RodriguesParam(0.,0.,0.)
gval = SVector(g)
ϕ = @SVector zeros(3)

Q = Diagonal(@SVector rand(3))

function mycost(g::SVector{3})
    g = RodriguesParam(g)
    err = SVector(g0\g)
    0.5*err'Q*err
end
function mycostdiff(ϕ::SVector{3})
    mycost(SVector(g*RodriguesParam(ϕ)))
end

grad_d = ForwardDiff.gradient(mycostdiff, ϕ)
hess_d = ForwardDiff.hessian(mycostdiff, ϕ)
err = g ⊖ g0
@test grad_d ≈ TO.∇differential(g0\g)'Q*err
@test hess_d ≈ TO.∇differential(g0\g)'Q*TO.∇differential(g0\g) + TO.∇²differential(g0\g,Q*err)


text:
using LambdaCalculus
using Test

tests = [
    "bool",
    "operators",
    "numbers"
]

@testset "LambdaCalculus" begin
    for t in tests
        include("$(t).jl")
    end
end


text:
@enum Lobby 一般 上級 特上 鳳凰
@enum Dan 新人 九級 八級 七級 六級 五級 四級 三級 二級 一級 初段 二段 三段 四段 五段 六段 七段 八段 九段 十段 天鳳位

@enum Round 東一 東二 東三 東四 南一 南二 南三 南四 西一 西二 西三 西四
@enum Dice ⚀ ⚁ ⚂ ⚃ ⚄ ⚅
@enum Seat 東家 南家 西家 北家

@enum Suit::UInt8 萬子 筒子 索子 字牌
@enum Rank::UInt8 赤 一 二 三 四 五 六 七 八 九 東 南 西 北 白 發 中

@enum Yaku mentsumo riichi ippatsu chankan rinshan haitei houtei pinfu tanyao ipeiko tonpai nanpai xiapai peipai tonhai nanhai xiahai peihai haku hatsu chun daburii chiitoitsu chanta ittsu sandoujun sandoukou sankantsu toitoi sanankou shousangen honroutou ryanpeikou junchan honitsu chinitsu renhou tenhou chihou daisangen suuankou suuankoutanki tsuuiisou ryuuiisou chinroutou chuurenpouto junseichuurenpouto kokushi kokushijuusan daisuushi shousuushi suukantsu dora uradora akadora
@enum Limit nolimit mangan haneman baiman sanbaiman yakuman
@enum Ryuukyoku tsuujou yao9 reach4 ron3 kan4 kaze4 nagashi

@enum State CLOSED OPENED RIICHI
@enum Play ポン チー 大明槓 小明槓 暗槓 キタ

struct Tile
    rank::      Rank
    suit::      Suit
end

const Tiles =
    Vector{Union{Tile,Missing}}

struct Meld
    play::      Play
    from::      Seat
    tile::      Tile
    with::      Vector{Tile}
end

const Melds =
    Vector{Union{Meld,Missing}}

struct Rules
    offine::    Bool
    akadora::   Bool
    kuitan::    Bool
    hanchan::   Bool
    sanma::     Bool
    blitz::     Bool
    lobby::     Lobby
end

struct Table
    namaes::    Vector{String}
    ranks::     Vector{Dan}
    rates::     Vector{Float32}
    sexes::     Vector{Char}
end

struct RoundWin
    value::     Int32
    hanfu::     Tuple{UInt8,UInt8}
    limit::     Limit
    yaku::      Vector{Tuple{Yaku,Int}}
    dora::      Tiles
    ura::       Tiles
    caller::    Seat
    provider::  Seat
end

struct RoundTie
    tierule::   Ryuukyoku
    reveal::    Vector{Seat}
end

const Result =
    Union{RoundWin,RoundTie,Nothing}

mutable struct PlayState
    PlayState(
        ::UndefInitializer
    ) = new()

    logid::     String
    rules::     Rules
    table::     Table
    dced::      Vector{Seat}
    turn::      Int8
    cycle::     Round
    rolls::     Tuple{Dice,Dice}
    dealer::    Seat
    player::    Seat
    honba::     Int8
    rbets::     Int8
    doraid::    Vector{Tile}
    scores::    Vector{Int32}
    hands::     Vector{Tiles}
    melds::     Vector{Melds}
    discard::   Vector{Tiles}
    tedashi::   Vector{Tiles}
    rchtile::   Vector{Tiles}
    status::    Vector{State}
    result::    Result
end


text:
function main()
print(" ")
end
main()

max_stars_repo_name: justletterh/why

max_issues_repo_name: justletterh/why

max_forks_repo_name: justletterh/why


text:
type Time_Selector
  DefaultDim :: Tuple{Int64,Vararg{Int64}}
  Starter :: Tuple{Int64,Vararg{Int64}}
  Ender   :: Tuple{Int64,Vararg{Int64}}
end

function T_Selector( Dim  , Starter, Ender )
  Time_Selector(tuple(Dim...), tuple(Starter...), tuple(Ender...))
end
export T_Selector

function TProcess(sst :: Array{Float64}, TS :: Time_Selector,
                      time :: Tuple{Array{Int64,1},Vararg{Array{Int64,1}}},
                      Dim :: Tuple{Int64,Vararg{Int64}} = TS.DefaultDim )
  sst1 = sst
  for (d,td, ster, eder) in zip(Dim,time, TS.Starter, TS.Ender)
    sdd = find(( td .>= ster) & (td .<= eder));
    sst1 = slicedim(sst1,d,sdd)
  end
  return sst1
end

export TProcess, Time_Selector;


text:
addprocs(2)

@everywhere using SharedDataStructures
@everywhere using Semaphores
using Base.Test

include("test_shared_circdq.jl")
include("test_shm_dict.jl")

test_shared_circdq()
test_shm_dict(false)
test_shm_dict(true)


text:
using FeedbackParticleFilters, Distributions, Random, PDMats

println("Testing LinearDiffusionStateModel.jl:")

@testset "LinearDiffusionStateModel.jl" begin

    A      = [0. 1.; -0.7 -0.3]
    B      = zeros(2,1)
    B[2,1] = sqrt(2.)
    init   = MvNormal(2, 1.)

    print("  inner constructor")
    mod = LinearDiffusionStateModel(A, B, init)
    print(".")
    @test mod isa LinearDiffusionStateModel{Float64,Array{Float64,2},Array{Float64,2},MvNormal{Float64,PDMats.ScalMat{Float64},Distributions.ZeroVector{Float64}}}
    print(".")
    B2 = zeros(Int, 2, 1)
    B2[2,1] = 2
    @test_throws MethodError LinearDiffusionStateModel(A, B2, init) # wrong element type of B2
    print(".")
    B3 = zeros(3, 1)
    @test_throws DimensionMismatch LinearDiffusionStateModel(A, B3, init) # wrong size of B3
    println("DONE.")

    print("  method find_stationary_variance")
    print(".")
    Sigma = find_stationary_variance(A, B*B')
    @test Sigma == [4.746796851882334 -5.543824348282954e-5; -5.543824348282954e-5 3.3192089048776765]
    println("DONE.")

    print("  method drift")
    print(".")
    @test drift(mod) == A
    println("DONE.")

    print("  method diffusion")
    print(".")
    @test diffusion(mod) == B
    println("DONE.")

    print("  method initial_condition")
    print(".")
    @test initial_condition(mod) == mod.init
    println("DONE.")

    print("  method initialize")
    print(".")
    Random.seed!(0)
    @test initialize(mod) == [0.6791074260357777, 0.8284134829000359]
    println("DONE.")

    print("  method state_dim")
    print(".")
    @test state_dim(mod) == 2
    println("DONE.")

    print("  method noise_dim")
    print(".")
    @test noise_dim(mod) == 1
    println("DONE.")

    print("  method drift_function")
    print(".")
    @test drift_function(mod)([1., 3.]) == [3.0, -1.5999999999999999]
    println("DONE.")

    print("  method diffusion_function")
    print(".")
    @test diffusion_function(mod)([1., 3.]) == hcat([0.0, 1.4142135623730951]...)'
    println("DONE.")

    print("  calling instance of DiffusionStateModel")
    print(".")
    Random.seed!(0)
    x0 = ones(2)
    @test mod(x0, 0.01) == [1.01, 1.086040293220808]
    print(".")
    Random.seed!(0)
    xx = hcat(ones(2), zeros(2))
    @test mod(xx, 0.01) == [1.01 0. ; 1.086040293220808 0.1171553582769963]
    println("DONE.")

    print("  method propagate!")
    print(".")
    Random.seed!(0)
    propagate!(x0, mod, 0.01)
    @test x0 == [1.01, 1.086040293220808]
    print(".")
    Random.seed!(0)
    propagate!(xx, mod, 0.01)
    @test xx == [1.01 0. ; 1.086040293220808 0.1171553582769963]
    println("DONE.")

end; #LinearDiffusionStateModel.jl


text:
#    Problem : GROUP A
#    *********
#    This problem is quartic and has a banded Hessian with bandwidth = 9.
#
#    Origonal SIF Source:
#    Problem 61 in
#    A.R. Conn, N.I.M. Gould, M. Lescrenier and Ph.L. Toint,
#    "Performance of a multifrontal scheme for partially separable
#    optimization",
#    Report 88/4, Dept of Mathematics, FUNDP (Namur, B), 1988.
#
#    Implementation translated from Source:
#    http://eprints.tsu.ge/234/14/Tests%20collection-K-F.pdf
#
#    BDQRTIC.SIF classification SUR2-AN-V-0
#
#    Number of variables (variable)

f = (x) -> begin
    fx = 0.0
    xn_sqr = x[lastindex(x)]^2
    for i in firstindex(x):lastindex(x)-4
        α = 3.0 - 4.0x[i]
        γ = x[i]^2 + 2.0x[i+1]^2 + 3.0x[i+2]^2 + 4.0x[i+3]^2 + 5.0xn_sqr
        fx += α*γ + γ^2
    end
    return fx
end

g! = (g, x) -> begin
    xn = x[lastindex(x)]
    cxn_sqr = 5.0xn^2
    gn = 0.0
    for i in firstindex(x):lastindex(x)-4
        α = 3.0 - 4.0x[i]
        γ = x[i]^2 + 2.0x[i+1]^2 + 3.0x[i+2]^2 + 4.0x[i+3]^2 + cxn_sqr
        g[i] += -8.0α + 4.0γ*x[i];
        g[i+1] += 8.0γ*x[i+1];
        g[i+2] += 12.0γ*x[i+2];
        g[i+3] += 16.0γ*x[i+3];
        gn += 20.0γ*xn;
    end
    g[lastindex(x)] = gn
    return g
end

fg! = (g, x) -> begin
    fx = 0.0
    xn = x[lastindex(x)]
    cxn_sqr = xn^2
    gn = 0.0
    for i in firstindex(x):lastindex(x)-4
        α = 3.0 - 4.0x[i]
        γ = x[i]^2 + 2.0x[i+1]^2 + 3.0x[i+2]^2 + 4.0x[i+3]^2 + cxn_sqr
        fx += α*γ + γ^2
        g[i]   += -8.0α + 4.0γ*x[i];
        g[i+1] += 8.0γ*x[i+1];
        g[i+2] += 12.0γ*x[i+2];
        g[i+3] += 16.0γ*x[i+3];
        gn += 20.0γ*xn;
    end
    g[lastindex(x)] = gn
    return fx, g
end

init = (n::Int=5000) -> begin
	n < 5 && @warn("BDQRTIC: number of variables must be ≥ 5")
	n = max(n, 5)

    return n, ones(n)
end

@warn "TODO: Debug BDQRTIC"
#TestSet["BDQRTIC"] = UncProgram("BDQRTIC", f, g!, fg!, init)


text:
import Base:copyto!
import Zygote:gradient

using Knet
using Zygote

Base.copyto!(dest::Knet.Param, src::Knet.Param) = copyto!(value(dest), value(src))

function gradient(f, ::Val{:Knet}, ps::Zygote.Params)
    gs = @diff f()
    Zygote.Grads(IdDict(p => grad(gs, p) for p in ps))
end


text:
include("FSI_FESpaces.jl")
include("ElasticFlag.jl")
include("Analytical.jl")
include("Oscillator.jl")

# Output function
function writePVD(filePath::String, trian::Triangulation, sol; append=false)
    outfiles = paraview_collection(filePath, append=append) do pvd
        for (i, (xh, t)) in enumerate(sol)
            println("STEP: $i, TIME: $t")
            println("============================")
            uh = xh[1]
            vh = xh[2]
						ph = xh[3]
            pvd[t] = createvtk(
                trian,
                filePath * "_$t.vtu",
                cellfields = ["uh" => uh, "vh" => vh, "ph" => ph]
            )
        end
    end
end

function get_FSI_triangulations(models,coupling)
  trian = Triangulation(models[:Ω])
  trian_s = Triangulation(models[:Ωs])
  trian_f = Triangulation(models[:Ωf])
  function Γi_triangulation(coupling)
    if typeof(coupling) == WeakForms.Coupling{:weak}
      InterfaceTriangulation(models[:Ωf],models[:Ωs])
    else
      BoundaryTriangulation(models[:Ωf],tags="interface")
    end
  end
  trian_Γi = Γi_triangulation(coupling)
  Dict(:Ω=>trian, :Ωs=>trian_s, :Ωf=>trian_f, :Γi=>trian_Γi)
end

function get_FSI_measures(triangulations,order)
  degree = 2*order
  bdegree = 2*order
  dΩ  = Measure(triangulations[:Ω],degree)
  dΩs = Measure(triangulations[:Ωs],degree)
  dΩf = Measure(triangulations[:Ωf],degree)
  dΓi = Measure(triangulations[:Γi],bdegree)
  Dict(:Ω=>dΩ, :Ωs=>dΩs, :Ωf=>dΩf, :Γi=>dΓi)
end

function get_Stokes_operator(X,Y,strategy,dΩ,μ,f)
  res(x,y) = WeakForms.stokes_residual(strategy,x,y,μ,f,dΩ)
  jac(x,dx,y) = WeakForms.stokes_jacobian(strategy,dx,y,μ,dΩ)
  op = FEOperator(res,jac,X,Y)
end

function get_FSI_operator(X,Y,coupling,strategy,Tₕ,dTₕ,params)
  m_params, f_params, s_params, Γi_params = params

  # Compute cell area (auxiliar quantity for mesh motion eq.)
  α_m = m_params[:α]
  if( m_params[:w_strategy] == "volume")
    # volf = get_cell_measure(Tₕ[:Ωf])
    # vols = get_cell_measure(Tₕ[:Ωs])
    # α_Ωf = α_m * reindex(volf,Tₕ[:Ωf])
    # α_Ωs = α_m * reindex(vols,Tₕ[:Ωs])
    α_Ωf = α_m * get_cell_measure(Tₕ[:Ωf])
    α_Ωs = α_m * get_cell_measure(Tₕ[:Ωs])
    # α_Ωs = α_Ωf
    if ( typeof(coupling) == WeakForms.Coupling{:weak} )
       α_Γi = lazy_map(Reindex(α_Ωf),get_cell_to_bgcell(Tₕ[:Γi].⁺))
    else
      α_Γi = lazy_map(Reindex(α_Ωf),get_cell_to_bgcell(Tₕ[:Γi]))
    end
  else
    α_Ωf = α_m; α_Ωs = α_m; α_Γi = α_m
  end

  # Compute interface element size (if weak coupling)
  if ( typeof(coupling) == WeakForms.Coupling{:weak} )
    dim = num_cell_dims(Tₕ[:Γi])
    h_Γfs = get_array(∫(1)dTₕ[:Γi])
    hΓᵢ = CellField( lazy_map(h->(h.^(-dim)),h_Γfs), Tₕ[:Γi])
    # trian_boundary_Γi = get_left_boundary(Tₕ[:Γi])
    # hΓᵢ = reindex(cell_measure(trian_boundary_Γi,Tₕ[:Ω]),trian_boundary_Γi)
  else
    hΓᵢ = 0.0
  end

  # Interface normal vector
  n_Γi = get_normal_vector(Tₕ[:Γi])

  # Complete params
  push!(f_params, :α=>α_Ωf)
  push!(f_params, :E=>m_params[:E])
  push!(f_params, :ν=>m_params[:ν])
  push!(s_params, :α=>α_Ωs)
  push!(Γi_params, :α=>α_Γi)
  push!(Γi_params, :E=>m_params[:E])
  push!(Γi_params, :ν=>m_params[:ν])
  push!(Γi_params, :n=>n_Γi)
  push!(Γi_params, :h=>hΓᵢ)

  # Define operator
  function res(t,(x,xt),y)
    WeakForms.fluid_residual_Ω(strategy,coupling,t,x,xt,y,f_params,dTₕ[:Ωf]) +
    WeakForms.solid_residual_Ω(strategy,coupling,t,x,xt,y,s_params,dTₕ[:Ωs]) +
    WeakForms.fsi_residual_Γi(strategy,coupling,x,y,Γi_params,dTₕ[:Γi])
  end
  function jac(t,(x,xt),dx,y)
    WeakForms.fluid_jacobian_Ω(strategy,coupling,t,x,xt,dx,y,f_params,dTₕ[:Ωf]) +
    WeakForms.solid_jacobian_Ω(strategy,coupling,t,x,xt,dx,y,s_params,dTₕ[:Ωs]) +
    WeakForms.fsi_jacobian_Γi(strategy,coupling,x,dx,y,Γi_params,dTₕ[:Γi])
  end
  function jac_t(t,(x,xt),dxt,y)
    WeakForms.fluid_jacobian_t_Ω(strategy,coupling,t,x,xt,dxt,y,f_params,dTₕ[:Ωf]) +
    WeakForms.solid_jacobian_t_Ω(strategy,coupling,t,x,xt,dxt,y,s_params,dTₕ[:Ωs])
  end
  op = TransientFEOperator(res,jac,jac_t,X,Y)

end


text:
using Peaks
using Glob
using FFTW
using DSP
using DataFrames

"""
    scope_rawdata(WAVEDESC::Int)

Returns data needed to read PMT rawdata format

# Fields

- `WAVEDESC::String`   : Base address that serves as reference to all other addresses.
"""
scope_rawdata(io::IOStream, WAVEDESC::Int) = Dict("WAVEDESC"         => WAVEDESC,
                                    "COMM_TYPE"	          => readword(io, WAVEDESC + 32),
                                    "COMM_ORDER"          => readlong(io, WAVEDESC + 34),
                                    "WAVE_DESCRIPTOR"     => readlong(io, WAVEDESC + 36),
                                    "USER_TEXT"           => readlong(io, WAVEDESC + 40),
                                    "TRIGTIME_ARRAY"      => readlong(io, WAVEDESC + 48),
                                    "WAVE_ARRAY_1"	      => readlong(io, WAVEDESC+ 60),
                                    "INSTRUMENT_NAME"     => readstring(io, WAVEDESC+ 76, 14),
                                    "INSTRUMENT_NUMBER"	  => readlong(io, WAVEDESC+ 92),
                                    "WAVE_ARRAY_COUNT"	  => readlong(io, WAVEDESC+ 116),
                                    "VERTICAL_GAIN"	      => readfloat(io, WAVEDESC+ 156),
                                    "VERTICAL_OFFSET"	  => readfloat(io, WAVEDESC+ 160),
                                    "NOMINAL_BITS"	      => readword(io, WAVEDESC+ 172),
                                    "HORIZ_INTERVAL"	  => readfloat(io, WAVEDESC+ 176),
                                    "HORIZ_OFFSET"	      => readdouble(io, WAVEDESC+ 180),
                                    "TRIGGER_TIME"	      => readtimestamp(io, WAVEDESC+ 296),
                                    "RECORD_TYPE"	      => readword(io, WAVEDESC+ 316),
                                    "PROCESSING_DONE"	  => readword(io, WAVEDESC+ 318),
                                    "TIMEBASE"	          => readword(io, WAVEDESC+ 324), #rg
                                    "VERT_COUPLING"	      => readword(io, WAVEDESC+ 326),
                                    "PROBE_ATT"	          => readfloat(io, WAVEDESC+ 328),
                                    "FIXED_VERT_GAIN"	  => readword(io, WAVEDESC+ 332), #rg
                                    "BANDWIDTH_LIMIT"	  => readword(io, WAVEDESC+ 334),
                                    "WAVE_SOURCE"	      => readword(io, WAVEDESC+ 344))

"""Returns the adress of the reference mark in the stream"""
function wavedesc(io::IOStream, strn="WAVEDESC", strl=20)
	seek(io, 0)
	header = Vector{UInt8}(undef, strl)
	readbytes!(io, header)
	header
	sh = String(header)
	result = findfirst(strn, sh)
	first(result) - 1
end

""" reads two bytes at location s and unpack them as an int16"""
function readword(s::IOStream, pos::Int)
	seek(s, pos)
	bytes = Vector{UInt8}(undef, 2)
	readbytes!(s, bytes)
    collect(reinterpret(Int16, bytes))[1]
end

""" reads four bytes at location s and unpack them as an Int32"""
function readlong(s::IOStream, pos::Int)
	seek(s, pos)
	bytes = Vector{UInt8}(undef, 4)
	readbytes!(s, bytes)
    collect(reinterpret(Int32, bytes))[1]
end

""" reads four bytes at location s and unpack them as an Float32"""
function readfloat(s::IOStream, pos::Int)
	seek(s, pos)
	bytes = Vector{UInt8}(undef, 4)
	readbytes!(s, bytes)
    collect(reinterpret(Float32, bytes))[1]
end

""" reads eight bytes at location s and unpack them as an Float64"""
function readdouble(s::IOStream, pos::Int)
	seek(s, pos)
	bytes = Vector{UInt8}(undef, 8)
	readbytes!(s, bytes)
    collect(reinterpret(Float64, bytes))[1]
end

""" reads eight bytes at location s and unpack them as an Float64"""
function readstring(s::IOStream, pos::Int, strlen::Int)
	seek(s, pos)
	bytes = Vector{UInt8}(undef, strlen)
	readbytes!(s, bytes)
    String(bytes)
end

""" reads the time stamp"""
function readtimestamp(s::IOStream, pos::Int)
	seek(s,pos)
	bytes = Vector{UInt8}(undef, 8)
	readbytes!(s, bytes)
	seconds = collect(reinterpret(Float64, bytes))[1]

	bytes = Vector{UInt8}(undef, 1)
	readbytes!(s, bytes)
	minutes = collect(reinterpret(Int8, bytes))[1]

	readbytes!(s, bytes)
	hours = collect(reinterpret(Int8, bytes))[1]

	readbytes!(s, bytes)
	days = collect(reinterpret(Int8, bytes))[1]

	readbytes!(s, bytes)
	months = collect(reinterpret(Int8, bytes))[1]

	bytes = Vector{UInt8}(undef, 2)
	readbytes!(s, bytes)
	years = collect(reinterpret(Int16, bytes))[1]

	(year=years,month=months,day=days,hour=hours,minute=minutes,second=seconds)
end

""" Returns the (time, ampl) data (t in mus ampl in mV)"""
function xydata(io::IOStream, addr::Dict{String, Any})
	yw = read_ywave(io, addr)
	wy = addr["VERTICAL_GAIN"] * yw .- addr["VERTICAL_OFFSET"]
	xw = collect(1:addr["WAVE_ARRAY_COUNT"]) * addr["HORIZ_INTERVAL"] .+ addr["HORIZ_OFFSET"]
	wx = -xw[1] .+ xw
	(time=wx *1e+6, ampl = -wy*1e+3 )
end

""" reads the waveform in y"""
function read_ywave(s::IOStream, addr::Dict{String, Any})
	seek(s, addr["WAVEDESC"] + addr["WAVE_DESCRIPTOR"] + addr["USER_TEXT"] + addr["TRIGTIME_ARRAY"])

	bytes = Vector{UInt8}(undef, addr["WAVE_ARRAY_1"])
	readbytes!(s, bytes)
	if addr["COMM_TYPE"] == 0  # file is type byte (Int8)
		collect(reinterpret(Int8, bytes))
	else
		collect(reinterpret(Int16, bytes))
	end
end

### Analysis functions

"""
Read  weveform specifying an iostream
"""
function read_waveform(zio::IOStream)
	WAVEDESC = wavedesc(zio)
	address = scope_rawdata(zio, WAVEDESC)
	xydata(zio, address)
end

"""
Reads  weveform specifying directory and file name
"""
function read_waveform(pmtdir::String, fname::String)
    zpath = joinpath(pmtdir,fname)
	zio = open(zpath, "r")
	wvfm = read_waveform(zio)
	close(zio)
	wvfm
end

"""
Read waveform specified by fnumber taking the full path from a vector of strings
"""
function read_waveform(csvf::Vector{String}, fnumber::Integer)
	zio = open(csvf[fnumber], "r")
	wvfm = read_waveform(zio)
	close(zio)
	wvfm
end

"""Return the mean, std, and (+-) threshold at nsigma, for raw or filtered waveform"""
function wstats(wvfm::NamedTuple{(:time, :ampl)}; nsigma::Float64=1.0)
	meanfs = mean(wvfm.ampl)
	stdfs = std(wvfm.ampl)
	thrp = meanfs + stdfs * nsigma
	thrn = meanfs - stdfs * nsigma
	(mean=meanfs, std=stdfs, thrp=thrp, thrn=thrn)
end

"""Return the sampling rate"""
function sampling_rate(wvfm::NamedTuple{(:time, :ampl)}, wmus::typeof(1.0μs))
	uconvert(GHz, (length(wvfm.time) / (wmus/1.0s)) * Hz)
end

"""Return the sampling period"""
function sampling_period(wvfm::NamedTuple{(:time, :ampl)}, wmus::typeof(1.0μs))
	uconvert(ns, 1.0 / sampling_rate(wvfm, wmus))
end

"""Return the fourier transform of the waveform wvfm"""
function fourier_transform(wvfm::NamedTuple{(:time, :ampl)}, wmus::typeof(1.0μs))
	#ts = sampling_period(wvfm, wmus)
	#tss = uconvert(s, ts)/s           # tss is the sampling period in seconds
    sr = uconvert(Hz, sampling_rate(wvfm, wmus)) /Hz
	F = fft(wvfm.ampl) |> fftshift   # fourier transform
	#freqs = fftfreq(length(wvfm.time), 1.0/tss) |> fftshift  # frequencies in Hz
    freqs = fftfreq(length(wvfm.time), sr) |> fftshift  # frequencies in Hz
	F, freqs
end

"""
	Returns the amplitude filtered by a low pass filter

*Fields*

`wvfm`        : Waveform
`wmus`        : width of time window in μs.
`filtertype`  : two possible filters: Butterworth or Chebyshev1
`flhz`        : higer frequency accepted by the filter in Hz
`n`           : filter order
`ϵ`           : ripple factor for Chebyshev1 filter
"""
function filter_signal_lp(wvfm::NamedTuple{(:time, :ampl)}, wmus::typeof(1.0μs);
                          filtertype::String, flhz::Float64, n=4, ϵ=1.0)

	fs = uconvert(Hz, sampling_rate(wvfm, wmus))/Hz
	_, _, fampl = filter_signal_lowpass(wvfm, flhz, fs, filtertype, n, ϵ)

    (time=wvfm.time, ampl=fampl)
end

"""
Returns frequency response, filter (Butterworth, or Chebyshev) and filtered signal

*Fields*

`flh`         : higer frequency accepted by the filter in Hz
`fs`          : sampling rate in Hz
`filtertype`  : two possible filters: Butterworth or Chebyshev1
`n`           : filter order
`ϵ`           : ripple factor for Chebyshev1 filter

"""
function filter_signal_lowpass(wvfm::NamedTuple{(:time, :ampl)},
	                           flh::Float64, fs::Float64, filtertype::String,
	                           n=4, ϵ=1.0)

	responsetype = Lowpass(flh; fs=fs)

	if filtertype == "Butterworth"
		designmethod = Butterworth(n)
	elseif filtertype == "Chebyshev1"
		ripple = 10.0*log10(ϵ^2 + 1.0)
		designmethod = Chebyshev1(n, ripple)
	end
	filter = digitalfilter(responsetype, designmethod)
	H, _ = freqresp(filter)
	H, filter, filt(filter, wvfm.ampl)
end

"""
Selects the peaks in the waveform above prominence cut

	Fields

`wvfm`        : Waveform
`thrp`        : threshold for peak search.
`promsel`     : cut on peak prominence
`wsel`        : cut on peak width

Returns

A filtered waveform and a SPeaks struct

"""
function select_filtered_peaks(wvfm::NamedTuple{(:time, :ampl)}, thrp::Float64;
                               promsel::Float64, wsel=0.0)

	wfdf = DataFrame(time=wvfm.time, ampl=wvfm.ampl)
    #amplth = wvfm.ampl[wvfm.ampl .> thrp]

	wvfmFlt = filter(row -> row[:ampl] > thrp, wfdf)
	pks, _ = findmaxima(wvfmFlt.ampl)
	peaks2, proms = peakproms(pks, wvfmFlt.ampl; minprom=promsel)
	#pks, _ = findmaxima(amplth)
	#peaks2, proms = peakproms(pks,amplth; minprom=promsel)

	if length(peaks2) == 0
		return Nothing
	end

	peaks2, widths, leftedge, rightedge = peakwidths(peaks2, wvfmFlt.ampl, proms; minwidth=wsel)

	#ys = [amplth[i] for i in peaks2]
	#xs = [wvfm.time[i] for i in peaks2]

	ys = [wvfmFlt.ampl[i] for i in peaks2]
	xs = [wvfmFlt.time[i] for i in peaks2]

	npeaks = length(peaks2)
	xbase = ones(npeaks,1)*[promsel]

	return (ampl = wvfmFlt.ampl, speaks = SPeaks(peaks2, proms, widths, leftedge, rightedge, xs,ys, xbase, promsel))
end

function select_filtered_peaks2(wvfm::NamedTuple{(:time, :ampl)}, thrp::Float64;
	                            promsel::Float64, wsel=0.0)

	amplthi = findall(x-> x > thrp, wvfm.ampl)
	amplth = [wvfm.ampl[i] for i in amplthi]
	timeth = [wvfm.time[i] for i in amplthi]

	pks, _ = findmaxima(amplth)
	peaks2, proms = peakproms(pks, amplth; minprom=promsel)

	if length(peaks2) == 0
		return Nothing
	end

	peaks2, widths, leftedge, rightedge = peakwidths(peaks2, amplth, proms; minwidth=wsel)

	ys = [amplth[i] for i in peaks2]
	xs = [timeth[i] for i in peaks2]

	npeaks = length(peaks2)
	xbase = ones(npeaks,1)*[promsel]

	return (ampl = amplth, speaks = SPeaks(peaks2, proms, widths, leftedge, rightedge, xs,ys, xbase, promsel))
end

"""
Computes the proms and widths of the events in the input vector
"""
function proms(csvf::Vector{String}, fi::Integer, fe::Integer;
	           flhz::Float64, promsel::Float64,
	           nsigma=2.0, filtertype="Butterworth")

	spks0 = select_peaks(csvf, fi, fe;  flhz=flhz,
				         promsel=promsel, nsigma=nsigma, filtertype=filtertype)

	sP = reduce(vcat,[spks0[i].proms for i in 1:length(spks0) if spks0[i] !=Nothing])
	sW = reduce(vcat,[spks0[i].widths for i in 1:length(spks0) if spks0[i] !=Nothing])
	return sP, sW
end

"""
Returns a vector o SPeaks, one per event in the input vector
"""
function select_peaks(csvf::Vector{String}, i0::Integer, il::Integer;
                      flhz::Float64, promsel::Float64,
	                  nsigma=2.0, filtertype="Butterworth")

	ndim = il - i0 + 1
    SPK = Vector{SPeaks}(undef, ndim)
	for fileNumber in i0:il
		wvfm   = read_waveform(csvf,fileNumber)
        tw     = (wvfm.time[end] - wvfm.time[1]) * μs
        fwvfm  = filter_signal_lp(wvfm, tw; filtertype= filtertype, flhz=flhz)
        fstats = wstats(fwvfm; nsigma=nsigma)

		result = select_filtered_peaks2(fwvfm, fstats.thrp; promsel=promsel, wsel=0.0)
        if result == Nothing
            SPK[fileNumber] = SPeaks([1], [0.0], [0.0], [0.0], [0.0], [0.0],[0.0], [0.0], 0.0)
        else
            SPK[fileNumber] = result.speaks
        end
	end
	SPK
end

### Plots

"""
Plot the  waveform wvfm
"""
function plot_waveform(wvfm::NamedTuple{(:time, :ampl)}, stats::NamedTuple{(:mean, :std, :thrp, :thrn)};
                       window=false, sct=false, trace=false, ws=-1, we=1)

    xend = length(wvfm.time)
    if window
        range  = ws:we
    else
        range = 1:xend
    end

    if sct
        p1 = scatter(wvfm.time[range], wvfm.ampl[range],  markersize=2,
        color = :black,
        label=false,
        fmt = :png)
    elseif trace
        p1 = plot(wvfm.ampl, lw=2, label=false, fmt = :png)
    else
        p1 = plot(wvfm.time[range], wvfm.ampl[range], lw=2, label=false, fmt = :png)
    end

    hline!([stats.thrp], label=false, fmt = :png)
    hline!([stats.thrn], label=false, fmt = :png)
    xlabel!("t (μs)")
    ylabel!("A (mV)")
    p1
end

"""
Plot the fourier transform of the waveform wvfm
"""
function plot_fft(wvfm::NamedTuple{(:time, :ampl)}, wmus::typeof(1.0μs); sct=true, window=false, ws=-1, we=1)
	F, freqs = fourier_transform(wvfm, wmus)
	xl = Integer(length(freqs))  #positive freqs
	xi = Integer(xl/2)
	fmhz = freqs[xi:xl] * 1.0e-6      # frequencies in MHz
	ampl = abs.(F)[xi:xl]  # abs amplitude

    if window
        xi = xi + ws
        xl = min(xl, we)
    end
    fmhz = freqs[xi:xl] * 1.0e-6      # frequencies in MHz
	ampl = abs.(F)[xi:xl]  # abs amplitude

    if sct
        p1 = scatter(fmhz, ampl,  markersize=2,
                     color = :black,
                     label=false,
                     fmt = :png)
    else
        p1 = plot(fmhz, ampl, lw=2, label=false, fmt = :png)
    end

	xlabel!("frequency (MHz)")
	ylabel!("A")
    p1
end

"""
	Plots filtered peaks
"""
function plot_filtered_peaks(ampl::Vector{Float64}, spks::SPeaks)
	plot(ampl, lw=2, label=false)
	scatter!(spks.peaks, spks.ys, label="peak")
	scatter!(spks.leftedge, spks.xbase, label="leftedge")
	scatter!(spks.rightedge, spks.xbase, label="rightedge")
	hline!([spks.promsel], label=false)
	xlabel!("peak index")
	ylabel!("peak amplitude")
end

max_stars_repo_name: jjgomezcadenas/LaserLab

max_issues_repo_name: jjgomezcadenas/LaserLab

max_forks_repo_name: jjgomezcadenas/LaserLab


text:
for T in (Float32, Float64, ComplexF32, ComplexF64)

  @info "Testing hsl_ma97 with $T data"
  matrix_type = T in (ComplexF32, ComplexF64) ? :herm_indef : :real_indef

  ϵ = sqrt(eps(real(T)))
  n = 5
  A = convert(T, 3) * sprand(T, n, n, .5)
  A = A + A' + sparse(T(1)*I, n, n)

  # Test symmetric indefinite A.
  ma97 = Ma97(A)
  ma97_factorize!(ma97, matrix_type=matrix_type)
  @test ma97.info.flag == 0

  # Solve with one rhs.
  b = rand(T, n)
  x = ma97_solve(ma97, b)
  @test ma97.info.flag == 0
  LU = lu(A)
  x_exact = LU \ b
  @test norm(x - x_exact) ≤ ϵ * norm(x_exact)

  # Solve with multiple rhs.
  b = rand(T, n, 3)
  x = ma97_solve(ma97, b)
  @test ma97.info.flag == 0
  x_exact = LU \ b
  @test norm(x - x_exact) ≤ ϵ * norm(x_exact)

  piv_order, d = ma97_inquire(ma97, matrix_type=matrix_type)
  @test ma97.info.flag == 0

  # Test backslash.
  x = ma97 \ b
  @test norm(x - x_exact) ≤ ϵ * norm(x_exact)

  # Alter d.
  d[1,1] *= 2
  ma97_alter!(ma97, d)

  # Test convenience interface.
  x = ma97_solve(A, b, matrix_type=matrix_type)
  @test norm(x - x_exact) ≤ ϵ * norm(x_exact)

  # Test symmetric positive definite A.
  A = A * A';
  matrix_type = T in (ComplexF32, ComplexF64) ? :herm_pd : :real_spd

  ma97 = Ma97(A)
  ma97_factorize!(ma97, matrix_type=matrix_type)
  @test ma97.info.flag == 0

  b = rand(T, n)
  x = ma97_solve(ma97, b)
  @test ma97.info.flag == 0
  LU = lu(A)
  x_exact = LU \ b
  @test norm(x - x_exact) ≤ ϵ * norm(x_exact)

  # Solve with multiple rhs.
  b = rand(T, n, 3)
  x = ma97_solve(ma97, b)
  @test ma97.info.flag == 0
  x_exact = LU \ b
  @test norm(x - x_exact) ≤ ϵ * norm(x_exact)

  d = ma97_inquire(ma97, matrix_type=matrix_type)
  @test ma97.info.flag == 0

  # Test rectangular A.
  A = rand(T, 10, 6)
  b = rand(T, 10)
  @info "Warning below is expected and normal"
  (r, x) = ma97_least_squares(A, b)
  x_exact = A \ b
  @test norm(x - x_exact) ≤ ϵ * norm(x_exact)

  A = rand(T, 6, 10)
  b = rand(T, 6)
  @info "Warning below is expected and normal"
  (x, y) = ma97_min_norm(A, b)
  x_exact = A \ b
  @test norm(x - x_exact) ≤ ϵ * norm(x_exact)

  # Test if errors are thrown correctly.
  A = rand(T, 10, 5)
  @test_throws Ma97Exception Ma97(A)

  A = rand(T, 6, 10)
  b = rand(T, 3)
  @test_throws Ma97Exception ma97_min_norm(A, b)
  @test_throws Ma97Exception ma97_solve(ma97, b)

end


text:
@doc """
    pairwise(dist::StringDistance, xs::AbstractVector; preprocess = nothing)
    pairwise(dist::StringDistance, xs::AbstractVector, ys::AbstractVector; preprocess = nothing)

Compute distances between all pairs of elements in `xs`  and `ys` according to the
`StringDistance` `dist`.

For QGramDistances preprocessing will be used either if `preprocess` is set
to true or if there are more than 5 elements in `xs`. Set `preprocess` to
false if no preprocessing should be used, regardless of length.

Both symmetric and asymmetric versions are available.

### Examples
```julia-repl
julia> using StringDistances
julia> iter = ["New York", "Princeton"]
julia> pairwise(Levenshtein(), iter) # symmetric
2×2 Array{Float64,2}:
 0.0  9.0
 9.0  0.0
julia> iter2 = ["San Francisco"]
julia> pairwise(Levenshtein(), iter, iter2) # asymmetric
2×1 Array{Float64,2}:
 12.0
 10.0
```
"""
Distances.pairwise

function Distances.pairwise(dist::StringDistance, xs::AbstractVector, ys::AbstractVector; preprocess = nothing)
    T = result_type(dist, eltype(xs), eltype(ys))
    if Missing <: Union{eltype(xs), eltype(ys)}
        T = Union{T, Missing}
    end
    R = Matrix{T}(undef, length(xs), length(ys))
    pairwise!(R, dist, xs, ys; preprocess = preprocess)
end

function Distances.pairwise(dist::StringDistance, xs::AbstractVector; preprocess = nothing)
    T = result_type(dist, eltype(xs), eltype(xs))
    if Missing <: eltype(xs)
        T = Union{T, Missing}
    end
    R = Matrix{T}(undef, length(xs), length(xs))
    pairwise!(R, dist, xs; preprocess = preprocess)
end

@doc """
    pairwise!(R::AbstractMatrix, dist::StringDistance, xs::AbstractVector; preprocess = nothing)
    pairwise!(R::AbstractMatrix, dist::StringDistance, xs::AbstractVector, ys::AbstractVector; preprocess = nothing)

Compute distances between all pairs of elements in `xs` and `ys` according to the
`StringDistance` `dist` and write the result in `R`.

For QGramDistances preprocessing will be used either if `preprocess` is set
to true or if there are more than 5 elements in `xs`. Set `preprocess` to
false if no preprocessing should be used, regardless of length.
"""
Distances.pairwise!

function Distances.pairwise!(R::AbstractMatrix, dist::StringDistance, xs::AbstractVector, ys::AbstractVector; preprocess = nothing)
    length(xs) == size(R, 1) || throw(DimensionMismatch("inconsistent length"))
    length(ys) == size(R, 2) || throw(DimensionMismatch("inconsistent length"))
    _asymmetric_pairwise!(R, dist, xs, ys; preprocess = preprocess)
end

function Distances.pairwise!(R::AbstractMatrix, dist::StringDistance, xs::AbstractVector; preprocess = nothing)
    length(xs) == size(R, 1) || throw(DimensionMismatch("inconsistent length"))
    length(xs) == size(R, 2) || throw(DimensionMismatch("inconsistent length"))
    (dist isa SemiMetric) ?
        _symmetric_pairwise!(R, dist, xs; preprocess = preprocess) :
        _asymmetric_pairwise!(R, dist, xs, xs; preprocess = preprocess)
end

function _preprocess(xs, dist::QGramDistance, preprocess)
    if (preprocess === true) || (isnothing(preprocess) && length(xs) >= 5)
        return map(x -> x === missing ? x : QGramSortedVector(x, dist.q), xs)
    else
        return xs
    end
end
_preprocess(xs, dist::StringDistance, preprocess) = xs

function _symmetric_pairwise!(R::AbstractMatrix, dist::StringDistance, xs::AbstractVector; preprocess = nothing)
    objs = _preprocess(xs, dist, preprocess)
    for i in 1:length(objs)
        # handle missing
        R[i, i] = objs[i] != objs[i]
        Threads.@threads for j in (i+1):length(objs)
            R[i, j] = R[j, i] = evaluate(dist, objs[i], objs[j])
        end
    end
    return R
end

function _asymmetric_pairwise!(R::AbstractMatrix, dist::StringDistance, xs::AbstractVector, ys::AbstractVector; preprocess = nothing)
    objsxs = _preprocess(xs, dist, preprocess)
    objsys = _preprocess(ys, dist, preprocess)
    for i in 1:length(objsxs)
        Threads.@threads for j in 1:length(objsys)
            R[i, j] = evaluate(dist, objsxs[i], objsys[j])
        end
    end
    return R
end


text:
function Vector1DFormat()
    return Vector1DFormat(())
end

function Vector1DFormat(arg0::JString, arg1::JString)
    return Vector1DFormat((JString, JString), arg0, arg1)
end

function Vector1DFormat(arg0::JString, arg1::JString, arg2::NumberFormat)
    return Vector1DFormat((JString, JString, NumberFormat), arg0, arg1, arg2)
end

function Vector1DFormat(arg0::NumberFormat)
    return Vector1DFormat((NumberFormat,), arg0)
end

function format(obj::Vector1DFormat, arg0::JVector, arg1::StringBuffer, arg2::FieldPosition)
    return jcall(obj, "format", StringBuffer, (JVector, StringBuffer, FieldPosition), arg0, arg1, arg2)
end

function format(obj::VectorFormat, arg0::JVector)
    return jcall(obj, "format", JString, (JVector,), arg0)
end

function get_available_locales(::Type{VectorFormat})
    return jcall(VectorFormat, "getAvailableLocales", Vector{Locale}, ())
end

function get_format(obj::VectorFormat)
    return jcall(obj, "getFormat", NumberFormat, ())
end

function get_prefix(obj::VectorFormat)
    return jcall(obj, "getPrefix", JString, ())
end

function get_separator(obj::VectorFormat)
    return jcall(obj, "getSeparator", JString, ())
end

function get_suffix(obj::VectorFormat)
    return jcall(obj, "getSuffix", JString, ())
end

function get_vector1d_format(::Type{Vector1DFormat})
    return jcall(Vector1DFormat, "getVector1DFormat", Vector1DFormat, ())
end

function get_vector1d_format(::Type{Vector1DFormat}, arg0::Locale)
    return jcall(Vector1DFormat, "getVector1DFormat", Vector1DFormat, (Locale,), arg0)
end

function parse(obj::Vector1DFormat, arg0::JString)
    return jcall(obj, "parse", Vector1D, (JString,), arg0)
end

function parse(obj::Vector1DFormat, arg0::JString, arg1::ParsePosition)
    return jcall(obj, "parse", Vector1D, (JString, ParsePosition), arg0, arg1)
end


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

"""
    add_tags_to_certificate(certificate_arn, tags)
    add_tags_to_certificate(certificate_arn, tags, params::Dict{String,<:Any})

Adds one or more tags to an ACM certificate. Tags are labels that you can use to identify
and organize your AWS resources. Each tag consists of a key and an optional value. You
specify the certificate on input by its Amazon Resource Name (ARN). You specify the tag by
using a key-value pair.  You can apply a tag to just one certificate if you want to
identify a specific characteristic of that certificate, or you can apply the same tag to
multiple certificates if you want to filter for a common relationship among those
certificates. Similarly, you can apply the same tag to multiple resources if you want to
specify a relationship among those resources. For example, you can add the same tag to an
ACM certificate and an Elastic Load Balancing load balancer to indicate that they are both
used by the same website. For more information, see Tagging ACM certificates.  To remove
one or more tags, use the RemoveTagsFromCertificate action. To view all of the tags that
have been applied to the certificate, use the ListTagsForCertificate action.

# Arguments
- `certificate_arn`: String that contains the ARN of the ACM certificate to which the tag
  is to be applied. This must be of the form:
  arn:aws:acm:region:123456789012:certificate/12345678-1234-1234-1234-123456789012  For more
  information about ARNs, see Amazon Resource Names (ARNs).
- `tags`: The key-value pair that defines the tag. The tag value is optional.

"""
add_tags_to_certificate(CertificateArn, Tags; aws_config::AbstractAWSConfig=global_aws_config()) = acm("AddTagsToCertificate", Dict{String, Any}("CertificateArn"=>CertificateArn, "Tags"=>Tags); aws_config=aws_config)
add_tags_to_certificate(CertificateArn, Tags, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = acm("AddTagsToCertificate", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("CertificateArn"=>CertificateArn, "Tags"=>Tags), params)); aws_config=aws_config)

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

Deletes a certificate and its associated private key. If this action succeeds, the
certificate no longer appears in the list that can be displayed by calling the
ListCertificates action or be retrieved by calling the GetCertificate action. The
certificate will not be available for use by AWS services integrated with ACM.   You cannot
delete an ACM certificate that is being used by another AWS service. To delete a
certificate that is in use, the certificate association must first be removed.

# Arguments
- `certificate_arn`: String that contains the ARN of the ACM certificate to be deleted.
  This must be of the form:
  arn:aws:acm:region:123456789012:certificate/12345678-1234-1234-1234-123456789012  For more
  information about ARNs, see Amazon Resource Names (ARNs).

"""
delete_certificate(CertificateArn; aws_config::AbstractAWSConfig=global_aws_config()) = acm("DeleteCertificate", Dict{String, Any}("CertificateArn"=>CertificateArn); aws_config=aws_config)
delete_certificate(CertificateArn, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = acm("DeleteCertificate", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("CertificateArn"=>CertificateArn), params)); aws_config=aws_config)

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

Returns detailed metadata about the specified ACM certificate.

# Arguments
- `certificate_arn`: The Amazon Resource Name (ARN) of the ACM certificate. The ARN must
  have the following form:
  arn:aws:acm:region:123456789012:certificate/12345678-1234-1234-1234-123456789012  For more
  information about ARNs, see Amazon Resource Names (ARNs).

"""
describe_certificate(CertificateArn; aws_config::AbstractAWSConfig=global_aws_config()) = acm("DescribeCertificate", Dict{String, Any}("CertificateArn"=>CertificateArn); aws_config=aws_config)
describe_certificate(CertificateArn, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = acm("DescribeCertificate", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("CertificateArn"=>CertificateArn), params)); aws_config=aws_config)

"""
    export_certificate(certificate_arn, passphrase)
    export_certificate(certificate_arn, passphrase, params::Dict{String,<:Any})

Exports a private certificate issued by a private certificate authority (CA) for use
anywhere. The exported file contains the certificate, the certificate chain, and the
encrypted private 2048-bit RSA key associated with the public key that is embedded in the
certificate. For security, you must assign a passphrase for the private key when exporting
it.  For information about exporting and formatting a certificate using the ACM console or
CLI, see Export a Private Certificate.

# Arguments
- `certificate_arn`: An Amazon Resource Name (ARN) of the issued certificate. This must be
  of the form:  arn:aws:acm:region:account:certificate/12345678-1234-1234-1234-123456789012
- `passphrase`: Passphrase to associate with the encrypted exported private key. If you
  want to later decrypt the private key, you must have the passphrase. You can use the
  following OpenSSL command to decrypt a private key:   openssl rsa -in encrypted_key.pem
  -out decrypted_key.pem

"""
export_certificate(CertificateArn, Passphrase; aws_config::AbstractAWSConfig=global_aws_config()) = acm("ExportCertificate", Dict{String, Any}("CertificateArn"=>CertificateArn, "Passphrase"=>Passphrase); aws_config=aws_config)
export_certificate(CertificateArn, Passphrase, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = acm("ExportCertificate", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("CertificateArn"=>CertificateArn, "Passphrase"=>Passphrase), params)); aws_config=aws_config)

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

Returns the account configuration options associated with an AWS account.

"""
get_account_configuration(; aws_config::AbstractAWSConfig=global_aws_config()) = acm("GetAccountConfiguration"; aws_config=aws_config)
get_account_configuration(params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = acm("GetAccountConfiguration", params; aws_config=aws_config)

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

Retrieves an Amazon-issued certificate and its certificate chain. The chain consists of the
certificate of the issuing CA and the intermediate certificates of any other subordinate
CAs. All of the certificates are base64 encoded. You can use OpenSSL to decode the
certificates and inspect individual fields.

# Arguments
- `certificate_arn`: String that contains a certificate ARN in the following format:
  arn:aws:acm:region:123456789012:certificate/12345678-1234-1234-1234-123456789012  For more
  information about ARNs, see Amazon Resource Names (ARNs).

"""
get_certificate(CertificateArn; aws_config::AbstractAWSConfig=global_aws_config()) = acm("GetCertificate", Dict{String, Any}("CertificateArn"=>CertificateArn); aws_config=aws_config)
get_certificate(CertificateArn, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = acm("GetCertificate", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("CertificateArn"=>CertificateArn), params)); aws_config=aws_config)

"""
    import_certificate(certificate, private_key)
    import_certificate(certificate, private_key, params::Dict{String,<:Any})

Imports a certificate into AWS Certificate Manager (ACM) to use with services that are
integrated with ACM. Note that integrated services allow only certificate types and keys
they support to be associated with their resources. Further, their support differs
depending on whether the certificate is imported into IAM or into ACM. For more
information, see the documentation for each service. For more information about importing
certificates into ACM, see Importing Certificates in the AWS Certificate Manager User
Guide.   ACM does not provide managed renewal for certificates that you import.  Note the
following guidelines when importing third party certificates:   You must enter the private
key that matches the certificate you are importing.   The private key must be unencrypted.
You cannot import a private key that is protected by a password or a passphrase.   The
private key must be no larger than 5 KB (5,120 bytes).   If the certificate you are
importing is not self-signed, you must enter its certificate chain.   If a certificate
chain is included, the issuer must be the subject of one of the certificates in the chain.
 The certificate, private key, and certificate chain must be PEM-encoded.   The current
time must be between the Not Before and Not After certificate fields.   The Issuer field
must not be empty.   The OCSP authority URL, if present, must not exceed 1000 characters.
To import a new certificate, omit the CertificateArn argument. Include this argument only
when you want to replace a previously imported certificate.   When you import a certificate
by using the CLI, you must specify the certificate, the certificate chain, and the private
key by their file names preceded by fileb://. For example, you can specify a certificate
saved in the C:temp folder as fileb://C:tempcertificate_to_import.pem. If you are making an
HTTP or HTTPS Query request, include these arguments as BLOBs.    When you import a
certificate by using an SDK, you must specify the certificate, the certificate chain, and
the private key files in the manner required by the programming language you're using.
The cryptographic algorithm of an imported certificate must match the algorithm of the
signing CA. For example, if the signing CA key type is RSA, then the certificate key type
must also be RSA.   This operation returns the Amazon Resource Name (ARN) of the imported
certificate.

# Arguments
- `certificate`: The certificate to import.
- `private_key`: The private key that matches the public key in the certificate.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"CertificateArn"`: The Amazon Resource Name (ARN) of an imported certificate to replace.
  To import a new certificate, omit this field.
- `"CertificateChain"`: The PEM encoded certificate chain.
- `"Tags"`: One or more resource tags to associate with the imported certificate.  Note:
  You cannot apply tags when reimporting a certificate.
"""
import_certificate(Certificate, PrivateKey; aws_config::AbstractAWSConfig=global_aws_config()) = acm("ImportCertificate", Dict{String, Any}("Certificate"=>Certificate, "PrivateKey"=>PrivateKey); aws_config=aws_config)
import_certificate(Certificate, PrivateKey, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = acm("ImportCertificate", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("Certificate"=>Certificate, "PrivateKey"=>PrivateKey), params)); aws_config=aws_config)

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

Retrieves a list of certificate ARNs and domain names. You can request that only
certificates that match a specific status be listed. You can also filter by specific
attributes of the certificate. Default filtering returns only RSA_2048 certificates. For
more information, see Filters.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"CertificateStatuses"`: Filter the certificate list by status value.
- `"Includes"`: Filter the certificate list. For more information, see the Filters
  structure.
- `"MaxItems"`: Use this parameter when paginating results to specify the maximum number of
  items to return in the response. If additional items exist beyond the number you specify,
  the NextToken element is sent in the response. Use this NextToken value in a subsequent
  request to retrieve additional items.
- `"NextToken"`: Use this parameter only when paginating results and only in a subsequent
  request after you receive a response with truncated results. Set it to the value of
  NextToken from the response you just received.
"""
list_certificates(; aws_config::AbstractAWSConfig=global_aws_config()) = acm("ListCertificates"; aws_config=aws_config)
list_certificates(params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = acm("ListCertificates", params; aws_config=aws_config)

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

Lists the tags that have been applied to the ACM certificate. Use the certificate's Amazon
Resource Name (ARN) to specify the certificate. To add a tag to an ACM certificate, use the
AddTagsToCertificate action. To delete a tag, use the RemoveTagsFromCertificate action.

# Arguments
- `certificate_arn`: String that contains the ARN of the ACM certificate for which you want
  to list the tags. This must have the following form:
  arn:aws:acm:region:123456789012:certificate/12345678-1234-1234-1234-123456789012  For more
  information about ARNs, see Amazon Resource Names (ARNs).

"""
list_tags_for_certificate(CertificateArn; aws_config::AbstractAWSConfig=global_aws_config()) = acm("ListTagsForCertificate", Dict{String, Any}("CertificateArn"=>CertificateArn); aws_config=aws_config)
list_tags_for_certificate(CertificateArn, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = acm("ListTagsForCertificate", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("CertificateArn"=>CertificateArn), params)); aws_config=aws_config)

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

Adds or modifies account-level configurations in ACM.  The supported configuration option
is DaysBeforeExpiry. This option specifies the number of days prior to certificate
expiration when ACM starts generating EventBridge events. ACM sends one event per day per
certificate until the certificate expires. By default, accounts receive events starting 45
days before certificate expiration.

# Arguments
- `idempotency_token`: Customer-chosen string used to distinguish between calls to
  PutAccountConfiguration. Idempotency tokens time out after one hour. If you call
  PutAccountConfiguration multiple times with the same unexpired idempotency token, ACM
  treats it as the same request and returns the original result. If you change the
  idempotency token for each call, ACM treats each call as a new request.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"ExpiryEvents"`: Specifies expiration events associated with an account.
"""
put_account_configuration(IdempotencyToken; aws_config::AbstractAWSConfig=global_aws_config()) = acm("PutAccountConfiguration", Dict{String, Any}("IdempotencyToken"=>IdempotencyToken); aws_config=aws_config)
put_account_configuration(IdempotencyToken, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = acm("PutAccountConfiguration", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("IdempotencyToken"=>IdempotencyToken), params)); aws_config=aws_config)

"""
    remove_tags_from_certificate(certificate_arn, tags)
    remove_tags_from_certificate(certificate_arn, tags, params::Dict{String,<:Any})

Remove one or more tags from an ACM certificate. A tag consists of a key-value pair. If you
do not specify the value portion of the tag when calling this function, the tag will be
removed regardless of value. If you specify a value, the tag is removed only if it is
associated with the specified value.  To add tags to a certificate, use the
AddTagsToCertificate action. To view all of the tags that have been applied to a specific
ACM certificate, use the ListTagsForCertificate action.

# Arguments
- `certificate_arn`: String that contains the ARN of the ACM Certificate with one or more
  tags that you want to remove. This must be of the form:
  arn:aws:acm:region:123456789012:certificate/12345678-1234-1234-1234-123456789012  For more
  information about ARNs, see Amazon Resource Names (ARNs).
- `tags`: The key-value pair that defines the tag to remove.

"""
remove_tags_from_certificate(CertificateArn, Tags; aws_config::AbstractAWSConfig=global_aws_config()) = acm("RemoveTagsFromCertificate", Dict{String, Any}("CertificateArn"=>CertificateArn, "Tags"=>Tags); aws_config=aws_config)
remove_tags_from_certificate(CertificateArn, Tags, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = acm("RemoveTagsFromCertificate", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("CertificateArn"=>CertificateArn, "Tags"=>Tags), params)); aws_config=aws_config)

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

Renews an eligible ACM certificate. At this time, only exported private certificates can be
renewed with this operation. In order to renew your ACM PCA certificates with ACM, you must
first grant the ACM service principal permission to do so. For more information, see
Testing Managed Renewal in the ACM User Guide.

# Arguments
- `certificate_arn`: String that contains the ARN of the ACM certificate to be renewed.
  This must be of the form:
  arn:aws:acm:region:123456789012:certificate/12345678-1234-1234-1234-123456789012  For more
  information about ARNs, see Amazon Resource Names (ARNs).

"""
renew_certificate(CertificateArn; aws_config::AbstractAWSConfig=global_aws_config()) = acm("RenewCertificate", Dict{String, Any}("CertificateArn"=>CertificateArn); aws_config=aws_config)
renew_certificate(CertificateArn, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = acm("RenewCertificate", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("CertificateArn"=>CertificateArn), params)); aws_config=aws_config)

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

Requests an ACM certificate for use with other AWS services. To request an ACM certificate,
you must specify a fully qualified domain name (FQDN) in the DomainName parameter. You can
also specify additional FQDNs in the SubjectAlternativeNames parameter.  If you are
requesting a private certificate, domain validation is not required. If you are requesting
a public certificate, each domain name that you specify must be validated to verify that
you own or control the domain. You can use DNS validation or email validation. We recommend
that you use DNS validation. ACM issues public certificates after receiving approval from
the domain owner.

# Arguments
- `domain_name`:  Fully qualified domain name (FQDN), such as www.example.com, that you
  want to secure with an ACM certificate. Use an asterisk (*) to create a wildcard
  certificate that protects several sites in the same domain. For example, *.example.com
  protects www.example.com, site.example.com, and images.example.com.   The first domain name
  you enter cannot exceed 64 octets, including periods. Each subsequent Subject Alternative
  Name (SAN), however, can be up to 253 octets in length.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"CertificateAuthorityArn"`: The Amazon Resource Name (ARN) of the private certificate
  authority (CA) that will be used to issue the certificate. If you do not provide an ARN and
  you are trying to request a private certificate, ACM will attempt to issue a public
  certificate. For more information about private CAs, see the AWS Certificate Manager
  Private Certificate Authority (PCA) user guide. The ARN must have the following form:
  arn:aws:acm-pca:region:account:certificate-authority/12345678-1234-1234-1234-123456789012
- `"DomainValidationOptions"`: The domain name that you want ACM to use to send you emails
  so that you can validate domain ownership.
- `"IdempotencyToken"`: Customer chosen string that can be used to distinguish between
  calls to RequestCertificate. Idempotency tokens time out after one hour. Therefore, if you
  call RequestCertificate multiple times with the same idempotency token within one hour, ACM
  recognizes that you are requesting only one certificate and will issue only one. If you
  change the idempotency token for each call, ACM recognizes that you are requesting multiple
  certificates.
- `"Options"`: Currently, you can use this parameter to specify whether to add the
  certificate to a certificate transparency log. Certificate transparency makes it possible
  to detect SSL/TLS certificates that have been mistakenly or maliciously issued.
  Certificates that have not been logged typically produce an error message in a browser. For
  more information, see Opting Out of Certificate Transparency Logging.
- `"SubjectAlternativeNames"`: Additional FQDNs to be included in the Subject Alternative
  Name extension of the ACM certificate. For example, add the name www.example.net to a
  certificate for which the DomainName field is www.example.com if users can reach your site
  by using either name. The maximum number of domain names that you can add to an ACM
  certificate is 100. However, the initial quota is 10 domain names. If you need more than 10
  names, you must request a quota increase. For more information, see Quotas.  The maximum
  length of a SAN DNS name is 253 octets. The name is made up of multiple labels separated by
  periods. No label can be longer than 63 octets. Consider the following examples:     (63
  octets).(63 octets).(63 octets).(61 octets) is legal because the total length is 253 octets
  (63+1+63+1+63+1+61) and no label exceeds 63 octets.    (64 octets).(63 octets).(63
  octets).(61 octets) is not legal because the total length exceeds 253 octets
  (64+1+63+1+63+1+61) and the first label exceeds 63 octets.    (63 octets).(63 octets).(63
  octets).(62 octets) is not legal because the total length of the DNS name
  (63+1+63+1+63+1+62) exceeds 253 octets.
- `"Tags"`: One or more resource tags to associate with the certificate.
- `"ValidationMethod"`: The method you want to use if you are requesting a public
  certificate to validate that you own or control domain. You can validate with DNS or
  validate with email. We recommend that you use DNS validation.
"""
request_certificate(DomainName; aws_config::AbstractAWSConfig=global_aws_config()) = acm("RequestCertificate", Dict{String, Any}("DomainName"=>DomainName); aws_config=aws_config)
request_certificate(DomainName, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = acm("RequestCertificate", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("DomainName"=>DomainName), params)); aws_config=aws_config)

"""
    resend_validation_email(certificate_arn, domain, validation_domain)
    resend_validation_email(certificate_arn, domain, validation_domain, params::Dict{String,<:Any})

Resends the email that requests domain ownership validation. The domain owner or an
authorized representative must approve the ACM certificate before it can be issued. The
certificate can be approved by clicking a link in the mail to navigate to the Amazon
certificate approval website and then clicking I Approve. However, the validation email can
be blocked by spam filters. Therefore, if you do not receive the original mail, you can
request that the mail be resent within 72 hours of requesting the ACM certificate. If more
than 72 hours have elapsed since your original request or since your last attempt to resend
validation mail, you must request a new certificate. For more information about setting up
your contact email addresses, see Configure Email for your Domain.

# Arguments
- `certificate_arn`: String that contains the ARN of the requested certificate. The
  certificate ARN is generated and returned by the RequestCertificate action as soon as the
  request is made. By default, using this parameter causes email to be sent to all top-level
  domains you specified in the certificate request. The ARN must be of the form:
  arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012
- `domain`: The fully qualified domain name (FQDN) of the certificate that needs to be
  validated.
- `validation_domain`: The base validation domain that will act as the suffix of the email
  addresses that are used to send the emails. This must be the same as the Domain value or a
  superdomain of the Domain value. For example, if you requested a certificate for
  site.subdomain.example.com and specify a ValidationDomain of subdomain.example.com, ACM
  sends email to the domain registrant, technical contact, and administrative contact in
  WHOIS and the following five addresses:   admin@subdomain.example.com
  administrator@subdomain.example.com   hostmaster@subdomain.example.com
  postmaster@subdomain.example.com   webmaster@subdomain.example.com

"""
resend_validation_email(CertificateArn, Domain, ValidationDomain; aws_config::AbstractAWSConfig=global_aws_config()) = acm("ResendValidationEmail", Dict{String, Any}("CertificateArn"=>CertificateArn, "Domain"=>Domain, "ValidationDomain"=>ValidationDomain); aws_config=aws_config)
resend_validation_email(CertificateArn, Domain, ValidationDomain, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = acm("ResendValidationEmail", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("CertificateArn"=>CertificateArn, "Domain"=>Domain, "ValidationDomain"=>ValidationDomain), params)); aws_config=aws_config)

"""
    update_certificate_options(certificate_arn, options)
    update_certificate_options(certificate_arn, options, params::Dict{String,<:Any})

Updates a certificate. Currently, you can use this function to specify whether to opt in to
or out of recording your certificate in a certificate transparency log. For more
information, see  Opting Out of Certificate Transparency Logging.

# Arguments
- `certificate_arn`: ARN of the requested certificate to update. This must be of the form:
  arn:aws:acm:us-east-1:account:certificate/12345678-1234-1234-1234-123456789012
- `options`: Use to update the options for your certificate. Currently, you can specify
  whether to add your certificate to a transparency log. Certificate transparency makes it
  possible to detect SSL/TLS certificates that have been mistakenly or maliciously issued.
  Certificates that have not been logged typically produce an error message in a browser.

"""
update_certificate_options(CertificateArn, Options; aws_config::AbstractAWSConfig=global_aws_config()) = acm("UpdateCertificateOptions", Dict{String, Any}("CertificateArn"=>CertificateArn, "Options"=>Options); aws_config=aws_config)
update_certificate_options(CertificateArn, Options, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = acm("UpdateCertificateOptions", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("CertificateArn"=>CertificateArn, "Options"=>Options), params)); aws_config=aws_config)