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

using Markdown
using InteractiveUtils

# ╔═╡ 9e3f9bda-7e79-411c-b57f-58e0081cb06e
begin
	using Downloads
	using ARFFFiles
	using ScientificTypes
	using DataFrames
	using StatsBase
	using LinearAlgebra
	using StatsPlots
	using ColorSchemes
	using PlutoUI
end

# ╔═╡ 52644433-b409-4a1e-9d51-165c073adc5a
html"""

Machine Learning using Julia and its ecosystem

Analyzing the 'glass' dataset (Part I)

&copy  Dr. Roland Schätzle

"""

# ╔═╡ 614cb588-5989-4157-8d49-8b9c7aa39216
PlutoUI.TableOfContents(title = "The Glass Dataset")

# ╔═╡ 12ff618e-7539-11ec-2041-713cfd67f6aa
md"""
# Introduction
"""

# ╔═╡ dbf90b18-f033-45fc-a040-1de8ba4f2b50
md"""
The 'glass' dataset comes with the [Weka](https://www.cs.waikato.ac.nz/ml/weka/index.html) toolkit and can be downloaded from [here](https://storm.cis.fordham.edu/~gweiss/data-mining/datasets.html).

The features of this dataset contain information about different variants of glass (like materials used for their production, optical features) and the target variable is the resulting glass variant.

In the follwoing we will analyze this dataset in a typical way, as a data scientist would do it when confronted with a new dataset.
"""

# ╔═╡ af00a498-52c8-47f0-b903-ce69153354cf
md"""
# Download and read data
"""

# ╔═╡ 0e193e6f-5b61-41d0-ba5e-3f7b017d86b4
md"""
The data comes in a file with [ARFF (Attribute-Relation File Format)](https://waikato.github.io/weka-wiki/formats_and_processing/arff_stable/) format. A format used by the Weka toolkit. Therefore we need a special package (`ARFFFiles.jl`) for reading this format.
"""

# ╔═╡ f8ca69eb-023f-408b-aa52-91e843ac2bbf
glass_file = Downloads.download("http://storm.cis.fordham.edu/~gweiss/data-mining/weka-data/glass.arff");

# ╔═╡ ccae5a24-ad05-4c4c-9d7b-41f704182dd9
glass = ARFFFiles.load(DataFrame, glass_file)

# ╔═╡ 6958cfa6-8d57-4fb4-8aa6-b78bf2030223
md"""
# First overview
"""

# ╔═╡ 3d6d331a-9775-4c59-a0d2-49eb5f7fff94
md"""
First we fetch some basic information form the dataset:
"""

# ╔═╡ d1d040b0-15c8-4010-bad9-ab854cf4360f
sz = size(glass)

# ╔═╡ 9f2a26e9-2c7a-4691-b10a-788316f3bc72
col_names = names(glass)

# ╔═╡ c2eb02ae-6610-4e54-8445-33821539b7e9
schema(glass)

# ╔═╡ 488e8edb-cd8a-450c-ba5a-6173b58974df
md"""
So we know now, that it has $(sz[2]) columns with the names identifyed by `names` and it consists of $(sz[1]) instances. All feature variables contain numeric data (`Float64`) and the target is a categorical value.

As we are using `ScientificTypes` we get also type informations (so-called `scitypes`) which is oriented towards the use in ML. On this abstraction level we see, that all feature variables are `Continuous` and the target is a `Multiclass`.
"""

# ╔═╡ 5293ce4e-a382-4a6a-9788-a73eef01d9ab
md"""
# The target variable
"""

# ╔═╡ 85aa2baf-a367-48d8-b36d-15c014f48714
glass_types = unique(glass.Type)

# ╔═╡ 3c92113a-97d4-495d-974e-acea209e64d7
md"""
There are $(length(glass_types)) different glass types in the data set.
"""

# ╔═╡ be2f0cea-96fa-40f2-ac7e-117ccd8f41fa
glass_levels = levels(glass.Type)

# ╔═╡ d4fe2584-33d1-4c21-b55c-f28e9cf284a5
md"""
... but this was only part of the truth. Using `levels` from `ScientificTypes` reveals that there are in fact $(length(glass_levels)) different glass types, but only $(length(glass_types)) of them appear in the glass dataset with count > 0. The type *' $(glass_levels[4])'* does not occur.

So this is an example where we can see the added value of using `ScientificTypes` instead of just relying on the native type system of the language.
"""

# ╔═╡ aa49c0cb-b7f2-4578-9ccc-a01a16c05991
type_count = countmap(glass.Type)

# ╔═╡ 4273fae9-e2b5-4157-80a2-d269125f02f4
md"""
A call to `countmap` tells us, how often each of these types occur within the target variable. So glass for windows used in buildings are the most common types, followed by a glass type used for headlamps.
"""

# ╔═╡ 4d7d9a9e-6313-4067-940d-8edb71d99a20
bar(type_count, leg = false, size = (700, 600), group = glass_types,
	xlabel = "glass types", xrotation = 20,
	ylabel = "count")

# ╔═╡ 05875a6f-ecc1-4f41-ac7a-f0d9b41f3511
md"""
A bar diagram shows graphically how many instances of each type are present in the dataset. Here we notice too, that *'vehic wind non-flat'* has a zero count.
"""

# ╔═╡ dae26d36-ba42-48d6-904a-a8312fbdc040
md"""
# The features in detail
"""

# ╔═╡ 79fb15a1-b914-4ca7-a898-6d793ab4ce36
md"""
The first feature 'RI' is the so-called [refractive index](https://en.wikipedia.org/wiki/Refractive_index) which tells us how fast light travels through the material.

The other features give the percentages of the different elements, like Sodium (= Latin **Na**trium), Magnesium (**Mg**) or **Al**uminium, which are used to produce the glass.
"""

# ╔═╡ f4f0952e-8b56-4846-89bd-6427c06ab6b1
md"""
## Are the values reasonable?
"""

# ╔═╡ 5922443d-c82d-4368-935c-735be201f52d
describe(glass)

# ╔═╡ 7a7ceea3-3634-46e1-8619-a394be7d9d32
md"""
The call to `describe` gives us an impression of the ranges of values in the dataset:
- RI varies slightly around 1.5. From the Wikipedia page mentioned above, we learn that typical RI values for different sorts of glass lie in the range of 1.49 ... 1.69. So the values in the glass dataset seem to be quite reasonable.
- The other features are percentages. So they have to be in the range of 0 ... 100, which holds true for our data.
- Silicon is by far the most important component of glass (here > 70%), followed by Natrium and Calcium.
"""

# ╔═╡ a30259b8-1f0a-4a48-b069-d678482fb112
md"""
We can do another quality check on the percentages. They should add up to 100% for each glass composition:
"""

# ╔═╡ fac902dc-2434-497f-8ee0-46ff24608ef9
glass_wsum = transform(glass, Between(:Na, :Fe) => ByRow(+) => :sumpct)

# ╔═╡ 38e3236b-f2c0-43d3-9b47-53fe50b17852
md"""
We take here the columns from 'Na' to 'Fe' of our glass `DataFrame` and add the values of these attributes in each row (`ByRow(+)`) and store the result in a new column called `sumpct`.
"""

# ╔═╡ ce458e7a-b903-4784-8500-196a1e3bdced
maximum(glass_wsum.sumpct), minimum(glass_wsum.sumpct)

# ╔═╡ 7936bed7-5ac8-4647-ab40-d563099a29a1
md"""
So the sums are for most rows not exactly 100%, but they vary only by a small amount around that value. This can be explained by measurement and rounding errors.
"""

# ╔═╡ 3d7c6a11-9fc5-4019-b580-acf4a8e03e8c
md"""
## Statistics & Visualizations
"""

# ╔═╡ a10ad0f2-81c0-4a5c-9a7b-d9c851d8a46b
md"""
Since all feature attributes are of type `Continuous`, some statistics can be calculated and diagrams of their distribution can be plotted to get more insight.
"""

# ╔═╡ cdb96618-085d-4e1a-815e-aa25b879d905
md"""
### Kurtosis & Skewness
"""

# ╔═╡ 998fdc0f-b7a6-47c3-aa4b-53085fd36de2
md"""
In order to get a first idea of how a distribution of a real valued variable looks like, the two measures [*kurtosis*](https://en.wikipedia.org/wiki/Kurtosis) and [*skewness*](https://en.wikipedia.org/wiki/Skewness) are computed.
- Skewness is a measure of the symmetry (or rather asymmetry) of the distribution. A negative value indicates that its left tail is longer (and rather flat) and that the mass is concentrated on the right. We speak of a *left-skewed* or left-tailed distribution. A positive value on the other hand indicates a *right-skewed* distribution.
- The kurtosis is a measure for the "tailedness" of the distribution. It indicates how far the tails reach out to the left or right. The kurtosis of the normal distribution is 3. So the kurtosis of other distributions are often compared with this value and the so-called *excess kurtosis* is calculated. It says how much the kurtosis of the distribution in consideration exceeds the kurtosis of the normal distribution. This is also the value we get from the `kurtosis` function from the `StatsBase` package.
"""

# ╔═╡ 55b1859c-97aa-4ffa-bf33-61fd090ca5ae
md"""
The following list shows these measures for all features of the glass dataset:
"""

# ╔═╡ f5312456-e727-4bd5-932a-5bf83f20f78b
[(atr, skewness(glass[:, atr]), kurtosis(glass[:, atr])) for atr in col_names[1:end-1]]

# ╔═╡ 112b1d3b-cd6d-47c1-a232-6b921541f596
md"""
From these figures we can already see that e.g. the distributions for *K* and *Ba* are quite asymmetric and long-tailed.
"""

# ╔═╡ 66771c27-0f9e-4ee6-8015-57a4c47b3be7
md"""
### Feature: RI
"""

