text:
module DBAPIBase

export cursor,
    execute!,
    executemany!,
    commit,
    rollback,
    rows,
    columns,
    connection,
    fetchintoarray!,
    fetchintorows!,
    fetchintocolumns!,
    DatabaseFetcher,
    interface,
    DatabaseInterface,
    DatabaseError,
    DatabaseConnection,
    DatabaseCursor,
    FixedLengthDatabaseCursor,
    DatabaseQuery,
    ParameterQuery,
    MultiparameterQuery,
    SimpleStringQuery,
    StringParameterQuery,
    StringMultiparameterQuery,
    DatabaseQueryError

import Base: connect, close, getindex, isopen, show, start, next, done, length, isempty

import Iterators: imap

abstract DatabaseInterface
abstract DatabaseError{T<:DatabaseInterface} <: Exception
abstract DatabaseConnection{T<:DatabaseInterface}
abstract DatabaseCursor{T<:DatabaseInterface}
abstract FixedLengthDatabaseCursor{T} <: DatabaseCursor{T}
Base.linearindexing(::Type{FixedLengthDatabaseCursor}) = Base.LinearSlow()
Base.ndims(cursor::FixedLengthDatabaseCursor) = 2

"A database query."
abstract DatabaseQuery

"A database query which includes a set of parameters."
abstract ParameterQuery <: DatabaseQuery

"A database query which includes a set of parameter sets."
abstract MultiparameterQuery <: ParameterQuery

"A database query stored in a string."
immutable SimpleStringQuery{T<:AbstractString} <: DatabaseQuery
    query::T
end

"A string database query accompanied by a set of parameters."
immutable StringParameterQuery{T<:AbstractString, S} <: ParameterQuery
    query::T
    params::S
end

"A string database query accompanied by a set of parameter sets."
immutable StringMultiparameterQuery{T<:AbstractString, S} <: MultiparameterQuery
    query::T
    param_list::S
end

typealias StringQuery Union{
    SimpleStringQuery, StringParameterQuery, StringMultiparameterQuery
}

function show(io::IO, connection::DatabaseConnection)
    print(io, typeof(connection), "(closed=$(!isopen(connection)))")
end

function show(io::IO, cursor::DatabaseCursor)
    print(io, typeof(cursor), "(", connection(cursor), ")")
end

"""
Returns the interface type for any database object.
"""
function interface{T<:DatabaseInterface}(
    database_object::Union{DatabaseCursor{T}, DatabaseConnection{T}, DatabaseError{T}}
)
    return T
end

"""
If this error is thrown, a driver has not implemented a required function
of this interface.
"""
type NotImplementedError{T<:DatabaseInterface} <: DatabaseError{T} end
function Base.showerror{T<:DatabaseInterface}(io::IO, e::NotImplementedError{T})
    print(io, T, " does not implement this required DBAPI feature")
end

"""
If this error is thrown, a user has attempted to use an optional function
of this interface which the driver does not implement.
"""
type NotSupportedError{T<:DatabaseInterface} <: DatabaseError{T} end
function Base.showerror{T<:DatabaseInterface}(io::IO, e::NotSupportedError{T})
    print(io, T, " does not support this optional DBAPI feature")
end

"""
If this error is thrown, an error occured while processing this database query.
"""
type DatabaseQueryError{T<:DatabaseInterface, S<:DatabaseQuery} <: DatabaseError{T}
    interface::Type{T}
    query::S
end

function Base.showerror{T<:DatabaseInterface}(io::IO, e::DatabaseQueryError{T})
    print(io, "An error occured while processing this query:\n", e.query)
end

"""
Constructs a database connection.

Returns `connection::DatabaseConnection`.
"""
function connect{T<:DatabaseInterface}(::Type{T}, args...; kwargs...)
    throw(NotImplementedError{T}())
end

"""
Close the connection now (rather than when the finalizer is called).

Any further attempted operations on the connection or its cursors will throw a
subtype of DatabaseError.

Closing a connection without committing will cause an implicit rollback to be
performed.

Returns `nothing`.
"""
function close{T<:DatabaseInterface}(conn::DatabaseConnection{T})
    throw(NotImplementedError{T}())
end

"""
Returns true if the connection is open and not broken.

Returns `Bool`
"""
function isopen{T<:DatabaseInterface}(conn::DatabaseConnection{T})
    throw(NotImplementedError{T}())
end

"""
Commit any pending transaction to the database.

Dataase drivers that do not support transactions should implement this
function with no body.

Returns `nothing`.
"""
function commit{T<:DatabaseInterface}(conn::DatabaseConnection{T})
    throw(NotImplementedError{T}())
end

"""
Roll back to the start of any pending transaction.

Database drivers that do not support transactions may not implement this
function.

Returns `nothing`.
"""
function rollback{T<:DatabaseInterface}(conn::DatabaseConnection{T})
    throw(NotSupportedError{T}())
end

"""
Constructs a database connection, runs `func` on that connection, and ensures the
connection is closed after `func` completes or errors.

Returns the result of calling `func`.
"""
function connect{T<:DatabaseInterface}(func::Function, ::Type{T}, args...; kwargs...)
    conn = connect(T, args...; kwargs...)

    try
        return func(conn)
    finally
        try
            close(conn)
        catch e
            warn(e)
        end
    end
end

"""
Create a new database cursor.

If the database does not implement cursors, the driver must implement a cursor
object which emulates cursors to the extent required by the interface.

Some drivers may implement multiple cursor types, but all must follow the
`DatabaseCursor` interface. Additional arguments may be given to the
driver's implementation of `cursor` but this method must be implemented with
reasonable defaults.

Returns `DatabaseCursor{T}`.
"""
function cursor{T<:DatabaseInterface}(conn::DatabaseConnection{T})
    throw(NotImplementedError{T}())
end

"""
Return the corresponding connection for a given cursor.

Returns `DatabaseConnection{T}`.
"""
function connection{T<:DatabaseInterface}(cursor::DatabaseCursor{T})
    throw(NotImplementedError{T}())
end

"""
Run a query on a database.

The results of the query are not returned by this function but are accessible
through the cursor.

`query` can be any subtype of `DatabaseQuery`. There are some query types
designed to work for many databases (e.g. `SimpleStringQuery`,
`StringParameterQuery`, `StringMultiparameterQuery`) and it is suggested that
drivers which support queries in the form of strings implement this method
for those query types. However, it is only required that some query type be
supported.

Returns `nothing`.
"""
function execute!{T<:DatabaseInterface}(
    cursor::DatabaseCursor{T},
    query::DatabaseQuery
)
    throw(NotImplementedError{T}())
end

function execute!{T<:DatabaseInterface}(
    cursor::DatabaseCursor{T},
    query::SimpleStringQuery
)
    throw(NotSupportedError{T}())
end

function execute!{T<:DatabaseInterface}(
    cursor::DatabaseCursor{T},
    query::StringParameterQuery
)
    throw(NotSupportedError{T}())
end

function execute!{T<:DatabaseInterface}(
    cursor::DatabaseCursor{T},
    query::AbstractString
)
    execute!(cursor, SimpleStringQuery(query))
end

function execute!{T<:DatabaseInterface}(
    cursor::DatabaseCursor{T},
    query::AbstractString,
    params
)
    execute!(cursor, StringParameterQuery(query, params))
end

"""
Run a query on a database multiple times with different parameters.

The results of the queries are not returned by this function. The result of
the final query run is accessible by the cursor.

`query` can be any subtype of `MultiparameterQuery`. A `MultiparameterQuery`
typically contains an iterable of iterables of parameters and causes a query to
be executed on each parameter set.

Returns `nothing`.
"""
function execute!{T<:DatabaseInterface}(
    cursor::DatabaseCursor{T},
    query::MultiparameterQuery
)
    throw(NotSupportedError{T}())
end

"""
Create a row iterator.

This method should return an instance of an iterator type which returns one row
on each iteration. Each row should be returned as a Tuple{...} with as much
type information in the Tuple{...} as possible. It is encouraged but not
necessary to have the rows be of the same type.
"""
function rows{T<:DatabaseInterface}(cursor::DatabaseCursor{T})
    throw(NotImplementedError{T}())
end

"""
Create a column iterator.

This method should return an instance of an iterator type which returns one
column on each iteration. Each column should be returned as a Vector{...} with
as much type information in the Vector{...} as possible.

This method is optional if rows can have different lengths or sets of values.
"""
function columns{T<:DatabaseInterface}(cursor::DatabaseCursor{T})
    throw(NotSupportedError{T}())
end

"""
Get result value from a database cursor.

This method gets a single result value in row `i` in column `j`.

This method is optional if rows or columns do not have a defined order.
"""
function getindex{T<:DatabaseInterface}(cursor::FixedLengthDatabaseCursor{T}, i::Integer, j::Integer)
    throw(NotImplementedError{T}())
end

"""
Get result value from a database cursor.

This method gets a single result value in row `i` in column named `col`.

This method is optional if rows do not have a defined order or if columns do
not have names.
"""
function getindex{T<:DatabaseInterface}(cursor::FixedLengthDatabaseCursor{T}, i::Integer, col::Symbol)
    throw(NotImplementedError{T}())
end

"""
Get result value from a database cursor.

This method gets a single result value in row named `row` in column `j`.

This method is optional if rows do not have names/keys or if columns do not
have a defined order.
"""
function getindex{T<:DatabaseInterface}(cursor::FixedLengthDatabaseCursor{T}, row::Symbol, j::Integer)
    throw(NotImplementedError{T}())
end

"""
Get result value from a database cursor.

This method gets a single result value in row named `row` in column named `col`.

This method is optional if rows do not have names/keys or if columns do not
have names.
"""
function getindex{T<:DatabaseInterface}(cursor::FixedLengthDatabaseCursor{T}, row::Symbol, col::Symbol)
    throw(NotImplementedError{T}())
end

"""
Get result value from a database cursor.

This method gets a single result value in row indexed by `row` in column
indexed by `col`.

Any other row or column index types are optional.
"""
function getindex{T<:DatabaseInterface}(cursor::FixedLengthDatabaseCursor{T}, row::Any, col::Any)
    throw(NotImplementedError{T}())
end

"""
Get result value from a database cursor.

Indexing is not required for types which don't subtype
FixedLengthDatabaseCursor.
"""
function getindex{T<:DatabaseInterface}(cursor::DatabaseCursor{T}, row::Any, col::Any)
    throw(NotSupportedError{T}())
end

"""
Get the number of rows available from a database cursor.

Returns `Int`
"""
function length{T<:DatabaseInterface}(cursor::FixedLengthDatabaseCursor{T})
    throw(NotImplementedError{T}())
end

"""
Get the number of rows available from a database cursor.

`length` is not required for types which don't subtype
FixedLengthDatabaseCursor.
"""
function length{T<:DatabaseInterface}(cursor::DatabaseCursor{T})
    throw(NotSupportedError{T}())
end

"""
A terrible hack to make the fetchintoarray! signature work.

See https://github.com/JuliaLang/julia/issues/13156#issuecomment-140618981
"""
typealias AssociativeVK{V, K} Associative{K, V}

index_return_type(a::Associative) = valtype(a)
index_return_type(a::Any) = eltype(a)

each_index_tuple(a::Associative) = eachindex(a)
each_index_tuple(a::Any) = imap(ind -> ind2sub(a, ind), eachindex(a))

"""
Get results from a database cursor and store them in a preallocated
two-dimensional data structure.

This out-of-the-box method supports a huge variety of data structures under the
`AbstractArray` and `Associative` supertypes. It uses the `getindex` functions
defined above.

When 2d indexing is used on an `Associative`, the result is usually tuple keys.

Returns the preallocated data structure.
"""
function fetchintoarray!{T<:DatabaseInterface}(
        preallocated::Union{AbstractArray, Associative},
        cursor::FixedLengthDatabaseCursor{T},
        offset::Int=0,
    )

    offset_row = offset

    for (row, column) in each_index_tuple(preallocated)
        offset_row = row + offset
        datum = cursor[offset_row, column]
        preallocated[row, column] = (
            isa(datum, Nullable) &&
            !(index_return_type(preallocated) <: Nullable ||
                Nullable <: index_return_type(preallocated)) ?
            get(datum) :
            datum
        )
    end

    return preallocated, offset_row
