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

using Markdown
using InteractiveUtils

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

# ╔═╡ 6b1079da-c189-48e0-8660-72465e502dc2
begin

	# Setup the Julia environment -
	using PlutoUI
	using Plots
	using DataFrames
	using Luxor

end

# ╔═╡ 12187222-b5b2-464f-926a-87d72f50624b
md"""
### Graphic Analysis of a Flash Separation

Let's revisit the previous flash separation example and explore the solution of the problem graphically.

A liquid feed stream that contains 60% component A (1) and 40% component B (2) enters the first flash drum operating at P = 1.21 MPa and T = 150$^{\circ}$C. The saturation pressures of pure component A and B can be modeled using the [Antoine equation](https://en.wikipedia.org/wiki/Antoine_equation):

$$\begin{eqnarray}
\ln\left(P_{1}^{sat}\right) &=& 15 -\frac{3010}{T+250}\\
\ln\left(P_{2}^{sat}\right) &=& 14 -\frac{2700}{T+205}
\end{eqnarray}$$

where $T$ denotes the temperature in units of celsius $^{\circ}$C and $P_{\star}^{sat}$ denotes the saturation pressure in units of kPa for component $\star$.

#### I'm curious ... what happens if we ...
What happens if the liquid effluent from the first flash drum is fed into a second flash drum for further separation.
What should the pressure be in the second unit to maximize the separation of component A and component B?

#### Assumptions
1. The fluid and vapor in all streams and systems are ideal
1. All systems are at steady-state

#### Strategy:
1. Compute a _Pxy_ diagram for the binary mixture of component A and B at T = 150$^{\circ}$C.
1. Use the _Pxy_ diagram to find the operating pressure with the _widest_ difference between the vapor and liquid composition for component $i$

"""

# ╔═╡ b3d73c72-be93-45c1-a8b2-4a3225c8ce30
md"""
###### Select a Pressure (in units of kPa):
"""

# ╔═╡ 58ad09e0-f241-4c30-ab57-7c3642ebfe65
md"""
##### Calculate $\hat{L}$ and $\hat{V}$ using the Flash Lever Rule (FLR)
The values for $\hat{L}$ and $\hat{V}$ depend upon the input composition $z_{i}$.

The FLR is given as:

$$\frac{\dot{L}}{\dot{V}} = \frac{y^{eq}_{i} - z_{i}}{z_{i} - x^{eq}_{i}}$$

where we defined in the notes: $a\equiv z_{i} - x^{eq}_{i}$ and $b\equiv y^{eq}_{i} - z_{i}$. We can rearrange the total mol balance to give:

$$\frac{b}{a} + 1 = \hat{V}^{-1}$$

from which we can solve for $\hat{V}$:

$$\hat{V} = \left(\frac{b}{a} + 1\right)^{-1}$$

Once we have $\hat{V}$, we can find $\hat{L}$ because we know:

$$\hat{L}+\hat{V} = 1$$

from the total mass balance.

"""

# ╔═╡ 155fc55a-6d48-4cb3-af14-2d3d14699237
md"""
###### Function library is below here (do not change, bad stuff will happen, step slowly away from the keyboard)
"""

# ╔═╡ f4d83604-b048-4759-a357-99bbf4c95bcd
# function to compute staturation pressures for different T
function compute_saturation_pressure(temperature::Float64)::Tuple{Float64,Float64}

	# use the Antoine equation to compute the saturation P -
	P₁ = exp(15 - 3010/(temperature + 250))		# P1_sat units: kPa
	P₂ = exp(14 - 2700/(temperature + 205))		# P2_sat units: kPa

	# return -
	return (P₁,P₂)
end

# ╔═╡ 03fd4f1b-93be-4d75-851e-94de224797ec
function compute_phase_diagram(temperature::Float64; step::Float64=0.01)

	# initailize -
	compostion_array = range(0.0, stop=1.0, step=step) |> collect
	phase_diagram_df = DataFrame(P=Float64[], x₁ = Float64[], y₁ = Float64[])

	# for this T - compute the saturation pressure -
	P_sat_tuple = compute_saturation_pressure(temperature)
	P1_sat = P_sat_tuple[1]
	P2_sat = P_sat_tuple[2]

	# compute the profile -
	for x₁ in compostion_array

		# liquid -
		P = (x₁)*P1_sat + (1.0 - x₁)*P2_sat

		# compute Y -
		y₁ = x₁*(P1_sat)/P

		# package into a row -
		row_data = (P, x₁, y₁)

		# push -
		push!(phase_diagram_df, row_data)
	end

	# return -
	return phase_diagram_df
end

# ╔═╡ 936a10ad-0d29-4d76-b78a-8e392e5cdb3f
begin

	T = 150.0 # units: C
	PS = floor.(compute_saturation_pressure(T))

	Pxy_df = compute_phase_diagram(T; step=0.001)
	(number_of_rows, number_of_cols) = size(Pxy_df)

	nothing
end

# ╔═╡ 1057e9b4-9734-4705-ae85-9aa4b426f79a
@bind P_op Slider(PS[2]:PS[1]; default=1210, show_value=true)

# ╔═╡ 2abed2c4-04b4-4afc-b22c-19afd2a6271a
# make a plot of the Pxy data for binary A and B -
begin

	# compute the pxy - data for the binary A + B mixture -

	# get stuff out of the df -
	P = Pxy_df[!,:P]
	x₁ = Pxy_df[!,:x₁]
	y₁ = Pxy_df[!,:y₁]

	# plot the liquid line -
	plot(x₁,P, lw=2, legend=:topleft, label="x₁",title="Binary A+B at T = 150C",
		ylims=(0,1.1*maximum(P)), color="royalblue1")

	# plot the vapor line -
	plot!(y₁,P,lw=2, label="y₁", color="red1")

	# label the axis -
	xlabel!("Mole fraction component 1 (AU)",fontsize=18)
	ylabel!("Pressure (kPa)",fontsize=18)

	# find intersection and plot the tie-line -
	idx_start = findfirst(x->x>=P_op,P)
	x_eq = x₁[idx_start]
	y_eq = y₁[idx_start]
	tie_line = [x_eq P_op ; y_eq P_op]
	plot!(tie_line[:,1],tie_line[:,2],lw=2, color="gray69", label="")

	# draw lines to x-axis -
	liquid_comp_line = [x_eq P_op ; x_eq 0.0]
	vapor_comp_line = [y_eq P_op ; y_eq 0.0]
	plot!(liquid_comp_line[:,1], liquid_comp_line[:,2], lw=2, color="royalblue1", label="",ls=:dot)
	plot!(vapor_comp_line[:,1], vapor_comp_line[:,2], lw=2, color="red", label="", ls=:dot)

	# draw points -
	scatter!(liquid_comp_line[:,1], liquid_comp_line[:,2], lw=2, color="royalblue1", label="")
	scatter!(vapor_comp_line[:,1], vapor_comp_line[:,2], lw=2, color="red", label="")
end

# ╔═╡ 48a12856-6b7c-44da-91d4-d92946cf9591
begin

	with_terminal() do
		println("x1_eq = $(x_eq) and y1_eq = $(y_eq)")
	end

end

# ╔═╡ 1cba22c8-5ae4-498b-93c2-57b0fb86eac8
begin

	# what is z1 -
	z1 = 0.60

	# What are a and b (and the total width)
	b = (y_eq - z1)
	a = (z1 - x_eq)
	Δ = (y_eq - x_eq)

	N = 1
	D = (b/a+1)
	V_hat = N/D
	L_hat = 1 - V_hat

	with_terminal() do
		println("V_hat = $(V_hat) and L_hat = $(L_hat)")
	end
end

# ╔═╡ 7018a302-2627-11ec-1f6f-4baef5c8f576
html"""
<style>
main {
    max-width: 1200px;
    width: 85%;
    margin: auto;
    font-family: "Roboto, monospace";
}

a {
    color: blue;
    text-decoration: none;
}

.H1 {
    padding: 0px 30px;
}
</style>
"""

# ╔═╡ 00000000-0000-0000-0000-000000000001
PLUTO_PROJECT_TOML_CONTENTS = """
[deps]
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
Luxor = "ae8d54c2-7ccd-5906-9d76-62fc9837b5bc"
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
PlutoUI = "7f904dfe-b85e-4ff6-b463-dae2292396a8"

[compat]
DataFrames = "~1.2.2"
Luxor = "~2.15.0"
Plots = "~1.22.4"
PlutoUI = "~0.7.14"
"""

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

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

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

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

[[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]]
deps = ["Cairo_jll", "Colors", "Glib_jll", "Graphics", "Libdl", "Pango_jll"]
git-tree-sha1 = "d0b3f8b4ad16cb0a2988c6788646a5e6a17b6b1b"
uuid = "159f3aea-2a34-519c-b102-8c37f9878175"
version = "1.0.5"

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

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

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

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

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

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

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

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

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

[[Crayons]]
git-tree-sha1 = "3f71217b538d7aaee0b69ab47d9b7724ca8afa0d"
uuid = "a8cc5b0e-0ffa-5ad4-8c14-923d3ee1735f"
version = "4.0.4"

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

[[DataFrames]]
deps = ["Compat", "DataAPI", "Future", "InvertedIndices", "IteratorInterfaceExtensions", "LinearAlgebra", "Markdown", "Missings", "PooledArrays", "PrettyTables", "Printf", "REPL", "Reexport", "SortingAlgorithms", "Statistics", "TableTraits", "Tables", "Unicode"]
git-tree-sha1 = "d785f42445b63fc86caa08bb9a9351008be9b765"
uuid = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
version = "1.2.2"

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

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

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

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

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

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

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

[[Future]]
deps = ["Random"]
uuid = "9fa8497b-333b-5362-9e8d-4d0656e87820"

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

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

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

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

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

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

