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

using Markdown
using InteractiveUtils

# ╔═╡ f811543d-b7e6-4f7a-b60c-048f1bf380bf
begin
	using Plots
	using Distributions
end

# ╔═╡ b9505974-1b92-11ec-2f68-b3324d215555
md"""
# Simple search

*STMO*

**Michiel Stock**
"""

# ╔═╡ acabcaa5-026d-4d20-b031-34c4025bb5a8
md"This notebook will cover two straightforward algorithms for approximately minimizing one-dimensional functions: *grid search* and *random search*. Both are used extensively in, for example, machine learning to find optimal hyperparameters. These two algorithms are ideal to get acquainted with the basics of Julia!
"

# ╔═╡ 2dafa118-f82f-4958-af6a-d013c01e998b
md"""
## Grid search

To minimize a one-dimensional function $f(x)$ using grid search in the interval $[a, b]$, we search the interval over $n$ equally-spaced steps and take value that results in the lowest objective value. The larger $n$, the more function evaluations and the better quality the solution will be. Even though grid search being ubiquitously used in machine learning, it is a biased method and often does not yield as good results as the random search. In some cases, it makes sense to search on a logarithmic scale as opposed to a linear scale.

Grid search can easily be extended to higher dimensions by extending the grid $a_i\le x_i \le b_j$, hence providing upper and lower bounds for every dimension. This approach is also called a *full factorial sampling*. Due to the curse of dimensionality, one has to perform exponentially more function evaluations to cover the search space.
"""

# ╔═╡ 90c40756-c1ff-4258-86a3-d746a85cafe4
md"""
## Random search

To minimize a one-dimensional function $f(x)$ using random search, we generate $n$ random values of $x$ in the interval $[a, b]$ and take the one with the lowest objective value. Clever algorithms use other samplings than uniform, driving the search towards particular regions of the search space. Random search can easily be extended to higher dimensions.
"""

# ╔═╡ 7f7e9617-44ad-423f-beec-e0b574818a61
md"""
## Exercises

We will explore both methods using the [Ackley alpine function](https://en.wikipedia.org/wiki/Ackley_function). We will search between the interval $[-π, π]$. The true minimizer is $x^\star=0$
"""

# ╔═╡ 82bf15a9-2bb0-4776-8bf0-c3ad9030ed04
md"""
**Assignments**

1. Complete the code `grid_search` and use it to minimize the Ackley function using 10 and 50 function evaluations. (hint: use `:` to generate a grid, e.g. `0:0.1:10`)
2. Complete the code `random_search` and use it to minimize the Ackley function using 50 function evaluations. (hint `rand()` generates an uniform random number in $[0,1]$.
3. Compare the solutions.
4. Compare their running time using the `@time` macro.
5. Plot the quality of your solution using the two algorithms as a function of the number of evaluations (use $n=10, 50, 100, 500, 1000, 5000$ or so).
6. (optional programming exercise) Extend the functions so that you can perform a higher-dimensional search. E.g., `grid_search(Ackley, (-pi, pi), (-pi, pi))` would search in two dimensions.
7. (optional exercise) Use dispatch such that there are two versions for grid search, i.e., `grid_search(f, (a, b); n=10)` works as before, but the second method `grid_search(f, grid::Vector)` recognizes that the grid is already given.

Can you see why I suggested the interval $[-π, π]$? I have been sneaky in question 5...
"""

# ╔═╡ 6ead28bf-a2e3-43f7-8962-3f0f4e6ce4ca
"""
    grid_search(f, (a, b); n=10)

Performs a grid search in [`a`, `b`] on `f` with a grid of size `n`.
Returns the best found value of `x`.
"""
function grid_search(f, (a, b); n=10)
    @assert a < b "Not a valid interval!"
	step_size = (b - a) / n
	return minimum(x for x in f.(a:step_size:b))
end

# ╔═╡ 8c434bb4-64ae-4232-bad0-36419d010ff2
"""
    random_search(f, (a, b); n=10)

Performs a random search in [`a`, `b`] on `f` using `n` samples.
Returns the best found value of `x`.
"""
function random_search(f, (a, b); n=10)
    @assert a < b "Not a valid interval!"
   	samples = rand(Uniform(a, b), n)
	return minimum(x for x in f.(samples))
end

# ╔═╡ ed771cc5-f20a-41dc-82a2-390dba65ebab
md"Defining some functions and useful variables."

# ╔═╡ aa38d4e8-5a55-42d3-9cfe-85c1b6f59f78
begin
	myblue = "#304da5"
	mygreen = "#2a9d8f"
	myyellow = "#e9c46a"
	myorange = "#f4a261"
	myred = "#e76f51"
	myblack = "#50514F"

	mycolors = [myblue, myred, mygreen, myorange, myyellow]
end;

# ╔═╡ 36a19aa4-f282-4d00-895d-a923439a2c3f
begin
	function ackley(x; a=20, b=0.2, c=2π)
    d = length(x)
    return -a * exp(-b*sqrt(sum(x.^2)/d)) -
        exp(sum(cos.(c .* x))/d)
	end

	ackley(x...; kwargs...) = ackley(x; kwargs...)
end

# ╔═╡ e79100c1-6a18-4aab-9461-a117fa1922a1
ackley(1.0)  # scalar version

# ╔═╡ cf404673-5418-4352-9fc2-524fd0325444
ackley([1.0, -1.0])  # optional 2D version

# ╔═╡ 88db7212-49d1-4944-bdbd-f783b3119aba
plot(ackley, -pi, pi, label="Ackley", color=myblue, xlabel="x")

# ╔═╡ 16d863f7-8157-4c7d-a5fc-e4a3c905ed69
contourf(-pi:0.01:pi, -pi:0.01:pi, ackley, color=:speed, xlabel="x1", ylabel="x2")

# ╔═╡ 25f57f53-a366-420b-a7de-b0a62c96bd57
grid_optimum_n_10 = grid_search(ackley, (-3, 3))

# ╔═╡ 1d25bd6a-2df3-485f-ab6c-8cc0298ddf54
grid_optimum_n_50 = grid_search(ackley, (-3, 3), n=50)

# ╔═╡ 7b0542ab-38c0-4255-ba56-1a2eb0d6a390
random_optimum_n_10 = random_search(ackley, (-3, 3))

# ╔═╡ 21d17028-ffa4-4827-a1b1-5a1b77cbe714
random_optimum_n_50 = random_search(ackley, (-3, 3), n = 50)

# ╔═╡ 00000000-0000-0000-0000-000000000001
PLUTO_PROJECT_TOML_CONTENTS = """
[deps]
Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f"
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"

[compat]
Distributions = "~0.25.16"
Plots = "~1.22.2"
"""

