### 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

# ╔═╡ 6e070dde-de71-11eb-11e0-9161dfe3c5b6
# Import the packages and export their exported functions to the main namespace.
using StatsPlots, PlutoUI, Random

# ╔═╡ 0d9c0987-470d-4b2e-a446-b321a92e2b59
md"""
# Assignment 1 - Q4: Simulating Gaussian Distributions

Welcome to Pluto Notebooks! In this course you will be spending quite a bit of time working within these notebooks. These notebooks are an HTML/CSS/Javascript built interface for interacting with and working with Julia. They were inspiried by Jupyter, and you can learn more about the notebooks through [their github](https://github.com/fonsp/Pluto.jl). The notebooks are lightweight (with files that can be used by the normal julia interpretor), and track how notebook cells depend on eachother to re-run cells when their dependencies change.

The only code in this notebook which is not found in julia's base is the plotting code provided by [StatsPlots](https://github.com/JuliaPlots/StatsPlots.jl) and [Plots](http://docs.juliaplots.org/latest/). Please go read more about these packages, as they will be useful for you throught the course. Another package we use is [PlutoUI]() which contains a multitude of utilities for building interfaces in pluto notebooks (see below). For all the cells, some are hidden by default. To see hidden cells click on the eye to the top-left of the cell of interest.

To complete this assignment you shouldn't need to import any other packages.

In this assignment you will:
1. Learn about using Pluto Notebooks and Stats Plots for visualizing and analysing data.
2. Get experience with calculating the mean, variance, and other metrics of sample data.
3. Build intuition about how a gaussian distribution acts with various parameters.

"""

# ╔═╡ 6e95d732-b580-450a-85db-447444fea409
md"""
### !!!IMPORTANT!!!

Insert your details below. You should see a green checkmark.
"""

# ╔═╡ 8228b64b-96bf-422a-8e01-0e69594343f1
student = (name="Nasif", email="nhossain@ualberta.ca", ccid="nhossain", idnumber=1545143)

# ╔═╡ c4769b33-9ecb-4a1d-b927-d3b5b3351bf4
let
	def_student = (name="NAME as in eclass", email="UofA Email", ccid="CCID", idnumber=0)
	if length(keys(def_student) ∩ keys(student)) != length(keys(def_student))
		md"You don't have all the right entries! Make sure you have `name`, `email`, `ccid`, `idnumber`. ❌"
	elseif any(getfield(def_student, k) == getfield(student, k) for k in keys(def_student))
		md"You haven't filled in all your details! ❌"
	elseif !all(typeof(getfield(def_student, k)) === typeof(getfield(student, k)) for k in keys(def_student))
		md"Your types seem to be off: `name::String`, `email::String`, `ccid::String`, `idnumber::Int`"
	else
		md"Welcome $(student.name)! ✅"
	end
end

# ╔═╡ a29b97a4-ad2e-4888-97bc-6d129c7a19ba
with_terminal() do
	println("Hello worlds")
end

# ╔═╡ 859041a0-98b0-427d-8d92-f53f29176805
md"""
## Gaussian Distribution

A gaussian distribution with mean μ, standard deviation σ. Sample data from this distribution using sample(gd, n). You can also get the mean (mean), standard deviation (stddev), and variance (var) through their corresponding functions.
"""

# ╔═╡ 6c1fd792-91ac-4b3d-96b2-8e108b8caf55
# The block of text below add documentation to julia struct or function.
# Check out the live docs to the right when your cursor
# is in GaussianDistribution.
"""
	GaussianDistribution(μ::Float64, σ::Float64)

	A gaussian distribution with mean μ, standard deviation σ. Sample data from this distribution using `sample(gd, n)`. You can also get the mean (`mean(gd)`), standard deviation (`stddev(gd)`), and variance (`var(gd)`) through their corresponding functions.
	"""
struct GaussianDistribution
	μ::Float64 # mean
	σ::Float64 # standard deviation
end

# ╔═╡ 29d1cde5-a0e5-4539-9490-bff81109bd07
mean(gd::GaussianDistribution) = gd.μ

# ╔═╡ d0d7c379-f03a-4dd9-a3f2-88835e2ecebb
stddev(gd::GaussianDistribution) = gd.σ

# ╔═╡ d281372b-1473-4e6c-8f9f-cb8f280d8f07
var(gd::GaussianDistribution) = gd.σ^2

# ╔═╡ 131f1dbd-c128-4d12-8163-250361c13337
function sample(gd::GaussianDistribution, n = 1)
	gd.σ*randn(n) .+ gd.μ
end

# ╔═╡ 07fba25d-ffc3-4eb5-aebf-742e31267aa1
md"""
## Implementing basic statistcs from data

Below you will be implementing the sample mean, variance, and standard deviation of a dataset X. This dataset is guaranteed to be a vector of floating point numbers. We want the estimates of the variance and standard deviation to be unbiased.

"""

# ╔═╡ e8437a34-c89b-4da6-ad49-56ea445bacd9

# ╔═╡ b950704c-4276-4e06-bad0-944aab0b767b

# ╔═╡ 6fcf8a25-07a9-4d5a-8aa4-af4c4d38c96c
function mean(X::Vector{Float64})
	s = 0
	for x in X
		s += x
	end
	return s / length(X)
end

# ╔═╡ 10a62eea-efc1-416c-a789-21245a2c3470
function var(X::Vector{Float64})
	m = mean(X)
	v = 0
	n = length(X) - 1
	for x in X
	    v += (x - m)^2 / n
	end
    return v
end

# ╔═╡ eb442864-b44d-4980-bb8f-912bc802d4d2
function stddev(X::Vector{Float64})
	sqrt(var(X))
end

# ╔═╡ 5a89a61f-fb50-4843-97da-a1c526aa6a64
let
	# Testing cell
	X = [1.0, 2.0, 3.0, 4.0, 5.0]
	μ̂ = mean(X)
	σ̂ = stddev(X)
	v = var(X)
	μ̂, σ̂, v
	if μ̂ == 3.0 && σ̂ == sqrt(2.0) && v == 2.0
		md"**Ok, but something is slightly wrong!** 🤔"
	elseif μ̂ == 3.0 && σ̂ == sqrt(2.5) && v == 2.5
		md"**Great job!** ✅🎉"
	else
		md"**Keep at it** ❌"
	end
end

# ╔═╡ ff54abab-068a-442f-8572-2da4d90f1fdd
md"""
### Simulating sample variance

Use the below let blocks to complete question 4(bcde). You will be graded on your written work, not on the code in these cells.
"""

# ╔═╡ dfe61219-4c26-4cf1-a9ba-3a6abddfdaa8
md"
Run the code for 10 samples with µ = 0 and σ 2 = 1.0. Write down the sample average that you obtain. Now do this another 4 times, giving you 5 estimates of the sample average M1, M2, M3, M4 and M5. What is the sample variance of these 5 estimates? Use the unbiased sample variance formula. Note that here we want to understand the
variability of the mean estimator itself, if it had been run on different datasets; beautifully we can actually simulate this using synthetic data.
"

# ╔═╡ 9406e2bf-c6c6-4cc2-a79e-376fdb554df5
let # 4b
	# using the means from the 10 samples
	X = [0.09177, 0.1362, 0.553, 0.3527, -0.1012]
	μ̂ = mean(X)
	σ̂ = stddev(X)
	v = var(X)
	with_terminal() do
		println("Sample average ", μ̂)
		println("Variance " , v)
	end
end

# ╔═╡ 1513064c-438e-44e3-9e95-6500219eac00
md"Now run the same experiment, but use 100 samples for each sample average
estimate. What is the sample variance of these 5 estimates? How is it different from the variance when you used 10 samples to compute the estimates?"

# ╔═╡ 42b5216b-1127-4733-953f-652e4afbc105
let # 4c
	# using the means from the 100 samples
	X = [-0.05904, 0.1058, 0.0677, -0.1398, 0.08169]
	μ̂ = mean(X)
	σ̂ = stddev(X)
	v = var(X)
	with_terminal() do
		println("Sample average ", μ̂)
		println("Variance " , v)
	end
end

# ╔═╡ 26839ac2-9e84-42e4-9936-7132711a3b8c
md"
(d) Now let us consider a higher variance situation, where σ
2 = 10.0. Imagine you
know this variance, and that the data comes from a Gaussian, but that you do not know the true
mean. Run the code to get 30 samples, and compute one sample average M. What is the 95%
confidence interval around this M? Give actual numbers.

(e) [8 marks] Now assume you know less: you do not know the data is Gaussian, though you
still know the variance is σ
2 = 10.0. Use the same 30 samples from (d) and resulting sample average
M. Give a 95% confidence interval around M, now without assuming the samples are Gaussian."

# ╔═╡ 34fe4272-f9e8-4a3d-acac-47e7b9c23797
let # 4d, 4e
	# using the means from the 30 samples with variance 10
	μ̂ = 0.1138
	v = 10
	with_terminal() do
		println("Variance " , v)
	end
end

# ╔═╡ 4ed5ce0f-8aa2-4a82-97a6-cdde9220f19f
md"""

## Plotting

Below you will see some example plotting code `plot_density` and `plot_box_and_violin`.

`plot_density` plots a histogram of the data passed through X and a density estimated through a kernel density algorithm (see implementation on [github](https://github.com/JuliaPlots/StatsPlots.jl) for more details).

`plot_box_and_violin` plots a box plot over a violin plot. A violin plot shows the density of the sampled data (same as the `density` function), while the overlayed box plot shows the first quartile, median, and third quartile. More information can be found on [github](https://github.com/JuliaPlots/StatsPlots.jl) about these plotting utilites.

"""

