text:
include("../src/ekp.jl")
include("../src/truth.jl")
include("../src/transforms.jl")
include("../src/units.jl") # for apply_units_transform

using Plots
using Plots.PlotMeasures
using ColorSchemes
using LinearAlgebra
using JLD2
using Statistics
using Distributions
using Random #(for seed)
using .EKP

######################################################################33
"""
Plots various objective functions along one parameter conditioned the other is
at its truth value.

each panel contains 2 objectives
- Noisy GCM objective \$\\| y - \\mathcal{G}_T(x)\\|_\\Gamma\$
- GP objective: \$\\|\\tilde y - \\tilde \\mathcal{G}_{GP}(x)\\|_{\\Gamma_{GP}(x)}\$
"""

function main()

    #load objects
    @load "ekp.jld2" ekpobj
    @load "truth.jld2" truthobj
    # gp runs at fixed param2
    @load "mean_mesh_fixed_param2.jld2" mean_mesh
    mean_mesh_fixed_param2 = mean_mesh
    @load "var_mesh_fixed_param2.jld2" var_mesh
    var_mesh_fixed_param2 = var_mesh
    # gp runs at fixed param1
    @load "mean_mesh_fixed_param1.jld2" mean_mesh
    mean_mesh_fixed_param1= mean_mesh
    @load "var_mesh_fixed_param1.jld2" var_mesh
    var_mesh_fixed_param1 = var_mesh

    # the outputs from the gcm at fixed param1 and param2
    @load fixed_param2_dir*"ekp.jld2" ekpobj
    gcm_fixed_param2 = ekpobj.g[1]
    @load fixed_param1_dir*"ekp.jld2" ekpobj
    gcm_fixed_param1 = ekpobj.g[1]

    true_params_raw = [0,0]

    # mean, sample or sample_mean

    # load the truth (with inflated cov)
    @load truthdir*"truth.jld2" truthobj
    gcm_truth_cov = cov(truthobj.sample)
    obs_truth_cov = truthobj.cov - gcm_truth_cov
    inv_truth_cov = inv(gcm_truth_cov+obs_truth_cov)

    #sample of truth in ekp
    yt = ekpobj.g_t

    #the param values:
    gcm_params_fixed_param2 = collect(0.6 : (0.8-0.6)/99.0 : 0.8)
    gcm_params_fixed_param1 = collect(3600.0 : (21600.0 - 3600.0)/99.0 : 21600.0)
    @load gpdir*"param1vals_fixed_param2.jld2" param1vals
    @load gpdir*"param2vals_fixed_param1.jld2" param2vals

    # meshes, see emulate_sample.jl, "emulator_uncertainty_graphs(...)"
    # GP mean and sd in svd - transformed coordinates
    # mesh = [outputdim x N x N ]
    # We calculate the objective function by
    # Obj(i,j) = 0.5sum_{k=1}^outputdim [ sd(i,j)^{-2} (y(k) - GP(k,i,j))^2 ]
    # note we do not account for the additional terms from prior+normalization constant

    # transform y into SVD coordinates first.
    #SVD=svd(truthobj.cov)#svd.U * svd.S * svd.Vt (can also get Vt)
    SVD = svd(gcm_truth_cov)
    Dinv = Diagonal(1.0 ./ sqrt.(SVD.S)) #diagonal matrix of 1/eigenvalues
    D = Diagonal(sqrt.(SVD.S))
    y_transform = Dinv*SVD.Vt*yt

    #create the EKP-GP objective for fixed param2
    gp_objective_fixed_param2 = zeros(size(param1vals))
    for i =1:length(param1vals)
        diff = mean_mesh_fixed_param2[:,i] - y_transform
        data_cov = Diagonal(var_mesh_fixed_param2[:,i]) + inflation_cov_transform #Gamma + Delta
        gp_fidelity = 0.5*log(det(data_cov))
        gp_objective_fixed_param2[i] = 0.5*diff'*inv(data_cov)*diff + gp_fidelity
    end
    #create the EKP-GP objective for fixed param1
    gp_objective_fixed_param1 = zeros(size(param2vals))
    for i =1:length(param2vals)
        diff = mean_mesh_fixed_param1[:,i] - y_transform
        data_cov = Diagonal(var_mesh_fixed_param1[:,i]) + inflation_cov_transform #Gamma + Delta
        gp_fidelity = 0.5*log(det(data_cov))
        gp_objective_fixed_param1[i] = 0.5*diff'*inv(data_cov)*diff + gp_fidelity
    end

    #create the GCM objective for fixed param2
    gcm_objective_fixed_param2 = zeros(size(gcm_params_fixed_param2))
    for i =1:length(gcm_params_fixed_param2)
        # in untransformed coordinates
        diff = gcm_fixed_param2[i,:] - yt
        gcm_objective_fixed_param2[i] = 0.5*diff'*inv_truth_cov*diff
    end

    #create the GCM objective for fixed param2
    gcm_objective_fixed_param1 = zeros(size(gcm_params_fixed_param1))
    for i =1:length(gcm_params_fixed_param1)
        # in untransformed coordinates
        diff = gcm_fixed_param1[i,:] - yt
        gcm_objective_fixed_param1[i] = 0.5*diff'*inv_truth_cov*diff
    end

    param1vals_raw,param2vals_raw = transform_prior_to_real([param1vals,param2vals])
    param1vals_raw,param2vals_raw = apply_units_transform([param1vals_raw,param2vals_raw])

    param1_in_bd = (gcm_params_fixed_param2 .> minimum(param1vals_raw)) .& (gcm_params_fixed_param2 .< maximum(param1vals_raw))
    param2_in_bd = (gcm_params_fixed_param1 .> minimum(param2vals_raw)) .& (gcm_params_fixed_param1 .< maximum(param2vals_raw))

    gcm_params_fixed_param2_in_bd = gcm_params_fixed_param2[param1_in_bd]
    gcm_params_fixed_param1_in_bd = gcm_params_fixed_param1[param2_in_bd]
    gcm_objective_fixed_param2_in_bd = gcm_objective_fixed_param2[param1_in_bd]
    gcm_objective_fixed_param1_in_bd = gcm_objective_fixed_param1[param2_in_bd]

    param1vals_raw,param2vals_raw = apply_units_transform([param1vals_raw,param2vals_raw])
    gcm_params_fixed_param2_in_bd,gcm_params_fixed_param1_in_bd = apply_units_transform([gcm_params_fixed_param2_in_bd,gcm_params_fixed_param1_in_bd])

    #prints min and max of objectives
    gp_minobj_fixed_param2 = minimum(gp_objective_fixed_param2)
    gp_maxobj_fixed_param2 = maximum(gp_objective_fixed_param2)
    println("minimum of fixed param2 gp objective", gp_minobj_fixed_param2)
    println("maximum of fixed param2 gp objective", gp_maxobj_fixed_param2)

    gp_minobj_fixed_param1 = minimum(gp_objective_fixed_param1)
    gp_maxobj_fixed_param1 = maximum(gp_objective_fixed_param1)
    println("minimum of fixed param1 gp objective", gp_minobj_fixed_param1)
    println("maximum of fixed param1 gp objective", gp_maxobj_fixed_param1)

    gcm_minobj_fixed_param2 = minimum(gcm_objective_fixed_param2_in_bd)
    gcm_maxobj_fixed_param2 = maximum(gcm_objective_fixed_param2_in_bd)
    println("minimum of fixed param2 gcm objective", gcm_minobj_fixed_param2)
    println("maximum of fixed param2 gcm objective", gcm_maxobj_fixed_param2)

    gcm_minobj_fixed_param1 = minimum(gcm_objective_fixed_param1_in_bd)
    gcm_maxobj_fixed_param1 = maximum(gcm_objective_fixed_param1_in_bd)
    println("minimum of fixed param1 gcm objective", gcm_minobj_fixed_param1)
    println("maximum of fixed param1 gcm objective", gcm_maxobj_fixed_param1)

    gr(size=(500,500))
    circ=Shape(Plots.partialcircle(0, 2π))

    #plot fixed_param2 plot
    plot(param1vals_raw,
         gp_objective_fixed_param2,
         color=:orange,
         linewidth=4,
         dpi=300,
         framestyle=:box,
         grid=false,
         legend=false,
         left_margin=50px,
         bottom_margin=50px)

    plot!(gcm_params_fixed_param2_in_bd,
          gcm_objective_fixed_param2_in_bd,
          seriestype=:scatter,
          markershape=:circ,
          markercolor=:grey,
          markersize=6,
          msw=0)

    vline!([true_params_raw[1]],color=:blue,linealpha=0.5,linestyle=:dash)

    xlabel!("param1")
    ylabel!("objective")
    savefig(outdir*"objective_fixed_param2.pdf")

    #plot fixed_param1 plot
    plot(param2vals_raw,
         gp_objective_fixed_param1,
         label="GP",
         color=:orange,
         linewidth=4,
             dpi=300,
         framestyle=:box,
         grid=false,
         left_margin=50px,
         bottom_margin=50px)

    plot!(gcm_params_fixed_param1_in_bd,
          gcm_objective_fixed_param1_in_bd,
          seriestype=:scatter,
          label="GCM",
          markershape=:circle,
          markercolor=:grey,
          markersize=6,
          msw=0)

    vline!([true_params_raw[2]/hour],color=:blue,linealpha=0.5,linestyle=:dash,label="")

    xlabel!("param2")
    ylabel!("objective")
    savefig(outdir*"objective_fixed_param1.pdf")

end

main()


text:
module DistributionsDiff

using Distributions
import ChainRulesCore
const CRC = ChainRulesCore
using ChainRulesCore: NO_FIELDS, @thunk

using SpecialFunctions: loggamma, digamma

include("univariates.jl")
include("logpdf.jl")
end # module

ext: jl


