text:
module TestFeatureSelectors

using Random
using Test
using CSV
using Statistics
using DataFrames
using AMLPipelineBase

function iris_test()
    X = getiris()
    catfeat = FeatureSelector([5])
    numfeat = FeatureSelector([1,2,3,4])
    catf = CatFeatureSelector()
    numf = NumFeatureSelector()
    @test (fit_transform!(catfeat,X) .== X[:,5]) |> Matrix |> sum == 150
    @test (fit_transform!(numfeat,X) .== X[:,1:4]) |> Matrix |> sum == 600
    @test (fit_transform!(catf,X) .== X[:,5]) |> Matrix |> sum == 150
    @test (fit_transform!(numf,X) .== X[:,1:4]) |> Matrix |> sum == 600
    catnumdata = hcat(X,repeat([1,2,3,4,5],30))
    catnum = CatNumDiscriminator()
    res = fit_transform!(catnum,catnumdata)
    @test infer_eltype(catnumdata[:,[2,4,6]]) <: Number
    @test infer_eltype(res[:,[2,4,6]]) <: String
    catnumdata = hcat(X,repeat([1,2,3,4,5],30))
    catnum = CatNumDiscriminator(0)
    res = fit_transform!(catnum,catnumdata)
    @test eltype(res[:,6]) <: Number
    catnumdata = hcat(X,repeat([1,2,3,4,5],30))
    catnum = CatNumDiscriminator(5)
    #ppp=@pipeline catnum |> ((catf |> OneHotEncoder()) + (numf |> Normalizer(Dict(:method=>:pca))))
    #res=fit_transform!(ppp,catnumdata)
    #@test ncol(res) == 11
end
@testset "Feature Selectors: Iris" begin
    Random.seed!(123)
    iris_test()
end

function diabetes_test()
    Random.seed!(123)
    diabetesdf = CSV.File(joinpath(dirname(pathof(AMLPipelineBase)),"../data/diabetes.csv")) |> DataFrame
    X = diabetesdf[:,1:end-1]
    Y = diabetesdf[:,end] |> Vector
    acc(X,Y)=score(:accuracy,X,Y)
    dt     = PrunedTree()
    ada    = Adaboost()
    rf     = RandomForest()
    ohe    = OneHotEncoder()
    catf   = CatFeatureSelector()
    numf   = NumFeatureSelector()
    #disc = CatNumDiscriminator(0)
    #pl = @pipeline disc |> ((numf |>  pca) + (catf |> ohe)) |> rf
    #@test crossvalidate(pl,X,Y,acc,10,false).mean > 0.60
    #pl = @pipeline disc |> ((numf |> zscore |>  pca) + (catf |> ohe)) |> ada
    #@test crossvalidate(pl,X,Y,acc,10,false).mean > 0.60
    #pl = @pipeline disc |> ((numf |> unitr |>  fa) + (catf |> ohe)) |> dt
    #@test crossvalidate(pl,X,Y,acc,10,false).mean > 0.60
    disc = CatNumDiscriminator(20)
    pl = @pipeline disc |> ( (catf |> ohe)) |> rf
    @test crossvalidate(pl,X,Y,acc,2,false).mean > 0.60
    #disc = CatNumDiscriminator(50)
    #pl = @pipeline disc |> ((numf |> zscore |>  pca) + (catf |> ohe)) |> rf
    #@test crossvalidate(pl,X,Y,acc,2,false).mean > 0.60
end
@testset "Feature Selectors: Diabetes" begin
    Random.seed!(123)
    diabetes_test()
end

end


text:
#=
# 1D mixed FE/FV example
=#

### If the Finch package has already been added, use this line #########
using Finch # Note: to add the package, first do: ]add "https://github.com/paralab/Finch.git"

### If not, use these four lines (working from the examples directory) ###
# if !@isdefined(Finch)
#     include("../Finch.jl");
#     using .Finch
# end
##########################################################################

init_finch("fefv1d");

useLog("fefv1dlog")

# Configuration setup
domain(1)
solverType([CG,FV])

# Select one of these time stepper setups. First type is for FE, second for FV. They are same if only one is given.
# timeStepper(EULER_EXPLICIT)
# timeStepper([EULER_IMPLICIT, EULER_EXPLICIT])
# timeStepper([RK4, EULER_EXPLICIT])
# timeStepper([CRANK_NICHOLSON, EULER_EXPLICIT])
timeStepper([LSRK4, LSRK4])

# Mesh
n = 40 # number of elements
mesh(LINEMESH, elsperdim=n)

# Variables and BCs
u = variable("u", method=CG)
testSymbol("v")
boundary(u, 1, DIRICHLET, 0)

w = variable("w", location=CELL, method=FV)
boundary(w, 1, DIRICHLET, 0)

# Time interval and initial condition
T = 1;
timeInterval(T)
initial(u, "x<0.5 ? sin(2*pi*x)^4 : 0")
initial(w, "x>0.5 ? sin(2*pi*x)^4 : 0")

coefficient("D", 0.001) # Diffusion rate
coefficient("f", "-0.5*sin(pi*x)^2")

# The flux and source terms of the conservation equation
# F and S in the following equation:
# Dt(int(u dx)) = int(S dx) - int(F ds)
flux(w, "-D*dot(grad(w),normal())") # in 1D -> -D * dx(u) * normal
source(w, "u")

# The weak form expression
weakForm(u, "Dt(u*v) + D * dot(grad(u),grad(v)) - w*v")

solve([u,w])

finalize_finch()

##### Uncomment below to plot
# xc = Finch.fv_info.cellCenters[:];
# xn = Finch.grid_data.allnodes[1,:];
# nnodes = length(xn); # The initial condition
# u0 = zeros(nnodes);
# for i=1:nnodes
#     u0[i] = sin(2*pi*xn[i])^4
# end

# using Plots
# pyplot();
# display(plot([xn xn], [u0 u.values[:]], markershape=:circle, label=["initial" "u, t="*string(T)]))
# plot!(xc, w.values[:], markershape=:square, label="w, t="*string(T))

ext: jl

max_stars_repo_name: paralab/Finch

max_issues_repo_name: paralab/Finch

max_forks_repo_name: paralab/Finch


text:
module Piwall

using Compose

greet() = print("Hello World Again! And Again !")

export Wall, Screen

struct Screen
    xres::Integer
    # Todo : extend this to use Units and Measurements uncertainty
    xsize::Float32
    xbezel_left::Float32
    xbezel_right::Float32
    yres::Integer
    ysize::Float32
    ybezel_left::Float32
    ybezel_right::Float32
    manufacturer::String
    model::String

    # Ctors
    Screen(x::Integer, y::Integer) = new(x, 0.0, 0.0, 0.0, y, 0.0, 0.0, 0.0, "Manufacturer undefined", "Model undefined")
end

# Todo: extension for non-planar walls
struct Wall
    screens::Array{Screen}
end

function render(s::Screen)
    picture = compose(context(), rectangle(0,0,s.xres, s.yres), fill="tomato")
end

end # module


text:
const a4width = 8.2677165354
const a4height = 11.6929133858

for b in ses.P
	for s in b
		if s[end] > 25; unshift!(b)
		else break end
	end

	for s in b[end:-1:1]
		if s[end] > 25; pop!(b)
		else break end
	end
end

f = figure(ses.viz.figBif[:number])
f[:set_figwidth](a4height)
f[:set_figheight](a4width)
f[:set_dpi](200)
PyPlot.draw()
savefig("roessleroverview.svg", format="svg")

map(println, keys(f))

# cut
include("$MASTER/lib/ncmprojPLOTINTERSECT.jl")
plotintersect(ses, 20, its=80000, h=.01)

f = gcf()
f[:set_figwidth](a4height)
f[:set_figheight](a4width)
f[:set_dpi](200)
PyPlot.draw()
savefig("roesslercut20.svg", format="svg")

# lorenz

f = figure(ses.viz.figBif[:number])
f[:set_figwidth](a4height)
f[:set_figheight](a4width)
f[:set_dpi](50)
PyPlot.draw()
savefig("lorenzoverview.svg", format="svg")

map(println, keys(f))

# cut
include("$MASTER/lib/ncmprojPLOTINTERSECT.jl")
plotintersect(ses, 80, its=80000, h=.005)

f = gcf()
f[:set_figwidth](a4height)
f[:set_figheight](a4width)
f[:set_dpi](200)
PyPlot.draw()
savefig("lorenzcut80.svg", format="svg")


text:
export CubicSplineInterpolator, BicubicSplineInterpolator

#-------------------------------------------------------------------------------
# piecewise cubics with continuous derivatives (splines!)

struct CubicSplineInterpolator{T,B} <: OneDimensionalInterpolator
    r::InterpolatorRange{T}
    coef::Vector{NTuple{4,T}}
    boundaries::B
    i::RefValue{Int64} #previous cell index
end

#-------------------------------------------------------------------------------
# constructors

"""
    CubicSplineInterpolator(x, y, boundaries=StrictBoundaries())

Construct a `CubicSplineInterpolator` for the points defined by coordinates `x` and values `y`. This constructor creates a natural spline, where the second derivative is set to zero at the boundaries.
"""
function CubicSplineInterpolator(x, y, boundaries::AbstractBoundaries=StrictBoundaries())
    #construct the underlying range, triggering some checks
    T = promote_type(eltype(x), eltype(y))
    r = InterpolatorRange(x, y)
    n = r.n
    #compute coefficients
    #Burden, Richard L., and J. Douglas Faires. Numerical Analysis. 2011.
    a = collect(T, r.y)
    b = zeros(T, n - 1)
    d = zeros(T, n - 1)
    h = diff(r.x)
    α = zeros(T, n-1)
    for i = 2:n-1
        α[i] = 3*(a[i+1] - a[i])/h[i] - 3*(a[i] - a[i-1])/h[i-1]
    end
    c = zeros(T, n)
    l = ones(T, n)
    μ = zeros(T, n)
    z = zeros(T, n)
    l[1] = 1
    for i = 2:n-1
        l[i] = 2*(x[i+1] - x[i-1]) - h[i-1]*μ[i-1]
        μ[i] = h[i]/l[i]
        z[i] = (α[i] - h[i-1]*z[i-1])/l[i]
    end
    for j = n-1:-1:1
        c[j] = z[j] - μ[j]*c[j+1]
        b[j] = (a[j+1] - a[j])/h[j] - h[j]*(c[j+1] + 2*c[j])/3
        d[j] = (c[j+1] - c[j])/(3*h[j])
    end
    a = a[1:end-1]
    c = c[1:end-1]
    #static arrays
    coef = Vector{NTuple{4,T}}(undef,n-1)
    for i = 1:n-1
        coef[i] = (a[i], b[i], c[i], d[i])
    end
    #construct the object
    CubicSplineInterpolator(r, coef, boundaries, Ref(1))
end

