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

using Markdown
using InteractiveUtils

# ╔═╡ b9c84fff-8579-46fc-94cf-8bf6651ed0c0
begin
	using Pkg
	Pkg.activate(mktempdir())
	Pkg.Registry.update()
	Pkg.add("Yao")
	Pkg.add("YaoPlots")
end

# ╔═╡ 2d4047c2-3ea3-11eb-1076-3dff1cef4ec9
using Yao, YaoPlots

# ╔═╡ 6184d07e-3dd2-11eb-085f-af802694d25a
md"# Simon's Algorithm"

# ╔═╡ 8006c71c-3dd2-11eb-13b5-e32876e57d65
md"Simon's algorithm was the algorithm that inspired Shor in making the Shor's Algorithm. This is a great algorithm to have a look at hybrid algorithms, as this is a hybrid algorithm.

The problem is : --

	Suppose there's a binary string of length n. Binary string is a string composed of 0s and 1s. There's a function f, where f(x) = f(y), if and only if, y = x or y = x ⨁ s. x and y are binary strings of length n. s is a “secret” binary string of length n, where s is not all 0s. So, s can be any one of the possible (2ⁿ - 1) binary strings.

	We've to find the secret string s.

	Example : -
		Suppose that n = 3.
		Suppose we find that f(000) = f(101), it means that 000 ⨁ s = 101.
		Remember, these are binary strings and not binary numbers. So 111 ⨁ 100 = 011.
		From the above information, we know that s is 101, as 000 ⨁ 101 = 101.

	The question is, how many times do we need to evaluate f, to find s?
	Also, we don't know what the secret string s or the function f are.

	Classically, we need at least 5 evaluations. We evaluate any four strings of length n, on the the function f, and they might all give different results. But the evaluation of the fifth string, on the function f, is bound to repeat one of the values from the previously evaluated four strings. Suppose f(010) and f(110) give the same result. That means, f(010) = f(110), which means 010 ⨁ s = 110. Adding 010 to both sides, 010 ⨁ 010 ⨁ s = 010 ⨁ 110 => 000 ⨁ s = 100, s = 100.

	In general, for binary string of length n, we need to make (2ⁿ⁻¹ + 1) evaluations.
"

# ╔═╡ b2550450-3ea0-11eb-20ea-dd5194029a53
md"### Kronecker Product of Hadamard Gate
We know that the Hadamard Gate can be represented by the matrix, `` \frac{1}{\sqrt2} \begin{bmatrix} 1 & 1 \\\ 1 &-1 \end{bmatrix} ``.

- Applying H gate on 2 qubits, in the state `` |00〉 ``, we get, `` \frac{1}{2} (|00〉 + |01〉 + |10〉 + |11〉) ``
- Applying H gate on 2 qubits, in the state `` |01〉 ``, we get, `` \frac{1}{2} (|00〉 - |01〉 + |10〉 - |11〉) ``
- Applying H gate on 2 qubits, in the state `` |10〉 ``, we get, `` \frac{1}{2} (|00〉 + |01〉 - |10〉 - |11〉) ``
- Applying H gate on 2 qubits, in the state `` |11〉 ``, we get, `` \frac{1}{2} (|00〉 - |01〉 - |10〉 + |11〉) ``

The Matrix representation for `` H^{⊗2} `` can be given as, `` \frac{1}{2} \begin{bmatrix} 1 & 1 & 1 & 1 \\\ 1 & -1 & 1 & -1 \\\ 1 & 1 & -1 & -1 \\\ 1 & -1 & -1 & 1 \end{bmatrix} ``"

# ╔═╡ a6fc0830-3ea3-11eb-1421-4ff353057988
md"Which we can verify below"

# ╔═╡ cc70b2e6-3ea0-11eb-02b4-6702888f6d4a
Matrix(repeat(2, H, 1:2))

# ╔═╡ b176596e-3ea3-11eb-3d0a-fff2f58159bf
md"We can rewrite the above as

`` \frac{1}{\sqrt2} \begin{bmatrix} \begin{matrix} \frac{1}{\sqrt2} & \frac{1}{\sqrt2} \\\ \frac{1}{\sqrt2} &-\frac{1}{\sqrt2} \end{matrix} & \begin{matrix} \frac{1}{\sqrt2} & \frac{1}{\sqrt2} \\\ \frac{1}{\sqrt2} &-\frac{1}{\sqrt2} \end{matrix} \\\ \\\ \begin{matrix} \frac{1}{\sqrt2} & \frac{1}{\sqrt2} \\\ \frac{1}{\sqrt2} &-\frac{1}{\sqrt2} \end{matrix}  & \begin{matrix} -\frac{1}{\sqrt2} & -\frac{1}{\sqrt2} \\\ -\frac{1}{\sqrt2} &\frac{1}{\sqrt2} \end{matrix} \end{bmatrix} ``

Which can be again rewritten as

`` H^{⊗2} = \frac{1}{\sqrt2} \begin{bmatrix} H & H \\\ H & -H \end{bmatrix} ``

Following this trend,

`` H^{⊗3} = \frac{1}{\sqrt2} \begin{bmatrix} H^{⊗2} & H^{⊗2} \\\ H^{⊗2} & -H^{⊗2} \end{bmatrix} ``

`` H^{⊗4} = \frac{1}{\sqrt2} \begin{bmatrix} H^{⊗3} & H^{⊗3} \\\ H^{⊗3} & -H^{⊗3} \end{bmatrix} ``
-
-
-
`` H^{⊗n} = \frac{1}{\sqrt2} \begin{bmatrix} H^{⊗n-1} & H^{⊗n-1} \\\ H^{⊗n-1} & -H^{⊗n-1} \end{bmatrix} `` "

# ╔═╡ 1a13150c-4683-11eb-13a8-736ddb1a95f9
md"This is known as the Kronecker product of Hadamard Gate."

# ╔═╡ 2ce259f4-4683-11eb-3bb4-5555ecb46d2c
md"### Dot Product of binary strings"

# ╔═╡ 81a82610-4683-11eb-04e9-11c2275ce99c
md"*The dot product of two binary strings, a and b, both of length n, where `` a = a_0 a_1 a_2 .... a_{n-1} ``, and `` b = b_0 b_1 b_2 .... b_{n-1} ``, the  **dot product**  of `` a `` and `` b, a·b, `` is defined as*

` a · b = a₀ × b₀ ⨁ a₁ × b₁ ⨁ a₂ × b₂ .... aₙ₋₁ × bₙ₋₁ `

It's always equal to `` 0 `` or `` 1 ``.
If `` a = 0010 `` and ``  b = 0101 ``, then

` a · b = 0 × 0 ⨁ 0 × 1 ⨁ 1 × 0 ⨁ 0 × 1 = 0 ⨁ 0 ⨁ 0 ⨁ 0 = 0 ` "

# ╔═╡ 16991654-4687-11eb-2516-3bb25117cc5e
md"Lets check out the dot products of all possible combinations for binary strings where `` n = 2. ``

`` \begin{bmatrix} 00·00 & 00·01 & 00·10 & 00·11 \\\ 01·00 & 01·01 & 01·10 & 01·11 \\\ 10·00 & 10·01 & 10·10 & 10·11 \\\ 11·00 & 11·01 & 11·10 & 11·11 \end{bmatrix} ``. Which calculates to `` \begin{bmatrix} 0 & 0 & 0 & 0 \\\ 0 & 1 & 0 & 1 \\\ 0 & 0 & 1 & 1 \\\ 0 & 1 & 1 & 0 \end{bmatrix} ``. "

# ╔═╡ bc0b8262-468e-11eb-0dee-936c2e985baa
md"Remember `` H^{⊗2} ``, which could be represented by, `` \frac{1}{2} \begin{bmatrix} 1 & 1 & 1 & 1 \\\ 1 & -1 & 1 & -1 \\\ 1 & 1 & -1 & -1 \\\ 1 & -1 & -1 & 1 \end{bmatrix} ? ``

It can also be represented by, `` \frac{1}{2} \begin{bmatrix} (-1)^{00·00} & (-1)^{00·01} & (-1)^{00·10} & (-1)^{00·11} \\\ (-1)^{01·00} & (-1)^{01·01} & (-1)^{01·10} & (-1)^{01·11} \\\ (-1)^{10·00} & (-1)^{10·01} & (-1)^{10·10} & (-1)^{10·11} \\\ (-1)^{11·00} & (-1)^{11·01} & (-1)^{11·10} & (-1)^{11·11} \end{bmatrix} ``   "

# ╔═╡ f544222a-4691-11eb-2bee-8d4bc225e181
md"So yeah, we can use dot products to denote Knonecker products of Hadamard Gates"

# ╔═╡ 0e8001f0-4692-11eb-2457-43db7e80877f
md"Now, assume `` s = 11 ``. We're going to add the columns with the pairs ` x ` and ` x ⨁ s `. In other words, in this case, columns `` 1 `` and `` 4 ``, and, columns `` 2 `` and `` 3 ``.

Adding columns `` 1 `` and `` 4 ``,

`` \frac{1}{2} \begin{bmatrix} 1 \\\ 1 \\\ 1 \\\ 1 \end{bmatrix} + \frac{1}{2} \begin{bmatrix} 1 \\\ -1 \\\ -1 \\\ 1 \end{bmatrix} = \frac{1}{2} \begin{bmatrix} 2 \\\ 0 \\\ 0 \\\ 2 \end{bmatrix} ``

Similarly adding columns `` 2 `` and `` 3 ``,

`` \frac{1}{2} \begin{bmatrix} 1 \\\ -1 \\\ 1 \\\ -1 \end{bmatrix} + \frac{1}{2} \begin{bmatrix} 1 \\\ 1 \\\ -1 \\\ -1 \end{bmatrix} = \frac{1}{2} \begin{bmatrix} 2 \\\ 0 \\\ 0 \\\ -2 \end{bmatrix} ``"

# ╔═╡ 59cd0f38-4695-11eb-02ab-7b82274020a9
md"As the above vectors are state vectors, when doing the operation ` x ⨁ s `, we see that some probability amplitudes are getting amplified and some are getting cancelled. If you've studied exponents, you know that
`` (−1)^{a⋅(b⊕s)} = (−1)^{a⋅b}(−1)^{a⋅s} ``. It means, if `` a⋅s = 0 ``, then `` (−1)^{a⋅(b⊕s)} = (−1)^{a⋅b} ``, hence they get added, and if, `` a⋅s = 1 ``, then  `` (−1)^{a⋅(b⊕s)} = -(−1)^{a⋅b} ``, hence they get cancelled out."

# ╔═╡ bf810cae-4699-11eb-3ef9-253fc690043e
md"### Circuit Implementation"

# ╔═╡ 319a200a-469a-11eb-0e9d-cb0f5d5c1a39
md"The circuit looks like this, where it takes a string of `` 0^{2n} `` as input, and the first n inputs, i.e. x, return x after passing through the circuit, and the next n inputs, i.e. y, return ` y ⨁ f(x) `, after passing through the circuit. Let's call this circuit `` U_𝑓 ``. This is for `` n = 2 ``."

# ╔═╡ 73c78fea-46ad-11eb-04f0-55a7f0b45d6a
plot(chain(2*2, repeat(H, 1:2), put(1:4 => label(chain(4), "U𝑓")), repeat(H, 1:2), Measure(4, locs=1:2)))

# ╔═╡ 4a5f9ac0-46ae-11eb-35c3-eb4cac737259
s = string(rand(1 : (2^2 - 1)), base=2, pad=2)

# ╔═╡ 14d1e556-46af-11eb-3a1e-152e76808279
begin
	if s == "11"
		U𝑓 = chain(4, control(1,3=>X), control(1,4=>X), control(2,3=>X), control(2,4=>X))
	elseif s == "01"
		U𝑓 = chain(4, control(1,3=>X), control(1,4=>X))
	elseif s == "10"
		U𝑓 = chain(4, control(2,3=>X), control(2,4=>X))
	end
	plot(U𝑓)
end

# ╔═╡ 716d8072-46d7-11eb-3837-fdfc895db226
begin
	SimonAlgoCircuit_for_n_2 = chain(4, repeat(H, 1:2), put(1:4=>U𝑓), repeat(H, 1:2))
	plot(SimonAlgoCircuit_for_n_2)
end

# ╔═╡ e4cfa0a4-46d7-11eb-3b3f-651d3646e5a2
output = zero_state(4) |> SimonAlgoCircuit_for_n_2 |> r->measure(r, 1:2, nshots=1024)