text:
function opamp_nondet(; X0 = Interval(0.0, 0.0),
                        R₁ = 2., R₂ = 6., C = 1.e-3,
                        Ein = Interval(1.9, 2.1))

    α = hcat(-1/(R₂ * C))
    β = hcat(-1/(R₁ * C))

    # continuous evolution
    invariant = Universe(1) # no state constraint
    sys = @system(eout' = α*eout + β*ein, ein ∈ Ein, eout ∈ invariant)

    # initial-value problem
    return @ivp(sys, x(0) ∈ X0)
end

sol_nondet = solve(opamp_nondet(), T=0.1, alg=INT(δ=1e-4));

fig = plot()
plot!(fig, sol_nondet, vars=(0, 1), xlab=L"t", ylab=L"e_{out}", title="Solution for non-deterministic input", lw=0.2)

fig = plot(xlab=L"t", ylab=L"e_{out}(t)", title="Solution for non-deterministic input")

Δt = 0 .. 0.04

sol = solve(opamp_nondet(X0=Interval(-1.0, 1.0)), T=0.1, alg=INT(δ=1e-4));
plot!(fig, sol(Δt), vars=(0, 1), lab="X0 = -1 .. 1", lw=0.2)

sol = solve(opamp_nondet(X0=Interval(-0.5, 0.5)), T=0.1, alg=INT(δ=1e-4));
plot!(fig, sol(Δt), vars=(0, 1), lab="X0 = -0.5 .. 0.5", lw=0.2)

sol = solve(opamp_nondet(), T=0.1, alg=INT(δ=1e-4));
plot!(fig, sol(Δt), vars=(0, 1), lab="X0 = 0", lw=0.2)

fig

using Symbolics

function opamp_with_saturation(; X0 = BallInf(zeros(2), 0.0),
                                 R₁ = 2., R₂ = 6., C = 1.e-3,
                                 γ = 100., δ = 100., Es = 2.)
    var = @variables eₒ ein
    α = hcat(-1/(R₂ * C))
    β = hcat(-1/(R₁ * C))

    # transition graph
    automaton = LightAutomaton(2)
    add_transition!(automaton, 1, 2, 1)

    # modes
    A = [α β; 0. γ]
    b = [0.; δ]
    invariant = HalfSpace(ein <= Es, var)
    mode1 = @system(x' = Ax + b, x ∈ invariant)
    A = [α β; 0. 0.];
    mode2 = @system(x' = Ax, x ∈ Universe(2))
    modes = [mode1, mode2]

    # transition mode1 -> mode2 (saturation)
    guard = Hyperplane(ein == Es, var)
    t1 = @map(x -> x, dim:2, x ∈ guard)
    resetmaps = [t1]

    H = HybridSystem(automaton, modes, resetmaps, [AutonomousSwitching()])

    # initial condition is at the origin in mode 1
    initial_condition = [(1, X0)]
    return IVP(H, initial_condition)
end

X0 = Hyperrectangle([0., 1.5], [0.0, 0.1]);

prob_const = opamp_with_saturation(X0=X0, γ = 0., δ = 0., Es = 3.)
sol_const = solve(prob_const, T=0.1, alg=BOX(δ=1e-4));

plot(sol_const, vars=(0, 2), xlab=L"t", ylab=L"e_{in}(t)", title="Constant input signal", lw=0.2)
plot!(x->x, x->1.5, 0., 0.1, line=2, color="red", linestyle=:dash, legend=nothing)

plot(sol_const, vars=(0, 1), xlab=L"t", ylab=L"e_{out}(t)", title="Solution for constant input", lw=0.2)

X0 = Singleton(zeros(2));

# linearly increasing input signal
prob_lin = opamp_with_saturation(X0=X0, γ=0., δ=100., Es=1.);
sol_lin = solve(prob_lin, T=0.1, alg=BOX(δ=1e-4));

# exponentially increasing input signal
prob_exp = opamp_with_saturation(X0=X0, γ=-100., δ=100., Es=3.);
sol_exp = solve(prob_exp, T=0.1, alg=BOX(δ=1e-4));

plot(sol_lin, vars=(0, 2), xlab=L"t", ylab=L"e_{in}(t)", title="Linear input w/saturation", lw=0.2)

plot(sol_exp, vars=(0, 2), xlab=L"t", ylab=L"e_{in}(t)", title="Exponential input", lw=0.2)

plot(sol_lin, vars=(0, 1), xlab=L"t", ylab=L"e_{out}(t)", title="Solution for linear input", lw=0.2)

plot(sol_exp, vars=(0, 1), xlab=L"t", ylab=L"e_{out}(t)", title="Solution for exponential input", lw=0.2)


text:
"Fetch driver by index"
getdriver(i::Integer) = Driver(GDAL.getdriver(i))

"Fetch a driver based on the short name (such as `GTiff`)."
getdriver(name::AbstractString) = Driver(GDAL.getdriverbyname(name))

"""
Destroy a `GDALDriver`.

This is roughly equivalent to deleting the driver, but is guaranteed to take
place in the GDAL heap. It is important this that function not be called on a
driver that is registered with the `GDALDriverManager`.
"""
destroy(drv::Driver) = (GDAL.destroydriver(drv.ptr); drv.ptr = C_NULL)

"Register a driver for use."
register(drv::Driver) = GDAL.registerdriver(drv.ptr)

"Deregister the passed drv."
deregister(drv::Driver) = GDAL.deregisterdriver(drv.ptr)

"Return the list of creation options of the driver [an XML string]"
options(drv::Driver) = GDAL.getdrivercreationoptionlist(drv.ptr)
driveroptions(name::AbstractString) = options(getdriver(name))

"Return the short name of a driver (e.g. `GTiff`)"
shortname(drv::Driver) = GDAL.getdrivershortname(drv.ptr)

"Return the long name of a driver (e.g. `GeoTIFF`), or empty string."
longname(drv::Driver) = GDAL.getdriverlongname(drv.ptr)

"Fetch the number of registered drivers."
ndriver() = GDAL.getdrivercount()

"Returns a listing of all registered drivers"
listdrivers() = Dict{String,String}([
    shortname(getdriver(i)) => longname(getdriver(i)) for i in 0:(ndriver()-1)
])

"""
Identify the driver that can open a raster file.

This function will try to identify the driver that can open the passed filename
by invoking the Identify method of each registered `GDALDriver` in turn. The
first driver that successful identifies the file name will be returned. If all
drivers fail then `NULL` is returned.
"""
identifydriver(filename::AbstractString) =
    Driver(GDAL.identifydriver(filename, C_NULL))

"""
Validate the list of creation options that are handled by a drv.

This is a helper method primarily used by `create()` and `createcopy()` to
validate that the passed in list of creation options is compatible with the
`GDAL_DMD_CREATIONOPTIONLIST` metadata item defined by some drivers.

### Parameters
* `drv`     the handle of the driver with whom the lists of creation option
            must be validated
* `options` the list of creation options. An array of strings, whose last
            element is a `NULL` pointer

### Returns
`true` if the list of creation options is compatible with the `create()` and
`createCopy()` method of the driver, `false` otherwise.

### Additional Remarks
See also: `options(drv::Driver)`

If the `GDAL_DMD_CREATIONOPTIONLIST` metadata item is not defined, this
function will return `TRUE`. Otherwise it will check that the keys and values
in the list of creation options are compatible with the capabilities declared
by the `GDAL_DMD_CREATIONOPTIONLIST` metadata item. In case of incompatibility
a (non fatal) warning will be emited and `FALSE` will be returned.
"""
validate(drv::Driver, options::Vector{T}) where {T <: AbstractString} =
    Bool(@gdal(GDALValidateCreationOptions::Cint,
        drv.ptr::GDALDriver,
        options::StringList
    ))

"Copy all the files associated with a dataset."
function copyfiles(drv::Driver, new::AbstractString, old::AbstractString)
    result = GDAL.copydatasetfiles(drv.ptr, new, old)
    @cplerr result "Failed to copy dataset files"
end

"Copy all the files associated with a dataset."
copyfiles(drvname::AbstractString, new::AbstractString, old::AbstractString) =
    copyfiles(getdriver(drvname), new, old)


text:
module Glo

using MacroTools
using MacroTools: striplines
using JSON
using HTTP
using DocStringExtensions
# Kindly adopted form Lyndon White (https://github.com/oxinabox/RESTful.jl/blob/master/src/proto.ipynb)
function declare_api(root, method, endpoint, param_names)
    page_paraval = Symbol[]
    page_params = Expr[]
    page_indxs = [1]
    for match in eachmatch(r"\{([A-z]+)\}", endpoint)
        cap = match.captures[1]
        cap_type = join((x->[uppercasefirst(x[1]),uppercase(x[2])])(split(cap,"_")))
        push!( page_paraval, Symbol(cap[1:end-3]) )
        push!( page_params, Expr(Symbol("::"),Symbol(cap),Symbol(cap_type) ) )
        append!( page_indxs, [match.offset-1, match.offset+1+length(match.match)] )
    end
    push!(page_indxs, lastindex(endpoint))
    endpoint = join( [ endpoint[page_indxs[i]:page_indxs[i+1]] for i in firstindex(page_indxs):2:lastindex(page_indxs) ] )
    function_name = Symbol(join(split(endpoint, "/"; keepempty=false), "_"))
    param_sig = Expr(:parameters, Expr(:kw, :header, String[]), Expr.(:kw, Symbol.(param_names), :nothing)...)

    set_query_code = Expr(:block)
    set_query_code.args = map(param_names) do param
        quote
            if $(Symbol(param)) != nothing
                query[$(String(param))] = $(Symbol(param))
            end
        end |> MacroTools.unblock
    end

    set_endpoint_code = Expr(:block)
    set_endpoint_code.args = map( page_paraval ) do p
        quote
            idx = findfirst(s->occursin($(String(p)),s), endpoint)
            endpoint = insert!( endpoint, idx + 1, getproperty($(Symbol(String(p)*"_id") ), :id ) )
        end |> MacroTools.unblock
    end

    if method == "POST"
        quote
            """
            $($DocStringExtensions.SIGNATURES)
            $($(String(method)))s `post_msg` to `$($function_name)` by `$(join(Symbol.($page_params),"`, `", "` and `"))`.
            """
            function $(function_name)($(param_sig),post_msg::Any,$(page_params...))
                query = Dict()
                $(set_query_code)
                endpoint = split($endpoint,"/")
                $(set_endpoint_code)
                endpoint = join( endpoint, "/" )
                uri = HTTP.URI($root)
                uri = merge(uri; path=uri.path*endpoint, query=query)
                resp = HTTP.request($method, uri, header, json(post_msg))
                JSON.parse(String(resp.body))
            end
        end|> MacroTools.unblock
    elseif method == "DELETE"
        quote
            """
            $($DocStringExtensions.SIGNATURES)
            $($(String(method)))s `$($function_name)` by `$(join(Symbol.($page_params),"`, `", "` and `"))`.
            """
            function $(function_name)($(param_sig),bang::typeof(!),$(page_params...))
                query = Dict()
                $(set_query_code)
                endpoint = split($endpoint,"/")
                $(set_endpoint_code)
                endpoint = join( endpoint, "/" )
                uri = HTTP.URI($root)
                uri = merge(uri; path=uri.path*endpoint, query=query)
                resp = HTTP.request($method, uri, header)
                resp != "" && return JSON.parse(String(resp.body))
                return nothing
            end
        end|> MacroTools.unblock
    else
        quote
            """
            $($DocStringExtensions.SIGNATURES)
            $($(String(method)))s `$($function_name)` by `$(join(Symbol.($page_params),"`, `", "` and `"))`.
            """
            function $(function_name)($(param_sig),$(page_params...))
                query = Dict()
                $(set_query_code)
                endpoint = split($endpoint,"/")
                $(set_endpoint_code)
                endpoint = join( endpoint, "/" )
                uri = HTTP.URI($root)
                uri = merge(uri; path=uri.path*endpoint, query=query)
                resp = HTTP.request($method, uri, header)
                JSON.parse(String(resp.body))
            end
        end|> MacroTools.unblock
    end
end

include("declarations.jl")
end # module


text:
# Misc post-processing functions

export catchment, catchments, catchment_flux, prune_catchments, fill_dem

"""
    prune_catchments(catchments, minsize; val=0)

Sets all catchments which are smaller than minsize to `val` (=0).
Catchments with number <1 are ignored.

Note that, as the catchments are re-numbered, the number will not correspond
to the pits anymore.
"""
function prune_catchments(catchments, minsize; val=0)
    c = copy(catchments)
    n1 = 1
    n2 = maximum(catchments)
    colormap = collect(n1:n2)
    for i=n1:n2
        if sum(x->x==i, catchments) < minsize
            colormap[i] = val
        end
    end

    # make new colors consecutive
    newcolors = unique(colormap)
    d = Dict((c=>i for (i,c) in enumerate(newcolors)))
    colormap2 = [d[c] for c in colormap]

    for i=eachindex(c)
        if c[i]>0
            c[i] = colormap2[c[i]]
        end
    end

    return c
end

"""
    catchment(dir, ij)

Calculates the catchment of one or several grid-point ij.  If desired, its
boundary can be calculated with `make_boundaries([c], [1])`.

Input
- dir -- direction field
- ij -- index of the point (2-Tuple, or CartesianIndex) or a Vector{CartesianIndex} for several

Returns
- catchment -- BitArray

Tip: only being off by one grid-point can make the difference
     between a tiny and a huge catchment!

See also: `catchments`
"""
catchment(dir, ij::Tuple) = catchment(dir, CartesianIndex(ij...))
function catchment(dir, ij::CartesianIndex)
    c = falses(size(dir))
    # recursively traverse the drainage tree in up-flow direction,
    # starting at ij
    _catchment!(c, dir, ij)
    return c
end
function catchment(dir, ijs::Union{<:Array{CartesianIndex{2}}, CartesianIndices{2}})
    cout = falses(size(dir))
    for ij in ijs
        _catchment!(cout, dir, ij)
    end
    return cout
end
function _catchment!(c, dir, ij)
    c[ij] = true
    # proc upstream points
    for IJ in iterate_D9(ij, c)
        ij==IJ && continue
        c[IJ] && continue # this hits a point which is already in the catchment,
                          # thus nothing more to do
        if flowsinto(IJ, dir[IJ], ij)
            _catchment!(c, dir, IJ)
        end
    end
end

"""
    catchment_flux(cellarea, c, color) = sum(cellarea[c.==color])
    catchment_flux(cellarea, c::Union{BitArray, Matrix{Bool}})

The total flux, i.e. input, in one catchment.
"""
catchment_flux(cellarea, c, color) = sum(cellarea[c.==color])
catchment_flux(cellarea, c::Union{BitArray, Matrix{Bool}}) = sum(cellarea[c])

"""
    catchments(dir, sinks::Union{Vector{Vector{CartesianIndex{2}}}, Vector{<:CartesianIndices{2}}};
                    check_catchments_overlap=true)

Make a map of catchments from different (non-overlapping) sinks.

See also: `catchment`
"""
function catchments(dir, sinks::Union{Vector{Vector{CartesianIndex{2}}}, Vector{<:CartesianIndices{2}}};
                    check_catchments_overlap=true)

    ncs = length(sinks)
    if check_catchments_overlap
        for s in sinks
            for loc in s
                for ss in sinks
                    ss===s && continue
                    loc in ss && error("Detected overlapping catchments.")
                end
            end
        end
    end
    @assert ncs<255 "More than 255 sinks not supported (yet)." # then use something else than UInt8
    out = zeros(UInt8, size(dir))
    for (i,s) in enumerate(sinks)
        c = catchment(dir, s)
        out += c*i
    end
    return out
end

"""
    fill_dem(dem, pits, dir)

Fill the pits (aka sinks) of a DEM (apply this after applying "drainpits",
which is the default). Returns the filled DEM.

Note, this is not needed as pre-processing step to use `upstream` area.

This uses a tree traversal to fill the DEM. It does it depth-first (as it
is easier) which may lead to a stack overflow on a large DEM.
"""
function fill_dem(dem, pits, dir; small=0.0)
    dem = copy(dem)
    # could use threading.
    Threads.@threads for pit in pits
        npts = _fill_ij!(0.0, dem, pit, dir, small)
    end
    return dem
end

function _fill_ij!(ele, dem, ij, dir, small)
    if ele > dem[ij]
        dem[ij] = ele
        ele += small
    else
        ele = dem[ij]
    end
    # proc upstream points
    for IJ in iterate_D9(ij, dem)
        ij==IJ && continue
        if flowsinto(IJ, dir[IJ], ij)
            _fill_ij!(ele, dem, IJ, dir, small)
        end
    end
end


text:
function _scale_value(k::T1, from::NTuple{2,T2}, to::NTuple{2,T3}) where {T1 <: Number, T2 <: Number, T3 <: Number}
    m, M = from
    l, U = to
    inter = (k-m)/(M-m)
    return inter*(U-l)+l
end

function finish_layout!(L; link=false)
    x = getfield.(values(L), :x)
    y = getfield.(values(L), :y)
    range_x = (minimum(x), maximum(x))
    range_y = (minimum(y), maximum(y))
    range_total = (minimum([range_x..., range_y...]), maximum([range_x..., range_y...]))
    if link
        range_x = range_total
        range_y = range_total
    end
    distribute_layout!(L, range_x, range_y)
end

function distribute_layout!(L, X::NTuple{2,T}, Y::NTuple{2,T}) where {T <: Number}
    x = getfield.(values(L), :x)
    y = getfield.(values(L), :y)
    range_x = (minimum(x), maximum(x))
    range_y = (minimum(y), maximum(y))
    for k in keys(L)
        L[k].x = EcologicalNetworksPlots._scale_value(L[k].x, range_x, X)
        L[k].y = EcologicalNetworksPlots._scale_value(L[k].y, range_y, Y)
    end
end

function _spread_levels!(L; ratio::Float64=1.0)
    for s in keys(L)
        L[s].y *= ratio
    end
end


text:
"""
collecting all needed  functions required to calculate Housdorff distance
"""
module Housdorff
using CUDA
using ..CUDAGpuUtils ,..IterationUtils,..ReductionUtils , ..MemoryUtils,..CUDAAtomicUtils
using ..BitWiseUtils,..MetadataAnalyzePass,..MetaDataUtils,..WorkQueueUtils,..ProcessMainDataVerB,..HFUtils,..ResultListUtils,..PrepareArrtoBool, ..MainLoopKernel,..ScanForDuplicates
export get_shmemMainKernel, getHousedorffDistance,boolKernelLoad,mainKernelLoad,get_shmemMainKernel,get_shmemBoolKernel,preparehousedorfKernel
"""
calculate housedorff distance of given arrays with given robustness percentage

"""
function getHousedorffDistance(goldGPUa,segmGPUa,boolKernelArgs,mainKernelArgs,threadsBoolKern,blocksBoolKern ,threadsMainKern,blocksMainKern,shmemSizeBool,shmemSizeMain)
    # boolKernelArgs[1]= goldGPU
    # boolKernelArgs[2]= segmGPU
    mainArrDims,dataBdim,metaData,metaDataDims,reducedGoldA,reducedSegmA,loopXinPlane,loopYinPlane,minxRes,maxxRes,minyRes,maxyRes,minzRes,maxzRes,fn,fp ,numberToLooFor,inBlockLoopXZIterWithPadding,shmemblockDataLoop,shmemblockDataLenght,loopAXFixed,loopBXfixed,loopAYFixed,loopBYfixed,loopAZFixed,loopBZfixed,loopdataDimMainX,loopdataDimMainY,loopdataDimMainZ,inBlockLoopX,inBlockLoopY,inBlockLoopZ,metaDataLength,loopMeta,loopWarpMeta,clearIterResShmemLoop,clearIterSourceShmemLoop,resShmemTotalLength,sourceShmemTotalLength = boolKernelArgs

    @cuda threads=threadsBoolKern blocks=blocksBoolKern shmem=shmemSizeBool  cooperative=true boolKernelLoad(goldGPUa,segmGPUa,boolKernelArgs...)
    #now time to get data structures dependent on bool kernel like for example loading subsections of meta data, creating work queue ...
    #some arrays needs to be instantiated only after we know the number of the false and true positives
    metaData,reducedGoldA  ,reducedSegmA ,paddingStore,resList,workQueue,workQueueCounter= getBigGPUForHousedorffAfterBoolKernel(metaData,minxRes,maxxRes,minyRes,maxyRes,minzRes,maxzRes,fn,fp,reducedGoldA,reducedSegmA,dataBdim)
    referenceArrs,dilatationArrs, mainArrDims,dataBdim,numberToLooFor,metaDataDims,metaData,iterThrougWarNumb,robustnessPercent,shmemSumLengthMaxDiv4,globalFpResOffsetCounter,globalFnResOffsetCounter    ,globalIterationNumber,globalCurrentFnCount,globalCurrentFpCount    ,globalIterationNumb,workQueue,workQueueCounter,loopAXFixed,loopBXfixed,loopAYFixed,loopBYfixed,loopAZFixed,loopBZfixed    ,loopdataDimMainX,loopdataDimMainY,loopdataDimMainZ,inBlockLoopX,inBlockLoopY,inBlockLoopZ,metaDataLength,loopMeta,loopWarpMeta,clearIterResShmemLoop    ,clearIterSourceShmemLoop,resShmemTotalLength,sourceShmemTotalLength, fn,fp,resList,inBlockLoopXZIterWithPadding,paddingStore,shmemblockDataLenght,shmemblockDataLoop= mainKernelArgs

    dilatationArrs= (reducedGoldA,reducedSegmA)
    #reverse order as when dilatating gold we want to establish do we cover segm voxels
    referenceArrs=(segmGPUa,goldGPUa )

    #main calculations
    @cuda threads=threadsMainKern blocks=blocksMainKern shmem=shmemSizeMain cooperative=true mainKernelLoadB( referenceArrs,dilatationArrs, mainArrDims,dataBdim
    ,numberToLooFor,metaDataDims,metaData,iterThrougWarNumb,robustnessPercent
    ,shmemSumLengthMaxDiv4,globalFpResOffsetCounter,globalFnResOffsetCounter
    ,globalIterationNumber,globalCurrentFnCount,globalCurrentFpCount
    ,globalIterationNumb,workQueue,workQueueCounter
    ,loopAXFixed,loopBXfixed,loopAYFixed,loopBYfixed,loopAZFixed,loopBZfixed
    ,loopdataDimMainX,loopdataDimMainY,loopdataDimMainZ,inBlockLoopX,inBlockLoopY
    ,inBlockLoopZ,metaDataLength,loopMeta,loopWarpMeta,clearIterResShmemLoop
    ,clearIterSourceShmemLoop,resShmemTotalLength,sourceShmemTotalLength, fn,fp,resList,inBlockLoopXZIterWithPadding,paddingStore,shmemblockDataLenght,shmemblockDataLoop)
        #@cuda threads=threadsMainKern blocks=blocksMainKern shmem=shmemSizeMain cooperative=true mainKernelLoad(dilatationArrs,referenceArrs, mainArrDims,dataBdim,numberToLooFor,metaDataDims,metaData,iterThrougWarNumb,robustnessPercent,shmemSumLengthMaxDiv4,globalFpResOffsetCounter,globalFnResOffsetCounter,globalIterationNumber,globalCurrentFnCount,globalCurrentFpCount,globalIterationNumb,workQueaue,resList,resListIndicies,maxResListIndex,inBlockLoopXZIterWithPadding,shmemblockDataLoop,shmemblockDataLenght,loopAXFixed,loopBXfixed,loopAYFixed,loopBYfixed,loopAZFixed,loopBZfixed,loopdataDimMainX,loopdataDimMainY,loopdataDimMainZ,inBlockLoopX,inBlockLoopY,inBlockLoopZ,metaDataLength,loopMeta,loopWarpMeta,clearIterResShmemLoop,clearIterSourceShmemLoop,resShmemTotalLength,sourceShmemTotalLength, fn,fp)
    return globalIterationNumb

    end

"""
clearing data before next execution
"""
function housClearForExecution()

end

"""
for invoking getBoolCubeKernel
"""
function boolKernelLoad(goldGPU,segmGPU,mainArrDims,dataBdim,metaData,metaDataDims,reducedGoldA,reducedSegmA,loopXinPlane,loopYinPlane,minxRes,maxxRes,minyRes,maxyRes,minzRes,maxzRes,fn,fp ,numberToLooFor,inBlockLoopXZIterWithPadding,shmemblockDataLoop,shmemblockDataLenght,loopAXFixed,loopBXfixed,loopAYFixed,loopBYfixed,loopAZFixed,loopBZfixed,loopdataDimMainX,loopdataDimMainY,loopdataDimMainZ,inBlockLoopX,inBlockLoopY,inBlockLoopZ,metaDataLength,loopMeta,loopWarpMeta,clearIterResShmemLoop,clearIterSourceShmemLoop,resShmemTotalLength,sourceShmemTotalLength)
    @getBoolCubeKernel()
    return
end
"""
main function responsible for calculations of Housedorff distance
"""
function mainKernelLoadB(referenceArrs,dilatationArrs, mainArrDims,dataBdim
    ,numberToLooFor,metaDataDims,metaData,iterThrougWarNumb,robustnessPercent
    ,shmemSumLengthMaxDiv4,globalFpResOffsetCounter,globalFnResOffsetCounter
    ,globalIterationNumber,globalCurrentFnCount,globalCurrentFpCount
    ,globalIterationNumb,workQueue,workQueueCounter
    ,loopAXFixed,loopBXfixed,loopAYFixed,loopBYfixed,loopAZFixed,loopBZfixed
    ,loopdataDimMainX,loopdataDimMainY,loopdataDimMainZ,inBlockLoopX,inBlockLoopY
    ,inBlockLoopZ,metaDataLength,loopMeta,loopWarpMeta,clearIterResShmemLoop
    ,clearIterSourceShmemLoop,resShmemTotalLength,sourceShmemTotalLength, fn,fp,resList,inBlockLoopXZIterWithPadding,paddingStore,shmemblockDataLenght,shmemblockDataLoop)

   #@mainLoopKernel()
    return
end

function mainKernelLoad(referenceArrs,dilatationArrs, mainArrDims,dataBdim
    ,numberToLooFor,metaDataDims,metaData,iterThrougWarNumb,robustnessPercent
    ,shmemSumLengthMaxDiv4,globalFpResOffsetCounter,globalFnResOffsetCounter
    ,globalIterationNumber,globalCurrentFnCount,globalCurrentFpCount
    ,globalIterationNumb,workQueue,workQueueCounter
    ,loopAXFixed,loopBXfixed,loopAYFixed,loopBYfixed,loopAZFixed,loopBZfixed
    ,loopdataDimMainX,loopdataDimMainY,loopdataDimMainZ,inBlockLoopX,inBlockLoopY
    ,inBlockLoopZ,metaDataLength,loopMeta,loopWarpMeta,clearIterResShmemLoop
    ,clearIterSourceShmemLoop,resShmemTotalLength,sourceShmemTotalLength, fn,fp,resList,inBlockLoopXZIterWithPadding,paddingStore,shmemblockDataLenght,shmemblockDataLoop)
    @mainLoopKernel()
    return
end

function get_shmemMainKernel(dataBdim)

    shmemblockData= sizeof(UInt32)*32*32
    # shmemblockData= sizeof(UInt32)*dataBdim[1]* dataBdim[2]
    resShmemblockData= sizeof(UInt32)*32*32
    # resShmemblockData= sizeof(UInt32)*dataBdim[1]* dataBdim[2]
    shmemPaddings= sizeof(Bool)*(  max(dataBdim[1], dataBdim[2]))*(  max(dataBdim[1], dataBdim[2]))*6
    shmemSum= sizeof(UInt32)*36*16
    areToBeValidated= sizeof(Bool)*14
    isAnythingInPadding= sizeof(Bool)*7
    alreadyCoveredInQueues= sizeof(UInt32)*14
    someBools = sizeof(Bool)*4
    someInt16 = sizeof(UInt16)*6
    workCountersInshmem = sizeof(UInt16)*2
return workCountersInshmem+shmemSum+areToBeValidated+isAnythingInPadding+alreadyCoveredInQueues+someBools+shmemblockData+someInt16+resShmemblockData+shmemPaddings
end

function get_shmemBoolKernel(dataBdim)
    # shmemSum= sizeof(Float32)*32*2
    shmemblockData= sizeof(Int32)*(dataBdim[1])*(dataBdim[2])
    localQuesValues = sizeof(UInt32)*14
    minMaxes = sizeof(Float32)*6
return minMaxes+localQuesValues+shmemblockData
end

"""
creates required cu arrays , calculates some kernel constants and uses occupancy API
to calculate optimal number of threads and blocks to run a kernel
robustnessPercent - frequently we do not want to analyze all of the fap and fn in order to reduce the impact of the outliers
numberToLooFor - what we will look for in main arrays
"""
function preparehousedorfKernel(goldGPU,segmGPU,robustnessPercent,numberToLooFor)
    mainArrDims = size(goldGPU)
    dataBdim = (32,32,32) # will be modified after number of threads gets calculated by occupancy API

    metaData = MetaDataUtils.allocateMetadata(mainArrDims,dataBdim);
    metaDataDims= size(metaData);
    #for bool cube kernel
    threadsBoolKern= (30,32); blocksBoolKern = 10#just some dummy will be modified after invoking occupancy API
    #for main kernel
    threadsMainKern= (30,32); blocksMainKern = 10#just some dummy will be modified after invoking occupancy API
    iterThrougWarNumb = cld(14,threadsMainKern[2])

    inBlockLoopXZIterWithPadding,shmemblockDataLoop,shmemblockDataLenght,loopAXFixed,loopBXfixed,loopAYFixed,loopBYfixed,loopAZFixed,loopBZfixed,loopdataDimMainX,loopdataDimMainY,loopdataDimMainZ,inBlockLoopX,inBlockLoopY,inBlockLoopZ,metaDataLength,loopMeta,loopWarpMeta,clearIterResShmemLoop,clearIterSourceShmemLoop,resShmemTotalLength,sourceShmemTotalLength=calculateLoopsIter(dataBdim,threadsBoolKern[1],threadsBoolKern[2],metaDataDims,blocksBoolKern)
        minxRes,maxxRes,minyRes,maxyRes,minzRes,maxzRes,fn,fp  =getSmallForBoolKernel();
        reducedGoldA,reducedSegmA=  getLargeForBoolKernel(mainArrDims,dataBdim);

    loopXinPlane,loopYinPlane = 1,1
    boolKernelArgs = (mainArrDims,dataBdim,metaData,metaDataDims,reducedGoldA,reducedSegmA,loopXinPlane,loopYinPlane,minxRes,maxxRes,minyRes,maxyRes,minzRes,maxzRes,fn,fp ,numberToLooFor,inBlockLoopXZIterWithPadding,shmemblockDataLoop,shmemblockDataLenght,loopAXFixed,loopBXfixed,loopAYFixed,loopBYfixed,loopAZFixed,loopBZfixed,loopdataDimMainX,loopdataDimMainY,loopdataDimMainZ,inBlockLoopX,inBlockLoopY,inBlockLoopZ,metaDataLength,loopMeta,loopWarpMeta,clearIterResShmemLoop,clearIterSourceShmemLoop,resShmemTotalLength,sourceShmemTotalLength)
    #needed also in bool kernel

    #### main kernel
    fpLoc = 10
    fnLoc = 10
    resList= allocateResultLists(fpLoc,fnLoc)
    globalFpResOffsetCounter,globalFnResOffsetCounter,globalIterationNumber,globalCurrentFnCount,globalCurrentFpCount,globalIterationNumb= getSmallGPUForHousedorff()

    referenceArrs=(goldGPU,segmGPU)

    inBlockLoopXZIterWithPadding,shmemblockDataLoop,shmemblockDataLenght,loopAXFixed,loopBXfixed,loopAYFixed,loopBYfixed,loopAZFixed,loopBZfixed,loopdataDimMainX,loopdataDimMainY,loopdataDimMainZ,inBlockLoopX,inBlockLoopY,inBlockLoopZ,metaDataLength,loopMeta,loopWarpMeta,clearIterResShmemLoop,clearIterSourceShmemLoop,resShmemTotalLength,sourceShmemTotalLength=calculateLoopsIter(dataBdim,threadsBoolKern[1],threadsBoolKern[2],metaDataDims,blocksBoolKern)
    shmemSumLengthMaxDiv4= fld((36*14),4)*4 # subject to futre changes

    metaData,reducedGoldA  ,reducedSegmA ,paddingStore,resList,workQueue,workQueueCounter = getBigGPUForHousedorffAfterBoolKernel(metaData,minxRes,maxxRes,minyRes,maxyRes,minzRes,maxzRes,fn,fp,reducedGoldA,reducedSegmA,dataBdim)

    reducedGoldA,reducedSegmA=  getLargeForBoolKernel(mainArrDims,dataBdim);

    dilatationArrs= (reducedGoldA,reducedSegmA)

    mainKernelArgs= (referenceArrs,dilatationArrs, mainArrDims,dataBdim
    ,numberToLooFor,metaDataDims,metaData,iterThrougWarNumb,robustnessPercent
    ,shmemSumLengthMaxDiv4,globalFpResOffsetCounter,globalFnResOffsetCounter
    ,globalIterationNumber,globalCurrentFnCount,globalCurrentFpCount
    ,globalIterationNumb,workQueue,workQueueCounter
    ,loopAXFixed,loopBXfixed,loopAYFixed,loopBYfixed,loopAZFixed,loopBZfixed
    ,loopdataDimMainX,loopdataDimMainY,loopdataDimMainZ,inBlockLoopX,inBlockLoopY
    ,inBlockLoopZ,metaDataLength,loopMeta,loopWarpMeta,clearIterResShmemLoop
    ,clearIterSourceShmemLoop,resShmemTotalLength,sourceShmemTotalLength, fn,fp,resList,inBlockLoopXZIterWithPadding,paddingStore,shmemblockDataLenght,shmemblockDataLoop)

    function get_shmemMainKernelLoc(threads)
        dataBdim = (threads[1],threads[2],32)
        get_shmemMainKernel(dataBdim)
    end

    threadsMainKern,blocksMainKern = getThreadsAndBlocksNumbForKernel(get_shmemMainKernel,mainKernelLoad,(mainKernelArgs))
    function get_shmemBoolKernelLoc(threads)
        dataBdim = (threadsMainKern[1],threadsMainKern[2],32)
        get_shmemBoolKernel(dataBdim)
    end

    boolKernelArgs = (mainArrDims,dataBdim,metaData,metaDataDims,reducedGoldA,reducedSegmA,loopXinPlane,loopYinPlane,minxRes,maxxRes,minyRes,maxyRes,minzRes,maxzRes,fn,fp ,numberToLooFor,inBlockLoopXZIterWithPadding,shmemblockDataLoop,shmemblockDataLenght,loopAXFixed,loopBXfixed,loopAYFixed,loopBYfixed,loopAZFixed,loopBZfixed,loopdataDimMainX,loopdataDimMainY,loopdataDimMainZ,inBlockLoopX,inBlockLoopY,inBlockLoopZ,metaDataLength,loopMeta,loopWarpMeta,clearIterResShmemLoop,clearIterSourceShmemLoop,resShmemTotalLength,sourceShmemTotalLength)

     ## now we need to make use of occupancy API to get optimal number of threads and blocks fo each kernel
    threadsBoolKern,blocksBoolKern = getThreadsAndBlocksNumbForKernel(get_shmemBoolKernelLoc,boolKernelLoad,(goldGPU,segmGPU,boolKernelArgs...))

    loopXinPlane,loopYinPlane = fld(threadsMainKern[1],threadsBoolKern[1] ), fld(threadsMainKern[2],threadsBoolKern[2] )
#now we get defoult values of data b dim  set on the basis of the threadsMainHKernel; and generally recalculating loops constants
    dataBdim = (threadsMainKern[1],threadsMainKern[2],32)
    metaData = MetaDataUtils.allocateMetadata(mainArrDims,dataBdim);
    metaDataDims= size(metaData);
    iterThrougWarNumb = cld(14,threadsMainKern[2])

    boolKernelArgs = (mainArrDims,dataBdim,metaData,metaDataDims,reducedGoldA,reducedSegmA,loopXinPlane,loopYinPlane,minxRes,maxxRes,minyRes,maxyRes,minzRes,maxzRes,fn,fp ,numberToLooFor,inBlockLoopXZIterWithPadding,shmemblockDataLoop,shmemblockDataLenght,loopAXFixed,loopBXfixed,loopAYFixed,loopBYfixed,loopAZFixed,loopBZfixed,loopdataDimMainX,loopdataDimMainY,loopdataDimMainZ,inBlockLoopX,inBlockLoopY,inBlockLoopZ,metaDataLength,loopMeta,loopWarpMeta,clearIterResShmemLoop,clearIterSourceShmemLoop,resShmemTotalLength,sourceShmemTotalLength)

    inBlockLoopXZIterWithPadding,shmemblockDataLoop,shmemblockDataLenght,loopAXFixed,loopBXfixed,loopAYFixed,loopBYfixed,loopAZFixed,loopBZfixed,loopdataDimMainX,loopdataDimMainY,loopdataDimMainZ,inBlockLoopX,inBlockLoopY,inBlockLoopZ,metaDataLength,loopMeta,loopWarpMeta,clearIterResShmemLoop,clearIterSourceShmemLoop,resShmemTotalLength,sourceShmemTotalLength=calculateLoopsIter(dataBdim,threadsBoolKern[1],threadsBoolKern[2],metaDataDims,blocksBoolKern)

    boolKernelArgs = (mainArrDims,dataBdim,metaData,metaDataDims,reducedGoldA,reducedSegmA,loopXinPlane,loopYinPlane,minxRes,maxxRes,minyRes,maxyRes,minzRes,maxzRes,fn,fp ,numberToLooFor,inBlockLoopXZIterWithPadding,shmemblockDataLoop,shmemblockDataLenght,loopAXFixed,loopBXfixed,loopAYFixed,loopBYfixed,loopAZFixed,loopBZfixed,loopdataDimMainX,loopdataDimMainY,loopdataDimMainZ,inBlockLoopX,inBlockLoopY,inBlockLoopZ,metaDataLength,loopMeta,loopWarpMeta,clearIterResShmemLoop,clearIterSourceShmemLoop,resShmemTotalLength,sourceShmemTotalLength)
    metaData,reducedGoldA  ,reducedSegmA ,paddingStore,resList,workQueue,workQueueCounter= getBigGPUForHousedorffAfterBoolKernel(metaData,minxRes,maxxRes,minyRes,maxyRes,minzRes,maxzRes,fn,fp,reducedGoldA,reducedSegmA,dataBdim)

    reducedGoldA,reducedSegmA=  getLargeForBoolKernel(mainArrDims,dataBdim);

    mainKernelArgs= (referenceArrs,dilatationArrs, mainArrDims,dataBdim
    ,numberToLooFor,metaDataDims,metaData,iterThrougWarNumb,robustnessPercent
    ,shmemSumLengthMaxDiv4,globalFpResOffsetCounter,globalFnResOffsetCounter
    ,globalIterationNumber,globalCurrentFnCount,globalCurrentFpCount
    ,globalIterationNumb,workQueue,workQueueCounter
    ,loopAXFixed,loopBXfixed,loopAYFixed,loopBYfixed,loopAZFixed,loopBZfixed
    ,loopdataDimMainX,loopdataDimMainY,loopdataDimMainZ,inBlockLoopX,inBlockLoopY
    ,inBlockLoopZ,metaDataLength,loopMeta,loopWarpMeta,clearIterResShmemLoop
    ,clearIterSourceShmemLoop,resShmemTotalLength,sourceShmemTotalLength, fn,fp,resList,inBlockLoopXZIterWithPadding,paddingStore,shmemblockDataLenght,shmemblockDataLoop)
    shmemSizeBool=get_shmemBoolKernelLoc(threadsBoolKern)
    shmemSizeMain=get_shmemMainKernelLoc(threadsMainKern)

    # CUDA.unsafe_free!(goldGPU)
    # CUDA.unsafe_free!(segmGPU)
    #CUDA.reclaim()
return (boolKernelArgs, mainKernelArgs,threadsBoolKern,blocksBoolKern ,threadsMainKern,blocksMainKern ,shmemSizeBool,shmemSizeMain)

end

end# module


text:
#=

  Euler #23 in Julia.

  Problem 23:
  """
  A perfect number is a number for which the sum of its proper divisors
  is exactly equal to the number. For example, the sum of the proper divisors
  of 28 would be 1 + 2 + 4 + 7 + 14 = 28, which means that 28 is a perfect number.

  A number n is called deficient if the sum of its proper divisors is less than
  n and it is called abundant if this sum exceeds n.

  As 12 is the smallest abundant number, 1 + 2 + 3 + 4 + 6 = 16, the smallest number
  that can be written as the sum of two abundant numbers is 24. By mathematical
  analysis, it can be shown that all integers greater than 28123 can be written
  as the sum of two abundant numbers. However, this upper limit cannot be reduced
  any further by analysis even though it is known that the greatest number that
  cannot be expressed as the sum of two abundant numbers is less than this limit.

  Find the sum of all the positive integers which cannot be written as the sum of
  two abundant numbers.
  """

  This Julia program was created by Hakan Kjellerstrand, hakank@gmail.com
  See also my Julia page: http://www.hakank.org/julia/

=#

include("Euler.jl")

#
# Note: This is a solution ported from my JavaScript program via Picat code
# which in turn is from some C++(?) code,
# 0.00790154s
function euler23a()
    limit = 20161;
    arr = ones(limit)
    for i in 2:limit
        for j in i*2:i:limit
            arr[j] = arr[j] + i
        end
    end

    abundant = [i for i in 12:limit if arr[i] > i]
    for a in abundant
        for b in abundant
            if b > a || a + b >= limit
                break;
            else
                arr[a + b] = 0
            end
        end
    end

    return sum([i for i in 1:limit if arr[i] != 0])
end

run_euler(euler23a)

max_stars_repo_name: Wikunia/hakank

max_issues_repo_name: Wikunia/hakank

max_forks_repo_name: Wikunia/hakank


text:
abstract type AbstractMaintenance end

@columns struct Maintenance{MoMoD} <: AbstractMaintenance
    j_E_mai::MoMoD       | 0.01 | mol*mol^-1*d^-1 | Beta(2.0, 2.0)  | [1e-4, 1.0] | true | "Spec somatic maint costs."
end

"""
Allocates reserve drain due to maintenance.
"""
maintenence!(o, u) = maintenence!(maintenance_pars(o), o, u)
maintenence!(f::Maintenance, o, u) = begin
    drain = j_E_mai(f) * tempcorrection(o) * u.V
    maint_prod = maintenance_production!(o, u)
    reserve_drain!(o, Val(:mai), drain)
    # reserve_loss!(o, drain - maint_prod) # all maintenance is loss
end

j_E_mai(f::Maintenance) = f.j_E_mai


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

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

Sets the AWS Firewall Manager administrator account. AWS Firewall Manager must be
associated with the master account of your AWS organization or associated with a member
account that has the appropriate permissions. If the account ID that you submit is not an
AWS Organizations master account, AWS Firewall Manager will set the appropriate permissions
for the given member account. The account that you associate with AWS Firewall Manager is
called the AWS Firewall Manager administrator account.

# Arguments
- `admin_account`: The AWS account ID to associate with AWS Firewall Manager as the AWS
  Firewall Manager administrator account. This can be an AWS Organizations master account or
  a member account. For more information about AWS Organizations and master accounts, see
  Managing the AWS Accounts in Your Organization.

"""
associate_admin_account(AdminAccount; aws_config::AbstractAWSConfig=global_aws_config()) = fms("AssociateAdminAccount", Dict{String, Any}("AdminAccount"=>AdminAccount); aws_config=aws_config)
associate_admin_account(AdminAccount, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = fms("AssociateAdminAccount", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("AdminAccount"=>AdminAccount), params)); aws_config=aws_config)

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

Permanently deletes an AWS Firewall Manager applications list.

# Arguments
- `list_id`: The ID of the applications list that you want to delete. You can retrieve this
  ID from PutAppsList, ListAppsLists, and GetAppsList.

"""
delete_apps_list(ListId; aws_config::AbstractAWSConfig=global_aws_config()) = fms("DeleteAppsList", Dict{String, Any}("ListId"=>ListId); aws_config=aws_config)
delete_apps_list(ListId, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = fms("DeleteAppsList", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("ListId"=>ListId), params)); aws_config=aws_config)

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

Deletes an AWS Firewall Manager association with the IAM role and the Amazon Simple
Notification Service (SNS) topic that is used to record AWS Firewall Manager SNS logs.

"""
delete_notification_channel(; aws_config::AbstractAWSConfig=global_aws_config()) = fms("DeleteNotificationChannel"; aws_config=aws_config)
delete_notification_channel(params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = fms("DeleteNotificationChannel", params; aws_config=aws_config)

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

Permanently deletes an AWS Firewall Manager policy.

# Arguments
- `policy_id`: The ID of the policy that you want to delete. You can retrieve this ID from
  PutPolicy and ListPolicies.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"DeleteAllPolicyResources"`: If True, the request performs cleanup according to the
  policy type.  For AWS WAF and Shield Advanced policies, the cleanup does the following:
  Deletes rule groups created by AWS Firewall Manager   Removes web ACLs from in-scope
  resources   Deletes web ACLs that contain no rules or rule groups   For security group
  policies, the cleanup does the following for each security group in the policy:
  Disassociates the security group from in-scope resources    Deletes the security group if
  it was created through Firewall Manager and if it's no longer associated with any resources
  through another policy   After the cleanup, in-scope resources are no longer protected by
  web ACLs in this policy. Protection of out-of-scope resources remains unchanged. Scope is
  determined by tags that you create and accounts that you associate with the policy. When
  creating the policy, if you specify that only resources in specific accounts or with
  specific tags are in scope of the policy, those accounts and resources are handled by the
  policy. All others are out of scope. If you don't specify tags or accounts, all resources
  are in scope.
"""
delete_policy(PolicyId; aws_config::AbstractAWSConfig=global_aws_config()) = fms("DeletePolicy", Dict{String, Any}("PolicyId"=>PolicyId); aws_config=aws_config)
delete_policy(PolicyId, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = fms("DeletePolicy", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("PolicyId"=>PolicyId), params)); aws_config=aws_config)

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

Permanently deletes an AWS Firewall Manager protocols list.

# Arguments
- `list_id`: The ID of the protocols list that you want to delete. You can retrieve this ID
  from PutProtocolsList, ListProtocolsLists, and GetProtocolsLost.

"""
delete_protocols_list(ListId; aws_config::AbstractAWSConfig=global_aws_config()) = fms("DeleteProtocolsList", Dict{String, Any}("ListId"=>ListId); aws_config=aws_config)
delete_protocols_list(ListId, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = fms("DeleteProtocolsList", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("ListId"=>ListId), params)); aws_config=aws_config)

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

Disassociates the account that has been set as the AWS Firewall Manager administrator
account. To set a different account as the administrator account, you must submit an
AssociateAdminAccount request.

"""
disassociate_admin_account(; aws_config::AbstractAWSConfig=global_aws_config()) = fms("DisassociateAdminAccount"; aws_config=aws_config)
disassociate_admin_account(params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = fms("DisassociateAdminAccount", params; aws_config=aws_config)

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

Returns the AWS Organizations master account that is associated with AWS Firewall Manager
as the AWS Firewall Manager administrator.

"""
get_admin_account(; aws_config::AbstractAWSConfig=global_aws_config()) = fms("GetAdminAccount"; aws_config=aws_config)
get_admin_account(params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = fms("GetAdminAccount", params; aws_config=aws_config)

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

Returns information about the specified AWS Firewall Manager applications list.

# Arguments
- `list_id`: The ID of the AWS Firewall Manager applications list that you want the details
  for.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"DefaultList"`: Specifies whether the list to retrieve is a default list owned by AWS
  Firewall Manager.
"""
get_apps_list(ListId; aws_config::AbstractAWSConfig=global_aws_config()) = fms("GetAppsList", Dict{String, Any}("ListId"=>ListId); aws_config=aws_config)
get_apps_list(ListId, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = fms("GetAppsList", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("ListId"=>ListId), params)); aws_config=aws_config)

"""
    get_compliance_detail(member_account, policy_id)
    get_compliance_detail(member_account, policy_id, params::Dict{String,<:Any})

Returns detailed compliance information about the specified member account. Details include
resources that are in and out of compliance with the specified policy. Resources are
considered noncompliant for AWS WAF and Shield Advanced policies if the specified policy
has not been applied to them. Resources are considered noncompliant for security group
policies if they are in scope of the policy, they violate one or more of the policy rules,
and remediation is disabled or not possible. Resources are considered noncompliant for
Network Firewall policies if a firewall is missing in the VPC, if the firewall endpoint
isn't set up in an expected Availability Zone and subnet, if a subnet created by the
Firewall Manager doesn't have the expected route table, and for modifications to a firewall
policy that violate the Firewall Manager policy's rules.

# Arguments
- `member_account`: The AWS account that owns the resources that you want to get the
  details for.
- `policy_id`: The ID of the policy that you want to get the details for. PolicyId is
  returned by PutPolicy and by ListPolicies.

"""
get_compliance_detail(MemberAccount, PolicyId; aws_config::AbstractAWSConfig=global_aws_config()) = fms("GetComplianceDetail", Dict{String, Any}("MemberAccount"=>MemberAccount, "PolicyId"=>PolicyId); aws_config=aws_config)
get_compliance_detail(MemberAccount, PolicyId, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = fms("GetComplianceDetail", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("MemberAccount"=>MemberAccount, "PolicyId"=>PolicyId), params)); aws_config=aws_config)

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