[[Graphics]]
deps = ["Colors", "LinearAlgebra", "NaNMath"]
git-tree-sha1 = "1c5a84319923bea76fa145d49e93aa4394c73fc2"
uuid = "a2bd30eb-e257-5431-a919-1863eab51364"
version = "1.1.1"

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

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

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

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

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

[[ImageMagick_jll]]
deps = ["Artifacts", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Libtiff_jll", "Pkg", "Zlib_jll", "libpng_jll"]
git-tree-sha1 = "ea2b6fd947cdfc43c6b8c15cff982533ec1f72cd"
uuid = "c73af94c-d91f-53ed-93a7-00f77d67a9d7"
version = "6.9.12+0"

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

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

[[InvertedIndices]]
git-tree-sha1 = "bee5f1ef5bf65df56bdd2e40447590b272a5471f"
uuid = "41ab1584-1d38-5bbf-9106-f11c6c58b48f"
version = "1.1.0"

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

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

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

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

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

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

[[Juno]]
deps = ["Base64", "Logging", "Media", "Profile"]
git-tree-sha1 = "07cb43290a840908a771552911a6274bc6c072c7"
uuid = "e5e0dc1b-0480-54bc-9374-aad01c23163d"
version = "0.8.4"

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

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

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

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

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

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

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

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

[[Libcroco_jll]]
deps = ["Artifacts", "Glib_jll", "JLLWrappers", "Libdl", "Pkg", "XML2_jll"]
git-tree-sha1 = "a8e3b1b67458c8933992b95db9c4b37865906e3f"
uuid = "57eb2189-7eb1-52c8-ac0e-99495f550b14"
version = "0.6.13+2"

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

[[Librsvg_jll]]
deps = ["Artifacts", "JLLWrappers", "Libcroco_jll", "Libdl", "Pango_jll", "Pkg", "gdk_pixbuf_jll"]
git-tree-sha1 = "af3e6dc6747e53a0236fbad80b37e3269cf66a9f"
uuid = "925c91fb-5dd6-59dd-8e8c-345e74382d89"
version = "2.42.2+3"

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

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

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

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

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

[[Luxor]]
deps = ["Base64", "Cairo", "Colors", "Dates", "FFMPEG", "FileIO", "ImageMagick", "Juno", "QuartzImageIO", "Random", "Rsvg"]
git-tree-sha1 = "b2b3d49ffb3b89d9e052da47a0b6df13579738ea"
uuid = "ae8d54c2-7ccd-5906-9d76-62fc9837b5bc"
version = "2.15.0"

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

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

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

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

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

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

[[Media]]
deps = ["MacroTools", "Test"]
git-tree-sha1 = "75a54abd10709c01f1b86b84ec225d26e840ed58"
uuid = "e89f7d12-3494-54d1-8411-f7d8b9ae1f27"
version = "0.5.0"

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

[[Pango_jll]]
deps = ["Artifacts", "Cairo_jll", "Fontconfig_jll", "FreeType2_jll", "FriBidi_jll", "Glib_jll", "HarfBuzz_jll", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "9bc1871464b12ed19297fbc56c4fb4ba84988b0d"
uuid = "36c8627f-9965-5494-a995-c6b170f724f3"
version = "1.47.0+0"

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

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

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

[[PooledArrays]]
deps = ["DataAPI", "Future"]
git-tree-sha1 = "a193d6ad9c45ada72c14b731a318bedd3c2f00cf"
uuid = "2dfb63ee-cc39-5dd5-95bd-886bf059d720"
version = "1.3.0"

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

[[PrettyTables]]
deps = ["Crayons", "Formatting", "Markdown", "Reexport", "Tables"]
git-tree-sha1 = "69fd065725ee69950f3f58eceb6d144ce32d627d"
uuid = "08abe8d2-0d0c-5749-adfa-8a2ac140af0d"
version = "1.2.2"

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

[[Profile]]
deps = ["Printf"]
uuid = "9abbd945-dff8-562f-b5e8-e1ebf5ef1b79"

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

[[QuartzImageIO]]
deps = ["FileIO", "ImageCore", "Libdl"]
git-tree-sha1 = "16de3b880ffdfbc8fc6707383c00a2e076bb0221"
uuid = "dca85d43-d64c-5e67-8c65-017450d5d020"
version = "0.7.4"

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

[[Rsvg]]
deps = ["Cairo", "Glib_jll", "Librsvg_jll"]
git-tree-sha1 = "3d3dc66eb46568fb3a5259034bfc752a0eb0c686"
uuid = "c4c386cf-5103-5370-be45-f3a111cca3b8"
version = "1.0.0"

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

[[gdk_pixbuf_jll]]
deps = ["Artifacts", "Glib_jll", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Libtiff_jll", "Pkg", "Xorg_libX11_jll", "libpng_jll"]
git-tree-sha1 = "0facfc4bfd873c21b83a053bbf182b9ef19c69d8"
uuid = "da03df04-f53b-5353-a52f-6a8b0620ced0"
version = "2.42.6+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:
# ╟─12187222-b5b2-464f-926a-87d72f50624b
# ╟─936a10ad-0d29-4d76-b78a-8e392e5cdb3f
# ╟─b3d73c72-be93-45c1-a8b2-4a3225c8ce30
# ╠═1057e9b4-9734-4705-ae85-9aa4b426f79a
# ╟─2abed2c4-04b4-4afc-b22c-19afd2a6271a
# ╟─48a12856-6b7c-44da-91d4-d92946cf9591
# ╟─58ad09e0-f241-4c30-ab57-7c3642ebfe65
# ╠═1cba22c8-5ae4-498b-93c2-57b0fb86eac8
# ╟─155fc55a-6d48-4cb3-af14-2d3d14699237
# ╠═6b1079da-c189-48e0-8660-72465e502dc2
# ╠═03fd4f1b-93be-4d75-851e-94de224797ec
# ╟─f4d83604-b048-4759-a357-99bbf4c95bcd
# ╟─7018a302-2627-11ec-1f6f-4baef5c8f576
# ╟─00000000-0000-0000-0000-000000000001
# ╟─00000000-0000-0000-0000-000000000002


text:
subfunction(x,y) = 3x+2y

derivative_of_my_f(x,y) = ForwardDiff.derivative(x->subfunction(x,y),x)
# ref:http://www.juliadiff.org/ForwardDiff.jl/stable/user/api/

ext: jl


text:
export finalize_global_grid

import MPI

"""
    finalize_global_grid()
    finalize_global_grid(;finalize_MPI=true)

Finalize the global grid (and also MPI by default).

# Arguments
!!! note "Advanced keyword arguments"
    - `finalize_MPI::Bool=true`: whether to finalize MPI (`true`) or not (`false`).

See also: [`init_global_grid`](@ref)
"""
function finalize_global_grid(;finalize_MPI::Bool=true)
    check_initialized();
    #TODO
    free_gather_buffer();
    free_update_halo_buffers();
    if (finalize_MPI)
        if (!MPI.Initialized()) error("MPI cannot be finalized as it has not been initialized. "); end  # This case should never occur as init_global_grid() must enforce that after a call to it, MPI is always initialized.
        if (MPI.Finalized()) error("MPI is already finalized. Set the argument 'finalize_MPI=false'."); end
        MPI.Finalize();
    end
    set_global_grid(GLOBAL_GRID_NULL);
    GC.gc();
    return nothing
end


text:
using JuMP, Gurobi
include("../basics/main.jl")
include("../basics/validations.jl")
include("../JuMP/JuMP_models.jl")
include("../heuristics/construction_algorithms.jl")
include("../heuristics/local_search.jl")

function GRASP_statistical_filter(CTS::Constants)
    tasks_by_w = OperationalShippingProblem(beta, CTS)
    sol_ini = Array{Tuple{Int, Int}, 1}()
    sol_local = Array{Tuple{Int, Int}, 1}()

    best_makespan = CTS.H
    best_LS = init_ls(CTS)
    for iter = 1:500
        makespan = CTS.H
        LS = init_ls(CTS)
        QC = init_qc(CTS)
        TIME = init_timer(CTS)

        while LS.tasks_left > 0 && TIME.period < TIME.horizon_plan
            cranes_status = "idle"
            while cranes_status != "All cranes are busy" && cranes_status != "Next time period" && cranes_status != "LS is completed"
                cranes_status = randomizedConstructionHeuristic("dist", tasks_by_w, bj, LS, TIME, QC, CTS)
            end
            next_time_period(TIME, QC);
        end

        if check_solution(LS, QC, CTS) == true
            makespan = total_makespan(LS, CTS)
            push!(sol_ini, (iter, makespan))
            if makespan < best_makespan
                best_makespan = makespan
                best_LS = LS
            end

            #perform local_search
            local_makespan = CTS.H
            for iter_ls = 1:100
                gamma = rand(0.3:0.05:0.9)
                new_LS = remove_tasks(1-gamma, LS, CTS)
                check, TIME, QC = get_current_state(new_LS, CTS)
                while new_LS.tasks_left > 0 && TIME.period < TIME.horizon_plan
                    cranes_status = "idle"
                    while cranes_status != "All cranes are busy" && cranes_status != "Next time period" && cranes_status != "LS is completed"
                        cranes_status = randomizedConstructionHeuristic("minimal", tasks_by_w, bj, new_LS, TIME, QC, CTS)
                    end
                    next_time_period(TIME, QC);
                end

                if check_solution(new_LS, QC, CTS) == true
                    makespan = total_makespan(new_LS, CTS)
                    if local_makespan < best_makespan
                        best_makespan = local_makespan = makespan
                        best_LS = deepcopy(new_LS)
                    elseif makespan < local_makespan
                        local_makespan = makespan
                    end
                else
                    println("STOP")
                    return(total_makespan(new_LS, CTS), new_LS)
                end
                push!(sol_local, (iter, local_makespan))
            end

        else
            println("ERROR")
            continue
        end
    end

    sol_ratio = Array{Float64,1}()
    for i = 1:500
        push!(sol_ratio, sol_ini[i][end]/sol_local[i][end])
    end
    filter_median = median(sol_ratio)

    return(filter_median, best_makespan, best_LS)
end

final_update, best_makespan, best_LS = GRASP_statistical_filter(CTS)
plot_solution(best_LS, best_makespan, CTS)

max_stars_repo_licenses: Zlib MIT

max_issues_repo_licenses: Zlib MIT

max_forks_repo_licenses: Zlib MIT


text:
module TestLinqMacro

using Test
using DataFrames
using DataFramesMeta
using Statistics
using Random

Random.seed!(100)
n = 100
df = DataFrame(a = rand(1:3, n),
               b = ["a","b","c","d"][rand(1:4, n)],
               x = rand(n))

x = @where(df, :a .> 2, :b .!= "c")
x = @transform(x, y = 10 * :x)
x = @by(x, :b, meanX = mean(:x), meanY = mean(:y))
x = @orderby(x, -:meanX)
x = @select(x, var = :b, :meanX, :meanY)

x1 = @linq transform(where(df, :a .> 2, :b .!= "c"), y = 10 * :x)
x1 = @linq by(x1, :b, meanX = mean(:x), meanY = mean(:y))
x1 = @linq select(orderby(x1, -:meanX), var = :b, :meanX, :meanY)

## chaining
xlinq = @linq df  |>
    where(:a .> 2, :b .!= "c")  |>
    transform(y = 10 * :x)  |>
    by(:b, meanX = mean(:x), meanY = mean(:y))  |>
    orderby(-:meanX)  |>
    select(var = :b, :meanX, :meanY)

@test x == x1
@test x == xlinq

xlinq2 = @linq df  |>
    where(:a .> 2, :b .!= "c")  |>
    transform(y = 10 * :x)  |>
    groupby(:b) |>
    orderby(-mean(:x))  |>
    based_on(meanX = mean(:x), meanY = mean(:y))

@test xlinq2[!, [:meanX, :meanY]] == xlinq[!, [:meanX, :meanY]]

xlinq3 = @linq df  |>
    where(:a .> 2, :b .!= "c")  |>
    transform(y = 10 * :x)  |>
    DataFrames.groupby(:b) |>
    orderby(-mean(:x))  |>
    based_on(meanX = mean(:x), meanY = mean(:y))

@test xlinq3[!, [:meanX, :meanY]] == xlinq[!, [:meanX, :meanY]]

@test (@linq df |> with(:a)) == df.a

end


text:
export getnotes, addnote!, addnotes!, addevent!, addevents!
export MIDITrack

"""
    MIDITrack <: Any

`MIDITrack` is simply a container for `TrackEvents`, since its only field is
`events::Vector{TrackEvent}`.

Track chunks begin with four bytes spelling out "MTrk", followed by the length
(in bytes) of the track (see [`readvariablelength`](@ref)), followed by a sequence
of events.
"""
mutable struct MIDITrack
    events::Vector{TrackEvent}
end
MIDITrack() = MIDITrack(TrackEvent[])
# Pretty print
function Base.show(io::IO, t::MIDITrack) where {N}
    L = length(t.events)
    M = count(x -> x isa MIDIEvent, t.events)
    T = count(x -> x isa MetaEvent, t.events)
    X = L - M - T
    print(io, "$(L)-event MIDITrack: $M MIDI, $T Meta, $X Sysex")
end

function readtrack(f::IO)

    mtrk = join(map(Char, read!(f, Array{UInt8}(undef, 4))))
    if mtrk != MTRK
        error("Not a valid MIDI file. Expected MTrk, got $(mtrk) starting at byte $(string(position(f)-4, base = 16, pad = 2))")
    end
    track = MIDITrack()

    # Get the length in bytes of the track
    tracklength = ntoh(read(f, UInt32))

    trackstart = position(f)
    bytesread = 0
    laststatus = UInt8(0) # Keeps track of running status
    while bytesread < tracklength
        # A track is made up of events. All events start with a variable length
        # value indicating the number of ticks (time) since the last event.

        # Get the time offset of the next event
        dT = readvariablelength(f)

        # Figure out the event type
        # Remember, endianness is byte order, not bit order. No need to ntoh here.
        event_start = read(f, UInt8)
        skip(f, -1)

        local event
        if isMIDIevent(event_start)
            event = readMIDIevent(dT, f, laststatus)
            laststatus = event.status
        elseif issysexevent(event_start)
            event = readsysexevent(dT, f)
            laststatus = UInt8(0)
        elseif ismetaevent(event_start)
            event = readmetaevent(dT, f)
            laststatus = UInt8(0)
        else
            error("Unrecognized event $(hex(event_start,2))")
        end
        push!(track.events, event)

        bytesread = position(f) - trackstart
    end

    # Validate that the track ends with a track end event
    lastevent = track.events[length(track.events)]
    if !isa(lastevent, EndOfTrackEvent)
        error("Invalid track - does not end with track metaevent")
    else
        # strip the track end event - we don't need to worry about manipulating it
        track.events = track.events[1:length(track.events)-1]
    end

    track
end

function writetrack(f::IO, track::MIDITrack)
    write(f, MTRK) # Track identifier

    writingMIDI = false
    previous_status = UInt8(0)

    event_buffer = IOBuffer()

    for event in track.events
        if isa(event, MIDIEvent) && previous_status != 0 && previous_status == event.status
            writeevent(event_buffer, event, false)
        elseif isa(event, MIDIEvent)
            writeevent(event_buffer, event)
            previous_status = event.status
        else
            writeevent(event_buffer, event)
            previous_status = UInt8(0)
        end
    end

    # Write the track end event
    writeevent(event_buffer, EndOfTrackEvent(0))

    bytes = take!(event_buffer)

    write(f, hton(UInt32(length(bytes))))
    write(f, bytes)
end

"""
    addevent!(track::MIDITrack, time::Int, event::TrackEvent)
Add an event to the `track` at given `time`. The `time` is in absolute time,
not relative.

If you want to add multiple events in one go, you should use the [`addevents!`](@ref)
function instead.
"""
function addevent!(track::MIDITrack, time::Integer, newevent::TrackEvent)
    tracktime = 0
    addedevent = false
    for (i, event) in enumerate(track.events)
        if tracktime + event.dT > time
            # Add to track at position
            newdt = time - tracktime
            newevent.dT = newdt

            insert!(track.events, i, newevent)
            addedevent = true

            nextevent = track.events[i+1]
            nextevent.dT -= newdt

            break
        else
            tracktime += event.dT
        end
    end

    if !addedevent
        newdt = time - tracktime
        newevent.dT = newdt
        push!(track.events, newevent)
    end
end

"""
    addevent_hint!(track::MIDITrack, time::Int, event::TrackEvent,
                    eventindex::UInt, eventtime::UInt)

Add an event to the `track` at given `time`. The `time` is in absolute time,
not relative. `eventindex` and `eventtime` have to be the index and the absolute
time of a known event in `track` which lays BEFORE the position where `event` shall be added.
This shortens the search for the correct position for `event` by skipping all
`TrackEvents` before the specified one.

Returns the index and absolute time of the added event.
"""
function addevent_hint!(track::MIDITrack, time::Integer, newevent::TrackEvent,
                    eventindex::Integer, eventtime::Integer)
    # start at known position
    eventtime > time && throw(ArgumentError("Eventtime has to be smaller than time."))
    tracktime = eventtime
    startindex = eventindex+1

    addedevent = false

    # start at known index
    for i = (startindex):length(track.events)
        if tracktime + track.events[i].dT > time

            # Add to track at position
            newdt = time - tracktime
            newevent.dT = newdt
            insert!(track.events, i, newevent)
            addedevent = true
            eventindex = i

            # update dT of following event
            nextevent = track.events[i+1]
            nextevent.dT -= newdt

            break
        else
            tracktime += track.events[i].dT
        end
    end

    if !addedevent
        newdt = time - tracktime
        newevent.dT = newdt
        push!(track.events, newevent)
        eventindex = length(track.events)
    end
    return (eventindex, Int(time))
end

"""
    addevents!(track::MIDITrack, times, events)

Add given `events` to given `track` at given `times`, internally
doing all translations from absolute time to relative time.

Using this function is more efficient than a loop over single [`addevent!`](@ref)
calls.
"""
function addevents!(track::MIDITrack, times::AbstractArray{Int}, events)

    # get a permutation that gives temporal order
    if issorted(times)
        perm = 1:length(times)
    else
        perm = sortperm(times)
    end

    # add the notes to the track using the faster version of addevent
    eventindex = 0
    eventtime = 0
    for i = 1:length(times)
        eventindex, eventtime = addevent_hint!(
            track, times[perm[i]], events[perm[i]], eventindex, eventtime
        )
    end
end

"""
    addnotes!(track::MIDITrack, notes)
Add given `notes` to given `track`, internally doing all translations from
absolute time to relative time.
"""
function addnotes!(track::MIDITrack, notes)
    # generate all events to be written to the track
    events = Vector{MIDI.TrackEvent}()
    posis = Vector{Int}()
    for anote in notes
        note = Note(anote)
        for (event, position) in [(NoteOnEvent, note.position), (NoteOffEvent, note.position + note.duration)]
            push!(events, event(0, Int(note.pitch), Int(note.velocity), channel = note.channel))
            push!(posis, position)
        end
    end

    addevents!(track, posis, events)
end

"""
    addnote!(track::MIDITrack, note::AbstractNote)
Add given `note` to given `track`, internally doing the translation from
absolute time to relative time.
"""
function addnote!(track::MIDITrack, anote::AbstractNote)
    # Convert to `Note
    note = Note(anote)
    for (event, position) in [(NoteOnEvent, note.position), (NoteOffEvent, note.position + note.duration)]
        addevent!(track, position, event(0, Int(note.pitch), Int(note.velocity), channel = note.channel))
    end
end

"""
    getnotes(midi::MIDIFile [, trackno])

Find all `NoteOnEvent`s and `NoteOffEvent`s in the `trackno` track of a `midi`
(default 1 or 2),
that correspond to the same note value (pitch) and convert them into
the `Note` datatype. There are special cases where NoteOffEvent is actually
encoded as NoteOnEvent with 0 velocity, but `getnotes` takes care of this.

Notice that the first track of a `midi` typically doesn't have any notes,
which is why the function defaults to track 2.

    getnotes(track::MIDITrack, tpq = 960)
Find the notes from `track` directly, passing also the ticks per quarter note.

Returns: `Notes{Note}`, setting the ticks per quarter note as `tpq`. You can find
the originally exported
ticks per quarter note from the original `MIDIFile` through `midi.tpq`.
"""
function getnotes(track::MIDITrack, tpq = 960)
    notes = Note[]
    tracktime = UInt(0)
    for (i, event) in enumerate(track.events)
        tracktime += event.dT
        # Read through events until a noteon with velocity higher tha 0 is found
        if event isa NoteOnEvent && event.velocity > 0
            duration = UInt(0)
            for event2 in track.events[i+1:length(track.events)]
                duration += event2.dT
                # If we have a MIDI event & it's a noteoff (or a note on with 0 velocity), and it's for the same note as the first event we found, make a note
                # Many MIDI files will encode note offs as note ons with velocity zero
                if (event2 isa NoteOffEvent || (event2 isa NoteOnEvent && event2.velocity == 0)) && event.note == event2.note
                    push!(notes, Note(event.note, event.velocity, tracktime, duration, channelnumber(event)))
                    break
                end
            end
        end
    end
    sort!(notes, lt=((x, y)->x.position<y.position))
    return Notes(notes, tpq)
end

"""
    programchange(track::MIDITrack, time::Integer, channel::UInt8, program::UInt8)

Change the program (instrument) on the given channel.
Time is absolute, not relative to the last event.

The `program` must be specified in the range 1-128, **not** in 0-127!
"""
function programchange(track::MIDITrack, time::Int, channel::Int, program::Int)
    if !(1 <= program <= 128)
        throw(ArgumentError("The `program` must be specified in the range 1-128"))
    end
    program -= 1
    addevent!(track, time, ProgramChangeEvent(0, program, channel = channel))
end

"""
    get_abs_pos(track::MIDITrack, idxs)
Return the absolute positions (since track start) of the events given by the
indices `idxs` of `track.events`.
"""
function get_abs_pos(idxs, track::MIDITrack)
    abspos = Int[]
    evtime = 0
    j = 1; L = length(idxs)
    for (i, event) in enumerate(track.events)
        evtime += event.dT
        if i == idxs[j]
            push!(abspos, evtime)
            j += 1
            j == L + 1 && return abspos
        end
    end
    return abspos
end


text:
const tt_rn_sep = ".v";
const res_source_dir_name = "source";
const res_data_file_ext = ".data.jld2";
const res_desc_file_ext = ".desc.jld2";

#=
    A struct representing a result repository file system.
=#
struct ResRepoTree
    res_name::AbstractString;
    time_tag::Int;
    parent_dir::AbstractString;
    res_dir::AbstractString;
    desc_file::AbstractString;
    data_file::AbstractString;
    source_dir::AbstractString;
    function ResRepoTree(parent_dir, rn::ResName, tt::TimeTag)
        res_name = rn.res_name;
        time_tag = tt.time_tag;
        res_ider = string(res_name, tt_rn_sep, time_tag);
        res_dir = joinpath(parent_dir, res_ider);
        desc_file = joinpath(res_dir, string(res_ider, res_desc_file_ext));
        data_file = joinpath(res_dir, string(res_ider, res_data_file_ext));
        source_dir = joinpath(res_dir, res_source_dir_name);
        new(res_name, time_tag, parent_dir, res_dir, desc_file, data_file, source_dir);
    end
end

function Base.parse(::Type{ResRepoTree}, s::AbstractString)
    base_s = basename(s);
    dirname_s = dirname(s);
    if is_valid_res_dir(base_s)
        res_name = extract_res_name(base_s);
        time_tag = extract_time_tag(base_s);
        return ResRepoTree(dirname(s), res_name, time_tag);
    elseif is_valid_res_repo_name(base_s)
        return ResRepoTree(dirname_s, base_s);
    else
        validate_res_dir(base_s);
    end
end

ResRepoTree(working_dir, res_name, time_tag) = ResRepoTree(working_dir, ResName(res_name), TimeTag(time_tag));
ResRepoTree(working_dir, res_name::ResName) = ResRepoTree(working_dir, res_name, TimeTag());
ResRepoTree(working_dir, res_name) = ResRepoTree(working_dir, ResName(res_name), TimeTag());
ResRepoTree(res::AbstractString) = parse(ResRepoTree, res);

# Tools
extract_res_name(res_dir) =
    ResName(String(split(basename(res_dir), tt_rn_sep)[1]));

extract_time_tag(res_dir) =
    TimeTag(String(split(basename(res_dir), tt_rn_sep)[2]));

sort_by_time_tag(rrs::Vector{ResRepoTree}) =
    [rrs[find_by_time_tag(rrs, tt)] for tt in sort!([rr.time_tag for rr in rrs])];

find_by_time_tag(rrs::Vector{ResRepoTree}, tt::Int) =
    findfirst((rr) -> rr.time_tag == tt, rrs);
find_by_time_tag(rrs::Vector{ResRepoTree}, tt::TimeTag) =
    find_by_time_tag(rrs, tt.time_tag);


text:
"""Convert hours in float to hours and minutes."""
function convert_time(old_time::Float64)
    h = floor(old_time)
    m = floor((old_time - h)*60)
    return Dates.Hour(h)+Dates.Minute(m)
end

function route_solve(lon1, lon2, lat1, lat2, x_nodes, y_nodes, y_dist, wisp, widi, cusp, cudi, polar, start)
    x, y, land = co_ordinates(lon1, lon2, lat1, lat2, x_nodes, y_nodes, y_dist)
    earliest_times = fill(Inf, size(x))
    prev_node = zero(x)
    node_indices = reshape(1:length(x), size(x))
    arrival_time = Inf
    @simd for idx in 1:size(x)[2]
        @inbounds d, b = haversine(lon1, lat1, x[1, idx], y[1, idx])
        ws_int = wisp[:interp](time=start, lon_b=x[1, idx], lat_b=y[1, idx])[:data][1]
        wd_int = widi[:interp](time=start, lon_b=x[1, idx], lat_b=y[1, idx])[:data][1]
        cs_int = cusp[:interp](time=start, lon_b=x[1, idx], lat_b=y[1, idx])[:data][1]
        cd_int = cudi[:interp](time=start, lon_b=x[1, idx], lat_b=y[1, idx])[:data][1]
        @inbounds earliest_times[1, idx] = d/correct_speed(polar, cd_int, cs_int, wd_int, ws_int, b)
    end

    for idy in 1:size(x)[1]-1
        @simd for idx in 1:size(x)[2]
            t = start + convert_time(earliest_times[idy, idx])
            @inbounds d, b = haversine(x[idy, idx], y[idy, idx], x[idy+1, idx], y[idy+1, idx])
            ws_int = wisp[:interp](time=t, lon_b=x[idy, idx], lat_b=y[idy, idx])[:data][1]
            wd_int = widi[:interp](time=t, lon_b=x[idy, idx], lat_b=y[idy, idx])[:data][1]
            cs_int = cusp[:interp](time=t, lon_b=x[idy, idx], lat_b=y[idy, idx])[:data][1]
            cd_int = cudi[:interp](time=t, lon_b=x[idy, idx], lat_b=y[idy, idx])[:data][1]
            @inbounds tt = earliest_times[idy, idx] + d/correct_speed(polar, cd_int, cs_int, wd_int, ws_int, b)
            if earliest_times[idy+1, idx] > tt
                earliest_times[idy+1, idx] = tt
                prev_node[idy+1, idx] = node_indices[idy, idx]
            end
        end
    end

    for idx in 1:size(x)[2]
        @inbounds d, b = haversine(x[end, idx], y[end, idx], lon2, lat2)
        t = start + convert_time(earliest_times[end, idx])
         ws_int = wisp[:interp](time=t, lon_b=x[end, idx], lat_b=y[end, idx])[:data][1]
         wd_int = widi[:interp](time=t, lon_b=x[end, idx], lat_b=y[end, idx])[:data][1]
         cs_int = cusp[:interp](time=t, lon_b=x[end, idx], lat_b=y[end, idx])[:data][1]
         cd_int = cudi[:interp](time=t, lon_b=x[end, idx], lat_b=y[end, idx])[:data][1]
        @inbounds tt = earliest_times[end, idx] + d/correct_speed(polar, cd_int, cs_int, wd_int, ws_int, b)
        if arrival_time > tt
            arrival_time = tt
            final_node = node_indices[end, idx]
        end
    end
    return arrival_time, earliest_times
end


text:
export XMLLayer, JSONLayer, load_legacy_database, save, load

import LightXML, JSON
import LightXML: parse_file, get_elements_by_tagname, root, content, XMLElement, attributes_dict, child_elements

struct XMLLayer <: DataLayer
    path::String
end

function get_ingredients(systembolagxml::XMLElement)
    ing = getr(systembolagxml, "Ingredient")
    Ingredient.(ing)
end

function get_recepies(drinkbookxml::XMLElement)
    drinks = getr(drinkbookxml, "Drink")
    Recipe.(drinks)
end

function load_legacy_database(loader::XMLLayer, filename)

    xdoc = parse_file(joinpath(loader.path,filename))
    xroot = root(xdoc)

    systembolagxml = get_elements_by_tagname(xroot, "Systembolaget")[1]
    cabinetxml = get_elements_by_tagname(xroot, "Barskapet")[1]
    drinkbookxml = get_elements_by_tagname(xroot, "Drinkboken")[1]

    sysing = get_ingredients(systembolagxml)
    systembolag = Systembolag(sysing)
    supermarket = Supermarket(sysing)
    cabinet = BarCabinet(get_ingredients(cabinetxml))
    for a in cabinet.alcohols
        push!(systembolag,a)
    end
    for s in cabinet.sides
        push!(supermarket,s)
    end
    drinkbook = DrinkBook(get_recepies(drinkbookxml))

    return systembolag, supermarket, cabinet, drinkbook
end

function Ingredient(ing::XMLElement)
    name = content.(get_elements_by_tagname(ing, "Name"))[]
    ad = attributes_dict(ing)
    if ad["type"] == "Sprit"
        sort = content.(get_elements_by_tagname(ing, "Spritsort"))[]
        return Alcohol(name)
    end
    Side(name)
end

function Recipe(drink::XMLElement)
    ings       = Ingredient.(getr(drink, "Ingredient"))
    quantities = content.(getr(drink, "Quantity"))
    mand       = parse.(Bool,content.(getr(drink, "Mandatory")))
    desciption = content(getr(drink, "Description")[])
    name       = content(getr(drink, "Name")[1])
    Recipe(name, DrinkComponent.(ings, mand, quantities), desciption)
end

"""
getr(root::XMLElement, tag::String)
Breadth first tree search for `tag`
"""
function getr(root::XMLElement, tag)
    ret = []
    queue = collect(child_elements(root))
    while !isempty(queue)
        e = popfirst!(queue)
        if LightXML.name(e) == tag
            push!(ret, e)
        else
            queue = vcat(queue, collect(child_elements(e)))
        end
    end
    ret
end

struct JSONLayer <: DataLayer
    path::String
end

function save(saver::JSONLayer, filename, object)
    file = joinpath(saver.path, filename)
    open(file, "w") do f
        print(f, object)
    end
end

function load(saver::JSONLayer, filename)
    file = joinpath(saver.path, filename)
    l = readlines(file)
    recipes = eval(Meta.parse(l[1]))
end


text:
"""
    AbstractFieldData

Abstract type representing any kind of dataset.
"""
abstract type AbstractFieldData end

"""
    VTKPointData <: AbstractFieldData

Represents data that is to be attached to grid points.
"""
struct VTKPointData <: AbstractFieldData end

"""
    VTKCellData <: AbstractFieldData

Represents data that is to be attached to grid cells.
"""
struct VTKCellData <: AbstractFieldData end

"""
    VTKFieldData <: AbstractFieldData

Represents data that is not attached to the grid geometry.

This is typically used for lightweight metadata, such as timestep information or
strings.
"""
struct VTKFieldData <: AbstractFieldData end

# These are the VTK names associated to each data "location".
node_type(::VTKPointData) = "PointData"
node_type(::VTKCellData) = "CellData"
node_type(::VTKFieldData) = "FieldData"

const ArrayOrValue = Union{AbstractArray, Number, String}
const ListOfStrings = Union{Tuple{Vararg{String}}, AbstractArray{String}}

# Determine number of components of input data.
function num_components(Ndata::Integer, num_points_or_cells)
    Nc = div(Ndata, num_points_or_cells)
    if Nc * num_points_or_cells != Ndata
        throw(DimensionMismatch("incorrect dimensions of input array."))
    end
    Nc
end

_type_length(::Type{<:Union{Number, String}}) = 1
_type_length(::Type{A}) where {A <: AbstractArray} = length(A)  # here, `A` may be a StaticArray subtype

# Returns the "length" of a single element of the array.
# This is 1 in the common case where `T <: Number`.
# However, if this is an array of StaticArrays (`T <: SArray`), then the length
# is the number of elements in each SArray.
_eltype_length(::AbstractArray{T}) where {T} = _type_length(T)
_eltype_length(::Any) = 1

num_components(data::ArrayOrValue, num_points_or_cells) =
    num_components(length(data), num_points_or_cells) * _eltype_length(data)

num_components(data::AbstractArray, vtk, ::VTKPointData) =
    num_components(data, vtk.Npts)

num_components(data::AbstractArray, vtk, ::VTKCellData) =
    num_components(data, vtk.Ncls)

num_components(data::AbstractArray, vtk, ::VTKFieldData) =
    _eltype_length(data)

num_components(::Union{Number,String}, args...) = 1
num_components(data::Tuple{Vararg{String}}, args...) = 1
num_components(data::Tuple, args...) = length(data)

# This is for the NumberOfTuples attribute of FieldData.
num_field_tuples(data::ArrayOrValue) = length(data)
num_field_tuples(data::ListOfStrings) = length(data)
num_field_tuples(data::String) = 1
num_field_tuples(data::Tuple) = num_field_tuples(first(data))

# Guess from data dimensions whether data should be associated to points,
# cells or none.
function guess_data_location(data, vtk)
    N = length(data)
    if rem(N, vtk.Npts) == 0
        VTKPointData()
    elseif rem(N, vtk.Ncls) == 0
        VTKCellData()
    else
        VTKFieldData()
    end
end

guess_data_location(data::Tuple, args...) =
    guess_data_location(first(data), args...)

guess_data_location(data::Tuple{}, args...) = VTKPointData()

"""
    VTKDataType

Union of data types allowed by VTK.
"""
const VTKDataType = Union{Int8, UInt8, Int16, UInt16, Int32, UInt32,
                          Int64, UInt64, Float32, Float64, String}

# Return the VTK string representation of a numerical data type.
function datatype_str(::Type{T}) where T <: VTKDataType
    # Note: the VTK type names are exactly the same as the Julia type names
    # (e.g.  Float64 -> "Float64"), so that we can simply use the `string`
    # function.
    string(T)
end

datatype_str(::Type{T}) where T =
    throw(ArgumentError("data type not supported by VTK: $T"))
datatype_str(v) = datatype_str(typeof(v))
datatype_str(::Type{A}) where {A <: AbstractArray} = datatype_str(eltype(A))
datatype_str(u::AbstractArray) = datatype_str(eltype(u))
datatype_str(::ListOfStrings) = datatype_str(String)

function datatype_str(t::Tuple)
    Ts = map(eltype, t)
    T = first(Ts)
    @assert all(Ts .== T)  # all elements of the tuple must have the same eltype
    datatype_str(T)
end

# Total size of data in bytes.
sizeof_data(x) = sizeof(x)
sizeof_data(x::String) = sizeof(x) + sizeof("\0")
sizeof_data(x::AbstractArray) = length(x) * sizeof(eltype(x))
sizeof_data(x::ListOfStrings) = sum(sizeof_data, x)
sizeof_data(x::Tuple) = sum(sizeof_data, x)

write_array(io, data) = write(io, data)
write_array(io, x::String) = write(io, x, '\0')
write_array(io, x::ListOfStrings) = sum(s -> write_array(io, s), x)

function write_array(io, data::Tuple)
    n = 0
    for i in eachindex(data...), x in data
        n += write(io, x[i])
    end
    n
end

function add_data_ascii(xml, x::Union{Number,String})
    add_text(xml, " ")
    add_text(xml, string(x))
end

add_data_ascii(xml, x::AbstractArray) = map(v -> add_data_ascii(xml, v), x)

function add_data_ascii(xml, data::Tuple)
    for i in eachindex(data...), x in data
        add_data_ascii(xml, x[i])
    end
end

function set_num_components(xDA, vtk, data, loc)
    Nc = num_components(data, vtk, loc)
    set_attribute(xDA, "NumberOfComponents", Nc)
    nothing
end

# In the specific case of FieldData, we also need to set the number of "tuples"
# (number of elements per field component).
function set_num_components(xDA, vtk, data, loc::VTKFieldData)
    Nc = num_components(data, vtk, loc)
    Nt = num_field_tuples(data)
    set_attribute(xDA, "NumberOfComponents", Nc)
    set_attribute(xDA, "NumberOfTuples", Nt)
    nothing
end

xml_data_array_name(::Any) = "DataArray"
xml_data_array_name(::Union{String,ListOfStrings}) = "Array"

"""
    data_to_xml(
        vtk::DatasetFile, xParent::XMLElement, data,
        name::AbstractString, Nc::Union{Int,AbstractFieldData} = 1,
    )

Add numerical data to VTK XML file.

Data is written under the `xParent` XML node.

`Nc` may be either the number of components, or the type of field data.
In the latter case, the number of components will be deduced from the data
dimensions and the type of field data.
"""
function data_to_xml(vtk, xParent, data, name,
                     Nc::Union{Int,AbstractFieldData}=1)
    xDA = new_child(xParent, xml_data_array_name(data))
    set_attribute(xDA, "type", datatype_str(data))
    set_attribute(xDA, "Name", name)
    if Nc isa Int
        set_attribute(xDA, "NumberOfComponents", Nc)
    else
        set_num_components(xDA, vtk, data, Nc)
    end
    if vtk.appended
        data_to_xml_appended(vtk, xDA, data)
    elseif vtk.ascii
        data_to_xml_ascii(vtk, xDA, data)
    else
        data_to_xml_inline(vtk, xDA, data)
    end
end

"""
    data_to_xml_appended(vtk::DatasetFile, xDA::XMLElement, data)

Add appended raw binary data to VTK XML file.

Data is written to the `vtk.buf` buffer.

When compression is enabled:

  * the data array is written in compressed form (obviously);

  * the header, written before the actual numerical data, is an array of
    HeaderType (UInt32 / UInt64) values:

        [num_blocks, blocksize, last_blocksize, compressed_blocksizes]

    All sizes are in bytes.
    The header itself is not compressed, only the data is.
    For more details, see:
     - <http://public.kitware.com/pipermail/paraview/2005-April/001391.html>
     - <http://mathema.tician.de/what-they-dont-tell-you-about-vtk-xml-binary-formats>

Otherwise, if compression is disabled, the header is just a single HeaderType value
containing the size of the data array in bytes.

"""
function data_to_xml_appended(vtk::DatasetFile, xDA::XMLElement, data)
    @assert vtk.appended

    buf = vtk.buf    # append buffer
    compress = vtk.compression_level > 0

    # DataArray node
    set_attribute(xDA, "format", "appended")
    set_attribute(xDA, "offset", position(buf))

    # Size of data array (in bytes).
    nb = sizeof_data(data)

    if compress
        initpos = position(buf)

        # Write temporary data that will be replaced later with the real header.
        let header = ntuple(d -> zero(HeaderType), Val(4))
            write(buf, header...)
        end

        # Write compressed data.
        zWriter = ZlibCompressorStream(buf, level=vtk.compression_level)
        write_array(zWriter, data)
        write(zWriter, TranscodingStreams.TOKEN_END)
        flush(zWriter)

        # Go back to `initpos` and write real header.
        endpos = position(buf)
        compbytes = endpos - initpos - 4 * sizeof(HeaderType)
        let header = HeaderType.((1, nb, nb, compbytes))
            seek(buf, initpos)
            write(buf, header...)
            seek(buf, endpos)
        end
    else
        write(buf, HeaderType(nb))  # header (uncompressed version)
        nb_write = write_array(buf, data)
        @assert nb_write == nb
    end

    xDA
end

"""
    data_to_xml_inline(vtk::DatasetFile, xDA::XMLElement, data)

Add inline, base64-encoded data to VTK XML file.
"""
function data_to_xml_inline(vtk::DatasetFile, xDA::XMLElement, data)
    @assert !vtk.appended && !vtk.ascii
    compress = vtk.compression_level > 0

    # DataArray node
    set_attribute(xDA, "format", "binary")   # here, binary means base64-encoded

    # Number of bytes of data.
    nb = sizeof_data(data)

    # Write data to a buffer, which is then base64-encoded and added to the
    # XML document.
    buf = IOBuffer()

    # NOTE: in the compressed case, the header and the data need to be
    # base64-encoded separately!!
    # That's why we don't use a single buffer that contains both, like in the
    # other data_to_xml function.
    local zWriter
    if compress
        # Write compressed data.
        zWriter = ZlibCompressorStream(buf, level=vtk.compression_level)
        write_array(zWriter, data)
        write(zWriter, TranscodingStreams.TOKEN_END)
        flush(zWriter)
    else
        write_array(buf, data)
    end

    # Write buffer with data to XML document.
    add_text(xDA, "\n")
    if compress
        add_text(xDA, base64encode(HeaderType.((1, nb, nb, position(buf)))...))
    else
        add_text(xDA, base64encode(HeaderType(nb)))     # header (uncompressed version)
    end
    add_text(xDA, base64encode(take!(buf)))
    add_text(xDA, "\n")

    if compress
        close(zWriter)
    end
    close(buf)

    xDA
end

"""
    data_to_xml_ascii(vtk::DatasetFile, xDA::XMLElement, data)

Add inline data to VTK XML file in ASCII format.
"""
function data_to_xml_ascii(vtk::DatasetFile, xDA::XMLElement, data)
    @assert !vtk.appended && vtk.ascii
    set_attribute(xDA, "format", "ascii")
    add_text(xDA, "\n")
    add_data_ascii(xDA, data)
    add_text(xDA, "\n")
    xDA
end

"""
    add_field_data(vtk::DatasetFile, data,
                   name::AbstractString, loc::AbstractFieldData)

Add either point or cell data to VTK file.
"""
function add_field_data(vtk::DatasetFile, data, name::AbstractString,
                        loc::AbstractFieldData)
    # Find Piece node.
    xroot = root(vtk.xdoc)
    xGrid = find_element(xroot, vtk.grid_type)

    xbase = if loc === VTKFieldData()
        xGrid
    else
        find_element(xGrid, "Piece")
    end

    # Find or create "nodetype" (PointData, CellData or FieldData) node.
    nodetype = node_type(loc)
    xtmp = find_element(xbase, nodetype)
    xPD = (xtmp === nothing) ? new_child(xbase, nodetype) : xtmp

    # DataArray node
    xDA = data_to_xml(vtk, xPD, data, name, loc)

    xDA
end

vtk_point_data(args...) = add_field_data(args..., VTKPointData())
vtk_cell_data(args...) = add_field_data(args..., VTKCellData())
vtk_field_data(args...) = add_field_data(args..., VTKFieldData())

"""
    setindex!(vtk::DatasetFile, data, name::AbstractString, [field_type])

Add a new dataset to VTK file.

The number of components of the dataset (e.g. for scalar or vector fields) is
determined automatically from the input data dimensions.

The optional argument `field_type` should be an instance of `VTKPointData`,
`VTKCellData` or `VTKFieldData`.
It determines whether the data should be associated to grid points, cells or
none.
If not given, this is guessed from the input data size and the grid dimensions.

# Example

Add "velocity" dataset and time scalar to VTK file.

```julia
vel = rand(3, 12, 14, 42)  # vector field
time = 42.0

vtk = vtk_grid(...)
vtk["velocity", VTKPointData()] = vel
vtk["time", VTKFieldData()] = time

# This also works, and will generally give the same result:
vtk["velocity"] = vel
vtk["time"] = time
```
"""
Base.setindex!(vtk::DatasetFile, data, name::AbstractString,
               loc::AbstractFieldData) = add_field_data(vtk, data, name, loc)

function Base.setindex!(vtk::DatasetFile, data, name::AbstractString)
    loc = guess_data_location(data, vtk) :: AbstractFieldData
    setindex!(vtk, data, name, loc)
end


text:
# This file is a part of MGVI.jl, licensed under the MIT License (MIT).

using MGVI

using Distributions
using LinearAlgebra
using Random

if :ModelPolyfit ∉ names(Main)
    include("test_models/model_polyfit.jl")
    import .ModelPolyfit
end

Test.@testset "test_jacobians_consistency" begin

    epsilon = 1E-5
    Random.seed!(145)

    _flat_model = MGVI._dists_flat_params_getter(ModelPolyfit.model)
    true_params = ModelPolyfit.true_params

    full_jac = FullJacobianFunc(_flat_model)(true_params)
    fwdder_jac = FwdDerJacobianFunc(_flat_model)(true_params)
    fwdrevad_jac = FwdRevADJacobianFunc(_flat_model)(true_params)

    for i in 1:min(size(full_jac)...)

        vec = rand(size(full_jac, 2))

        Test.@test norm(fwdder_jac*vec - full_jac*vec) < epsilon

        Test.@test norm(fwdrevad_jac*vec - full_jac*vec) < epsilon

    end

end


text:
###
### Copying
###
###
### Copying methods for biological sequences.
###
### This file is a part of BioJulia.
### License is MIT: https://github.com/BioJulia/BioSequences.jl/blob/master/LICENSE.md

# TODO: Add generic emethods for other bioseqs like mers and refseqs
##########
"""
    copy!(dst::LongSequence, src::BioSequence)

In-place copy content of `src` to `dst`, resizing `dst` to fit.
The alphabets of `src` and `dst` must be compatible.

# Examples
```
julia> seq = copy!(dna"TAG", dna"AACGTM")
6nt DNA Sequence:
AACGTM

julia> copy!(seq, rna"UUAG")
4nt DNA Sequence:
TTAG
```
"""
function Base.copy!(dst::LongSequence{A}, src::LongSequence{A}) where {A <: Alphabet}
    return _copy!(dst, src)
end

function Base.copy!(dst::LongSequence{<:NucleicAcidAlphabet{N}},
                    src::LongSequence{<:NucleicAcidAlphabet{N}}) where N
    return _copy!(dst, src)
end

function _copy!(dst::LongSequence, src::LongSequence)
    resize!(dst.data, length(src.data))
    copyto!(dst.data, src.data)
    dst.len = src.len
    return dst
end

"""
    copyto!(dst::LongSequence, src::BioSequence)

Equivalent to `copyto!(dst, 1, src, 1, length(src))`
"""
function Base.copyto!(dst::LongSequence{A}, src::LongSequence{A}) where {A <: Alphabet}
    return copyto!(dst, 1, src, 1, length(src))
end

function Base.copyto!(dst::LongSequence{<:NucleicAcidAlphabet{N}},
                      src::LongSequence{<:NucleicAcidAlphabet{N}}) where N
    return copyto!(dst, 1, src, 1, length(src))
end

"""
    copyto!(dst::LongSequence, soff, src::BioSequence, doff, N)

In-place copy `N` elements from `src` starting at `soff` to `dst`, starting at `doff`.
The length of `dst` must be greater than or equal to `N + doff - 1`.
The first N elements of `dst` are overwritten,
the other elements are left untouched. The alphabets of `src` and `dst` must be compatible.

# Examples
```
julia> seq = copyto!(dna"AACGTM", 1, dna"TAG", 1, 3)
6nt DNA Sequence:
TAGGTM

julia> copyto!(seq, 2, rna"UUUU", 1, 4)
6nt DNA Sequence:
TTTTTM
```
"""
function Base.copyto!(dst::LongSequence{A}, doff::Integer,
                      src::LongSequence{A}, soff::Integer,
                      N::Integer) where {A <: Alphabet}
    return _copyto!(dst, doff, src, soff, N)
end

function Base.copyto!(dst::LongSequence{<:NucleicAcidAlphabet{B}}, doff::Integer,
                      src::LongSequence{<:NucleicAcidAlphabet{B}}, soff::Integer,
                      N::Integer) where B
    return _copyto!(dst, doff, src, soff, N)
end

function _copyto!(dst::LongSequence{A}, doff::Integer,
                  src::LongSequence, soff::Integer,
                  N::Integer) where {A <: Alphabet}
    @boundscheck checkbounds(dst, doff:doff+N-1)
    @boundscheck checkbounds(src, soff:soff+N-1)

    # This prevents a sequence from destructively overwriting its own data
    if (dst === src) & (doff > soff)
        return _copyto!(dst, doff, src[soff:soff+N-1], 1, N)
    end

    id = bitindex(dst, doff)
    is = bitindex(src, soff)
    rest = N * bits_per_symbol(A())
    dstdata = dst.data
    srcdata = src.data

    @inbounds while rest > 0
        # move `k` bits from `src` to `dst`
        x = dstdata[index(id)]
        y = srcdata[index(is)]
        if offset(id) < offset(is)
            y >>= (offset(is) - offset(id)) & 63
            k = min(64 - offset(is), rest)
        else
            y <<= (offset(id) - offset(is)) & 63
            k = min(64 - offset(id), rest)
        end
        m = bitmask(k) << offset(id)
        dstdata[index(id)] = y & m | x & ~m

        id += k
        is += k
        rest -= k
    end

    return dst
end

Base.copy(seq::LongSequence) = typeof(seq)(copy(seq.data), seq.len)

#########
const SeqLike = Union{AbstractVector, AbstractString}
const ASCIILike = Union{String, SubString{String}}

"""
    copy!(dst::LongSequence, src)

In-place copy content of sequence-like object `src` to `dst`, resizing `dst` to fit.
The content of `src` must be able to be encoded to the alphabet of `dst`.

# Examples
```
julia> seq = copy!(dna"TAG", "AACGTM")
6nt DNA Sequence:
AACGTM

julia> copy!(seq, [0x61, 0x43, 0x54])
3nt DNA Sequence:
ACT
```
"""
function Base.copy!(dst::LongSequence{A}, src::SeqLike) where {A <: Alphabet}
    return copy!(dst, src, codetype(A()))
end

function Base.copy!(dst::LongSequence{<:Alphabet}, src::ASCIILike, C::AsciiAlphabet)
    v = GC.@preserve src unsafe_wrap(Vector{UInt8}, pointer(src), ncodeunits(src))
    return copy!(dst, v, C)
end

function Base.copy!(dst::LongSequence{<:Alphabet}, src::AbstractVector{UInt8}, ::AsciiAlphabet)
    resize!(dst, length(src))
    return encode_chunks!(dst, 1, src, 1, length(src))
end

function Base.copy!(dst::LongSequence{<:Alphabet}, src::SeqLike, ::AlphabetCode)
    len = length(src) # calculate only once
    resize!(dst, len)
    return copyto!(dst, 1, src, 1, len)
end

########

for (anum, atype) in enumerate((DNAAlphabet{4}, DNAAlphabet{2}, RNAAlphabet{4},
    RNAAlphabet{2}, AminoAcidAlphabet))
    tablename = Symbol("BYTE_TO_ALPHABET_CHAR" * string(anum))
    @eval begin
        alph = $(atype)()
        syms = symbols(alph)
        const $(tablename) = let
            bytes = fill(0x80, 256)
            for symbol in syms
                bytes[UInt8(Char(symbol)) + 1] = encode(alph, symbol)
                bytes[UInt8(lowercase(Char(symbol))) + 1] = encode(alph, symbol)
            end
            Tuple(bytes)
        end
        stringbyte(::$(atype), x::UInt8) = @inbounds $(tablename)[x + 1]
    end
end

# This is used to effectively scan an array of UInt8 for invalid bytes, when one is detected
@noinline function throw_encode_error(A::Alphabet, src::AbstractArray{UInt8}, soff::Integer)
    for i in 1:div(64, bits_per_symbol(A))
        sym = src[soff+i-1]
        stringbyte(A, sym) & 0x80 == 0x80 && error("Cannot encode $sym to $A")
    end
end

@inline function encode_chunk(A::Alphabet, src::AbstractArray{UInt8}, soff::Integer, N::Integer)
    chunk = zero(UInt64)
    check = 0x00
    @inbounds for i in 1:N
        enc = stringbyte(A, src[soff+i-1])
        check |= enc
        chunk |= UInt64(enc) << (bits_per_symbol(A) * (i-1))
    end
    check & 0x80 == 0x00 || throw_encode_error(A, src, soff)
    return chunk
end

# Use this for AsiiAlphabet alphabets only, internal use only, no boundschecks.
# This is preferential to `copyto!` if none of the sequence's original content
# needs to be kept, since this is faster.
function encode_chunks!(dst::LongSequence{A}, startindex::Integer, src::AbstractVector{UInt8},
                        soff::Integer, N::Integer) where {A <: Alphabet}
    chunks, rest = divrem(N, symbols_per_data_element(dst))
    @inbounds for i in startindex:startindex+chunks-1
        dst.data[i] = encode_chunk(A(), src, soff, symbols_per_data_element(dst))
        soff += symbols_per_data_element(dst)
    end
    @inbounds if !iszero(rest)
        dst.data[startindex+chunks] = encode_chunk(A(), src, soff, rest)
    end
    return dst
end

#########

# Two-argument method
"""
    copyto!(dst::LongSequence, src)

Equivalent to `copyto!(dst, 1, src, 1, length(src))`.
"""
function Base.copyto!(dst::LongSequence{A}, src::SeqLike) where {A <: Alphabet}
    return copyto!(dst, src, codetype(A()))
end

# Specialized method to avoid O(N) length call for string-like src
function Base.copyto!(dst::LongSequence{<:Alphabet}, src::ASCIILike, C::AsciiAlphabet)
    len = ncodeunits(src)
    @boundscheck checkbounds(dst, 1:len)
    v = GC.@preserve src unsafe_wrap(Vector{UInt8}, pointer(src), ncodeunits(src))
    encode_chunks!(dst, 1, v, 1, len)
    return dst
end

function Base.copyto!(dst::LongSequence{<:Alphabet}, src::SeqLike, C::AlphabetCode)
    return copyto!(dst, 1, src, 1, length(src), C)
end

"""
    copyto!(dst::LongSequence, soff, src, doff, N)

In-place encode `N` elements from `src` starting at `soff` to `dst`, starting at `doff`.
The length of `dst` must be greater than or equal to `N + doff - 1`.
The first N elements of `dst` are overwritten,
the other elements are left untouched. The content of `src` must be able to be encoded to
the alphabet of `dst`.

# Examples
```
julia> seq = copyto!(dna"AACGTM", 1, "TAG", 1, 3)
6nt DNA Sequence:
TAGGTM

julia> copyto!(seq, 2, rna"UUUU", 1, 4)
6nt DNA Sequence:
TTTTTM
```
"""
# Dispatch to codetype
function Base.copyto!(dst::LongSequence{A}, doff::Integer,
                        src::SeqLike, soff::Integer, N::Integer) where {A <: Alphabet}
    return copyto!(dst, doff, src, soff, N, codetype(A()))
end

# For ASCII seq and src, convert to byte vector and dispatch using that
function Base.copyto!(dst::LongSequence{A}, doff::Integer,
                      src::ASCIILike, soff::Integer,
                      N::Integer, C::AsciiAlphabet) where {A <: Alphabet}
    v = GC.@preserve src unsafe_wrap(Vector{UInt8}, pointer(src), ncodeunits(src))
    return Base.copyto!(dst, doff, v, soff, N, C)
end

@noinline function throw_enc_indexerr(N::Integer, len::Integer, soff::Integer)
    throw(ArgumentError("source of length $len does not contain $N elements from $soff"))
end

# Generic method for copyto!, i.e. NOT ASCII input
function Base.copyto!(dst::LongSequence{A}, doff::Integer,
                      src::SeqLike, soff::Integer,
                      len::Integer, ::AlphabetCode) where {A <: Alphabet}
    if soff != 1 && isa(src, AbstractString) && !isascii(src)
        throw(ArgumentError("source offset ≠ 1 is not supported for non-ASCII string"))
    end

    checkbounds(dst, doff:doff+len-1)
    length(src) < soff + len - 1 && throw_enc_indexerr(len, length(src), soff)

    n = 0
    i = soff
    @inbounds while n < len
        n += 1
        dst[doff + n - 1] = src[i]
        i = nextind(src, i)
    end
    return dst
end

# Special method possible for ASCII alphabet and UInt8 array
function Base.copyto!(dst::LongSequence{A}, doff::Integer, src::AbstractVector{UInt8},
                      soff::Integer, N::Integer, ::AsciiAlphabet) where {A<:Alphabet}
    checkbounds(dst, doff:doff+N-1)
    length(src) < soff + N - 1 && throw_enc_indexerr(N, length(src), soff)
    bitind = bitindex(dst, doff)
    remaining = N

    # Fill in first chunk. Since it may be only partially filled from both ends,
    # bit-tricks are harder and we do it the old-fashined way.
    # Maybe this can be optimized but eeehhh...
    @inbounds while (!iszero(offset(bitind)) & !iszero(remaining))
        dst[doff] = eltype(dst)(reinterpret(Char, (src[soff] % UInt32) << 24))
        doff += 1
        soff += 1
        remaining -= 1
        bitind += bits_per_symbol(A())
    end

    # Fill in middle
    n = remaining - rem(remaining, symbols_per_data_element(dst))
    encode_chunks!(dst, index(bitind), src, soff, n)
    remaining -= n
    soff += n
    bitind += n * bits_per_symbol(A())

    # Fill in last chunk
    @inbounds if !iszero(remaining)
        chunk = encode_chunk(A(), src, soff, remaining)
        before = dst.data[index(bitind)] & (typemax(UInt) << (remaining * bits_per_symbol(A())))
        dst.data[index(bitind)] = chunk | before
    end

    return dst
end


text:
#-------------------------------------------------------------------
#* EMSO Model Library (EML) Copyright (C) 2004 - 2007 ALSOC.
#*
#* This LIBRARY is free software; you can distribute it and/or modify
#* it under the therms of the ALSOC FREE LICENSE as available at
#* http://www.enq.ufrgs.br/alsoc.
#*
#* EMSO Copyright (C) 2004 - 2007 ALSOC, original code
#* from http://www.rps.eng.br Copyright (C) 2002-2004.
#* All rights reserved.
#*
#* EMSO is distributed under the therms of the ALSOC LICENSE as
#* available at http://www.enq.ufrgs.br/alsoc.
#*--------------------------------------------------------------------
#* Author: Gerson Balbueno Bicca
#* $Id$
#*--------------------------------------------------------------------
type Tube_Side_Main
	Tube_Side_Main()=begin
		new(
			DanaInteger(Dict{Symbol,Any}(
				:Brief=>"Total Number of Tubes in Shell",
				:Default=>100,
				:Lower=>1
			)),
			DanaInteger(Dict{Symbol,Any}(
				:Brief=>"Number of Tube Passes",
				:Lower=>1
			)),
			length(Dict{Symbol,Any}(
				:Brief=>"Effective Tube Length",
				:Lower=>0.1
			)),
			length(Dict{Symbol,Any}(
				:Brief=>"Tube Pitch",
				:Lower=>1E-8
			)),
			conductivity(Dict{Symbol,Any}(
				:Brief=>"Tube Wall Material Thermal Conductivity"
			)),
			length(Dict{Symbol,Any}(
				:Brief=>"Tube Outside Diameter",
				:Lower=>1E-6
			)),
			length(Dict{Symbol,Any}(
				:Brief=>"Tube Inside Diameter",
				:Lower=>1E-6
			)),
			positive(Dict{Symbol,Any}(
				:Brief=>"Tubeside Fouling Resistance",
				:Unit=>"m^2*K/kW",
				:Symbol=>"Rf_{tube}",
				:Default=>1E-6 ,
				:Lower=>0
			)),
			length(Dict{Symbol,Any}(
				:Brief=>"Inlet Nozzle Inside Diameter",
				:Lower=>1E-6
			)),
			length(Dict{Symbol,Any}(
				:Brief=>"Outlet Nozzle Inside Diameter",
				:Lower=>1E-6
			)),
			Tube_Pdrop(Dict{Symbol,Any}(
				:Brief=>"Tube Side Pressure Drop",
				:Symbol=>" "
			)),
			Tube_Heat_Transfer(Dict{Symbol,Any}(
				:Brief=>"Tube Side Heat Transfer",
				:Symbol=>" "
			)),
			Physical_Properties(Dict{Symbol,Any}(
				:Brief=>"Tube Side Properties",
				:Symbol=>" "
			)),
			[:NumberOfTubes,:Tubepasses,:TubeLength,:TubePitch,:Kwall,:TubeOD,:TubeID,:Fouling,:InletNozzleID,:OutletNozzleID,],
			[:PressureDrop,:HeatTransfer,:Properties,]
		)
	end
	NumberOfTubes::DanaInteger
	Tubepasses::DanaInteger
	TubeLength::length
	TubePitch::length
	Kwall::conductivity
	TubeOD::length
	TubeID::length
	Fouling::positive
	InletNozzleID::length
	OutletNozzleID::length
	PressureDrop::Tube_Pdrop
	HeatTransfer::Tube_Heat_Transfer
	Properties::Physical_Properties
	parameters::Array{Symbol,1}
	variables::Array{Symbol,1}
	attributes::Dict{Symbol,Any}
end
export Tube_Side_Main
function atributes(in::Tube_Side_Main,_::Dict{Symbol,Any})
	fields::Dict{Symbol,Any}=Dict{Symbol,Any}()
	fields[:Pallete]=false
	fields[:Brief]="Main variables in the Tube Side section of a shell and tube heat exchanger."
	drive!(fields,_)
	return fields
end
Tube_Side_Main(_::Dict{Symbol,Any})=begin
	newModel=Tube_Side_Main()
	newModel.attributes=atributes(newModel,_)
	newModel
end


text:
function Base.show(io::IO, n::T) where T <: NumberParameter
    @printf io "    ["
    printstyled(io, "NumberParameter", color = :blue)
    @printf io "]\n"

    printstyled(io, "    name", color = :blue)
    @printf io " : "
    printstyled(io, "$(n.name)\n", color = :bold)

    printstyled(io, "    min", color = :green)
    @printf io "  : %6f\n" n.min

    printstyled(io, "    max", color = :red)
    @printf io "  : %6f\n" n.max

    printstyled(io, "    value", color = :yellow)
    @printf io ": %6f\n" n.value
    return
end

function Base.show(io::IO, n::T) where T <: EnumParameter
    @printf io "    ["
    printstyled(io, "EnumParameter", color = :blue)
    @printf io "]\n"
    printstyled(io, "    name", color = :blue)
    @printf io "   : "
    printstyled(io, "$(n.name)\n", color = :bold)
    printstyled(io, "    current", color = :yellow)
    @printf io ": "
    printstyled(io, "$(n.current.name)\n", color = :bold)
    printstyled(io, "    values", color = :green)
    @printf io " : \n"
    for i = 1:length(n.values)
        show(io, n.values[i])
        if i < length(n.values) && length(n.values) > 1
            printstyled(io, "    ***\n", color = :blue)
        end
    end
    return
end

function Base.show(io::IO, n::T) where T <: PermutationParameter
    @printf io "    ["
    printstyled(io, "PermutationParameter", color = :blue)
    @printf io "]\n"

    printstyled(io, "    name", color = :blue)
    @printf io " : "
    printstyled(io, "$(n.name)\n", color = :bold)

    printstyled(io, "    value", color = :yellow)
    @printf io ": "
    show(io, n.value)
    @printf io "\n"

    printstyled(io, "    size", color = :green)
    @printf io " : %d\n" n.size
    return
end

function Base.show(io::IO, n::T) where T <: StringParameter
    @printf io "    ["
    printstyled(io, "StringParameter", color = :blue)
    @printf io "]\n"
    printstyled(io, "    name", color = :blue)
    @printf io " : "
    printstyled(io, "$(n.name)\n", color = :bold)
    printstyled(io, "    value", color = :yellow)
    @printf io ": \"%s\"\n" n.value
    return
end

function Base.show(io::IO, n::T) where T <: BoolParameter
    @printf io "    ["
    printstyled(io, "BoolParameter", color = :blue)
    @printf io "]\n"
    printstyled(io, "    name", color = :blue)
    @printf io " : "
    printstyled(io, "$(n.name)\n", color = :bold)
    printstyled(io, "    value", color = :yellow)
    @printf io ": %s\n" n.value
    return
end

function Base.show(io::IO, n::T) where T <: Configuration
    @printf io "  ["
    printstyled(io, "Configuration", color = :blue)
    @printf io "]\n"
    printstyled(io, "  name", color = :blue)
    @printf io "      : "
    printstyled(io, "$(n.name)\n", color = :bold)
    printstyled(io, "  parameters", color = :blue)
    @printf io ":\n"
    p = collect(keys(n.parameters))
    for i = 1:length(p)
        show(io, n[p[i]])
        if i < length(p) && length(p) > 1
            printstyled(io, "    ***\n", color = :blue)
        end
    end
    return
end

function Base.show(io::IO, n::T) where T <: Result
    if n.is_final
        @printf io "["
        printstyled(:bio, "Final Result", color = :blue)
        @printf io "]\n"
        printstyled(io, "Cost", color = :yellow)
        @printf io "                  : "
        printstyled(io, "$(n.cost_minimum)\n", color = :bold)
        printstyled(io, "Found in Iteration", color = :yellow)
        @printf io "    : "
        printstyled(io, "$(n.iterations)\n", color = :bold)
        printstyled(io, "Current Iteration", color = :blue)
        @printf io "     : "
        printstyled(io, "$(n.current_iteration)\n", color = :bold)
        printstyled(io, "Technique", color = :blue)
        @printf io "             : "
        printstyled(io, "$(n.technique)\n", color = :bold)
        printstyled(io, "Function Calls", color = :blue)
        @printf io "        : "
        printstyled(io, "$(n.cost_calls)\n", color = :bold)
        printstyled(io, "Starting Configuration", color = :blue)
        @printf io ":\n"
        show(io, n.start)
        printstyled(io, "Minimum Configuration", color = :blue)
        @printf io " :\n"
        show(io, n.minimum)
    else
        @printf io "["
        printstyled(io, "Result", color = :blue)
        @printf io "]\n"
        printstyled(io, "Cost", color = :yellow)
        @printf io "              : "
        printstyled(io, "$(n.cost_minimum)\n", color = :bold)
        printstyled(io, "Found in Iteration", color = :yellow)
        @printf io ": "
        printstyled(io, "$(n.iterations)\n", color = :bold)
        printstyled(io, "Current Iteration", color = :blue)
        @printf io " : "
        printstyled(io, "$(n.current_iteration)\n", color = :bold)
        printstyled(io, "Technique", color = :blue)
        @printf io "         : "
        printstyled(io, "$(n.technique)\n", color = :bold)
        printstyled(io, "Function Calls", color = :blue)
        @printf io "    : "
        printstyled(io, "$(n.cost_calls)\n", color = :bold)
        printstyled(io, "  ***\n", color = :blue)
    end
    return
end