# ╔═╡ 00000000-0000-0000-0000-000000000002
PLUTO_MANIFEST_TOML_CONTENTS = """
# This file is machine-generated - editing it directly is not advised

[[Adapt]]
deps = ["LinearAlgebra"]
git-tree-sha1 = "84918055d15b3114ede17ac6a7182f68870c16f7"
uuid = "79e6a3ab-5dfb-504d-930d-738a2a938a0e"
version = "3.3.1"

[[ArgTools]]
uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f"

[[Artifacts]]
uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33"

[[Base64]]
uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"

[[Bzip2_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "19a35467a82e236ff51bc17a3a44b69ef35185a2"
uuid = "6e34b625-4abd-537c-b88f-471c36dfa7a0"
version = "1.0.8+0"

[[Cairo_jll]]
deps = ["Artifacts", "Bzip2_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "JLLWrappers", "LZO_jll", "Libdl", "Pixman_jll", "Pkg", "Xorg_libXext_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"]
git-tree-sha1 = "f2202b55d816427cd385a9a4f3ffb226bee80f99"
uuid = "83423d85-b0ee-5818-9007-b63ccbeb887a"
version = "1.16.1+0"

[[ChainRulesCore]]
deps = ["Compat", "LinearAlgebra", "SparseArrays"]
git-tree-sha1 = "bd4afa1fdeec0c8b89dad3c6e92bc6e3b0fec9ce"
uuid = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4"
version = "1.6.0"

[[ColorSchemes]]
deps = ["ColorTypes", "Colors", "FixedPointNumbers", "Random"]
git-tree-sha1 = "9995eb3977fbf67b86d0a0a0508e83017ded03f2"
uuid = "35d6a980-a343-548e-a6ea-1d62b119f2f4"
version = "3.14.0"

[[ColorTypes]]
deps = ["FixedPointNumbers", "Random"]
git-tree-sha1 = "024fe24d83e4a5bf5fc80501a314ce0d1aa35597"
uuid = "3da002f7-5984-5a60-b8a6-cbb66c0b333f"
version = "0.11.0"

[[Colors]]
deps = ["ColorTypes", "FixedPointNumbers", "Reexport"]
git-tree-sha1 = "417b0ed7b8b838aa6ca0a87aadf1bb9eb111ce40"
uuid = "5ae59095-9a9b-59fe-a467-6f913c188581"
version = "0.12.8"

[[Compat]]
deps = ["Base64", "Dates", "DelimitedFiles", "Distributed", "InteractiveUtils", "LibGit2", "Libdl", "LinearAlgebra", "Markdown", "Mmap", "Pkg", "Printf", "REPL", "Random", "SHA", "Serialization", "SharedArrays", "Sockets", "SparseArrays", "Statistics", "Test", "UUIDs", "Unicode"]
git-tree-sha1 = "4866e381721b30fac8dda4c8cb1d9db45c8d2994"
uuid = "34da2185-b29b-5c13-b0c7-acf172513d20"
version = "3.37.0"

[[CompilerSupportLibraries_jll]]
deps = ["Artifacts", "Libdl"]
uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae"

[[Contour]]
deps = ["StaticArrays"]
git-tree-sha1 = "9f02045d934dc030edad45944ea80dbd1f0ebea7"
uuid = "d38c429a-6771-53c6-b99e-75d170b6e991"
version = "0.5.7"

[[DataAPI]]
git-tree-sha1 = "cc70b17275652eb47bc9e5f81635981f13cea5c8"
uuid = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a"
version = "1.9.0"

[[DataStructures]]
deps = ["Compat", "InteractiveUtils", "OrderedCollections"]
git-tree-sha1 = "7d9d316f04214f7efdbb6398d545446e246eff02"
uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8"
version = "0.18.10"

[[DataValueInterfaces]]
git-tree-sha1 = "bfc1187b79289637fa0ef6d4436ebdfe6905cbd6"
uuid = "e2d170a0-9d28-54be-80f0-106bbe20a464"
version = "1.0.0"

[[Dates]]
deps = ["Printf"]
uuid = "ade2ca70-3891-5945-98fb-dc099432e06a"

[[DelimitedFiles]]
deps = ["Mmap"]
uuid = "8bb1440f-4735-579b-a4ab-409b98df4dab"

[[Distributed]]
deps = ["Random", "Serialization", "Sockets"]
uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b"

[[Distributions]]
deps = ["ChainRulesCore", "FillArrays", "LinearAlgebra", "PDMats", "Printf", "QuadGK", "Random", "SparseArrays", "SpecialFunctions", "Statistics", "StatsBase", "StatsFuns"]
git-tree-sha1 = "f4efaa4b5157e0cdb8283ae0b5428bc9208436ed"
uuid = "31c24e10-a181-5473-b8eb-7969acd0382f"
version = "0.25.16"

[[DocStringExtensions]]
deps = ["LibGit2"]
git-tree-sha1 = "a32185f5428d3986f47c2ab78b1f216d5e6cc96f"
uuid = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae"
version = "0.8.5"

[[Downloads]]
deps = ["ArgTools", "LibCURL", "NetworkOptions"]
uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6"

[[EarCut_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "3f3a2501fa7236e9b911e0f7a588c657e822bb6d"
uuid = "5ae413db-bbd1-5e63-b57d-d24a61df00f5"
version = "2.2.3+0"

[[Expat_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "b3bfd02e98aedfa5cf885665493c5598c350cd2f"
uuid = "2e619515-83b5-522b-bb60-26c02a35a201"
version = "2.2.10+0"

[[FFMPEG]]
deps = ["FFMPEG_jll"]
git-tree-sha1 = "b57e3acbe22f8484b4b5ff66a7499717fe1a9cc8"
uuid = "c87230d0-a227-11e9-1b43-d7ebe4e7570a"
version = "0.4.1"

[[FFMPEG_jll]]
deps = ["Artifacts", "Bzip2_jll", "FreeType2_jll", "FriBidi_jll", "JLLWrappers", "LAME_jll", "Libdl", "Ogg_jll", "OpenSSL_jll", "Opus_jll", "Pkg", "Zlib_jll", "libass_jll", "libfdk_aac_jll", "libvorbis_jll", "x264_jll", "x265_jll"]
git-tree-sha1 = "d8a578692e3077ac998b50c0217dfd67f21d1e5f"
uuid = "b22a6f82-2f65-5046-a5b2-351ab43fb4e5"
version = "4.4.0+0"

[[FillArrays]]
deps = ["LinearAlgebra", "Random", "SparseArrays", "Statistics"]
git-tree-sha1 = "7f6ad1a7f4621b4ab8e554133dade99ebc6e7221"
uuid = "1a297f60-69ca-5386-bcde-b61e274b549b"
version = "0.12.5"

[[FixedPointNumbers]]
deps = ["Statistics"]
git-tree-sha1 = "335bfdceacc84c5cdf16aadc768aa5ddfc5383cc"
uuid = "53c48c17-4a7d-5ca2-90c5-79b7896eea93"
version = "0.8.4"

[[Fontconfig_jll]]
deps = ["Artifacts", "Bzip2_jll", "Expat_jll", "FreeType2_jll", "JLLWrappers", "Libdl", "Libuuid_jll", "Pkg", "Zlib_jll"]
git-tree-sha1 = "21efd19106a55620a188615da6d3d06cd7f6ee03"
uuid = "a3f928ae-7b40-5064-980b-68af3947d34b"
version = "2.13.93+0"

[[Formatting]]
deps = ["Printf"]
git-tree-sha1 = "8339d61043228fdd3eb658d86c926cb282ae72a8"
uuid = "59287772-0a20-5a39-b81b-1366585eb4c0"
version = "0.4.2"

[[FreeType2_jll]]
deps = ["Artifacts", "Bzip2_jll", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"]
git-tree-sha1 = "87eb71354d8ec1a96d4a7636bd57a7347dde3ef9"
uuid = "d7e528f0-a631-5988-bf34-fe36492bcfd7"
version = "2.10.4+0"

[[FriBidi_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "aa31987c2ba8704e23c6c8ba8a4f769d5d7e4f91"
uuid = "559328eb-81f9-559d-9380-de523a88c83c"
version = "1.0.10+0"

[[GLFW_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Libglvnd_jll", "Pkg", "Xorg_libXcursor_jll", "Xorg_libXi_jll", "Xorg_libXinerama_jll", "Xorg_libXrandr_jll"]
git-tree-sha1 = "dba1e8614e98949abfa60480b13653813d8f0157"
uuid = "0656b61e-2033-5cc2-a64a-77c0f6c09b89"
version = "3.3.5+0"

[[GR]]
deps = ["Base64", "DelimitedFiles", "GR_jll", "HTTP", "JSON", "Libdl", "LinearAlgebra", "Pkg", "Printf", "Random", "Serialization", "Sockets", "Test", "UUIDs"]
git-tree-sha1 = "c2178cfbc0a5a552e16d097fae508f2024de61a3"
uuid = "28b8d3ca-fb5f-59d9-8090-bfdbd6d07a71"
version = "0.59.0"

[[GR_jll]]
deps = ["Artifacts", "Bzip2_jll", "Cairo_jll", "FFMPEG_jll", "Fontconfig_jll", "GLFW_jll", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Libtiff_jll", "Pixman_jll", "Pkg", "Qt5Base_jll", "Zlib_jll", "libpng_jll"]
git-tree-sha1 = "ef49a187604f865f4708c90e3f431890724e9012"
uuid = "d2c73de3-f751-5644-a686-071e5b155ba9"
version = "0.59.0+0"

[[GeometryBasics]]
deps = ["EarCut_jll", "IterTools", "LinearAlgebra", "StaticArrays", "StructArrays", "Tables"]
git-tree-sha1 = "58bcdf5ebc057b085e58d95c138725628dd7453c"
uuid = "5c1252a2-5f33-56bf-86c9-59e7332b4326"
version = "0.4.1"

[[Gettext_jll]]
deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Libiconv_jll", "Pkg", "XML2_jll"]
git-tree-sha1 = "9b02998aba7bf074d14de89f9d37ca24a1a0b046"
uuid = "78b55507-aeef-58d4-861c-77aaff3498b1"
version = "0.21.0+0"

[[Glib_jll]]
deps = ["Artifacts", "Gettext_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Libiconv_jll", "Libmount_jll", "PCRE_jll", "Pkg", "Zlib_jll"]
git-tree-sha1 = "7bf67e9a481712b3dbe9cb3dac852dc4b1162e02"
uuid = "7746bdde-850d-59dc-9ae8-88ece973131d"
version = "2.68.3+0"

[[Graphite2_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "344bf40dcab1073aca04aa0df4fb092f920e4011"
uuid = "3b182d85-2403-5c21-9c21-1e1f0cc25472"
version = "1.3.14+0"

[[Grisu]]
git-tree-sha1 = "53bb909d1151e57e2484c3d1b53e19552b887fb2"
uuid = "42e2da0e-8278-4e71-bc24-59509adca0fe"
version = "1.0.2"

[[HTTP]]
deps = ["Base64", "Dates", "IniFile", "Logging", "MbedTLS", "NetworkOptions", "Sockets", "URIs"]
git-tree-sha1 = "60ed5f1643927479f845b0135bb369b031b541fa"
uuid = "cd3eb016-35fb-5094-929b-558a96fad6f3"
version = "0.9.14"

[[HarfBuzz_jll]]
deps = ["Artifacts", "Cairo_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "Graphite2_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Pkg"]
git-tree-sha1 = "8a954fed8ac097d5be04921d595f741115c1b2ad"
uuid = "2e76f6c2-a576-52d4-95c1-20adfe4de566"
version = "2.8.1+0"

[[IniFile]]
deps = ["Test"]
git-tree-sha1 = "098e4d2c533924c921f9f9847274f2ad89e018b8"
uuid = "83e8ac13-25f8-5344-8a64-a9f2b223428f"
version = "0.5.0"

[[InteractiveUtils]]
deps = ["Markdown"]
uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240"

[[IrrationalConstants]]
git-tree-sha1 = "f76424439413893a832026ca355fe273e93bce94"
uuid = "92d709cd-6900-40b7-9082-c6be49f344b6"
version = "0.1.0"

[[IterTools]]
git-tree-sha1 = "05110a2ab1fc5f932622ffea2a003221f4782c18"
uuid = "c8e1da08-722c-5040-9ed9-7db0dc04731e"
version = "1.3.0"

[[IteratorInterfaceExtensions]]
git-tree-sha1 = "a3f24677c21f5bbe9d2a714f95dcd58337fb2856"
uuid = "82899510-4779-5014-852e-03e436cf321d"
version = "1.0.0"

[[JLLWrappers]]
deps = ["Preferences"]
git-tree-sha1 = "642a199af8b68253517b80bd3bfd17eb4e84df6e"
uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210"
version = "1.3.0"

[[JSON]]
deps = ["Dates", "Mmap", "Parsers", "Unicode"]
git-tree-sha1 = "8076680b162ada2a031f707ac7b4953e30667a37"
uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6"
version = "0.21.2"

[[JpegTurbo_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "d735490ac75c5cb9f1b00d8b5509c11984dc6943"
uuid = "aacddb02-875f-59d6-b918-886e6ef4fbf8"
version = "2.1.0+0"

[[LAME_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "f6250b16881adf048549549fba48b1161acdac8c"
uuid = "c1c5ebd0-6772-5130-a774-d5fcae4a789d"
version = "3.100.1+0"

[[LZO_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "e5b909bcf985c5e2605737d2ce278ed791b89be6"
uuid = "dd4b983a-f0e5-5f8d-a1b7-129d4a5fb1ac"
version = "2.10.1+0"

[[LaTeXStrings]]
git-tree-sha1 = "c7f1c695e06c01b95a67f0cd1d34994f3e7db104"
uuid = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f"
version = "1.2.1"

[[Latexify]]
deps = ["Formatting", "InteractiveUtils", "LaTeXStrings", "MacroTools", "Markdown", "Printf", "Requires"]
git-tree-sha1 = "a4b12a1bd2ebade87891ab7e36fdbce582301a92"
uuid = "23fbe1c1-3f47-55db-b15f-69d7ec21a316"
version = "0.15.6"

[[LibCURL]]
deps = ["LibCURL_jll", "MozillaCACerts_jll"]
uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21"

[[LibCURL_jll]]
deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll", "Zlib_jll", "nghttp2_jll"]
uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0"

[[LibGit2]]
deps = ["Base64", "NetworkOptions", "Printf", "SHA"]
uuid = "76f85450-5226-5b5a-8eaa-529ad045b433"

[[LibSSH2_jll]]
deps = ["Artifacts", "Libdl", "MbedTLS_jll"]
uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8"

[[Libdl]]
uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb"

[[Libffi_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "761a393aeccd6aa92ec3515e428c26bf99575b3b"
uuid = "e9f186c6-92d2-5b65-8a66-fee21dc1b490"
version = "3.2.2+0"

[[Libgcrypt_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgpg_error_jll", "Pkg"]
git-tree-sha1 = "64613c82a59c120435c067c2b809fc61cf5166ae"
uuid = "d4300ac3-e22c-5743-9152-c294e39db1e4"
version = "1.8.7+0"

[[Libglvnd_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll", "Xorg_libXext_jll"]
git-tree-sha1 = "7739f837d6447403596a75d19ed01fd08d6f56bf"
uuid = "7e76a0d4-f3c7-5321-8279-8d96eeed0f29"
version = "1.3.0+3"

[[Libgpg_error_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "c333716e46366857753e273ce6a69ee0945a6db9"
uuid = "7add5ba3-2f88-524e-9cd5-f83b8a55f7b8"
version = "1.42.0+0"

[[Libiconv_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "42b62845d70a619f063a7da093d995ec8e15e778"
uuid = "94ce4f54-9a6c-5748-9c1c-f9c7231a4531"
version = "1.16.1+1"

[[Libmount_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "9c30530bf0effd46e15e0fdcf2b8636e78cbbd73"
uuid = "4b2f31a3-9ecc-558c-b454-b3730dcb73e9"
version = "2.35.0+0"

[[Libtiff_jll]]
deps = ["Artifacts", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Pkg", "Zlib_jll", "Zstd_jll"]
git-tree-sha1 = "340e257aada13f95f98ee352d316c3bed37c8ab9"
uuid = "89763e89-9b03-5906-acba-b20f662cd828"
version = "4.3.0+0"

[[Libuuid_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "7f3efec06033682db852f8b3bc3c1d2b0a0ab066"
uuid = "38a345b3-de98-5d2b-a5d3-14cd9215e700"
version = "2.36.0+0"

[[LinearAlgebra]]
deps = ["Libdl", "libblastrampoline_jll"]
uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"

[[LogExpFunctions]]
deps = ["ChainRulesCore", "DocStringExtensions", "IrrationalConstants", "LinearAlgebra"]
git-tree-sha1 = "34dc30f868e368f8a17b728a1238f3fcda43931a"
uuid = "2ab3a3ac-af41-5b50-aa03-7779005ae688"
version = "0.3.3"

[[Logging]]
uuid = "56ddb016-857b-54e1-b83d-db4d58db5568"

[[MacroTools]]
deps = ["Markdown", "Random"]
git-tree-sha1 = "5a5bc6bf062f0f95e62d0fe0a2d99699fed82dd9"
uuid = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09"
version = "0.5.8"

[[Markdown]]
deps = ["Base64"]
uuid = "d6f4376e-aef5-505a-96c1-9c027394607a"

[[MbedTLS]]
deps = ["Dates", "MbedTLS_jll", "Random", "Sockets"]
git-tree-sha1 = "1c38e51c3d08ef2278062ebceade0e46cefc96fe"
uuid = "739be429-bea8-5141-9913-cc70e7f3736d"
version = "1.0.3"

[[MbedTLS_jll]]
deps = ["Artifacts", "Libdl"]
uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1"

[[Measures]]
git-tree-sha1 = "e498ddeee6f9fdb4551ce855a46f54dbd900245f"
uuid = "442fdcdd-2543-5da2-b0f3-8c86c306513e"
version = "0.3.1"

[[Missings]]
deps = ["DataAPI"]
git-tree-sha1 = "bf210ce90b6c9eed32d25dbcae1ebc565df2687f"
uuid = "e1d29d7a-bbdc-5cf2-9ac0-f12de2c33e28"
version = "1.0.2"

[[Mmap]]
uuid = "a63ad114-7e13-5084-954f-fe012c677804"

[[MozillaCACerts_jll]]
uuid = "14a3606d-f60d-562e-9121-12d972cd8159"

[[NaNMath]]
git-tree-sha1 = "bfe47e760d60b82b66b61d2d44128b62e3a369fb"
uuid = "77ba4419-2d1f-58cd-9bb1-8ffee604a2e3"
version = "0.3.5"

[[NetworkOptions]]
uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908"

[[Ogg_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "7937eda4681660b4d6aeeecc2f7e1c81c8ee4e2f"
uuid = "e7412a2a-1a6e-54c0-be00-318e2571c051"
version = "1.3.5+0"

[[OpenBLAS_jll]]
deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"]
uuid = "4536629a-c528-5b80-bd46-f80d51c5b363"

[[OpenLibm_jll]]
deps = ["Artifacts", "Libdl"]
uuid = "05823500-19ac-5b8b-9628-191a04bc5112"

[[OpenSSL_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "15003dcb7d8db3c6c857fda14891a539a8f2705a"
uuid = "458c3c95-2e84-50aa-8efc-19380b2a3a95"
version = "1.1.10+0"

[[OpenSpecFun_jll]]
deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "13652491f6856acfd2db29360e1bbcd4565d04f1"
uuid = "efe28fd5-8261-553b-a9e1-b2916fc3738e"
version = "0.5.5+0"

[[Opus_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "51a08fb14ec28da2ec7a927c4337e4332c2a4720"
uuid = "91d4177d-7536-5919-b921-800302f37372"
version = "1.3.2+0"

[[OrderedCollections]]
git-tree-sha1 = "85f8e6578bf1f9ee0d11e7bb1b1456435479d47c"
uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d"
version = "1.4.1"

[[PCRE_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "b2a7af664e098055a7529ad1a900ded962bca488"
uuid = "2f80f16e-611a-54ab-bc61-aa92de5b98fc"
version = "8.44.0+0"

[[PDMats]]
deps = ["LinearAlgebra", "SparseArrays", "SuiteSparse"]
git-tree-sha1 = "4dd403333bcf0909341cfe57ec115152f937d7d8"
uuid = "90014a1f-27ba-587c-ab20-58faa44d9150"
version = "0.11.1"

[[Parsers]]
deps = ["Dates"]
git-tree-sha1 = "9d8c00ef7a8d110787ff6f170579846f776133a9"
uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0"
version = "2.0.4"

[[Pixman_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "b4f5d02549a10e20780a24fce72bea96b6329e29"
uuid = "30392449-352a-5448-841d-b1acce4e97dc"
version = "0.40.1+0"

[[Pkg]]
deps = ["Artifacts", "Dates", "Downloads", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"]
uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"

[[PlotThemes]]
deps = ["PlotUtils", "Requires", "Statistics"]
git-tree-sha1 = "a3a964ce9dc7898193536002a6dd892b1b5a6f1d"
uuid = "ccf2f8ad-2431-5c83-bf29-c5338b663b6a"
version = "2.0.1"

[[PlotUtils]]
deps = ["ColorSchemes", "Colors", "Dates", "Printf", "Random", "Reexport", "Statistics"]
git-tree-sha1 = "2537ed3c0ed5e03896927187f5f2ee6a4ab342db"
uuid = "995b91a9-d308-5afd-9ec6-746e21dbc043"
version = "1.0.14"

[[Plots]]
deps = ["Base64", "Contour", "Dates", "Downloads", "FFMPEG", "FixedPointNumbers", "GR", "GeometryBasics", "JSON", "Latexify", "LinearAlgebra", "Measures", "NaNMath", "PlotThemes", "PlotUtils", "Printf", "REPL", "Random", "RecipesBase", "RecipesPipeline", "Reexport", "Requires", "Scratch", "Showoff", "SparseArrays", "Statistics", "StatsBase", "UUIDs"]
git-tree-sha1 = "457b13497a3ea4deb33d273a6a5ea15c25c0ebd9"
uuid = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
version = "1.22.2"

[[Preferences]]
deps = ["TOML"]
git-tree-sha1 = "00cfd92944ca9c760982747e9a1d0d5d86ab1e5a"
uuid = "21216c6a-2e73-6563-6e65-726566657250"
version = "1.2.2"

[[Printf]]
deps = ["Unicode"]
uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7"

[[Qt5Base_jll]]
deps = ["Artifacts", "CompilerSupportLibraries_jll", "Fontconfig_jll", "Glib_jll", "JLLWrappers", "Libdl", "Libglvnd_jll", "OpenSSL_jll", "Pkg", "Xorg_libXext_jll", "Xorg_libxcb_jll", "Xorg_xcb_util_image_jll", "Xorg_xcb_util_keysyms_jll", "Xorg_xcb_util_renderutil_jll", "Xorg_xcb_util_wm_jll", "Zlib_jll", "xkbcommon_jll"]
git-tree-sha1 = "ad368663a5e20dbb8d6dc2fddeefe4dae0781ae8"
uuid = "ea2cea3b-5b76-57ae-a6ef-0a8af62496e1"
version = "5.15.3+0"

[[QuadGK]]
deps = ["DataStructures", "LinearAlgebra"]
git-tree-sha1 = "78aadffb3efd2155af139781b8a8df1ef279ea39"
uuid = "1fd47b50-473d-5c70-9696-f719f8f3bcdc"
version = "2.4.2"

[[REPL]]
deps = ["InteractiveUtils", "Markdown", "Sockets", "Unicode"]
uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb"

[[Random]]
deps = ["Serialization"]
uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"

[[RecipesBase]]
git-tree-sha1 = "44a75aa7a527910ee3d1751d1f0e4148698add9e"
uuid = "3cdcf5f2-1ef4-517c-9805-6587b60abb01"
version = "1.1.2"

[[RecipesPipeline]]
deps = ["Dates", "NaNMath", "PlotUtils", "RecipesBase"]
git-tree-sha1 = "7ad0dfa8d03b7bcf8c597f59f5292801730c55b8"
uuid = "01d81517-befc-4cb6-b9ec-a95719d0359c"
version = "0.4.1"

[[Reexport]]
git-tree-sha1 = "45e428421666073eab6f2da5c9d310d99bb12f9b"
uuid = "189a3867-3050-52da-a836-e630ba90ab69"
version = "1.2.2"

[[Requires]]
deps = ["UUIDs"]
git-tree-sha1 = "4036a3bd08ac7e968e27c203d45f5fff15020621"
uuid = "ae029012-a4dd-5104-9daa-d747884805df"
version = "1.1.3"

[[Rmath]]
deps = ["Random", "Rmath_jll"]
git-tree-sha1 = "bf3188feca147ce108c76ad82c2792c57abe7b1f"
uuid = "79098fc4-a85e-5d69-aa6a-4863f24498fa"
version = "0.7.0"

[[Rmath_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "68db32dff12bb6127bac73c209881191bf0efbb7"
uuid = "f50d1b31-88e8-58de-be2c-1cc44531875f"
version = "0.3.0+0"

[[SHA]]
uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce"

[[Scratch]]
deps = ["Dates"]
git-tree-sha1 = "0b4b7f1393cff97c33891da2a0bf69c6ed241fda"
uuid = "6c6a2e73-6563-6170-7368-637461726353"
version = "1.1.0"

[[Serialization]]
uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b"

[[SharedArrays]]
deps = ["Distributed", "Mmap", "Random", "Serialization"]
uuid = "1a1011a3-84de-559e-8e89-a11a2f7dc383"

[[Showoff]]
deps = ["Dates", "Grisu"]
git-tree-sha1 = "91eddf657aca81df9ae6ceb20b959ae5653ad1de"
uuid = "992d4aef-0814-514b-bc4d-f2e9a6c4116f"
version = "1.0.3"

[[Sockets]]
uuid = "6462fe0b-24de-5631-8697-dd941f90decc"

[[SortingAlgorithms]]
deps = ["DataStructures"]
git-tree-sha1 = "b3363d7460f7d098ca0912c69b082f75625d7508"
uuid = "a2af1166-a08f-5f64-846c-94a0d3cef48c"
version = "1.0.1"

[[SparseArrays]]
deps = ["LinearAlgebra", "Random"]
uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"

[[SpecialFunctions]]
deps = ["ChainRulesCore", "LogExpFunctions", "OpenLibm_jll", "OpenSpecFun_jll"]
git-tree-sha1 = "ad42c30a6204c74d264692e633133dcea0e8b14e"
uuid = "276daf66-3868-5448-9aa4-cd146d93841b"
version = "1.6.2"

[[StaticArrays]]
deps = ["LinearAlgebra", "Random", "Statistics"]
git-tree-sha1 = "3240808c6d463ac46f1c1cd7638375cd22abbccb"
uuid = "90137ffa-7385-5640-81b9-e52037218182"
version = "1.2.12"

[[Statistics]]
deps = ["LinearAlgebra", "SparseArrays"]
uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"

[[StatsAPI]]
git-tree-sha1 = "1958272568dc176a1d881acb797beb909c785510"
uuid = "82ae8749-77ed-4fe6-ae5f-f523153014b0"
version = "1.0.0"

[[StatsBase]]
deps = ["DataAPI", "DataStructures", "LinearAlgebra", "Missings", "Printf", "Random", "SortingAlgorithms", "SparseArrays", "Statistics", "StatsAPI"]
git-tree-sha1 = "8cbbc098554648c84f79a463c9ff0fd277144b6c"
uuid = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91"
version = "0.33.10"

[[StatsFuns]]
deps = ["ChainRulesCore", "IrrationalConstants", "LogExpFunctions", "Reexport", "Rmath", "SpecialFunctions"]
git-tree-sha1 = "46d7ccc7104860c38b11966dd1f72ff042f382e4"
uuid = "4c63d2b9-4356-54db-8cca-17b64c39e42c"
version = "0.9.10"

[[StructArrays]]
deps = ["Adapt", "DataAPI", "StaticArrays", "Tables"]
git-tree-sha1 = "2ce41e0d042c60ecd131e9fb7154a3bfadbf50d3"
uuid = "09ab397b-f2b6-538f-b94a-2f83cf4a842a"
version = "0.6.3"

[[SuiteSparse]]
deps = ["Libdl", "LinearAlgebra", "Serialization", "SparseArrays"]
uuid = "4607b0f0-06f3-5cda-b6b1-a6196a1729e9"

[[TOML]]
deps = ["Dates"]
uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76"

[[TableTraits]]
deps = ["IteratorInterfaceExtensions"]
git-tree-sha1 = "c06b2f539df1c6efa794486abfb6ed2022561a39"
uuid = "3783bdb8-4a98-5b6b-af9a-565f29a5fe9c"
version = "1.0.1"

[[Tables]]
deps = ["DataAPI", "DataValueInterfaces", "IteratorInterfaceExtensions", "LinearAlgebra", "TableTraits", "Test"]
git-tree-sha1 = "1162ce4a6c4b7e31e0e6b14486a6986951c73be9"
uuid = "bd369af6-aec1-5ad0-b16a-f7cc5008161c"
version = "1.5.2"

[[Tar]]
deps = ["ArgTools", "SHA"]
uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e"

[[Test]]
deps = ["InteractiveUtils", "Logging", "Random", "Serialization"]
uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[[URIs]]
git-tree-sha1 = "97bbe755a53fe859669cd907f2d96aee8d2c1355"
uuid = "5c2747f8-b7ea-4ff2-ba2e-563bfd36b1d4"
version = "1.3.0"

[[UUIDs]]
deps = ["Random", "SHA"]
uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4"

[[Unicode]]
uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5"

[[Wayland_jll]]
deps = ["Artifacts", "Expat_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Pkg", "XML2_jll"]
git-tree-sha1 = "3e61f0b86f90dacb0bc0e73a0c5a83f6a8636e23"
uuid = "a2964d1f-97da-50d4-b82a-358c7fce9d89"
version = "1.19.0+0"

[[Wayland_protocols_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Wayland_jll"]
git-tree-sha1 = "2839f1c1296940218e35df0bbb220f2a79686670"
uuid = "2381bf8a-dfd0-557d-9999-79630e7b1b91"
version = "1.18.0+4"

[[XML2_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Libiconv_jll", "Pkg", "Zlib_jll"]
git-tree-sha1 = "1acf5bdf07aa0907e0a37d3718bb88d4b687b74a"
uuid = "02c8fc9c-b97f-50b9-bbe4-9be30ff0a78a"
version = "2.9.12+0"

[[XSLT_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgcrypt_jll", "Libgpg_error_jll", "Libiconv_jll", "Pkg", "XML2_jll", "Zlib_jll"]
git-tree-sha1 = "91844873c4085240b95e795f692c4cec4d805f8a"
uuid = "aed1982a-8fda-507f-9586-7b0439959a61"
version = "1.1.34+0"

[[Xorg_libX11_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libxcb_jll", "Xorg_xtrans_jll"]
git-tree-sha1 = "5be649d550f3f4b95308bf0183b82e2582876527"
uuid = "4f6342f7-b3d2-589e-9d20-edeb45f2b2bc"
version = "1.6.9+4"

[[Xorg_libXau_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "4e490d5c960c314f33885790ed410ff3a94ce67e"
uuid = "0c0b7dd1-d40b-584c-a123-a41640f87eec"
version = "1.0.9+4"

[[Xorg_libXcursor_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXfixes_jll", "Xorg_libXrender_jll"]
git-tree-sha1 = "12e0eb3bc634fa2080c1c37fccf56f7c22989afd"
uuid = "935fb764-8cf2-53bf-bb30-45bb1f8bf724"
version = "1.2.0+4"

[[Xorg_libXdmcp_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "4fe47bd2247248125c428978740e18a681372dd4"
uuid = "a3789734-cfe1-5b06-b2d0-1dd0d9d62d05"
version = "1.1.3+4"

[[Xorg_libXext_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"]
git-tree-sha1 = "b7c0aa8c376b31e4852b360222848637f481f8c3"
uuid = "1082639a-0dae-5f34-9b06-72781eeb8cb3"
version = "1.3.4+4"

[[Xorg_libXfixes_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"]
git-tree-sha1 = "0e0dc7431e7a0587559f9294aeec269471c991a4"
uuid = "d091e8ba-531a-589c-9de9-94069b037ed8"
version = "5.0.3+4"

[[Xorg_libXi_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXext_jll", "Xorg_libXfixes_jll"]
git-tree-sha1 = "89b52bc2160aadc84d707093930ef0bffa641246"
uuid = "a51aa0fd-4e3c-5386-b890-e753decda492"
version = "1.7.10+4"

[[Xorg_libXinerama_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXext_jll"]
git-tree-sha1 = "26be8b1c342929259317d8b9f7b53bf2bb73b123"
uuid = "d1454406-59df-5ea1-beac-c340f2130bc3"
version = "1.1.4+4"

[[Xorg_libXrandr_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXext_jll", "Xorg_libXrender_jll"]
git-tree-sha1 = "34cea83cb726fb58f325887bf0612c6b3fb17631"
uuid = "ec84b674-ba8e-5d96-8ba1-2a689ba10484"
version = "1.5.2+4"

[[Xorg_libXrender_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"]
git-tree-sha1 = "19560f30fd49f4d4efbe7002a1037f8c43d43b96"
uuid = "ea2f1a96-1ddc-540d-b46f-429655e07cfa"
version = "0.9.10+4"

[[Xorg_libpthread_stubs_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "6783737e45d3c59a4a4c4091f5f88cdcf0908cbb"
uuid = "14d82f49-176c-5ed1-bb49-ad3f5cbd8c74"
version = "0.1.0+3"

[[Xorg_libxcb_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "XSLT_jll", "Xorg_libXau_jll", "Xorg_libXdmcp_jll", "Xorg_libpthread_stubs_jll"]
git-tree-sha1 = "daf17f441228e7a3833846cd048892861cff16d6"
uuid = "c7cfdc94-dc32-55de-ac96-5a1b8d977c5b"
version = "1.13.0+3"

[[Xorg_libxkbfile_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"]
git-tree-sha1 = "926af861744212db0eb001d9e40b5d16292080b2"
uuid = "cc61e674-0454-545c-8b26-ed2c68acab7a"
version = "1.1.0+4"

[[Xorg_xcb_util_image_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_xcb_util_jll"]
git-tree-sha1 = "0fab0a40349ba1cba2c1da699243396ff8e94b97"
uuid = "12413925-8142-5f55-bb0e-6d7ca50bb09b"
version = "0.4.0+1"

[[Xorg_xcb_util_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libxcb_jll"]
git-tree-sha1 = "e7fd7b2881fa2eaa72717420894d3938177862d1"
uuid = "2def613f-5ad1-5310-b15b-b15d46f528f5"
version = "0.4.0+1"

[[Xorg_xcb_util_keysyms_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_xcb_util_jll"]
git-tree-sha1 = "d1151e2c45a544f32441a567d1690e701ec89b00"
uuid = "975044d2-76e6-5fbe-bf08-97ce7c6574c7"
version = "0.4.0+1"

[[Xorg_xcb_util_renderutil_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_xcb_util_jll"]
git-tree-sha1 = "dfd7a8f38d4613b6a575253b3174dd991ca6183e"
uuid = "0d47668e-0667-5a69-a72c-f761630bfb7e"
version = "0.3.9+1"

[[Xorg_xcb_util_wm_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_xcb_util_jll"]
git-tree-sha1 = "e78d10aab01a4a154142c5006ed44fd9e8e31b67"
uuid = "c22f9ab0-d5fe-5066-847c-f4bb1cd4e361"
version = "0.4.1+1"

[[Xorg_xkbcomp_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libxkbfile_jll"]
git-tree-sha1 = "4bcbf660f6c2e714f87e960a171b119d06ee163b"
uuid = "35661453-b289-5fab-8a00-3d9160c6a3a4"
version = "1.4.2+4"

[[Xorg_xkeyboard_config_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_xkbcomp_jll"]
git-tree-sha1 = "5c8424f8a67c3f2209646d4425f3d415fee5931d"
uuid = "33bec58e-1273-512f-9401-5d533626f822"
version = "2.27.0+4"

[[Xorg_xtrans_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "79c31e7844f6ecf779705fbc12146eb190b7d845"
uuid = "c5fb5394-a638-5e4d-96e5-b29de1b5cf10"
version = "1.4.0+3"

[[Zlib_jll]]
deps = ["Libdl"]
uuid = "83775a58-1f1d-513f-b197-d71354ab007a"

[[Zstd_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "cc4bf3fdde8b7e3e9fa0351bdeedba1cf3b7f6e6"
uuid = "3161d3a3-bdf6-5164-811a-617609db77b4"
version = "1.5.0+0"

[[libass_jll]]
deps = ["Artifacts", "Bzip2_jll", "FreeType2_jll", "FriBidi_jll", "HarfBuzz_jll", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"]
git-tree-sha1 = "5982a94fcba20f02f42ace44b9894ee2b140fe47"
uuid = "0ac62f75-1d6f-5e53-bd7c-93b484bb37c0"
version = "0.15.1+0"

[[libblastrampoline_jll]]
deps = ["Artifacts", "Libdl", "OpenBLAS_jll"]
uuid = "8e850b90-86db-534c-a0d3-1478176c7d93"

[[libfdk_aac_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "daacc84a041563f965be61859a36e17c4e4fcd55"
uuid = "f638f0a6-7fb0-5443-88ba-1cc74229b280"
version = "2.0.2+0"

[[libpng_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"]
git-tree-sha1 = "94d180a6d2b5e55e447e2d27a29ed04fe79eb30c"
uuid = "b53b4c65-9356-5827-b1ea-8c7a1a84506f"
version = "1.6.38+0"

[[libvorbis_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Ogg_jll", "Pkg"]
git-tree-sha1 = "c45f4e40e7aafe9d086379e5578947ec8b95a8fb"
uuid = "f27f6e37-5d2b-51aa-960f-b287f2bc3b7a"
version = "1.3.7+0"

[[nghttp2_jll]]
deps = ["Artifacts", "Libdl"]
uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d"

[[p7zip_jll]]
deps = ["Artifacts", "Libdl"]
uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0"

[[x264_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "4fea590b89e6ec504593146bf8b988b2c00922b2"
uuid = "1270edf5-f2f9-52d2-97e9-ab00b5d0237a"
version = "2021.5.5+0"

[[x265_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "ee567a171cce03570d77ad3a43e90218e38937a9"
uuid = "dfaa095f-4041-5dcd-9319-2fabd8486b76"
version = "3.5.0+0"

[[xkbcommon_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Wayland_jll", "Wayland_protocols_jll", "Xorg_libxcb_jll", "Xorg_xkeyboard_config_jll"]
git-tree-sha1 = "ece2350174195bb31de1a63bea3a41ae1aa593b6"
uuid = "d8fb68d0-12a3-5cfd-a85a-d49703b185fd"
version = "0.9.1+5"
"""