"""
    CubicSplineInterpolator(x, y, dy₁, dyₙ, boundaries=StrictBoundaries())

Construct a `CubicSplineInterpolator` for the points defined by coordinates `x` and values `y`. This constructor creates a clamped spline, where the first derivatives at the boundaries are set by `dy₁` and `dyₙ`.
"""
function CubicSplineInterpolator(x,
                                 y,
                                 dy₁::Real,
                                 dyₙ::Real,
                                 boundaries::AbstractBoundaries=StrictBoundaries())
    #construct the underlying range, triggering some checks
    T = promote_type(eltype(x), eltype(y))
    r = InterpolatorRange(x, y)
    n = r.n
    #compute coefficients
    #Burden, Richard L., and J. Douglas Faires. Numerical Analysis. 2011.
    a = collect(T, r.y)
    b = zeros(T, n - 1)
    d = zeros(T, n - 1)
    h = diff(r.x)
    α = zeros(T, n)
    α[1] = 3*(a[2] - a[1])/h[1] - 3*dy₁
    for i = 2:n-1
        α[i] = 3*(a[i+1] - a[i])/h[i] - 3*(a[i] - a[i-1])/h[i-1]
    end
    α[n] = 3*dyₙ - 3*(a[n] - a[n - 1])/h[n-1]
    c = zeros(T, n)
    l = zeros(T, n)
    μ = zeros(T, n)
    z = zeros(T, n)
    l[1] = 2*h[1]
    μ[1] = 0.5
    z[1] = α[1]/l[1]
    for i = 2:n-1
        l[i] = 2*(x[i+1] - x[i-1]) - h[i-1]*μ[i-1]
        μ[i] = h[i]/l[i]
        z[i] = (α[i] - h[i-1]*z[i-1])/l[i]
    end
    l[n] = h[n-1]*(2 - μ[n-1])
    z[n] = (α[n] - h[n-1]*z[n-1])/l[n]
    c[n] = z[n]
    for j = n-1:-1:1
        c[j] = z[j] - μ[j]*c[j+1]
        b[j] = (a[j+1] - a[j])/h[j] - h[j]*(c[j+1] + 2*c[j])/3
        d[j] = (c[j+1] - c[j])/(3*h[j])
    end
    a = a[1:end-1]
    c = c[1:end-1]
    #static arrays
    coef = Vector{NTuple{4,T}}(undef,n-1)
    for i = 1:n-1
        coef[i] = (a[i], b[i], c[i], d[i])
    end
    #construct the object
    CubicSplineInterpolator(r, coef, boundaries, Ref(1))
end

"""
    CubicSplineInterpolator(f, xa, xb, n, boundaries=StrictBoundaries())

Construct a `CubicSplineInterpolator` for the function `f` using `n` evenly spaced function evaluations in the range [`xa`,`xb`]. A natural spline is created.
"""
function CubicSplineInterpolator(f,
                                 xa::Real,
                                 xb::Real,
                                 n::Int,
                                 boundaries::AbstractBoundaries=StrictBoundaries())
    linstruct(CubicSplineInterpolator, f, xa, xb, n, boundaries)
end

function (ϕ::CubicSplineInterpolator)(x)
    #enforce boundaries if desired
    ϕ.boundaries(x, ϕ.r.xa, ϕ.r.xb)
    #find the interpolation point
    i = findcell(x, ϕ)
    #offset from the nearest lower point
    @inbounds ξ = x - ϕ.r.x[i]
    #evaluate polynomial
    @inbounds ϕ.coef[i][1] + ϕ.coef[i][2]*ξ + ϕ.coef[i][3]*ξ^2 + ϕ.coef[i][4]*ξ^3
end

Base.getindex(ϕ::CubicSplineInterpolator, i) = ϕ.r.y[i]
function Base.copy(ϕ::CubicSplineInterpolator)
    CubicSplineInterpolator(ϕ.r, ϕ.coef, ϕ.boundaries, Ref(1))
end


text:
import Base: show, string, typejoin

abstract type AbstractLogicTerm end
abstract type SententialTerm{T<:AbstractLogicTerm} <: AbstractLogicTerm end
abstract type JunctionTerm{T<:AbstractLogicTerm} <: SententialTerm{T} end
abstract type QuantifierTerm{T<:AbstractLogicTerm} <: SententialTerm{T} end

# we make Variable <: SententialTerm to enable FOOL extensions
struct Variable <: SententialTerm{AbstractLogicTerm}
  name::String
end
Variable(name::AbstractString) = Variable(name)
Variable(v::Variable) = Variable(v.name)
Variable(name::AbstractString, t::Symbol) = Variable(name)
Variable(v::Variable, t::Symbol) = Variable(v.name)
string(v::Variable) = v.name

struct Functor <: AbstractLogicTerm
  name::String
end
string(f::Functor) = f.name

show(io::IO, q::AbstractLogicTerm) = print(io, string(q))

struct FunctionTerm <: AbstractLogicTerm
  name::Functor
  args::Vector{Union{FunctionTerm, Variable}}
end
FunctionTerm(n::AbstractString, args) = FunctionTerm(Functor(string(n)), args)
ConstantTerm(n::Functor) = FunctionTerm(n, Variable[])
ConstantTerm(n) = FunctionTerm(Functor(string(n)), Variable[])

function listString(xs::AbstractVector, head::AbstractString,
                    sep::AbstractString, tail::AbstractString)
  foldl((x,y) -> x*sep*string(y), xs[2:end]; init=head*string(xs[1]))*tail
end

struct PredicateTerm{T<:AbstractLogicTerm} <: SententialTerm{T}
  name::Functor
  args::Vector{T}
end
PredicateTerm(n::String, args) = PredicateTerm(Functor(n), args)

equalTerm(t1::Union{PredicateTerm,FunctionTerm,Variable},
          t2::Union{PredicateTerm,FunctionTerm,Variable}) =
            PredicateTerm(Functor("="), [t1,t2])

string(f::Union{FunctionTerm,PredicateTerm}) =
  isempty(f.args) ? string(f.name) : listString(f.args, string(f.name)*"(", ",", ")")

struct NegationTerm{T<:AbstractLogicTerm} <: SententialTerm{T}
  scope::T
end
string(n::NegationTerm) = "¬(" * string(n.scope) * ")"

const LiteralTerm = Union{PredicateTerm{T}, NegationTerm{PredicateTerm{T}}} where {T<:Union{Variable,FunctionTerm}}

Base.typejoin(::Type{PredicateTerm},::Type{NegationTerm{PredicateTerm}}) = LiteralTerm

struct AndTerm{T<:AbstractLogicTerm} <: JunctionTerm{T}
  juncts::Vector{T}
end
AndTerm(s::T, ss...) where {T<:SententialTerm} =
  AndTerm{foldr((si,T)->typejoin(T,typeof(si)), ss; init=T)}([s; collect(ss)])
string(a::AndTerm) = listString(a.juncts, "(", ")∧(", ")")

struct OrTerm{T<:AbstractLogicTerm} <: JunctionTerm{T}
  juncts::Vector{T}
end
OrTerm(s::T, ss...) where {T<:SententialTerm} =
  OrTerm{foldr((si,T)->typejoin(T,typeof(si)), ss; init=T)}([s; collect(ss)])
string(a::OrTerm) = listString(a.juncts, "(", ")∨(", ")")

struct EQuantifierTerm{T<:AbstractLogicTerm} <: QuantifierTerm{T}
  variables::Vector{Variable}
  scope::T
end
function EQuantifierTerm(args...)
  x = collect(args);  EQuantifierTerm{typeof(x[end])}(x[1:end-1], x[end])
end
string(q::EQuantifierTerm) = "∃" * listString(q.variables, "", ",", "") * " " * string(q.scope)

struct AQuantifierTerm{T<:AbstractLogicTerm} <: QuantifierTerm{T}
  variables::Vector{Variable}
  scope::T
end
function AQuantifierTerm(args...)
  x = collect(args);  AQuantifierTerm{typeof(x[end])}(x[1:end-1], x[end])
end
string(q::AQuantifierTerm) = "∀" * listString(q.variables, "", ",", "") * " " * string(q.scope)

for T in (Variable, FunctionTerm, PredicateTerm, NegationTerm, AndTerm, OrTerm,
          AQuantifierTerm,EQuantifierTerm)
  @eval rootType(_::$T) = $T
end

pairedQuantifierType(_::AndTerm) = AQuantifierTerm
pairedQuantifierType(_::OrTerm) = EQuantifierTerm

"""
    freeVar(a::AbstractLogicTerm)

Return a Vector{Variable} of the free variables in a.
"""
freeVar(v::Variable) = [v]
freeVar(fp::Union{FunctionTerm,PredicateTerm}) =
  foldl((u,x) -> union!(u, freeVar(x)), fp.args; init=Variable[])
freeVar(n::NegationTerm) = freeVar(n.scope)
freeVar(j::JunctionTerm) = foldl((u,x) -> union!(u, freeVar(x)), j.juncts; init=Variable[])
freeVar(q::QuantifierTerm) = setdiff(freeVar(q.scope), q.variables)


text:
export MultiHomTotalDegreeSystem

"""
    MultiHomogeneousTotalDegreeSystem(polynomials, vars) <: AbstractSystem

Create a multi-homogeneous total degree system as described in

An efficient start system for multi-homogeneous polynomial continuation,
Wampler, C.W. Numer. Math. (1993) 66: 517. https://doi.org/10.1007/BF01385710
"""
struct MultiHomTotalDegreeSystem{T} <: AbstractSystem
    D::Matrix{Int}
    C::Matrix{Float64}
    scaling_factors::Vector{T}
end

function MultiHomTotalDegreeSystem(D, C)
    MultiHomTotalDegreeSystem(D, C, ones(size(D, 2)))
end

struct MultiHomTotalDegreeSystemCache{M,T} <: AbstractSystemCache
    B::Matrix{T}
    Ĝ::Matrix{T}
    R::Matrix{T}
    S::Matrix{T}
    ranges::NTuple{M,UnitRange{Int}}
    homvars::NTuple{M,Int}
end

Base.size(F::MultiHomTotalDegreeSystem) = (size(F.D, 2), sum(size(F.D)))
Base.length(F::MultiHomTotalDegreeSystem) = size(F.D, 2)

function cache(F::MultiHomTotalDegreeSystem, x::PVector{TX,M}) where {TX,M}
    T = typeof(F.C[1, 1] * x[1] + F.C[1, 1] * x[1])
    n = length(F)
    B, Ĝ = zeros(T, M, n), zeros(T, M, n)
    R, S = zeros(T, M, n), zeros(T, M, n)
    ranges_homvars = ProjectiveVectors.dimension_indices_homvars(x)
    ranges = first.(ranges_homvars)
    homvars = last.(ranges_homvars)
    MultiHomTotalDegreeSystemCache(B, Ĝ, R, S, ranges, homvars)
end

function evaluate!(
    u,
    F::MultiHomTotalDegreeSystem,
    z::ProjectiveVectors.PVector,
    cache::MultiHomTotalDegreeSystemCache{M},
) where {M}
    D, C = F.D, F.C
    B, ranges, homvars = cache.B, cache.ranges, cache.homvars

    n = size(B, 2)

    @boundscheck checkbounds(u, 1:n)

    # Compute all bᵢⱼ and store in B
    # Since B is column major we store bᵢⱼ in B[j, i]
    @inbounds for i = 1:n
        for j = 1:M
            if D[j, i] != 0
                bᵢⱼ = -zero(B[j, i])
                for k in ranges[j]
                    bᵢⱼ += C[k, i] * z[k]
                end
                B[j, i] = bᵢⱼ
            end
        end
    end

    @inbounds for i = 1:n
        gᵢ = one(eltype(u))
        for j = 1:M
            dᵢⱼ, bᵢⱼ = D[j, i], B[j, i]
            if dᵢⱼ != 0
                gᵢ *= bᵢⱼ^dᵢⱼ - z[homvars[j]]^dᵢⱼ
            end
        end
        u[i] = gᵢ * F.scaling_factors[i]
    end

    u
end
function evaluate(F::MultiHomTotalDegreeSystem, x, cache::MultiHomTotalDegreeSystemCache)
    evaluate!(similar(x, size(F, 1)), F, x, cache)
end

