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

# ╔═╡ 56571409-a81d-4772-98fd-e85e883aa4e4
using Plots, PlutoUI, BioCCP

# ╔═╡ 20ce43cd-7634-4c94-afdf-d243415525cb
md"                                                             $(@bind date DateField())"

# ╔═╡ 4d246460-af05-11eb-382b-590e60ba61f5
md"## Collecting Coupons in combinatorial biotechnology

This notebook provides functions and corresponding visualizations to determine expected minimum sample sizes for combinatorial biotechnology experiments, based on the mathematical framework of the Coupon Collector Problem (references see [^1], [^2]).

"

# ╔═╡ 6183795b-62a0-4ed4-b8f9-ea522da956e2
begin
	begin
		function tocsv(raw)
		t = string(raw)
		t = split(t, "[")[2]
		t = split(t, "]")[1]
		return t
		end
	end
	md""
end

# ╔═╡ a8c81622-194a-443a-891b-bfbabffccff1
begin

md"""

👇 **COMPLETE THE FIELDS BELOW** 👇\
	*First, fill in the input parameters of your problem setting. Then, click outside the text field to update the report.*"""
end

# ╔═╡ e1b554a6-db6c-4d2a-9dd3-0a35095f4d8c
Show(MIME"image/png"(), read("BioCCP_scheme.png"))

# ╔═╡ 36a09fff-8b14-4d91-84e0-9ecabefa810a

# ╔═╡ 40ac5be1-6fc2-4fbc-b0ca-a021266b2247
begin
	vec_n = [];
	md"""🔹 **№ modules in design space** (`n`):                       $(@bind n_string TextField(default = "100")) $(@bind help_n Button("❓"))"""
end

# ╔═╡ 7b05669c-7abe-42a7-838c-61c06b261256
begin
	help_n
	switch_n = rem(length(vec_n), 2)
	push!(vec_n, 1)
	if switch_n == 1
		md"""
	                                                       =   *How many different modules or building                                                    blocks are available to construct designs?*"""
	end
end

# ╔═╡ 123d5b94-5772-42dc-bf74-d964d023b209
begin
	vec_r = []
md"""
🔹 **Expected № modules per design** (`r`):                      $(@bind r NumberField(1:20)) $(@bind help_r Button("❓")) """

end

# ╔═╡ 30cb2ab3-ad67-405e-95a1-8feea223938a
begin
	help_r
	switch_r = rem(length(vec_r), 2)
	push!(vec_r, 1)
	if switch_r == 1
		md"""

                                                 =    *How many modules are combined in                                                   a single design, on average?*"""
	end
end

# ╔═╡ c8164a38-fcf9-4f1b-b697-46c8ce978fce
begin
	vec_m = []
md"""
**🔹 № times you want to observe each module** (`m`):              $(@bind m NumberField(1:20)) $(@bind help_m Button("❓")) """

end

# ╔═╡ a5c3153f-0946-4af8-871c-634a71e8b7f1
begin
	help_m
	switch_m = rem(length(vec_m), 2)
	push!(vec_m, 1)
	if switch_m == 1
		md"""
	                                                    = 	   *How many times do you want to                                                     observe each of the available modules in                                                               the total set of designs?*"""
	end
end

# ╔═╡ d6c6be55-fc94-480a-bc58-ca67b0c44568
begin
	vec_p = []
md"""🔹 **Abundances of modules during library generation** (`p`):     $(@bind ps Select(["Equal", "Unequal"], default = "Equal"))  $(@bind help_p Button("❓"))"""

end

# ╔═╡ b88fab57-9f78-4450-90af-62ab860620a0
begin
	help_p
	switch_p = rem(length(vec_p), 2)
	push!(vec_p, 1)
	if switch_p == 1
		md"""

			      =    *How are the abundances of the                                                                 modules distributed during combinatorial                                                              generation of designs?
	Is each module                                                                    equally likely to be included in a design?*"""
	end
end

# ╔═╡ 45507d48-d75d-41c9-a018-299e209f900e
begin
	vec_p_unequal = []
	n = parse(Int64, n_string);
	if ps == "Equal"
		distribution = "Equal"
	end
		if ps == "Unequal"
	md""" ↳     **Specify distribution**:
	      $(@bind distribution Select(["Bell curve", "Zipf's law", "Custom vector"], default = " "))"""
		end
end

# ╔═╡ a937e514-4c4a-4f76-b8e5-3c2031afd416
if ps == "Unequal"

md"""
    *If the exact module probabilities are known, choose "Custom vector".*
 *Otherwise, select:*
  - *"Zipf's law" (when you expect a small number of modules to occur quite often, and a very large number of modules to occur at the statistical equivalent of zero, but, they do occur.)*
  - *"Bell curve" (when you expect a large number of modules to occur at an average probability and a smaller number of modules to occur with a small or large probability)* """
end

# ╔═╡ b17f3b8a-61ee-4563-97cd-19ff049a8e1e
begin
	if distribution == "Zipf's law" || distribution == "Bell curve"
		md"""                          ↳     **Specify pₘₐₓ/pₘᵢₙ**:         $(@bind pmaxpmin_string TextField(default = "4"))                                                 =    *The ratio of the largest and smallest                                                   module probability*"""
			end

end

# ╔═╡ 2639e3fb-ccbb-44de-bd15-1c5dbf6c1539
begin
	if distribution == "Custom vector"
				md"""        **↳  Enter/load your custom abundances by changing the cell below 👇**"""
		end
end

# ╔═╡ 464b67be-2dad-4315-a144-0b475414366f
if distribution == "Custom vector"

	md"""                                            $(@bind abundances_str TextField((30, 10), 	default=join(string.(rand(200:1:400, n)), "\n")))
	                                           *Make sure the number of abundances is equal to n!*"""
end

# ╔═╡ 1220c75b-303c-4b0a-84c4-a12ee834a5af
begin
	function tonumbers(text)
		text = split(text, "\n")
		text = rstrip.(text)
		text = text[text .!= ""]
		text = parse.(Float64,text)
		return text
	end

	if distribution == "Custom vector"
		abundances = (tonumbers(abundances_str))
	end

	md""
end

# ╔═╡ f6ebf9fb-0a29-4cb4-a544-6c6e32bedcc4
md"""

🎯 **REPORT**  🎯

**💻 Module probabilities**                                                                                $(@bind show_modprobs Select(["🔻 SHOW ", "🔺 HIDE "], default="🔻 SHOW ") )  \
*How the abundances of the modules are distributed during combinatorial library generation.*
"""

# ╔═╡ b0291e05-776e-49ce-919f-4ad7de4070af
begin
	function p_power(n, k)
    	p = (1:n) .^ -k
    	return p ./ sum(p)
	end

	if ps == "Equal"

		p_vec = ones(n)./sum(ones(n));

	elseif ps == "Unequal"
		if distribution == "Bell curve"
			ratio = parse(Float64, pmaxpmin_string)
			ab1 = 1
			ab2 = ratio*ab1
			μ = (ab1+ab2)/2
			σ = (ab2-ab1)/6

			#create fixed distribution of abundances according to percentiles of bell curve
			n_perc_1 = Int(floor(n*0.34));
			n_perc_2 = Int(floor(n*0.135));
			n_perc_3 = Int(floor(n*0.0215));
			#n_perc_4 = Int(floor(n*0.0013));
			n_perc_rest = n - 2*n_perc_1 - 2*n_perc_2 - 2*n_perc_3 ;
			p_vec_unnorm = vcat(fill(μ,2*n_perc_1+n_perc_rest), fill(μ+1.5*σ, n_perc_2), fill(μ-1.5*σ, n_perc_2), fill(μ+3*σ, n_perc_3), fill(μ-3*σ, n_perc_3) )

			# normalize sum to 1
			p_vec = sort(p_vec_unnorm ./ sum(p_vec_unnorm))
		end

		if distribution == "Custom vector"
			p_vec_unnorm = abundances
			p_vec = abundances ./ sum(abundances)
		end

		if distribution == "Zipf's law"
			ratio = parse(Float64, pmaxpmin_string)
			p_vec = p_power(n, log(ratio)/log(n))
			p_vec = p_vec ./ sum(p_vec)
		end
	end

	if show_modprobs == "🔻 SHOW "

	scatter(p_vec, title = "Probability mass function", ylabel = "module probability pⱼ", xlabel = "module j", label="", size = (700, 400))
	ylims!((0,2*maximum(p_vec)), titlefont=font(10), xguidefont=font(9), yguidefont=font(9))

	end
end

# ╔═╡ 87c3f5cd-79bf-4ad8-b7f8-3e98ec548a9f
begin
	if show_modprobs == "🔻 SHOW "  && distribution == "Bell curve"
		histogram(p_vec, normalize=:probability,  bar_edges=false,  size = (550, 320), orientation=:v, bins=[(μ -  3*σ)/sum(p_vec_unnorm), (μ - 2*σ)/sum(p_vec_unnorm), (μ-σ)/sum(p_vec_unnorm), (μ + σ)/sum(p_vec_unnorm), (μ + 2*σ)/sum(p_vec_unnorm), (μ +  3.2*σ)/sum(p_vec_unnorm)], titlefont=font(10), xguidefont=font(9), yguidefont=font(9), label="")
		# if distribution == "Normally distributed"
		# 	plot!(x->pdf(Normal(μ, σ), x), xlim=xlims())
		# 	xlabel!("Abundance"); ylabel!("probability"); title!("Distribution of module abundances")
		# end
		xlabel!("Probability"); ylabel!("Relative frequency"); title!("Distribution of module probabilities")
	end
end

# ╔═╡ 2313198e-3ac9-407b-b0d6-b79e02cefe35
begin
	if show_modprobs == "🔻 SHOW "  && distribution == "Bell curve"
md"""For $n_string modules of which the probabilities form a bell curve with ratio pₘₐₓ/pₘᵢₙ = $pmaxpmin_string , we follow the percentiles of a normal distribution to generate the probability vector.

We consider μ to be the mean module probability and σ to be the standard deviation of the module probabilities.

According to the percentiles
- 68% of the module probabilities lies in the interval [μ - σ, μ + σ],
- 95% of falls into the range [μ - 2σ, μ + 2σ] and
- 99.7% lies in [μ - 3σ, μ +3σ].

We use the ratio pₘₐₓ/pₘᵢₙ to fix the width of the interval [μ - 3σ, μ +3σ]. (We assume that pₘₐₓ = μ +3σ and pₘᵢₙ = μ - 3σ and calculate μ and σ from this assumption). In addition, we make sure the sum of the probability vector sums up to 1.

As a result, we get:
-  $(n_perc_1+n_perc_rest) modules with a probability of $(µ/sum(p_vec_unnorm))
-  $(n_perc_2)  modules with a probability of $((μ+1.5*σ)/sum(p_vec_unnorm))
-  $(n_perc_2)  modules with a probability of $((μ-1.5*σ)/sum(p_vec_unnorm))
-  $(n_perc_3)  modules with a probability of $((μ+2.5*σ)/sum(p_vec_unnorm))
-  $(n_perc_3)  modules with a probability of $((μ-2.5*σ)/sum(p_vec_unnorm))"""
	end