# ╔═╡ 50527e46-46d8-11eb-258f-d9943ac9187d
md"The reason it's a hybrid algorithm, is that we got two states, for `` n=2 ``, which have equal chances of being the secret string s. From here on, we've to classically deduce which of the measured states can be the output. Since s can't be `` 00 ``, s = $ $s $.

Note that this implementation is specific to `` n=2 ``

Deduction gets really complicated as n increases, and while its very very unlikely, on real quantum machines, there's a chance that you'll never get the secret string s for any number of runs, or nshots. This algorithm doesn't have much use/application cases either. Shor was inspired by this algorithm to make a general period finding algorithm."

# ╔═╡ Cell order:
# ╠═b9c84fff-8579-46fc-94cf-8bf6651ed0c0
# ╟─6184d07e-3dd2-11eb-085f-af802694d25a
# ╟─8006c71c-3dd2-11eb-13b5-e32876e57d65
# ╟─b2550450-3ea0-11eb-20ea-dd5194029a53
# ╠═2d4047c2-3ea3-11eb-1076-3dff1cef4ec9
# ╟─a6fc0830-3ea3-11eb-1421-4ff353057988
# ╠═cc70b2e6-3ea0-11eb-02b4-6702888f6d4a
# ╟─b176596e-3ea3-11eb-3d0a-fff2f58159bf
# ╟─1a13150c-4683-11eb-13a8-736ddb1a95f9
# ╟─2ce259f4-4683-11eb-3bb4-5555ecb46d2c
# ╟─81a82610-4683-11eb-04e9-11c2275ce99c
# ╟─16991654-4687-11eb-2516-3bb25117cc5e
# ╟─bc0b8262-468e-11eb-0dee-936c2e985baa
# ╟─f544222a-4691-11eb-2bee-8d4bc225e181
# ╟─0e8001f0-4692-11eb-2457-43db7e80877f
# ╟─59cd0f38-4695-11eb-02ab-7b82274020a9
# ╟─bf810cae-4699-11eb-3ef9-253fc690043e
# ╟─319a200a-469a-11eb-0e9d-cb0f5d5c1a39
# ╟─73c78fea-46ad-11eb-04f0-55a7f0b45d6a
# ╠═4a5f9ac0-46ae-11eb-35c3-eb4cac737259
# ╠═14d1e556-46af-11eb-3a1e-152e76808279
# ╠═716d8072-46d7-11eb-3837-fdfc895db226
# ╠═e4cfa0a4-46d7-11eb-3b3f-651d3646e5a2
# ╟─50527e46-46d8-11eb-258f-d9943ac9187d


text:
using Documenter
using TaD

makedocs(
    sitename = "TaD.jl",
    format = Documenter.HTML(),
    modules = [TaD]
)

# Documenter can also automatically deploy documentation to gh-pages.
# See "Hosting Documentation" and deploydocs() in the Documenter manual
# for more information.
deploydocs(
    repo = "github.com/johntfoster/TaD.jl.git"
)

ext: jl


text:
abstract type AbstractParticleFilter <: AbstractFilter end

struct PFstate{PT<:AbstractArray, FT<:AbstractFloat}
    x::Vector{PT}
    xprev::Vector{PT}
    w::Vector{FT}
    we::Vector{FT}
    maxw::Ref{FT}
    j::Vector{Int64}
    bins::Vector{Float64}
    t::Ref{Int}
end

PFstate(N::Integer) = PFstate([zeros(N)],[zeros(N)],fill(-log(N), N),fill(1/N, N),Ref(0.),collect(1:N),zeros(N), Ref(1))

@with_kw struct ParticleFilter{ST,FT,GT,FDT,GDT,IDT,RST<:DataType,RNGT} <: AbstractParticleFilter
    state::ST
    dynamics::FT
    measurement::GT
    dynamics_density::FDT
    measurement_density::GDT
    initial_density::IDT
    resample_threshold::Float64 = 0.1
    resampling_strategy::RST = ResampleSystematic
    rng::RNGT = MersenneTwister()
end

struct AuxiliaryParticleFilter{T<:AbstractParticleFilter} <: AbstractParticleFilter
    pf::T
end

AuxiliaryParticleFilter(args...;kwargs...) = AuxiliaryParticleFilter(ParticleFilter(args...;kwargs...))

"""
    ParticleFilter(num_particles, dynamics, measurement, dynamics_density, measurement_density, initial_density)
"""
function ParticleFilter(N::Integer, dynamics::Function, measurement::Function, dynamics_density, measurement_density, initial_density; kwargs...)
    xprev = Vector{SVector{length(initial_density),eltype(initial_density)}}([rand(initial_density) for n=1:N])
    x = deepcopy(xprev)
    w = fill(log(1/N), N)
    we = fill(1/N, N)
    s = PFstate(x,xprev,w,we,Ref(0.), collect(1:N), zeros(N),Ref(1))

    ParticleFilter(; state = s, dynamics, measurement,
    dynamics_density, measurement_density,
    initial_density, kwargs...)
end

function ParticleFilter(s::PFstate, dynamics::Function, measurement::Function, dynamics_density, measurement_density, initial_density; kwargs...)
    ParticleFilter(; state = s, dynamics, measurement,
    dynamics_density, measurement_density,
    initial_density, kwargs...)
end

Base.@propagate_inbounds function measurement_equation!(pf::ParticleFilter, u, y, t, w = pf.state.w, d=measurement_density(pf))
    x,g = particles(pf), measurement(pf)
    any(ismissing, y) && return w
    if d isa UnivariateDistribution && length(y) == 1
        for i = 1:num_particles(pf)
            w[i] += logpdf(d, (y-g(x[i],u,t))[1])
        end
    else
        for i = 1:num_particles(pf)
            w[i] += logpdf(d, y-g(x[i],u,t))
        end
    end
    w
end

Base.@propagate_inbounds function propagate_particles!(pf::ParticleFilter,u,j::Vector{Int}, t::Int, d=pf.dynamics_density)
    f = dynamics(pf)
    s = state(pf)
    x,xp = s.x, s.xprev
    VecT = eltype(s.x)
    D = length(VecT)
    noise = zeros(D)
    if d === nothing
        for i = eachindex(x)
            x[i] =  f(xp[j[i]], u, t)
        end
    else
        for i = eachindex(x)
            x[i] =  f(xp[j[i]], u, t) + VecT(rand!(pf.rng, d, noise))
        end
    end
    x
end

Base.@propagate_inbounds function propagate_particles!(pf::ParticleFilter, u, t::Int, d=pf.dynamics_density)
    f = pf.dynamics
    x,xp = pf.state.x, pf.state.xprev
    VecT = eltype(pf.state.x)
    D = length(VecT)
    noise = zeros(D)
    if d === nothing
        for i = eachindex(x)
            x[i] =  f(xp[i], u, t)
        end
    else
        for i = eachindex(x)
            x[i] =  f(xp[i], u, t) + VecT(rand!(pf.rng, d, noise))
        end
    end
    x
end

# AUX =================================================================================
@inline Random.rand!(_, d::Bool, args...) = 0 # To turn off noise in the dynamics update for pf aux

Base.@propagate_inbounds function add_noise!(pf::AbstractParticleFilter, d=pf.dynamics_density)
    x,xp = pf.state.x, pf.state.xprev
    VecT = eltype(pf.state.x)
    D = length(VecT)
    noise = zeros(D)

    for i = eachindex(x)
        x[i] += VecT(rand!(pf.rng, d, noise))
    end

    x
end
# Advanced =================================================================================

@with_kw struct AdvancedParticleFilter{ST,FT,GT,GLT,FDT,IDT,RST<:DataType,RNGT} <: AbstractParticleFilter
    state::ST
    dynamics::FT
    measurement::GT
    measurement_likelihood::GLT
    dynamics_density::FDT = Normal()
    initial_density::IDT
    resample_threshold::Float64 = 0.5
    resampling_strategy::RST = ResampleSystematic
    rng::RNGT = MersenneTwister()
end

"""
    AdvancedParticleFilter(Nparticles, dynamics, measurement, measurement_likelihood, dynamics_density, initial_density; kwargs...)
"""
function AdvancedParticleFilter(N::Integer, dynamics::Function, measurement::Function, measurement_likelihood, dynamics_density, initial_density; kwargs...)
    r1 = rand(initial_density)
    xprev = Vector{SVector{length(initial_density),eltype(r1)}}([rand(initial_density) for n=1:N])
    x  = deepcopy(xprev)
    w  = fill(log(1/N), N)
    we = fill(1/N, N)
    s = PFstate(x,xprev,w,we,Ref(0.), collect(1:N), zeros(N),Ref(1))

    AdvancedParticleFilter(; state = s, dynamics, measurement, measurement_likelihood, dynamics_density,
    initial_density, kwargs...)
end

Base.@propagate_inbounds function measurement_equation!(pf::AbstractParticleFilter, u, y, t, w = weights(pf))
    g = measurement_likelihood(pf)
    any(ismissing.(y)) && return w
    x = particles(pf)
    Threads.@threads for i = 1:num_particles(pf)
        @inbounds w[i] += g(x[i], u, y, t)
    end
    w
end

Base.@propagate_inbounds function propagate_particles!(pf::AdvancedParticleFilter, u, j::Vector{Int}, t::Int, noise=true)
    noise === nothing && (noise = false)
    f = dynamics(pf)
    s = state(pf)
    x,xp = s.x, s.xprev
    Threads.@threads for i = eachindex(x)
        @inbounds x[i] = f(xp[j[i]], u, t, noise) # TODO: lots of allocations here
    end
    x
end

Base.@propagate_inbounds function propagate_particles!(pf::AbstractParticleFilter, u, t::Int, noise=true)
    noise === nothing && (noise = false)
    f = pf.dynamics
    x,xp = particles(pf), state(pf).xprev
    Threads.@threads for i = eachindex(x)
        @inbounds x[i] = f(xp[i], u, t, noise)
    end
    x
end

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

@forward ParticleFilter.state num_particles, weights, particles, particletype
@forward AdvancedParticleFilter.state num_particles, weights, particles, particletype

@forward AuxiliaryParticleFilter.pf state, particles, weights, expweights, reset!, weigthed_mean, measurement_equation!, sample_state, sample_measurement, index, num_particles, particletype, dynamics, measurement, dynamics_density, measurement_density, initial_density, resample_threshold, resampling_strategy, rng, mode_trajectory, mean_trajectory

sample_state(pf::AbstractParticleFilter) = rand(pf.rng, pf.initial_density)
sample_state(pf::ParticleFilter, x, u, t) = dynamics(pf)(x,u,t) + rand(pf.rng, pf.dynamics_density)
sample_state(pf::AdvancedParticleFilter, x, u, t) = dynamics(pf)(x,u,t,true)
sample_measurement(pf::AdvancedParticleFilter, x, u, t) = measurement(pf)(x, u, t, true)
sample_measurement(pf::AbstractParticleFilter, x, u, t) = measurement(pf)(x, u, t) .+ rand(pf.rng, pf.measurement_density)
expweights(pf::AbstractParticleFilter) = pf.state.we
weights(s::PFstate)                    = s.w
expweights(s::PFstate)                 = s.we
index(pf::AbstractParticleFilter)      = pf.state.t[]

num_particles(s::PFstate)              = num_particles(s.x)
num_particles(s::AbstractArray)        = length(s)
particles(s::PFstate)                  = s.x
particletype(s::PFstate)               = eltype(s.x)
particletype(pf::AbstractFilter)       = eltype(state(pf).x)

@inline state(pf::AbstractParticleFilter) = pf.state
@inline dynamics(pf::AbstractParticleFilter) = pf.dynamics
@inline measurement(pf::AbstractParticleFilter) = pf.measurement
@inline measurement_likelihood(pf::AbstractParticleFilter) = pf.measurement_likelihood
@inline dynamics_density(pf::AbstractParticleFilter) = pf.dynamics_density
@inline measurement_density(pf::AbstractParticleFilter) = pf.measurement_density
@inline initial_density(pf::AbstractParticleFilter) = pf.initial_density
@inline resample_threshold(pf::AbstractParticleFilter) = pf.resample_threshold
@inline resampling_strategy(pf::AbstractParticleFilter) = pf.resampling_strategy
@inline rng(pf::AbstractParticleFilter) = pf.rng


text:
using Zygote, Test
using Zygote: gradient

@testset "Zygote" begin

@info "Testing compiler features"

@testset "Features" begin
  include("features.jl")
end

@info "Testing data structures"

@testset "Data Structures" begin
  include("structures.jl")
end

@info "Running Gradient Checks"