# ╔═╡ e8f1007c-e22e-42df-812a-d0ec931200df
md"""
The distribution of values of the *refractive index (RI)* can be visualized using a violin plot.
"""

# ╔═╡ 7ff78794-6bdb-4f89-a4d6-4ffabefbeeee
violin(glass.RI, leg = false)

# ╔═╡ a06a405d-64f9-46f3-bd9b-d207e79551ca
md"""
The following histogram shows the the distribution of RI in relation to the glass type.
"""

# ╔═╡ 1fd0f3d3-979c-4be4-9f32-ac03fdb62424
@df glass histogram(:RI, group = :Type, orientation = :hor)

# ╔═╡ 6ac23a84-9d85-474a-8a50-4cb54df9e441
md"""
### Features: Na ... Fe
"""

# ╔═╡ 22417be2-3d34-4323-bf86-01aef9240624
md"""
Violin plots are also a good choice to show the distribution of the values of the material features *Na ... Fe*.

*Note:* the different plots use different scales on the y-axis!
"""

# ╔═╡ c921d37f-5e1b-4132-886f-d01c3746892d
violins = [violin(glass[:, i], xlabel = col_names[i], leg = false) for i in 2:9];

# ╔═╡ 17bc22bd-3aaa-4b7f-a043-f8b400a13646
plot(violins..., layout = (1,8), size = (1200,800), leg = false)

# ╔═╡ 1381c079-0c2b-49c5-bc66-4daebfa13475
boxes = [boxplot(glass[:, i], xlabel = col_names[i], orientation = :vert, xaxis = nothing, leg = false) for i in 2:9];

# ╔═╡ edba3e88-b53f-4c14-a98a-9aab036a73cf
md"""
A possible alternative is in this case the use of box plots:
"""

# ╔═╡ 15437e60-76f7-4f52-91ca-e13a4b003d99
plot(boxes..., layout = (1,8), size = (1000,800), leg = false)

# ╔═╡ 889f417c-16d7-4615-8150-370ce3e73382
md"""
The following histograms show the distribution of the material values with respect to the glass type.
"""

# ╔═╡ e52ec39b-724a-4b21-9dad-29487e5cafd1
histos = [histogram(glass[:,i], group = glass.Type, xlabel = col_names[i], leg = :topright, orientation = :hor) for i in 2:9];

# ╔═╡ a86194d4-9b8c-4c86-b01d-0510c3b7e369
plot(histos..., layout = (2, 4), size = (1200,1200))

# ╔═╡ 09adeac8-6612-4ba2-b77c-1b1188c6a0a2
md"""
In the following step we want to see the three diagram types for each material side by side (one line per material).

The `plot` function takes an array of subplots as it's argument and arranges them in a grid according to the `layout` parameters. So we have to create an appropriate array that contains the subplots in the right order (i.e. for each material a violinplot, a boxplot and the histogram):
- `hcat` creates a matrix of plots (one column for each type of diagram)
- As Julia matrices are processed in a column-first order, we switch the dimensions with `permutedims` (so the diagrams for Na are in the first column, for Mg in the second and so on)
- Finally we have to 'flatten' the matrix into an array (as needed for `plot`) using `vec`.
"""

# ╔═╡ d52d9496-4c7f-415e-84df-e1f5778696bb
plts = vec(permutedims(hcat(violins, boxes, histos)));

# ╔═╡ 7b0862d4-2d2d-4373-bed2-8c0dc124a0cc
plot(plts..., layout = (8, 3), size = (1500, 2100))

# ╔═╡ 0a519abe-d15f-429e-91b9-9e32b030a97a
md"""
*Note*: `Plots` is not really good in calculating the correct viewport of such a grid layout. Depending on the `size` choosen, some parts may not be visible (especially the x-labels and y-ticks on the borders are 'vulnerable'). So you have to choose carefully appropriate `size` parameters.
"""

# ╔═╡ 27c238a3-7e31-483e-b985-20550cf95dd6
md"""
### Correlations
"""

# ╔═╡ 174e479a-fc34-4192-80b1-b64cc3b24860
md"""
When dealing with numerical attributes it is always important to know, if and how strongly they are correlated, since some models don't work well with strongly correlated attributes.

Apart from that, a strong correlation between two attributes is an indicator that one of them may be superfluous, because it doesn't carry much additional information. This knowledge can be used to reduce the number of features and thus make computations for training and evaluating a model more feasible.
"""

# ╔═╡ 1ce41e69-0adf-4ced-949e-119012aeb7c3
md"""
#### Correlation Matrix
"""

# ╔═╡ d7151974-80b1-4802-92b8-b05c6dc02016
md"""
`cor` gives us a matrix with the correlations of each pairing of attributes (the so-called *correlation matrix*):
"""

# ╔═╡ 12f5d6ac-8c78-40b3-a431-e7c58e2ae65b
cor_mat = cor(Matrix(glass[:, 1:9]))

# ╔═╡ f39d3fde-47db-4bdc-b648-501f2aebe4b6
md"""
As the correlation matrix is symmetric, it is sufficient to visualize only the lower part:
"""

# ╔═╡ 3de8c1ce-1e65-4213-88f2-368749d6b036
heatmap(LowerTriangular(cor_mat), yflip = true, size = (800,600),
	seriescolor = :tol_sunset,
	xticks = (1:9, col_names[1:9]),
	yticks = (1:9, col_names[1:9]))

# ╔═╡ 6a103029-8895-40d4-be9c-34ae942a147e
md"""
As can be seen in the heatmap above, the strongest positive correlations between the different glass attributes are:
- Ca/RI
- Ba/Al
- Ba/Na
- K/Al
... whereby only the first two are really 'strong' when we have a look at the numbers:
"""

# ╔═╡ 369391cd-db66-4495-9e01-30266849369f
cor(glass.RI, glass.Ca), cor(glass.Al, glass.Ba),
cor(glass.Ba, glass.Na), cor(glass.K, glass.Al)

# ╔═╡ dba0edaf-002a-48cf-9660-03f20d596068
md"""
Noticeable negative correlations can be observed with:
- Si/RI = $(cor(glass.Si, glass.RI))
- Ba/Mg = $(cor(glass.Ba, glass.Mg))
- Al/Mg = $(cor(glass.Al, glass.Mg))
- Ca/Mg = $(cor(glass.Ca, glass.Mg))
"""

# ╔═╡ ed72dfaf-3cc9-4bac-9c68-973ce7c41c76
md"""
#### Correlation Plot and Cornerplot
"""

# ╔═╡ a1b81178-5997-4616-9671-e46cab3eedc0
md"""
The following plots give an even more detailed insight into the correlations between pairings of different attributes:
- The *correlation plot* shows a scatter plot (each with the corresponding SD line) in the lower half and a heatmap in the upper half of the diagram. The scatter plots show positive correlations in blue, negative correlations in red and neutral ones in yellow.
- The *cornerplot* (here in it's compact form) shows the same scatter plots as the *correlation plot* in the lower half of the diagram. In addition it displays a histogram for each attribute on the top and right margin.
"""

# ╔═╡ f258bd86-0518-4d5e-a240-e19a29be9c92
@df glass corrplot(cols(1:9), size = (1000, 1200))

# ╔═╡ 3a3247ad-84c4-4108-af22-971a00cc65a9
@df glass cornerplot(cols(1:9), size = (1000, 1200), compact = true)

# ╔═╡ 00000000-0000-0000-0000-000000000001
PLUTO_PROJECT_TOML_CONTENTS = """
[deps]
ARFFFiles = "da404889-ca92-49ff-9e8b-0aa6b4d38dc8"
ColorSchemes = "35d6a980-a343-548e-a6ea-1d62b119f2f4"
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
Downloads = "f43a241f-c20a-4ad4-852c-f6b1247861c6"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
PlutoUI = "7f904dfe-b85e-4ff6-b463-dae2292396a8"
ScientificTypes = "321657f4-b219-11e9-178b-2701a2544e81"
StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91"
StatsPlots = "f3b207a7-027a-5e70-b257-86293d7955fd"

[compat]
ARFFFiles = "~1.4.1"
ColorSchemes = "~3.17.1"
DataFrames = "~1.3.2"
PlutoUI = "~0.7.37"
ScientificTypes = "~3.0.0"
StatsBase = "~0.33.16"
StatsPlots = "~0.14.33"
"""

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

[[ARFFFiles]]
deps = ["CategoricalArrays", "Dates", "Parsers", "Tables"]
git-tree-sha1 = "e8c8e0a2be6eb4f56b1672e46004463033daa409"
uuid = "da404889-ca92-49ff-9e8b-0aa6b4d38dc8"
version = "1.4.1"

[[AbstractFFTs]]
deps = ["ChainRulesCore", "LinearAlgebra"]
git-tree-sha1 = "6f1d9bc1c08f9f4a8fa92e3ea3cb50153a1b40d4"
uuid = "621f4979-c628-5d54-868e-fcf4e3e8185c"
version = "1.1.0"

[[AbstractPlutoDingetjes]]
deps = ["Pkg"]
git-tree-sha1 = "8eaf9f1b4921132a4cff3f36a1d9ba923b14a481"
uuid = "6e696c72-6542-2067-7265-42206c756150"
version = "1.1.4"

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

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

[[Arpack]]
deps = ["Arpack_jll", "Libdl", "LinearAlgebra", "Logging"]
git-tree-sha1 = "91ca22c4b8437da89b030f08d71db55a379ce958"
uuid = "7d9fca2a-8960-54d3-9f78-7d1dccf2cb97"
version = "0.5.3"

[[Arpack_jll]]
deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "OpenBLAS_jll", "Pkg"]
git-tree-sha1 = "5ba6c757e8feccf03a1554dfaf3e26b3cfc7fd5e"
uuid = "68821587-b530-5797-8361-c406ea357684"
version = "3.5.1+1"

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

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

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

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

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

