module GellMannMatrices

using LinearAlgebra, SparseArrays
export gellmann

# ---------------------------------------------------------------------------------------- #
# direct sum (i.e., block-diagonalization of matrices As)
if VERSION < v"1.8"
    # work around for https://github.com/JuliaLang/julia/pull/44211 for Julia ≤ v1.7
    @inline ⊕(As...) = cat(As..., dims=(1,2))::typeof((.+)(As...))
else
    @inline ⊕(As...) = cat(As..., dims=Val((1,2)))
end

# https://en.wikipedia.org/wiki/Matrix_unit
unit(::Type{<:SparseMatrixCSC}, i, j, n, m) = sparse([i], [j], [1], n, m)
unit(T::Type{<:AbstractMatrix}, i, j, n, m) = (A = zeros(eltype(T), n, m); A[i,j] = one(eltype(T)); A)

# ---------------------------------------------------------------------------------------- #
# implementation following https://en.wikipedia.org/wiki/Generalizations_of_Pauli_matrices

# off-diagonal matrices (f_ij on Wiki)
function offdiag_gellmann(T::Type{<:AbstractMatrix}, i, j, d)
    if i < j
        return unit(T, i, j, d, d) + unit(T, j, i, d, d)
    elseif i > j
        return -1im * (unit(T, j, i, d, d) - unit(T, i, j, d, d))
    else
        throw(DomainError((j, i), "i and j must differ"))
    end
end

# "diagonal"-like matrices (h_i on Wiki)
function diag_gellmann(T::Type{<:AbstractMatrix}, i, d; norm_identity::Bool=false)
    if i == 1
        return norm_identity ? T(I(d) / sqrt(d/2)) : T(I(d))
    elseif i < d
        return diag_gellmann(T, i, d-1) ⊕ zero(eltype(T))
    elseif i == d
        return sqrt(2/(d*(d-1))) * (T(I(d-1)) ⊕ convert(eltype(T), (1-d)))
    else
        throw(DomainError((i,d), "invalid combination of (i,d); must have i ≤ d and d ≥ 1"))
    end
end

# ---------------------------------------------------------------------------------------- #

"""
    gellmann([T::Type{<:AbstractMatrix}=Matrix{ComplexF64},] d::Integer; skip_identity=true)

Return the generalized Gell-Mann matrices in `d` dimensions as a vector of matrices of type
`T`.

`T` can be any mutable `AbstractMatrix`, including `SparseMatrixCSC`, with complex floating
point element type.

## Keyword arguments
- `skip_identity` (default, `true`): if `false`, the identity matrix of dimension `d` is
  included as the last element of the returned vector.
- `norm_identity` (default, `false`): if `true`, the elements of the identity are normalized
   by `sqrt(D/2)` to ensure the the orthogonality relation `tr(A'*A) = 2`.

## Examples
Compute the Pauli matrices ``σ_i`` (`d=2`) and Gell-Mann matrices ``Λ_i`` (`d=3`):
```jl
julia> σᵢ = gellmann(2)
julia> Λᵢ = gellmann(3)
```
"""
gellmann(d::Integer; kwargs...) = gellmann(Matrix{ComplexF64}, d; kwargs...)

function gellmann(T::Type{<:AbstractMatrix}, d::Integer;
                  skip_identity=true, norm_identity=false)
    d > 0 || throw(DomainError(d, "dimension must be a positive integer"))
    eltype(T) == complex(eltype(T)) || throw(DomainError(T, "the element type of T must be complex"))
    # we collect the matrices in the slightly odd-looking manner below in order to be
    # consistent with the conventional number of Pauli and Gell-Mann matrices.
    ms = Vector{T}(undef, d*d - skip_identity)
    k = 0
    for i in 2:d
        for  j in 1:i-1
            ms[k+=1] = offdiag_gellmann(T, j, i, d)
            ms[k+=1] = offdiag_gellmann(T, i, j, d)
        end
        ms[k+=1] = diag_gellmann(T, i, d)
    end
    skip_identity || (ms[k+=1] = diag_gellmann(T, 1, d; norm_identity))
    return ms
end

end


# p38.jl - solve u_xxxx = exp(x), u(-1)=u(1)=u'(-1)=u'(1)=0
#         (compare p13.jl)

# Construct discrete biharmonic operator:
N = 15; (D,x) = cheb(N);
S = diagm([0; 1 ./(1-x[2:N].^2); 0]);
D4 = (diagm(1-x.^2)*D^4 - 8*diagm(x)*D^3 - 12*D^2)*S;
D4 = D4[2:N,2:N];

# Solve boundary-value problem and plot result:
f = @. exp(x[2:N]); u = D4\f; u = [0;u;0];
clf(); axes([.1,.4,.8,.5]);
plot(x,u,".",markersize=10);
axis([-1,1,-.01,.06]); grid(true);
xx = (-1:.01:1);
uu = (1-xx.^2).*polyval(polyfit(x,S*u),xx);
plot(xx,uu);

# Determine exact solution and print maximum error:
A = [1 -1 1 -1; 0 1 -2 3; 1 1 1 1; 0 1 2 3];
V = xx.^(0:3)';
c = A\exp.([-1,-1,1,1]); exact = exp.(xx) - V*c;
title("max err = $(signif(norm(uu-exact,Inf),5))",fontsize=12);


"""
Gray Code Generator
===================

## References

- [knuth]: The Art of Computer Programming, Volume 4 Fascicle 2A, 7.2.1.1 Generating all n-tuples.
- [manber]: Introduction to Algorithms: A Creative Approach, 2.9 Gray Codes.
- [wiki]: https://en.wikipedia.org/wiki/Gray_code#Constructing_an_n-bit_Gray_code
"""
module GrayCode

# From [wiki]:
#
# "Once a binary number appears in $G_n$ it appears in the same position in all longer lists; so it
# makes sense to talk about the reflective Gray code value of a number."
#
# "These characteristics suggest a simple and fast method of translating a binary value into the
# corresponding Gray code. Each bit is inverted if the next higher bit of the input value is set to
# one."
#
# From [knuth]:
#
# "[R]eversing the order of Gray binary code is equivalent to complementing the fist bit."
#
# - - - -
#
# We have
#
#     g(i) = b(i) ⊕ b(i + 1)
#
# So g(i) of the MSbit is b(i) as b(i + 1) is 0. And for converting from Gray code to binary
# (c.f. https://math.stackexchange.com/a/1829662):
#
#     b(i) = g(i) ⊕ b(i + 1)
#
# or,
#
#     b(i) = g(i) ⊕ g(i + 1) ⊕ g(i + 2) ⊕ ...

#===== Gray code <-> Binary =====#

# b -> g  (n-th is 1-based)
"Computes the `n`-th reflective gray code."
function gray_nth(n::Integer)::Vector{Int8}
    @assert n > 0
    n -= 1
    g = xor(n, n >>> 1)
    digits(Int8, g, base=2)
end

# g -> b
function gray_to_binary(g::Vector{Int8})::Vector{Int8}
    b = copy(g)
    for i in (length(g) - 1):-1:1
        b[i] = xor(g[i], b[i + 1])
    end
    b
end

#===== Constructs Gray code by recursion =====#

# A slow implementation, just for proof-of-concept. See [manber].
function gray_codes_of_len(len::Integer)::Vector{Vector{Int8}}
    @assert iseven(len) "Gray code of odd length does not exist."
    @assert len > 0 "Length should be greater than 0."
    gray_codes_of_len_r(len)
end

function gray_codes_of_len_r(len::Integer)::Vector{Vector{Int8}}
    if len == 0
        return [[]]
    elseif len == 2
        g =
        [
            Int8[0],
            Int8[1],
        ]
        return g
    end

    if isodd(len)
        _2k = len - 1
        g_2k = gray_codes_of_len_r(_2k)
        if ispow2(_2k)
            g =
            [
                [[0; g] for g in g_2k]...,
                [1; g_2k[end]],
            ]
            return g
        else
            # There are some unused strings of length `ceil(log2(k * 2))`, one of which is connected
            # to one of the used strings.

            # Oops, this construction is useful for the proof but cannot be elegantly expressed as
            # code.
            len_pow2 = nextpow(2, _2k)
            g_pow2 = gray_codes_of_len_r(len_pow2)  # slow, fvck
            used = Set(g_2k)
            for (s, t) in [(i, mod1(i + 1, len_pow2)) for i in 1:len_pow2]
                if g_pow2[s] in used && !(g_pow2[t] in used)
                    index_2k = findfirst(x -> x == g_pow2[s], g_2k)
                    g =
                    [
                        g_2k[(index_2k + 1):end]
                        g_2k[1:index_2k]
                        [g_pow2[t]]
                    ]
                    return g
                end
            end
            error("Should not reach here.")
        end
    end

    half_len = div(len, 2)
    g_half = gray_codes_of_len_r(half_len)
    g_half_rev = reverse(g_half)

    [
        [[0; g] for g in g_half];
        [[1; g] for g in g_half_rev];
    ]
end

#===== Gray code iterating =====#

# c.f. [knuth] Algorithm G.
mutable struct GrayCodeIter
    n::Int
    code::Union{UInt128, Nothing}
    parity::Int8

    GrayCodeIter(n::Int) = new(n, 0, 0)
end

function Base.iterate(iter::GrayCodeIter, state=iter)
    if iter.code == nothing
        nothing
    else
        curr_code = iter.code
        iter.parity ⊻= 1
        if iter.parity == 1
            iter.code ⊻= 1
        elseif iter.code == 1 << (iter.n - 1)
            iter.code = nothing
        else
            iter.code ⊻= curr_code ⊻ (curr_code - 1) + 1
        end
        (curr_code, iter)
    end
end

Base.length(iter::GrayCodeIter) = UInt128(2) ^ iter.n

# TODO: [knuth] Fig. 14

#===== Unit Tests =====#
using Test
using Random: seed!

# Helpers for testing
function diff_one(a::Vector{T}, b::Vector{T})::Bool where T<:Integer
    len_diff = length(a) - length(b)
    if len_diff == 0
        return sum(xor.(a, b)) == 1
    elseif len_diff == 1
        # does nothing
    elseif len_diff == -1
        a, b = b, a
    else
        return false
    end

    a[end] == 1 && @view(a[1:(end - 1)]) == b
end

function binary_array_to_value(ba::Vector{Int8})::UInt64
    @assert length(ba) <= 64
    reduce((acc, x) -> (acc << 1) + x, @view ba[end:-1:1]; init=UInt64(0))
end

function check_grayness(gray_codes::Vector{Vector{Int8}})
    len = length(gray_codes)
    for i in 1:len
        @test diff_one(gray_codes[i], gray_codes[mod1(i + 1, len)])
    end
    @test length(unique(map(binary_array_to_value, gray_codes))) == len
end