end

"""
Get results from a database cursor and store them in a preallocated vector.

This out-of-the-box method supports a huge variety of data structures under the
`AbstractVector` supertype. It uses the `getindex` functions defined above.

Returns the preallocated vector.
"""
function fetchintoarray!{T<:DatabaseInterface}(
        preallocated::AbstractVector,
        cursor::FixedLengthDatabaseCursor{T},
        offset::Int=0,
    )

    offset_row = offset

    for row in eachindex(preallocated)
        offset_row = row + offset
        datum = cursor[offset_row, 1]
        preallocated[row] = (
            isa(datum, Nullable) &&
            !(index_return_type(preallocated) <: Nullable ||
                Nullable <: index_return_type(preallocated)) ?
            get(datum) :
            datum
        )
    end

    return preallocated, offset_row
end

"""
Get results from a database cursor and store them in a preallocated data
structure (a collection of rows).

This out-of-the-box method supports a huge variety of data structures under the
`AbstractArray` and `Associative` supertypes. It uses the `getindex` functions
defined above.

Returns the preallocated data structure.
"""
function fetchintorows!{T<:DatabaseInterface, U<:Union{AbstractArray, Associative}}(
        preallocated::Union{AbstractArray{U}, AssociativeVK{U}},
        cursor::FixedLengthDatabaseCursor{T},
        offset::Int=0,
    )

    offset_row = offset

    for row in eachindex(preallocated)
        offset_row = row + offset

        for column in eachindex(preallocated[row])
            datum = cursor[offset_row, column]
            preallocated[row][column] = (
                isa(datum, Nullable) && !(index_return_type(preallocated[row]) <: Nullable) ?
                get(datum) :
                datum
            )
        end
    end

    return preallocated, offset_row
end

"""
Get results from a database cursor and store them in a preallocated data
structure (a collection of columns).

This out-of-the-box method supports a huge variety of data structures under the
`AbstractArray` and `Associative` supertypes. It uses the `getindex` functions
defined above.

`offset` represents the offset into the cursor denoting where to start fetching
data.

Returns the preallocated data structure.
"""
function fetchintocolumns!{T<:DatabaseInterface, U<:Union{AbstractArray, Associative}}(
        preallocated::Union{AbstractArray{U}, AssociativeVK{U}},
        cursor::FixedLengthDatabaseCursor{T},
        offset::Int=0,
    )

    offset_row = offset

    for column in eachindex(preallocated), row in eachindex(preallocated[column])
        offset_row = row + offset
        datum = cursor[offset_row, column]
        preallocated[column][row] = (
            isa(datum, Nullable) && !(index_return_type(preallocated[row]) <: Nullable) ?
            get(datum) :
            datum
        )
    end

    return preallocated, offset_row
end

function fetchintoarray!{T<:DatabaseInterface}(
        preallocated::Union{AbstractArray, Associative},
        cursor::DatabaseCursor{T},
        offset::Int=0,
    )

    throw(NotSupportedError{T}())
end

function fetchintorows!{T<:DatabaseInterface, U<:Union{AbstractArray, Associative}}(
        preallocated::Union{AbstractArray{U}, AssociativeVK{U}},
        cursor::DatabaseCursor{T},
        offset::Int=0,
    )

    throw(NotSupportedError{T}())
end

function fetchintocolumns!{T<:DatabaseInterface, U<:Union{AbstractArray, Associative}}(
        preallocated::Union{AbstractArray{U}, AssociativeVK{U}},
        cursor::DatabaseCursor{T},
        offset::Int=0,
    )

    throw(NotSupportedError{T}())
end

typealias Orientation Union{Val{:rows}, Val{:columns}, Val{:array}}

immutable DatabaseFetcher{O<:Orientation, T<:DatabaseInterface, U<:Union{AbstractArray, Associative}}
    orientation::O
    preallocated::U
    cursor::FixedLengthDatabaseCursor{T}
end

function DatabaseFetcher{T, U}(
    # this is the actual required type for the `preallocated` field,
    # however there is no way to express that through parameters in the
    # type definition
        orientation::Symbol,
        preallocated::Union{AbstractArray{U}, AssociativeVK{U}},
        cursor::FixedLengthDatabaseCursor{T},
    )

    return DatabaseFetcher(Val{orientation}(), preallocated, cursor)
end

fetch_function(::DatabaseFetcher{Val{:columns}}) = fetchintocolumns!
fetch_function(::DatabaseFetcher{Val{:rows}}) = fetchintorows!
fetch_function(::DatabaseFetcher{Val{:array}}) = fetchintoarray!

first_empty(a::Associative) = isempty(first(values(a)))
first_empty(a) = isempty(first(a))

function isempty{O<:Union{Val{:rows}, Val{:array}}, T<:DatabaseInterface, U<:Union{AbstractArray, Associative}}(
        fetcher::DatabaseFetcher{O, T, U}
    )

    isempty(fetcher.preallocated)
end

function isempty{O<:Val{:columns}, T<:DatabaseInterface, U<:Union{AbstractArray, Associative}}(
        fetcher::DatabaseFetcher{O, T, U}
    )

    isempty(fetcher.preallocated) || first_empty(fetcher.preallocated)
end

start(fetcher::DatabaseFetcher) = (fetcher.preallocated, 0)

function next{O, T<:DatabaseInterface, U}(fetcher::DatabaseFetcher{O, T, U}, state)
    preallocated, offset = state
    preallocated::U, new_offset = fetch_function(fetcher)(preallocated, fetcher.cursor, offset)
    return preallocated, (preallocated, new_offset)
end

function done(fetcher::DatabaseFetcher, state)
    state[2] >= length(fetcher.cursor) || isempty(fetcher)
end

end # module


text:
using BenchmarkTools, Plots, Plots.PlotMeasures, LaTeXStrings
using BenchmarkTools: minimum, median

# ==============================================================================
# Decomposition-based approach for the ISS model
# ==============================================================================
include("iss_BFFPSV18.jl")

# ==============================================================================
# Execute benchmarks and save benchmark results
# ==============================================================================

# tune parameters
tune!(SUITE)

# run the benchmarks
results = run(SUITE, verbose=true)

# return the sample with the smallest time value in each test
println("minimum: ", minimum(results))

# return the median for each test
println("median: ", median(results))

# ==============================================================================
# Create plots
# ==============================================================================

function plot_dashed_lines!(v, time_horizon=20.)
    plot!(x->x, x->v, 0.0, time_horizon, line=2, color="red", linestyle=:dash,
          legend=nothing)
    plot!(x->x, x->-v, 0.0, time_horizon, line=2, color="red", linestyle=:dash,
          legend=nothing)
end

projection_matrix = C[:, 136:270]

𝑂_ISS01[:mode] = "reach"
𝑂_ISS01[:projection_matrix] = projection_matrix
opC = BFFPSV18(merge(𝑂_dense_ISS01, Options(:block_options_iter => nothing)))
res = solve(iss_TV, 𝑂_ISS01, op=opC)
res = ReachSolution(
    [ReachSet(Interval(rs.t_start, rs.t_end) × rs.X, rs.t_start, rs.t_end)
        for rs in res.Xk],
    Options(:plot_vars => [0, 1]))
plot(res,
     tickfont=font(30, "Times"), guidefontsize=45,
     xlab=L"t\raisebox{-0.5mm}{\textcolor{white}{.}}",
     ylab=L"y_3\raisebox{2mm}{\textcolor{white}{.}}",
     xtick=[0., 10., 20.], ytick=[-7e-4, -5e-4, 0., 5e-4, 7e-4],
     xlims=(0., 20.), ylims=(-8e-4, 8e-4),
     title=L"\textcolor{white}{.}\cdot 10^{-4}", title_location=:left,
     titlefont=font(35, "Times"),
     yformatter = yi -> yi == 0 ? "0" : "$(Int(yi*1e4))",
     bottom_margin=6mm, left_margin=2mm, right_margin=2mm,
     size=(1000, 1000))
plot_dashed_lines!(0.0005)
plot_dashed_lines!(0.0007)
savefig(@relpath "ISSF01.png")

𝑂_ISS02[:mode] = "reach"
𝑂_ISS02[:projection_matrix] = projection_matrix
opC = BFFPSV18(merge(𝑂_dense_ISS02, Options(:block_options_iter => nothing)))
res = solve(iss_CONST, 𝑂_ISS02, op=opC)
res = ReachSolution(
    [ReachSet(Interval(rs.t_start, rs.t_end) × rs.X, rs.t_start, rs.t_end)
        for rs in res.Xk],
    Options(:plot_vars => [0, 1]))
plot(res,
     tickfont=font(30, "Times"), guidefontsize=45,
     xlab=L"t\raisebox{-0.5mm}{\textcolor{white}{.}}",
     ylab=L"y_3\raisebox{2mm}{\textcolor{white}{.}}",
     xtick=[0., 10., 20.], ytick=[-17e-5, 0., 17e-5],
     xlims=(0., 20.), ylims=(-2e-4, 2e-4),
     title=L"\textcolor{white}{.}\cdot 10^{-4}", title_location=:left,
     titlefont=font(35, "Times"),
     yformatter = yi -> yi == 0 ? "0" : "$(round(yi*1e4, digits=1))",
     bottom_margin=6mm, left_margin=2mm, right_margin=2mm,
     size=(1000, 1000))
plot_dashed_lines!(0.00017)
savefig(@relpath "ISSC01.png")

ext: jl

max_stars_repo_name: Aadesha/ReachabilityBenchmarks

max_issues_repo_name: Aadesha/ReachabilityBenchmarks

max_forks_repo_name: Aadesha/ReachabilityBenchmarks


text:
# define environment
using LightGraphs
using StatsBase

households = Dict()

environments =
Dict(
     [("Arrival", 1),
     ("Hotel", 2) ,
     ("Home", 3),
     ("MediHotel Extension", 4),
     ("Hotel Extension", 5),
     ("Home Extension", 6),
     ("Isolation", 7),
     ("Community", 8)])
# reverse mapping
environments_2 = Dict(value => key for (key, value) in environments)

# specify possible transitions (this defines the basic pathway structure)
edges = [[("Arrival", "Hotel"), 1],
         [("Arrival", "Home"), 1],
         [("Arrival", "Community"), 0],
         [("Hotel", "Home"), 0],
         [("Hotel", "MediHotel Extension"), 1],
         [("Hotel", "Hotel Extension"), 1],
         [("Hotel", "Isolation"), 1],
         [("Hotel", "Community"), 1],
         [("Community", "Hotel"), 1], # workers can do this, travellers can't TODO enforce this
         [("Home", "MediHotel Extension"), 1],
         [("Home", "Home Extension"), 1],
         [("Home", "Isolation"), 1],
         [("Home", "Community"), 1],
         [("MediHotel Extension", "Isolation"), 1],
         [("MediHotel Extension", "Community"), 1],
         [("Home Extension", "Isolation"), 1],
         [("Home Extension", "Community"), 1],
         [("Hotel Extension", "Isolation"), 1],
         [("Hotel Extension", "Community"), 1],
         [("Isolation", "Community"), 1]]

 # initialise network representation of
 #environment with the right number of nodes
 ne = length(environments);
 g = SimpleDiGraph(ne)

  # add the edges specified in the list above:
 for e in edges
   #println(e[2])
   if e[2] == 1
   add_edge!(g, environments[e[1][1]], environments[e[1][2]])
   end
 end

 # Space is a digraph representing the transitions between different
 #quarantine environemnts
 #(arrival, hotel, home, medi-hotel extension, isolation, community).
 Space = GraphSpace(g)

#specify rosters - these are the work schedules used by worker agents to decide
# which days they're at work and which days they're in the community.
# each roster specifies the number of days worked per week, and a propprtion
# of the workforce working that number of days per week
rosters = Dict([
               (1, [[7; 5; 3; 1],AnalyticWeights([1.0; 0.0; 0.0; 0.0])]),
               (2, [[7; 5; 3; 1],AnalyticWeights([0.0; 1.0; 0.0; 0.0])]),
               (3, [[7; 5; 3; 1],AnalyticWeights([0.0; 0.6; 0.4; 0.0])]),
               (4, [[7; 5; 3; 1],AnalyticWeights([0.0; 0.6; 0.3; 0.1])])
              ])


