### A Pluto.jl notebook ###
# v0.16.3

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

# ╔═╡ 14a8ec5a-bad3-4eea-b66e-cfc990bb81e3
using Plots

# ╔═╡ ecafe340-4535-4bb7-86cf-0b2621599698
begin
	import PlutoUI
	PlutoUI.Resource("https://github.com/joydill/Pluto-notebook-Optics/blob/main/images/course_banner.png?raw=true")
end

# ╔═╡ 34f1ebc6-1dec-4b71-8405-d742402590d4
md" In this page, we will showcase using Julia and Pluto in teaching Optics. In this demonstration, I will explain data visualization and interactive quiz. "

# ╔═╡ 3b24a671-727b-4ea2-a6b5-7624818b5614

# ╔═╡ 4d88b926-9543-11ea-293a-1379b1b5ae64
md"## Content and images
Tranditional optics, such as eye glasses and camera lenses, have material of the same refractive index. "

# ╔═╡ d4b6fc53-a9e8-473e-b9b9-dbaf547a92f9
PlutoUI.Resource("https://github.com/joydill/Pluto-notebook-Optics/blob/main/images/tradition_lens.png?raw=true",:width=>400)

# ╔═╡ 9a54c4cb-dfb5-4ebf-bfed-bcbe9c544317
md" In other words, the materials of the traditional optics are uniform. Traditional optics relies on the **shape** of the lenses to focus or diverge light as shown below:"

# ╔═╡ f71f567a-0693-4e85-a6c9-4343b5a1087b
PlutoUI.Resource("https://github.com/joydill/Pluto-notebook-Optics/blob/main/images/tradition_optics.jpeg?raw=true",:width => 500)

# ╔═╡ 6acec85b-8e61-4408-b279-3bb13ac5ad4e
md"## Interactive instruction
In this section, the instruction will be based on how much optics knowledge you already have. Interact with the notebook as you see fit.
"

# ╔═╡ 677671b5-dc33-4931-a655-56433d1c1926
md" Are you familiar with the concept of focal length? "

# ╔═╡ 3e8e381e-953f-11ea-3d3e-71d0fea52560
@bind fl_flag html"<select><option value='unknown'> <option value='yes'>yes</option><option value='no'>no</option></select>"

# ╔═╡ 359f79b4-fd0d-4524-967e-11480494e323
md" Focal length characterizes the ability of a lens to converge or diverge light! **How is focal length related to the shape of a lens?** To answer this question, we need to learn the **Lens maker's equation**."

# ╔═╡ 737553ae-cbe5-42cc-a309-450fb3823da4
PlutoUI.Resource("https://github.com/joydill/Pluto-notebook-Optics/blob/main/images/lensMakerEquation.png?raw=true",:width => 300)

# ╔═╡ e94c2956-9633-4e1e-85f5-e1275fe0e17c
md""" Confused about Lens maker's equation? If you would like to watch a video to explain Lens maker's equation check here: $(@bind video_flag html"<input type=checkbox >")
"""

# ╔═╡ d41c740a-2e69-4021-99a0-eeed1886a960
if video_flag == true
	html"""

	"""
end

# ╔═╡ 36cb2426-95b4-45af-bc15-2889b6c00657
md"## Interactive data visualization
In this section, we will explore how focal length changes with the radii of the two lens surfaces. First, let's define a function to describe the Lens maker's equation:
"

# ╔═╡ 44c89b4c-f27a-4197-b67c-872a3db1e5ff
function calc_FocalLength(radius1, radius2, index_lens, index_medium)
	curvature1 = 1/radius1
	curvature2 = 1/radius2
	power = (index_lens-index_medium)/index_medium*(curvature1-curvature2)
	focal_length = 1/power
	return focal_length
end

# ╔═╡ 344ec8a5-eba1-4b31-ab8d-cf1019dea712
md" We assume the lens index **$\bf{n_{l}}$ = 1.5** and the medium is air with an index **$\bf{n_{m}}$ = 1**."

# ╔═╡ b5387f3d-974a-4a10-9662-cf10808c6c5e
begin
	index_lens = 1.5
	index_medium = 1
	md" [index\_lens, index\_medium] = \[$([index_lens]) , $([index_medium])\]"

end

# ╔═╡ a38dd3b1-0343-415b-8777-d35a85b221fb
begin
	r1_slider = @bind radius1 html"<input type=range min=1 max=100>"
	r2_slider = @bind radius2 html"<input type=range min=-100 max=-1>"

	md"""**What are the radii of your lens?**

	radius1: $(r1_slider)

	radius2: $(r2_slider)"""
end

# ╔═╡ fdc93341-9ea8-42b9-be0a-34594241aca0
[radius1, radius2]

# ╔═╡ cc6483b5-8017-4ccd-b233-28c6dfe35d8b
md"**The focal length for your lens is:**"

# ╔═╡ 2afe3fd5-ae91-4850-b0e8-031c19b06b51
focal_length = calc_FocalLength(radius1, radius2, index_lens, index_medium)

# ╔═╡ 87a0227e-84b8-4465-aa36-2d78db4ff766
md"## Plotting to visualize data
In this section, we will visualize how focal length changes with radius of the front surface of a plano lens. The data visualization is achieved by using the Plots package of Julia. First, we declare using the package Plots:"

# ╔═╡ 90ecbd02-b45c-419b-9dc9-95ea64432c00
plotly()

# ╔═╡ 611c28fa-9542-11ea-1751-fbdedcfb7690
html"""
We will start by defining x and y variables. We will use radius1 as the x variable and focal length as the y variable. raidus2 will be a fixed value for the plots.
"""

# ╔═╡ ffc78b82-074c-43eb-a399-86b69b473aeb
begin
	radius1_list = 50:5:100
	focal_length_list = []
	for x in radius1_list
		focal_length = calc_FocalLength(x,radius2,index_lens,index_medium)
		append!(focal_length_list,focal_length)
	end
end

# ╔═╡ 509f8571-540b-4261-9216-0cfc61abaf15
md" Here we use the same slider bar to pick a value for radius2"

# ╔═╡ ff30ab04-ccdd-47b8-80b6-12eb634a86e9
md"""radius2: $(r2_slider)"""

# ╔═╡ 4f8189d7-c181-4b4d-b549-856d4ec33e0b
radius2

# ╔═╡ da59d884-e4f5-4e9e-8414-13e063a1a863
plot(radius1_list, focal_length_list,legend=false, xlabel="radius1 (mm)", ylabel="focal length (mm)")

# ╔═╡ 9d3af596-9554-11ea-21bd-bf427c91c424
md"## ⚡ Julia and Pluto power ⚡
In this notebook, we have demonstrated using **Julia** and **Pluto** to teach introduction to Optics. We explored using Julia and Pluto to

1.  Prepare a **webpage**-like content

2.  Provide **interactive** guidance

3.  **Visualize** data and equations.
"

# ╔═╡ 2ba62c87-e874-4df8-93cb-4b441d0ac5be
Markdown.MD(Markdown.Admonition("correct", "Thoughts ?", [md"Now you have seen what Julia and Pluto are like. What do you think about using Julia and Pluto for teaching? Would you consider using this tool ?"]))

# ╔═╡ 67f7b60d-c62f-4943-8bb4-fe387a93a92a
hint(text) = Markdown.MD(Markdown.Admonition("hint", "Hint", [text]));

# ╔═╡ e8fa696b-5fd6-47c0-8d79-a3da0e7f925a
almost(text) = Markdown.MD(Markdown.Admonition("warning", "Almost there!", [text]));

# ╔═╡ 096d8464-06a1-4e4d-9144-7246fa96a4ca
keep_working(text=md"The answer is not quite right.") = Markdown.MD(Markdown.Admonition("danger", "Keep working on it!", [text]));

# ╔═╡ 54d628f3-d901-4a38-b296-385aa9e5f5da
correct(text=md"Great! You got the right answer! Let's move on to the next section.") = Markdown.MD(Markdown.Admonition("correct", "Got it!", [text]));

# ╔═╡ aeb3a6bc-9540-11ea-0b8f-6d37412bfe68
if fl_flag == "yes"
	correct(md"Great! I am glad you have already known some optics. ")
elseif fl_flag == "no"
	hint(md"Look at the above image on convex and concave lens and find which section is marked as focal length for each lens.")
else
	almost(md"Please make a selection!")
end

# ╔═╡ 00000000-0000-0000-0000-000000000001
PLUTO_PROJECT_TOML_CONTENTS = """
[deps]
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
PlutoUI = "7f904dfe-b85e-4ff6-b463-dae2292396a8"

[compat]
Plots = "~1.22.6"
PlutoUI = "~0.7.16"
"""

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

