function locS(w::Real, b::Real, c::Real, d::Real)
    S = (w^2 + c^2)^(-d) + b^2
  return S
end

function locS(b::Real, c::Real, d::Real)
  S = NaN*zeros(10000); n_w = length(S)
  w_grid = collect(linspace(0.001, 15*π, n_w))
  i = 0
  for w in w_grid
    i += 1
    S[i] = (w^2 + c^2)^(-d) + b^2
  end
  return S
end

function locSs(b::Real, c::Real, d::Real)
  S = locS(b, c, d)
  n = length(S)
  w_grid = collect(linspace(0.001, 15π, n))
  tmp, i1 = findmin(abs(w_grid-6*π))
  Ss = S./S[i1]
  x = w_grid/π
  return Ss, x
end
#
function plot_locS(b::Real, c::Real, d::Real)
    title("Local to Zero Spectrum: S(ω;b,c,d)∝(c² + d²)^(-1) + b²")
    Ss, x = locSs(b, c, d)
    _plt = plot(x, log(Ss), label = "($(round(b, 2)), $(round(c, 2)), $(round(d, 2)))")
    legend()
    return _plt
end
#
function plot_locS(m::String)
  if m == "local level"
    println("$m model")
    b_grid = [1/50 1/10 1/2 10e16]
    for b in b_grid
      Ss, x = locSs(b, 0, 1)
      plot(x, log(Ss), label = "b = $b")
      legend()
    end

  end
  if m == "local to unity"
    println("$m model")
    c_grid = [0 3 10 30]
    for c in c_grid
      Ss, x = locSs(0, c, 1)
      plot(x, log(Ss), label = "c = $c")
    end
    legend()
  end
  if m == "fractional"
    println("$m model")
    d_grid = [-1/3 0 1/3 1 4/3]
    for d in d_grid
      Ss, x = locSs(0, 0, d)
      plot(x, log(Ss), label = "d = $d")
    end
    legend()
  end
  if m == "bcd"
    b_grid = [1/50 1/10 1/2 10e16]
    c_grid = [0 3 10 30]
    d_grid = [-1/3 0 1/3 1 4/3]
    for b in b_grid
      for c in c_grid
        for d in d_grid
          println("$b, $c, $d")
          Ss, x = locSs(b, c, d)
          plot(x, log(Ss))
        end
      end
    end
  end
end

#end #module


###ODE for a predator-prey model
### t = timepoints to evaluate the ODE at
### y = the model's state variables (prey population size - predator population size)
### ydot = the derivate values
### paras = the equation parameters (r,K,s,a,u,v)
function predatorpreyode(t,y,ydot,paras)

    r = paras[1]
    k = paras[2]
    s = paras[3]
    a = paras[4]
    u = paras[5]
    v = paras[6]

    tmp = y[1]*y[2]/(a + y[1])

    ydot[1] = r*y[1]*(1-y[1]/k) - s*tmp
    ydot[2] = u*tmp - v*y[2]

    nothing
end

###Convenience function to create a Predator-Prey ODEModel
### y0: initial conditions of the dynamic system
### variance: the variance of the noise model
### paraminit: the default and boundary values of the parameters
function predatorpreymodel(y0::AbstractVector,variance::AbstractVector,paraminit...)
    modelparameters = parameters([:r,:k,:s,:a,:u,:v],paraminit...)
    noisemodel = noise(:gaussian,variance)
    measurementdata = data(:array,predatorpreydataset1()...)
    model(:ode,modelparameters,measurementdata,noisemodel,predatorpreyode,y0,2,[1,2];
          name="Predator-Prey",abstol=10e-9,reltol=10e-9)
end


#The m-by-n Hilbert matrix has matrix elements
# H_{ij} = 1/(i+j-1)
export Hilbert, InverseHilbert
"""
[`Hilbert` matrix](http://en.wikipedia.org/wiki/Hilbert_matrix)

```julia
julia> A=Hilbert(5)
Hilbert{Rational{Int64}}(5,5)

julia> Matrix(A)
5x5 Array{Rational{Int64},2}:
 1//1  1//2  1//3  1//4  1//5
 1//2  1//3  1//4  1//5  1//6
 1//3  1//4  1//5  1//6  1//7
 1//4  1//5  1//6  1//7  1//8
 1//5  1//6  1//7  1//8  1//9

julia> Matrix(Hilbert(5))
5x5 Array{Rational{Int64},2}:
 1//1  1//2  1//3  1//4  1//5
 1//2  1//3  1//4  1//5  1//6
 1//3  1//4  1//5  1//6  1//7
 1//4  1//5  1//6  1//7  1//8
 1//5  1//6  1//7  1//8  1//9
```
Inverses are also integer matrices:

```julia
julia> inv(A)
5x5 Array{Rational{Int64},2}:
    25//1    -300//1     1050//1    -1400//1     630//1
  -300//1    4800//1   -18900//1    26880//1  -12600//1
  1050//1  -18900//1    79380//1  -117600//1   56700//1
 -1400//1   26880//1  -117600//1   179200//1  -88200//1
   630//1  -12600//1    56700//1   -88200//1   44100//1
```
"""
struct Hilbert{T} <: AbstractMatrix{T}
    m :: Int
    n :: Int
end
Hilbert(T::Type, m::Integer, n::Integer) = Hilbert{T}(m, n)
Hilbert(T::Type{<:Integer}, m::Integer, n::Integer) = Hilbert(Rational{T}, m, n)
Hilbert(m::Integer, n::Integer) = Hilbert(Int, m, n)
Hilbert(n::Integer) = Hilbert(n, n)

# Define its size
size(H::Hilbert, dim::Integer) = dim==1 ? H.m : dim==2 ? H.n : 1
size(H::Hilbert)= size(H,1), size(H,2)

# Index into a Hilbert matrix
function getindex(H::Hilbert{T}, i::Integer, j::Integer) where {T}
    return one(T)/(i+j-1)
end

# Dense version
Matrix(H::Hilbert) = [H[i,j] for i=1:size(H,1), j=1:size(H,2)]

# Some properties
ishermitian(H::Hilbert) = H.m==H.n
isposdef(H::Hilbert) = H.m==H.n
det(H::Hilbert) = inv(det(inv(H)))

# Inverse of a Hilbert matrix
struct InverseHilbert{T} <: AbstractMatrix{T}
    n :: Int
end
InverseHilbert(T::Type, n::Integer) = InverseHilbert{T}(n)
InverseHilbert(n::Integer) = InverseHilbert(Int, n)

# Define its size
size(A::InverseHilbert, dim::Integer) = dim==1 || dim==2 ? A.n : 1
size(A::InverseHilbert) = size(A,1), size(A,2)

# Index into a inverse Hilbert matrix
function getindex(A::InverseHilbert{T}, i::Integer, j::Integer) where {T}
    return T((-1)^(i+j)*(i+j-1)*binomial(A.n+i-1,A.n-j)*
              binomial(A.n+j-1,A.n-i)*binomial(i+j-2,i-1)^2)
end
# Use bigger numbers in case of bigger types
function getindex(A::InverseHilbert{T}, i::Integer, j::Integer) where {T<:Union{BigInt,BigFloat}}
    N = big(A.n)
    return T((-1)^(i+j)*(i+j-1)*binomial(N+i-1,N-j)*
              binomial(N+j-1,N-i)*binomial(big(i+j-2),i-1)^2)
end

# Explicit formula for the determinant
det(A::InverseHilbert{T}) where {T} = prod(T,(2k+1)*binomial(2k,k)^2 for k=1:A.n-1)

# Dense version
Matrix(A::InverseHilbert) = [A[i,j] for i=1:size(A,1), j=1:size(A,2)]

# Define Base.inv
function inv(H::Hilbert{T}) where {T}
    H.m == H.n || throw(ArgumentError("Works only for square Hilbert matrices."))
    return InverseHilbert(T,H.n)
end
function inv(A::InverseHilbert{T}) where {T}
    HT = promote_type(T,typeof(Rational(one(T))))
    return Hilbert(HT,A.n)
end


using Distributions, Plots, CSV, Pandas

# Sample x_{k+1} given x_k
# Parameters:
# - x_k: The kth X sample. One of -1, 0, or 1
# Outputs:
# - x_{k+1}: The (k+1)th X sample
function sample_x(xk)
    # Define q parameter
    q = 0.3

    # Probability that x_{k+1} = 1
    p = q*(xk == 1) + (1-q)*(xk == 0)

    return rand() < p
end

# Obtains n samples of X based on HMM
# Inputs
# - n: An integer representing the number of samples to return
# Outputs
# A vector of length n where the kth element is the kth sample of X
function get_x_samples(n)
    # Store x samples
    samples = zeros(n)

    # Randomly set x1
    samples[1] = rand(0:1)

    # Get samples x_2 through x_n
    for i=2:n
        samples[i] = sample_x(samples[i-1])
    end

    return samples
end

# Obtains n samples of Y based on HMM
# Inputs
# - x_samples: A vector of length n representing the samples of X
# Outputs
# A vector of length n where the kth element is the kth sample of Y
function get_y_samples(x_samples)
    # Define normal standard deviation
    σ = 1.5

    # Store y samples
    n = length(x_samples)
    y_samples = zeros(n)

    for i=1:n
        𝒩 = Normal(x_samples[i], σ)
        y_samples[i] = rand(𝒩)
    end

    return y_samples
end

# Obtian and plot samples
n = 1500
x_samples = get_x_samples(n)
y_samples = get_y_samples(x_samples)

plot(1:n,x_samples, seriestype=:scatter, bg = RGB(247/255, 236/255, 226/255), color = RGB(0,191/255,255/255), label = "x", alpha = 0.2)
plot!(1:n,y_samples, seriestype=:scatter, color = RGB(191/255,1,0), label =
"y", alpha = 0.2)

cd("/Users/elvis/Documents/DSI/courses/content/stochastic-approximations/images/")

# Note X and Y variables swapped here from what was presented in example
df = DataFrame(Dict(:X=>y_samples))

CSV.write("/Users/elvis/Documents/DSI/courses/content/bayesian-inference-and-graphical-models/code/hmm_observations.csv", df)


# author: aliyyousuf@gmail.com

using Distributions
using DataFrames

srand(1234)

array_1 = zeros(Float64,30,3)

nd1 = Normal(0.0,1.0)
nd2 = Normal(10.0,2.0)

array_1[:,1] = rand(nd1, 30)
array_1[:,2] = rand(nd2, 30)
array_1[:,3] = rand(5:15,30)

println("Column 1 mean: ", mean(array_1[:,1]), ", variance: ", var(array_1[:,1]))
println("Column 2 mean: ", mean(array_1[:,2]), ", variance: ", var(array_1[:,2]))
println("Column 3 mean: ", mean(array_1[:,3]), ", variance: ", var(array_1[:,3]))