text:
#######################################################################################################################################################################################################
#
# Changes made to this function
# General
#     2021-Oct-01: rename the function to leaf_spectra! as the function updates not only fluorescence but also reflectance, transmittance, and absorption spectra
#     2021-Oct-22: add another method to prescribe leaf spectra such as transmittance and reflectance from broadband method
#
#######################################################################################################################################################################################################
"""
This function updates leaf level reflectance, transmittance, and fluorescence spectra related parameters. Supported methods are

$(METHODLIST)

"""
function leaf_spectra! end

#######################################################################################################################################################################################################
#
# Changes made to this method
# General
#     2020-Mar-30: account for carotenoid absorption as APAR as well as chlorophyll
#     2020-Mar-31: use 40° rather than 59° for _τ_α calculation (following PROSPECT-D)
#     2021-Aug-07: replace function `expint` with that from SpecialFunctions
#     2021-Oct-21: add α to input parameters so that one can roll back to 50° for _τ_α calculation
#     2021-Nov-29: separate HyperspectralAbsorption as constant struct
#     2022-Jan-13: use LeafBiophysics directly in the function rather than Leaf
#     2022-Feb-02: fix documentation
# Bug fix
#     2021-Aug-06: If bio.CBC and bio.PRO are not zero, they are accounted for twice in bio.LMA, thus the spectrum from LMA need to subtract the contribution from CBC and PRO
# To do
#     TODO: make brown pigment and absoption curve more general using realistic units
#     TODO: add References for this methods
#     TODO: speed up this function by preallocate memories using a cache structure
#
#######################################################################################################################################################################################################
"""

    leaf_spectra!(bio::LeafBiophysics{FT}, wls::WaveLengthSet{FT}, lha::HyperspectralAbsorption{FT}; APAR_car::Bool = true, α::FT=FT(40)) where {FT<:AbstractFloat}

Update leaf reflectance and transmittance spectra, and fluorescence spectrum matrices, given
- `bio` `ClimaCache.LeafBiophysics` type struct that contains leaf biophysical parameters
- `wls` `ClimaCache.WaveLengthSet` type struct that contain wave length bins
- `lha` `ClimaCache.HyperspectralAbsorption` type struct that contains absorption characteristic curves
- `APAR_car` If true, carotenoid absorption is accounted for in APAR, default is `true`
- `α` Optimum angle of incidence (default is 40° as in PROSPECT-D, SCOPE uses 59°)

# Examples
```julia
wls = WaveLengthSet{Float64}();
bio = LeafBiophysics{Float64}(wls);
lha = HyperspectralAbsorption{Float64}(wls);
leaf_spectra!(bio, wls, lha);
leaf_spectra!(bio, wls, lha; APAR_car=false);
leaf_spectra!(bio, wls, lha; APAR_car=false, α=59.0);
```
"""
leaf_spectra!(bio::LeafBiophysics{FT}, wls::WaveLengthSet{FT}, lha::HyperspectralAbsorption{FT}; APAR_car::Bool = true, α::FT=FT(40)) where {FT<:AbstractFloat} = (
    @unpack MESOPHYLL_N, NDUB = bio;
    @unpack K_ANT, K_BROWN, K_CAB, K_CAR_V, K_CAR_Z, K_CBC, K_H₂O, K_LMA, K_PRO, K_PS, NR = lha;
    @unpack IΛ_SIF, IΛ_SIFE, Λ_SIF, Λ_SIFE = wls;

    # calculate the average absorption feature and relative Cab and Car partitions
    bio.k_all    .= (K_CAB   .* bio.cab .+                      # chlorophyll absorption
                     K_CAR_V .* bio.car .* (1 - bio.f_zeax) .+  # violaxanthin carotenoid absorption
                     K_CAR_Z .* bio.car .* bio.f_zeax .+        # zeaxanthin carotenoid absorption
                     K_ANT   .* bio.ant .+                      # anthocynanin absorption absorption
                     K_BROWN .* bio.brown .+                    # TODO: needs to be a concentration
                     K_H₂O   .* bio.l_H₂O .+                    # water absorption
                     K_CBC   .* bio.cbc .+                      # carbon-based constituents absorption
                     K_PRO   .* bio.pro .+                      # protein absorption
                     K_LMA   .* (bio.lma - bio.cbc - bio.pro)   # dry mass absorption (if some remained)
                    ) ./ MESOPHYLL_N;
    bio.α_cab    .= (K_CAB .* bio.cab) ./ bio.k_all ./ MESOPHYLL_N;
    bio.α_cabcar .= (K_CAB .* bio.cab .+ K_CAR_V .* bio.car .* (1 - bio.f_zeax) .+ K_CAR_Z .* bio.car .* bio.f_zeax) ./ bio.k_all ./ MESOPHYLL_N;

    # calculate the reflectance and transmittance at the interfaces of one layer
    _τ   = (1 .- bio.k_all) .* exp.(-bio.k_all) .+ bio.k_all .^ 2 .* expint.(bio.k_all .+ eps(FT));
    _τ_α = average_transmittance.(α, NR);
    _ρ_α = 1 .- _τ_α;
    _τ₁₂ = average_transmittance.(FT(90), NR);
    _ρ₁₂ = 1 .- _τ₁₂;
    _τ₂₁ = _τ₁₂ ./ (NR .^ 2);
    _ρ₂₁ = 1 .- _τ₂₁;

    # top surface side
    _denom = 1 .- (_τ .* _ρ₂₁) .^ 2;
    _τ_top = _τ_α .* _τ .* _τ₂₁ ./ _denom;
    _ρ_top = _ρ_α .+ _ρ₂₁ .* _τ .* _τ_top;

    # bottom surface side
    _τ_bottom = _τ₁₂ .* _τ .* _τ₂₁ ./ _denom;
    _ρ_bottom = _ρ₁₂ .+ _ρ₂₁ .* _τ .* _τ_bottom;

    # calculate the reflectance and transmittance at the interfaces of N layer
    _d     = sqrt.((1 .+ _ρ_bottom .+ _τ_bottom) .* (1 .+ _ρ_bottom .- _τ_bottom) .* (1 .- _ρ_bottom .+ _τ_bottom) .* (1 .- _ρ_bottom .- _τ_bottom));
    _ρ²    = _ρ_bottom .^ 2;
    _τ²    = _τ_bottom .^ 2;
    _a     = (1 .+ _ρ² .- _τ² .+ _d) ./ (2 .* _ρ_bottom);
    _b     = (1 .- _ρ² .+ _τ² .+ _d) ./ (2 .* _τ_bottom);
    _bⁿ⁻¹  = _b .^ (MESOPHYLL_N - 1);
    _b²ⁿ⁻² = _bⁿ⁻¹ .^ 2;
    _a²    = _a .^ 2;
    _denom = _a² .* _b²ⁿ⁻² .- 1;
    _ρ_sub = _a .* (_b²ⁿ⁻² .- 1) ./ _denom;
    _τ_sub = _bⁿ⁻¹ .* (_a² .- 1) ./ _denom;

    # avoid case of zero absorption
    _j = findall(_ρ_bottom .+ _τ_bottom .>= 1);
    _τ_sub[_j] = _τ_bottom[_j] ./ (_τ_bottom[_j] + (1 .- _τ_bottom[_j]) * (MESOPHYLL_N - 1));
    _ρ_sub[_j] = 1 .- _τ_sub[_j];

    # reflectance & transmittance of the leaf: combine top layer with next N-1 layers
    _denom   = 1 .- _ρ_sub .* _ρ_bottom;
    bio.τ_SW = _τ_top .* _τ_sub ./ _denom;
    bio.ρ_SW = _ρ_top .+ _τ_top .* _ρ_sub .* _τ_bottom ./ _denom;
    bio.α_SW = 1 .- bio.τ_SW .- bio.ρ_SW;

    # Doubling method used to calculate fluoresence is now only applied to the part of the leaf where absorption takes place, that is, the part exclusive of the leaf-air interfaces.
    # The reflectance (rho) and transmittance (tau) of this part of the leaf are now determined by "subtracting" the interfaces.
    # CF Note: All of the below takes about 10 times more time than the RT above. Need to rething speed and accuracy. (10nm is bringing it down a lot!)
    _ρ_b = (bio.ρ_SW .- _ρ_α) ./ (_τ_α .* _τ₂₁ .+ (bio.ρ_SW - _ρ_α) .* _ρ₂₁);
    _tt1 = _τ_α .* _τ₂₁;
    _tt2 = bio.τ_SW .* (1 .- _ρ_b .* _ρ₂₁);
    _z   = _tt2 ./ _tt1;
    _tt1 = _ρ_b - _ρ₂₁ .* _z .^ 2;
    _tt2 = 1 .- (_ρ₂₁.* _z) .^ 2;
    _ρ   = max.(0, _tt1 ./ _tt2);
    _tt1 = 1 .- _ρ_b .* _ρ₂₁;
    _τ   = _tt1 ./ _tt2 .* _z;

    # Derive Kubelka-Munk s and k
    _i      = findall((_ρ .+ _τ) .< 1);
    _j      = findall((_ρ .+ _τ) .> 1);
    _d[_i] .= sqrt.((1 .+ _ρ[_i] .+ _τ[_i]) .* (1 .+ _ρ[_i] .- _τ[_i]) .* (1 .- _ρ[_i] .+ _τ[_i]) .*  (1 .- _ρ[_i] .- _τ[_i]));
    _a[_i] .= (1 .+ _ρ[_i] .^ 2 .- _τ[_i] .^ 2 .+ _d[_i]) ./ (2 .* _ρ[_i]);
    _b[_i] .= (1 .- _ρ[_i] .^ 2 .+ _τ[_i] .^ 2 .+ _d[_i]) ./ (2 .* _τ[_i]);
    _a[_j] .= 1;
    _b[_j] .= 1;

    _i      = findall((_a .> 1) .& (_a .!= Inf));
    _s      = _ρ ./ _τ;
    _k      = log.(_b);
    _s[_i] .= 2 .* _a[_i] ./ (_a[_i] .^ 2 .- 1) .* log.(_b[_i]);
    _k[_i] .= (_a[_i] .- 1) ./ (_a[_i] .+ 1) .* log.(_b[_i]);
    _k_chl  = (APAR_car ? bio.α_cabcar : bio.α_cab) .* _k;

    # indices of WLE and WLF within wlp
    _ϵ       = FT(2) ^ -NDUB;
    _τ_e     = 1 .- (_k[IΛ_SIFE] .+ _s[IΛ_SIFE]) * _ϵ;
    _τ_f     = 1 .- (_k[IΛ_SIF] .+ _s[IΛ_SIF]) * _ϵ;
    _ρ_e     = _s[IΛ_SIFE] * _ϵ;
    _ρ_f     = _s[IΛ_SIF] * _ϵ;
    _sigmoid = 1 ./ (1 .+ exp.(-Λ_SIF ./ 10) .* exp.(Λ_SIFE' ./ 10));
    _mat_f   = K_PS[IΛ_SIF] .* _ϵ ./ 2 .* _k_chl[IΛ_SIFE]' .* _sigmoid;
    _mat_b   = K_PS[IΛ_SIF] .* _ϵ ./ 2 .* _k_chl[IΛ_SIFE]' .* _sigmoid;

    # Doubling adding routine
    _1_h = ones(FT, 1, length(_τ_e));
    _1_v = ones(FT, length(_τ_f), 1);
    for i in 1:NDUB
        _x_e     = _τ_e ./ (1 .- _ρ_e .^ 2);
        _x_f     = _τ_f ./ (1 .- _ρ_f .^ 2);
        _τ_e_n   = _τ_e .* _x_e;
        _τ_f_n   = _τ_f .* _x_f;
        _ρ_e_n   = _ρ_e .* (1 .+ _τ_e_n);
        _ρ_f_n   = _ρ_f .* (1 .+ _τ_f_n);
        _a₁₁     = _x_f * _1_h .+ _1_v * _x_e';
        _a₁₂     = (_x_f * _x_e') .* (_ρ_f * _1_h .+ _1_v * _ρ_e');
        _a₂₁     = 1 .+ (_x_f * _x_e') .* (1 .+ _ρ_f * _ρ_e');
        _a₂₂     = (_x_f .* _ρ_f) * _1_h .+ _1_v * (_x_e.*_ρ_e)';
        _mat_f_n = _mat_f .* _a₁₁ .+ _mat_b .* _a₁₂;
        _mat_b_n = _mat_b .* _a₂₁ .+ _mat_f .* _a₂₂;
        _τ_e     = _τ_e_n;
        _ρ_e     = _ρ_e_n;
        _τ_f     = _τ_f_n;
        _ρ_f     = _ρ_f_n;
        _mat_f   = _mat_f_n;
        _mat_b   = _mat_b_n;
    end;

    # This reduced red SIF quite a bit in backscatter, not sure why.
    _ρ_b = _ρ .+ _τ .^ 2 .* _ρ₂₁ ./ (1 .- _ρ .* _ρ₂₁);
    _x_e = _1_v * (_τ_α[IΛ_SIFE] ./ (1 .- _ρ₂₁[IΛ_SIFE] .* _ρ_b[IΛ_SIFE]))';
    _x_f = _τ₂₁[IΛ_SIF] ./ (1 .- _ρ₂₁[IΛ_SIF] .* _ρ_b[IΛ_SIF]) * _1_h;
    _y_e = _1_v * (_τ[IΛ_SIFE] .* _ρ₂₁[IΛ_SIFE] ./ (1 .- _ρ[IΛ_SIFE] .* _ρ₂₁[IΛ_SIFE]))';
    _y_f = _τ[IΛ_SIF] .* _ρ₂₁[IΛ_SIF] ./ (1 .- _ρ[IΛ_SIF] .* _ρ₂₁[IΛ_SIF]) * _1_h;
    _a   = _x_e .* (1 .+ _y_e .* _y_f) .* _x_f;
    _b   = _x_e .* (_y_e .+ _y_f) .* _x_f;

    bio.mat_b = _a .* _mat_b + _b .* _mat_f;
    bio.mat_f = _a .* _mat_f + _b .* _mat_b;

    return nothing
);

#######################################################################################################################################################################################################
#
# Changes made to this method
# General
#     2021-Oct-22: add another method to prescribe leaf spectra such as transmittance and reflectance from broadband method
#     2021-Nov-29: separate HyperspectralAbsorption as constant struct
#     2022-Jan-13: use LeafBiophysics directly in the function rather than Leaf
#     2022-Feb-02: fix documentation
#
#######################################################################################################################################################################################################
"""

    leaf_spectra!(bio::LeafBiophysics{FT}, wls::WaveLengthSet{FT}, ρ_par::FT, ρ_nir::FT, τ_par::FT, τ_nir::FT) where {FT<:AbstractFloat}

Update leaf reflectance and transmittance (e.g., prescribe broadband PAR and NIR values), given
- `bio` `ClimaCache.LeafBiophysics` type struct that contains leaf biophysical parameters
- `wls` `ClimaCache.WaveLengthSet` type struct that contain wave length bins
- `ρ_par` Reflectance at PAR region
- `ρ_nir` Reflectance at NIR region
- `τ_par` Transmittance at PAR region
- `τ_nir` Transmittance at NIR region

# Examples
```julia
wls = WaveLengthSet{Float64}();
bio = LeafBiophysics{Float64}(wls);
leaf_spectra!(bio, wls, 0.1, 0.45, 0.05, 0.25);
```
"""
leaf_spectra!(bio::LeafBiophysics{FT}, wls::WaveLengthSet{FT}, ρ_par::FT, ρ_nir::FT, τ_par::FT, τ_nir::FT) where {FT<:AbstractFloat} = (
    @unpack IΛ_NIR, IΛ_PAR = wls;

    bio.ρ_SW[IΛ_PAR] .= ρ_par;
    bio.ρ_SW[IΛ_NIR] .= ρ_nir;
    bio.τ_SW[IΛ_PAR] .= τ_par;
    bio.τ_SW[IΛ_NIR] .= τ_nir;

    bio.α_SW = 1 .- bio.τ_SW .- bio.ρ_SW;

    return nothing
);


text:
@testset "smoptimize" begin

    search_range = [(-10.0,10.0), (0.0,2.0), (25.0,40.0)]
    n_samples = 6
    plan = SurrogateModelOptim.scaled_LHC_sampling_plan(search_range,n_samples,1000;trace=:silent)
    samples = mapslices(sum,plan,dims=1)

    #Make sure correct input format is used
    @test_throws ErrorException surrogate_model(permutedims(plan),samples;
    options=SurrogateModelOptim.Options(trace=:silent))
end


text:
using Pkg
function pkg_check(pkgs::Array{String,1})
  for p in pkgs
    if get(Pkg.installed(), p, nothing) == nothing
      Pkg.add(p)
    else
      println(p * " found, not installing.")
    end
  end
  return nothing
end
pkg_check(["Dates", "DSP", "SeisIO", "IJulia"])
using IJulia
import SeisIO: get_svn
get_svn("https://github.com/jpjones76/SeisIO-TestData/trunk/Tutorial", "DATA")
jupyterlab(dir=pwd())


text:
# This file describes how we decide which logger (e.g. LogText vs LogValue vs LogHistograms)
# to use for what data, and any preprocessing

"""
    preprocess(name, val, data)

This method takes a tag `name` and the value `val::T` pair. If type `T` can be
serialized to TensorBoard then the pair is pushed to `data`, otherwise it should
call `preprocess` recursively with some simpler types, until a serializable
type is finally hit.

For a struct, it calls preprocess on every field.
"""
function preprocess(name, val::T, data) where T
    if isstructtype(T)
        fn = logable_propertynames(val)
        for f=fn
            prop = getfield(val, f)
            preprocess(name*"/$f", prop, data)
        end
    else
        # If we do not know how to serialize a type, then
        # it will be simply logged as text
        push!(data, name=>val)
    end
    data
end

"""
    logable_propertynames(val::Any)

Returns a tuple with the name of the fields of the structure `val` that
should be logged to TensorBoard. This function should be overridden when
you want TensorBoard to ignore some fields in a structure when logging
it. The default behaviour is to return the  same result as `propertynames`.

See also: [`Base.propertynames`](@ref)
"""
logable_propertynames(val::Any) = propertynames(val)

## Default unpacking of key-value dictionaries
function preprocess(name, dict::AbstractDict, data)
    for (key, val) in dict
        # convert any key into a string, via interpolating it
        preprocess("$name/$key", val, data)
    end
    return data
end

## Default behaviours

########## For things going to LogImage ##############################
function preprocess(name,   img::AbstractArray{<:Colorant}, data)
    # If it has three dimensions (and we don't have 3D monitors) we log several
    # 2D slices under the same tag so that TB shows a slider along the z direction.
    dimensions = ndims(img)
    if dimensions == 3
        #3rd is channel dim as observed in testimages
        channels = size(img, 3)
        for c in 1:channels
            preprocess(name, convert(PngImage, img[:, :, c]), data)
        end
    else
        preprocess(name, convert(PngImage, img), data)
    end
    return data
end
preprocess(name, val::PngImage, data) = push!(data, name=>val)
summary_impl(name, value::PngImage) = image_summary(name, value)

########## For things going to LogText ##############################
preprocess(name, val::AbstractString, data) where T<:String = push!(data, name=>val)
summary_impl(name, value::Any) = text_summary(name, value)

########## For things going to LogHistograms ########################
# Only consider 1D histograms for histogram plotting
preprocess(name, hist::StatsBase.Histogram{T,1}, data) where T = push!(data, name=>hist)
summary_impl(name, hist::StatsBase.Histogram) = histogram_summary(name, hist)

# TODO: maybe deprecate? tuple means histogram (only if bins/weights match)
function preprocess(name,   (bins,weights)::Tuple{AbstractVector,AbstractVector}, data)
    # if ... this is an histogram
    if length(bins) == length(weights)+1
        return preprocess(name, Histogram(bins,weights), data)
    end
    preprocess(name*"/1", bins, data)
    preprocess(name*"/2", weights, data)
end

preprocess(name, val::AbstractArray{<:Real}, data) = push!(data, name=>val)
summary_impl(name, val::AbstractArray{<:Real}) = histogram_arr_summary(name, val)

# Split complex numbers into real/complex pairs
preprocess(name, val::AbstractArray{<:Complex}, data) = push!(data, name*"/re"=>real.(val), name*"/im"=>imag.(val))

########## For things going to LogValue #############################
preprocess(name, val::Real, data) = push!(data, name=>val)
summary_impl(name, value::Real) = scalar_summary(name, value)

# Split complex numbers into real/complex pairs
preprocess(name, val::Complex, data) = push!(data, name*"/re"=>real(val), name*"/im"=>imag(val))


text:
### A Pluto.jl notebook ###
# v0.12.11

using Markdown
using InteractiveUtils

# ╔═╡ 277997be-2b73-11eb-16f7-4f3f7796f140
using Images

# ╔═╡ b767bd16-2b9f-11eb-2172-4b59677353d7
dali_path = download("https://www.moma.org/media/W1siZiIsIjM4NjQ3MCJdLFsicCIsImNvbnZlcnQiLCItcXVhbGl0eSA5MCAtcmVzaXplIDIwMDB4MjAwMFx1MDAzZSJdXQ.jpg?sha=fdc1ab6baa7f5400")

# ╔═╡ fae6ff0c-2b9f-11eb-21a8-978b6f791a0f
dali = load(dali_path)

# ╔═╡ 3425b6f0-2ba0-11eb-0b35-271fc636ae36

# ╔═╡ Cell order:
# ╠═277997be-2b73-11eb-16f7-4f3f7796f140
# ╠═b767bd16-2b9f-11eb-2172-4b59677353d7
# ╠═fae6ff0c-2b9f-11eb-21a8-978b6f791a0f
# ╠═3425b6f0-2ba0-11eb-0b35-271fc636ae36

max_stars_repo_name: zuckberj/comp-thinking-lectures

max_issues_repo_name: zuckberj/comp-thinking-lectures

max_forks_repo_name: zuckberj/comp-thinking-lectures


text:
mutable struct ConsensusInfo
	nucCounts::Matrix{Float64} # counts for A,C,G,T
	nextBaseAligned::Vector{Int} # counts for consecutive bases by covered by the same read
	deletions::Vector{Dict{Int,Int}} # index i means deletion after i (length->reads supporting)
	insertions::Vector{Dict{String,Int}} # index i means insertion after i (string->reads supporting)
end

function ConsensusInfo(refLength::Int)
	nucCounts = zeros(refLength,4)
	nextBaseAligned = zeros(Int,refLength)
	deletions = [Dict{Int,Int}() for i=1:refLength] # to make sure each element is initalized
	insertions = [Dict{String,Int}() for i=1:refLength] # to make sure each element is initalized
	ConsensusInfo(nucCounts,nextBaseAligned,deletions,insertions)
end

function aggregate_reads(bamFile; mappingQualityThreshold::Int=30, ignoreChimeric::Bool=true)
	refSeqs = sequences(bamFile)
	ci = ConsensusInfo[ConsensusInfo(r[2]) for r in refSeqs]

	for (i,r) in enumerate(bamFile)
	# for r in bamFile
		if flagset(r,BAM_FUNMAP) || flagset(r,BAM_FSECONDARY) || flagset(r,BAM_FDUP)
			continue
		end

		mapq(r)<mappingQualityThreshold && continue
		ignoreChimeric && hastag(r,tag"SA") && continue

		ref = refID(r)+1
		s = seq(r)

		for c in r
			o = op(c)

			# TODO: Handle CIGAR X and =.
			# 	    NB: nextBaseAligned computation gets more messy
			(o==CIGAR_Eq || o==CIGAR_X) && error("CIGAR X and = are currently not suported")

			if o==CIGAR_M
				# aligned chunk

				readOffs = readpos(c)-1
				refOffs  = refpos(c)-1

				j = 1
				while true
					nuc = base_index(s[readOffs+j])
					1<=nuc<=4 && (ci[ref].nucCounts[refOffs+j,nuc] += 1)
					j>=len(c) && break
					ci[ref].nextBaseAligned[refOffs+j] += 1
					j += 1
				end
			elseif o==CIGAR_I
				# insertion
				readOffs = readpos(c)-1

				# TODO: implement better way to get substring of seq
				buf = zeros(UInt8,len(c))
				for j=1:len(c)
					buf[j] = base_char(s[readOffs+j])
				end
				str = String(buf)

				refPos = refpos(c)
				list = ci[ref].insertions[refPos-1] # -1 to index insertions by the ref base before the insertion

				list[str] = get(list,str,0)+1

			elseif o==CIGAR_D
				# deletion
				refPos = refpos(c)
				list = ci[ref].deletions[refPos-1] # -1 to index deletions by the ref base before the deletion
				l = len(c)
				list[l] = get(list,l,0)+1
			end
		end
	end

	ci
end

dict_max(d::Dict,defaultKey,defaultValue) = isempty(d) ? (defaultKey=>defaultValue) : (k=argmax(d);k=>d[k])

function consensus_single(ci::ConsensusInfo, ref, minSupport, indelMinSupport)
	refLength = size(ci.nucCounts,1)
	@assert length(ref)==refLength # given reference must match BAM header length

	io = IOBuffer()

	i = 1
	while i<=refLength

		nuc = argmax(ci.nucCounts[i,:])
		if ci.nucCounts[i,nuc] < minSupport
			print(io,ref[i])
		else
			bases = ['A','C','G','T']
			print(io,bases[nuc])
		end

		# handle edge (insertion/deletion/nothing)

		maxInsertion, maxInsertionCount = dict_max(ci.insertions[i],"",0)
		maxDeletion, maxDeletionCount   = dict_max(ci.deletions[i],0,0)

		edgeThresh = max(ci.nextBaseAligned[i]+1,indelMinSupport) # >nextBaseAligned, >=indelMinSupport
		if max(maxInsertionCount,maxDeletionCount) >= edgeThresh
			if maxInsertionCount > maxDeletionCount
				print(io,maxInsertion)
			else
				i += maxDeletion # jump over deletion gap
			end
		end

		i += 1
	end
	String(take!(io))
end

function consensus(bamFile::BamFile, fasta::Union{AbstractString,Reference,AbstractArray}=[];
				   minSupport::Integer=1, indelMinSupport::Integer=minSupport,
				   mappingQualityThreshold::Int=30, ignoreChimeric::Bool=true)
	@assert minSupport>0
	@assert indelMinSupport>0

	# make sure fasta matches bamFile header
	refSeqs = sequences(bamFile)

	if typeof(fasta) <: AbstractString
		fasta = loadfasta(fasta)
	elseif isempty(fasta)
		# create fake fasta with all N's
		fasta = Reference( length(refSeqs) )
		for i=1:length(refSeqs)
			fasta[i] = ( refSeqs[i][1], "N"^refSeqs[i][2] )
		end
	end

	fastaDict = Dict(fasta)
	@assert length(fastaDict)==length(refSeqs) "Reference mismatch between BAM and Fasta"
	for (name,len) in refSeqs
		@assert haskey(fastaDict,name) "Reference sequence \"$name\" not found in fasta"
		@assert length(fastaDict[name])==len "Reference sequence \"$name\" has different length in BAM and Fasta"
	end

	# gather data from BAM
	ci = aggregate_reads(bamFile, mappingQualityThreshold=mappingQualityThreshold,
		                 ignoreChimeric=ignoreChimeric)

	# make a new fasta file, keeping the same order as in the fasta file
	outFasta = Vector{Tuple{String,String}}(undef,length(fasta))

	for fastaInd = 1:length(fasta)
		# find corresponding index in BAM
		bamInd = findfirst( r -> r[1]==fasta[fastaInd][1], refSeqs )

		c = consensus_single( ci[bamInd], fasta[fastaInd][2], minSupport, indelMinSupport )
		outFasta[fastaInd] = (fasta[fastaInd][1], c)
	end

	outFasta
end

consensus(bamFileName::String, fasta::Union{AbstractString,Reference,AbstractArray}=[];
	      minSupport=1, indelMinSupport=minSupport,
	      mappingQualityThreshold::Int=30, ignoreChimeric::Bool=true) =
				consensus(BamFile(bamFileName),fasta,minSupport=minSupport,
					      indelMinSupport=indelMinSupport,
					      mappingQualityThreshold=mappingQualityThreshold,
					      ignoreChimeric=ignoreChimeric)


text:
using Test
using ExtensibleEffects
using DataTypesBasic
using TypeClasses
splitln(str) = split(strip(str), "\n")
import ExtensibleEffects: autorun

# Cosnt{Nothing}, Const, Identity
# ------------------------

program_option(a) = @syntax_eff_noautorun begin
  r0 = Identity(a)
  r1 = isodd(r0) ? Const(nothing) : Identity(3)
  @pure r0, r1
end
@test runhandlers(Option, program_option(1)) == Const(nothing)
@test runhandlers(Option, program_option(2)) == Identity((2, 3))

@test runhandlers((Const, Identity), program_option(1)) == Const(nothing)
@test runhandlers((Const, Identity), program_option(2)) == Identity((2, 3))

@test runhandlers((Identity, Const), program_option(1)) == Const(nothing)
@test runhandlers((Identity, Const), program_option(2)) == Identity((2, 3))

program_either(a) = @syntax_eff_noautorun begin
  r0 = @Try a
  r1 = @Try isodd(r0) ? error("nonono") : 3
  @pure r0, r1
end
@test runhandlers(Either, program_either(1)) isa Const
@test runhandlers(Either, program_either(2)) == Identity((2, 3))

@test runhandlers((Const, Identity), program_either(1)) isa Const
@test runhandlers((Const, Identity), program_either(2)) == Identity((2, 3))

# IMPORTANT: order matters
@test runhandlers((Identity, Const), program_either(1)) isa Const
@test runhandlers((Identity, Const), program_either(2)) == Identity((2, 3))

program_optioneither(a) = @syntax_eff_noautorun begin
  r0 = @Try a
  r1 = iftrue(r0 % 4 == 0, r0 + 1)
  r2 = @Try r1
  r3 = Option(r2)
  Option(r3)
  ## same as
  # ```
  # r4 = Option(r3)
  # @pure r4
  # ```
  ## same as
  # ```
  # r4 = Option(r3)
  # noeffect(4)
  # ```
end
r2_1 = runhandlers((Const, Identity), program_optioneither(4))
@test r2_1 == Identity(5)
@test autorun(program_optioneither(4)) == Identity(5)
@test runhandlers(Either, program_optioneither(4)) == Identity(5)

@test runhandlers((Const, Identity), program_optioneither(3)) == Const(nothing)
@test autorun(program_optioneither(3)) == Const(nothing)
@test runhandlers(Either, program_optioneither(3)) == Const(nothing)

programm_optioneither_flatmap(a) = @syntax_flatmap begin
  r0 = @Try a
  r1 = iftrue(r0 % 4 == 0, r0 + 1)
  r2 = @Try r1
  r3 = Option(r2)
  Option(r3)
end
@test programm_optioneither_flatmap(4) == Identity(5)
@test programm_optioneither_flatmap(3) == Const(nothing)

# test other syntax

handlers = (Const, Identity)
r6_1 = @runhandlers handlers @syntax_eff_noautorun begin
  r0 = @Try 4
  r1 = iftrue(r0 % 4 == 0, r0 + 1)
  r2 = @Try r1
  r3 = Option(r2)
  Option(r3)
end
@test r6_1 == Identity(5)

r6_2 = @runhandlers (Const, Identity) @syntax_eff_noautorun begin
  r0 = @Try 4
  r1 = iftrue(r0 % 4 == 0, r0 + 1)
  r2 = @Try r1
  r3 = Option(r2)
  Option(r3)
end
@test r6_2 == Identity(5)

max_stars_repo_name: schlichtanders/ExtensibleEffects

max_issues_repo_name: schlichtanders/ExtensibleEffects

max_forks_repo_name: schlichtanders/ExtensibleEffects


text:
using Printf
using LinearAlgebra
using SparseArrays
using Random
# Run FedAvg with logistic regression
function RunFedAvgAndProx(
    fileTrain::String,
    fileTest::String,
    lambda::Float64,
    mu::Float64,
    participationRate::Float64,
    localLr::Float64,
    numRounds::Int64,
    writeFileName::String
    )
    numClients = 100
    numRounds = numRounds
    # Read data
    Xtrain, Ytrain = read_libsvm(fileTrain)
    Xtest, Ytest = read_libsvm(fileTest)
    Ytrain, Ytest = labelTransform(Ytrain, Ytest)
    # Set Xtrain and Xtest same number of feature
    Itr, Jtr, Vtr = findnz(Xtrain)
    Ite, Jte, Vte = findnz(Xtest)
    d = max( size(Xtrain, 2), size(Xtest, 2) )
    Xtrain = sparse(Itr, Jtr, Vtr, size(Xtrain, 1), d)
    Xtest = sparse(Ite, Jte, Vte, size(Xtest, 1), d)
    numClasses = length( union( Set(Ytrain), Set(Ytest) ) )
    # Split data
    Xsplit, Ysplit = splitDataByRow(Xtrain, Ytrain, numClients)
    # Xsplit, Ysplit = splitDataByClass(Xtrain, Ytrain, numClients, numClasses )

    # Setup config, running FedAvg if mu=0.
    clientConfig = Dict(
        "num_classes" => numClasses,
        "lambda" => lambda,
        "mu" => mu,
        "learning_rate" => localLr,
        "numLocalEpochs" => 5,
    )

    serverConfig = Dict(
        "num_classes" => numClasses,
        "num_clients" => numClients,
        "participation_rate" => participationRate
    )

    # Construct clients
    clients = Vector{FedProxClient}(undef, numClients)
    for i = 1:numClients
        clients[i] = FedProxClient(i, Xsplit[i], Ysplit[i], clientConfig)
    end
    # Construct server
    server = FedProxServer(Xtest, Ytest, serverConfig)

    # Train
    _, objList, testAccList = fedAvgAndProx(server, clients, numRounds)
    writeToFile(
        fileTrain,
        "softmax classification",
        serverConfig,
        clientConfig,
        objList,
        testAccList,
        writeFileName
    )

    @printf("Finish training!\n")
end

# Run Scaffold with logistic regression
function RunScaffold(
    fileTrain::String,
    fileTest::String,
    lambda::Float64,
    participationRate::Float64,
    localLr::Float64,
    numRounds::Int64,
    writeFileName::String
    )
    numClients = 100
    numRounds = numRounds
    # Read data
    Xtrain, Ytrain = read_libsvm(fileTrain)
    Xtest, Ytest = read_libsvm(fileTest)
    Ytrain, Ytest = labelTransform(Ytrain, Ytest)
    # Set Xtrain and Xtest same number of feature
    Itr, Jtr, Vtr = findnz(Xtrain)
    Ite, Jte, Vte = findnz(Xtest)
    d = max( size(Xtrain, 2), size(Xtest, 2) )
    Xtrain = sparse(Itr, Jtr, Vtr, size(Xtrain, 1), d)
    Xtest = sparse(Ite, Jte, Vte, size(Xtest, 1), d)

    numClasses = length( union( Set(Ytrain), Set(Ytest) ) )
    # Split data
    Xsplit, Ysplit = splitDataByRow(Xtrain, Ytrain, numClients)
    # Xsplit, Ysplit = splitDataByClass(Xtrain, Ytrain, numClients, numClasses )

    # Setup config, running FedAvg if mu=0.
    clientConfig = Dict(
        "num_classes" => numClasses,
        "lambda" => lambda,
        "learning_rate" => localLr,
        "numLocalEpochs" => 5,
    )

    serverConfig = Dict(
        "num_classes" => numClasses,
        "num_clients" => numClients,
        "participation_rate" => participationRate,
        "learning_rate" => 1.0
    )

    # Construct clients
    clients = Vector{ScaffoldClient}(undef, numClients)
    for i = 1:numClients
        clients[i] = ScaffoldClient(i, Xsplit[i], Ysplit[i], clientConfig)
    end
    # Construct server
    server = ScaffoldServer(Xtest, Ytest, serverConfig)

    # Train
    _, objList, testAccList = Scaffold(server, clients, numRounds)
    writeToFile(
        fileTrain,
        "softmax classification",
        serverConfig,
        clientConfig,
        objList,
        testAccList,
        writeFileName
    )

    @printf("Finish training!\n")
end

# Run Newton method with logistic regression
function RunNewtonMethod()
    # fileTrain = "data/mnist.scale"
    # fileTest = "data/mnist.scale.t"
    fileTrain = "data/rcv1_train.binary"
    fileTest = "data/rcv1_train.binary"
    X, Y = read_libsvm(fileTrain);
    Xtest, Ytest = read_libsvm(fileTest)
    Xt = copy(X')
    Y, Ytest = labelTransform(Y, Ytest)
    K = length( unique(Y) )
    n, d = size(X)
    I, J, V = findnz(Xtest)
    Xtest = sparse(I, J, V, size(Xtest,1), d)
    @show(size(X))
    @show(size(Xtest))
    W = zeros(Float64, d, K)
    λ = 1e-3

    maxIter = 20
    tol = 1e-6
    @printf("start training!\n")
    startTime = time()
    for iter = 1:maxIter
        objval = obj(X, Y, W, λ)
        g = getGradient(X, Xt, Y, W, λ)
        accTrain = accuracy(X, Y, W)
        accTest = accuracy(Xtest, Ytest, W)
        gnorm = norm(g)
        @printf("Iter %3d, obj: %4.8e, gnorm: %4.5e, train: %3.3f, test: %3.3f, time: %4.2f\n", iter, objval, gnorm, accTrain*100, accTest*100, time()-startTime)
        if gnorm < tol
            break
        end
        # Compute Newton direction.
        D = ComputeNewtonDirection( X, Xt, W, λ, g)
        # Line-search
        η = lineSearch(X, Y, D, W, g, λ)
        W .-= η*D
    end

end

# Run FedAvg with neural network
function RunFedAvgAndProxNN(
    fileTrain::String,
    fileTest::String,
    lambda::Float64,
    mu::Float64,
    participationRate::Float64,
    localLr::Float64,
    numRounds::Int64,
    writeFileName::String
    )
    numClients = 100
    numRounds = numRounds
    # Read data
    Xtrain, Ytrain = read_libsvm(fileTrain)
    Xtest, Ytest = read_libsvm(fileTest)
    Ytrain, Ytest = labelTransform(Ytrain, Ytest)
    # Set Xtrain and Xtest same number of feature
    Itr, Jtr, Vtr = findnz(Xtrain)
    Ite, Jte, Vte = findnz(Xtest)
    d = max( size(Xtrain, 2), size(Xtest, 2) )
    Xtrain = sparse(Itr, Jtr, Vtr, size(Xtrain, 1), d)
    Xtest = sparse(Ite, Jte, Vte, size(Xtest, 1), d)

    numClasses = length( union( Set(Ytrain), Set(Ytest) ) )
    # Split data
    # Xsplit, Ysplit = splitDataByRow(Xtrain, Ytrain, numClients)
    Xsplit, Ysplit = splitDataByClass(Xtrain, Ytrain, numClients, numClasses )

    # Setup config, running FedAvg if mu=0.
    clientConfig = Dict(
        "num_classes" => numClasses,
        "lambda" => lambda,
        "mu" => mu,
        "learning_rate" => localLr,
        "numLocalEpochs" => 5,
    )

    serverConfig = Dict(
        "num_classes" => numClasses,
        "num_clients" => numClients,
        "participation_rate" => participationRate
    )

    # model structure
    dim = 32

    # Construct clients
    clients = Vector{FedProxClientNN}(undef, numClients)
    for i = 1:numClients
        clients[i] = FedProxClientNN(i, Xsplit[i], Ysplit[i], dim, clientConfig)
    end
    # Construct server
    server = FedProxServerNN(Xtest, Ytest, dim, serverConfig)

    # Train
    _, objList, testAccList = fedAvgAndProx(server, clients, numRounds)
    writeToFile(
        fileTrain,
        "softmax classification with MLP",
        serverConfig,
        clientConfig,
        objList,
        testAccList,
        writeFileName
    )

    @printf("Finish training!\n")
end

# Run Scaffold for neural network
function TestScaffoldNN(
    fileTrain::String,
    fileTest::String,
    lambda::Float64,
    participationRate::Float64,
    localLr::Float64,
    numRounds::Int64,
    writeFileName::String
    )
    numClients = 100
    numRounds = numRounds
    # Read data
    Xtrain, Ytrain = read_libsvm(fileTrain)
    Xtest, Ytest = read_libsvm(fileTest)
    Ytrain, Ytest = labelTransform(Ytrain, Ytest)
    # Set Xtrain and Xtest same number of feature
    Itr, Jtr, Vtr = findnz(Xtrain)
    Ite, Jte, Vte = findnz(Xtest)
    d = max( size(Xtrain, 2), size(Xtest, 2) )
    Xtrain = sparse(Itr, Jtr, Vtr, size(Xtrain, 1), d)
    Xtest = sparse(Ite, Jte, Vte, size(Xtest, 1), d)

    numClasses = length( union( Set(Ytrain), Set(Ytest) ) )
    # Split data
    Xsplit, Ysplit = splitDataByRow(Xtrain, Ytrain, numClients)
    # Xsplit, Ysplit = splitDataByClass(Xtrain, Ytrain, numClients, numClasses )

    # Setup config, running FedAvg if mu=0.
    clientConfig = Dict(
        "num_classes" => numClasses,
        "lambda" => lambda,
        "learning_rate" => localLr,
        "numLocalEpochs" => 5,
    )

    serverConfig = Dict(
        "num_classes" => numClasses,
        "num_clients" => numClients,
        "participation_rate" => participationRate,
        "learning_rate" => 1.0
    )

    # model structure
    dim = 32

    # Construct clients
    clients = Vector{ScaffoldClientNN}(undef, numClients)
    for i = 1:numClients
        clients[i] = ScaffoldClientNN(i, Xsplit[i], Ysplit[i], dim, clientConfig)
    end
    # Construct server
    server = ScaffoldServerNN(Xtest, Ytest, dim, serverConfig)

    # Train
    _, objList, testAccList = Scaffold(server, clients, numRounds)
    writeToFile(
        fileTrain,
        "softmax classification with MLP",
        serverConfig,
        clientConfig,
        objList,
        testAccList,
        writeFileName
    )

    @printf("Finish training!\n")
end


text:
@testset "nn" begin
    using LinearAlgebra
    k = NeuralNetworkKernel()
    v1 = rand(3); v2 = rand(3)
    @test k(v1,v2) ≈ asin(v1' * v2 / sqrt((1 + v1' * v1) * (1 + v2' * v2))) atol=1e-5

    # Standardised tests.
    TestUtils.test_interface(k, Float64)
    test_ADs(NeuralNetworkKernel)
    @test_broken "Zygote uncompatible with BaseKernel"
end


text:
"""
    function emptyquote_(exp)

Function that returns true if a quote expression has any arguments.
"""
function emptyquote_(exp::Expr)
    return [i for i in exp.args if typeof(i) != LineNumberNode] == []
end

max_stars_repo_name: gatocor/embryogenesisJulia

max_issues_repo_name: gatocor/embryogenesisJulia

max_forks_repo_name: gatocor/embryogenesisJulia


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

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

Creates an HTTP namespace. Service instances that you register using an HTTP namespace can
be discovered using a DiscoverInstances request but can't be discovered using DNS.  For the
current quota on the number of namespaces that you can create using the same AWS account,
see AWS Cloud Map quotas in the AWS Cloud Map Developer Guide.

# Arguments
- `name`: The name that you want to assign to this namespace.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"CreatorRequestId"`: A unique string that identifies the request and that allows failed
  CreateHttpNamespace requests to be retried without the risk of executing the operation
  twice. CreatorRequestId can be any unique string, for example, a date/time stamp.
- `"Description"`: A description for the namespace.
- `"Tags"`: The tags to add to the namespace. Each tag consists of a key and an optional
  value, both of which you define. Tag keys can have a maximum character length of 128
  characters, and tag values can have a maximum length of 256 characters.
"""
create_http_namespace(Name; aws_config::AbstractAWSConfig=global_aws_config()) = servicediscovery("CreateHttpNamespace", Dict{String, Any}("Name"=>Name, "CreatorRequestId"=>string(uuid4())); aws_config=aws_config)
create_http_namespace(Name, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = servicediscovery("CreateHttpNamespace", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("Name"=>Name, "CreatorRequestId"=>string(uuid4())), params)); aws_config=aws_config)

"""
    create_private_dns_namespace(name, vpc)
    create_private_dns_namespace(name, vpc, params::Dict{String,<:Any})

Creates a private namespace based on DNS, which will be visible only inside a specified
Amazon VPC. The namespace defines your service naming scheme. For example, if you name your
namespace example.com and name your service backend, the resulting DNS name for the service
will be backend.example.com. For the current quota on the number of namespaces that you can
create using the same AWS account, see AWS Cloud Map Limits in the AWS Cloud Map Developer
Guide.

# Arguments
- `name`: The name that you want to assign to this namespace. When you create a private DNS
  namespace, AWS Cloud Map automatically creates an Amazon Route 53 private hosted zone that
  has the same name as the namespace.
- `vpc`: The ID of the Amazon VPC that you want to associate the namespace with.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"CreatorRequestId"`: A unique string that identifies the request and that allows failed
  CreatePrivateDnsNamespace requests to be retried without the risk of executing the
  operation twice. CreatorRequestId can be any unique string, for example, a date/time stamp.
- `"Description"`: A description for the namespace.
- `"Tags"`: The tags to add to the namespace. Each tag consists of a key and an optional
  value, both of which you define. Tag keys can have a maximum character length of 128
  characters, and tag values can have a maximum length of 256 characters.
"""
create_private_dns_namespace(Name, Vpc; aws_config::AbstractAWSConfig=global_aws_config()) = servicediscovery("CreatePrivateDnsNamespace", Dict{String, Any}("Name"=>Name, "Vpc"=>Vpc, "CreatorRequestId"=>string(uuid4())); aws_config=aws_config)
create_private_dns_namespace(Name, Vpc, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = servicediscovery("CreatePrivateDnsNamespace", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("Name"=>Name, "Vpc"=>Vpc, "CreatorRequestId"=>string(uuid4())), params)); aws_config=aws_config)

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

Creates a public namespace based on DNS, which will be visible on the internet. The
namespace defines your service naming scheme. For example, if you name your namespace
example.com and name your service backend, the resulting DNS name for the service will be
backend.example.com. For the current quota on the number of namespaces that you can create
using the same AWS account, see AWS Cloud Map Limits in the AWS Cloud Map Developer Guide.

# Arguments
- `name`: The name that you want to assign to this namespace.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"CreatorRequestId"`: A unique string that identifies the request and that allows failed
  CreatePublicDnsNamespace requests to be retried without the risk of executing the operation
  twice. CreatorRequestId can be any unique string, for example, a date/time stamp.
- `"Description"`: A description for the namespace.
- `"Tags"`: The tags to add to the namespace. Each tag consists of a key and an optional
  value, both of which you define. Tag keys can have a maximum character length of 128
  characters, and tag values can have a maximum length of 256 characters.
"""
create_public_dns_namespace(Name; aws_config::AbstractAWSConfig=global_aws_config()) = servicediscovery("CreatePublicDnsNamespace", Dict{String, Any}("Name"=>Name, "CreatorRequestId"=>string(uuid4())); aws_config=aws_config)
create_public_dns_namespace(Name, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = servicediscovery("CreatePublicDnsNamespace", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("Name"=>Name, "CreatorRequestId"=>string(uuid4())), params)); aws_config=aws_config)

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

Creates a service, which defines the configuration for the following entities:   For public
and private DNS namespaces, one of the following combinations of DNS records in Amazon
Route 53:    A     AAAA     A and AAAA     SRV     CNAME      Optionally, a health check
After you create the service, you can submit a RegisterInstance request, and AWS Cloud Map
uses the values in the configuration to create the specified entities. For the current
quota on the number of instances that you can register using the same namespace and using
the same service, see AWS Cloud Map Limits in the AWS Cloud Map Developer Guide.

# Arguments
- `name`: The name that you want to assign to the service. If you want AWS Cloud Map to
  create an SRV record when you register an instance, and if you're using a system that
  requires a specific SRV format, such as HAProxy, specify the following for Name:   Start
  the name with an underscore (_), such as _exampleservice    End the name with ._protocol,
  such as ._tcp    When you register an instance, AWS Cloud Map creates an SRV record and
  assigns a name to the record by concatenating the service name and the namespace name, for
  example:  _exampleservice._tcp.example.com   For a single DNS namespace, you cannot create
  two services with names that differ only by case (such as EXAMPLE and example). Otherwise,
  these services will have the same DNS name. However, you can create multiple HTTP services
  with names that differ only by case because HTTP services are case sensitive.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"CreatorRequestId"`: A unique string that identifies the request and that allows failed
  CreateService requests to be retried without the risk of executing the operation twice.
  CreatorRequestId can be any unique string, for example, a date/time stamp.
- `"Description"`: A description for the service.
- `"DnsConfig"`: A complex type that contains information about the Amazon Route 53
  records that you want AWS Cloud Map to create when you register an instance.
- `"HealthCheckConfig"`:  Public DNS and HTTP namespaces only. A complex type that contains
  settings for an optional Route 53 health check. If you specify settings for a health
  check, AWS Cloud Map associates the health check with all the Route 53 DNS records that
  you specify in DnsConfig.  If you specify a health check configuration, you can specify
  either HealthCheckCustomConfig or HealthCheckConfig but not both.  For information about
  the charges for health checks, see AWS Cloud Map Pricing.
- `"HealthCheckCustomConfig"`: A complex type that contains information about an optional
  custom health check.  If you specify a health check configuration, you can specify either
  HealthCheckCustomConfig or HealthCheckConfig but not both.  You can't add, update, or
  delete a HealthCheckCustomConfig configuration from an existing service.
- `"NamespaceId"`: The ID of the namespace that you want to use to create the service.
- `"Tags"`: The tags to add to the service. Each tag consists of a key and an optional
  value, both of which you define. Tag keys can have a maximum character length of 128
  characters, and tag values can have a maximum length of 256 characters.
- `"Type"`: If present, specifies that the service instances are only discoverable using
  the DiscoverInstances API operation. No DNS records will be registered for the service
  instances. The only valid value is HTTP.
"""
create_service(Name; aws_config::AbstractAWSConfig=global_aws_config()) = servicediscovery("CreateService", Dict{String, Any}("Name"=>Name, "CreatorRequestId"=>string(uuid4())); aws_config=aws_config)
create_service(Name, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = servicediscovery("CreateService", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("Name"=>Name, "CreatorRequestId"=>string(uuid4())), params)); aws_config=aws_config)

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

Deletes a namespace from the current account. If the namespace still contains one or more
services, the request fails.

# Arguments
- `id`: The ID of the namespace that you want to delete.

"""
delete_namespace(Id; aws_config::AbstractAWSConfig=global_aws_config()) = servicediscovery("DeleteNamespace", Dict{String, Any}("Id"=>Id); aws_config=aws_config)
delete_namespace(Id, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = servicediscovery("DeleteNamespace", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("Id"=>Id), params)); aws_config=aws_config)

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

Deletes a specified service. If the service still contains one or more registered
instances, the request fails.

# Arguments
- `id`: The ID of the service that you want to delete.

"""
delete_service(Id; aws_config::AbstractAWSConfig=global_aws_config()) = servicediscovery("DeleteService", Dict{String, Any}("Id"=>Id); aws_config=aws_config)
delete_service(Id, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = servicediscovery("DeleteService", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("Id"=>Id), params)); aws_config=aws_config)

"""
    deregister_instance(instance_id, service_id)
    deregister_instance(instance_id, service_id, params::Dict{String,<:Any})

Deletes the Amazon Route 53 DNS records and health check, if any, that AWS Cloud Map
created for the specified instance.

# Arguments
- `instance_id`: The value that you specified for Id in the RegisterInstance request.
- `service_id`: The ID of the service that the instance is associated with.

"""
deregister_instance(InstanceId, ServiceId; aws_config::AbstractAWSConfig=global_aws_config()) = servicediscovery("DeregisterInstance", Dict{String, Any}("InstanceId"=>InstanceId, "ServiceId"=>ServiceId); aws_config=aws_config)
deregister_instance(InstanceId, ServiceId, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = servicediscovery("DeregisterInstance", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("InstanceId"=>InstanceId, "ServiceId"=>ServiceId), params)); aws_config=aws_config)

"""
    discover_instances(namespace_name, service_name)
    discover_instances(namespace_name, service_name, params::Dict{String,<:Any})

Discovers registered instances for a specified namespace and service. You can use
DiscoverInstances to discover instances for any type of namespace. For public and private
DNS namespaces, you can also use DNS queries to discover instances.

# Arguments
- `namespace_name`: The name of the namespace that you specified when you registered the
  instance.
- `service_name`: The name of the service that you specified when you registered the
  instance.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"HealthStatus"`: The health status of the instances that you want to discover.
- `"MaxResults"`: The maximum number of instances that you want AWS Cloud Map to return in
  the response to a DiscoverInstances request. If you don't specify a value for MaxResults,
  AWS Cloud Map returns up to 100 instances.
- `"OptionalParameters"`: Opportunistic filters to scope the results based on custom
  attributes. If there are instances that match both the filters specified in both the
  QueryParameters parameter and this parameter, they are returned. Otherwise, these filters
  are ignored and only instances that match the filters specified in the QueryParameters
  parameter are returned.
- `"QueryParameters"`: Filters to scope the results based on custom attributes for the
  instance. For example, {version=v1, az=1a}. Only instances that match all the specified
  key-value pairs will be returned.
"""
discover_instances(NamespaceName, ServiceName; aws_config::AbstractAWSConfig=global_aws_config()) = servicediscovery("DiscoverInstances", Dict{String, Any}("NamespaceName"=>NamespaceName, "ServiceName"=>ServiceName); aws_config=aws_config)
discover_instances(NamespaceName, ServiceName, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = servicediscovery("DiscoverInstances", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("NamespaceName"=>NamespaceName, "ServiceName"=>ServiceName), params)); aws_config=aws_config)

"""
    get_instance(instance_id, service_id)
    get_instance(instance_id, service_id, params::Dict{String,<:Any})

Gets information about a specified instance.

# Arguments
- `instance_id`: The ID of the instance that you want to get information about.
- `service_id`: The ID of the service that the instance is associated with.

"""
get_instance(InstanceId, ServiceId; aws_config::AbstractAWSConfig=global_aws_config()) = servicediscovery("GetInstance", Dict{String, Any}("InstanceId"=>InstanceId, "ServiceId"=>ServiceId); aws_config=aws_config)
get_instance(InstanceId, ServiceId, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = servicediscovery("GetInstance", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("InstanceId"=>InstanceId, "ServiceId"=>ServiceId), params)); aws_config=aws_config)

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

Gets the current health status (Healthy, Unhealthy, or Unknown) of one or more instances
that are associated with a specified service.  There is a brief delay between when you
register an instance and when the health status for the instance is available.

# Arguments
- `service_id`: The ID of the service that the instance is associated with.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"Instances"`: An array that contains the IDs of all the instances that you want to get
  the health status for. If you omit Instances, AWS Cloud Map returns the health status for
  all the instances that are associated with the specified service.  To get the IDs for the
  instances that you've registered by using a specified service, submit a ListInstances
  request.
- `"MaxResults"`: The maximum number of instances that you want AWS Cloud Map to return in
  the response to a GetInstancesHealthStatus request. If you don't specify a value for
  MaxResults, AWS Cloud Map returns up to 100 instances.
- `"NextToken"`: For the first GetInstancesHealthStatus request, omit this value. If more
  than MaxResults instances match the specified criteria, you can submit another
  GetInstancesHealthStatus request to get the next group of results. Specify the value of
  NextToken from the previous response in the next request.
"""
get_instances_health_status(ServiceId; aws_config::AbstractAWSConfig=global_aws_config()) = servicediscovery("GetInstancesHealthStatus", Dict{String, Any}("ServiceId"=>ServiceId); aws_config=aws_config)
get_instances_health_status(ServiceId, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = servicediscovery("GetInstancesHealthStatus", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("ServiceId"=>ServiceId), params)); aws_config=aws_config)

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

Gets information about a namespace.

# Arguments
- `id`: The ID of the namespace that you want to get information about.

"""
get_namespace(Id; aws_config::AbstractAWSConfig=global_aws_config()) = servicediscovery("GetNamespace", Dict{String, Any}("Id"=>Id); aws_config=aws_config)
get_namespace(Id, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = servicediscovery("GetNamespace", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("Id"=>Id), params)); aws_config=aws_config)

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

Gets information about any operation that returns an operation ID in the response, such as
a CreateService request.  To get a list of operations that match specified criteria, see
ListOperations.

# Arguments
- `operation_id`: The ID of the operation that you want to get more information about.

"""
get_operation(OperationId; aws_config::AbstractAWSConfig=global_aws_config()) = servicediscovery("GetOperation", Dict{String, Any}("OperationId"=>OperationId); aws_config=aws_config)
get_operation(OperationId, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = servicediscovery("GetOperation", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("OperationId"=>OperationId), params)); aws_config=aws_config)

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

Gets the settings for a specified service.

# Arguments
- `id`: The ID of the service that you want to get settings for.

"""
get_service(Id; aws_config::AbstractAWSConfig=global_aws_config()) = servicediscovery("GetService", Dict{String, Any}("Id"=>Id); aws_config=aws_config)
get_service(Id, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = servicediscovery("GetService", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("Id"=>Id), params)); aws_config=aws_config)

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

Lists summary information about the instances that you registered by using a specified
service.

# Arguments
- `service_id`: The ID of the service that you want to list instances for.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"MaxResults"`: The maximum number of instances that you want AWS Cloud Map to return in
  the response to a ListInstances request. If you don't specify a value for MaxResults, AWS
  Cloud Map returns up to 100 instances.
- `"NextToken"`: For the first ListInstances request, omit this value. If more than
  MaxResults instances match the specified criteria, you can submit another ListInstances
  request to get the next group of results. Specify the value of NextToken from the previous
  response in the next request.
"""
list_instances(ServiceId; aws_config::AbstractAWSConfig=global_aws_config()) = servicediscovery("ListInstances", Dict{String, Any}("ServiceId"=>ServiceId); aws_config=aws_config)
list_instances(ServiceId, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = servicediscovery("ListInstances", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("ServiceId"=>ServiceId), params)); aws_config=aws_config)

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