# ╔═╡ Cell order:
# ╟─b9505974-1b92-11ec-2f68-b3324d215555
# ╠═f811543d-b7e6-4f7a-b60c-048f1bf380bf
# ╟─acabcaa5-026d-4d20-b031-34c4025bb5a8
# ╟─2dafa118-f82f-4958-af6a-d013c01e998b
# ╟─90c40756-c1ff-4258-86a3-d746a85cafe4
# ╟─7f7e9617-44ad-423f-beec-e0b574818a61
# ╠═e79100c1-6a18-4aab-9461-a117fa1922a1
# ╠═cf404673-5418-4352-9fc2-524fd0325444
# ╠═88db7212-49d1-4944-bdbd-f783b3119aba
# ╠═16d863f7-8157-4c7d-a5fc-e4a3c905ed69
# ╟─82bf15a9-2bb0-4776-8bf0-c3ad9030ed04
# ╠═6ead28bf-a2e3-43f7-8962-3f0f4e6ce4ca
# ╠═25f57f53-a366-420b-a7de-b0a62c96bd57
# ╠═1d25bd6a-2df3-485f-ab6c-8cc0298ddf54
# ╠═8c434bb4-64ae-4232-bad0-36419d010ff2
# ╠═7b0542ab-38c0-4255-ba56-1a2eb0d6a390
# ╠═21d17028-ffa4-4827-a1b1-5a1b77cbe714
# ╟─ed771cc5-f20a-41dc-82a2-390dba65ebab
# ╠═aa38d4e8-5a55-42d3-9cfe-85c1b6f59f78
# ╠═36a19aa4-f282-4d00-895d-a923439a2c3f
# ╟─00000000-0000-0000-0000-000000000001
# ╟─00000000-0000-0000-0000-000000000002

