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

using Markdown
using InteractiveUtils

# ╔═╡ 889697ff-7f5a-4106-95f7-ca7a294faeb3
begin
	using JSServe
	Page()
end

# ╔═╡ 6946373c-8035-485b-9048-7b4d0fc6428f
using WGLMakie, Distributions, LinearAlgebra

# ╔═╡ cf1c3faa-1973-11ec-04bf-1f53406ce76b
md"""
# Chapter 1: Landmark location

For our first experiment, let's estimate the location of a **landmark**, some feature that we want to keep track of.

The true position of the landmark is a two-dimensional vector

```math
\mathbf{s} = \begin{bmatrix}
x_0 \\
y_0
\end{bmatrix}
```

At every time step, we'll take a measurement ``\mathbf{z}_t`` of the location of the landmark. This measurement will necessarily have some noise in it. We'll model the noise with an additive Gaussian term:

```math
\mathbf{z}_t = \mathbf{s} + \varepsilon_t
```

where the noise ``\varepsilon_t`` is multivariate normally distributed with a mean of zero and a covariance matrix Q:

```math
\varepsilon_t \sim \mathcal{N}(0,Q)
```

Because the noise is additive and Gaussian, we can write the distribution of each observation ``\mathbf{z}_t`` as

```math
\mathbf{z}_t | \mathbf{s} \sim \mathcal{N}(\mathbf{s},Q)
```

This is a *conditional distribution*: if we fix the position of the landmark at ``\mathbf{s}``, then the observations are independently and identically distributed with a multivariate normal distribution with mean ``\mathbf{s}`` and covariance $Q$.

To see what this model looks like, let's generate a data set. We'll fix our landmark at the origin (``\mathbf{s} = [0,0]``) and create some observations. Let's assume that the covariance matrix is diagonal with a variance of 1 for each component:

```math
Q = \begin{bmatrix}
1 & 0 \\
0 & 1
\end{bmatrix}
```
"""

# ╔═╡ 592cb2f4-7c38-4ecd-b0fc-50d9e409f928
# Landmark position
s = [0.0;0.0]

# ╔═╡ 3f91f4e8-e0c9-4f1f-a5a0-7cdfc8bb9d6b
# Covariance matrix
Q = [1.0 0.0;
	0.0 1.0]

# ╔═╡ 4c06bac4-05b8-4883-b3ca-3da505783fdc
# Distribution of observations
d = MvNormal(s,Q)

# ╔═╡ 54ec7d97-477e-4887-a73a-502eeba97910
# Number of observations
N = 10

# ╔═╡ 7c36f709-7bb0-4986-8337-e6dea6fc7690
# Observation data set
Z = rand(d,N)

# ╔═╡ bc925405-7574-46d4-aa59-7e32bc1acaf7
begin
	fig = Figure()
	ax1 = Axis(fig[1,1],xlabel="X",ylabel="Y")
	scatter!(ax1,Z,color=:black)
	scatter!(ax1,Point2[s],color=:red)
	fig
end

# ╔═╡ 94c08534-71fd-4265-9212-edf76b4517bf
md"""
If we don't know the position of the landmark, but we do have the measurements ``\mathbf{z}_{1:T} = [\mathbf{z}_1,\mathbf{z}_2,\dots,\mathbf{z}_T]``, how do we go about estimating the landmark's position? There are many different ways to do this, and we'll work up from the simplest one: maximum likelihood estimation.

## Maximum likelihood estimation

Recall our probabilistic model for the observations:

```math
\mathbf{z}_t | \mathbf{s} \sim \mathcal{N}(\mathbf{s},Q)
```

This model defines a probability distribution for the whole data set, ``p(\mathbf{z}_{1:T}|\mathbf{s})``.

```math
p(\mathbf{z}_{1:T}|\mathbf{s}) = \prod_{t=1}^T p(\mathbf{z}_t|\mathbf{s}) \propto \prod_{t=1}^T e^{-\frac{1}{2} \left(\mathbf{z}_t - \mathbf{s}\right)^\dagger Q^{-1} \left(\mathbf{z}_t - \mathbf{s}\right)}
```

The first equality here is because we assume that our observations are independently and identically distributed, and thus the joint distribution of the observations ``p(\mathbf{z}_{1:T}|\mathbf{s})`` factors into the product of the distribution of the individual observations. The second equality substitutes the expression for a multivariate normal distribution. We use the ``\propto`` symbol to indicate that we have dropped some terms that don't depend on ``\mathbf{z}_t$ or $\mathbf{s}``.

For convenience, we'll work with the logarithm of this distribution, which is called the (log-)likelihood function

```math
\log p(\mathbf{z}_{1:T}|\mathbf{s}) = \sum_{t=1}^T \log p(\mathbf{z}_t|\mathbf{s}) = \sum_{t=1}^T -\frac{1}{2} \left(\mathbf{z}_t - \mathbf{s}\right)^\dagger Q^{-1} \left(\mathbf{z}_t - \mathbf{s}\right)
```

To estimate ``\mathbf{s}`` by maximum likelihood, we want to find the value of ``\mathbf{s}`` that maximizes this expression when we fix our observations ``\mathbf{z}_{1:T}`` to our dataset. We can find the maximum likelihood estimator, ``\mathbf{s}``, by taking the derivative of the likelihood function with respect to ``\mathbf{s}`` and setting the resulting expression equal to zero. Once we've worked through the algebra, we find that

```math
\hat{\mathbf{s}} = \frac{1}{T}\sum_{t=1}^T \mathbf{z}_t
```

and we see that the maximum likelihood estimator of the position of the landmark is the mean of the observations. Let's plot that along with our dataset.
"""

# ╔═╡ e4b53a36-860b-4c3a-b9c9-6f6eafcaeeb9
# Compute the maximum likelihood estimator
ŝ = mean(Z,dims=2)[:,1]

# ╔═╡ 8b342e24-759b-4ce4-9349-1c46aebe6cad
begin
	fig2 = Figure()
	ax2 = Axis(fig2[1,1],xlabel="X",ylabel="Y")
	scatter!(ax2,Z,color=:black)
	scatter!(ax2,Point2[s],color=:red)
	scatter!(ax2,Point2[ŝ],color=:orange)
	fig2
end

# ╔═╡ dbf4d333-b34a-40ce-b6cf-3a17a20cda8f
md"""
There is a difference between the maximum likelihood estimator and the true landmark position. We will always have some error, and the magnitude of that error basically depends on how many observations we have. With more observations, the average error will shrink and, as the number of observations approaches infinity, the error will go to zero: our maximum likelihood estimator is *consistent*.

We can illustrate this property by simulating a bunch of data sets of different sizes and computing the root mean squared error between the maximum likelihood estimator and the true landmark position.
"""

# ╔═╡ 6312899f-e92b-42d6-977a-fe8fe8b63011
function rms_error_simulation(N)
    # Simulate a new data set of size N
    Z = rand(d,N)

    # Compute the maximum likelihood estimator of the landmark
    ŝ = mean(Z,dims=2)[:,1]

    # Return the distance between s and ŝ
    sqrt(sum(abs2,s .- ŝ))
end

# ╔═╡ 62c45184-2e26-4186-9f16-0b4d33944053
# Simulate 100 times for each value of N from 10 to 10000
errors = [mean(rms_error_simulation(N) for i in 1:100) for N in 10:100:10000]

# ╔═╡ 20610ddc-5160-458d-badd-850046a087e2
begin
	fig3 = Figure()
	ax3 = Axis(fig3[1,1],
    xlabel="Number of observations",
    ylabel="Root mean square error"
	)
	lines!(ax3,10:100:10000,errors)
	fig3
end