end

# ╔═╡ f098570d-799b-47e2-b692-476a4d95825b
if show_modprobs == "🔻 SHOW "
md"Each biological design in the design space is built by choosing $r module(s) (with replacement) out of a set of $n_string modules according to the module probabilities visualized above."
end

# ╔═╡ 85bfc3d5-447d-4078-af14-e3f369adfa71

# ╔═╡ caf67b2f-cc2f-4d0d-b619-6e1969fabc1a
md""" **💻 Expected minimum sample size**                                                                                $(@bind show_E Select(["🔻 SHOW ", "🔺 HIDE "], default="🔺 SHOW "))
\
*The expected minimum number of designs to observe each module at least $m time(s) in the sampled set of designs.* """

# ╔═╡ 6f14a72c-51d3-4759-bb8b-10db1dc260f0
begin
	if show_E == "🔻 SHOW "
		E = Int(ceil(expectation_minsamplesize(n; p = p_vec, m=m, r = r)))
		sd = Int(ceil(std_minsamplesize(n; p = p_vec, m=m, r = r)))

			md"""
     `Expected minimum sample size    `     = **$E designs**\

     `Standard deviation              `                = **$sd designs**  	"""
	end

end

# ╔═╡ f1e180e5-82a7-4fab-b894-75be4627af5d

# ╔═╡ 22fe8006-0e81-4e0a-a460-28610a55cd97
md""" **💻 Success probability**                                                                                $(@bind show_success Select(["🔻 SHOW ", "🔺 HIDE "], default="🔻 SHOW ") )\

*The probability that the minimum number of designs T is smaller than or equal to a given sample size t.* """

# ╔═╡ db4371e4-7f86-4db3-b076-12f6cd220b89
begin
	if show_success == "🔻 SHOW "
		sample_size_95 = 1
		while 0.95 - success_probability(n, sample_size_95;	p = p_vec,
				r = r, m = m) > 0.00005
			global sample_size_95 += Int(ceil(n/10))
		end
		md"""    👉 Enter your sample size of interest: $(@bind sample_size_1_string TextField(default=string(sample_size_95)))"""

	end
	#genereer tabel + download knop
end

# ╔═╡ 317995ed-bdf4-4f78-bd66-a39ffd1dc452
begin
	if show_success == "🔻 SHOW "
	sample_size_1 = parse(Int64, sample_size_1_string);
	p_success = Float64(success_probability(n, sample_size_1; p = p_vec, m = m, r = r))

	md"""
              ↳ `Success probability F(t)`  = **$p_success**\
	"""
	end
end

# ╔═╡ 3039ac2b-656e-4c2b-9036-cb1d9cdc0790

# ╔═╡ ca5a4cef-df67-4a5e-8a86-75a9fe8c6f37
if show_success == "🔻 SHOW "
	md"*A curve describing the success probability in function of sample size.*"
end

# ╔═╡ 9616af0e-810c-4e6a-bc67-cb70e5e620f5

# ╔═╡ 24f7aae7-d37a-4db5-ace0-c910b178da88
begin
if show_success == "🔻 SHOW "

sample_size_initial = 5
	while (1 - success_probability(n, sample_size_initial; p = p_vec, r = r, m = m)) > 0.0005
		global sample_size_initial += Int(ceil(n/10))
	end

	sample_sizes = Int.(ceil.(0:n/10:sample_size_initial))
	successes = success_probability.(n, sample_sizes; p = p_vec, r = r, m = m)
plot(sample_sizes, successes, title = "Success probability in function of sample size",
			xlabel = "sample size s",
			ylabel="P(minimum sample size <= s)", label = "", legend=:bottomright, size=(600,400), seriestype=:scatter, titlefont=font(10),xguidefont=font(9), yguidefont=font(9))
		end

end

# ╔═╡ 4902d817-3967-45cd-a283-b2872cf1b49c
if show_success == "🔻 SHOW "
	DownloadButton(string("sample_size,", tocsv(sample_sizes), "\n", "success_probability,", tocsv(successes)), "successprobability_$date.csv")
end

# ╔═╡ 37f951ee-885c-4bbe-a05f-7c5e48ff4b6b
begin
	#following one-sided version of Chebyshev's inequality.

	function chebyshev_onesided_larger(X, μ, σ)
		X_μ = X - μ
		return σ^2 / (σ^2 + X_μ^2)
	end
	function chebyshev_onesided_smaller(X, μ, σ)
		X_μ = μ - X
		return σ^2 / (σ^2 + X_μ^2)
	end
if show_success == "🔻 SHOW "
if sample_size_1 < E
	compare = "smaller"
		if sample_size_1 <= n/r
			print_sentence = "P(minimum sample size ≤ $sample_size_1) = 0."
		else
	prob_chebyshev = chebyshev_onesided_smaller(sample_size_1, E, sd)
	print_sentence = "P(minimum sample size ≤ $sample_size_1) ≤ $prob_chebyshev. "
		end

elseif sample_size_1 > E
	compare = "greater"
	prob_chebyshev = chebyshev_onesided_larger(sample_size_1, E, sd)
	print_sentence = "P(minimum sample size ≥ $sample_size_1) ≤ $prob_chebyshev. "

	elseif sample_size_1==E
		print_sentence = "P(minimum sample size ≤ $sample_size_1 OR minimum sample size ≥ $sample_size_1) ≤ 1."

end

	md"""*Upper bound on probability that minimum sample size is smaller than given sample size t, according to Chebychev's inequality.*:

	                            $print_sentence"""
	end
end

# ╔═╡ 702b158b-4f1c-453f-9e70-c00ec22226c3

# ╔═╡ dc696281-7a5b-4568-a4c2-8dde90af43f0
md""" **💻 Expected observed fraction of the total number of modules**                $(@bind show_satur Select(["🔻 SHOW ", "🔺 HIDE "], default="🔺 HIDE "))\
*The fraction of the total number of available modules that is expected to be observed after collecting a given number of designs.*"""

# ╔═╡ eb92ff7c-0140-468c-8b32-f15d1cf15913
if show_satur == "🔻 SHOW "
		md"""
   👉 Enter your sample size of interest: $(@bind sample_size_2_string TextField(default="50")) """
end

# ╔═╡ f0eaf96b-0bc0-4194-9a36-886cb1d66e00
begin
	if show_satur == "🔻 SHOW "
	sample_size_2 = Int(ceil(parse(Int64, sample_size_2_string)))
	E_fraction = expectation_fraction_collected(n, sample_size_2; p = p_vec, r = r)

	md""" 	            ↳ `Expected fraction observed`	= **$E_fraction**
	"""
	end
end

# ╔═╡ 8ce0d3d7-8081-4d08-9189-595e3dc1814f

# ╔═╡ 0099145a-5460-4549-9513-054bc1b04eea
if  show_satur == "🔻 SHOW "
md""" *A curve describing the expected fraction of modules observed in function of sample size.* """
	end

# ╔═╡ 7968de5e-5ae8-4ab4-b089-c3d33475af2f
begin
	if show_satur == "🔻 SHOW "
global sample_size_initial_frac = 5
		while (1 - expectation_fraction_collected(n, sample_size_initial_frac; p = p_vec, r = r)) > 0.0005
		global	 sample_size_initial_frac += Int(ceil(n/10))
		end

	sample_sizes_frac = Int.(ceil.(collect(0 : n/10 : sample_size_initial_frac)))

	fracs = expectation_fraction_collected.(n, sample_sizes_frac; p = p_vec, r = r)

	plot(sample_sizes_frac, fracs, title = "Expected observed fraction of the total number of modules",
	    xlabel = "sample size", seriestype=:scatter,
	    ylabel= "Expected observed fraction", label = "", size=(700,400), xguidefont=font(9), yguidefont=font(9), titlefont=font(10))
end
end

# ╔═╡ 0b95ccff-4c7b-400d-be61-8ea056ccc87f
if show_satur == "🔻 SHOW "
	DownloadButton(string("sample_size,", tocsv(sample_sizes_frac), "\n", "expected_observed_fraction,", tocsv(fracs)), "expectedobservedfraction_$date.csv")
end

# ╔═╡ 09fb9f1d-16e4-447c-a5c0-d153cec22665

# ╔═╡ f92a6b6e-a556-45cb-a1ae-9f5fe791ffd2
md""" **💻 Occurrence of a specific module**                                                                                $(@bind show_occ Select(["🔻 SHOW ", "🔺 HIDE "], default="🔺 HIDE "))\
*How many times one can expect to have collected a specific module in a sample of a given size.*"""

# ╔═╡ ec2a065f-0dc7-44d4-a18b-6c6a228b3ffc
if show_occ == "🔻 SHOW " && distribution != "Zipf's law"
	md"""    👉 Enter the probability of the module of interest: $(@bind p_string TextField(default="0.005"))\
	"""

end

# ╔═╡ 0e39a993-bb2f-4897-bfe2-5128ec62bef9
if show_occ == "🔻 SHOW " && distribution == "Zipf's law"
	md"""    👉 Enter the rank of the module of interest:        $(@bind rank_string TextField(default="5"))\
	"""
end

# ╔═╡ f3329934-d69b-48a0-9cf1-e9a5920cf414
if show_occ == "🔻 SHOW "
	 md"""   👉 Enter the sample size of interest:                $(@bind sample_size_3_string TextField(default="500"))"""
end

# ╔═╡ a041652b-365e-4594-9c48-c63d547b3295

# ╔═╡ 6acb0a97-6469-499f-a5cf-6335d6aa909a
begin

if show_occ == "🔻 SHOW "
	sample_size_3 = parse(Int64, sample_size_3_string)

	if distribution != "Zipf's law"
		pᵢ = parse(Float64, p_string)
		ed = Int(floor(sample_size_3*pᵢ*r))
		j = collect(0:1:3*ed)
		x  = prob_occurrence_module.(pᵢ, sample_size_3, r, j)

		plot(j,x, seriestype=[:scatter, :line], xlabel="occurrences in sample",
				ylabel="probability",
				title="Probability on number of occurrences for specific module
(for sample size = $sample_size_3)",
				size=((600,300)), label="",titlefont=font(10),
				xguidefont=font(9), yguidefont=font(9))

	else
		rank = parse(Int64, rank_string)
		pᵢ = p_vec[rank]
		ed = Int(floor(sample_size_3*pᵢ*r))
		j = collect(0:1:3*ed)
		x  = prob_occurrence_module.(pᵢ, sample_size_3, r, j)

		plot(j,x, seriestype=[:scatter, :line], xlabel="occurrences in sample",
				ylabel="probabilityS",
				title="Probability on number of occurrences for specific module
(for sample size = $sample_size_3)",
				size=((600,300)), label="",titlefont=font(10),
				xguidefont=font(9), yguidefont=font(9))

	end

	end