max_stars_repo_name: mathematiguy/STMO

max_issues_repo_name: mathematiguy/STMO

max_forks_repo_name: mathematiguy/STMO


text:
using Parameters
using AutoHashEquals
using ..Patterns

import Base.==

abstract type AbstractRule end
# Must override
==(a::AbstractRule, b::AbstractRule) = false

abstract type SymbolicRule <: AbstractRule end

"""
Rules defined as `left_hand => right_hand` are
called *symbolic rewrite* rules. Application of a *rewrite* Rule
is a replacement of the `left_hand` pattern with
the `right_hand` substitution, with the correct instantiation
of pattern variables. Function call symbols are not treated as pattern
variables, all other identifiers are treated as pattern variables.
Literals such as `5, :e, "hello"` are not treated as pattern
variables.

```julia
Rule(:(a * b => b * a))
```
"""
@auto_hash_equals struct RewriteRule <: SymbolicRule
    left::Pattern
    right::Pattern
    patvars::Vector{Symbol}
    ematch_program::Program
    function RewriteRule(l,r)
        pvars = patvars(l) ∪ patvars(r)
        # sort!(pvars)
        setindex!(l, pvars)
        setindex!(r, pvars)
        new(l,r,pvars, compile_pat(l))
    end
end

function Base.show(io::IO,  r::RewriteRule)
    print(io, "$(r.left) => $(r.right)")
end

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

abstract type BidirRule <: SymbolicRule end

"""
This type of *anti*-rules is used for checking contradictions in the EGraph
backend. If two terms, corresponding to the left and right hand side of an
*anti-rule* are found in an [`EGraph`], saturation is halted immediately.
"""
@auto_hash_equals struct UnequalRule <: BidirRule
    left::Pattern
    right::Pattern
    patvars::Vector{Symbol}
    ematch_program_l::Program
    ematch_program_r::Program
    function UnequalRule(l,r)
        pvars = patvars(l) ∪ patvars(r)
        extravars = setdiff(pvars, patvars(l) ∩ patvars(r))
        if !isempty(extravars)
            error("unbound pattern variables $extravars when creating bidirectional rule")
        end
        # sort!(pvars)
        setindex!(l, pvars)
        setindex!(r, pvars)
        progl = compile_pat(l)
        progr = compile_pat(r)
        new(l,r,pvars, progl, progr)
    end
end

function Base.show(io::IO, r::UnequalRule)
    print(io, "$(r.left) ≠ $(r.right)")
end

"""
```julia
Rule(:(a * b == b * a))
```
"""
@auto_hash_equals struct EqualityRule <: BidirRule
    left::Pattern
    right::Pattern
    patvars::Vector{Symbol}
    ematch_program_l::Program
    ematch_program_r::Program
    function EqualityRule(l,r)
        pvars = patvars(l) ∪ patvars(r)
        extravars = setdiff(pvars, patvars(l) ∩ patvars(r))
        if !isempty(extravars)
            error("unbound pattern variables $extravars when creating bidirectional rule")
        end
        # sort!(pvars)
        setindex!(l, pvars)
        setindex!(r, pvars)
        progl = compile_pat(l)
        progr = compile_pat(r)
        new(l,r,pvars, progl, progr)
    end
end

function Base.show(io::IO,  r::EqualityRule)
    print(io, "$(r.left) == $(r.right)")
end

# TODO document the additional parameters
"""
Rules defined as `left_hand |> right_hand` are
called `dynamic` rules. Dynamic rules behave like anonymous functions.
Instead of a symbolic substitution, the right hand of
a dynamic `|>` rule is evaluated during rewriting:
matched values are bound to pattern variables as in a
regular function call. This allows for dynamic computation
of right hand sides.

Dynamic rule
```julia
Rule(:(a::Number * b::Number |> a*b))
```
"""
@auto_hash_equals struct DynamicRule <: AbstractRule
    left::Pattern
    right::Any
    patvars::Vector{Symbol} # useful set of pattern variables
    ematch_program::Program
    mod::Module
    rhs_fun::Function
    function DynamicRule(l, r, mod)
        pvars = patvars(l)
        # sort!(pvars)
        setindex!(l, pvars)

        params = Expr(:tuple, :_lhs_expr, :_subst, :_egraph, pvars...)
        ex = :($params -> $r)
        f = closure_generator(mod, ex)

        new(l, r, pvars, compile_pat(l), mod, f)
    end
end

function DynamicRule(l, r; m=@__MODULE__)
    DynamicRule(l,r,m)
end

function Base.show(io::IO, r::DynamicRule)
    print(io, "$(r.left) |> $(r.right)")
end

ext: jl


text:
module WiringDiagrams

using Reexport

include("Core.jl")
include("Layers.jl")
include("Algorithms.jl")
include("Expressions.jl")
include("Serialization.jl")
include("GraphML.jl")
include("JSON.jl")
include("YFiles.jl")

@reexport using .WiringDiagramCore
@reexport using .WiringLayers
@reexport using .WiringDiagramAlgorithms
@reexport using .WiringDiagramExpressions

using .WiringDiagramSerialization
export convert_from_graph_data, convert_to_graph_data

@reexport using .GraphMLWiringDiagrams
@reexport using .JSONWiringDiagrams
@reexport using .YFilesWiringDiagrams

end


text:
@testset "Measurements" begin
    @info "Testing Measurements"
    import Measurements

    m = Measurements.measurement(1,2)
    @test Particles(m) isa Particles{Float64, MonteCarloMeasurements.DEFAULT_NUM_PARTICLES}
    @test StaticParticles(m) isa StaticParticles{Float64, MonteCarloMeasurements.DEFAULT_STATIC_NUM_PARTICLES}

    @test Particles(10, m) isa Particles{Float64, 10}
    @test StaticParticles(10, m) isa StaticParticles{Float64, 10}

    @test Particles.([m, m]) isa Vector{Particles{Float64, MonteCarloMeasurements.DEFAULT_NUM_PARTICLES}}
    @test StaticParticles.([m, m]) isa Vector{StaticParticles{Float64, MonteCarloMeasurements.DEFAULT_STATIC_NUM_PARTICLES}}

    @test Particles(m) ≈ 1 ± 2
    @test pstd(Particles(m)) ≈ 2 atol=1e-3
    @test pmean(Particles(m)) ≈ 1 atol=1e-3

    @test Measurements.uncertainty(Particles(m)) ≈ 2 atol=1e-3
    @test Measurements.value(Particles(m)) ≈ 1 atol=1e-3

end


text:
struct ImGuiViewportDataGlfw
    Window::Ptr{GLFWwindow}
    WindowOwned::Bool
    IgnoreWindowPosEventFrame::Cint
    IgnoreWindowSizeEventFrame::Cint
end

function Base.getproperty(x::Ptr{ImGuiViewportDataGlfw}, f::Symbol)
    f === :Window && return Ptr{Ptr{GLFWwindow}}(x + fieldoffset(ImGuiViewportDataGlfw,1))
    f === :WindowOwned && return Ptr{Bool}(x + fieldoffset(ImGuiViewportDataGlfw,2))
    f === :IgnoreWindowPosEventFrame && return Ptr{Cint}(x + fieldoffset(ImGuiViewportDataGlfw,3))
    f === :IgnoreWindowSizeEventFrame && return Ptr{Cint}(x + fieldoffset(ImGuiViewportDataGlfw,4))
    return getfield(x, f)
end

function Base.setproperty!(x::Ptr{ImGuiViewportDataGlfw}, f::Symbol, v)
    unsafe_store!(getproperty(x, f), v)
end