function jacobian!(
    U,
    F::MultiHomTotalDegreeSystem,
    z,
    cache::MultiHomTotalDegreeSystemCache{M},
) where {M}
    evaluate_and_jacobian!(nothing, U, F, z, cache)
    U
end
function jacobian(F::MultiHomTotalDegreeSystem, x, cache::MultiHomTotalDegreeSystemCache)
    jacobian!(similar(x, size(F)), F, x, cache)
end

function evaluate_and_jacobian!(
    u,
    U,
    F::MultiHomTotalDegreeSystem,
    z,
    cache::MultiHomTotalDegreeSystemCache{M},
) where {M}
    n, N = size(F)

    @boundscheck checkbounds(U, 1:n, 1:N)
    if u !== nothing
        @boundscheck checkbounds(u, 1:n)
    end

    D, C = F.D, F.C
    B, Ĝ, R, S, ranges, homvars =
        cache.B, cache.Ĝ, cache.R, cache.S, cache.ranges, cache.homvars

    n = size(B, 2)

    @inbounds for i = 1:n
        # Compute all bᵢⱼ and store in B
        # Since B is column major we store bᵢⱼ in B[j, i]
        for j = 1:M
            if D[j, i] != 0
                bᵢⱼ = -zero(B[j, i])
                for k in ranges[j]
                    bᵢⱼ += C[k, i] * z[k]
                end
                B[j, i] = bᵢⱼ
            end
        end

        # Compute all ĝᵢⱼ and store in Ĝ
        # Since Ĝ is column major we store ĝᵢⱼ in Ĝ[j, i]
        for j = 1:M
            dᵢⱼ, bᵢⱼ = D[j, i], B[j, i]
            if dᵢⱼ == 0
                Ĝ[j, i] = one(eltype(Ĝ))
            else
                Ĝ[j, i] = bᵢⱼ^dᵢⱼ - z[homvars[j]]^dᵢⱼ
            end
        end

        # Accumulate subproducts forward
        R[1, i] = rᵢⱼ_prev = Ĝ[1, i]
        for j = 2:M
            if D[j, i] != 0 # otherwise Ĝ[j, i] = 1
                R[j, i] = rᵢⱼ_prev = rᵢⱼ_prev * Ĝ[j, i]
            else
                R[j, i] = rᵢⱼ_prev
            end
            if u !== nothing
                u[i] = rᵢⱼ_prev * F.scaling_factors[i]
            end
        end

        # Accumulate subproducts backward
        S[M, i] = sᵢⱼ_prev = Ĝ[M, i]
        for j = M-1:-1:1
            if D[j, i] != 0 # otherwise Ĝ[j, i] = 1
                S[j, i] = sᵢⱼ_prev = sᵢⱼ_prev * Ĝ[j, i]
            else
                S[j, i] = sᵢⱼ_prev
            end
        end

        # Compute partial derivatives
        for j = 1:M
            dᵢⱼ = D[j, i]
            for k in ranges[j]
                c = C[k, i]
                if iszero(c) || iszero(dᵢⱼ)
                    U[i, k] = zero(eltype(U))
                else
                    if dᵢⱼ == 1
                        u_ik = c
                    else
                        u_ik = dᵢⱼ * B[j, i]^(dᵢⱼ - 1) * c
                    end
                    if j > 1
                        u_ik *= R[j-1, i]
                    end
                    if j < M
                        u_ik *= S[j+1, i]
                    end
                    U[i, k] = u_ik * F.scaling_factors[i]
                end
            end

            k = homvars[j]
            if iszero(dᵢⱼ)
                U[i, k] = zero(eltype(U))
            else
                if dᵢⱼ == 1
                    u_ik = -dᵢⱼ
                else
                    u_ik = -dᵢⱼ * z[k]^(dᵢⱼ - 1)
                end
                if j > 1
                    u_ik *= R[j-1, i]
                end
                if j < M
                    u_ik *= S[j+1, i]
                end
                U[i, k] = u_ik * F.scaling_factors[i]
            end
        end
    end
    nothing
end


text:
@testset "Kronecker products" begin

    A = randn(4, 4)
    B = Array{Float64, 2}([1 2 3;
         4 5 6;
         7 -2 9])
    C = rand(5, 6)
    D = rand(4, 4)

    v = rand(12)

    K = A ⊗ B
    K3 = kronecker(A, B, C)

    X = kron(A, B)  # true result

    @testset "Types and basic properties" begin
        @test issquare(A)
        @test !issquare(C)

        @test getmatrices(A)[1] === A

        @test !issymmetric(A)

        @test issquare(K)
        @test !issymmetric(K)

        @test K ≈ X

        @test order(A) == 1
        @test order(K) == 2
    end

    @testset "Linear algebra" begin
        @test tr(K) ≈ tr(X)
        @test det(K) ≈ det(X)
        @test !isposdef(K)
        @test transpose(K) ≈ transpose(X)
        @test conj(K) ≈ conj(X)
        @test K' ≈ X'
        @test inv(K) ≈ inv(X)

        # test on pos def functions
        As = A' * A
        Bs = B * B'
        @test logdet(As ⊗ Bs) ≈ log(det(As ⊗ Bs)) ≈ log(det(kron(As, Bs)))
    end

    @testset "Mismatch errors" begin
        P, Q = rand(10, 4), rand(4, 5)
        Kns = P ⊗ Q
        @test_throws DimensionMismatch inv(Kns)
        @test_throws DimensionMismatch det(Kns)
        @test_throws DimensionMismatch Kns * [1, 2, 3]
    end

    @testset "Higher order" begin
        @test order(K ⊗ A) == 3
        @test order(K3) == 3
        @test collect(K3) ≈ kron(X, C)

    end

    @testset "Kronecker powers" begin
        Kpow = ⊗(A, 5)
        @test order(Kpow) == 5
        @test size(Kpow, 1) == 4^5
        @test Kpow[1,1] ≈ A[1,1]^5
    end

    @testset "kron" begin
        @test kron(A ⊗ B, C) ≈ kron(A, B, C)
        @test kron(A, B ⊗ C) ≈ kron(A, B, C)
        @test kron(A ⊗ B, C ⊗ D) ≈ kron(A, B, C, D)
        @test collect((A⊗B) ⊗ (C⊗D)) ≈ kron(A, B, C, D)
    end

    @testset "Mixed product" begin
        A = rand(5, 4)
        B = rand(2, 3)
        C = rand(4, 6)
        D = rand(3, 4)

        K1 = (A ⊗ B)
        K2 = (C ⊗ D)

        @test K1 * K2 ≈ collect(K1) * collect(K2)

        @test_throws DimensionMismatch (A ⊗ C) * (B ⊗ D)
        @test_throws DimensionMismatch (A ⊗ D) * (C ⊗ B)
    end

    @testset "Add to dense" begin
        @test K + X ≈ Matrix(K) + X
        @test X + K ≈ X + Matrix(K)
    end

    @testset "Scalar multiplication" begin
        @test 3.0K ≈ 3.0X
        @test K * 2 ≈ 2X
        @test π * K3 ≈ π * collect(K3)
        @test 3.0K isa AbstractKroneckerProduct
        @test K * 2 isa AbstractKroneckerProduct
        @test 2(K ⊗ K) isa AbstractKroneckerProduct
    end

    @testset "Inplace scalar multiplication" begin
        A = rand(2, 2)
        B = rand(3, 4)

        K = copy(A) ⊗ copy(B)
        lmul!(3, K)
        rmul!(K, 2)
        @test K.A ≈ 3A
        @test K.B ≈ 2B
        @test K ≈ 6kron(A, B)
    end
end


text:
defaultminimum(::Union{Nothing, Missing}) = 4
defaultminimum(::Number) = 20
defaultminimum(::T) where {T <: Base.IEEEFloat} = Parsers.neededdigits(T)
defaultminimum(x::Bool) = ifelse(x, 4, 5)
defaultminimum(x::AbstractString) = ncodeunits(x) + 2
defaultminimum(x::Symbol) = ccall(:strlen, Csize_t, (Cstring,), x) + 2
defaultminimum(x::Enum) = 16
defaultminimum(::Type{T}) where {T} = 16
defaultminimum(x::Char) = 3
defaultminimum(x::Union{Tuple, AbstractSet, AbstractArray}) = isempty(x) ? 2 : sum(defaultminimum, x)
defaultminimum(x::Union{AbstractDict, NamedTuple, Pair}) = isempty(x) ? 2 : sum(defaultminimum(k) + defaultminimum(v) for (k, v) in StructTypes.keyvaluepairs(x))
defaultminimum(x) = max(2, sizeof(x))

"""
    JSON3.write([io], x; kw...)

Write JSON.

## Args

* `io`: Optionally, an `IO` object to write the resulting JSON string to.  By default, will return a `String` if `io` is not provided.
* `x`: An object to serialize as JSON.  If not a [built in type](#Builtin-types), must have a "struct mapping" registered with [StructTypes.jl](#Struct-API).

## Keyword Args

* `allow_inf`: Allow writing of `Inf` and `NaN` values (not part of the JSON standard). [default `false`]
* `dateformat`: A [`DateFormat`](https://docs.julialang.org/en/v1/stdlib/Dates/#Dates.DateFormat) describing how to format `Date`s in the object. [default `Dates.default_format(T)`]
"""
function write(io::IO, obj::T; kw...) where {T}
    len = defaultminimum(obj)
    buf = Base.StringVector(len)
    buf, pos, len = write(StructType(obj), buf, 1, length(buf), obj; kw...)
    return Base.write(io, resize!(buf, pos - 1))
end

function write(obj::T; kw...) where {T}
    len = defaultminimum(obj)
    buf = Base.StringVector(len)
    buf, pos, len = write(StructType(obj), buf, 1, length(buf), obj; kw...)
    return String(resize!(buf, pos - 1))
end

function write(fname::String, obj::T; kw...) where {T}
    open(fname, "w") do io
        write(io, obj; kw...)
    end
    fname
end

@noinline function realloc!(buf, len, n)
    # println("re-allocing...")
    new = zeros(UInt8, max(n, trunc(Int, len * 1.25)))
    copyto!(new, 1, buf, 1, len)
    return new, length(new)
end

macro check(n)
    esc(quote
        if (pos + $n - 1) > len
            buf, len = realloc!(buf, len, pos + $n - 1)
        end
    end)
end

macro writechar(chars...)
    block = quote
        @boundscheck @check($(length(chars)))
    end
    for c in chars
        push!(block.args, quote
            @inbounds buf[pos] = UInt8($c)
            pos += 1
        end)
    end
    #println(macroexpand(@__MODULE__, block))
    return esc(block)
end

# we need to special-case writing Type{T} because of ambiguities w/ StructTypes
write(::Struct, buf, pos, len, ::Type{T}; kw...) where {T} = write(StringType(), buf, pos, len, Base.string(T))
write(::Mutable, buf, pos, len, ::Type{T}; kw...) where {T} = write(StringType(), buf, pos, len, Base.string(T))
write(::DictType, buf, pos, len, ::Type{T}; kw...) where {T} = write(StringType(), buf, pos, len, Base.string(T))
write(::ArrayType, buf, pos, len, ::Type{T}; kw...) where {T} = write(StringType(), buf, pos, len, Base.string(T))
write(::StringType, buf, pos, len, ::Type{T}; kw...) where {T} = write(StringType(), buf, pos, len, Base.string(T))
write(::NumberType, buf, pos, len, ::Type{T}; kw...) where {T} = write(StringType(), buf, pos, len, Base.string(T))
write(::NullType, buf, pos, len, ::Type{T}; kw...) where {T} = write(StringType(), buf, pos, len, Base.string(T))
write(::BoolType, buf, pos, len, ::Type{T}; kw...) where {T} = write(StringType(), buf, pos, len, Base.string(T))
write(::AbstractType, buf, pos, len, ::Type{T}; kw...) where {T} = write(StringType(), buf, pos, len, Base.string(T))
write(::NoStructType, buf, pos, len, ::Type{T}; kw...) where {T} = write(StringType(), buf, pos, len, Base.string(T))