@testset "Gradients" begin
  include("gradcheck.jl")
end

@testset "Complex" begin
  include("complex.jl")
end

@info "Testing Inference & Debug Info"

@testset "Compiler" begin
  include("compiler.jl")
end

using CUDAapi
if has_cuda()
  @info "Starting GPU integration tests..."
  @testset "CUDA tests" begin
    include("cuda.jl")
  end
else
  @warn "CUDA not found - Skipping CUDA Tests"
end

end


text:
module test_bukdu_router

using Test
using Bukdu
using HTTP.Messages: header

struct ExController <: ApplicationController
    conn::Conn
end

f1(::ExController) = nothing
f2(::ExController) = "a"
f3(::ExController) = SubString("ab")
f4(::ExController) = 0
f5(::ExController) = 3.14
f6(::ExController) = (1, 2)
f7(::ExController) = (a=1,)

g1(::ExController) = render(JSON, nothing)
g2(::ExController) = render(JSON, "a")
g3(::ExController) = render(JSON, SubString("ab"))
g4(::ExController) = render(JSON, 0)
g5(::ExController) = render(JSON, 3.14)
g6(::ExController) = render(JSON, (1, 2))
g7(::ExController) = render(JSON, (a=1,))

routes() do
    for func in [f1 f2 f3 f4 f5 f6 f7
                 g1 g2 g3 g4 g5 g6 g7]
        get(string("/", nameof(func)), ExController, func)
    end
end

result = Router.call(get, "/f2")
@test result.got == "a"
@test header(result.resp, "Content-Type") == "application/julia; charset=utf-8"
@test result.resp.body == Vector{UInt8}(repr("a"))

for (f, g) in zip([f1 f2 f3 f4 f5 f6 f7],
                  [g1 g2 g3 g4 g5 g6 g7])
    result1 = Router.call(get, string("/", nameof(f)))
    result2 = Router.call(get, string("/", nameof(g)))
    @test result1.got == result2.got
end

Routing.reset!()

# issue 87

using  JSON

struct IndexController <: ApplicationController
    conn::Conn
end

function something(c::IndexController)
    data = c.conn.params[:data]
    return render(JSON,  Dict("status"=>data=="data"))
end

function config_routes()
    routes() do
        post("/something", IndexController, something)
    end
end

function test_something()
    config_routes()
    response = Router.call(post,
                           "/something",
                           ["Content-Type" => "application/json"],
                           JSON.json(Dict("data"=>"data")))
    @test response.got.status == true
end
test_something()

Routing.reset!()

end # module test_bukdu_router


text:
# VEM computes the virtual element solution of a Poisson problem on a polygonal mesh
# Translation to Julia of MATLAB code from paper:
# O. Suttton, The virtual element method in 50 lines of MATLAB, Numer Algor(2017) 75:1141-1159

using HDiscontinuousGalerkin
using Tensors
using SparseArrays
using LinearAlgebra

mesh = rectangle_mesh(RectangleCell, (10,10), Vec{2}((0.0,0.0)), Vec{2}((1.0,1.0)));

# forcing function
rhs(x::Vec{2}) = 15 * sin(π * x[1]) * sin(π * x[2]);
# Boundary condition
g(x::Vec{2}) = (1 - x[1])*x[2]*sin(π * x[1]);
n_dofs = getnnodes(mesh)
n_polys = 3; # Method has 1 degree of freedom per vertex
assembler = start_assemble(n_dofs)
F = zeros(n_dofs); # Forcing vector
u = zeros(n_dofs); # Degrees of freedom of the virtual element solution
linear_polynomials = ((0,0), (1,0), (0,1)); # Impose an ordering on the linear polynomials
for el_id = 1:getncells(mesh)
	vert_ids = mesh.cells[el_id].nodes # Global IDs of the vertices of this element
	verts = get_coordinates(mesh.cells[el_id],  mesh) # Coordinates of the vertices of this element
	n_sides = getnfaces(mesh.cells[el_id]) # Start computing the geometric information
	area = cell_volume(mesh, el_id)
	centr = cell_centroid(mesh, el_id)
	diameter = cell_diameter(mesh, el_id)
	D = zeros(n_sides, n_polys); D[:, 1] .= 1;
	B = zeros(n_polys, n_sides); B[1, :] .= 1/n_sides;
	for vertex_id = 1:n_sides
		vert = verts[vertex_id]; #This vertex and its neighbours
		prev = verts[mod1(vertex_id - 1, n_sides)];
		nextv = verts[mod1(vertex_id + 1, n_sides)];
		vertex_normal = [nextv[2] - prev[2], prev[1] - nextv[1]]; # Average of edge normals
		for poly_id in 2:n_polys # Only need to loop over non-constant polynomials
			poly_degree = linear_polynomials[poly_id];
			monomial_grad = poly_degree ./ diameter; # Gradient of a linear is constant
			D[vertex_id, poly_id] = dot(vert - centr, poly_degree) / diameter;
			B[poly_id, vertex_id] = 0.5 * dot(monomial_grad, vertex_normal);
		end
	end
	projector = (B*D) \ B; # Compute the local Ritz projector to polynomials
	stabilising_term = (I - D * projector)' * (I - D * projector);
	G = B*D; G[1, :] .= 0;
	local_stiffness = projector' * G * projector + stabilising_term;
	assemble!(assembler,vert_ids,local_stiffness)
	assemble!(F, [dof for dof in vert_ids], rhs(centr)*ones(length(vert_ids)) * area / n_sides)
end
K = end_assemble(assembler)
boundary_nodes = getnodeset(mesh,"boundary")
boundary_vals = Vector{Float64}(undef, length(boundary_nodes))
for (i,node) in enumerate(boundary_nodes)
	boundary_vals[i] = g(mesh.nodes[node].x)
end

internal_dofs = setdiff(1:n_dofs, boundary_nodes)
F = F - K[:, collect(boundary_nodes)] * boundary_vals; # Apply the boundary condition
u[internal_dofs] = K[collect(internal_dofs), collect(internal_dofs)] \ F[collect(internal_dofs)]; # Solve
u[collect(boundary_nodes)] = boundary_vals; # Set the boundary values

#plot solution
coordinates = get_vertices_matrix(mesh);
connectivity = getcells_matrix(mesh);
color = u
using Makie, CairoMakie
using ColorSchemes
scene = Scene(resolution = (400, 200), colorrange = (0.0, 1.0), colormap = ColorSchemes.viridis.colors)
for row in 1:size(connectivity)[1]
	#read coordinates
	points = node(:poly, Point2f0[coordinates[i,:] for i in connectivity[row,:]])
	colors = get(ColorSchemes.viridis, [color[i] for i in connectivity[row,:]])
	poly!(scene, points, strokewidth = 1, strokecolor = :black, color = colors, show_axis = true, scale_plot = false)
end
display(scene)


text:
include("../src/RawArray.jl")
using .RawArray
using Test

print("raquery ... ")
s = raquery("../examples/test.ra")
@test s == "---\nname: ../examples/test.ra\nendian: little\ncompressed: 0\nbits: 0\ntype: Complex{Float32}\nsize: 96\ndimension: 2\nshape:\n  - 3\n  - 4\n..."
println("PASS")

function test_wr(t, dims; compress=false)
  testfile = "tmp.ra"
  n = length(dims)
  print("Testing $n-d $t $(compress ? "compressed" : "uncompressed") ... ")
  if t == BitArray
    data = BitArray(rand(Bool, dims...))
  else
    data = rand(t, dims...)
  end
  rawrite(data, testfile; compress=compress)
  data2 = raread(testfile)
  @test isequal(data, data2)
  rm(testfile)
  println("PASS")
end

typelist = [Float16, Float32, Float64, Complex{Float16}, Complex{Float32}, Complex{Float64}, Int8, Int16, Int32, Int64, UInt8, UInt16, UInt32, UInt64, Bool, BitArray]
maxdims = 4
for t in typelist, n in 1:maxdims
  test_wr(t, collect(2:n+1))
  if t <: Integer && t != BitArray
    test_wr(t, collect(2:n+1); compress=true)
  end
end


text:
# solver
mutable struct FPSolver{algType,iip,uTolType,C<:DiffEqBase.AbstractNLSolverCache} <: OrdinaryDiffEq.AbstractNLSolver{algType,iip}
  alg::algType
  κ::uTolType
  fast_convergence_cutoff::uTolType
  ηold::uTolType
  iter::Int
  maxiters::Int
  status::OrdinaryDiffEq.NLStatus
  cache::C
end

# caches
struct FPFunctionalCache{uType,uNoUnitsType} <: DiffEqBase.AbstractNLSolverCache
  atmp::uNoUnitsType
  dz::uType
end

struct FPFunctionalConstantCache <: DiffEqBase.AbstractNLSolverCache end

mutable struct FPAndersonCache{uType,uNoUnitsType,uEltypeNoUnits,D} <: DiffEqBase.AbstractNLSolverCache
  atmp::uNoUnitsType
  dz::uType
  dzold::uType
  z₊old::uType
  Δz₊s::Vector{uType}
  Q::Matrix{uEltypeNoUnits}
  R::Matrix{uEltypeNoUnits}
  γs::Vector{uEltypeNoUnits}
  history::Int
  aa_start::Int
  droptol::D
end

mutable struct FPAndersonConstantCache{uType,uEltypeNoUnits,D} <: DiffEqBase.AbstractNLSolverCache
  dz::uType
  dzold::uType
  z₊old::uType
  Δz₊s::Vector{uType}
  Q::Matrix{uEltypeNoUnits}
  R::Matrix{uEltypeNoUnits}
  γs::Vector{uEltypeNoUnits}
  history::Int
  aa_start::Int
  droptol::D
end


text:
using DataFrames, RDatasets, FixedEffectModels, RegressionTables
df = dataset("plm", "Cigar")
r1 = reg(df, @formula(Sales ~ NDI + fe(State) + fe(Year)), Vcov.cluster(:State), weights = :Pop)
regtable(r1; renderSettings = asciiOutput())

max_stars_repo_name: nikhilklath/julia-test

max_issues_repo_name: nikhilklath/julia-test

max_forks_repo_name: nikhilklath/julia-test


text:
# load packages
using CSV
using Random

"""
    set_up(;μ_θ_1::Real = -0.7, τ_θ_1::Real = 1.5, μ_θ_2::Real = 1.5, τ_θ_2::Real = 1.2, μ_θ_3::Real = -0.9, τ_θ_3::Real = 1.5, σ_ϵ::Real = 0.3, dt::Real=0.1, M::Int=5, N::Int=100)

Returns ground-truth parameter values and simulated data from the OU SDEMEM model.
"""
function set_up(;μ_θ_1::Real = -0.7, τ_θ_1::Real = 4,
                 μ_θ_2::Real = 2.3, τ_θ_2::Real = 10,
                 μ_θ_3::Real = -0.9, τ_θ_3::Real = 4,
                 σ_ϵ::Real = 0.3, dt::Real=0.05, M::Int=5, N::Int=100,seed::Int)

    # sample random effects
    Random.seed!(seed)

    ϕ = zeros(M,3)

    for i = 1:M
        ϕ[i,1] = μ_θ_1 + sqrt(1/τ_θ_1)*randn()
        ϕ[i,2] = μ_θ_2 + sqrt(1/τ_θ_2)*randn()
        ϕ[i,3] = μ_θ_3 + sqrt(1/τ_θ_3)*randn()
    end

    # model paramters
    η = [μ_θ_1; μ_θ_2; μ_θ_3; τ_θ_1; τ_θ_2; τ_θ_3]

    # define priors
    μ_0_1 = 0
    M_0_1 = 1
    α_1 = 2
    β_1 = 1

    μ_0_2 = 1
    M_0_2 = 1
    α_2 = 2
    β_2 = 1/2

    μ_0_3 = 0
    M_0_3 = 1
    α_3 = 2
    β_3 = 1

    prior_parameters_η = [μ_0_1 M_0_1 α_1 β_1;μ_0_2 M_0_2 α_2 β_2;μ_0_3 M_0_3 α_3 β_3]

    prior_parameters_σ_ϵ = [1; 1/0.4] # Gamma with scale parameter α = 1 and rate parameter β = 1/0.4

    # generate data

    Random.seed!(seed)
    y,x,t_vec = generate_data(N, M, η, ϕ, σ_ϵ,dt)

    # return: data, model parameteres, and parameteres for priors
    return y,x,t_vec,dt,η,σ_ϵ,ϕ,prior_parameters_η,prior_parameters_σ_ϵ

end