@testset "N-th Gray code" begin
    seed!()
    N = 2^16
    for _ in 1:100
        n = rand(1:N)
        n_minus1 = mod1(n - 1, N)
        n_plus1 = mod1(n + 1, N)
        g = gray_nth(n)
        g_minus1 = gray_nth(n_minus1)
        g_plus1 = gray_nth(n_plus1)
        @test diff_one(g_minus1, g)
        @test diff_one(g, g_plus1)

        b = gray_to_binary(g)
        nb = binary_array_to_value(b) + 1
        @test n == nb
    end
end

@testset "Gray code of given length" begin
    for len in 2:2:64
        gray_codes = gray_codes_of_len(len)
        check_grayness(gray_codes)
    end
end

@testset "Gray code - Chinese ring puzzle" begin
    for n in 2:8
        gc_gen = GrayCodeIter(n)
        gray_codes = [digits(Int8, g, base=2, pad=n) for g in gc_gen]
        check_grayness(gray_codes)
    end
end
end  # module


# elastic-net regularization

"""
  ElasticNet(λ::Real=1.0, μ::Real=1.0)

Returns the function `g(x) = μ||x||_1 + (λ/2)||x||²`, for a real parameters `μ, λ ⩾ 0`.
"""

immutable ElasticNet{R <: Real} <: ProximableFunction
  mu::R
  lambda::R
  function ElasticNet(mu::R, lambda::R)
    if lambda < 0 || mu < 0
      error("parameters μ, λ must be nonnegative")
    else
      new(mu, lambda)
    end
  end
end

ElasticNet{R <: Real}(mu::R=1.0, lambda::R=1.0) = ElasticNet{R}(mu, lambda)

function (f::ElasticNet){T <: RealOrComplex}(x::AbstractArray{T})
  return f.mu*vecnorm(x,1) + (f.lambda/2)*vecnorm(x,2)^2
end

function prox!{T <: Real}(f::ElasticNet, x::AbstractArray{T}, y::AbstractArray{T}, gamma::Real=1.0)
  sqnorm2x = zero(Float64)
  norm1x = zero(Float64)
  gm = gamma*f.mu
  gl = gamma*f.lambda
  for i in eachindex(x)
    y[i] = (x[i] + (x[i] <= -gm ? gm : (x[i] >= gm ? -gm : -x[i])))/(1 + gl)
    sqnorm2x += abs2(y[i])
    norm1x += abs(y[i])
  end
  return f.mu*norm1x + (f.lambda/2)*sqnorm2x
end

function prox!{T <: Complex}(f::ElasticNet, x::AbstractArray{T}, y::AbstractArray{T}, gamma::Real=1.0)
  sqnorm2x = zero(Float64)
  norm1x = zero(Float64)
  gm = gamma*f.mu
  gl = gamma*f.lambda
  for i in eachindex(x)
    y[i] = sign(x[i])*max(0, abs(x[i]) - gm)/(1 + gl)
    sqnorm2x += abs2(y[i])
    norm1x += abs(y[i])
  end
  return f.mu*norm1x + (f.lambda/2)*sqnorm2x
end

fun_name(f::ElasticNet) = "elastic-net regularization"
fun_dom(f::ElasticNet) = "AbstractArray{Real}, AbstractArray{Complex}"
fun_expr(f::ElasticNet) = "x ↦ μ||x||_1 + (λ/2)||x||²"
fun_params(f::ElasticNet) = "μ = $(f.mu), λ = $(f.lambda)"

function prox_naive{T <: RealOrComplex}(f::ElasticNet, x::AbstractArray{T}, gamma::Real=1.0)
  uz = max.(0, abs.(x) - gamma*f.mu)/(1 + f.lambda*gamma);
  return sign.(x).*uz, f.mu*vecnorm(uz,1) + (f.lambda/2)*vecnorm(uz)^2
end


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

using Markdown
using InteractiveUtils

# ╔═╡ e8595a60-222f-11ec-0595-c9717ac06507
begin
	using Plots,SymPy, LaTeXStrings, LinearAlgebra
	import PlotlyJS
	plotlyjs()
end

# ╔═╡ 431922d9-e97a-4300-a482-2d458b66c67a
md""" ### Ejercicio 1
Considere la cónica dada por $y = x^2 − 4x + 5$ y la recta $L$ que pasa por $(2, 7)$ con
direcci´on $v = (1, 1)$
"""

# ╔═╡ 85cf0b95-4446-4e84-a6c3-f9d3fcf4cd61
md"""
1. En un mismo plano cartesiano grafique la recta y la c´onica, obtenga anal´ıticamente los
puntos de intersecci´on entre ambas y m´arquelos en su gr´afico.

2. Halle el ´area del tri´angulo definido por el v´ertice de la c´onica y los puntos de intersecci´on
obtenidos anteriormente.
"""

# ╔═╡ 53b8d020-b4a9-4c74-b0cc-dba03b6edbd3
# Definimos las variables s'imbolicas
@syms x λ

# ╔═╡ 4257606e-3eff-4124-aad3-517998f97d24
md"""
Definamos la conica
"""

# ╔═╡ 61b1bf99-507b-4379-b537-86ce040f1418
y = x^2 - 4x + 5

# ╔═╡ d17a8914-6ecf-4606-a664-7242d0f8f06c
md"""
Para la  recta tenemos la siguiente representaci'o

$$L  = \lambda (1, 1) + (2, 7)$$

pero para encontrar la interseccion con la par'abola va a ser m'as 'util representarlo en la forma can'onica de $y = m * x + b$

$$(x,y) = ( \lambda + 2, \lambda + 7 )$$

Igualando cada una de las componentes tenemos que

$$y = \lambda + 7 = (x-2) + 7 = x + 5$$
"""

# ╔═╡ 2056778d-12f0-4325-920f-f6c7e453918b
L =	x + 5

# ╔═╡ 4f6feaf8-5322-4afc-b31d-211761e222c2
md"""
Graficando ambas
"""

# ╔═╡ f676a35a-c61e-4547-b176-211dd3dc647b
begin
	p = plot()
	plot!(y,label=sympy.latex(y),xlims=(-2,6))
	plot!(L, label=sympy.latex(L))
end

# ╔═╡ d8e3eda0-cada-4047-81cc-137a54e1b5e6
md"""
Calculamos la intersecci'on entre la recta y la para'bola

`solve` busca las soluciones del sistema $S = 0$ entonces planteamos la igualdad como
$S = L - y = 0$ que es equivalente a tener $L=y$
"""

# ╔═╡ 897cfd67-1be7-40e4-87d9-c11bc9de55d4
interseccion = solve(y - L)

# ╔═╡ 67e7f9f5-6e78-4579-97f7-7c0349b39afd
y_interseccion = y.(interseccion)

# ╔═╡ 4d6550b8-4e9d-4437-84bd-d978e33bfdb8
md"""
Agregamos los puntos de la intersecci'on al gr'afico
"""

# ╔═╡ 997c5ff7-ac5d-484f-bcdf-a89194d112ee
p2 = scatter!(interseccion,y_interseccion,color=:black,label="interseccion",ax=p)

# ╔═╡ 19f1f8b8-0f5c-4388-b550-f4cdd7619311
md"""
Calculamos el vertice usando la formula de $x_{vert} = \frac{-b}{2a}$
y en nuestro caso queda

$$x_{vert} = \frac{-(-4)}{2*1} = 2$$
$$y_{vert} = y(x_{vert}) = 2^{2} - 4 * 2 + 5 = 4 - 8 + 5 = 1$$
"""

# ╔═╡ 8e1588d8-1d7a-4151-b8a1-afc3c1298887
p3 = scatter!([2],[1],color=:green,label="vertice",ax=p2)

# ╔═╡ 09baa7ce-fc86-42a5-8130-3c8125c93f5b
md"""
Entonces ya tenemos los tres vertices del triangulo
"""

# ╔═╡ 653aa355-0c9d-4520-befd-d3f91678ffaa
begin
	# defino con decimales por una cuestion del programa
	v₁ = [0.0, 5.0]
	v₂ = [5.0, 10.0]
	v₃ = [2.0, 1.0]
	nothing
end

# ╔═╡ 1938fc99-5366-4bc0-a2b3-892d7281db93
md"""
Ahora para hallar el area vamos a utilizar el determinante entre dos vectores que definan el triangulo. Voy a considerar los vectores que van desde el vertice $v_{3}$ a los dos puntos de las intersecciones, abusando un poco la notacion $V_{1,3} = v_{1} - v_{3}$, $V_{1,3} = v_{2} - v_{3}$
"""

# ╔═╡ 0f723758-c34e-4a3b-ad72-853cc23b29fc
m = [v₁-v₃ v₂-v₃]

# ╔═╡ 253ae086-b3b0-4243-b05d-4e575166aa53
det(m)

# ╔═╡ 9003d85b-eaf0-44f9-b2b7-1dd990e4fd76
md"""
El determinante es negativo porque estamos tomando como primer vector el que est'a m'as a la izquierda. Si invertimos el orden en el que armamos la matriz, el determinante va a dar positivo, estamos alternando dos columnas :)

En conclusion el a'rea es el valor absoluto del determinante dividido dos, entonces tenemos que

``area =`` $(abs(det(m))/2)
"""

# ╔═╡ c18c2f4b-1e5f-463f-8464-0a6387896efc
md"""
## Ejercicio 2.

Sean $\pi : x + 2y + 2z = 12$ y $P = (1, 0, 1)$.

a) Hallar el punto de π que se encuentra a menor distancia del punto P y calcular la distancia
de $P$ al plano $\pi$.

b) Hallar el volumen del tetraedro (pir´amide de base triangular) definido por P y las intersecciones del plano π con los ejes coordenados. (Recuerde que el volumen del tetraedo es un
sexto del volumen del paralelep´ıdedo)
"""

# ╔═╡ 0f3486cc-8de9-452c-a0a1-b8c5277c1592
n = [1, 2, 2]

# ╔═╡ f4ee2c3e-7a1b-4d4a-99cf-0dda3ae572d7
P = [1,0,1]

# ╔═╡ cf0c4904-6b99-4af7-8aee-d03593bc2091
L₂ = λ*n + P

# ╔═╡ e86f3da1-ab64-4582-9fc7-8f12966c318f
π(v) = n ⋅ v - 12

# ╔═╡ 84f78af9-2866-404e-a121-3e3c6afe617b
# SymMatrix esta indexada desde 0 :(
π(L₂)

# ╔═╡ 9bf041fa-c284-4425-831c-2d8a207e54e2
λₛₒₗ = solve(π(L₂))[1]

# ╔═╡ 512fadab-9352-4d45-b73e-d754b30114e4
Q = subs.(L₂,λ => λₛₒₗ)

# ╔═╡ 6612a222-06c1-47a8-b024-146ca8caea08
md"""
Un poco de sanity check, comprobamos que $Q \in \pi$
"""

# ╔═╡ 604e2572-57d6-4076-b4bc-35adcceb7994
π(Q)

# ╔═╡ ef868e86-0a6a-47fb-b2a5-d9190cd03c0a
md"""
Con esto ya podemos calcular la distancia entre $P$ y $Q$
"""