# ╔═╡ 1e613f8c-4639-449e-8c40-f0be93c1ca46
function plot_density(X)
	histogram(
		# data/transform paramters
		X, norm=true,
		# make plot pretty parameters
		grid=false, # removes background grid
		tickdir=:out, # changes tick direction to be out
		lw=1, # makes line width thicker
		color=RGB(87/255, 123/255, 181/255), # Changes fill color of histogram
		legend=nothing, # removes legend
		fillalpha=0.6) # makes the histogram transparent

	density!(X, color=:black, lw=2)
end

# ╔═╡ b62fa4e7-7ff6-46a0-8232-b106e8adbc20
function plot_box_and_violin(X)
	plt = violin(
		["data"], #The label for the data on the x-axis
		X, # the data
		grid=false, # remove the background grid
		tickdir=:out, # set the ticks to be out
		lw=0, # set the line width to be zero
		color=RGB(87/255, 123/255, 181/255), # set color
		legend=nothing)
	boxplot!(
		plt, # explicitly pass in plt object
		["data"], #The label for the data on the x-axis
		X, # the data
		fillalpha=0.5, # make transparent
		lw=3) # emphasize the lines

end

# ╔═╡ 8bbbfb8c-6582-4fe8-928d-714eb56a0ad1
# This is a markdown block.
md"""
## Visualizing the distribution from samples

Below are some sliders you can use to visualize different normal distribtuions interactively. The data is then plotted using the above plotting functions.

Mean: $(@bind mu Slider(-100.0 : 0.1 : 100.0; default=0.0, show_value=true))

Std Dev: $(@bind sigma Slider(0.1 : 0.1: 100.0; default=1.0, show_value=true))

number of samples: $(@bind n Slider(10 : 10 : 10000; default=100, show_value=true))

$(@bind resample_btn Button("Resample"))
"""

# ╔═╡ 6c23eb8c-bf97-462e-a0b0-5a72ff2ce1b7
gd = GaussianDistribution(mu, sigma)

# ╔═╡ cc2729ed-6e19-4596-8daf-6a87fb7763df
begin
	resample_btn
	X = sample(gd, n)
end;

# ╔═╡ eb13fb20-d27a-4cb2-b42c-2aba098a6a48
let
	x̄ = mean(X)
	σ̄ = stddev(X)
	"Sample mean: $(x̄), Sample Standard Deviation: $(σ̄)"
end

# ╔═╡ e94d17ab-862e-4c13-9b65-a4df009c59a0
let
	plt_1 = plot_density(X)
	plt_2 = plot_box_and_violin(X)
	plot(plt_1, plt_2)
end

# ╔═╡ 00000000-0000-0000-0000-000000000001
PLUTO_PROJECT_TOML_CONTENTS = """
[deps]
PlutoUI = "7f904dfe-b85e-4ff6-b463-dae2292396a8"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
StatsPlots = "f3b207a7-027a-5e70-b257-86293d7955fd"

[compat]
PlutoUI = "~0.7.9"
StatsPlots = "~0.14.26"
"""

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

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

[[Arpack]]
deps = ["Arpack_jll", "Libdl", "LinearAlgebra"]
git-tree-sha1 = "2ff92b71ba1747c5fdd541f8fc87736d82f40ec9"
uuid = "7d9fca2a-8960-54d3-9f78-7d1dccf2cb97"
version = "0.4.0"

[[Arpack_jll]]
deps = ["Libdl", "OpenBLAS_jll", "Pkg"]
git-tree-sha1 = "e214a9b9bd1b4e1b4f15b22c0994862b66af7ff7"
uuid = "68821587-b530-5797-8361-c406ea357684"
version = "3.5.0+3"

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

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

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

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

[[Clustering]]
deps = ["Distances", "LinearAlgebra", "NearestNeighbors", "Printf", "SparseArrays", "Statistics", "StatsBase"]
git-tree-sha1 = "75479b7df4167267d75294d14b58244695beb2ac"
uuid = "aaaa29a8-35af-508c-8bc3-b662a17a0fe5"
version = "0.14.2"

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

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

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

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

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

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

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

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

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

[[DataValues]]
deps = ["DataValueInterfaces", "Dates"]
git-tree-sha1 = "d88a19299eba280a6d062e135a43f00323ae70bf"
uuid = "e7dc6d0d-1eca-5fa6-8ad6-5aecde8b7ea5"
version = "0.4.13"

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

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

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

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

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

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

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

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

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

[[FillArrays]]
deps = ["LinearAlgebra", "Random", "SparseArrays", "Statistics"]
git-tree-sha1 = "8c8eac2af06ce35973c3eadb4ab3243076a408e7"
uuid = "1a297f60-69ca-5386-bcde-b61e274b549b"
version = "0.12.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"