"""
    generate_data(N::Int, M::Int, η::Array, ϕ::Array, σ_ϵ::Real,dt::Real)

Generate data from the OU SDEMEM model.
"""
function generate_data(N::Int, M::Int, η::Array, ϕ::Array, σ_ϵ::Real,dt::Real)

    x_0 = zeros(M) # pre-allocate matriceis
    x = zeros(M,N)
    y = zeros(M,N)
    t_vec = zeros(N)

    for m = 1:M

        x[m,1] = x_0[m] # set start value
        y[m,1] = x[m,1] + σ_ϵ*randn()

        θ_1 = exp(ϕ[m,1]) # set parameters for subject m
        θ_2 = exp(ϕ[m,2])
        θ_3 = exp(ϕ[m,3])

        σ_cond = sqrt((θ_3^2/(2*θ_1))*(1-exp(-2*θ_1*dt))) # set latent process std for subject m

        # simulate process for subject m
        for t = 2:N
            t_vec[t] = t_vec[t-1] + dt
            μ_cond = θ_2 + (x[m,t-1]-θ_2)*exp(-θ_1*dt)
            x[m,t] = μ_cond + σ_cond*randn()
            y[m,t] = x[m,t] + σ_ϵ*randn()
        end
    end

    # return data
    return y,x,t_vec

end

max_stars_repo_path: src/SDEMEM OU process/ou_sdemem.jl

max_issues_repo_path: src/SDEMEM OU process/ou_sdemem.jl

max_forks_repo_path: src/SDEMEM OU process/ou_sdemem.jl


text:
module FilesystemDatastructures

# Include the code for this package
include("DiskUtils.jl")
include("SizeConstrainedFileCache.jl")
include("NFileCache.jl")

# precompilation
include(joinpath(dirname(@__DIR__), "deps", "precompile.jl"))
if get(ENV, "FILESYSTEMDATASTRUCTURES_GENERATING_PRECOMPILE", nothing) === nothing
    _precompile_()
end
end # module


text:
@register ∨(x, y)   # \wedge
@register ∧(x, y)   # \vee
@register ¬(x)      # \neg

# @register ∨(x...)
# @register ∨(x::AbstractVector{T} where T)

const DiscreteTypes = Union{Integer, Symbol}

@register Base.:∈(x, y::AbstractRange{T} where T<:DiscreteTypes)
@register Base.:∈(x, y::UnitRange{T} where T<:DiscreteTypes) false  # false prevents defining
@register Base.:∈(x, y::AbstractVector{T} where T<:DiscreteTypes) false
@register Base.:∈(x, y::Vector{T} where T<:DiscreteTypes) false
@register Base.:∈(x, y::Tuple) false

# TODO: Varargs ∨

∨(x::Vector{T}) where {T} = isempty(x) ? T[] : ∨(x...)

∨(x...) = foldl(∨, x)
# ∨(x...) = Symbolics.Term(∨, [x...])
∧(x...) = foldl(∧, x)


text:
using Documenter, Literate
import TruncatedNormal as TN

ENV["JULIA_DEBUG"] = "Documenter,Literate,TruncatedNormal"

#=
We place Literate.jl source .jl files and the generated .md files inside docs/src/literate.
=#
const literate_dir = joinpath(@__DIR__, "src/literate")

#=
Helper function to remove all "*.md" files from a directory.
=#
function clear_md_files(dir::String)
    for file in readdir(dir; join=true)
        if endswith(file, ".md")
            rm(file)
        end
    end
end

#=
Remove previously Literate.jl generated files. This removes all "*.md" files inside
`literate_dir`. This is a precaution: if we build docs locally and something fails,
and then change the name of a source file (".jl"), we will be left with a lingering
".md" file which will be included in the docs build. The following line makes sure
that doesn't happen.
=#
clear_md_files(literate_dir)

#=
Run Literate.jl on the .jl source files within docs/literate.
This creates the markdown .md files inside docs/src/literate,
with the same name but with the extension changes (.jl -> .md).
=#
for file in readdir(literate_dir; join=true)
    if endswith(file, ".jl")
        Literate.markdown(file, literate_dir)
    end
end

#=
Build docs.
=#
makedocs(
    modules = [TN],
    sitename = "TruncatedNormal.jl",
    pages = [
        "Home" => "index.md",
        "Reference" => "reference.md",
    ],
    strict = true
)

#=
After the docs have been compiled, we can remove the *.md files generated by Literate.
=#
clear_md_files(literate_dir)

#=
Deploy docs to Github pages.
=#
deploydocs(
    repo = "github.com/cossio/TruncatedNormal.jl.git",
    devbranch = "master"
)


text:
using RungeKutta
using Base.Test

# TEST 1 - Correct answer.
x_truth_test1 = [1.0 0.5416666666666; 0.0 -0.8333333333333333];
t_truth_test1 = [0.0 1.0];

f_test1 = [(t,x) -> x[2], (t,x) -> -x[1]];
x0_test1 = [1.0, 0.0];
t0_test1 = 0.0;
h_test1 = 1.0;
n_test1 = 1;

t_answer_test1, x_answer_test1 =
    rk4f(f_test1, x0_test1, t0_test1, h_test1, n_test1);

@test_approx_eq t_answer_test1 t_truth_test1
@test_approx_eq x_answer_test1 x_truth_test1

# TEST 2 - Invalid argument dimension.
f_test2 = [(t,x) -> x[2], (t,x) -> -x[1]];
x0_test2 = [1.0, 0.0, -1.0];
t0_test2 = 0.0;
h_test2 = 1.0;
n_test2 = 1;

@test_throws ArgumentError rk4f(
    f_test2, x0_test2, t0_test2, h_test2, n_test2);

# TEST 3 - Invalid number of iterations.
f_test3 = f_test2;
x0_test3 = x0_test2;
t0_test3 = t0_test2;
h_test3 = h_test2;
n_test3 = -4;

@test_throws ArgumentError rk4f(
    f_test3, x0_test3, t0_test3, h_test3, n_test3);

# TEST 4 - Invalid step size.
f_test4 = f_test3;
x0_test4 = x0_test3;
t0_test4 = t0_test3;
h_test4 = -0.5;
n_test4 = n_test2;

@test_throws ArgumentError rk4f(
    f_test4, x0_test4, t0_test4, h_test4, n_test4);


text:
using LowRankModels
Random.seed!(1);

test_losses = Loss[
QuadLoss(),
L1Loss(),
HuberLoss(),
PeriodicLoss(1),
OrdinalHingeLoss(1,10),
LogisticLoss(),
WeightedHingeLoss()
]

for test_iteration = 1:500
	# Create the configuration for the model (random losses)
	config = round.(Int, abs(round(4*rand(length(test_losses)))));
	#config = [0 0 1 0 10 0 100]
	losses, doms = Array(Loss,1), Array(Domain,1);
	for (n,l) in zip(config, test_losses)
		for i=1:n
			push!(losses, l);
			push!(doms, l.domain);
		end
	end
	losses, doms = losses[2:end], doms[2:end]; # this is because the initialization leaves us with an #undef
	my_error_metric(glrm::GLRM, X::Array{Float64,2}, Y::Array{Float64,2}) = error_metric(glrm, X, Y, doms, standardize=true) # embed the domains into the error function.

	# Make a low rank matrix as our data precursor
	m, n, true_k = 100, length(doms), round.(Int, round(length(losses)/2))+1;

	X_real, Y_real = randn(true_k,m), randn(true_k,n);
	A_real = X_real'*Y_real;

	# Impute over the low rank-precursor to make our heterogenous dataset
	A = impute(doms, losses, A_real);				# our data with noise

	# Create a glrm using these losses and data
	p = Params(1e-2, max_iter=1000, abs_tol=0.00000001, min_stepsize=1e-15)
	rx, ry = ZeroReg(), ZeroReg();

	k_range = [int(round(true_k/2)), true_k]
	train_err_at_k = Dict()
	println("\n########################################################")
	println("Model:\nlosses = $(losses)\nrx,ry = $(rx), $(ry)")
	for k in k_range
		model = GLRM(A, losses, rx,ry,k, scale=false, offset=false);
		println("\nk = $(k)")
		# Test that our imputation is consistent
		if my_error_metric(model, X_real, Y_real) != 0
			error("Imputation failed.")
		end

		real_obj = objective(model, X_real, Y_real, include_regularization=false);
		X_fit,Y_fit,ch = fit!(model, params=p, verbose=false);
		println("Starting objective: $(ch.objective[1])\t Ending objective: $(ch.objective[end])")

		train_err, test_err, trainers, testers = cross_validate(model, nfolds=3, error_fn=my_error_metric, verbose=false);
		train_err_at_k[k] = my_error_metric(model, model.X, model.Y)
		println("train err: $train_err")
		println("Test err: $test_err")
	end
	train_err_at_k = [train_err_at_k[k] for k in k_range]
	if !all([de<0 for de in diff(train_err_at_k)]) #check that errors monotonically decrease as k increases
		@warn("==================================================================================")
		@warn("ERRORS WENT UP FOR THIS CONFIGURATION")
		@warn("Model:\nlosses = $(losses)\nrx,ry = $(rx), $(ry)")
		@warn("Ranks: $(string(k_range))")
		@warn("Training errors: $(string(train_err_at_k))")
		@warn("==================================================================================")
	end
end


text:
######################################
# Boolean functions for xy coordinates
######################################

"Parylene walls in between sensors"
function inwalls(x::Float64, y::Float64)::Bool
    withinx = !incentersensorx(x) && !ininnersensorx(x) && !inoutersensorx(x)
    withiny = y <= WALL_Y
    return withinx && withiny
end

"Where no walls, enzymatic layer, nor ppd layers are"
function inwater(xy::Array{Float64,1})::Bool
    x, y = xy
    inadjacentwell = PPD_MAX_Y < y <= WALL_Y && (ininnersensorx(x) || inoutersensorx(x))
    inwaterabovewalls = y > WALL_Y && inescapebounds(x, y) && !inoverflowenz(x, y)
    return inadjacentwell || inwaterabovewalls
end

"Only in the center sensor over ppd, overflowing slightly over adjacent walls"
function inenz(xy::Array{Float64,1})::Bool
    x, y = xy
    if PPD_MAX_Y < y <= WALL_Y
        return incentersensorx(x)
    else
        return inoverflowenz(x, y)
    end
end

"Thin layer of phenylenediamine covering all five sensors"
function inppd(xy::Array{Float64,1})::Bool
    x,y = xy
    withinx = incentersensorx(x) || ininnersensorx(x) || inoutersensorx(x)
    withiny = PPD_MIN_Y < y <= PPD_MAX_Y
    return withinx && withiny
end

"Helper for inenz, specifies how much overflow"
function inoverflowenz(x::Float64, y::Float64)::Bool
    withinx = ENZYME_LEFT_X <= x <= ENZYME_RIGHT_X
    withiny = WALL_Y < y <= ENZYME_MAX_Y
    return withinx && withiny
end

"Above the center sensor"
function incentersensorx(x::Float64)::Bool
    return abs(x) < SENSOR_CENTER_MAX_X
end

"Above either inner sensor"
function ininnersensorx(x::Float64)::Bool
    return SENSOR_INNER_ADJ_MIN_X < abs(x) < SENSOR_INNER_ADJ_MAX_X
end

"Above either outer sensor"
function inoutersensorx(x::Float64)::Bool
    return SENSOR_OUTER_ADJ_MIN_X < abs(x) < SENSOR_OUTER_ADJ_MAX_X
end

"Entered any sensor"
function insensor(xy::Array{Float64,1})::Bool
    y = xy[2]
    return y <= 0
end

#####################################
# HELPER FUNCTIONS FOR boundary_check
#####################################

function sensorcases(initx::Float64)::String
    if incentersensorx(initx)
        return "center sensor"
    elseif initx < 0
        position = "left"
    else
        position = "right"
    end

    if ininnersensorx(initx)
        return position * " inner sensor"
    elseif inoutersensorx(initx)
        return position * " outer sensor"
    end
end

function wallcases(initxy::Array{Float64,1},
                   dx::Float64,
                   dy::Float64,
                   ending_step_size::Float64)
    if !CATALASE_ON_WALLS
        return approach_wall!(initxy, dx, dy, ending_step_size), "no collision"
    end

    if sortwall(initxy) == "side wall"
        return approach_wall!(initxy, dx, dy, ending_step_size), "side wall"
    else
        return undef, "top wall"
    end
end