write(::NoStructType, buf, pos, len, ::T; kw...) where {T} = throw(ArgumentError("$T doesn't have a defined `StructTypes.StructType`"))

@inline function write(::RawType, buf, pos, len, x::T; kw...) where {T}
    bytes = rawbytes(x)
    @check length(bytes)
    for b in bytes
        @inbounds buf[pos] = b
        pos += 1
    end
    return buf, pos, len
end

mutable struct WriteClosure{T, KW}
    buf::T
    pos::Int
    len::Int
    afterfirst::Bool
    kw::KW
end

@inline function (f::WriteClosure)(i, nm, TT, v; kw...)
    buf, pos, len = f.buf, f.pos, f.len
    if f.afterfirst
        @writechar ','
    else
        f.afterfirst = true
    end
    kw2 = merge(values(kw), f.kw)
    buf, pos, len = write(StringType(), buf, pos, len, nm; kw2...)
    @writechar ':'
    buf, pos, len = write(StructType(v), buf, pos, len, v; kw2...)
    f.buf = buf
    f.pos = pos
    f.len = len
    return
end

# generic object writing
@inline function write(::Union{Struct, Mutable}, buf, pos, len, x::T; kw...) where {T}
    @writechar '{'
    c = WriteClosure(buf, pos, len, false, values(kw))
    StructTypes.foreachfield(c, x)
    buf = c.buf
    pos = c.pos
    len = c.len
    @writechar '}'
    return buf, pos, len
end

@inline function write(::CustomStruct, buf, pos, len, x; kw...)
    y = StructTypes.lower(x)
    return write(StructType(y), buf, pos, len, y; kw...)
end

function write(::DictType, buf, pos, len, x::T; kw...) where {T}
    @writechar '{'
    pairs = StructTypes.keyvaluepairs(x)

    next = iterate(pairs)
    while next !== nothing
        (k, v), state = next

        buf, pos, len = write(StringType(), buf, pos, len, Base.string(k))
        @writechar ':'
        buf, pos, len = write(StructType(v), buf, pos, len, v; kw...)

        next = iterate(pairs, state)
        next === nothing || @writechar ','
    end
    @writechar '}'
    return buf, pos, len
end

function write(::ArrayType, buf, pos, len, x::T; kw...) where {T}
    @writechar '['
    n = length(x)
    i = 1
    for y in x
        buf, pos, len = write(StructType(y), buf, pos, len, y; kw...)
        if i < n
            @writechar ','
        end
        i += 1
    end
    @writechar ']'
    return buf, pos, len
end

function write(::NullType, buf, pos, len, x; kw...)
    @writechar 'n' 'u' 'l' 'l'
    return buf, pos, len
end

write(::BoolType, buf, pos, len, x; kw...) = write(BoolType(), buf, pos, len, StructTypes.construct(Bool, x); kw...)
function write(::BoolType, buf, pos, len, x::Bool; kw...)
    if x
        @writechar 't' 'r' 'u' 'e'
    else
        @writechar 'f' 'a' 'l' 's' 'e'
    end
    return buf, pos, len
end

# adapted from base/intfuncs.jl
function write(::NumberType, buf, pos, len, y::Integer; kw...)
    x, neg = Base.split_sign(y)
    if neg
        @writechar UInt8('-')
    end
    n = i = ndigits(x, base=10, pad=1)
    @check i
    while i > 0
        @inbounds buf[pos + i - 1] = 48 + rem(x, 10)
        x = oftype(x, div(x, 10))
        i -= 1
    end
    return buf, pos + n, len
end

write(::NumberType, buf, pos, len, x::T; kw...) where {T} =
    write(NumberType(), buf, pos, len, StructTypes.construct(StructTypes.numbertype(T), x); kw...)
function write(::NumberType, buf, pos, len, x::AbstractFloat; allow_inf::Bool=false, kw...)
    isfinite(x) || allow_inf || error("$x not allowed to be written in JSON spec")
    bytes = codeunits(Base.string(x))
    sz = sizeof(bytes)
    @check sz
    for i = 1:sz
        @inbounds @writechar bytes[i]
    end

    return buf, pos, len
end

@inline function write(::NumberType, buf, pos, len, x::T; allow_inf::Bool=false, kw...) where {T <: Base.IEEEFloat}
    isfinite(x) || allow_inf || error("$x not allowed to be written in JSON spec")
    if isinf(x)
        # Although this is non-standard JSON, "Infinity" is commonly used.
        # See https://docs.python.org/3/library/json.html#infinite-and-nan-number-values.
        if sign(x) == -1
            @writechar '-'
        end
        @writechar 'I' 'n' 'f' 'i' 'n' 'i' 't' 'y'
        return buf, pos, len
    end
    @check Parsers.neededdigits(T)
    pos = Parsers.writeshortest(buf, pos, x)
    return buf, pos, len
end

const NEEDESCAPE = Set(map(UInt8, ('"', '\\', '\b', '\f', '\n', '\r', '\t')))

function escapechar(b)
    b == UInt8('"')  && return UInt8('"')
    b == UInt8('\\') && return UInt8('\\')
    b == UInt8('\b') && return UInt8('b')
    b == UInt8('\f') && return UInt8('f')
    b == UInt8('\n') && return UInt8('n')
    b == UInt8('\r') && return UInt8('r')
    b == UInt8('\t') && return UInt8('t')
    return 0x00
end

iscntrl(c::Char) = c <= '\x1f' || '\x7f' <= c <= '\u9f'
function escaped(b)
    if b == UInt8('/')
        return [UInt8('/')]
    elseif b >= 0x80
        return [b]
    elseif b in NEEDESCAPE
        return [UInt8('\\'), escapechar(b)]
    elseif iscntrl(Char(b))
        return UInt8[UInt8('\\'), UInt8('u'), Base.string(b, base=16, pad=4)...]
    else
        return [b]
    end
end

const ESCAPECHARS = [escaped(b) for b = 0x00:0xff]
const ESCAPELENS = [length(x) for x in ESCAPECHARS]

function escapelength(str)
    x = 0
    @simd for i = 1:ncodeunits(str)
        @inbounds len = ESCAPELENS[codeunit(str, i) + 1]
        x += len
    end
    return x
end

write(::StringType, buf, pos, len, x::T; dateformat::Dates.DateFormat=Dates.default_format(T), kw...) where {T <: Dates.TimeType} = write(StringType(), buf, pos, len, Dates.format(x, dateformat); kw...)
write(::StringType, buf, pos, len, x; kw...) = write(StringType(), buf, pos, len, Base.string(x); kw...)
function write(::StringType, buf, pos, len, x::AbstractString; kw...)
    sz = ncodeunits(x)
    el = escapelength(x)
    @check (el + 2)
    @inbounds @writechar '"'
    if el > sz
        for i = 1:sz
            @inbounds escbytes = ESCAPECHARS[codeunit(x, i) + 1]
            for j = 1:length(escbytes)
                @inbounds buf[pos] = escbytes[j]
                pos += 1
            end
        end
    else
        @simd for i = 1:sz
            @inbounds buf[pos] = codeunit(x, i)
            pos += 1
        end
    end
    @inbounds @writechar '"'
    return buf, pos, len
end

write(::StringType, buf, pos, len, x::Symbol; kw...) = write(StringType(), buf, pos, len, String(x); kw...)


text:
#=============================================

Defines a RealInterval type, used to represent intervals of the real line.
RealIntervals are primarily used for representing the output ranges of similarity
functions.

=============================================#

#====================
RealInterval definition and constructors
====================#

@doc """
    struct RealInterval{T<:Real}

Encodes an interval of the real line, such as `[-1,1]` or `[0,Inf)`.

# Fields
- `lower::T`: lower bound on the interval.
- `upper::T`: upper bound on the interval.
- `closed_below::Bool`: whether or not the interval is closed below.
- `closed_above::Bool`: whether or not the interval is closed above.

# Examples
The following snippet constructs `RealInterval` represeting the interval [0,1)

```jldoctest; setup = :(using LSHFunctions)
julia> interval = LSHFunctions.RealInterval(0, 1, true, false);
```

It's generally easier to construct an interval using the `@interval` macro. Check out the documentation for [`@interval`](@ref) for more information.

See also: [`@interval`](@ref)
"""
struct RealInterval{T<:Real}
    # Lower bound on the interval
    lower :: T

    # Upper bound on the interval
    upper :: T

    # Whether or not the lower end of the interval is closed
    closed_below :: Bool

    # Whether or not the upper end of the interval is closed
    closed_above :: Bool

    ### Internal RealInterval constructors
    RealInterval{T}(L, U, CB, CA) where T = RealInterval{T}(T(L), T(U), CB, CA)
    RealInterval{T}(L::T, U::T, CB::Bool, CA::Bool) where T =
        new{T}(L, U, CB, CA)
end

### External RealInterval constructors
@generated function RealInterval(L::Real, U::Real, CB::Bool, CA::Bool)
    bounds_type = begin
        if L <: Integer && U <: AbstractFloat
            U
        else
            L
        end
    end
    return :(RealInterval{$bounds_type}(L, U, CB, CA))
end

### String conversion and displaying RealInterval structs
function Base.string(interval::RealInterval)
    lower_bracket = (interval.closed_below) ? "[" : "("
    upper_bracket = (interval.closed_above) ? "]" : ")"
    lower_bracket * string(interval.lower) * "," * string(interval.upper) * upper_bracket
end

show(io::IO, interval::RealInterval) = print(io, string(interval))

#====================
Operator definitions for RealInterval
====================#
# Interval membership
function Base.:(∈)(x::Real, interval::RealInterval)
    if interval.lower < x < interval.upper
        true
    elseif interval.lower == x && interval.closed_below
        true
    elseif interval.upper == x && interval.closed_above
        true
    else
        false
    end
end

# Interval intersection
function Base.:(∩)(int1::RealInterval, int2::RealInterval)
    lower = max(int1.lower, int2.lower)
    upper = min(int1.upper, int2.upper)
    closed_below = (lower ∈ int1) && (lower ∈ int2)
    closed_above = (upper ∈ int1) && (upper ∈ int2)
    RealInterval(lower, upper, closed_below, closed_above)
end

intersect(int1, int2) = int1 ∩ int2

# Equality
# There are two cases in which equality can hold:
# - both intervals are empty; or
# - both intervals are non-empty, in which case the intervals can only be
#   equal if all of their fields are equal.
function Base.:(==)(int1::RealInterval, int2::RealInterval)
    # Case 1
    if isempty(int1)
        isempty(int2)
    # Case 2
    else
        int1 === int2
    end
end

#====================
Definitions of other methods for RealInterval
====================#

@doc """
    isempty(interval::RealInterval)

Returns `true` if `interval` is empty (i.e. there doesn't exist an `x` for which `x ∈ interval` is `true`), and `false` otherwise.
"""
Base.isempty(interval::RealInterval) =
    interval.lower > interval.upper ||
    (interval.lower == interval.upper && !(interval.closed_below && interval.closed_above))

@doc """
    width(interval::RealInterval)

Return the width of a `RealInterval` (i.e. the difference between its upper and lower bounds.
"""
width(interval::RealInterval{T}) where T =
    isempty(interval) ? T(0) : (interval.upper - interval.lower)

