### A Pluto.jl notebook ###
# v0.16.0

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

# ╔═╡ f66e452c-97db-11eb-23fc-bd95da10e2d6
using Plots, FFTW, TestImages, PlutoUI, Images, ImageShow

# ╔═╡ adaa78fa-d805-45e6-ba5f-d91a5d4823d6
using ImageContrastAdjustment

# ╔═╡ 09fc6764-2050-4276-8a14-3596368d0c32
md"""
# How much data do we need?

One of the challenges of information representation generally is how much data do we need to represent an object/concept sufficiently. It should be noted, that "represent sufficiently" is an ill-defined term, since this judgement will be application specific.

In order to address this question, we are going to assume that a concept can be represented as a function. A concept can be quite general but in this notebook we will be using images as our concepts. That is, an image represents some function and this function can be represented as a combination of __basis functions__. So when we ask, "How much data do you need?" can be restated as "How many basis functions do you need?"

## What is a basis function?

I'll answer this later.

We will generally need an infinite number of basis functions to represent an arbitrary ???. However, in our digital world we can only use a finite number of basis functions, and with a finite number of basis functions we can only __approximate__ these functions.

### Sinusoidal Basis Functions

In this notebook we are going to look at information representation in terms of sinusoidal basis functions. The canonical information representation based on sinusoidal basis functions is the [Fourier transform](https://en.wikipedia.org/wiki/Fourier_transform). In 1822 Jean-Baptiste Joseph Fourier demonstrated that (most) continous functions can be represented as a summation of an infinite number of appropriately weighted and phased sinusoidal (sines, cosines, complex exponentials) functions. The sinusoidal functions are an example of __basis__ functions. A key concept in the Fourier transform is the need for an infinite number of basis functions.

In applications such as magnetic resonance imaging (MRI) of the human body, we can view the body region being imaged as the continuous function. The MRI technique actually measures samples from the Fourier transform of the person's body region. How we sample this signal relates to both the cost of the acquisition and the integrity (error) of our approximation.

In communications, how many basis functions we use relates to how quickly we can transmit information using a radio wave or a fiber optic cable.

In this notebook we are going to look at information representation in terms of sinusoidal basis functions, specifically, the [Discrete Cosine Transform (DCT)](https://en.wikipedia.org/wiki/Discrete_cosine_transform), which is commonly used in signal processing and image.

## Image Example

To start with, we are going to look at a public domain digital image. As the image is already digitized, we are not going to worry about how the discrete image is only an approximation of the continuous physical object.

I will refer to the inverse DCT of an image as the __spectrum__. The spectrum corresponds to the coefficients for each basis function.

I've taken the DCT of the image and then prior to taking the inverse DCT (IDCT), I've zeroed out a fraction of the spectrum. (NEED TO DEFINE SPECTRUM.) based on the slider value _kp_. _kp_ represents what fraction of the spectrum was kept (not zeroed out) starting with the DC term. So a _kp_ of zero throws out all the original data and a _kp_ of 1 keeps all the original data.

$(@bind go Button("Start/Restart"))
"""

# ╔═╡ e50c3024-140d-4312-ad8e-66b94482964b
md"""# DCT Basis Functions

DCT forms the basis of JPEG image compression. In JPEG compression the image is split into 8x8 tiles. The DCT is computed for each tile. As was demonstrated above, an image can be recognized with a high degree of certitude wihtout many of the high frequency terms. JPEG exploits this by discarding the coefficients for high frequency basis functions within the DCT for each tile.

What do these basis functions look like? For an 8x8 tile, there are 64 distinct basis functions. We can visualize these basis functions by taking the inverse DCT for a Kronecker Delta spectrum.

Plot the desired basis function by selecting the desired Kronecker Delta value δ(i,j)
"""

# ╔═╡ f90425a3-8e6f-440a-8522-5ccd5cca2797

md"""

Create a Different Kronecker Delta spectrum by changin the (i,j) vaues.

i
$(@bind i Slider(1:1:8))
j
$(@bind j Slider(1:1:8))
"""

# ╔═╡ 899a3541-9cef-48de-a03d-367223fe3100
md"""### All 64 Basis Functions"""

# ╔═╡ 99028838-babb-4d29-a400-c321c65e455d
md"""### Code cells are hidden down here"""

# ╔═╡ 85859f7b-26e2-415e-87f1-b61180449a61
images = [i for i in TestImages.remotefiles if ! contains(i, "tif") && ! contains(i, "256") && ! contains(i, "color")] ;

# ╔═╡ 0d142947-1e3c-4adc-b6ca-09c502f89f0e
begin
	go
	ind = rand(1:length(images))
	iname = popat!(images, ind)
	img = Gray.(testimage(iname));
	aimg = Float64.(img) ;
	kimg = dct(aimg) ;
	@bind kp Slider(0:0.01:1, show_value=true)
end

# ╔═╡ c4d7b864-275a-4e6f-9cc1-74138a1f07ae
function kd2(i,j)
	img = zeros((8,8))
	img[i,j] = 1
	img
end ;

# ╔═╡ 4369ef1b-4291-4b2f-8bc1-ac011080bb18
bimg = kd2(i,j) ;

# ╔═╡ 740ecf61-15d7-4b30-8ef8-e9ed339912af
md"""
### Kronecker Delta Spectrum

$(Gray.(bimg))

### Resulting Basis Function

$(Gray.(6*idct(bimg)))
"""

# ╔═╡ 6c43a6ef-41af-4343-910b-04e72fdc87fd
alg = Equalization(nbins = 256);

# ╔═╡ 2b98ae25-097e-4dd5-aa4b-01a3b06c2fc8
begin
	tmp3 = zeros(64,64) ;
	for j in 0:7
		for i in 0:7
			tmp3[i*8+1:i*8+8,j*8+1:j*8+8] =idct(kd2(i+1,j+1))
		end
	end
	imresize(adjust_histogram(Gray.(tmp3), alg), 256, 256)
end

# ╔═╡ 628877b2-6e8d-47a1-8dce-752b9d75ab56
function filter_img(kimg, p)
	sz = size(kimg)
	fsz = convert.(Int64, round.(sz .* p))
	f = zeros(sz)
	f[1:fsz[1],1:fsz[2]] .= 1
	idct(kimg .* f)
end;

# ╔═╡ 12fb095f-02a6-48c6-856a-afbc71a6ad64
fimg = filter_img(kimg, kp) ;

# ╔═╡ 48412713-7266-43dc-a49e-bab349318cce
md"""
## What do you think this image is?

Increase kp to provide more basis functions for representing the image. kp represents the fraction of the discrete cosine transform basis functions to use (extending out from the DC term. What is the minimum kp for you to recognize the image?

$(Gray.(fimg))

kp
"""

# ╔═╡ 00000000-0000-0000-0000-000000000001
PLUTO_PROJECT_TOML_CONTENTS = """
[deps]
FFTW = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341"
ImageContrastAdjustment = "f332f351-ec65-5f6a-b3d1-319c6670881a"
ImageShow = "4e3cecfd-b093-5904-9786-8bbb286a6a31"
Images = "916415d5-f1e6-5110-898d-aaa5f9f070e0"
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
PlutoUI = "7f904dfe-b85e-4ff6-b463-dae2292396a8"
TestImages = "5e47fb64-e119-507b-a336-dd2b206d9990"

[compat]
FFTW = "~1.4.5"
ImageContrastAdjustment = "~0.3.7"
ImageShow = "~0.3.1"
Images = "~0.24.1"
Plots = "~1.22.2"
PlutoUI = "~0.7.11"
TestImages = "~1.6.1"
"""

# ╔═╡ 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"

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

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

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

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

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

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

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

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

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

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

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

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

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