"Distinguish top and side wall collisions when CATALASE_ON_WALLS"
function sortwall(initxy::Array{Float64,1})::String
    initx, inity = initxy
    if inity <= WALL_Y      # from between walls
        return "side wall"
    elseif inity > WALL_Y   # from above walls
        if !(incentersensorx(initx) || ininnersensorx(initx) || inoutersensorx(initx))
            return "top wall"
        end
    end
    # ambiguous corner case:
    #   in sensor region, above wall height -- coinflip
    if rand(Float64) > 0.5
        return "side wall"
    else
        return "top wall"
    end
end

max_stars_repo_name: s-y-wu/HMCResearchRandomWalks

max_issues_repo_name: s-y-wu/HMCResearchRandomWalks

max_forks_repo_name: s-y-wu/HMCResearchRandomWalks


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

"""
    BatchGetNamedQuery()

Returns the details of a single named query or a list of up to 50 queries, which you provide as an array of query ID strings. Requires you to have access to the workgroup in which the queries were saved. Use ListNamedQueriesInput to get the list of named query IDs in the specified workgroup. If information could not be retrieved for a submitted query ID, information about the query ID submitted is listed under UnprocessedNamedQueryId. Named queries differ from executed queries. Use BatchGetQueryExecutionInput to get details about each unique query execution, and ListQueryExecutionsInput to get a list of query execution IDs.

# Required Parameters
- `NamedQueryIds`: An array of query IDs.

"""
batch_get_named_query(NamedQueryIds; aws_config::AWSConfig=global_aws_config()) = athena("BatchGetNamedQuery", Dict{String, Any}("NamedQueryIds"=>NamedQueryIds); aws_config=aws_config)
batch_get_named_query(NamedQueryIds, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = athena("BatchGetNamedQuery", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("NamedQueryIds"=>NamedQueryIds), args)); aws_config=aws_config)

"""
    BatchGetQueryExecution()

Returns the details of a single query execution or a list of up to 50 query executions, which you provide as an array of query execution ID strings. Requires you to have access to the workgroup in which the queries ran. To get a list of query execution IDs, use ListQueryExecutionsInputWorkGroup. Query executions differ from named (saved) queries. Use BatchGetNamedQueryInput to get details about named queries.

# Required Parameters
- `QueryExecutionIds`: An array of query execution IDs.

"""
batch_get_query_execution(QueryExecutionIds; aws_config::AWSConfig=global_aws_config()) = athena("BatchGetQueryExecution", Dict{String, Any}("QueryExecutionIds"=>QueryExecutionIds); aws_config=aws_config)
batch_get_query_execution(QueryExecutionIds, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = athena("BatchGetQueryExecution", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("QueryExecutionIds"=>QueryExecutionIds), args)); aws_config=aws_config)

"""
    CreateDataCatalog()

Creates (registers) a data catalog with the specified name and properties. Catalogs created are visible to all users of the same AWS account.

# Required Parameters
- `Name`: The name of the data catalog to create. The catalog name must be unique for the AWS account and can use a maximum of 128 alphanumeric, underscore, at sign, or hyphen characters.
- `Type`: The type of data catalog to create: LAMBDA for a federated catalog, GLUE for AWS Glue Catalog, or HIVE for an external hive metastore.

# Optional Parameters
- `Description`: A description of the data catalog to be created.
- `Parameters`: Specifies the Lambda function or functions to use for creating the data catalog. This is a mapping whose values depend on the catalog type.    For the HIVE data catalog type, use the following syntax. The metadata-function parameter is required. The sdk-version parameter is optional and defaults to the currently supported version.  metadata-function=lambda_arn, sdk-version=version_number     For the LAMBDA data catalog type, use one of the following sets of required parameters, but not both.   If you have one Lambda function that processes metadata and another for reading the actual data, use the following syntax. Both parameters are required.  metadata-function=lambda_arn, record-function=lambda_arn      If you have a composite Lambda function that processes both metadata and data, use the following syntax to specify your Lambda function.  function=lambda_arn       The GLUE type has no parameters.
- `Tags`: A list of comma separated tags to add to the data catalog that is created.
"""
create_data_catalog(Name, Type; aws_config::AWSConfig=global_aws_config()) = athena("CreateDataCatalog", Dict{String, Any}("Name"=>Name, "Type"=>Type); aws_config=aws_config)
create_data_catalog(Name, Type, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = athena("CreateDataCatalog", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("Name"=>Name, "Type"=>Type), args)); aws_config=aws_config)

"""
    CreateNamedQuery()

Creates a named query in the specified workgroup. Requires that you have access to the workgroup. For code samples using the AWS SDK for Java, see Examples and Code Samples in the Amazon Athena User Guide.

# Required Parameters
- `Database`: The database to which the query belongs.
- `Name`: The query name.
- `QueryString`: The contents of the query with all query statements.

# Optional Parameters
- `ClientRequestToken`: A unique case-sensitive string used to ensure the request to create the query is idempotent (executes only once). If another CreateNamedQuery request is received, the same response is returned and another query is not created. If a parameter has changed, for example, the QueryString, an error is returned.  This token is listed as not required because AWS SDKs (for example the AWS SDK for Java) auto-generate the token for users. If you are not using the AWS SDK or the AWS CLI, you must provide this token or the action will fail.
- `Description`: The query description.
- `WorkGroup`: The name of the workgroup in which the named query is being created.
"""
create_named_query(Database, Name, QueryString; aws_config::AWSConfig=global_aws_config()) = athena("CreateNamedQuery", Dict{String, Any}("Database"=>Database, "Name"=>Name, "QueryString"=>QueryString, "ClientRequestToken"=>string(uuid4())); aws_config=aws_config)
create_named_query(Database, Name, QueryString, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = athena("CreateNamedQuery", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("Database"=>Database, "Name"=>Name, "QueryString"=>QueryString, "ClientRequestToken"=>string(uuid4())), args)); aws_config=aws_config)

"""
    CreateWorkGroup()

Creates a workgroup with the specified name.

# Required Parameters
- `Name`: The workgroup name.

# Optional Parameters
- `Configuration`: The configuration for the workgroup, which includes the location in Amazon S3 where query results are stored, the encryption configuration, if any, used for encrypting query results, whether the Amazon CloudWatch Metrics are enabled for the workgroup, the limit for the amount of bytes scanned (cutoff) per query, if it is specified, and whether workgroup's settings (specified with EnforceWorkGroupConfiguration) in the WorkGroupConfiguration override client-side settings. See WorkGroupConfigurationEnforceWorkGroupConfiguration.
- `Description`: The workgroup description.
- `Tags`: A list of comma separated tags to add to the workgroup that is created.
"""
create_work_group(Name; aws_config::AWSConfig=global_aws_config()) = athena("CreateWorkGroup", Dict{String, Any}("Name"=>Name); aws_config=aws_config)
create_work_group(Name, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = athena("CreateWorkGroup", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("Name"=>Name), args)); aws_config=aws_config)

"""
    DeleteDataCatalog()

Deletes a data catalog.

# Required Parameters
- `Name`: The name of the data catalog to delete.

"""
delete_data_catalog(Name; aws_config::AWSConfig=global_aws_config()) = athena("DeleteDataCatalog", Dict{String, Any}("Name"=>Name); aws_config=aws_config)
delete_data_catalog(Name, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = athena("DeleteDataCatalog", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("Name"=>Name), args)); aws_config=aws_config)

"""
    DeleteNamedQuery()

Deletes the named query if you have access to the workgroup in which the query was saved. For code samples using the AWS SDK for Java, see Examples and Code Samples in the Amazon Athena User Guide.

# Required Parameters
- `NamedQueryId`: The unique ID of the query to delete.

"""
delete_named_query(NamedQueryId; aws_config::AWSConfig=global_aws_config()) = athena("DeleteNamedQuery", Dict{String, Any}("NamedQueryId"=>NamedQueryId); aws_config=aws_config)
delete_named_query(NamedQueryId, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = athena("DeleteNamedQuery", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("NamedQueryId"=>NamedQueryId), args)); aws_config=aws_config)

"""
    DeleteWorkGroup()

Deletes the workgroup with the specified name. The primary workgroup cannot be deleted.

# Required Parameters
- `WorkGroup`: The unique name of the workgroup to delete.

# Optional Parameters
- `RecursiveDeleteOption`: The option to delete the workgroup and its contents even if the workgroup contains any named queries.
"""
delete_work_group(WorkGroup; aws_config::AWSConfig=global_aws_config()) = athena("DeleteWorkGroup", Dict{String, Any}("WorkGroup"=>WorkGroup); aws_config=aws_config)
delete_work_group(WorkGroup, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = athena("DeleteWorkGroup", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("WorkGroup"=>WorkGroup), args)); aws_config=aws_config)

"""
    GetDataCatalog()

Returns the specified data catalog.

# Required Parameters
- `Name`: The name of the data catalog to return.

"""
get_data_catalog(Name; aws_config::AWSConfig=global_aws_config()) = athena("GetDataCatalog", Dict{String, Any}("Name"=>Name); aws_config=aws_config)
get_data_catalog(Name, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = athena("GetDataCatalog", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("Name"=>Name), args)); aws_config=aws_config)

"""
    GetDatabase()

Returns a database object for the specfied database and data catalog.

# Required Parameters
- `CatalogName`: The name of the data catalog that contains the database to return.
- `DatabaseName`: The name of the database to return.

"""
get_database(CatalogName, DatabaseName; aws_config::AWSConfig=global_aws_config()) = athena("GetDatabase", Dict{String, Any}("CatalogName"=>CatalogName, "DatabaseName"=>DatabaseName); aws_config=aws_config)
get_database(CatalogName, DatabaseName, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = athena("GetDatabase", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("CatalogName"=>CatalogName, "DatabaseName"=>DatabaseName), args)); aws_config=aws_config)

"""
    GetNamedQuery()

Returns information about a single query. Requires that you have access to the workgroup in which the query was saved.

# Required Parameters
- `NamedQueryId`: The unique ID of the query. Use ListNamedQueries to get query IDs.

"""
get_named_query(NamedQueryId; aws_config::AWSConfig=global_aws_config()) = athena("GetNamedQuery", Dict{String, Any}("NamedQueryId"=>NamedQueryId); aws_config=aws_config)
get_named_query(NamedQueryId, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = athena("GetNamedQuery", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("NamedQueryId"=>NamedQueryId), args)); aws_config=aws_config)

"""
    GetQueryExecution()

Returns information about a single execution of a query if you have access to the workgroup in which the query ran. Each time a query executes, information about the query execution is saved with a unique ID.

# Required Parameters
- `QueryExecutionId`: The unique ID of the query execution.

"""
get_query_execution(QueryExecutionId; aws_config::AWSConfig=global_aws_config()) = athena("GetQueryExecution", Dict{String, Any}("QueryExecutionId"=>QueryExecutionId); aws_config=aws_config)
get_query_execution(QueryExecutionId, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = athena("GetQueryExecution", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("QueryExecutionId"=>QueryExecutionId), args)); aws_config=aws_config)

"""
    GetQueryResults()

Streams the results of a single query execution specified by QueryExecutionId from the Athena query results location in Amazon S3. For more information, see Query Results in the Amazon Athena User Guide. This request does not execute the query but returns results. Use StartQueryExecution to run a query. To stream query results successfully, the IAM principal with permission to call GetQueryResults also must have permissions to the Amazon S3 GetObject action for the Athena query results location.  IAM principals with permission to the Amazon S3 GetObject action for the query results location are able to retrieve query results from Amazon S3 even if permission to the GetQueryResults action is denied. To restrict user or role access, ensure that Amazon S3 permissions to the Athena query location are denied.

# Required Parameters
- `QueryExecutionId`: The unique ID of the query execution.

# Optional Parameters
- `MaxResults`: The maximum number of results (rows) to return in this request.
- `NextToken`: A token generated by the Athena service that specifies where to continue pagination if a previous request was truncated. To obtain the next set of pages, pass in the NextToken from the response object of the previous page call.
"""
get_query_results(QueryExecutionId; aws_config::AWSConfig=global_aws_config()) = athena("GetQueryResults", Dict{String, Any}("QueryExecutionId"=>QueryExecutionId); aws_config=aws_config)
get_query_results(QueryExecutionId, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = athena("GetQueryResults", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("QueryExecutionId"=>QueryExecutionId), args)); aws_config=aws_config)