#====================
The @interval macro, which provides the primary interface for users to construct
new RealIntervals.
====================#

@doc """
    @interval(expr)

Construct a new `LSHFunctions.RealInterval` representing an interval on the real line from an expression such as

    0 ≤ x < 1

The returned expression constructs an `LSHFunctions.RealInterval` encoding the lower and upper bounds on the interval, as well as whether the ends are opened or closed.

# Examples
You can construct an interval using the following syntax:

```jldoctest; setup = :(using LSHFunctions)
julia> interval = @interval(0 ≤ x < 1);
```

There are usually multiple ways of constructing the same interval. For instance, each of the expressions below are equivalent ways of constructing the interval `[-1,1]`.

```jldoctest; setup = :(using LSHFunctions)
julia> @interval(-1 ≤  x ≤  1) ==
       @interval(-1 <= x <= 1) ==
       @interval(-1 ≤  y ≤  1) ==
       @interval( 1 ≥  x ≥ -1)
true
```

You can even create intervals with `Inf` or `-Inf` at the endpoints, e.g. `@interval(-Inf < x < Inf)`.

There are two primary operations you can run on an interval: testing for membership and intersection. You can test whether or not `x` is in an interval using `x ∈ interval`, as shown below.

```jldoctest; setup = :(using LSHFunctions)
julia> interval = @interval(0 ≤ x < 1);

julia> 0 ∈ interval && 1 ∉ interval
true

julia> 0 in interval    # This syntax also works
true
```

You can also intersect two intervals using the `∩` operator (or by using `intersect(interval_1, interval_2)`).

```
julia> @interval(0 ≤ x < 1) ∩ @interval(1/2 < x ≤ 1) == @interval(1/2 < x < 1)
true
```

See also: [`RealInterval`](@ref)
"""
macro interval(expr)
    invalid_expr_msg = """
    Expression is invalid. The expression passed to @interval must take the form

        a {<,≤,>,≥} x {<,≤,>,≥} b

    where a and b evaluate to real numbers, and x is a valid variable identifier.
    See the documentation for @interval for more information.
    """

    if !isa(expr, Expr)
        return :($invalid_expr_msg |> ErrorException |> throw)
    end

    args = expr.args

    # Convert >= and <= to ≥ and ≤ to reduce the number of symbols we have to deal with
    map!(sym -> (sym == :>=) ? :≥ : sym, args, args)
    map!(sym -> (sym == :<=) ? :≤ : sym, args, args)

    valid_expr = (
        length(args) == 5          &&
        expr.head == :comparison   &&

        # 3rd argument must be a valid variable identifier
        isa(args[3], Symbol)       &&
        Base.isidentifier(args[3]) &&

        # 2nd and 4th arguments must be inequalities
        args[2] ∈ (:<, :≤, :>, :≥) &&
        args[4] ∈ (:<, :≤, :>, :≥)
    )

    if !valid_expr
        return :($invalid_expr_msg |> ErrorException |> throw)
    end

    # Extract a tuple (a, op1, b, op2) from the expression $a $op1 x $op2 $b. E.g.
    # if expr == :(0 ≤ x < b), return (0, :≤, :b, :<)
    interval_tuple = begin
        # Flip inequalities if expression is of the form b {>,≥} x {>,≥} a
        if args[2] in (:>, :≥) && args[4] in (:>, :≥)
            map!(x -> (x == :>) ? :< : x, args, args)
            map!(x -> (x == :≥) ? :≤ : x, args, args)
            reverse!(args)
        elseif !(args[2] in (:<, :≤) && args[4] in (:<, :≤))
            return quote
                "Inequalities point in opposite directions" |>
                ErrorException |>
                throw
            end
        end
        (args[1], args[2], args[5], args[4])
    end

    # Construct a new RealInterval based on the inequalities that were provided
    a, b = interval_tuple[1], interval_tuple[3]
    closed_below = (interval_tuple[2] == :≤)
    closed_above = (interval_tuple[4] == :≤)
    quote
        RealInterval($(esc(a)), $(esc(b)), $(esc(closed_below)), $(esc(closed_above)))
    end
end


text:
using Test
using BinaryBuilderBase: extract_kwargs, extract_fields, strip_path_separator, ArchiveSource, stdlib_version
using Downloads: RequestError
using UUIDs: UUID

@testset "Compat functions" begin
    foo(; kwargs...) = collect(extract_kwargs(kwargs, (:bar, :qux)))
    @test foo(; a = 1) == Pair[]
    @test foo(; bar = 2, b = 3) == [:bar => 2]
    @test foo(; c = 4, qux = 5) == [:qux => 5]
    @test Set(foo(; qux = 6, d = 7, bar = 8)) == Set([:bar => 8, :qux => 6])

    @test extract_fields(ArchiveSource("http://example.org", "this is the hash"; unpack_target = "target")) == Dict("url" => "http://example.org", "hash" => "this is the hash", "unpack_target" => "target")

    @test strip_path_separator("/home/wizard") == "/home/wizard"
    @test strip_path_separator("/home/wizard/") == "/home/wizard"
    @test strip_path_separator("/home//wizard///") == "/home//wizard"
    @test strip_path_separator("wizard.jl") == "wizard.jl"
    # Test integration with `basename`
    @test basename(strip_path_separator("/home/wizard")) == "wizard"
    @test basename(strip_path_separator("/home/wizard/")) == "wizard"
    @test basename(strip_path_separator("/home//wizard///")) == "wizard"
    @test basename(strip_path_separator("wizard.jl")) == "wizard.jl"

    gmp_jll = UUID("781609d7-10c4-51f6-84f2-b8444358ff6d")
    llvmlibwnwind_jll = UUID("47c5dbc3-30ba-59ef-96a6-123e260183d9")
    @test stdlib_version(gmp_jll, v"1.6") == v"6.2.0+5"
    @test stdlib_version(llvmlibwnwind_jll, v"1.6") === nothing
    if VERSION ≥ v"1.7-DEV"
        # Fascinating, different versions of Julia have different opinions about GMP_jll in v1.7
        if VERSION ≥ v"1.8-DEV"
            @test stdlib_version(gmp_jll, v"1.7") == v"6.2.1+1"
            @test stdlib_version(gmp_jll, v"1.8") == v"6.2.1+1"
            @test stdlib_version(llvmlibwnwind_jll, v"1.8") == v"12.0.1+0"
        else
            @test stdlib_version(gmp_jll, v"1.7") == v"6.2.1+0"
            @test stdlib_version(llvmlibwnwind_jll, v"1.7") == v"11.0.1+1"
        end
    end
end

using BinaryBuilderBase: download_verify, list_tarball_files
@testset "ArchiveUtils" begin
    mktempdir() do dir
        # Test that we can download, then list, the contents of .tar.gz, .tar.bz2 and .tar.xz files.
        for (url, hash) in [("https://github.com/staticfloat/small_bin/raw/master/socrates.tar.gz",
                             "e65d2f13f2085f2c279830e863292312a72930fee5ba3c792b14c33ce5c5cc58"),
                            ("https://github.com/staticfloat/small_bin/raw/master/socrates.tar.xz",
                             "61bcf109fcb749ee7b6a570a6057602c08c836b6f81091eab7aa5f5870ec6475"),
                            ("https://github.com/staticfloat/small_bin/raw/master/socrates.tar.bz2",
                             "13fc17b97be41763b02cbb80e9d048302cec3bd3d446c2ed6e8210bddcd3ac76")]
            # First, download to a path:
            path = joinpath(dir, basename(url))
            @test_logs (:info, "Downloading $(url) to $(path)...") download_verify(url, hash, path)
            @test isfile(path)

            # Ensure that we can list the tarball:
            @test "bin/socrates" in list_tarball_files(path)
        end

        # Test that a 404 throws
        url = "https://github.com/not_a_file"
        dest = joinpath(dir, "blah")
        @test_logs (:info, "Downloading $(url) to $(dest)...") @test_throws RequestError download_verify(url, "0"^64, dest)

        # Test that a bad hash logs a message and fails
        url = "https://github.com/staticfloat/small_bin/raw/master/socrates.tar.xz"
        dest = joinpath(dir, "blah2")
        @test_logs (:error, r"Hash Mismatch") (:info, "Downloading $(url) to $(dest)...") match_mode=:any @test_throws ErrorException begin
            download_verify(url, "0"^64, dest)
        end
    end
end


text:
function lexicographicallylessthan_vectorofvariables_test(
    model::MOI.ModelLike,
    config::MOIT.Config,
)
    MOI.empty!(model)

    @test MOI.supports_constraint(model, MOI.VariableIndex, MOI.Integer)
    @test MOI.supports_constraint(model, MOI.VariableIndex, MOI.EqualTo{Int})
    @test MOI.supports_constraint(
        model,
        MOI.VectorOfVariables,
        CP.LexicographicallyLessThan,
    )

    x1, _ = MOI.add_constrained_variable(model, MOI.Integer())
    x2, _ = MOI.add_constrained_variable(model, MOI.Integer())
    x3, _ = MOI.add_constrained_variable(model, MOI.Integer())
    x4, _ = MOI.add_constrained_variable(model, MOI.Integer())

    c1 = MOI.add_constraint(model, x1, MOI.EqualTo(2))
    c2 = MOI.add_constraint(model, x2, MOI.GreaterThan(1))
    c3 = MOI.add_constraint(model, x3, MOI.EqualTo(2))
    c4 = MOI.add_constraint(model, x4, MOI.EqualTo(2))

    c5 = MOI.add_constraint(
        model,
        MOI.VectorOfVariables([x1, x2, x3, x4]),
        CP.LexicographicallyLessThan(2),
    )

    @test MOI.is_valid(model, x1)
    @test MOI.is_valid(model, x2)
    @test MOI.is_valid(model, x3)
    @test MOI.is_valid(model, x4)
    @test MOI.is_valid(model, c1)
    @test MOI.is_valid(model, c2)
    @test MOI.is_valid(model, c3)
    @test MOI.is_valid(model, c4)
    @test MOI.is_valid(model, c5)

    if config.solve
        MOI.optimize!(model)
        @test MOI.get(model, MOI.TerminationStatus()) == MOI.OPTIMAL
        @test MOI.get(model, MOI.PrimalStatus()) == MOI.FEASIBLE_POINT

        @test MOI.get(model, MOI.ResultCount()) >= 1
        @test MOI.get(model, MOI.VariablePrimal(), x1) == 2
        @test MOI.get(model, MOI.VariablePrimal(), x2) in [1, 2]
        @test MOI.get(model, MOI.VariablePrimal(), x3) == 2
        @test MOI.get(model, MOI.VariablePrimal(), x4) == 2
    end
end