[[Calculus]]
deps = ["LinearAlgebra"]
git-tree-sha1 = "f641eb0a4f00c343bbc32346e1217b86f3ce9dad"
uuid = "49dc2e85-a5d0-5ad3-a950-438e2897f1b9"
version = "0.5.1"

[[CategoricalArrays]]
deps = ["DataAPI", "Future", "Missings", "Printf", "Requires", "Statistics", "Unicode"]
git-tree-sha1 = "5196120341b6dfe3ee5f33cf97392a05d6fe80d0"
uuid = "324d7699-5711-5eae-9e2f-1d82baa6b597"
version = "0.10.4"

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

[[ChangesOfVariables]]
deps = ["ChainRulesCore", "LinearAlgebra", "Test"]
git-tree-sha1 = "bf98fa45a0a4cee295de98d4c1462be26345b9a1"
uuid = "9e997f8a-9a97-42d5-a9f1-ce6bfc15e2c0"
version = "0.1.2"

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

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

[[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 = "96b0bc6c52df76506efc8a441c6cf1adcb1babc4"
uuid = "34da2185-b29b-5c13-b0c7-acf172513d20"
version = "3.42.0"

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

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

[[Crayons]]
git-tree-sha1 = "249fe38abf76d48563e2f4556bebd215aa317e15"
uuid = "a8cc5b0e-0ffa-5ad4-8c14-923d3ee1735f"
version = "4.1.1"

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

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

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

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

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

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

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

[[DensityInterface]]
deps = ["InverseFunctions", "Test"]
git-tree-sha1 = "80c3e8639e3353e5d2912fb3a1916b8455e2494b"
uuid = "b429d917-457f-4dbc-8f4c-0cc954292b1d"
version = "0.4.0"

[[Distances]]
deps = ["LinearAlgebra", "SparseArrays", "Statistics", "StatsAPI"]
git-tree-sha1 = "3258d0659f812acde79e8a74b11f17ac06d0ca04"
uuid = "b4f34e82-e78d-54a5-968a-f98e89d6e8f7"
version = "0.10.7"

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

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

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

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

[[DualNumbers]]
deps = ["Calculus", "NaNMath", "SpecialFunctions"]
git-tree-sha1 = "90b158083179a6ccbce2c7eb1446d5bf9d7ae571"
uuid = "fa6b7ba4-c1ee-5f82-b5fc-ecf0adba8f74"
version = "0.6.7"

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

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

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

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

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

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

[[FileWatching]]
uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee"

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

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

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

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

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

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

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

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

[[GR]]
deps = ["Base64", "DelimitedFiles", "GR_jll", "HTTP", "JSON", "Libdl", "LinearAlgebra", "Pkg", "Printf", "Random", "RelocatableFolders", "Serialization", "Sockets", "Test", "UUIDs"]
git-tree-sha1 = "9f836fb62492f4b0f0d3b06f55983f2704ed0883"
uuid = "28b8d3ca-fb5f-59d9-8090-bfdbd6d07a71"
version = "0.64.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 = "a6c850d77ad5118ad3be4bd188919ce97fffac47"
uuid = "d2c73de3-f751-5644-a686-071e5b155ba9"
version = "0.64.0+0"

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

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

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

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

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

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

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

[[HypergeometricFunctions]]
deps = ["DualNumbers", "LinearAlgebra", "SpecialFunctions", "Test"]
git-tree-sha1 = "65e4589030ef3c44d3b90bdc5aac462b4bb05567"
uuid = "34004b35-14d8-5ef3-9330-4cdb6864b03a"
version = "0.3.8"

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

[[HypertextLiteral]]
git-tree-sha1 = "2b078b5a615c6c0396c77810d92ee8c6f470d238"
uuid = "ac1192a8-f4b3-4bfe-ba22-af5b92cd3ab2"
version = "0.9.3"

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

[[IniFile]]
git-tree-sha1 = "f550e6e32074c939295eb5ea6de31849ac2c9625"
uuid = "83e8ac13-25f8-5344-8a64-a9f2b223428f"
version = "0.5.1"

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

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

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

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

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

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

[[IterTools]]
git-tree-sha1 = "fa6287a4469f5e048d763df38279ee729fbd44e5"
uuid = "c8e1da08-722c-5040-9ed9-7db0dc04731e"
version = "1.4.0"

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

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

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

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

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

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

[[LERC_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "bf36f528eec6634efc60d7ec062008f171071434"
uuid = "88015f11-f218-50d7-93a8-a6af411a945d"
version = "3.0.0+1"

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

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

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

[[LazyArtifacts]]
deps = ["Artifacts", "Pkg"]
uuid = "4af54fe1-eca0-43a8-85a7-787d91b784e3"

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

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

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

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

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

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

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

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

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

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

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

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

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

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

[[LogExpFunctions]]
deps = ["ChainRulesCore", "ChangesOfVariables", "DocStringExtensions", "InverseFunctions", "IrrationalConstants", "LinearAlgebra"]
git-tree-sha1 = "58f25e56b706f95125dcb796f39e1fb01d913a71"
uuid = "2ab3a3ac-af41-5b50-aa03-7779005ae688"
version = "0.3.10"

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

[[MKL_jll]]
deps = ["Artifacts", "IntelOpenMP_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "Pkg"]
git-tree-sha1 = "e595b205efd49508358f7dc670a940c790204629"
uuid = "856f044c-d86e-5d09-b602-aeab76dc8ba7"
version = "2022.0.0+0"

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

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

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

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

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

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

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

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

[[MultivariateStats]]
deps = ["Arpack", "LinearAlgebra", "SparseArrays", "Statistics", "StatsAPI", "StatsBase"]
git-tree-sha1 = "7008a3412d823e29d370ddc77411d593bd8a3d03"
uuid = "6f286f6a-111f-5878-ab1e-185364afe411"
version = "0.9.1"

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

[[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 = "bb16469fd5224100e422f0b027d26c5a25de1200"
uuid = "995b91a9-d308-5afd-9ec6-746e21dbc043"
version = "1.2.0"

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

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

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

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

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

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

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

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

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

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

[[Ratios]]
deps = ["Requires"]
git-tree-sha1 = "dc84268fe0e3335a62e315a3a7cf2afa7178a734"
uuid = "c84ed2f1-dad5-54f0-aa8e-dbefe2724439"
version = "0.4.3"

[[RecipesBase]]
git-tree-sha1 = "6bf3f380ff52ce0832ddd3a2a7b9538ed1bcca7d"
uuid = "3cdcf5f2-1ef4-517c-9805-6587b60abb01"
version = "1.2.1"

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

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

[[RelocatableFolders]]
deps = ["SHA", "Scratch"]
git-tree-sha1 = "cdbd3b1338c72ce29d9584fdbe9e9b70eeb5adca"
uuid = "05181044-ff0b-4ac5-8273-598c1e38db00"
version = "0.1.3"

[[Requires]]
deps = ["UUIDs"]
git-tree-sha1 = "838a3a4188e2ded87a4f9f184b4b0d78a1e91cb7"
uuid = "ae029012-a4dd-5104-9daa-d747884805df"
version = "1.3.0"

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

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

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

[[ScientificTypes]]
deps = ["CategoricalArrays", "ColorTypes", "Dates", "Distributions", "PrettyTables", "Reexport", "ScientificTypesBase", "StatisticalTraits", "Tables"]
git-tree-sha1 = "ba70c9a6e4c81cc3634e3e80bb8163ab5ef57eb8"
uuid = "321657f4-b219-11e9-178b-2701a2544e81"
version = "3.0.0"

[[ScientificTypesBase]]
git-tree-sha1 = "a8e18eb383b5ecf1b5e6fc237eb39255044fd92b"
uuid = "30f210dd-8aff-4c5f-94ba-8e64358c1161"
version = "3.0.0"

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

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

[[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 = "5ba658aeecaaf96923dce0da9e703bd1fe7666f9"
uuid = "276daf66-3868-5448-9aa4-cd146d93841b"
version = "2.1.4"

[[StaticArrays]]
deps = ["LinearAlgebra", "Random", "Statistics"]
git-tree-sha1 = "6976fab022fea2ffea3d945159317556e5dad87c"
uuid = "90137ffa-7385-5640-81b9-e52037218182"
version = "1.4.2"

[[StatisticalTraits]]
deps = ["ScientificTypesBase"]
git-tree-sha1 = "271a7fea12d319f23d55b785c51f6876aadb9ac0"
uuid = "64bff920-2084-43da-a3e6-9bb72801c0c9"
version = "3.0.0"

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

[[StatsAPI]]
deps = ["LinearAlgebra"]
git-tree-sha1 = "c3d8ba7f3fa0625b062b82853a7d5229cb728b6b"
uuid = "82ae8749-77ed-4fe6-ae5f-f523153014b0"
version = "1.2.1"

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

[[StatsFuns]]
deps = ["ChainRulesCore", "HypergeometricFunctions", "InverseFunctions", "IrrationalConstants", "LogExpFunctions", "Reexport", "Rmath", "SpecialFunctions"]
git-tree-sha1 = "25405d7016a47cf2bd6cd91e66f4de437fd54a07"
uuid = "4c63d2b9-4356-54db-8cca-17b64c39e42c"
version = "0.9.16"

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

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

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

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

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

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

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

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

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

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

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

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

[[UnicodeFun]]
deps = ["REPL"]
git-tree-sha1 = "53915e50200959667e78a92a418594b428dffddf"
uuid = "1cfade01-22cf-5700-b092-accc4b62d6e1"
version = "0.4.1"

[[Unzip]]
git-tree-sha1 = "34db80951901073501137bdbc3d5a8e7bbd06670"
uuid = "41fe7b60-77ed-43a1-b4f0-825fd5a5650d"
version = "0.1.2"

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

[[Wayland_protocols_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "4528479aa01ee1b3b4cd0e6faef0e04cf16466da"
uuid = "2381bf8a-dfd0-557d-9999-79630e7b1b91"
version = "1.25.0+0"

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

[[libblastrampoline_jll]]
deps = ["Artifacts", "Libdl", "OpenBLAS_jll"]
uuid = "8e850b90-86db-534c-a0d3-1478176c7d93"
version = "4.0.0+0"

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

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

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

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

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

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

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

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

# ╔═╡ Cell order:
# ╟─52644433-b409-4a1e-9d51-165c073adc5a
# ╟─614cb588-5989-4157-8d49-8b9c7aa39216
# ╟─12ff618e-7539-11ec-2041-713cfd67f6aa
# ╟─dbf90b18-f033-45fc-a040-1de8ba4f2b50
# ╠═9e3f9bda-7e79-411c-b57f-58e0081cb06e
# ╟─af00a498-52c8-47f0-b903-ce69153354cf
# ╟─0e193e6f-5b61-41d0-ba5e-3f7b017d86b4
# ╠═f8ca69eb-023f-408b-aa52-91e843ac2bbf
# ╠═ccae5a24-ad05-4c4c-9d7b-41f704182dd9
# ╟─6958cfa6-8d57-4fb4-8aa6-b78bf2030223
# ╟─3d6d331a-9775-4c59-a0d2-49eb5f7fff94
# ╠═d1d040b0-15c8-4010-bad9-ab854cf4360f
# ╠═9f2a26e9-2c7a-4691-b10a-788316f3bc72
# ╠═c2eb02ae-6610-4e54-8445-33821539b7e9
# ╟─488e8edb-cd8a-450c-ba5a-6173b58974df
# ╟─5293ce4e-a382-4a6a-9788-a73eef01d9ab
# ╠═85aa2baf-a367-48d8-b36d-15c014f48714
# ╟─3c92113a-97d4-495d-974e-acea209e64d7
# ╠═be2f0cea-96fa-40f2-ac7e-117ccd8f41fa
# ╟─d4fe2584-33d1-4c21-b55c-f28e9cf284a5
# ╠═aa49c0cb-b7f2-4578-9ccc-a01a16c05991
# ╟─4273fae9-e2b5-4157-80a2-d269125f02f4
# ╠═4d7d9a9e-6313-4067-940d-8edb71d99a20
# ╟─05875a6f-ecc1-4f41-ac7a-f0d9b41f3511
# ╟─dae26d36-ba42-48d6-904a-a8312fbdc040
# ╟─79fb15a1-b914-4ca7-a898-6d793ab4ce36
# ╟─f4f0952e-8b56-4846-89bd-6427c06ab6b1
# ╠═5922443d-c82d-4368-935c-735be201f52d
# ╟─7a7ceea3-3634-46e1-8619-a394be7d9d32
# ╟─a30259b8-1f0a-4a48-b069-d678482fb112
# ╠═fac902dc-2434-497f-8ee0-46ff24608ef9
# ╟─38e3236b-f2c0-43d3-9b47-53fe50b17852
# ╠═ce458e7a-b903-4784-8500-196a1e3bdced
# ╟─7936bed7-5ac8-4647-ab40-d563099a29a1
# ╟─3d7c6a11-9fc5-4019-b580-acf4a8e03e8c
# ╟─a10ad0f2-81c0-4a5c-9a7b-d9c851d8a46b
# ╟─cdb96618-085d-4e1a-815e-aa25b879d905
# ╟─998fdc0f-b7a6-47c3-aa4b-53085fd36de2
# ╟─55b1859c-97aa-4ffa-bf33-61fd090ca5ae
# ╠═f5312456-e727-4bd5-932a-5bf83f20f78b
# ╟─112b1d3b-cd6d-47c1-a232-6b921541f596
# ╟─66771c27-0f9e-4ee6-8015-57a4c47b3be7
# ╟─e8f1007c-e22e-42df-812a-d0ec931200df
# ╠═7ff78794-6bdb-4f89-a4d6-4ffabefbeeee
# ╟─a06a405d-64f9-46f3-bd9b-d207e79551ca
# ╠═1fd0f3d3-979c-4be4-9f32-ac03fdb62424
# ╟─6ac23a84-9d85-474a-8a50-4cb54df9e441
# ╟─22417be2-3d34-4323-bf86-01aef9240624
# ╠═c921d37f-5e1b-4132-886f-d01c3746892d
# ╠═17bc22bd-3aaa-4b7f-a043-f8b400a13646
# ╠═1381c079-0c2b-49c5-bc66-4daebfa13475
# ╟─edba3e88-b53f-4c14-a98a-9aab036a73cf
# ╠═15437e60-76f7-4f52-91ca-e13a4b003d99
# ╟─889f417c-16d7-4615-8150-370ce3e73382
# ╠═e52ec39b-724a-4b21-9dad-29487e5cafd1
# ╠═a86194d4-9b8c-4c86-b01d-0510c3b7e369
# ╟─09adeac8-6612-4ba2-b77c-1b1188c6a0a2
# ╠═d52d9496-4c7f-415e-84df-e1f5778696bb
# ╠═7b0862d4-2d2d-4373-bed2-8c0dc124a0cc
# ╟─0a519abe-d15f-429e-91b9-9e32b030a97a
# ╟─27c238a3-7e31-483e-b985-20550cf95dd6
# ╟─174e479a-fc34-4192-80b1-b64cc3b24860
# ╟─1ce41e69-0adf-4ced-949e-119012aeb7c3
# ╟─d7151974-80b1-4802-92b8-b05c6dc02016
# ╠═12f5d6ac-8c78-40b3-a431-e7c58e2ae65b
# ╟─f39d3fde-47db-4bdc-b648-501f2aebe4b6
# ╠═3de8c1ce-1e65-4213-88f2-368749d6b036
# ╟─6a103029-8895-40d4-be9c-34ae942a147e
# ╠═369391cd-db66-4495-9e01-30266849369f
# ╟─dba0edaf-002a-48cf-9660-03f20d596068
# ╟─ed72dfaf-3cc9-4bac-9c68-973ce7c41c76
# ╟─a1b81178-5997-4616-9671-e46cab3eedc0
# ╠═f258bd86-0518-4d5e-a240-e19a29be9c92
# ╠═3a3247ad-84c4-4108-af22-971a00cc65a9
# ╟─00000000-0000-0000-0000-000000000001
# ╟─00000000-0000-0000-0000-000000000002

max_stars_repo_path: notebooks/ML-Tutorial 1 - Analyzing the Glass dataset.jl

max_stars_repo_name: roland-KA/JuliaForMLTutorial

max_issues_repo_name: roland-KA/JuliaForMLTutorial

max_forks_repo_name: roland-KA/JuliaForMLTutorial


text:
@doc raw"""
Penning trap with magnetic bottle in (x,y,z) coordinates.
Based on Yanyan Shi, Yajuan Sun, Yulei Wang, Jian Liu, Study of adaptive symplectic methods for
      simulating charged particle dynamics, Journal of Computational Dynamics 6, 429-448, 2019.

The covariant components of the vector potential are given by
```math
A (x,y,z) = B_0 / 2 \, ( -y , x, 0)^T - B_1 \, (xz, yz, (x^2 + y^2)/2 - z^2)^T,
```
resulting in the magnetic field with covariant components
```math
B (x,y,z) = B_0 \, ( 0, 0, 1)^T - B_1 \, ( yz^2 - y^3 / 6, x^3 / 6, xyz )^T ,
```
and the electrostatic potential given by
```math
\varphi (x,y,z) = E_0 \, ( x^2 / 2 + y^2 / 2 - z^2) ,
```
resulting in the electric field with covariant components
```math
E (x,y,z) = E_0 \, ( x, y, - 2 z)^T .
```

Parameters:
* `B₀`: B-field strength
* `Bₚ`: B-field perturbation strength
* `E₀`: E-field strength
"""
module PenningTrapBottle

    using RecipesBase

    import ..ElectromagneticFields
    import ..ElectromagneticFields: CartesianEquilibrium, code
    import ..AnalyticCartesianField: X, Y, Z

    export  PenningTrapBottleEquilibrium

    const DEFAULT_B₀ = 100.0
    const DEFAULT_Bₚ = 200.0
    const DEFAULT_E₀ = 10.0

    struct PenningTrapBottleEquilibrium{T <: Number} <: CartesianEquilibrium
        name::String
        B₀::T
        Bₚ::T
        E₀::T

        function PenningTrapBottleEquilibrium{T}(B₀::T, Bₚ::T, E₀::T) where T <: Number
            new("PenningTrapBottleEquilibrium", B₀, Bₚ, E₀)
        end
    end

    PenningTrapBottleEquilibrium(B₀::T=DEFAULT_B₀, Bₚ::T=DEFAULT_Bₚ, E₀::T=DEFAULT_E₀) where T <: Number = PenningTrapBottleEquilibrium{T}(B₀, Bₚ, E₀)

    function init(B₀=DEFAULT_B₀, Bₚ=DEFAULT_Bₚ, E₀=DEFAULT_E₀)
        PenningTrapBottleEquilibrium(B₀, E₀, Bₚ)
    end

    macro code(B₀=DEFAULT_B₀, Bₚ=DEFAULT_Bₚ, E₀=DEFAULT_E₀)
        code(init(B₀, Bₚ, E₀); escape=true)
    end

    function Base.show(io::IO, equ::PenningTrapBottleEquilibrium)
        print(io, "Penning trap with magnetic bottle in (x,y,z) coordinates with\n")
        print(io, "  B₀ = ", equ.B₀, "\n")
        print(io, "  Bₚ = ", equ.Bₚ, "\n")
        print(io, "  E₀ = ", equ.E₀)
    end

    ElectromagneticFields.A₁(x::AbstractVector, equ::PenningTrapBottleEquilibrium) = - equ.B₀ / 2 * Y(x,equ) - equ.Bₚ * (Y(x,equ) * Z(x,equ)^2 - Y(x,equ)^3 / 6)
    ElectromagneticFields.A₂(x::AbstractVector, equ::PenningTrapBottleEquilibrium) = + equ.B₀ / 2 * X(x,equ) - equ.Bₚ * X(x,equ)^3 / 6
    ElectromagneticFields.A₃(x::AbstractVector, equ::PenningTrapBottleEquilibrium) = - equ.Bₚ * X(x,equ) * Y(x,equ) * Z(x,equ)
    ElectromagneticFields.φ(x::AbstractVector, equ::PenningTrapBottleEquilibrium) = - equ.E₀ * (X(x,equ)^2 / 2 + Y(x,equ)^2 / 2 - Z(x,equ)^2)

    ElectromagneticFields.get_functions(::PenningTrapBottleEquilibrium) = (X=X, Y=Y, Z=Z)

end

ext: jl


text:
using Dates: now
"""
convert an array of bib data into a bib file ready to be used in your latex document.
"""
function bibtex(bibs::Vector{BibInfo}, filename::String)
    open("$(filename).bib", "a") do io
        println("writing results to $(filename).bib")

        write(io, "generated by arXiv.jl on $(now())\n")
        for bib in bibs
            write(io, "\n@article{$(bib.key),\n")
            write(io, "title={$(bib.title)},\n")
            author_list = ""
            for (a, author) in enumerate(bib.authors)
                if a != length(bib.authors)
                    author_list *= "$(author) and "
                else
                    author_list *= "$(author)"
                end
            end
            write(io, "author={$(author_list)},\n")
            write(io, "year={$(bib.year)},\n")
            write(io, "journal={$(bib.journal)},\n")
            write(io, "url={$(bib.url)}\n")
            write(io, "}\n\n")
        end
    end
end


text:
using ArcadeLearningEnvironment
using IICGP
using Test
using Combinatorics
using BenchmarkTools

function generate_white_img(sz::Tuple{Int64,Int64}=(210, 320))
    convert(UInt8, 255) * ones(UInt8, sz)
end

function generate_black_img(sz::Tuple{Int64,Int64}=(210, 320))
    zeros(UInt8, sz)
end

function generate_noisy_img(sz::Tuple{Int64,Int64}=(210, 320))
    rand(collect(UInt8, 0:255), sz)
end

function generate_exp_img_set()
    set = Array{Array{UInt8,2},1}()
    # Min / Max / Random images
    append!(set, [
        generate_black_img(),
        generate_white_img(),
        generate_noisy_img()
    ])
    set
end

function generate_atari_img_set(frame_number::Int64)
    set = Array{Array{UInt8,2},1}()
    #rom_list = setdiff(getROMList(), ["pacman", "surround"]) # All games
    rom_list = ["boxing"]
    for rom in rom_list
        append!(set, IICGP.load_rgb(rom, frame_number))
    end
    set
end

function generate_img_pair_set()
    exp_set = generate_exp_img_set()
    atari_set_30 = generate_atari_img_set(30)
    atari_set_31 = generate_atari_img_set(31)
    exp_set = append!(exp_set, atari_set_30[4:9])
    img_pairs = collect(Combinatorics.combinations(exp_set, 2))
    for i in eachindex(atari_set_30)
        append!(img_pairs, [[atari_set_30[i], atari_set_31[i]]])
    end
    img_pairs
end

function test_inputs(f::Function, inps::AbstractArray; idempotent::Bool=true,
                     is_img::Bool=true)
    for p in [0.0, 0.57, 1.0]
        out = copy(f(inps..., [p]))
        @test size(out) == size(inps[1])
        @test size(out) == size(inps[2])
        if idempotent
            @test all(out == f(inps..., [p]))
        end
        if is_img
            @test typeof(out) == Array{UInt8,2}
            @test all(out .>= 0)
            @test all(out .<= 255)
        else
            @test typeof(out) == Float64
            @test all(out .>= -1.0)
            @test all(out .<= 1.0)
        end
    end
end

function test_img_functions(functions::Array{Function},
                            pairs::Array{Array{Array{UInt8,2},1},1};
                            idempotent::Bool=true)
    for f in functions
        for p in pairs
            test_inputs(f, p, idempotent=idempotent)
        end
    end
end

function test_scalar_functions(functions::Array{Function};
                               idempotent::Bool=true)
    for f in functions
        # println(f)
        test_inputs(f, [-1.0, -1.0], is_img=false)
        test_inputs(f, [0.0, 0.0], is_img=false)
        test_inputs(f, [1e-310, 1e-310], is_img=false)
        test_inputs(f, [-1e-310, -2e-310], is_img=false)
        test_inputs(f, [1.0, 1.0], is_img=false)
        for i in 1:5
            test_inputs(f, [2 * rand() - 1, 2 * rand() - 1], is_img=false)
        end
    end
end

# Fetch functions
idempotent_img_functions = [
    IICGP.CGPFunctions.f_dilate,
    IICGP.CGPFunctions.f_erode,
    IICGP.CGPFunctions.f_subtract,
    IICGP.CGPFunctions.f_remove_details,
    IICGP.CGPFunctions.f_make_boxes,
    IICGP.CGPFunctions.f_felzenszwalb_segmentation,
    IICGP.CGPFunctions.f_components_segmentation,
    IICGP.CGPFunctions.f_box_segmentation,
    IICGP.CGPFunctions.f_threshold,
    IICGP.CGPFunctions.f_binary,
    IICGP.CGPFunctions.f_corners,
    IICGP.CGPFunctions.f_gaussian,
    IICGP.CGPFunctions.f_laplacian,
    IICGP.CGPFunctions.f_sobel_x,
    IICGP.CGPFunctions.f_sobel_y,
    IICGP.CGPFunctions.f_canny,
    IICGP.CGPFunctions.f_edges,
    IICGP.CGPFunctions.f_opening,
    IICGP.CGPFunctions.f_closing,
    IICGP.CGPFunctions.f_tophat,
    IICGP.CGPFunctions.f_bothat,
    IICGP.CGPFunctions.f_morphogradient,
    IICGP.CGPFunctions.f_morpholaplace,
    IICGP.CGPFunctions.f_bitwise_not,
    IICGP.CGPFunctions.f_bitwise_and,
    IICGP.CGPFunctions.f_bitwise_or,
    IICGP.CGPFunctions.f_bitwise_xor
]

non_idempotent_img_functions = [
    IICGP.CGPFunctions.f_motion_capture,
    IICGP.CGPFunctions.f_motion_distances
]

scalar_functions = [
    IICGP.CGPFunctions.f_add,
    IICGP.CGPFunctions.f_subtract,
    IICGP.CGPFunctions.f_mult,
    IICGP.CGPFunctions.f_div,
    IICGP.CGPFunctions.f_abs,
    IICGP.CGPFunctions.f_sqrt,
    IICGP.CGPFunctions.f_pow,
    IICGP.CGPFunctions.f_exp,
    IICGP.CGPFunctions.f_sin,
    IICGP.CGPFunctions.f_cos,
    IICGP.CGPFunctions.f_tanh,
    IICGP.CGPFunctions.f_sqrt_xy,
    IICGP.CGPFunctions.f_lt,
    IICGP.CGPFunctions.f_gt,
    IICGP.CGPFunctions.f_and,
    IICGP.CGPFunctions.f_or,
    IICGP.CGPFunctions.f_xor,
    IICGP.CGPFunctions.f_not
]

@testset "CGP functions for scalars" begin
    test_scalar_functions(scalar_functions, idempotent=true)
end

@testset "CGP functions for images" begin
    pairs = generate_img_pair_set()
    test_img_functions(idempotent_img_functions, pairs, idempotent=true)
    test_img_functions(non_idempotent_img_functions, pairs, idempotent=false)
end

rom_sublist = ["boxing", "freeway", "kung_fu_master", "montezuma_revenge"]

@testset "Motion capture function" begin
    for rom in rom_sublist
        p = rand(1)
        r1, g1, b1 = IICGP.load_rgb(rom, 30)
        r2, g2, b2 = IICGP.load_rgb(rom, 31)

        out1 = IICGP.CGPFunctions.f_motion_capture(r1, g1, p)
        @test out1 == r1
        @test convert(Array{UInt8}, reshape(p, size(r1))) == r1

        out2 = IICGP.CGPFunctions.f_motion_capture(r2, g2, p)
        @test out2 == r2 .- r1
        @test convert(Array{UInt8}, reshape(p, size(r2))) == r2
    end
end

@testset "Motion distance function" begin
    for rom in rom_sublist
        for param in [0.0, 0.1, 0.5, 1.0]
            p = [param]
            r1, g1, b1 = IICGP.load_rgb(rom, 30)
            r2, g2, b2 = IICGP.load_rgb(rom, 31)

            out1 = IICGP.CGPFunctions.f_motion_distances(r1, g1, p)
            @test out1 == r1
            @test convert(Array{UInt8}, reshape(p[end-length(r1)+1:end], size(r1))) == r1

            out2 = IICGP.CGPFunctions.f_motion_distances(r2, g2, p)
            @test convert(Array{UInt8}, reshape(p[end-length(r2)+1:end], size(r2))) == r2
        end
    end
end

function time_functions(functions::Array{Function}, io_type::String)
    if io_type == "img"
        r, g, b = IICGP.load_rgb("freeway", 30)
        inps = [r, g]
    elseif io_type == "scalar"
        inps = [-0.2, 0.33]
    else
        println("I/O type not implemented")
    end
    # n_spaces = maximum([length(string(f)) for f in scalar_functions]) + 1
    println("Functions timing:")
    for f in functions
        f_name = string(f)
        print("| ", f_name, " | ")
        p = [0.33]
        out = f(inps..., p)
        @btime out = $f($inps..., $p)
    end
end

do_time = false
if do_time
    time_functions(scalar_functions, "scalar")
    time_functions(idempotent_img_functions, "img")
    time_functions(non_idempotent_img_functions, "img")
end


text:
# Randomized Nonlinear Component Analysis

function rnca(X)
end


text:
module PVCAM

include("libpvcam/PL.jl")

using .PL
export PL

export camera_name, open_camera, setup_cont, start_cont, stop_cont, latest_frame, polled_cont!
import Base.showerror
#FIXME: global struct
const CAMERA_NUMBER = 0 # default to first camera available
const CAMERA_NAME = Ref{String}()
const CAMERA_HANDLE = Ref{Int16}(-1)
const FRAME_OFFSET = Ref{Int64}(0)
const FULL_FRAME = [PL.rgn_type(0,1199,1,0,1199,1)]
const EXPOSURE_MODE = Ref{Int16}(PL.TIMED_MODE)
const CIRC_BUFFER = Ref{Vector{UInt16}}()
const CB_START = Ref{Ptr{UInt16}}(0)

struct PVCAMError <: Exception
code::Int16
end

function Base.showerror(io::IO, error::PVCAMError)
    message = Vector{UInt8}(undef, PL.ERROR_MSG_LEN)
    PL.error_message(error.code, message)
    message[end] = 0
    print(io, unsafe_string(pointer(message)))
end

check_error() = throw(PVCAMError(PL.error_code()))

function __init__()
    PL.pvcam_init() || check_error()
    CAMERA_NAME[] = camera_name(CAMERA_NUMBER)
    CAMERA_HANDLE[] = open_camera(CAMERA_NAME[])
    atexit(PL.pvcam_uninit)
end

function camera_name(cam_num::Integer)
    name = Vector{UInt8}(undef, PL.CAM_NAME_LEN)
    PL.cam_get_name(cam_num, name) || check_error()
    name[end] = 0
    return unsafe_string(pointer(name))
end

function open_camera(camera_name::String)
    camera_handle = Ref{Int16}()
    open_mode = Int16(PL.OPEN_EXCLUSIVE)
    PL.cam_open(camera_name, camera_handle, open_mode) || check_error()
    return camera_handle[]
end

function setup_cont(camera_handle = CAMERA_HANDLE[], region_array = FULL_FRAME, exp_mode = EXPOSURE_MODE[], exposure_time = 40)::Int64
    exp_bytes = Ref{UInt32}()
    num_regions = length(region_array)
    PL.exp_setup_cont(camera_handle, num_regions, region_array, exp_mode, exposure_time, exp_bytes, PL.CIRC_OVERWRITE) || check_error()
    return exp_bytes[]
end

function start_cont(camera_handle, circ_buffer_size = length(CIRC_BUFFER[]) * sizeof(UInt16))
    PL.exp_start_cont(camera_handle, CIRC_BUFFER[], circ_buffer_size) || check_error()
    return @async println("Continuous acquisition started...")
end

function get_latest_frame_ptr(camera_h)
    frame_address = Ref{Ptr{UInt16}}(0)
    PL.exp_get_latest_frame(camera_h, frame_address) || check_error()
    return frame_address[]
end

function stop_cont(camera_h = CAMERA_HANDLE[])
    PL.exp_stop_cont(camera_h, PL.CCS_CLEAR) || check_error()
    return @async println("Stopped continuous acquisition.")
end

function latest_frame(camera_h = CAMERA_HANDLE[], start_ptr = CB_START[])
    current_ptr = get_latest_frame_ptr(camera_h)
    start_idx = (current_ptr - start_ptr) ÷ sizeof(UInt16) + 1
    stop_idx = start_idx + FRAME_OFFSET[]
    return CIRC_BUFFER[][start_idx:stop_idx] #FIXME: find zero-copy solution
end

function polled_cont!(; camera_handle = CAMERA_HANDLE[], regions = FULL_FRAME, exposure_mode = EXPOSURE_MODE[], exposure_time = 40)
    exposure_bytes = setup_cont(camera_handle, regions, exposure_mode, exposure_time)
    nframes = 20
    CIRC_BUFFER[] = Vector{UInt16}(undef, (nframes * exposure_bytes) ÷ sizeof(UInt16))
    FRAME_OFFSET[] = exposure_bytes ÷ sizeof(UInt16) - 1
    CB_START[] = pointer(CIRC_BUFFER[])
    start_cont(camera_handle)
    return
end

function initialize_cont()
    polled_cont!()
    sleep(0.5) # arbitrary length pause: we need to pause while we wait for acquisition to get rolling
    retry(latest_frame, delays = fill(0.5, 10))()
end

end # module


text:
include("../src/LightGraphsMatching.jl")
using LightGraphs
using LightGraphsMatching
using Base.Test
using Cbc: CbcSolver

g = CompleteGraph(4)
w = LightGraphsMatching.default_weights(g)
@test all((w + w') .≈ ones(4,4) - eye(4,4))

w1 = [
    1 3
    5 1
]
w0 = [
    0 3
    5 0
]
@test all(w0 .≈ LightGraphsMatching.cutoff_weights(w1, 2))

g = CompleteGraph(3)
w = [
    1 2 1
    1 1 1
    3 1 1
]
match = maximum_weight_matching(g, CbcSolver(), w)
@test match.mate[1] == 3
@test match.weight ≈ 3

g = CompleteBipartiteGraph(2,2)
w = zeros(4,4)
w[1,3] = 10.
w[1,4] = 1.
w[2,3] = 2.
w[2,4] = 11.
match = maximum_weight_maximal_matching(g, CbcSolver(), w)
@test match.weight ≈ 21
@test match.mate[1] == 3
@test match.mate[3] == 1
@test match.mate[2] == 4
@test match.mate[4] == 2

g =CompleteBipartiteGraph(2,4)
w =zeros(6,6)
w[1,3] = 10
w[1,4] = 0.5
w[2,3] = 11
w[2,4] = 1
match = maximum_weight_maximal_matching(g, CbcSolver(), w)
@test match.weight ≈ 11.5
@test match.mate[1] == 4
@test match.mate[4] == 1
@test match.mate[2] == 3
@test match.mate[3] == 2

g =CompleteBipartiteGraph(2,6)
w =zeros(8,8)
w[1,3] = 10
w[1,4] = 0.5
w[2,3] = 11
w[2,4] = 1
w[2,5] = -1
w[2,6] = -1
match = maximum_weight_maximal_matching(g,CbcSolver(),w,0)
@test match.weight ≈ 11.5
@test match.mate[1] == 4
@test match.mate[4] == 1
@test match.mate[2] == 3
@test match.mate[3] == 2

g =CompleteBipartiteGraph(4,2)
w = zeros(6,6)
w[3,5] = 10
w[3,6] = 0.5
w[2,5] = 11
w[1,6] = 1
w[1,5] = -1

match = maximum_weight_maximal_matching(g,CbcSolver(),w,0)
@test match.weight ≈ 12
@test match.mate[1] == 6
@test match.mate[2] == 5
@test match.mate[3] == -1
@test match.mate[4] == -1
@test match.mate[5] == 2
@test match.mate[6] == 1

g = CompleteGraph(3)
w = zeros(3,3)
w[1,2] = 1
w[3,2] = 1
w[1,3] = 1
match = maximum_weight_matching(g,CbcSolver(),w)
@test match.weight ≈ 1

g = Graph(4)
add_edge!(g, 1,3)
add_edge!(g, 1,4)
add_edge!(g, 2,4)

w =zeros(4,4)
w[1,3] = 1
w[1,4] = 3
w[2,4] = 1

match = maximum_weight_matching(g,CbcSolver(),w)
@test match.weight ≈ 3
@test match.mate[1] == 4
@test match.mate[2] == -1
@test match.mate[3] == -1
@test match.mate[4] == 1

g = Graph(4)
add_edge!(g, 1,2)
add_edge!(g, 2,3)
add_edge!(g, 3,1)
add_edge!(g, 3,4)
match = maximum_weight_matching(g,CbcSolver())
@test match.weight ≈ 2
@test match.mate[1] == 2
@test match.mate[2] == 1
@test match.mate[3] == 4
@test match.mate[4] == 3

w = zeros(4,4)
w[1,2] = 1
w[2,3] = 1
w[1,3] = 1
w[3,4] = 1

match = maximum_weight_matching(g,CbcSolver(), w)
@test match.weight ≈ 2
@test match.mate[1] == 2
@test match.mate[2] == 1
@test match.mate[3] == 4
@test match.mate[4] == 3

w = zeros(4,4)
w[1,2] = 1
w[2,3] = 1
w[1,3] = 5
w[3,4] = 1

match = maximum_weight_matching(g,CbcSolver(),w)
@test match.weight ≈ 5
@test match.mate[1] == 3
@test match.mate[2] == -1
@test match.mate[3] == 1
@test match.mate[4] == -1

w = Dict(Edge(1,2)=> 500)
g =Graph(2)
add_edge!(g,1,2)
match = minimum_weight_perfect_matching(g, w)
@test match.mate[1] == 2

w=Dict( Edge(1,2)=>500,
        Edge(1,3)=>600,
        Edge(2,3)=>700,
        Edge(3,4)=>100,
        Edge(2,4)=>1000)

g = CompleteGraph(4)
match = minimum_weight_perfect_matching(g, w)
@test match.mate[1] == 2
@test match.mate[2] == 1
@test match.mate[3] == 4
@test match.mate[4] == 3
@test match.weight ≈ 600

w = Dict(
        Edge(1, 2) => 500,
        Edge(1, 3) => 400,
        Edge(2, 3) => 300,
        Edge(3, 4) => 1000,
        Edge(2, 4) => 1000
    )
g = CompleteGraph(4)
match = minimum_weight_perfect_matching(g, w)
@test match.mate[1] == 3
@test match.mate[2] == 4
@test match.mate[3] == 1
@test match.mate[4] == 2
@test match.weight ≈ 1400

g =CompleteBipartiteGraph(2,2)
w =Dict{Edge,Float64}()
w[Edge(1,3)] = -10
w[Edge(1,4)] = -0.5
w[Edge(2,3)] = -11
w[Edge(2,4)] = -1

match = minimum_weight_perfect_matching(g, w)
@test match.mate[1] == 4
@test match.mate[4] == 1
@test match.mate[2] == 3
@test match.mate[3] == 2
@test match.weight ≈ -11.5

g =CompleteGraph(4)
w =Dict{Edge,Float64}()
w[Edge(1,3)] = 10
w[Edge(1,4)] = 0.5
w[Edge(2,3)] = 11
w[Edge(2,4)] = 2
w[Edge(1,2)] = 100

match = minimum_weight_perfect_matching(g, w, 50)
@test match.mate[1] == 4
@test match.mate[4] == 1
@test match.mate[2] == 3
@test match.mate[3] == 2
@test match.weight ≈ 11.5


text:
struct allchoices{T,V<:AbstractVector{T}}
    choices::Vector{V}
    dims::Vector{Int}
end
function allchoices(choices::Vector{V}) where {T,V<:AbstractVector{T}}
    allchoices(choices, length.(choices))
end
Base.length(ac::allchoices) = prod(ac.dims)
Base.eltype(ac::allchoices{T}) where T = Vector{T}

function Base.iterate(ac::allchoices{T,V}, (el,i)=(first.(ac.choices),ones(Int,length(ac.dims)))) where {T,V}
    i == 0 && return nothing
    finish_next = false
    for j ∈ reverse(eachindex(i))
        i[j] += 1
        if i[j] > ac.dims[j]
            j == 1 && (finish_next = true)
            i[j] = 1
        else
            break
        end
    end
    el, ([c[ii] for (c,ii) in zip(ac.choices,i)], finish_next ? 0 : i)
end


text:
# -*- coding: utf-8 -*-
using Catlab
using Catlab.Doctrines
import TikzPictures
import Catlab.Graphics: to_tikz
import SemanticModels.ModelTools.RelOlogModels: RelOlogModel, model, ⊚, ⊗
using SemanticModels.ModelTools.CategoryTheory
import SemanticModels.ModelTools.CategoryTheory: ⊔, FinSetMorph

# ### Defining a Relational Olog For SEIR

# +
@present seir(FreeBicategoryRelations) begin
    S::Ob
    E::Ob
    I::Ob
    R::Ob

    exposes::Hom(I, S)
    becomes_exposed::Hom(S, E)
    falls_ill::Hom(E, I)
    recovers_to::Hom(I, R)

    illness := compose(becomes_exposed, falls_ill)
    exposure := compose(exposes, becomes_exposed)
end

to_tikz(reduce(⊗, generators(seir, FreeBicategoryRelations.Hom)); arrowtip="Stealth", labels=true)
# -

for i in equations(seir)
    display(i[1])
    display(to_tikz(i[2]; arrowtip="Stealth", labels=true))
end


text:
using Pkg
tempdir = mktempdir()
Pkg.activate(tempdir)
Pkg.develop(PackageSpec(path=joinpath(@__DIR__, "..")))
Pkg.add(["BenchmarkTools", "PkgBenchmark", "Random"])
Pkg.resolve()

using DataStructures
using BenchmarkTools

const SUITE = BenchmarkGroup()

SUITE["CircularBuffer"] = include("bench_circular_buffer.jl")
SUITE["heap"] = include("bench_heap.jl")
SUITE["SparseIntSet"] = include("bench_sparse_int_set.jl")


text:
"""
    logit10(x::Real)
Function transforming interval [0,1] to [-Inf, Inf] using logit transformation.
"""
function logit10(x::Real)
    log10(x / (1.0 - x))
end

"""
    logistic10(x::Real)
Function transforming interval [-Inf, Inf] to [0,1] using logistic transformation.
Inverse function for [`logit10`](@ref).
"""
function logistic10(x::Real)
    exp10(x) / (exp10(x) + 1.0)
end

"""
    scaling(x::Real, scale::Symbol = :direct)
Transforms values from specific scale to range [-Inf, Inf] based on option.

## Return
Transformed value.

## Arguments
* `x`: input value.
* `scale`: transformation type: `:direct, :log, :logit`.
"""
function scaling(x::Real, scale::Symbol = :direct)
    if scale == :direct
        return x
    elseif scale == :log
        return log10(x)
    elseif scale == :logit
        return logit10(x)
    else
        throw(DomainError(scale, "scale type is not supported"))
    end
end

"""
    unscaling(x::Real, scale::Symbol = :direct)
Transforms values from [-Inf, Inf] to specific scale based on option. Inverse function
for [`scaling`](@ref).

## Return
Transformed value.

## Arguments
* `x`: input value.
* `scale`: transformation type: `:direct, :log, :logit`.
"""
function unscaling(x::Real, scale::Symbol = :direct)
    if scale == :direct
        return x
    elseif scale == :log
        return exp10(x)
    elseif scale == :logit
        return logistic10(x)
    else
        throw(DomainError(scale, "scale type is not supported"))
    end
end

scaling(x_tup::Tuple{Float64,Float64}, scale::Symbol = :direct) = scaling.(x_tup, scale)

unscaling(x_tup::Tuple{Float64,Float64}, scale::Symbol = :direct) = unscaling.(x_tup, scale)

scaling(::Nothing, ::Symbol) = nothing

unscaling(::Nothing, ::Symbol) = nothing

"""
    function get_endpoint(
        theta_init::Vector{Float64},
        theta_num::Int,
        loss_func::Function,
        method::Symbol,
        direction::Symbol = :right;

        loss_crit::Float64 = 0.0,
        scale::Vector{Symbol} = fill(:direct, length(theta_init)),
        theta_bounds::Vector{Tuple{Float64,Float64}} = unscaling.(
            fill((-Inf, Inf), length(theta_init)),
            scale
            ),
        scan_bound::Float64 = unscaling(
            (direction==:left) ? -9.0 : 9.0,
            scale[theta_num]
            ),
        scan_tol::Float64 = 1e-3,
        loss_tol::Float64 = 1e-3,
        local_alg::Symbol = :LN_NELDERMEAD,
        kwargs...
        )

Calculates confidence interval's right or left endpoints for a given parameter `theta_num`.

## Return
[`EndPoint`](@ref) object storing confidence interval's endpoint and intermediate profile points.

## Arguments
- `theta_init`: starting values of parameter vector ``\\theta``. The starting values should not necessary be the optimum values of `loss_func` but `loss_func(theta_init)` should be lower than `loss_crit`.
- `theta_num`: index of vector component for identification: `theta_init(theta_num)`.
- `loss_func`: loss function ``\\Lambda\\left(\\theta\\right)`` for profile likelihood-based (PL) identification. Usually we use log-likelihood for PL analysis: ``\\Lambda( \\theta ) = - 2 ln\\left( L(\\theta) \\right)``.
- `method`: computational method to estimate confidence interval's endpoint. Currently the following methods are implemented: `:CICO_ONE_PASS`, `:LIN_EXTRAPOL`, `:QUADR_EXTRAPOL`.
- `direction`: `:right` or `:left` endpoint to estimate.

## Keyword arguments
see [`get_interval`](@ref)

"""
function get_endpoint(
    theta_init::Vector{Float64},
    theta_num::Int,
    loss_func::Function,
    method::Symbol,
    direction::Symbol = :right;

    loss_crit::Float64 = 0.0,
    # :direct, :log, :logit
    scale::Vector{Symbol} = fill(:direct, length(theta_init)),
    theta_bounds::Vector{Tuple{Float64,Float64}} = unscaling.(
        fill((-Inf, Inf), length(theta_init)),
        scale
        ),
    scan_bound::Float64 = unscaling(
        (direction==:left) ? -9.0 : 9.0,
        scale[theta_num]
        ),
    scan_tol::Float64 = 1e-3,
    loss_tol::Float64 = 1e-3,
    local_alg::Symbol = :LN_NELDERMEAD,
    kwargs... # other options for get_right_endpoint
    )
    isLeft = direction == :left

    # checking arguments
    # theta_bound[1] < theta_init < theta_bound[2]
    theta_init_outside_theta_bounds = .! [theta_bounds[i][1] < theta_init[i] < theta_bounds[i][2] for i in 1:length(theta_init)]
    if any(theta_init_outside_theta_bounds)
        throw(ArgumentError("theta_init is outside theta_bound: $(findall(theta_init_outside_theta_bounds))"))
    end
    # scan_bound should be within theta_bounds
    !(theta_bounds[theta_num][1] < scan_bound < theta_bounds[theta_num][2]) &&
        throw(ArgumentError("scan_bound are outside of the theta_bounds $(theta_bounds[theta_num])"))
    # theta_init should be within scan_bound
    if (theta_init[theta_num] >= scan_bound && !isLeft) || (theta_init[theta_num] <= scan_bound && isLeft)
        throw(ArgumentError("init values are outside of the scan_bound $scan_bound"))
    end
    # 0 <= theta_bound[1] for :log
    less_than_zero_theta_bounds = (scale .== :log) .& [theta_bounds[i][1] < 0 for i in 1:length(theta_init)]
    if any(less_than_zero_theta_bounds)
        throw(ArgumentError(":log scaled theta_bound min is negative: $(findall(less_than_zero_theta_bounds))"))
    end
    # 0 <= theta_bounds <= 1 for :logit
    less_than_zero_theta_bounds = (scale .== :logit) .& [theta_bounds[i][1] < 0 || theta_bounds[i][2] > 1 for i in 1:length(theta_init)]
    if any(less_than_zero_theta_bounds)
        throw(ArgumentError(":logit scaled theta_bound min is outside range [0,1]: $(findall(less_than_zero_theta_bounds))"))
    end
    # loss_func(theta_init) < loss_crit
    !(loss_func(theta_init) < loss_crit) &&
        throw(ArgumentError("Check theta_init and loss_crit: loss_func(theta_init) should be < loss_crit"))

    # set counter in the scope
    counter::Int = 0
    # set supreme, maximal or minimal value of scanned parameter inside critical
    supreme_gd = nothing

    # transforming
    theta_init_gd = scaling.(theta_init, scale)
    if isLeft theta_init_gd[theta_num] *= -1 end # change direction
    function loss_func_gd(theta_gd)
        theta_g = copy(theta_gd)
        if isLeft theta_g[theta_num] *= -1 end # change direction
        theta = unscaling.(theta_g, scale)
        # calculate function
        loss_norm = loss_func(theta) - loss_crit

        # update counter
        counter += 1
        # update supreme ?
        update_supreme = (loss_norm < 0.) &&
            (typeof(supreme_gd)==Nothing || (theta_gd[theta_num] > supreme_gd))
        if update_supreme
            supreme_gd = theta_gd[theta_num]
        end

        return loss_norm
    end
    theta_bounds_gd = scaling.(theta_bounds, scale)
    if isLeft theta_bounds_gd[theta_num] = (-1*theta_bounds_gd[theta_num][2], -1*theta_bounds_gd[theta_num][1]) end # change direction
    scan_bound_gd = scaling(scan_bound, scale[theta_num])
    if isLeft scan_bound_gd *= -1 end # change direction

    # calculate endpoint using base method
    (optf_gd, pp_gd, status) = get_right_endpoint(
        theta_init_gd,
        theta_num,
        loss_func_gd,
        Val(method);
        theta_bounds = theta_bounds_gd,
        scan_bound = scan_bound_gd,
        scan_tol = scan_tol,
        loss_tol = loss_tol,
        local_alg = local_alg,
        kwargs... # options for local fitter
    )

    # transforming back
    if (isLeft && typeof(optf_gd)!==Nothing) optf_gd *= -1 end # change direction
    optf = unscaling(optf_gd, scale[theta_num])
    temp_fun = (pp::ProfilePoint) -> begin
        if isLeft pp.params[theta_num] *= -1 end # change direction
        ProfilePoint(
            unscaling(pp.params[theta_num], scale[theta_num]),
            pp.loss + loss_crit,
            unscaling.(pp.params, scale),
            pp.ret,
            pp.counter
        )
    end
    pps = [ temp_fun(pp_gd[i]) for i in 1:length(pp_gd) ]
    # transforming supreme back
    if (isLeft && typeof(supreme_gd)!==Nothing) supreme_gd *= -1 end # change direction
    supreme = unscaling(supreme_gd, scale[theta_num])

    EndPoint(optf, pps, status, direction, counter, supreme)
end

"""
    function get_endpoint(
        theta_init::Vector{Float64},
        scan_func::Function,
        loss_func::Function,
        method::Symbol,
        direction::Symbol = :right;

        loss_crit::Float64 = 0.0,
        scale::Vector{Symbol} = fill(:direct, length(theta_init)),
        theta_bounds::Vector{Tuple{Float64,Float64}} = unscaling.(
            fill((-Inf, Inf), length(theta_init)),
            scale
            ),
        scan_bound::Float64 = unscaling(
            (direction==:left) ? -9.0 : 9.0,
            scale[theta_num]
            ),
        scan_tol::Float64 = 1e-3,
        loss_tol::Float64 = 1e-3,
        local_alg::Symbol = :LN_NELDERMEAD,
        kwargs...
        )

Calculates confidence interval's right or left endpoints for a function of parameters `scan_func`.

## Return
[`EndPoint`](@ref) object storing confidence interval's endpoint and intermediate profile points.

## Arguments
- `theta_init`: starting values of parameter vector ``\\theta``. The starting values should not necessary be the optimum values of `loss_func` but `loss_func(theta_init)` should be lower than `loss_crit`.
- `scan_func`: function of parameters.
- `loss_func`: loss function ``\\Lambda\\left(\\theta\\right)`` for profile likelihood-based (PL) identification. Usually we use log-likelihood for PL analysis: ``\\Lambda( \\theta ) = - 2 ln\\left( L(\\theta) \\right)``.
- `method`: computational method to estimate confidence interval's endpoint. Currently the only supported method is: `:CICO_ONE_PASS`.
- `direction`: `:right` or `:left` endpoint to estimate.

## Keyword arguments
see [`get_interval`](@ref)

"""
function get_endpoint(
    theta_init::Vector{Float64},
    scan_func::Function,
    loss_func::Function,
    method::Symbol,
    direction::Symbol = :right;

    loss_crit::Float64 = 0.0,
    # :direct, :log, :logit
    scale::Vector{Symbol} = fill(:direct, length(theta_init)),
    theta_bounds::Vector{Tuple{Float64,Float64}} = unscaling.(
        fill((-Inf, Inf), length(theta_init)),
        scale
        ),
    scan_bound::Float64 = (direction==:left) ? -1e9 : 1e9, # log scan bound is not implemented
    scan_tol::Float64 = 1e-3,
    loss_tol::Float64 = 1e-3,
    local_alg::Symbol = :LN_NELDERMEAD,
    kwargs... # other options for get_right_endpoint
    )
    isLeft = direction == :left

    # checking arguments
    # theta_bound[1] < theta_init < theta_bound[2]
    theta_init_outside_theta_bounds = .! [theta_bounds[i][1] < theta_init[i] < theta_bounds[i][2] for i in 1:length(theta_init)]
    if any(theta_init_outside_theta_bounds)
        throw(ArgumentError("theta_init is outside theta_bound: $(findall(theta_init_outside_theta_bounds))"))
    end

    # 0 <= theta_bound[1] for :log
    less_than_zero_theta_bounds = (scale .== :log) .& [theta_bounds[i][1] < 0 for i in 1:length(theta_init)]
    if any(less_than_zero_theta_bounds)
        throw(ArgumentError(":log scaled theta_bound min is negative: $(findall(less_than_zero_theta_bounds))"))
    end
    # 0 <= theta_bounds <= 1 for :logit
    less_than_zero_theta_bounds = (scale .== :logit) .& [theta_bounds[i][1] < 0 || theta_bounds[i][2] > 1 for i in 1:length(theta_init)]
    if any(less_than_zero_theta_bounds)
        throw(ArgumentError(":logit scaled theta_bound min is outside range [0,1]: $(findall(less_than_zero_theta_bounds))"))
    end
    # loss_func(theta_init) < loss_crit
    !(loss_func(theta_init) < loss_crit) &&
        throw(ArgumentError("Check theta_init and loss_crit: loss_func(theta_init) should be < loss_crit"))

    # set counter in the scope
    counter::Int = 0
    # set supreme, maximal or minimal value of scanned parameter inside critical
    supreme_gd = nothing

    # transforming
    theta_init_gd = scaling.(theta_init, scale)

    function scan_func_gd(theta_gd)
        theta_g = copy(theta_gd)
        theta = unscaling.(theta_g, scale)
        scan_val = scan_func(theta)
        scan_val_gd = isLeft ? (-1)*scan_val : scan_val

        return scan_val_gd
    end

    function loss_func_gd(theta_gd)
        theta_g = copy(theta_gd)
        theta = unscaling.(theta_g, scale)
        # calculate function
        loss_norm = loss_func(theta) - loss_crit

        # update counter
        counter += 1
        # update supreme ?
        #scan_val = scan_func(theta)
        #scan_val_gd = isLeft ? (-1)*scan_val : scan_val
        #update_supreme = (loss_norm < 0.) &&
        #    (typeof(supreme_gd)==Nothing || (scan_val_gd > supreme_gd))
        #if update_supreme
        #    supreme_gd = scan_val_gd
        #end

        return loss_norm
    end
    theta_bounds_gd = scaling.(theta_bounds, scale)

    # TODO: transformed by scan_scale: scaling(scan_bound, scan_scale)
    scan_bound_gd = scan_bound
    if isLeft scan_bound_gd *= -1 end # change direction

    # calculate endpoint using base method
    (optf_gd, pp_gd, status) = get_right_endpoint(
        theta_init_gd,
        scan_func_gd,
        loss_func_gd,
        Val(method);
        theta_bounds = theta_bounds_gd,
        scan_bound = scan_bound_gd,
        scan_tol = scan_tol,
        loss_tol = loss_tol,
        local_alg = local_alg,
        kwargs... # options for local fitter
    )

    # transforming back
    temp_fun = (pp::ProfilePoint) -> begin
        value = isLeft ? (-1)*pp.value : pp.value # TODO: scan_scale
        ProfilePoint(
            value,
            pp.loss + loss_crit,
            unscaling.(pp.params, scale),
            pp.ret,
            pp.counter
        )
    end
    pps = [ temp_fun(pp_gd[i]) for i in 1:length(pp_gd) ]
    # transforming supreme back
    if (isLeft && typeof(supreme_gd)!==Nothing) supreme_gd *= -1 end # change direction
    supreme = unscaling(supreme_gd, :direct) # scan_scale ??

    if (isLeft && typeof(optf_gd)!==Nothing) optf_gd *= -1 end # change direction
    # optf = unscaling(optf_gd, scan_scale)
    optf = optf_gd

    EndPoint(optf, pps, status, direction, counter, supreme)
end


text:
# Run tests

using SafeTestsets
using Test
using DiscreteChoiceModels
using MacroTools

@testset "Multinomial logit" begin
    include("mnl/mnl.jl")
end

@testset "Macros" begin
    include("macro.jl")
end