"""
    GetTableMetadata()

Returns table metadata for the specified catalog, database, and table.

# Required Parameters
- `CatalogName`: The name of the data catalog that contains the database and table metadata to return.
- `DatabaseName`: The name of the database that contains the table metadata to return.
- `TableName`: The name of the table for which metadata is returned.

"""
get_table_metadata(CatalogName, DatabaseName, TableName; aws_config::AWSConfig=global_aws_config()) = athena("GetTableMetadata", Dict{String, Any}("CatalogName"=>CatalogName, "DatabaseName"=>DatabaseName, "TableName"=>TableName); aws_config=aws_config)
get_table_metadata(CatalogName, DatabaseName, TableName, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = athena("GetTableMetadata", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("CatalogName"=>CatalogName, "DatabaseName"=>DatabaseName, "TableName"=>TableName), args)); aws_config=aws_config)

"""
    GetWorkGroup()

Returns information about the workgroup with the specified name.

# Required Parameters
- `WorkGroup`: The name of the workgroup.

"""
get_work_group(WorkGroup; aws_config::AWSConfig=global_aws_config()) = athena("GetWorkGroup", Dict{String, Any}("WorkGroup"=>WorkGroup); aws_config=aws_config)
get_work_group(WorkGroup, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = athena("GetWorkGroup", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("WorkGroup"=>WorkGroup), args)); aws_config=aws_config)

"""
    ListDataCatalogs()

Lists the data catalogs in the current AWS account.

# Optional Parameters
- `MaxResults`: Specifies the maximum number of data catalogs to return.
- `NextToken`: A token generated by the Athena service that specifies where to continue pagination if a previous request was truncated. To obtain the next set of pages, pass in the NextToken from the response object of the previous page call.
"""
list_data_catalogs(; aws_config::AWSConfig=global_aws_config()) = athena("ListDataCatalogs"; aws_config=aws_config)
list_data_catalogs(args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = athena("ListDataCatalogs", args; aws_config=aws_config)

"""
    ListDatabases()

Lists the databases in the specified data catalog.

# Required Parameters
- `CatalogName`: The name of the data catalog that contains the databases to return.

# Optional Parameters
- `MaxResults`: Specifies the maximum number of results to return.
- `NextToken`: A token generated by the Athena service that specifies where to continue pagination if a previous request was truncated. To obtain the next set of pages, pass in the NextToken from the response object of the previous page call.
"""
list_databases(CatalogName; aws_config::AWSConfig=global_aws_config()) = athena("ListDatabases", Dict{String, Any}("CatalogName"=>CatalogName); aws_config=aws_config)
list_databases(CatalogName, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = athena("ListDatabases", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("CatalogName"=>CatalogName), args)); aws_config=aws_config)

"""
    ListNamedQueries()

Provides a list of available query IDs only for queries saved in the specified workgroup. Requires that you have access to the specified workgroup. If a workgroup is not specified, lists the saved queries for the primary workgroup. For code samples using the AWS SDK for Java, see Examples and Code Samples in the Amazon Athena User Guide.

# Optional Parameters
- `MaxResults`: The maximum number of queries to return in this request.
- `NextToken`: A token generated by the Athena service that specifies where to continue pagination if a previous request was truncated. To obtain the next set of pages, pass in the NextToken from the response object of the previous page call.
- `WorkGroup`: The name of the workgroup from which the named queries are being returned. If a workgroup is not specified, the saved queries for the primary workgroup are returned.
"""
list_named_queries(; aws_config::AWSConfig=global_aws_config()) = athena("ListNamedQueries"; aws_config=aws_config)
list_named_queries(args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = athena("ListNamedQueries", args; aws_config=aws_config)

"""
    ListQueryExecutions()

Provides a list of available query execution IDs for the queries in the specified workgroup. If a workgroup is not specified, returns a list of query execution IDs for the primary workgroup. Requires you to have access to the workgroup in which the queries ran. For code samples using the AWS SDK for Java, see Examples and Code Samples in the Amazon Athena User Guide.

# Optional Parameters
- `MaxResults`: The maximum number of query executions to return in this request.
- `NextToken`: A token generated by the Athena service that specifies where to continue pagination if a previous request was truncated. To obtain the next set of pages, pass in the NextToken from the response object of the previous page call.
- `WorkGroup`: The name of the workgroup from which queries are being returned. If a workgroup is not specified, a list of available query execution IDs for the queries in the primary workgroup is returned.
"""
list_query_executions(; aws_config::AWSConfig=global_aws_config()) = athena("ListQueryExecutions"; aws_config=aws_config)
list_query_executions(args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = athena("ListQueryExecutions", args; aws_config=aws_config)

"""
    ListTableMetadata()

Lists the metadata for the tables in the specified data catalog database.

# Required Parameters
- `CatalogName`: The name of the data catalog for which table metadata should be returned.
- `DatabaseName`: The name of the database for which table metadata should be returned.

# Optional Parameters
- `Expression`: A regex filter that pattern-matches table names. If no expression is supplied, metadata for all tables are listed.
- `MaxResults`: Specifies the maximum number of results to return.
- `NextToken`: A token generated by the Athena service that specifies where to continue pagination if a previous request was truncated. To obtain the next set of pages, pass in the NextToken from the response object of the previous page call.
"""
list_table_metadata(CatalogName, DatabaseName; aws_config::AWSConfig=global_aws_config()) = athena("ListTableMetadata", Dict{String, Any}("CatalogName"=>CatalogName, "DatabaseName"=>DatabaseName); aws_config=aws_config)
list_table_metadata(CatalogName, DatabaseName, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = athena("ListTableMetadata", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("CatalogName"=>CatalogName, "DatabaseName"=>DatabaseName), args)); aws_config=aws_config)

"""
    ListTagsForResource()

Lists the tags associated with an Athena workgroup or data catalog resource.

# Required Parameters
- `ResourceARN`: Lists the tags for the resource with the specified ARN.

# Optional Parameters
- `MaxResults`: The maximum number of results to be returned per request that lists the tags for the resource.
- `NextToken`: The token for the next set of results, or null if there are no additional results for this request, where the request lists the tags for the resource with the specified ARN.
"""
list_tags_for_resource(ResourceARN; aws_config::AWSConfig=global_aws_config()) = athena("ListTagsForResource", Dict{String, Any}("ResourceARN"=>ResourceARN); aws_config=aws_config)
list_tags_for_resource(ResourceARN, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = athena("ListTagsForResource", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("ResourceARN"=>ResourceARN), args)); aws_config=aws_config)

"""
    ListWorkGroups()

Lists available workgroups for the account.

# Optional Parameters
- `MaxResults`: The maximum number of workgroups to return in this request.
- `NextToken`: A token generated by the Athena service that specifies where to continue pagination if a previous request was truncated. To obtain the next set of pages, pass in the NextToken from the response object of the previous page call.
"""
list_work_groups(; aws_config::AWSConfig=global_aws_config()) = athena("ListWorkGroups"; aws_config=aws_config)
list_work_groups(args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = athena("ListWorkGroups", args; aws_config=aws_config)

"""
    StartQueryExecution()

Runs the SQL query statements contained in the Query. Requires you to have access to the workgroup in which the query ran. Running queries against an external catalog requires GetDataCatalog permission to the catalog. For code samples using the AWS SDK for Java, see Examples and Code Samples in the Amazon Athena User Guide.

# Required Parameters
- `QueryString`: The SQL query statements to be executed.

# Optional Parameters
- `ClientRequestToken`: A unique case-sensitive string used to ensure the request to create the query is idempotent (executes only once). If another StartQueryExecution request is received, the same response is returned and another query is not created. If a parameter has changed, for example, the QueryString, an error is returned.  This token is listed as not required because AWS SDKs (for example the AWS SDK for Java) auto-generate the token for users. If you are not using the AWS SDK or the AWS CLI, you must provide this token or the action will fail.
- `QueryExecutionContext`: The database within which the query executes.
- `ResultConfiguration`: Specifies information about where and how to save the results of the query execution. If the query runs in a workgroup, then workgroup's settings may override query settings. This affects the query results location. The workgroup settings override is specified in EnforceWorkGroupConfiguration (true/false) in the WorkGroupConfiguration. See WorkGroupConfigurationEnforceWorkGroupConfiguration.
- `WorkGroup`: The name of the workgroup in which the query is being started.
"""
start_query_execution(QueryString; aws_config::AWSConfig=global_aws_config()) = athena("StartQueryExecution", Dict{String, Any}("QueryString"=>QueryString, "ClientRequestToken"=>string(uuid4())); aws_config=aws_config)
start_query_execution(QueryString, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = athena("StartQueryExecution", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("QueryString"=>QueryString, "ClientRequestToken"=>string(uuid4())), args)); aws_config=aws_config)

"""
    StopQueryExecution()

Stops a query execution. Requires you to have access to the workgroup in which the query ran. For code samples using the AWS SDK for Java, see Examples and Code Samples in the Amazon Athena User Guide.

# Required Parameters
- `QueryExecutionId`: The unique ID of the query execution to stop.

"""
stop_query_execution(QueryExecutionId; aws_config::AWSConfig=global_aws_config()) = athena("StopQueryExecution", Dict{String, Any}("QueryExecutionId"=>QueryExecutionId); aws_config=aws_config)
stop_query_execution(QueryExecutionId, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = athena("StopQueryExecution", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("QueryExecutionId"=>QueryExecutionId), args)); aws_config=aws_config)

"""
    TagResource()

Adds one or more tags to an Athena resource. A tag is a label that you assign to a resource. In Athena, a resource can be a workgroup or data catalog. Each tag consists of a key and an optional value, both of which you define. For example, you can use tags to categorize Athena workgroups or data catalogs by purpose, owner, or environment. Use a consistent set of tag keys to make it easier to search and filter workgroups or data catalogs in your account. For best practices, see Tagging Best Practices. Tag keys can be from 1 to 128 UTF-8 Unicode characters, and tag values can be from 0 to 256 UTF-8 Unicode characters. Tags can use letters and numbers representable in UTF-8, and the following characters: + - = . _ : / @. Tag keys and values are case-sensitive. Tag keys must be unique per resource. If you specify more than one tag, separate them by commas.

# Required Parameters
- `ResourceARN`: Specifies the ARN of the Athena resource (workgroup or data catalog) to which tags are to be added.
- `Tags`: A collection of one or more tags, separated by commas, to be added to an Athena workgroup or data catalog resource.

"""
tag_resource(ResourceARN, Tags; aws_config::AWSConfig=global_aws_config()) = athena("TagResource", Dict{String, Any}("ResourceARN"=>ResourceARN, "Tags"=>Tags); aws_config=aws_config)
tag_resource(ResourceARN, Tags, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = athena("TagResource", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("ResourceARN"=>ResourceARN, "Tags"=>Tags), args)); aws_config=aws_config)

"""
    UntagResource()

Removes one or more tags from a data catalog or workgroup resource.

# Required Parameters
- `ResourceARN`: Specifies the ARN of the resource from which tags are to be removed.
- `TagKeys`: A comma-separated list of one or more tag keys whose tags are to be removed from the specified resource.

"""
untag_resource(ResourceARN, TagKeys; aws_config::AWSConfig=global_aws_config()) = athena("UntagResource", Dict{String, Any}("ResourceARN"=>ResourceARN, "TagKeys"=>TagKeys); aws_config=aws_config)
untag_resource(ResourceARN, TagKeys, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = athena("UntagResource", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("ResourceARN"=>ResourceARN, "TagKeys"=>TagKeys), args)); aws_config=aws_config)

"""
    UpdateDataCatalog()

Updates the data catalog that has the specified name.

# Required Parameters
- `Name`: The name of the data catalog to update. The catalog name must be unique for the AWS account and can use a maximum of 128 alphanumeric, underscore, at sign, or hyphen characters.
- `Type`: Specifies the type of data catalog to update. Specify LAMBDA for a federated catalog, GLUE for AWS Glue Catalog, or HIVE for an external hive metastore.

# Optional Parameters
- `Description`: New or modified text that describes the data catalog.
- `Parameters`: Specifies the Lambda function or functions to use for updating the data catalog. This is a mapping whose values depend on the catalog type.    For the HIVE data catalog type, use the following syntax. The metadata-function parameter is required. The sdk-version parameter is optional and defaults to the currently supported version.  metadata-function=lambda_arn, sdk-version=version_number     For the LAMBDA data catalog type, use one of the following sets of required parameters, but not both.   If you have one Lambda function that processes metadata and another for reading the actual data, use the following syntax. Both parameters are required.  metadata-function=lambda_arn, record-function=lambda_arn      If you have a composite Lambda function that processes both metadata and data, use the following syntax to specify your Lambda function.  function=lambda_arn       The GLUE type has no parameters.
"""
update_data_catalog(Name, Type; aws_config::AWSConfig=global_aws_config()) = athena("UpdateDataCatalog", Dict{String, Any}("Name"=>Name, "Type"=>Type); aws_config=aws_config)
update_data_catalog(Name, Type, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = athena("UpdateDataCatalog", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("Name"=>Name, "Type"=>Type), args)); aws_config=aws_config)