df = convert(DataFrame, array_1)

# Change the name of the columns in df

# Reanaming the columns's name of the dataframe:

rename!(df, f => t for (f, t) = zip([:x1, :x2, :x3], [:Var1, :Var2, :Var3]))

# Declaring array df2:

df2 = df[11:end,:]
describe(df2)

# Adding a new column to df2 named "Cat1":
df2[:Cat1] = rand(["GroupA", "GroupB"], 20)

# Declaring another DataFrame named as df3:
df3 = DataFrame(A = 1:20, B = 21:40, C = 41:60)

# Changing some row's values to NA:
df3[10,1] = df3[15,2] = df3[19,3] = NA

#Remove all rows that contain the NA values:

df4 = completecases!(df3)


"""
    B, T = brun(H)

Brun's integer-relations alg implemented as a matrix decomposition. Takes as
input the matrix `H` and returns a reduced basis `B` and `T`, a unimodular
transformation matrix such that `B = H*T`. Brun reduction is often done with
`pinv(H)` as input to yield `B = pinv(H)*T`.

See V. Brun, "En generalisation av kjedebrøken I," Skr. Vid
ensk. Selsk. Kristiana, Mat. Nat. Klasse, 1919.  See
https://archive.org/stream/skrifterutgitavv201chri#page/300/mode/2up

Follows code from D. Wuebben, D. Seethaler, J. Jalden, and G. Matz, "Lattice
Reduction - A Survey with Applications in Wireless Communications" IEEE
Signal Processing Magazine, March 2011

# Examples
```jldoctest
julia> H=[1 2; 3 4]; B,T=brun(H); T
2×2 Array{Int64,2}:
  3  -1
 -2   1

```
"""
function brun(H::AbstractArray{Td,2}) where Td

    # get size
    K, M = size(H)

    if rank(H) < K
        error("Input basis does not have full row rank")
    end

    Ti= getIntType(Td)

    grammian = H'*H
    metric_vec = sqrt.(abs.(diag(grammian')))
    mu  = grammian[:,1] # initialize mu with an arbitrary column of the Grammian
    T = Matrix{Ti}(I, K, K)   # unimodular transformation matrix
    B = copy(H)

    doBrun = true
    while doBrun

        # Calculation of Brun's update value
        zw,s = findmax(mu)
        zw = copy(mu)
        zw[s] = -typemax(zw[1])
        zw,t = findmax(zw)
        r = round(mu[s]/mu[t])

        # basis update
        updated_vec = B[:,s] .- r'*B[:,t]

        # if metric can be improved
        updated_norm = norm(updated_vec)
        if updated_norm < metric_vec[s]

            # update mu, unimodular and reduced matrices, metric
            mu[s] -= r*mu[t]
            T[:,s] .-= r'*view(T,:,t)  #T[:,s] -= r'*T[:,t]
            B[:,s] .= updated_vec
            metric_vec[s] = updated_norm

        else
            doBrun = false
        end

    end #

    return B, T
end


using EngEconomics

function learningCurveModel(T_init, N, b)
	return T_init * N^b
end

function learningCurveModelCoeff(T1, N1, T2, N2)
	b = log(T1 / T2) / log(N1 / N2)
end

function learningCurvePercentage(b)
	return exp(b * log(2))
end

# Given
avgLaborCost = 20 # $/hr
prodTimeUnit1 = 10 # hrs
prodTimeUnit4 = 8.1 # hrs
overheadExpenses = 1.0 * avgLaborCost # Dollars / hr

# Find
# a.) Learning Curve Model
Tinit = prodTimeUnit1
T4 = prodTimeUnit4
b = log(T4 / Tinit) / log(4 / 1)
percentage = exp(b * log(2))
b = learningCurveModelCoeff(Tinit, 1, T4, 4)
percentage = learningCurvePercentage(b)


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

using Markdown
using InteractiveUtils

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

# ╔═╡ b6681ace-5cb1-49cb-9ed7-e749c7a67428
using Distributions, Plots, StatsPlots, PlutoUI

# ╔═╡ a4a73fb0-2213-11ec-02f8-1d2409575d4c
md""" # Bayesian modelling

Three stages to building a model:
1. Data story: Motivate the model by narrating how the data might arise.
2. Update: Educate your model by feeding it the data.
3. Evaluate: evaluate the model, possibly leading to model revision.

### Example: Estimate the proportion of water on earth

Suppose you have a toy model of the Earth. You estimate the proportion of water on the model of Earth (and, if the model is a good one, the proportion of water on the actual Earth) by tossing it into the air and catching it. When you catch it record whether or not your right index finger is on land or water. Repeat this procedure.

###### 1. Data story
This story may either be *descriptive* specifying associations that can be used to predict outcomes, or *causal*, a theory of how some events produce other events.

For Earth example:
1. The true proportion of the Earth coverd in water is $P$.
2. A single toss and catch has a probability $p$ of producing a water (W) observation
3. Each toss of the globe is independent of each other.

###### 2. Update
Use the data to update the model.

For Earth example:
1. Assume all values of $P$ equally likely (the *prior*)
2. As observed data is binary, use Binomial distribution to find probability of observed data for each possible value of $P$
3. Update prior probability using Bayes Theorem and the observed data (the *posterior*)

Calculating these probabilities from the PDF's to generate the posterior is almost impossible for more complex examples, so numerical techniques used instead.

###### 3. Evaluate
The Bayesian model learns in a way that is demonstrably optimal. However this is only the performance of the model (the "small world") if the assumptions of the model do not match reality then the "large world" performance will not be good.

The objective is to check the model's adequacy for you purpose e.g. prediction, comprehension, measurement.

"""

# ╔═╡ 4dcf2480-7a37-4236-b51b-a171407bd8a0
md"""
#### Constructing the model
"""

# ╔═╡ e044742f-15dd-42e1-9271-43f11c7e2785
begin
	N = 9 # number of trials
	S = 6 # number of success (water)
end

# ╔═╡ c2d41bee-4952-429b-b3d8-aea46eefd2c4
md"""
Given the outcome of our experiment can be one of two options (W or L) with a fixed probability $p$ and each toss is independent (these come from the assumptions/data story), the probability of a single toss being W can be described by a binomial distribution:

``W\sim Binomial(N,p)``

"""

# ╔═╡ ef92132a-211d-4e8f-8ab2-12ae2c4a9c49
md"""
Initially we don't know what the parameter $P$ is, so assume all values equally likely, i.e. a uniform distribution between 0 and 1 (0% to 100% covered in water):

``P\sim Uniform(0,1)``
"""

# ╔═╡ e2b04571-e4e5-49fe-823d-af0404eceab7
P = Uniform(0,1);

# ╔═╡ e2bb3b47-1973-4d40-91f2-a703470d1a6a
begin
	plot(P, lw=2, legend=:none, title="prior distribution of P", ylims=(0,1.2))
	#plot!([0.0 1.0; 0.0 1.0],[0.0 0.0; 1.0 1.0], color=1)
end

# ╔═╡ 8f3de7b2-2fa3-4aa3-ba81-243aa4722a3a
md"""#### Update the model with data

The model gets updated (the posterior is generated) using Bayes theorem:

``Pr(p\mid W,L) = \frac{Pr(W,L\mid p)Pr(p)}{Pr(W,L)}``

Analytical this would give a posterior probability density function, but using grid approximation the values given are probabilities (masses):
"""

# ╔═╡ d142a6c3-e11c-44b5-930f-16deae98e74f
@bind n Slider(5:100, default=100) # fine-ness of the grid

# ╔═╡ 1a193ab0-10b0-447d-a68a-0fc978c6e804
md"""n = $n"""

# ╔═╡ 33fc930d-8693-41f9-8614-dc71870bd56d
begin
	grid = range(0,1,length=n) # possible values for P
	prior = @. pdf(Uniform(0,1), grid) # prior
	likelihood = @. pdf(Binomial(N,grid), S) # probability of observed data for diff P
	posterior = likelihood .* prior # unnormalised posterior distribution
	posterior /= sum(posterior) 	# normalise posterior to sum to one -> probability
end;

# ╔═╡ 4d10cc40-213e-4c4e-bea8-ea318aee18f7
begin
	plot(grid, posterior, markershape=:circle, mc=:white, msc=1, legend=:none, 		title="posterior distribution", annotations=(0.8,0.5," $(round(grid[argmax(posterior)], digits=3))"), ylims=(0,0.6))
	vline!([grid[argmax(posterior)]], color=1, ls=:dash)
end

# ╔═╡ 9149e9e5-b0b9-4c22-8795-d982a3cacf6a
md"""##### Collecting new data"""

# ╔═╡ 361b07cc-6554-40d1-9448-6f4e4cd4bb71
@bind Nnew Slider(1:100, default=9)

# ╔═╡ af901869-37bf-4992-bfc3-ee8477a37b34
md"""Number of tosses: $Nnew"""

# ╔═╡ e82146d1-ea29-40a3-b89f-5d178f4a900a
@bind Snew Slider(0:Nnew, default=6)

# ╔═╡ 2161be7c-571e-46c9-b3c2-0543d5e3296f
md"""Number of tosses on water $Snew"""

# ╔═╡ da10276c-1381-4edb-9171-1ec0820a1809
let
	n = 500
	grid = range(0,1,length=n) # possible values for P
	prior = repeat([1],n) # relative priors
	likelihood = @. pdf(Binomial(Nnew,grid), Snew) # probability of S successes
	posterior = likelihood .* prior |> x -> x/sum(x) # posterior distribution
	plot(grid, posterior, lw=2, legend=:none, title="posterior distribution", ylims=(0,0.04))
end

# ╔═╡ b8e49bec-d582-4695-b7ac-6b0ccbca5e08
md"""##### Trying out different priors (using original data)"""

# ╔═╡ 2cccdace-62df-4771-aaa4-1dd4342335e4
md"""###### Step prior
```prior = [zeros(n÷2)..., ones(n÷2)...]```
"""

# ╔═╡ a673dd91-ccdf-4783-8b24-357a7307d7d3
let
	n = 500
	grid = range(0,1,length=n) # possible values for P
	prior = [zeros(n÷2)..., ones(n÷2)...]
	likelihood = @. pdf(Binomial(Nnew,grid), Snew) # probability of S successes
	posterior = likelihood .* prior |> x -> x/sum(x) # posterior distribution
	p1 = plot(grid, prior, lw=2, legend=:none, title="prior distribution")
	p2 = plot(grid, posterior, lw=2, legend=:none, title="posterior distribution", ylims=(0,0.04))
	plot(p1,p2, layout=(1,2))
end

# ╔═╡ 3a34068e-4fed-40c3-89ef-2aabafb5144e
md"""###### Exponential prior
```prior = exp.(-5*abs.(collect(grid).-0.5) ) ```
"""

# ╔═╡ 9c599a59-96f7-4b2c-b690-13750a14eef8
let
	n = 500
	grid = range(0,1,length=n) # possible values for P
	prior = exp.(-5*abs.(collect(grid).-0.5) )
	likelihood = @. pdf(Binomial(Nnew,grid), Snew) # probability of S successes
	posterior = likelihood .* prior |> x -> x/sum(x) # posterior distribution
	p1 = plot(grid, prior, lw=2, legend=:none, title="prior distribution")
	p2 = plot(grid, posterior, lw=2, legend=:none, title="posterior distribution", ylims=(0,0.04))
	plot(p1,p2, layout=(1,2))
end

# ╔═╡ ffc34a30-9dba-40d0-afca-2279f4753661
md"""### Practice problems"""

# ╔═╡ a70c520f-860f-48be-a1a6-e87607298c37
md""" #### 2H1

##### Question
Suppose there are two species of panda, identical in every way except family sizes. Species A gives birth to twins 10% of the time, otherwise a single infant. Species B gives birth to twins 20% of the time, otherwise a single infant.

You have a new panda of unknown species just given birth to twins, what is the probability the next birth will also be twins?

##### Solution

1. Construct data story

Need to find probability of new panda being species A or B. The probability of next birth being twins will then be:

``Pr(A)*Pr(twins \mid A) + Pr(B)*Pr(twins \mid B)``

Species distribution:
- Assume prior to be equally likely for both species.
- Model outcome with binomial distribution: ``T \sim Binomial(2,p)`` where ``p = 0.1`` if species A or ``p = 0.2`` if species B.
"""

# ╔═╡ 8c493e53-bb7b-4110-a732-1cf93ecb754b
let
	N = 1
	S = 1
	grid = [0.1, 0.2]
	prior = [1, 1]
	likelihood = @. pdf(Binomial(N,grid), S)
	posterior = posterior = likelihood .* prior |> x -> x/sum(x)
	p = posterior .* grid |> sum
	md"""**Probability of next birth being twins = $p**"""
end

# ╔═╡ 9908c97f-efb4-4634-aa97-9b4d2f9d7acc
md""" #### 2H2

##### Question

Same as above, compute probability that the new panda is of species A given that the first birth was twins.

##### Answer

Want to find ``Pr(A \mid twins)`` which is reverse of ``Pr(twins \mid A)`` which has probability 0.1. Therefore use Bayes Theorem:

``Pr(A \mid twins) = \frac{Pr(twins \mid A)*Pr(A)}{Pr(twins)}``

"""

# ╔═╡ d690e1d2-654c-4da4-9255-d3871429d262
let
	p_twins_A = 0.1
	p_A = 0.5 # assuming equally likely
	p_twins = p_A*0.1 + (1-p_A)*0.2
	p_A_twins = (p_twins_A * p_A) / p_twins
	md"""**Probability of being species A given twins = $(p_A_twins)**"""
end

# ╔═╡ ee5bfc95-bbb0-4d1b-8a83-77e884171f05
md""" #### 2H3

##### Question

Same as above, the new panda next gives birth to a single infant. Compute probability that she is of species A.

##### Solution

Use posterior distribution from 2H1 which gives probabilities after first birth of twins as the prior and generate new posterior given new birth of not twins.

"""

# ╔═╡ b7033638-277e-4610-89ef-3abe33ef35bf
let
	N = 1
	S = 0
	grid = [0.1, 0.2]
	prior = [1, 2] # from 2H1
	likelihood = @. pdf(Binomial(N, grid), S) # probability of observing data given p
	posterior = likelihood .* prior |> x -> x ./ sum(x)
	md"""**Probability of being species A given second birth of twins = $(posterior[1])**"""
end

# ╔═╡ 00000000-0000-0000-0000-000000000001
PLUTO_PROJECT_TOML_CONTENTS = """
[deps]
Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f"
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
PlutoUI = "7f904dfe-b85e-4ff6-b463-dae2292396a8"
StatsPlots = "f3b207a7-027a-5e70-b257-86293d7955fd"

[compat]
Distributions = "~0.25.17"
Plots = "~1.22.3"
PlutoUI = "~0.7.14"
StatsPlots = "~0.14.28"
"""

# ╔═╡ 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"

[[Arpack]]
deps = ["Arpack_jll", "Libdl", "LinearAlgebra"]
git-tree-sha1 = "2ff92b71ba1747c5fdd541f8fc87736d82f40ec9"
uuid = "7d9fca2a-8960-54d3-9f78-7d1dccf2cb97"
version = "0.4.0"

[[Arpack_jll]]
deps = ["Libdl", "OpenBLAS_jll", "Pkg"]
git-tree-sha1 = "e214a9b9bd1b4e1b4f15b22c0994862b66af7ff7"
uuid = "68821587-b530-5797-8361-c406ea357684"
version = "3.5.0+3"

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

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

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

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

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

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

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

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

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

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

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

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

[[DataAPI]]
git-tree-sha1 = "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"

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

[[MultivariateStats]]
deps = ["Arpack", "LinearAlgebra", "SparseArrays", "Statistics", "StatsBase"]
git-tree-sha1 = "8d958ff1854b166003238fe191ec34b9d592860a"
uuid = "6f286f6a-111f-5878-ab1e-185364afe411"
version = "0.8.0"

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

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

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

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

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

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

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

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

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

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

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

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

[[PlotThemes]]
deps = ["PlotUtils", "Requires", "Statistics"]
git-tree-sha1 = "a3a964ce9dc7898193536002a6dd892b1b5a6f1d"
uuid = "ccf2f8ad-2431-5c83-bf29-c5338b663b6a"
version = "2.0.1"

[[PlotUtils]]
deps = ["ColorSchemes", "Colors", "Dates", "Printf", "Random", "Reexport", "Statistics"]
git-tree-sha1 = "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 = "cfbd033def161db9494f86c5d18fbf874e09e514"
uuid = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
version = "1.22.3"

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

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

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

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

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

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

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

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

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

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

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

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

[[SentinelArrays]]
deps = ["Dates", "Random"]
git-tree-sha1 = "54f37736d8934a12a200edea2f9206b03bdf3159"
uuid = "91c51154-3ec4-41a3-a24f-3f23e20d615c"
version = "1.3.7"

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

[[Widgets]]
deps = ["Colors", "Dates", "Observables", "OrderedCollections"]
git-tree-sha1 = "80661f59d28714632132c73779f8becc19a113f2"
uuid = "cc8bc4a8-27d6-5769-a93b-9d913e69aa62"
version = "0.6.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:
# ╟─a4a73fb0-2213-11ec-02f8-1d2409575d4c
# ╠═b6681ace-5cb1-49cb-9ed7-e749c7a67428
# ╟─4dcf2480-7a37-4236-b51b-a171407bd8a0
# ╠═e044742f-15dd-42e1-9271-43f11c7e2785
# ╟─c2d41bee-4952-429b-b3d8-aea46eefd2c4
# ╟─ef92132a-211d-4e8f-8ab2-12ae2c4a9c49
# ╠═e2b04571-e4e5-49fe-823d-af0404eceab7
# ╟─e2bb3b47-1973-4d40-91f2-a703470d1a6a
# ╟─8f3de7b2-2fa3-4aa3-ba81-243aa4722a3a
# ╟─1a193ab0-10b0-447d-a68a-0fc978c6e804
# ╟─d142a6c3-e11c-44b5-930f-16deae98e74f
# ╠═33fc930d-8693-41f9-8614-dc71870bd56d
# ╟─4d10cc40-213e-4c4e-bea8-ea318aee18f7
# ╟─9149e9e5-b0b9-4c22-8795-d982a3cacf6a
# ╟─af901869-37bf-4992-bfc3-ee8477a37b34
# ╟─361b07cc-6554-40d1-9448-6f4e4cd4bb71
# ╟─2161be7c-571e-46c9-b3c2-0543d5e3296f
# ╟─e82146d1-ea29-40a3-b89f-5d178f4a900a
# ╟─da10276c-1381-4edb-9171-1ec0820a1809
# ╟─b8e49bec-d582-4695-b7ac-6b0ccbca5e08
# ╟─2cccdace-62df-4771-aaa4-1dd4342335e4
# ╟─a673dd91-ccdf-4783-8b24-357a7307d7d3
# ╟─3a34068e-4fed-40c3-89ef-2aabafb5144e
# ╟─9c599a59-96f7-4b2c-b690-13750a14eef8
# ╟─ffc34a30-9dba-40d0-afca-2279f4753661
# ╟─a70c520f-860f-48be-a1a6-e87607298c37
# ╠═8c493e53-bb7b-4110-a732-1cf93ecb754b
# ╟─9908c97f-efb4-4634-aa97-9b4d2f9d7acc
# ╠═d690e1d2-654c-4da4-9255-d3871429d262
# ╟─ee5bfc95-bbb0-4d1b-8a83-77e884171f05
# ╠═b7033638-277e-4610-89ef-3abe33ef35bf
# ╟─00000000-0000-0000-0000-000000000001
# ╟─00000000-0000-0000-0000-000000000002


"""
    simplex_basis(a, b, i, j)

Evaluate 2D orthonormal polynomial at simplex (a, b) of order (i, j)

Translated from Simplex2DP.m

"""
function simplex_basis(a::T, b::T, i, j) where {T<:Real}
    # x, a, b, n
    h1 = JacobiP(a, 0, 0, i)
    h2 = JacobiP(b, 2 * i + 1, 0, j)

    return sqrt(2.0) * h1 * h2 * (1 - b)^i
end

simplex_basis(a::AbstractVector{T}, b::AbstractVector{T}, i, j) where {T<:Real} =
    [simplex_basis(a[k], b[k], i, j) for k in eachindex(a)]

function ∂simplex_basis(a::T, b::T, id, jd) where {T<:Real}
    #fa = jacobi(a, id, 0, 0)
    #dfa = djacobi(a, id, 0, 0)
    #gb = jacobi(b, jd, 2*id+1, 0)
    #dgb = djacobi(b, jd, 2*id+1, 0)
    fa = JacobiP(a, 0, 0, id)
    dfa = ∂JacobiP(a, 0, 0, id)
    gb = JacobiP(b, 2 * id + 1, 0, jd)
    dgb = ∂JacobiP(b, 2 * id + 1, 0, jd)

    # r-derivative
    # d/dr = da/dr d/da + db/dr d/db = (2/(1-s)) d/da = (2/(1-b)) d/da
    dmodedr = dfa * gb
    if id > 0
        dmodedr *= (0.5 * (1.0 - b))^(id - 1)
    end

    # s-derivative
    # d/ds = ((1+a)/2)/((1-b)/2) d/da + d/db
    dmodeds = dfa * (gb * (0.5 * (1.0 + a)))
    if id > 0
        dmodeds *= (0.5 * (1.0 - b))^(id - 1)
    end

    tmp = dgb * (0.5 * (1.0 - b))^id
    if id > 0
        tmp -= 0.5 * id * gb * ((0.5 * (1.0 - b))^(id - 1))
    end
    dmodeds += fa * tmp

    # normalization
    dmodedr *= 2^(id + 0.5)
    dmodeds *= 2^(id + 0.5)

    return dmodedr, dmodeds
end

function ∂simplex_basis(a::AbstractVector{T}, b::AbstractVector{T}, id, jd) where {T<:Real}
    dmodedr = zero(a)
    dmodeds = zero(b)

    for i in eachindex(a)
        dmodedr[i], dmodeds[i] = ∂simplex_basis(a[i], b[i], id, jd)
    end

    return dmodedr, dmodeds
end


using Distributions, Random, Plots, LaTeXStrings; pyplot()

N, K, M = 10^2, 50, 10^3
lamRange = 0.01:0.01:0.99

prn(lambda,rng) = quantile(Poisson(lambda),rand(rng))
zDist(lam) = Uniform(0,2*(1-lam))

rv(lam,rng) = sum([rand(rng,zDist(lam)) for _ in 1:prn(K*lam,rng)])
rv2(lam,rng1,rng2) = sum([rand(rng1,zDist(lam)) for _ in 1:prn(K*lam,rng2)])

mEst(lam,rng) = mean([rv(lam,rng) for _ in 1:N])
mEst2(lam,rng1,rng2) = mean([rv2(lam,rng1,rng2) for _ in 1:N])

function mGraph0(seed)
    singleRng = MersenneTwister(seed)
    [mEst(lam,singleRng) for lam in lamRange]
end
mGraph1(seed) = [mEst(lam,MersenneTwister(seed)) for lam in lamRange]
mGraph2(seed1,seed2) = [mEst2(lam,MersenneTwister(seed1),
		MersenneTwister(seed2)) for lam in lamRange]

argMaxLam(graph) = lamRange[findmax(graph)[2]]

std0 = std([argMaxLam(mGraph0(seed)) for seed in 1:M])
std1 = std([argMaxLam(mGraph1(seed)) for seed in 1:M])
std2 = std([argMaxLam(mGraph2(seed,seed+M)) for seed in 1:M])

println("Standard deviation with no CRN: ", std0)
println("Standard deviation with CRN and single RNG: ", std1)
println("Standard deviation with CRN and two RNGs: ", std2)

plot(lamRange,mGraph0(1987),
	c=:red, label="No CRN")
plot!(lamRange,mGraph1(1987),
	c=:green, label="CRN and one RNG")
plot!(lamRange,mGraph2(1987,1988),
	c=:blue, label="CRN and two RNG's", xlims=(0,1),ylims=(0,14),
    xlabel=L"\lambda", ylabel = "Mean")


using Optim
# using MPI_Tools
##This uses equations presented in "Stray capacitances of single-layer air-core inductors for high-frequency applications"
# By"Grandi, G., Kazimierczuk, M. K.,Massarini, A. Reggiani, U."

function F(p,t,r,ϵr)
    return (p/(2*r))./((1+t/r)^(1-1/ϵr))
end
function Ct(D,r,p,t,ϵr)
    #D - solenoid diameter in meters
    #r - wire radius
    #p - pitch in meters
    #t - wire insulation thickness
    #ϵr - insulation relative permittivity

    ϵ0 = 8.85e-12

    FVal = F(p,t,r,ϵr)
    CtVal = pi^2*D*ϵ0 / (log(FVal+sqrt(FVal^2 - (1+t/r)^(2/ϵr))))
end

## Using derivation in appendix
function CC(r,t,ϵr)
    return 2*π*ϵ₀*ϵr / (log(1+t/r))
end

function CG(p,r,t)
    (π * ϵ₀) ./ log( (p/(2*r))/(1+t/r) + sqrt( ( (p/(2*r))/(1+t/r) )^2 - 1) )
end

"""
This function calculates the equivalent turn-turn capacitcance of a single layer solenoid
    #D - solenoid diameter in meters
    #r - wire radius
    #p - pitch in meters
    #t - wire insulation thickness
    #ϵr - insulation relative permittivity
"""
function Ceq(D,r,p,t,ϵr)
    CC(r,t,ϵr)*CG(p,r,t) / (CC(r,t,ϵr)+2*CG(p,r,t)) * π*D
end

# Ceq(.2,.15e-3,1.33e-3,.5e-3,4)/10

## Medhurst
function CMedh(L,D)
    ϵ₀ = 8.85*1e-12
    return (4*ϵ₀*L/π + 8e-12*D + 27e-12*D*sqrt(D/L)) #Farads
end

function CMedh_Knight(L,D)
    ϵ₀ = 8.85*1e-12
    return 4*ϵ₀*L/π*(1+ 0.8249* (D/L)+ 2.329*(D/L)^(3/2))#Farads
end

## Equation 5.3, Cₗ-DAE in Knight, p.52

function CL_DAE_Knight(
    L, #Coil length (axial)
    D, #Coil diamter
    N, #N turns
    ϵᵣₓ, #external permittivity
    ϵᵣᵢ) #Internal permittivity
    kc(L,D) =0.717439(D/L) +0.933048(D/L)^(3/2) +0.106(D/L)^2

    ϵ₀ = 8.85*1e-12
    p = L/N

    Ψ = atan(p/(π*D))

    C_L = (4*ϵ₀*ϵᵣₓ)/π *L * (1+kc(L,D)*(1+ϵᵣᵢ/ϵᵣₓ)/2)*1/(cos(Ψ)^2)
    return C_L
end


using Test
include("../src/Probabilities.jl")

@testset verbose = true "Probabilities" begin
	@testset "Probabilities.A" begin
		# 测试m为0的情况
		@test Probabilities.A(0, 1) == 1
		# 测试参数校验功能， m不能大于n，两者都不能为负数
		@test_throws ArgumentError Probabilities.A(0, 0)
		@test_throws ArgumentError Probabilities.A(0, -1)
		@test_throws ArgumentError Probabilities.A(3, -1)
		@test_throws ArgumentError Probabilities.A(3, 2)
		# 测试正常计算
		@test Probabilities.A(3, 3) == factorial(3)
		@test Probabilities.A(10, 10) == factorial(10)
		@test Probabilities.A(2, 3) == 6
		@test Probabilities.A(5, 12) == 95040
	end
	@testset "Probabilities.C" begin
		# 测试m为0的情况
		@test Probabilities.C(0, 1) == 1
		# 测试参数校验功能， m不能大于n，两者都不能为负数
		@test_throws ArgumentError Probabilities.C(0, 0)
		@test_throws ArgumentError Probabilities.C(0, -1)
		@test_throws ArgumentError Probabilities.C(3, -1)
		@test_throws ArgumentError Probabilities.C(3, 2)
		# 测试正常计算
		@test Probabilities.C(3, 3) == 1
		@test Probabilities.C(10, 10) == 1
		@test Probabilities.C(2, 3) == 3
		@test Probabilities.C(5, 12) == 792
	end
    	@testset "Probabilities.Binomial_Distribution" begin
		# 参数类型检查
		@test_throws MethodError Probabilities.Binomial_Distribution(10, 0)
		# 概率范围检查
		@test_throws ArgumentError Probabilities.Binomial_Distribution(10, 2.1)
		@test_throws ArgumentError Probabilities.Binomial_Distribution(10, -0.2)
	end

	@testset "Probabilities.Poisson_Distribution" begin
		@test Probabilities.Poisson_Distribution(0:5, 1)[1] ≈ ℯ^-1
		@test Probabilities.Poisson_Distribution(0:200, 1)[1] ≈ ℯ^-1
	end

	@testset "Probabilities.Geometric_distribution" begin
		@test_throws ArgumentError Probabilities.Geometric_distribution(1:3, 2.1)
		@test Probabilities.Geometric_distribution(1:3, 0.9)[1] == 0.9
	end

	@testset "Probabilities.checks" begin
		@test_throws ArgumentError Probabilities.check_is_nonnegative(-1, 3)
	end

end


### A Pluto.jl notebook ###
# v0.12.6

using Markdown
using InteractiveUtils

# ╔═╡ de7a5e8a-1ee4-11eb-2bbf-0f0eb3e489ec
using Pkg, DrWatson

# ╔═╡ e4eeb13a-1ee4-11eb-0113-91f9a9c3b659
begin
	@quickactivate "StatisticsWithJuliaPlutoNotebooks"
	using SpecialFunctions
end

# ╔═╡ ac3ceabe-1ee4-11eb-3935-313e96aafd62
md"## Listing 2.11"

# ╔═╡ e9fa266e-1eeb-11eb-2630-59930c474ed3
n = 2000

# ╔═╡ d595cf1a-1f62-11eb-3503-753f5260ea5c
begin
	probAgivenB(k) = 1- 1/(k+1)
	probB(k) = 6/(pi*(k+1))^2
end

# ╔═╡ d5960d36-1f62-11eb-01dc-b59f88c34a97
begin
	numerical= sum([probAgivenB(k)*probB(k) for k in 0:n])
	analytic = 1 - 6*zeta(3)/pi^2
	Text("Analytic: $(analytic) Numerical: $(numerical)")
end

# ╔═╡ ea086874-1eeb-11eb-2cb0-4186697d4294
md"## End of listing 2.11"

# ╔═╡ Cell order:
# ╟─ac3ceabe-1ee4-11eb-3935-313e96aafd62
# ╠═de7a5e8a-1ee4-11eb-2bbf-0f0eb3e489ec
# ╠═e4eeb13a-1ee4-11eb-0113-91f9a9c3b659
# ╠═e9fa266e-1eeb-11eb-2630-59930c474ed3
# ╠═d595cf1a-1f62-11eb-3503-753f5260ea5c
# ╠═d5960d36-1f62-11eb-01dc-b59f88c34a97
# ╟─ea086874-1eeb-11eb-2cb0-4186697d4294


using StatsBase, Distributions, Plots; pyplot()

binomialRV(n,p) = sum(rand(n) .< p)

p, n, N = 0.5, 10, 10^6

bDist = Binomial(n,p)
xGrid = 0:n
bPmf = [pdf(bDist,i) for i in xGrid]
data = [binomialRV(n,p) for _ in 1:N]
pmfEst = counts(data,0:n)/N

plot( xGrid, pmfEst,
	line=:stem, marker=:circle,
	c=:blue, ms=10, msw=0, lw=4, label="MC estimate")
plot!( xGrid, bPmf,
	 line=:stem, marker=:xcross, c=:red,
	 ms=6, msw=0, lw=2, label="PMF", xticks=(0:1:10),
	 ylims=(0,0.3), xlabel="x", ylabel="Probability")


using Test
import Roots.newton, Roots.halley

@testset "Test Newton, Halley, Schroder methods" begin

    @test abs(newton(sin, cos, 0.5) - 0.0) <= 100*eps(1.0)
    @test newton(cos, x -> -sin(x), 1.0)  ≈ pi/2
    @test newton(x -> x^2 - 2x - 1, x -> 2x - 2, 3.0)  ≈ 2.414213562373095
    @test abs(newton(x -> exp(x) - cos(x), x -> exp(x) + sin(x), 3.0) - 0.0) <= 1e-14
    @test halley(x -> x^2 - 2x - 1,x -> 2x - 2,x -> 2, 3.0)  ≈ 2.414213562373095
    a = halley(x -> exp(x) - cos(x),
               x -> exp(x) + sin(x),
               x -> exp(x) + cos(x), 3.0)
    @test abs(a - 0.0) <= 1e-14

    # find_zero calls
    @test find_zero((x -> x^2 - 2x - 1,x -> 2x - 2), 3.0, Roots.Newton())  ≈ 2.414213562373095
    @test find_zero((x -> x^2 - 2x - 1,x -> 2x - 2,x -> 2), 3.0, Roots.Halley())  ≈ 2.414213562373095
    @test find_zero((x -> x^2 - 2x - 1,x -> 2x - 2,x -> 2), 3.0, Roots.Schroder())  ≈ 2.414213562373095

    ## test with Complex input

    @test real(Roots.newton(x ->  x^3 - 1, x ->  3x^2, 1+im)) ≈ 1.0
    @test real(Roots.newton(x ->  x^3 - 1, x ->  3x^2, 1+10im)) ≈ (-1/2)

    ## Issue #143 test with new interface
    Roots.newton(sin, cos, 3.0) ≈ π # uses find_zero
    Roots.newton((sin,cos), 3.0) ≈ π # uses simple

    fdf = x -> (sin(x), sin(x)/cos(x))  # (f, f/f')
    @test Roots.find_zero(fdf, 3.0, Roots.Newton())  ≈ π # uses find_zero
    Roots.newton(fdf, 3.0)  ≈ π # uses simple

    fdfdf = x -> (sin(x), sin(x)/cos(x), -cos(x)/sin(x), sin(x)/cos(x))   # (f, f/f', f'/f'', f''/f''')
    @test Roots.find_zero(fdfdf, 3.0, Roots.Halley()) ≈ π

    # check that functions with multiple return values can work with other
    # methods
    for M in [Roots.Schroder(), Roots.Halley(), Roots.Newton(), Roots.Order1(), Roots.Order0()]
        @test Roots.find_zero(fdfdf, 3.0, M) ≈ π  # can pass function to others
    end

    for M in [Roots.Bisection(), Roots.A42(), Roots.AlefeldPotraShi()]
        @test Roots.find_zero(fdfdf, (3.0, 4.0), M) ≈ π  # can pass function to others
    end

    @test find_zero(x -> (x^2 -2, (x^2-2)/2x), 1.0, Roots.Newton(), Roots.Bisection()) ≈ sqrt(2)

end


using NumericalMethodsforEngineers
using Test

a = Float64[16 4 8;4 5 -4;8 -4 22]
b = Float64[4, 2, 5]
n = size(a, 1)

(lower, upper) = lufac(a)
subfor!(lower, b)
subbac!(upper, b)
s = copy(b)

a = Float64[16 4 8;4 5 -4;8 -4 22]
b = Float64[4, 2, 5]
n = size(a, 1)
d = zeros(n)

ldlt!(a, d)
lower = zeros(3, 3)
for i in 1:n
  for j in 1:i
    lower[i, j] = a[i, j] / d[j]
  end
end

ldlfor!(a, b)
for i in 1:n
  a[i, :] = a[i, :] / d[i]
end

subbac!(a, b)

@test b == s


cd(@__DIR__)
using Pkg; Pkg.activate("."); Pkg.instantiate()

# Single experiment, move to ensemble further on
# Some good parameter values are stored as comments right now
# because this is really good practice

using OrdinaryDiffEq
using ModelingToolkit
using DataDrivenDiffEq
using LinearAlgebra, DiffEqSensitivity, Optim
using DiffEqFlux, Flux
using Plots
gr()

function corona!(du,u,p,t)
    S,E,I,R,N,D,C = u
    F, β0,α,κ,μ,σ,γ,d,λ = p
    dS = -β0*S*F/N - β(t,β0,D,N,κ,α)*S*I/N -μ*S # susceptible
    dE = β0*S*F/N + β(t,β0,D,N,κ,α)*S*I/N -(σ+μ)*E # exposed
    dI = σ*E - (γ+μ)*I # infected
    dR = γ*I - μ*R # removed (recovered + dead)
    dN = -μ*N # total population
    dD = d*γ*I - λ*D # severe, critical cases, and deaths
    dC = σ*E # +cumulative cases

    du[1] = dS; du[2] = dE; du[3] = dI; du[4] = dR
    du[5] = dN; du[6] = dD; du[7] = dC
end
β(t,β0,D,N,κ,α) = β0*(1-α)*(1-D/N)^κ
S0 = 14e6
u0 = [0.9*S0, 0.0, 0.0, 0.0, S0, 0.0, 0.0]
p_ = [10.0, 0.5944, 0.4239, 1117.3, 0.02, 1/3, 1/5,0.2, 1/11.2]
R0 = p_[2]/p_[7]*p_[6]/(p_[6]+p_[5])
tspan = (0.0, 21.0)
prob = ODEProblem(corona!, u0, tspan, p_)
solution = solve(prob, Vern7(), abstol=1e-12, reltol=1e-12, saveat = 1)

tspan2 = (0.0,60.0)
prob = ODEProblem(corona!, u0, tspan2, p_)
solution_extrapolate = solve(prob, Vern7(), abstol=1e-12, reltol=1e-12, saveat = 1)

# Ideal data
tsdata = Array(solution)
# Add noise to the data
noisy_data = tsdata + Float32(1e-5)*randn(eltype(tsdata), size(tsdata))

plot(abs.(tsdata-noisy_data)')

### Neural ODE

ann_node = FastChain(FastDense(7, 64, tanh),FastDense(64, 64, tanh), FastDense(64, 64, tanh), FastDense(64, 7))
p = Float64.(initial_params(ann_node))

function dudt_node(u,p,t)
    S,E,I,R,N,D,C = u
    F,β0,α,κ,μ,σ,γ,d,λ = p_
    dS,dE,dI,dR,dD = ann_node([S/N,E,I,R,N,D/N,C],p)

    dN = -μ*N # total population
    dC = σ*E # +cumulative cases

    [dS,dE,dI,dR,dN,dD,dC]
end
prob_node = ODEProblem(dudt_node, u0, tspan, p)
s = concrete_solve(prob_node, Tsit5(), u0, p, saveat = solution.t)

function predict(θ)
    Array(concrete_solve(prob_node, Vern7(), u0, θ, saveat = 1,
                         abstol=1e-6, reltol=1e-6,
                         sensealg = InterpolatingAdjoint(autojacvec=ReverseDiffVJP())))
end

# No regularisation right now
function loss(θ)
    pred = predict(θ)
    sum(abs2, (noisy_data[2:4,:] .- pred[2:4,:])), pred # + 1e-5*sum(sum.(abs, params(ann)))
end

loss(p)

const losses = []
callback(θ,l,pred) = begin
    push!(losses, l)
    if length(losses)%50==0
        println(losses[end])
    end
    false
end

res1_node = DiffEqFlux.sciml_train(loss, p, ADAM(0.01), cb=callback, maxiters = 500)
res2_node = DiffEqFlux.sciml_train(loss, res1_node.minimizer, BFGS(initial_stepnorm=0.01), cb=callback, maxiters = 10000)

prob_node2 = ODEProblem(dudt_node, u0, tspan, res2_node.minimizer)
s = solve(prob_node2, Tsit5(), saveat = 1)
scatter(solution, vars=[2,3,4], label=["True Exposed" "True Infected" "True Recovered"])
plot!(s, vars=[2,3,4], label=["Estimated Exposed" "Estimated Infected" "Estimated Recovered"])

# Plot the losses
plot(losses, yaxis = :log, xaxis = :log, xlabel = "Iterations", ylabel = "Loss")

# Extrapolate out
prob_node_extrapolate = ODEProblem(dudt_node,u0, tspan2, res2_node.minimizer)
_sol_node = solve(prob_node_extrapolate, Vern7(), abstol=1e-12, reltol=1e-12, saveat = 1)
p_node = scatter(solution_extrapolate, vars=[2,3,4], legend = :topleft, label=["True Exposed" "True Infected" "True Recovered"], title="Neural ODE Extrapolation")
plot!(p_node,_sol_node, lw=5, vars=[2,3,4], label=["Estimated Exposed" "Estimated Infected" "Estimated Recovered"])
plot!(p_node,[20.99,21.01],[0.0,maximum(hcat(Array(solution_extrapolate[2:4,:]),Array(_sol_node[2:4,:])))],lw=5,color=:black,label="Training Data End")

savefig("neuralode_extrapolation.png")
savefig("neuralode_extrapolation.pdf")

### Universal ODE Part 1

ann = FastChain(FastDense(3, 64, tanh),FastDense(64, 64, tanh), FastDense(64, 1))
p = Float64.(initial_params(ann))

function dudt_(u,p,t)
    S,E,I,R,N,D,C = u
    F, β0,α,κ,μ,σ,γ,d,λ = p_
    z = ann([S/N,I,D/N],p) # Exposure does not depend on exposed, removed, or cumulative!
    dS = -β0*S*F/N - z[1] -μ*S # susceptible
    dE = β0*S*F/N + z[1] -(σ+μ)*E # exposed
    dI = σ*E - (γ+μ)*I # infected
    dR = γ*I - μ*R # removed (recovered + dead)
    dN = -μ*N # total population
    dD = d*γ*I - λ*D # severe, critical cases, and deaths
    dC = σ*E # +cumulative cases

    [dS,dE,dI,dR,dN,dD,dC]
end
prob_nn = ODEProblem(dudt_,u0, tspan, p)
s = concrete_solve(prob_nn, Tsit5(), u0, p, saveat = 1)

plot(solution, vars=[2,3,4])
plot!(s[2:4,:]')

function predict(θ)
    Array(concrete_solve(prob_nn, Vern7(), u0, θ, saveat = solution.t,
                         abstol=1e-6, reltol=1e-6,
                         sensealg = InterpolatingAdjoint(autojacvec=ReverseDiffVJP())))
end

# No regularisation right now
function loss(θ)
    pred = predict(θ)
    sum(abs2, noisy_data[2:4,:] .- pred[2:4,:]), pred # + 1e-5*sum(sum.(abs, params(ann)))
end

loss(p)

const losses = []
callback(θ,l,pred) = begin
    push!(losses, l)
    if length(losses)%50==0
        println(losses[end])
    end
    false
end

res1_uode = DiffEqFlux.sciml_train(loss, p, ADAM(0.01), cb=callback, maxiters = 500)
res2_uode = DiffEqFlux.sciml_train(loss, res1_uode.minimizer, BFGS(initial_stepnorm=0.01), cb=callback, maxiters = 10000)

loss(res2_uode.minimizer)

prob_nn2 = ODEProblem(dudt_,u0, tspan, res2_uode.minimizer)
uode_sol = solve(prob_nn2, Tsit5(), saveat = 1)
plot(solution, vars=[2,3,4])
plot!(uode_sol, vars=[2,3,4])

# Plot the losses
plot(losses, yaxis = :log, xaxis = :log, xlabel = "Iterations", ylabel = "Loss")

# Collect the state trajectory and the derivatives
X = noisy_data
# Ideal derivatives
DX = Array(solution(solution.t, Val{1}))

# Extrapolate out
prob_nn2 = ODEProblem(dudt_,u0, tspan2, res2_uode.minimizer)
_sol_uode = solve(prob_nn2, Vern7(), abstol=1e-12, reltol=1e-12, saveat = 1)
p_uode = scatter(solution_extrapolate, vars=[2,3,4], legend = :topleft, label=["True Exposed" "True Infected" "True Recovered"], title="Universal ODE Extrapolation")
plot!(p_uode,_sol_uode, lw = 5, vars=[2,3,4], label=["Estimated Exposed" "Estimated Infected" "Estimated Recovered"])
plot!(p_uode,[20.99,21.01],[0.0,maximum(hcat(Array(solution_extrapolate[2:4,:]),Array(_sol_uode[2:4,:])))],lw=5,color=:black,label="Training Data End")

savefig("universalode_extrapolation.png")
savefig("universalode_extrapolation.pdf")

### Universal ODE Part 2: SInDy to Equations

# Create a Basis
@variables u[1:3]
# Lots of polynomials
polys = Operation[]
for i ∈ 0:2, j ∈ 0:2, k ∈ 0:2
    push!(polys, u[1]^i * u[2]^j * u[3]^k)
end

# And some other stuff
h = [cos.(u)...; sin.(u)...; unique(polys)...]
basis = Basis(h, u)

X = noisy_data
# Ideal derivatives
DX = Array(solution(solution.t, Val{1}))
S,E,I,R,N,D,C = eachrow(X)
F,β0,α,κ,μ,_,γ,d,λ = p_
L = β.(0:tspan[end],β0,D,N,κ,α).*S.*I./N
L̂ = vec(ann([S./N I D./N]',res2_uode.minimizer))
X̂ = [S./N I D./N]'

scatter(L,title="Estimated vs Expected Exposure Term",label="True Exposure")
plot!(L̂,label="Estimated Exposure")
savefig("estimated_exposure.png")
savefig("estimated_exposure.pdf")

# Create an optimizer for the SINDY problem
opt = SR3()
# Create the thresholds which should be used in the search process
thresholds = exp10.(-6:0.1:1)

# Test on original data and without further knowledge
Ψ_direct = SInDy(X[2:4, :], DX[2:4, :], basis, thresholds, opt = opt, maxiter = 50000) # Fail
println(Ψ_direct.basis)
# Test on ideal derivative data ( not available )
Ψ_ideal = SInDy(X[2:4, 5:end], L[5:end], basis, thresholds, opt = opt, maxiter = 50000) # Succeed
println(Ψ_ideal.basis)
# Test on uode derivative data
Ψ = SInDy(X̂[:, 2:end], L̂[2:end], basis, thresholds,  opt = opt, maxiter = 10000, normalize = true, denoise = true) # Succeed
println(Ψ.basis)

# Build a ODE for the estimated system
function approx(u,p,t)
    S,E,I,R,N,D,C = u
    F, β0,α,κ,μ,σ,γ,d,λ = p_
    z = Ψ([S/N,I,D/N]) # Exposure does not depend on exposed, removed, or cumulative!
    dS = -β0*S*F/N - z[1] -μ*S # susceptible
    dE = β0*S*F/N + z[1] -(σ+μ)*E # exposed
    dI = σ*E - (γ+μ)*I # infected
    dR = γ*I - μ*R # removed (recovered + dead)
    dN = -μ*N # total population
    dD = d*γ*I - λ*D # severe, critical cases, and deaths
    dC = σ*E # +cumulative cases

    [dS,dE,dI,dR,dN,dD,dC]
end

# Create the approximated problem and solution
a_prob = ODEProblem{false}(approx, u0, tspan2, p_)
a_solution = solve(a_prob, Tsit5())

p_uodesindy = scatter(solution_extrapolate, vars=[2,3,4], legend = :topleft, label=["True Exposed" "True Infected" "True Recovered"])
plot!(p_uodesindy,a_solution, lw = 5, vars=[2,3,4], label=["Estimated Exposed" "Estimated Infected" "Estimated Recovered"])
plot!(p_uodesindy,[20.99,21.01],[0.0,maximum(hcat(Array(solution_extrapolate[2:4,:]),Array(_sol_uode[2:4,:])))],lw=5,color=:black,label="Training Data End")

savefig("universalodesindy_extrapolation.png")
savefig("universalodesindy_extrapolation.pdf")


"""
    ahp_RI(n)
Return AHP Random Index for a given n.
# Arguments:
 - `n::Int64`: Index value

 # Description
 The function returns the random index for a given integer between 3 and 15.

# Output
- `::Float64`: Random Index (RI) value.

# Examples
```julia-repl
julia> ahp_RI(3)
0.58
```

# References

Saaty, Thomas L. "Decision making with the analytic hierarchy process." International journal of services sciences 1.1 (2008): 83-98.
```
"""
function ahp_RI(n::Int64)::Float64
    # First index is n = 3
    # RI[3] = 0.58
    # RI[4] = 0.90
    ris = [0.58, 0.90, 1.12, 1.24, 1.32,
          1.41, 1.45, 1.49, 1.51, 1.53,
          1.56, 1.57, 1.59]
    if n < 3
        return 0
    elseif n <= 15
        return ris[n - 2]
    else
        return ris |> last
    end

end

"""
    ahp_consistency(comparisonMatrix)
    Test if a comparison matrix whether consistent or not.

 # Arguments:
 - `comparisonMatrix::DataFrame`: Comparison matrix for AHP.

 # Description
 AHP is based on subjective comparison between criteria. The success of the method highly depends on
 consistency of these comparison.

# Output
- `::AHPConsistencyResult`: AhpConsistencyResult object that holds multiple outputs including the boolean that indicates the matrix is whether consistent or not.

# Examples
```julia-repl
julia> K = [
               1 7 (1 / 5) (1 / 8) (1 / 2) (1 / 3) (1 / 5) 1;
               (1 / 7) 1 (1 / 8) (1 / 9) (1 / 4) (1 / 5) (1 / 9) (1 / 8);
               5 8 1 (1 / 3) 4 2 1 1;
               8 9 3 1 7 5 3 3;
               2 4 (1 / 4) (1 / 7) 1 (1 / 2) (1 / 5) (1 / 5);
               3 5 (1 / 2) (1 / 5) 2 1 (1 / 3) (1 / 3);
               5 9 1 (1 / 3) 5 3 1 1;
               1 8 1 (1 / 3) 5 3 1 1
           ];

julia> K
8×8 Array{Float64,2}:
 1.0       7.0  0.2    0.125     0.5   0.333333  0.2       1.0
 0.142857  1.0  0.125  0.111111  0.25  0.2       0.111111  0.125
 5.0       8.0  1.0    0.333333  4.0   2.0       1.0       1.0
 8.0       9.0  3.0    1.0       7.0   5.0       3.0       3.0
 2.0       4.0  0.25   0.142857  1.0   0.5       0.2       0.2
 3.0       5.0  0.5    0.2       2.0   1.0       0.333333  0.333333
 5.0       9.0  1.0    0.333333  5.0   3.0       1.0       1.0
 1.0       8.0  1.0    0.333333  5.0   3.0       1.0       1.0

julia> dmat = makeDecisionMatrix(K);
julia> ahp_consistency(dmat).isConsistent
true
```

# References
Saaty, Thomas L. "Decision making with the analytic hierarchy process." International journal of services sciences 1.1 (2008): 83-98.
"""
function ahp_consistency(comparisonMatrix::DataFrame)::AHPConsistencyResult

    n, m = size(comparisonMatrix)

    csums = colsums(comparisonMatrix)
    normalizedComparisonMatrix = zeros(Float64, n, n)

    for i in 1:n
        for j in 1:n
            normalizedComparisonMatrix[i, j] = comparisonMatrix[i, j] / csums[j]
        end
    end

    priority_vector = rowmeans(normalizedComparisonMatrix)
    #mcomparisonMatrix = convert(Array{Float64,2}, comparisonMatrix)
    mcomparisonMatrix = Matrix{Float64}(comparisonMatrix)

    consistency_vector = mcomparisonMatrix * priority_vector

    pc_matrix = consistency_vector ./ priority_vector

    lambda_max = sum(pc_matrix) / n

    CI  = (lambda_max - n) / (n - 1)
    ri  = ahp_RI(n)
    CR  = CI / ri

    isConsistent = (CR < 0.1)

    result = AHPConsistencyResult(
        comparisonMatrix,
        makeDecisionMatrix(normalizedComparisonMatrix),
        consistency_vector,
        priority_vector,
        pc_matrix,
        lambda_max,
        CI,
        ri,
        CR,
        isConsistent
    )

    return result

end

"""
    AHP(comparisonMatrixList, criteriaComparisonMatrix)

Apply AHP (Analytical Hierarchy Process) for a given list of comparison matrices and criteria comparison matrix.

# Arguments:
 - `comparisonMatrixList::Array{DataFrame,1}`: Array of comparison matrices for all of the criteria.
 - `criteriaComparisonMatrix::DataFrame`: Criteria comparison matrix for AHP (Comparison of columns).

 # Description
 AHP is based on subjective comparison between criteria. The success of the method highly depends on
 consistency of these comparison. The method test the consistency first. At the next step, weights are
 calculated. The ordering of rows is determined by these weights.

# Output
- `::AHPResult`: AhpResult object that holds multiple outputs including calculated weights and scores.

# Examples
```julia-repl
julia> K
8×8 Array{Float64,2}:
 1.0       7.0  0.2    0.125     0.5   0.333333  0.2       1.0
 0.142857  1.0  0.125  0.111111  0.25  0.2       0.111111  0.125
 5.0       8.0  1.0    0.333333  4.0   2.0       1.0       1.0
 8.0       9.0  3.0    1.0       7.0   5.0       3.0       3.0
 2.0       4.0  0.25   0.142857  1.0   0.5       0.2       0.2
 3.0       5.0  0.5    0.2       2.0   1.0       0.333333  0.333333
 5.0       9.0  1.0    0.333333  5.0   3.0       1.0       1.0
 1.0       8.0  1.0    0.333333  5.0   3.0       1.0       1.0

julia> A1
4×4 Array{Float64,2}:
 1.0       3.0  0.2       2.0
 0.333333  1.0  0.142857  0.333333
 5.0       7.0  1.0       4.0
 0.5       3.0  0.25      1.0

julia> A2
4×4 Array{Float64,2}:
 1.0   0.5       4.0       5.0
 2.0   1.0       6.0       7.0
 0.25  0.166667  1.0       3.0
 0.2   0.142857  0.333333  1.0

julia> A3
4×4 Array{Float64,2}:
 1.0       0.5  0.166667  3.0
 2.0       1.0  0.25      5.0
 6.0       4.0  1.0       9.0
 0.333333  0.2  0.111111  1.0

julia> A4
4×4 Array{Float64,2}:
 1.0       7.0  0.25      2.0
 0.142857  1.0  0.111111  0.2
 4.0       9.0  1.0       5.0
 0.5       5.0  0.2       1.0

julia> A5
4×4 Array{Float64,2}:
 1.0       6.0  2.0   3.0
 0.166667  1.0  0.25  0.333333
 0.5       4.0  1.0   2.0
 0.333333  3.0  0.5   1.0

julia> A6
4×4 Array{Float64,2}:
 1.0  0.25  0.5  0.142857
 4.0  1.0   2.0  0.333333
 2.0  0.5   1.0  0.2
 7.0  3.0   5.0  1.0

julia> A7
4×4 Array{Float64,2}:
 1.0       3.0   7.0  1.0
 0.333333  1.0   4.0  0.333333
 0.142857  0.25  1.0  0.142857
 1.0       3.0   7.0  1.0

julia> A8
4×4 Array{Float64,2}:
 1.0    2.0       5.0       8.0
 0.5    1.0       3.0       6.0
 0.2    0.333333  1.0       3.0
 0.125  0.166667  0.333333  1.0

julia> km = makeDecisionMatrix(K);
julia> as = map(makeDecisionMatrix, [A1, A2, A3, A4, A5, A6, A7, A8]);
julia> result = ahp(as, km);
julia> result.bestIndex
3
julia> result.scores
4-element Array{Float64,1}:
 0.2801050163111839
 0.14822726478768022
 0.3813036392434616
 0.19036407965767424
```

# References
Saaty, Thomas L. "Decision making with the analytic hierarchy process." International journal of services sciences 1.1 (2008): 83-98.
"""
function ahp(comparisonMatrixList::Array{DataFrame,1}, criteriaComparisonMatrix::DataFrame)::AHPResult

    result_list = map(ahp_consistency, comparisonMatrixList)

    n = length(result_list)

    ncriteria, _ = size(criteriaComparisonMatrix)

    ncandidates, _ = size(comparisonMatrixList[1])

    decision_matrix = zeros(Float64, ncandidates, ncriteria)

    @inbounds for i in 1:n
        decision_matrix[:,i] = result_list[i].priority
    end

    criteria_consistency = ahp_consistency(criteriaComparisonMatrix)

    weights = criteria_consistency.priority

    decision_matrix_df = makeDecisionMatrix(decision_matrix)
    ordering_result =  decision_matrix * weights

    bestIndex = sortperm(ordering_result) |> last

    result = AHPResult(
        comparisonMatrixList,
        criteriaComparisonMatrix,
        criteria_consistency,
        decision_matrix_df,
        ordering_result,
        weights,
        bestIndex
    )

    return result
end


using Test
using MultivariateBases

@testset "Orthogonal" begin
    orthogonal_test(LegendreBasis, x -> [
        1,
        x,
        (3x^2 - 1) / 2,
        (5x^3 - 3x) / 2,
        (35x^4 - 30x^2 + 3) / 8
    ], true)
end

@testset "API degree = $degree" for degree in 0:3
    api_test(LegendreBasis, degree)
end


# function calculate how close a matrix is to identity (up to permutations and rescaling)
using Combinatorics, LinearAlgebra
"""
    err_pd(Ie)

Calculate the error (up to permuataion and rescaling) between Ie and identity matrix:

```math
err = \\mathrm{min}_{P, D} ||PDI_{e} - I||_{F},
```

where ``P`` runs over all permuataion matrices and `` D `` runs over all diagonal non-singular matrices.
``||~||_F`` denotes the Frobeneous norm.

# Arguments
-   `Ie`: the input matrix, supposed to be close to identity matrix.

# Outputs
-   `err`: the error between `Ie` and identity matrix defined as above.
"""
function err_pd(Ie)
    # get dimension
    s = size(Ie, 1)

    # generate all permutations in Sym(s)
    perm = permutations(1:s) |> collect

    # identity matrix
    idm = Matrix{Float64}(I, s, s)

    err = []
    for i in 1:factorial(s)
        Iep = Ie[perm[i],:]
        errtemp = []
        for j in 1: s
            eie = Iep[:,j]
            ei = idm[:,j]
            push!(errtemp, norm((I - eie*eie'/norm(eie)^2)*ei))
        end
        push!(err, norm(errtemp))
    end

    return minimum(err)
end


function irr(annuity::Array{Float64})

    tvmnpv(i,annuity)=begin
         n=collect(1:length(annuity));
         sum(annuity./(1+i).^n)
        end

	if tvmnpv(0.0,annuity) <= 1.0
        return 0.0
    end

	if tvmnpv(0.99, annuity) >= 0
        return 99.9
    end

    f(x)=tvmnpv(x, annuity)

    return find_zeros(f, 0, 1)

end

function npv(annuities::Array{Float64}, rate::Float64)
    pv = 0.0

    for (ix,p) in enumerate(annuities)

        pv += p/(1+rate)^(ix-1)

    end

    return pv

end

function period_pay(P::Float64, rate::Float64, n::Int64)

 return (P*rate*(1.0+rate)^n)/((1.0+rate)^n -1)

end

function amortize(rate::Float64, n::Int64, P::Float64)
    balance = Array{Float64,1}()
    p = period_pay(P,rate,n)

    for k in 1:n
        btmp = P*(1.0+rate)^k - p*(1.0-(1.0+rate)^k)/(-rate)
        push!(balance,btmp)
    end

    intp = vcat([P]*rate, balance[1:end-1]*rate)
    balance = balance[1:end]
    principal_paid = p .- intp

    return  principal_paid, intp, balance, p

end


# Poverty_gap.jl

###### poverty_gap #####
"""
    poverty_gap(v, z)

Compute the Poverty Gap of a vector `v` at a specified poverty threshold `z`.

# Examples
```julia
julia> using Inequality
julia> poverty_gap([8, 5, 1, 3, 5, 6, 7, 6, 3], 4)
0.1388888888888889
```
"""
poverty_gap(v::AbstractVector{<:Real}, z::Real)::Float64 = sum((z .- v[v .< z])/z)/length(v)

###### weighted poverty_gap #####
"""
    poverty_gap(v, w, z)

Compute the Poverty Gap of a vector `v` at a specified poverty threshold `z`,
using weights given by a weight vector `w`.

Weights must not be negative, missing or NaN. The weights and data vectors must have the same length.

# Examples
```julia
julia> using Inequality
julia> poverty_gap([8, 5, 1, 3, 5, 6, 7, 6, 3], [0.1,0.5,0.3,0.8,0.1,0.5,0.3,0.8,0.2], 4)
0.13194444444444445
```
"""
function poverty_gap(v::AbstractVector{<:Real}, w::AbstractVector{<:Real}, z::Real)::Float64
    checks_weights(v, w)

    return sum((z .- v[v .< z])/z .* w[v .< z])/sum(w)
end

function poverty_gap(v::AbstractVector{<:Real}, w::AbstractWeights, z::Real)::Float64
    checks_weights(v, w)

    return sum((z .- v[v .< z])/z .* w[v .< z])/w.sum
end

wpoverty_gap(v::AbstractVector{<:Real}, w::AbstractVector{<:Real}, z::Real)::Float64 = poverty_gap(v, w, z)

wpoverty_gap(v::AbstractVector{<:Real}, w::AbstractWeights, z::Real)::Float64 = poverty_gap(v, w, z)


@doc raw"""
    gaussradau(n::Integer) -> Tuple{Vector{Float64},Vector{Float64}}

Return nodes and weights of [Gauss-Radau quadrature](https://mathworld.wolfram.com/RadauQuadrature.html).

```math
\int_{-1}^{1} f(x) dx \approx \sum_{i=1}^{n} w_i f(x_i)
```

# Examples
```jldoctest
julia> x, w = gaussradau(3);

julia> f(x) = x^4;

julia> I = dot(w, f.(x));

julia> I ≈ 2/5
true
```

Note that the first node is fixed at -1.

```jldoctest
julia> x, w = gaussradau(3);

julia> x[1]
-1.0
```
"""
function gaussradau(n::Integer, T::Type=Float64)
    a = b = zero(T)
    # RADAUPTS   Gauss-Legendre-Radau Quadrature Nodes and Weights
    if n == 1
        return T[-1], T[2]
    elseif n == 2
        return [-1, one(T)/3], [one(T)/2, convert(T,3)/2]
    else
        # Compute via GaussJacobi:
        x, w = gaussjacobi(n - 1, a, b+1)
        @inbounds for i in 1:length(w)
            w[i] = w[i] / (1 + x[i])
        end
        pushfirst!(x, -1)
        pushfirst!(w, convert(T, 2) / n^2)
        return x, w
    end
end

function gaussradau(n::Integer, α, β)
    if n ≤ 0
        throw(DomainError(n, "Input N must be a positive integer"))
    end
    m = n - 1
    s = α + β
    T = float(eltype(s))
    μ = jacobimoment(α, β)
    n == 0 && return T[], T[]
    n == 1 && return [-one(T)], [μ]
    J = jacobi_jacobimatrix(n, α, β)
    aᴿ = -1 + 2m*convert(T,m+α)/((2m+s)*(2m+s+1))
    J.dv[end] = aᴿ
    x, V = eigen(J)
    w = V[1,:].^2 .* μ
    x[1] = -1 # fix rounding from eigen computation
    return x, w
end


plan_fejer1(μ) = FFTW.plan_r2r!(μ, FFTW.REDFT01)

"""
Compute nodes of Fejer's first quadrature rule.
"""
fejernodes1(::Type{T}, N::Int) where T = chebyshevpoints(T, N, Val(1))

"""
Compute weights of Fejer's first quadrature rule with modified Chebyshev moments of the first kind ``\\mu``.
"""
fejerweights1(μ::Vector) = fejerweights1!(copy(μ))
fejerweights1!(μ::Vector) = fejerweights1!(μ, plan_fejer1(μ))
function fejerweights1!(μ::Vector{T}, plan) where T
    N = length(μ)
    rmul!(μ, inv(T(N)))
    return plan*μ
end

plan_fejer2(μ) = FFTW.plan_r2r!(μ, FFTW.RODFT00)

"""
Compute nodes of Fejer's second quadrature rule.
"""
fejernodes2(::Type{T}, N::Int) where T = T[sinpi((N-2k-one(T))/(2N+two(T))) for k=0:N-1]

"""
Compute weights of Fejer's second quadrature rule with modified Chebyshev moments of the second kind ``\\mu``.
"""
fejerweights2(μ::Vector) = fejerweights2!(copy(μ))
fejerweights2!(μ::Vector) = fejerweights2!(μ, plan_fejer2(μ))
function fejerweights2!(μ::Vector{T}, plan) where T
    N = length(μ)
    Np1 = N+one(T)
    rmul!(μ, inv(Np1))
    plan*μ
    @inbounds for i=1:N μ[i] = sinpi(i/Np1)*μ[i] end
    return μ
end


@testset "Test Fractional Differences Equations PECE method" begin
    differencefun(x) = 0.5*x+1
    α=0.5;x0=1;
    T=1; h=0.1
    prob = FractionalDifferenceProblem(differencefun, α, x0)
    sol = solve(prob, T, h, PECEDifference())
    @test isapprox(sol.u, [1.0, 2.5, 4.25, 5.125, 5.5625, 5.78125, 5.890625, 5.9453125, 5.97265625, 5.986328125, 5.9931640625]; atol=1e-3)
end

@testset "Test Grunwald Letnikov method for fractional difference equatoins" begin
    function sys!(du, u, p, t)
        du[1] = -0.05*u[2] - 0.05*u[3] + 0.01*tanh(u[2])
        du[2] = 0.05*u[1] + 0.02*u[2] + 0.01*tanh(u[1])
        du[3] = 0.1 - 0.2*u[3] + 0.05*u[1]*u[3] + 0.01*tanh(u[3])
    end

    prob = FractionalDifferenceSystem(sys!, 0.98, [1, -1, 0])
    result = solve(prob, 7, GL())

    @test isapprox(result, [1.0  0.0423841  0.0350316  0.0241793  0.0103323  -0.00594294  -0.0241551
    -1.0  0.0376159  0.0401587  0.0426291  0.0445987   0.0458335    0.0461619
     0.0  0.1        0.179209   0.24285    0.294188    0.335624     0.369018]; atol=1e-5)
end


using Convex, LinearAlgebra
using UnbalancedOptimalTransport: AbstractDivergence, KL, TV, RG, Balanced

"""
    divergence(D::AbstractDivergence, a, b)

Computes the the divergence `D` between `a` and `b`, where `a` can be a
Convex.jl expression, and `b` is a constant.
"""
function divergence end

lg(x) = x <= 0 ? zero(x) : log(x)

# need to type-pirate a method for non-Convex.jl objects
Convex.entropy(a::AbstractArray) = sum(x -> -x * lg(x), a)

function divergence(::KL{ρ}, a, b) where {ρ}
    ρ * (-entropy(a) - dot(a, lg.(b)) + sum(b - a))
end

function divergence(::TV{ρ}, a, b) where {ρ}
    ρ * norm(a - b, 1)
end

# Computes the objective of the unbalanced regularized optimal transport problem
# This is a separate method so it can be reused for testing `optimal_coupling`.
function objective(
    π,
    D::AbstractDivergence,
    a::DiscreteMeasure,
    b::DiscreteMeasure,
    ϵ;
    C = (x, y) -> norm(x - y),
)
    π_1 = sum(π, dims = 2)
    π_2 = vec(sum(π, dims = 1))

    # instantiate the cost matrix
    C_matrix = [C(xᵢ, yⱼ) for xᵢ in a.set, yⱼ in b.set]

    obj = dot(C_matrix, π) + ϵ * divergence(KL(), vec(π), kron(b.density, a.density))

    if (D isa KL) || (D isa TV)
        obj += divergence(D, π_1, a.density) + divergence(D, π_2, b.density)
    end

    return obj
end

function OT_convex(
    D::AbstractDivergence,
    a::DiscreteMeasure,
    b::DiscreteMeasure,
    ϵ;
    solver,
    verbose = false,
    C = (x, y) -> norm(x - y),
)
    ϵ >= 0 || throw(DomainError(ϵ, "Need ϵ >= 0"))

    # coupling and its marginals
    π = Variable(length(a), length(b))
    π_1 = sum(π, dims = 2)
    π_2 = vec(sum(π, dims = 1))

    obj = objective(π, D, a, b, ϵ; C = C)

    if D isa Balanced
        constraints = [π >= 0, π_1 == a.density, π_2 == b.density]
    elseif D isa RG
        l, u = params(D)
        constraints = [
            π >= 0,
            l * a.density <= π_1,
            π_1 <= u * a.density,
            l * b.density <= π_2,
            π_2 <= u * b.density,
        ]
    else
        constraints = [π >= 0]
    end

    problem = minimize(obj, constraints)

    solve!(problem, solver; verbose = verbose)

    return (optimal_value = problem.optval, optimal_coupling = evaluate(π))
end

params(::RG{l,u}) where {l,u} = (l, u)


struct Polynomial{DT<:AbstractFloat, BT <: Basis}
    b::BT
    c::Vector{DT}

    function Polynomial{DT,BT}(b::BT, c) where {DT, BT <: Basis{DT}}
        @assert nnodes(b) == length(c)
        new(b, c)
    end
end

function Polynomial(b::Basis{DT}, c::Vector{DT}) where {DT}
    Polynomial{DT, typeof(b)}(b, c)
end

function LagrangePolynomial(x::Vector{DT}, c::Vector{DT}) where {DT}
    b = LagrangeBasis(x)
    Polynomial{DT, typeof(b)}(b, c)
end

function BernsteinPolynomial(x::Vector{DT}, c::Vector{DT}) where {DT}
    b = BernsteinBasis(x)
    Polynomial{DT, typeof(b)}(b, c)
end

function Base.similar(lag::Polynomial{T}, c::Vector{T}) where {T}
    @assert length(c) == length(lag.c)
    Polynomial(lag.b, c)
end

function evaluate!(pol::Polynomial{T}, x::Vector{T}, y::Vector{T}) where {T}
    @assert length(x) == length(y)

    local tx::T
    local ty::T

    for k in eachindex(x,y)
        tx = x[k]
        ty = 0
        for j in eachindex(pol.c)
            ty += pol.c[j] * eval_basis(pol.b, j, tx)
        end
        y[k] = ty
    end

    return nothing
end

function evaluate!(b::LagrangeBasis{T}, c::Vector{T}, x::Vector{T}, y::Vector{T}) where {T}
    evaluate!(Polynomial(b, c), x, y)
end


# test functions for developing optimizers
# see https://en.wikipedia.org/wiki/Test_functions_for_optimization

# optimal = (0, 0, 0, ...)
function sphere(x)
    return sum(x^2)
end

# optimal = (1,1,1,...)
function rosenbrock(x)
    x1 = x[1:end-1]
    x2 = x[2:end]
    return sum(100 * (x2-x1^2)^2 + (1-x1)^2)
end

# optimal = (3, 0.5)
function beale(p)
    x, y = p[1], p[2]
    return (1.5 - x + x*y)^2 + (2.25 - x + x*y^2)^2 +
            (2.625 - x + x*y^3)
end

# optimal = (1, 3)
function booth(p)
    x, y = p[1], p[2]
    return (x + 2y - 7)^2 + (2x+y-5)^2
end

#optimal = (-0.2903, ...)
function stybliski_tang(x)
    return sum(x^4 - 16x^2 + 5x)/2
end

# optimal = (1,1)
function levi(p)
    x, y = p[1], p[2]
    sin(3π*x)^2 + (x-1)^2 * (1+sin(3π*y)^2) + (y-1)^2 * (1+sin(2π*y)^2)
end


#=
Probability distributions useful in economics.

@author : Spencer Lyon <spencer.lyon@nyu.edu>

@date: 2014-08-04

References
----------

http://en.wikipedia.org/wiki/Beta-binomial_distribution

=#

"""
The Beta-Binomial distribution

##### Fields

- `n, a, b::Float64` The three paramters to the distribution

##### Notes

See also http://en.wikipedia.org/wiki/Beta-binomial_distribution

"""
type BetaBinomial
    n::Integer
    a::Real
    b::Real
end

# moments
mean(d::BetaBinomial) = d.n * d.a / (d.a + d.b)

std(d::BetaBinomial) = sqrt(var(d))

function var(d::BetaBinomial)
    n, a, b = d.n, d.a, d.b
    top = n*a*b * (a + b + n)
    btm = (a+b)^2.0 * (a+b+1.0)
    top / btm
end

function skewness(d::BetaBinomial)
    n, a, b = d.n, d.a, d.b
    t1 = (a+b+2*n) * (b - a) / (a+b+2)
    t2 = sqrt((1+a+b) / (n*a*b * (n+a+b)))
    t1 * t2
end

"""
Evaluate the pdf of the distributions at the points 0, 1, ..., k

##### Arguments

`d::BetaBinomial`: Instance of `BetaBinomial` type

##### Returns

- `probs::vector{Float64}`: pdf of the distribution `d`, at `0:d.k`

"""
function pdf(d::BetaBinomial)
    n, a, b = d.n, d.a, d.b
    k = 0:n
    binoms = Float64[binomial(n, i) for i in k]
    probs = binoms .* beta(k .+ a, n .- k .+ b) ./ beta(a, b)
end