[[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 = "d189c6d2004f63fd3c91748c458b09f26de0efaa"
uuid = "28b8d3ca-fb5f-59d9-8090-bfdbd6d07a71"
version = "0.61.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 = "cafe0823979a5c9bff86224b3b8de29ea5a44b2e"
uuid = "d2c73de3-f751-5644-a686-071e5b155ba9"
version = "0.61.0+0"

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

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

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

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

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

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

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

[[Hyperscript]]
deps = ["Test"]
git-tree-sha1 = "8d511d5b81240fc8e6802386302675bdf47737b9"
uuid = "47d2ed2b-36de-50cf-bf87-49c2cf4b8b91"
version = "0.0.4"

[[HypertextLiteral]]
git-tree-sha1 = "f6532909bf3d40b308a0f360b6a0e626c0e263a8"
uuid = "ac1192a8-f4b3-4bfe-ba22-af5b92cd3ab2"
version = "0.9.1"

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

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

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

[[InverseFunctions]]
deps = ["Test"]
git-tree-sha1 = "f0c6489b12d28fb4c2103073ec7452f3423bd308"
uuid = "3587e190-3f89-42d0-90ee-14403ec27112"
version = "0.1.1"

[[IrrationalConstants]]
git-tree-sha1 = "7fd44fd4ff43fc60815f8e764c0f352b83c49151"
uuid = "92d709cd-6900-40b7-9082-c6be49f344b6"
version = "0.1.1"

[[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 = "669315d963863322302137c4591ffce3cb5b8e68"
uuid = "23fbe1c1-3f47-55db-b15f-69d7ec21a316"
version = "0.15.8"

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

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

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

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

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

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

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

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

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

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

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

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

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

[[OpenSSL_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "15003dcb7d8db3c6c857fda14891a539a8f2705a"
uuid = "458c3c95-2e84-50aa-8efc-19380b2a3a95"
version = "1.1.10+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"

[[Parsers]]
deps = ["Dates"]
git-tree-sha1 = "98f59ff3639b3d9485a03a72f3ab35bab9465720"
uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0"
version = "2.0.6"

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

[[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", "LogExpFunctions", "Missings", "Printf", "Random", "SortingAlgorithms", "SparseArrays", "Statistics", "StatsAPI"]
git-tree-sha1 = "eb35dcc66558b2dda84079b9a1be17557d32091a"
uuid = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91"
version = "0.33.12"

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

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

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

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

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

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

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

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

[[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:
# ╟─ecafe340-4535-4bb7-86cf-0b2621599698
# ╟─34f1ebc6-1dec-4b71-8405-d742402590d4
# ╠═3b24a671-727b-4ea2-a6b5-7624818b5614
# ╟─4d88b926-9543-11ea-293a-1379b1b5ae64
# ╟─d4b6fc53-a9e8-473e-b9b9-dbaf547a92f9
# ╟─9a54c4cb-dfb5-4ebf-bfed-bcbe9c544317
# ╟─f71f567a-0693-4e85-a6c9-4343b5a1087b
# ╟─6acec85b-8e61-4408-b279-3bb13ac5ad4e
# ╟─677671b5-dc33-4931-a655-56433d1c1926
# ╟─3e8e381e-953f-11ea-3d3e-71d0fea52560
# ╟─aeb3a6bc-9540-11ea-0b8f-6d37412bfe68
# ╟─359f79b4-fd0d-4524-967e-11480494e323
# ╟─737553ae-cbe5-42cc-a309-450fb3823da4
# ╟─e94c2956-9633-4e1e-85f5-e1275fe0e17c
# ╟─d41c740a-2e69-4021-99a0-eeed1886a960
# ╟─36cb2426-95b4-45af-bc15-2889b6c00657
# ╟─44c89b4c-f27a-4197-b67c-872a3db1e5ff
# ╟─344ec8a5-eba1-4b31-ab8d-cf1019dea712
# ╟─b5387f3d-974a-4a10-9662-cf10808c6c5e
# ╟─a38dd3b1-0343-415b-8777-d35a85b221fb
# ╠═fdc93341-9ea8-42b9-be0a-34594241aca0
# ╟─cc6483b5-8017-4ccd-b233-28c6dfe35d8b
# ╟─2afe3fd5-ae91-4850-b0e8-031c19b06b51
# ╟─87a0227e-84b8-4465-aa36-2d78db4ff766
# ╠═14a8ec5a-bad3-4eea-b66e-cfc990bb81e3
# ╠═90ecbd02-b45c-419b-9dc9-95ea64432c00
# ╟─611c28fa-9542-11ea-1751-fbdedcfb7690
# ╟─ffc78b82-074c-43eb-a399-86b69b473aeb
# ╟─509f8571-540b-4261-9216-0cfc61abaf15
# ╟─ff30ab04-ccdd-47b8-80b6-12eb634a86e9
# ╠═4f8189d7-c181-4b4d-b549-856d4ec33e0b
# ╠═da59d884-e4f5-4e9e-8414-13e063a1a863
# ╟─9d3af596-9554-11ea-21bd-bf427c91c424
# ╟─2ba62c87-e874-4df8-93cb-4b441d0ac5be
# ╟─67f7b60d-c62f-4943-8bb4-fe387a93a92a
# ╟─e8fa696b-5fd6-47c0-8d79-a3da0e7f925a
# ╟─096d8464-06a1-4e4d-9144-7246fa96a4ca
# ╟─54d628f3-d901-4a38-b296-385aa9e5f5da
# ╟─00000000-0000-0000-0000-000000000001
# ╟─00000000-0000-0000-0000-000000000002


n = 4
print("Testing NCSubSeq for ", n, " items:\n   ")
for a in NCSubSeq(n)
    print(" ", a)
end
println()

s = "Rosetta"
cs = split(s, "")
m = 10
n = length(NCSubSeq(length(s))) - m
println()
println("The first and last ", m, " NC sub-sequences of \"", s, "\":")
for (i,a) in enumerate(NCSubSeq(length(cs)))
    i <= m || n < i || continue
    println(@sprintf "%6d %s" i join(cs[a], ""))
    i == m || continue
    println("    .. ......")
end

t = {}
append!(t, collect(1:10))
append!(t, collect(20:10:40))
append!(t, big(50):50:200)
println()
println("Numbers of NC sub-sequences of a given length:")
for i in t
    println(@sprintf("%7d => ", i), length(NCSubSeq(i)))
end


#=

Implements the empirical cumulative distribution function given an array
of observations.

@author : Spencer Lyon <spencer.lyon@nyu.edu>

@date: 2014-08-25

=#

@deprecate ECDF(x) StatsBase.ecdf(x)


workspace = pwd()
f = openxl(string(workspace,"/tsp/roadtrip_quebec.xls"))

data_cities = readxl(f, "Donnees!A9:A17")

cities = map(strip, data_cities)

data_distances = readxl(f, "Donnees!C9:K17")

nb_row, nb_col = size(data_distances)
distances = zeros(Int, nb_row, nb_col)
for i = 1:nb_row
    for j = 1:nb_col
        distances[i,j] = Int(data_distances[i,j])
    end
end

save(string(workspace,"/tsp/distances_quebec.jld"),"distances",distances)
save(string(workspace,"/tsp/cities_quebec.jld"),"cities",cities)

f2 = openxl(string(workspace,"/tsp/roadtrip_usa.xls"))

data_cities2 = readxl(f2, "Feuille1!A4:A53")

cities2 = map(strip, data_cities2)

data_distances2 = readxl(f2, "Feuille1!B4:B53")
string_distances2 = map(strip, data_distances2)
n = length(string_distances2)
latitude = zeros(n)
longitude = zeros(n)
for i = 1:n
	string_distances2[i] = string_distances2[i][2:end-1]
	latlon = split(string_distances2[i], ",")
	latitude[i] = parse(Float64, latlon[1])
	longitude[i] = parse(Float64, latlon[2])
end

function distance_earth(lat1, lon1, lat2, lon2)
    # approximate radius of earth in km
    R = 6371.0

    rlat1 = 2 * pi * lat1 / 360
    rlon1 = 2 * pi * lon1 / 360
    rlat2 = 2 * pi * lat2 / 360
    rlon2 = 2 * pi * lon2 / 360

    dlon = (lon2 - lon1) * 2 * pi / 360
    dlat = (lat2 - lat1) * 2 * pi / 360

    a = sin(dlat/2)^2 + cos(rlat1)*cos(rlat2)*sin(dlon/2)^2
    c = 2 * atan(sqrt(a), sqrt(1 - a))

    return R * c
end

distances2 = zeros(n,n)
for i = 1:n
	for j = 1:n
		distances2[i,j] = distance_earth(latitude[i], longitude[i], latitude[j], longitude[j])
	end
end

save(string(workspace,"/tsp/cities_usa.jld"),"cities2",cities2)
save(string(workspace,"/tsp/distances_usa.jld"),"distances2",distances2)


module CartesianDiscreteModelsTests

using Gridap
using Gridap.Fields
using Gridap.ReferenceFEs
using Gridap.Geometry
using Gridap.Geometry: CartesianMap
using Gridap.Io
using Test

domain = (0,1,0,1,0,1)
partition = (3,3,3)
parts = nothing
model = CartesianDiscreteModel(parts,domain,partition)
test_discrete_model(model)

domain = (0,1,0,1,0,1)
partition = (3,3,3)
model = CartesianDiscreteModel(domain,partition)
test_discrete_model(model)
@test is_oriented(get_grid(model)) == true
@test is_oriented(get_grid_topology(model)) == true

grid_topology = get_grid_topology(model)
cell_to_pindices = get_cell_permutations(grid_topology)

labels = get_face_labeling(model)
@test num_tags(labels) == 28
@test num_entities(labels) == 27

map = get_cell_map(get_grid(model))
@test isa(map,CartesianMap)

desc = get_cartesian_descriptor(model)

function polar(q)
  r, t, z = q
  x = r*cos(t)
  y = r*sin(t)
  Point(x,y,z)
end

domain = (1,2,0,pi,0,0.5)
partition = (10,30,4)
model = CartesianDiscreteModel(domain,partition;map=polar)
test_discrete_model(model)
@test is_oriented(get_grid(model)) == true

model2 = from_dict(DiscreteModel,to_dict(model))
test_discrete_model(model2)

model3 = CartesianDiscreteModel(desc,CartesianIndex(2,2,2),CartesianIndex(3,3,3))
test_discrete_model(model3)

domain = (0,1,0,1)
partition = (4,4)
desc = CartesianDescriptor(domain,partition;isperiodic=(true,true))
model = CartesianDiscreteModel(desc)
@test num_vertices(model) == 16
@test count(get_face_mask(get_face_labeling(model),"boundary",0)) == 0
model = CartesianDiscreteModel(desc,CartesianIndex(1,1),CartesianIndex(4,4))
@test num_vertices(model) == 16
@test count(get_face_mask(get_face_labeling(model),"boundary",0)) == 0
model = CartesianDiscreteModel(desc,CartesianIndex(2,2),CartesianIndex(4,4))
@test num_vertices(model) == 16
@test count(get_face_mask(get_face_labeling(model),"boundary",0)) == 7

desc = CartesianDescriptor(domain,partition;isperiodic=(true,false))
model = CartesianDiscreteModel(desc)
@test num_vertices(model) == 20
@test count(get_face_mask(get_face_labeling(model),"boundary",0)) == 8
model = CartesianDiscreteModel(desc,CartesianIndex(1,1),CartesianIndex(4,4))
@test num_vertices(model) == 20
@test count(get_face_mask(get_face_labeling(model),"boundary",0)) == 8
model = CartesianDiscreteModel(desc,CartesianIndex(2,2),CartesianIndex(4,4))
@test num_vertices(model) == 16
@test count(get_face_mask(get_face_labeling(model),"boundary",0)) == 7

desc = CartesianDescriptor(domain,partition;isperiodic=(false,true))
model = CartesianDiscreteModel(desc)
@test num_vertices(model) == 20
model = CartesianDiscreteModel(desc,CartesianIndex(1,1),CartesianIndex(4,4))
@test num_vertices(model) == 20
model = CartesianDiscreteModel(desc,CartesianIndex(2,2),CartesianIndex(4,4))
@test num_vertices(model) == 16
@test count(get_face_mask(get_face_labeling(model),"boundary",0)) == 7

desc = CartesianDescriptor(domain,partition;isperiodic=(false,false))
model = CartesianDiscreteModel(desc)
@test num_vertices(model) == 25
@test count(get_face_mask(get_face_labeling(model),"boundary",0)) == 16
model = CartesianDiscreteModel(desc,CartesianIndex(1,1),CartesianIndex(4,4))
@test num_vertices(model) == 25
@test count(get_face_mask(get_face_labeling(model),"boundary",0)) == 16
model = CartesianDiscreteModel(desc,CartesianIndex(2,2),CartesianIndex(4,4))
@test num_vertices(model) == 16
@test count(get_face_mask(get_face_labeling(model),"boundary",0)) == 7

desc = CartesianDescriptor(domain,partition)
remove_boundary = (false,true)
model = CartesianDiscreteModel(desc,CartesianIndex(1,1),CartesianIndex(4,4),remove_boundary)
@test num_vertices(model) == 25
@test count(get_face_mask(get_face_labeling(model),"boundary",0)) == 10

#using Gridap.Visualization
#writevtk(model2,"model2")

end # module


# ---
# title: 764. Largest Plus Sign
# id: problem764
# author: Tian Jun
# date: 2020-10-31
# difficulty: Medium
# categories: Dynamic Programming
# link: <https://leetcode.com/problems/largest-plus-sign/description/>
# hidden: true
# ---
#
# In a 2D `grid` from (0, 0) to (N-1, N-1), every cell contains a `1`, except
# those cells in the given list `mines` which are `0`. What is the largest axis-
# aligned plus sign of `1`s contained in the grid? Return the order of the plus
# sign. If there is none, return 0.
#
# An " _axis-aligned plus sign of`1`s_ of order **k** " has some center
# `grid[x][y] = 1` along with 4 arms of length `k-1` going up, down, left, and
# right, and made of `1`s. This is demonstrated in the diagrams below. Note that
# there could be `0`s or `1`s beyond the arms of the plus sign, only the
# relevant area of the plus sign is checked for 1s.
#
# **Examples of Axis-Aligned Plus Signs of Order k:**
#
#
#
#     Order 1:
#     000
#     0 **1** 0
#     000
#
#     Order 2:
#     00000
#     00 **1** 00
#     0 **111** 0
#     00 **1** 00
#     00000
#
#     Order 3:
#     0000000
#     000 **1** 000
#     000 **1** 000
#     0 **11111** 0
#     000 **1** 000
#     000 **1** 000
#     0000000
#
#
# **Example 1:**
#
#
#
#     Input: N = 5, mines = [[4, 2]]
#     Output: 2
#     Explanation:
#     11111
#     11111
#     1 **1** 111
#     **111** 11
#     1 **1** 011
#     In the above grid, the largest plus sign can only be order 2.  One of them is marked in bold.
#
#
# **Example 2:**
#
#
#
#     Input: N = 2, mines = []
#     Output: 1
#     Explanation:
#     There is no plus sign of order 2, but there is of order 1.
#
#
# **Example 3:**
#
#
#
#     Input: N = 1, mines = [[0, 0]]
#     Output: 0
#     Explanation:
#     There is no plus sign, so return 0.
#
#
# **Note:**
#
#   1. `N` will be an integer in the range `[1, 500]`.
#   2. `mines` will have length at most `5000`.
#   3. `mines[i]` will be length 2 and consist of integers in the range `[0, N-1]`.
#   4. _(Additionally, programs submitted in C, C++, or C# will be judged with a slightly smaller time limit.)_
#
#
## @lc code=start
using LeetCode

## add your code here:
## @lc code=end


using CellListMap
using ForwardDiff
using Unitful
using Measurements
using LinearAlgebra: norm_sqr

#
# This function computes the sum of the squared distances of the particles
#
function sumsq(x,sides,cutoff;parallel=false)
    box = Box(sides,cutoff)
    cl = CellList(x,box,parallel=parallel)
    s = zero(eltype(x[1]^2))
    s = map_pairwise(
        (x,y,i,j,d2,s) -> s += d2,
        s, box, cl, parallel=parallel
    )
    return s
end

#
# This is the analytical gradient of the above function
#
function sumsq_grad(x,sides,cutoff;parallel=false)
    g = similar(x)
    box = Box(sides,cutoff)
    cl = CellList(x,box)
    g .= zero(eltype(x))
    map_pairwise!(
        (x,y,i,j,d2,g) -> begin
            dx = x - y
            g[:,i] .+= 2*dx
            g[:,j] .-= 2*dx
            return g
        end,
        g, box, cl, parallel=parallel
    )
    return g
end

#
# Propagating measurements (or other more general types)
#
function sumsq_measurements(x_input,sides,cutoff;parallel=false)

    # The dual type of measurement does not propagate well on
    # all operations of CellListMap, and also comes with a signficant
    # cost, because they are not isbits types. Therefore, it is better
    # to bypass the internal computations by closing over the
    # input array and computing the result directly from the original
    # data. Here, `x_input` is the original array of coordinates
    # with uncertainties.
    function sum_sqr_pair(i,j,s,x_input,box)
        xi = x_input[i]
        xj = CellListMap.wrap_relative_to(x_input[j],xi,box)
        s += norm_sqr(xi - xj)
        return s
    end

    # Copy input vector of coordinates with uncertainties to a vector
    # of coordintes with values only
    x = [ getproperty.(v,:val) for v in x_input ]

    # Build cell lists
    box = Box(sides,cutoff)
    cl = CellList(x,box)

    # The initial result is initialized with the appropriate type.
    s = measurement(0.,0.)

    # And instead of using the `x` and `y` coordinates provided by the
    # interface, we close over the `x_input` and `box` for the calculations.
    s = map_pairwise(
        (x,y,i,j,d2,s) -> sum_sqr_pair(i,j,s,x_input,box),
        s, box, cl, parallel=parallel
    )
    return s
end

#
# Using generic types
#
function generic_types(iprint=true;parallel=false)

    #
    # Compare analtical and finite-difference gradient. Note that we convert the `sides`
    # and `cutoff` variables to the type of variable in `x`, to allow the propagation of the
    # dual numbers required to ForwardDiff and Unitful
    #
    x = rand(3,1000)
    cutoff = 0.1
    sides = [1.0, 1.0, 1.0]

    function sumsq_generic(x)
        cutoff_generic = eltype(x).(cutoff) # cutoff is closed over
        sides_generic = eltype(x).(sides) # sides is closed over
        return sumsq(x, sides_generic, cutoff_generic)
    end
    check_grad = sumsq_grad(x,sides,cutoff) ≈ ForwardDiff.gradient(sumsq_generic, x)
    iprint && println("Gradients are correct: ", check_grad)

    #
    # Now let us propagate units
    #
    x = rand(3,1000)u"nm"
    cutoff = 0.1u"nm"
    sides = [1.0, 1.0, 1.0]u"nm"
    runit = sumsq(x,sides,cutoff)
    iprint && println("Result with units: ", runit)

    #
    # Propagating uncertainties with Measurements
    #
    x = [ SVector{3}(measurement(rand(),0.01*rand()) for i in 1:3) for j in 1:1000 ]
    cutoff = 0.1
    sides = [1.0, 1.0, 1.0]
    rmeasurement = sumsq_measurements(x,sides,cutoff,parallel=parallel)
    iprint && println("Result with uncertainty: ", rmeasurement)

    return check_grad, unit(runit), typeof(rmeasurement)
end


samples = hcat([rand(predictor_joint) for sample = 1:1_000]...)
modes = [mode(samples[i, :]) for i = 1:size(samples, 1)]
overall_accuracy = Statistics.mean(modes .== iris[!, label_column])
@test overall_accuracy > 0.9 # make sure the overall accuracy is greater than 90%
classes = sort(unique(iris[!, label_column]))
@test length(classes) == 3
@test classes == ["setosa", "versicolor", "virginica"]
setosa_rows = findall(iris[!, label_column] .== "setosa")
versicolor_rows = findall(iris[!, label_column] .== "versicolor")
virginica_rows = findall(iris[!, label_column] .== "virginica")
@test length(setosa_rows) == 50
@test length(versicolor_rows) == 50
@test length(virginica_rows) == 50
@test length(iris[setosa_rows, label_column]) == 50
@test length(iris[versicolor_rows, label_column]) == 50
@test length(iris[virginica_rows, label_column]) == 50
setosa_accuracy  = Statistics.mean(modes[setosa_rows] .== iris[setosa_rows, label_column])
versicolor_accuracy  = Statistics.mean(modes[versicolor_rows] .== iris[versicolor_rows, label_column])
virginica_accuracy  = Statistics.mean(modes[virginica_rows] .== iris[virginica_rows, label_column])
@test setosa_accuracy > 0.9 # make sure the accuracy on the "setosa" class is greater than 90%
@test versicolor_accuracy > 0.9 # make sure the accuracy on the "setosa" class is greater than 90%
@test virginica_accuracy > 0.9 # make sure the accuracy on the "setosa" class is greater than 90%

predictor_marginal = MLJBase.predict(mach, iris[1:size(iris, 1), feature_columns])
@test predictor_marginal isa MLJBase.UnivariateFiniteVector
@test size(predictor_marginal) == (size(iris, 1),)

# Now, we make sure that the cross-validated accuracy is greater than 90%
nfolds = 4
evaluation_results = evaluate!(mach, resampling=CV(; nfolds = nfolds, shuffle = true), measure=MLJBase.accuracy, operation=MLJBase.predict_mode)
@test evaluation_results.measurement isa Vector
@test length(evaluation_results.measurement) == 1
@test evaluation_results.measurement[1] > 0.9 # make sure that the overall cross-validated accuracy is greater than 90%
@test evaluation_results.per_fold isa Vector
@test length(evaluation_results.per_fold) == 1
@test evaluation_results.per_fold[1] isa Vector
@test length(evaluation_results.per_fold[1]) == nfolds # make sure that the accuracy is greater than 80% in each fold
@test all(evaluation_results.per_fold[1] .> 0.8)


# SignalExtensions.jl

module SignalExtensions

using OffsetArrays

import Base: eltype, getindex, setindex!, eachindex, collect, nzrange

import Base: transpose, ctranspose, conj, reverse

import Base: *

# A collection of useful functions
include("util.jl")
# Definition of abstract Sequence type
include("sequences.jl")
# Definition of z-transform and Fourier transform
include("transforms.jl")
# The various types of extensions
include("extensions.jl")
# Extension types that store a vector
include("extension_sequences.jl")
# Lazy representations of sequence manipulations
include("lazy.jl")
# A number of special sequences
include("special_sequences.jl")

## We list all functions exported by the package below.
## This list is exhaustive and may serve as a reference for the functionality.

# From util.jl
export reverse_indices

# From sequences.jl:
# - types
export Sequence
# - iterator methods
export firstindex, lastindex, each_nonzero_index
# - properties
export iscompact

# From transforms.jl
# - types
export ZTransform, FourierTransform
# - methods
export ztransform, fouriertransform

# From extensions.jl
# - types
export PeriodicExtension, ZeroPadding, ConstantPadding, SymmetricExtension
# - methods
export element, periodic_index

# From extension_sequences.jl
# - types
export ExtensionSequence, PeriodicSequence, CompactSequence, ZeroPaddingSequence,
    ConstantPaddingSequence, SymmetricSequence
# - convenience constructors
export symmetric_extension_wholepoint_even, symmetric_extension_wholepoint_odd,
    symmetric_extension_halfpoint_even, symmetric_extension_halfpoint_odd
# - other methods
export subvector, first_subindex, last_subindex, sublength
export left_parity, right_parity, left_symmetry, right_symmetry

# From lazy.jl
# - types
export ModulatedSequence, DownsampledSequence, UpsampledSequence, ShiftedSequence,
    ReversedSequence, Convolution
# - methods
export downsample, upsample, convolve, shift, modulate, reverse
export sequence_shift, samplefactor

# From special_sequences.jl
export ZeroSequence, DiracSequence

end # module


# Shelling Segregation model

## Instructions

# Source: Thomas C. Schelling (1971) Dynamic models of segregation, The Journal of Mathematical Sociology, 1:2, 143-186, DOI: [10.1080/0022250X.1971.9989794](https://doi.org/10.1080/0022250X.1971.9989794)

# Your job is implementing the Shelling Segregation Model, a classical agent-based model in the social sciences, introducing the concepts of emerging macro behaviours and tipping points, leading to its author (Thomas Schelling) receiving the Nobel prize in Economics in 2005.
# One of the very first results obtained from running simulations, especially in the social sciences.
# The world is modelled as a gridded space inhabited by two groups (in that period racial and segregation questions concerning the cohabitation of "blacks" and "whites" were topical).
# Each group has a preference to live in a neighbourhood inhabited by agents of their own type, with a certain "tolerance". When the share of agents of its own type in the neighbourhood falls below this "tolerance" level, the agent tries to relocate somewhere with a higher share of its own type of agents.
# The main result of the paper was that even a relatively mild preference for the presence of similar agents would have led to a segregated world and that there was a specific threshold level (between 30% and 40%) that was driving these two completely different outputs, i.e. a "tipping point".
# The simulation algorithm then goes in this way:
# For each step, look at each agent, check if it is "happy" with its current location (looking at the share of own types in the neighbourhood), and, if not, relocate the agent to a position where it would be (setting its past location as empty).

# There are various ways of "generality" vs "specificity" to code the algorithm above. On one end you could hard-code the two agent types, e.g. as `1` and `2`, on the other you could be very generic and create an abstract type `Agent` and a concrete class for each agent type.
# The skeleton below proposes an intermediate approach with only one `Agent` class and the kind of agent encoded as an integer, with 0 representing an empty cell. Fill free to use it or to develop your own algorithm from scratch!

# ------------------------------------------------------------------------------
# ## 1) Setting the environment...
cd(@__DIR__)
using Pkg
Pkg.activate(".")
# If using a Julia version different than 1.7 please uncomment and run the following line (reproductibility guarantee will hower be lost)
# Pkg.resolve()
Pkg.instantiate()
using Random
Random.seed!(123)
using Plots

# ------------------------------------------------------------------------------
# ## 2) Defining the `Agent` and `Env` classes...
mutable struct Agent
    gid::Int64
end
mutable struct Env
    nR::Int64                       # number of rows
    nC::Int64                       # number of columns
    similarityThreeshold::Float64   # threeshold for agents to be "happy" with their location
    neighborhood::Int64             # how far looking for "similar" agents
    nSteps::Int64                   # number of iteractive steps to employ
    cells::Vector{Agent}            # total cells in the environment
    gids::Vector{Int64}             # ids of the agents types (or "groups")
    grsizes::Vector{Int64}          # number of agents per group
end

# ------------------------------------------------------------------------------
# ## 3) Defining some utility functions...
xyToId(x,y,nR,nC) =  nR*(x-1)+y
iDToXY(id,nR,nC)  =  Int(floor((id-1)/nR)+1), (id-1)%(nR)+1
printableGrid(env) = reshape([a.gid for a in env.cells],env.nR,env.nC)

# ------------------------------------------------------------------------------
# ## 4) Defining the main functions of the algorithm...
"""
   getNeighbours(x,y,env,gid=nothing)

Return the number of total neighbours if `gid` is `nothing` (skipping the empty cells) or of a specific `gid` if this one is provided.

"""
function getNeighbours(x,y,env,gid=nothing)
    board  = reshape(env.cells,env.nR,env.nC)
    region = board[max(1,y-env.neighborhood):min(nR,y+env.neighborhood),max(1,x-env.neighborhood):min(nC,x+env.neighborhood)]
    ## [...] Write your code here
end

"""
   isHappy(x,y,a,env)

Return whether the specific agent `a` is happy at his current location
"""
function isHappy(x,y,a,env)
   ## [...] Write your code here
end

"""
   reallocatePoints!(env)

Loop over all the cells and if an agent on that location is unhappy, it moves it to a location where it is happy and set the departing cell as empty (i.e. occupied by an agent whose gid is zero).
It returns the share of agents that were happy before the move.
"""
function reallocatePoints!(env)
    happyCount = 0
    for (i,a) in enumerate(env.cells)
       ## [...] Write your code here
    end
    return happyCount/sum(env.grsizes)
end

"""
    run!(env)

Run the reallocation algorithm for the given steps printing a heatmap at each iteration.
Also, print at the end the chart of the happy agents by epoch
"""
function run!(env)
    outplot = heatmap(printableGrid(env), legend=nothing, title="START", color=mypal,aspect_ratio=env.nR/env.nC, size=(600,600*env.nR/env.nC))
    nHappyCount = Float64[]
    display(outplot)
    for i in 1:env.nSteps
        println("Running iteration $i...")
        nHappy = reallocatePoints!(env)
        push!(nHappyCount,nHappy)
        outplot = heatmap(printableGrid(env), legend=nothing, title="Iteration $i", color=mypal,aspect_ratio=env.nR/env.nC, size=(600,600*env.nR/env.nC))
        display(outplot)
    end
    happyCountPlot = plot(nHappyCount,title="Share of happy agents by iteration")
    display(happyCountPlot)
end

# ------------------------------------------------------------------------------
# ## 5) Setting the parameters of the specific simulation to run...

# Parameters...
nR         = 200
nC         = 200
nSteps     = 20
similarityThreeshold = 0.4         # Agent is happy if at least 40% similar
neighborhood = 5                   # Defining how far looking for similar agents
mypal        = [:white,:red,:blue] # First colour is for the empty cell
gids         = [1,2]               # Gid 0 is reserved for empty cell
grShares     = [0.4,0.4]           # Shares of cells occupied by agents, by type

# ------------------------------------------------------------------------------
# ## 6) Initialising the simulation with the given parameters...

nCells  = nR*nC
nGroups = length(gids)
grsizes = Int.(ceil.(nCells .* grShares))

cells  = fill(Agent(0),nCells)
count = 1
for g in 1:nGroups
    [cells[j] = Agent(gids[g]) for j in count:count+grsizes[g]-1]
    count += grsizes[g]
end

shuffle!(cells)
env = Env(nR,nC,similarityThreeshold,neighborhood,nSteps,cells,gids,grsizes)
heatmap(printableGrid(env), legend=nothing, title="Iteration 0", color=mypal,aspect_ratio=env.nR/env.nC, size=(600,600*env.nR/env.nC))

# ------------------------------------------------------------------------------
# ## 7) Running the model...
run!(env)

# ------------------------------------------------------------------------------
# ## 8) Optional variations...

# You can try to implement variations of this model. Some ideas:

# - implement multiple agent groups;
# - run Monte-Carlo simulations with respect to the tolerance threshold, with it being a property of the agent rather than of the group, perhaps with the group having a certain distribution of it and each agent of that group sampling from it;
# - implement two thresholds, with one to define happiness and one to define the actual decision to move (to consider costs associated with relocation);
# - ....


using Bits, Test
using Bits: NOTFOUND

x ≜ y = typeof(x) == typeof(y) && x == y

@testset "bitsize" begin
    for T in (Base.BitInteger_types..., Float16, Float32, Float64)
        @test bitsize(T) === sizeof(T) * 8
        @test bitsize(zero(T)) === bitsize(one(T)) === bitsize(T)
    end
    @test bitsize(BigInt) === Bits.INF
    @test bitsize(Bool) === 1
    @test bitsize(Float64) === 64
    @test bitsize(Float32) === 32
    @test bitsize(Float16) === 16
    @test_throws MethodError bitsize(BigFloat)
    @test bitsize(BigFloat(1, 256)) == 321
    @test bitsize(BigFloat(1, 100)) == 165
end

@testset "bit functions" begin
    @testset "weight" begin
        for T = (Base.BitInteger_types..., BigInt)
            @test weight(T(123)) === 6
            T == BigInt && continue
            @test weight(typemax(T)) === bitsize(T) - (T <: Signed)
            @test weight(typemin(T)) === Int(T <: Signed)
        end
        @test weight(big(-1)) === weight(big(-999)) === Bits.INF
    end

    @testset "bit & tstbit" begin
        val(::typeof(bit), x) = x
        val(::typeof(tstbit), x) = x % Bool
        for _bit = (bit, tstbit)
            for T = (Base.BitInteger_types..., BigInt)
                T0, T1 = T(0), T(1)
                @test _bit(T(0), rand(1:bitsize(T))) ≜ val(_bit, T0)
                @test _bit(T(1), 1) ≜ val(_bit, T1)
                @test _bit(T(1), 2) ≜ val(_bit, T0)
                @test _bit(T(5), 1) ≜ val(_bit, T1)
                @test _bit(T(5), 2) ≜ val(_bit, T0)
                @test _bit(T(5), 3) ≜ val(_bit, T1)
                @test _bit(T(5), 4) ≜ val(_bit, T0)
            end
            @test _bit( 1.0, 64) ≜ val(_bit, 0)
            @test _bit(-1.0, 64) ≜ val(_bit, 1)
            @test _bit( Float32(1.0), 32) ≜ val(_bit, Int32(0))
            @test _bit(-Float32(1.0), 32) ≜ val(_bit, Int32(1))
            x = BigFloat(-1.0, 128)
            for i=1:128+65
                @test _bit(x, i) == val(_bit, big(i ∈ [128, 129, 128+65]))
            end
        end
    end

    @testset "mask" begin
        for T = Base.BitInteger_types
            i = rand(0:min(999, bitsize(T)))
            @test mask(T, i) ≜ Bits.mask_2(T, i)
            @test mask(T) ≜ -1 % T
        end
        i = rand(0:bitsize(Bits.Word))
        @test mask(Bits.Word, i) === mask(i)
        @test mask(Bits.Word) === mask()
        @test mask(0) == 0
        @test mask(1) == 1
        @test mask(Sys.WORD_SIZE) === mask() === 0xffffffffffffffff
        @test mask(UInt64, 64) == mask(64)
        @test mask(UInt64, 63) == 0x7fffffffffffffff
        @test mask(BigInt, 0) ≜ big(0)
        @test mask(BigInt, 1) ≜ big(1)
        @test mask(BigInt, 1024) ≜ big(2)^1024 - 1

        # 2-arg mask
        for T = (Base.BitInteger_types..., BigInt)
            j, i = minmax(rand(0:min(999, bitsize(T)), 2)...)
            m = mask(T, j, i)
            @test m ≜ Bits.mask_2(T, j, i)
            if T === Bits.Word
                @test m === mask(j, i)
            end
            @test count_ones(m) == i-j
            @test m >>> j ≜ mask(T, i-j)
            @test mask(T, j, -1) ≜ ~mask(T, j)
        end
        @test mask(UInt64, 2, 4) === 0x000000000000000c
    end

    @testset "masked" begin
        for T = (Base.BitInteger_types..., BigInt)
            j, i = minmax(rand(0:min(999, bitsize(T)), 2)...)
            @test masked(mask(T), j) ≜ mask(T, j)
            @test masked(mask(T), j, i) ≜ mask(T, j, i)
            @test masked(mask(T, i), i) ≜ mask(T, i)
            @test masked(mask(T, i), j, i) ≜ mask(T, j, i)
            T == BigInt && continue
            x = rand(T)
            @test masked(x, j) ≜ x & mask(T, j)
            @test masked(x, j, i) ≜ x & mask(T, j, i)
        end
        @test masked(0b11110011, 1, 5) ≜ 0b00010010
        @test masked(-1.0, 52, 63) === 1.0
    end

    @testset "low0, low1, scan0, scan1" begin
        for T = (Base.BitInteger_types..., BigInt)
            x = T(0b01011010)
            @test low1(x, 0) == NOTFOUND
            @test low1(x)    == 2
            @test low1(x, 1) == 2
            @test low1(x, 2) == 4
            @test low1(x, 3) == 5
            @test low1(x, 4) == 7
            for i = 5:min(128, bitsize(T))+1
                @test low1(x, i) == NOTFOUND
            end
            @test low0(x, 0) == NOTFOUND
            @test low0(x)    == 1
            @test low0(x, 1) == 1
            @test low0(x, 2) == 3
            @test low0(x, 3) == 6
            @test low0(x, 4) == 8
            for i = 5:min(128, bitsize(T))
                @test low0(x, i) == (i+4 <= bitsize(T) ? i+4 : NOTFOUND)
            end

            @test scan1(x, 0) == NOTFOUND
            @test scan1(x, 1) == 2
            @test scan1(x)    == 2
            @test scan1(x, 2) == 2
            @test scan1(x, 3) == 4
            @test scan1(x, 4) == 4
            @test scan1(x, 5) == 5
            @test scan1(x, 6) == 7
            @test scan1(x, 7) == 7
            for i = 8:min(128, bitsize(T))+1
                @test scan1(x, i) == NOTFOUND
            end
            @test scan0(x, 0) == NOTFOUND
            @test scan0(x, 1) == 1
            @test scan0(x)    == 1
            @test scan0(x, 2) == 3
            @test scan0(x, 3) == 3
            @test scan0(x, 4) == 6
            @test scan0(x, 5) == 6
            @test scan0(x, 6) == 6
            @test scan0(x, 7) == 8
            @test scan0(x, 8) == 8
            for i = 9:min(128, bitsize(T))
                @test scan0(x, i) == i
            end
            T === BigInt && continue
            @test scan0(x, bitsize(T)+1) == NOTFOUND
        end
    end
end

@testset "bits" begin
    for T in Base.BitInteger_types
        v = bits(one(T))
        @test length(v) == bitsize(T)
        @test v[1] === true
        @test count(v) == 1
        v = bits(zero(T))
        @test length(v) == bitsize(T)
        @test v[1] === false
        @test count(v) == 0
    end
    v = bits(7)
    @test v[1] === v[2] === v[3] === true
    @test count(v) == 3
    for T = (Int64, UInt64)
        v = bits(T(2)^63)
        @test v[64] === true
        @test count(v) == 1
    end
    @test bits(Float64(-16))[53:end] == [1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1]
    @test count(bits(Float64(-16))) == 4

    @testset "array indexing" begin
        v = bits(1234)[1:8]
        @test v == [0, 1, 0, 0, 1, 0, 1, 1]
        @test v isa Bits.BitVector1Mask
        v = bits(1.2)[10:17]
        @test v == [1, 0, 0, 1, 1, 0, 0, 1]
        @test v isa Bits.BitVector1Mask{Int64}
        @test all(bits(123)[[1, 2, 4, 5, 6, 7]])
        @test count(bits(123)) == 6
        # test optimization for v[i:j]
        for T = (Base.BitInteger_types..., BigInt)
            i, j = minmax(rand(1:min(999, bitsize(T)), 2)...)
            v = bits(rand(T == BigInt ? (big(-999):big(999)) : T))
            @test v[i:j] == v[i:1:j]
        end
    end
end


module Percolation

export dfspercolate, dfspercolate!, colorpercolate, hkpercolate,
    clustgyration, hklabel, hkmaxclust!, genclust, clustfractal

using DataStructures

"""
Find out whether or not percolation is possible in a 2D grid using depth-first search

Percolation is assumed to occur from top to bottom.

# Argument

`grid::Matrix{Bool}`: Represents the grid cells; True if porous (part of the path;
    can pass through) and False if blocked (can't pass through; no path passes through)

# Returns

boolean indicating whether percolation is possible
"""
function dfspercolate(grid::BitMatrix)
    height, width = size(grid)

    undiscovered = BitMatrix(undef, height + 2, width + 2)
    # "pad" the array to avoid bound checking for neigboring
    undiscovered[:, 1] .= false
    undiscovered[1, :] .= false
    undiscovered[end, :] .= false
    undiscovered[:, end] .= false
    # insert grid into undiscovered array
    undiscovered[2:end-1, 2:end-1] = grid

    stack = Stack{Tuple{Integer, Integer}}()
    for root in 2:width+1
        if undiscovered[2, root] # top row
            # search this path
            push!(stack, (2, root))
            if dfs!(height + 1, stack, undiscovered)
                return true
            end
        end
    end
    return false
end

"""
Depth-first search algorithm adopted for the dfspercolate function
"""
@inline function dfs!(destination::Integer, stack::Stack, undiscovered::BitMatrix)
    while !isempty(stack)
        vertex = pop!(stack)
        undiscovered[vertex...] = false

        # check if destination is reached
        if vertex[1] == destination
            return true
        end

        # check neighboring vertices
        for step in ((-1, 0), (0, -1), (0, 1), (1, 0))
            neighbor = vertex .+ step
            if undiscovered[neighbor...]
                push!(stack, neighbor)
            end
        end
    end
    return false
end

"""
Find out whether or not percolation is possible in a 2D grid using depth-first search

Percolation is assumed to occur from top to bottom. this version marks points visited by
the dfs in the visited

# Arguments

- `grid::Matrix{Bool}`: Represents the grid cells; True if porous (part of the path;
    can pass through) and False if blocked (can't pass through; no path passes through)
- `visited::BitMatrix`: whether each point is visited by the dfs

# Returns

boolean indicating whether percolation is possible
"""
function dfspercolate!(grid::BitMatrix, visited::BitMatrix)
    height, width = size(grid)

    undiscovered = BitMatrix(undef, height + 2, width + 2)
    # "pad" the array to avoid bound checking for neigboring
    undiscovered[:, 1] .= false
    undiscovered[1, :] .= false
    undiscovered[end, :] .= false
    undiscovered[:, end] .= false
    # insert grid into undiscovered array
    undiscovered[2:end-1, 2:end-1] = grid

    stack = Stack{Tuple{Integer, Integer}}()
    for root in 2:width+1
        if undiscovered[2, root] # top row
            # search this path
            push!(stack, (2, root))
            if dfs!(height + 1, stack, undiscovered, visited)
                return true
            end
        end
    end
    return false
end

"""
Depth-first search algorithm adopted for the dfspercolate function

this version modifies the `visited` matrix
"""
@inline function dfs!(destination::Integer, stack::Stack, undiscovered::BitMatrix,
        visited::BitMatrix)
    while !isempty(stack)
        vertex = pop!(stack)
        undiscovered[vertex...] = false
        visited[(vertex .- (1, 1))...] = true

        # check if destination is reached
        if vertex[1] == destination
            return true
        end

        # check neighboring vertices
        for step in ((-1, 0), (0, -1), (0, 1), (1, 0))
            neighbor = vertex .+ step
            if undiscovered[neighbor...]
                push!(stack, neighbor)
            end
        end
    end
    return false
end

"""
labels the clusters in a 2D grid for solving the percolation problem

Uses and inefficient O((length * width)^2) coloring algorithm.
Percolation is assumed to occur from top to bottom.

# Argument

`grid::Matrix{Bool}`: Represents the grid cells; True if porous (part of the path;
    can pass through) and False if blocked (can't pass through; no path passes through)

# Returns

a boolean indicating whether percolation is possible and a matrix "colored" with integers.
Points with value 1 are connected to the starting row.
"""
function colorpercolate(grid::BitMatrix)
    height, width = size(grid)
    colors = zeros(Integer, height + 1, width)
    colors[1, :] .= 1
    counter = 2

    for row in 2:height+1
        for col in 1:width
            if grid[row - 1, col]
                neighbors = neighborhood(colors, row, col)
                nonzero = filter(n -> colors[n...] != 0, neighbors)
                if length(nonzero) == 0
                    colors[row, col] = counter
                    counter += 1
                elseif length(nonzero) == 1
                    colors[row, col] = colors[nonzero[1]...]
                else
                    minneighbor = minimum([colors[n...] for n in nonzero])
                    colors[row, col] = minneighbor
                    for neighbor in nonzero
                        if colors[neighbor...] != minneighbor
                            colors[colors .== colors[neighbor...]] .= minneighbor
                        end
                    end
                end
            end
        end
    end
    return any(x -> x == 1, colors[end, :]), colors[2:end, :]
end

"""
Retruns a vector of tuples of the neighbors of the point at [row, col]
"""
@inline function neighborhood(grid::Matrix, row::Integer, col::Integer)
    neighbors = [(row + 1, col), (row - 1, col), (row, col + 1), (row, col - 1)]
    return filter(n -> checkbounds(Bool, grid, n...), neighbors)
end

"""
Uses the Hoshen-Kopelman algorithm to label the clusters in a 2D grid for percolation

Percolation is assumed to occur from top to bottom.

# Arguments

- `grid::Matrix{Bool}`: Represents the grid cells; True if porous (part of the path;
    can pass through) and False if blocked (can't pass through; no path passes through)
- `color::Bool` (default = `false`): whether to color the grid after finishing the algorithm

# Returns

[a boolean indicating whether percolation is possible or, if qinfty is true,
the sum of the sizes of the open connecting clusters (integer)],
an integer matrix where clusters, are marked by unique integers, and the cluster lengths.
"""
function hkpercolate(grid::BitMatrix; color::Bool=false, qinfty::Bool=false)
    height, width = size(grid)
    labels = zeros(Integer, height, width)
    list = Vector{Integer}()
    clusters = Vector{Integer}()

    # first row, special case because of top boundary
    hkfirstrow!(grid, labels, list, clusters)

    for row in 2:height
        # first column, special case because of left boundary
        if grid[row, 1]
            hkfirstcol!(labels, list, clusters, row)
        end

        # "middle" points, with appropriate neighbors
        for col in 2:width
            if grid[row, col]
                hkiter!(labels, list, clusters, row, col)
            end
        end
    end

    if color
        hkcolor!(labels, list)
    end
    if qinfty
        return hkresult(labels, list, clusters), labels, clusters
    else
        return hkbool(labels, list), labels, clusters
    end
end

"""
Finds the radius of gyration for the largest closed cluster in the percolation grid

Percolation is assumed to occur from top to bottom. Uses the Hoshen-Kopelman algorithm.

# Argument

`grid::Matrix{Bool}`: Represents the grid cells; True if porous (part of the path;
    can pass through) and False if blocked (can't pass through; no path passes through)

# Returns

The gyration radius of the largest closed cluster (the largest cluster that doesn't connect
the top and bottom of the grid)
"""
function clustgyration(grid::BitMatrix)
    labels, list, clusters = hklabel(grid)
    if length(clusters) == 0
        return 0
    end

    maxlabel = hkmaxclust!(labels, list, clusters)
    if maxlabel == 0
        return 0
    end

    maxclust = Tuple.(findall(x -> x != 0 && hkfind(list, x) == maxlabel, labels))
    mass = length(maxclust)
    if mass == 0
        return 0
    end

    centerofmass = reduce(.+, maxclust) ./ mass
    return √(sum(pos -> reduce(.+, (pos .- centerofmass).^2),
        maxclust) / mass)
end

"""
Uses the Hoshen-Kopelman algorithm to label the grid

returns the labels, label mapping list, and cluster lengths
"""
@inline function hklabel(grid::BitMatrix)
    height, width = size(grid)
    labels = zeros(Integer, height, width)
    list = Vector{Integer}()
    clusters = Vector{Integer}()

    # first row, special case because of top boundary
    hkfirstrow!(grid, labels, list, clusters)

    for row in 2:height
        # first column, special case because of left boundary
        if grid[row, 1]
            hkfirstcol!(labels, list, clusters, row)
        end

        # "middle" points, with appropriate neighbors
        for col in 2:width
            if grid[row, col]
                hkiter!(labels, list, clusters, row, col)
            end
        end
    end

    return labels, list, clusters
end

"""
Finds the label of the largest closed cluster (a.k.a. finite cluster).

Also, sets the size of open clusters (a.k.a. infinite cluster) to zero, hence the !
"""
@inline function hkmaxclust!(labels::Matrix{Integer}, list::Vector{Integer},
        clusters::Vector{Integer})
    for root in labels[1, :]
        if root > 0
            for dest in labels[end, :]
                if dest > 0
                    toplabel = hkfind(list, root)
                    bottomlabel = hkfind(list, dest)
                    if toplabel == bottomlabel
                        clusters[toplabel] = 0
                    end
                end
            end
        end
    end

    if maximum(clusters) > 0
        return argmax(clusters)
    else
        return 0
    end
end

"""
Uses a depth-first search algorithm to generate a cluster with the given site probability

# Arguments

- `probability`: the probability a site is generated when growing the cluster
- `maxsize::Integer` (optional, defaults to 10000): the maximum size of the cluster
    (breaks the loop after the cluster is grown to this size)

# Returns

an integer matrix (8-bit) with 0 where no site exists, 1 where a cell is generated, and 2
where the growth is stopped
"""
function genclust(probability::Float64; maxsize::Integer=10000, crop::Bool=true)
    cluster = zeros(Int8, maxsize, maxsize)
    # create "fences" to stop the cluster form reaching the bounds of the matrix
    cluster[1, :] .= 2
    cluster[:, 1] .= 2
    cluster[end, :] .= 2
    cluster[:, end] .= 2

    stack = Stack{Tuple{Integer, Integer}}()
    seed = (maxsize÷2, maxsize÷2)
    push!(stack, seed)
    cluster[seed...] = 1

    clustdfs!(stack, cluster, probability, maxsize)

    if crop
        return cropclust!(cluster)
    else
        return cluster
    end
end

"""
The depth-first search algorithm used for `genclust`
"""
@inline function clustdfs!(stack::Stack, cluster::Matrix{<:Integer},
        probability::Float64, maxsize::Integer)
    size = 1
    while !isempty(stack) && size < maxsize
        site = pop!(stack)
        for step in ((-1, 0), (0, -1), (0, 1), (1, 0))
            neighbor = site .+ step
            if cluster[neighbor...] == 0
                if rand() <= probability
                    cluster[neighbor...] = 1
                    push!(stack, neighbor)
                    size += 1
                else
                    cluster[neighbor...] = 2
                end
            end
        end
    end
end

"""
finds the mass and radius of gyration of the cluster from the output of `genclust`
"""
function clustfractal(cluster::Matrix{Int8})
    clust = Tuple.(findall(x -> x == 1, cluster))
    mass = length(clust)
    if mass == 0
        return 0
    end

    centerofmass = reduce(.+, clust) ./ mass
    return mass, √(sum(pos -> reduce(.+, (pos .- centerofmass).^2), clust) / mass)
end

"""
crop the `genclust` function output to remove excess zeros
"""
@inline function cropclust!(cluster::Matrix{<:Integer})
    height, width = size(cluster)
    top = bottom = height ÷ 2
    left = right = width ÷ 2

    while 1 in cluster[top + 1, 2:end-1] || 2 in cluster[top + 1, 2:end-1]
        top += 1
    end
    while 1 in cluster[bottom - 1, 2:end-1] || 2 in cluster[bottom - 1, 2:end-1]
        bottom -= 1
    end
    while 1 in cluster[2:end-1, left - 1] || 2 in cluster[2:end-1, left - 1]
        left -= 1
    end
    while 1 in cluster[2:end-1, right + 1] || 2 in cluster[2:end-1, right + 1]
        right += 1
    end

    return cluster[bottom:top, left:right]
end

"""
Helper function for `hkpercolate`; Adds new label.
"""
@inline function hknew!(labels::Matrix{<:Integer}, list::Vector{<:Integer},
        clusters::Vector{<:Integer}, row::Integer, col::Integer)
    labels[row, col] = length(list) + 1
    push!(list, labels[row, col])
    push!(clusters, 0)
end

"""
Helper function for `hkpercolate`; Applies the Hoshen-Kopelman algorithm to the first row.
"""
@inline function hkfirstrow!(grid::BitMatrix, labels::Matrix{<:Integer},
        list::Vector{<:Integer}, clusters::Vector{<:Integer})
    width = size(labels)[2]
    if grid[1, 1]
        hknew!(labels, list, clusters, 1, 1)
        clusters[labels[1, 1]] += 1
    end
    for col in 2:width
        if grid[1, col]
            if labels[1, col - 1] != 0
                labels[1, col] = hkfind!(list, labels[1, col - 1])
            else
                hknew!(labels, list, clusters, 1, col)
            end
            clusters[labels[1, col]] += 1
        end
    end
end

"""
Helper function for `hkpercolate`; Applies the Hoshen-Kopelman algorithm to the first column.
"""
@inline function hkfirstcol!(labels::Matrix{<:Integer}, list::Vector{<:Integer},
        clusters::Vector{<:Integer}, row::Integer)
    if labels[row - 1, 1] != 0
        labels[row, 1] = hkfind!(list, labels[row - 1, 1])
    else
        hknew!(labels, list, clusters, row, 1)
    end
    clusters[labels[row, 1]] += 1
end

"""
Helper function for `hkpercolate`; Applies one iteration of the Hoshen-Kopelman algorithm.
"""
@inline function hkiter!(labels::Matrix{<:Integer}, list::Vector{<:Integer},
        clusters::Vector{<:Integer}, row::Integer, col::Integer)
    above, left = labels[row - 1, col], labels[row, col - 1]
    if above == 0
        if left == 0
            hknew!(labels, list, clusters, row, col)
        else
            labels[row, col] = hkfind!(list, left)
        end
    elseif left == 0
        labels[row, col] = hkfind!(list, above)

    else
        labels[row, col] = hkunion!(list, clusters, above, left)
    end
    clusters[labels[row, col]] += 1
end

"""
Union function of the union-find algorithm used by the Hoshen-Kopelman algorithm
"""
@inline function hkunion!(list::Vector{<:Integer}, clusters::Vector{<:Integer},
        x::Integer, y::Integer)
    root = hkfind!(list, x)
    dest = hkfind!(list, y)
    if dest != root
        list[dest] = root
        clusters[root] += clusters[dest]
        clusters[dest] = 0
    end
    return root
end

"""
Find function of the union-find algorithm used by the Hoshen-Kopelman algorithm
"""
@inline function hkfind!(list::Vector{<:Integer}, x::Integer)
    y = x;
    while list[y] != y
        y = list[y];
    end
    while list[x] != x
        z = list[x];
        list[x] = y;
        x = z;
    end
    return y;
end

"""
Same as hkfind!, but doesn't update labels
"""
@inline function hkfind(list::Vector{<:Integer}, x::Integer)
    while x != list[x]
        x = list[x];
    end
    return x
end

"""
Color resultant grid of the Hoshen-Kopelman algorithm appropriately
"""
@inline function hkcolor!(labels::Matrix{<:Integer}, list::Vector{<:Integer})
    width, height = size(labels)
    for row in 1:height
        for col in 1:width
            if labels[row, col] != 0
                labels[row, col] = hkfind(list, labels[row, col])
            end
        end
    end
end

"""
Find the sum of the sizes of the open connecting clusters (integer)
"""
@inline function hkresult(labels::Matrix{<:Integer}, list::Vector{<:Integer},
        clusters::Vector{<:Integer})
    sumsize = 0
    # to avoid over-counting the clusters, save the label of each cluster that is counted
    counted = []

    for root in labels[1, :]
        if root > 0
            for dest in labels[end, :]
                if dest > 0
                    toplabel = hkfind(list, root)
                    bottomlabel = hkfind(list, dest)
                    if toplabel == bottomlabel && !(toplabel in counted)
                        sumsize += clusters[toplabel]
                        append!(counted, toplabel)
                    end
                end
            end
        end
    end
    return sumsize
end

"""
Find out whether percolation is possible or not in hkpercolate output
"""
@inline function hkbool(labels::Matrix{<:Integer}, list::Vector{<:Integer})
    for root in labels[1, :]
        if root > 0
            for dest in labels[end, :]
                if dest > 0 && hkfind(list, dest) == hkfind(list, root)
                    return true
                end
            end
        end
    end
    return false
end

end


# ---
# title: 525. Contiguous Array
# id: problem525
# author: Indigo
# date: 2021-06-03
# difficulty: Medium
# categories: Hash Table
# link: <https://leetcode.com/problems/contiguous-array/description/>
# hidden: true
# ---
#
# Given a binary array, find the maximum length of a contiguous subarray with
# equal number of 0 and 1.
#
# **Example 1:**
#
#
#
#     Input: [0,1]
#     Output: 2
#     Explanation: [0, 1] is the longest contiguous subarray with equal number of 0 and 1.
#
#
# **Example 2:**
#
#
#
#     Input: [0,1,0]
#     Output: 2
#     Explanation: [0, 1] (or [1, 0]) is a longest contiguous subarray with equal number of 0 and 1.
#
#
# **Note:** The length of the given binary array will not exceed 50,000.
#
#
## @lc code=start
using LeetCode

function find_max_length(nums::Vector{Int})
    dc = Dict(0 => 0)
    res = acc = 0
    @inbounds for i in eachindex(nums)
        acc += nums[i] == 0 ? -1 : 1
        haskey(dc, acc) ? res = max(res, i - dc[acc]) : dc[acc] = i
    end
    return res
end
## @lc code=end


using SparseArrays
using LinearAlgebra
using Plots
using RecipesBase

include("C:/Users/lucas/OneDrive/Documents/GitHub/GDB.jl/experimental/ThermalLBM.jl")

function boundary_thermal_lattice_boltzmann_method(streamtensor, Dx, Dy, Dxx, Dyy, xres, yres, f, g, fb, gb, K1, K2, tau, tauc, deltax, deltat)
    c = deltax/deltat
    nu = tau*c^2/3 # kinetic viscosity

    fnew = streamtensor * f
    gnew = streamtensor * g

    rho = zeros((xres*yres))
    for i=1:xres*yres
        rho[i] = sum(fnew[i,:])
    end

    p = zeros(xres*yres)
    for i=1:xres*yres
        p[i] = c^2/3 * rho[i]
    end

    u = zeros(xres*yres, 2)
    for i=1:xres*yres
        ux = (fnew[i,2] - fnew[i,4] + 1/sqrt(2) * (fnew[i,6] + fnew[i,9] - fnew[i,7] - fnew[i,8])) / rho[i]
        uy = (fnew[i,3] - fnew[i,5] + 1/sqrt(2) * (fnew[i,6] + fnew[i,8] - fnew[i,9] - fnew[i,8])) / rho[i]
        u[i,1] = ux
        u[i,2] = uy
    end

    q = zeros(xres*yres, 9)

    grad = [Dx, Dy]
    divu = Dx*u[:,1] + Dy*u[:,2]
    gradp = zeros(xres*yres, 2)
    gradp[:,1] = Dx*p[:]
    gradp[:,2] = Dy*p[:]
    laplu = zeros(xres*yres, 2)
    laplu[:,1] = (Dxx+Dyy)*u[:,1]
    laplu[:,2] = (Dxx+Dyy)*u[:,2]

    unitvecs = zeros(9,2)
    unitvecs[:,1] = [0, c, 0, -c, 0, 1/sqrt(2)*c, -1/sqrt(2)*c, -1/sqrt(2)*c, 1/sqrt(2)*c]
    unitvecs[:,2] = [0, 0, c, 0, -c, 1/sqrt(2)*c, 1/sqrt(2)*c, -1/sqrt(2)*c, -1/sqrt(2)*c]

    vgradu = zeros(xres*yres, 9, 2)
    for k=1:9
        vgradu[:,k,1] = (unitvecs[k,1] .- u[:,1]) .* (Dx*u[:,1]) + (unitvecs[k,2] .- u[:,2]) .* (Dy*u[:,1])
        vgradu[:,k,2] = (unitvecs[k,1] .- u[:,1]) .* (Dx*u[:,2]) + (unitvecs[k,2] .- u[:,2]) .* (Dy*u[:,2])
    end

    for i=1:xres*yres
        for a=1:9
            q[i,a] = - 1/rho[i]*dot(unitvecs[a,:]-u[i,:], gradp[i,:])
            + nu*(dot(unitvecs[a,:]-u[i,:], laplu[i,:]))
            + dot(unitvecs[a,:]-u[i,:], vgradu[i,a,:])
        end
    end

    w = zeros(9)
    w[1] = 4/9
    w[2] = 1/9
    w[3] = 1/9
    w[4] = 1/9
    w[5] = 1/9
    w[6] = 1/36
    w[7] = 1/36
    w[8] = 1/36
    w[9] = 1/36

    s = zeros((xres*yres, 9))
    usquared = zeros((xres*yres))
    for i=1:xres*yres
        usquared[i] = dot(u[i,:],u[i,:])
        s[i,:] = w[:] .* (3*(unitvecs*u[i,:])/c^2 .+ 9/2*((unitvecs*u[i,:])).^2/c^3 .- 3/2*usquared[i]/c^2)
    end

    feq = zeros((xres*yres, 9))
    for i=1:xres*yres
        feq[i,:] = w[:].*rho[i] .+ rho[i].*s[i,:]
    end

    lambda = zeros(xres*yres)
    fnext = zeros((xres*yres, 9))
    for i=1:xres*yres
        lambda[i] = (1 - K1[i]*deltat/tau + K2[i]*deltat)
        fnext[i,:] = (fnew[i,:] .- K1[i]*(deltat/tau).*feq[i,:] .+ K2[i]*deltat.*fb[i,:])./lambda[i]
    end

    rhoepsilon = zeros(xres*yres)
    for i=1:xres*yres
        rhoepsilon[i] = sum(gnew[i,:] .- deltat/2 .*fnext[i,:].*q[i,:])
    end

    geq = zeros((xres*yres, 9))
    for i=1:xres*yres
        geq[i,1] = -2/3*rhoepsilon[i] * usquared[i]/c^2
        geq[i,2] = rhoepsilon[i]/9 * (1.5 + 1.5*dot(unitvecs[2,:],u[i,:])/c^2 + 4.5*(dot(unitvecs[2,:],u[i,:]))^2/c^4 - 1.5*usquared[i]/c^2)
        geq[i,3] = rhoepsilon[i]/9 * (1.5 + 1.5*dot(unitvecs[3,:],u[i,:])/c^2 + 4.5*(dot(unitvecs[3,:],u[i,:]))^2/c^4 - 1.5*usquared[i]/c^2)
        geq[i,4] = rhoepsilon[i]/9 * (1.5 + 1.5*dot(unitvecs[4,:],u[i,:])/c^2 + 4.5*(dot(unitvecs[4,:],u[i,:]))^2/c^4 - 1.5*usquared[i]/c^2)
        geq[i,5] = rhoepsilon[i]/9 * (1.5 + 1.5*dot(unitvecs[5,:],u[i,:])/c^2 + 4.5*(dot(unitvecs[5,:],u[i,:]))^2/c^4 - 1.5*usquared[i]/c^2)
        geq[i,6] = rhoepsilon[i]/36 * (3 + 6*dot(unitvecs[6,:],u[i,:])/c^2 + 4.5*(dot(unitvecs[6,:],u[i,:]))^2/c^4 - 1.5*usquared[i]/c^2)
        geq[i,7] = rhoepsilon[i]/36 * (3 + 6*dot(unitvecs[7,:],u[i,:])/c^2 + 4.5*(dot(unitvecs[7,:],u[i,:]))^2/c^4 - 1.5*usquared[i]/c^2)
        geq[i,8] = rhoepsilon[i]/36 * (3 + 6*dot(unitvecs[8,:],u[i,:])/c^2 + 4.5*(dot(unitvecs[8,:],u[i,:]))^2/c^4 - 1.5*usquared[i]/c^2)
        geq[i,9] = rhoepsilon[i]/36 * (3 + 6*dot(unitvecs[9,:],u[i,:])/c^2 + 4.5*(dot(unitvecs[9,:],u[i,:]))^2/c^4 - 1.5*usquared[i]/c^2)
    end

    gnext = zeros(xres*yres, 9)
    for i=1:xres*yres
        gnext[i,:] = (gnew[i,:] .- K1[i]*deltat/tauc.*geq[i,:] .+ K1[i]*deltat.*fnext[i,:].*q[i,:] .+ K2[i]*deltat.*gb[i,:])./lambda[i]
    end

    return fnext, gnext, q, rho, u, feq, geq
end

function stepclamp(x, lowerlim, upperlim)
    if x < lowerlim
        x = lowerlim
    elseif x > upperlim
        x = upperlim
    end
    return x
end

function stepsmooth(innerrad, outerrad, radius)
    x = stepclamp((radius-innerrad)/(outerrad-innerrad), 0.0, 1.0)
    return x
end

N = 20
tau = 1000
tauc = 1000
deltax = 1
deltat = 1
c = deltax/deltat
nu = c^2/3
xres = 100
yres = 100
periodictensor = PeriodicStreamTensor(xres, yres)
innerrad = .5
outerrad = .8
#K2 = 1 - K1 times some large (~100?) constant
#K1 = 1 in domain of interest
#step function in radius - 1 for values within domain of interest
#circle of radius res/3 centered at (xres/2,yres/2)

radius = zeros(xres,yres)
K1 = zeros(xres*yres)
for j=1:yres
    for i=1:xres
        radius[i,j] = sqrt(((i-50)/50)^2 + ((j-50)/50)^2)
        K1[(j-1)*xres+i] = 1 - stepsmooth(innerrad, outerrad, radius[i,j])
    end
end
K1trans = zeros(xres, yres)
for j=1:yres
    for i=1:xres
        K1trans[i,j] = K1[(j-1)*xres+i]
    end
end

K2 = zeros(xres*yres)
K2[:] .= (1 .- K1[:]) .* 1000

K2trans = zeros(xres, yres)
for j=1:yres
    for i=1:xres
        K2trans[i,j] = K2[(j-1)*xres+i]
    end
end

sx = 5
sy = 5
dx = 1
dy = 1
pDx = periodic_derivative(1, 0, sx, sy, xres, yres, dx, dy)
pDy = periodic_derivative(0, 1, sx, sy, xres, yres, dx, dy)
pDxx = periodic_derivative(2, 0, sx, sy, xres, yres, dx, dy)
pDyy = periodic_derivative(0, 2, sx, sy, xres, yres, dx, dy)

fsine = zeros(xres, yres, 9)
gsine = zeros(xres, yres, 9)
rhoinit = zeros(xres, yres)
uinit = zeros(xres, yres, 9)
for i=1:xres
    for j=1:yres
        for k=1:9
            fsine[i,j,k] = .5 + .4*sin(i*pi/(xres))*sin(j*pi/(yres))
        end
        rhoinit[i,j] = sum(fsine[i,j,:])
        uinit[i,j,:] = fsine[i,j,:] ./ rhoinit[i,j]
        for k=1:9
            gsine[i,j,k] = (1 - uinit[i,j,k])^2/2 * fsine[i,j,k]
        end
    end
end

fs2 = zeros(xres*yres, 9)
gs2 = zeros(xres*yres, 9)

for j=1:yres
    for i=1:xres
        fs2[(j-1)*xres+i,:] = fsine[i,j,:]
        gs2[(j-1)*xres+i,:] = gsine[i,j,:]
    end
end

fb = zeros(xres*yres, 9)
gb = zeros(xres*yres, 9)

rho = zeros((xres*yres))
for i=1:xres*yres
    rho[i] = sum(fs2[i,:])
end
p = zeros(xres*yres)
for i=1:xres*yres
    p[i] = c^2/3 * rho[i]
end
u = zeros(xres*yres, 2)
for i=1:xres*yres
    ux = (fs2[i,2] - fs2[i,4] + 1/sqrt(2) * (fs2[i,6] + fs2[i,9] - fs2[i,7] - fs2[i,8])) / rho[i]
    uy = (fs2[i,3] - fs2[i,5] + 1/sqrt(2) * (fs2[i,6] + fs2[i,8] - fs2[i,9] - fs2[i,8])) / rho[i]
    u[i,1] = ux
    u[i,2] = uy
end
unitvecs = zeros(9,2)
unitvecs[:,1] = [0, c, 0, -c, 0, 1/sqrt(2)*c, -1/sqrt(2)*c, -1/sqrt(2)*c, 1/sqrt(2)*c]
unitvecs[:,2] = [0, 0, c, 0, -c, 1/sqrt(2)*c, 1/sqrt(2)*c, -1/sqrt(2)*c, -1/sqrt(2)*c]
w = zeros(9)
w[1] = 4/9
w[2] = 1/9
w[3] = 1/9
w[4] = 1/9
w[5] = 1/9
w[6] = 1/36
w[7] = 1/36
w[8] = 1/36
w[9] = 1/36
s = zeros((xres*yres, 9))
usquared = zeros((xres*yres))
for i=1:xres*yres
    usquared[i] = dot(u[i,:],u[i,:])
    s[i,:] = w[:] .* (3*(unitvecs*u[i,:])/c^2 .+ 9/2*((unitvecs*u[i,:])).^2/c^3 .- 3/2*usquared[i]/c^2)
end
feqb = zeros((xres*yres, 9))
for i=1:xres*yres
    feqb[i,:] = w[:].*rho[i] .+ rho[i].*s[i,:]
end
q = zeros(xres*yres, 9)

grad = [pDx, pDy]
divu = pDx*u[:,1] + pDy*u[:,2]
gradp = zeros(xres*yres, 2)
gradp[:,1] = pDx*p[:]
gradp[:,2] = pDy*p[:]
laplu = zeros(xres*yres, 2)
laplu[:,1] = (pDxx+pDyy)*u[:,1]
laplu[:,2] = (pDxx+pDyy)*u[:,2]

vgradu = zeros(xres*yres, 9, 2)
for k=1:9
    vgradu[:,k,1] = (unitvecs[k,1] .- u[:,1]) .* (pDx*u[:,1]) + (unitvecs[k,2] .- u[:,2]) .* (pDy*u[:,1])
    vgradu[:,k,2] = (unitvecs[k,1] .- u[:,1]) .* (pDx*u[:,2]) + (unitvecs[k,2] .- u[:,2]) .* (pDy*u[:,2])
end

for i=1:xres*yres
    for a=1:9
        q[i,a] = - 1/rho[i]*dot(unitvecs[a,:]-u[i,:], gradp[i,:])
        + nu*(dot(unitvecs[a,:]-u[i,:], laplu[i,:]))
        + dot(unitvecs[a,:]-u[i,:], vgradu[i,a,:])
    end
end
lambda = zeros(xres*yres)
rhoepsilon = zeros(xres*yres)
for i=1:xres*yres
    rhoepsilon[i] = sum(gs2[i,:] .- deltat/2 .*fs2[i,:].*q[i,:])
end
geq = zeros((xres*yres, 9))
for i=1:xres*yres
    geq[i,1] = -2/3*rhoepsilon[i] * usquared[i]/c^2
    geq[i,2] = rhoepsilon[i]/9 * (1.5 + 1.5*dot(unitvecs[2,:],u[i,:])/c^2 + 4.5*(dot(unitvecs[2,:],u[i,:]))^2/c^4 - 1.5*usquared[i]/c^2)
    geq[i,3] = rhoepsilon[i]/9 * (1.5 + 1.5*dot(unitvecs[3,:],u[i,:])/c^2 + 4.5*(dot(unitvecs[3,:],u[i,:]))^2/c^4 - 1.5*usquared[i]/c^2)
    geq[i,4] = rhoepsilon[i]/9 * (1.5 + 1.5*dot(unitvecs[4,:],u[i,:])/c^2 + 4.5*(dot(unitvecs[4,:],u[i,:]))^2/c^4 - 1.5*usquared[i]/c^2)
    geq[i,5] = rhoepsilon[i]/9 * (1.5 + 1.5*dot(unitvecs[5,:],u[i,:])/c^2 + 4.5*(dot(unitvecs[5,:],u[i,:]))^2/c^4 - 1.5*usquared[i]/c^2)
    geq[i,6] = rhoepsilon[i]/36 * (3 + 6*dot(unitvecs[6,:],u[i,:])/c^2 + 4.5*(dot(unitvecs[6,:],u[i,:]))^2/c^4 - 1.5*usquared[i]/c^2)
    geq[i,7] = rhoepsilon[i]/36 * (3 + 6*dot(unitvecs[7,:],u[i,:])/c^2 + 4.5*(dot(unitvecs[7,:],u[i,:]))^2/c^4 - 1.5*usquared[i]/c^2)
    geq[i,8] = rhoepsilon[i]/36 * (3 + 6*dot(unitvecs[8,:],u[i,:])/c^2 + 4.5*(dot(unitvecs[8,:],u[i,:]))^2/c^4 - 1.5*usquared[i]/c^2)
    geq[i,9] = rhoepsilon[i]/36 * (3 + 6*dot(unitvecs[9,:],u[i,:])/c^2 + 4.5*(dot(unitvecs[9,:],u[i,:]))^2/c^4 - 1.5*usquared[i]/c^2)
end
fb .= feqb
gb .= geq

anim = @animate for i=1:N
    fs2, gs2, ex, ex1, ex2 = boundary_thermal_lattice_boltzmann_method(periodictensor, pDx, pDy, pDxx, pDyy, xres, yres, fs2, gs2, fb, gb, K1, K2, tau, tauc, deltax, deltat)
    fplot = zeros(xres, yres)
    gplot = zeros(xres, yres)
    explot = zeros(xres, yres)
    exp1 = zeros(xres, yres)
    exp2 = zeros(xres, yres)
    ucheck = zeros(xres, yres)
    for i=1:xres
        for j=1:yres
            exp1[i,j] = ex1[(j-1)*xres+i]
            exp2[i,j] = ex2[(j-1)*xres+i,2]
            for k=1:9
                fplot[i,j] = fplot[i,j] + fs2[(j-1)*xres+i,k]
                gplot[i,j] = gplot[i,j] + gs2[(j-1)*xres+i,k]
                explot[i,j] = explot[i,j] + ex[(j-1)*xres+i,k]
                ucheck[i,j] = ucheck[i,j] + ex[(j-1)*xres+i,k]/ex1[(j-1)*xres+i]
            end
        end
    end
    println(fplot[Int32(xres/2),Int32(yres/2)])
    println(gplot[Int32(xres/2),Int32(yres/2)])
    heatmap(gplot, clims=(1,4))
end
gif(anim, fps=10)
# map = heatmap(K2trans)
# display(map)