Information about the Amazon Simple Notification Service (SNS) topic that is used to record
AWS Firewall Manager SNS logs.

"""
get_notification_channel(; aws_config::AbstractAWSConfig=global_aws_config()) = fms("GetNotificationChannel"; aws_config=aws_config)
get_notification_channel(params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = fms("GetNotificationChannel", params; aws_config=aws_config)

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

Returns information about the specified AWS Firewall Manager policy.

# Arguments
- `policy_id`: The ID of the AWS Firewall Manager policy that you want the details for.

"""
get_policy(PolicyId; aws_config::AbstractAWSConfig=global_aws_config()) = fms("GetPolicy", Dict{String, Any}("PolicyId"=>PolicyId); aws_config=aws_config)
get_policy(PolicyId, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = fms("GetPolicy", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("PolicyId"=>PolicyId), params)); aws_config=aws_config)

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

If you created a Shield Advanced policy, returns policy-level attack summary information in
the event of a potential DDoS attack. Other policy types are currently unsupported.

# Arguments
- `policy_id`: The ID of the policy for which you want to get the attack information.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"EndTime"`: The end of the time period to query for the attacks. This is a timestamp
  type. The request syntax listing indicates a number type because the default used by AWS
  Firewall Manager is Unix time in seconds. However, any valid timestamp format is allowed.
- `"MaxResults"`: Specifies the number of objects that you want AWS Firewall Manager to
  return for this request. If you have more objects than the number that you specify for
  MaxResults, the response includes a NextToken value that you can use to get another batch
  of objects.
- `"MemberAccountId"`: The AWS account that is in scope of the policy that you want to get
  the details for.
- `"NextToken"`: If you specify a value for MaxResults and you have more objects than the
  number that you specify for MaxResults, AWS Firewall Manager returns a NextToken value in
  the response, which you can use to retrieve another group of objects. For the second and
  subsequent GetProtectionStatus requests, specify the value of NextToken from the previous
  response to get information about another batch of objects.
- `"StartTime"`: The start of the time period to query for the attacks. This is a timestamp
  type. The request syntax listing indicates a number type because the default used by AWS
  Firewall Manager is Unix time in seconds. However, any valid timestamp format is allowed.
"""
get_protection_status(PolicyId; aws_config::AbstractAWSConfig=global_aws_config()) = fms("GetProtectionStatus", Dict{String, Any}("PolicyId"=>PolicyId); aws_config=aws_config)
get_protection_status(PolicyId, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = fms("GetProtectionStatus", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("PolicyId"=>PolicyId), params)); aws_config=aws_config)

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

Returns information about the specified AWS Firewall Manager protocols list.

# Arguments
- `list_id`: The ID of the AWS Firewall Manager protocols list that you want the details
  for.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"DefaultList"`: Specifies whether the list to retrieve is a default list owned by AWS
  Firewall Manager.
