module bearingDistributions
using Distributions
using LinearAlgebra
using Plots

include("App.jl")
using .App

export Bearing, intersectGrid, bisectionRoots, plotConfidenceInterval, App

"""
Bearing object.

Defined by a mean direction θ, a standard deviation of direction σ, and a position (x,y).
"""
struct Bearing
    θ
    σ
    x
    y
end

"""
findIntersect(b1::Bearing, b2::Bearing)

This solves the simultaneous equations of the two paramterized lines as below:
    x₁ + cos(θ₁)t₁ = x₂ + cos(θ₂)t₂
    y₁ + sin(θ₁)t₁ = y₂ + sin(θ₂)t₂

    x₁ - x₂ = -cos(θ₁)t₁ + cos(θ₂)t₂
    y₁ - y₂ = -sin(θ₁)t₁ + sin(θ₂)t₂

    This can be represented in matrix form as:
    b = Ax̄

    where:
    x̄ = [t₁, t₂]
    A = |-cos(θ₁) cos(θ₂)|
        |-sin(θ₁) sin(θ₂)|

    b = [x₁ - x₂, y₁ - y₂]

    Solved by:
    x̄ = A⁻¹ ⋅ b

    This matrix is singular when θ₁ and θ₂ are parallel, i.e.  mod(θ₁,π) == mod(θ₂,π)
    In this case, there is a heuristic that sets the "intersection" as the midpoint between the two points.

    Furthermore, if tᵢ<0 then there is no defined intersection point (lines pointed away from each other)
"""
function findIntersect(b1::Bearing, b2::Bearing)
    if mod(b1.θ-b2.θ,π)≈0
        point = ([b1.x;b1.y] + [b2.x;b2.y])/2
        return point
    end

    A = [-cos(b1.θ) cos(b2.θ);
         -sin(b1.θ) sin(b2.θ)]

    b = [b1.x - b2.x, b1.y - b2.y];

    t = inv(A) * b;
    if any(t .< 0)
        point = ([b1.x;b1.y] + [b2.x;b2.y])/2
        return point
    end
    point = [b1.x, b1.y] + [cos(b1.θ), sin(b1.θ)]*t[1];
    return point
end

"""
boundingBox(b1::Bearing, b2::Bearing)
    Return the box [xₗ xᵤ;yₗ yᵤ] around the intersection of two bearings.

    Size the box such that the origin of the bearings will always be included in the box.
"""
function boundingBox(b1::Bearing, b2::Bearing)
    pᵢ = findIntersect(b1, b2);
    p₁ = [b1.x;b1.y];
    p₂ = [b2.x;b2.y];
    d₁ = pᵢ - p₁;
    d₂ = pᵢ - p₂;
    D₁ = norm(d₁);
    D₂ = norm(d₂);
    D = 1.5*max(D₁, D₂);
    box = pᵢ .+ 1*[-D D;-D D];
    box
end

"""
rangeFromBox(box::Array{Number,2}; length = 51)

    Wrapper to splat out the box array.
"""
function rangeFromBox(box::Array{T,2} where T<:Number ; length = 51)
    x,y = rangeFromBox(box...; length)
    return x,y
end

"""
rangeFromBox(x1,y1,x2,y2; length)

    convert `x1`:`x2` and `y1`:`y2` into ranges of length `length`
"""
function rangeFromBox(x1,y1,x2,y2; length)
    x = range(x1, x2; length);
    y = range(y1, y2; length);
    return x,y
end