end

# ╔═╡ 595423df-728b-43b1-ade4-176785c54be3
begin
	if show_occ == "🔻 SHOW "

	md""" 	            ↳ `Expected number of times observed`	≈ **$ed**
		"""
	end
end

# ╔═╡ a2bf1368-20a9-42cd-af58-67397644d725
if show_occ == "🔻 SHOW "
	DownloadButton(string("number_of_occurence,", tocsv(j), "\n", "probability,", tocsv(x)), "occurrencemodule_$date.csv")
end

# ╔═╡ fbffaab6-3154-49df-a226-d5810d0b7c38
md"""## References"""

# ╔═╡ 1f48143a-2152-4bb9-a765-a25e70c281a3
md"""[^1]:  Doumas, A. V., & Papanicolaou, V. G. (2016). *The coupon collector’s problem revisited: generalizing the double Dixie cup problem of Newman and Shepp.* ESAIM: Probability and Statistics, 20, 367-399.

[^2]: Boneh, A., & Hofri, M. (1997). *The coupon-collector problem revisited—a survey of engineering problems and computational methods.* Stochastic Models, 13(1), 39-66.

"""

# ╔═╡ 00000000-0000-0000-0000-000000000001
PLUTO_PROJECT_TOML_CONTENTS = """
[deps]
BioCCP = "79e6b149-e254-49fe-a721-3c4960de1574"
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
PlutoUI = "7f904dfe-b85e-4ff6-b463-dae2292396a8"

[compat]
BioCCP = "~0.1.1"
Plots = "~1.22.4"
PlutoUI = "~0.7.16"
"""

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

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

[[ArgTools]]
git-tree-sha1 = "bdf73eec6a88885256f282d48eafcad25d7de494"
uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f"
version = "1.1.1"

[[Artifacts]]
deps = ["Pkg"]
git-tree-sha1 = "c30985d8821e0cd73870b17b0ed0ce6dc44cb744"
uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33"
version = "1.3.0"

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

[[BioCCP]]
deps = ["Distributions"]
git-tree-sha1 = "4293935cfb1576a783192e81d965addff4d1b47a"
uuid = "79e6b149-e254-49fe-a721-3c4960de1574"
version = "0.1.1"

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

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

[[ChainRulesCore]]
deps = ["Compat", "LinearAlgebra", "SparseArrays"]
git-tree-sha1 = "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"

