### A Pluto.jl notebook ###
# v0.15.1

using Markdown
using InteractiveUtils

# This Pluto notebook uses @bind for interactivity. When running this notebook outside of Pluto, the following 'mock version' of @bind gives bound variables a default value (instead of an error).
macro bind(def, element)
    quote
        local el = $(esc(element))
        global $(esc(def)) = Core.applicable(Base.get, el) ? Base.get(el) : missing
        el
    end
end

# ╔═╡ c028100e-f47b-11eb-2547-df779889e265
using WaterLily, StaticArrays, PlutoUI, Interpolations, Plots, Images

# ╔═╡ b0c8df66-1e63-456e-be32-d469e6972e00
md"""
## Simulation of a swimming dogfish shark

**Gabriel D Weymouth** [![Twitter URL](https://img.shields.io/twitter/url/https/twitter.com/gabrielweymouth.svg?style=social&label=)](https://twitter.com/gabrielweymouth) | 11 Aug 2021

---

This post will demonstrate how to set up and simulate a model of a swimming dogfish shark using [Julia](https://julialang.org/) and [WaterLily.jl](https://www.youtube.com/watch?v=YsPkfZqbNSQ). The post is actually a [Pluto notebook](https://www.youtube.com/watch?v=IAF8DjrQSSk) and can be run interactively by clicking on the top right icon.

We'll use a simple model of the shark based on Lighthill's pioneering [paper on the swimming of slender fish](https://doi.org/10.1017/S0022112060001110). It focuses on the "backbone" of the fish; idealizing the shape as a thickness distribution on either side of the center, and the motion as a lateral ("side-to-side") traveling wave. Amazingly, this simple approach provides insight across a huge range of a swimming animals as illustrated in the image below. [Image credit: Gazzola et al, _Nature_ 2014](https://www.nature.com/articles/nphys3078)
"""

# ╔═╡ e9384124-9708-44eb-b5e7-75abc5177a69
begin
	url = "https://media.springernature.com/full/springer-static/image/art%3A10.1038%2Fnphys3078/MediaObjects/41567_2014_Article_BFnphys3078_Fig1_HTML.jpg"
	filename = download(url)
	lighthill = load(filename)
end

# ╔═╡ c8293b7b-0585-45b3-8763-8cfa4671b155
md"""

### Modeling the shark's thickness and motion

We'll define the thickness and motion distributions using `interpolate` to fit splines through a few points.
"""

# ╔═╡ a48ac92c-5cb0-4e51-a08f-b34bc8b06d04
fit = y-> scale(interpolate(y, BSpline(Quadratic(Line(OnGrid())))), range(0,1,length=length(y)))

# ╔═╡ ad96bcbe-4482-4486-b817-e788604d1d96
md"""
Here is an image of the dogfish shark we will model. [Image credit: Shark Trust](file:///C:/Users/admin/Downloads/Spiny%20Dogfish%20ST%20Factsheet.PDF)
"""

# ╔═╡ 47eb9e3f-5617-417a-8924-5a38d90390e8
# Fish shape
begin
	url2 = "https://pterosaurheresies.files.wordpress.com/2020/01/squalus-acanthias-invivo588.jpg"
	filename2 = download(url2)
	dogfish = load(filename2)
end

# ╔═╡ 8752bf4d-50e6-42ea-8d43-c41699769f77
md"""
The bottom view shows the outline we're interested in, and adding a few points along the length defines the thickness distribution function `thk`.
"""

# ╔═╡ 71c9bc8f-921d-4df4-acc2-7321ce6648e0
begin
	plot(dogfish)
	nose,len = (30,224),500
	width = [0.02,0.07,0.06,0.048,0.03,0.019,0.01]
	scatter!(nose[1].+len.*range(0,1,length=length(width)),
		nose[2].-len.*width,color=:blue,legend=false)
	thk = fit(width)
	x = 0:0.01:1
	plot!(nose[1].+len.*x, [nose[2].-len.*thk.(x),nose[2].+len.*thk.(x)],color=:blue)
end

# ╔═╡ d830e69e-f085-495e-be2c-8ea44582fa20
md"""
Looking at [videos of swimming dogfish](https://youtu.be/nMa6lD2CQVI?t=200), we can see a couple general features
 - The motion of the front half of the body has a small amplitude (around 20% of the tail). This sets the amplitude evelope for the traveling wave.
"""

# ╔═╡ c2b7a082-c05d-41bb-af4c-eb23f6a331bf
begin
	envelope = [0.2,0.21,0.23,0.4,0.88,1.0]
	amp = fit(envelope)
end

# ╔═╡ ea51f472-7ffc-4062-a45c-1410d26b1c3e
md"""
- The wavelength of the traveling wave is a bit longer than the body length.

The slider below controls the wavelength of the traveling wave λ, which you can adjust to see the impact it has on the backbone over the motion cycle.
"""

# ╔═╡ 454f7849-2d9b-4250-94bb-9aed4c2f0d54
md"λ: $@bind λ Slider(0.5:0.1:2, show_value=true, default = 1.1)"

# ╔═╡ 67f29fd6-11ed-415f-abc0-2ed3c84fe60f
begin
	scatter(0:0.2:1, envelope)
	colors = palette(:cyclic_wrwbw_40_90_c42_n256)
	for t in 1/12:1/12:1
		plot!(x,amp.(x).*sin.(2π/λ*x.-2π*t),color=colors[floor(Int,t*256)])
	end
	plot!(ylim=(-1.4,1.4),legend=false)
end

# ╔═╡ db1ed38c-a1aa-4173-bb43-d81afedbfab9
md"""

### Setting up the simulation

Now the thickness and motion are defined, but how will we apply these to a fluid simulation? `WaterLily`uses an [immersed boundary method](https://eprints.soton.ac.uk/369635/) and [automatic differentiation](https://en.wikipedia.org/wiki/Automatic_differentiation) to embed a body into the flow. The upshot is that we don't need to do any meshing; all we need is a signed distance function (SDF) to the surface.

Let's start by defining the SDF to the "backbone", which is a line segment from $x=0\ldots 1$. [See this great video from Inigo Quilez for a derivation of this sdf.](https://www.youtube.com/watch?v=PMltMdi1Wzg) The plot below shows the sdf and the zero contour, which is ... just a line segment.

Simple adjustments to the SDF give us more control of the shape and position. By shifting the y offset as `y = y-shift`, we can move the body laterally. And by subtracting a thickness from the distance as `sdf = sdf-thickness`, we can give the line some width. This is all we need to model the shark.
"""

# ╔═╡ bfc73784-8c27-4cdb-a8e4-fbcbf9ddb3c2
md"shift: $@bind shift Slider(-1:0.5:2, show_value=true, default=0.5)"

# ╔═╡ 76dc572f-3cd2-4f1a-9296-2065c877a6ef
md"thickness: $@bind T Slider(0.001:0.25:2, show_value=true)"

# ╔═╡ 839db88c-a477-4718-88bb-796ab0cd6591
begin
	function segment_sdf(x,y)
		s = clamp(x,0,1)         # distance along the segment
		y = y-shift              # shift laterally
		sdf = √sum(abs2,(x-s,y)) # line segment SDF
		return sdf-T*thk(s)      # subtract thickness
	end
	grid = -1:0.05:2
	contourf(grid,grid,segment_sdf,clim=(-1,2),linewidth=0)
	contour!(grid,grid,segment_sdf,levels=[0],color=:black) # zero contour
end

# ╔═╡ f33b9315-b2d1-4fab-91c9-ed3b63fb4d41
md"""
With the basic SDF tested out, we are ready to set up the WaterLily simulation using the function `fish` defined below:
 - The functions `thk` is passed in to create the `sdf` and the function `amp` is passed in to create the traveling wave `map`.
 - The only numerical parameter passed into `fish` is the length of the fish `L` measured in computational cells. This sets the resolution of the simulation and the size of the fluid arrays.
 - The other parameters are the tail amplitude `A` as a fraction of the length, the [Stouhal number](https://en.wikipedia.org/wiki/Strouhal_number) which sets the motion frequency `ω`, and the [Reynolds number](https://en.wikipedia.org/wiki/Reynolds_number) which sets the fluid viscosity `ν`.
"""

# ╔═╡ 11702be3-d125-4ea4-b7e9-74d34943a164
begin
	function fish(thk,amp,k=5.3;L=2^6,A=0.1,St=0.3,Re=1e4)
		# fraction along fish length
		s(x) = clamp(x[1]/L,0,1)

		# fish geometry: thickened line SDF
		sdf(x,t) = √sum(abs2,x-L*SVector(s(x),0.))-L*thk(s(x))

		# fish motion: travelling wave
		U=1
		ω = 2π*St*U/(2A*L)
		function map(x,t)
			xc = x.-L # shift origin
			return xc-SVector(0.,A*L*amp(s(xc))*sin(k*s(xc)-ω*t))
		end

		# make the fish simulation
		return Simulation((4L+2,2L+2),[U,0.],L;
							ν=U*L/Re,body=AutoBody(sdf,map))
	end

	# Create the swimming shark
	L,A,St = 3*2^5,0.1,0.3
	swimmer = fish(thk,amp;L,A,St);

	# Save a time span for one swimming cycle
	period = 2A/St
	cycle = range(0,23/24*period,length=24)
end

# ╔═╡ d844c02d-41c8-43a8-95dd-742ea6c24f86
md"""
We can test our geometry by plotting the immersed boundary function `μ₀`; which equals 1 in the fluid and 0 in the body.
"""

# ╔═╡ e618b2d9-abe8-4e97-aab2-e87c6278378f
@gif for t ∈ cycle
	measure!(swimmer,t*swimmer.L/swimmer.U);
	contour(swimmer.flow.μ₀[:,:,1]',
		aspect_ratio=:equal,legend=false,border=:none)
end

# ╔═╡ 7329a0ba-c57c-4d77-99da-2f8f66d5a94e
md"""

### Running visualizing and measuring the simulation

That animation of the motion looks great, so we are ready to run the flow simulator!

The `sim_step!(sim,t,remeasure=true)` function runs the simulator up to time `t`, remeasuring the body position every time step. (`remeasure=false` by default since it takes a little extra computational time and isn't needed for statics geometries.)
"""

# ╔═╡ 845cbd96-c01b-49f7-94df-15836a68ebba
# run the simulation a few cycles (this takes few seconds)
begin
	sim_step!(swimmer,3,remeasure=true)
	sim_time(swimmer)
end

# ╔═╡ d5c82801-8387-4ef0-9f34-3279a54603c5
md"""
The simulation has now run forward in time, but there are no visualizations or measurements by default.

To see what is going on, lets make a gif of the vorticity `ω=curl(u)` to visualize the vortices in the wake of the shark. This requires simulating a cycle of motion, and computing the `curl` at all the points `@inside` the simulation.
"""

# ╔═╡ a5c9b186-332f-4812-bb07-0bbe288f5091
begin
	# plot the vorcity ω=curl(u) scaled by the body length L and flow speed U
	function plot_vorticity(sim)
		@inside sim.flow.σ[I] = WaterLily.curl(3,I,sim.flow.u)*sim.L/sim.U
		contourf(sim.flow.σ',
			color=palette(:BuGn), clims=(-10,10),linewidth=0,
			aspect_ratio=:equal,legend=false,border=:none)
	end

	# make a gif over a swimming cycle
	@gif for t ∈ sim_time(swimmer).+cycle
		sim_step!(swimmer,t,remeasure=true)
		plot_vorticity(swimmer)
	end
end

# ╔═╡ 85c555bb-f584-4064-9da7-f7f1e4fd5320
md"""
This is pretty (CFD does stand for _Colorful Fluid Dynamics_ after all), but also tells us something important about the flow. Notice that there are no eddies coming off the body anywhere other than the tail! This is a sign of efficiency since energy is only used to create those trailing vortices.

We can dig in and get some quantitative measurements from the simulation as well. The function `∮nds` takes a integral over the body surface. By passing in the pressure `p`, we can measure the thrust force and side force generated by the shark!
"""