"""
get_protocols_list(ListId; aws_config::AbstractAWSConfig=global_aws_config()) = fms("GetProtocolsList", Dict{String, Any}("ListId"=>ListId); aws_config=aws_config)
get_protocols_list(ListId, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = fms("GetProtocolsList", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("ListId"=>ListId), params)); aws_config=aws_config)

"""
    get_violation_details(member_account, policy_id, resource_id, resource_type)
    get_violation_details(member_account, policy_id, resource_id, resource_type, params::Dict{String,<:Any})

Retrieves violations for a resource based on the specified AWS Firewall Manager policy and
AWS account.

# Arguments
- `member_account`: The AWS account ID that you want the details for.
- `policy_id`: The ID of the AWS Firewall Manager policy that you want the details for.
  This currently only supports security group content audit policies.
- `resource_id`: The ID of the resource that has violations.
- `resource_type`: The resource type. This is in the format shown in the AWS Resource Types
  Reference. Supported resource types are: AWS::EC2::Instance, AWS::EC2::NetworkInterface,
  AWS::EC2::SecurityGroup, AWS::NetworkFirewall::FirewallPolicy, and AWS::EC2::Subnet.

"""
get_violation_details(MemberAccount, PolicyId, ResourceId, ResourceType; aws_config::AbstractAWSConfig=global_aws_config()) = fms("GetViolationDetails", Dict{String, Any}("MemberAccount"=>MemberAccount, "PolicyId"=>PolicyId, "ResourceId"=>ResourceId, "ResourceType"=>ResourceType); aws_config=aws_config)
get_violation_details(MemberAccount, PolicyId, ResourceId, ResourceType, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = fms("GetViolationDetails", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("MemberAccount"=>MemberAccount, "PolicyId"=>PolicyId, "ResourceId"=>ResourceId, "ResourceType"=>ResourceType), params)); aws_config=aws_config)

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

Returns an array of AppsListDataSummary objects.

# Arguments
- `max_results`: The maximum number of objects that you want AWS Firewall Manager to return
  for this request. If more objects are available, in the response, AWS Firewall Manager
  provides a NextToken value that you can use in a subsequent call to get the next batch of
  objects. If you don't specify this, AWS Firewall Manager returns all available objects.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"DefaultLists"`: Specifies whether the lists to retrieve are default lists owned by AWS
  Firewall Manager.
- `"NextToken"`: If you specify a value for MaxResults in your list request, and you have
  more objects than the maximum, AWS Firewall Manager returns this token in the response. For
  all but the first request, you provide the token returned by the prior request in the
  request parameters, to retrieve the next batch of objects.
"""
list_apps_lists(MaxResults; aws_config::AbstractAWSConfig=global_aws_config()) = fms("ListAppsLists", Dict{String, Any}("MaxResults"=>MaxResults); aws_config=aws_config)
list_apps_lists(MaxResults, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = fms("ListAppsLists", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("MaxResults"=>MaxResults), params)); aws_config=aws_config)

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