# ╔═╡ 12a3d2e5-7f9f-4b8b-94d4-87911e8ad699
md"""
The error decreases with the number of observations and, while it does flatten out as the number of observations increases, it will indeed go to zero as ``N \to \infty`` (it decreases like ``\frac{1}{\sqrt{N}}``).

The maximum likelihood estimator of the landmark location is consistent, and it is really easy to compute: just take the mean of the observations. However, we'll find that the maximum likelihood estimator is not so easy to compute in the more complicated models that we'll encounter in the future. We'll turn to Bayesian inference methods to handle these more complicated models, and we'll start our exploration of Bayesian methods by thinking about the landmark location model sequentially.

## Sequential inference

In the localization and mapping problems that we'll encounter throughout this series, we acquire observations at separate times, and we want our robot to incorporate each observation into its map of the environment when that observation is acquired. We can do this with our maximum likelihood estimator, ``\hat{\mathbf{s}}``. We say that the maximum likelihood estimate of the landmark location at time ``t`` is ``\hat{\mathbf{s}}_t``, then we can update it with an observation at time ``t+1`` by:

```math
\hat{\mathbf{s}}_{t+1} = \frac{1}{t+1} \left(t\hat{\mathbf{s}}_t + \mathbf{z}_t\right)
```

But for more complicated models, this update may not be so simple. Instead, we will represent our current map of the environment by a probability distribution

```math
p_t(\mathbf{s}) = p\left(\mathbf{s}|\mathbf{z}_{1:t}\right)
```

The map at time ``t`` is the conditional distribution of ``\mathbf{s}`` given all of the observations up to and including ``\mathbf{z}_t``.

Bayes' rule tells us how to update ``p_t(\mathbf{s})`` to ``p_{t+1}(\mathbf{s})``:

```math
p_{t+1}(\mathbf{s}) = p(\mathbf{s}|\mathbf{z}_{1:t+1}) = \frac{p(\mathbf{z}_{t+1}|\mathbf{s},\mathbf{z}_{1:t}) p(\mathbf{s}|\mathbf{z}_{1:t})}{p(\mathbf{z}_{t+1})} = \frac{p(\mathbf{z}_{t+1}|\mathbf{s},\mathbf{z}_{1:t})}{p(\mathbf{z}_{t+1})} p_t(\mathbf{s})
```

The first term in the numerator of Bayes' rule ``p(\mathbf{z}_{t+1}|\mathbf{s},\mathbf{z}_{1:t})`` is called the *likelihood*, and it is similar to our likelihood function from before in that it tells us the probability of obtaining an observation ``\mathbf{z}_{t+1}`` given that the landmark is at location ``\mathbf{s}`` and that we've seen the observations ``\mathbf{z}_{1:t}``. Because our model tells us that the observations are independent and identically distributed given the landmark location, ``\mathbf{z}_{t+1}`` is actually conditionally independent of ``\mathbf{z}_{1:t}``, and this part of the likelihood drops out

```math
p(\mathbf{z}_{t+1} | \mathbf{s}, \mathbf{z}_{1:t}) = p(\mathbf{z}_{t+1}|\mathbf{s})
```

which is exactly the same likelihood function we saw before.

The second term in the numerator is just our distribution ``p_t(\mathbf{s})``. This is the *prior distribution* because it reflects a probability distribution over ``\mathbf{s}`` before we have incorporated the next measurement. Bayes' rule tells us how to update the prior distribution ``p_t(\mathbf{s})`` into the *posterior distribution* ``p_{t+1}(\mathbf{s})``. The rule is recursive: we start with a probability distribution ``p_0(\mathbf{s})`` that reflects our uncertainty in the location of ``\mathbf{s}`` before we have seen any data, and from that distribution, we can add information from the observations to form ``p_t(\mathbf{s})`` at any time. While ``p_t(\mathbf{s})`` plays the role of the prior distribution in Bayes' rule when we incorporate the observation ``\mathbf{z}_{t+1}``, ``p_0(\mathbf{s})`` is usually what is meant by the prior distribution in Bayesian statistics: the distribution before we have seen any data.

The denominator is the *marginal probability distribution* of ``\mathbf{z}_{t+1}``. This is given by integrating the numerator over the landmark location

```math
p(\mathbf{z}_{t+1}) = \int p(\mathbf{z}_{t+1}|\mathbf{s}) p(\mathbf{s}|\mathbf{z}_{1:t})\ \mathrm{d}\mathbf{s}
```

Computing this integral is the hard part in Bayesian statistics, and we'll encounter many different ways of calculating it analytically or approximating it when we can't.

### Sequential Bayesian inference for the landmark location model

Our model above says that the observation ``\mathbf{z}_t`` conditioned on the landmark location ``\mathbf{s}`` is multivariate normal with mean ``\mathbf{s}`` and covariance ``Q``.

```math
\mathbf{z}_t | \mathbf{s} \sim \mathcal{N}(\mathbf{s},Q)
```

This gives us the likelihood we need for Bayes' rule.

To complete the specification of our Bayesian model, we need to define the prior distribution ``p_0(\mathbf{s})``. It turns out that everything will be very simple if we let the prior distribution also be a multivariate Gaussian with mean ``\boldsymbol{\mu}_0`` and covariance ``\Sigma_0``. We will write

```math
\mathbf{s} \sim \mathcal{N}(\boldsymbol{\mu}_0,\Sigma_0)
```

to say that ``\mathbf{s}`` has this prior distribution, i.e. that

```math
p_0(\mathbf{s}) \propto e^{-\frac{1}{2} (\mathbf{s} - \boldsymbol{\mu}_0)^\dagger \Sigma_0^{-1} (\mathbf{s} - \boldsymbol{\mu}_0)}
```

Let's specify our prior distribution with a mean of zero and a variance of 100 in each dimension. The high variance reflects a high initial uncertainty about the landmark's location.
"""

# ╔═╡ f4751245-b653-45fb-8950-0043fdc20018
begin
	μ₀ = [0.0;0.0]
	Σ₀ = [100.0 0.0;0.0 100.0]
	p₀ = MvNormal(μ₀,Σ₀)
end

# ╔═╡ de7b4d82-f035-4f26-989a-bbe0f5d6daa6
md"""
Let's focus on updating this prior distribution with the information from our first observation ``\mathbf{z}_1``. We write Bayes' rule

```math
p(\mathbf{s}|\mathbf{z}_1) = \frac{p(\mathbf{z}_1|\mathbf{s})}{p(\mathbf{z}_1)} p_0(\mathbf{s})
```

To make some of the algebra easier, let's take the logarithm of both sides of this equation

```math
\log p(\mathbf{s}|\mathbf{z}_1) = \log p(\mathbf{z}_1|\mathbf{s}) + \log p_0(\mathbf{s}) - \log p(\mathbf{z}_1)
```

and substitute in our expressions for the likelihood and the prior

```math
\log p(\mathbf{s}|\mathbf{z}_1) = -\frac{1}{2}(\mathbf{z}_1 - \mathbf{s})^\dagger Q^{-1}(\mathbf{z}_1 - \mathbf{s}) - \frac{1}{2}(\mathbf{s} - \boldsymbol{\mu}_0)^\dagger \Sigma_0^{-1} (\mathbf{s} - \boldsymbol{\mu}_0) - \log p(\mathbf{z}_1) + C
```

where the constant ``C`` contains some terms that we won't worry about right now because they don't depend on ``\mathbf{s}``.

We still haven't written down what the marginal distribution ``p(\mathbf{z}_1)`` is. However, we don't really need to do that at this point. ``p(\mathbf{z}_1)`` doesn't depend on ``\mathbf{s}`` at all, and we can include it in the constant ``C``. There are times when we will want to compute the marginal distribution, but now is not one of them.

Our log posterior density is

```math
\log p(\mathbf{s}|\mathbf{z}_1) = -\frac{1}{2}(\mathbf{z}_1 - \mathbf{s})^\dagger Q^{-1}(\mathbf{z}_1 - \mathbf{s}) - \frac{1}{2}(\mathbf{s} - \boldsymbol{\mu}_0)^\dagger \Sigma_0^{-1} (\mathbf{s} - \boldsymbol{\mu}_0) + C
```

which we might not immediately recognize as a familiar probability distribution. But we can expand the quadratic functions of ``\mathbf{s}`` to find:

```math
\log p(\mathbf{s}|\mathbf{z}_1) = -\frac{1}{2} \left( \mathbf{s}^\dagger \left(Q^{-1} + \Sigma_0^{-1}\right) \mathbf{s} - 2\mathbf{s}^\dagger\left(Q^{-1}\mathbf{z}_1 + \Sigma_0^{-1} \boldsymbol{\mu}_0\right)\right) + C
```

Messing around a bit further with the algebra, we ultimately find that the posterior distribution is a multivariate normal with a mean

```math
\boldsymbol{\mu}_1 = \left(Q^{-1} + \Sigma_0^{-1}\right)^{-1} \left(Q^{-1}\mathbf{z}_1 + \Sigma_0^{-1}\boldsymbol{\mu}\right)
```

and covariance

```math
\Sigma_1 = \left(Q^{-1} + \Sigma_0^{-1}\right)^{-1}
```

These are the update equations that we need, and it will work the same for updating at any time step: simply replace ``\boldsymbol{\mu}_0`` and ``\Sigma_0`` on the right-hand side of these equations with ``\boldsymbol{\mu}_t`` and ``\Sigma_t``, and the observation ``\mathbf{z}_1`` with ``\mathbf{z}_{t+1}`` to find the updated mean and covariance ``\boldsymbol{\mu}_{t+1}`` and ``\Sigma_{t+1}``.

Let's see how this works in practice. We'll first implement our update equations
"""

# ╔═╡ 8a174193-2916-4a01-a2c9-8663e7f4aca2
function update(p₀,z,Q)
	μ₀,Σ₀ = p₀.μ,p₀.Σ
    Σ₁ = inv(inv(Q) + inv(Σ₀))
    μ₁ = Σ₁ * (Q\z + Σ₀\μ₀)
    MvNormal(μ₁,Σ₁)
end

# ╔═╡ 90007f4c-1ded-4a08-afa7-a5c344b6c03b
md"""
And then we'll apply the update equations to our model by scanning through the data set one point at a type, updating the probability distribution as we go. We can do this simply with the `accumulate` function.
"""