Lists summary information about the namespaces that were created by the current AWS account.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"Filters"`: A complex type that contains specifications for the namespaces that you want
  to list. If you specify more than one filter, a namespace must match all filters to be
  returned by ListNamespaces.
- `"MaxResults"`: The maximum number of namespaces that you want AWS Cloud Map to return in
  the response to a ListNamespaces request. If you don't specify a value for MaxResults, AWS
  Cloud Map returns up to 100 namespaces.
- `"NextToken"`: For the first ListNamespaces request, omit this value. If the response
  contains NextToken, submit another ListNamespaces request to get the next group of results.
  Specify the value of NextToken from the previous response in the next request.  AWS Cloud
  Map gets MaxResults namespaces and then filters them based on the specified criteria. It's
  possible that no namespaces in the first MaxResults namespaces matched the specified
  criteria but that subsequent groups of MaxResults namespaces do contain namespaces that
  match the criteria.
"""
list_namespaces(; aws_config::AbstractAWSConfig=global_aws_config()) = servicediscovery("ListNamespaces"; aws_config=aws_config)
list_namespaces(params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = servicediscovery("ListNamespaces", params; aws_config=aws_config)

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

Lists operations that match the criteria that you specify.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"Filters"`: A complex type that contains specifications for the operations that you want
  to list, for example, operations that you started between a specified start date and end
  date. If you specify more than one filter, an operation must match all filters to be
  returned by ListOperations.