"""
    UpdateWorkGroup()

Updates the workgroup with the specified name. The workgroup's name cannot be changed.

# Required Parameters
- `WorkGroup`: The specified workgroup that will be updated.

# Optional Parameters
- `ConfigurationUpdates`: The workgroup configuration that will be updated for the given workgroup.
- `Description`: The workgroup description.
- `State`: The workgroup state that will be updated for the given workgroup.
"""
update_work_group(WorkGroup; aws_config::AWSConfig=global_aws_config()) = athena("UpdateWorkGroup", Dict{String, Any}("WorkGroup"=>WorkGroup); aws_config=aws_config)
update_work_group(WorkGroup, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = athena("UpdateWorkGroup", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("WorkGroup"=>WorkGroup), args)); aws_config=aws_config)


text:
# This file is part of InvertedFiles.jl

function push_posting_list!(Q, idx::BinaryInvertedFile, tokenID, val)
	@inbounds push!(Q, PostingList(idx.lists[tokenID]))
end

"""
	search(callback::Function, idx::BinaryInvertedFile, Q, P, t)

Find candidates for solving query `Q` using `idx`. It calls `callback` on each candidate `(objID, dist)`

# Arguments

- `callback`: callback function on each candidate
- `idx`: inverted index
- `Q`: the set of involved posting lists, see [`prepare_posting_lists_for_querying`](@ref)
- `P`: a vector of starting positions in Q (initial state as ones)
- `t`: threshold (t=1 union, t > 1 solves the t-threshold problem)
"""
search(callback::Function, idx::BinaryInvertedFile, Q::Vector{PostingList}, P_::Vector{UInt32}, t) =
	search_(callback, idx, idx.dist, Q, P_, t)

function search_(callback::Function, idx::BinaryInvertedFile, dist, Q::Vector{PostingList}, P_::Vector{UInt32}, t)
    n = length(Q)

	umerge(Q, P_; t) do L, P, isize
        @inbounds objID = L[1].I[P[1]]
        @inbounds d = set_distance_evaluate(dist, isize, n, idx.sizes[objID])
		callback(objID, d)
	end
end


text:
struct DBInstrument
    pkey::Int
    laboratory::DBLaboratory
    vendor::String
    model::String
    location::String
end

#CREATE TABLE INSTRUMENT (
#    PKEY INTEGER PRIMARY KEY AUTOINCREMENT,
#    LABKEY INTEGER NOT NULL,
#    VENDOR TEXT NOT NULL,
#    MODEL TEXT NOT NULL,
#    LOCATION TEXT NOT NULL,
#    FOREIGN KEY(LABKEY) REFERENCES LABORATORY(PKEY)
#);

Base.show(io::IO, inst::DBInstrument) = print(io, "$(inst.laboratory)'s $(inst.vendor) $(inst.model)")

function Base.write(
    db::SQLite.DB,
    ::Type{DBInstrument},
    labkey::Int,
    vendor::String,
    model::String,
    location::String,
)::Int
    stmt1 = SQLite.Stmt(db, "INSERT INTO INSTRUMENT ( LABKEY, VENDOR, MODEL, LOCATION ) VALUES ( ?, ?, ?, ? );")
    r = DBInterface.execute(stmt1, (labkey, vendor, model, location))
    return DBInterface.lastrowid(r)
end

Base.write(
    db::SQLite.DB,
    ::Type{DBInstrument},
    lab::DBLaboratory,
    vendor::String,
    model::String,
    location::String,
)::Int = write(db, DBInstrument, lab.pkey, vendor, model, location)

function Base.read(db::SQLite.DB, ::Type{DBInstrument}, pkey::Int)::DBInstrument
    stmt1 = SQLite.Stmt(db, "SELECT * FROM INSTRUMENT WHERE PKEY=?;")
    q = DBInterface.execute(stmt1, (pkey,))
    if SQLite.done(q)
        error("No known instrument with key '$(pkey)'.")
    end
    r = SQLite.Row(q)
    return DBInstrument(r[:PKEY], read(db, DBLaboratory, r[:LABKEY]), r[:VENDOR], r[:MODEL], r[:LOCATION])
end

function Base.findall(db::SQLite.DB, ::Type{DBInstrument}, lab::DBLaboratory; vendor=missing, model=missing)::Vector{DBInstrument}
    bs = "SELECT * FROM INSTRUMENT WHERE LABKEY=?"
    args = Any[ lab.pkey ]
    if !ismissing(vendor)
        bs *= " AND VENDOR=?"
        push!(args, vendor)
    end
    if !ismissing(model)
        bs *= " AND MODEL=?"
        push!(args, model)
    end
    stmt1 = SQLite.Stmt(db, bs*";")
    q = DBInterface.execute(stmt1, args)
    if SQLite.done(q)
        error("No known instruments for $args.")
    end
    return [ DBInstrument(r[:PKEY], lab, r[:VENDOR], r[:MODEL], r[:LOCATION]) for r in q ]
end

#CREATE TABLE DETECTOR (
#    PKEY INTEGER PRIMARY KEY AUTOINCREMENT,
#    INSTRUMENT INTEGER NOT NULL,
#    VENDOR TEXT NOT NULL,
#    MODEL TEXT NOT NULL,
#    DESCRIPTION TEXT,
#    FOREIGN KEY(INSTRUMENT) REFERENCES INSTRUMENT(PKEY)
#);

struct DBDetector
    database::SQLite.DB
    pkey::Int
    instrument::DBInstrument
    vendor::String
    model::String
    description::String
    resolution::Float64
    lld::Int
    zero::Float64
    gain::Float64
    minK::Int
    minL::Int
    minM::Int
    minN::Int
end

function NeXLUncertainties.asa(::Type{DataFrame}, det::DBDetector)::DataFrame
    prop, value = String[], String[]
    push!(prop,"Laboratory"), push!(value, det.instrument.laboratory.name)
    push!(prop,"Instrument"), push!(value, "$(det.instrument.vendor) $(det.instrument.model)")
    push!(prop,"Detector"), push!(value, "$(det.vendor) $(det.model): $(det.description)")
    push!(prop,"Resolution"), push!(value, "$(det.resolution) eV")
    push!(prop,"LLD"), push!(value, "$(det.lld) channels")
    push!(prop,"Energy"), push!(value, "$(det.zero) + $(det.gain)·i [eV]")
    sym(z) = elements[z].symbol
    push!(prop,"Visible"), push!(value, "K: Z>=$(sym(det.minK)), L: Z>=$(sym(det.minL)),  M: Z>=$(sym(det.minM)), N: Z>=$(sym(det.minN))")
    return DataFrame(Property=prop, Value=value)
end

Base.show(io::IO, det::DBDetector) = print(io, "$(det.vendor) $(det.model) $(det.description) on $(repr(det.instrument))")

function Base.write(
    db::SQLite.DB,
    ::Type{DBDetector}, #
    instkey::Int,
    vendor::String,
    model::String,
    desc::String, #
    fwhm::Float64,
    lld::Int,
    zero::Float64,
    gain::Float64, #
    minK::Int,
    minL::Int,
    minM::Int,
    minN::Int, #
)::Int
    stmt1 = SQLite.Stmt(
        db,
        "INSERT INTO DETECTOR ( INSTRUMENT, VENDOR, MODEL, DESCRIPTION, RESOLUTION, LLD, ZERO, GAIN, MINK, MINL, MINM, MINN ) VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? );",
    )
    r = DBInterface.execute(stmt1, (instkey, vendor, model, desc, fwhm, lld, zero, gain, minK, minL, minM, minN))
    return convert(Int, DBInterface.lastrowid(r))
end

Base.write(
    db::SQLite.DB,
    ::Type{DBDetector}, #
    inst::DBInstrument,
    vendor::String,
    model::String,
    desc::String, #
    fwhm::Float64,
    lld::Int,
    zero::Float64,
    gain::Float64, #
    minK::Int,
    minL::Int,
    minM::Int,
    minN::Int, #
)::Int = write(db, DBDetector, inst.pkey, vendor, model, desc, fwhm, lld, zero, gain, minK, minL, minM, minN)

function Base.read(db::SQLite.DB, ::Type{DBDetector}, pkey::Int)::DBDetector
    stmt1 = SQLite.Stmt(db, "SELECT * FROM DETECTOR WHERE PKEY=?;")
    q = DBInterface.execute(stmt1, (pkey,))
    if SQLite.done(q)
        error("No known detector with key '$(pkey)'.")
    end
    r = SQLite.Row(q)
    return DBDetector(
        db,
        r[:PKEY],
        read(
            db,
            DBInstrument, #
            r[:INSTRUMENT],
        ),
        r[:VENDOR],
        r[:MODEL],
        r[:DESCRIPTION], #
        r[:RESOLUTION],
        r[:LLD],
        r[:ZERO],
        r[:GAIN], #
        r[:MINK],
        r[:MINL],
        r[:MINM],
        r[:MINN],
    )
end

Base.convert(::Type{BasicEDS}, dbd::DBDetector, chCount::Int = 4096) = BasicEDS(
    chCount,
    dbd.zero,
    dbd.gain,
    dbd.resolution,
    dbd.lld, #
    Dict{Shell,Element}(
        Shell(1) => elements[dbd.minK],
        Shell(2) => elements[dbd.minL], #
        Shell(3) => elements[dbd.minM],
        Shell(4) => elements[dbd.minN],
    ),
)

function Base.findall(db::SQLite.DB, ::Type{DBDetector}, inst::DBInstrument; vendor=missing, model=missing)::Vector{DBDetector}
    bs="SELECT PKEY FROM DETECTOR WHERE INSTRUMENT=?"
    args = Any[inst.pkey]
    if !ismissing(vendor)
        bs *= " AND VENDOR=?"
        push!(args,vendor)
    end
    if !ismissing(model)
        bs *= " AND MODEL=?"
        push!(args, model)
    end
    stmt1 = SQLite.Stmt(db, bs*";")
    q = DBInterface.execute(stmt1, args)
    return [ read(db, DBDetector, r[:PKEY]) for r in q ]
end

function find(db::SQLite.DB, ::Type{DBDetector}, inst::DBInstrument, desc::String)::Union{DBDetector, Missing}
    stmt1 = SQLite.Stmt(db, "SELECT PKEY FROM DETECTOR WHERE INSTRUMENT=? AND DESCRIPTION=?;")
    q = DBInterface.execute(stmt1, (inst.pkey, desc))
    return SQLite.done(q) ? missing : read(db, DBDetector, r[:PKEY])
end

function simpleEDS(dbd::DBDetector, channelcount::Int)
    return BasicEDS(channelcount, dbd.zero, dbd.gain, dbd.resolution, dbd.lld)
end


text:
using Gadfly

"""
Precision-recall curve for two hypothetical classifications systems.
A is better than B.
"""
function PRhandDemo()
    domain = 0:0.01:1

    fA(x) = 1-x.^(3)
    fB(x) = 1-x.^(3/2)

    lA = layer(x=domain, y=fA(domain), Geom.line, Theme(default_color=colorant"red",  line_width=3px, line_style=Gadfly.get_stroke_vector(:dot)))
    lB = layer(x=domain, y=fB(domain), Geom.line, Theme(default_color=colorant"blue", line_width=3px))

    plot(lA, lB, Guide.xlabel("recall"), Guide.ylabel("precision"))
end

"""
ROC curves for two hypothetical classification systems.
A is better than B.
Plots true positive rate, (tpr) vs false positive (fpr).
"""
function ROChandDemo()
    domain = 0:0.01:1

    fA(x) = x.^(1/3)
    fB(x) = x.^(2/3)

    lA = layer(x=domain, y=fA(domain), Geom.line, Theme(default_color=colorant"red",  line_width=3px, line_style=Gadfly.get_stroke_vector(:dot)))
    lB = layer(x=domain, y=fB(domain), Geom.line, Theme(default_color=colorant"blue", line_width=3px))
    shade = layer(x=domain, y=zeros(domain), ymin=zeros(domain), ymax=fB(domain), Geom.ribbon)

    plot(lA, shade, lB, Guide.xlabel("fpr"), Guide.ylabel("tpr"))
end


text:
# Code based on: https://www.geeksforgeeks.org/largest-rectangle-under-histogram/