[[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", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "8e695f735fca77e9708e795eda62afdb869cbb70"
uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae"
version = "0.3.4+0"

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

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

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

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

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

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

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

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

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

[[Downloads]]
deps = ["ArgTools", "LibCURL", "NetworkOptions"]
git-tree-sha1 = "135bf1896be424235eadb17474b2a78331567f08"
uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6"
version = "1.5.1"

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

[[Expat_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "1402e52fcda25064f51c77a9655ce8680b76acf0"
uuid = "2e619515-83b5-522b-bb60-26c02a35a201"
version = "2.2.7+6"

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

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

[[FillArrays]]
deps = ["LinearAlgebra", "Random", "SparseArrays", "Statistics"]
git-tree-sha1 = "8756f9935b7ccc9064c6eef0bff0ad643df733a3"
uuid = "1a297f60-69ca-5386-bcde-b61e274b549b"
version = "0.12.7"

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

[[Fontconfig_jll]]
deps = ["Artifacts", "Bzip2_jll", "Expat_jll", "FreeType2_jll", "JLLWrappers", "Libdl", "Libuuid_jll", "Pkg", "Zlib_jll"]
git-tree-sha1 = "35895cf184ceaab11fd778b4590144034a167a2f"
uuid = "a3f928ae-7b40-5064-980b-68af3947d34b"
version = "2.13.1+14"

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

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

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

[[GLFW_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Libglvnd_jll", "Pkg", "Xorg_libXcursor_jll", "Xorg_libXi_jll", "Xorg_libXinerama_jll", "Xorg_libXrandr_jll"]
git-tree-sha1 = "a199aefead29c3c2638c3571a9993b564109d45a"
uuid = "0656b61e-2033-5cc2-a64a-77c0f6c09b89"
version = "3.3.4+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 = "d59e8320c2747553788e4fc42231489cc602fa50"
uuid = "d2c73de3-f751-5644-a686-071e5b155ba9"
version = "0.58.1+0"

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

[[Gettext_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Libiconv_jll", "Pkg", "XML2_jll"]
git-tree-sha1 = "8c14294a079216000a0bdca5ec5a447f073ddc9d"
uuid = "78b55507-aeef-58d4-861c-77aaff3498b1"
version = "0.20.1+7"

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

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

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

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

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

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

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

[[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 = "9aff0587d9603ea0de2c6f6300d9f9492bbefbd3"
uuid = "aacddb02-875f-59d6-b918-886e6ef4fbf8"
version = "2.0.1+3"

[[LAME_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "df381151e871f41ee86cee4f5f6fd598b8a68826"
uuid = "c1c5ebd0-6772-5130-a774-d5fcae4a789d"
version = "3.100.0+3"

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

[[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"]
git-tree-sha1 = "cdbe7465ab7b52358804713a53c7fe1dac3f8a3f"
uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21"
version = "0.6.3"

[[LibCURL_jll]]
deps = ["LibSSH2_jll", "Libdl", "MbedTLS_jll", "Pkg", "Zlib_jll", "nghttp2_jll"]
git-tree-sha1 = "897d962c20031e6012bba7b3dcb7a667170dad17"
uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0"
version = "7.70.0+2"

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

[[LibSSH2_jll]]
deps = ["Libdl", "MbedTLS_jll", "Pkg"]
git-tree-sha1 = "717705533148132e5466f2924b9a3657b16158e8"
uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8"
version = "1.9.0+3"

[[LibVPX_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "85fcc80c3052be96619affa2fe2e6d2da3908e11"
uuid = "dd192d2f-8180-539f-9fb4-cc70b1dcf69a"
version = "1.9.0+1"

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

[[Libffi_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "a2cd088a88c0d37eef7d209fd3d8712febce0d90"
uuid = "e9f186c6-92d2-5b65-8a66-fee21dc1b490"
version = "3.2.1+4"

[[Libgcrypt_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgpg_error_jll", "Pkg"]
git-tree-sha1 = "b391a18ab1170a2e568f9fb8d83bc7c780cb9999"
uuid = "d4300ac3-e22c-5743-9152-c294e39db1e4"
version = "1.8.5+4"

[[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 = "ec7f2e8ad5c9fa99fc773376cdbc86d9a5a23cb7"
uuid = "7add5ba3-2f88-524e-9cd5-f83b8a55f7b8"
version = "1.36.0+3"

[[Libiconv_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "cba7b560fcc00f8cd770fa85a498cbc1d63ff618"
uuid = "94ce4f54-9a6c-5748-9c1c-f9c7231a4531"
version = "1.16.0+8"

[[Libmount_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "51ad0c01c94c1ce48d5cad629425035ad030bfd5"
uuid = "4b2f31a3-9ecc-558c-b454-b3730dcb73e9"
version = "2.34.0+3"

[[Libtiff_jll]]
deps = ["Artifacts", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Pkg", "Zlib_jll", "Zstd_jll"]
git-tree-sha1 = "291dd857901f94d683973cdf679984cdf73b56d0"
uuid = "89763e89-9b03-5906-acba-b20f662cd828"
version = "4.1.0+2"

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

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

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

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

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

[[MbedTLS_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "0eef589dd1c26a3ac9d753fe1a8bcad63f956fa6"
uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1"
version = "2.16.8+1"

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

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

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

[[MozillaCACerts_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "f1662575f7bf53c73c2bbc763bace4b024de822c"
uuid = "14a3606d-f60d-562e-9121-12d972cd8159"
version = "2021.1.19+0"

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

[[NetworkOptions]]
git-tree-sha1 = "ed3157f48a05543cce9b241e1f2815f7e843d96e"
uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908"
version = "1.2.0"

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

[[OpenLibm_jll]]
deps = ["Libdl", "Pkg"]
git-tree-sha1 = "d22054f66695fe580009c09e765175cbf7f13031"
uuid = "05823500-19ac-5b8b-9628-191a04bc5112"
version = "0.7.1+0"

[[OpenSSL_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "71bbbc616a1d710879f5a1021bcba65ffba6ce58"
uuid = "458c3c95-2e84-50aa-8efc-19380b2a3a95"
version = "1.1.1+6"

[[OpenSpecFun_jll]]
deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "9db77584158d0ab52307f8c04f8e7c08ca76b5b3"
uuid = "efe28fd5-8261-553b-a9e1-b2916fc3738e"
version = "0.5.3+4"

[[Opus_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "f9d57f4126c39565e05a2b0264df99f497fc6f37"
uuid = "91d4177d-7536-5919-b921-800302f37372"
version = "1.3.1+3"

[[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 = "1b556ad51dceefdbf30e86ffa8f528b73c7df2bb"
uuid = "2f80f16e-611a-54ab-bc61-aa92de5b98fc"
version = "8.42.0+4"

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

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

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

[[Pkg]]
deps = ["Dates", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "UUIDs"]
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", "Hyperscript", "HypertextLiteral", "IOCapture", "InteractiveUtils", "JSON", "Logging", "Markdown", "Random", "Reexport", "UUIDs"]
git-tree-sha1 = "4c8a7d080daca18545c56f1cac28710c362478f3"
uuid = "7f904dfe-b85e-4ff6-b463-dae2292396a8"
version = "0.7.16"

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

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

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

[[QuadGK]]
deps = ["DataStructures", "LinearAlgebra"]
git-tree-sha1 = "78aadffb3efd2155af139781b8a8df1ef279ea39"
uuid = "1fd47b50-473d-5c70-9696-f719f8f3bcdc"
version = "2.4.2"

[[REPL]]
deps = ["InteractiveUtils", "Markdown", "Sockets"]
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"

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

[[Rmath_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "1b7bf41258f6c5c9c31df8c1ba34c1fc88674957"
uuid = "f50d1b31-88e8-58de-be2c-1cc44531875f"
version = "0.2.2+2"

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

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

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

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

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

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

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

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

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

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

[[StatsFuns]]
deps = ["ChainRulesCore", "IrrationalConstants", "LogExpFunctions", "Reexport", "Rmath", "SpecialFunctions"]
git-tree-sha1 = "95072ef1a22b057b1e80f73c2a89ad238ae4cfff"
uuid = "4c63d2b9-4356-54db-8cca-17b64c39e42c"
version = "0.9.12"

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

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

[[TOML]]
deps = ["Dates"]
git-tree-sha1 = "44aaac2d2aec4a850302f9aa69127c74f0c3787e"
uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76"
version = "1.0.3"

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

[[Test]]
deps = ["Distributed", "InteractiveUtils", "Logging", "Random"]
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 = "dc643a9b774da1c2781413fd7b6dcd2c56bb8056"
uuid = "a2964d1f-97da-50d4-b82a-358c7fce9d89"
version = "1.17.0+4"

[[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 = "be0db24f70aae7e2b89f2f3092e93b8606d659a6"
uuid = "02c8fc9c-b97f-50b9-bbe4-9be30ff0a78a"
version = "2.9.10+3"

[[XSLT_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgcrypt_jll", "Libgpg_error_jll", "Pkg", "XML2_jll"]
git-tree-sha1 = "2b3eac39df218762d2d005702d601cd44c997497"
uuid = "aed1982a-8fda-507f-9586-7b0439959a61"
version = "1.1.33+4"

[[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 = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "320228915c8debb12cb434c59057290f0834dbf6"
uuid = "83775a58-1f1d-513f-b197-d71354ab007a"
version = "1.2.11+18"

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

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

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

[[libpng_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"]
git-tree-sha1 = "6abbc424248097d69c0c87ba50fcb0753f93e0ee"
uuid = "b53b4c65-9356-5827-b1ea-8c7a1a84506f"
version = "1.6.37+6"

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

[[nghttp2_jll]]
deps = ["Libdl", "Pkg"]
git-tree-sha1 = "8e2c44ab4d49ad9518f359ed8b62f83ba8beede4"
uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d"
version = "1.40.0+2"

[[x264_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "d713c1ce4deac133e3334ee12f4adff07f81778f"
uuid = "1270edf5-f2f9-52d2-97e9-ab00b5d0237a"
version = "2020.7.14+2"

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

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

# ╔═╡ Cell order:
# ╠═56571409-a81d-4772-98fd-e85e883aa4e4
# ╟─20ce43cd-7634-4c94-afdf-d243415525cb
# ╟─4d246460-af05-11eb-382b-590e60ba61f5
# ╟─6183795b-62a0-4ed4-b8f9-ea522da956e2
# ╟─a8c81622-194a-443a-891b-bfbabffccff1
# ╟─e1b554a6-db6c-4d2a-9dd3-0a35095f4d8c
# ╟─36a09fff-8b14-4d91-84e0-9ecabefa810a
# ╟─40ac5be1-6fc2-4fbc-b0ca-a021266b2247
# ╟─7b05669c-7abe-42a7-838c-61c06b261256
# ╟─123d5b94-5772-42dc-bf74-d964d023b209
# ╟─30cb2ab3-ad67-405e-95a1-8feea223938a
# ╟─c8164a38-fcf9-4f1b-b697-46c8ce978fce
# ╟─a5c3153f-0946-4af8-871c-634a71e8b7f1
# ╟─d6c6be55-fc94-480a-bc58-ca67b0c44568
# ╟─b88fab57-9f78-4450-90af-62ab860620a0
# ╟─45507d48-d75d-41c9-a018-299e209f900e
# ╟─a937e514-4c4a-4f76-b8e5-3c2031afd416
# ╟─b17f3b8a-61ee-4563-97cd-19ff049a8e1e
# ╟─2639e3fb-ccbb-44de-bd15-1c5dbf6c1539
# ╟─464b67be-2dad-4315-a144-0b475414366f
# ╟─1220c75b-303c-4b0a-84c4-a12ee834a5af
# ╟─f6ebf9fb-0a29-4cb4-a544-6c6e32bedcc4
# ╟─87c3f5cd-79bf-4ad8-b7f8-3e98ec548a9f
# ╟─2313198e-3ac9-407b-b0d6-b79e02cefe35
# ╟─b0291e05-776e-49ce-919f-4ad7de4070af
# ╟─f098570d-799b-47e2-b692-476a4d95825b
# ╟─85bfc3d5-447d-4078-af14-e3f369adfa71
# ╟─caf67b2f-cc2f-4d0d-b619-6e1969fabc1a
# ╟─6f14a72c-51d3-4759-bb8b-10db1dc260f0
# ╟─f1e180e5-82a7-4fab-b894-75be4627af5d
# ╟─22fe8006-0e81-4e0a-a460-28610a55cd97
# ╟─db4371e4-7f86-4db3-b076-12f6cd220b89
# ╟─317995ed-bdf4-4f78-bd66-a39ffd1dc452
# ╟─3039ac2b-656e-4c2b-9036-cb1d9cdc0790
# ╟─ca5a4cef-df67-4a5e-8a86-75a9fe8c6f37
# ╟─9616af0e-810c-4e6a-bc67-cb70e5e620f5
# ╟─24f7aae7-d37a-4db5-ace0-c910b178da88
# ╟─4902d817-3967-45cd-a283-b2872cf1b49c
# ╟─37f951ee-885c-4bbe-a05f-7c5e48ff4b6b
# ╟─702b158b-4f1c-453f-9e70-c00ec22226c3
# ╟─dc696281-7a5b-4568-a4c2-8dde90af43f0
# ╟─eb92ff7c-0140-468c-8b32-f15d1cf15913
# ╟─f0eaf96b-0bc0-4194-9a36-886cb1d66e00
# ╟─8ce0d3d7-8081-4d08-9189-595e3dc1814f
# ╟─0099145a-5460-4549-9513-054bc1b04eea
# ╟─7968de5e-5ae8-4ab4-b089-c3d33475af2f
# ╟─0b95ccff-4c7b-400d-be61-8ea056ccc87f
# ╟─09fb9f1d-16e4-447c-a5c0-d153cec22665
# ╟─f92a6b6e-a556-45cb-a1ae-9f5fe791ffd2
# ╟─ec2a065f-0dc7-44d4-a18b-6c6a228b3ffc
# ╟─0e39a993-bb2f-4897-bfe2-5128ec62bef9
# ╟─f3329934-d69b-48a0-9cf1-e9a5920cf414
# ╟─a041652b-365e-4594-9c48-c63d547b3295
# ╟─6acb0a97-6469-499f-a5cf-6335d6aa909a
# ╟─595423df-728b-43b1-ade4-176785c54be3
# ╟─a2bf1368-20a9-42cd-af58-67397644d725
# ╟─fbffaab6-3154-49df-a226-d5810d0b7c38
# ╟─1f48143a-2152-4bb9-a765-a25e70c281a3
# ╟─00000000-0000-0000-0000-000000000001
# ╟─00000000-0000-0000-0000-000000000002

max_issues_repo_name: kirstvh/BioCCP

max_forks_repo_name: kirstvh/BioCCP


text:
using SurfaceGeometry

using JLD
data = load("sphere.jld")
points = data["points"]
faces = data["faces"]

function velocity(points::Array{Float64,2},faces::Array{Int,2},t::Float64) ### Iterator also can be fit as an argument
    v = Array(Float64,size(points)...)
    for xkey in 1:size(points,2)
        v[:,xkey] = velocity(points[:,xkey],t)
    end
    return v
end

function velocity(pos,t)
    x,y,z = pos

    x = x*0.15 + 0.35
    y = y*0.15 + 0.35
    z = z*0.15 + 0.35

    u = 2*sin(pi*x)^2 * sin(2*pi*y) * sin(2*pi*z) * sin(2/3*pi*t)
    v = - sin(2*pi*x) * sin(pi*y)^2 * sin(2*pi*z) * sin(2/3*pi*t)
    w = - sin(2*pi*x) * sin(2*pi*y) * sin(pi*z)^2 * sin(2/3*pi*t)

    [u,v,w]  # /0.15
end

ti = 1.
v = velocity(points,faces,ti)

FBDS = FaceBasedDS(faces)
n = Array(Float64,size(points)...)
for i in 1:size(points,2)
    #iter = FaceVRing(i,faces)
    iter = FaceVRing(i,FBDS)
    n[:,i] = vnormal(i,points,faces,iter)
    v[:,i] = dot(n[:,i],v[:,i])*n[:,i]
end

#res = stabilise(points,faces,n,v)
res = stabilise(points,faces,n,v;method=:Zinchenko2013)

ext: jl


text:
# CASE expression.

mutable struct CaseClause <: AbstractSQLClause
    args::Vector{SQLClause}

    CaseClause(; args) =
        new(args)
end

CaseClause(arg1, arg2, args...) =
    CaseClause(args = SQLClause[arg1, arg2, args...])

"""
    CASE(; args)
    CASE(args...)

A `CASE` expression.

# Examples

```jldoctest
julia> c = CASE(OP("<", :year_of_birth, 1970), "boomer", "millenial");

julia> print(render(c))
(CASE WHEN ("year_of_birth" < 1970) THEN 'boomer' ELSE 'millenial' END)
```
"""
CASE(args...; kws...) =
    CaseClause(args...; kws...) |> SQLClause

dissect(scr::Symbol, ::typeof(CASE), pats::Vector{Any}) =
    dissect(scr, CaseClause, pats)

function PrettyPrinting.quoteof(c::CaseClause, ctx::QuoteContext)
    ex = Expr(:call, nameof(CASE))
    if length(c.args) < 2
        push!(ex.args, Expr(:kw, :args, Expr(:vect, quoteof(c.args, ctx)...)))
    else
        append!(ex.args, quoteof(c.args, ctx))
    end
    ex
end


text:
using Random
using Images, CoordinateTransformations

#taken from
#https://github.com/FluxML/Metalhead.jl/blob/b3c09e58ffa907ab1ef8946a6f9e45858bea0a93/src/utils.jl#L28
function preprocess(img)
    img = channelview(imresize(img, (224, 224))) .* 255
    img .-= [123.68, 116.779, 103.939]
    img = permutedims(img, (3,2,1))
end

function copyimg(img)
    # to reset index of imageview
    imresize(img,size(img))
end

struct Dataset
    len::Int
    data::Array{Tuple{String,Int64},1}
    augment::Bool
    image_cache::Dict{Int,Array{RGB{Normed{UInt8,8}},2}}
    function Dataset(data; train=true)
        augment=train
        cache = Dict{Int,Array{RGB{Normed{UInt8,8}},2}}()
        new(length(data), data, augment, cache)
    end
end

function get_example(dataset::Dataset, i::Int)
    path, label = dataset.data[i]
    if haskey(dataset.image_cache, i)
        img = dataset.image_cache[i]
    else
        img = load(path)
        dataset.image_cache[i]=img
    end
    img = copyimg(img)
    if dataset.augment
        tfm = LinearMap(RotMatrix(-pi/2 * rand([0,1,2,3])))
        img = warp(img, tfm)
        domirror = rand([true,false])
        if domirror
            img = channelview(copyimg(img))
            newimg = zeros(eltype(img), size(img))
            x_range = size(img)[3]
            for i in 1:x_range
                newimg[:,:,x_range - i + 1] = img[:,:,i]
            end
            img = colorview(RGB, newimg)
        end
    end
    img = preprocess(img)
    return img, label
end

Base.length(dataset::Dataset) = dataset.len

max_stars_repo_name: terasakisatoshi/juliaExer

max_issues_repo_name: terasakisatoshi/juliaExer

max_forks_repo_name: terasakisatoshi/juliaExer


text:
using Distributed
addprocs(2)
@everywhere using MBSinBLG
# using MKL
# BLAS.set_num_threads(2)
@everywhere using MKL, SharedArrays
@everywhere using Quantica, StaticArrays, Parameters, LinearAlgebra, StatsBase
@everywhere using Baselet, Arpack, StaticArrays, CSV, DataFrames, Dates
@everywhere using Optim, ProgressMeter, Distributed
@everywhere using PhysicalConstants.CODATA2018: ustrip, @u_str, ħ, k_B, m_e, e, μ_B

using MBSinBLG, Quantica, StaticArrays, Parameters, LinearAlgebra, StatsBase, SharedArrays
using Baselet, Arpack, StaticArrays, CSV, DataFrames, Dates
using PhysicalConstants.CODATA2018: ustrip, @u_str, ħ, k_B, m_e, e, μ_B
using Optim, ProgressMeter

max_stars_repo_path: src/using Distributed.jl

max_stars_repo_name: fernandopenaranda/MBSinBLG

max_issues_repo_path: src/using Distributed.jl

max_issues_repo_name: fernandopenaranda/MBSinBLG

max_forks_repo_path: src/using Distributed.jl

max_forks_repo_name: fernandopenaranda/MBSinBLG


text:
# ******************************************************************************************
# Notices:
#
# Copyright © 2022 United States Government as represented by the Administrator of the
# National Aeronautics and Space Administration.  All Rights Reserved.
#
# Disclaimers
#
# No Warranty: THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY KIND,
# EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY
# THAT THE SUBJECT SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY
# WARRANTY THAT THE SUBJECT SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT DOCUMENTATION,
# IF PROVIDED, WILL CONFORM TO THE SUBJECT SOFTWARE. THIS AGREEMENT DOES NOT, IN ANY MANNER,
# CONSTITUTE AN ENDORSEMENT BY GOVERNMENT AGENCY OR ANY PRIOR RECIPIENT OF ANY RESULTS,
# RESULTING DESIGNS, HARDWARE, SOFTWARE PRODUCTS OR ANY OTHER APPLICATIONS RESULTING FROM
# USE OF THE SUBJECT SOFTWARE.  FURTHER, GOVERNMENT AGENCY DISCLAIMS ALL WARRANTIES AND
# LIABILITIES REGARDING THIRD-PARTY SOFTWARE, IF PRESENT IN THE ORIGINAL SOFTWARE, AND
# DISTRIBUTES IT "AS IS."
#
# Waiver and Indemnity:  RECIPIENT AGREES TO WAIVE ANY AND ALL CLAIMS AGAINST THE UNITED
# STATES GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY PRIOR RECIPIENT.
# IF RECIPIENT'S USE OF THE SUBJECT SOFTWARE RESULTS IN ANY LIABILITIES, DEMANDS, DAMAGES,
# EXPENSES OR LOSSES ARISING FROM SUCH USE, INCLUDING ANY DAMAGES FROM PRODUCTS BASED ON,
# OR RESULTING FROM, RECIPIENT'S USE OF THE SUBJECT SOFTWARE, RECIPIENT SHALL INDEMNIFY AND
# HOLD HARMLESS THE UNITED STATES GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL
# AS ANY PRIOR RECIPIENT, TO THE EXTENT PERMITTED BY LAW.  RECIPIENT'S SOLE REMEDY FOR ANY
# SUCH MATTER SHALL BE THE IMMEDIATE, UNILATERAL TERMINATION OF THIS AGREEMENT.
# ******************************************************************************************

const TERMINATE = 0x0

"""
Make k workers available in total, spawning new processes only as necessary.
"""
get_workers(k::Int64) = addprocs(max(k + 1 - nprocs(), 0))

"""
Recursively build miniminal k-d tree that defines volumes proven to satisfy given condition
or its complement. At specificed depth levels, places children onto jobs queue instead of
refining, to maintain a balanced work distribution across processes.
"""
function refine!(cell::Cell, r::AbstractRefinery, jobs::RemoteChannel)
    cell.data.pid = myid()
    if needs_refinement(cell, r)
        split!(cell, child_data)
        cs = children(cell)

        if cell.data.depth in r.ks
            for c in cs
                c.parent = nothing      # detach to avoid copying entire tree onto queue
                c.data.detached = true
                put!(jobs, c)
            end
            remotecall_wait(add_todo, 1, length(cs)) # wait for acknowledgement
        else
            for c in cs
                refine!(c, r, jobs)
            end
            merge!(cell)
        end
    end
    return nothing
end

"""
Take a cell from the jobs queue, refine it (possibly spawning more jobs in the process), and
place it onto the results queue. Result may not be a well-formed tree; the worker does not
wait on detached children. The forest is stitched together by the main process after all
workers have completed. Workers increment a shared atomic counter when adding to the jobs
queue and decerement it when adding to the results queue. Workers break upon receiving a
termination signal (a cell with a null hash).
"""
function work(jobs::RemoteChannel, results::RemoteChannel, r::AbstractRefinery)
    while true
        cell = take!(jobs)
        cell.data.hash == TERMINATE && break
        refine!(cell, r, jobs)
        put!(results, cell)
        remote_do(add_done, 1, 1) # no wait required
    end
end

"""
Recursively reassemble forest, merging a cell with a dictionary of subtrees ordered by cell
hash.
"""
function merge!(cell::Cell, subtrees::Dict{UInt64, <:Cell})
    isleaf(cell) && return nothing
    for c in children(cell)
        # Repair bidirectional link
        c.parent = cell

        # If child is detached, search dictionary for computed version and merge.
        if c.data.detached
            cc = pop!(subtrees, c.data.hash)
            c.data = cc.data
            c.data.detached = false
            c.children = cc.children
        end
        merge!(c, subtrees)
    end
    merge!(cell)
    return nothing
end

"""
Empty results channel, perform merge, and verify correctness.
"""
function merge!(root::Cell, results::RemoteChannel)
    # Collects subtrees.
    forest = []
    while isready(results)
        push!(forest, take!(results))
    end

    # Creates hash set.
    subtrees = Dict(c.data.hash => c for c in forest)

    # Merges root with subtrees.
    rootc = pop!(subtrees, root.data.hash)
    root.children = rootc.children
    merge!(root, subtrees)

    # Verfies successful merge.
    @assert isempty(subtrees)
    @assert verify(root)

    return nothing
end

"""
Terminate worker processes.
"""
function terminate(jobs::RemoteChannel)
    signal = Cell(SVector{0, Float64}(), SVector{0, Float64}(), CellStatus(hash=TERMINATE))
    for _ in workers()
        put!(jobs, signal)
    end
end

"""
    refine_multiprocess!(root::Cell, r::AbstractRefinery)

Run multiprocess refinement.
"""
function refine_multiprocess!(root::Cell, r::AbstractRefinery)
    jobs = RemoteChannel(() -> Channel{Cell}(1_000_000))
    results = RemoteChannel(() -> Channel{Cell}(1_000_000))

    # Remotely execute work task.
    put!(jobs, root)
    TODO_COUNTER[] = 1
    for p in workers()
        remote_do(work, p, jobs, results, r)
    end

    # Block until there is no outstanding work, then send termination signal.
    while TODO_COUNTER[] > 0
    	sleep(0.1)
    end
    terminate(jobs)

    # Collect results and merge with root.
    merge!(root, results)
end


text:
# This file is a part of Julia. License is MIT: https://julialang.org/license

"""
Like UnitRange{Int}, but can handle the `last` field, being temporarily
< first (this can happen during compacting)
"""
struct StmtRange <: AbstractUnitRange{Int}
    start::Int
    stop::Int
end

first(r::StmtRange) = r.start
last(r::StmtRange) = r.stop
iterate(r::StmtRange, state=0) = (last(r) - first(r) < state) ? nothing : (first(r) + state, state + 1)

StmtRange(range::UnitRange{Int}) = StmtRange(first(range), last(range))

struct BasicBlock
    stmts::StmtRange
    preds::Vector{Int}
    succs::Vector{Int}
end

function BasicBlock(stmts::StmtRange)
    return BasicBlock(stmts, Int[], Int[])
end

function BasicBlock(old_bb, stmts)
    return BasicBlock(stmts, old_bb.preds, old_bb.succs)
end

copy(bb::BasicBlock) = BasicBlock(bb.stmts, copy(bb.preds), copy(bb.succs))

max_stars_repo_name: vanillajonathan/julia

max_issues_repo_name: vanillajonathan/julia

max_forks_repo_name: vanillajonathan/julia


text:
## -*-Julia-*-
## Test suite for Julia's WAV module
import WAV
using Base.Test
using Compat
using Compat.String

# These float array comparison functions are from dists.jl
function absdiff{T<:Real}(current::AbstractArray{T}, target::AbstractArray{T})
    @assert all(size(current) == size(target))
    maximum(abs(current - target))
end

function reldiff{T<:Real}(current::T, target::T)
    abs((current - target)/(bool(target) ? target : 1))
end

function reldiff{T<:Real}(current::AbstractArray{T}, target::AbstractArray{T})
    @assert all(size(current) == size(target))
    maximum([reldiff(current[i], target[i]) for i in 1:numel(target)])
end

## example from README, modified to use an IO buffer
let
    x = [0:7999;]
    y = sin(2 * pi * x / 8000)
    io = IOBuffer()
    WAV.wavwrite(y, io, Fs=8000)
    seek(io, 0)
    y, Fs = WAV.wavread(io)
    y = cos(2 * pi * x / 8000)
    WAV.wavappend(y, io)
    seek(io, 0)
    y, Fs = WAV.wavread(io)
end

let
    x = [0:7999;]
    y = sin(2 * pi * x / 8000)
    WAV.wavwrite(y, "example.wav", Fs=8000)
    y, Fs = WAV.wavread("example.wav")
    y = cos(2 * pi * x / 8000)
    WAV.wavappend(y, "example.wav")
    y, Fs = WAV.wavread("example.wav")
    @test length(y) == (2 * length(x))
    rm("example.wav")
end

## default arguments, GitHub Issue #10
let
    tmp=rand(Float32,(10,2))
    io = IOBuffer()
    WAV.wavwrite(tmp, io; nbits=32)
    seek(io, 0)
    y, fs, nbits, extra = WAV.wavread(io; format="native")
    @test typeof(y) == Array{Float32, 2}
    @test fs == 8000.0
    @test nbits == 32
    @test WAV.isformat(extra[:fmt], WAV.WAVE_FORMAT_IEEE_FLOAT)
end

## malformed subchunk header, GitHub Issue #18
let
    # Create a malformed WAV file
    samples = rand(Float32, (10, 1))
    io = IOBuffer()

    const compression = WAV.get_default_compression(samples)
    nbits = WAV.get_default_precision(samples, compression)

    const nchannels = size(samples, 2)
    const sample_rate = 8000
    nbits = ceil(Integer, nbits / 8) * 8
    const block_align = nbits / 8 * nchannels
    const bps = sample_rate * block_align
    const data_length::UInt32 = size(samples, 1) * block_align

    const fmt = WAV.WAVFormat(compression,
                              nchannels,
                              sample_rate,
                              bps,
                              block_align,
                              nbits,
                              WAV.WAVFormatExtension())

    WAV.write_header(io, @compat UInt32(data_length + 37)) # 37 instead of 36 is the broken part
    WAV.write_format(io, fmt)

    # write the data subchunk header
    WAV.write(io, b"data")
    WAV.write_le(io, data_length) # UInt32
    WAV.write_data(io, fmt, samples)

    seek(io, 0)
    y, fs, nbits, opt = WAV.wavread(io, format="native")
    @test fs == 8000
    @test nbits == 32
    @test opt[:fmt].compression_code == compression
    @test opt[:fmt].nchannels == nchannels
    @test opt[:fmt].sample_rate == sample_rate
    @test opt[:fmt].bytes_per_second == bps
    @test opt[:fmt].block_align == block_align
    @test WAV.isformat(opt[:fmt], compression)
    @test WAV.bits_per_sample(opt[:fmt]) == nbits
    @test samples == y
end

let
    tmp=rand(Float64,(10,2))
    io = IOBuffer()
    WAV.wavwrite(tmp, io; nbits=64)
    seek(io, 0)
    y, fs, nbits, extra = WAV.wavread(io; format="native")
    @test typeof(y) == Array{Float64, 2}
    @test fs == 8000.0
    @test nbits == 64
    @test WAV.isformat(extra[:fmt], WAV.WAVE_FORMAT_IEEE_FLOAT)
    @test WAV.isformat(extra[:fmt], WAV.WAVE_FORMAT_EXTENSIBLE)
end

let
    tmp=rand(Float32,(10,2))
    io = IOBuffer()
    WAV.wavwrite(tmp, io; compression=WAV.WAVE_FORMAT_IEEE_FLOAT)
    seek(io, 0)
    y, fs, nbits, extra = WAV.wavread(io; format="native")
    @test typeof(y) == Array{Float32, 2}
    @test fs == 8000.0
    @test nbits == 32
    @test WAV.isformat(extra[:fmt], WAV.WAVE_FORMAT_IEEE_FLOAT)
end

## Test wavread and wavwrite
## Generate some wav files for writing and reading
for fs = (8000,11025,22050,44100,48000,96000,192000), nbits = (1,7,8,9,12,16,20,24,32,64), nsamples = convert(Array{Int}, [0; logspace(1, 4, 4)]), nchans = 1:4
    ## Test wav files
    ## The tolerance is based on the number of bits used to encode the file in wavwrite
    tol = 2.0 / (2.0^(nbits - 1))

    in_data = rand(nsamples, nchans)
    if nsamples > 0
        @test maximum(in_data) <= 1.0
        @test minimum(in_data) >= -1.0
    end
    io = IOBuffer()
    WAV.wavwrite(in_data, io, Fs=fs, nbits=nbits, compression=WAV.WAVE_FORMAT_PCM)
    file_size = position(io)

    ## Check for the common header identifiers
    seek(io, 0)
    @test read(io, UInt8, 4) == b"RIFF"
    @test WAV.read_le(io, UInt32) == file_size - 8
    @test read(io, UInt8, 4) == b"WAVE"

    ## Check that wavread works on the wavwrite produced memory
    seek(io, 0)
    sz = WAV.wavread(io, format="size")
    @test sz == (nsamples, nchans)

    seek(io, 0)
    out_data, out_fs, out_nbits, out_extra = WAV.wavread(io)
    @test length(out_data) == nsamples * nchans
    @test size(out_data, 1) == nsamples
    @test size(out_data, 2) == nchans
    @test typeof(out_data) == Array{Float64, 2}
    @test out_fs == fs
    @test out_nbits == nbits
    fmt = out_extra[:fmt]
    @test WAV.bits_per_sample(fmt) == nbits
    @test WAV.isformat(fmt, WAV.WAVE_FORMAT_PCM)
    @test fmt.nchannels == nchans
    if nsamples > 0
        @test absdiff(out_data, in_data) < tol
    end
    if nchans > 2
        @test WAV.isformat(fmt, WAV.WAVE_FORMAT_EXTENSIBLE)
    end

    ## test the "subrange" option.
    if nsamples > 0
        seek(io, 0)
        # Don't convert to Int, test if passing a float (nsamples/2) behaves as expected
        subsamples = min(10, trunc(Int, nsamples / 2))
        out_data, out_fs, out_nbits, out_extra = WAV.wavread(io, subrange=subsamples)
        @test length(out_data) == subsamples * nchans
        @test size(out_data, 1) == subsamples
        @test size(out_data, 2) == nchans
        @test typeof(out_data) == Array{Float64, 2}
        @test out_fs == fs
        @test out_nbits == nbits
        fmt = out_extra[:fmt]
        @test WAV.bits_per_sample(fmt) == nbits
        @test WAV.isformat(fmt, WAV.WAVE_FORMAT_PCM)
        @test fmt.nchannels == nchans
        @test absdiff(out_data, in_data[1:subsamples, :]) < tol

        seek(io, 0)
        sr = convert(Int, min(5, trunc(Int, nsamples / 2))):convert(Int, min(23, nsamples - 1))
        out_data, out_fs, out_nbits, out_extra = WAV.wavread(io, subrange=sr)
        @test length(out_data) == length(sr) * nchans
        @test size(out_data, 1) == length(sr)
        @test size(out_data, 2) == nchans
        @test typeof(out_data) == Array{Float64, 2}
        @test out_fs == fs
        @test out_nbits == nbits
        fmt = out_extra[:fmt]
        @test WAV.bits_per_sample(fmt) == nbits
        @test WAV.isformat(fmt, WAV.WAVE_FORMAT_PCM)
        @test fmt.nchannels == nchans
        @test absdiff(out_data, in_data[sr, :]) < tol
    end
end

## Test wavappend
x0=rand(2,3)
x1=rand(3,3)
x2=rand(4,3)
io = IOBuffer()
WAV.wavwrite(x0, io)
WAV.wavappend(x1, io)
WAV.wavappend(x2, io)
seek(io, 0)
x, fs, nbits, extra = WAV.wavread(io)
@test x == [x0; x1; x2]

## Test native encoding of 8 bits
for nchans = (1,2,4)
    in_data_8 = reshape(typemin(UInt8):typemax(UInt8), (trunc(Int, 256 / nchans), nchans))
    io = IOBuffer()
    WAV.wavwrite(in_data_8, io)

    seek(io, 0)
    out_data_8, fs, nbits, extra = WAV.wavread(io, format="native")
    @test fs == 8000
    @test nbits == 8
    fmt = extra[:fmt]
    @test WAV.bits_per_sample(fmt) == nbits
    @test WAV.isformat(fmt, WAV.WAVE_FORMAT_PCM)
    @test fmt.nchannels == nchans
    @test in_data_8 == out_data_8
end

## Test native encoding of 16 bits
for nchans = (1,2,4)
    in_data_16 = reshape(typemin(Int16):typemax(Int16), (trunc(Int, 65536 / nchans), nchans))
    io = IOBuffer()
    WAV.wavwrite(in_data_16, io)

    seek(io, 0)
    out_data_16, fs, nbits, extra = WAV.wavread(io, format="native")
    @test fs == 8000
    @test nbits == 16
    fmt = extra[:fmt]
    @test WAV.bits_per_sample(fmt) == nbits
    @test WAV.isformat(fmt, WAV.WAVE_FORMAT_PCM)
    @test fmt.nchannels == nchans
    @test in_data_16 == out_data_16
end

## Test native encoding of 24 bits
for nchans = (1,2,4)
    in_data_24 = convert(Array{Int32}, reshape(-63:64, trunc(Int, 128 / nchans), nchans))
    io = IOBuffer()
    WAV.wavwrite(in_data_24, io)

    seek(io, 0)
    out_data_24, fs, nbits, extra = WAV.wavread(io, format="native")
    @test fs == 8000
    @test nbits == 24
    fmt = extra[:fmt]
    @test WAV.bits_per_sample(fmt) == nbits
    @test WAV.isformat(fmt, WAV.WAVE_FORMAT_PCM)
    @test fmt.nchannels == nchans
    @test in_data_24 == out_data_24
end

## Test encoding 32 bit values
for nchans = (1,2,4)
    in_data_single = convert(Array{Float32}, reshape(linspace(-1.0, 1.0, 128), trunc(Int, 128 / nchans), nchans))
    io = IOBuffer()
    WAV.wavwrite(in_data_single, io)

    seek(io, 0)
    out_data_single, fs, nbits, extra = WAV.wavread(io, format="native")
    @test fs == 8000
    @test nbits == 32
    fmt = extra[:fmt]
    @test WAV.bits_per_sample(fmt) == nbits
    @test WAV.isformat(fmt, WAV.WAVE_FORMAT_IEEE_FLOAT)
    @test fmt.nchannels == nchans
    @test in_data_single == out_data_single
end

## Test encoding 32 bit values outside the [-1, 1] range
for nchans = (1,2,4)
    nsamps = trunc(Int, 128 / nchans)
    in_data_single = convert(Array{Float32}, reshape(-63:64, nsamps, nchans))
    io = IOBuffer()
    WAV.wavwrite(in_data_single, io)

    seek(io, 0)
    out_data_single, fs, nbits, extra = WAV.wavread(io, format="native")
    @test fs == 8000
    @test nbits == 32
    fmt = extra[:fmt]
    @test WAV.bits_per_sample(fmt) == nbits
    @test WAV.isformat(fmt, WAV.WAVE_FORMAT_IEEE_FLOAT)
    @test fmt.nchannels == nchans
    @test [in_data_single[i, j] for i = 1:nsamps, j = 1:nchans] == out_data_single
end

## Test encoding 64 bit values
for nchans = (1,2,4)
    in_data_single = convert(Array{Float64}, reshape(linspace(-1.0, 1.0, 128), trunc(Int, 128 / nchans), nchans))
    io = IOBuffer()
    WAV.wavwrite(in_data_single, io)

    seek(io, 0)
    out_data_single, fs, nbits, extra = WAV.wavread(io, format="native")
    @test fs == 8000
    @test nbits == 64
    fmt = extra[:fmt]
    @test WAV.bits_per_sample(fmt) == nbits
    @test fmt.nchannels == nchans
    @test WAV.isformat(fmt, WAV.WAVE_FORMAT_IEEE_FLOAT)
    @test in_data_single == out_data_single
end

## Test encoding 64 bit values outside the [-1, 1] range
for nchans = (1,2,4)
    nsamps = trunc(Int, 128 / nchans)
    in_data_single = convert(Array{Float64}, reshape(-63:64, nsamps, nchans))
    io = IOBuffer()
    WAV.wavwrite(in_data_single, io)

    seek(io, 0)
    out_data_single, fs, nbits, extra = WAV.wavread(io, format="native")
    @test fs == 8000
    @test nbits == 64
    fmt = extra[:fmt]
    @test WAV.bits_per_sample(fmt) == nbits
    @test WAV.isformat(fmt, WAV.WAVE_FORMAT_IEEE_FLOAT)
    @test fmt.nchannels == nchans
    @test [in_data_single[i, j] for i = 1:nsamps, j = 1:nchans] == out_data_single
end

### Test A-Law and Mu-Law
for nbits = (8, 16), nsamples = convert(Array{Int}, [0; logspace(1, 4, 4)]), nchans = 1:4, fmt=(WAV.WAVE_FORMAT_ALAW, WAV.WAVE_FORMAT_MULAW)
    const fs = 8000.0
    const tol = 2.0 / (2.0^6)
    in_data = rand(nsamples, nchans)
    if nsamples > 0
        @test maximum(in_data) <= 1.0
        @test minimum(in_data) >= -1.0
    end
    io = IOBuffer()
    WAV.wavwrite(in_data, io, Fs=fs, nbits=nbits, compression=fmt)
    file_size = position(io)

    ## Check for the common header identifiers
    seek(io, 0)
    @test read(io, UInt8, 4) == b"RIFF"
    @test WAV.read_le(io, UInt32) == file_size - 8
    @test read(io, UInt8, 4) == b"WAVE"

    ## Check that wavread works on the wavwrite produced memory
    seek(io, 0)
    sz = WAV.wavread(io, format="size")
    @test sz == (nsamples, nchans)

    seek(io, 0)
    out_data, out_fs, out_nbits, out_extra = WAV.wavread(io)
    @test length(out_data) == nsamples * nchans
    @test size(out_data, 1) == nsamples
    @test size(out_data, 2) == nchans
    @test typeof(out_data) == Array{Float64, 2}
    @test out_fs == fs
    @test out_nbits == 8
    @test WAV.bits_per_sample(out_extra[:fmt]) == 8
    @test out_extra[:fmt].nchannels == nchans
    @test WAV.isformat(out_extra[:fmt], fmt)
    if nchans > 2
        @test WAV.isformat(out_extra[:fmt], WAV.WAVE_FORMAT_EXTENSIBLE)
    else
        @test !WAV.isformat(out_extra[:fmt], WAV.WAVE_FORMAT_EXTENSIBLE)
    end
    if nsamples > 0
        @test absdiff(out_data, in_data) < tol
    end

    ## test the "subrange" option.
    if nsamples > 0
        seek(io, 0)
        # Don't convert to Int, test if passing a float (nsamples/2) behaves as expected
        subsamples = min(10, trunc(Int, nsamples / 2))
        out_data, out_fs, out_nbits, out_extra = WAV.wavread(io, subrange=subsamples)
        @test length(out_data) == subsamples * nchans
        @test size(out_data, 1) == subsamples
        @test size(out_data, 2) == nchans
        @test typeof(out_data) == Array{Float64, 2}
        @test out_fs == fs
        @test out_nbits == 8
        @test WAV.bits_per_sample(out_extra[:fmt]) == 8
        @test out_extra[:fmt].nchannels == nchans
        @test WAV.isformat(out_extra[:fmt], fmt)
        @test absdiff(out_data, in_data[1:subsamples, :]) < tol

        seek(io, 0)
        sr = convert(Int, min(5, trunc(Int, nsamples / 2))):convert(Int, min(23, nsamples - 1))
        out_data, out_fs, out_nbits, out_extra = WAV.wavread(io, subrange=sr)
        @test length(out_data) == length(sr) * nchans
        @test size(out_data, 1) == length(sr)
        @test size(out_data, 2) == nchans
        @test typeof(out_data) == Array{Float64, 2}
        @test out_fs == fs
        @test out_nbits == 8
        @test WAV.bits_per_sample(out_extra[:fmt]) == 8
        @test out_extra[:fmt].nchannels == nchans
        @test WAV.isformat(out_extra[:fmt], fmt)
        @test absdiff(out_data, in_data[sr, :]) < tol
    end
end

### Test float formatting
for nbits = (32, 64), nsamples = convert(Array{Int}, [0; logspace(1, 4, 4)]), nchans = 1:2, fmt=(WAV.WAVE_FORMAT_IEEE_FLOAT)
    const fs = 8000.0
    const tol = 1e-6
    in_data = rand(nsamples, nchans)
    if nsamples > 0
        @test maximum(in_data) <= 1.0
        @test minimum(in_data) >= -1.0
    end
    io = IOBuffer()
    WAV.wavwrite(in_data, io, Fs=fs, nbits=nbits, compression=fmt)
    file_size = position(io)

    ## Check for the common header identifiers
    seek(io, 0)
    @test read(io, UInt8, 4) == b"RIFF"
    @test WAV.read_le(io, UInt32) == file_size - 8
    @test read(io, UInt8, 4) == b"WAVE"

    ## Check that wavread works on the wavwrite produced memory
    seek(io, 0)
    sz = WAV.wavread(io, format="size")
    @test sz == (nsamples, nchans)

    seek(io, 0)
    out_data, out_fs, out_nbits, out_extra = WAV.wavread(io)
    @test length(out_data) == nsamples * nchans
    @test size(out_data, 1) == nsamples
    @test size(out_data, 2) == nchans
    @test typeof(out_data) == Array{Float64, 2}
    @test out_fs == fs
    @test out_nbits == nbits
    @test WAV.bits_per_sample(out_extra[:fmt]) == nbits
    @test WAV.isformat(out_extra[:fmt], WAV.WAVE_FORMAT_IEEE_FLOAT)
    @test out_extra[:fmt].nchannels == nchans
    if nsamples > 0
        @test absdiff(out_data, in_data) < tol
    end

    ## test the "subrange" option.
    if nsamples > 0
        seek(io, 0)
        # Don't convert to Int, test if passing a float (nsamples/2) behaves as expected
        subsamples = min(10, trunc(Int, nsamples / 2))
        out_data, out_fs, out_nbits, out_extra = WAV.wavread(io, subrange=subsamples)
        @test length(out_data) == subsamples * nchans
        @test size(out_data, 1) == subsamples
        @test size(out_data, 2) == nchans
        @test typeof(out_data) == Array{Float64, 2}
        @test out_fs == fs
        @test out_nbits == nbits
        @test WAV.bits_per_sample(out_extra[:fmt]) == nbits
        @test out_extra[:fmt].nchannels == nchans
        @test WAV.isformat(out_extra[:fmt], WAV.WAVE_FORMAT_IEEE_FLOAT)
        @test absdiff(out_data, in_data[1:subsamples, :]) < tol

        seek(io, 0)
        sr = convert(Int, min(5, trunc(Int, nsamples / 2))):convert(Int, min(23, nsamples - 1))
        out_data, out_fs, out_nbits, out_extra = WAV.wavread(io, subrange=sr)
        @test length(out_data) == length(sr) * nchans
        @test size(out_data, 1) == length(sr)
        @test size(out_data, 2) == nchans
        @test typeof(out_data) == Array{Float64, 2}
        @test out_fs == fs
        @test out_nbits == nbits
        @test WAV.bits_per_sample(out_extra[:fmt]) == nbits
        @test WAV.isformat(out_extra[:fmt], WAV.WAVE_FORMAT_IEEE_FLOAT)
        @test out_extra[:fmt].nchannels == nchans
        @test absdiff(out_data, in_data[sr, :]) < tol
    end
end

### Read unknown chunks
let
    const fs = 8000.0
    in_data = rand(1024, 2)
    io = IOBuffer()
    in_chunks = @compat Dict(:test=>[0x1, 0x2, 0x3])
    WAV.wavwrite(in_data, io, Fs=fs, chunks=in_chunks)

    seek(io, 0)
    data, fs, nbits, ext = WAV.wavread(io)

    @test haskey(ext, :test) == true
    @test ext[:test] == in_chunks[:test]
end

### WAVArray
type TestHtmlDisplay <: Display
    io::IOBuffer
end
function display(d::TestHtmlDisplay, mime::MIME"text/html", x)
    print(d.io, reprmime(mime, x))
end

let
    io = IOBuffer()
    wa = WAV.WAVArray(8000, sin(1:256 * 8000.0 / 1024));
    myio = IOBuffer()
    display(TestHtmlDisplay(myio), MIME"text/html"(), wa)
    @test @compat ismatch(r"audio controls", String(myio))
end

### playback
# The playback tests don't work on travis.
let
    const fs = 44100.0
    t = 1:44100;
    in_data = sin(5.0 * t / fs) * 1e-6;
    #WAV.wavplay(in_data, fs);
    #WAV.wavplay([in_data in_data], fs);
end


text:
module MultiResolutionIterators
using IterTools: imap
export ALL_LEVELS, lvls,
    flatten_levels, join_levels,
    consolidate_levels, full_consolidate, consolidate

include("basic_functions.jl")
include("core.jl")
include("namedlevels.jl")

const ALL_LEVELS=0:typemax(Int)
include("functionality.jl")

end # module


text:
struct HydroDispatchReservoirCascade <: PSI.AbstractHydroReservoirFormulation end

function energy_balance_external_input_param_cascade(
    optimization_container::PSI.OptimizationContainer,
    initial_conditions::Vector{PSI.InitialCondition},
    time_series_data::Tuple{
        Vector{PSI.DeviceTimeSeriesConstraintInfo},
        Vector{PSI.DeviceTimeSeriesConstraintInfo},
    },
    upstream::Vector{
        Vector{NamedTuple{(:unit, :lag, :multiplier), Tuple{PSY.HydroGen, Int64, Float64}}},
    },
    cons_names::Tuple{Symbol, Symbol},
    var_names::Tuple{Symbol, Symbol, Symbol},
    param_references::Tuple{PSI.UpdateRef, PSI.UpdateRef},
)
    time_steps = PSI.model_time_steps(optimization_container)
    resolution = PSI.model_resolution(optimization_container)
    fraction_of_hour = Dates.value(Dates.Minute(resolution)) / 60.0

    inflow_data = time_series_data[1]
    target_data = time_series_data[2]

    name_index = [PSI.get_component_name(d) for d in inflow_data]

    varspill = PSI.get_variable(optimization_container, var_names[1])
    varout = PSI.get_variable(optimization_container, var_names[2])
    varenergy = PSI.get_variable(optimization_container, var_names[3])

    balance_cons_name = cons_names[1]
    target_cons_name = cons_names[2]
    balance_param_reference = param_references[1]
    target_param_reference = param_references[2]

    container_inflow = PSI.add_param_container!(
        optimization_container,
        balance_param_reference,
        name_index,
        time_steps,
    )
    param_inflow = PSI.get_parameter_array(container_inflow)
    multiplier_inflow = PSI.get_multiplier_array(container_inflow)

    container_target = PSI.add_param_container!(
        optimization_container,
        target_param_reference,
        name_index,
        time_steps,
    )
    param_target = PSI.get_parameter_array(container_target)
    multiplier_target = PSI.get_multiplier_array(container_target)

    balance_constraint = PSI.add_cons_container!(
        optimization_container,
        balance_cons_name,
        name_index,
        time_steps,
    )
    target_constraint =
        PSI.add_cons_container!(optimization_container, target_cons_name, name_index, 1)

    for (ix, d) in enumerate(inflow_data)
        name = PSI.get_component_name(d)
        upstream_devices = upstream[ix]

        multiplier_inflow[name, 1] = d.multiplier
        param_inflow[name, 1] =
            PJ.add_parameter(optimization_container.JuMPmodel, d.timeseries[1])
        exp =
            initial_conditions[ix].value +
            (
                multiplier_inflow[name, 1] * param_inflow[name, 1] - varspill[name, 1] -
                varout[name, 1]
            ) * fraction_of_hour

        #= This is commented out because it constrains the cascading inflow in period 1 to upstream outflows in period 1 instead of 0
        if !isempty(upstream_devices)
            for j in upstream_devices
                JuMP.add_to_expression!(exp, varspill[IS.get_name(j), 1], fraction_of_hour)
                JuMP.add_to_expression!(exp, varout[IS.get_name(j), 1], fraction_of_hour)
            end
        end
        =#

        balance_constraint[name, 1] =
            JuMP.@constraint(optimization_container.JuMPmodel, varenergy[name, 1] == exp)

        for t in time_steps[2:end]
            param_inflow[name, t] =
                PJ.add_parameter(optimization_container.JuMPmodel, d.timeseries[t])
            exp =
                varenergy[name, t - 1] +
                (
                    d.multiplier * param_inflow[name, t] - varspill[name, t] -
                    varout[name, t]
                ) * fraction_of_hour

            if !isempty(upstream_devices)
                for j in upstream_devices
                    if t - j.lag >= 1
                        JuMP.add_to_expression!(
                            exp,
                            varspill[IS.get_name(j.unit), t - j.lag],
                            j.multiplier * fraction_of_hour,
                        )
                        JuMP.add_to_expression!(
                            exp,
                            varout[IS.get_name(j.unit), t - j.lag],
                            j.multiplier * fraction_of_hour,
                        )
                    end
                end
            end

            balance_constraint[name, t] = JuMP.@constraint(
                optimization_container.JuMPmodel,
                varenergy[name, t] == exp
            )
        end
    end

    for (ix, d) in enumerate(target_data)
        name = PSI.get_component_name(d)
        for t in time_steps
            param_target[name, t] =
                PJ.add_parameter(optimization_container.JuMPmodel, d.timeseries[t])
        end
        target_constraint[name, 1] = JuMP.@constraint(
            optimization_container.JuMPmodel,
            varenergy[name, time_steps[end]] >=
            d.multiplier * param_target[name, time_steps[end]]
        )
    end

    return
end

function energy_balance_external_input_cascade(
    optimization_container::PSI.OptimizationContainer,
    initial_conditions::Vector{PSI.InitialCondition},
    time_series_data::Tuple{
        Vector{PSI.DeviceTimeSeriesConstraintInfo},
        Vector{PSI.DeviceTimeSeriesConstraintInfo},
    },
    upstream::Vector{
        Vector{NamedTuple{(:unit, :lag, :multiplier), Tuple{PSY.HydroGen, Int64, Float64}}},
    },
    cons_names::Tuple{Symbol, Symbol},
    var_names::Tuple{Symbol, Symbol, Symbol},
)
    time_steps = PSI.model_time_steps(optimization_container)
    resolution = PSI.model_resolution(optimization_container)
    fraction_of_hour = Dates.value(Dates.Minute(resolution)) / 60.0

    inflow_data = time_series_data[1]
    target_data = time_series_data[2]

    name_index = [PSI.get_component_name(d) for d in inflow_data]

    varspill = PSI.get_variable(optimization_container, var_names[1])
    varout = PSI.get_variable(optimization_container, var_names[2])
    varenergy = PSI.get_variable(optimization_container, var_names[3])

    balance_cons_name = cons_names[1]
    target_cons_name = cons_names[2]

    balance_constraint = PSI.add_cons_container!(
        optimization_container,
        balance_cons_name,
        name_index,
        time_steps,
    )
    target_constraint =
        PSI.add_cons_container!(optimization_container, target_cons_name, name_index, 1)

    for (ix, d) in enumerate(inflow_data)
        name = PSI.get_component_name(d)
        upstream_devices = upstream[ix]

        exp =
            initial_conditions[ix].value +
            (d.multiplier * d.timeseries[1] - varspill[name, 1] - varout[name, 1]) *
            fraction_of_hour

        #= This is commented out because it constrains the cascading inflow in period 1 to upstream outflows in period 1 instead of
        if !isempty(upstream_devices)
            for j in upstream_devices
                JuMP.add_to_expression!(exp, varspill[IS.get_name(j), 1], fraction_of_hour)
                JuMP.add_to_expression!(exp, varout[IS.get_name(j), 1], fraction_of_hour)
            end
        end
        =#

        balance_constraint[name, 1] =
            JuMP.@constraint(optimization_container.JuMPmodel, varenergy[name, 1] == exp)

        for t in time_steps[2:end]
            exp =
                varenergy[name, t - 1] +
                (d.multiplier * d.timeseries[t] - varspill[name, t] - varout[name, t]) *
                fraction_of_hour

            if !isempty(upstream_devices)
                for j in upstream_devices
                    if t - j.lag >= 1
                        JuMP.add_to_expression!(
                            exp,
                            varspill[IS.get_name(j.unit), t - j.lag],
                            j.multiplier * fraction_of_hour,
                        )
                        JuMP.add_to_expression!(
                            exp,
                            varout[IS.get_name(j.unit), t - j.lag],
                            j.multiplier * fraction_of_hour,
                        )
                    end
                end
            end

            balance_constraint[name, t] = JuMP.@constraint(
                optimization_container.JuMPmodel,
                varenergy[name, t] == exp
            )
        end
    end

    for (ix, d) in enumerate(target_data)
        name = PSI.get_component_name(d)
        target_constraint[name, 1] = JuMP.@constraint(
            optimization_container.JuMPmodel,
            varenergy[name, time_steps[end]] >=
            d.multiplier * d.timeseries[time_steps[end]]
        )
    end

    return
end

function energy_balance_cascade_constraint!(
    optimization_container::PSI.OptimizationContainer,
    devices::IS.FlattenIteratorWrapper{H},
    model::PSI.DeviceModel{H, HydroDispatchReservoirCascade},
    system_formulation::Type{<:PM.AbstractPowerModel},
    feedforward::Union{Nothing, PSI.AbstractAffectFeedForward},
) where {H <: HydroEnergyCascade}
    key = PSI.ICKey(PSI.EnergyLevel, H)
    parameters = PSI.model_has_parameters(optimization_container)
    use_forecast_data = PSI.model_uses_forecasts(optimization_container)

    if !PSI.has_initial_conditions(optimization_container.initial_conditions, key)
        throw(
            IS.DataFormatError(
                "Initial Conditions for $(H) Energy Constraints not in the model",
            ),
        )
    end

    inflow_forecast_label = "inflow"
    target_forecast_label = "storage_target"
    constraint_infos_inflow =
        Vector{PSI.DeviceTimeSeriesConstraintInfo}(undef, length(devices))
    constraint_infos_target =
        Vector{PSI.DeviceTimeSeriesConstraintInfo}(undef, length(devices))
    upstream_data = Vector{
        Vector{NamedTuple{(:unit, :lag, :multiplier), Tuple{PSY.HydroGen, Int64, Float64}}},
    }(
        undef,
        length(devices),
    )

    for (ix, d) in enumerate(devices)
        ts_vector_inflow =
            PSI.get_time_series(optimization_container, d, inflow_forecast_label)
        constraint_info_inflow = PSI.DeviceTimeSeriesConstraintInfo(
            d,
            x -> PSY.get_inflow(x) * PSY.get_conversion_factor(x),
            ts_vector_inflow,
        )
        PSI.add_device_services!(constraint_info_inflow.range, d, model)
        constraint_infos_inflow[ix] = constraint_info_inflow

        ts_vector_target =
            PSI.get_time_series(optimization_container, d, target_forecast_label)
        constraint_info_target = PSI.DeviceTimeSeriesConstraintInfo(
            d,
            x -> PSY.get_storage_target(x) * PSY.get_storage_capacity(x),
            ts_vector_target,
        )
        PSI.add_device_services!(constraint_info_target.range, d, model)
        constraint_infos_target[ix] = constraint_info_target

        upstream_data[ix] = get_upstream(d)
    end

    if parameters
        energy_balance_external_input_param_cascade(
            optimization_container,
            PSI.get_initial_conditions(optimization_container, key),
            (constraint_infos_inflow, constraint_infos_target),
            upstream_data,
            (
                PSI.make_constraint_name(PSI.ENERGY_CAPACITY, H),
                PSI.make_constraint_name(PSI.ENERGY_TARGET, H),
            ),
            (
                PSI.make_variable_name(PSI.SPILLAGE, H),
                PSI.make_variable_name(PSI.ACTIVE_POWER, H),
                PSI.make_variable_name(PSI.ENERGY, H),
            ),
            (
                PSI.UpdateRef{H}(PSI.INFLOW, inflow_forecast_label),
                PSI.UpdateRef{H}(PSI.TARGET, target_forecast_label),
            ),
        )
    else
        energy_balance_external_input_cascade(
            optimization_container,
            PSI.get_initial_conditions(optimization_container, key),
            (constraint_infos_inflow, constraint_infos_target),
            upstream_data,
            (
                PSI.make_constraint_name(PSI.ENERGY_CAPACITY, H),
                PSI.make_constraint_name(PSI.ENERGY_TARGET, H),
            ),
            (
                PSI.make_variable_name(PSI.SPILLAGE, H),
                PSI.make_variable_name(PSI.ACTIVE_POWER, H),
                PSI.make_variable_name(PSI.ENERGY, H),
            ),
        )
    end
    return
end