function ImGui_ImplGlfw_CreateWindow(viewport::Ptr{ImGuiViewport})
    io::Ptr{ImGuiIO} = igGetIO()
    ctx = unsafe_pointer_to_objref(unsafe_load(io.UserData))

    data::Ptr{ImGuiViewportDataGlfw} = Libc.malloc(sizeof(ImGuiViewportDataGlfw))
    data.Window = C_NULL
    data.WindowOwned = true
    data.IgnoreWindowPosEventFrame = -1
    data.IgnoreWindowSizeEventFrame = -1
    viewport.PlatformUserData = data

    glfwWindowHint(GLFW_VISIBLE, false)
    glfwWindowHint(GLFW_FOCUSED, false)
    glfwWindowHint(GLFW_FOCUS_ON_SHOW, false)
    glfwWindowHint(GLFW_DECORATED, !(unsafe_load(viewport.Flags) & ImGuiViewportFlags_NoDecoration == ImGuiViewportFlags_NoDecoration))
    glfwWindowHint(GLFW_FLOATING, unsafe_load(viewport.Flags) & ImGuiViewportFlags_TopMost == ImGuiViewportFlags_TopMost)

    share_window = ctx.ClientApi == GlfwClientApi_OpenGL ? ctx.Window : C_NULL
    wx, wy = trunc(Cint, unsafe_load(viewport.Size.x)), trunc(Cint, unsafe_load(viewport.Size.y))
    data_Window = glfwCreateWindow(wx, wy, "No Title Yet", C_NULL, share_window)
    viewport.PlatformHandle = data_Window
    if Sys.iswindows()
        viewport.PlatformHandleRaw = ccall((:glfwGetWin32Window, libglfw), Ptr{Cvoid}, (Ptr{GLFWwindow},), data_Window)
    end
    data.Window = data_Window

    glfwSetWindowPos(data_Window, trunc(Cint, unsafe_load(viewport.Pos.x)), trunc(Cint, unsafe_load(viewport.Pos.y)))

    # install window userdata
    glfwSetWindowUserPointer(data_Window, unsafe_load(io.UserData))

    # install callbacks
    glfwSetMouseButtonCallback(data_Window, @cfunction(ImGui_ImplGlfw_MouseButtonCallback, Cvoid, (Ptr{GLFWwindow}, Cint, Cint, Cint)))
    glfwSetScrollCallback(data_Window, @cfunction(ImGui_ImplGlfw_ScrollCallback, Cvoid, (Ptr{GLFWwindow}, Cdouble, Cdouble)))
    glfwSetKeyCallback(data_Window, @cfunction(ImGui_ImplGlfw_KeyCallback, Cvoid, (Ptr{GLFWwindow}, Cint, Cint, Cint, Cint)))
    glfwSetCharCallback(data_Window, @cfunction(ImGui_ImplGlfw_CharCallback, Cvoid, (Ptr{GLFWwindow}, Cuint)))
    glfwSetWindowCloseCallback(data_Window, @cfunction(ImGui_ImplGlfw_WindowCloseCallback, Cvoid, (Ptr{GLFWwindow},)))
    glfwSetWindowPosCallback(data_Window, @cfunction(ImGui_ImplGlfw_WindowPosCallback, Cvoid, (Ptr{GLFWwindow}, Cint, Cint)))
    glfwSetWindowSizeCallback(data_Window, @cfunction(ImGui_ImplGlfw_WindowSizeCallback, Cvoid, (Ptr{GLFWwindow}, Cint, Cint)))
    if ctx.ClientApi == GlfwClientApi_OpenGL
        glfwMakeContextCurrent(data_Window)
        glfwSwapInterval(0)
    end

    return nothing
end

function ImGui_ImplGlfw_DestroyWindow(viewport::Ptr{ImGuiViewport})
    io::Ptr{ImGuiIO} = igGetIO()
    ctx = unsafe_pointer_to_objref(unsafe_load(io.UserData))

    data::Ptr{ImGuiViewportDataGlfw} = unsafe_load(viewport.PlatformUserData)
    if data != C_NULL
        if unsafe_load(data.WindowOwned)
            # release any keys that were pressed in the window being destroyed and are still held down,
            # because we will not receive any release events after window is destroyed.
            win = Ptr{GLFWwindow}(unsafe_load(data.Window))
            for i = 0:length(ctx.KeyOwnerWindows)-1
                if ctx.KeyOwnerWindows[i+1] == win
                    ImGui_ImplGlfw_KeyCallback(win, i, 0, GLFW_RELEASE, 0)
                end
            end
            glfwDestroyWindow(win)
        end
        data.Window = C_NULL
        Libc.free(data)
    end

    viewport.PlatformUserData = C_NULL
    viewport.PlatformHandle = C_NULL

    return nothing
end

function ImGui_ImplGlfw_ShowWindow(viewport::Ptr{ImGuiViewport})
    data::Ptr{ImGuiViewportDataGlfw} = unsafe_load(viewport.PlatformUserData)

    if Sys.iswindows()
        # TODO: impl
    end

    glfwShowWindow(unsafe_load(data.Window))

    return nothing
end

function ImGui_ImplGlfw_GetWindowPos(viewport::Ptr{ImGuiViewport})
    data::Ptr{ImGuiViewportDataGlfw} = unsafe_load(viewport.PlatformUserData)
    x, y = Cint(0), Cint(0)
    @c glfwGetWindowPos(unsafe_load(data.Window), &x, &y)
    return ImVec2(Cfloat(x), Cfloat(y))
end

function ImGui_ImplGlfw_SetWindowPos(viewport::Ptr{ImGuiViewport}, pos::ImVec2)
    data::Ptr{ImGuiViewportDataGlfw} = unsafe_load(viewport.PlatformUserData)
    data.IgnoreWindowPosEventFrame = igGetFrameCount()
    x, y = trunc(Cint, pos.x), trunc(Cint, pos.y)
    glfwSetWindowPos(unsafe_load(data.Window), x, y)
    return nothing
end

function ImGui_ImplGlfw_GetWindowSize(viewport::Ptr{ImGuiViewport})
    data::Ptr{ImGuiViewportDataGlfw} = unsafe_load(viewport.PlatformUserData)
    w, h = Cint(0), Cint(0)
    @c glfwGetWindowSize(unsafe_load(data.Window), &w, &h)
    return ImVec2(Cfloat(w), Cfloat(h))
end

function ImGui_ImplGlfw_SetWindowSize(viewport::Ptr{ImGuiViewport}, size::ImVec2)
    data::Ptr{ImGuiViewportDataGlfw} = unsafe_load(viewport.PlatformUserData)
    data.IgnoreWindowSizeEventFrame = igGetFrameCount()
    x, y = trunc(Cint, size.x), trunc(Cint, size.y)
    glfwSetWindowSize(unsafe_load(data.Window), x, y)
    return nothing
end

function ImGui_ImplGlfw_SetWindowTitle(viewport::Ptr{ImGuiViewport}, title::Ptr{Cchar})
    data::Ptr{ImGuiViewportDataGlfw} = unsafe_load(viewport.PlatformUserData)
    glfwSetWindowTitle(unsafe_load(data.Window), title)
    return nothing
end

function ImGui_ImplGlfw_SetWindowFocus(viewport::Ptr{ImGuiViewport})
    data::Ptr{ImGuiViewportDataGlfw} = unsafe_load(viewport.PlatformUserData)
    glfwFocusWindow(unsafe_load(data.Window))
    return nothing
end

function ImGui_ImplGlfw_GetWindowFocus(viewport::Ptr{ImGuiViewport})::Bool
    data::Ptr{ImGuiViewportDataGlfw} = unsafe_load(viewport.PlatformUserData)
    return glfwGetWindowAttrib(unsafe_load(data.Window), GLFW_FOCUSED) != 0
end

function ImGui_ImplGlfw_GetWindowMinimized(viewport::Ptr{ImGuiViewport})::Bool
    data::Ptr{ImGuiViewportDataGlfw} = unsafe_load(viewport.PlatformUserData)
    return glfwGetWindowAttrib(unsafe_load(data.Window), GLFW_ICONIFIED) != 0
end

function ImGui_ImplGlfw_SetWindowAlpha(viewport::Ptr{ImGuiViewport}, alpha::Cfloat)
    data::Ptr{ImGuiViewportDataGlfw} = unsafe_load(viewport.PlatformUserData)
    glfwSetWindowOpacity(unsafe_load(data.Window), alpha)
    return nothing
end

function ImGui_ImplGlfw_RenderWindow(viewport::Ptr{ImGuiViewport}, userdata::Ptr{Cvoid})
    data::Ptr{ImGuiViewportDataGlfw} = unsafe_load(viewport.PlatformUserData)
    # FIXME: pass through userdata(add a new field in ImGuiViewportDataGlfw)
    io::Ptr{ImGuiIO} = igGetIO()
    ctx = unsafe_pointer_to_objref(unsafe_load(io.UserData))
    if ctx.ClientApi == GlfwClientApi_OpenGL
        glfwMakeContextCurrent(unsafe_load(data.Window))
    end
    return nothing
end

function ImGui_ImplGlfw_SwapBuffers(viewport::Ptr{ImGuiViewport}, userdata::Ptr{Cvoid})
    data::Ptr{ImGuiViewportDataGlfw} = unsafe_load(viewport.PlatformUserData)
    # FIXME: pass through userdata(add a new field in ImGuiViewportDataGlfw)
    io::Ptr{ImGuiIO} = igGetIO()
    ctx = unsafe_pointer_to_objref(unsafe_load(io.UserData))
    if ctx.ClientApi == GlfwClientApi_OpenGL
        glfwMakeContextCurrent(unsafe_load(data.Window))
        glfwSwapBuffers(unsafe_load(data.Window))
    end
    return nothing
end

function ImGui_ImplGlfw_InitPlatformInterface(ctx::Context)
    # register platform interface (will be coupled with a renderer interface)
    platform_io::Ptr{ImGuiPlatformIO} = igGetPlatformIO()
    platform_io.Platform_CreateWindow = @cfunction(ImGui_ImplGlfw_CreateWindow, Cvoid, (Ptr{ImGuiViewport},))
    platform_io.Platform_DestroyWindow = @cfunction(ImGui_ImplGlfw_DestroyWindow, Cvoid, (Ptr{ImGuiViewport},))
    platform_io.Platform_ShowWindow = @cfunction(ImGui_ImplGlfw_ShowWindow, Cvoid, (Ptr{ImGuiViewport},))
    platform_io.Platform_SetWindowPos = @cfunction(ImGui_ImplGlfw_SetWindowPos, Cvoid, (Ptr{ImGuiViewport}, ImVec2))
    platform_io.Platform_GetWindowPos = @cfunction(ImGui_ImplGlfw_GetWindowPos, ImVec2, (Ptr{ImGuiViewport},))
    platform_io.Platform_SetWindowSize = @cfunction(ImGui_ImplGlfw_SetWindowSize, Cvoid, (Ptr{ImGuiViewport}, ImVec2))
    platform_io.Platform_GetWindowSize = @cfunction(ImGui_ImplGlfw_GetWindowSize, ImVec2, (Ptr{ImGuiViewport},))
    platform_io.Platform_SetWindowFocus = @cfunction(ImGui_ImplGlfw_SetWindowFocus, Cvoid, (Ptr{ImGuiViewport},))
    platform_io.Platform_GetWindowFocus = @cfunction(ImGui_ImplGlfw_GetWindowFocus, Bool, (Ptr{ImGuiViewport},))
    platform_io.Platform_GetWindowMinimized = @cfunction(ImGui_ImplGlfw_GetWindowMinimized, Bool, (Ptr{ImGuiViewport},))
    platform_io.Platform_SetWindowTitle = @cfunction(ImGui_ImplGlfw_SetWindowTitle, Cvoid, (Ptr{ImGuiViewport}, Ptr{Cchar}))
    platform_io.Platform_RenderWindow = @cfunction(ImGui_ImplGlfw_RenderWindow, Cvoid, (Ptr{ImGuiViewport}, Ptr{Cvoid}))
    platform_io.Platform_SwapBuffers = @cfunction(ImGui_ImplGlfw_SwapBuffers, Cvoid, (Ptr{ImGuiViewport}, Ptr{Cvoid}))
    platform_io.Platform_SetWindowAlpha = @cfunction(ImGui_ImplGlfw_SetWindowAlpha, Cvoid, (Ptr{ImGuiViewport}, Cfloat))
    # TODO: support Vulkan
    # platform_io.Platform_CreateVkSurface = ImGui_ImplGlfw_CreateVkSurface;
    # TODO: support IME
    # platform_io.Platform_SetImeInputPos = ImGui_ImplWin32_SetImeInputPos;

    # register main window handle (which is owned by the main application, not by us)
    # this is mostly for simplicity and consistency, so that our code (e.g. mouse handling etc.) can use same logic for main and secondary viewports.
    main_viewport::Ptr{ImGuiViewport} = igGetMainViewport()
    data::Ptr{ImGuiViewportDataGlfw} = Libc.malloc(sizeof(ImGuiViewportDataGlfw))
    data.Window = ctx.Window
    data.WindowOwned = false
    data.IgnoreWindowPosEventFrame = -1
    data.IgnoreWindowSizeEventFrame = -1
    main_viewport.PlatformUserData = data
    main_viewport.PlatformHandle = Ptr{Cvoid}(ctx.Window)

    return nothing
end

function ImGui_ImplGlfw_ShutdownPlatformInterface(ctx::Context)
    main_viewport::Ptr{ImGuiViewport} = igGetMainViewport()
    Libc.free(unsafe_load(main_viewport.PlatformUserData))
    main_viewport.PlatformUserData = C_NULL
    return nothing
end


text:
function f(x,θ)
  α,β,γ,δ = θ
  x1,x2=x
   dx1 = α*x1 - β*x1*x2
   dx2 = δ*x1*x2 - γ*x2
  [dx1,dx2]
end

function solve(f,x0::AbstractVector,θ,dt,N)
  X = hcat([zero(x0) for i=1:N]...)
  X[:,1]=x0
  for t=1:N-1
     X[:,t+1]=X[:,t]+dt*f(X[:,t],θ)
  end
  X
end

θ0 = [0.1,0.2,0.3,0.2]
x0 = [1.0,1.0]
dt = 0.1
N  = 1000
X=solve(f,x0,θ0,dt,N)

using Plots

p=plot(X[1,:],xlabel="t",label="x",color=:blue)
p=plot!(X[2,:],xlabel="t",label="y",color=:red)

K=100
X0 = [x0 .+ 0.1*randn(2) for k=1:K]
θ1 =[[θ0[1]+0.01randn();θ0[2:end]] for k=1:K]
Xens=[X=solve(f,X0[i],θ1[i],dt,N) for i=1:K]

p=plot(X[1,:],xlabel="t",label="x",color=:blue)
p=plot!(X[2,:],xlabel="t",label="y",color=:red)
for i=1:K
  p=plot!(Xens[i][1,:],label="",color=:blue,alpha=0.2)
  p=plot!(Xens[i][2,:],label="",color=:red,alpha=0.2)
end
savefig("LV_MC_param.svg")