Returns an array of PolicyComplianceStatus objects. Use PolicyComplianceStatus to get a
summary of which member accounts are protected by the specified policy.

# Arguments
- `policy_id`: The ID of the AWS Firewall Manager policy that you want the details for.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"MaxResults"`: Specifies the number of PolicyComplianceStatus objects that you want AWS
  Firewall Manager to return for this request. If you have more PolicyComplianceStatus
  objects than the number that you specify for MaxResults, the response includes a NextToken
  value that you can use to get another batch of PolicyComplianceStatus objects.
- `"NextToken"`: If you specify a value for MaxResults and you have more
  PolicyComplianceStatus objects than the number that you specify for MaxResults, AWS
  Firewall Manager returns a NextToken value in the response that allows you to list another
  group of PolicyComplianceStatus objects. For the second and subsequent ListComplianceStatus
  requests, specify the value of NextToken from the previous response to get information
  about another batch of PolicyComplianceStatus objects.
"""
list_compliance_status(PolicyId; aws_config::AbstractAWSConfig=global_aws_config()) = fms("ListComplianceStatus", Dict{String, Any}("PolicyId"=>PolicyId); aws_config=aws_config)
list_compliance_status(PolicyId, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = fms("ListComplianceStatus", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("PolicyId"=>PolicyId), params)); aws_config=aws_config)

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

Returns a MemberAccounts object that lists the member accounts in the administrator's AWS
organization. The ListMemberAccounts must be submitted by the account that is set as the
AWS Firewall Manager administrator.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"MaxResults"`: Specifies the number of member account IDs that you want AWS Firewall
  Manager to return for this request. If you have more IDs than the number that you specify
  for MaxResults, the response includes a NextToken value that you can use to get another
  batch of member account IDs.