"""
probabilityGrid(b::Bearing, x::AbstractArray, y::AbstractArray)

    Compute the probability grid over ranges `x` and `y` of bearing `b`
"""
function probabilityGrid(b::Bearing, x::AbstractArray, y::AbstractArray)
    θ = mod(b.θ + π,2π) - π
    D = Normal(0, b.σ);
    Θ = atan.(y .- b.y, x' .- b.x) .- θ;
    Θ = mod.(Θ .+ π, 2π) .-π
    P = pdf.(D, Θ);
    ΔA = convert(Float64,x.step * y.step);
    P = P./sum(P[:])/ΔA;
    return x, y, P
end

"""
intersectGrid(b1::Bearing, b2::Bearing; length = 51)

    Compute the probability grid of the intersection of `b1` and `b2` over ranges `x` and `y`

    Return the probability grids for `b1`, `b2`, and the intersection.

"""
function intersectGrid(b1::Bearing, b2::Bearing; length = 51)
    box = boundingBox(b1, b2);
    x,y = rangeFromBox(box...; length)
    _,_,P₁ = probabilityGrid(b1, x, y);
    _,_,P₂ = probabilityGrid(b2, x, y);
    Pᵢ = P₁ .* P₂
    ΔA = convert(Float64,x.step * y.step);
    Pᵢ = Pᵢ ./ sum(Pᵢ[:])
    Pᵢ = Pᵢ ./ ΔA;
    return x, y, Pᵢ, P₁, P₂
end

"""
plotProbabilityGrids(x::AbstractArray, y::AbstractArray, P...)

    Plot of tuple of probability grids `P` on ranges x and y with the default contour levels
"""
function plotProbabilityGrids(x::AbstractArray, y::AbstractArray, P...)
    p = contour(;aspect_ratio=:equal);
    for P in P
        contour!(x,y,P)
    end
    display(p)
end

"""
plotConfidenceInterval(α::Number, x::AbstractArray, y::AbstractArray, P...)

    Plot the tuple of probability grids `P` on ranges x and y at the defined confidence interval `α`
"""
function plotConfidenceInterval(α::Number, x::AbstractArray, y::AbstractArray, P...)
    handle = contour(;aspect_ratio=:equal);
    ΔA = convert(Float64,x.step*y.step);
    zeros = [bisectionRoots((p) -> ΔA*sum(Pⱼ[Pⱼ .> p]) - α, minimum(Pⱼ),maximum(Pⱼ)) for Pⱼ in P]
    for (i,P) in enumerate(P)
        contour!(x,y,P; levels=[zeros[i]])
    end
    return handle
end

"""
bisectionRoots(f::Function,xₗ::Number,xᵤ::Number; max_iteration = 100, xtol = 1e-3)

    Computes the root of function `f(x)` on the bounded range `[xₗ xᵤ]` with the Bisection algorithm.

    Exit condition is defined by xᵤ-xₗ < xtol
"""
function bisectionRoots(f::Function,xₗ::Number,xᵤ::Number; max_iteration = 100, xtol = 1e-3)
    fₗ  = f(xₗ);
    fᵤ = f(xᵤ);

    if fₗ == 0
        return xₗ
    elseif fᵤ == 0
        return xᵤ
    end

    if (fₗ<0 && fᵤ<0) || (fₗ>0 && fᵤ>0)
        return NaN
    end

    xᵢ = (xₗ + xᵤ)/2;
    iter = 1;

    while abs(xᵤ-xₗ) > xtol
        iter > max_iteration ? break : iter+=1;
        if sign(f(xᵢ))==sign(fₗ)
            xₗ = xᵢ
            xᵢ = (xᵢ + xᵤ)/2
        else
            xᵤ = xᵢ
            xᵢ = (xₗ + xᵢ)/2
        end
    end
    return xᵢ
end

end


"""
    integral(f, t = echotime(f))

Integral of time profile `f` between `0` and `t`. Unless specified, the echotime
is used as the upper integral limit.

For the `PGSE`, `SinOGSE`, `CosOGSE` and `DoublePGSE` sequences, analytical
expressions are available. Otherwise a numerical integral is computed.
"""
function integral(f::TimeProfile, t = echotime(f))
    quadgk(f, zero(t), t)
end

function integral(f::PGSE, t = echotime(f))
    δ, Δ = f.δ, f.Δ
    (zero(t) ≤ t < δ) * t + (δ ≤ t < Δ + δ) * δ - (Δ ≤ t < Δ + δ) * (t - Δ)
end

function integral(f::DoublePGSE, t = echotime(f))
    δ, Δ, p = f.δ, f.Δ, f.tpause
    tmid = p + Δ + δ
    if zero(t) ≤ t < δ
        t
    elseif δ ≤ t < Δ
        δ
    elseif Δ ≤ t < Δ + δ
        δ - (t - Δ)
    elseif tmid ≤ t < tmid + δ
        t - tmid
    elseif tmid + δ ≤ t < tmid + Δ
        δ
    elseif tmid + Δ ≤ t < tmid + Δ + δ
        δ - (t - Δ - tmid)
    else
        zero(t)
    end
end

function integral(f::CosOGSE, t = echotime(f))
    δ, Δ, n = f.δ, f.Δ, f.nperiod
    ((t < δ) * sin(2π * n * t / δ) - (Δ ≤ t) * sin(2π * n * (t - Δ) / δ)) * δ / (2π * n)
end

function integral(f::SinOGSE, t = echotime(f))
    δ, Δ, n = f.δ, f.Δ, f.nperiod
    ((t < δ) * (1 - cos(2π * n * t / δ)) - (Δ ≤ t) * (1 - cos(2π * n * (t - Δ) / δ))) * δ /
    (2π * n)
end


# Load Julia packages (libraries) needed  for the snippets in chapter 0

using StatisticalRethinking, Optim
#gr(size=(600,600));

# CmdStan uses a tmp directory to store the output of cmdstan

ProjDir = rel_path("..", "scripts", "04")
cd(ProjDir)

# ### snippet 4.24

howell1 = CSV.read(rel_path("..", "data", "Howell1.csv"), delim=';')
df = convert(DataFrame, howell1);
df2 = filter(row -> row[:age] >= 18, df);
first(df2, 5)

# ### snippet 4.25

# Our first model:

m4_1 = "
  height ~ Normal(μ, σ) # likelihood
  μ ~ Normal(178,20) # prior
  σ ~ Uniform(0, 50) # prior
"

# ### snippet 4.26

# Compute MAP

obs = df2[:height]

function loglik(x)
  ll = 0.0
  ll += log(pdf(Normal(178, 20), x[1]))
  ll += log(pdf(Uniform(0, 50), x[2]))
  ll += sum(log.(pdf.(Normal(x[1], x[2]), obs)))
  -ll
end

# ### snippet 4.28

x0 = [ 178, 10.0]
lower = [0.0, 0.0]
upper = [250.0, 50.0]

# ### snippet 4.27

inner_optimizer = GradientDescent()

optimize(loglik, lower, upper, x0, Fminbox(inner_optimizer))

# Our second model:

m4_2 = "
  height ~ Normal(μ, σ) # likelihood
  μ ~ Normal(178, 0.1) # prior
  σ ~ Uniform(0, 50) # prior
"

# ### snippet 4.29

# Compute MAP

obs = df2[:height]

function loglik2(x)
  ll = 0.0
  ll += log(pdf(Normal(178, 0.1), x[1]))
  ll += log(pdf(Uniform(0, 50), x[2]))
  ll += sum(log.(pdf.(Normal(x[1], x[2]), obs)))
  -ll
end

optimize(loglik2, lower, upper, x0, Fminbox(inner_optimizer))

# End of `clip-24-29s.jl`


## This is an implementation of
## Marsaglia, G. and Tsang, W.W., 2000. A simple method for generating gamma
## variables. ACM Transactions on Mathematical Software (TOMS), 26(3)
@inline function sampleGamma(α::Float64, rng::RNG) where RNG <: AbstractRNG
  @assert α > 0.0
  if α < 1.0
    return rand(rng)^(1/α)*sampleGamma(1.0+α, rng)
  end
  d::Float64 = α - 1/3
  c::Float64 = 1/sqrt(9*d)
  while true
    x::Float64 = randn(rng)
    v::Float64 = (1 + c*x)^3
    v < 0 && continue
    u::Float64 = rand(rng)
    x2::Float64 = x*x
    x4::Float64 = x2*x2
    if u < 1.0 - 0.0331*x4 return d*v end
    if log(u) < 0.5*x2 + d*(1-v+log(v))
      return d*v
    end
  end
end

@inline function sampleGamma(α::Float64, β::Float64,
  rng::RNG = GLOBAL_RNG) where RNG <: AbstractRNG
  return sampleGamma(α, rng) / β
end


using ImplicitGraphs, Primes

"""
    iPaley(p::Int)

Create an implicit Paley graph on `p` vertices.
Here `p` must be a prime congruent to 1 modulo 4.
The vertices of the graph are integers in the range
`0:p-1` and two vertices are adjacent if their difference
is a quadratic residue mod `p`.
"""
function iPaley(p::Int)::ImplicitGraph
    if !isprime(p) || p % 4 != 1
        error("Argument ($p) must be prime and congruent to 1 mod 4")
    end

    vcheck(v::Int) = (0 <= v < p)

    function outs(v::Int)
        return unique((v + k^2) % p for k = 1:p-1)
    end

    return ImplicitGraph{Int}(vcheck, outs)
end


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

using Markdown
using InteractiveUtils

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

# ╔═╡ f66e452c-97db-11eb-23fc-bd95da10e2d6
using Plots, FFTW, TestImages, PlutoUI, Images, ImageShow

# ╔═╡ adaa78fa-d805-45e6-ba5f-d91a5d4823d6
using ImageContrastAdjustment

# ╔═╡ 09fc6764-2050-4276-8a14-3596368d0c32
md"""
# How much data do we need?

One of the challenges of information representation generally is how much data do we need to represent an object/concept sufficiently. It should be noted, that "represent sufficiently" is an ill-defined term, since this judgement will be application specific.

In order to address this question, we are going to assume that a concept can be represented as a function. A concept can be quite general but in this notebook we will be using images as our concepts. That is, an image represents some function and this function can be represented as a combination of __basis functions__. So when we ask, "How much data do you need?" can be restated as "How many basis functions do you need?"

## What is a basis function?

I'll answer this later.

We will generally need an infinite number of basis functions to represent an arbitrary ???. However, in our digital world we can only use a finite number of basis functions, and with a finite number of basis functions we can only __approximate__ these functions.

### Sinusoidal Basis Functions

In this notebook we are going to look at information representation in terms of sinusoidal basis functions. The canonical information representation based on sinusoidal basis functions is the [Fourier transform](https://en.wikipedia.org/wiki/Fourier_transform). In 1822 Jean-Baptiste Joseph Fourier demonstrated that (most) continous functions can be represented as a summation of an infinite number of appropriately weighted and phased sinusoidal (sines, cosines, complex exponentials) functions. The sinusoidal functions are an example of __basis__ functions. A key concept in the Fourier transform is the need for an infinite number of basis functions.

In applications such as magnetic resonance imaging (MRI) of the human body, we can view the body region being imaged as the continuous function. The MRI technique actually measures samples from the Fourier transform of the person's body region. How we sample this signal relates to both the cost of the acquisition and the integrity (error) of our approximation.

In communications, how many basis functions we use relates to how quickly we can transmit information using a radio wave or a fiber optic cable.

In this notebook we are going to look at information representation in terms of sinusoidal basis functions, specifically, the [Discrete Cosine Transform (DCT)](https://en.wikipedia.org/wiki/Discrete_cosine_transform), which is commonly used in signal processing and image.

## Image Example

To start with, we are going to look at a public domain digital image. As the image is already digitized, we are not going to worry about how the discrete image is only an approximation of the continuous physical object.

I will refer to the inverse DCT of an image as the __spectrum__. The spectrum corresponds to the coefficients for each basis function.

I've taken the DCT of the image and then prior to taking the inverse DCT (IDCT), I've zeroed out a fraction of the spectrum. (NEED TO DEFINE SPECTRUM.) based on the slider value _kp_. _kp_ represents what fraction of the spectrum was kept (not zeroed out) starting with the DC term. So a _kp_ of zero throws out all the original data and a _kp_ of 1 keeps all the original data.

$(@bind go Button("Start/Restart"))
"""

# ╔═╡ e50c3024-140d-4312-ad8e-66b94482964b
md"""# DCT Basis Functions

DCT forms the basis of JPEG image compression. In JPEG compression the image is split into 8x8 tiles. The DCT is computed for each tile. As was demonstrated above, an image can be recognized with a high degree of certitude wihtout many of the high frequency terms. JPEG exploits this by discarding the coefficients for high frequency basis functions within the DCT for each tile.

What do these basis functions look like? For an 8x8 tile, there are 64 distinct basis functions. We can visualize these basis functions by taking the inverse DCT for a Kronecker Delta spectrum.

Plot the desired basis function by selecting the desired Kronecker Delta value δ(i,j)
"""

# ╔═╡ f90425a3-8e6f-440a-8522-5ccd5cca2797

md"""

Create a Different Kronecker Delta spectrum by changin the (i,j) vaues.

i
$(@bind i Slider(1:1:8))
j
$(@bind j Slider(1:1:8))
"""

# ╔═╡ 899a3541-9cef-48de-a03d-367223fe3100
md"""### All 64 Basis Functions"""

# ╔═╡ 99028838-babb-4d29-a400-c321c65e455d
md"""### Code cells are hidden down here"""

# ╔═╡ 85859f7b-26e2-415e-87f1-b61180449a61
images = [i for i in TestImages.remotefiles if ! contains(i, "tif") && ! contains(i, "256") && ! contains(i, "color")] ;

# ╔═╡ 0d142947-1e3c-4adc-b6ca-09c502f89f0e
begin
	go
	ind = rand(1:length(images))
	iname = popat!(images, ind)
	img = Gray.(testimage(iname));
	aimg = Float64.(img) ;
	kimg = dct(aimg) ;
	@bind kp Slider(0:0.01:1, show_value=true)
end

# ╔═╡ c4d7b864-275a-4e6f-9cc1-74138a1f07ae
function kd2(i,j)
	img = zeros((8,8))
	img[i,j] = 1
	img
end ;

# ╔═╡ 4369ef1b-4291-4b2f-8bc1-ac011080bb18
bimg = kd2(i,j) ;

# ╔═╡ 740ecf61-15d7-4b30-8ef8-e9ed339912af
md"""
### Kronecker Delta Spectrum

$(Gray.(bimg))

### Resulting Basis Function

$(Gray.(6*idct(bimg)))
"""

# ╔═╡ 6c43a6ef-41af-4343-910b-04e72fdc87fd
alg = Equalization(nbins = 256);

# ╔═╡ 2b98ae25-097e-4dd5-aa4b-01a3b06c2fc8
begin
	tmp3 = zeros(64,64) ;
	for j in 0:7
		for i in 0:7
			tmp3[i*8+1:i*8+8,j*8+1:j*8+8] =idct(kd2(i+1,j+1))
		end
	end
	imresize(adjust_histogram(Gray.(tmp3), alg), 256, 256)
end

# ╔═╡ 628877b2-6e8d-47a1-8dce-752b9d75ab56
function filter_img(kimg, p)
	sz = size(kimg)
	fsz = convert.(Int64, round.(sz .* p))
	f = zeros(sz)
	f[1:fsz[1],1:fsz[2]] .= 1
	idct(kimg .* f)
end;

# ╔═╡ 12fb095f-02a6-48c6-856a-afbc71a6ad64
fimg = filter_img(kimg, kp) ;

# ╔═╡ 48412713-7266-43dc-a49e-bab349318cce
md"""
## What do you think this image is?

Increase kp to provide more basis functions for representing the image. kp represents the fraction of the discrete cosine transform basis functions to use (extending out from the DC term. What is the minimum kp for you to recognize the image?

$(Gray.(fimg))

kp
"""

# ╔═╡ 00000000-0000-0000-0000-000000000001
PLUTO_PROJECT_TOML_CONTENTS = """
[deps]
FFTW = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341"
ImageContrastAdjustment = "f332f351-ec65-5f6a-b3d1-319c6670881a"
ImageShow = "4e3cecfd-b093-5904-9786-8bbb286a6a31"
Images = "916415d5-f1e6-5110-898d-aaa5f9f070e0"
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
PlutoUI = "7f904dfe-b85e-4ff6-b463-dae2292396a8"
TestImages = "5e47fb64-e119-507b-a336-dd2b206d9990"

[compat]
FFTW = "~1.4.5"
ImageContrastAdjustment = "~0.3.7"
ImageShow = "~0.3.1"
Images = "~0.24.1"
Plots = "~1.22.2"
PlutoUI = "~0.7.11"
TestImages = "~1.6.1"
"""

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

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

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

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

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

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

[[AxisArrays]]
deps = ["Dates", "IntervalSets", "IterTools", "RangeArrays"]
git-tree-sha1 = "d127d5e4d86c7680b20c35d40b503c74b9a39b5e"
uuid = "39de3d68-74b9-583c-8d2d-e117c070f3a9"
version = "0.4.4"

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

[[CatIndices]]
deps = ["CustomUnitRanges", "OffsetArrays"]
git-tree-sha1 = "a0f80a09780eed9b1d106a1bf62041c2efc995bc"
uuid = "aafaddc9-749c-510e-ac4f-586e18779b91"
version = "0.2.2"

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

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

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

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

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

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

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

[[ComputationalResources]]
git-tree-sha1 = "52cb3ec90e8a8bea0e62e275ba577ad0f74821f7"
uuid = "ed09eef8-17a6-5b46-8889-db040fac31e3"
version = "0.3.2"

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

[[CoordinateTransformations]]
deps = ["LinearAlgebra", "StaticArrays"]
git-tree-sha1 = "6d1c23e740a586955645500bbec662476204a52c"
uuid = "150eb455-5306-5404-9cee-2592286d6298"
version = "0.6.1"

[[CustomUnitRanges]]
git-tree-sha1 = "1a3f97f907e6dd8983b744d2642651bb162a3f7a"
uuid = "dc8bdbbb-1ca9-579f-8c36-e416f6a65cce"
version = "1.0.2"

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

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

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

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

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

[[Distances]]
deps = ["LinearAlgebra", "Statistics", "StatsAPI"]
git-tree-sha1 = "9f46deb4d4ee4494ffb5a40a27a2aced67bdd838"
uuid = "b4f34e82-e78d-54a5-968a-f98e89d6e8f7"
version = "0.10.4"

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

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

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

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

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

[[FFTViews]]
deps = ["CustomUnitRanges", "FFTW"]
git-tree-sha1 = "70a0cfd9b1c86b0209e38fbfe6d8231fd606eeaf"
uuid = "4f61f5a4-77b1-5117-aa51-3ab5ef4ef0cd"
version = "0.3.1"

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

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

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

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

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

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

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

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

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

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

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

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

[[Graphics]]
deps = ["Colors", "LinearAlgebra", "NaNMath"]
git-tree-sha1 = "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"

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

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

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

[[IdentityRanges]]
deps = ["OffsetArrays"]
git-tree-sha1 = "be8fcd695c4da16a1d6d0cd213cb88090a150e3b"
uuid = "bbac6d45-d8f3-5730-bfe4-7a449cd117ca"
version = "0.3.1"

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

[[ImageAxes]]
deps = ["AxisArrays", "ImageCore", "Reexport", "SimpleTraits"]
git-tree-sha1 = "794ad1d922c432082bc1aaa9fa8ffbd1fe74e621"
uuid = "2803e5a7-5153-5ecf-9a86-9b4c37f5f5ac"
version = "0.6.9"

[[ImageContrastAdjustment]]
deps = ["ColorVectorSpace", "ImageCore", "ImageTransformations", "Parameters"]
git-tree-sha1 = "2e6084db6cccab11fe0bc3e4130bd3d117092ed9"
uuid = "f332f351-ec65-5f6a-b3d1-319c6670881a"
version = "0.3.7"

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

[[ImageDistances]]
deps = ["ColorVectorSpace", "Distances", "ImageCore", "ImageMorphology", "LinearAlgebra", "Statistics"]
git-tree-sha1 = "6378c34a3c3a216235210d19b9f495ecfff2f85f"
uuid = "51556ac3-7006-55f5-8cb3-34580c88182d"
version = "0.2.13"

[[ImageFiltering]]
deps = ["CatIndices", "ColorVectorSpace", "ComputationalResources", "DataStructures", "FFTViews", "FFTW", "ImageCore", "LinearAlgebra", "OffsetArrays", "Requires", "SparseArrays", "StaticArrays", "Statistics", "TiledIteration"]
git-tree-sha1 = "bf96839133212d3eff4a1c3a80c57abc7cfbf0ce"
uuid = "6a3955dd-da59-5b1f-98d4-e7296123deb5"
version = "0.6.21"

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

[[ImageMetadata]]
deps = ["AxisArrays", "ColorVectorSpace", "ImageAxes", "ImageCore", "IndirectArrays"]
git-tree-sha1 = "ae76038347dc4edcdb06b541595268fca65b6a42"
uuid = "bc367c6b-8a6b-528e-b4bd-a4b897500b49"
version = "0.9.5"

[[ImageMorphology]]
deps = ["ColorVectorSpace", "ImageCore", "LinearAlgebra", "TiledIteration"]
git-tree-sha1 = "68e7cbcd7dfaa3c2f74b0a8ab3066f5de8f2b71d"
uuid = "787d08f9-d448-5407-9aad-5290dd7ab264"
version = "0.2.11"

[[ImageQualityIndexes]]
deps = ["ColorVectorSpace", "ImageCore", "ImageDistances", "ImageFiltering", "OffsetArrays", "Statistics"]
git-tree-sha1 = "1198f85fa2481a3bb94bf937495ba1916f12b533"
uuid = "2996bd0c-7a13-11e9-2da2-2f5ce47296a9"
version = "0.2.2"

[[ImageShow]]
deps = ["Base64", "FileIO", "ImageCore", "OffsetArrays", "Requires", "StackViews"]
git-tree-sha1 = "832abfd709fa436a562db47fd8e81377f72b01f9"
uuid = "4e3cecfd-b093-5904-9786-8bbb286a6a31"
version = "0.3.1"

[[ImageTransformations]]
deps = ["AxisAlgorithms", "ColorVectorSpace", "CoordinateTransformations", "IdentityRanges", "ImageCore", "Interpolations", "OffsetArrays", "Rotations", "StaticArrays"]
git-tree-sha1 = "e4cc551e4295a5c96545bb3083058c24b78d4cf0"
uuid = "02fcd773-0e25-5acc-982a-7f6622650795"
version = "0.8.13"

[[Images]]
deps = ["AxisArrays", "Base64", "ColorVectorSpace", "FileIO", "Graphics", "ImageAxes", "ImageContrastAdjustment", "ImageCore", "ImageDistances", "ImageFiltering", "ImageIO", "ImageMagick", "ImageMetadata", "ImageMorphology", "ImageQualityIndexes", "ImageShow", "ImageTransformations", "IndirectArrays", "OffsetArrays", "Random", "Reexport", "SparseArrays", "StaticArrays", "Statistics", "StatsBase", "TiledIteration"]
git-tree-sha1 = "8b714d5e11c91a0d945717430ec20f9251af4bd2"
uuid = "916415d5-f1e6-5110-898d-aaa5f9f070e0"
version = "0.24.1"

[[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 = "c2a145a145dc03a7620af1444e0264ef907bd44f"
uuid = "9b13fd28-a010-5f03-acff-a1bbcff69959"
version = "0.5.1"

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

[[LogExpFunctions]]
deps = ["ChainRulesCore", "DocStringExtensions", "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"

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

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

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

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

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

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

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

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

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

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

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

[[Netpbm]]
deps = ["ColorVectorSpace", "FileIO", "ImageCore"]
git-tree-sha1 = "09589171688f0039f13ebe0fdcc7288f50228b52"
uuid = "f09324ee-3d7c-5217-9330-fc30815ba969"
version = "1.0.1"

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

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

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

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

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

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

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

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

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

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

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

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

[[Parameters]]
deps = ["OrderedCollections", "UnPack"]
git-tree-sha1 = "34c0e9ad262e5f7fc75b10a9952ca7692cfc5fbe"
uuid = "d96e819e-fc66-5662-9728-84c9c7592b0a"
version = "0.12.3"

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

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

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

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

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

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

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

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

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

[[RangeArrays]]
git-tree-sha1 = "b9039e93773ddcfc828f12aadf7115b4b4d225f5"
uuid = "b3c3ace0-ae52-54e7-9d0b-2c1406fd6b9d"
version = "0.3.2"

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

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

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

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

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

[[Rotations]]
deps = ["LinearAlgebra", "StaticArrays", "Statistics"]
git-tree-sha1 = "2ed8d8a16d703f900168822d83699b8c3c1a5cd8"
uuid = "6038ab10-8711-5258-84ad-4b1120ba62dc"
version = "1.0.2"

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

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

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

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

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

[[SimpleTraits]]
deps = ["InteractiveUtils", "MacroTools"]
git-tree-sha1 = "5d7e3f4e11935503d3ecaf7186eac40602e7d231"
uuid = "699a6c99-e7fa-54fc-8d76-47d257e15c1d"
version = "0.9.4"

[[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", "OpenLibm_jll", "OpenSpecFun_jll"]
git-tree-sha1 = "ad42c30a6204c74d264692e633133dcea0e8b14e"
uuid = "276daf66-3868-5448-9aa4-cd146d93841b"
version = "1.6.2"

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

[[StringDistances]]
deps = ["Distances"]
git-tree-sha1 = "a4c05337dfe6c4963253939d2acbdfa5946e8e31"
uuid = "88034a9c-02f8-509d-84a9-84ec65e18404"
version = "0.10.0"

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

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

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

[[Tables]]
deps = ["DataAPI", "DataValueInterfaces", "IteratorInterfaceExtensions", "LinearAlgebra", "TableTraits", "Test"]
git-tree-sha1 = "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"

[[TestImages]]
deps = ["AxisArrays", "ColorTypes", "FileIO", "OffsetArrays", "Pkg", "StringDistances"]
git-tree-sha1 = "db28237376a6b7ae9c9fe05880ece0ab8bb90b75"
uuid = "5e47fb64-e119-507b-a336-dd2b206d9990"
version = "1.6.1"

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

[[TiledIteration]]
deps = ["OffsetArrays"]
git-tree-sha1 = "5683455224ba92ef59db72d10690690f4a8dc297"
uuid = "06e1c1a7-607b-532d-9fad-de7d9aa2abac"
version = "0.3.1"

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

[[UnPack]]
git-tree-sha1 = "387c1f73762231e86e0c9c5443ce3b4a0a9a0c2b"
uuid = "3a884ed6-31ef-47d7-9d2a-63182c4928ed"
version = "1.0.2"

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

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

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

[[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_libXcursor_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXfixes_jll", "Xorg_libXrender_jll"]
git-tree-sha1 = "12e0eb3bc634fa2080c1c37fccf56f7c22989afd"
uuid = "935fb764-8cf2-53bf-bb30-45bb1f8bf724"
version = "1.2.0+4"

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

# ╔═╡ Cell order:
# ╟─09fc6764-2050-4276-8a14-3596368d0c32
# ╟─48412713-7266-43dc-a49e-bab349318cce
# ╟─0d142947-1e3c-4adc-b6ca-09c502f89f0e
# ╟─e50c3024-140d-4312-ad8e-66b94482964b
# ╟─f90425a3-8e6f-440a-8522-5ccd5cca2797
# ╟─740ecf61-15d7-4b30-8ef8-e9ed339912af
# ╟─899a3541-9cef-48de-a03d-367223fe3100
# ╟─2b98ae25-097e-4dd5-aa4b-01a3b06c2fc8
# ╟─99028838-babb-4d29-a400-c321c65e455d
# ╟─85859f7b-26e2-415e-87f1-b61180449a61
# ╟─12fb095f-02a6-48c6-856a-afbc71a6ad64
# ╟─f66e452c-97db-11eb-23fc-bd95da10e2d6
# ╟─4369ef1b-4291-4b2f-8bc1-ac011080bb18
# ╟─c4d7b864-275a-4e6f-9cc1-74138a1f07ae
# ╟─adaa78fa-d805-45e6-ba5f-d91a5d4823d6
# ╟─6c43a6ef-41af-4343-910b-04e72fdc87fd
# ╟─628877b2-6e8d-47a1-8dce-752b9d75ab56
# ╟─00000000-0000-0000-0000-000000000001
# ╟─00000000-0000-0000-0000-000000000002


"""
    The function that will modulate tha activation value of
    each non-input neuron in the network. It maps the z vale (∑wx + b)
    of the neurons to ℝ. By default its taken the sigmoid function
    one(z)/(one(z) + exp(-z)). To change it, just redefine it!!!
    But remember change the act_function_prime function too!!!!!!
"""
function act_function(z::T)::T where T<:Real
    return sigmoid(z);
end

"""
    The first derivative of the act_function.
"""
function act_function_prime(z::T) where T<:Real
    return sigmoid_prime(z);
end

function sigmoid(z::T)::T where T<:Real
    return one(z)/(one(z) + exp(-z));
end
function sigmoid_prime(z::T) where T<:Real
    return exp(-z)/((one(z) + exp(-z))^2);
end

ReLU_ng = 0.1;

function ReLU(z::T)::T where T<:Real
    return max(ReLU_ng * z, z);
end

function ReLU_prime(z::T)::T where T<:Real
    if z < 0
        return ReLU_ng;
    else
        return one(z);
    end
end


# See https://rdrr.io/rforge/copula/man/Sibuya.html

struct Sibuya{T<:Real} <: Distributions.DiscreteUnivariateDistribution
    p::T
    function Sibuya(p::T) where {T <: Real}
        new{T}(p)
    end
end
function Distributions.rand(rng::Distributions.AbstractRNG, d::Sibuya{T}) where {T <: Real}
    u = rand(rng, T)
    if u <= d.p
        return T(1)
    end
    xMax = 1/eps(T)
    Ginv = ((1-u)*SpecialFunctions.gamma(1-d.p))^(-1/d.p)
    fGinv = floor(Ginv)
    if Ginv > xMax
        return fGinv
    end
    if 1-u < 1/(fGinv*SpecialFunctions.beta(fGinv,1-d.p))
        return ceil(Ginv)
    end
    return fGinv
end


"""
condentropy \\

for the condEntropy function specifically, calculating conditional entropy \\
Arguments:	marg_dist, marginal distributions \\
						cond_dists, conditional distributions \\
						base, the base for entropy calculations \\
Returns:		Float64, the conditional entropy \\
"""

function condentropy(marg_dist::Array{Float64,1}, cond_dists::Array{Float64}; base::Int=2)
	length(marg_dist) == size(cond_dists)[2] || error("Number of marginal and conditional distributions does not match!")
	ents = [entropy(cond_dists[:, i], base) for i = 1:size(cond_dists)[2]]
	return sum(marg_dist .* ents)
end


using DifferentialEquations
using DiffEqSensitivity
using Random
using Distributions
using Turing
using DataFrames
using StatsPlots

function sir_ode!(du,u,p,t)
    (S,I,R,C) = u
    (β,c,γ) = p
    N = S+I+R
    infection = β*c*I/N*S
    recovery = γ*I
    @inbounds begin
        du[1] = -infection
        du[2] = infection - recovery
        du[3] = recovery
        du[4] = infection
    end
    nothing
end;

tmax = 40.0
tspan = (0.0,tmax)
obstimes = 1.0:1.0:tmax
u0 = [990.0,10.0,0.0,0.0] # S,I.R,C
p = [0.05,10.0,0.25]; # β,c,γ

prob_ode = ODEProblem(sir_ode!,u0,tspan,p);

sol_ode = solve(prob_ode,
            Tsit5(),
            saveat = 1.0);

C = Array(sol_ode)[4,:] # Cumulative cases
X = C[2:end] - C[1:(end-1)];

Random.seed!(1234)
Y = rand.(Poisson.(X));

bar(obstimes,Y,legend=false)
plot!(obstimes,X,legend=false)

@model bayes_sir(y) = begin
  # Calculate number of timepoints
  l = length(y)
  i₀  ~ Uniform(0.0,1.0)
  β ~ Uniform(0.0,1.0)
  I = i₀*1000.0
  u0=[1000.0-I,I,0.0,0.0]
  p=[β,10.0,0.25]
  tspan = (0.0,float(l))
  prob = ODEProblem(sir_ode!,
          u0,
          tspan,
          p)
  sol = solve(prob,
              Tsit5(),
              saveat = 1.0)
  sol_C = Array(sol)[4,:] # Cumulative cases
  sol_X = sol_C[2:end] - sol_C[1:(end-1)]
  l = length(y)
  for i in 1:l
    y[i] ~ Poisson(sol_X[i])
  end
end;

ode_nuts = sample(bayes_sir(Y),NUTS(0.65),10000);

describe(ode_nuts)

plot(ode_nuts)

posterior = DataFrame(ode_nuts);

histogram2d(posterior[!,:β],posterior[!,:i₀],
                bins=80,
                xlabel="β",
                ylab="i₀",
                ylim=[0.006,0.016],
                xlim=[0.045,0.055],
                legend=false)
plot!([0.05,0.05],[0.0,0.01])
plot!([0.0,0.05],[0.01,0.01])

function predict(y,chain)
    # Length of data
    l = length(y)
    # Length of chain
    m = length(chain)
    # Choose random
    idx = sample(1:m)
    i₀ = chain[:i₀][idx]
    β = chain[:β][idx]
    I = i₀*1000.0
    u0=[1000.0-I,I,0.0,0.0]
    p=[β,10.0,0.25]
    tspan = (0.0,float(l))
    prob = ODEProblem(sir_ode!,
            u0,
            tspan,
            p)
    sol = solve(prob,
                Tsit5(),
                saveat = 1.0)
    out = Array(sol)
    sol_X = [0.0; out[4,2:end] - out[4,1:(end-1)]]
    hcat(sol_ode.t,out',sol_X)
end;

Xp = []
for i in 1:10
    pred = predict(Y,ode_nuts)
    push!(Xp,pred[2:end,6])
end

scatter(obstimes,Y,legend=false)
plot!(obstimes,Xp,legend=false)


"""
    value_at_risk(returns, confidence, method; multiplier=1.0)

Computes the Value-at-Risk (VaR) for a given significance level `α` based on the chosen estimation method. The VaR value represents the maximum expected loss at a certain significance level `α`. For a more tail-risk focused measure, see `expected_shortfall`.

# Arguments
- `returns`:     Vector of asset returns.
- `α`:           Significance level, e.g. use `0.05` for 95% confidence, or `0.01` for 99% confidence.
- `method`:      Distribution estimation method: `:historical`, `:gaussian` or `:cornish_fisher`.
- `multiplier`:  Optional scalar multiplier, i.e. use `12` to annualize monthly returns, and use `252` to annualize daily returns.

# Methods
- `:historical`:        Historical based on empirical distribution of returns.
- `:gaussian`:          Gaussian distribution based on parametric fit (mean, variance).
- `:cornish_fisher`:    Cornish-Fisher based on Gaussian parametric distribution fit adjusted for third and fourth moments (skewness, kurtosis). Cornish-Fisher expansion aims to approximate the quantile of a true distribution by using higher moments (skewness and kurtosis) of that distribution to adjust for its non-normality. See https://thema.u-cergy.fr/IMG/pdf/2017-21.pdf for details.

# Sources
- Favre, Laurent and Galeano, Jose-Antonio (2002). Mean-Modified Value-at-Risk Optimization with Hedge Funds. Journal of Alternative Investment.
- Amédée-Manesme, Charles-Olivier and Barthélémy, Fabrice and Maillard, Didier (2017). Computation of the Corrected Cornish–Fisher Expansion using the Response Surface Methodology: Application to VaR and CVaR. THEMA Working Paper n°2017-21, Université de Cergy-Pontoise, France.
"""
function value_at_risk(returns, α, method::Symbol; multiplier=1.0)
    if method == :historical
        # empirical quantile for VaR estimation
        return quantile(returns, α) * sqrt(multiplier)
    elseif method == :gaussian
        # parametric Gaussian distribution fit
        μ = mean(returns)
        σ = std(returns; corrected=false)
        return quantile(Normal(μ, σ), α)
    elseif method == :cornish_fisher
        # third/fourth moment adjusted Gaussian distribution fit
        # http://www.diva-portal.org/smash/get/diva2:442078/FULLTEXT01.pdf
        # https://papers.ssrn.com/sol3/papers.cfm?abstract_id=1024151
        q = quantile(Normal(), α)
        S = skewness(returns)
        K = kurtosis(returns; method=:excess)
        z = q + 1/6*(q^2-1)S + 1/24*(q^3-3q)*K - 1/36*(2q^3-5q)*S^2
        μ = mean(returns)
        σ = std(returns; corrected=false)
        return (μ + z*σ) * sqrt(multiplier)
    end

    throw(ArgumentError("Passed method parameter '$(method)' is invalid, must be one of :historical, :gaussian, :cornish_fisher."))
end


"""
   DiscretizeUniformWidth(alg::Symbol)
If `nbins` is a symbol, automatically determine the number of bins to use
"""
struct DiscretizeUniformWidth <: DiscretizationAlgorithm
    nbins::Union{Int,Symbol}
end

function get_nbins(alg::Symbol, data::AbstractArray{N}) where {N<:Real}

    n = length(data)

    if alg == :sqrt
        # Square root (of data size) estimator, used by Excel and other programs for its speed and simplicity.
        nbins = ceil(Int, sqrt(n))
    elseif alg == :sturges
        # R’s default method, only accounts for data size.
        # Only optimal for gaussian data and underestimates number of bins for large non-gaussian datasets.
        # It implicitly bases the bin sizes on the range of the data and can perform poorly if n < 30,
        # because the number of bins will be small—less than seven—and unlikely to show trends in the data well.
        # It may also perform poorly if the data are not normally distributed.
        nbins = ceil(Int, log(2,n)) + 1
    elseif alg == :rice
        # Estimator does not take variability into account, only data size. Commonly overestimates number of bins required.
        nbins = ceil(Int, 2cbrt(n))
    elseif alg == :doane
        # An improved version of Sturges’ estimator that works better with non-normal datasets.
        g₁ = moment(data, 3)
        σ = sqrt((6*(n-2))/((n+1)*(n+3)))
        nbins = ceil(Int, 1 + log(2,n) + log(2, 1+abs(g₁)/σ))
    elseif alg == :scott
        # Less robust estimator that that takes into account data variability and data size.
        σ = std(data)
        binwidth = 3.5σ/cbrt(n)
        lo, hi = extrema(data)
        nbins = ceil(Int, (hi - lo)/binwidth)
    elseif alg == :fd # Freedman Diaconis Estimator
        # Robust (resilient to outliers) estimator that takes into account data variability and data size
        binwidth = 2iqr(data)/cbrt(n)
        lo, hi = extrema(data)
        nbins = ceil(Int, (hi - lo)/binwidth)
    else # alg == :auto
        # Maximum of the ‘sturges’ and ‘fd’ estimators. Provides good all round performance

        binwidth = 2iqr(data)/cbrt(n)
        lo, hi = extrema(data)
        nbins_fd = ceil(Int, (hi - lo)/binwidth)
        nbins_sturges = ceil(Int, log(2,n)) + 1
        nbins = max(nbins_fd, nbins_sturges)
    end

    nbins
end

function binedges(alg::DiscretizeUniformWidth, data::AbstractArray{N}) where {N<:AbstractFloat}
    lo, hi = extrema(data)
    @assert(hi > lo)

    nbins = (isa(alg.nbins, Symbol) ? get_nbins(alg.nbins, data) : alg.nbins)::Int

    convert(Vector{N}, collect(range(lo, stop=hi, length=nbins+1)))
end
function binedges(alg::DiscretizeUniformWidth, data::AbstractArray{N}) where {N<:Integer}
    lo, hi = extrema(data)
    @assert(hi > lo)
    collect(range(lo, stop=hi, length=alg.nbins+1))
end


import Base.:+
import Base.:*

export Biquaternion
export getrotation
export gettranslation

"""
    Represents a bi-quaternion for describing rotation and translation in a unified way.

fields: real and dual.
"""
struct Biquaternion
    real::Quaternion
    dual::Quaternion
    Biquaternion(qr::Quaternion, qd::Quaternion) = new(qr, qd)
    Biquaternion(q::Quaternion, t::ℝ³) = begin
        qr = normalize(q)
        qd = 0.5 * Quaternion([0; vec(t)]) * qr
        new(qr, qd)
    end
end

"""
    show(b)

Print a string representation of the given Biquaternion `b`.
"""
Base.show(io::IO, q::Biquaternion) = print(io, "$(q.real) + $(q.dual)𝜺")

"""
    Biquaternion(q)

Construct a Biquaternion with the given Biquaternion `q` and also normalize.
"""
Biquaternion(q::Biquaternion) = begin
    q̂ = normalize(q)
    Biquaternion(q̂.real, q̂.dual)
end

"""
    Biquaternion(rotation)

Construct a Biquaternion with the given Quaternion `rotation` and also normalize.
"""
Biquaternion(rotation::Quaternion) = Biquaternion(normalize(rotation), ℝ³(0, 0, 0))

"""
    Biquaternion(translation)

Construct a Biquaternion with the given ℝ³ `translation`.
"""
Biquaternion(translation::ℝ³) = Biquaternion(Quaternion(1, 0, 0, 0), translation)

vec(q::Biquaternion) = [vec(q.real); vec(q.dual)]

+(q₁::Biquaternion, q₂::Biquaternion) = Biquaternion(q₁.real + q₂.real, q₁.dual + q₂.dual)
-(q₁::Biquaternion, q₂::Biquaternion) = Biquaternion(q₁.real - q₂.real, q₁.dual - q₂.dual)
*(q₁::Biquaternion, q₂::Biquaternion) = Biquaternion(q₁.real * q₂.real,
                                                     q₁.real * q₂.dual + q₁.dual * q₂.real)
*(q::Biquaternion, λ::Real) = Biquaternion(λ * q.real, λ * q.dual)
*(λ::Real, q::Biquaternion) = q * λ
Base.conj(q::Biquaternion) = Biquaternion(conj(q.real), conj(q.dual))
norm(q::Biquaternion) = begin
    qrnorm = norm(q.real)
    Q = (conj(q.real) * q.dual + q.real * conj(q.dual)) * (1 / 2qrnorm)
    scalar = vec(Q)[1]
    sqrt(qrnorm^2 + scalar^2)
end
normalize(q::Biquaternion) = begin
    magnitude = norm(q)
    @assert(magnitude > 1e-5, "The magnitude is almost equal to zero, too small.")
    q * (1 / magnitude)
end
getrotation(q::Biquaternion) = q.real
gettranslation(q::Biquaternion) = ℝ³(vec(2q.dual * conj(q.real))[2:4])

Base.isapprox(q1::Biquaternion, q2::Biquaternion) = isapprox(q1.real, q2.real) &&
                                                    isapprox(q1.dual, q2.dual)


function derivative(ex::Constant, wrt::Variable)
    return Constant( default_variable_value() )
end

function derivative(ex::Variable, wrt::Variable)
    T   = typeof(wrt.value)
    val = default_variable_value()
    if ex == wrt
        val = one(T)
    end
    return Constant(val)
end

function derivative(ex::SymbolicExpression, wrt::Variable)

    if is_unary_operator(ex.head)
        der_ex = __derivative_unary(ex, wrt)
    elseif is_binary_operator(ex.head)
        der_ex = __derivative_binary(ex, wrt)
    else
        error("not implemented yet")
        # er_ex = __derivative_k_ary(ex, wrt)
    end

    return der_ex
end

function __derivative_unary(ex::SymbolicExpression, wrt::Variable)

    # dy/dz = sin(x)
    # dy/dz = cos(x) * dx/dz
    #
    # derivative of the arguments
    # derivative of the function itself

    arg = ex.args[1]
    der_arg = derivative(arg, wrt)

    sym = ex.head

    if is_sin_operator(sym)
        der_ex = der_arg * cos(arg)

    elseif is_cos_operator(sym)
        der_ex = -der_arg * sin(arg)

    elseif is_tan_operator(sym)
        der_ex = der_arg / (cos(arg) * cos(args))

    elseif is_sqrt_operator(sym)
        der_ex = der_arg / sqrt(arg)

    elseif is_cbrt_operator(sym)
        der_ex = der_arg * 1/3 * arg^(-2/3)

    elseif is_exp_operator(sym)
        der_ex = der_arg * exp(arg)

    elseif is_log_operator(sym)
        der_ex = der_arg / arg

    elseif is_log2_operator(sym)
        der_ex = der_arg / (arg * log(2))

    elseif is_log10_operator(sym)
        der_ex = der_arg / (arg * log(10))

    elseif is_abs_operator(sym)
        der_ex = der_arg * sign(arg)

    elseif is_sign_operator(sym)
        der_ex = Constant(0.0)

    else
        error("not implemented yet")
    end

    return der_ex
end

function __derivative_binary(ex::SymbolicExpression, wrt::Variable)

    sym = ex.head

    if is_plus_operator(sym) || is_product_operator(sym)
        # der_ex = der_left_arg + der_right_arg
        # der_ex = der_left_arg * right_arg + left_arg * der_right_arg

        df(expr) = derivative(expr, wrt)
        der_args = map(df, ex.args)

        if is_plus_operator(sym)
            return reduce(+, der_args)
        end

        L = length(ex.args)
        if L == 2
            left_arg      = ex.args[1]
            right_arg     = ex.args[2]
            der_left_arg  = der_args[1]
            der_right_arg = der_args[2]

            return der_left_arg * right_arg + left_arg * der_right_arg
        else
            der_ex = der_args[1] * reduce(*, ex.args[2:end])
            for ii = 2:L
                # left   = reduce(*, ex.args[1:ii-1])
                # right  = reduce(*, ex.args[ii+1:end])
                args      = [ex.args[1:ii-1]; der_args[ii]; ex.args[ii+1:end]]
                left_expr = SymbolicExpression(:*, args...)
                der_ex    = der_ex + left_expr # left * der_args[ii] * right
            end
            return der_ex
        end
    end

    L = length(ex.args)
    if is_minus_operator(sym) && L == 1
        der_right_arg = derivative(ex.args[1], wrt)
        der_ex = -der_right_arg
        return der_ex
    end

    left_arg  = ex.args[1]
    right_arg = ex.args[2]

    der_left_arg  = derivative(left_arg, wrt)
    der_right_arg = derivative(right_arg, wrt)

    if is_minus_operator(sym)
        der_ex = der_left_arg - der_right_arg

    elseif is_divide_operator(sym)
        der_ex = (der_left_arg * right_arg - left_arg * der_right_arg) / (right_arg * right_arg)

    elseif is_power_operator(sym)
        # most complicated expression
        der_ex = ex * (der_left_arg * right_arg / left_arg + der_right_arg * log(left_arg))

    else
        error("not implemented yet")
    end

    return der_ex
end

function gradient(
    ex::S,
    wrt_vec::A) where {
        S <: Union{Constant, Variable, SymbolicExpression},
        A <: AbstractArray}

    L = length(wrt_vec)
    g = Vector{Any}(undef, L)

    for ii = 1:L
        g[ii] = symbolics.derivative(ex, wrt_vec[ii])
    end

    return g
end

function jacobian(
    ex_vec::S,
    wrt_vec::A) where {
        S <: AbstractArray,
        A <: AbstractArray}

    n_row = length(ex_vec)
    n_col = length(wrt_vec)
    jac   = Matrix{Any}(undef, n_row, n_col)

    for ii = 1:n_row
        ex = ex_vec[ii]
        for jj = 1:n_col
            jac[ii, jj] = symbolics.derivative(ex, wrt_vec[jj])
        end
    end

    return jac
end


module NewtonsMethod

greet() = print("Hello World!")

using LinearAlgebra, ForwardDiff

function fixedpointmap(f, f_prime, x_0; tolerance=1E-12, maxiter=1000)
    # setup the algorithm
#     x_old = iv
    normdiff = Inf
    iter = 1
    if f(x_0) == 0
        return (value = x_0, normdiff=0, iter=0)
    end
    while normdiff > tolerance && iter <= maxiter
        x_new = x_0 - f(x_0)/f_prime(x_0) # use the passed in map
        if norm(x_new - x_0) > tolerance && iter > 100
            println("non-convergence ")
            return #(value = nothing, normdiff=nothing, iter=iter)
        end
        normdiff = norm(x_new - x_0)
        x_0 = x_new
        iter = iter + 1
    end
    return (value = x_0, normdiff=normdiff, iter=iter) # A named tuple
end

D(f) = x -> ForwardDiff.derivative(f, x)
function fixedpointmap(f, x_0; tolerance=1E-12, maxiter=1000)
    f_prime = D(f)
    if nothing == fixedpointmap(f, f_prime, x_0)
        return
    end
    x_0, normdiff, iter = fixedpointmap(f, f_prime, x_0, tolerance=tolerance, maxiter=maxiter)
    return (value = x_0, normdiff=normdiff, iter=iter)
end

export fixedpointmap

end # module


# Multicollinearity Test for regression models:
# see associated pdf and data file
# overview of notes:
# if doing regression, the coefficient of each predictor (indepedent) variable tells you how much on average the response changes
# when the variable changes by a unit. however, when variables are corellated (they are not really independent) it makes inference harder

using RCall
using DataFrames
using CSV
using GLM
using Statistics

# read the first 4 columns (not the columns with S)
df = CSV.read("multicollinearity_regression_models_data.csv" , header=1, select=(i, name) -> i < 5 && return true) |> DataFrame#, select=(i, name) -> i < 5 && return true)
insertcols!(df, :pfat_weight => df.pfat .* df.weight) # create interaction term manually

rmodel = lm(@formula(neck ~ pfat + weight + activity + pfat_weight), df)
# lets calculate the Variance Inflation numbers
# using r2 of the model, calculate VIF

rmodel1 = lm(@formula(pfat ~  weight + activity + pfat_weight), df)
rmodel2 = lm(@formula(weight ~ pfat + activity + pfat_weight), df)
rmodel3 = lm(@formula(activity ~ pfat + weight + pfat_weight), df)
rmodel4 = lm(@formula(pfat_weight ~ pfat + weight + activity), df)
rmodels = (rmodel1, rmodel2, rmodel3, rmodel4)
vifs = @. 1 / (1 - r2(rmodels))

# node that for activity, VIF is 1.05 so this variable has no mulitcollinearity.
# for the other ones, there seems to be definitely multicollinearity
# first, let's remove the structural multicollinearity from the interaction term.
# to do tht, we will first mean center the data.

# there is a bug in Query that messes up the dataframe types and GLM can't read it anymore
# https://discourse.julialang.org/t/query-jl-mutate-command-does-not-preserve-column-types/41231/4
# aa = mean(df.pfat)::Float64
# ab = mean(df.weight)::Float64
# ac = mean(df.activity)::Float64
# ad = mean(df.pfat_weight)::Float64
# df_s = df |> @mutate(pfat_s .= _.pfat .- aa,
#               weight_s .= _.weight .- ab,
#               activity_s .= _.activity .- ac,
#               pfat_weight_s .= _.pfat_weight .- ad) |> DataFrame

# equivalent linq
# @from i in df begin
# @select {i..., pfat_s = mean(df.pfat)}
# @collect DataFrame
# end

# lets use standard method to add columns
df[!, :neck_s] .= mean(df.neck) .- df.neck
df[!, :pfat_s] .= mean(df.pfat) .- df.pfat
df[!, :weight_s] .= mean(df.weight) .- df.weight
df[!, :activity_s] .= mean(df.activity) .- df.activity
df[!, :pfat_weight_s] .=  df.pfat_s .* df.weight_s

smodel = lm(@formula(neck ~ pfat_s + weight_s + activity_s + pfat_weight_s), df)
# lets calculate the Variance Inflation numbers
# using r2 of the model, calculate VIF
smodel1 = lm(@formula(pfat_s ~  weight_s + activity_s + pfat_weight_s), df)
smodel2 = lm(@formula(weight_s ~ pfat_s + activity_s + pfat_weight_s), df)
smodel3 = lm(@formula(activity_s ~ pfat_s + weight_s + pfat_weight_s), df)
smodel4 = lm(@formula(pfat_weight_s ~ pfat_s + weight_s + activity_s), df)
smodels = (smodel1, smodel2, smodel3, smodel4)
vifs = @. 1 / (1 - r2(smodels))
# (3.323870441091722, 4.745648167219831, 1.0530047498114123, 1.9910631706191224)

# you see now how small the VIFs have gotten just by removing the multicollinearity


"A `DomainPoint` is a point which is an element of a domain by construction."
abstract type DomainPoint{T} end

in(p::DomainPoint, d::Domain) = domain(p) == d || in(point(p), d)

## Points on a sphere

"A point on the unit sphere."
abstract type SpherePoint{T} <: DomainPoint{T} end

domain(p::SpherePoint{T}) where {T<:StaticTypes} = UnitSphere{T}()
domain(p::SpherePoint{T}) where {T<:AbstractVector} = UnitSphere{T}(length(point(p)))

"A point on the unit sphere represented by a standard Euclidean vector."
struct EuclideanSpherePoint{T} <: SpherePoint{T}
    x   ::  T
end
point(p::EuclideanSpherePoint) = p.x

"A point on the unit sphere represented in spherical coordinates."
struct SphericalCoordinate{T} <: SpherePoint{SVector{3,T}}
    θ   ::  T   # inclination or polar angle
    ϕ   ::  T   # azimuthal angle
end

point(p::SphericalCoordinate) = SVector(sin(p.θ)*cos(p.ϕ), sin(p.θ)*sin(p.ϕ), cos(p.θ))


# ## Data

import CSV

using DataFrames
using TuringModels: project_root

path = joinpath(project_root, "data", "Kline.csv")
df = CSV.read(path, DataFrame; delim=';')
df.log_pop = log.(df.population)
df.society = 1:nrow(df)
df

# ## Model

using Turing

@model function m12_6(total_tools, log_pop, society)
    N = length(total_tools)

    α ~ Normal(0, 10)
    βp ~ Normal(0, 1)

    σ_society ~ truncated(Cauchy(0, 1), 0, Inf)

    N_society = length(unique(society)) ## 10

    α_society ~ filldist(Normal(0, σ_society), N_society)

    for i in 1:N
        λ = exp(α + α_society[society[i]] + βp*log_pop[i])
        total_tools[i] ~ Poisson(λ)
    end
end;

# ## Output

chains = sample(
    m12_6(df.total_tools, df.log_pop, df.society),
    NUTS(0.95),
    1000
)

# \defaultoutput{}

# ## Original output

m12_6rethinking = "
              Mean StdDev lower 0.89 upper 0.89 n_eff Rhat
a              1.11   0.75      -0.05       2.24  1256    1
bp             0.26   0.08       0.13       0.38  1276    1
a_society[1]  -0.20   0.24      -0.57       0.16  2389    1
a_society[2]   0.04   0.21      -0.29       0.38  2220    1
a_society[3]  -0.05   0.19      -0.36       0.25  3018    1
a_society[4]   0.32   0.18       0.01       0.60  2153    1
a_society[5]   0.04   0.18      -0.22       0.33  3196    1
a_society[6]  -0.32   0.21      -0.62       0.02  2574    1
a_society[7]   0.14   0.17      -0.13       0.40  2751    1
a_society[8]  -0.18   0.19      -0.46       0.12  2952    1
a_society[9]   0.27   0.17      -0.02       0.52  2540    1
a_society[10] -0.10   0.30      -0.52       0.37  1433    1
sigma_society  0.31   0.13       0.11       0.47  1345    1
";


#!/usr/bin/env julia

#=
# data should be sorted and works by divide and conquer
# improved variant of binary search for equally distributed data
# it tries probing the position
#
# Interpolation search finds particular item by computing probe position.
# Start from middle of list. If not a match, divide list using probe position and find new middle.
# Do binary search in 2 blocks.
# Repeat
#
# run-time complexity is O(log log n)
=#

function interpolation_search(search_for, data_array, low=1, high=-1)
  if high == -1
    high = length(data_array)
  end
  if high < low
    return -1
  end

  ## calculation of probe position
  mid = Int(floor(
          (
           low +
           ((high - low) / (data_array[high] - data_array[low])) *
           (search_for - data_array[low])
          )
        ))
  if mid < low
    return -1
  end

  if data_array[mid] == search_for
    return mid
  end

  if data_array[mid] < search_for
    return interpolation_search(search_for, data_array, mid+1, high)
  else
    return interpolation_search(search_for, data_array, low, mid-1)
  end

  -1
end

     ## 1   2   3   4   5   6   7   8   9   10
data = [10, 11, 12, 22, 24, 27, 34, 47, 56, 67]

@time println("at index: ", interpolation_search(47, data))
@time println("at index: ", interpolation_search(12, data))
@time println("at index: ", interpolation_search(7, data))
@time println("at index: ", interpolation_search(67, data))
@time println("at index: ", interpolation_search(34, data))


#
#--------------------------------------#
export gordonHall
#--------------------------------------#
"""
 Transfinite interpolation
"""
function gordonHall(xrm,xrp,xsm,xsp,yrm,yrp,ysm,ysp,zr,zs)

ze  = [-1,1]
Jer = interpMat(zr,ze)
Jes = interpMat(zs,ze)

xv = [xrm[1]   xrp[1]
      xrm[end] xrp[end]]

yv = [yrm[1]   yrp[1]
      yrm[end] yrp[end]]

xv = ABu(Jes,Jer,xv)
yv = ABu(Jes,Jer,yv)

#display(mesh(xv,yv,0*xv,0,90))

x = ABu([],Jer,vcat(xrm',xrp')) .+ ABu(Jes,[],hcat(xsm,xsp)) .- xv
y = ABu([],Jer,vcat(yrm',yrp')) .+ ABu(Jes,[],hcat(ysm,ysp)) .- yv

#display(mesh(x,y,0*x,0,90))

return x,y
end
#--------------------------------------#
export annulus
#--------------------------------------#
"""
 Convert (r,s) in [-1,1]^2 grid
 internal/external radii r0,r1,
 and angular extent span
"""
function annulus(r,s;r0=0.5,r1=1.0,span=2pi)

R  = @. (r1-r0)/2*(r+1) + r0
th = @. span   /2*(s+1) + 0.

x = @. R * cos(th)
y = @. R * sin(th)

return x,y
end
#--------------------------------------#
#"""
# Transforms [-1,1]^2 to annulus with
# internal/external radii r0,r1,
# and angular extent span
#"""
#function annulus(r0,r1,span,zr,zs)
#
#ze  = [-1.,1.]
#Jer = interpMat(zr,ze);
#Jes = interpMat(zs,ze);
#
#xrp = 0*zs;
#yrp = Jes*[-r0,-r1];
#
#spn = span-pi/2;
#
#xrm = Jes*[r0;r1]*cos(spn);
#yrm = Jes*[r0;r1]*sin(spn);
#
#as = Jer*[-pi/2,spn];
#xsm = r0 .* cos.(as);
#ysm = r0 .* sin.(as);
#
#xsp = r1 .* cos.(as);
#ysp = r1 .* sin.(as);
#
#pl=plot(xsm,ysm ,label="sm");
#pl=plot!(xsp,ysp,label="sp");
#pl=plot!(xrm,yrm,label="rm");
#pl=plot!(xrp,yrp,label="rp");
#pl=xlabel!("x")
#pl=ylabel!("y")
#display(pl)
#
#return gordonHall(xrm,xrp,xsm,xsp,yrm,yrp,ysm,ysp,zr,zs)
#end
#--------------------------------------#


using ODE

# Definition of parameters
const a = 1.0
const b = 0.1
const c = 1.5
const d = 0.75

function diffeq(t,u)
    du = similar(u)
    # u1: rabbits
    # u2: foxes
    du[1] = a*u[1] - b*u[1]*u[2]
    du[2] = -c*u[2] + d*b*u[1]*u[2]

    return du

end

# Simulation time
const t = collect(range(0.0,stop=18.0,length=1000))

# Initial condition
u0 = zeros(2)
u0[1] = 10
u0[2] = 5

# Solve ODE
(sol_t,sol_u) = ode45(diffeq,u0,tpoints=:specified)

#= Visualization of Results

using PyPlot
plot(sol_t,sol_u)
show()

=#


### Notation
#   small letters => vectors
# capital letters => matrices

function build_A(k, u, η, D, D2, ϕ, params)

    a = params.a
    N = params.Nϕ
    g = params.g

    k2 = k^2
    ik = 1im*k

    h = η .+ params.H
    axcos = a * cos.(ϕ)

           D = D/a
       IxCos = diagm(0=>axcos)
       IoCos = diagm(0=>1 ./ axcos)
    TwoΩxSin = diagm(0=>params.TwoΩ*sin.(ϕ));
       UoCos = diagm(0=>u./axcos)
       UxTan = diagm(0=>u./axcos.*sin.(ϕ))
       HoCos = diagm(0=>h./axcos)
       HxCos = diagm(0=>h.*axcos)
          dU = diagm(0=>D*u);
          dH = diagm(0=>D*h);

    # Form 1L-RSW Matrix
    #   [u1, v1, h1]

     U = diagm(0 => u)
     H = diagm(0 => h)
     F = diagm(0 => params.TwoΩ .+ 0*ϕ)
    dU = diagm(0 => D*u)

    A = [                UoCos             (-TwoΩxSin - UxTan + dU)[:, 2:N]    g*IoCos;
          -(TwoΩxSin + 2*UxTan)[2:N,:]/k2                     UoCos[2:N,2:N]  -g/k2*D[2:N,:];
                         HoCos                  (IxCos * D * HxCos)[:, 2:N]     UoCos];
    return A
end


123.4 + 234.5     # This is a comment, ignored by Julia

7 / 3             # Integer division returns floating point

7 ÷ 3             # Truncated integer division (type \div and press tab)

7 % 3             # Remainder

2^7 - 4           # All integer expression which returns an integer

3 * (4.2 - 9.7)   # Use parentheses to control the order of operations

# We will later learn why the previous result is not exact

5.6e7 ^ 0.1       # Scientific notation, means 5.6⋅10⁷ (not e⁷)

1 + 2
34 / 3

println(1 + 2.1)
println("The result is ", -123 / 1000000)
34 / 3; # The output will not be displayed

typeof(1.23)

typeof(-745)

typeof(1e20)

println(typemin(Int64))
println(typemax(Int64))
println(typemin(Int128))
println(typemax(Int128))

1000^7

Int128(1000)^7


"""
```
shapeFunction(::LinearQuadrangle3D, x, y, z)
```
Computes the linear shape functions `N[1:8]` for an `(x, y, z)` point on a 3D linear quadrangle.
"""
function shapeFunction(::LinearQuadrangle3D, x, y, z)
    # N[n](x,y,z) := shape function n.
    omx = 1 - x
    omy = 1 - y
    omz = 1 - z
    opx = 1 + x
    opy = 1 + y
    opz = 1 + z

    N =
        SVector{8, typeof(x)}(
            omx * omy * omz,
            opx * omy * omz,
            opx * opy * omz,
            omx * opy * omz,
            omx * omy * opz,
            opx * omy * opz,
            opx * opy * opz,
            omx * opy * opz,
        ) / 8

    return N
end
"""
```
shapeFunction(::LinearQuadrangle3D, x::AbstractVector, y::AbstractVector, z::AbstractVector)
```
Computes the linear shape functions `N[1:8][p]` for `(x, y, z)` point `p` on a 3D linear quadrangle.
"""
function shapeFunction(
    ::LinearQuadrangle3D,
    x::AbstractVector,
    y::AbstractVector,
    z::AbstractVector,
)
    numPoints = length(x)
    @assert numPoints == length(y) == length(z)
    xType = eltype(x)
    # N[n, p](x_vec,y_vec,z_vec) := shape function n for point p.
    N = Array{SVector{8, xType}}(undef, numPoints)

    @inbounds @simd for i in eachindex(x)
        omx = 1 - x[i]
        omy = 1 - y[i]
        omz = 1 - z[i]
        opx = 1 + x[i]
        opy = 1 + y[i]
        opz = 1 + z[i]

        N[i] =
            SVector{8, xType}(
                omx * omy * omz,
                opx * omy * omz,
                opx * opy * omz,
                omx * opy * omz,
                omx * omy * opz,
                opx * omy * opz,
                opx * opy * opz,
                omx * opy * opz,
            ) / 8
    end

    return N
end

"""
```
shapeFunctionDeriv(shape<:AbstractShapeFunction, x, y, z)
```
Computes the derivatives of the linear shape functions `N[1:3, 1:8]` for an `(x, y, z)` point on a 3D linear quadrangle.

## Returns
```
dNdS[x, n](x,y,z) := x'th derivative of shape function n.
```
"""
function shapeFunctionDeriv(::LinearQuadrangle3D, x, y, z)
    # dNdS[x, n](x,y,z) := x'th derivative of shape function n.
    omx = 1 - x
    omy = 1 - y
    omz = 1 - z
    opx = 1 + x
    opy = 1 + y
    opz = 1 + z

    dNdS =
        SMatrix{3, 8, typeof(x)}(
            -omy * omz,
            -omx * omz,
            -omx * omy,
            omy * omz,
            -opx * omz,
            -opx * omy,
            opy * omz,
            opx * omz,
            -opx * opy,
            -opy * omz,
            omx * omz,
            -omx * opy,
            -omy * opz,
            -omx * opz,
            omx * omy,
            omy * opz,
            -opx * opz,
            opx * omy,
            opy * opz,
            opx * opz,
            opx * opy,
            -opy * opz,
            omx * opz,
            omx * opy,
        ) / 8

    return dNdS
end
"""
```
shapeFunctionDeriv(shape<:AbstractShapeFunction, x::AbstractVector, y::AbstractVector, z::AbstractVector)
```
Computes the derivatives of the linear shape functions `N[1:3, 1:8]` for an `(x, y, z)` point on a 3D linear quadrangle.

## Returns
```
dNdS[x, n, p](x,y,z) := x'th derivative of shape function n for point p.
```
"""
function shapeFunctionDeriv(
    ::LinearQuadrangle3D,
    x::AbstractVector,
    y::AbstractVector,
    z::AbstractVector,
)
    numPoints = length(x)
    @assert numPoints == length(y) == length(z)
    xType = eltype(x)

    # dNdS[x, n, p](x,y,z) := x'th derivative of shape function n for point p.
    dNdS = Array{SMatrix{3, 8, xType}}(undef, numPoints)

    @inbounds @simd for i in eachindex(x)
        omx = 1 - x[i]
        omy = 1 - y[i]
        omz = 1 - z[i]
        opx = 1 + x[i]
        opy = 1 + y[i]
        opz = 1 + z[i]

        dNdS[i] =
            SMatrix{3, 8, xType}(
                -omy * omz,
                -omx * omz,
                -omx * omy,
                omy * omz,
                -opx * omz,
                -opx * omy,
                opy * omz,
                opx * omz,
                -opx * opy,
                -opy * omz,
                omx * omz,
                -omx * opy,
                -omy * opz,
                -omx * opz,
                omx * omy,
                omy * opz,
                -opx * opz,
                opx * omy,
                opy * opz,
                opx * opz,
                opx * opy,
                -opy * opz,
                omx * opz,
                omx * opy,
            ) / 8
    end

    return dNdS
end


using DataDrivenDiffEq
using ModelingToolkit
using OrdinaryDiffEq
using LinearAlgebra
using Plots
gr()

# Create a
function pendulum(u, p, t)
    x = u[2]
    y = -9.81sin(u[1]) - 0.1u[2]^3 -0.2*cos(u[1])
    return [x;y]
end

u0 = [0.99π; -1.0]
tspan = (0.0, 20.0)
prob = ODEProblem(pendulum, u0, tspan)
sol = solve(prob, Tsit5(), saveat = 0.3)

plot(sol)

# Create the differential data
DX = similar(sol[:,:])
for (i, xi) in enumerate(eachcol(sol[:,:]))
    DX[:,i] = pendulum(xi, [], 0.0)
end

# Create a basis
@variables u[1:2]
@parameters w[1:2]
# Lots of polynomials
polys = Operation[1]
for i ∈ 1:5
    push!(polys, u.^i...)
    for j ∈ 1:i-1
        push!(polys, u[1]^i*u[2]^j)
    end
end

# And some other stuff
h = [w[1]*cos(u[1]); w[2]*sin(u[1]); u[1]*u[2]; u[1]*sin(u[2]); u[2]*cos(u[2]); polys...]

basis = Basis(h, u, parameters = w)
println(basis)

# Get the reduced basis via the sparse regression
# Thresholded Sequential Least Squares, works fine for more data
# than assumptions, converges fast but fails sometimes with too much noise
opt = STRRidge(1e-2)
# Enforce all 100 iterations
Ψ = SINDy(sol[:,1:25], DX[:, 1:25], basis, opt, p = [1.0; 1.0], maxiter = 100, convergence_error = 1e-5)
println(Ψ)
print_equations(Ψ)

# Lasso as ADMM, typically needs more information, more tuning
opt = ADMM(1e-2, 1.0)
Ψ = SINDy(sol[:,1:50], DX[:, 1:50], basis, opt, p = [1.0; 1.0], maxiter = 5000, convergence_error = 1e-3)
println(Ψ)
print_equations(Ψ)

# Get the associated parameters out of the result
parameters(Ψ)

# SR3, works good with lesser data and tuning
opt = SR3(1e-2, 1.0)
Ψ = SINDy(sol[:,1:end], DX[:, 1:end], basis, opt, p = [0.5; 0.5], maxiter = 5000, convergence_error = 1e-5)
println(Ψ)
print_equations(Ψ, show_parameter = true)

# Vary the sparsity threshold -> gives better results
λs = exp10.(-5:0.1:-1)
# Use SR3 with high relaxation (allows the solution to diverge from LTSQ) and high iterations
opt = SR3(1e-2, 5.0)
Ψ = SINDy(sol[:,1:10], DX[:, 1:10], basis, λs, opt, p = [1.0; 1.0], maxiter = 15000)
println(Ψ)
print_equations(Ψ)

# Transform into ODE System
sys = ODESystem(Ψ)
dudt = ODEFunction(sys)
ps = parameters(Ψ)

# Simulate
estimator = ODEProblem(dudt, u0, tspan, ps)
sol_ = solve(estimator, Tsit5(), saveat = sol.t)

# Yeah! We got it right
scatter(sol.t[1:10], sol[:,1:10]', color = :red, label = nothing)
scatter!(sol.t[11:end], sol[:,11:end]', color = :blue, label = nothing)
plot!(sol_.t, sol_[:, :]', color = :green, label = "Estimation")

plot(sol.t, abs.(sol-sol_)')
norm(sol[:,:]-sol_[:,:], 2)


# Corresponds to https://en.wikipedia.org/wiki/Logarithmic_distribution
struct Logarithmic{T<:Real} <: Distributions.DiscreteUnivariateDistribution
    p::T
    function Logarithmic(p::T) where {T <: Real}
        new{T}(p)
    end
end
function Distributions.logpdf(d::Logarithmic, x::Real)
    insupport(d, x) ? x*log(d.p) - log(x) - log(-log(1-d.p)) : log(zero(d.p))
end
function Distributions.rand(rng::Distributions.AbstractRNG, d::Logarithmic)
    # Sample a Log(p) distribution with the algorithm "LK" of Kemp (1981).
    u = rand(rng)
    if u > d.p
        return 1
    end
    q = 1 - (1-d.p)^rand(rng)
    if u < q*q
        return floor(1+log(u)/log(q))
    end
    if u < q
        return 1
    end
    return 2
end


export IdentityLink, LogLink, LogitLink
export link, unlink, jacobian_link

abstract type Link end

"""
    link(args...)

The link function is a map that brings a parameter ``f`` in a subspace ``\\mathcal{F} \\subset \\mathbb{R}`` to ``\\mathbb{R}``.
"""
function link end

"""
    unlink(args...)

The unlink function is the inverse map of [`link`](@ref link). It brings ``\\tilde{f}`` in ``\\mathbb{R}`` to the subspace ``\\mathcal{F} \\subset \\mathbb{R}``.
"""
function unlink end

"""
    jacobian_link(args...)

Evaluates the derivative of the [`link`](@ref link) with respect to the parameter ``f``.
"""
function jacobian_link end

"""
    IdentityLink <: Link

Define the map ``\\tilde{f} = f`` where ``f \\in \\mathbb{R}`` and ``\\tilde{f} \\in \\mathbb{R}``
"""
struct IdentityLink <: Link end

link(::Type{IdentityLink}, param::T) where T = param
unlink(::Type{IdentityLink}, param_tilde::T) where T = param_tilde
jacobian_link(::Type{IdentityLink}, param::T) where T = one(T)

"""
    LogLink <: Link

Define the map ``\\tilde{f} = \\ln(f - a)`` where ``f \\in [a, \\infty), a \\in \\mathbb{R}`` and ``\\tilde{f} \\in \\mathbb{R}``
"""
struct LogLink <: Link end

link(::Type{LogLink}, param::T, lower_bound::T) where T = log(param - lower_bound)
unlink(::Type{LogLink}, param_tilde::T, lower_bound::T) where T = exp(param_tilde) + lower_bound
jacobian_link(::Type{LogLink}, param::T, lower_bound::T) where T = 1/(param - lower_bound)

"""
    LogitLink <: Link

Define the map ``\\tilde{f} = \\ln(\\frac{f - a}{b - f})`` where ``f \\in [a, b], a, b \\in \\mathbb{R}`` and ``\\tilde{f} \\in \\mathbb{R}``
"""
struct LogitLink <: Link end

function link(::Type{LogitLink}, param::T, lower_bound::T, upper_bound::T) where T
    return log((param - lower_bound)/(upper_bound - param))
end
function unlink(::Type{LogitLink}, param_tilde::T, lower_bound::T, upper_bound::T) where T
    return lower_bound + ((upper_bound - lower_bound)/(1 + exp(-param_tilde)))
end
function jacobian_link(::Type{LogitLink}, param::T, lower_bound::T, upper_bound::T) where T
    return (upper_bound + lower_bound)/((upper_bound - param) * (param - lower_bound))
end

const LINKS = [
    IdentityLink;
    LogLink;
    LogitLink
]