[[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 = "31d0151f5716b655421d9d75b7fa74cc4e744df2"
uuid = "34da2185-b29b-5c13-b0c7-acf172513d20"
version = "3.39.0"

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

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

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

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

[[CustomUnitRanges]]
git-tree-sha1 = "1a3f97f907e6dd8983b744d2642651bb162a3f7a"
uuid = "dc8bdbbb-1ca9-579f-8c36-e416f6a65cce"
version = "1.0.2"

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

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

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

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

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

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

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

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

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

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

[[EllipsisNotation]]
deps = ["ArrayInterface"]
git-tree-sha1 = "8041575f021cba5a099a456b4163c9a08b566a02"
uuid = "da5c29d0-fa7d-589e-88eb-ea29b0a81949"
version = "1.1.0"

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

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

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

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

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

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

[[FileIO]]
deps = ["Pkg", "Requires", "UUIDs"]
git-tree-sha1 = "3c041d2ac0a52a12a27af2782b34900d9c3ee68c"
uuid = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549"
version = "1.11.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 = "21efd19106a55620a188615da6d3d06cd7f6ee03"
uuid = "a3f928ae-7b40-5064-980b-68af3947d34b"
version = "2.13.93+0"

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

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

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

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

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

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

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

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

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

[[Graphics]]
deps = ["Colors", "LinearAlgebra", "NaNMath"]
git-tree-sha1 = "2c1cf4df419938ece72de17f368a021ee162762e"
uuid = "a2bd30eb-e257-5431-a919-1863eab51364"
version = "1.1.0"

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

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

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

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

[[HypertextLiteral]]
git-tree-sha1 = "72053798e1be56026b81d4e2682dbe58922e5ec9"
uuid = "ac1192a8-f4b3-4bfe-ba22-af5b92cd3ab2"
version = "0.9.0"

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

[[Imath_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "87f7662e03a649cffa2e05bf19c303e168732d3e"
uuid = "905a6f67-0a94-5f89-b386-d35d92009cd1"
version = "3.1.2+0"

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

[[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 = "5a5bc6bf062f0f95e62d0fe0a2d99699fed82dd9"
uuid = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09"
version = "0.5.8"

[[MappedArrays]]
git-tree-sha1 = "e8b359ef06ec72e8c030463fe02efe5527ee5142"
uuid = "dbb5928d-eab1-5f90-85c2-b9b0edb7c900"
version = "0.4.1"

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

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

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

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

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

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

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

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

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

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

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

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

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

[[OpenEXR]]
deps = ["Colors", "FileIO", "OpenEXR_jll"]
git-tree-sha1 = "327f53360fdb54df7ecd01e96ef1983536d1e633"
uuid = "52e1d378-f018-4a11-a4be-720524705ac7"
version = "0.3.2"

[[OpenEXR_jll]]
deps = ["Artifacts", "Imath_jll", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"]
git-tree-sha1 = "923319661e9a22712f24596ce81c54fc0366f304"
uuid = "18a262bb-aa17-5467-a713-aee519bc75cb"
version = "3.1.1+0"

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

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

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

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

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

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

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

[[PaddedViews]]
deps = ["OffsetArrays"]
git-tree-sha1 = "646eed6f6a5d8df6708f15ea7e02a7a2c4fe4800"
uuid = "5432bcbf-9aad-5242-b902-cca2824c8663"
version = "0.5.10"

[[Parameters]]
deps = ["OrderedCollections", "UnPack"]
git-tree-sha1 = "34c0e9ad262e5f7fc75b10a9952ca7692cfc5fbe"
uuid = "d96e819e-fc66-5662-9728-84c9c7592b0a"
version = "0.12.3"

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

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

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

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

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

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

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

[[PlutoUI]]
deps = ["Base64", "Dates", "HypertextLiteral", "IOCapture", "InteractiveUtils", "JSON", "Logging", "Markdown", "Random", "Reexport", "UUIDs"]
git-tree-sha1 = "0c3e067931708fa5641247affc1a1aceb53fff06"
uuid = "7f904dfe-b85e-4ff6-b463-dae2292396a8"
version = "0.7.11"

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

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

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

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

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

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

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

[[Ratios]]
deps = ["Requires"]
git-tree-sha1 = "01d341f502250e81f6fec0afe662aa861392a3aa"
uuid = "c84ed2f1-dad5-54f0-aa8e-dbefe2724439"
version = "0.4.2"

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

[[StringDistances]]
deps = ["Distances"]
git-tree-sha1 = "a4c05337dfe6c4963253939d2acbdfa5946e8e31"
uuid = "88034a9c-02f8-509d-84a9-84ec65e18404"
version = "0.10.0"

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

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

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

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

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

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

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

[[TestImages]]
deps = ["AxisArrays", "ColorTypes", "FileIO", "OffsetArrays", "Pkg", "StringDistances"]
git-tree-sha1 = "db28237376a6b7ae9c9fe05880ece0ab8bb90b75"
uuid = "5e47fb64-e119-507b-a336-dd2b206d9990"
version = "1.6.1"

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

[[TiledIteration]]
deps = ["OffsetArrays"]
git-tree-sha1 = "5683455224ba92ef59db72d10690690f4a8dc297"
uuid = "06e1c1a7-607b-532d-9fad-de7d9aa2abac"
version = "0.3.1"

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

# ╔═╡ Cell order:
# ╟─09fc6764-2050-4276-8a14-3596368d0c32
# ╟─48412713-7266-43dc-a49e-bab349318cce
# ╟─0d142947-1e3c-4adc-b6ca-09c502f89f0e
# ╟─e50c3024-140d-4312-ad8e-66b94482964b
# ╟─f90425a3-8e6f-440a-8522-5ccd5cca2797
# ╟─740ecf61-15d7-4b30-8ef8-e9ed339912af
# ╟─899a3541-9cef-48de-a03d-367223fe3100
# ╟─2b98ae25-097e-4dd5-aa4b-01a3b06c2fc8
# ╟─99028838-babb-4d29-a400-c321c65e455d
# ╟─85859f7b-26e2-415e-87f1-b61180449a61
# ╟─12fb095f-02a6-48c6-856a-afbc71a6ad64
# ╟─f66e452c-97db-11eb-23fc-bd95da10e2d6
# ╟─4369ef1b-4291-4b2f-8bc1-ac011080bb18
# ╟─c4d7b864-275a-4e6f-9cc1-74138a1f07ae
# ╟─adaa78fa-d805-45e6-ba5f-d91a5d4823d6
# ╟─6c43a6ef-41af-4343-910b-04e72fdc87fd
# ╟─628877b2-6e8d-47a1-8dce-752b9d75ab56
# ╟─00000000-0000-0000-0000-000000000001
# ╟─00000000-0000-0000-0000-000000000002


"""
    The function that will modulate tha activation value of
    each non-input neuron in the network. It maps the z vale (∑wx + b)
    of the neurons to ℝ. By default its taken the sigmoid function
    one(z)/(one(z) + exp(-z)). To change it, just redefine it!!!
    But remember change the act_function_prime function too!!!!!!
"""
function act_function(z::T)::T where T<:Real
    return sigmoid(z);
end

"""
    The first derivative of the act_function.
"""
function act_function_prime(z::T) where T<:Real
    return sigmoid_prime(z);
end

function sigmoid(z::T)::T where T<:Real
    return one(z)/(one(z) + exp(-z));
end
function sigmoid_prime(z::T) where T<:Real
    return exp(-z)/((one(z) + exp(-z))^2);
end

ReLU_ng = 0.1;

function ReLU(z::T)::T where T<:Real
    return max(ReLU_ng * z, z);
end

function ReLU_prime(z::T)::T where T<:Real
    if z < 0
        return ReLU_ng;
    else
        return one(z);
    end
end


module VerticesTests

using Contour, Test
using Base.MathConstants: π, φ
using LinearAlgebra: Diagonal
using OffsetArrays
using StaticArrays

# Setup test axes that will be shared among the tests

# Shift the axes so that they do not line up with
# integer values
Δ = 0.01
X = collect(0:Δ:4) .+ π
Y = collect(0:Δ:3) .+ φ

# TEST CASE 1
#
# f(x,y) = x^2 + y^2
#
Z = [(x^2 + y^2)::Float64 for x in X, y in Y]
h = rand() * (maximum(Z) - minimum(Z)) + minimum(Z)

contourlevels = Contour.contour(X, Y, Z, h)
for line in contourlevels.lines
    # Contour vertices lie on a circle around the origin
    for v in line.vertices
        @test isapprox(v[1]^2 + v[2]^2, h, atol=0.1Δ)
    end

    # coordinates() returns the correct values
    xs, ys = coordinates(line)
    xs .== [v[1] for v in line.vertices]
    ys .== [v[2] for v in line.vertices]
end

# TEST CASE 1.5
#
# Same as test case 1, but with a shifted center
#
h = 1
x0, y0 = (.5, -1.5)
Z = Float64[(x - x0)^2 + (y - y0)^2 for x in X, y in Y]

contourlevels = Contour.contour(X, Y, Z, h)
for line in contourlevels.lines
    for v in line.vertices
        @test isapprox((v[1] - x0)^2 + (v[2] - y0)^2, h, atol=0.01Δ)
    end
end

# TEST CASE 2
#
# Check that ambigious cells (5, 10) are handled correctly
# Case 5: z_center > h
X = [1.0, 2.0]
Y = [2.0, 1.0]

Z = float([1 0;
           0 1])
h = 0.1

lines = Contour.contour(X, Y, Z, h).lines
@test length(lines) == 2

for line in lines
    @test length(line.vertices) == 2
    d = line.vertices[2] - line.vertices[1]
    @test d[2] / d[1] ≈ -1.0
end

# Case 5: z_center < h
Z = float([1 0;
           0 1])
h = 0.9

lines = Contour.contour(X, Y, Z, h).lines

@test length(lines) == 2

for line in lines
    @test length(line.vertices) == 2
    d = line.vertices[2] - line.vertices[1]
    @test d[2] / d[1] ≈ 1.0
end

# Case 10: z_center > h
Z = float([0 1;
           1 0])
h = 0.1

lines = Contour.contour(X, Y, Z, h).lines
@test length(lines) == 2

for line in lines
    @test length(line.vertices) == 2
    d = line.vertices[2] - line.vertices[1]
    @test d[2] / d[1] ≈ 1.0
end

# Case 10: z_center < h

Z = float([0 1;
           1 0])
h = 0.9

lines = Contour.contour(X, Y, Z, h).lines
@test length(lines) == 2

for line in lines
    @test length(line.vertices) == 2
    d = line.vertices[2] - line.vertices[1]
    @test d[2] / d[1] ≈ -1.0
end

# Test curvilinear coordinates
θ = range(0.0, stop=2π,length=100)
R = range(1.0, stop=2.0, length=100)
ζ = ComplexF64[r*exp(im*ϕ) for ϕ in θ, r in R]
x, y, z = real.(ζ), imag.(ζ), abs.(ζ)

h = 1 + rand()
xs, ys = coordinates(contour(x, y, z, h).lines[1])
@test all(xs.^2 + ys.^2 .≈ h^2)

# Test offset arrays
offset_x, offset_y = -10, 27
z = cumsum(cumsum(randn(20,20); dims=1); dims=2)
zoff = OffsetArray(z, offset_x, offset_y)

x, y = axes(z)
xoff, yoff = axes(zoff)
curves = Contour.contour(x,y,z,0.5)
curves_off = Contour.contour(xoff, yoff, zoff, 0.5)

# sort offset and non-offset curves to the same order
offset = SVector(offset_x, offset_y)
lns = sort(Contour.lines(curves); by=c->sum(sum.(c.vertices.+[offset])))
lns_off = sort(Contour.lines(curves_off); by=c->sum(sum.(c.vertices)))

# verify that each line matches a possibly circularly shifted or reversed
# line from the offset array
opencurve(a) = first(a) == last(a) ? a[1:end-1] : a
for (c1, c2) in zip(lns, lns_off)
    o1 = opencurve(c1.vertices) .+ [offset]
    o2 = opencurve(c2.vertices)
    m = length(o1)
    @test m == length(o2)
    cshifts = [circshift(o2,i) for i=1:m]
    @test any(o1 ≈ c || o1 ≈ reverse(c) for c in cshifts)
end

# Test Known Bugs

# Issue #12
x = float(collect(1:3));
y = copy(x);
z = Diagonal(ones(3));
contours(x, y, z)

# Test handling of saddle points
#
# f(x,y) = x^2 - y^2
#
Δ = 0.01
X = collect(-3:Δ:3)
Y = collect(-3:Δ:3)

Z = [(x^2 - y^2)::Float64 for x in X, y in Y]
h = rand() * (maximum(Z) - minimum(Z)) + minimum(Z)

contourlevels = Contour.contour(X, Y, Z, h)
for line in contourlevels.lines
    # Contour vertices lie on a circle around the origin
    for v in line.vertices
        @test isapprox(v[1]^2 - v[2]^2, h, atol=0.1Δ)
    end

    # coordinates() returns the correct values
    xs, ys = coordinates(line)
    xs .== [v[1] for v in line.vertices]
    ys .== [v[2] for v in line.vertices]
end

# Test range API
#
# f(x,y) = x^2 - y^2
#
Δ = 0.01
X = -3:Δ:3
Y = -3:Δ:3

Z = [(x^2 - y^2)::Float64 for x in X, y in Y]
h = rand() * (maximum(Z) - minimum(Z)) + minimum(Z)

contourlevels = Contour.contour(X, Y, Z, h)
for line in contourlevels.lines
    # Contour vertices lie on a circle around the origin
    for v in line.vertices
        @test isapprox(v[1]^2 - v[2]^2, h, atol=0.1Δ)
    end

    # coordinates() returns the correct values
    xs, ys = coordinates(line)
    xs .== [v[1] for v in line.vertices]
    ys .== [v[2] for v in line.vertices]
end

# Test that closed contours are identified correctly
# when ambiguous cells are involved

Z = float([1 1 1 1 1 1
           1 0 0 1 1 1
           1 0 0 1 1 1
           1 1 1 0 0 1
           1 1 1 0 0 1
           1 1 1 1 1 1])

X = Y = collect(0:0.2:1)
h = 0.75
contourlevels = Contour.contour(X, Y, Z, h)

# There should be only one closed contour
@test length(contourlevels.lines) == 1

# Test contour location on a realistic dataset
include("testdata.jl")
cts = Contour.contours(x, y, z)
@test length(cts.contours) == 10
cts_ct = (8, 8, 8, 8, 126, 7, 5, 5, 5, 4)

# Length need to be sorted as hashing might change see #62
lines_ct = sort!.([[138, 220, 469, 138, 469, 208, 143, 143],
            [220, 475, 140, 210, 146, 475, 140, 146],
            [222, 481, 140, 214, 481, 145, 140, 145],
            [228, 485, 214, 142, 485, 142, 146, 146],
            [9, 7, 515, 501, 9, 9, 9, 7, 7, 9, 7, 15, 7, 18, 39, 9, 7, 7, 9, 9, 7, 7, 7, 7, 9, 7, 18, 9, 7, 7, 9, 9, 35, 9, 9, 9, 7, 7, 9, 35, 9, 7, 9, 7, 9, 9, 7, 7, 7, 9, 7, 7, 7, 7, 9, 7, 9, 7, 9, 7, 7, 9, 9, 9, 7, 9, 7, 7, 9, 7, 7, 9, 7, 9, 7, 7, 7, 7, 7, 9, 7, 7, 7, 9, 7, 7, 7, 7, 9, 7, 7, 7, 7, 7, 7, 7, 9, 7, 9, 9, 7, 7, 7, 7, 7, 7, 9, 7, 7, 7, 7, 7, 7, 7, 9, 7, 9, 7, 7, 7, 7, 9, 7, 7, 7, 7],
            [5, 17, 14, 34, 34, 14, 5],
            [29, 12, 29, 15, 12],
            [26, 10, 13, 26, 10],
            [11, 11, 23, 11, 21],
            [18, 19, 7, 7]])
for i in eachindex(cts_ct)
    @test length(cts.contours[i].lines) == cts_ct[i]
    @test all(lines_ct[i] .== sort!([length(c.vertices) for c in cts.contours[i].lines]))
end

# support non-float z
using StatsBase

N = 10000
x = randn(N)
y = randn(N)
H = fit(Histogram, (x, y), closed = :left)
contours(midpoints.(H.edges)..., H.weights)

end


function e003(N)
    return largest_factor(N)
end

function largest_factor(N::Integer)
    if N < 2
        return 1
    else
        f::Integer = 2
        while f <= sqrt(N)
            while N % f == 0
                N ÷= f
                if N == 1
                    return f
                end
            end
            f += 1
        end
        return N
    end
end

function solution003()
    N = 600851475143
    answer = e003(N)
    println("The answer to problem 003 is $answer")
end


# Using names.txt (right click and 'Save Link/Target As...'), a 46K text file
# containing over five-thousand first names, begin by sorting it into
# alphabetical order. Then working out the alphabetical value for each name,
# multiply this value by its alphabetical position in the list to obtain a name
# score.
#
# For example, when the list is sorted into alphabetical order, COLIN, which is
# worth 3 + 15 + 12 + 9 + 14 = 53, is the 938th name in the list. So, COLIN
# would obtain a score of 938 × 53 = 49714.
#
# What is the total of all the name scores in the file?

using ProjectEulerSolutions

# Use sort then map and collect to order names then convert to char array then
# difference from the char 'A'.
function p022_solution(path::String="")::Integer
    all_names = Vector{String}()
    if isfile(path)
        open(path) do file
            for row in eachline(file)
                rownames = split(row, ",")
                append!(all_names, [n[2:end-1] for n in rownames]) # Remove quotes
            end
        end
    end
    sort!(all_names)

    return sum(map(x->sum(collect(x[2]).-'A'.+1)*x[1], enumerate(all_names)))
end

p022 = Problems.Problem(p022_solution)

input_path = "../data/p022_names.txt"

Problems.benchmark(p022, input_path)


spectral_resolutions = (31,42,85,170,341)

@testset "FFT of geopotential" begin

    # Test for variable resolution
    for trunc in spectral_resolutions

        P = Parameters(NF=Float64;trunc=trunc)
        G = GeoSpectral(P)
        B = Boundaries(P,G)

        geopot_surf_spectral = B.geopot_surf
        geopot_surf_grid = gridded(geopot_surf_spectral,G)

        @test all(geopot_surf_grid .≈ fourier_inverse(fourier(geopot_surf_grid,G),G))
    end
end

@testset "Legendre transform of geopotential" begin

    # Test for variable resolution
    for trunc in spectral_resolutions

        P = Parameters(NF=Float64;trunc=trunc)
        G = GeoSpectral(P)
        B = Boundaries(P,G)

        geopot_surf_spectral = B.geopot_surf
        geopot_surf_grid = gridded(geopot_surf_spectral,G)

        # using the already spectrally truncated geopotential
        # geopot_surf_grid2 = gridded(spectral(geopot_surf_grid,G),G)

        @test geopot_surf_grid ≈ gridded(spectral(geopot_surf_grid,G),G)

    end
end

@testset "Spectral transform of spectral noise" begin

    # Test for variable resolution
    for trunc in spectral_resolutions

        P = Parameters(NF=Float64;trunc=trunc)
        G = GeoSpectral(P)

        mx = G.spectral.mx
        nx = G.spectral.nx

        A = rand(mx,nx) + im*rand(mx,nx)
        At = spectral(gridded(A,G),G)   # the first transform includes truncation

        # the next should be exact to machine precision
        At2 = spectral(gridded(At,G),G)
        At3 = spectral(gridded(At2,G),G)
        @test all(At .≈ At2)
        @test all(At2 .≈ At3)
    end
end


function square_of_sum(n)
    # https://docs.julialang.org/en/v1/manual/control-flow/#man-conditional-evaluation
    # https://docs.julialang.org/en/v1/base/collections/#Base.collect-Tuple{Any}
    # https://www.simonwenkel.com/2019/02/03/project-euler-problem-0006.html
    return n > 1 ? sum(1:n)^2 : n
end

function sum_of_squares(n)
    # https://docs.julialang.org/en/v1/base/collections/#Base.mapreduce-Tuple{Any,%20Any,%20Any}
    # https://github.com/programacaodinamica/exercism-julia/blob/main/difference-of-squares/difference-of-squares.jl
    return n > 1 ? mapreduce(x -> x^2, +, 1:n) : n
end

function difference(n)
    return square_of_sum(n) - sum_of_squares(n)
end


using Test
using UnPack
using MOONS.Grids
using MOONS.Fields
using MOONS.FFTs

@testset "FFTs" begin

    # Solve ∇² u = f
    # where f = -2*(3*π)^2*cos(3*π*x)*cos(3*π*y)
    #       u = cos(3*π*x)*cos(3*π*y))

    FT = Float64
    n = 128
    cx = Coordinates(FT(0),FT(1),n)
    cy = Coordinates(FT(0),FT(1),n)
    cz = Coordinates(FT(0),FT(1),2)
    g = Grid((cx,cy,cz))

    f_exact = CellCenter(g)
    u_exact = CellCenter(g)
    f = CellCenter(g)
    u = CellCenter(g)
    u_err = CellCenter(g)

    @inbounds for local_grid in GridField(g,f_exact)
        @unpack ijk,x,y = local_grid
        u_exact[ijk] = cos(3*π*x)*cos(3*π*y)
        f_exact[ijk] = -2*(3*π)^2*cos(3*π*x)*cos(3*π*y)
    end

    ∇²!(f, u_exact, g)
    extrap!(f, g)
    solve!(u, f_exact, g, 3)

    @test max(average(u, 3)...) ≈ 1.0003012584260522
    @test max(abs.(diff(u; dims=3))...) ≈ 0

    u_err .= abs.(u_exact - u);
    assign!(u_err, 0, Ghost())
    @test max(u_err...) ≈ 0.0004518490779501505
end


# See https://rdrr.io/rforge/copula/man/Sibuya.html

struct Sibuya{T<:Real} <: Distributions.DiscreteUnivariateDistribution
    p::T
    function Sibuya(p::T) where {T <: Real}
        new{T}(p)
    end
end
function Distributions.rand(rng::Distributions.AbstractRNG, d::Sibuya{T}) where {T <: Real}
    u = rand(rng, T)
    if u <= d.p
        return T(1)
    end
    xMax = 1/eps(T)
    Ginv = ((1-u)*SpecialFunctions.gamma(1-d.p))^(-1/d.p)
    fGinv = floor(Ginv)
    if Ginv > xMax
        return fGinv
    end
    if 1-u < 1/(fGinv*SpecialFunctions.beta(fGinv,1-d.p))
        return ceil(Ginv)
    end
    return fGinv
end


"""
condentropy \\

for the condEntropy function specifically, calculating conditional entropy \\
Arguments:	marg_dist, marginal distributions \\
						cond_dists, conditional distributions \\
						base, the base for entropy calculations \\
Returns:		Float64, the conditional entropy \\
"""

function condentropy(marg_dist::Array{Float64,1}, cond_dists::Array{Float64}; base::Int=2)
	length(marg_dist) == size(cond_dists)[2] || error("Number of marginal and conditional distributions does not match!")
	ents = [entropy(cond_dists[:, i], base) for i = 1:size(cond_dists)[2]]
	return sum(marg_dist .* ents)
end


using DifferentialEquations
using DiffEqSensitivity
using Random
using Distributions
using Turing
using DataFrames
using StatsPlots

function sir_ode!(du,u,p,t)
    (S,I,R,C) = u
    (β,c,γ) = p
    N = S+I+R
    infection = β*c*I/N*S
    recovery = γ*I
    @inbounds begin
        du[1] = -infection
        du[2] = infection - recovery
        du[3] = recovery
        du[4] = infection
    end
    nothing
end;

tmax = 40.0
tspan = (0.0,tmax)
obstimes = 1.0:1.0:tmax
u0 = [990.0,10.0,0.0,0.0] # S,I.R,C
p = [0.05,10.0,0.25]; # β,c,γ

prob_ode = ODEProblem(sir_ode!,u0,tspan,p);

sol_ode = solve(prob_ode,
            Tsit5(),
            saveat = 1.0);

C = Array(sol_ode)[4,:] # Cumulative cases
X = C[2:end] - C[1:(end-1)];

Random.seed!(1234)
Y = rand.(Poisson.(X));

bar(obstimes,Y,legend=false)
plot!(obstimes,X,legend=false)

@model bayes_sir(y) = begin
  # Calculate number of timepoints
  l = length(y)
  i₀  ~ Uniform(0.0,1.0)
  β ~ Uniform(0.0,1.0)
  I = i₀*1000.0
  u0=[1000.0-I,I,0.0,0.0]
  p=[β,10.0,0.25]
  tspan = (0.0,float(l))
  prob = ODEProblem(sir_ode!,
          u0,
          tspan,
          p)
  sol = solve(prob,
              Tsit5(),
              saveat = 1.0)
  sol_C = Array(sol)[4,:] # Cumulative cases
  sol_X = sol_C[2:end] - sol_C[1:(end-1)]
  l = length(y)
  for i in 1:l
    y[i] ~ Poisson(sol_X[i])
  end
end;

ode_nuts = sample(bayes_sir(Y),NUTS(0.65),10000);

describe(ode_nuts)

plot(ode_nuts)

posterior = DataFrame(ode_nuts);

histogram2d(posterior[!,:β],posterior[!,:i₀],
                bins=80,
                xlabel="β",
                ylab="i₀",
                ylim=[0.006,0.016],
                xlim=[0.045,0.055],
                legend=false)
plot!([0.05,0.05],[0.0,0.01])
plot!([0.0,0.05],[0.01,0.01])

function predict(y,chain)
    # Length of data
    l = length(y)
    # Length of chain
    m = length(chain)
    # Choose random
    idx = sample(1:m)
    i₀ = chain[:i₀][idx]
    β = chain[:β][idx]
    I = i₀*1000.0
    u0=[1000.0-I,I,0.0,0.0]
    p=[β,10.0,0.25]
    tspan = (0.0,float(l))
    prob = ODEProblem(sir_ode!,
            u0,
            tspan,
            p)
    sol = solve(prob,
                Tsit5(),
                saveat = 1.0)
    out = Array(sol)
    sol_X = [0.0; out[4,2:end] - out[4,1:(end-1)]]
    hcat(sol_ode.t,out',sol_X)
end;

Xp = []
for i in 1:10
    pred = predict(Y,ode_nuts)
    push!(Xp,pred[2:end,6])
end

scatter(obstimes,Y,legend=false)
plot!(obstimes,Xp,legend=false)


"""
    value_at_risk(returns, confidence, method; multiplier=1.0)

Computes the Value-at-Risk (VaR) for a given significance level `α` based on the chosen estimation method. The VaR value represents the maximum expected loss at a certain significance level `α`. For a more tail-risk focused measure, see `expected_shortfall`.

# Arguments
- `returns`:     Vector of asset returns.
- `α`:           Significance level, e.g. use `0.05` for 95% confidence, or `0.01` for 99% confidence.
- `method`:      Distribution estimation method: `:historical`, `:gaussian` or `:cornish_fisher`.
- `multiplier`:  Optional scalar multiplier, i.e. use `12` to annualize monthly returns, and use `252` to annualize daily returns.

# Methods
- `:historical`:        Historical based on empirical distribution of returns.
- `:gaussian`:          Gaussian distribution based on parametric fit (mean, variance).
- `:cornish_fisher`:    Cornish-Fisher based on Gaussian parametric distribution fit adjusted for third and fourth moments (skewness, kurtosis). Cornish-Fisher expansion aims to approximate the quantile of a true distribution by using higher moments (skewness and kurtosis) of that distribution to adjust for its non-normality. See https://thema.u-cergy.fr/IMG/pdf/2017-21.pdf for details.

# Sources
- Favre, Laurent and Galeano, Jose-Antonio (2002). Mean-Modified Value-at-Risk Optimization with Hedge Funds. Journal of Alternative Investment.
- Amédée-Manesme, Charles-Olivier and Barthélémy, Fabrice and Maillard, Didier (2017). Computation of the Corrected Cornish–Fisher Expansion using the Response Surface Methodology: Application to VaR and CVaR. THEMA Working Paper n°2017-21, Université de Cergy-Pontoise, France.
"""
function value_at_risk(returns, α, method::Symbol; multiplier=1.0)
    if method == :historical
        # empirical quantile for VaR estimation
        return quantile(returns, α) * sqrt(multiplier)
    elseif method == :gaussian
        # parametric Gaussian distribution fit
        μ = mean(returns)
        σ = std(returns; corrected=false)
        return quantile(Normal(μ, σ), α)
    elseif method == :cornish_fisher
        # third/fourth moment adjusted Gaussian distribution fit
        # http://www.diva-portal.org/smash/get/diva2:442078/FULLTEXT01.pdf
        # https://papers.ssrn.com/sol3/papers.cfm?abstract_id=1024151
        q = quantile(Normal(), α)
        S = skewness(returns)
        K = kurtosis(returns; method=:excess)
        z = q + 1/6*(q^2-1)S + 1/24*(q^3-3q)*K - 1/36*(2q^3-5q)*S^2
        μ = mean(returns)
        σ = std(returns; corrected=false)
        return (μ + z*σ) * sqrt(multiplier)
    end

    throw(ArgumentError("Passed method parameter '$(method)' is invalid, must be one of :historical, :gaussian, :cornish_fisher."))
end


include("euler/euler.jl")
using .Numbers: count_divisors
using BenchmarkTools
BenchmarkTools.DEFAULT_PARAMETERS.samples = 100

function compute(n::Int)::Int
    i, triangle = 1, 1
    while count_divisors(triangle) < n
        i += 1
        triangle += i
    end
    return triangle
end

compute(5)

compute(500)

@benchmark compute(500)


"""
   DiscretizeUniformWidth(alg::Symbol)
If `nbins` is a symbol, automatically determine the number of bins to use
"""
struct DiscretizeUniformWidth <: DiscretizationAlgorithm
    nbins::Union{Int,Symbol}
end

function get_nbins(alg::Symbol, data::AbstractArray{N}) where {N<:Real}

    n = length(data)

    if alg == :sqrt
        # Square root (of data size) estimator, used by Excel and other programs for its speed and simplicity.
        nbins = ceil(Int, sqrt(n))
    elseif alg == :sturges
        # R’s default method, only accounts for data size.
        # Only optimal for gaussian data and underestimates number of bins for large non-gaussian datasets.
        # It implicitly bases the bin sizes on the range of the data and can perform poorly if n < 30,
        # because the number of bins will be small—less than seven—and unlikely to show trends in the data well.
        # It may also perform poorly if the data are not normally distributed.
        nbins = ceil(Int, log(2,n)) + 1
    elseif alg == :rice
        # Estimator does not take variability into account, only data size. Commonly overestimates number of bins required.
        nbins = ceil(Int, 2cbrt(n))
    elseif alg == :doane
        # An improved version of Sturges’ estimator that works better with non-normal datasets.
        g₁ = moment(data, 3)
        σ = sqrt((6*(n-2))/((n+1)*(n+3)))
        nbins = ceil(Int, 1 + log(2,n) + log(2, 1+abs(g₁)/σ))
    elseif alg == :scott
        # Less robust estimator that that takes into account data variability and data size.
        σ = std(data)
        binwidth = 3.5σ/cbrt(n)
        lo, hi = extrema(data)
        nbins = ceil(Int, (hi - lo)/binwidth)
    elseif alg == :fd # Freedman Diaconis Estimator
        # Robust (resilient to outliers) estimator that takes into account data variability and data size
        binwidth = 2iqr(data)/cbrt(n)
        lo, hi = extrema(data)
        nbins = ceil(Int, (hi - lo)/binwidth)
    else # alg == :auto
        # Maximum of the ‘sturges’ and ‘fd’ estimators. Provides good all round performance

        binwidth = 2iqr(data)/cbrt(n)
        lo, hi = extrema(data)
        nbins_fd = ceil(Int, (hi - lo)/binwidth)
        nbins_sturges = ceil(Int, log(2,n)) + 1
        nbins = max(nbins_fd, nbins_sturges)
    end

    nbins
end

function binedges(alg::DiscretizeUniformWidth, data::AbstractArray{N}) where {N<:AbstractFloat}
    lo, hi = extrema(data)
    @assert(hi > lo)

    nbins = (isa(alg.nbins, Symbol) ? get_nbins(alg.nbins, data) : alg.nbins)::Int

    convert(Vector{N}, collect(range(lo, stop=hi, length=nbins+1)))
end
function binedges(alg::DiscretizeUniformWidth, data::AbstractArray{N}) where {N<:Integer}
    lo, hi = extrema(data)
    @assert(hi > lo)
    collect(range(lo, stop=hi, length=alg.nbins+1))
end


import Base.:+
import Base.:*

export Biquaternion
export getrotation
export gettranslation

"""
    Represents a bi-quaternion for describing rotation and translation in a unified way.

fields: real and dual.
"""
struct Biquaternion
    real::Quaternion
    dual::Quaternion
    Biquaternion(qr::Quaternion, qd::Quaternion) = new(qr, qd)
    Biquaternion(q::Quaternion, t::ℝ³) = begin
        qr = normalize(q)
        qd = 0.5 * Quaternion([0; vec(t)]) * qr
        new(qr, qd)
    end
end

"""
    show(b)

Print a string representation of the given Biquaternion `b`.
"""
Base.show(io::IO, q::Biquaternion) = print(io, "$(q.real) + $(q.dual)𝜺")

"""
    Biquaternion(q)

Construct a Biquaternion with the given Biquaternion `q` and also normalize.
"""
Biquaternion(q::Biquaternion) = begin
    q̂ = normalize(q)
    Biquaternion(q̂.real, q̂.dual)
end

"""
    Biquaternion(rotation)

Construct a Biquaternion with the given Quaternion `rotation` and also normalize.
"""
Biquaternion(rotation::Quaternion) = Biquaternion(normalize(rotation), ℝ³(0, 0, 0))

"""
    Biquaternion(translation)

Construct a Biquaternion with the given ℝ³ `translation`.
"""
Biquaternion(translation::ℝ³) = Biquaternion(Quaternion(1, 0, 0, 0), translation)

vec(q::Biquaternion) = [vec(q.real); vec(q.dual)]

+(q₁::Biquaternion, q₂::Biquaternion) = Biquaternion(q₁.real + q₂.real, q₁.dual + q₂.dual)
-(q₁::Biquaternion, q₂::Biquaternion) = Biquaternion(q₁.real - q₂.real, q₁.dual - q₂.dual)
*(q₁::Biquaternion, q₂::Biquaternion) = Biquaternion(q₁.real * q₂.real,
                                                     q₁.real * q₂.dual + q₁.dual * q₂.real)
*(q::Biquaternion, λ::Real) = Biquaternion(λ * q.real, λ * q.dual)
*(λ::Real, q::Biquaternion) = q * λ
Base.conj(q::Biquaternion) = Biquaternion(conj(q.real), conj(q.dual))
norm(q::Biquaternion) = begin
    qrnorm = norm(q.real)
    Q = (conj(q.real) * q.dual + q.real * conj(q.dual)) * (1 / 2qrnorm)
    scalar = vec(Q)[1]
    sqrt(qrnorm^2 + scalar^2)
end
normalize(q::Biquaternion) = begin
    magnitude = norm(q)
    @assert(magnitude > 1e-5, "The magnitude is almost equal to zero, too small.")
    q * (1 / magnitude)
end
getrotation(q::Biquaternion) = q.real
gettranslation(q::Biquaternion) = ℝ³(vec(2q.dual * conj(q.real))[2:4])

Base.isapprox(q1::Biquaternion, q2::Biquaternion) = isapprox(q1.real, q2.real) &&
                                                    isapprox(q1.dual, q2.dual)


function derivative(ex::Constant, wrt::Variable)
    return Constant( default_variable_value() )
end

function derivative(ex::Variable, wrt::Variable)
    T   = typeof(wrt.value)
    val = default_variable_value()
    if ex == wrt
        val = one(T)
    end
    return Constant(val)
end

function derivative(ex::SymbolicExpression, wrt::Variable)

    if is_unary_operator(ex.head)
        der_ex = __derivative_unary(ex, wrt)
    elseif is_binary_operator(ex.head)
        der_ex = __derivative_binary(ex, wrt)
    else
        error("not implemented yet")
        # er_ex = __derivative_k_ary(ex, wrt)
    end

    return der_ex
end

function __derivative_unary(ex::SymbolicExpression, wrt::Variable)

    # dy/dz = sin(x)
    # dy/dz = cos(x) * dx/dz
    #
    # derivative of the arguments
    # derivative of the function itself

    arg = ex.args[1]
    der_arg = derivative(arg, wrt)

    sym = ex.head

    if is_sin_operator(sym)
        der_ex = der_arg * cos(arg)

    elseif is_cos_operator(sym)
        der_ex = -der_arg * sin(arg)

    elseif is_tan_operator(sym)
        der_ex = der_arg / (cos(arg) * cos(args))

    elseif is_sqrt_operator(sym)
        der_ex = der_arg / sqrt(arg)

    elseif is_cbrt_operator(sym)
        der_ex = der_arg * 1/3 * arg^(-2/3)

    elseif is_exp_operator(sym)
        der_ex = der_arg * exp(arg)

    elseif is_log_operator(sym)
        der_ex = der_arg / arg

    elseif is_log2_operator(sym)
        der_ex = der_arg / (arg * log(2))

    elseif is_log10_operator(sym)
        der_ex = der_arg / (arg * log(10))

    elseif is_abs_operator(sym)
        der_ex = der_arg * sign(arg)

    elseif is_sign_operator(sym)
        der_ex = Constant(0.0)

    else
        error("not implemented yet")
    end

    return der_ex
end

function __derivative_binary(ex::SymbolicExpression, wrt::Variable)

    sym = ex.head

    if is_plus_operator(sym) || is_product_operator(sym)
        # der_ex = der_left_arg + der_right_arg
        # der_ex = der_left_arg * right_arg + left_arg * der_right_arg

        df(expr) = derivative(expr, wrt)
        der_args = map(df, ex.args)

        if is_plus_operator(sym)
            return reduce(+, der_args)
        end

        L = length(ex.args)
        if L == 2
            left_arg      = ex.args[1]
            right_arg     = ex.args[2]
            der_left_arg  = der_args[1]
            der_right_arg = der_args[2]

            return der_left_arg * right_arg + left_arg * der_right_arg
        else
            der_ex = der_args[1] * reduce(*, ex.args[2:end])
            for ii = 2:L
                # left   = reduce(*, ex.args[1:ii-1])
                # right  = reduce(*, ex.args[ii+1:end])
                args      = [ex.args[1:ii-1]; der_args[ii]; ex.args[ii+1:end]]
                left_expr = SymbolicExpression(:*, args...)
                der_ex    = der_ex + left_expr # left * der_args[ii] * right
            end
            return der_ex
        end
    end

    L = length(ex.args)
    if is_minus_operator(sym) && L == 1
        der_right_arg = derivative(ex.args[1], wrt)
        der_ex = -der_right_arg
        return der_ex
    end

    left_arg  = ex.args[1]
    right_arg = ex.args[2]

    der_left_arg  = derivative(left_arg, wrt)
    der_right_arg = derivative(right_arg, wrt)

    if is_minus_operator(sym)
        der_ex = der_left_arg - der_right_arg

    elseif is_divide_operator(sym)
        der_ex = (der_left_arg * right_arg - left_arg * der_right_arg) / (right_arg * right_arg)

    elseif is_power_operator(sym)
        # most complicated expression
        der_ex = ex * (der_left_arg * right_arg / left_arg + der_right_arg * log(left_arg))

    else
        error("not implemented yet")
    end

    return der_ex
end

function gradient(
    ex::S,
    wrt_vec::A) where {
        S <: Union{Constant, Variable, SymbolicExpression},
        A <: AbstractArray}

    L = length(wrt_vec)
    g = Vector{Any}(undef, L)

    for ii = 1:L
        g[ii] = symbolics.derivative(ex, wrt_vec[ii])
    end

    return g
end

function jacobian(
    ex_vec::S,
    wrt_vec::A) where {
        S <: AbstractArray,
        A <: AbstractArray}

    n_row = length(ex_vec)
    n_col = length(wrt_vec)
    jac   = Matrix{Any}(undef, n_row, n_col)

    for ii = 1:n_row
        ex = ex_vec[ii]
        for jj = 1:n_col
            jac[ii, jj] = symbolics.derivative(ex, wrt_vec[jj])
        end
    end

    return jac
end


"""
    half_saturation(max, half, x)
Half satration curve.
"""
half_saturation(max, half, x) = max/(oneunit(half/x) + half/x)


module NewtonsMethod

greet() = print("Hello World!")

using LinearAlgebra, ForwardDiff

function fixedpointmap(f, f_prime, x_0; tolerance=1E-12, maxiter=1000)
    # setup the algorithm
#     x_old = iv
    normdiff = Inf
    iter = 1
    if f(x_0) == 0
        return (value = x_0, normdiff=0, iter=0)
    end
    while normdiff > tolerance && iter <= maxiter
        x_new = x_0 - f(x_0)/f_prime(x_0) # use the passed in map
        if norm(x_new - x_0) > tolerance && iter > 100
            println("non-convergence ")
            return #(value = nothing, normdiff=nothing, iter=iter)
        end
        normdiff = norm(x_new - x_0)
        x_0 = x_new
        iter = iter + 1
    end
    return (value = x_0, normdiff=normdiff, iter=iter) # A named tuple
end

D(f) = x -> ForwardDiff.derivative(f, x)
function fixedpointmap(f, x_0; tolerance=1E-12, maxiter=1000)
    f_prime = D(f)
    if nothing == fixedpointmap(f, f_prime, x_0)
        return
    end
    x_0, normdiff, iter = fixedpointmap(f, f_prime, x_0, tolerance=tolerance, maxiter=maxiter)
    return (value = x_0, normdiff=normdiff, iter=iter)
end

export fixedpointmap

end # module


# Multicollinearity Test for regression models:
# see associated pdf and data file
# overview of notes:
# if doing regression, the coefficient of each predictor (indepedent) variable tells you how much on average the response changes
# when the variable changes by a unit. however, when variables are corellated (they are not really independent) it makes inference harder

using RCall
using DataFrames
using CSV
using GLM
using Statistics

# read the first 4 columns (not the columns with S)
df = CSV.read("multicollinearity_regression_models_data.csv" , header=1, select=(i, name) -> i < 5 && return true) |> DataFrame#, select=(i, name) -> i < 5 && return true)
insertcols!(df, :pfat_weight => df.pfat .* df.weight) # create interaction term manually

rmodel = lm(@formula(neck ~ pfat + weight + activity + pfat_weight), df)
# lets calculate the Variance Inflation numbers
# using r2 of the model, calculate VIF

rmodel1 = lm(@formula(pfat ~  weight + activity + pfat_weight), df)
rmodel2 = lm(@formula(weight ~ pfat + activity + pfat_weight), df)
rmodel3 = lm(@formula(activity ~ pfat + weight + pfat_weight), df)
rmodel4 = lm(@formula(pfat_weight ~ pfat + weight + activity), df)
rmodels = (rmodel1, rmodel2, rmodel3, rmodel4)
vifs = @. 1 / (1 - r2(rmodels))

# node that for activity, VIF is 1.05 so this variable has no mulitcollinearity.
# for the other ones, there seems to be definitely multicollinearity
# first, let's remove the structural multicollinearity from the interaction term.
# to do tht, we will first mean center the data.

# there is a bug in Query that messes up the dataframe types and GLM can't read it anymore
# https://discourse.julialang.org/t/query-jl-mutate-command-does-not-preserve-column-types/41231/4
# aa = mean(df.pfat)::Float64
# ab = mean(df.weight)::Float64
# ac = mean(df.activity)::Float64
# ad = mean(df.pfat_weight)::Float64
# df_s = df |> @mutate(pfat_s .= _.pfat .- aa,
#               weight_s .= _.weight .- ab,
#               activity_s .= _.activity .- ac,
#               pfat_weight_s .= _.pfat_weight .- ad) |> DataFrame

# equivalent linq
# @from i in df begin
# @select {i..., pfat_s = mean(df.pfat)}
# @collect DataFrame
# end

# lets use standard method to add columns
df[!, :neck_s] .= mean(df.neck) .- df.neck
df[!, :pfat_s] .= mean(df.pfat) .- df.pfat
df[!, :weight_s] .= mean(df.weight) .- df.weight
df[!, :activity_s] .= mean(df.activity) .- df.activity
df[!, :pfat_weight_s] .=  df.pfat_s .* df.weight_s

smodel = lm(@formula(neck ~ pfat_s + weight_s + activity_s + pfat_weight_s), df)
# lets calculate the Variance Inflation numbers
# using r2 of the model, calculate VIF
smodel1 = lm(@formula(pfat_s ~  weight_s + activity_s + pfat_weight_s), df)
smodel2 = lm(@formula(weight_s ~ pfat_s + activity_s + pfat_weight_s), df)
smodel3 = lm(@formula(activity_s ~ pfat_s + weight_s + pfat_weight_s), df)
smodel4 = lm(@formula(pfat_weight_s ~ pfat_s + weight_s + activity_s), df)
smodels = (smodel1, smodel2, smodel3, smodel4)
vifs = @. 1 / (1 - r2(smodels))
# (3.323870441091722, 4.745648167219831, 1.0530047498114123, 1.9910631706191224)

# you see now how small the VIFs have gotten just by removing the multicollinearity


using DiscreteMarkovChains
using Test
using LinearAlgebra

@testset "Converting To Discrete" begin
    # Empty test
    T = [][:,:]
    X = ContinuousMarkovChain(T)
    @test DiscreteMarkovChain(X) == DiscreteMarkovChain(T)

    state_space = [1, 2, 3]
    T = [
        0 1 0;
        1 0 0;
        0 0 1;
    ] - I
    X = ContinuousMarkovChain(state_space, T)

    T1 = transition_matrix(DiscreteMarkovChain(X))
    T2 = transition_matrix(DiscreteMarkovChain(state_space, exp(T)))
    @test T1 == T2
end

@testset "Converting To Continuous" begin
    # Empty test
    T = [][:,:]
    X = DiscreteMarkovChain(T)
    @test ContinuousMarkovChain(X) == ContinuousMarkovChain(T)

    state_space = [1, 2, 3]
    T = [
        0.2 0.8 0;
        0 0.2 0.8;
        0 0 1;
    ]
    X = DiscreteMarkovChain(state_space, T)

    Q1 = transition_matrix(ContinuousMarkovChain(X))
    Q2 = transition_matrix(ContinuousMarkovChain(state_space, log(T)))
    @test Q1 == Q2
end

@testset "Embedded Chains" begin
    # Empty test
    T = [][:,:]
    X = ContinuousMarkovChain(T)
    @test embedded(X) == DiscreteMarkovChain(T)

    X = DiscreteMarkovChain(T)
    @test embedded(X) == X

    T = [
        -2 1 1;
        1 -1 0;
        2 3 -5;
    ]
    X = ContinuousMarkovChain(T)
    desired_new_matrix = [
        0 0.5 0.5;
        1 0 0;
        0.4 0.6 0;
    ]
    @test transition_matrix(embedded(X)) ≈ desired_new_matrix
end

@testset "Communication Classes" begin
    # Empty test
    T = [][:,:]
    X = ContinuousMarkovChain(T)
    @test communication_classes(X) == ([], [])

    state_space = [1, 2, 3]
    T = [
        0 1 0;
        1 0 0;
        0 0 1;
    ] - I
    X = ContinuousMarkovChain(state_space, T)
    @test communication_classes(X) == ([[1, 2], [3]], [true, true])

    T = [
        0 5 5 0 0;
        0 0 0 10 0;
        5 0 5 0 0;
        0 10 0 0 0;
        0 3 0 3 4;
    ]/10 - I
    X = ContinuousMarkovChain(0:4, T)
    classes, recurrence = communication_classes(X)
    @test classes == [[1, 3], [0, 2], [4]]
    @test recurrence == [true, false, false]

    T = [
        0 0 0 10 0 0;
        5 0 5 0 0 0;
        0 4 0 0 0 6;
        10 0 0 0 0 0;
        0 10 0 0 0 0;
        0 0 0 5 5 0;
    ]/10 - I
    X = ContinuousMarkovChain(0:5, T)
    classes, recurrence = communication_classes(X)
    @test classes == [[0, 3], [1, 2, 5, 4]]
    @test recurrence == [true, false]

    T = [
        2 0 0 3 0 0 3 2 0 0;
        0 10 0 0 0 0 0 0 0 0;
        0 2 2 0 0 0 0 0 3 3;
        0 0 0 3 0 0 6 1 0 0;
        0 0 0 0 5 5 0 0 0 0;
        0 0 0 0 0 10 0 0 0 0;
        4 0 0 5 0 0 1 0 0 0;
        2 0 0 4 0 0 2 2 0 0;
        3 0 1 0 0 0 0 0 4 2;
        0 0 4 0 0 0 0 0 3 3;
    ]/10 - I
    X = ContinuousMarkovChain(0:9, T)
    classes, recurrence = communication_classes(X)
    @test classes == [[0, 3, 6, 7], [1], [2, 8, 9], [5], [4]]
    @test recurrence == [true, true, false, true, false]
end

@testset "Decompose and Canonical Form" begin
    # Empty test
    T = Array{Any}(undef, 0, 0)
    X = ContinuousMarkovChain([], T)
    @test decompose(X) == ([], T, T, T)

    T = [
        1 0 0;
        1//3 1//3 1//3;
        0 1//4 3//4;
    ] - I
    X = ContinuousMarkovChain(["Sunny", "Cloudy", "Rainy"], T)
    @test decompose(X) == (
        ["Sunny", "Cloudy", "Rainy"],
        T[1:1, 1:1], T[2:3, 1:1], T[2:3, 2:3],
    )
    @test canonical_form(X) == (["Sunny", "Cloudy", "Rainy"], T)

    T = [
        1//4 3//4 0;
        1//3 1//3 1//3;
        0 1//4 3//4;
    ] - I
    X = ContinuousMarkovChain([1, 2, 3], T)
    @test decompose(X) == ([1, 2, 3], T, Array{Any}(undef, 0, 3), Array{Any}(undef, 0, 0))
    @test canonical_form(X) == ([1, 2, 3], T)

    T = [
        1 0 0 0 0;
        1//2 0 1//2 0 0;
        0 1//2 0 1//2 0;
        0 0 1//2 0 1//2;
        0 0 0 0 1;
    ] - I
    X = ContinuousMarkovChain(0:4, T)
    states, A, B, C = decompose(X)
    @test states == [0, 4, 1, 2, 3]
    @test A == [1 0; 0 1] - I
    @test B == [1//2 0; 0 0; 0 1//2]
    @test C == [0 1//2 0; 1//2 0 1//2; 0 1//2 0] - I
    states, new_matrix = canonical_form(X)
    @test states == [0, 4, 1, 2, 3]
    desired_new_matrix = [
        1 0 0 0 0;
        0 1 0 0 0;
        1//2 0 0 1//2 0;
        0 0 1//2 0 1//2;
        0 1//2 0 1//2 0;
    ] - I
    @test new_matrix == desired_new_matrix
end

@testset "Regular, Ergodic, Absorbing" begin
    # Empty test
    T = Array{Any}(undef, 0, 0)
    X = ContinuousMarkovChain([], T)
    @test !is_ergodic(X)
    @test !is_absorbing(X)

    T = [
        0 4 0 0 0;
        1 0 3 0 0;
        0 2 0 2 0;
        0 0 3 0 1;
        0 0 0 4 0;
    ]/4.0 - I
    X = ContinuousMarkovChain(1:5, T)
    @test is_ergodic(X)
    @test !is_absorbing(X)

    T = [
        0 1;
        1 0;
    ] - I
    X = ContinuousMarkovChain(1:2, T)
    @test is_ergodic(X)
    @test !is_absorbing(X)

    T = [
        2 1 1;
        2 0 2;
        1 1 2;
    ]/4 - I
    X = ContinuousMarkovChain(1:3, T)
    @test is_ergodic(X)
    @test !is_absorbing(X)

    T = [
        1 1;
        1 1;
    ]/2 - I
    X = ContinuousMarkovChain(1:2, T)
    @test is_ergodic(X)
    @test !is_absorbing(X)
end

@testset "Reversible" begin
    # Empty test
    T = Array{Any}(undef, 0, 0)
    X = DiscreteMarkovChain(T)
    @test is_reversible(X)
    X = ContinuousMarkovChain(T)
    @test is_reversible(X)

    T = [
        .425 .000 .075 .500;
        .000 .550 .250 .200;
        .300 .250 .450 .000;
        .500 .050 .000 .450;
    ]
    X = DiscreteMarkovChain(T)
    @test is_reversible(X)
    X = ContinuousMarkovChain(T-I)
    @test is_reversible(X)

    T = [
        0.5 0.5 0.0;
        0.0 0.2 0.8;
        0.3 0.3 0.4;
    ]
    X = DiscreteMarkovChain(T)
    @test !is_reversible(X)
    X = ContinuousMarkovChain(T-I)
    @test !is_reversible(X)
end

@testset "Stationary distribution" begin
    # Empty test
    T = Array{Any}(undef, 0, 0)
    X = ContinuousMarkovChain([], T)
    @test stationary_distribution(X) == Any[]

    T = [-2 2 0; 0 -4 4; 0 0 0]
    X = ContinuousMarkovChain(T)
    @test stationary_distribution(X) == [0, 0, 1]

    T = [
        -25 20 5;
        300 -500 200;
        20 400 -420;
    ]//1000
    X = ContinuousMarkovChain(T)
    @test stationary_distribution(X) == [100//113, 8//113, 5//113]
end

@testset "Fundamental Matrix" begin
    # Empty test
    T = Array{Any}(undef, 0, 0)
    X = ContinuousMarkovChain([], T)
    @test fundamental_matrix(X) == Array{Any}(undef, 0, 0)

    # http://www.mat.ufrgs.br/~giacomo/Softwares/R/Dobrow%20-%20Stochastic%20with%20R/ch7.pdf
    # Example 7.16
    T = [
        0 0 0;
        1 -1 0;
        1 1 -2;
    ]
    X = ContinuousMarkovChain(T)
    desired_new_matrix = [
        1 0;
        1/2 1/2;
    ]
    @test fundamental_matrix(X) == desired_new_matrix
end

@testset "Expected Time To Absorption" begin
    # Empty test
    T = Array{Any}(undef, 0, 0)
    X = ContinuousMarkovChain([], T)
    @test mean_time_to_absorption(X) == Any[]

    T = [
        0 0 0;
        4 -4 0;
        0 2 -2;
    ]
    X = ContinuousMarkovChain(T)
    @test mean_time_to_absorption(X) == [0.25, 0.75]
end

@testset "Exit Probability" begin
    # Empty test
    T = Array{Any}(undef, 0, 0)
    X = ContinuousMarkovChain([], T)
    @test exit_probabilities(X) == Array{Any}(undef, 0, 0)

    T = [
        0 0 0;
        0 -1 1;
        2 2 -4;
    ]
    X = ContinuousMarkovChain(T)
    @test exit_probabilities(X) == [1; 1][:, :]

    T = [
        0 0 0;
        0 0 0;
        8 8 -16;
    ]
    X = ContinuousMarkovChain(T)
    @test exit_probabilities(X) == [0.5 0.5]
end

@testset "Mean Recurrence Time" begin
    # Empty test
    T = Array{Any}(undef, 0, 0)
    X = ContinuousMarkovChain([], T)
    @test mean_recurrence_time(X) == Any[]

    T = [
        -1 1 0;
        0 -1 1;
        1 0 -1;
    ]
    X = ContinuousMarkovChain(T)
    @test mean_recurrence_time(X) == [3, 3, 3]

    T = [
        -1 1 0;
        0 -1 1;
        2 2 -4;
    ]
    X = ContinuousMarkovChain(T)
    @test mean_recurrence_time(X) == [3.5, 1.75, 1.75]
end

@testset "Mean First Passage Time" begin
    # Empty test
    T = Array{Any}(undef, 0, 0)
    X = ContinuousMarkovChain([], T)
    @test mean_first_passage_time(X) == Array{Any}(undef, 0, 0)

    T = [
        -1 1 0;
        0 -1 1;
        1 0 -1;
    ]
    X = ContinuousMarkovChain(T)
    @test mean_first_passage_time(X) == [
        0.0 1.0 2.0;
        2.0 0.0 1.0;
        1.0 2.0 0.0;
    ]

    T = [
        -1 1 0;
        0 -1 1;
        2 2 -4;
    ]
    X = ContinuousMarkovChain(T)
    @test mean_first_passage_time(X) == [
        0.0 1.0 2.0;
        2.5 0.0 1.0;
        1.5 0.75 0.0;
    ]
end


# This file includes modulation pulses

export RectangularPulse, RaisedCosinePulse, bandwidth

abstract type AbstractPulse end

"""
    $TYPEDEF

RectangularPulse pulse shape of the form,
```math
    p(t) =
    \\begin{cases}
    A & 0 \\leq t \\leq T \\
    0 & othersie \\
    \\end{cases}
```
where ``A`` is the amplitude and ``T`` is the pulse duration.

# Fields
    $TYPEDFIELDS
"""
struct RectangularPulse <: AbstractPulse
    "Amplitude"
    amplitude::Float64
    "Period"
    duration::Float64
end
RectangularPulse() = RectangularPulse(1., 1.)

(pulse::RectangularPulse)(t) = 0 ≤ t ≤ pulse.duration ? pulse.amplitude : zero(Float64)

"""
    $TYPEDEF

Raised coise pulse shapse of the form,
```math
    p(t) =
    \\begin{cases}
    \\drac{A}{2}(1 - cos(w t) & 0 \\leq t \\leq T \\
    0 & othersie \\
    \\end{cases}
```

# Fields
    $TYPEDFIELDS
"""
struct RaisedCosinePulse <: AbstractPulse
    amplitude::Float64
    duration::Float64
end
RaisedCosinePulse() = RaisedCosinePulse(1., 1.)

(pulse::RaisedCosinePulse)(t) = 0 ≤ t ≤ pulse.duration ? pulse.amplitude/2 * (1 - cos(2π/pulse.duration * t)) : zero(Float64)

"""
    $SIGNATURES

Returns the energy of the pulse `pulse`

# Example
```julia

```
"""
energy(pulse::RectangularPulse) = (pulse.amplitude)^2 * pulse.duration
energy(pulse::RaisedCosinePulse) = (pulse.amplitude)^2 * 3 / 8

"""
    $SIGNATURES

Returns the effetive bandwidth of the `pulse` in units of Hz.
"""
bandwidth(pulse::Union{RectangularPulse, RaisedCosinePulse}) = 1 / pulse.duration


function normal_model(t :: Trace, data :: Vector{Float64})
    loc = sample(t, :loc, Normal(0.0, 10.0))
    scale = sample(t, :scale, LogNormal())
    plate(t, observe, :obs, Normal(loc, scale), data)
end

function cat_model(t, dim, data)
    probs = sample(t, "probs", Dirichlet(ones(dim)))
    plate(t, observe, "data", Categorical(probs), data)
end

@testset "aic 1" begin
    data = [1, 2, 1, 3, 4, 3, 3, 3, 4, 2]
    dim = 4
    results = mh(cat_model; params = (dim, data), burn=1000, thin=100, num_iterations=21000)
    @time cat_aic = aic(results)
    @info "Computed AIC of cat model: $cat_aic"
    @info "Cat model has $(numparams(results.traces[1])) parameters"
end

@testset "hpds 1" begin
    data = randn(10) .* 2.0 .+ 4.0
    results = mh(normal_model; params = (data,), burn=1000, thin=100, num_iterations=21000)
    @time intervals = hpdi(results, 0.95, [:loc, :scale])
    @info "HPDI: $intervals"
    normal_aic = aic(results)
    @info "Computed AIC of normal model: $normal_aic"
    @info "Normal model has $(numparams(results.traces[1])) parameters"
end


"A `DomainPoint` is a point which is an element of a domain by construction."
abstract type DomainPoint{T} end

in(p::DomainPoint, d::Domain) = domain(p) == d || in(point(p), d)

## Points on a sphere

"A point on the unit sphere."
abstract type SpherePoint{T} <: DomainPoint{T} end

domain(p::SpherePoint{T}) where {T<:StaticTypes} = UnitSphere{T}()
domain(p::SpherePoint{T}) where {T<:AbstractVector} = UnitSphere{T}(length(point(p)))

"A point on the unit sphere represented by a standard Euclidean vector."
struct EuclideanSpherePoint{T} <: SpherePoint{T}
    x   ::  T
end
point(p::EuclideanSpherePoint) = p.x

"A point on the unit sphere represented in spherical coordinates."
struct SphericalCoordinate{T} <: SpherePoint{SVector{3,T}}
    θ   ::  T   # inclination or polar angle
    ϕ   ::  T   # azimuthal angle
end

point(p::SphericalCoordinate) = SVector(sin(p.θ)*cos(p.ϕ), sin(p.θ)*sin(p.ϕ), cos(p.θ))


# ## Data

import CSV

using DataFrames
using TuringModels: project_root

path = joinpath(project_root, "data", "Kline.csv")
df = CSV.read(path, DataFrame; delim=';')
df.log_pop = log.(df.population)
df.society = 1:nrow(df)
df

# ## Model

using Turing

@model function m12_6(total_tools, log_pop, society)
    N = length(total_tools)

    α ~ Normal(0, 10)
    βp ~ Normal(0, 1)

    σ_society ~ truncated(Cauchy(0, 1), 0, Inf)

    N_society = length(unique(society)) ## 10

    α_society ~ filldist(Normal(0, σ_society), N_society)

    for i in 1:N
        λ = exp(α + α_society[society[i]] + βp*log_pop[i])
        total_tools[i] ~ Poisson(λ)
    end
end;

# ## Output

chains = sample(
    m12_6(df.total_tools, df.log_pop, df.society),
    NUTS(0.95),
    1000
)

# \defaultoutput{}

# ## Original output

m12_6rethinking = "
              Mean StdDev lower 0.89 upper 0.89 n_eff Rhat
a              1.11   0.75      -0.05       2.24  1256    1
bp             0.26   0.08       0.13       0.38  1276    1
a_society[1]  -0.20   0.24      -0.57       0.16  2389    1
a_society[2]   0.04   0.21      -0.29       0.38  2220    1
a_society[3]  -0.05   0.19      -0.36       0.25  3018    1
a_society[4]   0.32   0.18       0.01       0.60  2153    1
a_society[5]   0.04   0.18      -0.22       0.33  3196    1
a_society[6]  -0.32   0.21      -0.62       0.02  2574    1
a_society[7]   0.14   0.17      -0.13       0.40  2751    1
a_society[8]  -0.18   0.19      -0.46       0.12  2952    1
a_society[9]   0.27   0.17      -0.02       0.52  2540    1
a_society[10] -0.10   0.30      -0.52       0.37  1433    1
sigma_society  0.31   0.13       0.11       0.47  1345    1
";


#!/usr/bin/env julia

#=
# data should be sorted and works by divide and conquer
# improved variant of binary search for equally distributed data
# it tries probing the position
#
# Interpolation search finds particular item by computing probe position.
# Start from middle of list. If not a match, divide list using probe position and find new middle.
# Do binary search in 2 blocks.
# Repeat
#
# run-time complexity is O(log log n)
=#

function interpolation_search(search_for, data_array, low=1, high=-1)
  if high == -1
    high = length(data_array)
  end
  if high < low
    return -1
  end

  ## calculation of probe position
  mid = Int(floor(
          (
           low +
           ((high - low) / (data_array[high] - data_array[low])) *
           (search_for - data_array[low])
          )
        ))
  if mid < low
    return -1
  end

  if data_array[mid] == search_for
    return mid
  end

  if data_array[mid] < search_for
    return interpolation_search(search_for, data_array, mid+1, high)
  else
    return interpolation_search(search_for, data_array, low, mid-1)
  end

  -1
end

     ## 1   2   3   4   5   6   7   8   9   10
data = [10, 11, 12, 22, 24, 27, 34, 47, 56, 67]

@time println("at index: ", interpolation_search(47, data))
@time println("at index: ", interpolation_search(12, data))
@time println("at index: ", interpolation_search(7, data))
@time println("at index: ", interpolation_search(67, data))
@time println("at index: ", interpolation_search(34, data))