function _rectangular_area!(stack, histogram, i₀, i)
	min_height_index = pop!(stack)
	height = histogram[min_height_index]

	left_index = isempty(stack) ? i₀ : first(stack) + 1
	rigth_index = i - 1
	base = rigth_index - left_index + 1

	area = height * base

	return area, (left_index, rigth_index), (0, height)
end

function _update_max_area!(x_coordinates, y_coordinates, xs, ys, max_area, area)
	if area ≥ max_area
		if area > max_area
			empty!(x_coordinates)
			empty!(y_coordinates)
		end
		max_area = area
		push!(x_coordinates, xs)
		push!(y_coordinates, ys)
	end
	max_area
end

function maximum_rectagular_area(histogram)
	height_type = eltype(histogram)
    rectangle_type = height_type == Bool ? Int : height_type

	indices = eachindex(histogram)
	i₀ = first(indices)
	indextype = typeof(i₀)
    stack = Stack{typeof(i₀)}()
	x_coordinates = Tuple{indextype,indextype}[]
	y_coordinates = Tuple{rectangle_type,rectangle_type}[]
	max_area = 0
	if isempty(indices)
		return max_area, x_coordinates, y_coordinates
	end

    i = i₀
	while true
        if isempty(stack) || (histogram[first(stack)] ≤ histogram[i])
            push!(stack, i)
            next = iterate(indices, i)
			if next === nothing
				break
    			else
				_, i = next
			end
		else
			area, xs, ys = _rectangular_area!(stack, histogram, i₀, i)
			max_area = _update_max_area!(x_coordinates, y_coordinates,
				xs, ys, max_area, area)
		end
	end

	i = last(indices) + 1
	while !isempty(stack)
		area, xs, ys = _rectangular_area!(stack, histogram, i₀, i)
		max_area = _update_max_area!(x_coordinates, y_coordinates,
			xs, ys, max_area, area)
	end

	max_area, x_coordinates, y_coordinates
end


text:
export LSLoss, loss
## Represents a loss function for a linear inverse problem
# of the form loss(r) = ||r||_2
struct LSLoss <: Loss end
using LinearAlgebra
#evaluates and returns the gradient of the loss w.r.t r
loss(l :: LSLoss,r :: Vector{Float64}) = norm(r,2), r/norm(r,2)


text:
using CSFML.LibCSFML
using Test

mode = sfVideoMode(1280, 720, 32)


text:
###############
## VALUATION ##
###############

"""
    Valuation(x::PVector; affine = true)
    Valuation(x::AbstractVector)

A data structure for approximating the valuation of a solution path ``x(s)``.
It does this by computing an approximation function [`ν`](@ref) and approximating
the first and second derivatives ``ν̇`` and ``ν̈``.
If `affine = true` then the computed valuation is the valuation pulled back
to the affine space.
The valuation is estimated continously along a solution path. For this it is assumed that
the path is tracked in a **logarithmic time scale**.
"""
struct Valuation
    # Estimate
    ν::Vector{Float64}
    ν̇::Vector{Float64}
    ν̈::Vector{Float64}
    s::Base.RefValue{Float64}
    affine::Bool
    # Data for computing the ν̇ and ν̈ by finite differences
    ν_data::NTuple{2,Vector{Float64}}
    s_data::Base.RefValue{NTuple{2,Float64}}
end

function Valuation(n::Integer, affine::Bool = true)
    v = zeros(n)
    ν̇ = zeros(n)
    ν̈ = zeros(n)
    ν_data = (zeros(n), zeros(n))
    s_data = (NaN, NaN)
    Valuation(v, ν̇, ν̈, Ref(NaN), affine, ν_data, Ref(s_data))
end
function Valuation(x::ProjectiveVectors.PVector; affine::Bool = true)
    if affine
        Valuation(length(x) - length(ProjectiveVectors.dims(x)), affine)
    else
        Valuation(length(x), affine)
    end
end
Valuation(x::Vector; affine::Bool = true) = Valuation(length(x))

function Base.show(io::IO, val::Valuation)
    println(io, typeof(val), ":")
    println(io, " • s → ", round.(val.s[]; sigdigits = 4))
    for name in [:ν, :ν̇, :ν̈]
        println(io, " • ", name, " → ", round.(getfield(val, name); sigdigits = 4))
    end
end
Base.show(io::IO, ::MIME"application/prs.juno.inline", v::Valuation) = v

"""
    init!(val::Valuation)

Initialize the valuation estimator to start from scratch.
"""
function init!(val::Valuation)
    val.ν .= NaN
    val.ν̇ .= NaN
    val.ν̈ .= NaN
    val.s[] = NaN
    val.s_data[] = (NaN, NaN)
    val.ν_data[1] .= NaN
    val.ν_data[2] .= NaN
    val
end

#############################
## Valuation approximation ##
#############################

# First to the coordinate wise functions

"""
    ν(z, ż)

Computes the function ``ν(z) = -(xẋ + yẏ) / |z|²`` where `x,y = reim(z)`.
This is equivalent to computing ``d/ds log|x(s)|``.
"""
function ν(z::Complex, ż::Complex)
    x, y = reim(z)
    ẋ, ẏ = reim(ż)
    -(x * ẋ + y * ẏ) / abs2(z)
end

"""
    ν_ν̇_ν̈(z::Complex, ż::Complex, z̈::Complex, z³::Complex)

Computes the function [`ν`](@ref) and the derivaties ``ν̇``, ``ν̈`` by using the analytic
derivatives.
"""
function ν_ν̇_ν̈(z::Complex, ż::Complex, z̈::Complex, z³::Complex)
    x, y = reim(z)
    ẋ, ẏ = reim(ż)
    ẍ, ÿ = reim(z̈)
    x³, y³ = reim(z³)

    μ = x * ẋ + y * ẏ
    # The following is just applying product rule properly
    μ̇ = x * ẍ + ẋ^2 + y * ÿ + ẏ^2
    μ̈ = x * x³ + 3 * ẋ * ẍ + y * y³ + 3 * ẏ * ÿ

    z² = abs2(z)
    ν = -μ / z²
    # ν̇ = -μ̇ / z² + 2μ²/(z²)² and  μ²/(z²)² = ν^2
    ν̇ = 2 * ν^2 - μ̇ / z²
    ν̈ = 4 * ν * ν̇ - μ̈ / z² + 2 * μ̇ * μ / z²^2
    ν, ν̇, ν̈
end

"""
    ν̇_ν̈(z::Complex, ż::Complex, s::Float64, ν₂, s₂, ν₁, s₁)

Computes the function [`ν`](@ref) and the derivaties ``ν̇``, ``ν̈`` by using a finite
difference scheme. This uses the value `ν₂` of ``ν`` at `s₂` and `ν₁` of ``ν`` at `s₁` with
``s > s₂ > s₁``.
Since we have a non-uniform grid, we need a more elaborate difference scheme.
The implementation follows the formulas derived in [^BS05].

[^BS05]: Bowen, M. K., and Ronald Smith. "Derivative formulae and errors for non-uniformly
  spaced points." Proceedings of the Royal Society A: Mathematical, Physical and Engineering
  Sciences 461.2059 (2005): 1975-1997.
"""
function ν̇_ν̈(ν::Float64, s::Float64, ν₂::Float64, s₂::Float64, ν₁::Float64, s₁::Float64)
    Δ₁, Δ₂, Δ₁₂ = s - s₁, s - s₂, s₁ - s₂
    ν̇ = (Δ₂ * ν₁) / (Δ₁₂ * Δ₁) - ((Δ₁₂ + Δ₂) * ν₂) / (Δ₁₂ * Δ₂) - (Δ₁₂ * ν) / (Δ₁ * Δ₂)
    ν̈ = -2ν₁ / (Δ₁₂ * Δ₁) + 2ν₂ / (Δ₁₂ * Δ₂) + 2ν / (Δ₁ * Δ₂)
    ν̇, ν̈
end

"""
    update!(val::Valuation, x, ẋ, s,
            predictor::AbstractPredictorCache = NullPredictorCache())

Compute new approximations of the valuation of the path ``x(s)`` from `x` and `ẋ` at `s`.
This queries the given predictor cache `predictor` for the second and third derivative
of the path ``x(s)``. If this information is available it computes the derivatives
of ``ν`` analytically otherwise a finite differene scheme is used.
"""
function update!(
    val::Valuation,
    z::AbstractVector,
    ż,
    s,
    predictor::AbstractPredictorCache = NullPredictorCache(),
)
    @unpack ν̇, ν̈, ν_data, s_data = val

    ν₂, ν₁ = ν_data
    s₂, s₁ = s_data[]

    if val.affine && isa(z, PVector)
        k = 1
        for (rⱼ, j) in ProjectiveVectors.dimension_indices_homvars(z)
            νⱼ = ν(z[j], ż[j])
            for i in rⱼ
                ν_k = ν(z[i], ż[i]) - νⱼ
                if !isnan(s₁)
                    ν̇_k, ν̈_k = ν̇_ν̈(ν_k, s, ν₂[k], s₂, ν₁[k], s₁)
                    ν̇[k], ν̈[k] = ν̇_k, ν̈_k
                end
                # update ν and shift the previous ones
                val.ν[k], ν₂[k], ν₁[k] = ν_k, val.ν[k], ν₂[k]
                k += 1
            end
        end
    else
        for i = 1:length(val.ν)
            # try to compute the values using higher derivatives from the predictor
            z̈ = second_derivative(predictor)
            z³ = third_derivative(predictor)
            if !isnothing(z̈) && !isnothing(z³)
                νᵢ, ν̇ᵢ, ν̈ᵢ = ν_ν̇_ν̈(z[i], ż[i], z̈[i], z³[i])
                if !isnan(s₁)
                    # The estimates can be sensitive to numerical errors, so we still compare
                    # against the numerical derivatives since this is cheap anyway
                    ν̇ᵢ′, ν̈ᵢ′ = ν̇_ν̈(νᵢ, s, ν₂[i], s₂, ν₁[i], s₁)
                    ν̇[i] = abs(ν̇ᵢ) < abs(ν̇ᵢ′) ? ν̇ᵢ : ν̇ᵢ′
                    ν̈[i] = abs(ν̈ᵢ) < abs(ν̈ᵢ′) ? ν̈ᵢ : ν̈ᵢ′
                else
                    ν̇[i], ν̈[i] = ν̇ᵢ, ν̈ᵢ
                end
            else
                νᵢ = ν(z[i], ż[i])
                if !isnan(s₁)
                    ν̇ᵢ, ν̈ᵢ = ν̇_ν̈(νᵢ, s, ν₂[i], s₂, ν₁[i], s₁)
                    ν̇[i], ν̈[i] = ν̇ᵢ, ν̈ᵢ
                end
            end
            # update ν and shift the previous ones
            val.ν[i], ν₂[i], ν₁[i] = νᵢ, val.ν[i], ν₂[i]
        end
    end

    # Shift the s
    s_data[] = (val.s[], s₂)
    val.s[] = s
    val
end

"""
    enum ValuationVerdict

The possible states the [`judge`](@ref) function returns:

* `VAL_INDECISIVE`: The estimates are not trustworthy enough.
* `VAL_FINITE`: The valuation indicates that the path is finite.
* `VAL_AT_INFINITY`: The valuation indicates that the path is diverging.
"""
@enum ValuationVerdict begin
    VAL_INDECISIVE
    VAL_FINITE
    VAL_AT_INFINITY
end

"""
    judge(val::Valuation; tol = 1e-3, tol_at_infinity = 1e-4)::ValuationVerdict

Judge the current valuation to determine whether a path is diverging. Returns
a [`ValuationVerdict`](@ref).
A path is diverging if one entry of the valuation is negative. To assure that
the estimate is trust worthy we require that the first and second derivative
of [`ν``](@ref) is smaller than `tol_at_infinity`.
The `tol` is used to estimate whether a finite valuation is trustworthy.
"""
function judge(val::Valuation; tol::Float64 = 1e-3, tol_at_infinity::Float64 = 1e-4)
    @unpack ν, ν̇, ν̈ = val
    finite = true
    indecisive = false
    for (i, νᵢ) in enumerate(ν)
        if νᵢ < -max(0.01, tol)
            finite = false
        end

        if νᵢ < -0.05 && abs(ν̇[i]) < tol_at_infinity && abs(ν̈[i]) < tol_at_infinity
            return VAL_AT_INFINITY
        end

        if abs(ν̇[i]) > tol
            indecisive = true
        end
    end
    finite && !indecisive && return VAL_FINITE

    VAL_INDECISIVE
end