# ╔═╡ c4c3fa5c-79bc-4d55-827f-c952b129ab5f
begin
	function get_force(sim,t)
		sim_step!(sim,t,remeasure=true)
		return WaterLily.∮nds(sim.flow.p,sim.body,t*sim.L/sim.U)./(0.5*sim.L*sim.U^2)
	end
	forces = [get_force(swimmer,t) for t ∈ sim_time(swimmer).+cycle]
	"got forces"
end

# ╔═╡ 9fc14582-108f-4434-83da-f7b32b676bd3
scatter(cycle./period,[first.(forces),last.(forces)],
	labels=permutedims(["thrust","side"]),
	xlabel="scaled time", ylabel="scaled force")

# ╔═╡ 9c67282b-e26a-42bf-836e-fc464b8ca63d
md"""
We can learn a lot from this simple plot. For example, the side-to-side force has the same frequency as the swimming motion itself while the thrust force has double the frequency, with a peak every time the tail passes through the centerline.

### Next steps

This simple model is a great start and it opens up a ton of avenues for improving the shark simulation and suggesting research questions:
 - The instantaneous net forces should be zero in a free swimming body! We could add reaction motions to our model to achieve this. Would the model shark swim in a straight line if we did this, or is a control-loop needed?
 - Real sharks are 3D (gasp!). While we could easily extend this approach using 2D splines, it will take much longer to simulate. Is there a way use GPUs to accelerate the simulations without completely changing the solver?
 - If we were going to make a bio-inspired robot of this shark, we will have constraints on the shape and motion and powering available. Can we use this framework to help optimize our robotic within it's contraints?

Below you can find links to all the packages used in this notebook. Happy Simulating!

1. [Interpolations.jl](https://juliamath.github.io/Interpolations.jl/stable/)
1. [JuliaDiff](https://juliadiff.org/)
1. [JuliaImages](https://juliaimages.org/stable/)
1. [Plots.jl](http://docs.juliaplots.org/latest/)
1. [Pluto.jl](https://github.com/fonsp/Pluto.jl) and [PlutoUI.jl](https://github.com/fonsp/PlutoUI.jl)
1. [StaticArrays.jl](https://github.com/JuliaArrays/StaticArrays.jl)
1. [WaterLily.jl](https://github.com/weymouth/WaterLily.jl)
"""

# ╔═╡ 00000000-0000-0000-0000-000000000001
PLUTO_PROJECT_TOML_CONTENTS = """
[deps]
Images = "916415d5-f1e6-5110-898d-aaa5f9f070e0"
Interpolations = "a98d9a8b-a2ab-59e6-89dd-64a1c18fca59"
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
PlutoUI = "7f904dfe-b85e-4ff6-b463-dae2292396a8"
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
WaterLily = "ed894a53-35f9-47f1-b17f-85db9237eebd"

[compat]
Images = "~0.24.1"
Interpolations = "~0.13.3"
Plots = "~1.20.0"
PlutoUI = "~0.7.9"
StaticArrays = "~1.2.11"
WaterLily = "~0.2.3"
"""

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

[[ANSIColoredPrinters]]
git-tree-sha1 = "574baf8110975760d391c710b6341da1afa48d8c"
uuid = "a4c015fc-c6ff-483c-b24f-f7ea428134e9"
version = "0.0.1"

[[AbstractFFTs]]
deps = ["LinearAlgebra"]
git-tree-sha1 = "485ee0867925449198280d4af84bdb46a2a404d0"
uuid = "621f4979-c628-5d54-868e-fcf4e3e8185c"
version = "1.0.1"

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

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

[[ArrayInterface]]
deps = ["IfElse", "LinearAlgebra", "Requires", "SparseArrays", "Static"]
git-tree-sha1 = "2e004e61f76874d153979effc832ae53b56c20ee"
uuid = "4fba245c-0d91-5ea0-9b3e-6abc04ee57a9"
version = "3.1.22"

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

[[AxisAlgorithms]]
deps = ["LinearAlgebra", "Random", "SparseArrays", "WoodburyMatrices"]
git-tree-sha1 = "a4d07a1c313392a77042855df46c5f534076fab9"
uuid = "13072b0f-2c55-5437-9ae7-d433b7a33950"
version = "1.0.0"

[[AxisArrays]]
deps = ["Dates", "IntervalSets", "IterTools", "RangeArrays"]
git-tree-sha1 = "d127d5e4d86c7680b20c35d40b503c74b9a39b5e"
uuid = "39de3d68-74b9-583c-8d2d-e117c070f3a9"
version = "0.4.4"

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