[[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 = "0c603255764a1fa0b61752d2bec14cfbd18f7fe8"
uuid = "0656b61e-2033-5cc2-a64a-77c0f6c09b89"
version = "3.3.5+1"

[[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 = "a32d672ac2c967f3deb8a81d828afc739c838a06"
uuid = "7746bdde-850d-59dc-9ae8-88ece973131d"
version = "2.68.3+2"

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

[[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 = "61aa005707ea2cebf47c8d780da8dc9bc4e0c512"
uuid = "a98d9a8b-a2ab-59e6-89dd-64a1c18fca59"
version = "0.13.4"

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

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

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

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

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

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

[[KernelDensity]]
deps = ["Distributions", "DocStringExtensions", "FFTW", "Interpolations", "StatsBase"]
git-tree-sha1 = "591e8dc09ad18386189610acafb970032c519707"
uuid = "5ab0869b-81aa-558d-bb23-cbf5423bbe9b"
version = "0.6.3"

[[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 = "0b4a5d71f3e5200a7dff793393e09dfc2d874290"
uuid = "e9f186c6-92d2-5b65-8a66-fee21dc1b490"
version = "3.2.2+1"

[[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", "IrrationalConstants", "LinearAlgebra"]
git-tree-sha1 = "3d682c07e6dd250ed082f883dc88aee7996bf2cc"
uuid = "2ab3a3ac-af41-5b50-aa03-7779005ae688"
version = "0.3.0"

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

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

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

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

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

[[MultivariateStats]]
deps = ["Arpack", "LinearAlgebra", "SparseArrays", "Statistics", "StatsBase"]
git-tree-sha1 = "8d958ff1854b166003238fe191ec34b9d592860a"
uuid = "6f286f6a-111f-5878-ab1e-185364afe411"
version = "0.8.0"

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

[[NearestNeighbors]]
deps = ["Distances", "StaticArrays"]
git-tree-sha1 = "16baacfdc8758bc374882566c9187e785e85c2f0"
uuid = "b8a86587-4115-5ab1-83bc-aa920d37bbce"
version = "0.4.9"

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

[[Observables]]
git-tree-sha1 = "fe29afdef3d0c4a8286128d4e45cc50621b1e43d"
uuid = "510215fc-4207-5dde-b226-833fc4488ee2"
version = "0.4.0"

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

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

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

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

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

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

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

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

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

[[Parsers]]
deps = ["Dates"]
git-tree-sha1 = "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"

[[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 = "8365fa7758e2e8e4443ce866d6106d8ecbb4474e"
uuid = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
version = "1.20.1"

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

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

[[QuadGK]]
deps = ["DataStructures", "LinearAlgebra"]
git-tree-sha1 = "12fbe86da16df6679be7521dfb39fbc861e1dc7b"
uuid = "1fd47b50-473d-5c70-9696-f719f8f3bcdc"
version = "2.4.1"

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

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

[[Ratios]]
deps = ["Requires"]
git-tree-sha1 = "7dff99fbc740e2f8228c6878e2aad6d7c2678098"
uuid = "c84ed2f1-dad5-54f0-aa8e-dbefe2724439"
version = "0.4.1"

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

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

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

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

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

[[SentinelArrays]]
deps = ["Dates", "Random"]
git-tree-sha1 = "a3a337914a035b2d59c9cbe7f1a38aaba1265b02"
uuid = "91c51154-3ec4-41a3-a24f-3f23e20d615c"
version = "1.3.6"

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

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

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

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

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

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

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

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

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

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

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

[[StatsFuns]]
deps = ["IrrationalConstants", "LogExpFunctions", "Reexport", "Rmath", "SpecialFunctions"]
git-tree-sha1 = "20d1bb720b9b27636280f751746ba4abb465f19d"
uuid = "4c63d2b9-4356-54db-8cca-17b64c39e42c"
version = "0.9.9"

[[StatsPlots]]
deps = ["Clustering", "DataStructures", "DataValues", "Distributions", "Interpolations", "KernelDensity", "LinearAlgebra", "MultivariateStats", "Observables", "Plots", "RecipesBase", "RecipesPipeline", "Reexport", "StatsBase", "TableOperations", "Tables", "Widgets"]
git-tree-sha1 = "e7d1e79232310bd654c7cef46465c537562af4fe"
uuid = "f3b207a7-027a-5e70-b257-86293d7955fd"
version = "0.14.26"

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

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

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

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

[[TableOperations]]
deps = ["SentinelArrays", "Tables", "Test"]
git-tree-sha1 = "a7cf690d0ac3f5b53dd09b5d613540b230233647"
uuid = "ab02a1b2-a7df-11e8-156e-fb1833f50b87"
version = "1.0.0"

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

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

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

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

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

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

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

[[Widgets]]
deps = ["Colors", "Dates", "Observables", "OrderedCollections"]
git-tree-sha1 = "eae2fbbc34a79ffd57fb4c972b08ce50b8f6a00d"
uuid = "cc8bc4a8-27d6-5769-a93b-9d913e69aa62"
version = "0.6.3"

[[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 = "b910cb81ef3fe6e78bf6acee440bda86fd6ae00c"
uuid = "f27f6e37-5d2b-51aa-960f-b287f2bc3b7a"
version = "1.3.7+1"

[[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:
# ╟─0d9c0987-470d-4b2e-a446-b321a92e2b59
# ╠═6e070dde-de71-11eb-11e0-9161dfe3c5b6
# ╠═6e95d732-b580-450a-85db-447444fea409
# ╠═8228b64b-96bf-422a-8e01-0e69594343f1
# ╠═c4769b33-9ecb-4a1d-b927-d3b5b3351bf4
# ╠═a29b97a4-ad2e-4888-97bc-6d129c7a19ba
# ╟─859041a0-98b0-427d-8d92-f53f29176805
# ╠═6c1fd792-91ac-4b3d-96b2-8e108b8caf55
# ╠═29d1cde5-a0e5-4539-9490-bff81109bd07
# ╠═d0d7c379-f03a-4dd9-a3f2-88835e2ecebb
# ╠═d281372b-1473-4e6c-8f9f-cb8f280d8f07
# ╠═131f1dbd-c128-4d12-8163-250361c13337
# ╟─07fba25d-ffc3-4eb5-aebf-742e31267aa1
# ╠═e8437a34-c89b-4da6-ad49-56ea445bacd9
# ╟─b950704c-4276-4e06-bad0-944aab0b767b
# ╠═5a89a61f-fb50-4843-97da-a1c526aa6a64
# ╠═6fcf8a25-07a9-4d5a-8aa4-af4c4d38c96c
# ╠═10a62eea-efc1-416c-a789-21245a2c3470
# ╠═eb442864-b44d-4980-bb8f-912bc802d4d2
# ╟─ff54abab-068a-442f-8572-2da4d90f1fdd
# ╟─dfe61219-4c26-4cf1-a9ba-3a6abddfdaa8
# ╠═9406e2bf-c6c6-4cc2-a79e-376fdb554df5
# ╟─1513064c-438e-44e3-9e95-6500219eac00
# ╠═42b5216b-1127-4733-953f-652e4afbc105
# ╟─26839ac2-9e84-42e4-9936-7132711a3b8c
# ╠═34fe4272-f9e8-4a3d-acac-47e7b9c23797
# ╟─4ed5ce0f-8aa2-4a82-97a6-cdde9220f19f
# ╟─1e613f8c-4639-449e-8c40-f0be93c1ca46
# ╟─b62fa4e7-7ff6-46a0-8232-b106e8adbc20
# ╟─8bbbfb8c-6582-4fe8-928d-714eb56a0ad1
# ╠═6c23eb8c-bf97-462e-a0b0-5a72ff2ce1b7
# ╠═cc2729ed-6e19-4596-8daf-6a87fb7763df
# ╠═eb13fb20-d27a-4cb2-b42c-2aba098a6a48
# ╠═e94d17ab-862e-4c13-9b65-a4df009c59a0
# ╟─00000000-0000-0000-0000-000000000001
# ╟─00000000-0000-0000-0000-000000000002


function TestHill()

close all
nx=20;
ny=1;
lx=2000;
ly=100;
x0=-1000;
y0=0;
Param.Grav=9.81;
Param.Coriolis=false;
Param.HyperDM=1.e15*(nx/30)^3.2;
Param.HyperD=1.e15*(nx/30)^3.2;
Param.Flat=false;

Boundary.WE='Period';
Boundary.BT='Period';
Param.Grid=CartGridCartGrid(nx,ny,lx,ly,x0,y0,OrientFaceCart,Boundary,Param);

OrdPoly=3;
CG.OrdPoly=OrdPoly;
[CG.w,CG.xw]=GaussLobattoQuad(CG.OrdPoly);
[CG.wX,CG.xwX]=GaussLobattoQuad(CG.OrdPoly);
[CG.wY,CG.xwY]=GaussLobattoQuad(CG.OrdPoly);
[CG.DW,CG.DS]=DerivativeMatrixSingle(CG);

Param.J=zeros(OrdPoly+1,OrdPoly+1,Param.Grid.NumFaces);
Param.X=zeros(OrdPoly+1,OrdPoly+1,3,Param.Grid.NumFaces);
Param.dXdx=zeros(OrdPoly+1,OrdPoly+1,2,2,Param.Grid.NumFaces);
Param.lat=zeros(OrdPoly+1,OrdPoly+1,Param.Grid.NumFaces);
% Param.J1=zeros(OrdPoly+1,OrdPoly+1,Param.Grid.NumFaces);
% Param.X1=zeros(OrdPoly+1,OrdPoly+1,3,Param.Grid.NumFaces);
% Param.dXdx1=zeros(OrdPoly+1,OrdPoly+1,2,2,Param.Grid.NumFaces);
% Param.lat1=zeros(OrdPoly+1,OrdPoly+1,Param.Grid.NumFaces);
for iF=1:Param.Grid.NumFaces
%   [Param.X1(:,:,:,iF),Param.J1(:,:,iF),Param.dXdx1(:,:,:,:,iF),Param.lat1(:,:,iF)]=...
%     JacobiSphereDG(CG,Param.Grid.Faces(iF),Param.Grid);
  for j=1:OrdPoly+1
    for i=1:OrdPoly+1
      [Param.X(i,j,:,iF),Param.J(i,j,iF),Param.dXdx(i,j,:,:,iF),Param.lat(i,j,iF)]...
        =JacobiSphere2(CG.xw(i),CG.xw(j),Param.Grid.Faces(iF),Param.Grid);
    end
  end
end

%Setting initial Values
Profh='Hill';
ProfVel='Hill';
Param.ProfVel=ProfVel;
Param.Profh=Profh;

[CG.Faces,CG.NumG,CG.NumI]=NumberingFemCG(Param.Grid,OrdPoly);
[CG.M]=MassCG(CG,Param);

U=zeros(CG.NumG,3);
U(:,1)=Project(@fh,CG,@JacobiSphere2,Param);
Param.hS=Project(@fhs,CG,@JacobiSphere2,Param);
[U(:,2),U(:,3)]=ProjectVec(@fVel,CG,@JacobiSphere2,Param);
fig=1;
fig=PlotCG(U(:,2),CG,@JacobiSphere2,Param,fig);

CFL=0.125;
dtau=100;
time=0;

IntMethod='RungeKutta';
nIter=5*3600*24/100;
v = VideoWriter ('Hill.avi');
open (v);
switch IntMethod
  case 'RungeKutta'
    for i=1:nIter
      F=FcnShallowCurl(U,CG,Param);
      UNeu=U+1/3*dtau*F;
      F=FcnShallowCurl(UNeu,CG,Param);
      UNeu=U+1/2*dtau*F;
      F=FcnShallowCurl(UNeu,CG,Param);
      U=U+dtau*F;
      time=time+dtau;
      if mod(i,50)==0
        PlotCG(U(:,2),CG,@JacobiSphere2,Param,fig);
        frame=getframe(gcf);
        writeVideo (v, frame);
      end
    end
end
%fig=PlotCG(U(:,1),CG,@JacobiSphere2,Param,fig);
close(v)
end


# Series evaluation of:
#    N_n/(4*b*r)^{n/2} = \int_{-\kappa/2}^{\kappa/2} (k^2-\sin^2{x})^{n/2} sin^2{x} dx
# for the top two values of n = n_max-1 to n_max.
function Nn_series!(t::Transit_Struct{T}) where {T <: Real}
# Use series expansion to compute M_n:
k2 = t.k2; n_max = t.n_max
# Computing leading coefficient (n=0):
if k2 < 1
  tol = eps(k2); term = zero(T)
  fac = t.sqonembmr2^(n_max-1)*t.k*t.k2
# Now, compute higher order terms until desired precision is reached:
  @inbounds for j=1:2
    # Add leading term to M_n:
    t.Nn[n_max-1+j] = t.Nn_coeff[j,1]
    k2n = one(T)   # k^{2n}
    @inbounds for n =1:t.jmax-1
      k2n *= k2
      term = k2n*t.Nn_coeff[j,n+1]
      t.Nn[n_max-1+j] += term
      if abs(term) < tol
        break
      end
    end
    t.Nn[n_max-1+j] *= fac
    fac *= t.sqonembmr2
  end
  return
else # k^2 >= 1
  # No need for this one.
end
end

# Recursive computation of N_n starting at m=0 to 1, and raising to n_max:
function Nn_raise!(t::Transit_Struct{T}) where {T <: Real}
r=t.r; b=t.b; n_max=t.n_max; k2 = t.k2; kc = t.kc; t.k2inv = inv(t.k2); sqarea=t.sqarea; onemr2mb2=t.onemr2mb2
# Computes the integrals:
# N_n(r,b) = (4*b*r)^(n/2) \int_{-\kappa/2}^{\kappa/2} (k^2 - \sin^2{x})^{n/2} \sin^2{x} dx
# where k^2 = (1-(r-b)^2)/(4*b*r), \kappa/2 = \sin^{-1}(k) for k<1; otherwise \kappa=\pi.
# Compute first two integrals:
Nn_two!(t)
@inbounds for m=2:n_max
  t.Nn[m+1]=(t.Mn[m+1]+m*t.onembpr2*t.Nn[m-1])*t.ninv[m+2]
end
return
end

# Compute N_n for m=0 to 1:
function Nn_two!(t::Transit_Struct{T}) where {T <: Real}
r=t.r; b=t.b; n_max=t.n_max; k2 = t.k2; kc = t.kc; t.k2inv = inv(t.k2)
# Computes the integrals:
# N_n(r,b) = (4*b*r)^(m/2) \int_{-\kappa/2}^{\kappa/2} (k^2 - \sin^2{x})^{m/2} \sin^2{x} dx
# where k^2 = (1-(r-b)^2)/(4*b*r), \kappa/2 = \sin^{-1}(k) for k<1; otherwise \kappa=\pi.
# Compute first two integrals:
if k2 <= 1.0
  t.Nn[1] = 0.5*t.kap0-t.k*t.kc
  t.Nn[2] = t.twothird*2*t.sqbr*t.k2*(-t.Eofk+2*t.Em1mKdm)
else
  t.Nn[1] = 0.5*pi
  t.Nn[2] =  t.twothird*2*t.sqbr*t.k*(2*t.Eofk - t.Em1mKdm)
end
return
end

# Recursive computation of N_n starting at m=n_max to n_max-1, and lowering to m=1:
function Nn_lower!(t::Transit_Struct{T}) where {T <: Real}
r=t.r; b=t.b; n_max=t.n_max; k2 = t.k2
# Computes the integrals:
# N_n(r,b) = (4*b*r)^(m/2) \int_{-\kappa/2}^{\kappa/2} (k^2 - \sin^2{x})^{m/2} \sin^2{x} dx
# where k^2 = (1-(r-b)^2)/(4*b*r), \kappa/2 = \sin^{-1}(k) for k<1; otherwise \kappa=\pi.
# Compute series version for top two:
Nn_series!(t)
# Now iterate downwards:
invonembpr2 = inv(t.onembpr2)
@inbounds for m=n_max-2:-1:2
  t.Nn[m+1]=((m+4)*t.Nn[m+3]-t.Mn[m+3])*invonembpr2*t.ninv[m+2]
end
# Now, compute lowest two exactly:
Nn_two!(t)
return
end


using MPI
using Test
using LinearAlgebra
using Random
using StaticArrays
using ClimateMachine
using ClimateMachine.SystemSolvers
using ClimateMachine.MPIStateArrays
using CUDA
using Random
using KernelAbstractions

import ClimateMachine.MPIStateArrays: array_device

ClimateMachine.init(; fix_rng_seed = true)

@kernel function multiply_by_A!(x, A, y, n1, n2)
    I = @index(Global)
    for i in 1:n1
        tmp = zero(eltype(x))
        for j in 1:n2
            tmp += A[i, j, I] * y[j, I]
        end
        x[i, I] = tmp
    end
end

let
    if CUDA.has_cuda_gpu()
        Arrays = [Array, CuArray]
    else
        Arrays = [Array]
    end

    for ArrayType in Arrays
        for T in [Float32, Float64]
            ϵ = eps(T)

            @testset "($ArrayType, $T) Basic Test" begin
                Random.seed!(42)

                # Test 1: Basic Functionality
                n = 100   # size of local (batch) matrix
                ni = 100  # batch size

                b = ArrayType(randn(n, ni))  # rhs
                x = ArrayType(randn(n, ni))  # initial guess
                x_ref = similar(x)

                A = ArrayType(randn((n, n, ni)) ./ sqrt(n))
                for i in 1:n
                    A[i, i, :] .+= 10i
                end

                ss = size(b)[1]
                bgmres = BatchedGeneralizedMinimalResidual(
                    b,
                    n,
                    ni,
                    M = ss,
                    atol = ϵ,
                    rtol = ϵ,
                )

                # Define the linear operator
                function closure_linear_operator_multi!(A, n1, n2, n3)
                    function linear_operator!(x, y)
                        device = array_device(x)
                        if isa(device, CPU)
                            groupsize = Threads.nthreads()
                        else # isa(device, CUDADevice)
                            groupsize = 256
                        end
                        event = Event(device)
                        event = multiply_by_A!(device, groupsize)(
                            x,
                            A,
                            y,
                            n1,
                            n2,
                            ndrange = n3,
                            dependencies = (event,),
                        )
                        wait(device, event)
                        nothing
                    end
                end
                linear_operator! = closure_linear_operator_multi!(A, size(A)...)

                # Now solve
                linearsolve!(
                    linear_operator!,
                    nothing,
                    bgmres,
                    x,
                    b;
                    max_iters = Inf,
                )

                # reference solution
                for i in 1:ni
                    x_ref[:, i] = A[:, :, i] \ b[:, i]
                end

                @test norm(x - x_ref) < 1000ϵ
            end

            ###
            # Test 2: MPI State Array
            ###
            @testset "($ArrayType, $T) MPIStateArray Test" begin
                Random.seed!(43)

                n1 = 8
                n2 = 3
                n3 = 10
                mpicomm = MPI.COMM_WORLD
                mpi_b = MPIStateArray{T}(mpicomm, ArrayType, n1, n2, n3)
                mpi_x = MPIStateArray{T}(mpicomm, ArrayType, n1, n2, n3)
                mpi_A = ArrayType(randn(n1 * n2, n1 * n2, n3))

                mpi_b.data[:] .= ArrayType(randn(n1 * n2 * n3))
                mpi_x.data[:] .= ArrayType(randn(n1 * n2 * n3))

                bgmres = BatchedGeneralizedMinimalResidual(
                    mpi_b,
                    n1 * n2,
                    n3,
                    M = n1 * n2,
                    atol = ϵ,
                    rtol = ϵ,
                )

                # Now define the linear operator
                function closure_linear_operator_mpi!(A, n1, n2, n3)
                    function linear_operator!(x, y)
                        alias_x = reshape(x.data, (n1, n3))
                        alias_y = reshape(y.data, (n1, n3))
                        device = array_device(x)
                        if isa(device, CPU)
                            groupsize = Threads.nthreads()
                        else # isa(device, CUDADevice)
                            groupsize = 256
                        end
                        event = Event(device)
                        event = multiply_by_A!(device, groupsize)(
                            alias_x,
                            A,
                            alias_y,
                            n1,
                            n2,
                            ndrange = n3,
                            dependencies = (event,),
                        )
                        wait(device, event)
                        nothing
                    end
                end
                linear_operator! =
                    closure_linear_operator_mpi!(mpi_A, size(mpi_A)...)

                # Now solve
                linearsolve!(
                    linear_operator!,
                    nothing,
                    bgmres,
                    mpi_x,
                    mpi_b;
                    max_iters = Inf,
                )

                # check all solutions
                norms = -zeros(n3)
                for cidx in 1:n3
                    sol =
                        Array(mpi_A[:, :, cidx]) \
                        Array(mpi_b.data[:, :, cidx])[:]
                    norms[cidx] = norm(sol - Array(mpi_x.data[:, :, cidx])[:])
                end
                @test maximum(norms) < 8000ϵ
            end

            ###
            # Test 3: Columnwise test
            ###
            @testset "(Array, $T) Columnwise Test" begin

                Random.seed!(2424)
                function closure_linear_operator_columwise!(A, tup)
                    function linear_operator!(y, x)
                        alias_x = reshape(x, tup)
                        alias_y = reshape(y, tup)
                        for i6 in 1:tup[6]
                            for i4 in 1:tup[4]
                                for i2 in 1:tup[2]
                                    for i1 in 1:tup[1]
                                        tmp = alias_x[i1, i2, :, i4, :, i6][:]
                                        tmp2 = A[i1, i2, i4, i6] * tmp
                                        alias_y[i1, i2, :, i4, :, i6] .=
                                            reshape(tmp2, (tup[3], tup[5]))
                                    end
                                end
                            end
                        end
                    end
                end

                tup = (3, 4, 7, 6, 5, 2)
                B = [
                    randn(tup[3] * tup[5], tup[3] * tup[5])
                    for
                    i1 in 1:tup[1],
                    i2 in 1:tup[2], i4 in 1:tup[4], i6 in 1:tup[6]
                ]
                columnwise_A = [
                    B[i1, i2, i4, i6] + 10I
                    for
                    i1 in 1:tup[1],
                    i2 in 1:tup[2], i4 in 1:tup[4], i6 in 1:tup[6]
                ]
                # taking the inverse of A isn't great, but it is convenient
                columnwise_inv_A = [
                    inv(columnwise_A[i1, i2, i4, i6])
                    for
                    i1 in 1:tup[1],
                    i2 in 1:tup[2], i4 in 1:tup[4], i6 in 1:tup[6]
                ]
                columnwise_linear_operator! =
                    closure_linear_operator_columwise!(columnwise_A, tup)
                columnwise_inverse_linear_operator! =
                    closure_linear_operator_columwise!(columnwise_inv_A, tup)

                mpi_tup = (tup[1] * tup[2] * tup[3], tup[4], tup[5] * tup[6])
                b = randn(mpi_tup)
                x = copy(b)
                columnwise_inverse_linear_operator!(x, b)
                x +=
                    randn((tup[1] * tup[2] * tup[3], tup[4], tup[5] * tup[6])) *
                    0.1

                reshape_tuple_f = tup
                permute_tuple = (5, 3, 1, 4, 2, 6)

                bgmres = BatchedGeneralizedMinimalResidual(
                    b,
                    tup[3] * tup[5],
                    tup[1] * tup[2] * tup[4] * tup[6];
                    M = tup[3] * tup[5],
                    forward_reshape = tup,
                    forward_permute = permute_tuple,
                    atol = 10ϵ,
                    rtol = 10ϵ,
                )

                x_exact = copy(x)
                linearsolve!(
                    columnwise_linear_operator!,
                    nothing,
                    bgmres,
                    x,
                    b,
                    max_iters = tup[3] * tup[5],
                )

                columnwise_inverse_linear_operator!(x_exact, b)
                @test norm(x - x_exact) / norm(x_exact) < 1000ϵ
                columnwise_linear_operator!(x_exact, x)
                @test norm(x_exact - b) / norm(b) < 1000ϵ
            end
        end
    end
end


#ENV["MOSEKLM_LICENSE_FILE"]="/home/ryancw/mosek/mosek.lic" #needed on cluster
using JuMP, LinearAlgebra, Suppressor, StatsBase, CSV, DataFrames, Compat, Test, Random
include("exact_approaches.jl")
include("alt_min.jl")
include("convexpenalties.jl")

results_template = DataFrame(n=Int[], r=Int[], p=Real[], gamma=Real[], err=Real[], rnk=Int[], solvetime=Real[], nc=Int[], gap=Real[], cuts=Int[])

results_exact=similar(results_template, 0)

for ARG in ARGS
    for iter in 1:1
        p=0.2
        n=20
        r=1
        seeds=1:20
        gammaMults=[100.0, 500.0]
        array_num = parse(Int, ARG)

        theSeed=seeds[(array_num-1)÷2+1]
        gamma=gammaMults[(array_num-1)%2+1]
        Random.seed!(theSeed)
        M_L=randn(n, r)
        M_L=round.(1000*M_L)./1000
        M_R=randn(n, r)
        M_R=round.(1000*M_R)./1000
        M=M_L*M_R'

        @show maximum(M)
        numEntriesSampled=Int(round(p*n^2))
        coords=[repeat(1:n, inner=size(1:n,1)) repeat(1:n, outer=size(1:n,1))];
        entries=sample(1:n^2, Int(numEntriesSampled), replace=false)

        theIndices=zeros(n,n)
        for t=1:Int(numEntriesSampled)
            theIndices[coords[entries[t],1], coords[entries[t],2]]=1.0
        end

        k=r

        t_GD=@elapsed X_GD,LB, UB, Y_relax=getMatrixFrobeniusNorm_greedy(coords, entries, M, n, numEntriesSampled, k, gamma, true)

        @show "SDP lower bound is: " LB
        u,sigma,v,=svd(X_GD)
        rnk_GD=sum(sigma.>1e-2);
        err_GD = norm(X_GD.-M)/norm(M);

        t_AM=@elapsed X_AM,=alternatingminimization_matrixcompletion(coords, entries, M, n, numEntriesSampled, k, gamma, 50, X_GD)

        # Test solution on overall data.
        u,sigma,v,=svd(X_AM)
        @show rnk_AM=sum(sigma.>1e-2);
        @show err_AM = norm(X_AM.-M)/norm(M);

        u_0=u[:,1:k]
        v_0=(Diagonal(sigma[1:k])*v[:,1:k]')'

        t_EX=@elapsed X_EX, nc_EX, gap_EX=getMatrixFrobeniusNorm_exact(coords, entries, M, n, numEntriesSampled, k, gamma, false, u_0, v_0)
        u,v,=svd(X_EX)
        @show rnk_EX=sum(v.>1e-2);
        @show err_EX = norm(X_EX.-M)/norm(M);

        push!(results_exact, [n, r, p, gamma, err_EX, rnk_EX, t_EX, nc_EX, gap_EX, theSeed])
        CSV.write("sensitivity_analysis_grb_p"*string(p)*"_n_"*string(n)*".csv", results_exact, append=true)

        end
    end


"""
GradientGrassmann is an optimisation methdod that keeps the MPS in left-canonical form, and
treats the tensors as points on Grassmann manifolds. It then applies one of the standard
gradient optimisation methods, e.g. conjugate gradient, to the MPS, making use of the
Riemannian manifold structure. A preconditioner is used, so that effectively the metric used
on the manifold is that given by the Hilbert space inner product.

The arguments to the constructor are
method = OptimKit.ConjugateGradient
    The gradient optimisation method to be used. Should either be an instance or a subtype
    of `OptimKit.OptimizationAlgorithm`. If it's an instance, this `method` is simply used
    to do the optimisation. If it's a subtype, then an instance is constructed as
    `method(; maxiter=maxiter, verbosity=verbosity, gradtol=tol)`

finalize! = OptimKit._finalize!
    A function that gets called once each iteration. See OptimKit for details.

tol = Defaults.tol
maxiter = Defaults.maxiter
verbosity = 2
    Arguments passed to the `method` constructor. If `method` is an instance of
    `OptimKit.OptimizationAlgorithm`, these argument are ignored.

In other words, by default conjugate gradient is used. One can easily set `tol`, `maxiter`
and `verbosity` for it, or switch to LBFGS or gradient descent by setting `method`. If more
control is wanted over things like specifics of the linesearch, CG flavor or the `m`
parameter of LBFGS, then the user should create the `OptimKit.OptimizationAlgorithm`
instance manually and pass it as `method`.
"""
struct GradientGrassmann <: Algorithm
    method::OptimKit.OptimizationAlgorithm
    finalize!::Function

    function GradientGrassmann(; method = ConjugateGradient,
                               finalize! = OptimKit._finalize!,
                               tol = Defaults.tol,
                               maxiter = Defaults.maxiter,
                               verbosity = 2)
        if isa(method, OptimKit.OptimizationAlgorithm)
            # We were given an optimisation method, just use it.
            m = method
        elseif method <: OptimKit.OptimizationAlgorithm
            # We were given an optimisation method type, construct an instance of it.
            m = method(; maxiter=maxiter, verbosity=verbosity, gradtol=tol)
        else
            msg = "method should be either an instance or a subtype of OptimKit.OptimizationAlgorithm."
            throw(ArgumentError(msg))
        end
        return new(m, finalize!)
    end
end

function find_groundstate(state::InfiniteMPS, H::Hamiltonian, alg::GradientGrassmann,
                          pars=params(state, H))
    method = alg.method
    method === nothing && (method = alg.methodtype(; maxiter=alg.maxiter,
                                                       verbosity=alg.verbosity,
                                                       gradtol=alg.tol))
    res = optimize(GrassmannInfiniteMPS.fg, (state, pars), method;
                   transport! = GrassmannInfiniteMPS.transport!,
                   retract = GrassmannInfiniteMPS.retract,
                   inner = GrassmannInfiniteMPS.inner,
                   scale! = GrassmannInfiniteMPS.scale!,
                   add! = GrassmannInfiniteMPS.add!,
                   finalize! = alg.finalize!,
                   precondition = GrassmannInfiniteMPS.precondition,
                   isometrictransport = true)
    (x, fx, gx, numfg, normgradhistory) = res
    (state, pars) = x
    return state, pars, normgradhistory[end]
end

# We separate some of the internals needed for implementing GradientGrassmann into a
# submodule, to keep the MPSKit module namespace cleaner.
"""
A module for functions related to treating an InfiniteMPS in left-canonical form as a bunch
of points on Grassmann manifolds, and performing things like retractions and transports
on these Grassmann manifolds.

The module exports nothing, and all references to it should be qualified, e.g.
`GrassmannInfiniteMPS.fg`.
"""
module GrassmannInfiniteMPS

using ..MPSKit
using TensorKit
import TensorKitManifolds.Grassmann

"""
Compute the expectation value, and its gradient with respect to the tensors in the unit
cell as tangent vectors on Grassmann manifolds.
"""
function fg(x)
    (state, pars) = x
    # The partial derivative with respect to AL, al_d, is the partial derivative with
    # respect to AC times CR'.
    ac_d = [ac_prime(state.AC[v], v, state, pars) for v in 1:length(state)]
    al_d = [d*c' for (d, c) in zip(ac_d, state.CR)]
    g = [Grassmann.project(d, a) for (d, a) in zip(al_d, state.AL)]
    f = real(sum(expectation_value(state, pars.opp, pars)))
    return f, g
end

"""
Retract a left-canonical infinite MPS along Grassmann tangent `g` by distance `alpha`.
"""
function retract(x, g, alpha)
    (state, pars) = x
    yal = similar(state.AL)  # The end-point
    h = similar(g)  # The tangent at the end-point
    for i in 1:length(g)
        (yal[i], h[i]) = Grassmann.retract(state.AL[i], g[i], alpha)
    end
    y = (InfiniteMPS(yal, leftgauged=true), pars)
    return y, h
end

"""
Transport a tangent vector `h` along the retraction from `x` in direction `g` by distance
`alpha`. `xp` is the end-point of the retraction.
"""
function transport!(h, x, g, alpha, xp)
    (state, pars) = x
    for i in 1:length(state)
        h[i] = Grassmann.transport!(h[i], state.AL[i], g[i], alpha, xp[1].AL[i])
    end
    return h
end

"""
Euclidean inner product between two Grassmann tangents of an infinite MPS.
"""
function inner(x, g1, g2)
    (state, pars) = x
    tot = sum(Grassmann.inner(a, d1, d2) for (a, d1, d2) in zip(state.AL, g1, g2))
    return 2*real(tot)
end

"""
Scale a tangent vector by scalar `alpha`.
"""
scale!(g, alpha) = g .* alpha

"""
Add two tangents vectors, scaling the latter by `alpha`.
"""
add!(g1, g2, alpha) = g1 + g2 .* alpha

"""
Take the L2 Tikhonov regularised inverse of a matrix `m`.

The regularisation parameter is the larger of `delta` (the optional argument that defaults
to zero) and square root of machine epsilon. The inverse is done using an SVD.
"""
function reginv(m, delta=zero(eltype(m)))
    delta = max(delta, sqrt(eps(real(float(one(eltype(m)))))))
    U, S, Vdg = tsvd(m)
    Sinv = inv(real(sqrt(S^2 + delta^2*one(S))))
    minv = Vdg' * Sinv * U'
    return minv
end

"""
Precondition a given Grassmann tangent `g` at state `x` by the Hilbert space inner product.

This requires inverting the right MPS transfer matrix. This is done using `reginv`, with a
regularisation parameter that is the norm of the tangent `g`.
"""
function precondition(x, g)
    (state, pars) = x
    delta = min(real(one(eltype(state))), sqrt(inner(x, g, g)))
    crinvs = [reginv(cr, delta) for cr in state.CR]
    g_prec = [Grassmann.project(d[]*(crinv'*crinv), a)
              for (d, crinv, a) in zip(g, crinvs, state.AL)]
    return g_prec
end

end  # module GrassmannInfiniteMPS


#Compute reduced quantities for error checking without resorting to the "ncdiff" tool
function reductions(model, grid)
    mass = 0.0
    te = 0.0
    nx, nz = grid.nx, grid.nz
    dx, dz = grid.dx, grid.dz

    for k = 1:nz, i = 1:nx
        r = model.state[hs+i, hs+k, ID_DENS] + model.hy_dens_cell[hs+k]       # Density
        u = model.state[hs+i, hs+k, ID_UMOM] / r                           # U-wind
        w = model.state[hs+i, hs+k, ID_WMOM] / r                           # W-wind
        th = (model.state[hs+i, hs+k, ID_RHOT] + model.hy_dens_theta_cell[hs+k]) / r # Potential Temperature (theta)
        p = C0 * (r * th)^gamma      # Pressure
        t = th / (p0 / p)^(rd / cp)  # Temperature
        ke = r * (u * u + w * w)           # Kinetic Energy
        ie = r * cv * t                # Internal Energy
        mass = mass + r * dx * dz # Accumulate domain mass
        te = te + (ke + ie) * dx * dz # Accumulate domain total energy
        #println(r, u, w, th)
      end

    #    double glob[2], loc[2];
    #    loc[0] = mass;
    #    loc[1] = te;
    #    int ierr = MPI_Allreduce(loc,glob,2,MPI_DOUBLE,MPI_SUM,MPI_COMM_WORLD);
    #    mass = glob[0];
    #    te   = glob[1];
    return mass, te
end

function createSnapshot(model, grid)

    snapshot = Array{Float64}(undef, 4, grid.nx, grid.nz)
    #Store perturbed values in the temp arrays for output
    for k = 1:grid.nz
        for i = 1:grid.nx
            snapshot[1, i, k] =  model.state[i, hs+k, ID_DENS]
            snapshot[2, i, k] =
                model.state[i, hs+k, ID_UMOM] /
                (model.hy_dens_cell[hs+k] + model.state[i, hs+k, ID_DENS])
            snapshot[3, i, k] =
                model.state[i, hs+k, ID_WMOM] /
                (model.hy_dens_cell[hs+k] + model.state[i, hs+k, ID_DENS])
            snapshot[4, i, k] =
                (model.state[i, hs+k, ID_RHOT] + model.hy_dens_theta_cell[hs+k]) /
                (model.hy_dens_cell[hs+k] + model.state[i, hs+k, ID_DENS]) -
                model.hy_dens_theta_cell[hs+k] / model.hy_dens_cell[hs+k]
        end
    end

    return snapshot
end

#
#Output the fluid state (state) to a NetCDF file at a given elapsed model time (etime)
#The file I/O uses parallel-netcdf, the only external library required for this mini-app.
#If it's too cumbersome, you can comment the I/O out, but you'll miss out on some potentially cool graphics
function output(snapshots, grid, etimes, ncfile="output.nc")
  #Create new file
  isfile(ncfile) && rm(ncfile)
  _, nx, nz, _ = size(snapshots)
  nt = length(etimes)
  nccreate(ncfile, "dens", "x", collect(range(0,stop=grid.nx*grid.dx, length=grid.nx)), Dict("units"=>"m"),
                           "z", collect(range(0,stop=grid.nz*grid.dz, length=grid.nz)), Dict("units"=>"m"),
                           "time", etimes, Dict("units"=>"s"))
  nccreate(ncfile, "uwnd", "x", collect(range(0,stop=grid.nx*grid.dx, length=grid.nx)), Dict("units"=>"m"),
                           "z", collect(range(0,stop=grid.nz*grid.dz, length=grid.nz)), Dict("units"=>"m"),
                           "time", etimes, Dict("units"=>"s"))
  nccreate(ncfile, "wwnd", "x", collect(range(0,stop=grid.nx*grid.dx, length=grid.nx)), Dict("units"=>"m"),
                           "z", collect(range(0,stop=grid.nz*grid.dz, length=grid.nz)), Dict("units"=>"m"),
                           "time", etimes, Dict("units"=>"s"))
  nccreate(ncfile, "theta", "x", collect(range(0,stop=grid.nx*grid.dx, length=grid.nx)), Dict("units"=>"m"),
                           "z", collect(range(0,stop=grid.nz*grid.dz, length=grid.nz)), Dict("units"=>"m"),
                           "time", etimes, Dict("units"=>"s"))

                           #nccreate(ncfile, "uwnd", "x", nx, Dict("units"=>"m"), "z", nz, Dict("units"=>"m"), "time", nt, Dict("units"=>"s"))
  #nccreate(ncfile, "wwnd", "x", nx, Dict("units"=>"m"), "z", nz, Dict("units"=>"m"), "time", nt, Dict("units"=>"s"))
  #nccreate(ncfile, "theta", "x", nx, Dict("units"=>"m"), "z", nz, Dict("units"=>"m"), "time", nt, Dict("units"=>"s"))
  #exit()
  #Write the grid data to file with all the processes writing collectively
#  ncwrite(etimes, ncfile, "time")
  ncwrite(snapshots[1, :, :, 1:nt], ncfile, "dens")
  ncwrite(snapshots[2, :, :, 1:nt], ncfile, "uwnd")
  ncwrite(snapshots[3, :, :, 1:nt], ncfile, "wwnd")
  ncwrite(snapshots[4, :, :, 1:nt], ncfile, "theta")

end


using Test
using Random
using Statistics

using EnsembleKalmanProcesses.Observations

@testset "Observations" begin

    # Generate samples as vector of vectors
    sample_dim = 3  # sample dimension, i.e., number of elements per sample
    n_samples = 5   # number of samples
    samples = [vec(i * ones(sample_dim)) for i in 1:n_samples]
    data_names = ["d$(string(i))" for i in 1:sample_dim]
    obs = Obs(samples, data_names)
    @test obs.data_names == data_names
    @test obs.mean == [3.0, 3.0, 3.0]
    @test obs.obs_noise_cov == 2.5 * ones(3, 3)

    # Generate samples as vector of vectors, pass a covariance to Obs
    covar = ones(sample_dim, sample_dim)
    obs = Obs(samples, covar, data_names)
    @test obs.obs_noise_cov == covar
    covar_wrong_dims = ones(sample_dim, sample_dim - 1)
    @test_throws AssertionError Obs(samples, covar_wrong_dims, data_names)

    # Generate samples as a 2d-array (each row corresponds to 1 sample)
    samples_t = vcat([i * ones(sample_dim)' for i in 1:n_samples]...)
    samples = permutedims(samples_t, (2, 1))
    obs = Obs(samples, data_names)
    @test obs.mean == [3.0, 3.0, 3.0]
    @test obs.obs_noise_cov == 2.5 * ones(3, 3)

    # Generate samples as a 2d-array (each row corresponds to 1 sample),
    # pass a covariance to Obs
    obs = Obs(samples, covar, data_names)
    @test obs.obs_noise_cov == covar
    @test_throws AssertionError Obs(samples, covar_wrong_dims, data_names)
    @test_throws AssertionError Obs(samples_t, covar, data_names) #as default is data are columns

    ## Edge cases:
    # Generating a single sample (a column vector)
    sample = reshape([1.0, 2.0, 3.0], 3, 1)
    data_name = "d1"
    obs = Obs(sample, data_name)
    @test obs.mean == vec(sample)
    @test obs.obs_noise_cov == nothing

    #Generating a single sample (vector of vector)
    sample = [[1.0, 2.0, 3.0]]
    data_name = "d1"
    obs = Obs(sample, data_name)
    @test obs.mean == sample[1]
    @test obs.obs_noise_cov == nothing

    # Generating a single sample with cov (a column vector)
    sample = reshape([1.0, 2.0, 3.0], 3, 1)
    obs_noise_cov = ones(3, 3)
    data_name = "d1"
    obs = Obs(sample, obs_noise_cov, data_name)
    @test obs.mean == vec(sample)
    @test obs.obs_noise_cov == obs_noise_cov

    #Generating a single sample with cov (vector of vector)
    sample = [[1.0, 2.0, 3.0]]
    obs_noise_cov = ones(3, 3)
    data_name = "d1"
    obs = Obs(sample, obs_noise_cov, data_name)
    @test obs.mean == sample[1]
    @test obs.obs_noise_cov == obs_noise_cov

    # Generate 1D-samples (data are columns of the row vector) -- this should result in scalar values for the mean and obs_noise_cov
    sample = reshape([1.0, 2.0, 3.0], 1, 3)
    data_name = "d1"
    obs = Obs(sample, data_name)
    @test obs.mean == 2.0
    @test obs.obs_noise_cov == 1.0

    # Generate 1D-samples (Vector of vector)
    sample = [[1.0], [2.0], [3.0]]
    data_name = "d1"
    obs = Obs(sample, data_name)
    @test obs.mean == 2.0
    @test obs.obs_noise_cov == 1.0

    # Generate 1D-samples, but also passing a known "1x1 covarince matrix" (stored as a variance)
    # (data are columns of the row vector)
    # this should result in scalar values for the mean and obs_noise_cov
    sample = reshape([1.0, 2.0, 3.0], 1, 3)
    obs_noise_cov = reshape([3.0], 1, 1) #needs to be of type Array{,2}
    data_name = "d1"
    obs = Obs(sample, obs_noise_cov, data_name)
    @test obs.mean == 2.0
    @test obs.obs_noise_cov == 3.0

    # Generate 1D-samples, but also passing a known "1x1 covarince matrix"
    # (data are vector of vectors)
    sample = [[1.0], [2.0], [3.0]]
    obs_noise_cov = reshape([3.0], 1, 1) #needs to be of type Array{,2}
    data_name = "d1"
    obs = Obs(sample, obs_noise_cov, data_name)
    @test obs.mean == 2.0
    @test obs.obs_noise_cov == 3.0

end


if length(ARGS) >= 1
  nw = parse(ARGS[1])
  if nw >1
     addprocs(nw)
  end
else
  addprocs()
end

import Distributions; @everywhere using Distributions
import ABC; @everywhere using ABC

@everywhere include(joinpath(Pkg.dir("ABC"),"src/composite.jl"))
@everywhere using CompositeDistributions
@everywhere import Compat.view

# Set Prior for Population Parameters
@everywhere d1 = Rayleigh(0.3)
@everywhere d2 = MvNormal([0.0, 1.0], ones(2))
@everywhere param_prior = CompositeDist( ContinuousDistribution[d1,d2] )

# Code to generate simulated data given array of model parameters
@everywhere num_data_default = 100
@everywhere num_outputs = 2
@everywhere function gen_data(theta::Array, n::Integer = num_data_default)
  data = Array(Float64,(3,n))
  data[1,:] = rand(Rayleigh(theta[1]),n)
  data[2:3,:] = rand(MvNormal(theta[2:3],ones(2)),n)
  return data
end

# Function to adjust originally proposed model parameters, so that they will be valid
@everywhere function normalize_theta13_pos!(theta::Array)
 theta[1] = abs(theta[1])
 theta[3] = abs(theta[3])
 return theta
end

# Function to test if the proposed model parameter are valid
@everywhere function is_valid_theta13_pos(theta::Array)
  theta[1]>0.0  && theta[3]>0.0
end

# True Population Parameters
@everywhere theta_true = [0.3, 0.0, 1.0]

# Tell ABC what it needs to know for a simulation
@everywhere in_parallel = length(workers()) > 1 ? true : false
@everywhere abc_plan = abc_pmc_plan_type(gen_data,ABC.calc_summary_stats_mean_var,ABC.calc_dist_max, param_prior, is_valid=is_valid_theta13_pos,num_max_attempt=10000, in_parallel=in_parallel);

# Generate "true/observed "data"
data_true = gen_data(theta_true)   # Draw "real" data from same model as for analysis
ss_true = abc_plan.calc_summary_stats(data_true)
#println("theta= ",theta_true," ss= ",ss_true, " d= ", 0.)

# Run ABC simulation
@time pop_out = run_abc(abc_plan,ss_true,in_parallel=in_parallel);

#=
# Optional plotting of results
using PyPlot

hist(pop_out.weights*length(pop_out.weights));
hist(pop_out.dist);

num_grid_x = 100
num_grid_y = 100
limit = 1.0
x = collect(linspace(theta_true[1]-limit,theta_true[1]+limit,num_grid_x));
y = collect(linspace(theta_true[2]-limit,theta_true[2]+limit,num_grid_y));
z = zeros(Float64,(num_param,length(x),length(y)))
for i in 1:length(x), j in 1:length(y)
    z[1,i,j] = x[i]
    z[2,i,j] = y[j]
end
z = reshape(z,(num_param,length(x)*length(y)))
zz = [ ABC.pdf(ABC.GaussianMixtureModelCommonCovar(pop_out.theta,pop_out.weights,ABC.cov_weighted(pop_out.theta',pop_out.weights)),vec(z[:,i])) for i in 1:size(z,2) ]
zz = reshape(zz ,(length(x),length(y)));
levels = [exp(-0.5*i^2)/sqrt(2pi^num_param) for i in 0:5];
PyPlot.contour(x,y,zz',levels);
plot(pop_out.theta[1,:],pop_out.theta[2,:],".");
=#


export embedding,
       View,
       CoRegularizedMultiView,
       KernelProduct,
       KernelAddition

"""
A view

```julia
struct View
    embedder::NgLaplacian
    lambda::Float64
end
```
# Members
    - embedder::EigenvectorEmbedder
    - lambda::Float64

"""
struct View
    nev::Integer
    lambda::Float64
end

"""
# Co-regularized Multi-view Spectral Clustering

### Abhishek Kumar, Piyush Rai, Hal Daumé

"""
struct CoRegularizedMultiView <: EigenvectorEmbedder
    max_iterations::Integer
    views::Vector{View}
end
function CoRegularizedMultiView(views::Vector{View}; max_iterations::Integer=500)
    return CoRegularizedMultiView(max_iterations, views)
end
"""
```julia
embedding(cfg::CoRegularizedMultiView, X::Vector)
```

# Arguments
    - `cfg::CoRegularizedMultiView`
    - `X::Vector{Graph}`

An example that shows how to use this methods is provied in the Usage section of the manual
"""
function embedding(cfg::CoRegularizedMultiView, X::Vector{Graph}; disagreement::Union{Nothing,Vector} = nothing)
    U = Vector{Matrix}(undef, length(cfg.views))

    Laplacians = [sparse(NormalizedAdjacency(CombinatorialAdjacency(adjacency_matrix(X[i], dir=:both))))  for i=1:length(cfg.views) ]
    #Initialize all U(v),2≤v≤m$
    for i=2:length(cfg.views)
        U[i] = embedding(NgLaplacian(cfg.views[i].nev, false), Laplacians[i])
    end
    curr_objective = -Inf
    prev_objective = 0
    best_objective = Inf
    iterations_without_improvement = 0
    threshold = 0.00001
    iteration = 0
    while  iteration < cfg.max_iterations && (abs(curr_objective - prev_objective)  > threshold) && (iterations_without_improvement < 8)
        for i=1:length(cfg.views)
           L = Laplacians[i]
            for j=1:length(cfg.views)
                if (j!=i)
                    L = L + cfg.views[j].lambda*U[j]*U[j]'
                end
            end
            U[i] = embedding(NgLaplacian(cfg.views[i].nev, false), L)
            prev_objective = curr_objective
            curr_objective = sum([tr((U[d]*U[d]')*(U[j]*U[j]')) for j=1:length(U)  for d=1:length(U)])
            if curr_objective < best_objective
                best_objective = curr_objective
                iterations_without_improvement = 0
            else
                iterations_without_improvement += 1
            end
            if (disagreement!=nothing)
                push!( disagreement, curr_objective)
            end
            iteration += 1
        end
    end
    return U[1]
end

struct KernelAddition <: EigenvectorEmbedder
    embedder::EigenvectorEmbedder
end
function embedding(cfg::KernelAddition, X::Vector{Graph})
    W = adjacency_matrix(X[1])
    for j=2:length(X)
        W += adjacency_matrix(X[j])
    end
    W = CombinatorialAdjacency(W)
    return  embedding(cfg.embedder, W)
end
struct KernelProduct <: EigenvectorEmbedder
    embedder::EigenvectorEmbedder
end
function embedding(cfg::KernelProduct, X::Vector{Graph})
    W = adjacency_matrix(X[1])
    for j=2:length(X)
        W .*= adjacency_matrix(X[j])
    end
    W = CombinatorialAdjacency(W)
    return embedding(cfg.embedder, W)
end

"""
type LargeScaleMultiView
# Large-Scale Multi-View Spectral Clustering via Bipartite Graph. In AAAI (pp. 2750-2756).
## Li, Y., Nie, F., Huang, H., & Huang, J. (2015, January).

[Matlab implementation](https://github.com/zzz123xyz/MVSC/blob/master/MVSC.m)
# Members
- `k::Integer`. Number of clusters.
- `n_salient_points::Integer`. Number of salient points.
- `k_nn::Integer`. k nearest neighbors.
- 'gamma::Float64`.
"""
struct LargeScaleMultiView
	k::Integer
	n_salient_points::Integer
	k_nn::Integer
	gamma::Float64
end
"""
# Parameters
- `cfg::LargeScaleMultiView`
- `data::Vector`. An array of views.
"""

#=function embedding(cfg::LargeScaleMultiView, data::Vector)
	niters = 10;
    n =
	V = length(data)
	salient_points =
    rest_of_points =

	a = fill(1/nbclusters, [V]);
	for v = 1:V
	    RestPnt = data{v}(:,RestPntInd)';
    PairDist = pdist2(RestPnt,SltPnt(:,dim_V_ind1(v): dim_V_ind2(v)));
    [score, ind] = sort(PairDist,2);
    ind = ind(:,1:k);

%*****
%make a Indicator Mask to record j \in \theta_i
    IndMask = zeros(n - nbSltPnt, nbSltPnt);
    for i = 1:n - nbSltPnt
         IndMask(i, ind(i,:)) = 1;
    end

    Kernel = exp(-(PairDist).^2 ./ (2*param^2));
    Kernel = Kernel.*IndMask;

    SumSltKnl = repmat(sum(Kernel, 2),[1,nbSltPnt]);
    Z{v} = Kernel ./ SumSltKnl;
    Dc{v} = diag(sum(Z{v},1)+eps);
    Dr{v} = diag(sum(Z{v},2));
    D{v} = blkdiag(Dr{v},Dc{v});

    tmp1 = zeros(n);
    tmp1(1:n-nbSltPnt,n-nbSltPnt+1:end) = Z{v};
    tmp1(n-nbSltPnt+1:end,1:n-nbSltPnt) = Z{v}';
    W{v} = tmp1;

    L{v} = eye(n) - (D{v}^-0.5) * W{v} * (D{v}^-0.5);
end

for t = 1:niters
    L_sum = zeros(n, n);
    Z_hat = zeros(n - nbSltPnt, nbSltPnt);

    for v = 1:V
        Z_hat = Z_hat + a(v)^gamma*Z{v}*(Dc{v})^(-0.5);
    end

    % compute G according to (14)
    [Gx_a, S, Gu_a] = svd(Z_hat, 'econ');
    Gx = Gx_a(:,1:nbclusters);
    Gu = Gu_a(:,1:nbclusters);
    G = [Gx', Gu']';

    for v = 1:V
        h(v) = trace(G'*L{v}*G); %*** h(v) = trace(G'*L{v}*G); dim of G mismatch L
    end

    % compute a(v) according to (10)
    tmp1 = (gamma .* h).^(1/(1-gamma)) ;
    a = tmp1 ./ sum(tmp1);

    [Y, C] = kmeans(G, nbclusters);

    % compute the value of objective function (5)
    for v = 1:V
        L_sum = L_sum + a(v)^gamma*L{v};
    end
    obj_value(t) = trace(G'*L_sum*G);  %obj_value(t) = trace(G'*L_sum*G);

end
=#


export Avac, vacA, vacExpVal

Avac(A::Union{BosonDestroy,FermionDestroy,σminus}) = scal(0)
Avac(A::Union{BosonCreate,FermionCreate,σplus}) = A
# vacuum is an eigenstate of σz
Avac(A::σ) = (A.a == z) ? scal(-1) : A
Avac(A::OpSum) = Avac(A.A) + Avac(A.B)
Avac(A::OpSumAnalytic) = OpSumAnalytic(A.ind,Avac(A.A))
Avac(A::OpProd) = begin
    Bv = Avac(A.B)
    Bv != A.B && return Avac(A.A*Bv)
    # if A.A and A.B commute, try what happens with Avac(A.B*A.A)
    if comm(A.A,A.B) == scal(0)
        Av = Avac(A.A)
        Av != A.A && return Avac(A.B*Av)
    end
    # did not find a way to simplify
    A
end
Avac(A::Scalar) = A

vacA(A::Union{BosonDestroy,FermionDestroy,σminus}) = A
vacA(A::Union{BosonCreate,FermionCreate,σplus}) = scal(0)

# vacuum is an eigenstate of σz
vacA(A::σ) = (A.a == z) ? scal(-1) : A
vacA(A::OpSum) = vacA(A.A) + vacA(A.B)
vacA(A::OpSumAnalytic) = OpSumAnalytic(A.ind,vacA(A.A))
vacA(A::OpProd) = begin
    if A.A isa Scalar
        A.A*vacA(A.B)
    else
        # for applying ⟨0|AB, see if ⟨0|A changes A, and if so,
        # keep going with the new operator
        vA = vacA(A.A)
        vA==A.A ?  A : vacA(vA * A.B)
    end
end
vacA(A::Scalar) = A

"""
    Avac(A::Operator), vacA(A::Operator)

Simplify operator by assuming it is applied to the vacuum from the left or
right, respectively. To be precise, `Avac(A)` returns ``A'`` such that ``A'|0⟩ =
A|0⟩``, while `vacA(A)` does the same for ``⟨0|A``."""
Avac, vacA

"""
    vacExpVal(A::Operator,S::Operator=scal(1))

Calculate the vacuum expectation value ``⟨0|S^\\dagger A S|0⟩``, i.e., the
expectation value ``⟨ψ|A|ψ⟩`` for the state defined by ``|ψ⟩= S|0⟩```.
"""
function vacExpVal(A::Operator,stateop::Operator=scal(1))
    # simplify down as much as possible by applying vacuum from left and right
    vsAsv = vacA(Avac(stateop' * A * stateop))
    # only operators that should survive here as operators are σs
    _vacExpVal(vsAsv)
end
_vacExpVal(A::Scalar) = A
# the terms that survive until here have at most a single σ of any particle
# so it does not matter if we are inside a product
_vacExpVal(A::σ) = A.a == z ? scal(-1) : scal(0)
_vacExpVal(A::OpSum) = _vacExpVal(A.A) + _vacExpVal(A.B)
_vacExpVal(A::OpSumAnalytic) = OpSumAnalytic(A.ind,_vacExpVal(A.A))
# we know that the operators here commute (all a and a† have disappeared, and at most a single σ remaining for each particle)
_vacExpVal(A::OpProd) = _vacExpVal(A.A) * _vacExpVal(A.B)


using MonteCarloMarkovKernels
using StaticArrays
import Statistics.mean
using Random
using LinearAlgebra
using Test

function makelogMVN(μ::SVector{d, Float64}, Σ::SMatrix{d, d, Float64}) where d
  invΣ = inv(Σ)
  lognc = - 0.5 * d * log(2 * π) - 0.5 * logdet(Σ)
  function lpi(x::SVector{d, Float64})
    v = x - μ
    return lognc - 0.5*dot(v, invΣ * v)
  end
  return lpi
end

function testd(d::Int64)
  zerod = zeros(d)
  Szerod = SVector{d, Float64}(zerod)
  μ = randn(d)
  A = randn(d, d)
  Σ = A * A'

  logtarget = makelogMVN(SVector{d, Float64}(μ), SMatrix{d, d, Float64}(Σ))

  # P_AM = makeAMKernel(logtarget, d, Random.GLOBAL_RNG, d^2)
  P_AM = makeAMKernel(logtarget, Matrix{Float64}(I, d, d), Random.GLOBAL_RNG, d^2)
  chain = simulateChainProgress(P_AM, Szerod, d*2^21)

  @test maximum(abs.(mean(chain) - μ)) < 0.1
  @test maximum(abs.(MonteCarloMarkovKernels.cov(chain) - Σ)) < 0.1

  @test 0.234 < P_AM(:acceptanceRate) < 0.45
  @test P_AM(:meanEstimate) ≈ mean(chain) atol=0.01
  @test maximum(abs.(MonteCarloMarkovKernels.cov(chain) - P_AM(:covEstimate))) < 0.01

end

Random.seed!(12345)

for d in 1:3
  testd(d)
end


using Point2DTools
using Test

# Computational Geometry, Third Edition, P.243 - not in order
const hull1 = Point2D[(0.1, 0.35), (0.13, 0.22), (0.07, 0.15), (0.12, 0.3), (0.16,0.2)]
const hull1vx = Point2D[(0.07, 0.15), (0.1, 0.35), (0.16, 0.2), (0.07, 0.15)]

const rpoints = Point2D[[408, 22], [424, 65], [484, 416], [283, 161], [98, 358], [126, 288], [309, 56], [63, 410], [221, 8] ,[71, 378] ,[73, 138], [305, 105], [397, 475], [376, 212], [292, 304], [498, 303]]

@testset "Point2DTools.jl" begin
   @test hull1[convexperm(hull1)] == hull1vx
   @test convex(hull1) == hull1vx
   @test convexperm(rpoints) == ([8, 13, 3, 16, 2, 1, 9, 11, 8])
   @test bounds([Point2D(0,0), Point2D(1,2), Point2D(2,1)]) == [Point2D(0,0), Point2D(2,2)]
   @test scalefns(Point2D(0.,0.), Point2D(10., 10.), 10, 100)[2](10) == 10.0
end


module Chair

export chair, chair_system, ChairElem, ChairPTile

using Luxor
import Luxor.transform
using Colors
using StructEquality

using ...CoreDefs

@def_structequal struct ChairElem <: DGroupElem
    angle :: Int # should be 0 ≤ i ≤ 3
    x :: Int
    y :: Int
end

struct ChairPTile end

function chair(angle, x, y)
    return ChairElem(angle, x, y) => ChairPTile()
end

function sin(i :: Integer)
    if i == 0 || i == 2
        return 0
    elseif i == 1
        return 1
    else
        return -1
    end
end

function cos(i :: Integer)
    return sin((i+1) % 4 )
end

function Base.:*(s :: ChairElem, t :: ChairElem)
    return ChairElem(
        (s.angle + t.angle) % 4,
        s.x + cos(s.angle)*t.x + sin(s.angle)*t.y,
        s.y - sin(s.angle)*t.x + cos(s.angle)*t.y
    )
end
function Base.:*(s :: ChairElem, t :: Pair{Integer, Integer})
    return [
        s.x + cos(s.angle)*t[1] + sin(s.angle)*t[2],
        s.y - sin(s.angle)*t[1] + cos(s.angle)*t[2],
    ]
end

function CoreDefs.dilate(λ :: Int, t :: ChairElem)
    return ChairElem(t.angle, λ*t.x, λ*t.y)
end
function CoreDefs.id(::ChairElem)
    return ChairElem(0,0,0)
end
function Base.inv(g::ChairElem)
    return ChairElem(-g.angle,0,0)*ChairElem(0,-g.x, -g.y)
end

chair_subst = Dict([
    (ChairPTile(), [
        ChairElem(0, -1, -1) => ChairPTile(),
        ChairElem(0, 1, 1) => ChairPTile(),
        ChairElem(1, -1, 5) => ChairPTile(),
        ChairElem(3, 5, -1) => ChairPTile(),
    ])
])

chair_system = SubSystem(chair_subst, 2)

function CoreDefs.embed_aff(t :: ChairElem)
    return float([
        cos(t.angle), -sin(t.angle),
        sin(t.angle), cos(t.angle),
        t.x, t.y])
end

function CoreDefs.draw(::ChairPTile, action)
    Luxor.poly([
        Point(-1, -1),
        Point(-1, 3),
        Point(1, 3),
        Point(1, 1),
        Point(3, 1),
        Point(3, -1),
    ], close = true, action)
end

const origin = chair(0,0,0)

function in_bounds(t :: Pair{ChairElem, ChairPTile}, n, window)
    return 2^(n+1)*(abs(t[1].x)) < window.w + 2^(n+1)*3 && 2^(n+1)*(abs(t[1].y)) < window.h + 2^(n+1)*3
end

function squares(g)
    return g*[
        (0,0),
        (0,2),
        (2,0),
    ]
end
function adjacent(xs,ys)
    for x in xs
        for y in ys
            if max(abs.(x-y)...) ≤ 2
                return true
            end
        end
    end
    return false
end
function collar_in(tiling, g::ChairElem)
    result = Pair{ChairElem, ChairPTile}[]
    tiling_dict = Dict(tiling)
    squares_g = tiling_dict[g]
    for (h,l) in tiling_dict
        if adjacent(squares(g), squares(h))
            push!(result, (h,l))
        end
    end
    return result
end

end