ind = 100:100:1000
scatter!(p,ind,[X[1,ind]],errorbar=[0.2],label="measurements")
savefig("LV_MC_param_data.svg")

Ds=zeros(100)
Ind=[]
for i=1:100
 Ds[i] = sum((X[1,ind]-Xens[i][1,ind]).^2)
 if Ds[i]<1
  push!(Ind,i)
 end
end

p=plot(X[1,:],xlabel="t",label="x",color=:blue)
p=plot!(X[2,:],xlabel="t",label="y",color=:red)
for i=Ind
  p=plot!(Xens[i][1,:],label="",color=:blue,alpha=0.2)
  p=plot!(Xens[i][2,:],label="",color=:red,alpha=0.2)
end
scatter!(p,ind,[X[1,ind]],errorbar=[0.2],label="measurements")
savefig("LV_MC_param_assim.svg")

MXe=mean(Xens)
SXe=std(Xens)
plot(MXe[1,1:30:end],label="x",color=:blue,errorbar=SXe[1,1:30:end])
plot!(MXe[2,1:30:end],label="y",color=:red,errorbar=SXe[2,1:30:end])

using LinearAlgebra
function solve_res(f,x0::AbstractVector,sqΣ0, θ,dt,N,Nr)
   n = length(x0)
   n2 = 2*length(x0)
   Qp = sqrt(n)*[I(n) -I(n)]

  X = hcat([zero(x0) for i=1:N]...)
  S = hcat([zero(x0) for i=1:N]...)
  X[:,1]=x0
  Xp = x0 .+ sqΣ0*Qp
  sqΣ = sqΣ0
  Σ = sqΣ* sqΣ'
  S[:,1]= diag(Σ)
  for t=1:N-1
    if rem(t,Nr)==0
      Xp .= X[:,t] .+ sqΣ * Qp
    end
    for i=1:n2 # all quadrature points
      Xp[:,i].=Xp[:,i] + [dt*f(Xp[1:2,i],[Xp[3,i];θ[2:end]]);0]
    end
    mXp=mean(Xp,dims=2)
    X[:,t+1]=mXp
    Σ=Matrix((Xp.-mXp)*(Xp.-mXp)'/n2)
    S2=cov(Xp,dims=2)
    @show Σ
    @show S2
    S[:,t+1]=sqrt.(diag(Σ))
    # @show Σ

    sqΣ = cholesky(Σ).L

  end
  X,S
end

QXr,QSr=solve_res(f,[1.0,1.0,0.1],diagm([0.1,0.1,0.01]),θ0,0.1,1000,100)
plot(QXr[1,1:30:end],label="x",color=:blue,errorbar=QSr[1,1:30:end])
plot!(QXr[2,1:30:end],label="y",color=:red,errorbar=QSr[2,1:30:end])

function solve(f,x0::AbstractVector,Σ0, θ,dt,N)
   n = length(x0)
   n2 = 2*length(x0)
   Qp = sqrt(n)*[I(n) -I(n)]

  X = hcat([zero(x0) for i=1:N]...)
  S = hcat([zero(x0) for i=1:N]...)
  X[:,1]=x0
  Σ=Hermitian(Σ0)
  sqΣ = cholesky(Σ).L
  Xp = x0 .+ sqΣ*Qp
  S[:,1]= diag(Σ)
  for t=1:N-1
    # if rem(t,Nr)==0
    #   Xp .= X[:,t] .+ sqΣ * Qp
    # end
    for i=1:n2 # all quadrature points
      Xp[:,i].=Xp[:,i] + [dt*f(Xp[1:2,i],[Xp[3,i];θ[2:end]]);0]
    end
    mXp=mean(Xp,dims=2)
    X[:,t+1]=mXp
    Σ=cov(Xp,dims=2)
    S[:,t+1]=sqrt.(diag(Σ))
    # @show Σ
  end
  X,S,Xp
end

## Extension to arbitrary

QX,QS=solve(f,[1.0,1.0,0.1],diagm([0.1,0.1,0.01].^2),θ0,0.1,1000)
plot(QX[1,1:30:end],label="x",color=:blue,errorbar=QS[1,1:30:end])
plot!(QX[2,1:30:end],label="y",color=:red,errorbar=QS[2,1:30:end])

savefig("LV_Quadrics.svg")

function filter(f,x0::AbstractVector,Σ0, θ,dt,Ne,Y,σy)
  XX=[]
  SS=[]
  Σ=Σ0
  x=x0
  for t=1:length(Y)
    @show x
    @show diag(Σ)
    Xt,St,Xp=solve(f,x,Σ,θ,dt,Ne) # prediction
    if false
      @show Xp
      Yp = Xp[1,:] # measure only the first variable
      @show Yp
      mYp = mean(Yp)
      mXp = mean(Xp,dims=2)
      SYp = cov(Yp)+σy
      @show SYp
      C = mean([(Xp[:,i].-mXp)*(Yp[i].-mYp) for i=1:6])
      @show C
      G = C*inv(SYp)
      @show G
      x = vec(Xt[:,end] + G*(Y[t]-mYp))
      Σ = Matrix((Xp.-mXp)*(Xp.-mXp)'/n2) - G*SYp*G'

      # Σ = cov(Xp,dims=2) - G*SYp*G'
    else
        x = vec(mean(Xp,dims=2))
        Σ = Matrix((Xp.-x)*(Xp.-x)'/6.0)

    end

    push!(XX,Xt)
    push!(SS,St)
  end
  XX,SS
end

Y = X[1,100:100:end]
Xh,Sh=filter(f,[1.0,1.0,0.1],diagm([0.1,0.1,0.01].^2),θ0,0.1,100,Y[1:end],1e8)
XF=hcat(Xh...)
SF=hcat(Sh...)

step=1
plot([1:step:size(XF,2)],XF[1,1:step:end],label="x",color=:blue,errorbar=SF[1,1:step:end])
plot!([1:step:size(XF,2)],XF[2,1:step:end],label="y",color=:red,errorbar=SF[2,1:step:end])
scatter!([100:100:size(XF,2)],Y,label="measured",marker=:xcross)


text:
# Abstract representation of flexible demand.

"""
Demands that have flexibility.

# Type parameters
- `T <: TimeType`: timestamp
- `L`            : network location
"""
abstract type FlexibleDemand{T,L} <: Demand{T,L} end


text:
function coreVIfixedcov(logl::Function, μ::Array{Float64,1}, fixedC::Matrix{Float64}; gradlogl = gradlogl, seed = 1, S = 100, optimiser=Optim.LBFGS(), iterations = 1, numerical_verification = false, Stest=0, show_every=-1, inititerations=0, adaptvariance=1)

    D = length(μ)

    @assert(D == size(fixedC, 1) == size(fixedC, 2))

    @printf("Running VI with fixed covariance with S=%d, D=%d for %d iterations\n", S, D, iterations)

    #----------------------------------------------------
    # generate latent variables
    #----------------------------------------------------

    Ztrain = generatelatentZ(S = S, D = D, seed = seed)

    Ztest  = generatelatentZ(S = Stest, D = D, seed = seed+1)

    #----------------------------------------------------
    function unpack(param)
    #----------------------------------------------------

        @assert(length(param) == D)

        local μ = param[1:D]

        return μ

    end

    #----------------------------------------------------
    function minauxiliary(param)
    #----------------------------------------------------

        local μ = unpack(param)

        return -1.0 * elbo(μ, Ztrain)

    end

    #----------------------------------------------------
    function minauxiliary_grad(param)
    #----------------------------------------------------

        local μ = unpack(param)

        return -1.0 * elbo_grad(μ, Ztrain)

    end

    #----------------------------------------------------
    function elbo(μ, Z)
    #----------------------------------------------------

        mean(map(z -> logl(μ .+ fixedC*z), Z)) + ApproximateVI.entropy(fixedC)

    end

    #----------------------------------------------------
    function elbo_grad(μ, Z)
    #----------------------------------------------------

        local gradμ = zeros(eltype(μ), D)

        local S     = length(Z)

        for s=1:S
            g      = gradlogl(μ .+ fixedC * Z[s])
            gradμ += g / S
        end

        return gradμ

    end

    gradhelper(storage, param) = copyto!(storage, minauxiliary_grad(param))

    #----------------------------------------------------
    # Numerically verify gradient
    #----------------------------------------------------

    if numerical_verification

        adgrad = ForwardDiff.gradient(minauxiliary, μ)
        angrad = minauxiliary_grad(μ)
        @printf("gradient from AD vs analytical gradient\n")
        display([vec(adgrad) vec(angrad)])
        @printf("maximum absolute difference is %f\n", maximum(abs.(vec(adgrad) - vec(angrad))))

    end

    #----------------------------------------------------
    # Call optimiser
    #----------------------------------------------------

    options = Optim.Options(extended_trace = false, store_trace = false, show_trace = true, show_every=show_every, iterations = iterations, g_tol = 1e-6)

    result  = Optim.optimize(minauxiliary, gradhelper, μ, optimiser, options)

    μopt = unpack(result.minimizer)

    #----------------------------------------------------
    # Return results
    #----------------------------------------------------

    return MvNormal(μopt, fixedC*fixedC'), elbo(μopt, generatelatentZ(S = 10*S, D = D, seed = seed+2))

end


text:
function constructor(new_sdef, sdef)
    defs = []
    sname = sdef.name
    if is_handle_with_multiple_create_info(sname)
        ci_indices = findall(x -> x.name == sdef.name, eachrow(vulkan_creation_info))
        for index ∈ ci_indices
            ci = vulkan_creation_info[index, :]
            fun, type, id = (ci.create_function, first(ci.create_info_structs), first(ci.create_info_identifiers))
            cons = constructor(new_sdef, sdef, CreateVkHandle(), fun, type, id; add_create_info_type_annotation=true, is_inner_constructor=false)
            !isnothing(cons) && push!(defs, cons)
            cons = constructor(new_sdef, sdef, CreateVkHandle(), fun, type, id; add_create_info_type_annotation=true, is_inner_constructor=false, add_fun_ptr=true)
            !isnothing(cons) && push!(defs, cons)
        end
    elseif is_handle_with_create_info(sname)
        # new_sdef.inner_constructor = constructor(new_sdef, sdef, CreateVkHandle())
        push!(defs, constructor(new_sdef, sdef, CreateVkHandle()))
        push!(defs, constructor(new_sdef, sdef, CreateVkHandle(), add_fun_ptr=true))
        # push!(defs, constructor(new_sdef, sdef, CreateVkHandleWithCreateInfo()))
    elseif !is_handle(sname) && dfmatch(vulkan_structs, :name, sname).returnedonly
        vk_sig = Signature(sdef)
        args_undropped = [name for (name, type) ∈ zip(argnames(vk_sig), argtypes(vk_sig)) if !drop_field(name, type, sname)]
        body = [Statement("$(new_sdef.name)($(join_args("from_vk(" .* argtypes(Signature(new_sdef)) .* ", vks." .* args_undropped .* ")")))")]
        fdef = FDefinition(new_sdef.name, Signature(new_sdef.name, [PositionalArgument("vks", sname)], KeywordArgument[]), true, body)
        push!(defs, fdef)
        push!(defs, FDefinition("from_vk", Signature("from_vk", PositionalArgument.(["T::Type{$(new_sdef.name)}", "x::$sname"]), KeywordArgument[]), true, [Statement("$(new_sdef.name)(x)")]))
    elseif !is_handle(sname)
        if keeps_original_layout(sdef)
            push!(defs, constructor(new_sdef, sdef, GenericConstructor(), is_inner_constructor=false, add_type_annotations=false))
            push!(defs, FDefinition("from_vk", Signature("from_vk", PositionalArgument.(["T::Type{$(new_sdef.name)}", "x::$sname"]), KeywordArgument[]), true, [Statement("$(new_sdef.name)($(join_args("x." .* collect(keys(sdef.fields)))))")]))
            if length(Signature(new_sdef).args) == 1
                new_sdef.inner_constructor = FDefinition(new_sdef.name, Signature(new_sdef.name, [PositionalArgument("vks", sname)], KeywordArgument[]), true, [Statement("new(vks)")])
            end
        else
            new_sdef.inner_constructor = constructor(new_sdef, sdef, GenericConstructor(), is_inner_constructor=true)
        end
        # push!(defs, conversion(new_sdef, sdef, ConvertVkStructure()))
        # for dep ∈ vk_dependencies(sname)
        #     if !is_enumerated_property(dep)
        #         push!(defs, conversion(structure(api.structs[dep]), api.structs[dep], ConvertVkStructure()))
        #         push!(defs, constructor(new_sdef, sdef, GenericConstructor()))
        #     end
        # end
    end
    defs
end

function wrap_constructor!(w_api, new_sdef, sdef)
    defs = constructor(new_sdef, sdef)
    for (i, def) ∈ enumerate(defs)
        w_api.funcs[sdef.name * "_$i"] = def
    end
    if !is_handle(sdef.name)
        vk_extended_constructor = constructor(sdef, ExtendVkConstructor())
        length(vk_extended_constructor.signature.args) ≠ length(sdef.fields) && setindex!(w_api.extended_vk_constructors, vk_extended_constructor, sdef.name)
    end
end

"""
Pass type used to build the body of a function.

For each argument of a function, passes are layed out on top of one another to construct statements, that will define a major part of the function body. All passes receive a common set of parameters bundled in a PassArgs argument.

Passes that are triggered upon a field yield one or more statements.
"""
abstract type Pass end

"""
Translate wrapped types to Vulkan types. Triggers on Strings (converted to Cstrings) and Arrays (converted to their Ptr representation).
"""
struct TranslateVkTypesBack <: Pass end

"""
Compute the length of an array (for example, exposed in a wrapped type), which is required for *Count variables.
"""
struct ComputeLengthArgument <: Pass end

"""
Convert arrays to their expected type. For example, converts an Array{Int64} to an Array{UInt32}.
"""
struct ConvertArrays <: Pass end

"""
Translate Vulkan types into wrapped types. For example, takes a Cstring or a NTuple{256, UInt8} and converts it back to a String; or takes a Ptr{T} which encodes an array and converts it back to an array, to be safely stored inside a wrapped struct.
"""
struct TranslateVkTypes <: Pass end

"""
Handle the integration of pNext dependencies. Fill the `extra_bag` field of the struct to be created with either `next.bag` or `EmptyBag` (if `C_NULL`)
"""
struct HandlePNextDeps <: Pass end

"""
Generate a new variable with either a Ref or `C_NULL` for each pointer argument.
"""
struct GenerateRefs <: Pass end

"""
Generate a pointer for any variable beginning with p[A-Z].
"""
struct GeneratePointers <: Pass end

"""
Define a pointer that is to be passed to a Vulkan function for filling it with data.
"""
struct InitializePointers <: Pass end

function has_bag(sname)
    !is_handle(sname) && is_vulkan_struct(sname) && any(map(x -> occursin("Cstring", x) || (is_ptr(x) && !is_extension_ptr(x)), collect(values(api.structs[sname].fields))))
end

function bagtype(sname)
    return "Bag" * name_transform(sname, SDefinition)
end

function steal_bag!(bag_args, name, type, new_name, new_type, last_name, sname)
    conditional_statement = is_optional_parameter(name, sname) ? "$new_name == C_NULL ? EmptyBag : " : ""

    if has_bag(type)
        push!(bag_args, conditional_statement * new_name * ".bag")
    elseif is_ptr(type) && has_bag(inner_type(type))
        if is_array_variable(name, sname) || is_array_type(new_name)
            push!(bag_args, conditional_statement * "getproperty.($new_name, :bag)")
        else
            push!(bag_args, conditional_statement * new_name * ".bag")
        end
    end
end

function instantiate_bag(sdef, body)
    !has_bag(sdef.name) && error("Type $(sdef.name) does not have a bag to instantiate.")
    bag_args = String[]
    sname = sdef.name
    if "pNext" ∈ keys(sdef.fields)
        push!(bag_args, "bag_next")
    end
    for (name, type) ∈ sdef.fields
        new_name, new_type = field_transform(name, type, sdef.name)
        tmp_name = tmp_argname(name, type)
        last_name = last_argname(body, tmp_name, new_name)
        if is_array_type(new_type) && inner_type(new_type) == "String"
            push!(bag_args, new_name * "_ptrarray")
        else
            steal_bag!(bag_args, name, type, new_name, new_type, last_name, sname)
        end
        if type == "Cstring" || is_ptr(type)
            push!(bag_args, last_name)
        end
    end
    bag = bagtype(sdef.name)
    Statement("bag = $bag($(join_args(bag_args)))", "bag")
end

# @preprocess_pass struct AutomateCreateInfo <: Pass
#     create_info_sdef
#     create_info_new_sdef
#     pass!
# end

"""
Check whether an argument matches by name a pointer to the type defined in `new_sname`, and returns a default Ref of the corresponding type.
"""
@preprocess_pass struct DefineSelfPointers <: Pass
    new_sname
    pass!
end

# @preprocess_pass struct AddDefaults <: Pass
#     opt_params
#     pass!
# end

"""
Encode possible arguments available for each Pass.
"""
Base.@kwdef mutable struct PassArgs
    name = nothing
    type = nothing
    new_name = nothing
    new_type = nothing
    sdef = nothing
    fdef = nothing
    arg = nothing
    last_name = nothing
    tmp_name = nothing
    passes = nothing
    sname = nothing
end

"""
Store the result of a Pass, recording its triggered state and the PassArgs that was passed to it.
"""
struct PassResult{T <: Pass}
    pass::T
    pass_args::PassArgs
    is_triggered::Bool
end

Base.broadcastable(x::PassResult) = Ref(x)
Base.broadcastable(x::PassArgs) = Ref(x)

PassResult(pass::Pass, pass_args::PassArgs) = PassResult(pass, pass_args, is_triggered(typeof(pass), pass_args))

"""
Return a Vulkan name if assigned from any statement in the body, else take the Julian name

For example, if a statement assigns the name pAllocator from the Julian name allocator, as in 'pAllocator = allocator.vks', then return pAllocator; else, return allocator.
"""
function last_argname(body, id, fallback)
    id_list = getproperty.(body, :assigned_id)
    id ∈ id_list ? id : fallback
end

function last_argname(body, ids::Tuple, fallback)
    id_list = getproperty.(body, :assigned_id)
    for id ∈ ids
        id ∈ id_list && return id
    end
    fallback
end

abstract type ConstructorDefinition end
struct GenericConstructor <: ConstructorDefinition end

"""
Extend the original Vulkan type by adding a constructor with Refs, Strings and arrays instead of pointers and converting them to the expected type. It was added to improve performance, since some keyword arguments (notably, pointers with a default value of `C_NULL`) do not have a stable type.
"""
struct ExtendVkConstructor <: ConstructorDefinition end

"""
It is assumed that the Vk*CreateInfos are provided and that only vkCreate* and vkDestroy* are required in order to build the struct. Therefore, it mainly wraps the vkCreate* function and adds the corresponding vkDestroy* finalizer.
"""
struct CreateVkHandle <: ConstructorDefinition end

"""
Transform only names from the original constructor.
"""
struct ConstructorNoVKS <: ConstructorDefinition end

"""
It add a constructor which exposes the parameters of Vk*CreateInfo fields before calling the handle's inner constructor.
"""
struct CreateVkHandleWithCreateInfo <: ConstructorDefinition end

abstract type ConversionDefinition end

"""
Convert a Vk* struct to its corresponding wrapped struct. It is particularly useful for structs that are returned from a Vulkan function (such as vkEnumeratePhysicalDeviceProperties => VkPhysicalDeviceProperties).
"""
struct ConvertVkStructure <: ConversionDefinition end

preprocess_pass(pass_type::Type{T}; kwargs...) where {T <: Pass} = (pass_args, T) -> pass!(pass_args, T; kwargs...)

function inline_getproperty(type)
    # (name == "sType" || is_enum(type) || is_bitmask(type) || is_base_type(type) || is_base_type(converted_type(fieldtype_transform(name, type, sname))) || is_optional_parameter(name, sname)) && return ""
    is_handle(type) && return ".handle"
    is_vulkan_struct(type) && return ".vks"
    ""
end

"""
Get initialization arguments from the results of `accumulate_passes`.
"""
function init_args(pass_results, body, ::ExtendVkConstructor; use_all_args=true, take_property=true, include_stype=true, name_depth=2)
    args = String[]
    for (name, pr_list) ∈ pass_results
        pass_args = last(pr_list).pass_args
        @unpack tmp_name, new_name, type, new_type, sname = pass_args
        if name == "sType" && include_stype
            push!(args, stypes[sname])
        elseif (!any(values(pass_args.passes)) || use_all_args)
            push!(args, last_argname(body, name_hierarchy(name_depth, name, tmp_name, new_name)...) * (take_property ? inline_getproperty(type) : ""))
        end
    end
    args
end

"""
Get initialization arguments from the results of `accumulate_passes`.
"""
function init_args(pass_results, body, ::GenericConstructor; use_all_args=true, take_property=true, include_stype=true, name_depth=2, sname=nothing)
    args = String[]
    for (name, pr_list) ∈ pass_results
        pass_args = last(pr_list).pass_args
        @unpack tmp_name, new_name, type, new_type, sname = pass_args
        if name == "sType" && include_stype
            push!(args, stypes[sname])
        elseif is_ptr(type) && inner_type(type) == "Cstring"
            push!(args, new_name * "_ptrarray")
        else (!any(values(pass_args.passes)) || use_all_args)
            last_name = last_argname(body, name_hierarchy(name_depth, name, tmp_name, new_name)...)
            if !isnothing(sname) && is_optional_parameter(name, sname)
                default_val = default(name, type)
                name_with_test = "$last_name == $default_val ? $default_val : "
            else
                name_with_test = ""
            end
            first_part = name_with_test * last_name
            push!(args, take_property ? first_part * inline_getproperty(type) : first_part)
        end
    end
    args
end

function name_hierarchy(name_depth, names...)
    length(names) == 1 && return (), names[1]
    @assert name_depth < length(names)
    names[1:name_depth], names[name_depth + 1]
end

is_enabled(::Type{T}, args::PassArgs) where {T <: Pass} = any(T ∈ keys(args.passes))
is_triggered(::Type{T}, args::PassArgs) where {T <: Pass} = is_enabled(T, args) && args.passes[T]

function check_is_default(name, type, action)
    if is_ptr(type)
        predicate = "$name == C_NULL"
    else
        predicate = "$name == 0"
    end
    predicate * " ? $name : $action"
end

pass_new_nametype(::Type{SDefinition}) = (name, type, sname) -> (fieldname_transform(name, type), fieldtype_transform(name, type, sname))

function type_annotate_argument(arg)
    new_type = fieldtype_transform(arg.name, arg.type)
    is_vulkan_struct(arg.type) && return PositionalArgument(arg.name, new_type)
    PositionalArgument(arg.name, "")
end

function constructor(new_sdef, sdef, definition::GenericConstructor; add_type_annotations=false, is_inner_constructor=true)
    vk_sig = Signature(sdef)
    sname = sdef.name
    args, kwargs = arguments(vk_sig), keyword_arguments(vk_sig, expose_create_info_kwargs=false)
    # @info "$(rpad(sdef.name * " ", 60, '\u2015'))" * "→ " * (isempty(kwargs) ? "" : join(kwargs))
    co_sig = Signature(new_sdef.name, add_type_annotations ? type_annotate_argument.(arguments(vk_sig, drop_type=false)) : args, kwargs)
    default_sig = Signature(new_sdef)
    sig_notype = Signature(new_sdef.name, remove_type.(default_sig.args), [])
    args_for_passes = arguments(vk_sig, transform_name=false, transform_type=false, drop_type=false, remove_parameters=false)
    body, pass_results = accumulate_passes(sname, args_for_passes, pass_new_nametype(SDefinition), [ConvertArrays(), GenerateRefs(), HandlePNextDeps()])
    has_bag(sname) && push!(body, instantiate_bag(sdef, body))
    init_vk = init_args(pass_results, body, definition, use_all_args=true, include_stype=false, name_depth=2; sname)
    push!(body, Statement("vks = $sname($(join_args(init_vk)))"))
    init_struct = ["vks"]
    has_bag(sname) && push!(init_struct, "bag")
    push!(body, Statement("$(is_inner_constructor ? "new" : new_sdef.name)($(join_args(init_struct)))"))
    FDefinition(new_sdef.name, co_sig, false, body, "Generic constructor.")
end

function constructor(new_sdef, sdef, definition::ConstructorNoVKS; add_type_annotations=false, is_inner_constructor=true)
    vk_sig = Signature(sdef)
    args = arguments(vk_sig)
    kwargs = keyword_arguments(vk_sig)
    new_sig = Signature(new_sdef.name, args, kwargs)
    body = [Statement("$(is_inner_constructor ? "new" : new_sdef.name)($(join_args(argnames(new_sig))))")]
    FDefinition(new_sdef.name, new_sig, false, body, "Constructor identical to the corresponding $(sdef.name).")
end

function constructor(new_sdef, sdef, def::CreateVkHandle; add_fun_ptr=false)
    ci = dfmatch(vulkan_creation_info, :name, sdef.name)
    create_fun, create_info_struct, create_info_id = (ci.create_function, first(ci.create_info_structs), first(ci.create_info_identifiers))
    constructor(new_sdef, sdef, def, create_fun, create_info_struct, create_info_id; add_create_info_type_annotation=true, is_inner_constructor=false, add_fun_ptr)
end

function constructor(new_sdef, sdef, ::CreateVkHandle, create_fun, create_info_struct, create_info_id; is_inner_constructor=true, add_create_info_type_annotation=false, add_fun_ptr=false)
    create_fun ∉ keys(api.funcs) && (@warn("$create_fun not found. Skipping.") ; return)
    create_fun_fdef = api.funcs[create_fun]
    create_fun_sig = create_fun_fdef.signature
    args = arguments(create_fun_sig)
    has_multiple_create_info = create_info_id ∈ ["pCreateInfos", "pAllocateInfos"] ? true : create_info_id ∈ ["pCreateInfo", "pAllocateInfo"] ? false : error("Unknown create info ID $create_info_id")
    new_create_info_id = has_multiple_create_info ? "create_infos" : "create_info"
    if add_create_info_type_annotation
        args = map(x -> x.name == new_create_info_id ? (has_multiple_create_info ? PositionalArgument(x.name, "AbstractArray{$(name_transform(create_info_struct, SDefinition)), 1}") : PositionalArgument(x.name, name_transform(create_info_struct, SDefinition))) : x, args)
    end
    kwargs = keyword_arguments(create_fun_sig)
    identifier = last(args).name
    add_fun_ptr ? push!(args, PositionalArgument("fun_ptr_create")) : nothing
    new_sig = Signature(new_sdef.name, filter(x -> x.name ≠ identifier, args), kwargs)
    creates_multiple_handles = is_command_type(create_fun, ALLOCATE)
    body = has_multiple_create_info ? [
        Statement("create_info_count = length($new_create_info_id)"),
        Statement("$identifier = Array{$(sdef.name), 1}(undef, create_info_count)", identifier),
    ] : creates_multiple_handles ? [
        Statement("$identifier = Array{$(sdef.name), 1}(undef, n)", identifier),
    ] : [
        Statement("$identifier = Ref{$(sdef.name)}()", identifier),
    ]

    broadcast_if_multiple_create_info = has_multiple_create_info || creates_multiple_handles ? "." : ""
    deref = has_multiple_create_info || creates_multiple_handles ? identifier : "$identifier[]"
    body = [
        body...,
        Statement("@check $(create_fun_sig.name)($(join_args(map(fieldname_transform, argnames(create_fun_sig), argtypes(create_fun_sig))))$(add_fun_ptr ? ", fun_ptr_create" : ""))", nothing),
        Statement("vks = $(is_inner_constructor ? "new" : new_sdef.name)$broadcast_if_multiple_create_info($deref)", "vks"),
    ]
    if is_handle_destructible(sdef.name)
        inf = dfmatch(vulkan_destruction_info, :name, sdef.name)
        destroy_fun = inf.destroy_function
        if !is_command_type(destroy_fun, FREE)
            destroyed_el = inf.identifier
            destroy_fun_fdef = api.funcs[destroy_fun]
            identifiers, types = argnames(destroy_fun_fdef.signature), argtypes(destroy_fun_fdef.signature)
            add_fun_ptr ? push!(args, PositionalArgument("fun_ptr_destroy")) : nothing
            lambda_fun = "x -> $destroy_fun($(join_args(map(x -> x == fieldname_transform(destroyed_el, sdef.name) ? "x" : x, map(fieldname_transform, identifiers, types))))$(add_fun_ptr ? ", fun_ptr_destroy" : ""))"
            push!(body, Statement("finalizer$broadcast_if_multiple_create_info($(has_multiple_create_info ? "Ref($lambda_fun)" : lambda_fun), vks)", nothing))
        end
    end
    new_sig = Signature(new_sdef.name, is_command_type(create_fun, ALLOCATE) ? map(x -> x.name == identifier ? PositionalArgument("n") : x, args) : filter(x -> x.name ≠ identifier, args), kwargs)
    FDefinition(new_sig.name, new_sig, false, body, "")
end

function constructor(new_sdef, sdef, ::CreateVkHandleWithCreateInfo ; create_info_sdef, create_fun_fdef)
    create_info_var = last(handle_creation_info[sdef.name])
    create_fun_sig = create_fun_fdef.signature

    kwargs = [kwargs_info..., KeywordArgument("pAllocator", "C_NULL")]
    new_sig = Signature(new_sdef.name, args_info, kwargs)
    create_info_sig = Signature(create_info_sdef)
    name = nc_convert(SnakeCaseLower, sdef.name)
    body = [
    Statement("$name = Ref{$(sdef.name)}()", name),
    Statement("$create_info_var = $(create_info_sig.name)($(join_args(argnames(create_info_sig))))", nothing),
    Statement("@check $(create_fun_sig.name)($(join_args(argnames(create_fun_sig))))", nothing),
    ]
    FDefinition(new_sig.name, new_sig, false, body, "")
end

function constructor(sdef, def::ExtendVkConstructor)
    sname = sdef.name
    sname_extended = "api." * sname
    vk_sig = Signature(sdef)
    args = arguments(vk_sig, remove_parameters=false)
    # filter!(x -> x.name != "sType", args)
    body, pass_results = accumulate_passes(sname, vk_sig.args, pass_new_nametype(SDefinition), [ComputeLengthArgument(), GeneratePointers(), TranslateVkTypesBack()])
    init = init_args(pass_results, body, def, use_all_args=true, take_property=false)
    push!(body, Statement("api.$(sdef.name)($(join_args(init)))"))
    new_sig = Signature(sname_extended, args, KeywordArgument[])
    FDefinition(sname_extended, new_sig, false, body, "Julian constructor for $(sdef.name). All struct pointers should be replaced by an equivalent Ref or `C_NULL`. The Refs need to be explicitly preserved during and after this call to keep the struct pointers valid.")
end

converted_type(type) = type
converted_type(type::Converted) = type.final_type

function accumulate_passes(sname, args, new_nametype_f, passes; common_pass_kwargs=())
    body = Statement[]
    pass_results = DefaultOrderedDict(() -> [])
    @assert unique(typeof.(passes)) == typeof.(passes) "A Pass type cannot be provided more than once"
    for arg ∈ args
        name, type = arg.name, arg.type
        tmp_name = tmp_argname(name, type)
        new_name, new_type = new_nametype_f(name, type, sname)
        pass_args = PassArgs(; name, type, arg, new_name, new_type, sdef=nothing, fdef=nothing, last_name=last_argname(body, tmp_name, new_name), tmp_name, passes=Dict(typeof.(passes) .=> false), sname)
        setproperty!.(Ref(pass_args), keys(common_pass_kwargs), values(common_pass_kwargs))
        for el ∈ passes
            pass_func = hasfield(typeof(el), :pass!) ? el.pass! : pass! # prioritize preprocessed pass inside struct
            body_statements = pass_func(pass_args, typeof(el))
            if !isnothing(body_statements) && typeof(body_statements) != Bool
                body_statements = typeof(body_statements) <: AbstractArray ? body_statements : Statement[body_statements]
                body_statements = annotate_pass(body_statements, el)
                append!(body, body_statements)
                pass_args.passes[typeof(el)] = true
            end
            pass_args.last_name = last_argname(body, tmp_name, new_name)
            push!(pass_results[name], PassResult(el, deepcopy(pass_args)))
        end
        @assert length(passes) == length(pass_results[name]) "Number of passes ($(length(passes))) and pass_results ($(length(pass_results[name]))) differ for $name ($sname)"
    end
    body, pass_results
end

function conversion(new_sdef, sdef, T::ConvertVkStructure)
    transformed_vars = transform_vars(sdef, T)
    sig = conversion_sig(new_sdef.name, sdef.name, T)
    body = [Statement("$(new_sdef.name)($(join_args(transformed_vars)))", nothing)]
    FDefinition(sig.name, sig, false, body)
end

conversion_sig(new_sname, sname, T::ConvertVkStructure) = conversion_sig(new_sname, sname)
conversion_sig(to, from) = new_sig = Signature("vk_convert", PositionalArgument.(["T::Type{$to}", "var::$from"]), [])

function transform_vars(sdef, T::ConversionDefinition)
    vars = String[]
    for args ∈ Signature(sdef).args
        name, type, sname = args.name, args.type, sdef.name
        res = transform_inline(name, type, sname, T)
        !isnothing(res) && push!(vars, res)
    end
    vars
end

function transform_inline(name, type, sname, ::ConvertVkStructure)
    drop_field(name, type, sname) && return nothing
    new_name, new_type = field_transform(name, type, sname)
    "vk_convert($new_type, $name)"
end

function pass!(args::PassArgs, ::Type{TranslateVkTypes})
    @unpack new_type, name, last_name, tmp_name = args
    new_type isa Converted && return Statement("$name = from_vk($(new_type.final_type), $last_name)", name)
end

function pass!(args::PassArgs, ::Type{TranslateVkTypesBack})
    @unpack new_type, name, last_name, type, tmp_name = args
    new_type isa Converted && return Statement("$name = to_vk($type, $last_name)", name)
end

# function pass!(args::PassArgs, ::Type{AddDefaults}; opt_params)
#     @unpack tmp_name, name, new_name, arg, sname = args
#     if !isempty(opt_params)
#         last_param = last(collect(keys(opt_params)))
#     end
#     if arg ∈ keys(opt_params)
#         default = opt_params[arg]
#         eol = name == last_param.name ? "\n" : ""
#         return Statement("$tmp_name = isnothing($new_name) ? $default : $new_name$eol", tmp_name)
#     end
#     nothing
# end

function pass!(args::PassArgs, ::Type{ComputeLengthArgument})
    @unpack tmp_name, name, sname, type = args
    if is_count_variable(name, sname)
        array_arg = first(associated_array_variables(name, sname))
        array_arg_new_name = fieldname_transform(array_arg, type)
        return Statement("$tmp_name = pointer_length($array_arg_new_name)", tmp_name)
    end
    nothing
end

# function pass!(args::PassArgs, ::Type{ReplaceStructureType})
#     @unpack sdef, name = args
#     name == "sType" && return Statement("sType = $(stypes[sdef.name])\n", "sType")
# end

function pass!(args::PassArgs, ::Type{DefineSelfPointers}; new_sname)
    @unpack name, tmp_name = args
    startswith(name, "p") && lstrip(name, 'p') == new_sname && return Statement("$tmp_name = Ref{$(sdef.name)}()", tmp_name)
end

function pass!(args::PassArgs, ::Type{ConvertArrays})
    @unpack new_type, name, sname, type, last_name, new_name, tmp_name = args
    if !(new_type isa Converted) && is_array_type(new_type)
        if is_ptr(type) && inner_type(type) == "Cstring"
            ptrarr = new_name * "_ptrarray"
            return Statement("$ptrarr = pointer.($new_name)", ptrarr)
        elseif is_array_of_vk_objects(name, type, sname)
            return Statement("$tmp_name = $(check_is_default(new_name, type, "getproperty.($new_name, $(is_handle(inner_type(type)) ? ":handle" : ":vks"))"))", tmp_name)
        else
            eltype = inner_type(new_type)
            if is_base_type(eltype)
                eltype_widen_eval = eval(Meta.parse(widen_type(eltype)))
                isabstracttype(eltype_widen_eval) && return Statement("$new_name = convert(Array{$eltype}, $new_name)", last_name)
            end
        end
    end
end

function pass!(args::PassArgs, ::Type{HandlePNextDeps})
    @unpack name, tmp_name, new_name = args
    if name == "pNext"
        return Statement("bag_next = $new_name == C_NULL ? EmptyBag : $new_name.bag", "bag_next")
    end
end

function pass!(args::PassArgs, ::Type{GenerateRefs})
    @unpack tmp_name, new_name, new_type, last_name, type, sname = args
    if is_ptr(type) && !is_array_type(new_type) && !is_extension_ptr(type)
        return Statement("$tmp_name = $(check_is_default(last_name, type, "Ref($last_name$(inline_getproperty(inner_type(type))))"))", tmp_name)
    end
end

function pass!(args::PassArgs, ::Type{GeneratePointers})
    @unpack type, name, tmp_name, new_name, new_type, last_name, passes = args
    if !any(is_triggered.((DefineSelfPointers, InitializePointers), args)) && startswith(name, r"p{1,2}[A-Z]") && !is_extension_ptr(type)
        Statement("$name = unsafe_pointer($last_name)", name)
    end
end

function pass!(args::PassArgs, ::Type{InitializePointers})
    @unpack name, type, new_name, new_type, sname = args
    matches = info_df(sname)[(parent=sname,)]
    inf = info(name, sname)
    if !inf.constant && is_ptr(type) && !is_array_type(new_type) && name == last(matches.name)
        eltype = inner_type(type)
        return Statement("$name = Ref{$eltype}()", name)
    end
end

# a parameter is a keyword argument that relates to the use of a particular function, e.g. flags or allocators.
# a keyword argument is an argument with a default value.
# all structs have arguments, some of which are turned into keyword arguments if default values are defined

function drop_argument(name, sname)
    is_command(sname) && is_command_type(sname, ENUMERATE) && has_count_to_be_filled(sname) && name == enumeration_command_array_variable(sname).name && return true
    name == "sType" && return true
    is_count_variable(name, sname) && return true
end

function is_parameter(name, sname)
    !drop_argument(name, sname) && is_optional_parameter(name, sname)
end

is_keyword_argument(name, type, sname) = is_parameter(name, sname)

function create_info_arguments(sig, args::AbstractArray{T}) where {T <: Argument}
    nonptr_args = filter(x -> !is_ptr(x.type), sig.args)
    nonptr_types = types(nonptr_args)
    create_info_type_indices = findall(occursin.(Ref("Info"), nonptr_types))
    isempty(create_info_type_indices) && return T[]
    args_f = (T == PositionalArgument ? arguments : T == KeywordArgument ? keyword_arguments : error("Unknown argument type $T"))
    new_args = collect(args_f(Signature(api.structs[type])) for type ∈ getindex.(Ref(nonptr_types), create_info_type_indices))
    map(x -> x.name ∈ getproperty.(args, :name) ? PositionalArgument(x.name * "_create_info", T == PositionalArgument ? x.type : x.default) : x, vcat(new_args)...)
end

function keyword_arguments(sig; expose_create_info_kwargs = false, transform_name=true)
    kwargs = sig.args |> Filter(x -> is_keyword_argument(x.name, x.type, sig.name)) |> Map(x -> KeywordArgument(transform_name ? fieldname_transform(x.name, x.type) : x.name, default(x.name, x.type))) |> collect
    expose_create_info_kwargs ? vcat(kwargs, create_info_arguments(sig, kwargs)) : kwargs
end

function parameters(sig; expose_create_info_kwargs=false)
    param_args = filter(x -> is_parameter(x.name, sig.name), sig.args)
    OrderedDict(name => value for (name, value) ∈ zip(param_args, map(x -> default(x.name, x.type), param_args)))
end

function arguments(sig; expose_create_info_kwargs = false, drop_type=true, transform_type = false, transform_name = true, remove_parameters=true)
    sname = sig.name
    args = sig.args |> Filter(x -> !drop_argument(x.name, sname) && (!remove_parameters || !is_parameter(x.name, sname))) |> Map(x -> PositionalArgument(transform_name ? fieldname_transform(x.name, x.type) : x.name, drop_type ? nothing : transform_type ? fieldtype_transform(x.name, x.type, sname) : x.type)) |> collect
    # for arg ∈ filtered_args
    #     name, type = sig.args
    #     new_name, new_type = field_transform(name, type, sname)
    #     if startswith(new_type, "AbstractArray")
    #         eltype = inner_type(new_type)
    #         if eltype ∈ ["String", "AbstractString"]
    #             new_type_ = "AbstractArray"
    #         else
    #             new_type_ = eltype ∈ base_types ? replace(new_type, Regex("(?<={)(?<include>.*?)$eltype") => string("<:" * widen_type(eltype))) : new_type
    #             # println("$new_type => $eltype => $new_type_")
    #         end
    #     else
    #         new_type_ = new_type ∈ base_types ? widen_type(new_type) : new_type
    #     end
    #     push!(args, PositionalArgument(new_name, new_type_))
    # end
    expose_create_info_kwargs ? vcat(args, create_info_arguments(sig, args)) : args
end

remove_type(arg::PositionalArgument) = PositionalArgument(arg.name)


text:
#### References

abstract type AbstractReference end

Base.@kwdef struct Article <: AbstractReference
    hyphenation
    author
    title
    shorttitle
    journaltitle
    volume
    date
    pages
    annotation
    indextitle
    number
end

Base.@kwdef struct Book <: AbstractReference
    keywords
    hyphenation
    author
    editor
    title
    publisher
    location
    date
    series
    annotation
    sorttitle
    commentator
    annotator
    translator
    indextitle
    shorttitle
    volumes
end