# ╔═╡ 74bf0f00-bc45-4d26-9c61-f164de3d21e0
md"""
``dist(P,Q) = \vert P- Q \vert =`` $(norm(P - Q))
"""

# ╔═╡ 50432a00-fdbe-448e-a6c1-89b1d01d435d
md"""
#### b) Buscamos la interseccion de $\pi$ con los ejes coordenados
"""

# ╔═╡ 4bf801a5-b443-46d1-a110-ab8096527116
md"""
Primero definimos el vector can'onico en una direccio'on
"""

# ╔═╡ 2136e05b-bef2-4dc8-8723-3fbb8360cba0
e₁ = [1,0,0]

# ╔═╡ 5532b807-94e2-4ff2-9b65-41d1423863c6
md"""
Como queremos encontrar la intersecci'on entre el eje y el plano, definimos la recta que sigue al eje coordenado como
"""

# ╔═╡ 904d25d1-2244-4f76-ac1c-b1fef76ff41a
λ * e₁

# ╔═╡ 2ba19d1c-f22e-47c8-b81c-979e0edd7348
md"""
Cuando lo evaluamos en la definici'on del plano tenemos que la interseccio'n occurre cuando la siguiente es igual a $0$
"""

# ╔═╡ 9eeb302c-a277-437d-9e95-345b72d1a3a4
π(λ * e₁)

# ╔═╡ 15949e72-93cf-4aa6-8524-5b55fe9eb229
md"""
Y eso ocurre cuando ``\lambda =`` $(solve(π(λ * e₁)))
"""

# ╔═╡ 6935e6f6-e926-4766-89a4-8d86408cc15d
interseccion_con_π(v) = solve(π(λ * v))[1]

# ╔═╡ aed97c10-bcae-4552-b522-afb0a6073c7a
md"""
Extendemos el c'alculo de la intersecci'on del plano con los otros dos ejes obteniendo los siguentes puntos
"""

# ╔═╡ 760596e6-3937-4024-b3b4-7bfae97c4a4f
begin
	e₂ = [0,1,0]
	e₃ = [0,0,1]
	x₁ = interseccion_con_π(e₁) * e₁
	x₂ = interseccion_con_π(e₂) * e₂
	x₃ = interseccion_con_π(e₃) * e₃
	x₁, x₂, x₃
end

# ╔═╡ 64d47104-23e7-492c-aa5e-c6eee7f6d10d
md"""
Para tener un poco m'as de intuici'on los graficamos
"""

# ╔═╡ ff31fe54-2f96-4e12-bf62-395dd29fa515
begin
	figura = scatter3d([P[1]],[P[2]],[P[3]],label="P",xlabel="x",ylabel="y",zlabel="z")
	for p in [x₁, x₂, x₃]
		scatter3d!([p[1]],[p[2]],[p[3]],label=false,ax=figura)
	end
	figura
end

# ╔═╡ 6867d0da-516e-4924-b3f3-be7bbe75d4fc
md"""
Para calcular el volumen primero consideramos los vectores que van de $P$ a cada uno de los tres puntos $V_{i}$ que encontramos antes. Para eso calculamos $V_{i} - P$, para cada uno de los ejes y los ordenamos a cada uno como una columna de una matriz de manera de que podamos calcular el determinante
"""

# ╔═╡ 9ca33e7d-aa40-4970-9072-bcf0f754ec04
M = [x₁ x₂ x₃] .- P

# ╔═╡ fe385398-3bd1-4662-8411-e2c9beda9a9f
md"""
Que tiene el determinante igual a
"""

# ╔═╡ 672d62df-7c33-4260-8c4f-a72e8a900bd3
det(M)

# ╔═╡ c389944d-1d91-44aa-a28d-d0848bb10228
md"""
Usando la ayuda del enunciado tenemos que el volumen del tetaedro es ``\frac{\vert det(M)\vert}{6} = `` $(det(M)/6)
"""

# ╔═╡ e7d5a0e0-0b6b-4012-88cd-7e50283fb721
md"""
### Ejercicio 3.

Para $\alpha \in \mathbb{R}$ considere la matriz $A \in \mathbb{R}^{3×3}$ dada por

$\left[
\begin{array}{rrr}
 1       & 0          & \alpha     \\
 \alpha  & \alpha - 1 & \alpha^{2} \\
 0       & 1          & \alpha - 2
\end{array}\right]$

1. Halle todos los valores de $\alpha$ para los cuales $A^{2021} − A^{2022}$ no resulte inversible.

2. Ponga $\alpha = 2$. Utilice el m´etodo de Gauss para resolver

$$Ax = e_{1} + e_{2} − e_{3}$$

`INDICANDO TODAS LAS OPERACIONES DE FILAS QUE UTILIZA. Exhiba la solucien forma param´etrica y la matriz escalonada reducida del sistema; se˜nale el rango de
A.`
"""

# ╔═╡ fc2349c9-fb1f-4ce3-94fc-adb98f3170b8
# Definimos el simbolo
@syms α

# ╔═╡ 30e16586-b504-4332-9f6c-cdeb3f9c686c
A = [1 0 α;
	α α-1 α^2;
	0 1 α-2]

# ╔═╡ 2f8356bc-311a-4826-861e-4852ed01bca1
md"""
El ejercicio nos pide que calculemos cuando la siguiente matriz no es inversible $A^{2021} − A^{2022}$, lo cual parece una locura. Pero recordemos que una manera de evaluar si una matriz es inversible es a partir del determinante. Y la matriz va a ser inversible en el caso de que su determinante sea distinto de 0
"""

# ╔═╡ 45d7938f-a9ff-4a21-b0f8-74a7b69e1a3d
md"""
$\begin{align}
det(A^{2021} − A^{2022}) &= det( A^{2021}*(I-A)  ) \\
                         &= det( A^{2021} ) * det(I-A)\\
                         &= det( A )^{2021} * det(I-A)
\end{align}$
"""

# ╔═╡ af547106-640f-4d89-b96d-b1d625822697
I - A

# ╔═╡ a2779834-7808-4bad-a97a-bc90dba2adc7
determinante = det(A)^2021 * det(I-A)

# ╔═╡ d9ef00bd-1ac5-4406-9cb7-ae9d90d2387e
md"""
El determinante se anula en el caso de que cualquiera de los terminos sea igual a cero, entonces tenemos que
"""

# ╔═╡ 76c7aed0-4bac-4a03-8a07-00583c571c52
solve(det(A))

# ╔═╡ 180d6fe0-baeb-4c05-bfc9-213a6f1ec07c
solve(det(I-A))

# ╔═╡ a7da009c-b867-46d2-84a2-087287521aa8
md"""
Entonces es la matriz no es inversible si $\alpha \in \{0,1,2\}$
"""

# ╔═╡ 5522536b-a0df-41f8-8525-856923f49104
md"""
Para resolver el punto b), reemplazamos $\alpha = 2$. Notemos que es uno de los valores en los que el la matriz no era inversible, eso nos esta diciendo que el sistema no est'a completamete determinado.
"""

# ╔═╡ 337404a1-514b-4f40-9aa7-738d0c07d34f
Aₛ = subs.(A, α=>2)

# ╔═╡ 7dcb7519-f6cc-47b6-bbf2-1896a0053a85
md"""
Resolviendo el sistema por el metodo de Gauss-Jordan

$$A x = (1,1,-1)$$
"""

# ╔═╡ dd6b45de-fa40-4697-a372-d7f6a75bc704
md"""
Llegamos a que la solucion es el siguiente vector
"""

# ╔═╡ e052a4ad-90a0-4f7f-a1e7-a35b92270d74
Aₛ\[1,1,-1]

# ╔═╡ 65dcc377-8278-4c08-867b-012065989b1f
md"""
Que representa la recta

``
L_{s} = λ (-2,0,1) + (1,-1,0)
``
"""

# ╔═╡ c2c157cb-29fa-488b-a5dd-caef9e392c6b
md"""
### Ejercicio 4.

a) Sean $u, v \in \mathbb{R}^{2}$ dos vectores tales que $det(u, v) = 3$.
Hallar el ´area del paralelogramo determinado por los vectores $2u + 3v$ y $u − 4v$.

b) Decidir si la siguiente afirmaci´on es verdadera o falsa. Si es verdadera, dar una demostraci´on. Si es falsa, dar un contraejemplo.

Sean $L_{1}$ y $L_{2}$ son dos rectas en $R^{3}$

Si $L_{1} \cap L_{2} = \emptyset$, entonces $L_{1}$ y $L_{2}$ son paralelas.
"""

# ╔═╡ 1dd55ddd-2c09-4d8c-83d3-442ebcf0d579
# definimos los vectores simbolicos
begin
	@syms u[1:2] v[1:2]
end

# ╔═╡ 0bd81932-75a6-4b74-a264-bb16ae798a44
det([u v])

# ╔═╡ c3ca6592-a758-40fc-96c5-e8609071697f
det([2u+3v u-4v])

# ╔═╡ 279a2774-a565-4ffa-9577-b2e987abed37
simplify(det([2u+3v u-4v]) / det([u v]))

# ╔═╡ 1dd28947-9fa2-42bc-9666-6e1b5e12a147
md"""
En el punto **b)** La afirmación es **falsa**, pues pueden existir dos rectas que no sean paralelas y no tengan intersección

Por ejemplo, si tomamos:

$$L_{a} = \lambda_{a} (1,0,0) + (0,0,0)$$

$$L_{b} = \lambda_{b} (0,1,0) + (0,0,1)$$
"""

# ╔═╡ 2b0dc6fe-ae32-48c9-a400-ad5fb8fbef4e
Lₐ(λ) = λ*[1,0,0]

# ╔═╡ 40420c6d-122b-454d-9954-8ec29765f3f2
Lₚ(λ) = λ*[0,1,0] + [0,0,1]

# ╔═╡ be180c4f-177b-41a7-83d3-5a0220c9ad0e
begin
t = -1:1
# scatter3d(,)
end

# ╔═╡ 067ffcf8-a398-4e50-bcf7-5425906f4a87
collect(map(Array,zip(Lₐ.(t)...)))

# ╔═╡ a1ce3aac-6e19-4de0-bcd5-4e86b26aab71
let
x,y,z =	collect(zip(Lₐ.(t)...))
# plot3d([x],[y],[z],xlabel="x",ylabel="y",zlabel="z")
end

# ╔═╡ 3cf6ffc2-0f08-41f7-adaa-9fa22f585881
# Para referencia
repr("text/latex",A);

# ╔═╡ 00000000-0000-0000-0000-000000000001
PLUTO_PROJECT_TOML_CONTENTS = """
[deps]
LaTeXStrings = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
PlotlyJS = "f0f68f2c-4968-5e81-91da-67840de0976a"
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
SymPy = "24249f21-da20-56a4-8eb1-6a02cf4ae2e6"

[compat]
LaTeXStrings = "~1.2.1"
PlotlyJS = "~0.18.7"
Plots = "~1.22.3"
SymPy = "~1.0.52"
"""

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

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

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

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