- `"NextToken"`: If you specify a value for MaxResults and you have more account IDs than
  the number that you specify for MaxResults, AWS Firewall Manager returns a NextToken value
  in the response that allows you to list another group of IDs. For the second and subsequent
  ListMemberAccountsRequest requests, specify the value of NextToken from the previous
  response to get information about another batch of member account IDs.
"""
list_member_accounts(; aws_config::AbstractAWSConfig=global_aws_config()) = fms("ListMemberAccounts"; aws_config=aws_config)
list_member_accounts(params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = fms("ListMemberAccounts", params; aws_config=aws_config)

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

Returns an array of PolicySummary objects.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"MaxResults"`: Specifies the number of PolicySummary objects that you want AWS Firewall
  Manager to return for this request. If you have more PolicySummary objects than the number
  that you specify for MaxResults, the response includes a NextToken value that you can use
  to get another batch of PolicySummary objects.
- `"NextToken"`: If you specify a value for MaxResults and you have more PolicySummary
  objects than the number that you specify for MaxResults, AWS Firewall Manager returns a
  NextToken value in the response that allows you to list another group of PolicySummary
  objects. For the second and subsequent ListPolicies requests, specify the value of
  NextToken from the previous response to get information about another batch of
  PolicySummary objects.
"""
list_policies(; aws_config::AbstractAWSConfig=global_aws_config()) = fms("ListPolicies"; aws_config=aws_config)
list_policies(params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = fms("ListPolicies", params; aws_config=aws_config)

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

Returns an array of ProtocolsListDataSummary objects.

# Arguments
- `max_results`: The maximum number of objects that you want AWS Firewall Manager to return
  for this request. If more objects are available, in the response, AWS Firewall Manager
  provides a NextToken value that you can use in a subsequent call to get the next batch of
  objects. If you don't specify this, AWS Firewall Manager returns all available objects.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"DefaultLists"`: Specifies whether the lists to retrieve are default lists owned by AWS
  Firewall Manager.
- `"NextToken"`: If you specify a value for MaxResults in your list request, and you have
  more objects than the maximum, AWS Firewall Manager returns this token in the response. For
  all but the first request, you provide the token returned by the prior request in the
  request parameters, to retrieve the next batch of objects.
"""
list_protocols_lists(MaxResults; aws_config::AbstractAWSConfig=global_aws_config()) = fms("ListProtocolsLists", Dict{String, Any}("MaxResults"=>MaxResults); aws_config=aws_config)
list_protocols_lists(MaxResults, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = fms("ListProtocolsLists", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("MaxResults"=>MaxResults), params)); aws_config=aws_config)

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

Retrieves the list of tags for the specified AWS resource.

# Arguments
- `resource_arn`: The Amazon Resource Name (ARN) of the resource to return tags for. The
  AWS Firewall Manager resources that support tagging are policies, applications lists, and
  protocols lists.

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

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

Creates an AWS Firewall Manager applications list.

# Arguments
- `apps_list`: The details of the AWS Firewall Manager applications list to be created.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"TagList"`: The tags associated with the resource.
"""
put_apps_list(AppsList; aws_config::AbstractAWSConfig=global_aws_config()) = fms("PutAppsList", Dict{String, Any}("AppsList"=>AppsList); aws_config=aws_config)
put_apps_list(AppsList, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = fms("PutAppsList", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("AppsList"=>AppsList), params)); aws_config=aws_config)

"""
    put_notification_channel(sns_role_name, sns_topic_arn)
    put_notification_channel(sns_role_name, sns_topic_arn, params::Dict{String,<:Any})

Designates the IAM role and Amazon Simple Notification Service (SNS) topic that AWS
Firewall Manager uses to record SNS logs. To perform this action outside of the console,
you must configure the SNS topic to allow the Firewall Manager role AWSServiceRoleForFMS to
publish SNS logs. For more information, see Firewall Manager required permissions for API
actions in the AWS Firewall Manager Developer Guide.

# Arguments
- `sns_role_name`: The Amazon Resource Name (ARN) of the IAM role that allows Amazon SNS to
  record AWS Firewall Manager activity.
- `sns_topic_arn`: The Amazon Resource Name (ARN) of the SNS topic that collects
  notifications from AWS Firewall Manager.

"""
put_notification_channel(SnsRoleName, SnsTopicArn; aws_config::AbstractAWSConfig=global_aws_config()) = fms("PutNotificationChannel", Dict{String, Any}("SnsRoleName"=>SnsRoleName, "SnsTopicArn"=>SnsTopicArn); aws_config=aws_config)
put_notification_channel(SnsRoleName, SnsTopicArn, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = fms("PutNotificationChannel", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("SnsRoleName"=>SnsRoleName, "SnsTopicArn"=>SnsTopicArn), params)); aws_config=aws_config)

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

Creates an AWS Firewall Manager policy. Firewall Manager provides the following types of
policies:    An AWS WAF policy (type WAFV2), which defines rule groups to run first in the
corresponding AWS WAF web ACL and rule groups to run last in the web ACL.   An AWS WAF
Classic policy (type WAF), which defines a rule group.    A Shield Advanced policy, which
applies Shield Advanced protection to specified accounts and resources.   A security group
policy, which manages VPC security groups across your AWS organization.    An AWS Network
Firewall policy, which provides firewall rules to filter network traffic in specified
Amazon VPCs.   Each policy is specific to one of the types. If you want to enforce more
than one policy type across accounts, create multiple policies. You can create multiple
policies for each type. You must be subscribed to Shield Advanced to create a Shield
Advanced policy. For more information about subscribing to Shield Advanced, see
CreateSubscription.