function lexicographicallylessthan_vectoraffinefunction_test(
    model::MOI.ModelLike,
    config::MOIT.Config,
)
    MOI.empty!(model)

    @test MOI.supports_constraint(model, MOI.VariableIndex, MOI.Integer)
    @test MOI.supports_constraint(model, MOI.VariableIndex, MOI.EqualTo{Int})
    @test MOI.supports_constraint(
        model,
        MOI.VectorAffineFunction{Int},
        CP.LexicographicallyLessThan,
    )

    x1, _ = MOI.add_constrained_variable(model, MOI.Integer())
    x2, _ = MOI.add_constrained_variable(model, MOI.Integer())
    x3, _ = MOI.add_constrained_variable(model, MOI.Integer())
    x4, _ = MOI.add_constrained_variable(model, MOI.Integer())

    c1 = MOI.add_constraint(model, x1, MOI.EqualTo(2))
    c2 = MOI.add_constraint(model, x2, MOI.GreaterThan(1))
    c3 = MOI.add_constraint(model, x3, MOI.EqualTo(2))
    c4 = MOI.add_constraint(model, x4, MOI.EqualTo(2))

    c5 = MOI.add_constraint(
        model,
        MOIU.vectorize([x1, x2, x3, x4]),
        CP.LexicographicallyLessThan(2),
    )

    @test MOI.is_valid(model, x1)
    @test MOI.is_valid(model, x2)
    @test MOI.is_valid(model, x3)
    @test MOI.is_valid(model, x4)
    @test MOI.is_valid(model, c1)
    @test MOI.is_valid(model, c2)
    @test MOI.is_valid(model, c3)
    @test MOI.is_valid(model, c4)
    @test MOI.is_valid(model, c5)

    if config.solve
        MOI.optimize!(model)
        @test MOI.get(model, MOI.TerminationStatus()) == MOI.OPTIMAL
        @test MOI.get(model, MOI.PrimalStatus()) == MOI.FEASIBLE_POINT

        @test MOI.get(model, MOI.ResultCount()) >= 1
        @test MOI.get(model, MOI.VariablePrimal(), x1) == 2
        @test MOI.get(model, MOI.VariablePrimal(), x2) in [1, 2]
        @test MOI.get(model, MOI.VariablePrimal(), x3) == 2
        @test MOI.get(model, MOI.VariablePrimal(), x4) == 2
    end
end

const lexicographicallytests = Dict(
    "lexicographicallylessthan_vectorofvariables" =>
        lexicographicallylessthan_vectorofvariables_test,
    "lexicographicallylessthan_vectoraffinefunction" =>
        lexicographicallylessthan_vectoraffinefunction_test,
)

MOIT.@moitestset lexicographically


text:
include("prelude.jl")

# Ensure sendeof works both with/out a pty

@static if Sys.isunix()
    @testset "pty" begin
        interact(`cat`, 1) do proc
            @require sendeof(proc)

            # A final \n before the EOF is required by the
            # canonical tty processing
            @test read(proc, String) == "\n"

            wait(proc)
            @test success(proc)
        end
    end
end

@testset "nopty" begin
    interact(`cat`, 1; pty=false) do proc
        @require sendeof(proc)
        @test length(read(proc, String)) == 0
        wait(proc)
        @test success(proc)
    end
end


text:
mutable struct GuidedPolicy{
    P1 <: AbstractPolicy,
    P2 <: AbstractPolicy
} <: AbstractPolicy
    learner::P1
    teacher::P2
    norm_limit::Float32
    update_norm_limit::Bool
    target_ratio::Float32
    actor_history::Vector{Bool} # True if learner took action, false if not
    history_ptr::Int
end
function GuidedPolicy(
    learner, teacher;
    norm_limit=2f0,
    update_norm_limit=false,
    history_length=3600,
    target_ratio=0.95f0,
    kwargs...
)
    GuidedPolicy(
        learner,
        teacher,
        norm_limit,
        update_norm_limit,
        target_ratio,
        Vector{Bool}(undef, update_norm_limit ? history_length : 1),
        0
    )
end

function (p::GuidedPolicy)(env)
    A = action_space(env[!])
    alow = [x.left for x in A]
    ahigh = [x.right for x in A]

    a = p.learner(env)
    ascaled = 0.5 .* (p.learner(env) .+ 1) .* (ahigh .- alow) .+ alow
    p.history_ptr = (p.history_ptr % length(p.actor_history)) + 1
    last_a = map(ms -> length(ms.target_nodes), env[!].microservices)
    normsq = sum(abs2, (ascaled .- last_a))
    p.actor_history[p.history_ptr] = normsq / length(a) < p.norm_limit^2
    if p.update_norm_limit
        p.norm_limit *= mean(p.actor_history) / p.target_ratio
    end
    if !p.actor_history[p.history_ptr]
        a = 2 .* (p.teacher(env[!]) .- alow) ./ (ahigh .- alow) .- 1
    end
    return a
end

function RLBase.update!(
    p::GuidedPolicy,
    traj::CircularArraySARTTrajectory,
    env::AbstractEnv,
    stage::PreActStage,
)
    update!(p.learner, traj, env, stage)
    update!(p.teacher, traj, env, stage)
end

max_stars_repo_name: albheim/ServiceMeshControl

max_issues_repo_name: albheim/ServiceMeshControl

max_forks_repo_name: albheim/ServiceMeshControl


text:
using ModelingToolkit
using Test

@parameters t
@variables x(t) y(t) z(t)

null_op = 0*t
@test isequal(simplify(null_op), 0)

one_op = 1*t
@test isequal(simplify(one_op), t)

identity_op = Operation(identity,[x])
@test isequal(simplify(identity_op), x)

minus_op = -x
@test isequal(simplify(minus_op), -1x)
simplify(minus_op)

@variables x

@test simplified_expr(expand_derivatives(Differential(x)((x-2)^2))) == :(2 * (-2 + x))
@test simplified_expr(expand_derivatives(Differential(x)((x-2)^3))) == :(3 * (-2 + x)^2)
@test simplified_expr(simplify(x+2+3)) == :(5 + x)

d1 = Differential(x)((-2 + x)^2)
d2 = Differential(x)(d1)
d3 = Differential(x)(d2)

@test simplified_expr(expand_derivatives(d3)) == :(0)
@test simplified_expr(simplify(x^0)) == :(1)


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

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

Authorizes the Shield Response Team (SRT) to access the specified Amazon S3 bucket
containing log data such as Application Load Balancer access logs, CloudFront logs, or logs
from third party sources. You can associate up to 10 Amazon S3 buckets with your
subscription. To use the services of the SRT and make an AssociateDRTLogBucket request, you
must be subscribed to the Business Support plan or the Enterprise Support plan.

# Arguments
- `log_bucket`: The Amazon S3 bucket that contains the logs that you want to share.