[[AssetRegistry]]
deps = ["Distributed", "JSON", "Pidfile", "SHA", "Test"]
git-tree-sha1 = "b25e88db7944f98789130d7b503276bc34bc098e"
uuid = "bf4720bc-e11a-5d0c-854e-bdca1663c893"
version = "0.1.0"

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

[[BinDeps]]
deps = ["Libdl", "Pkg", "SHA", "URIParser", "Unicode"]
git-tree-sha1 = "1289b57e8cf019aede076edab0587eb9644175bd"
uuid = "9e28174c-4ba2-5203-b857-d8d62c4213ee"
version = "1.0.2"

[[Blink]]
deps = ["Base64", "BinDeps", "Distributed", "JSExpr", "JSON", "Lazy", "Logging", "MacroTools", "Mustache", "Mux", "Reexport", "Sockets", "WebIO", "WebSockets"]
git-tree-sha1 = "08d0b679fd7caa49e2bca9214b131289e19808c0"
uuid = "ad839575-38b3-5650-b840-f874b8c74a25"
version = "0.12.5"

[[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 = "e8a30e8019a512e4b6c56ccebc065026624660e8"
uuid = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4"
version = "1.7.0"

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

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

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

[[CommonEq]]
git-tree-sha1 = "d1beba82ceee6dc0fce8cb6b80bf600bbde66381"
uuid = "3709ef60-1bee-4518-9f2f-acd86f176c50"
version = "0.2.0"

[[CommonSolve]]
git-tree-sha1 = "68a0743f578349ada8bc911a5cbd5a2ef6ed6d1f"
uuid = "38540f10-b2f7-11e9-35d8-d573e4eb0ff2"
version = "0.2.0"

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

[[Conda]]
deps = ["JSON", "VersionParsing"]
git-tree-sha1 = "299304989a5e6473d985212c28928899c74e9421"
uuid = "8f4d0f93-b110-5947-807f-2305c1781a2d"
version = "1.5.2"

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

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

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

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

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

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

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

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

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

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

[[FunctionalCollections]]
deps = ["Test"]
git-tree-sha1 = "04cb9cfaa6ba5311973994fe3496ddec19b6292a"
uuid = "de31a74c-ac4f-5751-b3fd-e18cd04993ca"
version = "0.5.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"

[[Hiccup]]
deps = ["MacroTools", "Test"]
git-tree-sha1 = "6187bb2d5fcbb2007c39e7ac53308b0d371124bd"
uuid = "9fb69e20-1954-56bb-a84f-559cc56a8ff7"
version = "0.2.2"

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

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

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

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

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

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

[[JSExpr]]
deps = ["JSON", "MacroTools", "Observables", "WebIO"]
git-tree-sha1 = "bd6c034156b1e7295450a219c4340e32e50b08b1"
uuid = "97c1335a-c9c5-57fe-bc5d-ec35cebe8660"
version = "0.5.3"

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

[[Kaleido_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "2ef87eeaa28713cb010f9fb0be288b6c1a4ecd53"
uuid = "f7e6163d-2fa5-5f23-b69c-1db539e41963"
version = "0.1.0+0"

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

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

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

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

[[Lazy]]
deps = ["MacroTools"]
git-tree-sha1 = "1370f8202dac30758f3c345f9909b97f53d87d3f"
uuid = "50d2b5c4-7a5e-59d5-8109-a42b560f39c0"
version = "0.15.1"

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

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

[[Mustache]]
deps = ["Printf", "Tables"]
git-tree-sha1 = "36995ef0d532fe08119d70b2365b7b03d4e00f48"
uuid = "ffc61752-8dc7-55ee-8c37-f3e9cdd09e70"
version = "1.0.10"

[[Mux]]
deps = ["AssetRegistry", "Base64", "HTTP", "Hiccup", "Pkg", "Sockets", "WebSockets"]
git-tree-sha1 = "82dfb2cead9895e10ee1b0ca37a01088456c4364"
uuid = "a975b10e-0019-58db-a62f-e48ff68538c9"
version = "0.7.6"

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

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

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

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

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

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

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

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

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

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

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

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

[[Pidfile]]
deps = ["FileWatching", "Test"]
git-tree-sha1 = "1be8660b2064893cd2dae4bd004b589278e4440d"
uuid = "fa939f87-e72e-5be4-a000-7fc836dbe307"
version = "1.2.0"

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

[[PlotlyBase]]
deps = ["ColorSchemes", "Dates", "DelimitedFiles", "DocStringExtensions", "JSON", "LaTeXStrings", "Logging", "Parameters", "Pkg", "REPL", "Requires", "Statistics", "UUIDs"]
git-tree-sha1 = "180d744848ba316a3d0fdf4dbd34b77c7242963a"
uuid = "a03496cd-edff-5a9b-9e67-9cda94a718b5"
version = "0.8.18"

[[PlotlyJS]]
deps = ["Base64", "Blink", "DelimitedFiles", "JSExpr", "JSON", "Kaleido_jll", "Markdown", "Pkg", "PlotlyBase", "REPL", "Reexport", "Requires", "WebIO"]
git-tree-sha1 = "ec6bc7270269be2d565b272116ca74ca2f8bd9ab"
uuid = "f0f68f2c-4968-5e81-91da-67840de0976a"
version = "0.18.7"

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

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

[[PyCall]]
deps = ["Conda", "Dates", "Libdl", "LinearAlgebra", "MacroTools", "Serialization", "VersionParsing"]
git-tree-sha1 = "169bb8ea6b1b143c5cf57df6d34d022a7b60c6db"
uuid = "438e738f-606a-5dbb-bf0a-cddfbfd45ab0"
version = "1.92.3"

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

[[SymPy]]
deps = ["CommonEq", "CommonSolve", "LinearAlgebra", "Markdown", "PyCall", "RecipesBase", "SpecialFunctions"]
git-tree-sha1 = "1ef257ecbcab8058595a68ca36a6844b41babcbd"
uuid = "24249f21-da20-56a4-8eb1-6a02cf4ae2e6"
version = "1.0.52"

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

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

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

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

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

[[URIParser]]
deps = ["Unicode"]
git-tree-sha1 = "53a9f49546b8d2dd2e688d216421d050c9a31d0d"
uuid = "30578b45-9adc-5946-b283-645ec420af67"
version = "0.4.1"

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

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

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

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

[[VersionParsing]]
git-tree-sha1 = "80229be1f670524750d905f8fc8148e5a8c4537f"
uuid = "81def892-9a0e-5fdd-b105-ffc91e053289"
version = "1.2.0"

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

[[WebIO]]
deps = ["AssetRegistry", "Base64", "Distributed", "FunctionalCollections", "JSON", "Logging", "Observables", "Pkg", "Random", "Requires", "Sockets", "UUIDs", "WebSockets", "Widgets"]
git-tree-sha1 = "5fe32e4086d49f7ab9b087296742859f3ae6d62a"
uuid = "0f1e0344-ec1d-5b48-a673-e5cf874b6c29"
version = "0.8.16"

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

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

[[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:
# ╠═e8595a60-222f-11ec-0595-c9717ac06507
# ╟─431922d9-e97a-4300-a482-2d458b66c67a
# ╟─85cf0b95-4446-4e84-a6c3-f9d3fcf4cd61
# ╟─53b8d020-b4a9-4c74-b0cc-dba03b6edbd3
# ╟─4257606e-3eff-4124-aad3-517998f97d24
# ╠═61b1bf99-507b-4379-b537-86ce040f1418
# ╟─d17a8914-6ecf-4606-a664-7242d0f8f06c
# ╠═2056778d-12f0-4325-920f-f6c7e453918b
# ╟─4f6feaf8-5322-4afc-b31d-211761e222c2
# ╟─f676a35a-c61e-4547-b176-211dd3dc647b
# ╟─d8e3eda0-cada-4047-81cc-137a54e1b5e6
# ╟─897cfd67-1be7-40e4-87d9-c11bc9de55d4
# ╟─67e7f9f5-6e78-4579-97f7-7c0349b39afd
# ╟─4d6550b8-4e9d-4437-84bd-d978e33bfdb8
# ╠═997c5ff7-ac5d-484f-bcdf-a89194d112ee
# ╟─19f1f8b8-0f5c-4388-b550-f4cdd7619311
# ╠═8e1588d8-1d7a-4151-b8a1-afc3c1298887
# ╟─09baa7ce-fc86-42a5-8130-3c8125c93f5b
# ╠═653aa355-0c9d-4520-befd-d3f91678ffaa
# ╟─1938fc99-5366-4bc0-a2b3-892d7281db93
# ╠═0f723758-c34e-4a3b-ad72-853cc23b29fc
# ╠═253ae086-b3b0-4243-b05d-4e575166aa53
# ╟─9003d85b-eaf0-44f9-b2b7-1dd990e4fd76
# ╟─c18c2f4b-1e5f-463f-8464-0a6387896efc
# ╠═0f3486cc-8de9-452c-a0a1-b8c5277c1592
# ╠═f4ee2c3e-7a1b-4d4a-99cf-0dda3ae572d7
# ╠═cf0c4904-6b99-4af7-8aee-d03593bc2091
# ╠═e86f3da1-ab64-4582-9fc7-8f12966c318f
# ╟─84f78af9-2866-404e-a121-3e3c6afe617b
# ╠═9bf041fa-c284-4425-831c-2d8a207e54e2
# ╠═512fadab-9352-4d45-b73e-d754b30114e4
# ╟─6612a222-06c1-47a8-b024-146ca8caea08
# ╠═604e2572-57d6-4076-b4bc-35adcceb7994
# ╟─ef868e86-0a6a-47fb-b2a5-d9190cd03c0a
# ╟─74bf0f00-bc45-4d26-9c61-f164de3d21e0
# ╟─50432a00-fdbe-448e-a6c1-89b1d01d435d
# ╟─4bf801a5-b443-46d1-a110-ab8096527116
# ╠═2136e05b-bef2-4dc8-8723-3fbb8360cba0
# ╟─5532b807-94e2-4ff2-9b65-41d1423863c6
# ╠═904d25d1-2244-4f76-ac1c-b1fef76ff41a
# ╟─2ba19d1c-f22e-47c8-b81c-979e0edd7348
# ╠═9eeb302c-a277-437d-9e95-345b72d1a3a4
# ╟─15949e72-93cf-4aa6-8524-5b55fe9eb229
# ╟─6935e6f6-e926-4766-89a4-8d86408cc15d
# ╟─aed97c10-bcae-4552-b522-afb0a6073c7a
# ╟─760596e6-3937-4024-b3b4-7bfae97c4a4f
# ╟─64d47104-23e7-492c-aa5e-c6eee7f6d10d
# ╟─ff31fe54-2f96-4e12-bf62-395dd29fa515
# ╟─6867d0da-516e-4924-b3f3-be7bbe75d4fc
# ╟─9ca33e7d-aa40-4970-9072-bcf0f754ec04
# ╟─fe385398-3bd1-4662-8411-e2c9beda9a9f
# ╟─672d62df-7c33-4260-8c4f-a72e8a900bd3
# ╟─c389944d-1d91-44aa-a28d-d0848bb10228
# ╟─e7d5a0e0-0b6b-4012-88cd-7e50283fb721
# ╟─fc2349c9-fb1f-4ce3-94fc-adb98f3170b8
# ╠═30e16586-b504-4332-9f6c-cdeb3f9c686c
# ╟─2f8356bc-311a-4826-861e-4852ed01bca1
# ╟─45d7938f-a9ff-4a21-b0f8-74a7b69e1a3d
# ╠═af547106-640f-4d89-b96d-b1d625822697
# ╠═a2779834-7808-4bad-a97a-bc90dba2adc7
# ╟─d9ef00bd-1ac5-4406-9cb7-ae9d90d2387e
# ╠═76c7aed0-4bac-4a03-8a07-00583c571c52
# ╠═180d6fe0-baeb-4c05-bfc9-213a6f1ec07c
# ╟─a7da009c-b867-46d2-84a2-087287521aa8
# ╟─5522536b-a0df-41f8-8525-856923f49104
# ╠═337404a1-514b-4f40-9aa7-738d0c07d34f
# ╟─7dcb7519-f6cc-47b6-bbf2-1896a0053a85
# ╟─dd6b45de-fa40-4697-a372-d7f6a75bc704
# ╟─e052a4ad-90a0-4f7f-a1e7-a35b92270d74
# ╟─65dcc377-8278-4c08-867b-012065989b1f
# ╟─c2c157cb-29fa-488b-a5dd-caef9e392c6b
# ╟─1dd55ddd-2c09-4d8c-83d3-442ebcf0d579
# ╠═0bd81932-75a6-4b74-a264-bb16ae798a44
# ╠═c3ca6592-a758-40fc-96c5-e8609071697f
# ╠═279a2774-a565-4ffa-9577-b2e987abed37
# ╟─1dd28947-9fa2-42bc-9666-6e1b5e12a147
# ╠═2b0dc6fe-ae32-48c9-a400-ad5fb8fbef4e
# ╠═40420c6d-122b-454d-9954-8ec29765f3f2
# ╠═be180c4f-177b-41a7-83d3-5a0220c9ad0e
# ╠═067ffcf8-a398-4e50-bcf7-5425906f4a87
# ╠═a1ce3aac-6e19-4de0-bcd5-4e86b26aab71
# ╟─3cf6ffc2-0f08-41f7-adaa-9fa22f585881
# ╟─00000000-0000-0000-0000-000000000001
# ╟─00000000-0000-0000-0000-000000000002


# Seasonality and unobserved components
# models: an overview
# Andrew Harvey
# December 4, 2006

# Are Seasonal Patterns Constant Over Time?
# A Test for Seasonal Stability
# Fabio CANOVA
# Bruce E. HANSEN
# Journal of Business & Economic Statistics, July 1995, Vol. 13, No. 3

λ(j::Int, s::Int) = (2*pi*j)/s

const CRIT_VALS_05 = [0.470, 0.749, 1.01, 1.24, 1.47, 1.68, 1.9, 2.11, 2.32, 2.54, 2.75, 2.96]

function crit_val_05_generalized_von_mises_distribution(s::Int)
    return s > 12 ? 0.269 * s^0.928 : CRIT_VALS_05[s]
end

function π_statistic(ε::Vector{Fl}, σ::Fl, T::Int) where Fl
    return (T^(-2)*σ^(-2))*
            sum(
                sum(ε[i]*(-1)^(i) for i = 1:t) for t = 1:T
            )
end

function std_statistic(ε::Vector{Fl}, σ::Fl, T::Int, s::Int, j::Int) where Fl
    return (2*T^(-2)*σ^(-2))*
            sum(
                sum(ε[i]*cos(λ(j, s)*i) for i = 1:t)^2
                +
                sum(ε[i]*sin(λ(j, s)*i) for i = 1:t)^2 for t = 1:T
            )
end

function test_statistic(ε::Vector{Fl}, σ::Fl, T::Int, s::Int) where Fl
    # joint statistical test for all frequencies in
    # trigonometric pattern
    ω = 0
    # for each frequency in the trigonometric pattern
    for j = 1:floor(Int, s/2)
        ω += j == s/2 ? π_statistic(ε, σ, T) : std_statistic(ε, σ, T, s, j)
    end
    return ω
end

function build_std(T::Int, s::Int, j::Int)
    # add cossine and sine with frequency (2*pi*j)/s
    # to exogenous matrix X
    return hcat(cos.(λ(j, s)*(1:T)), sin.(λ(j, s)*(1:T)))
end

function build_π(T::Int, s::Int, j::Int)
    # when j = s/2
    # add cossine with frequency pi and intercept
    # to exogenous matrix X
    return hcat(cos.(λ(j, s)*(1:T)), ones(T))
end

function build_X(s::Int, T::Int, Fl::Type)
    # create the exogenous matrix
    X = Array{Fl, 2}(undef, T, s)
    # for each frequency in the trigonometric pattern
    for j = 1:floor(Int, s/2)
        X[:,[2*j-1, 2*j]] = j == s/2 ? build_π(T, s, j) : build_std(T, s, j)
    end
    return X
end

function seasonal_regression(y::Vector{Fl}, s::Int, T::Int) where Fl
    X = build_X(s, T, Fl)
    return y - X * (X \ y)
end

function seasonal_stationarity_test(y::Vector{Fl}, s::Int) where Fl
    T  = length(y)
    ε  = seasonal_regression(y, s, T)
    σ  = std(ε)
    ω  = test_statistic(ε, σ, T, s)
    crit_val = crit_val_05_generalized_von_mises_distribution(s)
    res = ω - crit_val
    res > 0 ? println("Rejected seasonal stationarity at 5% significance level"*
            " Test Statistic: $ω - Critical Value: $crit_val") :
              println("Didn't reject seasonal stationarity at 5% significance level"*
            " Test Statistic: $ω - Critical Value: $crit_val")
    return res > 0 ? false : true
end


using LinearAlgebra

function decomp_qr(A)

    (m,n) = size(A);
    R = zeros(n,n);
    Q = zeros(m,n);

    for j = 1:n

        soma = zeros(m,1);

        for i = 1 : j-1
            R[i,j] = Q[:,i]' * A[:,j];
            soma = soma + R[i,j]*Q[:,i];
        end

        v = A[:,j] - soma;
        R[j,j] = norm(v);
        Q[:,j] = v/R[j,j];

    end

    return (Q,R);

end


using Random, Distributions, Plots; pyplot()
Random.seed!(0)

lambda  = 1/4.5
expDist = Exponential(1/lambda)
n, N    = 10, 10^6

means     = Array{Float64}(undef, N)
variances = Array{Float64}(undef, N)

for i in 1:N
    data = rand(expDist,n)
    means[i] = mean(data)
    variances[i] = var(data)
end

println("Actual mean: ",mean(expDist),
		"\nMean of sample means: ",mean(means))
println("Actual variance: ",var(expDist),
	"\nMean of sample variances: ",mean(variances))

stephist(means, bins=200, c=:blue, normed=true,
	label="Histogram of Sample Means")
stephist!(variances, bins=600, c=:red, normed=true,
	label="Histogram of Sample Variances", xlims=(0,40), ylims=(0,0.4),
    	xlabel = "Statistic value", ylabel = "Density")


# Algorithm 2.8
# Approximate the root of a function by constructing a parabola
# between three points and intersect it with y = 0 to find the
# next approximation.

function muller(f, x0, x1, x2, tol, n0)
	h1 = x1 - x0
	h2 = x2 - x1
	δ1 = (f(x1) - f(x0))/h1
	δ2 = (f(x2) - f(x1))/h2
	d = (δ2 - δ1)/(h2 + h1)
	for i ∈ 3:n0
		b = δ2 + h2 * d
		D = (b^2 -4f(x2)d)^(1/2)
		E = 0
		if abs(b - D) < abs(b + D)
			E = b + D
		else
			E = b - D
		end
		h = (-2f(x2))/E
		p = x2 + h
		if abs(h) < tol
			return p
		end
		x0 = x1
		x1 = x2
		x2 = p
		h1 = x1 - x0
		h2 = x2 - x1
		δ1 = (f(x1) - f(x0))/h1
		δ2 = (f(x2) - f(x1))/h2
		d = (δ2 - δ1)/(h2 + h1)
	end
	throw(ArgumentError("Method failed"))
end

println(muller(x -> (16x^4 - 40x^3 + 5x^2 + 20x +6), 0.5, 1.0, 1.5, 10^-5, 50))


# p21.jl - eigenvalues of Mathieu operator -u_xx + 2qcos(2x)u
#         (compare p8.jl and p. 724 of Abramowitz & Stegun)

N = 42; h = 2*pi/N; x = h*(1:N);
D2 = toeplitz([-pi^2/(3*h^2)-1/6; @. -.5*(-1)^(1:N-1)/sin(h*(1:N-1)/2)^2]);
qq = 0:.2:15; data = [];
for q = qq;
    e = sort(eigvals(-D2 + 2*q*diagm(cos.(2*x))));
    data = [data; e[1:11]'];
end
clf(); subplot(1,2,1);
plot(qq,data[:,1:2:end],"b-");
plot(qq,data[:,2:2:end],"b--");
xlabel("q"); ylabel("λ");
axis([0,15,-24,32]); yticks(-24:4:32);


using Distributions

lRange = 1; uRange = 9;         unidd    = DiscreteUniform(lRange,uRange)
p = 0.8;                         bernd    = Bernoulli(p)
n = 20; p = 0.8;                 bind     = Binomial(n,p)
ps = [0.2,0.5,0.2,0.1];          catd     = Categorical(ps)
n=20; ps = [0.2,0.5,0.3];        multind  = Multinomial(n, ps)
p = 0.2;                         geomd    = Geometric(p)
sAv = 10; fAv = 20; nTrials = 3; hypgeomd = Hypergeometric(sAv, fAv, nTrials)
# hypergeometrical multivariate form not available
rate = 10;                       poissd   = Poisson(rate)
nSucc =10; p = 0.2;              pascd    = NegativeBinomial(nSucc,p) # Return the number of failures before n successes instead of total trials to n successes

lRange = 1; uRange = 9;          unicd    = Uniform(lRange,uRange)
rate = 2.5;                      expd     = Exponential(rate)
μ = 2; σsq = 5 ;                 normd    = Normal(μ,sqrt(σsq))  # parametrised by sd!
n = 5; rate = 2.5;               erld     = Erlang(n,rate)
μ = 2.5; σ = 2.5;                cuachyd  = Cauchy(μ, σ)
df = 3;                          chisqd   = Chisq(df)
df = 4.5;                        td       = TDist(df)
df1 = 6 ; df2 = 7;               fd       = FDist(df1, df2)
shapeα = 2.5; shapeβ=4.5;        betad    = Beta(shapeα,shapeβ)
shapeα = 2.5; rateβ = 4.5;       gammad   = Gamma(shapeα,1/rateβ)

uvds = [unidd, bernd, bind, catd, geomd, hypgeomd, poissd, pascd,
        unicd, expd, normd, erld, cuachyd, chisqd,  td, fd, betad, gammad]
mvds = [multind]

x = 3
for d in uvds
    println("--------")
    println(d)
    println("Mean: $(mean(d))")
    println("Variance: $(var(d))")
    d != chisqd && println("Median: $(median(d))")
    println("Sample: $(rand(d))")
    println("PDF: $(pdf(d,x))")
    println("CDF: $(cdf(d,x))")
    println("20% quantile: $(quantile(d,0.2))")
end

xs = [3,12,5]
for d in mvds
    println("--------")
    println(d)
    println("Mean: $(mean(d))")
    println("Variance: $(var(d))")
    #println("Median: $(median(d))")
    println("Sample: $(rand(d))")
    println("PDF: $(pdf(d,xs))")
    #println("CDF: $(cdf(d,xs))")
    #println("20% quantile: $(quantile(d,0.2))")
end


#' ---
#' title: Problems on Graphs
#' ---

#' **Originally Contributed by**: Arpit Bhatia

#' In the mathematical discipline of graph theory, a number of problems can be solved by modelling them as optimization problems.
#' We will see some examples of such problems in the tutorial.
#' These problems are also sometimes referred to as combinatorial optimization problems as
#' they consist of finding an optimal object from a finite set of objects.

#' Let's first import all the packages we will be using for this tutorial.
#' We'll be plotting all the graphs we work with in this tutorial for which we will need some additional packages.

using JuMP
using GLPK
using GraphPlot
using LightGraphs
using Colors

#' ## Representing Graphs
#' For the purpose of this tutorial, we will represent graphs using adjacency matrices.
#' An adjacency matrix, sometimes also called the connection matrix, is a square matrix used to represent a finite graph.
#' Its rows and columns are labeled by the graph vertices,
#' with a 1 or 0 in position ($v_{i}$,$v_{j}$) according to whether $v_{i}$ and $v_{j}$ are adjacent or not.

#' ## Minimum Vertex Cover
#' Given a graph $G = (V, E)$, a vertex-cover $V' \subset V$ of $G$ is a collection of vertices such that
#' each edge in $E$ is incident to at least one of the vertices in $V'$.
#' The size of a vertex-cover $|V'|$ is the number of vertices present in the cover.
#' We wish to find the minimum vertex cover of $G$ i.e. a minimum size vertex cover.
#' We model this problem as an ILP by defining a decision variable $y_{v}$ for each vertex $v \in V$ and
#' a constraint for each edge $e \in E$ as follows:

#' $$
#' \begin{align*}
#' \min && \sum_{v \in V} y_{v} \\
#' s.t. && y_{u} + y_{v} \geq 1 && \forall \{u,v\} \in E \\
#' && y_{v} \in \{0,1\} && \forall v \in V
#' \end{align*}
#' $$

G = [
0 1 0 0 0 0;
1 0 1 1 0 0;
0 1 0 0 1 1;
0 1 0 0 1 0;
0 0 1 1 0 0;
0 0 1 0 0 0
]

g = SimpleGraph(G)

gplot(g)

#+

vertex_cover = Model(with_optimizer(GLPK.Optimizer))

@variable(vertex_cover, y[1:nv(g)], Bin)
@constraint(vertex_cover, [i = 1:nv(g), j = 1:nv(g); G[i,j] == 1], y[i] + y[j] >= 1)
@objective(vertex_cover, Min, sum(y))

optimize!(vertex_cover)
@show value.(y);

#+

membership = convert(Array{Int},value.(y)) # Change to Int
membership = membership + ones(Int, nv(g)) # Make the color groups one indexed
nodecolor = [colorant"red", colorant"blue"] # Blue to represent vertices in the cover
nodefillc = nodecolor[membership]
gplot(g, nodefillc = nodefillc)

#' ## Dominating Set
#' A dominating set in a graph $G = (V, E)$ is a set $S \subset V$ such that
#' for each vertex $v \in V$ either $v$ or one of its neighbour should be in $S$.
#' Note that for some vertex $u$, $u$ and its neighbour both can be present in $S$.
#' We wish to find the smallest dominating set for a graph.
#' We model this problem as an ILP by defining a decision variable $x_{v}$ for each vertex $v \in V$ along with
#' a constraint for its neighbourhood.

#' $$
#' \begin{align*}
#' \min && \sum_{v \in V} x_{v} \\
#' s.t. && \sum_{u \in N(v)}x_{u} \geq 1 && \forall v \in V \\
#' && x_{v} \in \{0,1\} && \forall v \in V
#' \end{align*}
#' $$

G = [
0 1 0 0 0 0 0 0 0 1 0 ;
1 0 1 0 0 0 0 0 0 0 1;
0 1 0 1 0 1 0 0 0 0 0;
0 0 1 0 1 0 0 0 0 0 0;
0 0 0 1 0 1 0 0 0 0 0;
0 0 1 0 1 0 1 0 0 0 0;
0 0 0 0 0 1 0 1 0 0 0;
0 0 0 0 0 0 1 0 1 0 1;
0 0 0 0 0 0 0 1 0 1 1;
1 0 0 0 0 0 0 0 1 0 1;
0 1 0 0 0 0 0 1 1 1 0
]

g = SimpleGraph(G)

gplot(g)

#+

dominating_set = Model(with_optimizer(GLPK.Optimizer))

@variable(dominating_set, x[1:nv(g)], Bin)
@constraint(dominating_set, [i = 1:nv(g)], sum(G[i,:] .* x) >= 1)
@objective(dominating_set, Min, sum(x))

optimize!(dominating_set)
@show value.(x);

#+

membership = convert(Array{Int},value.(x)) # Change to Int
membership = membership + ones(Int, nv(g)) # Make the color groups one indexed
nodecolor = [colorant"red", colorant"blue"] # Blue to represent vertices in the set
nodefillc = nodecolor[membership]
gplot(g, nodefillc = nodefillc)

#' ## Maximum Matching Problem
#' Given a graph $G = (V, E)$, a matching $M \subset E$ of $G$ is a collection of vertex disjoint edges.
#' The size of the matching $M$ is the number of edges present in $M$ i.e. $|M|$.
#' We wish to find the Maximum matching of $G$ i.e. a matching of maximum size.
#' We can solve this problem by modelling it as an integer linear program (ILP).
#' We define a decision variable $m_{e}$ for each edge $e \in E$ and a constraint for each vertex $u \in V$ as follows:

#' $$
#' \begin{align*}
#' \max && \sum_{e \in E} m_{e} \\
#' s.t. && \sum_{e \sim u} m_{e} \leq 1 && \forall u \in V \\
#' && m_{e} \in \{0,1\} && \forall e \in E
#' \end{align*}
#' $$

#' Let's now use JuMP to solve this problem for a sample graph.

G = [
0 0 0 0 1 0 0 0;
0 0 0 0 0 1 0 0;
0 0 0 0 0 0 1 0;
0 0 0 0 0 0 0 1;
1 0 0 0 0 1 0 1;
0 1 0 0 1 0 1 0;
0 0 1 0 0 1 0 1;
0 0 0 1 1 0 1 0;
]

g = SimpleGraph(G)

gplot(g)

#+

matching = Model(with_optimizer(GLPK.Optimizer))

@variable(matching, m[i = 1:nv(g), j = 1:nv(g)], Bin)
@constraint(matching, [i = 1:nv(g)], sum(m[i,:]) <= 1)
@constraint(matching, [i = 1:nv(g), j = 1:nv(g); G[i,j] == 0], m[i,j] == 0)
@constraint(matching, [i = 1:nv(g), j = 1:nv(g)], m[i,j] == m[j,i])
@objective(matching, Max, sum(m))

optimize!(matching)
@show value.(m);

#' The edges corresponding to the Matching are marked as one in the above matrix.

#' ## k-Coloring Problem
#' A k-coloring of a graph $G=(V,E)$ is a function $c: V \rightarrow \{1,2...k\}$ such that
#' $c(u) \neq c(v)$ for every edge $(u,v) \in E$. In other words, the numbers 1,2...k represent k colors,
#' and adjacent vertices must have different colours.
#' The goal of a graph coloring problem is to find a minimum number of colours needed to colour a graph.

#' We model this problem as an ILP by defining a variable decision variable $z_{i}$ for each colour we have available.
#' Given an upper bound $k$ on the number of colors needed,
#' we use $|V| \times k$ decision variables $c_{v,k}$ denoting if vertex $v$ is assigned color $k$.
#' Our model will become:

#' $$
#' \begin{align*}
#' \min && \sum_{i=1}^{k} z_{i} \\
#' s.t. && \sum_{i=1}^{k} c_{v,i} = 1 && \forall v \in V \\
#' && c_{u,i} + c_{v,i} \leq 1 && \forall (u,v) \in V, i \in \{1,2...k\} \\
#' && c_{v,i} \in \{0,1\} && \forall v \in V, i \in \{1,2...k\} \\
#' && z_{i} \in \{0,1\} && \forall i \in \{1,2...k\}
#' \end{align*}
#' $$

G = [
0 1 0 0 1 1 0 0 0 0;
1 0 1 0 0 0 1 0 0 0;
0 1 0 1 0 0 0 1 0 0;
0 0 1 0 1 0 0 0 1 0;
1 0 0 1 0 0 0 0 0 1;
1 0 0 0 0 0 1 0 0 1;
0 1 0 0 0 1 0 1 0 0;
0 0 1 0 0 0 1 0 1 0;
0 0 0 1 0 0 0 1 0 1;
0 0 0 0 1 1 0 0 1 0;
]

g = SimpleGraph(G)

gplot(g)

#+

k = nv(g)

k_colouring = Model(with_optimizer(GLPK.Optimizer))

@variable(k_colouring, z[1:k], Bin)
@variable(k_colouring, c[1:nv(g),1:k], Bin)
@constraint(k_colouring, [i = 1:nv(g)], sum(c[i,:]) == 1)
@constraint(k_colouring, [i = 1:nv(g), j = 1:nv(g), l = 1:k; G[i,j] == 1], c[i,l] + c[j,l] <= 1)
@constraint(k_colouring, [i = 1:nv(g), l = 1:k], c[i,l] <= z[l])

@objective(k_colouring, Min, sum(z))

optimize!(k_colouring)
@show value.(z);
@show value.(c);

#+

c = value.(c)
membership = zeros(nv(g))
for i in 1:nv(g)
    for j in 1:k
        if c[i,j] == 1
            membership[i] = j
            break
        end
    end
end
membership = convert(Array{Int},membership)

nodecolor = distinguishable_colors(nv(g), colorant"green")
nodefillc = nodecolor[membership]
gplot(g, nodefillc = nodefillc)


export Cubic, Spline, npatches, funk

# see http://mathworld.wolfram.com/CubicSpline.html

import Base: getindex, show

"""
`Cubic(a,b,c,d)` is a 3rd degree polynomial `a+bx+cx^2+dx^3`.
Use `f(x)` to evalue `f` at the value `x`.
"""
struct Cubic
    a::Number
    b::Number
    c::Number
    d::Number
end

(f::Cubic)(x::Number) = f.a + x * (f.b + x * (f.c + x * f.d))

"""
`f'` where `f` is a `Cubic` or `Spline` is the derivative
of `f`.
"""
Base.adjoint(f::Cubic) = Cubic(f.b, 2f.c, 3f.d, 0)

struct Spline
    patches::Array{Cubic,1}
    closed::Bool
end

is_closed(S::Spline) = S.closed

function Base.adjoint(S::Spline)
    plist = adjoint.(S.patches)
    return Spline(plist, S.closed)
end

"""
`Spline(vals,kind)` returns a cubic spline based on the values in `vals`.
The resulting spline `S` will have the property that `S(1)==y[1]`,
`S(2)==y[2]`, and so on up to `S(n)==y[n]` where `n` is the length of `y`.

+ If `kind` is `:open` then the second derivatives at the end points will be zero. (This is the default.)
+ If `kind` is `:closed` then we assume that we are interpolating a periodic function where `S(n+1)==S(1)`.
"""
function Spline(y::Array{T,1}, kind::Symbol = :open)::Spline where {T<:Number}
    n = length(y)
    if kind == :open
        @assert n > 2 "Open splines must have at least three points"
        return open_spline(y)
    end
    if kind == :closed
        @assert n > 3 "Closed splines must have at least four points"
        return closed_spline(y)
    end

    error("Spline type must be :open or :closed, not $kind")
end

function npatches(S::Spline)
    return length(S.patches)
end

function show(io::IO, S::Spline)
    adjective = is_closed(S) ? "Closed" : "Open"
    print(io, "$adjective spline with $(npatches(S)) patches")
end

getindex(S::Spline, idx::Int) = S.patches[idx]

"""
`funk(S)` converts the Spline `S` into a callable function
(e.g., that can be passed to `plot`).
"""
function funk(S)::Function
    return x -> S(x)
end

function (S::Spline)(x::Real)
    np = npatches(S)

    if is_closed(S)

        p = Int(floor(x))
        x = mod(x, np)
        p = Int(floor(x))

        if p == 0
            p = np
            x += np
        end
        f = S[p]
        return f(x - p)
    end

    # open spline
    p = Int(floor(x))
    if x < 1
        p = 1
    end

    if p > np
        p = np
    end
    f = S[p]
    return f(x - p)
end

function open_spline(y::Array{T,1})::Spline where {T<:Number}
    n = length(y)
    M = zeros(n, n)
    for i = 1:n
        M[i, i] = 4
    end
    M[1, 1] = 2
    M[n, n] = 2

    for i = 1:n-1
        M[i, i+1] = 1
        M[i+1, i] = 1
    end

    rhs = zeros(T, n)
    rhs[1] = 3 * (y[2] - y[1])
    for k = 2:n-1
        rhs[k] = 3 * (y[k+1] - y[k-1])
    end
    rhs[n] = 3 * (y[n] - y[n-1])

    D = M \ rhs

    a = zeros(Number, n - 1)
    b = zeros(Number, n - 1)
    c = zeros(Number, n - 1)
    d = zeros(Number, n - 1)

    for j = 1:n-1
        a[j] = y[j]
        b[j] = D[j]
        c[j] = 3 * (y[j+1] - y[j]) - 2D[j] - D[j+1]
        d[j] = 2 * (y[j] - y[j+1]) + D[j] + D[j+1]
    end
    return Spline([Cubic(a[i], b[i], c[i], d[i]) for i = 1:n-1], false)
end

function closed_spline(y::Array{T,1})::Spline where {T<:Number}
    yy = copy(y)
    n = length(y)
    prepend!(yy, y[end-3:end])
    append!(yy, y[1:4])

    S = open_spline(yy)

    plist = S.patches[5:end-3]
    return Spline(plist, true)

end


# Regression

## Auxiliary

function lreg_chkdims(X::AbstractMatrix, Y::AbstractVecOrMat, trans::Bool)
    mX, nX = size(X)
    dX = ifelse(trans, mX, nX)
    dY = ifelse(trans, nX, mX)
    size(Y, 1) == dY || throw(DimensionMismatch("Dimensions of X and Y mismatch."))
    return dX
end

lrsoltype(::AbstractVector{T}) where T = Vector{T}
lrsoltype(::AbstractMatrix{T}) where T = Matrix{T}

_vaug(X::AbstractMatrix{T}) where T = vcat(X, ones(T, 1, size(X,2)))::Matrix{T}
_haug(X::AbstractMatrix{T}) where T = hcat(X, ones(T, size(X,1), 1))::Matrix{T}

## Linear Least Square Regression

"""
    llsq(X, y; ...)

Solve the linear least square problem.

Here, `y` can be either a vector, or a matrix where each column is a response vector.

This function accepts two keyword arguments:

- `dims`: whether input observations are stored as rows (`1`) or columns (`2`). (default is `1`)
- `bias`: whether to include the bias term `b`. (default is `true`)

The function results the solution `a`. In particular, when `y` is a vector (matrix), `a` is also a vector (matrix). If `bias` is true, then the returned array is augmented as `[a; b]`.
"""
function llsq(X::AbstractMatrix{T}, Y::AbstractVecOrMat{T};
              trans::Bool=false, bias::Bool=true,
              dims::Union{Integer,Nothing}=nothing) where {T<:Real}
    if dims === nothing && trans
        Base.depwarn("`trans` argument is deprecated, use llsq(X, Y, dims=d) instead.", :trans)
        dims = 1
    end
    if dims == 2
        mX, nX = size(X)
        size(Y, 1) == nX || throw(DimensionMismatch("Dimensions of X and Y mismatch."))
        mX <= nX || error("mX <= nX is required when trans is false.")
    else
        mX, nX = size(X)
        size(Y, 1) == mX || throw(DimensionMismatch("Dimensions of X and Y mismatch."))
        mX >= nX || error("mX >= nX is required when trans is false.")
    end
    _ridge(X, Y, zero(T), dims == 2, bias)
end
llsq(x::AbstractVector{T}, y::AbstractVector{T}; kwargs...) where {T<:Real} =
    llsq(x[:,:], y; dims=1, kwargs...)

## Ridge Regression (Tikhonov regularization)

"""

    ridge(X, y, r; ...)

Solve the ridge regression problem.

Here, ``y`` can be either a vector, or a matrix where each column is a response vector.

The argument `r` gives the quadratic regularization matrix ``Q``, which can be in either of the following forms:

- `r` is a real scalar, then ``Q`` is considered to be `r * eye(n)`, where `n` is the dimension of `a`.
- `r` is a real vector, then ``Q`` is considered to be `diagm(r)`.
- `r` is a real symmetric matrix, then ``Q`` is simply considered to be `r`.

This function accepts two keyword arguments:

- `dims`: whether input observations are stored as rows (`1`) or columns (`2`). (default is `1`)
- `bias`: whether to include the bias term `b`. (default is `true`)

The function results the solution `a`. In particular, when `y` is a vector (matrix), `a` is also a vector (matrix). If `bias` is true, then the returned array is augmented as `[a; b]`.
"""
function ridge(X::AbstractMatrix{T}, Y::AbstractVecOrMat{T}, r::Union{Real, AbstractVecOrMat};
               trans::Bool=false, bias::Bool=true,
               dims::Union{Integer,Nothing}=nothing) where {T<:Real}
    if dims === nothing && trans
        Base.depwarn("`trans` argument is deprecated, use ridge(X, Y, r, dims=d) instead.", :trans)
        dims = 1
    end
    d = lreg_chkdims(X, Y, dims == 2)
    if isa(r, Real)
        r >= zero(r) || error("r must be non-negative.")
        r = convert(T <: AbstractFloat ? T : Float64, r)
    elseif isa(r, AbstractVector)
        length(r) == d || throw(DimensionMismatch("Incorrect length of r."))
    elseif isa(r, AbstractMatrix)
        size(r) == (d, d) || throw(DimensionMismatch("Incorrect size of r."))
    end
    _ridge(X, Y, r, dims == 2, bias)
end
ridge(x::AbstractVector{T}, y::AbstractVector{T}, r::Union{Real, AbstractVecOrMat};
      kwargs...) where {T<:Real} = ridge(x[:,:], y, r; dims=1, kwargs...)

### implementation

function _ridge(_X::AbstractMatrix{T}, _Y::AbstractVecOrMat{T},
                r::Union{Real, AbstractVecOrMat}, trans::Bool, bias::Bool) where {T<:Real}
    # convert integer data to Float64
    X = T <: AbstractFloat ? _X : convert(Array{Float64}, _X)
    Y = T <: AbstractFloat ? _Y : convert(Array{Float64}, _Y)
    if bias
        if trans
            X_ = _vaug(X)
            A = cholesky!(Hermitian(_ridge_reg!(X_ * transpose(X_), r, bias))) \ (X_ * Y)
        else
            X_ = _haug(X)
            A = cholesky!(Hermitian(_ridge_reg!(X_'X_, r, bias))) \ (X_'Y)
        end
    else
        if trans
            A = cholesky!(Hermitian(_ridge_reg!(X * X', r, bias))) \ (X * Y)
        else
            A = cholesky!(Hermitian(_ridge_reg!(X'X, r, bias))) \ (X'Y)
        end
    end
    return A::lrsoltype(Y)
end

function _ridge_reg!(Q::Matrix, r::Real, bias::Bool)
    if r > zero(r)
        n = size(Q, 1) - Int(bias)
        for i = 1:n
            @inbounds Q[i,i] += r
        end
    end
    return Q
end

function _ridge_reg!(Q::AbstractMatrix, r::AbstractVector, bias::Bool)
    n = size(Q, 1) - Int(bias)
    @assert length(r) == n
    for i = 1:n
        @inbounds Q[i,i] += r[i]
    end
    return Q
end

function _ridge_reg!(Q::AbstractMatrix, r::AbstractMatrix, bias::Bool)
    n = size(Q, 1) - Int(bias)
    @assert size(r) == (n, n)
    for j = 1:n, i = 1:n
        @inbounds Q[i,j] += r[i,j]
    end
    return Q
end

## Isotonic Regression

"""
    isotonic(x, y[, w])

Solve the isotonic regression problem using the pool adjacent violators algorithm[^1].

Here `x` is the regressor vector, `y` is response vector, and `w` is an optional
weights vector.

The function returns a prediction vector of the same size as the regressor vector `x`.
"""
function isotonic(x::AbstractVector{T}, y::AbstractVector{T},
                  w::AbstractVector{T} = ones(T, length(y))) where {T<:Real}
    n = length(x)
    n == length(y) || throw(DimensionMismatch("Dimensions of x and y mismatch."))

    idx = sortperm(x)

    # PVA algorithm
    J = map(i->(Float64(y[i]*w[i]), w[i], [i]), idx)
    i = 1
    B₀ = J[i]
    while i < length(J)
        B₊ = J[i+1]
        if B₀[1] <= B₊[1] # step 1
            B₀ = B₊
            i += 1
        else # step 2
            ww = B₀[2] + B₊[2]
            J[i] = ((B₀[1]*B₀[2]+B₊[1]*B₊[2])/ww, ww, append!(B₀[3], B₊[3]))
            deleteat!(J, i+1)
            B₀ = J[i]
            while i > 1 # step 2.1
                B₋ = J[i-1]
                if B₀[1] <= B₋[1]
                    ww = B₀[2] + B₋[2]
                    J[i] = ((B₀[1]*B₀[2]+B₋[1]*B₋[2])/ww, ww, append!(B₀[3], B₋[3]))
                    deleteat!(J, i-1)
                    i-=1
                else
                    break
                end
            end
        end
    end
    [y for (y,w,ii) in J for i in sort(ii)]
end


#=
Created on 09/01/2021 14:50:19
Last update: -

@author: Michiel Stock
michielfmstock@gmail.com

Implement Stirling's approximation for the factorial.
=#

using Plots

stirling(n) = n * log(n / exp(1)) + 0.5log(2π*n)

# here we use `m=:circle` to use the :circle symbols as a marker on our plots.

plot(1:10, n->log(factorial(big(n))), m=:circle, label="log-factorial")
xlabel!("n")
plot!(stirling, 1:10, m=:circle, label = "Stirling's approximation")


#   Unit classification.jl, part of PosDefManifold Package for julia language
#   v 0.1.3 - last update 7th of Mai 2019
#
#   MIT License
#   Copyright (c) 2019, Marco Congedo, CNRS, Grenobe, France:
#   https://sites.google.com/site/marcocongedo/home
#
#   DESCRIPTION
#   This Unit implements classification methods and related functions
#   useful in relation to the Riemannian Geometry on the manifold
#   of Symmetric Positive Definite (SPD) or Hermitian matrices.
#
#   CONTENT
#   1. Probability
# __________________________________________________________________

#  ------------------------
## 1. Probability
#  ------------------------

"""
    softmax(χ::Vector{T}) where T<:Real

 Given a real vector of ``k`` non-negative scores ``χ=c_1,...,c_k``,
 return the vector ``π=p_1,...,p_k`` of their
 [softmax](https://en.wikipedia.org/wiki/Softmax_function) probabilities,
 as per

 `` p_i=\\frac{\\textrm{e}^{c_i}}{\\sum_{i=1}^{k}\\textrm{e}^{c_i}} ``.

 ## Examples
    χ=[1.0, 2.3, 0.4, 5.0]
    π=softmax(χ)

"""
softmax(χ::Vector{T}) where T<:Real = exp.(χ) ./ 𝚺(exp.(χ))


export hermite, HermiteBasis

"""
    y = hermite(n::Int, x::Real)

Evaluate the ``n``-degree Hermite polynomial at ``x``.

```math
n!\\sum_{m=0}^{[n/2]} \\frac{(-1)^m}{m!(n-2m)!} \\frac{x^{n-2m}}{2^m}
```
"""
function hermite(n::Int, x::Real)
  if n == 0
    1
  else
    N = Int(floor(n/2))
    fac = factorial
    s = reduce((s, m) -> s + (-1)^m * x^(n-2m) / (fac(m) * fac(n-2m) * 2^m), 0:N; init=0)
    fac(n) * s
  end
end

"""
    basis = HermiteBasis(vectors)

Encode an ordered basis (`basis`) of polynomials ``(ψ_j)_{j\\in \\mathcal J_i}`` of the form

```math
ψ_{(j_1,\\ldots,j_n)}(θ_1, \\ldots, θ_n) = \\prod_{k=1}^n φ_{j_k}(θ_k),
```

where ``φ_{j_k}`` is the Hermite polynomial of degree ``j_k``.
The indices ``\\mathcal J_i`` of the polynomials is provided by `vectors` in the natural
way. For example,

```julia
HermiteBasis([[1, 2, 3], [0, 1, 1, 2]])
```

will encode the ordered basis ``( ψ_{(1,2,3)}, ψ_{(0,1,1,2)} )``.
"""
struct HermiteBasis <: OrderedBasis
  vectors::AbstractArray{<:AbstractArray{<:Int}}
  function HermiteBasis(vectors::AbstractArray{<:AbstractArray{<:Int}})
    if all(vec -> all(x -> x >= 0, vec), vectors)
      new(vectors)
    else
      throw(
        ArgumentError("A `HermiteBasis` must only include vectors with positive components.")
       )
    end
  end
end

Base.length(basis::HermiteBasis) = length(basis.vectors)

function Base.union(basis::HermiteBasis, bases...)
  HermiteBasis(union(basis.vectors, map(b -> b.vectors, bases)...))
end

function (basis::HermiteBasis)(theta::AbstractArray{<:Real})
  if any(vec -> length(vec) > length(theta), basis.vectors)
    throw(ArgumentError("$theta is not in the domain of $basis"))
  else
    map(vec -> prod(map(p -> hermite(p[2], theta[p[1]]), enumerate(vec))), basis.vectors)
  end
end


# # Gaussian process prior samples
#
# The kernels defined in this package can also be used to specify the
# covariance of a Gaussian process prior.
# A Gaussian process (GP) is defined by its mean function $m(\cdot)$ and its covariance function or kernel $k(\cdot, \cdot')$:
# ```math
#   f \sim \mathcal{GP}\big(m(\cdot), k(\cdot, \cdot')\big)
# ```
# In this notebook we show how the choice of kernel affects the samples from a GP (with zero mean).

## Load required packages
using KernelFunctions, LinearAlgebra
using Plots, Plots.PlotMeasures
default(; lw=1.0, legendfontsize=8.0)
using Random: seed!
seed!(42); # reproducibility

##
# ## Evaluation at finite set of points
#
# The function values $\mathbf{f} = \{f(x_n)\}_{n=1}^N$ of the GP at a finite number $N$ of points $X = \{x_n\}_{n=1}^N$ follow a multivariate normal distribution $\mathbf{f} \sim \mathcal{MVN}(\mathbf{m}, \mathrm{K})$ with mean vector $\mathbf{m}$ and covariance matrix $\mathrm{K}$, where
# ```math
# \begin{aligned}
#   \mathbf{m}_i &= m(x_i) \\
#   \mathrm{K}_{i,j} &= k(x_i, x_j)
# \end{aligned}
# ```
# with $1 \le i, j \le N$.
#
# We can visualize the infinite-dimensional GP by evaluating it on a fine grid to approximate the dense real line:
num_inputs = 101
xlim = (-5, 5)
X = range(xlim...; length=num_inputs);

# Given a kernel `k`, we can compute the kernel matrix as `K = kernelmatrix(k, X)`.

##
# ## Random samples
#
# To sample from the multivariate normal distribution $p(\mathbf{f}) = \mathcal{MVN}(0, \mathrm{K})$, we could make use of Distributions.jl and call `rand(MvNormal(K))`.
# Alternatively, we could use the [AbstractGPs.jl](https://github.com/JuliaGaussianProcesses/AbstractGPs.jl) package and construct a `GP` object which we evaluate at the points of interest and from which we can then sample: `rand(GP(k)(X))`.

# Here, we will explicitly construct samples using the Cholesky factorization $\mathrm{L} = \operatorname{cholesky}(\mathrm{K})$,
# with $\mathbf{f} = \mathrm{L} \mathbf{v}$, where $\mathbf{v} \sim \mathcal{N}(0, \mathbf{I})$ is a vector of standard-normal random variables.

# We will use the same randomness $\mathbf{v}$ to generate comparable samples across different kernels.
num_samples = 7
v = randn(num_inputs, num_samples);

# Mathematically, a kernel matrix is by definition positive semi-definite, but due to finite-precision inaccuracies, the computed kernel matrix might not be exactly positive definite. To avoid Cholesky errors, we add a small "nugget" term on the diagonal:
function mvn_sample(K)
    L = cholesky(K + 1e-6 * I)
    f = L.L * v
    return f
end;

##
# ## Visualization
# We now define a function that visualizes a kernel for us.

function visualize(k::Kernel)
    K = kernelmatrix(k, X)
    f = mvn_sample(K)

    p_kernel_2d = heatmap(
        X,
        X,
        K;
        yflip=true,
        colorbar=false,
        ylabel=string(nameof(typeof(k))),
        ylim=xlim,
        yticks=([xlim[1], 0, xlim[end]], ["\u22125", raw"$x'$", "5"]),
        vlim=(0, 1),
        title=raw"$k(x, x')$",
        aspect_ratio=:equal,
        left_margin=5mm,
    )

    p_kernel_cut = plot(
        X,
        k.(X, 0.0);
        title=string(raw"$k(x, x_\mathrm{ref})$"),
        label=raw"$x_\mathrm{ref}=0.0$",
        legend=:topleft,
        foreground_color_legend=nothing,
    )
    plot!(X, k.(X, 1.5); label=raw"$x_\mathrm{ref}=1.5$")

    p_samples = plot(X, f; c="blue", title=raw"$f(x)$", ylim=(-3, 3), label=nothing)

    return plot(
        p_kernel_2d,
        p_kernel_cut,
        p_samples;
        layout=(1, 3),
        xlabel=raw"$x$",
        xlim=xlim,
        xticks=collect(xlim),
    )
end;

##
# We can now visualize a kernel and show samples from
# a Gaussian process with a given kernel:

plot(visualize(SqExponentialKernel()); size=(800, 210), bottommargin=5mm, topmargin=5mm)

##
# ## Kernel comparison
# This also allows us to compare different kernels:

kernels = [
    Matern12Kernel(),
    Matern32Kernel(),
    Matern52Kernel(),
    SqExponentialKernel(),
    WhiteKernel(),
    ConstantKernel(),
    LinearKernel(),
    compose(PeriodicKernel(), ScaleTransform(0.2)),
    NeuralNetworkKernel(),
]
plot(
    [visualize(k) for k in kernels]...;
    layout=(length(kernels), 1),
    size=(800, 220 * length(kernels) + 100),
)


module NewtonRaphson

export newton_raphson

function newton_raphson(f::Function, x0::Number, fprime::Function, args::Tuple=();
                tol::AbstractFloat=1e-8, maxiter::Integer=50, eps::AbstractFloat=1e-10)
    for _ in 1:maxiter
        yprime = fprime(x0, args...)
        if abs(yprime) < eps
           warn("First derivative is zero")
           return x0
        end
        y = f(x0, args...)
        x1 = x0 - y/yprime
        if abs(x1-x0) < tol
          return x1
        end
        x0 = x1
    end
    error("Max iteration exceeded")
end

end