- `"MaxResults"`: The maximum number of items that you want AWS Cloud Map to return in the
  response to a ListOperations request. If you don't specify a value for MaxResults, AWS
  Cloud Map returns up to 100 operations.
- `"NextToken"`: For the first ListOperations request, omit this value. If the response
  contains NextToken, submit another ListOperations request to get the next group of results.
  Specify the value of NextToken from the previous response in the next request.  AWS Cloud
  Map gets MaxResults operations and then filters them based on the specified criteria. It's
  possible that no operations in the first MaxResults operations matched the specified
  criteria but that subsequent groups of MaxResults operations do contain operations that
  match the criteria.
"""
list_operations(; aws_config::AbstractAWSConfig=global_aws_config()) = servicediscovery("ListOperations"; aws_config=aws_config)
list_operations(params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = servicediscovery("ListOperations", params; aws_config=aws_config)

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

Lists summary information for all the services that are associated with one or more
specified namespaces.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"Filters"`: A complex type that contains specifications for the namespaces that you want
  to list services for.  If you specify more than one filter, an operation must match all
  filters to be returned by ListServices.
- `"MaxResults"`: The maximum number of services that you want AWS Cloud Map to return in
  the response to a ListServices request. If you don't specify a value for MaxResults, AWS
  Cloud Map returns up to 100 services.
- `"NextToken"`: For the first ListServices request, omit this value. If the response
  contains NextToken, submit another ListServices request to get the next group of results.
  Specify the value of NextToken from the previous response in the next request.  AWS Cloud
  Map gets MaxResults services and then filters them based on the specified criteria. It's
  possible that no services in the first MaxResults services matched the specified criteria
  but that subsequent groups of MaxResults services do contain services that match the
  criteria.
"""
list_services(; aws_config::AbstractAWSConfig=global_aws_config()) = servicediscovery("ListServices"; aws_config=aws_config)
list_services(params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = servicediscovery("ListServices", params; aws_config=aws_config)

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

Lists tags for the specified resource.

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

"""
list_tags_for_resource(ResourceARN; aws_config::AbstractAWSConfig=global_aws_config()) = servicediscovery("ListTagsForResource", Dict{String, Any}("ResourceARN"=>ResourceARN); aws_config=aws_config)
list_tags_for_resource(ResourceARN, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = servicediscovery("ListTagsForResource", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("ResourceARN"=>ResourceARN), params)); aws_config=aws_config)

"""
    register_instance(attributes, instance_id, service_id)
    register_instance(attributes, instance_id, service_id, params::Dict{String,<:Any})

Creates or updates one or more records and, optionally, creates a health check based on the
settings in a specified service. When you submit a RegisterInstance request, the following
occurs:   For each DNS record that you define in the service that is specified by
ServiceId, a record is created or updated in the hosted zone that is associated with the
corresponding namespace.   If the service includes HealthCheckConfig, a health check is
created based on the settings in the health check configuration.   The health check, if
any, is associated with each of the new or updated records.    One RegisterInstance request
must complete before you can submit another request and specify the same service ID and
instance ID.  For more information, see CreateService. When AWS Cloud Map receives a DNS
query for the specified DNS name, it returns the applicable value:    If the health check
is healthy: returns all the records    If the health check is unhealthy: returns the
applicable value for the last healthy instance    If you didn't specify a health check
configuration: returns all the records   For the current quota on the number of instances
that you can register using the same namespace and using the same service, see AWS Cloud
Map Limits in the AWS Cloud Map Developer Guide.

# Arguments
- `attributes`: A string map that contains the following information for the service that
  you specify in ServiceId:   The attributes that apply to the records that are defined in
  the service.    For each attribute, the applicable value.   Supported attribute keys
  include the following:  AWS_ALIAS_DNS_NAME  If you want AWS Cloud Map to create an Amazon
  Route 53 alias record that routes traffic to an Elastic Load Balancing load balancer,
  specify the DNS name that is associated with the load balancer. For information about how
  to get the DNS name, see \"DNSName\" in the topic AliasTarget in the Route 53 API
  Reference. Note the following:   The configuration for the service that is specified by
  ServiceId must include settings for an A record, an AAAA record, or both.   In the service
  that is specified by ServiceId, the value of RoutingPolicy must be WEIGHTED.   If the
  service that is specified by ServiceId includes HealthCheckConfig settings, AWS Cloud Map
  will create the Route 53 health check, but it won't associate the health check with the
  alias record.   Auto naming currently doesn't support creating alias records that route
  traffic to AWS resources other than Elastic Load Balancing load balancers.   If you specify
  a value for AWS_ALIAS_DNS_NAME, don't specify values for any of the AWS_INSTANCE
  attributes.    AWS_EC2_INSTANCE_ID   HTTP namespaces only. The Amazon EC2 instance ID for
  the instance. If the AWS_EC2_INSTANCE_ID attribute is specified, then the only other
  attribute that can be specified is AWS_INIT_HEALTH_STATUS. When the AWS_EC2_INSTANCE_ID
  attribute is specified, then the AWS_INSTANCE_IPV4 attribute will be filled out with the
  primary private IPv4 address.  AWS_INIT_HEALTH_STATUS  If the service configuration
  includes HealthCheckCustomConfig, you can optionally use AWS_INIT_HEALTH_STATUS to specify
  the initial status of the custom health check, HEALTHY or UNHEALTHY. If you don't specify a
  value for AWS_INIT_HEALTH_STATUS, the initial status is HEALTHY.  AWS_INSTANCE_CNAME  If
  the service configuration includes a CNAME record, the domain name that you want Route 53
  to return in response to DNS queries, for example, example.com. This value is required if
  the service specified by ServiceId includes settings for an CNAME record.
  AWS_INSTANCE_IPV4  If the service configuration includes an A record, the IPv4 address that
  you want Route 53 to return in response to DNS queries, for example, 192.0.2.44. This
  value is required if the service specified by ServiceId includes settings for an A record.
  If the service includes settings for an SRV record, you must specify a value for
  AWS_INSTANCE_IPV4, AWS_INSTANCE_IPV6, or both.  AWS_INSTANCE_IPV6  If the service
  configuration includes an AAAA record, the IPv6 address that you want Route 53 to return
  in response to DNS queries, for example, 2001:0db8:85a3:0000:0000:abcd:0001:2345. This
  value is required if the service specified by ServiceId includes settings for an AAAA
  record. If the service includes settings for an SRV record, you must specify a value for
  AWS_INSTANCE_IPV4, AWS_INSTANCE_IPV6, or both.  AWS_INSTANCE_PORT  If the service includes
  an SRV record, the value that you want Route 53 to return for the port. If the service
  includes HealthCheckConfig, the port on the endpoint that you want Route 53 to send
  requests to.  This value is required if you specified settings for an SRV record or a
  Route 53 health check when you created the service.  Custom attributes  You can add up to
  30 custom attributes. For each key-value pair, the maximum length of the attribute name is
  255 characters, and the maximum length of the attribute value is 1,024 characters. The
  total size of all provided attributes (sum of all keys and values) must not exceed 5,000
  characters.
- `instance_id`: An identifier that you want to associate with the instance. Note the
  following:   If the service that is specified by ServiceId includes settings for an SRV
  record, the value of InstanceId is automatically included as part of the value for the SRV
  record. For more information, see DnsRecord > Type.   You can use this value to update
  an existing instance.   To register a new instance, you must specify a value that is unique
  among instances that you register by using the same service.    If you specify an existing
  InstanceId and ServiceId, AWS Cloud Map updates the existing DNS records, if any. If
  there's also an existing health check, AWS Cloud Map deletes the old health check and
  creates a new one.   The health check isn't deleted immediately, so it will still appear
  for a while if you submit a ListHealthChecks request, for example.
- `service_id`: The ID of the service that you want to use for settings for the instance.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"CreatorRequestId"`: A unique string that identifies the request and that allows failed
  RegisterInstance requests to be retried without the risk of executing the operation twice.
  You must use a unique CreatorRequestId string every time you submit a RegisterInstance
  request if you're registering additional instances for the same namespace and service.
  CreatorRequestId can be any unique string, for example, a date/time stamp.
"""
register_instance(Attributes, InstanceId, ServiceId; aws_config::AbstractAWSConfig=global_aws_config()) = servicediscovery("RegisterInstance", Dict{String, Any}("Attributes"=>Attributes, "InstanceId"=>InstanceId, "ServiceId"=>ServiceId, "CreatorRequestId"=>string(uuid4())); aws_config=aws_config)
register_instance(Attributes, InstanceId, ServiceId, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = servicediscovery("RegisterInstance", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("Attributes"=>Attributes, "InstanceId"=>InstanceId, "ServiceId"=>ServiceId, "CreatorRequestId"=>string(uuid4())), params)); aws_config=aws_config)

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

Adds one or more tags to the specified resource.

# Arguments
- `resource_arn`: The Amazon Resource Name (ARN) of the resource that you want to retrieve
  tags for.
- `tags`: The tags to add to the specified resource. Specifying the tag key is required.
  You can set the value of a tag to an empty string, but you can't set the value of a tag to
  null.

"""
tag_resource(ResourceARN, Tags; aws_config::AbstractAWSConfig=global_aws_config()) = servicediscovery("TagResource", Dict{String, Any}("ResourceARN"=>ResourceARN, "Tags"=>Tags); aws_config=aws_config)
tag_resource(ResourceARN, Tags, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = servicediscovery("TagResource", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("ResourceARN"=>ResourceARN, "Tags"=>Tags), params)); aws_config=aws_config)

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

Removes one or more tags from the specified resource.

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

"""
untag_resource(ResourceARN, TagKeys; aws_config::AbstractAWSConfig=global_aws_config()) = servicediscovery("UntagResource", Dict{String, Any}("ResourceARN"=>ResourceARN, "TagKeys"=>TagKeys); aws_config=aws_config)
untag_resource(ResourceARN, TagKeys, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = servicediscovery("UntagResource", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("ResourceARN"=>ResourceARN, "TagKeys"=>TagKeys), params)); aws_config=aws_config)

"""
    update_instance_custom_health_status(instance_id, service_id, status)
    update_instance_custom_health_status(instance_id, service_id, status, params::Dict{String,<:Any})

Submits a request to change the health status of a custom health check to healthy or
unhealthy. You can use UpdateInstanceCustomHealthStatus to change the status only for
custom health checks, which you define using HealthCheckCustomConfig when you create a
service. You can't use it to change the status for Route 53 health checks, which you
define using HealthCheckConfig. For more information, see HealthCheckCustomConfig.

# Arguments
- `instance_id`: The ID of the instance that you want to change the health status for.
- `service_id`: The ID of the service that includes the configuration for the custom health
  check that you want to change the status for.
- `status`: The new status of the instance, HEALTHY or UNHEALTHY.

"""
update_instance_custom_health_status(InstanceId, ServiceId, Status; aws_config::AbstractAWSConfig=global_aws_config()) = servicediscovery("UpdateInstanceCustomHealthStatus", Dict{String, Any}("InstanceId"=>InstanceId, "ServiceId"=>ServiceId, "Status"=>Status); aws_config=aws_config)
update_instance_custom_health_status(InstanceId, ServiceId, Status, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = servicediscovery("UpdateInstanceCustomHealthStatus", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("InstanceId"=>InstanceId, "ServiceId"=>ServiceId, "Status"=>Status), params)); aws_config=aws_config)

"""
    update_service(id, service)
    update_service(id, service, params::Dict{String,<:Any})

Submits a request to perform the following operations:   Update the TTL setting for
existing DnsRecords configurations   Add, update, or delete HealthCheckConfig for a
specified service  You can't add, update, or delete a HealthCheckCustomConfig
configuration.    For public and private DNS namespaces, note the following:   If you omit
any existing DnsRecords or HealthCheckConfig configurations from an UpdateService request,
the configurations are deleted from the service.   If you omit an existing
HealthCheckCustomConfig configuration from an UpdateService request, the configuration is
not deleted from the service.   When you update settings for a service, AWS Cloud Map also
updates the corresponding settings in all the records and health checks that were created
by using the specified service.

# Arguments
- `id`: The ID of the service that you want to update.
- `service`: A complex type that contains the new settings for the service.

"""
update_service(Id, Service; aws_config::AbstractAWSConfig=global_aws_config()) = servicediscovery("UpdateService", Dict{String, Any}("Id"=>Id, "Service"=>Service); aws_config=aws_config)
update_service(Id, Service, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = servicediscovery("UpdateService", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("Id"=>Id, "Service"=>Service), params)); aws_config=aws_config)