"""
function associate_drtlog_bucket(
    LogBucket; aws_config::AbstractAWSConfig=global_aws_config()
)
    return shield(
        "AssociateDRTLogBucket",
        Dict{String,Any}("LogBucket" => LogBucket);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function associate_drtlog_bucket(
    LogBucket,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return shield(
        "AssociateDRTLogBucket",
        Dict{String,Any}(
            mergewith(_merge, Dict{String,Any}("LogBucket" => LogBucket), params)
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Authorizes the Shield Response Team (SRT) using the specified role, to access your Amazon
Web Services account to assist with DDoS attack mitigation during potential attacks. This
enables the SRT to inspect your WAF configuration and create or update WAF rules and web
ACLs. You can associate only one RoleArn with your subscription. If you submit an
AssociateDRTRole request for an account that already has an associated role, the new
RoleArn will replace the existing RoleArn.  Prior to making the AssociateDRTRole request,
you must attach the AWSShieldDRTAccessPolicy managed policy to the role you will specify in
the request. For more information see Attaching and Detaching IAM Policies. The role must
also trust the service principal  drt.shield.amazonaws.com. For more information, see IAM
JSON Policy Elements: Principal. The SRT will have access only to your WAF and Shield
resources. By submitting this request, you authorize the SRT to inspect your WAF and Shield
configuration and create and update WAF rules and web ACLs on your behalf. The SRT takes
these actions only if explicitly authorized by you. You must have the iam:PassRole
permission to make an AssociateDRTRole request. For more information, see Granting a User
Permissions to Pass a Role to an Amazon Web Services Service.  To use the services of the
SRT and make an AssociateDRTRole request, you must be subscribed to the Business Support
plan or the Enterprise Support plan.

# Arguments
- `role_arn`: The Amazon Resource Name (ARN) of the role the SRT will use to access your
  Amazon Web Services account. Prior to making the AssociateDRTRole request, you must attach
  the AWSShieldDRTAccessPolicy managed policy to this role. For more information see
  Attaching and Detaching IAM Policies.

"""
function associate_drtrole(RoleArn; aws_config::AbstractAWSConfig=global_aws_config())
    return shield(
        "AssociateDRTRole",
        Dict{String,Any}("RoleArn" => RoleArn);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function associate_drtrole(
    RoleArn, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return shield(
        "AssociateDRTRole",
        Dict{String,Any}(mergewith(_merge, Dict{String,Any}("RoleArn" => RoleArn), params));
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    associate_health_check(health_check_arn, protection_id)
    associate_health_check(health_check_arn, protection_id, params::Dict{String,<:Any})

Adds health-based detection to the Shield Advanced protection for a resource. Shield
Advanced health-based detection uses the health of your Amazon Web Services resource to
improve responsiveness and accuracy in attack detection and mitigation.  You define the
health check in Route 53 and then associate it with your Shield Advanced protection. For
more information, see Shield Advanced Health-Based Detection in the WAF Developer Guide.

# Arguments
- `health_check_arn`: The Amazon Resource Name (ARN) of the health check to associate with
  the protection.
- `protection_id`: The unique identifier (ID) for the Protection object to add the health
  check association to.

"""
function associate_health_check(
    HealthCheckArn, ProtectionId; aws_config::AbstractAWSConfig=global_aws_config()
)
    return shield(
        "AssociateHealthCheck",
        Dict{String,Any}(
            "HealthCheckArn" => HealthCheckArn, "ProtectionId" => ProtectionId
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function associate_health_check(
    HealthCheckArn,
    ProtectionId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return shield(
        "AssociateHealthCheck",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}(
                    "HealthCheckArn" => HealthCheckArn, "ProtectionId" => ProtectionId
                ),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Initializes proactive engagement and sets the list of contacts for the Shield Response Team
(SRT) to use. You must provide at least one phone number in the emergency contact list.
After you have initialized proactive engagement using this call, to disable or enable
proactive engagement, use the calls DisableProactiveEngagement and
EnableProactiveEngagement.   This call defines the list of email addresses and phone
numbers that the SRT can use to contact you for escalations to the SRT and to initiate
proactive customer support. The contacts that you provide in the request replace any
contacts that were already defined. If you already have contacts defined and want to use
them, retrieve the list using DescribeEmergencyContactSettings and then provide it to this
call.

# Arguments
- `emergency_contact_list`: A list of email addresses and phone numbers that the Shield
  Response Team (SRT) can use to contact you for escalations to the SRT and to initiate
  proactive customer support.  To enable proactive engagement, the contact list must include
  at least one phone number.  The contacts that you provide here replace any contacts that
  were already defined. If you already have contacts defined and want to use them, retrieve
  the list using DescribeEmergencyContactSettings and then provide it here.

"""
function associate_proactive_engagement_details(
    EmergencyContactList; aws_config::AbstractAWSConfig=global_aws_config()
)
    return shield(
        "AssociateProactiveEngagementDetails",
        Dict{String,Any}("EmergencyContactList" => EmergencyContactList);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function associate_proactive_engagement_details(
    EmergencyContactList,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return shield(
        "AssociateProactiveEngagementDetails",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}("EmergencyContactList" => EmergencyContactList),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    create_protection(name, resource_arn)
    create_protection(name, resource_arn, params::Dict{String,<:Any})

Enables Shield Advanced for a specific Amazon Web Services resource. The resource can be an
Amazon CloudFront distribution, Elastic Load Balancing load balancer, Global Accelerator
accelerator, Elastic IP Address, or an Amazon Route 53 hosted zone. You can add protection
to only a single resource with each CreateProtection request. If you want to add protection
to multiple resources at once, use the WAF console. For more information see Getting
Started with Shield Advanced and Add Shield Advanced Protection to more Amazon Web Services
Resources.

# Arguments
- `name`: Friendly name for the Protection you are creating.
- `resource_arn`: The ARN (Amazon Resource Name) of the resource to be protected. The ARN
  should be in one of the following formats:   For an Application Load Balancer:
  arn:aws:elasticloadbalancing:region:account-id:loadbalancer/app/load-balancer-name/load-bala
  ncer-id     For an Elastic Load Balancer (Classic Load Balancer):
  arn:aws:elasticloadbalancing:region:account-id:loadbalancer/load-balancer-name     For an
  Amazon CloudFront distribution: arn:aws:cloudfront::account-id:distribution/distribution-id
      For an Global Accelerator accelerator:
  arn:aws:globalaccelerator::account-id:accelerator/accelerator-id     For Amazon Route 53:
  arn:aws:route53:::hostedzone/hosted-zone-id     For an Elastic IP address:
  arn:aws:ec2:region:account-id:eip-allocation/allocation-id

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"Tags"`: One or more tag key-value pairs for the Protection object that is created.
"""
function create_protection(
    Name, ResourceArn; aws_config::AbstractAWSConfig=global_aws_config()
)
    return shield(
        "CreateProtection",
        Dict{String,Any}("Name" => Name, "ResourceArn" => ResourceArn);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function create_protection(
    Name,
    ResourceArn,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return shield(
        "CreateProtection",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}("Name" => Name, "ResourceArn" => ResourceArn),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    create_protection_group(aggregation, pattern, protection_group_id)
    create_protection_group(aggregation, pattern, protection_group_id, params::Dict{String,<:Any})

Creates a grouping of protected resources so they can be handled as a collective. This
resource grouping improves the accuracy of detection and reduces false positives.

# Arguments
- `aggregation`: Defines how Shield combines resource data for the group in order to
  detect, mitigate, and report events.   Sum - Use the total traffic across the group. This
  is a good choice for most cases. Examples include Elastic IP addresses for EC2 instances
  that scale manually or automatically.   Mean - Use the average of the traffic across the
  group. This is a good choice for resources that share traffic uniformly. Examples include
  accelerators and load balancers.   Max - Use the highest traffic from each resource. This
  is useful for resources that don't share traffic and for resources that share that traffic
  in a non-uniform way. Examples include Amazon CloudFront and origin resources for
  CloudFront distributions.
- `pattern`: The criteria to use to choose the protected resources for inclusion in the
  group. You can include all resources that have protections, provide a list of resource
  Amazon Resource Names (ARNs), or include all resources of a specified resource type.
- `protection_group_id`: The name of the protection group. You use this to identify the
  protection group in lists and to manage the protection group, for example to update,
  delete, or describe it.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"Members"`: The Amazon Resource Names (ARNs) of the resources to include in the
  protection group. You must set this when you set Pattern to ARBITRARY and you must not set
  it for any other Pattern setting.
- `"ResourceType"`: The resource type to include in the protection group. All protected
  resources of this type are included in the protection group. Newly protected resources of
  this type are automatically added to the group. You must set this when you set Pattern to
  BY_RESOURCE_TYPE and you must not set it for any other Pattern setting.
- `"Tags"`: One or more tag key-value pairs for the protection group.
"""
function create_protection_group(
    Aggregation,
    Pattern,
    ProtectionGroupId;
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return shield(
        "CreateProtectionGroup",
        Dict{String,Any}(
            "Aggregation" => Aggregation,
            "Pattern" => Pattern,
            "ProtectionGroupId" => ProtectionGroupId,
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function create_protection_group(
    Aggregation,
    Pattern,
    ProtectionGroupId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return shield(
        "CreateProtectionGroup",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}(
                    "Aggregation" => Aggregation,
                    "Pattern" => Pattern,
                    "ProtectionGroupId" => ProtectionGroupId,
                ),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Activates Shield Advanced for an account. When you initally create a subscription, your
subscription is set to be automatically renewed at the end of the existing subscription
period. You can change this by submitting an UpdateSubscription request.

"""
function create_subscription(; aws_config::AbstractAWSConfig=global_aws_config())
    return shield(
        "CreateSubscription"; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end
function create_subscription(
    params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return shield(
        "CreateSubscription", params; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end

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

Deletes an Shield Advanced Protection.

# Arguments
- `protection_id`: The unique identifier (ID) for the Protection object to be deleted.

"""
function delete_protection(ProtectionId; aws_config::AbstractAWSConfig=global_aws_config())
    return shield(
        "DeleteProtection",
        Dict{String,Any}("ProtectionId" => ProtectionId);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function delete_protection(
    ProtectionId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return shield(
        "DeleteProtection",
        Dict{String,Any}(
            mergewith(_merge, Dict{String,Any}("ProtectionId" => ProtectionId), params)
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Removes the specified protection group.

# Arguments
- `protection_group_id`: The name of the protection group. You use this to identify the
  protection group in lists and to manage the protection group, for example to update,
  delete, or describe it.

"""
function delete_protection_group(
    ProtectionGroupId; aws_config::AbstractAWSConfig=global_aws_config()
)
    return shield(
        "DeleteProtectionGroup",
        Dict{String,Any}("ProtectionGroupId" => ProtectionGroupId);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function delete_protection_group(
    ProtectionGroupId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return shield(
        "DeleteProtectionGroup",
        Dict{String,Any}(
            mergewith(
                _merge, Dict{String,Any}("ProtectionGroupId" => ProtectionGroupId), params
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Removes Shield Advanced from an account. Shield Advanced requires a 1-year subscription
commitment. You cannot delete a subscription prior to the completion of that commitment.

"""
function delete_subscription(; aws_config::AbstractAWSConfig=global_aws_config())
    return shield(
        "DeleteSubscription"; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end
function delete_subscription(
    params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return shield(
        "DeleteSubscription", params; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end

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

Describes the details of a DDoS attack.

# Arguments
- `attack_id`: The unique identifier (ID) for the attack that to be described.

"""
function describe_attack(AttackId; aws_config::AbstractAWSConfig=global_aws_config())
    return shield(
        "DescribeAttack",
        Dict{String,Any}("AttackId" => AttackId);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function describe_attack(
    AttackId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return shield(
        "DescribeAttack",
        Dict{String,Any}(
            mergewith(_merge, Dict{String,Any}("AttackId" => AttackId), params)
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Provides information about the number and type of attacks Shield has detected in the last
year for all resources that belong to your account, regardless of whether you've defined
Shield protections for them. This operation is available to Shield customers as well as to
Shield Advanced customers. The operation returns data for the time range of midnight UTC,
one year ago, to midnight UTC, today. For example, if the current time is 2020-10-26
15:39:32 PDT, equal to 2020-10-26 22:39:32 UTC, then the time range for the attack data
returned is from 2019-10-26 00:00:00 UTC to 2020-10-26 00:00:00 UTC.  The time range
indicates the period covered by the attack statistics data items.

"""
function describe_attack_statistics(; aws_config::AbstractAWSConfig=global_aws_config())
    return shield(
        "DescribeAttackStatistics"; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end
function describe_attack_statistics(
    params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return shield(
        "DescribeAttackStatistics",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Returns the current role and list of Amazon S3 log buckets used by the Shield Response Team
(SRT) to access your Amazon Web Services account while assisting with attack mitigation.

"""
function describe_drtaccess(; aws_config::AbstractAWSConfig=global_aws_config())
    return shield(
        "DescribeDRTAccess"; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end
function describe_drtaccess(
    params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return shield(
        "DescribeDRTAccess", params; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end

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

A list of email addresses and phone numbers that the Shield Response Team (SRT) can use to
contact you if you have proactive engagement enabled, for escalations to the SRT and to
initiate proactive customer support.

"""
function describe_emergency_contact_settings(;
    aws_config::AbstractAWSConfig=global_aws_config()
)
    return shield(
        "DescribeEmergencyContactSettings";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function describe_emergency_contact_settings(
    params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return shield(
        "DescribeEmergencyContactSettings",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Lists the details of a Protection object.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"ProtectionId"`: The unique identifier (ID) for the Protection object that is described.
  When submitting the DescribeProtection request you must provide either the ResourceArn or
  the ProtectionID, but not both.
- `"ResourceArn"`: The ARN (Amazon Resource Name) of the Amazon Web Services resource for
  the Protection object that is described. When submitting the DescribeProtection request you
  must provide either the ResourceArn or the ProtectionID, but not both.
"""
function describe_protection(; aws_config::AbstractAWSConfig=global_aws_config())
    return shield(
        "DescribeProtection"; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end
function describe_protection(
    params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return shield(
        "DescribeProtection", params; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end

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

Returns the specification for the specified protection group.

# Arguments
- `protection_group_id`: The name of the protection group. You use this to identify the
  protection group in lists and to manage the protection group, for example to update,
  delete, or describe it.

"""
function describe_protection_group(
    ProtectionGroupId; aws_config::AbstractAWSConfig=global_aws_config()
)
    return shield(
        "DescribeProtectionGroup",
        Dict{String,Any}("ProtectionGroupId" => ProtectionGroupId);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function describe_protection_group(
    ProtectionGroupId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return shield(
        "DescribeProtectionGroup",
        Dict{String,Any}(
            mergewith(
                _merge, Dict{String,Any}("ProtectionGroupId" => ProtectionGroupId), params
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Provides details about the Shield Advanced subscription for an account.

"""
function describe_subscription(; aws_config::AbstractAWSConfig=global_aws_config())
    return shield(
        "DescribeSubscription"; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end
function describe_subscription(
    params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return shield(
        "DescribeSubscription",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Removes authorization from the Shield Response Team (SRT) to notify contacts about
escalations to the SRT and to initiate proactive customer support.

"""
function disable_proactive_engagement(; aws_config::AbstractAWSConfig=global_aws_config())
    return shield(
        "DisableProactiveEngagement"; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end
function disable_proactive_engagement(
    params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return shield(
        "DisableProactiveEngagement",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Removes the Shield Response Team's (SRT) access to the specified Amazon S3 bucket
containing the logs that you shared previously. To make a DisassociateDRTLogBucket request,
you must be subscribed to the Business Support plan or the Enterprise Support plan.
However, if you are not subscribed to one of these support plans, but had been previously
and had granted the SRT access to your account, you can submit a DisassociateDRTLogBucket
request to remove this access.

# Arguments
- `log_bucket`: The Amazon S3 bucket that contains the logs that you want to share.

"""
function disassociate_drtlog_bucket(
    LogBucket; aws_config::AbstractAWSConfig=global_aws_config()
)
    return shield(
        "DisassociateDRTLogBucket",
        Dict{String,Any}("LogBucket" => LogBucket);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function disassociate_drtlog_bucket(
    LogBucket,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return shield(
        "DisassociateDRTLogBucket",
        Dict{String,Any}(
            mergewith(_merge, Dict{String,Any}("LogBucket" => LogBucket), params)
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Removes the Shield Response Team's (SRT) access to your Amazon Web Services account. To
make a DisassociateDRTRole request, you must be subscribed to the Business Support plan or
the Enterprise Support plan. However, if you are not subscribed to one of these support
plans, but had been previously and had granted the SRT access to your account, you can
submit a DisassociateDRTRole request to remove this access.

"""
function disassociate_drtrole(; aws_config::AbstractAWSConfig=global_aws_config())
    return shield(
        "DisassociateDRTRole"; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end
function disassociate_drtrole(
    params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return shield(
        "DisassociateDRTRole",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    disassociate_health_check(health_check_arn, protection_id)
    disassociate_health_check(health_check_arn, protection_id, params::Dict{String,<:Any})

Removes health-based detection from the Shield Advanced protection for a resource. Shield
Advanced health-based detection uses the health of your Amazon Web Services resource to
improve responsiveness and accuracy in attack detection and mitigation.  You define the
health check in Route 53 and then associate or disassociate it with your Shield Advanced
protection. For more information, see Shield Advanced Health-Based Detection in the WAF
Developer Guide.

# Arguments
- `health_check_arn`: The Amazon Resource Name (ARN) of the health check that is associated
  with the protection.
- `protection_id`: The unique identifier (ID) for the Protection object to remove the
  health check association from.

"""
function disassociate_health_check(
    HealthCheckArn, ProtectionId; aws_config::AbstractAWSConfig=global_aws_config()
)
    return shield(
        "DisassociateHealthCheck",
        Dict{String,Any}(
            "HealthCheckArn" => HealthCheckArn, "ProtectionId" => ProtectionId
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function disassociate_health_check(
    HealthCheckArn,
    ProtectionId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return shield(
        "DisassociateHealthCheck",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}(
                    "HealthCheckArn" => HealthCheckArn, "ProtectionId" => ProtectionId
                ),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Authorizes the Shield Response Team (SRT) to use email and phone to notify contacts about
escalations to the SRT and to initiate proactive customer support.

"""
function enable_proactive_engagement(; aws_config::AbstractAWSConfig=global_aws_config())
    return shield(
        "EnableProactiveEngagement"; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end
function enable_proactive_engagement(
    params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return shield(
        "EnableProactiveEngagement",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Returns the SubscriptionState, either Active or Inactive.

"""
function get_subscription_state(; aws_config::AbstractAWSConfig=global_aws_config())
    return shield(
        "GetSubscriptionState"; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end
function get_subscription_state(
    params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return shield(
        "GetSubscriptionState",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Returns all ongoing DDoS attacks or all DDoS attacks during a specified time period.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"EndTime"`: The end of the time period for the attacks. This is a timestamp type. The
  sample request above indicates a number type because the default used by WAF is Unix time
  in seconds. However any valid timestamp format is allowed.
- `"MaxResults"`: The maximum number of AttackSummary objects to return. If you leave this
  blank, Shield Advanced returns the first 20 results. This is a maximum value. Shield
  Advanced might return the results in smaller batches. That is, the number of objects
  returned could be less than MaxResults, even if there are still more objects yet to return.
  If there are more objects to return, Shield Advanced returns a value in NextToken that you
  can use in your next request, to get the next batch of objects.
- `"NextToken"`: The ListAttacksRequest.NextMarker value from a previous call to
  ListAttacksRequest. Pass null if this is the first call.
- `"ResourceArns"`: The ARN (Amazon Resource Name) of the resource that was attacked. If
  this is left blank, all applicable resources for this account will be included.
- `"StartTime"`: The start of the time period for the attacks. This is a timestamp type.
  The sample request above indicates a number type because the default used by WAF is Unix
  time in seconds. However any valid timestamp format is allowed.
"""
function list_attacks(; aws_config::AbstractAWSConfig=global_aws_config())
    return shield("ListAttacks"; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET)
end
function list_attacks(
    params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return shield(
        "ListAttacks", params; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end

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

Retrieves the ProtectionGroup objects for the account.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"MaxResults"`: The maximum number of ProtectionGroup objects to return. If you leave
  this blank, Shield Advanced returns the first 20 results. This is a maximum value. Shield
  Advanced might return the results in smaller batches. That is, the number of objects
  returned could be less than MaxResults, even if there are still more objects yet to return.
  If there are more objects to return, Shield Advanced returns a value in NextToken that you
  can use in your next request, to get the next batch of objects.
- `"NextToken"`: The next token value from a previous call to ListProtectionGroups. Pass
  null if this is the first call.
"""
function list_protection_groups(; aws_config::AbstractAWSConfig=global_aws_config())
    return shield(
        "ListProtectionGroups"; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end
function list_protection_groups(
    params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return shield(
        "ListProtectionGroups",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Lists all Protection objects for the account.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"MaxResults"`: The maximum number of Protection objects to return. If you leave this
  blank, Shield Advanced returns the first 20 results. This is a maximum value. Shield
  Advanced might return the results in smaller batches. That is, the number of objects
  returned could be less than MaxResults, even if there are still more objects yet to return.
  If there are more objects to return, Shield Advanced returns a value in NextToken that you
  can use in your next request, to get the next batch of objects.
- `"NextToken"`: The ListProtectionsRequest.NextToken value from a previous call to
  ListProtections. Pass null if this is the first call.
"""
function list_protections(; aws_config::AbstractAWSConfig=global_aws_config())
    return shield("ListProtections"; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET)
end
function list_protections(
    params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return shield(
        "ListProtections", params; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end

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

Retrieves the resources that are included in the protection group.

# Arguments
- `protection_group_id`: The name of the protection group. You use this to identify the
  protection group in lists and to manage the protection group, for example to update,
  delete, or describe it.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"MaxResults"`: The maximum number of resource ARN objects to return. If you leave this
  blank, Shield Advanced returns the first 20 results. This is a maximum value. Shield
  Advanced might return the results in smaller batches. That is, the number of objects
  returned could be less than MaxResults, even if there are still more objects yet to return.
  If there are more objects to return, Shield Advanced returns a value in NextToken that you
  can use in your next request, to get the next batch of objects.
- `"NextToken"`: The next token value from a previous call to
  ListResourcesInProtectionGroup. Pass null if this is the first call.
"""
function list_resources_in_protection_group(
    ProtectionGroupId; aws_config::AbstractAWSConfig=global_aws_config()
)
    return shield(
        "ListResourcesInProtectionGroup",
        Dict{String,Any}("ProtectionGroupId" => ProtectionGroupId);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function list_resources_in_protection_group(
    ProtectionGroupId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return shield(
        "ListResourcesInProtectionGroup",
        Dict{String,Any}(
            mergewith(
                _merge, Dict{String,Any}("ProtectionGroupId" => ProtectionGroupId), params
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Gets information about Amazon Web Services tags for a specified Amazon Resource Name (ARN)
in Shield.

# Arguments
- `resource_arn`: The Amazon Resource Name (ARN) of the resource to get tags for.

"""
function list_tags_for_resource(
    ResourceARN; aws_config::AbstractAWSConfig=global_aws_config()
)
    return shield(
        "ListTagsForResource",
        Dict{String,Any}("ResourceARN" => ResourceARN);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function list_tags_for_resource(
    ResourceARN,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return shield(
        "ListTagsForResource",
        Dict{String,Any}(
            mergewith(_merge, Dict{String,Any}("ResourceARN" => ResourceARN), params)
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    tag_resource(resource_arn, tags)
    tag_resource(resource_arn, tags, params::Dict{String,<:Any})

Adds or updates tags for a resource in Shield.

# Arguments
- `resource_arn`: The Amazon Resource Name (ARN) of the resource that you want to add or
  update tags for.
- `tags`: The tags that you want to modify or add to the resource.

"""
function tag_resource(ResourceARN, Tags; aws_config::AbstractAWSConfig=global_aws_config())
    return shield(
        "TagResource",
        Dict{String,Any}("ResourceARN" => ResourceARN, "Tags" => Tags);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function tag_resource(
    ResourceARN,
    Tags,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return shield(
        "TagResource",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}("ResourceARN" => ResourceARN, "Tags" => Tags),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    untag_resource(resource_arn, tag_keys)
    untag_resource(resource_arn, tag_keys, params::Dict{String,<:Any})

Removes tags from a resource in Shield.

# Arguments
- `resource_arn`: The Amazon Resource Name (ARN) of the resource that you want to remove
  tags from.
- `tag_keys`: The tag key for each tag that you want to remove from the resource.

"""
function untag_resource(
    ResourceARN, TagKeys; aws_config::AbstractAWSConfig=global_aws_config()
)
    return shield(
        "UntagResource",
        Dict{String,Any}("ResourceARN" => ResourceARN, "TagKeys" => TagKeys);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function untag_resource(
    ResourceARN,
    TagKeys,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return shield(
        "UntagResource",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}("ResourceARN" => ResourceARN, "TagKeys" => TagKeys),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Updates the details of the list of email addresses and phone numbers that the Shield
Response Team (SRT) can use to contact you if you have proactive engagement enabled, for
escalations to the SRT and to initiate proactive customer support.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"EmergencyContactList"`: A list of email addresses and phone numbers that the Shield
  Response Team (SRT) can use to contact you if you have proactive engagement enabled, for
  escalations to the SRT and to initiate proactive customer support. If you have proactive
  engagement enabled, the contact list must include at least one phone number.
"""
function update_emergency_contact_settings(;
    aws_config::AbstractAWSConfig=global_aws_config()
)
    return shield(
        "UpdateEmergencyContactSettings";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function update_emergency_contact_settings(
    params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return shield(
        "UpdateEmergencyContactSettings",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    update_protection_group(aggregation, pattern, protection_group_id)
    update_protection_group(aggregation, pattern, protection_group_id, params::Dict{String,<:Any})

Updates an existing protection group. A protection group is a grouping of protected
resources so they can be handled as a collective. This resource grouping improves the
accuracy of detection and reduces false positives.

# Arguments
- `aggregation`: Defines how Shield combines resource data for the group in order to
  detect, mitigate, and report events.   Sum - Use the total traffic across the group. This
  is a good choice for most cases. Examples include Elastic IP addresses for EC2 instances
  that scale manually or automatically.   Mean - Use the average of the traffic across the
  group. This is a good choice for resources that share traffic uniformly. Examples include
  accelerators and load balancers.   Max - Use the highest traffic from each resource. This
  is useful for resources that don't share traffic and for resources that share that traffic
  in a non-uniform way. Examples include Amazon CloudFront distributions and origin resources
  for CloudFront distributions.
- `pattern`: The criteria to use to choose the protected resources for inclusion in the
  group. You can include all resources that have protections, provide a list of resource
  Amazon Resource Names (ARNs), or include all resources of a specified resource type.
- `protection_group_id`: The name of the protection group. You use this to identify the
  protection group in lists and to manage the protection group, for example to update,
  delete, or describe it.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"Members"`: The Amazon Resource Names (ARNs) of the resources to include in the
  protection group. You must set this when you set Pattern to ARBITRARY and you must not set
  it for any other Pattern setting.
- `"ResourceType"`: The resource type to include in the protection group. All protected
  resources of this type are included in the protection group. You must set this when you set
  Pattern to BY_RESOURCE_TYPE and you must not set it for any other Pattern setting.
"""
function update_protection_group(
    Aggregation,
    Pattern,
    ProtectionGroupId;
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return shield(
        "UpdateProtectionGroup",
        Dict{String,Any}(
            "Aggregation" => Aggregation,
            "Pattern" => Pattern,
            "ProtectionGroupId" => ProtectionGroupId,
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function update_protection_group(
    Aggregation,
    Pattern,
    ProtectionGroupId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return shield(
        "UpdateProtectionGroup",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}(
                    "Aggregation" => Aggregation,
                    "Pattern" => Pattern,
                    "ProtectionGroupId" => ProtectionGroupId,
                ),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Updates the details of an existing subscription. Only enter values for parameters you want
to change. Empty parameters are not updated.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"AutoRenew"`: When you initally create a subscription, AutoRenew is set to ENABLED. If
  ENABLED, the subscription will be automatically renewed at the end of the existing
  subscription period. You can change this by submitting an UpdateSubscription request. If
  the UpdateSubscription request does not included a value for AutoRenew, the existing value
  for AutoRenew remains unchanged.
"""
function update_subscription(; aws_config::AbstractAWSConfig=global_aws_config())
    return shield(
        "UpdateSubscription"; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end
function update_subscription(
    params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return shield(
        "UpdateSubscription", params; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end