# ╔═╡ ab3b1a1b-8e74-47cf-9b95-df044e2fb86b
md"""
The contours represent the posterior probability density function, with the yellow contour surrounding the area with the highest probability density. As we add observations by moving the slider to the right, we can see how the contours shrink and the maximum of the probability density moves towards the origin, which is the true location of the landmark.

### Another parameterization

The update equations in their current form are slightly complicated. One particularly annoying problem is that you have to invert a couple of matrices to figure out the covariance. This isn't too bad when we're working with 2x2 covariance matrices, but with models that have more degrees of freedom, we'd like to avoid inverting big matrices both because it is slow and because it can lead to numerical problems if we're not careful.

We've parameterized our posterior probability density with its mean ``\boldsymbol{\mu}_t`` and covariance ``\Sigma_t``, which leads to the log posterior probability

```math
\log p(\mathbf{s}|\mathbf{z}_{1:t}) = -\frac{1}{2} \left(\mathbf{s} - \boldsymbol{\mu}_t\right)^{\dagger} \Sigma_t^{-1} \left(\mathbf{s} - \boldsymbol{\mu}_t\right) + C
```

If we expand the quadratic form and put the terms that don't depend on ``\mathbf{s}`` in the constant, we end up with

```math
\log p(\mathbf{s}|\mathbf{z}_{1:t}) = -\frac{1}{2} \mathbf{s}^{\dagger}\Sigma_t^{-1}\mathbf{s} + \Sigma_t^{-1}\boldsymbol{\mu}_t^{\dagger}\mathbf{s} + C
```

The coefficients in this parameterization of the multivariate normal distribution are

```math
\begin{align}
\Lambda_t &= \Sigma_t^{-1} \\
\mathbf{j}_t &= \Sigma_t^{-1} \boldsymbol{\mu}_t
\end{align}
```

We call the matrix ``\Lambda_t`` the *precision matrix* and the vector $\mathbf{j}_t$ the *potential vector* or the *information vector*. With these substitutions, the posterior log probability density becomes

```math
\log p(\mathbf{s}|\mathbf{z}_{1:t}) = -\frac{1}{2} \mathbf{s}^{\dagger}\Lambda_t\mathbf{s} + \mathbf{j}_t^{\dagger}\mathbf{s} + C
```

We can represent any multivariate normal distribution with this parameterization. For example, our prior distribution with ``\boldsymbol{\mu}_0 = 0`` and ``\Sigma_t = \begin{bmatrix} 100 & 0 \\ 0 & 100 \end{bmatrix}`` has a precision matrix ``\Lambda_0 = \begin{bmatrix} 0.01 & 0 \\ 0 & 0.01 \end{bmatrix}`` and a potential vector ``\mathbf{j}_0 = 0``

The benefit of this parameterization is that the update equations become very simple:

```math
\begin{align}
\Lambda_{t+1} &= \Lambda_t + Q^{-1} \\
\mathbf{j}_{t+1} &= \mathbf{j}_t + Q^{-1}\mathbf{z}_{t+1}
\end{align}
```

If we store ``Q^{-1}`` rather than ``Q``, we don't need to invert any matrices to update our probability distribution.
"""

# ╔═╡ 329dae9e-a9b4-4608-bc6e-88953679ea1a
Qi = inv(Q)

# ╔═╡ b00453b8-beb5-4e5f-ac75-ec0d4acc8e72
md"""
The Distributions package gives us an `MvNormalCanon` type that uses this "canonical" parameterization directly. We can create a new prior distribution using the canonical parameterization.
"""

# ╔═╡ bd6bbe7d-5e46-4bab-8d75-6439614a747d
begin
	Λ₀ = inv(Σ₀)
	j₀ = Σ₀\μ₀
	q₀ = MvNormalCanon(j₀,Λ₀)
end

# ╔═╡ cd9b1e2a-8049-4c44-8d52-c7dcd146708f
md"""
And we can rewrite our update method to work on canonically parameterized distributions. We need to be careful to pass the inverse of the observation noise covariance rather than the the noise covariance.
"""

# ╔═╡ 68107d23-f41e-46cc-b6ac-b3195651c35b
function update(q₀::D,z,Qi) where {D <: MvNormalCanon}
	j₀,Λ₀ = q₀.h,q₀.J

	j₁ = j₀ + Qi*z
	Λ₁ = Λ₀ + Qi
	MvNormalCanon(j₁,Λ₁)
end

# ╔═╡ 55a37b10-7163-4887-942c-1b2d42b7330b
begin
	Zs = Point2[Z[:,i] for i in 1:N]
	ps = [p₀;accumulate((p,z) -> update(p,z,Q),Zs,init=p₀)]
end

# ╔═╡ 2985b01b-c602-4cb5-80b2-b39457f3686d
begin

	fig4 = Figure()
	ax4 = Axis(fig4[1,1],xlabel="X",ylabel="Y")
	b1 = Makie.Slider(fig4[2,1],range=0:10)

	xs = range(-10,10,length=256)

	P = lift(b1.value) do i
		[pdf(ps[i+1],[x;y]) for x in xs, y in xs]
	end

	obs = lift(b1.value) do i
		if i == 0
			[Point2(20,20.0)]
		else
			Zs[1:i]
		end
	end

	contour!(ax4,xs,xs,P)
	scatter!(ax4,obs,color=:black)

	xlims!(ax4,-10,10)
	ylims!(ax4,-10,10)
	fig4
end

# ╔═╡ 5b07dc5d-6676-4d93-bb4a-7296beded4c3
md"""
And apply our new parameterization to our data set.
"""

# ╔═╡ 18cb7c76-5721-4023-b475-edaf410ea1da
qs = [q₀;accumulate((q,z) -> update(q,z,Qi),Zs,init=q₀)]

# ╔═╡ a098c5b0-6734-4bd2-90d7-5daf69cfd111
md"""
We can visualize the results the same way, and they should look the same. We haven't changed the model, just how we write the model.
"""

# ╔═╡ 51369139-452b-4fe9-a441-9f0f13713faf
begin

	fig5 = Figure()
	ax5 = Axis(fig5[1,1],xlabel="X",ylabel="Y")
	sl5 = Makie.Slider(fig5[2,1],range=0:N)

	P5 = lift(sl5.value) do i
		[pdf(qs[i+1],[x;y]) for x in xs, y in xs]
	end

	obs5 = lift(sl5.value) do i
		if i == 0
			[Point2(20,20.0)]
		else
			Zs[1:i]
		end
	end

	contour!(ax5,xs,xs,P5)
	scatter!(ax5,obs5,color=:black)

	xlims!(ax5,-10,10)
	ylims!(ax5,-10,10)
	fig5
end

# ╔═╡ b7c5a266-8264-4829-a040-7d11fe635b8a
md"""
This canonical parameterization is very useful for Bayesian inference problems because the Bayesian update equations become simple additions. There are tradeoffs, though. You have to solve a system of linear equations to recover the mean of the posterior distribution, which is also the maximum a posteriori estimate of the landmark location. Computing the marginal probability ``p(\mathbf{z}_t)`` is also harder in the canonical parameterization. So you have to choose which is right for the particular application you are using, and we'll flip back and forth throughout these experiments.

That's it for our first lesson. In future lessons, we'll look at increasingly complicated localization and mapping problems and how we can solve them with Bayesian inference.
"""

# ╔═╡ 00000000-0000-0000-0000-000000000001
PLUTO_PROJECT_TOML_CONTENTS = """
[deps]
Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f"
JSServe = "824d6782-a2ef-11e9-3a09-e5662e0c26f9"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
WGLMakie = "276b4fcb-3e11-5398-bf8b-a0c2d153d008"

[compat]
Distributions = "~0.25.16"
JSServe = "~1.2.3"
WGLMakie = "~0.4.6"
"""

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

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

[[AbstractTrees]]
git-tree-sha1 = "03e0550477d86222521d254b741d470ba17ea0b5"
uuid = "1520ce14-60c1-5f80-bbc7-55ef81b5835c"
version = "0.3.4"

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

[[Animations]]
deps = ["Colors"]
git-tree-sha1 = "e81c509d2c8e49592413bfb0bb3b08150056c79d"
uuid = "27a7e980-b3e6-11e9-2bcd-0b925532e340"
version = "0.4.1"

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

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

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

[[Automa]]
deps = ["Printf", "ScanByte", "TranscodingStreams"]
git-tree-sha1 = "d50976f217489ce799e366d9561d56a98a30d7fe"
uuid = "67c07d97-cdcb-5c2c-af73-a7f9c32a568b"
version = "0.8.2"

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

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

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

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

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

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

[[CodecZlib]]
deps = ["TranscodingStreams", "Zlib_jll"]
git-tree-sha1 = "ded953804d019afa9a3f98981d99b33e3db7b6da"
uuid = "944b1d66-785c-5afd-91f1-9de20f533193"
version = "0.7.0"

[[ColorBrewer]]
deps = ["Colors", "JSON", "Test"]
git-tree-sha1 = "61c5334f33d91e570e1d0c3eb5465835242582c4"
uuid = "a2cac450-b92f-5266-8821-25eda20663c8"
version = "0.4.0"

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

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

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