[[Bzip2_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "c3598e525718abcc440f69cc6d5f60dda0a1b61e"
uuid = "6e34b625-4abd-537c-b88f-471c36dfa7a0"
version = "1.0.6+5"

[[CEnum]]
git-tree-sha1 = "215a9aa4a1f23fbd05b92769fdd62559488d70e9"
uuid = "fa961155-64e5-5f13-b03f-caf6b980ea82"
version = "0.4.1"

[[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 = "e2f47f6d8337369411569fd45ae5753ca10394c6"
uuid = "83423d85-b0ee-5818-9007-b63ccbeb887a"
version = "1.16.0+6"

[[CatIndices]]
deps = ["CustomUnitRanges", "OffsetArrays"]
git-tree-sha1 = "a0f80a09780eed9b1d106a1bf62041c2efc995bc"
uuid = "aafaddc9-749c-510e-ac4f-586e18779b91"
version = "0.2.2"

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

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

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

[[ColorVectorSpace]]
deps = ["ColorTypes", "FixedPointNumbers", "LinearAlgebra", "SpecialFunctions", "Statistics", "TensorCore"]
git-tree-sha1 = "42a9b08d3f2f951c9b283ea427d96ed9f1f30343"
uuid = "c3611d14-8923-5661-9e6a-0046d554d3a4"
version = "0.9.5"

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

[[CommonSubexpressions]]
deps = ["MacroTools", "Test"]
git-tree-sha1 = "7b8a93dba8af7e3b42fecabf646260105ac373f7"
uuid = "bbf7d656-a473-5ed7-a52c-81e309532950"
version = "0.3.0"

[[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 = "344f143fa0ec67e47917848795ab19c6a455f32c"
uuid = "34da2185-b29b-5c13-b0c7-acf172513d20"
version = "3.32.0"

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

[[ComputationalResources]]
git-tree-sha1 = "52cb3ec90e8a8bea0e62e275ba577ad0f74821f7"
uuid = "ed09eef8-17a6-5b46-8889-db040fac31e3"
version = "0.3.2"

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

[[CoordinateTransformations]]
deps = ["LinearAlgebra", "StaticArrays"]
git-tree-sha1 = "6d1c23e740a586955645500bbec662476204a52c"
uuid = "150eb455-5306-5404-9cee-2592286d6298"
version = "0.6.1"

[[CustomUnitRanges]]
git-tree-sha1 = "537c988076d001469093945f3bd0b300b8d3a7f3"
uuid = "dc8bdbbb-1ca9-579f-8c36-e416f6a65cce"
version = "1.0.1"

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

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

[[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"

[[DiffResults]]
deps = ["StaticArrays"]
git-tree-sha1 = "c18e98cba888c6c25d1c3b048e4b3380ca956805"
uuid = "163ba53b-c6d8-5494-b064-1a9d43ac40c5"
version = "1.0.3"

[[DiffRules]]
deps = ["NaNMath", "Random", "SpecialFunctions"]
git-tree-sha1 = "85d2d9e2524da988bffaf2a381864e20d2dae08d"
uuid = "b552c78f-8df3-52c6-915a-8e097449b14b"
version = "1.2.1"

[[Distances]]
deps = ["LinearAlgebra", "Statistics", "StatsAPI"]
git-tree-sha1 = "abe4ad222b26af3337262b8afb28fab8d215e9f8"
uuid = "b4f34e82-e78d-54a5-968a-f98e89d6e8f7"
version = "0.10.3"

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

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

[[Documenter]]
deps = ["ANSIColoredPrinters", "Base64", "Dates", "DocStringExtensions", "IOCapture", "InteractiveUtils", "JSON", "LibGit2", "Logging", "Markdown", "REPL", "Test", "Unicode"]
git-tree-sha1 = "350dced36c11f794c6c4da5dc6493ec894e50c16"
uuid = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
version = "0.27.5"

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

[[EarCut_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "92d8f9f208637e8d2d28c664051a00569c01493d"
uuid = "5ae413db-bbd1-5e63-b57d-d24a61df00f5"
version = "2.1.5+1"

[[EllipsisNotation]]
deps = ["ArrayInterface"]
git-tree-sha1 = "8041575f021cba5a099a456b4163c9a08b566a02"
uuid = "da5c29d0-fa7d-589e-88eb-ea29b0a81949"
version = "1.1.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", "LibVPX_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 = "3cc57ad0a213808473eafef4845a74766242e05f"
uuid = "b22a6f82-2f65-5046-a5b2-351ab43fb4e5"
version = "4.3.1+4"

[[FFTViews]]
deps = ["CustomUnitRanges", "FFTW"]
git-tree-sha1 = "70a0cfd9b1c86b0209e38fbfe6d8231fd606eeaf"
uuid = "4f61f5a4-77b1-5117-aa51-3ab5ef4ef0cd"
version = "0.3.1"

[[FFTW]]
deps = ["AbstractFFTs", "FFTW_jll", "LinearAlgebra", "MKL_jll", "Preferences", "Reexport"]
git-tree-sha1 = "f985af3b9f4e278b1d24434cbb546d6092fca661"
uuid = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341"
version = "1.4.3"

[[FFTW_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "3676abafff7e4ff07bbd2c42b3d8201f31653dcc"
uuid = "f5851436-0d7a-5f13-b9de-f02708fd171a"
version = "3.3.9+8"

[[FileIO]]
deps = ["Pkg", "Requires", "UUIDs"]
git-tree-sha1 = "256d8e6188f3f1ebfa1a5d17e072a0efafa8c5bf"
uuid = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549"
version = "1.10.1"

[[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 = "35895cf184ceaab11fd778b4590144034a167a2f"
uuid = "a3f928ae-7b40-5064-980b-68af3947d34b"
version = "2.13.1+14"

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

[[ForwardDiff]]
deps = ["CommonSubexpressions", "DiffResults", "DiffRules", "LinearAlgebra", "NaNMath", "Printf", "Random", "SpecialFunctions", "StaticArrays"]
git-tree-sha1 = "b5e930ac60b613ef3406da6d4f42c35d8dc51419"
uuid = "f6369f11-7733-5829-9624-2563aa707210"
version = "0.10.19"

[[FreeType2_jll]]
deps = ["Artifacts", "Bzip2_jll", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"]
git-tree-sha1 = "cbd58c9deb1d304f5a245a0b7eb841a2560cfec6"
uuid = "d7e528f0-a631-5988-bf34-fe36492bcfd7"
version = "2.10.1+5"

[[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 = "182da592436e287758ded5be6e32c406de3a2e47"
uuid = "28b8d3ca-fb5f-59d9-8090-bfdbd6d07a71"
version = "0.58.1"

[[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 = "d59e8320c2747553788e4fc42231489cc602fa50"
uuid = "d2c73de3-f751-5644-a686-071e5b155ba9"
version = "0.58.1+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"

[[Graphics]]
deps = ["Colors", "LinearAlgebra", "NaNMath"]
git-tree-sha1 = "2c1cf4df419938ece72de17f368a021ee162762e"
uuid = "a2bd30eb-e257-5431-a919-1863eab51364"
version = "1.1.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 = "44e3b40da000eab4ccb1aecdc4801c040026aeb5"
uuid = "cd3eb016-35fb-5094-929b-558a96fad6f3"
version = "0.9.13"

[[IOCapture]]
deps = ["Logging", "Random"]
git-tree-sha1 = "f7be53659ab06ddc986428d3a9dcc95f6fa6705a"
uuid = "b5f81e59-6552-4d32-b1f0-c071b021bf89"
version = "0.2.2"

[[IdentityRanges]]
deps = ["OffsetArrays"]
git-tree-sha1 = "be8fcd695c4da16a1d6d0cd213cb88090a150e3b"
uuid = "bbac6d45-d8f3-5730-bfe4-7a449cd117ca"
version = "0.3.1"

[[IfElse]]
git-tree-sha1 = "28e837ff3e7a6c3cdb252ce49fb412c8eb3caeef"
uuid = "615f187c-cbe4-4ef1-ba3b-2fcf58d6d173"
version = "0.1.0"

[[ImageAxes]]
deps = ["AxisArrays", "ImageCore", "Reexport", "SimpleTraits"]
git-tree-sha1 = "794ad1d922c432082bc1aaa9fa8ffbd1fe74e621"
uuid = "2803e5a7-5153-5ecf-9a86-9b4c37f5f5ac"
version = "0.6.9"

[[ImageContrastAdjustment]]
deps = ["ColorVectorSpace", "ImageCore", "ImageTransformations", "Parameters"]
git-tree-sha1 = "2e6084db6cccab11fe0bc3e4130bd3d117092ed9"
uuid = "f332f351-ec65-5f6a-b3d1-319c6670881a"
version = "0.3.7"

[[ImageCore]]
deps = ["AbstractFFTs", "Colors", "FixedPointNumbers", "Graphics", "MappedArrays", "MosaicViews", "OffsetArrays", "PaddedViews", "Reexport"]
git-tree-sha1 = "db645f20b59f060d8cfae696bc9538d13fd86416"
uuid = "a09fc81d-aa75-5fe9-8630-4744c3626534"
version = "0.8.22"

[[ImageDistances]]
deps = ["ColorVectorSpace", "Distances", "ImageCore", "ImageMorphology", "LinearAlgebra", "Statistics"]
git-tree-sha1 = "6378c34a3c3a216235210d19b9f495ecfff2f85f"
uuid = "51556ac3-7006-55f5-8cb3-34580c88182d"
version = "0.2.13"

[[ImageFiltering]]
deps = ["CatIndices", "ColorVectorSpace", "ComputationalResources", "DataStructures", "FFTViews", "FFTW", "ImageCore", "LinearAlgebra", "OffsetArrays", "Requires", "SparseArrays", "StaticArrays", "Statistics", "TiledIteration"]
git-tree-sha1 = "bf96839133212d3eff4a1c3a80c57abc7cfbf0ce"
uuid = "6a3955dd-da59-5b1f-98d4-e7296123deb5"
version = "0.6.21"

[[ImageIO]]
deps = ["FileIO", "Netpbm", "PNGFiles", "TiffImages", "UUIDs"]
git-tree-sha1 = "d067570b4d4870a942b19d9ceacaea4fb39b69a1"
uuid = "82e4d734-157c-48bb-816b-45c225c6df19"
version = "0.5.6"

[[ImageMagick]]
deps = ["FileIO", "ImageCore", "ImageMagick_jll", "InteractiveUtils", "Libdl", "Pkg", "Random"]
git-tree-sha1 = "5bc1cb62e0c5f1005868358db0692c994c3a13c6"
uuid = "6218d12a-5da1-5696-b52f-db25d2ecc6d1"
version = "1.2.1"

[[ImageMagick_jll]]
deps = ["JpegTurbo_jll", "Libdl", "Libtiff_jll", "Pkg", "Zlib_jll", "libpng_jll"]
git-tree-sha1 = "1c0a2295cca535fabaf2029062912591e9b61987"
uuid = "c73af94c-d91f-53ed-93a7-00f77d67a9d7"
version = "6.9.10-12+3"

[[ImageMetadata]]
deps = ["AxisArrays", "ColorVectorSpace", "ImageAxes", "ImageCore", "IndirectArrays"]
git-tree-sha1 = "ae76038347dc4edcdb06b541595268fca65b6a42"
uuid = "bc367c6b-8a6b-528e-b4bd-a4b897500b49"
version = "0.9.5"

[[ImageMorphology]]
deps = ["ColorVectorSpace", "ImageCore", "LinearAlgebra", "TiledIteration"]
git-tree-sha1 = "68e7cbcd7dfaa3c2f74b0a8ab3066f5de8f2b71d"
uuid = "787d08f9-d448-5407-9aad-5290dd7ab264"
version = "0.2.11"

[[ImageQualityIndexes]]
deps = ["ColorVectorSpace", "ImageCore", "ImageDistances", "ImageFiltering", "OffsetArrays", "Statistics"]
git-tree-sha1 = "1198f85fa2481a3bb94bf937495ba1916f12b533"
uuid = "2996bd0c-7a13-11e9-2da2-2f5ce47296a9"
version = "0.2.2"

[[ImageShow]]
deps = ["Base64", "FileIO", "ImageCore", "OffsetArrays", "Requires", "StackViews"]
git-tree-sha1 = "832abfd709fa436a562db47fd8e81377f72b01f9"
uuid = "4e3cecfd-b093-5904-9786-8bbb286a6a31"
version = "0.3.1"

[[ImageTransformations]]
deps = ["AxisAlgorithms", "ColorVectorSpace", "CoordinateTransformations", "IdentityRanges", "ImageCore", "Interpolations", "OffsetArrays", "Rotations", "StaticArrays"]
git-tree-sha1 = "e4cc551e4295a5c96545bb3083058c24b78d4cf0"
uuid = "02fcd773-0e25-5acc-982a-7f6622650795"
version = "0.8.13"

[[Images]]
deps = ["AxisArrays", "Base64", "ColorVectorSpace", "FileIO", "Graphics", "ImageAxes", "ImageContrastAdjustment", "ImageCore", "ImageDistances", "ImageFiltering", "ImageIO", "ImageMagick", "ImageMetadata", "ImageMorphology", "ImageQualityIndexes", "ImageShow", "ImageTransformations", "IndirectArrays", "OffsetArrays", "Random", "Reexport", "SparseArrays", "StaticArrays", "Statistics", "StatsBase", "TiledIteration"]
git-tree-sha1 = "8b714d5e11c91a0d945717430ec20f9251af4bd2"
uuid = "916415d5-f1e6-5110-898d-aaa5f9f070e0"
version = "0.24.1"

[[IndirectArrays]]
git-tree-sha1 = "c2a145a145dc03a7620af1444e0264ef907bd44f"
uuid = "9b13fd28-a010-5f03-acff-a1bbcff69959"
version = "0.5.1"

[[Inflate]]
git-tree-sha1 = "f5fc07d4e706b84f72d54eedcc1c13d92fb0871c"
uuid = "d25df0c9-e2be-5dd7-82c8-3ad0b3e990b9"
version = "0.1.2"

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

[[IntelOpenMP_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "d979e54b71da82f3a65b62553da4fc3d18c9004c"
uuid = "1d5cc7b8-4909-519e-a0f8-d0f5ad9712d0"
version = "2018.0.3+2"

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

[[Interpolations]]
deps = ["AxisAlgorithms", "ChainRulesCore", "LinearAlgebra", "OffsetArrays", "Random", "Ratios", "Requires", "SharedArrays", "SparseArrays", "StaticArrays", "WoodburyMatrices"]
git-tree-sha1 = "1470c80592cf1f0a35566ee5e93c5f8221ebc33a"
uuid = "a98d9a8b-a2ab-59e6-89dd-64a1c18fca59"
version = "0.13.3"

[[IntervalSets]]
deps = ["Dates", "EllipsisNotation", "Statistics"]
git-tree-sha1 = "3cc368af3f110a767ac786560045dceddfc16758"
uuid = "8197267c-284f-5f27-9208-e0e47529a953"
version = "0.5.3"

[[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"

[[LazyArtifacts]]
deps = ["Artifacts", "Pkg"]
uuid = "4af54fe1-eca0-43a8-85a7-787d91b784e3"

[[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"

[[LibVPX_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "12ee7e23fa4d18361e7c2cde8f8337d4c3101bc7"
uuid = "dd192d2f-8180-539f-9fb4-cc70b1dcf69a"
version = "1.10.0+0"

[[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"]
uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"

[[LogExpFunctions]]
deps = ["DocStringExtensions", "LinearAlgebra"]
git-tree-sha1 = "7bd5f6565d80b6bf753738d2bc40a5dfea072070"
uuid = "2ab3a3ac-af41-5b50-aa03-7779005ae688"
version = "0.2.5"

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

[[MKL_jll]]
deps = ["Artifacts", "IntelOpenMP_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "Pkg"]
git-tree-sha1 = "c253236b0ed414624b083e6b72bfe891fbd2c7af"
uuid = "856f044c-d86e-5d09-b602-aeab76dc8ba7"
version = "2021.1.1+1"

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

[[MappedArrays]]
git-tree-sha1 = "18d3584eebc861e311a552cbb67723af8edff5de"
uuid = "dbb5928d-eab1-5f90-85c2-b9b0edb7c900"
version = "0.4.0"

[[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 = "4ea90bd5d3985ae1f9a908bd4500ae88921c5ce7"
uuid = "e1d29d7a-bbdc-5cf2-9ac0-f12de2c33e28"
version = "1.0.0"

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

[[MosaicViews]]
deps = ["MappedArrays", "OffsetArrays", "PaddedViews", "StackViews"]
git-tree-sha1 = "b34e3bc3ca7c94914418637cb10cc4d1d80d877d"
uuid = "e94cdb99-869f-56ef-bcf0-1ae2bcbe0389"
version = "0.3.3"

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

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

[[Netpbm]]
deps = ["ColorVectorSpace", "FileIO", "ImageCore"]
git-tree-sha1 = "09589171688f0039f13ebe0fdcc7288f50228b52"
uuid = "f09324ee-3d7c-5217-9330-fc30815ba969"
version = "1.0.1"

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

[[OffsetArrays]]
deps = ["Adapt"]
git-tree-sha1 = "5cc97a6f806ba1b36bac7078b866d4297ae8c463"
uuid = "6fe1bfb0-de20-5000-8ca7-80f57d26f881"
version = "1.10.4"

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

[[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"

[[PNGFiles]]
deps = ["Base64", "CEnum", "ImageCore", "IndirectArrays", "OffsetArrays", "libpng_jll"]
git-tree-sha1 = "520e28d4026d16dcf7b8c8140a3041f0e20a9ca8"
uuid = "f57f5aa1-a3ce-4bc8-8ab9-96f992907883"
version = "0.3.7"

[[PaddedViews]]
deps = ["OffsetArrays"]
git-tree-sha1 = "59925f4ae6861cddc2313a47514b93b6740f9b6f"
uuid = "5432bcbf-9aad-5242-b902-cca2824c8663"
version = "0.5.9"

[[Parameters]]
deps = ["OrderedCollections", "UnPack"]
git-tree-sha1 = "2276ac65f1e236e0a6ea70baff3f62ad4c625345"
uuid = "d96e819e-fc66-5662-9728-84c9c7592b0a"
version = "0.12.2"

[[Parsers]]
deps = ["Dates"]
git-tree-sha1 = "477bf42b4d1496b454c10cce46645bb5b8a0cf2c"
uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0"
version = "2.0.2"

[[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"

[[PkgVersion]]
deps = ["Pkg"]
git-tree-sha1 = "a7a7e1a88853564e551e4eba8650f8c38df79b37"
uuid = "eebad327-c553-4316-9ea0-9fa01ccd7688"
version = "0.1.1"

[[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 = "501c20a63a34ac1d015d5304da0e645f42d91c9f"
uuid = "995b91a9-d308-5afd-9ec6-746e21dbc043"
version = "1.0.11"

[[Plots]]
deps = ["Base64", "Contour", "Dates", "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 = "e39bea10478c6aff5495ab522517fae5134b40e3"
uuid = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
version = "1.20.0"

[[PlutoUI]]
deps = ["Base64", "Dates", "InteractiveUtils", "JSON", "Logging", "Markdown", "Random", "Reexport", "Suppressor"]
git-tree-sha1 = "44e225d5837e2a2345e69a1d1e01ac2443ff9fcb"
uuid = "7f904dfe-b85e-4ff6-b463-dae2292396a8"
version = "0.7.9"

[[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"

[[ProgressMeter]]
deps = ["Distributed", "Printf"]
git-tree-sha1 = "afadeba63d90ff223a6a48d2009434ecee2ec9e8"
uuid = "92933f4c-e287-5a05-a399-4b506db050ca"
version = "1.7.1"

[[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"

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

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

[[RangeArrays]]
git-tree-sha1 = "b9039e93773ddcfc828f12aadf7115b4b4d225f5"
uuid = "b3c3ace0-ae52-54e7-9d0b-2c1406fd6b9d"
version = "0.3.2"

[[Ratios]]
git-tree-sha1 = "37d210f612d70f3f7d57d488cb3b6eff56ad4e41"
uuid = "c84ed2f1-dad5-54f0-aa8e-dbefe2724439"
version = "0.4.0"

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

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

[[Reexport]]
git-tree-sha1 = "5f6c21241f0f655da3952fd60aa18477cf96c220"
uuid = "189a3867-3050-52da-a836-e630ba90ab69"
version = "1.1.0"

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

[[Rotations]]
deps = ["LinearAlgebra", "StaticArrays", "Statistics"]
git-tree-sha1 = "2ed8d8a16d703f900168822d83699b8c3c1a5cd8"
uuid = "6038ab10-8711-5258-84ad-4b1120ba62dc"
version = "1.0.2"

[[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"

[[SimpleTraits]]
deps = ["InteractiveUtils", "MacroTools"]
git-tree-sha1 = "5d7e3f4e11935503d3ecaf7186eac40602e7d231"
uuid = "699a6c99-e7fa-54fc-8d76-47d257e15c1d"
version = "0.9.4"

[[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", "OpenSpecFun_jll"]
git-tree-sha1 = "508822dca004bf62e210609148511ad03ce8f1d8"
uuid = "276daf66-3868-5448-9aa4-cd146d93841b"
version = "1.6.0"

[[StackViews]]
deps = ["OffsetArrays"]
git-tree-sha1 = "46e589465204cd0c08b4bd97385e4fa79a0c770c"
uuid = "cae243ae-269e-4f55-b966-ac2d0dc13c15"
version = "0.1.1"

[[Static]]
deps = ["IfElse"]
git-tree-sha1 = "62701892d172a2fa41a1f829f66d2b0db94a9a63"
uuid = "aedffcd0-7271-4cad-89d0-dc628f76c6d3"
version = "0.3.0"

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

[[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 = "fed1ec1e65749c4d96fc20dd13bea72b55457e62"
uuid = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91"
version = "0.33.9"

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

[[Suppressor]]
git-tree-sha1 = "a819d77f31f83e5792a76081eee1ea6342ab8787"
uuid = "fd094767-a336-5f1f-9728-57cf17d0bbfb"
version = "0.2.0"

[[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 = "d0c690d37c73aeb5ca063056283fde5585a41710"
uuid = "bd369af6-aec1-5ad0-b16a-f7cc5008161c"
version = "1.5.0"

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

[[TensorCore]]
deps = ["LinearAlgebra"]
git-tree-sha1 = "1feb45f88d133a655e001435632f019a9a1bcdb6"
uuid = "62fd8b95-f654-4bbd-a8a5-9c27f68ccd50"
version = "0.1.1"

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

[[TiffImages]]
deps = ["ColorTypes", "DocStringExtensions", "FileIO", "FixedPointNumbers", "IndirectArrays", "Inflate", "OffsetArrays", "OrderedCollections", "PkgVersion", "ProgressMeter"]
git-tree-sha1 = "03fb246ac6e6b7cb7abac3b3302447d55b43270e"
uuid = "731e570b-9d59-4bfa-96dc-6df516fadf69"
version = "0.4.1"

[[TiledIteration]]
deps = ["OffsetArrays"]
git-tree-sha1 = "52c5f816857bfb3291c7d25420b1f4aca0a74d18"
uuid = "06e1c1a7-607b-532d-9fad-de7d9aa2abac"
version = "0.3.0"

[[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"

[[UnPack]]
git-tree-sha1 = "387c1f73762231e86e0c9c5443ce3b4a0a9a0c2b"
uuid = "3a884ed6-31ef-47d7-9d2a-63182c4928ed"
version = "1.0.2"

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

[[WaterLily]]
deps = ["Documenter", "ForwardDiff", "LinearAlgebra", "StaticArrays"]
git-tree-sha1 = "9d17cfa8a5e12d568bddd18f4332d1919316fc28"
uuid = "ed894a53-35f9-47f1-b17f-85db9237eebd"
version = "0.2.3"

[[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"

[[WoodburyMatrices]]
deps = ["LinearAlgebra", "SparseArrays"]
git-tree-sha1 = "59e2ad8fd1591ea019a5259bd012d7aee15f995c"
uuid = "efce3f68-66dc-5838-9240-27a6d6f5f9b6"
version = "0.5.3"

[[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", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"]
git-tree-sha1 = "acc685bcf777b2202a904cdcb49ad34c2fa1880c"
uuid = "0ac62f75-1d6f-5e53-bd7c-93b484bb37c0"
version = "0.14.0+4"

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

[[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 = "d713c1ce4deac133e3334ee12f4adff07f81778f"
uuid = "1270edf5-f2f9-52d2-97e9-ab00b5d0237a"
version = "2020.7.14+2"

[[x265_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "487da2f8f2f0c8ee0e83f39d13037d6bbf0a45ab"
uuid = "dfaa095f-4041-5dcd-9319-2fabd8486b76"
version = "3.0.0+3"

[[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:
# ╠═c028100e-f47b-11eb-2547-df779889e265
# ╟─b0c8df66-1e63-456e-be32-d469e6972e00
# ╟─e9384124-9708-44eb-b5e7-75abc5177a69
# ╟─c8293b7b-0585-45b3-8763-8cfa4671b155
# ╠═a48ac92c-5cb0-4e51-a08f-b34bc8b06d04
# ╟─ad96bcbe-4482-4486-b817-e788604d1d96
# ╟─47eb9e3f-5617-417a-8924-5a38d90390e8
# ╟─8752bf4d-50e6-42ea-8d43-c41699769f77
# ╠═71c9bc8f-921d-4df4-acc2-7321ce6648e0
# ╟─d830e69e-f085-495e-be2c-8ea44582fa20
# ╠═c2b7a082-c05d-41bb-af4c-eb23f6a331bf
# ╟─ea51f472-7ffc-4062-a45c-1410d26b1c3e
# ╟─454f7849-2d9b-4250-94bb-9aed4c2f0d54
# ╠═67f29fd6-11ed-415f-abc0-2ed3c84fe60f
# ╟─db1ed38c-a1aa-4173-bb43-d81afedbfab9
# ╟─bfc73784-8c27-4cdb-a8e4-fbcbf9ddb3c2
# ╟─76dc572f-3cd2-4f1a-9296-2065c877a6ef
# ╠═839db88c-a477-4718-88bb-796ab0cd6591
# ╟─f33b9315-b2d1-4fab-91c9-ed3b63fb4d41
# ╠═11702be3-d125-4ea4-b7e9-74d34943a164
# ╟─d844c02d-41c8-43a8-95dd-742ea6c24f86
# ╠═e618b2d9-abe8-4e97-aab2-e87c6278378f
# ╟─7329a0ba-c57c-4d77-99da-2f8f66d5a94e
# ╠═845cbd96-c01b-49f7-94df-15836a68ebba
# ╟─d5c82801-8387-4ef0-9f34-3279a54603c5
# ╠═a5c9b186-332f-4812-bb07-0bbe288f5091
# ╟─85c555bb-f584-4064-9da7-f7f1e4fd5320
# ╠═c4c3fa5c-79bc-4d55-827f-c952b129ab5f
# ╟─9fc14582-108f-4434-83da-f7b32b676bd3
# ╟─9c67282b-e26a-42bf-836e-fc464b8ca63d
# ╟─00000000-0000-0000-0000-000000000001
# ╟─00000000-0000-0000-0000-000000000002


using Plots
order = 2 # 0 for local constant, 1 for local linear, 2 for local quadratic
k = 3 # number of regressors
srand(1) # set seed to enable testing by fact check
bandwidth = 0.07
n = 100000
neval = 100
x = rand(n,k)*pi*2.0
xeval = [pi*ones(neval,k-1) linspace(pi/2.,pi*1.5,neval)]
y = cos.(sum(x,2)) + cos.(2. * sum(x,2)) + 0.5*randn(n,1)
ytrue = cos.(sum(xeval,2)) + cos.(2. * sum(xeval,2))
weights = kernelweights(x, xeval, bandwidth, true, "gaussian", 200)
yhat = npreg(y, x, xeval, weights, order)
plot(xeval[:,k], [yhat ytrue])


struct WilsonClover
    cSW::Float64
    σ::Array{Array{ComplexF64,2},2}

    function WilsonClover(cSW)
        σ = make_σμν()
        return new(cSW,σ)
    end
end

function make_σμν()
    σ = Array{Array{ComplexF64,2},2}(undef,4,4)
    for μ=1:4
        γμ = γ_all[:,:,μ]
        for ν=1:4
            γν = γ_all[:,:,ν]
            σ[μ,ν] =  (γμ*γν .- γν*γμ )*(1/2)
        end
    end
    return σ
end

function Wclover!(y,U,x,A) #clover operator

end

struct WilsonClover_misc
    clover_coefficient::Float64
    internal_flags::Array{Bool,1}
    inn_table::Array{Int64,3}
    _ftmp_vectors::Array{Array{ComplexF64,3},1}
    _is1::Array{Int64,1}
    _is2::Array{Int64,1}

    function WilsonClover(U::Array{<: AbstractGaugefields{NC,Dim},1},x,clover_coefficient) where {NC,Dim}
        _,_,NN... = size(U[1])
        NV = prod(NN)
        inn_table= zeros(Int64,NV,4,2)
        internal_flags = zeros(Bool,2)
        _ftmp_vectors = Array{Array{ComplexF64,3},1}(undef,6)
        for i=1:6
            _ftmp_vectors[i] = zeros(ComplexF64,NC,NV,4)
        end

        _is1 = zeros(Int64,NV)
        _is2 = zeros(Int64,NV)

        return new(clover_coefficient,internal_flags,inn_table,_ftmp_vectors,_is1,_is2)
    end
end


include("euler/euler.jl")
using .Numbers: get_digits
using Combinatorics: with_replacement_combinations
using StatsBase: countmap

function compute(n::Int)::Int
    squares = (0:9) .* (0:9)
    result = 0
    for i ∈ with_replacement_combinations(0:9, n)
        combination = parse(Int, join(i))
        while true
            combination = sum(squares[get_digits(combination) .+ 1])
            if combination == 89
                result += factorial(n) ÷ prod(factorial.(values(countmap(i))))
                break
            elseif combination ≤ 1
                break
            end
        end
    end
    return result
end


module Gear

using JuMP, BARON, Compat.Test

m = Model(with_optimizer(BARON.Optimizer))
@variable(m, 12 ≤ x[i=1:4] ≤ 60, start = 24)

@NLconstraints(m, begin
    x[3] ≤ x[4]
    x[2] ≤ x[1]
end)

@NLobjective(m, Min, 6.931 - x[1]*x[2]/(x[3]*x[4])^2 + 1)

optimize!(m)

end # module


farmer = @stochastic_model begin
    @stage 1 begin
        @parameters begin
            Crops = [:wheat, :corn, :beets]
            Cost = Dict(:wheat=>150, :corn=>230, :beets=>260)
            Budget = 500
        end
        @decision(model, x[c in Crops] >= 0)
        @objective(model, Min, sum(Cost[c]*x[c] for c in Crops))
        @constraint(model, sum(x[c] for c in Crops) <= Budget)
    end
    @stage 2 begin
        @parameters begin
            Crops = [:wheat, :corn, :beets]
            Required = Dict(:wheat=>200, :corn=>240, :beets=>0)
            PurchasePrice = Dict(:wheat=>238, :corn=>210)
            SellPrice = Dict(:wheat=>170, :corn=>150, :beets=>36, :extra_beets=>10)
        end
        @uncertain ξ[c in Crops]
        @variable(model, y[p in setdiff(Crops, [:beets])] >= 0)
        @variable(model, w[s in Crops ∪ [:extra_beets]] >= 0)
        @objective(model, Min, sum(PurchasePrice[p] * y[p] for p in setdiff(Crops, [:beets]))
                   - sum(SellPrice[s] * w[s] for s in Crops ∪ [:extra_beets]))
        @constraint(model, minimum_requirement[p in setdiff(Crops, [:beets])],
            ξ[p] * x[p] + y[p] - w[p] >= Required[p])
        @constraint(model, minimum_requirement_beets,
            ξ[:beets] * x[:beets] - w[:beets] - w[:extra_beets] >= Required[:beets])
        @constraint(model, beets_quota, w[:beets] <= 6000)
    end
end

ξ₁ = Scenario(wheat = 3.0, corn = 3.6, beets = 24.0, probability = 1/3)
ξ₂ = Scenario(wheat = 2.5, corn = 3.0, beets = 20.0, probability = 1/3)
ξ₃ = Scenario(wheat = 2.0, corn = 2.4, beets = 16.0, probability = 1/3)

farmer_res = SPResult([170,80,250], -108390, -115405.56, 7015.56, 1150, -118600, -107240)
push!(problems, (farmer, [ξ₁,ξ₂,ξ₃], farmer_res,"Farmer"))


module _Simple

include("binarysplitting.jl")

const c3_over_24 = Int64(640320)^3 ÷ 24
const digits_per_term = log10(c3_over_24 / (6 * 2 * 6))
const guard_terms = 5

# Compute int(pi * 10^digits)
# This is done using Chudnovsky's series with binary splitting
pi(digits::T) where {T<:Integer} = begin
    # how many terms to compute
    n = ceil(T, digits / digits_per_term) + guard_terms

    # calculate P(0, N), Q(0, N) and T(0, N)
    p, q, t = binarysplitting(zero(n), n)

    one_squared = big(10)^(2*digits)
    sqrtC = isqrt(10005 * one_squared)

    (q * 426880 * sqrtC) ÷ t
end

#
# Source: http://www.craig-wood.com/nick/articles/pi-chudnovsky/
#
# Computes the terms for binary splitting the Chudnovsky infinite series
#
# a(a) = +/- (13591409 + 545140134*a)
# p(a) = (6*a-5)*(2*a-1)*(6*a-1)
# b(a) = 1
# q(a) = a*a*a*C3_OVER_24
#
compute(a, b) = compute(convert(BigInt, a), convert(BigInt, b))
compute(a::BigInt, b::BigInt) = begin
    if a == 0
        p = q = big(1)
    else
        a2 = a * 2
        a6 = a2 * 3
        p = (a6 - 5) * (a2 - 1) * (a6 - 1)
        q = a^3 * c3_over_24
    end

    # t(a) = p(a) * a(a)
    t = p * (13591409 + 545140134 * a)
    isodd(a) && (t = -t)

    p, q, t
end

combine(am, mb) = begin
    p₁, q₁, t₁ = am
    p₂, q₂, t₂ = mb

    p = p₁ * p₂
    q = q₁ * q₂
    t = q₂ * t₁ + p₁ * t₂

    p, q, t
end

end


using Test
using SimpleRandom, LinearAlgebra

A = random_subset(100)
@test length(A) <= 100
B = random_subset(Set(1:20),5)
@test length(B) == 5
A = random_subset(20,15)
@test length(A) == 15

wt = [ 1/2, 1/3, 1/6]
t = random_choice(wt)
@test 0 < t < 4

d = Dict{String, Float64}()
d["alpha"] = 0.5
d["gamma"] = 0.5
t = random_choice(d)
@test length(t) == 5

x = binom_rv(10)
@test 0 <= x <= 10
x = poisson_rv(.25)
@test x >= 0
x = exp_rv(1.2)
@test x >= 0

X = RV{Int, Rational{Int}}()
X[1] = 1//2
X[2] = 1//3
X[3] = 1//6
@test E(X) == 1//2 + 2//3 + 3//6
@test Var(X) ==  5//9
@test length(X)== 3
a = random_choice(X)
@test 0<a<4
@test sum(probs(X)) == 1

v = random_unit_vector(5)
@test 0.9999 <= norm(v) <=1.0001


module DoubletSource

using LinearAlgebra
using Base.Iterators
using StaticArrays
using Statistics

using ..AeroMDAO: AbstractPanel2D, Panel2D, WakePanel2D, Point2D, collocation_point, p1, p2, transform_panel, affine_2D, panel_length, panel_angle, panel_tangent, panel_normal, panel_dist, rotation, inverse_rotation, midpair_map, pressure_coefficient, wake_panel, wake_panels, panel_points, panel_vector

## Doublet-source Dirichlet boundary condition
#===========================================================================#

source_potential(str, x, z, x1, x2) = str / 4π * ((x - x1) * log((x - x1)^2 + z^2) - (x - x2) * log((x - x2)^2 + z^2) + 2z * (atan(z, x - x2) - atan(z, x - x1)))

source_velocity(str, x, z, x1, x2) = SVector(str / (4π) * log(((x - x1)^2 + z^2) / ((x - x2)^2 + z^2)), doublet_potential(str, x, z, x1, x2))

doublet_potential(str, x, z, x1, x2) = str / (2π) * (atan(z, x - x1) - atan(z, x - x2))

doublet_velocity(str, x, z, x1, x2) = SVector(str / (2π) * - (z / ((x - x1)^2 + z^2) - z / ((x - x2)^2 + z^2) ), str / (2π) * ( (x - x1) / ((x - x1)^2 + z^2) - (x - x2) / ((x - x2)^2 + z^2)))

## Matrix helpers
#===========================================================================#

function doublet_influence(panel_j :: AbstractPanel2D, panel_i :: AbstractPanel2D)
    xp, yp = transform_panel(panel_j, panel_i)
    ifelse(panel_i == panel_j, 0.5, doublet_potential(1., xp, yp, 0., panel_length(panel_j)))
end

function source_influence(panel_j :: AbstractPanel2D, panel_i :: AbstractPanel2D)
    xp, yp = transform_panel(panel_j, panel_i)
    source_potential(1., xp, yp, 0., panel_length(panel_j))
end

boundary_condition(panel_j :: AbstractPanel2D, panel_i :: AbstractPanel2D, u) = -source_influence(panel_j, panel_i) * dot(u, panel_normal(panel_j))

## Aerodynamic coefficients
#===========================================================================#

panel_velocity(dφ, dr, u, α) = dφ / dr + dot(u, α)

lift_coefficient(cp, dist_colpoints, panel_angle) = - cp * dist_colpoints * cos(panel_angle)

lift_coefficient(wake_strength, speed) = 2. * wake_strength / speed

"""
    aerodynamic_coefficients(vels, Δrs, panel_angles, speed, α)

Compute the lift, moment, and pressure coefficients given associated arrays of edge speeds, adjacent collocation point distances, panel angles, the freestream speed, and angle of attack ``\\alpha``.
"""
function eval_coefficients(vels, Δrs, xjs, panel_angles, speed, α)
    cps   = @. pressure_coefficient(speed, vels)
    cls   = @. lift_coefficient(cps, Δrs, panel_angles)
    cms   = @. -cls * xjs * cos(α)

    cls, cms, cps
end

## Matrix assembly
#===========================================================================#

include("matrix_func.jl")

export solve_problem

function solve_problem(panels, u, α, sources :: Bool, wake_length)
    speed 			= norm(u)
    xs	 			= getindex.(panel_points(panels)[2:end-1], 1)

    # Blunt trailing edge tests
    te_panel 		= Panel2D((p2 ∘ last)(panels), (p1 ∘ first)(panels))
    r_te 			= panel_vector(te_panel)
    φ_TE  			= dot(u, r_te)

    # Solve for doublet strengths
    φs 				= solve_strengths(panels, u, α, r_te, sources; bound = wake_length)

    # Evaluate inviscid edge velocities
    u_es, Δrs 		= tangential_velocities(panels, φs, u, sources)

    # Compute coefficients
    cls, cms, cps 	= eval_coefficients(u_es, Δrs, xs, panel_angle.(panels[2:end]), speed, α)

    # Evaluate lift coefficient from wake doublet strength
    cl_wake 		= lift_coefficient(φs[end] - φs[1] + φ_TE, speed)

    cls, cms, cps, cl_wake
end

function solve_problem(panels, u, α, num_wake :: Integer, wake_length)
    speed 			= norm(u)
    xs	 			= getindex.(panel_points(panels)[2:end-1], 1)
    wakes 			= wake_panels(panels, wake_length, num_wake)
    φs				= solve_strengths(panels, u, α, wakes; bound = wake_length)
    u_es, Δrs 		= tangential_velocities(panels, φs[1:end-1], u, false)
    cls, cms, cps 	= evaluate_coefficients(u_es, Δrs, xs, panel_angle.(panels[2:end]), speed, α)
    cl_wake 		= lift_coefficient(last(φs), speed)

    cls, cms, cps, cl_wake
end

end


"""
    schursolve(A, x₀, howmany, which, algorithm)

Compute a partial Schur decomposition containing `howmany` eigenvalues from the linear map
encoded in the matrix or function `A`. Return the reduced Schur matrix, the basis of Schur
vectors, the extracted eigenvalues and a `ConvergenceInfo` structure.

See also [`eigsolve`](@eigsolve) to obtain the eigenvectors instead. For real symmetric or
complex hermitian problems, the (partial) Schur decomposition is identical to the (partial)
eigenvalue decomposition, and `eigsolve` should always be used.

### Arguments:
The linear map can be an `AbstractMatrix` (dense or sparse) or a general function or callable
object, that acts on vector like objects similar to `x₀`, which is the starting guess from
which a Krylov subspace will be built. `howmany` specifies how many Schur vectors should be
converged before the algorithm terminates; `which` specifies which eigenvalues should be targetted.
Valid specifications of `which` are
*   `LM`: eigenvalues of largest magnitude
*   `LR`: eigenvalues with largest (most positive) real part
*   `SR`: eigenvalues with smallest (most negative) real part
*   `LI`: eigenvalues with largest (most positive) imaginary part, only if `T <: Complex`
*   `SI`: eigenvalues with smallest (most negative) imaginary part, only if `T <: Complex`
*   [`ClosestTo(λ)`](@ref): eigenvalues closest to some number `λ`
!!! note "Note about selecting `which` eigenvalues"
    Krylov methods work well for extremal eigenvalues, i.e. eigenvalues on the periphery of
    the spectrum of the linear map. Even with `ClosestTo`, no shift and invert is performed.
    This is useful if, e.g., you know the spectrum to be within the unit circle in the complex
    plane, and want to target the eigenvalues closest to the value `λ = 1`.

The final argument `algorithm` can currently only be an instance of [`Arnoldi`](@ref), but
should nevertheless be specified. Since `schursolve` is less commonly used as `eigsolve`, no
convenient keyword syntax is currently available.

### Return values:
The return value is always of the form `T, vecs, vals, info = schursolve(...)` with
*   `T`: a `Matrix` containing the partial Schur decomposition of the linear map, i.e. it's
    elements are given by `T[i,j] = dot(vecs[i], f(vecs[j]))`. It is of Schur form, i.e.
    upper triangular in case of complex arithmetic, and block upper triangular (with at most
    2x2 blocks) in case of real arithmetic.
*   `vecs`: a `Vector` of corresponding Schur vectors, of the same length as `vals`. Note
    that Schur vectors are not returned as a matrix, as the linear map could act on any
    custom  Julia type with vector like behavior, i.e. the elements of the list `vecs` are
    objects that are typically similar to the starting guess `x₀`, up to a possibly
    different `eltype`. When the linear map is a simple `AbstractMatrix`, `vecs` will be
    `Vector{Vector{<:Number}}`. Schur vectors are by definition orthogonal, i.e.
    `dot(vecs[i],vecs[j]) = I[i,j]`. Note that Schur vectors are real if the problem (i.e.
    the linear map and the initial guess) are real.
*   `vals`: a `Vector` of eigenvalues, i.e. the diagonal elements of `T` in case of complex
    arithmetic, or extracted from the diagonal blocks in case of real arithmetic. Note that
    `vals` will always be complex, independent of the underlying arithmetic.
*   `info`: an object of type [`ConvergenceInfo`], which has the following fields
    -   `info.converged::Int`: indicates how many eigenvalues and Schur vectors were
        actually converged to the specified tolerance (see below under keyword arguments)
    -   `info.residuals::Vector`: a list of the same length as `vals` containing the actual
        residuals
        ```julia
          info.residuals[i] = f(vecs[i]) - sum(vecs[j]*T[j,i] for j = 1:i+1)
        ```
        where `T[i+1,i]` is definitely zero in case of complex arithmetic and possibly zero
        in case of real arithmetic
    -   `info.normres::Vector{<:Real}`: list of the same length as `vals` containing the
        norm of the residual for every Schur vector, i.e.
        `info.normes[i] = norm(info.residual[i])`
    -   `info.numops::Int`: number of times the linear map was applied, i.e. number of times
        `f` was called, or a vector was multiplied with `A`
    -   `info.numiter::Int`: number of times the Krylov subspace was restarted (see below)
!!! warning "Check for convergence"
    No warning is printed if not all requested eigenvalues were converged, so always check
    if `info.converged >= howmany`.

### Algorithm
The actual algorithm is an implementation of the Krylov-Schur algorithm, where the
[`Arnoldi`](@ref) algorithm is used to generate the Krylov subspace. During the algorith,
the Krylov subspace is dynamically grown and shrunk, i.e. the restarts are so-called thick
restarts where a part of the current Krylov subspace is kept.
"""
function schursolve(A, x₀, howmany::Int, which::Selector, alg::Arnoldi)
    T, U, fact, converged, numiter, numops = _schursolve(A, x₀, howmany, which, alg)
    if eltype(T) <: Real && howmany < length(fact) && T[howmany+1,howmany] != 0
        howmany += 1
    end
    if converged > howmany
        howmany = converged
    end
    TT = view(T,1:howmany,1:howmany)
    values = schur2eigvals(TT)
    vectors = let B = basis(fact)
        [B*u for u in cols(U, 1:howmany)]
    end
    residuals = let r = residual(fact)
        [mul!(similar(r), r, last(u)) for u in cols(U, 1:howmany)]
    end
    normresiduals = [normres(fact)*abs(last(u)) for u in cols(U, 1:howmany)]

    if alg.verbosity > 0
        if converged < howmany
            @warn """Arnoldi schursolve finished without convergence after $numiter iterations:
             *  $converged eigenvalues converged
             *  norm of residuals = $((normresiduals...,))"""
        else
            @info """Arnoldi schursolve finished after $numiter iterations:
             *  $converged eigenvalues converged
             *  norm of residuals = $((normresiduals...,))"""
        end
    end
    return TT, vectors, values, ConvergenceInfo(converged, residuals, normresiduals, numiter, numops)
end

function eigsolve(A, x₀, howmany::Int, which::Selector, alg::Arnoldi)
    T, U, fact, converged, numiter, numops = _schursolve(A, x₀, howmany, which, alg)
    if eltype(T) <: Real && howmany < length(fact) && T[howmany+1,howmany] != 0
        howmany += 1
    end
    if converged > howmany
        howmany = converged
    end
    TT = view(T,1:howmany,1:howmany)
    values = schur2eigvals(TT)

    # Compute eigenvectors
    V = view(U, :, 1:howmany)*schur2eigvecs(TT)
    vectors = let B = basis(fact)
        [B*v for v in cols(V)]
    end
    residuals = let r = residual(fact)
        [mul!(similar(r, Base.promote_type(eltype(V), eltype(r))), r, last(v)) for v in cols(V)]
    end
    normresiduals = [normres(fact)*abs(last(v)) for v in cols(V)]

    if alg.verbosity > 0
        if converged < howmany
            @warn """Arnoldi eigsolve finished without convergence after $numiter iterations:
             *  $converged eigenvalues converged
             *  norm of residuals = $((normresiduals...,))
             *  number of operations = $numops"""
        else
            @info """Arnoldi eigsolve finished after $numiter iterations:
             *  $converged eigenvalues converged
             *  norm of residuals = $((normresiduals...,))
             *  number of operations = $numops"""
        end
    end
    return values, vectors, ConvergenceInfo(converged, residuals, normresiduals, numiter, numops)
end

function _schursolve(A, x₀, howmany::Int, which::Selector, alg::Arnoldi)
    krylovdim = alg.krylovdim
    maxiter = alg.maxiter
    howmany > krylovdim && error("krylov dimension $(krylovdim) too small to compute $howmany eigenvalues")

    ## FIRST ITERATION: setting up
    numiter = 1
    # Compute arnoldi factorization
    iter = ArnoldiIterator(A, x₀, alg.orth)
    fact = initialize(iter; verbosity = alg.verbosity - 2)
    numops = 1
    sizehint!(fact, krylovdim)
    β = normres(fact)
    tol::eltype(β) = alg.tol
    if normres(fact) > tol || howmany > 1
        while length(fact) < krylovdim
            fact = expand!(iter, fact; verbosity = alg.verbosity-2)
            numops += 1
            normres(fact) < tol && length(fact) >= howmany && break
        end
    end

    # Process
    # allocate storage
    HH = fill(zero(eltype(fact)), krylovdim+1, krylovdim)
    UU = fill(zero(eltype(fact)), krylovdim, krylovdim)

    # initialize
    β = normres(fact)
    m = length(fact)
    H = view(HH, 1:m, 1:m)
    U = view(UU, 1:m, 1:m)
    f = view(HH, m+1, 1:m)
    copyto!(U, I)
    copyto!(H, rayleighquotient(fact))

    # compute dense schur factorization
    T, U, values = hschur!(H, U)
    by, rev = eigsort(which)
    p = sortperm(values, by = by, rev = rev)
    T, U = permuteschur!(T, U, p)
    f = mul!(f, view(U, m, :), β)
    converged = 0
    while converged < length(fact) && abs(f[converged+1]) < tol
        converged += 1
    end
    if eltype(T) <: Real && 0< converged < length(fact) && T[converged+1,converged] != 0
        converged -= 1
    end

    if alg.verbosity > 1
        msg = "Arnoldi schursolve in iter $numiter: "
        msg *= "$converged values converged, normres = ("
        msg *= @sprintf("%.2e", abs(f[1]))
        for i = 2:howmany
            msg *= ", "
            msg *= @sprintf("%.2e", abs(f[i]))
        end
        msg *= ")"
        @info msg
    end
    ## OTHER ITERATIONS: recycle
    while numiter < maxiter && converged < howmany
        numiter += 1

        # Determine how many to keep
        keep = div(3*m + 2*converged, 5) # strictly smaller than krylovdim since converged < howmany <= krylovdim, at least equal to converged
        if eltype(H) <: Real && H[keep+1,keep] != 0 # we are in the middle of a 2x2 block
            keep += 1 # conservative choice
            keep >= krylovdim && error("krylov dimension $(krylovdim) too small to compute $howmany eigenvalues")
        end

        # Restore Arnoldi form in the first keep columns
        @inbounds for j = 1:keep
            H[keep+1,j] = f[j]
        end
        @inbounds for j = keep:-1:1
            h, ν = householder(H, j+1, 1:j, j)
            H[j+1,j] = ν
            H[j+1,1:j-1] .= 0
            lmul!(h, H)
            rmul!(view(H, 1:j,:), h')
            rmul!(U, h')
        end
        copyto!(rayleighquotient(fact), H) # copy back into fact

        # Update B by applying U
        B = basis(fact)
        basistransform!(B, view(U, :, 1:keep))
        # for j = 1:m
        #     h, ν = householder(U, j:m, j)
        #     lmul!(h, view(U, :, j+1:krylovdim))
        #     rmul!(B, h')
        # end
        r = residual(fact)
        B[keep+1] = rmul!(r, 1/normres(fact))

        # Shrink Arnoldi factorization
        fact = shrink!(fact, keep)

        # Arnoldi factorization: recylce fact
        while length(fact) < krylovdim
            fact = expand!(iter, fact; verbosity = alg.verbosity-2)
            numops += 1
            normres(fact) < tol && length(fact) >= howmany && break
        end

        # post process
        β = normres(fact)
        m = length(fact)
        H = view(HH, 1:m, 1:m)
        U = view(UU, 1:m, 1:m)
        f = view(HH, m+1, 1:m)
        copyto!(U, I)
        copyto!(H, rayleighquotient(fact))

        # compute dense schur factorization
        T, U, values = hschur!(H, U)
        by, rev = eigsort(which)
        p = sortperm(values, by = by, rev = rev)
        T, U = permuteschur!(T, U, p)
        mul!(f, view(U,m,:), β)
        converged = 0
        while converged < length(fact) && abs(f[converged+1]) < tol
            converged += 1
        end
        if eltype(T) <: Real && 0 < converged < length(fact) && T[converged+1,converged] != 0
            converged -= 1
        end
        if alg.verbosity > 1
            msg = "Arnoldi schursolve in iter $numiter: "
            msg *= "$converged values converged, normres = ("
            msg *= @sprintf("%.2e", abs(f[1]))
            for i = 2:howmany
                msg *= ", "
                msg *= @sprintf("%.2e", abs(f[i]))
            end
            msg *= ")"
            @info msg
        end
    end
    return T, U, fact, converged, numiter, numops
end


module SpherePacking

using AbstractAlgebra
using SpecialFunctions
using MPMP
using WriteFilesSDPB

export testbound_sphere_packing, Nsphere_packing_2point

function spherevolume(n, r)
    sqrt(BigFloat(pi))^n / gamma(BigFloat(n)/2 + 1) * r^n
end

laguerre(k, alpha, x) = MPMP.laguerrebasis(k, alpha, x)[end]

function standard_basis(N, i, j, element = 1, symmetric = true)
    # symmetric: place element on both i,j and j,i (i = j => element on i,i)
    # non-symmetric: place element on i,j
    E = zeros(parent(element), N, N)
    E[i,j] = element
    if symmetric
        E[j,i] = element
    end
    return E
end

function Nsphere_packing_2point(n, d, r, N=2, file_path="", write_only=false; omega = BigFloat(10)^2, normalize_prep = false,kwargs...)
    if precision(BigFloat) == 256 #default, usually need at least 512
        setprecision(512)
    end
    #NOTE: we use 2d as max degree, and sort of 4d because we use u = ||x||^2 as variable
    #variables M, a_{ij,k} : for k = 0:d for i = 1:N for j=1:i
    # max -M
    # M - f_ii(0)>=0 for i=1:N
    # F(f)(0) - (vol B(r_i)^1/2 vol B(r_j)^1/2 )_{ij=1}^N >=0
    # F(f)(t) >=0 for t>=0
    # -f(w)_ij >= 0 for w>=r_i+r_j for i=1:N j=1:i (symmetric)
    # With f(x) = sum_k a_k k!/pi^k L_k^{n/2-1}(pi ||x||^2)
    # and thus F(f)(x) = sum_k a_k x^{k}
    # max -M
    # s.t.  - (vol B(r_i)^1/2 vol B(r_j)^1/2 )_{ij=1}^N + \sum_i \sum_j a_{ij,k} E_ij >= 0 (G={1}) (NxN)
    #       0 + sum_k sum_i sum_j a_{ij,k} E_{ij} x^k >= 0        (G = {1,x}) (NxN)
    #       0 - sum_k a_ijk k! pi^-k L_k^{n/2-1}(pi x) >=0          (G = {1,x - (r_i + r_j)^2}) (1x1)
    #       M - sum_k a_iik k!/pi^k L_k^{n/2-1}(0) >= 0 for i=1:N   G = {1}, 1x1

    #Initialize polynomial rings for the polynomial constraints
    F = RealField
    R, (x,) = PolynomialRing(F,["x"])
    SN = MatrixSpace(R,N,N)
    S1 = MatrixSpace(R,1,1) # for the constraints on individual elements

    # The constraint matrices
    #order: constant matrix, matrix for M, matrices for a_ijk: for k=0:2d for i=1:N for j=1:i
    # - (vol B(r_i)^1/2 vol B(r_j)^1/2 )_{ij=1}^N + \sum_i \sum_j a_{ij,k} E_ij >= 0 (G={1}) (NxN)
    M0 = [vcat(SN([R(-sqrt(spherevolume(n,r[i])*spherevolume(n,r[j]))) for i=1:N, j=1:N]), SN(R(0)),
            [ k == 0 ? SN(standard_basis(N,i,j,R(1),true)) : SN(R(0)) for k=0:2d for i=1:N for j=1:i])]
    #    0 + sum_k sum_i sum_j a_{ij,k} E_{ij} x^k >= 0        (G = {1,x}) (NxN)
    M1 = [vcat(SN(R(0)), SN(R(0)),[SN(standard_basis(N,i,j,x^k,true)) for k=0:2d for i=1:N for j=1:i])]
    #    0 - sum_k a_ijk k! pi^-k L_k^{n/2-1}(pi x) >=0       i=1:N, j=1:i   (G = {1,x - (r_i + r_j)^2}) (1x1)
    M2 = [[vcat(S1(R(0)), S1(R(0)),
        [ (r == i && s == j) ? S1(-factorial(big(k)) / BigFloat(pi)^k * laguerre(k, BigFloat(n)/2-1, BigFloat(pi)*x)) : S1(R(0)) for k=0:2d for r=1:N for s=1:r])] for i=1:N for j=1:i]
    #    0 + M - sum_k a_iik k!/pi^k L_k^{n/2-1}(0) >= 0 for i=1:N   G = {1}, 1x1
    M3 = [[vcat(S1(R(0)), S1(R(1)),
        [ r == s == i ? S1(-factorial(big(k)) / BigFloat(pi)^k * laguerre(k, BigFloat(n)/2-1, BigFloat(0))) : S1(R(0)) for k=0:2d for r=1:N for s=1:r])] for i=1:N]
    M = vcat(M0,M1,M2...,M3...) #M2 and M3 contain multiple constraints

    #sample points
    sample_points = vcat([BigFloat(0)],
      [BigFloat.(MPMP.create_sample_points_1d(2d))], #laguerre sample points
      [BigFloat.(MPMP.create_sample_points_1d(2d)).+ (r[i]+r[j])^2 for i=1:N for j=1:i],
      [[BigFloat(0)] for i=1:N])

    #polynomial weights
    G = vcat([[R(1)]],
        [[R(1),x]],
        [[R(1),x-(r[i]+r[j])^2] for i=1:N for j=1:i],
        [[R(1)] for i=1:N])

    # bases
    q = MPMP.laguerrebasis(d, BigFloat(n)/2-1, BigFloat(2*pi)*x)
    max_coef = [max(coeffs(q[i])...) for i=1:length(q)]
    q = [max_coef[i]^(-1)*q[i] for i=1:length(q)]

    #maximum degrees
    delta = vcat(0,2d,[2d for i=1:N for j=1:i],[0 for i=1:N])

    #objective = -M
    b = vcat(BigFloat(-1),[BigFloat(0) for k=0:2d for i=1:N for j=1:i])

    #get the constraints and blockinfo
    constraints = [prepareabc(M[j],G[j],q,sample_points[j],delta[j];normalize=normalize_prep) for j=1:length(G)]
    blockinfo = MPMP.get_block_info(constraints)

    if length(file_path)>0
        #if needed, write the constraints to files in SDPB format
        write_files(file_path,constraints,blockinfo,b)
    end
    if length(M) == 7#only change ordering if N = 2, and hence J = 7.
        #ordering of sdpb: pairs (3,6),(5,7), (4,1) and (2) on cores (if 4 cores).
        # In any case, not the polynomial constraints 2,3,4,5 on the same core if multiple cores
        ordering = [3,6,5,7,4,1,2]
        constraints = constraints[ordering]
        blockinfo = MPMP.get_block_info(constraints)
    end

    if !write_only
        #solve the MPMP and return the values.
        # default start value is 100. For large n,d this may need to be increased.
        x,X,y,Y,P,p,d,dual_gap,primal_obj,dual_obj = solverank1sdp(constraints, b, blockinfo;omega_p = omega,omega_d = omega, kwargs...);
    else
        return true
    end
end

function test_bound_sphere_packing(n=3,d=8)
    if precision(BigFloat) == 256
        setprecision(512)
    end

    #best lower bound known with these ratio is obtained from the crystal structure of NaCl
    cur_bound = Nsphere_packing_2point(n,d,[BigFloat(1), sqrt(BigFloat(2))-1],2)
    println(-cur_bound[end])

    NaCl_density = 0.793
    NaCl_bound = 0.813 #this is the bound reported by de Laat et al. in Upper bounds for packings of spheres of several radii.
    #The same bound is programmed by Nsphere_packing_2point, but then with sampling instead of coefficient matching.
    println("Compare to the density of NaCL: $NaCl_density (Current bound: $NaCl_bound)")
end

end # end module


using Parameters
polynomial(x::Vector, P::Int) = hcat([x.^p for p in 1:P]...)
polynomial(x::AbstractRange, P::Int)= polynomial(collect(x),P)
polynomial(x::Vector, P::Int, f::Function) = hcat([f.(p*x) for p in 1:P]...)
polynomial(x::AbstractRange, P::Int, f::Function) = polynomial(collect(x), P, f)

function polyfit(x,y;x_pred=x, P::Int=3)
    X = polynomial(x,P)
    X_pred=polynomial(x_pred,P)
    β̂ = multiple_regression(X,y)
    y_pred = insert_ones(X_pred) * β̂
end

@with_kw mutable struct Spline{T<:Number}
    xmin::T = -1.0
    xmax::T = 1.0
    K::Int = 5
end

function (s::Spline)(x::Vector, y::Vector, x_pred::Vector;
          splinematrix::Function=spline_matrix)
    @unpack xmin, xmax, K = s
    X = splinematrix(x,K,xmin,xmax)
    β = (X'X)\X'y
    X_pred = splinematrix(x_pred,K,xmin,xmax)
    ypred = X_pred*β
end

(s::Spline)(x::Vector, y::Vector, x_pred::AbstractRange;kwargs...) = s(x,y,collect(x_pred);kwargs...)

function spline_matrix(x::Vector,K::Int,xmin,xmax;use_x=false)
    n = length(x)
    Knots = use_x ? x : range(xmin,stop=xmax,length=K)
    K = length(Knots)
    X = zeros(n,K+4)
    X[:,1] .= 1
    X[:,2] .= x
    X[:,3] .= x.^2
    X[:,4] .= x.^3
    for j in 1:K
        X[:,j+4] = @. max(x - Knots[j], 0)^3
    end
    return X
end

function natural_spline_matrix(x::Vector,K::Int,xmin,xmax)
    n = length(x)
    Knots  = range(xmin,stop=xmax,length=K)
    X = zeros(n,K)
    X[:,1] .= 1
    X[:,2] .= x
    d(x::Vector,a::Number,b::Number) =  @. (max(x-a,0)^3 - max(x-b,0)^3) / (b-a)
    for j in 1:K-2
        X[:,j+2] = d(x,Knots[j],Knots[end]) - d(x,Knots[end-1],Knots[end])
    end
    return X
end

struct SmoothingSpline
    x::Vector
end

function (s::SmoothingSpline)(y::Vector,x_pred::Union{Vector,AbstractRange};λ=0)
    x = s.x
    X = smoothing_spline_matrix(x,x)
    G = green_silverman(x)
    γ = (X'X + λ*G)\X'y
    X_pred = smoothing_spline_matrix(x_pred,x)
    ypred = X_pred*γ
end

function smoothing_spline_matrix(x::Union{Vector,AbstractRange},Knots::Union{Vector, AbstractRange})
    n = length(x)
    K = length(Knots)
    X = zeros(n,K)
    X[:,1] .= 1
    X[:,2] .= x
    d(x::Union{Vector,AbstractRange},a::Number,b::Number) =  @. (max(x-a,0)^3 - max(x-b,0)^3) / (b-a)
    for j in 1:K-2
        X[:,j+2] = d(x,Knots[j],Knots[end]) - d(x,Knots[end-1],Knots[end])
    end
    return X
end

function green_silverman(x::Vector)
    n = length(x)
    g = zeros(n,n)
    for i in 3:n ,j in i:n
        g[j,i] =(12(x[end]-x[end-1])*(x[end-1]-x[j-2])*(x[end-1]-x[i-2])+
                (x[end-1]-x[j-2])^2*(12x[end-1]+6x[j-2]-18x[i-2]))/
                (x[end]-x[i-2])/(x[end]-x[j-2])
        g[i,j] = g[j,i]
    end
    return g
end

function cv_ss_fast(X::AbstractArray,y::Vector,G::AbstractArray,K::Int;λ=0)
    n = length(y); m = round(Int,n/K)
    H = X*((X'X + λ*G)\X')
    e = (I(n) - H)*y
    S = 0
    @views for j in 1:K
        test = j*m-m+1:j*m;
        err = (I(m)-H[test,test]) \ e[test]
        S += dot(err,err)
    end
    return S/n,tr(H)
end

#epanechnikovカーネル
epanechnikov(x,y,λ) = norm(x-y)/λ |> c -> max(0.75*(1-c^2),0)
#nadaraya-watson推定量
function nadaraya_watson_estimator(x_observed::Vector,y_observed::Vector,kk::Function,x_pred::TP;λ=1.0) where {TP<:Number}
    n = length(y_observed)
    S = 0; T = 0;
    for i in 1:n
        S += kk(x_observed[i],x_pred,λ)*y_observed[i]
        T += kk(x_observed[i],x_pred,λ)
    end
    result = T == 0 ? 0 : S/T
    return result
end

function nadaraya_watson_estimator(x_observed::Vector,y_observed::Vector,kk::Function,x_pred::Union{Vector,AbstractRange};λ=1.0)
    y_pred = [nadaraya_watson_estimator(x_observed,y_observed,kk,xp; λ) for xp in x_pred]
end

function local_regression(x::Matrix{T},
                          y::Vector{T},
                          kernel::Function;
                          x_pred=x, λ=1) where {T<:Number}
    N,_ = size(x)
    @assert N==length(y)
    X = insert_ones(x)
    m,_ = size(x_pred)
    y_pred = Vector{T}(undef,m)
    for j in 1:m
        W = [kernel(x[i,:],x_pred[j,:],λ) for i in 1:N] |> diagm
        β̂ = (X'*W*X)\X'*W*y
        y_pred[j] = dot(insert_ones(x_pred[j,:]),β̂)
    end
    return y_pred
end
function local_regression(x::Vector{T}, y::Vector{T},kernel::Function;x_pred=x,λ=1) where {T<:Number}
    return local_regression(x[:,:],y,kernel;x_pred=x_pred[:,:],λ=λ)
end


"""
    lincomb(a::Field,b::AbstractVector)

Returns a field obtained by the "linear combination" of
the value of the field basis `a` and the coefficient vector `b`.  The value of the resulting field
evaluated at a vector of points `x` is defined as

    ax = evaluate(a,x)
    ax*b

On the other hand, the gradient of the resulting field is defined as

    ∇ax = evaluate(gradient(a),x)
    ∇ax*b

"""
function lincomb(a::Field,b::AbstractVector)
  LinComField(a,b)
end

struct LinCom <: Kernel end

function kernel_cache(k::LinCom,a,b)
  _lincomb_checks(a,b)
  Ta = eltype(a)
  Tb = eltype(b)
  T = return_type(outer,Ta,Tb)
  np = length(b)
  r = zeros(T,np)
  CachedArray(r)
end

function _lincomb_checks(a,b)
  nb = length(b)
  np, na = size(a)
  s = "lincom: Number of fields in basis needs to be equal to number of coefs."
  @assert nb == na s
end

@inline function apply_kernel!(r,k::LinCom,a,b)
  _lincomb_checks(a,b)
  np, nf = size(a)
  setsize!(r,(np,))
  z = zero(eltype(r))
  for i in 1:np
    @inbounds r[i] = z
    for j in 1:nf
      @inbounds r[i] += outer(a[i,j],b[j])
    end
  end
  r
end

mutable struct LinComField{A,B} <: Field
  basis::A
  coefs::B
  @inline function LinComField(basis,coefs)
    A = typeof(basis)
    B = typeof(coefs)
    new{A,B}(basis,coefs)
  end
end

function field_cache(f::LinComField,x)
  ca = field_cache(f.basis,x)
  a = evaluate_field!(ca,f.basis,x)
  b = f.coefs
  k = LinCom()
  ck = kernel_cache(k,a,b)
  (ca,ck)
end

@inline function evaluate_field!(cache,f::LinComField,x)
  ca, ck = cache
  a = evaluate_field!(ca,f.basis,x)
  b = f.coefs
  k = LinCom()
  apply_kernel!(ck,k,a,b)
end

function field_gradient(f::LinComField)
  g = field_gradient(f.basis)
  LinComField(g,f.coefs)
end

struct LinComValued <: Kernel end

@inline function kernel_cache(k::LinComValued,a,b)
  LinComField(a,b)
end

@inline function apply_kernel!(f,k::LinComValued,a,b)
  f.basis = a
  f.coefs = b
  f
end

function apply_gradient(k::LinComValued,a,b)
  g = field_array_gradient(a)
  lincomb(g,b)
end

function kernel_evaluate(k::LinComValued,x,a,b)
  ax = evaluate_field_array(a,x)
  k = LinCom()
  apply(k,ax,b)
end

"""
    lincomb(a::AbstractArray{<:Field},b::AbstractArray)

Returns an array of fields numerically equivalent to

    map(lincomb,a,b)
"""
function lincomb(a::AbstractArray,b::AbstractArray)
  k = LinComValued()
  apply(k,a,b)
end


"""
Spin-weighted spherical harmonics and associated functions and operators.
"""
module Sphere

#setprecision(2048) # BigFloat precision in bits

import FastGaussQuadrature as FGQ
import Jacobi: jacobi

export Y_vals, cos_vals, sin_vals, swal, swal_laplacian_matrix, swal_raising_matrix, swal_lowering_matrix, swal_filter_matrix, angular_matrix_mult!

function convertBF(v)
   return return v#parse(Float64,"$v")
end

"""
number of l angular values given ny theta values

num_l = ny - 2*(max_s+max_m), where max_s and max_m
are hard coded in Sphere.jl

num_l(
   ny::Int64
   )::Int64
"""
const max_s = 2
const max_m = 6

function num_l(ny::Int64)::Int64
   @assert ny>(2*max_s+2*max_m)+4
   return ny-2*max_s-2*max_m
end

"""
Computes Gauss-Legendre points (y) over the interval [-1,1].

Y_vals(
   ny::Int64
   )::Vector{Float64}
"""
function Y_vals(ny::Int64)::Vector{Float64}

   roots, weights= FGQ.gausslegendre(ny)

   return roots
end

"""
Computes inner product over the interval [-1,1] using
Gauss quadrature:

return: ∫ dy v1 conj(v2)

inner_product(
   v1::Vector{T},
   v2::Vector{T},
   )::T where T<:Number
"""
function inner_product(
      v1::Array{T,1},
      v2::Array{T,1}
   )::T where T<:Number
   ny = size(v1)[1]

   roots, weights= FGQ.gausslegendre(ny)

   s = 0.0
   for j=1:ny
      s += weights[j]*v1[j]*conj(v2[j])
   end

   return s
end

"""
Computes cos(y) at Gauss-Legendre points over interval [-1,1].

cos_vals(
   ny::Int64
   )::Vector{Float64}
"""
function cos_vals(ny::Int64)::Vector{Float64}

   roots, weights= FGQ.gausslegendre(ny)

   return [-pt for pt in roots]
end

"""
Computes sin(y) at Gauss-Legendre points over interval [-1,1].

sin_vals(
   ny::Int64
   )::Vector{Float64}
"""
function sin_vals(ny::Int64)::Vector{Float64}

   roots, weights= FGQ.gausslegendre(ny)

   return [convert(Float64,
                   sqrt(1.0-convertBF(pt))*sqrt(1.0+convertBF(pt))
                  )
           for pt in roots
          ]
end

"""
Computes the spin-weighted associated Legendre function Y^s_{lm}(y).

swal(
   spin::Int64,
   m_ang::Int64,
   l_ang::Int64,
   y::Float64
   )::Float64
"""
function swal(
      spin::Int64,
      m_ang::Int64,
      l_ang::Int64,
      y::Float64
   )::Float64
   @assert l_ang>=abs(m_ang)

   al = abs(m_ang-spin)
   be = abs(m_ang+spin)
   @assert((al+be)%2==0)
   n = l_ang - (al+be)/2

   if n<0
      return convert(Float64,0)
   end

   norm = sqrt(
      (2*n+al+be+1)*(convertBF(2)^(-al-be-1.0))
   *  factorial(convertBF(n+al+be))/factorial(convertBF(n+al))
   *  factorial(convertBF(n      ))/factorial(convertBF(n+be))
   )
   norm *= convertBF(-1)^(max(m_ang,-spin))

   return convert(
      Float64,
      norm
      *(convertBF(1-y)^(al/2.))
      *(convertBF(1+y)^(be/2.))
      *jacobi(convertBF(y),convertBF(n),convertBF(al),convertBF(be))
   )
end

"""
Computes matrix swal^s_{lm}(y) at Gauss-Legendre points,
over a grid of l values (and fixed m).

swal_vals(
   ny::Int64,
   spin::Int64,
   m_ang::Int64,
   )::Matrix{Float64}
"""
function swal_vals(
      ny::Int64,
      spin::Int64,
      m_ang::Int64,
   )::Matrix{Float64}

   roots, weights= FGQ.gausslegendre(ny)

   nl = num_l(ny)

   vals = zeros(Float64,ny,nl)

   lmin = max(abs(spin),abs(m_ang))

   for k=1:nl
      l_ang = k-1+lmin
      for j=1:length(roots)
         vals[j,k] = swal(spin,m_ang,l_ang,roots[j])
      end
   end
   return vals
end

"""
Computes matrix to compute spin-weighted spherical harmonic laplacian.

swal_laplacian_matrix(
   ny::Int64,
   spin::Int64,
   m_ang::Int64
   )::Matrix{Float64}
"""
function swal_laplacian_matrix(
      ny::Int64,
      spin::Int64,
      m_ang::Int64
   )::Matrix{Float64}

   yv, wv = FGQ.gausslegendre(ny)

   nl   = num_l(ny)
   lmin = max(abs(spin),abs(m_ang))

   lap = zeros(Float64,ny,ny)

   for j=1:ny
      for i=1:ny
         for k=1:nl
            l = k-1+lmin
            lap[j,i] -= (l-spin)*(l+spin+1.0)*(swal(spin,m_ang,l,yv[i])*
                                               swal(spin,m_ang,l,yv[j])
                                              )
         end
         lap[j,i] *= wv[j]
      end
   end

   return lap
end

"""
Computes matrix to raise spin-weighted spherical harmonics.

swal_raising_matrix(
   ny::Int64,
   spin::Int64,
   m_ang::Int64
   )::Matrix{Float64}
"""
function swal_raising_matrix(
      ny::Int64,
      spin::Int64,
      m_ang::Int64
   )::Matrix{Float64}

   yv, wv = FGQ.gausslegendre(ny)

   nl   = num_l(ny)
   lmin = max(abs(spin), abs(spin+1), abs(m_ang))

   raise = zeros(Float64,ny,ny)

   for (i,yi) in enumerate(yv)
      for (j,yj) in enumerate(yv)

         for k=1:nl
            l = k-1+lmin
            raise[j,i] += (
                  sqrt((l-spin)*(l+spin+1.0))
                  *
                  wv[j]*swal(spin,m_ang,l,yj)
                  *
                  swal(spin+1,m_ang,l,yi)
                 )
         end
      end
   end

   return raise
end

"""
Computes matrix to lower spin-weighted spherical harmonics.

swal_lowering_matrix(
   ny::Int64,
   spin::Int64,
   m_ang::Int64
   )::Matrix{Float64}
"""
function swal_lowering_matrix(
      ny::Int64,
      spin::Int64,
      m_ang::Int64
   )::Matrix{Float64}

   yv, wv = FGQ.gausslegendre(ny)

   nl   = num_l(ny)
   lmin = max(abs(spin), abs(spin-1), abs(m_ang))

   lower = zeros(Float64,ny,ny)

   for (i,yi) in enumerate(yv)
      for (j,yj) in enumerate(yv)

         for k=1:nl
            l = k-1+lmin
            lower[j,i] += (
               -
               sqrt((l+spin)*(l-spin+1.0))
               *
               wv[j]*swal(spin,m_ang,l,yj)
               *
               swal(spin-1,m_ang,l,yi)
              )
         end
      end
   end

   return lower
end

"""
Computes matrix to compute low pass filter.

swal_filter_matrix(
   ny::Int64,
   spin::Int64,
   m_ang::Int64
   )::Matrix{Float64}

Multiply the matrix on the left: v[i]*M[i,j] -> v[j]
"""
function swal_filter_matrix(
      ny::Int64,
      spin::Int64,
      m_ang::Int64
   )::Matrix{Float64}

   yv, wv = FGQ.gausslegendre(ny)

   nl = num_l(ny)

   filter = zeros(Float64,ny,ny)

   lmin = max(abs(spin),abs(m_ang))

   for j=1:ny
      for i=1:ny
         for l=lmin:(nl-1+lmin)
            filter[j,i] += exp(-30.0*(l/(nl-1.0))^10)*(
                           swal(spin,m_ang,l,yv[i])*
                           swal(spin,m_ang,l,yv[j])
                        )
         end
         filter[j,i] *= wv[j]
      end
   end

   return filter
end

"""
Matrix multiplication in the angular direction

angular_matrix_mult!(
   f_m,
   f,
   mat
   )
"""
function angular_matrix_mult!(
      f_m,
      f,
      mat
   )
   nx, ny = size(f)
   for j=1:ny
      for i=1:nx
         f_m[i,j] = 0.0
         for k=1:ny
            f_m[i,j] += f[i,k]*mat[k,j]
         end
      end
   end
   return nothing
end

end


using CurveProximityQueries
using ConvexBodyProximityQueries
using IntervalArithmetic
using Plots
using StaticArrays
import Random: seed!

pgfplots()
seed!(22)

curve = Bernstein([[-0.6, 0.0], [-0.6, 0.0], [0.1, -0.4], [0.4, 1.6], [-0.2, -1.6], [-0.1, 0.4], [0.8, 0.0], [0.8, 0.0]])

poly = randpoly([0.3, 0.7]; n=20, scale=0.5)
pts = closest_points(curve(0.5), poly, @SVector(ones(2)))

plot(curve, 0.0..0.2,
     linewidth=2.0,
     linestyle=:dash,
     linecolor=Gray(0.5),
    )
plot!(curve, 0.2..0.8,
      linewidth=2.0,
      linecolor=:black,
     )
plot!(curve, 0.8..1.0,
      linewidth=2.0,
      linestyle=:dash,
      linecolor=Gray(0.5),
     )
scatter!(curve(0.2), markersize=4, markercolor=:black)
scatter!(curve(0.8), markersize=4, markercolor=:black)

plot!(poly,
      fillalpha=1.0,
      fillcolor=Gray(0.7),
     )
plot!(pts,
      linestyle=:dash,
      markersize=8.0,
      linewidth=2.0,
     )
savefig("out/fig5a.pdf")