# Arguments
- `policy`: The details of the AWS Firewall Manager policy to be created.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"TagList"`: The tags to add to the AWS resource.
"""
put_policy(Policy; aws_config::AbstractAWSConfig=global_aws_config()) = fms("PutPolicy", Dict{String, Any}("Policy"=>Policy); aws_config=aws_config)
put_policy(Policy, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = fms("PutPolicy", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("Policy"=>Policy), params)); aws_config=aws_config)

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

Creates an AWS Firewall Manager protocols list.

# Arguments
- `protocols_list`: The details of the AWS Firewall Manager protocols list to be created.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"TagList"`: The tags associated with the resource.
"""
put_protocols_list(ProtocolsList; aws_config::AbstractAWSConfig=global_aws_config()) = fms("PutProtocolsList", Dict{String, Any}("ProtocolsList"=>ProtocolsList); aws_config=aws_config)
put_protocols_list(ProtocolsList, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = fms("PutProtocolsList", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("ProtocolsList"=>ProtocolsList), params)); aws_config=aws_config)

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

Adds one or more tags to an AWS resource.

# Arguments
- `resource_arn`: The Amazon Resource Name (ARN) of the resource to return tags for. The
  AWS Firewall Manager resources that support tagging are policies, applications lists, and
  protocols lists.
- `tag_list`: The tags to add to the resource.

"""
tag_resource(ResourceArn, TagList; aws_config::AbstractAWSConfig=global_aws_config()) = fms("TagResource", Dict{String, Any}("ResourceArn"=>ResourceArn, "TagList"=>TagList); aws_config=aws_config)
tag_resource(ResourceArn, TagList, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = fms("TagResource", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("ResourceArn"=>ResourceArn, "TagList"=>TagList), 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 an AWS resource.

# Arguments
- `resource_arn`: The Amazon Resource Name (ARN) of the resource to return tags for. The
  AWS Firewall Manager resources that support tagging are policies, applications lists, and
  protocols lists.
- `tag_keys`: The keys of the tags to remove from the resource.

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


text:
include("datasets.jl");

# =============================================================================
# bondad.jl -> Funciones útiles calcular la bondad de un sistema:
#   - accuracy (precisión)
#   - confusionMatrix
#   - printConfusionMatrix
#
# Este módulo solo trabaja con booleanos, a excepción de accuracy que lo necesita la
# rna para su entrenamientos
#
# =============================================================================

#Precisión bool 1d
accuracy(outputs::Array{Bool,1}, targets::Array{Bool,1}) = mean(outputs.==targets);

#Precisión bool 2d
function accuracy(outputs::Array{Bool,2}, targets::Array{Bool,2}; dataInRows::Bool=true)
    @assert(all(size(outputs).==size(targets)));
    if (dataInRows)
        # Cada patron esta en cada fila
        if (size(targets,2)==1)
            return accuracy(outputs[:,1], targets[:,1]);
        else
            classComparison = targets .== outputs
            correctClassifications = all(classComparison, dims=2)
            return mean(correctClassifications)
        end;
    else
        # Cada patron esta en cada columna
        if (size(targets,1)==1)
            return accuracy(outputs[1,:], targets[1,:]);
        else
            classComparison = targets .== outputs
            correctClassifications = all(classComparison, dims=1)
            return mean(correctClassifications)
        end;
    end;
end;

#Precisión Float64 1d
accuracy(outputs::Array{Float64,1}, targets::Array{Bool,1}; threshold::Float64=0.5) = accuracy(Array{Bool,1}(outputs.>=threshold), targets);

#Precisión Float64 2d
function accuracy(outputs::Array{Float64,2}, targets::Array{Bool,2}; dataInRows::Bool=true)
    @assert(all(size(outputs).==size(targets)));
    if (dataInRows)
        # Cada patron esta en cada fila
        if (size(targets,2)==1)
            return accuracy(outputs[:,1], targets[:,1]);
        else
            vmax = maximum(outputs, dims=2);
            outputs = Array{Bool,2}(outputs .== vmax);
            return accuracy(outputs, targets);
        end;
    else
        # Cada patron esta en cada columna
        if (size(targets,1)==1)
            return accuracy(outputs[1,:], targets[1,:]);
        else
            vmax = maximum(outputs, dims=1)
            outputs = Array{Bool,2}(outputs .== vmax)
            return accuracy(outputs, targets; dataInRows=false);
        end;
    end;
end;

# Añado estas funciones porque las RR.NN.AA. dan la salida como matrices de valores Float32 en lugar de Float64
# Con estas funciones se pueden usar indistintamente matrices de Float32 o Float64
accuracy(outputs::Array{Float32,1}, targets::Array{Bool,1}; threshold::Float64=0.5) = accuracy(Float64.(outputs), targets; threshold=threshold);
accuracy(outputs::Array{Float32,2}, targets::Array{Bool,2}; dataInRows::Bool=true)  = accuracy(Float64.(outputs), targets; dataInRows=dataInRows);

# =============================================================================
# confusionMatrix
# =============================================================================

function confusionMatrix(outputs::Array{Bool,1}, targets::Array{Bool,1})
    @assert(length(outputs)==length(targets));
    # Para calcular la precision y la tasa de error, se puede llamar a las funciones definidas en la practica 2
    acc         = accuracy(outputs, targets); # Precision, definida previamente en una practica anterior
    errorRate   = 1. - acc;
    recall      = mean(  outputs[  targets]); # Sensibilidad
    specificity = mean(.!outputs[.!targets]); # Especificidad
    precision   = mean(  targets[  outputs]); # Valor predictivo positivo
    NPV         = mean(.!targets[.!outputs]); # Valor predictivo negativo
    # Controlamos que algunos casos pueden ser NaN
    #  Para el caso de sensibilidad y especificidad, en un conjunto de entrenamiento estos no pueden ser NaN, porque esto indicaria que se ha intentado entrenar con una unica clase
    #   Sin embargo, sí pueden ser NaN en el caso de aplicar un modelo en un conjunto de test, si este sólo tiene patrones de una clase
    #  Para VPP y VPN, sí pueden ser NaN en caso de que el clasificador lo haya clasificado todo como negativo o positivo respectivamente
    # En estos casos, estas metricas habria que dejarlas a NaN para indicar que no se han podido evaluar
    #  Sin embargo, como es posible que se quiera combinar estos valores al evaluar una clasificacion multiclase, es necesario asignarles un valor. El criterio que se usa aqui es que estos valores seran igual a 0
    # Ademas, hay un caso especial: cuando los VP son el 100% de los patrones, o los VN son el 100% de los patrones
    #  En este caso, el sistema ha actuado correctamente, así que controlamos primero este caso
    if isnan(recall) && isnan(precision) # Los VN son el 100% de los patrones
        recall = 1.;
        precision = 1.;
    elseif isnan(specificity) && isnan(NPV) # Los VP son el 100% de los patrones
        specificity = 1.;
        NPV = 1.;
    end;
    # Ahora controlamos los casos en los que no se han podido evaluar las metricas excluyendo los casos anteriores
    recall      = isnan(recall)      ? 0. : recall;
    specificity = isnan(specificity) ? 0. : specificity;
    precision   = isnan(precision)   ? 0. : precision;
    NPV         = isnan(NPV)         ? 0. : NPV;
    # Calculamos F1, teniendo en cuenta que si sensibilidad o VPP es NaN (pero no ambos), el resultado tiene que ser 0 porque si sensibilidad=NaN entonces VPP=0 y viceversa
    F1          = (recall==precision==0.) ? 0. : 2*(recall*precision)/(recall+precision);
    # Reservamos memoria para la matriz de confusion
    confMatrix = Array{Int64,2}(undef, 2, 2);
    # Ponemos en las filas los que pertenecen a cada clase (targets) y en las columnas los clasificados (outputs)
    #  Primera fila/columna: negativos
    #  Segunda fila/columna: positivos
    # Primera fila: patrones de clase negativo, clasificados como negativos o positivos
    confMatrix[1,1] = sum(.!targets .& .!outputs); # VN
    confMatrix[1,2] = sum(.!targets .&   outputs); # FP
    # Segunda fila: patrones de clase positiva, clasificados como negativos o positivos
    confMatrix[2,1] = sum(  targets .& .!outputs); # FN
    confMatrix[2,2] = sum(  targets .&   outputs); # VP
    return (acc, errorRate, recall, specificity, precision, NPV, F1, confMatrix)
end;

function confusionMatrix(outputs::Array{Bool,2}, targets::Array{Bool,2}; weighted::Bool=true)
    @assert(size(outputs)==size(targets));
    numClasses = size(targets,2);
    # Nos aseguramos de que no hay dos columnas
    @assert(numClasses!=2);
    if (numClasses==1)
        return confusionMatrix(outputs[:,1], targets[:,1]);
    else
        # Nos aseguramos de que en cada fila haya uno y sólo un valor a true
        @assert(all(sum(outputs, dims=2).==1));
        # Reservamos memoria para las metricas de cada clase, inicializandolas a 0 porque algunas posiblemente no se calculen
        recall      = zeros(numClasses);
        specificity = zeros(numClasses);
        precision   = zeros(numClasses);
        NPV         = zeros(numClasses);
        F1          = zeros(numClasses);
        # Reservamos memoria para la matriz de confusion
        confMatrix  = Array{Int64,2}(undef, numClasses, numClasses);
        # Calculamos el numero de patrones de cada clase
        numInstancesFromEachClass = vec(sum(targets, dims=1));
        # Calculamos las metricas para cada clase, esto se haria con un bucle similar a "for numClass in 1:numClasses" que itere por todas las clases
        #  Sin embargo, solo hacemos este calculo para las clases que tengan algun patron
        #  Puede ocurrir que alguna clase no tenga patrones como consecuencia de haber dividido de forma aleatoria el conjunto de patrones entrenamiento/test
        #  En aquellas clases en las que no haya patrones, los valores de las metricas seran 0 (los vectores ya estan asignados), y no se tendran en cuenta a la hora de unir estas metricas
        for numClass in findall(numInstancesFromEachClass.>0)
            # Calculamos las metricas de cada problema binario correspondiente a cada clase y las almacenamos en los vectores correspondientes
            (_, _, recall[numClass], specificity[numClass], precision[numClass], NPV[numClass], F1[numClass], _) = confusionMatrix(outputs[:,numClass], targets[:,numClass]);
        end;

        # Calculamos la matriz de confusión haciendo un bucle doble que itere sobre las clases
        for numClassTarget in 1:numClasses, numClassOutput in 1:numClasses
            # Igual que antes, ponemos en las filas los que pertenecen a cada clase (targets) y en las columnas los clasificados (outputs)
            confMatrix[numClassTarget, numClassOutput] = sum(targets[:,numClassTarget] .& outputs[:,numClassOutput]);
        end;

        # Aplicamos las forma de combinar las metricas macro o weighted
        if weighted
            # Calculamos los valores de ponderacion para hacer el promedio
            weights = numInstancesFromEachClass./sum(numInstancesFromEachClass);
            recall      = sum(weights.*recall);
            specificity = sum(weights.*specificity);
            precision   = sum(weights.*precision);
            NPV         = sum(weights.*NPV);
            F1          = sum(weights.*F1);
        else
            # No realizo la media tal cual con la funcion mean, porque puede haber clases sin instancias
            #  En su lugar, realizo la media solamente de las clases que tengan instancias
            numClassesWithInstances = sum(numInstancesFromEachClass.>0);
            recall      = sum(recall)/numClassesWithInstances;
            specificity = sum(specificity)/numClassesWithInstances;
            precision   = sum(precision)/numClassesWithInstances;
            NPV         = sum(NPV)/numClassesWithInstances;
            F1          = sum(F1)/numClassesWithInstances;
        end;
        # Precision y tasa de error las calculamos con las funciones definidas previamente
        acc = accuracy(outputs, targets; dataInRows=true);
        errorRate = 1 - acc;

        return (acc, errorRate, recall, specificity, precision, NPV, F1, confMatrix);
    end;
end;

function confusionMatrix(outputs::Array{Any,1}, targets::Array{Any,1}; weighted::Bool=true)
    # Comprobamos que todas las clases de salida esten dentro de las clases de las salidas deseadas
    @assert(all([in(output, unique(targets)) for output in outputs]));
    classes = unique(targets);
    # Es importante calcular el vector de clases primero y pasarlo como argumento a las 2 llamadas a oneHotEncoding para que el orden de las clases sea el mismo en ambas matrices
    return confusionMatrix(oneHotEncoding(outputs, classes), oneHotEncoding(targets, classes); weighted=weighted);
end;

confusionMatrix(outputs::Array{Float64,1}, targets::Array{Bool,1}; threshold::Float64=0.5) = confusionMatrix(Array{Bool,1}(outputs.>=threshold), targets);
confusionMatrix(outputs::Array{Float64,2}, targets::Array{Bool,2}; weighted::Bool=true) = confusionMatrix(classifyOutputs(outputs), targets; weighted=weighted);
confusionMatrix(outputs::Array{Float32,2}, targets::Array{Bool,2}; weighted::Bool=true) = confusionMatrix(convert(Array{Float64,2}, outputs), targets; weighted=weighted);

# =============================================================================
# printConfusionMatrix
# =============================================================================

function printConfusionMatrix(outputs::Array{Bool,2}, targets::Array{Bool,2}; weighted::Bool=true)
    (acc, errorRate, recall, specificity, precision, NPV, F1, confMatrix) = confusionMatrix(outputs, targets; weighted=weighted);
    numClasses = size(confMatrix,1);
    writeHorizontalLine() = (for i in 1:numClasses+1 print("--------") end; println(""); );
    writeHorizontalLine();
    print("\t| ");
    if (numClasses==2)
        println(" - \t + \t|");
    else
        print.("Cl. ", 1:numClasses, "\t| ");
    end;
    println("");
    writeHorizontalLine();
    for numClassTarget in 1:numClasses
        # print.(confMatrix[numClassTarget,:], "\t");
        if (numClasses==2)
            print(numClassTarget == 1 ? " - \t| " : " + \t| ");
        else
            print("Cl. ", numClassTarget, "\t| ");
        end;
        print.(confMatrix[numClassTarget,:], "\t| ");
        println("");
        writeHorizontalLine();
    end;
    println("Accuracy: ", acc);
    println("Error rate: ", errorRate);
    println("Recall: ", recall);
    println("Specificity: ", specificity);
    println("Precision: ", precision);
    println("Negative predictive value: ", NPV);
    println("F1-score: ", F1);
    return (acc, errorRate, recall, specificity, precision, NPV, F1, confMatrix);
end;
printConfusionMatrix(outputs::Array{Float64,2}, targets::Array{Bool,2}; weighted::Bool=true) =  printConfusionMatrix(classifyOutputs(outputs), targets; weighted=weighted)
printConfusionMatrix(outputs::Array{Float32,2}, targets::Array{Bool,2}; weighted::Bool=true) = printConfusionMatrix(convert(Array{Float64,2}, outputs), targets; weighted=weighted);

function printConfusionMatrix(outputs::Array{Any,1}, targets::Array{Any,1}; weighted::Bool=true)
    # Comprobamos que todas las clases de salida esten dentro de las clases de las salidas deseadas
    @assert(all([in(output, unique(targets)) for output in outputs]));
    classes = unique(targets);
    # Es importante calcular el vector de clases primero y pasarlo como argumento a las 2 llamadas a oneHotEncoding para que el orden de las clases sea el mismo en ambas matrices
    printConfusionMatrix(oneHotEncoding(outputs, classes), oneHotEncoding(targets, classes); weighted=weighted);
end;


text:
using SymEngineExtras, SymEngine
using Base.Test

x,y,z = symbols("x y z")

## logsimp
@test logsimp( 2log(x)) == log(x^2)
@test logsimp(log(x) + log(y)) == log(x*y)
@test logsimp(log(x) - log(y)) == log(x/y)
@test logsimp(log(x) + log(2x) - log(y) + x) == x + log(2*x^2/y)
@test logsimp(3 * log(x)) == log(x^3)
@test logsimp(y * log(x)) == log(x^y)

## expand_log
@test expand_log(log(x*y)) == log(x) + log(y)
@test expand_log(log(x/y)) == log(x) - log(y)
@test expand_log(x + log(2*x^2)) == x + log(Basic(2)) + 2*log(x)
@test expand_log(log(x^3)) == 3 * log(x)
@test expand_log(log(x^y)) == y * log(x)

## powsimp
@test powsimp(x^2 * x^3) == x^5
@test powsimp(x^2 * x^y) == x^(2+y)
@test powsimp(x + log(x^2 * x^y)) == x + log(x^(2 + y))
@test powsimp(x^2*y^2*2) == 2*(x*y)^2
@test powsimp(x^3 * y^3) == (x*y)^3 # broken, as it gets put together, taken apart

## powdenest
@test powdenest(2^(3*x)) == 8^x

## expand_power_exp
@test expand_power_exp(x^(y+z)) == x^y * x^z

## expand_power_base
@test expand_power_base((x*y)^z) == x^z * y^z

## rewrite_trig
@test rewrite_trig(sec(x)) == 1/cos(x)
@test rewrite_trig(sech(x)) == 1/cosh(x)
@test rewrite_trig(cos(cot(x))) == cos(1/tan(x))

## expand_trig
@test expand_trig(sin(x + y)) == sin(x)*cos(y) + sin(y) * cos(x)
@test expand_trig(sin(2x)) == 2*sin(x) * cos(x)
@test expand_trig(sin(x/2)^2) == (1-cos(x))/2

@test expand_trig(cos(x + y)) == cos(x)*cos(y) - sin(y) * sin(x)
@test expand_trig(x*y + cos(x + y)) == x*y + cos(x)*cos(y) - sin(y) * sin(x)
@test expand_trig(cos(2x)) == cos(x)^2 - sin(x)^2
@test expand_trig(cos(x/2)^2) == (1+cos(x))/2

@test expand_trig(tan(x + y)) == (tan(x) + tan(y)) / (1 - tan(x)*tan(y))
@test expand_trig(tan(2x)) == 2*tan(x) / (1 - tan(x)^2)
@test expand_trig(tan(x/2)) == sin(x) / (1 + cos(x))

@test expand_trig(sinh(x + y)) == sinh(x)*cosh(y) + sinh(y) * cosh(x)
@test expand_trig(sinh(2x)) == 2 * sinh(x) * cosh(x)
@test expand_trig(sinh(x/2)^2) == (cosh(x) - 1)/2

## trigsimp
@test trigsimp(2*sin(x)*cos(x)) == sin(2x)
@test trigsimp(2*sin(sin(x))*cos(sin(x)) * cos(x) * sin(x) * tan(x)) == (1//2)*sin(2*x)*sin(2*sin(x))*tan(x)
ex = sin(sin(sin(2x)))
@test trigsimp(expand_trig(ex)) == ex

@test trigsimp(2*sinh(exp(x))*cosh(exp(x))) == sinh(2exp(x))

ex = sin(x)*cos(y) + sin(y)*cos(x)
@test trigsimp(ex) == sin(x + y)

ex = sin(x)*cos(y) - sin(y)*cos(x)
@test trigsimp(ex) == sin(x - y)

ex = sin(x)*cos(y) + cos(x)*sin(y)
@test trigsimp(ex) == sin(x + y)

ex = cos(x)*cos(y) - sin(x)*sin(y)
@test trigsimp(ex) == cos(x + y)

ex = cos(x)*cos(y) + sin(x)*sin(y)
@test trigsimp(ex) == cos(x - y)

ex = sin(cos(x) + cos(y))
@test trigsimp(expand_trig(ex)) == ex


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

name = "Triangle"
version = v"1.6.0"

# Collection of sources required to build triangle
#
# Please be aware that triunsuitable.c is not part of the original Triangle distribution.
# It provides the possibility to pass a cfunction created in Julia as  user refinement callback.
# For this reason at least triunsiutable.c must be downloaded from Triangulate.jl repo.
#
sources = [
    GitSource("https://github.com/JuliaGeometry/Triangulate.jl.git","b2ffb23ca7d89c567fd31367882bd216757cdb9c")
]

# Bash recipe for building across all platforms

script = raw"""
cd $WORKSPACE/srcdir
cd Triangulate.jl/deps/src
if [[ ${target} == *-mingw32 ]]; then     libdir="bin"; else     libdir="lib"; fi
mkdir ${prefix}/${libdir}
sed -e "s/  exit/extern void error_exit(int); error_exit/g" triangle/triangle.c > triangle_patched.c
$CC -Itriangle -DREAL=double -DTRILIBRARY -O3 -fPIC -DNDEBUG -DNO_TIMER -DEXTERNAL_TEST $LDFLAGS --shared -o ${prefix}/${libdir}/libtriangle.${dlext} triangle_patched.c triwrap.c
"""

platforms = BinaryBuilder.supported_platforms()

# The products that we will ensure are always built
products = [  LibraryProduct("libtriangle", :libtriangle) ]

# Dependencies that must be installed before this package can be built
dependencies = []

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

max_stars_repo_name: JuliaGeometry/TriangleBuilder

max_issues_repo_name: JuliaGeometry/TriangleBuilder

max_forks_repo_name: JuliaGeometry/TriangleBuilder


text:
include("I474.jl")

s = """
function f(x)
    while false
        println("test")
    end
    return x
end
"""
msgs = lintstr(s)
@test msgs[1].code == :W645
@test occursin("while false block is unreachable", msgs[1].message)

s = """
function f(x)
    arr = Array(Int, 1)
    for i in [1,2], j in arr
        println(i*j)
    end
    return x
end
"""
msgs = lintstr(s)
@test isempty(msgs)

s = """
function f(x)
    for i in (1,2,3)
        println(i)
    end
    return x
end
"""
msgs = lintstr(s)
@test isempty(msgs)

s = """
function f(x::Int)
    @lintpragma("Info type x")
    for i in x
        println(i)
    end
    return x
end
"""
msgs = lintstr(s)
@test msgs[1].code == :I271
@test occursin("typeof(x, msgs[1].message) == Int")
@test msgs[2].code == :I672
@test occursin("iteration works for a number but it may be a typo", msgs[2].message)


text:
# module JuliaPBF_UI
using Test
using QML
using Qt5QuickControls_jll
using Observables
using Printf
push!(LOAD_PATH,pwd()) # Hard coded, need to be changed
using JuliaPBF

## PARAMETERS ##
dirExamples = "D:\\work\\JuliaPBF_UI.jl\\examples\\" # Hard coded, need to be changed
dirCaseXml  = ""
const bParseCheck = Observable(false)
const bRunCheck   = Observable(false)

## TIME VARIABLE ##
endTime = 0.0
timeStep = 0.0

## XML 파일의 존재 유무를 확인합니다.
function read_xml_file_dir(caseXML)
  global dirExamples
  global dirCaseXml = dirExamples*caseXML
  global parseCheck
  out = false
  if isfile(dirCaseXml)
    print("\nXML DIR: "*dirCaseXml*"\n")
    out = true
    bParseCheck[]  = true
  else
    print("\nXML DIR: "*dirCaseXml*"\n")
    print("\nXML DIR: The case does not exist.\n")
    out = false
    bParseCheck[]  = false
  end
  return out
end

## 파일이 존재하면 XML을 파싱해 모의정보를 생성합니다.
function parsing_xml_file()
  print("XML PARSED...")
  global dirCaseXml, endTime, timeStep
  try
    global ANALYDATA   = JuliaPBF.IO_dev.Parsing_xml_file(dirCaseXml) # Parsing Inputs
    endTime = ANALYDATA.endTime
    timeStep= ANALYDATA.timeStep.dt
    print("DONE.\n")
  catch err
    print("\n\n")
    println(err)
    print("Please, check XML file..\n")
  end
end

## 생성된 모의정보로 모의데이터를 생성합니다.
function initializing_simulation_data()
  print("INITIALIZING SIMULATION...")
  try
    global SIMULDATA   = JuliaPBF.Solver.PreProcessing(ANALYDATA) # FillInitialBox, GenInitGrid
  catch err
    print("\n\n")
    println(err)
    print("Please, check XML file..\n")
  end
end

function stop_run()
  bRunCheck[] = false
end

function model_run()
  print("RUNNING MODEL...\n")
  bRunCheck[] = true
  curr_time = 0.0
  ii        = 0
  while curr_time >=0# ANALYDATA.endTime
    curr_time += ANALYDATA.timeStep.dt
    @printf("Time = %10.5f \n",curr_time)
    # @printf("  Update for          :")
    # @time JuliaPBF.Solver.Update(SIMULDATA, ANALYDATA)

    ii += 1
    # @printf("  Writing Output for  :")
    # @time JuliaPBF.IO_dev.Writing_ascii_file(SIMULDATA, "out__"*lpad(ii,5,"0"))
    println(bRunCheck)
    # if check_run() == false
    #   break
    # end
  end
  print("END MODEL.\n")
end

@qmlfunction read_xml_file_dir parsing_xml_file initializing_simulation_data model_run stop_run

# absolute path in case working dir is overridden
qml_file = joinpath(dirname(@__FILE__), "qml", "gui.qml")

# Load the QML file
properties = JuliaPropertyMap("parsing_check" => bParseCheck,
                              "running_check" => bRunCheck,
                              "tims_step" => timeStep,
                              "end_time" => endTime)
loadqml(qml_file, guiproperties=properties)

# Run the application
exec()


text: Dict{String, Any}("sectioN" => Dict{String, Any}("value" => "NN", "type" => "string"), "section" => Dict{String, Any}("name" => Dict{String, Any}("value" => "lower", "type" => "string"), "Name" => Dict{String, Any}("value" => "capitalized", "type" => "string"), "NAME" => Dict{String, Any}("value" => "upper", "type" => "string")), "Section" => Dict{String, Any}("name" => Dict{String, Any}("value" => "different section!!", "type" => "string"), "Μ" => Dict{String, Any}("value" => "greek capital letter MU", "type" => "string"), "μ" => Dict{String, Any}("value" => "greek small letter mu", "type" => "string"), "M" => Dict{String, Any}("value" => "latin letter M", "type" => "string")))


text:
module ShapeFromShading

using Blink
using Distributions
using DSP
using FFTW
using Images
using Interact
using LinearAlgebra
using Makie
using Optim
using Statistics

abstract type AbstractShapeAlgorithm end
struct Deterministic <: AbstractShapeAlgorithm end
struct DeterministicCustom <: AbstractShapeAlgorithm end
struct DiscreteShape <: AbstractShapeAlgorithm end
struct DiscreteShapeBound <: AbstractShapeAlgorithm end
struct Pentland <: AbstractShapeAlgorithm end
struct Photometric <: AbstractShapeAlgorithm end
struct Shah <: AbstractShapeAlgorithm end

abstract type AbstractIntegrationScheme end
struct Durou <: AbstractIntegrationScheme end
struct Frankot <: AbstractIntegrationScheme end
struct Horn <: AbstractIntegrationScheme end
struct Path <: AbstractIntegrationScheme end
struct SplitPath <: AbstractIntegrationScheme end

abstract type AbstractSyntheticShape end
struct Cake <: AbstractSyntheticShape end
struct Cake2 <: AbstractSyntheticShape end
struct Cup <: AbstractSyntheticShape end
struct Dem <: AbstractSyntheticShape end
struct Ripple <: AbstractSyntheticShape end
struct Ripple2 <: AbstractSyntheticShape end
struct SynthGaussian <: AbstractSyntheticShape end
struct SuperGaussian <: AbstractSyntheticShape end
struct SynthSphere <: AbstractSyntheticShape end
struct SynthVase <: AbstractSyntheticShape end
struct Tent <: AbstractSyntheticShape end

include("benchmark.jl")
include("common.jl")
include("estimatealbedo.jl")
include("discreteshape.jl")
include("discreteshapebound.jl")
include("deterministic.jl")
include("integration.jl")
include("pentland.jl")
include("photometric.jl")
include("shah.jl")
include("syntheticsurface.jl")

export
    # main functions and datatypes
    benchmark_iterative,
    benchmark_noniterative,
    benchmark_shape,
    Cake,
    Cake2,
    compare_benchmark,
    convert_gradient,
    create_image,
    Cup,
    Dem,
    Deterministic,
    DeterministicCustom,
    DiscreteShape,
    DiscreteShapeBound,
    Durou,
    estimate_img_properties,
    Frankot,
    generate_photometric,
    generate_surface,
    generate_surface_data,
    ground_truth,
    Horn,
    Path,
    Pentland,
    Photometric,
    retrieve_surface,
    Ripple,
    Ripple2,
    Shah,
    SplitPath,
    SuperGaussian,
    synthetic_gradient,
    SynthGaussian,
    SynthSphere,
    SynthVase,
    Tent
end # module