[[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 = "4866e381721b30fac8dda4c8cb1d9db45c8d2994"
uuid = "34da2185-b29b-5c13-b0c7-acf172513d20"
version = "3.37.0"

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

[[FreeType]]
deps = ["CEnum", "FreeType2_jll"]
git-tree-sha1 = "cabd77ab6a6fdff49bfd24af2ebe76e6e018a2b4"
uuid = "b38be410-82b0-50bf-ab77-7b57e271db43"
version = "4.0.0"

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

[[FreeTypeAbstraction]]
deps = ["ColorVectorSpace", "Colors", "FreeType", "GeometryBasics", "StaticArrays"]
git-tree-sha1 = "19d0f1e234c13bbfd75258e55c52aa1d876115f5"
uuid = "663a7486-cb36-511b-a19d-713bb74d65c9"
version = "0.9.2"

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

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

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

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

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

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

[[GridLayoutBase]]
deps = ["GeometryBasics", "InteractiveUtils", "Match", "Observables"]
git-tree-sha1 = "e2f606c87d09d5187bb6069dab8cee0af7c77bdb"
uuid = "3955a311-db13-416c-9275-1d80ed98e5e9"
version = "0.6.1"

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

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

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

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

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

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

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

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

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

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

[[IndirectArrays]]
git-tree-sha1 = "012e604e1c7458645cb8b436f8fba789a51b257f"
uuid = "9b13fd28-a010-5f03-acff-a1bbcff69959"
version = "1.0.0"

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

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

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

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

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

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

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

[[Isoband]]
deps = ["isoband_jll"]
git-tree-sha1 = "f9b6d97355599074dc867318950adaa6f9946137"
uuid = "f1662d9f-8043-43de-a69a-05efc1cc6ff4"
version = "0.1.1"

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

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

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

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

[[JSON3]]
deps = ["Dates", "Mmap", "Parsers", "StructTypes", "UUIDs"]
git-tree-sha1 = "b3e5984da3c6c95bcf6931760387ff2e64f508f3"
uuid = "0f8b85d8-7281-11e9-16c2-39a750bddbf1"
version = "1.9.1"

[[JSServe]]
deps = ["Base64", "CodecZlib", "Colors", "HTTP", "Hyperscript", "JSON3", "LinearAlgebra", "Markdown", "MsgPack", "Observables", "SHA", "Sockets", "Tables", "Test", "UUIDs", "WebSockets", "WidgetsBase"]
git-tree-sha1 = "91101a4b8ac8eefeed6ca8eb4f663fc660e4d9f9"
uuid = "824d6782-a2ef-11e9-3a09-e5662e0c26f9"
version = "1.2.3"

[[JpegTurbo_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "d735490ac75c5cb9f1b00d8b5509c11984dc6943"
uuid = "aacddb02-875f-59d6-b918-886e6ef4fbf8"
version = "2.1.0+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"

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

[[MKL_jll]]
deps = ["Artifacts", "IntelOpenMP_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "Pkg"]
git-tree-sha1 = "5455aef09b40e5020e1520f551fa3135040d4ed0"
uuid = "856f044c-d86e-5d09-b602-aeab76dc8ba7"
version = "2021.1.1+2"

[[Makie]]
deps = ["Animations", "Base64", "ColorBrewer", "ColorSchemes", "ColorTypes", "Colors", "Contour", "Distributions", "DocStringExtensions", "FFMPEG", "FileIO", "FixedPointNumbers", "Formatting", "FreeType", "FreeTypeAbstraction", "GeometryBasics", "GridLayoutBase", "ImageIO", "IntervalSets", "Isoband", "KernelDensity", "LaTeXStrings", "LinearAlgebra", "MakieCore", "Markdown", "Match", "MathTeXEngine", "Observables", "Packing", "PlotUtils", "PolygonOps", "Printf", "Random", "RelocatableFolders", "Serialization", "Showoff", "SignedDistanceFields", "SparseArrays", "StaticArrays", "Statistics", "StatsBase", "StatsFuns", "StructArrays", "UnicodeFun"]
git-tree-sha1 = "7e49f989e7c7f50fe55bd92d45329c9cf3f2583d"
uuid = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a"
version = "0.15.2"

[[MakieCore]]
deps = ["Observables"]
git-tree-sha1 = "7bcc8323fb37523a6a51ade2234eee27a11114c8"
uuid = "20f20a25-4f0e-4fdf-b5d1-57303727442b"
version = "0.1.3"

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

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

[[Match]]
git-tree-sha1 = "5cf525d97caf86d29307150fcba763a64eaa9cbe"
uuid = "7eb4fadd-790c-5f42-8a69-bfa0b872bfbf"
version = "1.1.0"

[[MathTeXEngine]]
deps = ["AbstractTrees", "Automa", "DataStructures", "FreeTypeAbstraction", "GeometryBasics", "LaTeXStrings", "REPL", "RelocatableFolders", "Test"]
git-tree-sha1 = "f5c8789464aed7058107463e5cef53e6ad3f1f3e"
uuid = "0a4f8689-d25c-4efe-a92b-7142dfc1aa53"
version = "0.2.0"

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

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

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

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

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

[[MsgPack]]
deps = ["Serialization"]
git-tree-sha1 = "a8cbf066b54d793b9a48c5daa5d586cf2b5bd43d"
uuid = "99f44e22-a591-53d1-9472-aa23ef4bd671"
version = "1.1.0"

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

[[Netpbm]]
deps = ["FileIO", "ImageCore"]
git-tree-sha1 = "18efc06f6ec36a8b801b23f076e3c6ac7c3bf153"
uuid = "f09324ee-3d7c-5217-9330-fc30815ba969"
version = "1.0.2"

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

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

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

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

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

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

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

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

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

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

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

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

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

[[Packing]]
deps = ["GeometryBasics"]
git-tree-sha1 = "1155f6f937fa2b94104162f01fa400e192e4272f"
uuid = "19eb6ba3-879d-56ad-ad62-d5c202156566"
version = "0.4.2"

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

[[Parsers]]
deps = ["Dates"]
git-tree-sha1 = "438d35d2d95ae2c5e8780b330592b6de8494e779"
uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0"
version = "2.0.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"

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

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

[[PolygonOps]]
git-tree-sha1 = "c031d2332c9a8e1c90eca239385815dc271abb22"
uuid = "647866c9-e3ac-4575-94e7-e3d426903924"
version = "0.1.1"

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

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

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

[[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 = ["Serialization"]
uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"

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

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

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

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

[[Rmath]]
deps = ["Random", "Rmath_jll"]
git-tree-sha1 = "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"

[[SIMD]]
git-tree-sha1 = "9ba33637b24341aba594a2783a502760aa0bff04"
uuid = "fdea26ae-647d-5447-a871-4b548cad5224"
version = "3.3.1"

[[ScanByte]]
deps = ["Libdl", "SIMD"]
git-tree-sha1 = "9cc2955f2a254b18be655a4ee70bc4031b2b189e"
uuid = "7b38b023-a4d7-4c5e-8d43-3f3097f304eb"
version = "0.3.0"

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

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

[[ShaderAbstractions]]
deps = ["ColorTypes", "FixedPointNumbers", "GeometryBasics", "LinearAlgebra", "Observables", "StaticArrays", "StructArrays", "Tables"]
git-tree-sha1 = "0d97c895406b552bed78f3a1fe9925248e908ae2"
uuid = "65257c39-d410-5151-9873-9b3e5be5013e"
version = "0.2.8"

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

[[SignedDistanceFields]]
deps = ["Random", "Statistics", "Test"]
git-tree-sha1 = "d263a08ec505853a5ff1c1ebde2070419e3f28e9"
uuid = "73760f76-fbc4-59ce-8f25-708e95d2df96"
version = "0.4.0"

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

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

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

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

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

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

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

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

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

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

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

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

[[StructTypes]]
deps = ["Dates", "UUIDs"]
git-tree-sha1 = "8445bf99a36d703a09c601f9a57e2f83000ef2ae"
uuid = "856f2bd8-1eba-4b0a-8007-ebc267875bd4"
version = "1.7.3"

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

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

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

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

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

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

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

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

[[TranscodingStreams]]
deps = ["Random", "Test"]
git-tree-sha1 = "216b95ea110b5972db65aa90f88d8d89dcb8851c"
uuid = "3bb67fe8-82b1-5028-8e26-92a6c54297fa"
version = "0.9.6"

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

[[WGLMakie]]
deps = ["Colors", "FileIO", "FreeTypeAbstraction", "GeometryBasics", "Hyperscript", "ImageMagick", "JSServe", "LinearAlgebra", "Makie", "Observables", "ShaderAbstractions", "StaticArrays"]
git-tree-sha1 = "bafa1c4ab77626f8d8199209b740e097ae03805f"
uuid = "276b4fcb-3e11-5398-bf8b-a0c2d153d008"
version = "0.4.6"

[[WebSockets]]
deps = ["Base64", "Dates", "HTTP", "Logging", "Sockets"]
git-tree-sha1 = "f91a602e25fe6b89afc93cf02a4ae18ee9384ce3"
uuid = "104b5d7c-a370-577a-8038-80a2059c5097"
version = "1.5.9"

[[WidgetsBase]]
deps = ["Observables"]
git-tree-sha1 = "c1ef6e02bc457c3b23aafc765b94c3dcd25f174d"
uuid = "eead4739-05f7-45a1-878c-cee36b57321c"
version = "0.1.3"

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

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

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

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

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

[[Xorg_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_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_xtrans_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "79c31e7844f6ecf779705fbc12146eb190b7d845"
uuid = "c5fb5394-a638-5e4d-96e5-b29de1b5cf10"
version = "1.4.0+3"

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

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

[[isoband_jll]]
deps = ["Libdl", "Pkg"]
git-tree-sha1 = "a1ac99674715995a536bbce674b068ec1b7d893d"
uuid = "9a68df92-36a6-505f-a73e-abb412b6bfb4"
version = "0.2.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"

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

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

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

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

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

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

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

# ╔═╡ Cell order:
# ╠═889697ff-7f5a-4106-95f7-ca7a294faeb3
# ╠═6946373c-8035-485b-9048-7b4d0fc6428f
# ╟─cf1c3faa-1973-11ec-04bf-1f53406ce76b
# ╠═592cb2f4-7c38-4ecd-b0fc-50d9e409f928
# ╠═3f91f4e8-e0c9-4f1f-a5a0-7cdfc8bb9d6b
# ╠═4c06bac4-05b8-4883-b3ca-3da505783fdc
# ╠═54ec7d97-477e-4887-a73a-502eeba97910
# ╠═7c36f709-7bb0-4986-8337-e6dea6fc7690
# ╟─bc925405-7574-46d4-aa59-7e32bc1acaf7
# ╟─94c08534-71fd-4265-9212-edf76b4517bf
# ╠═e4b53a36-860b-4c3a-b9c9-6f6eafcaeeb9
# ╟─8b342e24-759b-4ce4-9349-1c46aebe6cad
# ╟─dbf4d333-b34a-40ce-b6cf-3a17a20cda8f
# ╠═6312899f-e92b-42d6-977a-fe8fe8b63011
# ╠═62c45184-2e26-4186-9f16-0b4d33944053
# ╟─20610ddc-5160-458d-badd-850046a087e2
# ╟─12a3d2e5-7f9f-4b8b-94d4-87911e8ad699
# ╠═f4751245-b653-45fb-8950-0043fdc20018
# ╟─de7b4d82-f035-4f26-989a-bbe0f5d6daa6
# ╠═8a174193-2916-4a01-a2c9-8663e7f4aca2
# ╟─90007f4c-1ded-4a08-afa7-a5c344b6c03b
# ╠═55a37b10-7163-4887-942c-1b2d42b7330b
# ╟─2985b01b-c602-4cb5-80b2-b39457f3686d
# ╟─ab3b1a1b-8e74-47cf-9b95-df044e2fb86b
# ╠═329dae9e-a9b4-4608-bc6e-88953679ea1a
# ╟─b00453b8-beb5-4e5f-ac75-ec0d4acc8e72
# ╠═bd6bbe7d-5e46-4bab-8d75-6439614a747d
# ╟─cd9b1e2a-8049-4c44-8d52-c7dcd146708f
# ╠═68107d23-f41e-46cc-b6ac-b3195651c35b
# ╟─5b07dc5d-6676-4d93-bb4a-7296beded4c3
# ╠═18cb7c76-5721-4023-b475-edaf410ea1da
# ╟─a098c5b0-6734-4bd2-90d7-5daf69cfd111
# ╟─51369139-452b-4fe9-a441-9f0f13713faf
# ╟─b7c5a266-8264-4829-a040-7d11fe635b8a
# ╟─00000000-0000-0000-0000-000000000001
# ╟─00000000-0000-0000-0000-000000000002


import Base:show
import JGraph: Edge, EdgeIter

abstract type AbstractGraph end

mutable struct Graph{T <: Integer} <: AbstractGraph
    ne::T
    adjlist::Vector{Vector{T}}

    function Graph()
        new{Int64}(0, [])
    end
end

"""
    Graph(n, m)

generate a graph with n vertices and m edges. Return a graph G.
"""
function Graph(n, m)
    g = Graph();
    add_vertex!(g, n);
    num_vertice = 0

    while num_vertice < m
        s = rand(1:n)
        t = rand(1:n)
        if s != t && !has_edge(g, s, t)
            add_edge!(g, s, t);
            num_vertice += 1
        end
    end
    return g
end

nv(G::AbstractGraph) = length(G.adjlist)

ne(G::AbstractGraph) = G.ne

"""
    has_edge(G::AbstractGraph, u::T, v::T)

Test if egde(u,v) exist in the graph G.
"""
function has_edge(G::AbstractGraph, u::T, v::T) where T <: Integer
    if max(u,v) > nv(G)
        return false
    elseif v in G.adjlist[u]
        return true
    else
        return false
    end
end

has_edge(G::AbstractGraph, e::Edge) = has_edge(G, src(e), dst(e))

"""
    add_edge(G::AbstractGraph, u::T, v::T)

Add the egde(u,v) in the graph G. Return False is the edge already exit, true otherwise.
"""
function add_edge!(G::AbstractGraph, u::T, v::T) where T <: Integer
    if has_edge(G, u, v)
        return false
    end
    if nv(G) < max(u,v)
        add_vertex!(G, max(u,v));
    end
    push!(G.adjlist[u], v);
    push!(G.adjlist[v], u);
    G.ne += 1;
    return true
end

add_edge!(G::AbstractGraph, e::Edge) = add_edge(G, src(e), dst(e))

"""
    add_edge(G::AbstractGraph, num_vertex)

Add "num_vertex" vertices to the graph G.
"""
function add_vertex!(G::AbstractGraph, num_vertex::T) where T <: Integer
    while nv(G) < num_vertex
        add_vertex!(G);
    end
    return true
end

function add_vertex!(G::AbstractGraph)
    return push!(G.adjlist, Vector{Int}());
end

function remove_edge!(G::AbstractGraph, u, v)
    if has_edge(G, u, v)
        G.adjlist[u] = deleteat!(G.adjlist[u], findall(x-> x == v, G.adjlist[u]));
        G.adjlist[v] = deleteat!(G.adjlist[v], findall(x-> x == u, G.adjlist[v]));
        return true
    else
        return false
    end
end

remove_edge!(G::AbstractGraph, e::Edge) = remove_edge!(G, src(e), dst(e))

function remove_vertex!(G::AbstractGraph, s)
    for t in neighbors(G, s)
        remove_edge!(G, s, t)
    end
    return true
end

"""
    degree(G::Graph)

Return the list of nodes degree.
"""
function degree(G::Graph)
    degree_list = []
    for u in G.adjlist
        push!(degree_list, length(u))
    end
    degree_list
end

"""
    neighbors(G::Graph, u)

return the neighbors list adjacent to the node u
"""
function neighbors(G::Graph, u)
    return copy(G.adjlist[u])
end

"""
    vertices(G::Graph)

vertices itertor
"""
vertices(G::Graph) = Base.OneTo(nv(G))

edges(g::AbstractGraph) = EdgeIter(g)

"""
    fadj(G::Graph)

return the adjacency list of the graph
"""
fadj(G::Graph) = G.adjlist
fadj(G::Graph, v::Integer) = G.adjlist[v]

show(io::IO, G::Graph) = print(io, "Undirected Graph G(V,E) with |V|=$(nv(G)), |E|=$(ne(G))");


# # A simple FEM application
#
# ## Introduction
# The Finite Element Method is widely used to solve PDEs in Engineering applications and particularly in Structural Analysis problems [Add citation Bathe]. A specific case os structures is Truss structures, where trusses or bars are connected but not welded. Truss models are considered during the conceptual design of bridges or other structures.
#
# The stiffness matrix of a truss element in the local coordinate system is given by
# ```math
# K_L = s
# \left(
#  \begin{matrix}
#  1 & 0 & -1 & 0 \\
#  0 & 0 &  0 & 0 \\
# -1 & 0 &  1 & 0 \\
#  0 & 0 &  0 & 0
# \end{matrix}
# \right),
# ```
#
# where $s$ is the stiffness, given by $EA/L$, with $E$ being the Young modulus, $A$ the area of the cross-section and $L$ the length of that truss element.
#
# The change-of-basis matrix is given by
# ```math
# _G(Q)_L = Q =
# \left(
#   \begin{matrix}
# \cos(\alpha) & -\sin(\alpha) & 0 & 0 \\
#  sin(alpha) & cos(alpha) &  0 & 0 \\
#  0 & 0 &  cos(alpha) & sin(alpha) \\
#  0 & 0 &  sin(alpha) & cos(alpha)
# \end{matrix}
# \right),
# ```
#
# The system of equations for each element is written in local coordinates as
# ```math
# K_L d_L = f_L
# ```
# and using the change-of-basis we obtain
# ```math
# K_G d_G = f_G \qquad K_G = Q K_L Q^T
# ```
#
# The stiffness matrix can be computed using the following function, which computes the unitary stiffness matrix for an element defined by the coordinates of its first and second nodes.
function unitaryStiffnessMatrix( coordFirstNode, coordSecondNode  )
  diff      = (coordSecondNode - coordFirstNode)
  length   = sqrt( diff'*diff )
  c        = diff[1] / length ;
  s        = diff[2] / length ;
  Qloc2glo = [ c -s 0 0 ; s c 0 0 ; 0 0 c -s ; 0 0 s c ] ;
  Kloc     = [ 1 0 -1 0 ; 0 0 0 0 ; -1 0 1 0 ; 0 0 0 0 ] ;
  Kglo     = Qloc2glo * Kloc * transpose(Qloc2glo)
  return     Kglo, length
end
#
# ## Problem with fixed parameters
# In this section, a problem based on Example 4.1 from [https://github.com/JuliaIntervals/IntervalLinearAlgebra.jl/files/7271616/skalna2006.pdf] is considered. The following diagram shows the truss structure considered.
#
# \fig{../../assets/trussDiagram.png}
#
# The scalar parameters considered are given by
E = 2e11 ; # Young modulus
A = 5e-3 ; # Cross-section area
# while the coordinate matrix is given by
nodesCMatrix = [ 0. 0. ;
                 1. 1. ;
                 2. 0. ;
                 3. 1. ;
                 4. 0. ]
# and connectivity matrix is given by
connecMatrix = [ 1 2 ;
                 1 3 ;
                 2 3 ;
                 2 4 ;
                 3 4 ;
                 3 5 ;
                 4 5 ]
# and the fixed degrees of freedom (supports) are
fixedDofs     = [2 9 10 ]

# calculations
numNodes = size( nodesCMatrix )[1]
numElems = size( connecMatrix )[1]
freeDofs = zeros(Int8, 2*numNodes-length(fixedDofs))
indDof  = 1 ;
counter = 0 ;
while indDof <= (2*numNodes)
  if !(indDof in fixedDofs)
    global counter = counter + 1 ;
    freeDofs[ counter ] = indDof ;
    print(indDof)
  end
  global indDof = indDof + 1 ;
end
print(freeDofs)
KG = zeros( 2*numNodes, 2*numNodes ) ;
FG = zeros( 2*numNodes )

# assembly
for elem in 1:numElems
  print(" assembling stiffness matrix of element ", elem , "\n")
  indexFirstNode  = connecMatrix[ elem, 1 ]
  indexSecondNode = connecMatrix[ elem, 2 ]
  dofsElem = [2*indexFirstNode-1 2*indexFirstNode 2*indexSecondNode-1 2*indexSecondNode ]
  KGelem, lengthElem = unitaryStiffnessMatrix( nodesCMatrix[ indexSecondNode, : ], nodesCMatrix[ indexFirstNode, : ] )
  stiffnessParam = E * A / lengthElem ;
  for i in 1:4
    for j in 1:4
      KG[ dofsElem[i], dofsElem[j] ] = KG[ dofsElem[i], dofsElem[j] ] + stiffnessParam * KGelem[i,j]
    end
  end
end
FG[4] = -1e4 ;
KG = KG[ freeDofs, : ]
KG = KG[ :, freeDofs ]
FG = FG[ freeDofs ]

u = KG \ FG
print(u)

# ## Problem with interval parameters


targetSystem = Sys.WORD_SIZE;
println("Sys.WORD_SIZE indicates whether the target system is 32-bit or 64-bit. ['$targetSystem']");

integerType = typeof(1);
println("integerType is ['$integerType']");

#Unsigned integers are input and output using the 0x prefix and
#hexadecimal (base 16) digits 0-9a-f (the capitalized digits A-F also work for input).
unsignedValue = 0x1;
unsignedValuesType = typeof(unsignedValue);
println("unsignedValue(0x1) has value: ['$unsignedValue'] and is of type $unsignedValuesType");

#over-flow behavior
maxInt = typemax(Int64);
println("Max integer value: [$maxInt]");

overflowResult = maxInt + 1; #overflow by 1
println("Max integer after an overflow by 1: [$overflowResult]");

println("floating-point literals:");
println("------------------------");

value = 1.0;
println("1.0 -> $value");

value = 1.;
println("1. -> $value");

value = 0.5;
println("0.5 -> $value");

value = .5;
println(".5 -> $value");

value = -1.23;
println("-1.23 -> $value");

value = 1e10;
println("1e10 -> $value");

value = 2.5e-4;
println("2.5e-4 -> $value");

println(typeof(value));

println();
println("Hexadecimal floating-point literals, only have Float64 values, with p preceding the base-2 exponent");

hexValue = 0x1p0;
println("0x1p0 -> $hexValue");

hexValue = 0x1.8p3;
println("0x1.8p3 -> $hexValue");

hexValue = 0x.4p-1;
println("0x.4p-1 -> $hexValue");

println();
println("Underscores can be used as a digit seperator (10,000 would be 10_000)");
seperatedDigit = 10_000;
println("10_0000 -> $seperatedDigit");

println();
println("Floating-Point zero");

areZerosEqual = 0.0 == -0.0;
println("0.0 == -0.0 -> $areZerosEqual");
println("0.0 -> " * bitstring(0.0));
println("-0.0 -> " * bitstring(-0.0));

println();
println("Numeric Literal Coefficients");
x = 3;

answer = 2x^2 - 3x + 1;
println("2x^2 - 3x + 1 = " * string(answer));

answer = 1.5x^2 - .5x + 1;
println("1.5x^2 - .5x + 1 = " * string(answer));

answer = 2(x-1)^2 - 3(x -1) + 1;
println("2(x-1)^2 - 3(x -1) + 1 = " * string(answer));

println();
println("Literal zero and one");
println("zero(Float32) -> " * string(zero(Float32)));
println("zero(1.0) -> " * string(zero(1.0)));
println("one(Int64) -> " * string(one(Int64)));
println("one(BigFloat) -> " * string(one(BigFloat)));


using Test, SpecialFunctions
import TruncatedNormal as TruncNorm

@test TruncNorm.tnmean(-Inf, Inf) == 0
@test TruncNorm.tnmean(0, +Inf) ≈ +√(2/π)
@test TruncNorm.tnmean(-Inf, 0) ≈ -√(2/π)
@test TruncNorm.tnmean(-Inf, -Inf) == -Inf
@test TruncNorm.tnmean(+Inf, +Inf) == +Inf

for x = -10:10
    @test TruncNorm.tnmean(x, +Inf) ≈ +√(2/π) / erfcx(+x/√2)
    @test TruncNorm.tnmean(-Inf, x) ≈ -√(2/π) / erfcx(-x/√2)
end

for x = exp.(-100:100)
    @test TruncNorm.tnmean(-x, x) ≈ 0
    @test TruncNorm.tnmean(0, x) ≈ -√(2/π) * expm1(-x^2 / 2) / erf(x / √2)
end

@test TruncNorm.tnmean(1e-44, 1e-43) ≈ 5.4999999999999999999999999999999999999999e-44

@test TruncNorm.tnmean(-Inf, Inf, 0, 1) == TruncNorm.tnmean(-Inf, Inf)
for x = -100:100
    @test TruncNorm.tnmean(x, +Inf, 0, 1) ≈ TruncNorm.tnmean(x, +Inf)
    @test TruncNorm.tnmean(-Inf, x, 0, 1) ≈ TruncNorm.tnmean(-Inf, x)
end

@test TruncNorm.tnmean(-1e4, 1e4, 0, 1) == TruncNorm.tnmean(-1e4, 1e4)

for x = -100:100, y = x + 1 : 100
    @test TruncNorm.tnmean(x, y, 0, 1) == TruncNorm.tnmean(x, y)
end

@test TruncNorm.tnmean(100, 115) ≈ 100.00999800099926070518490239457545847490332879043
@test TruncNorm.tnmean(-1e6, -999000) ≈ -999000.00000100100100099899498898098
@test TruncNorm.tnmean(+1e6, +Inf) ≈ +1.00000000000099999999999800000e6
@test TruncNorm.tnmean(-Inf, -1e6) ≈ -1.00000000000099999999999800000e6

@test TruncNorm.tnmean(-1e200, 1e200) ≈ 0
@test TruncNorm.tnmean(0, +1e200) ≈ +0.797884560802865355879892119869
@test TruncNorm.tnmean(-1e200, 0) ≈ -0.797884560802865355879892119869

@test TruncNorm.tnmean(50, 70, -2, 3) ≈ 50.171943499898757645751683644632860837133138152489
@test TruncNorm.tnmean(-100.0, 0.0, 0.0, 2.0986317998643735) ≈ -1.6744659119217125058885983754999713622460154892645
@test TruncNorm.tnmean(0.0, 0.9, 0.0, 0.07132755843183151) ≈ 0.056911157632522598806524588414964004271754161737065
@test TruncNorm.tnmean(-100.0, 100.0, 0.0, 17.185261847875548) ≈ 0
@test TruncNorm.tnmean(-100.0, 0.5, 0.0, 0.47383322897860064) ≈ -0.1267981330521791493635176736743283314399
@test TruncNorm.tnmean(-100.0, 100.0, 0.0, 17.185261847875548) ≈ 0

for x = 1e1 .^ (-400:100:400)
    @test TruncNorm.tnmean(-x, x) == TruncNorm.tnmean(-x, x) || isnan(TruncNorm.tnmean(-x, x)) && isnan(TruncNorm.tnmean(-x, x))
end

for a = exp.(-10:10), b = exp.(-10:10)
    a ≤ b || continue
    @test a ≤ TruncNorm.tnmean(a, b) ≤ b
end

# https://github.com/JuliaStats/Distributions.jl/issues/827
@test TruncNorm.tnmean(0, 1000, 1000000, 1) ≈ 999.99999899899899900100501101901899090472046236710608108591983


using Sundials, Test, SparseArrays

# Test for Jacobian usage
function Lotka(du, u, p, t)
  du[1] = u[1] - u[1] * u[2] # REPL[7], line 3:
  du[2] = -3 * u[2] + 1 * u[1] * u[2]
  nothing
end

jac_called = false
function Lotka_jac(J,u,p,t)
  global jac_called
  jac_called = true
  J[1,1] = 1.0 - u[2]
  J[1,2] = -u[1]
  J[2,1] = 1 * u[2]
  J[2,2] = -3 + u[1]
  nothing
end

Lotka_f = ODEFunction(Lotka,jac=Lotka_jac)
prob = ODEProblem(Lotka_f,ones(2),(0.0,10.0))
good_sol = solve(prob,CVODE_BDF())
@test jac_called == true

Lotka_f = ODEFunction(Lotka,jac=Lotka_jac,
                      jac_prototype = sparse([1,2,1,2],[1,1,2,2],zeros(4)))

prob = ODEProblem(Lotka_f,ones(2),(0.0,10.0))
jac_called = false
sol9 = solve(prob,CVODE_BDF(linear_solver=:KLU))
@test jac_called == true

function f2!(res, du, u, p, t)
    res[1] = 1.01du[1]
    return
end

jac_called = false
function f2_jac!(out, du, u, p, gamma, t)
    global jac_called
    jac_called = true
    out[1] = 1.01
end

f2_f = DAEFunction(f2!,jac=f2_jac!)

u0 = [0.]
tspan = (0.0, 10.)
du0 = [0.]
dae_prob = DAEProblem(f2_f,du0, u0,tspan, differential_vars=[true])
good_sol = solve(dae_prob,IDA())
@test jac_called == true

function testjac(res, du, u, p, t)
  res[1] = du[1] - 1.5 * u[1] + 1.0 * u[1]*u[2]
  res[2] = du[2] +3 * u[2] - u[1]*u[2]
end

jac_called = false
function testjac_jac(J, du, u, p, gamma, t)
  global jac_called
  jac_called = true
  J[1,1] = gamma - 1.5 + 1.0 * u[2]
  J[1,2] = 1.0 * u[1]
  J[2,1] = - 1 * u[2]
  J[2,2] = gamma + 3 - u[1]
  nothing
end

testjac_f = DAEFunction(testjac,jac=testjac_jac)

prob3 = DAEProblem(testjac_f,[0.5,-2.0],ones(2),(0.0,10.0),differential_vars=[true,true])
sol3 = solve(prob3, IDA())

@test jac_called == true
jac_called = false
prob4 = DAEProblem(testjac,[0.5,-2.0],ones(2),(0.0,10.0))
sol4 = solve(prob4, IDA())

@test jac_called == false

println("Jacobian vs no Jacobian difference:")
println(maximum(sol3-sol4))
@test maximum(sol3-sol4) < 1e-6


# Advent of Code, day 7
#
# Find target position that minimizes fuel use for a population
# of crabs that need to move there, under different assumptions
# about fuel use per each crab's distance moved (optimization
# problem, but used brute force).
#
# AK, 7 Dec 2021

# Read input data, convert to list of numbers
#raw_data = readlines(open("test_input.txt"))
raw_data = readlines(open("input.txt"))
data = map(x -> parse(Int64, x), split(raw_data[1], ","))
maxPos = maximum(data)
#println("Data:", data)
println("Max position =", maximum(data))

# Find the "horizontal" position that minimizes fuel use.  For part 1 (function
# argument true), use one unit of fuel expended per step. For part 2 (false),
# fuel use increases by 1 for each step taken.
function crabs(part1::Bool)

    bestPos = 0
    bestFuel = 0

    # Try each possible position
    for pos in 1:maxPos

        # Add up fuel use
        fuel = 0
        for crab in data
            dist = abs(crab - pos)
            if part1
                fuel += dist
            else
                fuel += sumnums(dist)
            end
        end
        #println("Position $pos: fuel = $fuel")

        # Remember if better than last (i.e., less fuel use)
        if bestPos == 0 || fuel < bestFuel
            bestPos = pos
            bestFuel = fuel
        end
    end

    # Show the best result found
    println("Best position $bestPos: fuel = $bestFuel")

end

# Sum up integers up to n
function sumnums(n)
    nn = 0
    for i in 1:n
        nn += i
    end
    return nn
end

println("--- PART 1 ---")
crabs(true)

println("--- PART 2 ---")
crabs(false)


# ---
# title: Fourier transform of Moisan periodic image component
# id: demo_perfft2
# cover: assets/perfft2.png
# author: Peter Kovesi
# date: 2018-10-26
# ---

# The function `perfft2()` implements Moisan's "Periodic plus Smooth Image
# Decomposition" which decomposes an image into two components
#
#         img = p + s
#
# where `s` is the 'smooth' component with mean 0 and `p` is the 'periodic' component
# which has no sharp discontinuities when one moves cyclically across the image
# boundaries.
#
# This decomposition is very useful when one wants to obtain an FFT of an image
# with minimal artifacts introduced from the boundary discontinuities. The image
# `p` gathers most of the image information but avoids periodization artifacts.
#
# Reference:
# L. Moisan, "Periodic plus Smooth Image Decomposition", Journal of
# Mathematical Imaging and Vision, vol 39:2, pp. 161-179, 2011.

using Images
using FFTW
using ImagePhaseCongruency
using ImageContrastAdjustment
using TestImages

img = Float64.(Gray.(testimage("lena")))

IMG = fft(img)               # 'Standard' fft
(P, S, p, s) = perfft2(img)  # 'Periodic' fft

mosaic(
    adjust_histogram(Gray.(p), LinearStretching()),
    adjust_histogram(s, LinearStretching()),
    ## Note the vertical and horizontal cross in
    ## the spectrum induced by the non-periodic edges.
    adjust_histogram(log.(abs.(fftshift(IMG)) .+ 1), LinearStretching()),
    ## Note the clean spectrum because p is periodic.
    adjust_histogram(log.(abs.(fftshift(P)) .+ 1), LinearStretching());
    nrow=2, rowmajor=true
)
# Top 1) left: periodic component 2) right: smooth component
#
# Bottom 3) left: spectrum of standard FFT 4) right: spectrum of periodic component

# save cover image #src
isdir("assets") || mkdir("assets") #src
cover = Gray.(adjust_histogram(log.(abs.(fftshift(P)) .+ 1), LinearStretching())) #src
save(joinpath("assets", "perfft2.png"), cover) #src


"""
    rlap(A)

Compute renormalized graph Laplacian for adjacency matrix A.
"""
function rlap(A)
    W = diagm(A * ones(size(A, 1)))
    L̃ = W \ (W \ A)'
    W̃ = diagm(L̃ * ones(size(L̃, 1)))

    println("Laplacian sparsity = $(sparsity(W̃ \ (W̃ - L̃)))")
    return sparse(W̃ \ (W̃ - L̃))
end

"""
    gaussian_similarity(X, σ=1)

Compute similarity using Gaussian kernel. Enforces sparsity by setting the similarity of
distant points to zero.
"""
function gaussian_similarity(X, σ=1)
    𝒦 = pairwise(Euclidean(), X, dims=2)
    n = size(X, 2)
    for i in 1:n
        for j in i+1:n
            𝒦[j, i] = 𝒦[j, i] <= 3σ ? exp(-(𝒦[j,i]/σ)^2) : 0
            𝒦[i, j] = 𝒦[j, i]
        end
    end
    𝒦[diagind(𝒦)] .= 1

    s = sparsity(𝒦)
    if s < 0.8
        println("Warning. sparsity = $s")
    end

    return 𝒦
end


"""
    Base.rand(pp::PoissonProcess, intervallength::Float64) -> Vector{Float64}

Simulate `pp` over the time interval `[0, intervallength]`, and return a vector of the
arrival times.

##### Complexity
- Time complexity: `O(E[N(t)]log(E[N(t)]))`, in expectation, where E[N(t)] denotes the
expected number of arrivals over the time interval `[0, intervallength]`.
"""
function Base.rand(pp::PoissonProcess, intervallength::AbstractFloat)
    # instantiate a Poisson random variable with distribution F1
    F1 = Poisson(nexpectedarrivals(pp, intervallength))
    # sample from F1
    narrivals = rand(F1)
    # no arrivals; return an empty Vector
    if narrivals == 0
        return []
    end

    # instantiate a Uniform random variable with distribution F2
    F2 = Uniform(0, intervallength)
    # allocate memory
    arrivaltimes = Vector{Float64}(undef, narrivals)
    for i in 1:narrivals
        arrivaltimes[i] = rand(F2)
    end
    return sort!(arrivaltimes)
end

"""
    Base.rand(pp::PoissonProcess, intervallength::Integer) -> Vector{Float64}

Allow the user to supply an integer `intervallength`, and work-around method ambiguity
arising from `Random`.
"""
function Base.rand(pp::PoissonProcess, intervallength::Integer)
    return Base.rand(pp, Float64(intervallength))
end


using Pkg
Pkg.install(["LinearAlgebra", "Plots", "PlotThemes", "ScikitLearn"])
using LinearAlgebra

m = [1 2 3 4; 4 3 2 1; 1 1 1 1; 0 0 0 0]
transpose(m)


#! /usr/bin/julia

# Rosetta Code, Truncatable primes

function isltruncprime{T<:Integer}(n::T, base::T=10)
    isprime(n) || return false
    p = n
    f = prevpow(base, p)
    while 1 < f
        (d, p) = divrem(p, f)
        isprime(p) || return false
        d != 0 || return false
        f = div(f, base)
    end
    return true
end

function isrtruncprime{T<:Integer}(n::T, base::T=10)
    isprime(n) || return false
    p = n
    while base < p
        p = div(p, base)
        isprime(p) || return false
    end
    return true
end

hi = 10^6

for i in reverse(primes(hi))
    isltruncprime(i) || continue
    println("The largest  left truncatable prime ≤ ", hi, " is ", i, ".")
    break
end

for i in reverse(primes(hi))
    isrtruncprime(i) || continue
    println("The largest right truncatable prime ≤ ", hi, " is ", i, ".")
    break
end


using Clustering;
using Distances;

function getLandmarks(X, p, method=:Random)
    if(method == :Random)
        numberOfPoints = size(X,2);
        landmarks = X[:,randperm(numberOfPoints)[1:p]];
        return landmarks;
    end

    if(method == :Kmeans)
        kmeansResult = kmeans(X,p)
        return kmeansResult.centers;
    end

    throw(ArgumentError("method can only be :Kmeans or :Random"));
end

function gaussianKernel(distance, bandwidth)
    exp(-distance / (2*bandwidth^2));
end

function getLinearCoding(X, landmarks, bandwidth, r)
    distances = pairwise(Distances.Euclidean(), landmarks, X);
    similarities = map(x -> gaussianKernel(x, bandwidth), distances);
    ZHat = spzeros(size(similarities,1), size(similarities,2));

    for i in 1:(size(similarities,2))
        topLandMarksIndices = selectperm(similarities[:,i], 1:r, rev=true);
        topLandMarksCoefficients = similarities[topLandMarksIndices, i];
        topLandMarksCoefficients = topLandMarksCoefficients / sum(topLandMarksCoefficients);
        ZHat[topLandMarksIndices,i] = topLandMarksCoefficients;
    end
    return diagm(sum(ZHat,2)[:])^(-1/2) * ZHat;
end

function LSCClustering(X, nrOfClusters, nrOfLandmarks, method, nonZeroLandmarkWeights, bandwidth)
    landmarks = getLandmarks(X, nrOfLandmarks, method)
    ZHat = getLinearCoding(X, landmarks, bandwidth, nonZeroLandmarkWeights)
    svdResult = svd(transpose(ZHat))
    clusteringResult = kmeans(transpose(svdResult[1][:,1:nrOfClusters]),nrOfClusters);
    return clusteringResult
end


using Test
using WavePropBase
using StaticArrays
using WavePropBase.Geometry
using WavePropBase.Integration
using LinearAlgebra
using Random
Random.seed!(1)

@testset "Reference segment" begin
    qstd = Fejer(20)
    d     = domain(qstd)
    # simple test on smooth integrand
    for shand in [IMT(), Kress(), KressP()]
        q     = SingularQuadratureRule(qstd,shand)
        @test isapprox(integrate(cos,q),sin(1),rtol=1e-2)
    end
    # non-smooth integrand, singularity at 0
    f     = (x) -> log(abs(x))
    Ie    = -1
    for shand in [IMT(), Kress(),KressP()]
        q     = SingularQuadratureRule(qstd,shand)
        Ia    = integrate(f,q)
        @test isapprox(Ia,Ie,rtol=1e-4)
        # check that the `naive` integration woudl have failed the test
        Istd    = integrate(f,qstd)
        @test !isapprox(Istd,Ie,rtol=1e-4)
    end
    # test that KressP() can handle singularity at both endpoints, and Kress() cannot
    f     = (x) -> log(x) + log(1-x)
    Ie    = -2
    q     = SingularQuadratureRule(qstd,KressP())
    Ia    = integrate(f,q)
    @test isapprox(Ia,Ie,rtol=1e-5)
    q     = SingularQuadratureRule(qstd,Kress())
    Ia    = integrate(f,q)
    @test !isapprox(Ia,Ie,rtol=1e-5)
    # non-smooth integrand with singularity inside
    s     = 0.1
    f     = (x) -> log(abs(x-s))
    Ie    = -1.32508297337145
    # FIXME: for the time being, usign IMT with a singular quadrature rule for
    # interior points yields a rounding problem because the change of variables
    # accumulates points very quickly around zero. That is OK when the
    # singularity is at zero, but shifting such nodes to another point s ∈ (0,1]
    # has to be done with care since these can get rounded to `s`, which in turn
    # yields a singular value for the integrand. Note that the same can happen
    # with other singularity handler types, but IMT is worse due to the
    # exponential nature of the change of variables.
    for shand in [Kress(),KressP()]
        q     = SingularQuadratureRule(qstd,shand)
        x,w  = q(s) # singular nodes and weights which depend on s
        Ia    = integrate(f,x,w)
        @test isapprox(Ia,Ie,rtol=1e-5)
        # check that the `naive` integration woudl have failed the test
        Istd    = integrate(f,qstd)
        @test !isapprox(Istd,Ie,rtol=1e-5)
    end
end

@testset "Duffy" begin
    q1d   = Fejer(5)
    qstd  = TensorProductQuadrature(q1d,q1d)
    duffy = Duffy()
    qsin  = SingularQuadratureRule(qstd,duffy)
    x,w  = qsin()
    # regular kernel
    k    = x -> cos(x[1])*sin(x[2])
    Ie   = 1/2*(sin(1)-cos(1))
    Ia   = integrate(k,qsin)
    @test isapprox(Ie,Ia,rtol=1e-4)
    # singular kernel at right vertex
    s    = SVector(1,0)
    k    = x -> 1/norm(x-s)
    Ia   = integrate(k,qsin)
    Ie   = acosh(sqrt(2))
    @test isapprox(Ie,Ia,rtol=1e-5)
    # singular kernel at wrong vertex (should not be accurate, see test below)
    s    = SVector(0,1)
    k    = x -> 1/norm(x-s)
    Ie   = acosh(sqrt(2))
    Ia   = integrate(k,qsin)
    @test !isapprox(Ie,Ia,rtol=1e-5)
end

@testset "2d Kress" begin
    q1d   = Fejer{10}()
    qstd  = TensorProductQuadrature(q1d,q1d)
    s1d   = Kress(order=2)
    sing_handler = TensorProductSingularityHandler(s1d,s1d)
    qsin = SingularQuadratureRule(qstd,sing_handler)
    x,w  = qsin()
    # regular kernel
    k = (x) -> cos(x[1])
    Ie   = sin(1)
    Isin = sum(k.(x).*w)
    @test isapprox(Ie,Isin,rtol=1e-6)
    # singular kernel at origin
    k  = (x) -> 1/norm(x)
    Ie = 1/2*log(17 + 12*sqrt(2)) # Wolfram alpha is your friend ;-)
    Isin = sum(k.(x).*w)
    @test isapprox(Ie,Isin,rtol=1e-2)
end


#
# Simple Pendulum Problem
#
using OrdinaryDiffEq, Plots

#Constants
const g = 9.81
L = 1.0

#Initial Conditions
u₀ = [0,π/2]
tspan = (0.0,6.3)

#Define the problem
function simplependulum(du,u,p,t)
    θ  = u[1]
    dθ = u[2]
    du[1] = dθ
    du[2] = -(g/L)*sin(θ)
end

#Pass to solvers
prob = ODEProblem(simplependulum,u₀, tspan)
sol = solve(prob,Tsit5())

#Plot
plot(sol,linewidth=2,title ="Simple Pendulum Problem", xaxis = "Time", yaxis = "Height", label = ["Theta","dTheta"])