text:
export SpiralTrajectoryDualDensity

"""
    SpiralTrajectoryDualDensity(numProfiles, numSamplingPerProfile
                  ; TE::Float64=0.0
                  , AQ::Float64=1.e-3
                  , windings::Real= 6.25
                  , angleOffset= :equispaced
                  , densityFactor=2.0
                  , kargs...)

returns a 2d spiral trajectory.

# Arguments
* `numProfiles::Int64`            - number of profiles
* `numSamplingPerProfile::Int64`  - number of sampling points per profile
* (`TE::Float64=0.0`)             - echo time in s
* (`AQ::Float64=1.e-3`)           - readout duration in s (per profile)
* (`windings::Real= 6.25`)        - number of windings of the spiral profiles
* (`angleOffset= :equispaced`)    - spacing of profile angles (`:equispaced` sampling, `:golden` angle sampling or `:random` sampling)
"""
function SpiralTrajectoryDualDens(::Type{T}, numProfiles, numSamplingPerProfile
                  ; TE=0.0
                  , AQ=1.e-3
                  , windings::Real= 6.25
                  , kmax::Real=0.5
                  , angleOffset::String="equispaced"
                  , densityFactor::Float64=2.0
                  , kargs...) where T
  nodes = spiralNodesDualDens(T, numProfiles, numSamplingPerProfile; windings=windings,
              angleOffset=angleOffset, densityFactor=densityFactor, kmax=kmax)
  times = readoutTimes(T, numProfiles, numSamplingPerProfile; TE=TE, AQ=AQ)
  return  Trajectory("SpiralDualDens", nodes, times, TE, AQ, numProfiles, numSamplingPerProfile, 1, false, true)
end

function spiralNodesDualDens(::Type{T}, numProfiles::Int64
                            , numSamplingPerProfile::Int64
                            ; windings::Real= 6.25
                            , kmax::Real=0.5
                            , angleOffset::String="equispaced"
                            , densityFactor::Float64
                            , kargs...) where T

  nodes = zeros(T, 2, numSamplingPerProfile, numProfiles)
  if angleOffset == "golden"
      angles = [i*(3-sqrt(5))/2  for i=0:numProfiles-1 ]
  elseif angleOffset == "random"
      angles = sort(rand(numProfiles))
  elseif angleOffset == "equispaced"
      angles = collect((0:numProfiles-1)/numProfiles)
  end

  w = windings
  for l = 1:numProfiles
    for k = 1:numSamplingPerProfile

      t = sqrt((k-1)/(numSamplingPerProfile-1))

      if t < 1/2
        tau = t/densityFactor
      else
        tau = 0.5/densityFactor + (t-0.5)*2*(1.0-0.5/densityFactor)
      end

      nodes[1,k,l] = kmax*tau*cos(2*pi*( w*t + angles[l] ))
      nodes[2,k,l] = kmax*tau*sin(2*pi*( w*t + angles[l] ))
    end
  end
  return reshape(nodes, 2, numSamplingPerProfile*numProfiles)
end


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

using Markdown
using InteractiveUtils

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

# ╔═╡ eb4f8227-3010-445a-bc26-1fee616643b6
begin
	using PlutoUI # user-interface do Pluto.jl
	using Plots  # Julia package para gráficos
	using Dierckx  # Julia package para interpolação/extrapolação de dados
end

# ╔═╡ 7ac0b63a-e703-40d8-8207-37f43b682718
html"<button onclick='present()'>present</button>"

# ╔═╡ f691a604-30a7-432b-b2d4-8915f41fe8fe
md"""
# Máquinas de corrente contínua (Ex. 9)
Motores de corrente contínua, exercício 9:
"""

# ╔═╡ 8cf2189b-a1fb-454f-a5bb-8c2caa1b6532
(Pᵤₙ, Uₙ, nₙ, ηₙ, nₘₐₓ, nmag)=(12e3, 250, 1400, 80, 2400, 1500);

# ╔═╡ 7b358be8-59b1-4494-8c48-d842b10c5912
md"""
**Um motor série de $(Pᵤₙ/1000)kW, $(Uₙ)V, $(nₙ)rpm, $(ηₙ)% de rendimento, velocidade máxima
$(nₘₐₓ)rpm, tem a seguinte característica magnética obtida a $(nmag)rpm:**
"""

# ╔═╡ b099a590-b481-4748-bf4b-b6be08e958ea
begin
	Iₑₓ=[10, 20, 30, 40, 50, 60, 70, 80]
	E₀=[80.0, 140, 190, 225, 250, 270, 285, 295]
	Iₑₓ, E₀
end;

# ╔═╡ aa89c8da-459e-4c4f-852b-55373d9c8fa1
(Rᵢ, Rₛ)=(0.35, 0.1);

# ╔═╡ 415a598c-3eaa-4a9a-b2c8-43e6ef34d0a2
md"""
Sabendo que a resistência do induzido é $(Rᵢ)Ω, do indutor é $(Rₛ)Ω, calcular:
"""

# ╔═╡ bcf9e628-959c-4c4d-bb00-9e683413d3f9

# ╔═╡ 8e3c4cdd-a4ea-4ab6-82d2-2bb01f176680
md"""
## **a) $$p_{(mec+Fe)}$$**
**As perdas mecânicas e no ferro, $$p_{(mec+Fe)}$$, em carga;**
"""

# ╔═╡ 58ab2478-3e28-461a-97fb-5e50c1248931
md"""
As perdas mecânicas e magnéticas de uma máquina de corrente contínua,  $$p_{(mec+Fe)}$$, também designadas como perdas rotacionais, $$p_{rot}$$, têm um comportamento aproxiamadamente constante, considerando tensão de alimentação constante  e variações de velocidade não muito expressivas.\
Assim, genericamente, pode obter-se o resultado das perdas rotacionais a partir de ensaio em vazio do motor, ou usando os dados nominais da chapa de características.\

No caso concreto do motor série, o ensaio em vazio apenas seria possível operando a máquina como gerador série, pois como poderá constatar adiante neste exercício, o motor série não pode perder a carga mecânica aplicada ao veio de rotação.
"""

# ╔═╡ fa13e734-4453-4bfe-910c-71c4734bd07b
Iₙ=Pᵤₙ/(Uₙ*ηₙ/100)

# ╔═╡ 8ca57c4e-c674-4899-ae77-c03ceaa7c691
begin
	pᵣₒₜ=Pᵤₙ/(ηₙ/100)-Pᵤₙ-(Rᵢ+Rₛ)Iₙ^2
	pᵣₒₜ=round(pᵣₒₜ, digits=0)
end;

# ╔═╡ 9a478bbc-1984-436c-8558-53376fc48a04
md"""
Assim, as perdas rotacionais obtêm-se do balanço de potência do motor série em regime nominal:

$$p_{rot}=P_{ab}-P_{un}-p_J$$

ou seja,

$$p_{rot}=\frac{P_{un}}{\eta}-P_{un}-(Rᵢ+Rₛ)Iₙ^2$$

onde,

$$I_n=\frac{P_{ab}}{U_n}$$

Calculando, obtém-se: $$p_{rot}=$$ $(pᵣₒₜ)W
"""

# ╔═╡ 73c7adad-e494-4566-8ecb-51d657819f30

# ╔═╡ 656b989f-f988-4eb5-b464-e1326104a9d6
md"""
## **b) $$I_{min}$$**
**O valor mínimo da corrente que o motor pode absorver;**
"""

# ╔═╡ d1664dac-d1c1-491a-b92e-f28470ac4f01
md"""
A velocidade é inversamente proporcional ao fluxo magnético, por conseguinte, se o fluxo reduzir acentuadamente conduz a velocidades de funcionamento perigosas, situação comummente designada por **embalamento do motor de corrente contínua**.

O motor série tem na sua chapa de características uma de duas indicações para prevenir o seu embalamento do motor:
- velocidade máxima
- corrente mínima

Assim, para pontos de funcionamento ($$I$$, $$n$$) da característica de velocidade relativos a binários de carga reduzidos, a curva de velocidade tende a tornar-se assimptótica e por conseguinte, o motor ter um funcionamento instável.
"""

# ╔═╡ e4b2a383-b37e-4b82-ad57-7ae8b743c340
md"""
A obtenção da característica de velocidade do motor série permite encontrar a corrente mínima, $$I_{min}$$, observando o ponto de funcionamento correspondente à velocidade máxima, $$n_{max}$$.
"""

# ╔═╡ 77c74f69-5033-4ba9-b0d8-8c17110f477d
md"""
Este exercício não traz informação sobre a q.d.t. devido à reacção magnética do induzido, com excepção em regime nominal (como se verá adiante), pelo que se considera nula para os diversos valores de corrente considerados na determinação da característica de velocidade: $$\Delta E=0$$V.

Assim, a característica de velocidade obedece a:

$$n=\frac{U-(R_i+R_s)I}{kϕ}$$ com $$n$$ em rpm, $$kϕ=f(I)$$, em V/rpm:
"""

# ╔═╡ c2d9b1c5-4330-426e-9b60-e0e37476121d
begin
	I=0:1:Iₙ*1.25
	# forma computacional de consultar a curva de E₀(Iₑₓ), por interpolação dos dados através do Pkg Dierckx.jl
	E_int=Spline1D(Iₑₓ,E₀, k=1, bc="extrapolate")
	E₀ᵢ=E_int(I)
	kϕ=E₀ᵢ/nmag
end

# ╔═╡ 781ddebe-67b7-4a52-bb9d-154f40a1fd28
n=(Uₙ.-(Rᵢ+Rₛ)I)./kϕ

# ╔═╡ ee722749-602d-4f89-8dd9-fcb1d673f554
begin
	# forma computacional de consultar a curva de n(I), por interpolação dos dados através do Pkg Dierckx.jl
	n_int=Spline1D(-n,I)
	Iₘᵢₙ=n_int(-nₘₐₓ)
	Iₘᵢₙ=round(Iₘᵢₙ, digits=1)
end;

# ╔═╡ d215327c-a380-471a-a761-faade0c2020d
md"""
Consultando a característica de velocidade obtida verifica-se para $$n_{max}=$$ $(nₘₐₓ)rpm uma corrente mínima: $$I_{min}=$$ $(Iₘᵢₙ)A
"""

# ╔═╡ 16259eea-b205-4887-a09c-decba225f381
begin
	plot(I, n, title="n=f(I)", xlabel = "I (A)", ylabel="n (rpm)", xlims=(0,Iₙ*1.25), ylims=(0,n[10]), framestyle = :origin, minorticks=5, label=:none, linewidth=2)
	plot!([nₘₐₓ], seriestype = :hline, label=:none, linewidth=1, linestyle=:dash, linecolor=:red)
	plot!([Iₘᵢₙ], seriestype = :vline, label="Iₘᵢₙ(nₘₐₓ)=$(Iₘᵢₙ)A", linewidth=1, linestyle=:dash, linecolor=:red)
end

# ╔═╡ 024d8f06-f459-4431-9d46-b0e1b904b19c

# ╔═╡ e348ff8f-16cd-40ca-8f15-057158b5dad2
md"""
## **c) $$ΔE$$**
**A queda de tensão devida à reação magnética do induzido a plena carga;**
"""

# ╔═╡ de95a620-19dc-4f7f-8cce-7cf5a4c47d14
md"""
Nas condições nominais, dado que se tem o conhecimento da velocidade pela leitura da chapa de caracteríticas do motor série, é possível aferir o valor da velocidade, conhecidas as q.d.t. da máquina devido aos enrolamentos indutor e induzido e valor das f.c.e.m. da máquina na situação nominal.
Assim, tém-se:
"""

# ╔═╡ c5408a8d-8ed4-43cd-b712-65806e55c0da
md"""
$$ΔE=E_{0}^{'}-E^{'}$$
$$E^{'}=U_n-(R_i+R_s)I_n$$
$$E_{0}^{'}=kϕ_0*n_n$$
$$kϕ_0=\frac{E_{0} (I_n)} {n_{mag}}$$
"""

# ╔═╡ 4f617818-ea32-4f00-a9dc-8f0e2c38525d
begin
	E₀ₙ=E_int(Iₙ)	# fem para Iₙ. Consultando a característica magnética
	kϕ₀=E₀ₙ/nmag   # kϕ₀ para Iₙ
	E₀ₗ=kϕ₀*nₙ     # fcem₀ para Iₙ
	E=Uₙ-(Rᵢ+Rₛ)Iₙ  # fcem efetiva para Iₙ
	ΔE=E₀ₗ-E
end;

# ╔═╡ 04c1c186-d018-4f27-9632-005e4dd4271e
md"""
Alternativamente, a conjugação das expressões anteriores conduz à expressão da velocidade que permite também o cálculo de $$ΔE$$:

$$n=\frac{U-R_iI_i+\Delta E}{k\phi_0}$$

Obtendo-se, $$ΔE=$$ $(ΔE)V
"""

# ╔═╡ 2f428692-91f9-4d6c-a860-07c36a43c939

# ╔═╡ 2eef0ec0-6878-479f-b27f-e00795704e61
md"""
## **d)** $$\eta_{max}$$
**A potência do motor que corresponde ao rendimento máximo;**
"""

# ╔═╡ 3b1aeb22-712d-429e-a76d-f2d3692af16a
md"""
A situação de rendimento máximo corresponde corresponde à igualdade entre perdas variáveis, $$p_v$$, e perdas constantes, $$p_c$$, na máquina:

$$ηₘₐₓ\Rightarrow p_v=p_c$$

No caso presente, tém-se:

$$p_v=(R_i+R_s)I^2$$
$$p_c=p_{rot}$$

Por conseguinte, a condição de rendimento máximo verifica-se quando:

$$I=\sqrt{\frac{p_{rot}}{R_i+R_s}}$$
"""

# ╔═╡ 765bc8ab-0c8e-482a-b6fb-4020d7813b3c
begin
	Iᵣₘ=√(pᵣₒₜ/(Rᵢ+Rₛ))   # Iᵣₘ, corrente relativa ao rendimento máximo do motor
	Iᵣₘ=round(Iᵣₘ, digits=1)
end

# ╔═╡ ddcaf382-9f44-48e1-a44d-747910505e36
Pᵤ=Uₙ*Iᵣₘ-2pᵣₒₜ;

# ╔═╡ cbfdf1f5-dd00-4796-87ea-529be3934620
md"""
Tendo em conta o balanço de potências na situação de rendimento máximo, resulta a potência útil: $$P_u=$$ $(Pᵤ/1000)kW"""

# ╔═╡ 5931d230-68e9-4003-bf94-800fbda03e79

# ╔═╡ 21baaeb1-a64e-4931-abd8-2efb4ec6583d
md"""
## **e) MS: variação $$n=f(I)$$ e $$T=f(I)$$ 💻**
**e) Explicite qualitativamente a influência do reóstato de campo sobre a característica de velocidade do motor série.**
"""

# ╔═╡ 83e8d548-d212-49ec-89f4-d794c9d26c85
md"""
Numa máquina DC série o reostato de campo é colocado em paralelo com o enrolamento de excitação, criando um divisor de corrente que permite regular o fluxo magnético indutor.

Assim, a corrente do enrolamento série, $$I_s$$, vem dado por:

$$I_s=\frac{R_{cs}}{R{cs}+R_s}I$$

Consultando a característica magnética da máquina série para $$I_s$$ obtém-se o valor do fluxo magnético, traduzido no valor de $$kϕ$$ em (V/rpm):

$$kϕ(I_s)=\frac{E_0(I_s)}{n_{mag}}$$
"""

# ╔═╡ fb887b2d-9639-499b-9f1a-9cb3a4afaf73
md"""
Assim, a expressão da velocidade do motor terá a sua q.d.t. modificada pelo paralelo das resistências relativas ao enrolamento de excitação série e ao reóstato de campo:

$$n=\frac{U-[R_i+(R_s // R_{cs})]I}{kϕ(I_s)}$$

"""

# ╔═╡ a9bab890-8171-41cc-ae1d-80fc64d7196a
begin
	H4=("Rcs", @bind Rcs PlutoUI.Slider(0.05:0.01:4, default=4, show_value=true))
	H4
end

# ╔═╡ 0d9375fb-4b18-4c41-8f51-3e2c85af382c
begin
	Iₛ=(Rcs).*I/(Rcs+Rₛ)
	E₀₁=E_int(Iₛ)
	kϕ₁=E₀₁/nmag
end;

# ╔═╡ 86f9c718-dd8d-46dd-930e-aff35147f214
begin
	R₁=(Rcs*Rₛ)/(Rcs+Rₛ)
	n₁=(Uₙ.-(Rᵢ+R₁)I)./kϕ₁
end;

# ╔═╡ adedad68-149d-45e7-8a89-4e9c93c7f08a
plot(I, n₁, title="n=f(I), efeito de Rcs", xlabel = "I (A)", ylabel="n (rpm)", xlims=(0,Iₙ*1.25), ylims=(0,n[10]), framestyle = :origin, minorticks=5, label=:none, linewidth=2)

# ╔═╡ 304dae29-66b9-4349-a3f3-7fb6f0901307
md"""
!!! nota
	**Aproveitando esta alínea pode-se verificar o efeito do reostato de campo também na característica de binário do motor série:**
"""

# ╔═╡ 577bfb9d-0c76-45cd-904c-f4a92f51cbec
md"""
Genericamente, a expressão do binário desenvolvido, $$T_d$$, é obtida por:
$$T_d=kϕI$$

No entanto, os valores de $$kϕ$$ têm estado a ser apresentados em V/rpm. Embora as "rotações por minuto" sejam uma unidade de uso generalizado, a velocidade angular é medida em rad/s no Sistema Internacional de Unidades. Assim, mantendo  $$kϕ$$ em V/rpm, a expressão do binário desenvolvido vem dada por:

$$T_d=kϕ\frac{60}{2π}I$$
"""

# ╔═╡ d4f4837f-0d2f-4062-b943-74cfb2803bb7
Td=kϕ₁*(60/(2π)).*I;

# ╔═╡ 6de1a6c2-11c8-497d-9a28-5dd19d6255c0
plot(I, Td, title="Td=f(I), efeito de Rcs", xlabel = "I (A)", ylabel="Td (Nm)", xlims=(0,Iₙ*1.25), ylims=(0,150), framestyle = :origin, minorticks=5, label=:none, linewidth=2)

# ╔═╡ ca47feed-7bce-4804-bc88-cce163bbc9c2
md"""
> Procure analisar o efeito da variação do reóstato de campo no comportamento das características de velocidade e binário do motor série, justificando.
"""

# ╔═╡ 6a0e3660-3e0f-4222-bbdb-a5cb1cce3dff

# ╔═╡ ab4ce472-cd7d-4a44-9ba7-fafa1b61a6e2
md"""
# Setup
"""

# ╔═╡ 001308d3-a420-40f8-b971-4b0f5936064b
TableOfContents(title="📚 Índice, Exercício 9")

# ╔═╡ 62ecbc17-e888-4bf0-8963-df72c08dd319
md"""
!!! info
	No índice deste *notebook*, os tópicos assinalados com "💻" permitem interacção com o utilizador.
"""

# ╔═╡ 18f00352-961a-4ecd-b57b-ca9c2fd0725d
md"""
## _Julia packages_
*Links to package help*: [Dierckx](https://github.com/kbarbary/Dierckx.jl), [Plots](http://docs.juliaplots.org/latest/), [PlutoUI](https://juliahub.com/docs/PlutoUI/abXFp/0.7.6/)
"""

# ╔═╡ daa1b737-ca90-4a6f-874a-3a2151a33e23
md"""
!!! info
	## Programação _Julia_ e _notebooks Pluto_
	Saber mais sobre: [_Julia_/_Pluto_](https://github.com/Ricardo-Luis/notebooks/blob/main/ME2/README.md)
"""

# ╔═╡ ce754233-46d2-402c-bfa4-b7072bf15b1d
version=VERSION;

# ╔═╡ 3128383a-fa17-413b-8d57-330f019300d5
md"""
## _Notebook_

*Notebook* realizado em linguagem de programação *Julia* versão $(version) para a unidade curricular de Máquinas Elétricas 2 (ISEL\LEE) \
**Ricardo Luís** (Professor Adjunto, ISEL\DEEEA\GDME) \
ISEL, 25/Out/2021
"""

# ╔═╡ 885f7cff-b9e7-4314-93ed-81661b7d98c1
md"""
Este conteúdo possui licença [![](https://img.shields.io/badge/License-CC%20BY--SA%204.0-lightgrey.svg)](http://creativecommons.org/licenses/by-sa/4.0/).
"""

# ╔═╡ 00000000-0000-0000-0000-000000000001
PLUTO_PROJECT_TOML_CONTENTS = """
[deps]
Dierckx = "39dd38d3-220a-591b-8e3c-4c3a8c710a94"
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
PlutoUI = "7f904dfe-b85e-4ff6-b463-dae2292396a8"

[compat]
Dierckx = "~0.5.1"
Plots = "~1.22.6"
PlutoUI = "~0.7.16"
"""

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

[[Dierckx]]
deps = ["Dierckx_jll"]
git-tree-sha1 = "5fefbe52e9a6e55b8f87cb89352d469bd3a3a090"
uuid = "39dd38d3-220a-591b-8e3c-4c3a8c710a94"
version = "0.5.1"

[[Dierckx_jll]]
deps = ["CompilerSupportLibraries_jll", "Libdl", "Pkg"]
git-tree-sha1 = "a580560f526f6fc6973e8bad2b036514a4e3b013"
uuid = "cd4c43a9-7502-52ba-aa6d-59fb2a88580b"
version = "0.0.1+0"

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

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

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

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

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

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

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

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

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

[[HypertextLiteral]]
git-tree-sha1 = "f6532909bf3d40b308a0f360b6a0e626c0e263a8"
uuid = "ac1192a8-f4b3-4bfe-ba22-af5b92cd3ab2"
version = "0.9.1"

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

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

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

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

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

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

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

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

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

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

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

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

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

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

[[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", "InverseFunctions", "IrrationalConstants", "LinearAlgebra"]
git-tree-sha1 = "6193c3815f13ba1b78a51ce391db8be016ae9214"
uuid = "2ab3a3ac-af41-5b50-aa03-7779005ae688"
version = "0.3.4"

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

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

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

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

[[OpenSSL_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "15003dcb7d8db3c6c857fda14891a539a8f2705a"
uuid = "458c3c95-2e84-50aa-8efc-19380b2a3a95"
version = "1.1.10+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"

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

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

[[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 = "ba43b248a1f04a9667ca4a9f782321d9211aa68e"
uuid = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
version = "1.22.6"

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

[[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:
# ╟─7ac0b63a-e703-40d8-8207-37f43b682718
# ╟─f691a604-30a7-432b-b2d4-8915f41fe8fe
# ╟─7b358be8-59b1-4494-8c48-d842b10c5912
# ╠═8cf2189b-a1fb-454f-a5bb-8c2caa1b6532
# ╠═b099a590-b481-4748-bf4b-b6be08e958ea
# ╟─415a598c-3eaa-4a9a-b2c8-43e6ef34d0a2
# ╠═aa89c8da-459e-4c4f-852b-55373d9c8fa1
# ╟─bcf9e628-959c-4c4d-bb00-9e683413d3f9
# ╟─8e3c4cdd-a4ea-4ab6-82d2-2bb01f176680
# ╟─58ab2478-3e28-461a-97fb-5e50c1248931
# ╟─9a478bbc-1984-436c-8558-53376fc48a04
# ╠═fa13e734-4453-4bfe-910c-71c4734bd07b
# ╠═8ca57c4e-c674-4899-ae77-c03ceaa7c691
# ╟─73c7adad-e494-4566-8ecb-51d657819f30
# ╟─656b989f-f988-4eb5-b464-e1326104a9d6
# ╟─d1664dac-d1c1-491a-b92e-f28470ac4f01
# ╟─e4b2a383-b37e-4b82-ad57-7ae8b743c340
# ╟─77c74f69-5033-4ba9-b0d8-8c17110f477d
# ╠═c2d9b1c5-4330-426e-9b60-e0e37476121d
# ╠═781ddebe-67b7-4a52-bb9d-154f40a1fd28
# ╟─d215327c-a380-471a-a761-faade0c2020d
# ╠═ee722749-602d-4f89-8dd9-fcb1d673f554
# ╟─16259eea-b205-4887-a09c-decba225f381
# ╟─024d8f06-f459-4431-9d46-b0e1b904b19c
# ╟─e348ff8f-16cd-40ca-8f15-057158b5dad2
# ╟─de95a620-19dc-4f7f-8cce-7cf5a4c47d14
# ╟─c5408a8d-8ed4-43cd-b712-65806e55c0da
# ╠═4f617818-ea32-4f00-a9dc-8f0e2c38525d
# ╟─04c1c186-d018-4f27-9632-005e4dd4271e
# ╟─2f428692-91f9-4d6c-a860-07c36a43c939
# ╟─2eef0ec0-6878-479f-b27f-e00795704e61
# ╟─3b1aeb22-712d-429e-a76d-f2d3692af16a
# ╠═765bc8ab-0c8e-482a-b6fb-4020d7813b3c
# ╟─cbfdf1f5-dd00-4796-87ea-529be3934620
# ╠═ddcaf382-9f44-48e1-a44d-747910505e36
# ╟─5931d230-68e9-4003-bf94-800fbda03e79
# ╟─21baaeb1-a64e-4931-abd8-2efb4ec6583d
# ╟─83e8d548-d212-49ec-89f4-d794c9d26c85
# ╠═0d9375fb-4b18-4c41-8f51-3e2c85af382c
# ╟─fb887b2d-9639-499b-9f1a-9cb3a4afaf73
# ╠═86f9c718-dd8d-46dd-930e-aff35147f214
# ╟─adedad68-149d-45e7-8a89-4e9c93c7f08a
# ╟─a9bab890-8171-41cc-ae1d-80fc64d7196a
# ╟─304dae29-66b9-4349-a3f3-7fb6f0901307
# ╟─577bfb9d-0c76-45cd-904c-f4a92f51cbec
# ╠═d4f4837f-0d2f-4062-b943-74cfb2803bb7
# ╟─6de1a6c2-11c8-497d-9a28-5dd19d6255c0
# ╟─ca47feed-7bce-4804-bc88-cce163bbc9c2
# ╠═6a0e3660-3e0f-4222-bbdb-a5cb1cce3dff
# ╟─ab4ce472-cd7d-4a44-9ba7-fafa1b61a6e2
# ╠═001308d3-a420-40f8-b971-4b0f5936064b
# ╟─62ecbc17-e888-4bf0-8963-df72c08dd319
# ╟─18f00352-961a-4ecd-b57b-ca9c2fd0725d
# ╠═eb4f8227-3010-445a-bc26-1fee616643b6
# ╟─daa1b737-ca90-4a6f-874a-3a2151a33e23
# ╟─3128383a-fa17-413b-8d57-330f019300d5
# ╟─ce754233-46d2-402c-bfa4-b7072bf15b1d
# ╟─885f7cff-b9e7-4314-93ed-81661b7d98c1
# ╟─00000000-0000-0000-0000-000000000001
# ╟─00000000-0000-0000-0000-000000000002

ext: jl

max_stars_repo_name: Ricardo-Luis/notebooks

max_issues_repo_name: Ricardo-Luis/notebooks

max_forks_repo_name: Ricardo-Luis/notebooks


text:
# MIT License
#
# Copyright (c) 2018 Martin Biel
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

# Attributes #
# ========================== #
"""
    AbstractLShapedAttribute

Abstract supertype for attribute objects specific to the L-shaped algorithm.
"""
abstract type AbstractLShapedAttribute <: AbstractStructuredOptimizerAttribute end
"""
    FeasibilityStrategy

An optimizer attribute for specifying a strategy for dealing with second-stage feasibility the L-shaped algorithm. Options are:

- [`IgnoreFeasibility`](@ref) (default)
- [`FeasibilityCuts`](@ref)
"""
struct FeasibilityStrategy <: AbstractLShapedAttribute end
"""
    IntegerStrategy

An optimizer attribute for specifying a strategy for dealing with integers the L-shaped algorithm. Options are:

- [`IgnoreIntegers`](@ref) (default)
- [`CombinatorialCuts`](@ref)
- [`Convexification`](@ref)
"""
struct IntegerStrategy <: AbstractLShapedAttribute end
"""
    Regularizer

An optimizer attribute for specifying a regularization procedure to be used in the L-shaped algorithm. Options are:

- [`NoRegularization`](@ref):  L-shaped algorithm (default)
- [`RegularizedDecomposition`](@ref):  Regularized decomposition ?RegularizedDecomposition for parameter descriptions.
- [`TrustRegion`](@ref):  Trust-region ?TrustRegion for parameter descriptions.
- [`LevelSet`](@ref):  Level-set ?LevelSet for parameter descriptions.
"""
struct Regularizer <: AbstractLShapedAttribute end
"""
    Aggregator

An optimizer attribute for specifying an aggregation procedure to be used in the L-shaped algorithm. Options are:

- [`NoAggregation`](@ref):  Multi-cut L-shaped algorithm (default)
- [`PartialAggregation`](@ref):  ?PartialAggregation for parameter descriptions.
- [`FullAggregation`](@ref):  ?FullAggregation for parameter descriptions.
- [`DynamicAggregation`](@ref):  ?DynamicAggregation for parameter descriptions.
- [`ClusterAggregation`](@ref):  ?ClusterAggregation for parameter descriptions.
- [`HybridAggregation`](@ref):  ?HybridAggregation for parameter descriptions.
"""
struct Aggregator <: AbstractLShapedAttribute end
"""
    Consolidator

An optimizer attribute for specifying a consolidation procedure to be used in the L-shaped algorithm. Options are:

- [`NoConsolidation`](@ref) (default)
- [`Consolidation`](@ref)
"""
struct Consolidator <: AbstractLShapedAttribute end
"""
    IntegerParameter

Abstract supertype for integer-specific attributes.
"""
abstract type IntegerParameter <: AbstractLShapedAttribute end
"""
    RegularizationParameter

Abstract supertype for regularization-specific attributes.
"""
abstract type RegularizationParameter <: AbstractLShapedAttribute end
"""
    AggregationParameter

Abstract supertype for aggregation-specific attributes.
"""
abstract type AggregationParameter <: AbstractLShapedAttribute end
"""
    ConsolidationParameter

Abstract supertype for consolidation-specific attributes.
"""
abstract type ConsolidationParameter <: AbstractLShapedAttribute end


text:
module TrajectoryMatrices

include("matrix.jl")

export TrajectoryMatrix, traj

end # end of module


text:
doc = """Overview:
  Downscale & merge input images to tiff files.

Usage:
  ScaleMerge.jl <srcdir> <outdir> [--tif|--bin]
                [--width <w>] [--height <h>] [--overlap <L:R:T:B>]
                [--scale <ratio>] [--skip-z <num>] [--grid]
                [--CW-rot|--CCW-rot] [--V-flip] [--H-flip] [--flip-V] [--flip-H]
                [--config <json>]
  ScaleMerge.jl (-h | --help)
  ScaleMerge.jl --version

Options:
  -h, --help             Show this screen.
  --version              Show version.
  --tif                  Search for input images in .tif format.
  --bin                  Search for input images in .bin format.
  --width <w>            Width of the rotated image[default: 2160].
  --height <h>           Height of the rotated image[default: 2560].
  --overlap <L:R:T:B>    Overlaping pixels[default: 100:100:100:100].
  --scale <ratio>        Downscale by this ratio[default: 0.05].
  --skip-z <num>         Skip images along z axis[default: 1].
  --grid                 Overlay grid with input image size.
  --CW-rot               Rotate by 90 degrees clockwise before merging.
  --CCW-rot              Rotate by 90 degrees counter clockwise before merging.
  --V-flip               Flip vertically before merging.
  --H-flip               Flip horizontally before merging.
  --flip-V               Flip vertically after merging.
  --flip-H               Flip horizontally after merging.
  --config <json>        Load configuration in json format.
"""

using DocOpt
using Nullables
import JSON

function searchImages(basedir::String, imgformat::Int)
    yset = Set{String}()
    xset = Set{String}()
    zset = Set{String}()
    imgdict = Dict{String,Dict}()
    ext = (imgformat == 0) ? ".tif" : ".bin"
    for yname in sort(readdir(basedir))
        ybasedir = joinpath(basedir, yname)
        if !isdir(ybasedir)
            continue
        end
        push!(yset, yname)
        imgdict[yname] = Dict{String,Array}()
        for yxname in sort(readdir(ybasedir))
            xybasedir = joinpath(ybasedir, yxname)
            if !isdir(xybasedir)
                continue
            end
            xname = last(split(yxname, '_'))
            push!(xset, xname)
            imgdict[yname][xname] = sort(map(fname->fname[1:end-4],
                                             filter(fname->endswith(fname, ext),
                                                    readdir(xybasedir)
                                                    )
                                             )
                                         )
            union!(zset, imgdict[yname][xname])
        end
    end
    ylist = sort(collect(yset))
    xlist = sort(collect(xset))
    zlist = sort(collect(zset))
    imgdict,ylist,xlist,zlist
end

function writeParamFile(param_path::String, z::String, imgdict::Dict, ylist::Array, xlist::Array, basedir::String, width::Int, height::Int, sampling_rate::Float32, overlap::String, flip_rot_before::Int, flip_rot_after::Int, imgformat::Int, showgrid::Int)
    len_x = length(xlist)
    len_y = length(ylist)
    open(param_path, "w") do f
        write(f, "$(width):$(height):$(len_x):$(len_y):$(sampling_rate):$(overlap):$(flip_rot_before):$(flip_rot_after):$(imgformat):$(showgrid):\n")
        #println("imgdict keys:", keys(imgdict))
        for y in ylist
            #println("imgdict[$y] keys:", keys(imgdict[y]))
            for x in xlist
                #println("imgdict[$y][$x] array", imgdict[y][x])
                if z in imgdict[y][x]
                    #println((basedir, imgdict[y][x], y, "$(y)_$(x)", z))
                    imgpath = joinpath(basedir, y, "$(y)_$(x)", string(z, (imgformat==0) ? ".tif" : ".bin"))
                    write(f, "$(imgpath)\n")
                else
                    write(f, "\n")
                end
            end
        end
    end
end

function main()
    arguments = docopt(doc, version=v"0.2.0")
    println("arguments:")
    #for (k,v) in arguments
    #    println("\t$(k) : $(v)")
    #end

    if arguments["--config"] != nothing
        j = JSON.parsefile(arguments["--config"])
        #println("config file:")
        #for (k,v) in j
        #    println("\t$(k) : $(v)")
        #end
        arguments = merge(j, arguments)
        #println("arguments after merged:")
        #for (k,v) in arguments
        #    println("\t$(k) : $(v)")
        #end
    end
    basedir = arguments["<srcdir>"]
    outdir = arguments["<outdir>"]
    width = parse(Int, arguments["--width"])
    height = parse(Int, arguments["--height"])
    overlap = arguments["--overlap"]
    if any(map(x->tryparse(Float64,x) === nothing, split(overlap, ":")))
        println("invalid overlap syntax.")
    end
    skip_z = parse(Int, arguments["--skip-z"])
    sampling_rate = parse(Float32, arguments["--scale"])
    flip_rot_before = 0
    flip_rot_before += (arguments["--H-flip"] ? 1 : 0)
    flip_rot_before += (arguments["--V-flip"] ? 2 : 0)
    flip_rot_before += (arguments["--CCW-rot"] ? 4 : 0)
    flip_rot_before += (arguments["--CW-rot"] ? 8 : 0)
    flip_rot_after = 0
    flip_rot_after += (arguments["--flip-H"] ? 1 : 0)
    flip_rot_after += (arguments["--flip-V"] ? 2 : 0)
    imgformat = arguments["--tif"] ? 0 : 1
    showgrid = arguments["--grid"] ? 1 : 0

    println("basedir:", basedir)
    println("outdir:", outdir)
    imgdict,ylist,xlist,zlist = searchImages(basedir, imgformat)
    if !isdir(outdir)
        mkpath(outdir)
    end

    len_z = length(zlist)
    len_z_skip = Int(round(len_z / skip_z))
    println("max z length: $len_z")
    println("skipped z length: $len_z_skip")

    randomID = rand(UInt32)
    zlist_skip = zlist[1:skip_z:len_z]
    paramfiles = ["/tmp/param_ScaleMerge_$(randomID)_$zname.txt" for zname in zlist_skip]
    mergedfiles = [joinpath(outdir, "$zname.tif") for zname in zlist_skip]

    for (zname,paramfile) in zip(zlist_skip, paramfiles)
        writeParamFile(paramfile, zname, imgdict, ylist, xlist, basedir,
                       width, height, sampling_rate, String(overlap), flip_rot_before, flip_rot_after, imgformat, showgrid)
    end
    pmap((paramfile,mergedfile)->run(`./ScaleMerge $paramfile $mergedfile`), paramfiles,mergedfiles)
    map((paramfile)->rm(paramfile), paramfiles)
end

main()

max_stars_repo_name: lsb-riken/CUBIC-informatics

max_issues_repo_name: lsb-riken/CUBIC-informatics

max_forks_repo_name: lsb-riken/CUBIC-informatics


text:
using Revise
using Sockets
using HTTP: setstatus, setheader, header, hasheader
using HTTP.WebSockets: WebSocket, accept_hash, check_upgrade, WebSocketError

export start, stop

# Due to some issues with InteruptExceptions we need to implement live
# reload using a cancel token
# https://github.com/JuliaLang/julia/issues/25790#issuecomment-618986924

# Much of the code is adapted from here
# https://github.com/JuliaWeb/HTTP.jl/issues/587

macro async_logged(exs...)
    if length(exs) == 2
        taskname, body = exs
    elseif length(exs) == 1
        taskname = "Task"
        body = only(exs)
    end
    quote
        @async try
            $(esc(body))
        catch exc
            @error string($(esc(taskname)), " failed") exception = (exc, catch_backtrace())
            rethrow()
        end
    end
end

# function ws_upgrade(http::HTTP.Stream)
#     # adapted from HTTP.WebSockets.upgrade; note that here the upgrade will always
#     # have  the right format as it always triggered by after a Response
#     HTTP.setstatus(http, 101)
#     HTTP.setheader(http, "Upgrade" => "websocket")
#     HTTP.setheader(http, "Connection" => "Upgrade")
#     key = HTTP.header(http, "Sec-WebSocket-Key")
#     HTTP.setheader(http, "Sec-WebSocket-Accept" => HTTP.WebSockets.accept_hash(key))
#     HTTP.startwrite(http)
#     io = http.stream
#     return HTTP.WebSockets.WebSocket(io; server=true)
# end

function start(
    app::App;
    host=ip"0.0.0.0",
    port=8081,
    kwargs...)

    addr = Sockets.InetAddr(host, port)

    if !isnothing(app.docs)
        # app.get(app.docs) do stream
        #     @info "docs"
        #     html = create_docs_html(app)
        #     Bonsai.write(stream, Body(html))
        #     Bonsai.write(stream, Header(content_type="text/html; charset=UTF-8"))
        # end

        # app.get(app.docs * ".json") do stream
        #     @info "docs json"
        #     json = JSON3.write(OpenAPI(app))
        #     Bonsai.write(stream, Body(json))
        # end
    end

    function handler_function(stream::HTTP.Stream)
        try
            app(stream)
        catch e
            rethrow(e)
        end
    end

    function serve_fn(server) end

    app.inet_addr = addr
    app.cancel_token = CancelToken()

    server = Sockets.listen(addr)

    @async HTTP.serve(
        handler_function,
        host, port; server=server, stream=true, kwargs...
    )

    app.server = server
    @info "Started Server"
end

function stop(app::App)
    @info "Stopping Server"
    close(app.cancel_token)
    close(app.server)
    app.inet_addr = nothing
end

Base.wait(app::App) = wait(app.cancel_token)

max_stars_repo_name: onetonfoot/Tree

max_issues_repo_name: onetonfoot/Tree

max_forks_repo_name: onetonfoot/Tree


text:
module PFVG_AA2021

    using PyPlot, PyCall, Printf, DelimitedFiles, Distances, StatsBase, ProbabilisticFluxVariationGradient

    include("PFVG.jl")

    export Check3C120, runPFVG, PlotPFVGdist

end


text:
function create_testfuncs(g::OneDGrid{Tg,<:Array}) where Tg
    g.nx > 8 || error("nx must be > 8")
    m = 5
    k₀ = g.k[2] # Fundamental wavenumber

    # Analytic function
    φ = π/3
    f₁ = @. cos(m*k₀*g.x + φ)

    # Transform of f₁ three ways
     f₁h = fft(f₁)
    f₁hr = rfft(f₁)
    f₁hr_mul = zeros(Complex{eltype(g.x)}, g.nkr)
    mul!(f₁hr_mul, g.rfftplan, f₁)

    # Analytical values of the fft and rfft
     f₁h_analytical = zeros(Complex{Float64}, size(f₁h))
    f₁hr_analytical = zeros(Complex{Float64}, size(f₁hr))

    for i in 1:g.nk
      if abs(real(g.k[i])) == m*k₀
        f₁h_analytical[i] = -exp(sign(real(g.k[i]))*im*φ)*g.nx/2
      end
    end

    for i in 1:g.nkr
      if abs(real(g.k[i])) == m*k₀
        f₁hr_analytical[i] = -exp(sign(real(g.kr[i]))*im*φ)*g.nx/2
      end
    end

    f₁, f₁h, f₁hr, f₁hr_mul, f₁h_analytical, f₁hr_analytical
end

function create_testfuncs(g::TwoDGrid{Tg,<:Array}) where Tg
    g.nx > 8 || error("nx must be > 8")
    m, n = 5, 2
    k₀ = g.k[2]
    l₀ = g.l[2]

    # Analytic functions
    f₁ = @. cos(m*k₀*g.x) * cos(n*l₀*g.y)
    f₂ = @. sin(m*k₀*g.x + n*l₀*g.y)

     f₁h = fft(f₁)
     f₂h = fft(f₂)
    f₁hr = rfft(f₁)
    f₂hr = rfft(f₂)

    f₁hr_mul = zeros(Complex{eltype(g.x)}, (g.nkr, g.nl))
    f₂hr_mul = zeros(Complex{eltype(g.x)}, (g.nkr, g.nl))
    mul!(f₁hr_mul, g.rfftplan, f₁)
    mul!(f₂hr_mul, g.rfftplan, f₂)

    # Theoretical results
     f₁h_analytical = zeros(Complex{eltype(g.x)}, size(f₁h))
     f₂h_analytical = zeros(Complex{eltype(g.x)}, size(f₂h))
    f₁hr_analytical = zeros(Complex{eltype(g.x)}, size(f₁hr))
    f₂hr_analytical = zeros(Complex{eltype(g.x)}, size(f₂hr))

    for j in 1:g.nl, i in 1:g.nk
      if ( abs(real(g.k[i])) == m*k₀ && abs(real(g.l[j])) == n*l₀ )
        f₁h_analytical[i, j] = - g.nx*g.ny/4
      end
      if ( real(g.k[i]) == m*k₀ && real(g.l[j]) == n*l₀ )
        f₂h_analytical[i, j] = -g.nx*g.ny/2
      elseif ( real(g.k[i]) == -m*k₀ && real(g.l[j]) == -n*l₀ )
        f₂h_analytical[i, j] = g.nx*g.ny/2
      end
    end
    f₂h_analytical = -im*f₂h_analytical;

    for j in 1:g.nl, i in 1:g.nkr
      if ( abs(g.kr[i])==m*k₀ && abs(g.l[j])==n*l₀ )
        f₁hr_analytical[i, j] = - g.nx*g.ny/4
      end
      if ( real(g.kr[i]) == m*k₀ && real(g.l[j]) == n*l₀ )
        f₂hr_analytical[i, j] = -g.nx*g.ny/2
      elseif ( real(g.kr[i]) == -m*k₀ && real(g.l[j]) == -n*l₀ )
        f₂hr_analytical[i, j] = g.nx*g.ny/2
      end
    end
    f₂hr_analytical = -im*f₂hr_analytical;

    f₁, f₂, f₁h, f₂h, f₁hr, f₂hr, f₁hr_mul, f₂hr_mul, f₁h_analytical, f₁hr_analytical, f₂h_analytical, f₂hr_analytical
end

@has_cuda begin
  function create_testfuncs(g::OneDGrid{Tg, <:CuArray}) where Tg
    cpugrid = OneDGrid(g.nx, g.Lx)
    out = create_testfuncs(cpugrid)
    return map(x->CuArray(x), out)
  end

  function create_testfuncs(g::TwoDGrid{Tg, <:CuArray}) where Tg
    cpugrid = TwoDGrid(g.nx, g.Lx, g.ny, g.Ly)
    out = create_testfuncs(cpugrid)
    return map(x->CuArray(x), out)
  end
end


text:
# constraints
"""
    get_joint(mechanism, name)

    return JointConstraint from Mechanism

    mechanism: Mechanism
    name: unique identifier for joint
"""
function get_joint(mechanism::Mechanism, name::Symbol)
    for joint in mechanism.joints
        if joint.name == name
            return joint
        end
    end
    return
end

get_joint(mechanism::Mechanism, id::Integer) = mechanism.joints[id]

# bodies
"""
    get_body(mechanism, name)

    returns Body from Mechanism

    mechanism: Mechanism
    name: unique identifier for body
"""
function get_body(mechanism::Mechanism, name::Symbol)
    if name == :origin
        return mechanism.origin
    else
        for body in mechanism.bodies
            if body.name == name
                return body
            end
        end
    end
    return
end

get_body(mechanism::Mechanism{T,Nn,Ne,Nb,Ni}, id::Integer) where {T,Nn,Ne,Nb,Ni} = id == 0 ? mechanism.origin : mechanism.bodies[id-Ne]

# contacts
"""
    get_contact(mechanism, name)

    returns ContactConstraint from Mechanism

    mechanism: Mechanism
    name: unique identifier for contact
"""
function get_contact(mechanism::Mechanism, name::Symbol)
    for contact in mechanism.contacts
        if contact.name == name
            return contact
        end
    end
    return
end

get_contact(mechanism::Mechanism{T,Nn,Ne,Nb,Ni}, id::Integer) where {T,Nn,Ne,Nb,Ni} = mechanism.contacts[id-Ne-Nb]

# nodes
"""
    get_node(mechanism, name)

    return Node from Mechanism

    mechanism: Mechanism
    name: unique identifier for node
"""
function get_node(mechanism::Mechanism, name::Symbol)
    node = get_body(mechanism, name)
    if node === nothing
        node = get_joint(mechanism,name)
    end
    if node === nothing
        node = get_contact(mechanism,name)
    end
    return node
end

function get_node(mechanism::Mechanism{T,Nn,Ne,Nb}, id::Integer;
    origin::Bool=false) where {T,Nn,Ne,Nb}
    (origin && id == 0) && return mechanism.origin
    if id <= Ne
        return get_joint(mechanism, id)
    elseif id <= Ne+Nb
        return get_body(mechanism, id)
    else
        return get_contact(mechanism, id)
    end
end

# maximal
"""
    get_maximal_state(mechanism)

    return the current maximal state of mechanism

    mechanism: Mechanism
"""
function get_maximal_state(mechanism::Mechanism{T,Nn,Ne,Nb,Ni}) where {T,Nn,Ne,Nb,Ni}
	z = zeros(T, 13Nb)
	for (i, body) in enumerate(mechanism.bodies)
		x2 = body.state.x2
		v15 = body.state.v15
		q2 = body.state.q2
		ϕ15 = body.state.ϕ15
		pack_maximal_state!(z, x2, v15, q2, ϕ15, i)
	end
	return z
end

"""
    get_next_state(mechanism)

    return the maximal state of mechanism after one simulation step

    mechanism: Mechanism
"""
function get_next_state(mechanism::Mechanism{T,Nn,Ne,Nb,Ni}) where {T,Nn,Ne,Nb,Ni}
	timestep= mechanism.timestep
	z_next = zeros(T, 13Nb)
	for (i, body) in enumerate(mechanism.bodies)
        x3, v25, q3, ϕ25 = next_configuration_velocity(body.state, timestep)
		z_next[13 * (i-1) .+ (1:13)] = [x3; v25; vector(q3); ϕ25]
	end
	return z_next
end

# minimal
"""
    get_minimal_state(mechanism)

    return minimal state for mechanism

    mechanism: Mechanism
"""
function get_minimal_state(mechanism::Mechanism{T,Nn,Ne,Nb,Ni}) where {T,Nn,Ne,Nb,Ni}
	x = []

	mechanism = deepcopy(mechanism)
	timestep= mechanism.timestep

	for id in mechanism.root_to_leaves
		(id > Ne) && continue # only treat joints
		joint = mechanism.joints[id]
		c = zeros(T,0)
		v = zeros(T,0)
		pbody = get_body(mechanism, joint.parent_id)
		cbody = get_body(mechanism, joint.child_id)
		for element in [joint.translational, joint.rotational]
			pos = minimal_coordinates(element, pbody, cbody)
			vel = minimal_velocities(element, pbody, cbody, timestep)
			push!(c, pos...)
			push!(v, vel...)
		end
		push!(x, [c; v]...)
	end
	x = [x...]
	return x
end

function get_minimal_coordinates(mechanism::Mechanism{T}) where T
    d = Dict{Int,Vector{T}}()
    for joint in mechanism.joints
        push!(d, joint.id => minimal_coordinates(mechanism, joint))
    end
    return d
end


text:
@testset "compute_v.jl" begin

end


text:
# Test environment
using Pkg
Pkg.add("Documenter")
Pkg.add("LaTeXStrings")

max_stars_repo_name: charleskawczynski/PkgTemplates

max_issues_repo_name: charleskawczynski/PkgTemplates

max_forks_repo_name: charleskawczynski/PkgTemplates


text:
###############################################################################
## Logistic Problem
#    min_x  1/m * ∑_{i=1}^m log(1 + exp(-y_i dot(A_i, x))) + g(reg, x)
#
#    x : R^n
#    y : R^m observations, *values -1 and 1*
#    A : mxn matrix of samples

mutable struct LogisticPb{Tr, Tm, Txman} <: CompositeProblem
    A::Matrix{Float64}
    y::Vector{Float64}
    regularizer::Tr
    n::Int64
    x0::Txman
    M_x0::Tm
    λ₂::Float64
    function LogisticPb(
            A::Matrix{Float64},
            y::Vector{Float64},
            regularizer::Tr,
            n::Int64,
            x0::Txman,
            M_x0::Tm,
            λ₂::Float64
        ) where {Tr, Tm, Txman}
        @assert Set(y) ⊆ Set([-1.0, 1.0]) "Logistic rhs vector shoudl take values -1.0, 1.0, here: $(Set(y))."
        return new{Tr, Tm, Txman}(A, y, regularizer, n, x0, M_x0, λ₂)
    end
end

problem_dimension(pb::LogisticPb) = pb.n

"""
    logsig(t)

Compute the logarithm of sigmoid `-log(1+exp(-t))` with higher precision than plain
implementation.

Reference:
- F. Pedragosa's blog post http://fa.bianp.net/blog/2019/evaluate_logistic/
"""
@inline function logsig(t)
    if t < -33.3
        return t
    elseif t <= -18
        return t - exp(t)
    elseif t <= 37
        return -log1p(exp(-t))
    else
        return -exp(-t)
    end
end

## f
# 0th order
function f(pb::LogisticPb, x)
    m = size(pb.A, 1)

    Ax = pb.A * x
    fval = 0.0
    @inbounds @simd for i in 1:m
        fval -= logsig(pb.y[i] * Ax[i])
    end

    return fval / m + 0.5 * pb.λ₂ * norm(x, 2)^2
end

# 1st order
σ(x) = 1/(1+exp(-x))
function ∇f!(pb::LogisticPb, res, x)
    m = size(pb.A, 1)

    σyAx = pb.A * x
    σyAx .*= -pb.y
    σyAx .= σ.(σyAx)
    res .= transpose(pb.A) * (σyAx .* pb.y)
    res ./= -m
    res .+= pb.λ₂ .* x

    return res
end

# 2nd order
∇σ(x) = σ(x) * σ(-x)
function ∇²f_h!(pb::LogisticPb, res, x, h)
    m = size(pb.A, 1)

    yAx = -pb.y .* (pb.A * x)
    Ah = pb.A * h

    res .= transpose(pb.A) * (Ah .* ∇σ.(yAx))
    res ./= m
    res .+= pb.λ₂ .* h

    return res
end

# conditioning
function get_gradlips(pb::LogisticPb)
    m = size(pb.A, 1)
    return opnorm(pb.A)^2 / m + pb.λ₂
end
# get_μ_cvx(pb::LogisticPb) = (svdvals(pb.A)[end])^2


text:
"""
    base_ip_model(nteams::Int, isphased::Bool)

Builds the base IP model, adding all base constraints for a RR2-timetable with
`nteams` teams and enforces a phased structure if `isphased == true`.

# Arguments

- `nteams::Int`: the number of teams participating
- `isphased::Bool`: sets the format of the tournament to be phased, enforcing
    each pair of teams to meet once in each half of the tournament

# Returns

- `Model` - Gurobi Optimizer model

# Model Description

The model uses the following binary variables:
- `x[i,j,s]` - Team `i` plays team `j` at home in slot `s`.
- `bh[i,s]` - Team `i` has a home break in slot `s`.
- `ba[i,s]` - Team `i` has an away break in slot `s`.
"""
function base_ip_model(nteams::Int, isphased::Bool)
    nslots = 2 * nteams - 2
    model = direct_model(Gurobi.Optimizer(GRB_ENV))

    @variable(model, x[1:nteams, 1:nteams, 1:nslots], Bin)
    @variable(model, bh[1:nteams, 1:nslots], Bin)
    @variable(model, ba[1:nteams, 1:nslots], Bin)

    @constraint(model, [i = 1:nteams], x[i, i, :] .== 0)
    @constraint(model, hgames[s = 1:nteams], sum(x[:, :, s]) == div(nteams, 2))
    @constraint(
        model,
        onefactor[i = 1:nteams, s = 1:nslots],
        sum(x[i, :, s]) + sum(x[:, i, s]) == 1
    )
    @constraint(
        model,
        alldiffh[i = 1:nteams, j = 1:nteams; i != j],
        sum(x[i, j, :]) == 1
    )
    @constraint(
        model,
        [i = 1:nteams, j = 1:nteams, s = 1:nslots],
        x[i, j, s] + x[j, i, s] <= 1
    )
    if isphased
        @constraint(
            model,
            phased[i = 1:nteams, j = 1:nteams; i != j],
            sum(x[i, j, 1:div(nslots, 2)]) + sum(x[j, i, 1:div(nslots, 2)]) ==
            1
        )
    end

    @constraint(
        model,
        [i = 1:nteams, s = 2:nslots],
        ba[i, s] - bh[i, s] + sum(x[i, :, s]) + sum(x[i, :, s-1]) == 1
    )

    @constraint(model, [i = 1:nteams, s = 2:nslots], bh[i, s] + ba[i, s] <= 1)

    return model
end

"""
    ip_model(inst::Instance)

Builds the base IP-model and populates it with the constraints given by `inst`.
"""
function ip_model(inst::Instance)

    @assert(length(inst.constrSE) <= 1)
    @assert(length(inst.constrFA) <= 1)

    rr2 = inst.rr2
    model = base_ip_model(rr2.nteams, rr2.isphased)

    # expression used to build objective function iterativly
    @expression(model, obj, AffExpr(0))

    # adding capacity constraints
    length(inst.constrCA) == 0 || @constraint(
        model,
        ca[i = 1:length(inst.constrCA)],
        expr!(model, inst.constrCA[i]) <= inst.constrCA[i].ub
    )

    # adding break constraints
    length(inst.constrBR) == 0 || @constraint(
        model,
        br[i = 1:length(inst.constrBR)],
        expr!(model, inst.constrBR[i]) <= inst.constrBR[i].ub
    )

    # adding game constraints
    if length(inst.constrGA) != 0
        @constraint(
            model,
            gaub[i = 1:length(inst.constrGA)],
            expr!(model, inst.constrGA[i], true) <= inst.constrGA[i].ub
        )
        @constraint(
            model,
            galb[i = 1:length(inst.constrGA)],
            inst.constrGA[i].lb <= expr!(model, inst.constrGA[i], false)
        )
    end

    # adding fairness constraints
    if length(inst.constrFA) > 0
        cfa = only(inst.constrFA)
        for i in cfa.teams
            for j in cfa.teams
                j <= i && continue
                offset = add_offset_var(model, cfa)
                for s in cfa.slots
                    @constraint(
                        model,
                        expr!(model, cfa, i, j, s) <= cfa.ub + offset
                    )
                    @constraint(
                        model,
                        expr!(model, cfa, j, i, s) <= cfa.ub + offset
                    )
                end
            end
        end
    end

    # adding separation constraints
    if length(inst.constrSE) > 0
        cse = only(inst.constrSE)
        for i in cse.teams
            for j in cse.teams
                j <= i && continue
                homefirst = @variable(model, binary = true)
                @constraint(
                    model,
                    expr!(model, cse, i, j, true) >=
                    cse.lb + 2 * rr2.nslots * homefirst - 2 * rr2.nslots
                )
                @constraint(
                    model,
                    expr!(model, cse, i, j, false) >=
                    cse.lb - 2 * rr2.nslots * homefirst
                )
            end
        end
    end

    @objective(model, Min, model[:obj])
    model
end

"""
    game_alloc_model(
        rr2::RR2,
        patternset::Vector{Int64},
        cons::Vector{Constraint};
        feas_relax = false,
    )

Creates the game allocation model for a given `patternset`. If
`feas_relax == true` a feasibility relaxation is computed, minimizing the offset
of the generic constraints `cons`.
"""
function game_alloc_model(
    rr2::RR2,
    patternset::Vector{Int64},
    cons::Vector{Constraint};
    feas_relax = false,
)
    ps(j, s) = patternset[j] >>> (s - 1) & 1
    gam = direct_model(Gurobi.Optimizer())

    @variable(gam, x[1:rr2.nteams, 1:rr2.nteams, 1:rr2.nslots], Bin)

    # setting pattern set
    for s = 1:rr2.nslots
        for i = 1:rr2.nteams
            for j = 1:rr2.nteams
                if ps(i, s) == 1
                    fix(x[j, i, s], 0; force = true)
                else
                    fix(x[i, j, s], 0; force = true)
                end
            end
        end
    end
    # one-factor
    @constraint(
        gam,
        [i = 1:rr2.nteams, s = 1:rr2.nslots],
        sum(x[i, :, s]) + sum(x[:, i, s]) == 1
    )
    # alldiff
    @constraint(
        gam,
        [i = 1:rr2.nteams, j = 1:rr2.nteams; i != j],
        sum(x[i, j, :]) == 1
    )

    # phased cons
    if rr2.isphased
        @constraint(
            gam,
            [i = 1:rr2.nteams, j = i+1:rr2.nteams],
            sum(x[i, j, 1:rr2.nslots÷2]) + sum(x[j, i, 1:rr2.nslots÷2]) == 1
        )
        @constraint(
            gam,
            [i = 1:rr2.nteams, j = i+1:rr2.nteams],
            sum(x[i, j, rr2.nslots÷2+1:end]) +
            sum(x[j, i, rr2.nslots÷2+1:end]) == 1
        )
    end

    offsets = Vector{VariableRef}()
    for con in cons
        @assert isa(con, CAcon) || isa(con, GAcon)
        if isa(con, CAcon)
            os = feas_relax ? @variable(gam, lower_bound = 0) : 0
            feas_relax && push!(offsets, os)
            @constraint(gam, expr!(gam, con) <= con.ub + os)
        elseif isa(con, GAcon)
            os1 = feas_relax ? @variable(gam, lower_bound = 0) : 0
            os2 = feas_relax ? @variable(gam, lower_bound = 0) : 0
            feas_relax && push!(offsets, os1, os2)
            @constraint(gam, expr!(gam, con, true) <= con.ub + os1)
            @constraint(gam, con.lb <= expr!(gam, con, false) + os2)
        end
    end
    if feas_relax
        @objective(gam, Min, sum(offsets))
    end
    return gam
end

"""
    add_offset_var(model::Model, con::Constraint)

Adds an offset variable for a given soft constraint and updates the objective
expression.
"""
function add_offset_var(model::Model, con::Constraint)
    offset = @variable(model, lower_bound = 0)
    add_to_expression!(model[:obj], con.pen, offset)
    offset
end

"""
    expr!(model::Model, con::Constraint, [args...])

Adds an expression to `model` representing the left-hand-side of the
IP-constraint modelling `con`, or if additional `args` are given, one of the
expresions used to model `con`. If the constraint is `soft` offset variables are
added accordingly.

# Arguments

- `model::Model` - The direct model of the Gurobi Optimizer
- `con::Constraint` - The constraint to be modeled
"""
function expr! end

"""
    expr!(model::Model, ca::CAcon)

Adds a capacity constraint expression for `ca`.
"""
function expr!(model::Model, ca::CAcon)
    x = model[:x]
    offset = ca.pen == 0 ? 0 : add_offset_var(model, ca)
    if ca.mode == MODE_H
        return @expression(
            model,
            sum(
                x[i, j, s] for i in ca.teams1 for j in ca.teams2 for
                s in ca.slots
            ) - offset
        )
    elseif ca.mode == MODE_A
        return @expression(
            model,
            sum(
                x[j, i, s] for i in ca.teams1 for j in ca.teams2 for
                s in ca.slots
            ) - offset
        )
    elseif ca.mode == MODE_HA
        return @expression(
            model,
            sum(
                x[j, i, s] + x[i, j, s] for i in ca.teams1 for j in ca.teams2
                for s in ca.slots
            ) - offset
        )
    end
end

"""
    expr!(model::Model, br::BRcon)

Adds a break constraint expression for `br`.
"""
function expr!(model::Model, br::BRcon)
    bh, ba = model[:bh], model[:ba]
    offset = br.pen == 0 ? 0 : add_offset_var(model, br)
    if br.mode == MODE_H
        return @expression(
            model,
            sum(bh[i, s] for i in br.teams for s in br.slots) - offset
        )
    elseif br.mode == MODE_A
        return @expression(
            model,
            sum(ba[i, s] for i in br.teams for s in br.slots) - offset
        )
    elseif br.mode == MODE_HA
        return @expression(
            model,
            sum(bh[i, s] + ba[i, s] for i in br.teams for s in br.slots) -
            offset
        )
    end
end

"""
    expr!(model::Model, ga::GAcon, upperbound)

Adds upper (`upperbound == true`) or lower (`upperbound == false`) bound
expession for `ga`.
"""
function expr!(model::Model, ga::GAcon, upperbound)
    x = model[:x]
    offset = ga.pen == 0 ? 0 : add_offset_var(model, ga)
    if upperbound
        return @expression(
            model,
            sum(
                x[ga.meetings[i][1], ga.meetings[i][2], s] for
                i = 1:length(ga.meetings) for s in ga.slots
            ) - offset
        )
    else
        return @expression(
            model,
            sum(
                x[ga.meetings[i][1], ga.meetings[i][2], s] for
                i = 1:length(ga.meetings) for s in ga.slots
            ) + offset
        )
    end
end

"""
    expr!(model::Model, se::SEcon, i::Int, j::Int, homefirst::Bool)

Adds expression for the separation of games between teams `i` and `j`.
`homefirst == true` is used to model the case that `i` plays the first game at
home, whereas `homefirst == false` is used to model the case that `i` plays the
first game away.
"""
function expr!(model::Model, se::SEcon, i::Int, j::Int, homefirst::Bool)
    x = model[:x]
    offset = se.pen == 0 ? 0 : add_offset_var(model, se)
    if homefirst
        return @expression(
            model,
            -sum(s * x[i, j, s] - s * x[j, i, s] for s = 1:size(x, 3)) +
            offset - 1
        )
    else
        return @expression(
            model,
            sum(s * x[i, j, s] - s * x[j, i, s] for s = 1:size(x, 3)) + offset -
            1
        )
    end
end

"""
    expr!(model::Model, fa::FAcon, i::Int, j::Int, s::Int)

Adds expression of the difference in home games played after slot `s` between
teams `i` and `j`.
"""
function expr!(model::Model, fa::FAcon, i::Int, j::Int, s::Int)
    x = model[:x]
    return @expression(model, sum(x[i, :, 1:s]) - sum(x[j, :, 1:s]))
end

"""
    parse_solution!(sched::Matrix{Int}, sol, model)

parses solution data `sol` into a schedule `sched`.
"""
function parse_solution!(sched::Matrix{Int}, sol, model)
    for s = 1:size(sched, 2)
        for i = 1:size(sched, 1)
            for j = 1:size(sched, 1)
                if sol[i, j, s] == 1.0
                    sched[i, s] = j
                    sched[j, s] = -i
                end
            end
        end
    end
    return sched
end

"""
    parse_solution(model)

Retrieves a solution schedule from `model`.
"""
function parse_solution(model)
    sol = value.(model[:x])
    sched = Array{Int64}(undef, size(sol, 1), size(sol, 3))
    return parse_solution!(sched, sol, model)
end


text:
# Copyright (c) 2019 Harshavardhan P K
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT

function checkNum(x::Int64)::Bool
    x1 = x
    isadj = false
    prev = x % 10
    x = x ÷ 10
    while x > 0
        curr = x % 10
        if prev == curr
            isadj = true
        end
        if prev < curr
            return false
        end
        prev = curr
        x = x ÷ 10
    end
    return isadj
end

function checkNum2(x::Int64)::Bool
    x1 = x
    isadj = false
    isprevadj = false
    prev = x % 10
    x = x ÷ 10
    while x > 0
        curr = x % 10
        if prev < curr
            return false
        end
        prev = curr
        x = x ÷ 10
    end
    x = x1
    prevprev = x % 10
    x = x ÷ 10
    prev = x % 10
    x = x ÷ 10
    if prev == prevprev ≠ x % 10
        return true
    end
    curr = x % 10
    x = x ÷ 10
    while x > 0
        if x % 10 ≠ prev == curr ≠ prevprev
            return true
        end
        prevprev = prev
        prev = curr
        curr = x % 10
        x = x ÷ 10
    end

    if prev == curr ≠ prevprev
        return true
    end

    return false
end

function findnum(l::Int64, r::Int64)::Int32
    count::Int32 = 0
    for x ∈ l:r
        if checkNum(x)
            count += 1
        end
    end
    return count
end

function findnum2(l::Int64, r::Int64)::Int32
    count::Int32 = 0
    for x ∈ l:r
        if checkNum2(x)
            count += 1
        end
    end
    return count
end

println(findnum(134792, 675810))
println(findnum2(134792, 675810))


text:
#=

#######################################################################################################################################

using CSV, DataFrames, Query

cd("C:\\Users\\BCP\\github\\ICP\\Test")
dfX1 = CSV.File("X1.csv") |> DataFrame
dfY1 = CSV.File("Y1.csv") |> DataFrame
dfE1 = CSV.File("ExpInd1.csv") |> DataFrame

dfX2 = CSV.File("X2.csv") |> DataFrame
dfY2 = CSV.File("Y2.csv") |> DataFrame
dfE2 = CSV.File("ExpInd2.csv") |> DataFrame

dfX3small = CSV.File("X3small.csv") |> DataFrame
dfY3samll = CSV.File("Y3small.csv") |> DataFrame
dfE3small = CSV.File("ExpInd3small.csv") |> DataFrame

# college data
dfX3 = CSV.File("X3a.csv") |> DataFrame
dfY3 = CSV.File("Y3a.csv") |> DataFrame
dfE3 = CSV.File("E3a.csv") |> DataFrame

dfX1forest = CSV.File("treeManX1.csv") |> DataFrame
dfY1forest = CSV.File("treeManY1.csv") |> DataFrame
dfE1forest = CSV.File("treeManE1.csv") |> DataFrame

dfX2forest = CSV.File("treeManX2.csv") |> DataFrame
dfY2forest = CSV.File("treeManY2.csv") |> DataFrame
dfE2forest = CSV.File("treeManE2.csv") |> DataFrame

dfX2forest2 = CSV.File("treeX2.csv") |> DataFrame
dfY2forest2 = CSV.File("treeY2.csv") |> DataFrame
dfE2forest2 = CSV.File("treeE2.csv") |> DataFrame

r = LinearInvariantCausalPrediction!(dfX1, dfY1, dfE1, α = 0.02, verbose = false)
##Invariant Casual Predictors:
##1 = V1  2 = V2

r = LinearInvariantCausalPrediction!(dfX2, dfY2, dfE2, α = 0.02, verbose = false)
##Invariant Casual Predictors:
##1 = X1

boxPlotICPconfidenceIntervals( r.X, r.ConfInt)

r = LinearInvariantCausalPrediction!(dfX3, dfY3, dfE3, α = 0.02, verbose = false)
##Invariant Casual Predictors:
##3 = fcollege

plotICPacceptedSets( r.X, r.acceptedSets)
barPlotICPCasualPredictors( r.X, r.casualPredictors)

r = ForestInvariantCausalPrediction!(dfX1forest, dfY1forest, dfE1forest, α = 0.05, verbose = false)
##Invariant Casual Predictors:
##1 = V1

r = ForestInvariantCausalPrediction!(dfX2forest, dfY2forest, dfE2forest, α = 0.05, verbose = false)
## none

r = ForestInvariantCausalPrediction!(dfX3, dfY3, dfE3, α = 0.05, verbose = false)

r = ForestInvariantCausalPrediction!(dfX2forest2, dfY2forest2, dfE2forest2, α = 0.05, verbose = false)
## ERROR: AssertionError: ncol(X) >= 2

r = ForestInvariantCausalPrediction!(dfX3, dfY3, dfE3, α = 0.05, verbose = false)

#=
dfJoin = CSV.File("join_clean.csv"; normalizenames=true) |> DataFrame
#Bolivia
dfJoinICP = dfJoin |> @filter( _.Country_name == "Bolivia") |> DataFrame
E = DataFrame(E = dfJoinICP.Environment)
Y = DataFrame(Y = dfJoinICP.Unemployment_Rate_aged_15_64)
X = select(dfJoinICP, Not([:Unemployment_Rate_aged_15_64, :Environment, :Country_name, :Year_of_Survey]))
r = LinearInvariantCausalPrediction!(X, Y, E, α = 0.01, selection = "all", verbose = false)
boxPlotICPconfidenceIntervals( r.X, r.ConfInt)
plotICPacceptedSets( r.X, r.acceptedSets)
barPlotICPCasualPredictors( r.X, r.casualPredictors)
r = ForestInvariantCausalPrediction!(X, Y, E, α = 0.01, selection = "all", verbose = false)

#Colombia
dfJoinICP = dfJoin |> @filter( _.Country_name == "Colombia") |> DataFrame
E = DataFrame(E = dfJoinICP.Environment)
Y = DataFrame(Y = dfJoinICP.Unemployment_Rate_aged_15_64)
X = select(dfJoinICP, Not([:Unemployment_Rate_aged_15_64, :Environment, :Country_name, :Year_of_Survey]))
r = LinearInvariantCausalPrediction!(X, Y, E, α = 0.05, selection = "all", verbose = false)
r = ForestInvariantCausalPrediction!(X, Y, E, α = 0.01, selection = "all", verbose = false)

#Honduras
dfJoinICP = dfJoin |> @filter( _.Country_name == "Honduras") |> DataFrame
E = DataFrame(E = dfJoinICP.Environment)
Y = DataFrame(Y = dfJoinICP.Unemployment_Rate_aged_15_64)
X = select(dfJoinICP, Not([:Unemployment_Rate_aged_15_64, :Environment, :Country_name, :Year_of_Survey]))
r = LinearInvariantCausalPrediction!(X, Y, E, α = 0.05, selection = "all", verbose = false)
r = ForestInvariantCausalPrediction!(X, Y, E, α = 0.01, selection = "all", verbose = false)
##  missing values, no go

=#

r = LinearInvariantCausalPrediction!(dfX1, dfY1, dfE1, α = 0.02, verbose = false)
r = LinearInvariantCausalPredictionSequential!(dfX1, dfY1, dfE1, α = 0.02, verbose = false)

r = LinearInvariantCausalPrediction!(dfX2, dfY2, dfE2, α = 0.02, verbose = false)
r = LinearInvariantCausalPredictionSequential!(dfX2, dfY2, dfE2, α = 0.02, verbose = false)

r = LinearInvariantCausalPrediction!(dfX3, dfY3, dfE3, α = 0.02, verbose = false)
r = LinearInvariantCausalPredictionSequential!(dfX3, dfY3, dfE3, α = 0.02, verbose = false)

r = ForestInvariantCausalPrediction!(dfX1forest, dfY1forest, dfE1forest, α = 0.05, verbose = true)
r = ForestInvariantCausalPredictionSequential(dfX1forest, dfY1forest, dfE1forest, α = 0.05, verbose = false)

r = ForestInvariantCausalPrediction!(dfX2forest, dfY2forest, dfE2forest, α = 0.05, verbose = true)
r = ForestInvariantCausalPredictionSequential!(dfX2forest, dfY2forest, dfE2forest, α = 0.05, verbose = false)

 r = ForestInvariantCausalPrediction!(dfX3, dfY3, dfE3, α = 0.05, verbose = false)
 #r = ForestInvariantCausalPredictionSequential!(dfX3, dfY3, dfE3, α = 0.05, verbose = false)

=#

#######################################################################################################################################
#=
using CSV, DataFrames

cd("C:\\Users\\BCP\\github\\ICP\\Test")
dfX3 = CSV.File("X3a.csv") |> DataFrame
dfY3 = CSV.File("Y3a.csv") |> DataFrame
dfE3 = CSV.File("E3a.csv") |> DataFrame

dfX1tree = CSV.File("treeManX1.csv") |> DataFrame
dfY1tree = CSV.File("treeManY1.csv") |> DataFrame
dfE1tree = CSV.File("treeManE1.csv") |> DataFrame

dfX2tree = CSV.File("treeManX2.csv") |> DataFrame
dfY2tree = CSV.File("treeManY2.csv") |> DataFrame
dfE2tree = CSV.File("treeManE2.csv") |> DataFrame

dfX2forest2 = CSV.File("treeX2.csv") |> DataFrame
dfY2forest2 = CSV.File("treeY2.csv") |> DataFrame
dfE2forest2 = CSV.File("treeE2.csv") |> DataFrame

numberOfTargets = length(unique(dfY1tree[:, 1]))
numberOfEnvironments = nrow(unique(dfE1tree))
setScientificTypes!(dfX1tree)
InvariantEnvironmentPrediction(dfX1tree, dfY1tree[:,1], dfE1tree, numberOfTargets, numberOfEnvironments)

numberOfTargets = length(unique(dfY2tree[:, 1]))
numberOfEnvironments = nrow(unique(dfE2tree))
setScientificTypes!(dfX2tree)
InvariantEnvironmentPrediction(dfX2tree, dfY2tree[:,1], dfE2tree, numberOfTargets, numberOfEnvironments)

numberOfTargets = length(unique(dfY2forest2[:, 1]))
numberOfEnvironments = nrow(unique(dfE2forest2))
setScientificTypes!(dfX2forest2)
InvariantEnvironmentPrediction(dfX2forest2, dfY2forest2[:,1], dfE2forest2, numberOfTargets, numberOfEnvironments)

# college data
numberOfTargets = length(unique(dfY3[:, 1]))
numberOfEnvironments = nrow(unique(dfE3))
setScientificTypes!(dfX3)
InvariantEnvironmentPrediction(dfX3, Float64.(dfY3[:,1]), dfE3, numberOfTargets, numberOfEnvironments)
=#

#=
using CSV, DataFrames, Distributions
include("ICPlibrary.jl")

using .InvariantEnvironmentPredictionModule

# Example 1
n = 1000
p = 0.2
E =  rand(Binomial(1,p), n)
X = 4 .+ 2 .* E .+ rand(Normal(), n)
Y = 3 .* (X).^2 .+ rand(Normal(), n)
dfX = DataFrame(X=X)
dfY = DataFrame(Y=Y)
dfE = DataFrame(E=E)
numberOfTargets = length(unique(dfY[:, 1]))
numberOfEnvironments = nrow(unique(dfE))
ICPlibrary.setScientificTypes!(dfX)
InvariantEnvironmentPrediction(dfX, Float64.(dfY[:,1]), dfE, numberOfTargets, numberOfEnvironments)

#example 1 from R code
cd("C:\\Users\\BCP\\github\\ICP\\Test")
dfX = CSV.File("Xexample1.csv") |> DataFrame
dfY = CSV.File("Yexample1.csv") |> DataFrame
dfE = CSV.File("Eexample1.csv") |> DataFrame
InvariantEnvironmentPrediction(dfX, Float64.(dfY[:,1]), dfE, numberOfTargets, numberOfEnvironments)
=#

##########################################################################################################################################################
# Testing the examples from the IPC manual.  The X, Y and the other inputs are created in RStudio and saved in CSV file to read here
# The Julia ouput is compared to the RStudio output
##########################################################################################################################################################

#=
using CSV, DataFrames

cd("C:\\Users\\BCP\\github\\ICP\\Test")
dfX1 = CSV.File("X1.csv") |> DataFrame
dfY1 = CSV.File("Y1.csv") |> DataFrame
dfE1 = CSV.File("ExpInd1.csv") |> DataFrame

dfX2 = CSV.File("X2.csv") |> DataFrame
dfY2 = CSV.File("Y2.csv") |> DataFrame

# College data
dfX3 = CSV.File("X3a.csv") |> DataFrame
dfY3 = CSV.File("Y3a.csv") |> DataFrame
dfE3 = CSV.File("E3a.csv") |> DataFrame

################## Simulate data with interventions

Xhot1 = ICPlibrary.hotEncoder(dfX1)
Xhot2 = ICPlibrary.hotEncoder(dfX2)
Xhot3 = ICPlibrary.hotEncoder(dfX3)

r = getBlanketBoosting(Xhot1, dfY1, "booster", ncol(Xhot1)-1)
r = getBlanketBoosting(Xhot2, dfY2, "booster", ncol(Xhot2)-1)
r = getBlanketBoosting(Xhot3, dfY3, "booster", ncol(Xhot3)-1)
# println("result \t", r)

# calling the XGBooster
# r = getBlanketBoosting(dfX3, dfY3, 3)
# println("maxNoVariables=2 \t", r)

r = getBlanketRandomForest(Xhot1, dfY1, "forest")
r = getBlanketRandomForest(Xhot2, dfY2, "forest")
r = getBlanketRandomForest(Xhot3, dfY3, "forest")
# println("result \t", r)

# ncolumns = 3
# X = MLJ.table(rand("abc", (10, ncolumns))|> categorical);
# schema(X)
# Xhot = hotEncoder(DataFrame(X))
# y = rand("MF", 10) |> categorical;
# r = getBlanketRandomForest(Xhot, DataFrame(y=y), "tree", ncol(Xhot)-1)

##################  College Distance data

# r = getBlanketBoosting(dfX3, dfY3)
# println("maxNoVariables=10 \t", r)

# calling the XGBooster
# r = getBlanketBoosting(dfX3, dfY3.Y, 3)
# println("maxNoVariables=2 \t", r)

=#

##########################################################################################################################################################
# Testing the examples from the IPC manual.  The X, Y and the other inputs are created in RStudio and saved in CSV file to read here
# The Julia ouput is compared to the RStudio output
##########################################################################################################################################################

#=
using  CSV, DataFrames
cd("C:\\Users\\BCP\\github\\ICP\\Test")

dfX1 = CSV.File("X1.csv") |> DataFrame
dfY1 = CSV.File("Y1.csv") |> DataFrame
dfE1 = CSV.File("ExpInd1.csv") |> DataFrame

dfX2 = CSV.File("X2.csv") |> DataFrame
dfY2 = CSV.File("Y2.csv") |> DataFrame
dfE2 = CSV.File("ExpInd2.csv") |> DataFrame

dfX3 = CSV.File("X3small.csv") |> DataFrame
dfY3 = CSV.File("Y3small.csv") |> DataFrame
dfE3 = CSV.File("ExpInd3small.csv") |> DataFrame

dfX3 = CSV.File("X3a.csv") |> DataFrame
dfY3 = CSV.File("Y3a.csv") |> DataFrame
dfE3 = CSV.File("E3a.csv") |> DataFrame

numberOfTargets = length(unique(dfY3[:, 1]))
numberOfEnvironments = nrow(unique(dfE3))
X = hotEncoder(dfX3)

r = getPValueLinear(X[:,[1,3] ], float.(dfY3[:,1]), dfE3, numberOfTargets, numberOfEnvironments )
println("\n\ngetPValue \t", r) =#

# xSample = [1, 2, 3, 4, 5]
# ySample = [6, 7, 8, 9, 5]
# pval = getpvalClassification(Float64.(xSample), Float64.(ySample))
# pval = getpvalClassification(Float64.(xSample), Float64.(ySample), 500, "crazy")
# println("\n pval:  ", pval)

# setScientificTypes!(dfX3)
# y = float.(dfY3.Y)
# r = getPValue(dfX3[:,[3,8,9,10]], y, dfE3,2,2)
# println("getPValue \t", r)

#################################################################################################################

#=
using CSV, Queryverse, DataFrames

cd("C:\\Users\\BCP\\github\\ICP\\Test")

#dfX3 = CSV.File("X3small.csv") |> DataFrame
#dfY3 = CSV.File("Y3small.csv") |> DataFrame
#dfE3 = CSV.File("ExpInd3small.csv") |> DataFrame

dfX3 = CSV.File("X3a.csv") |> DataFrame
dfY3 = CSV.File("Y3a.csv") |> DataFrame
dfE3 = CSV.File("E3a.csv") |> DataFrame

dfJoin = CSV.File("join_clean.csv"; normalizenames=true) |> DataFrame
#Bolivia
dfJoinICP = dfJoin |> @filter( _.Country_name == "Bolivia") |> DataFrame
E = DataFrame(E = dfJoinICP.Environment)
Y = DataFrame(Y = dfJoinICP.Unemployment_Rate_aged_15_64)
Xb = select(dfJoinICP, Not([:Unemployment_Rate_aged_15_64, :Environment, :Country_name, :Year_of_Survey]))

#Colombia
dfJoinICP = dfJoin |> @filter( _.Country_name == "Colombia") |> DataFrame
E = DataFrame(E = dfJoinICP.Environment)
Y = DataFrame(Y = dfJoinICP.Unemployment_Rate_aged_15_64)
Xc = select(dfJoinICP, Not([:Unemployment_Rate_aged_15_64, :Environment, :Country_name, :Year_of_Survey]))

#Honduras
dfJoinICP = dfJoin |> @filter( _.Country_name == "Honduras") |> DataFrame
E = DataFrame(E = dfJoinICP.Environment)
Y = DataFrame(Y = dfJoinICP.Unemployment_Rate_aged_15_64)
Xh = select(dfJoinICP, Not([:Unemployment_Rate_aged_15_64, :Environment, :Country_name, :Year_of_Survey]))

println("\n\n")
Xhot = hotEncoder(Xc)
println(first(Xhot))

Xhot = hotEncoder(Xb)
println(first(Xhot))

println("\n\n")
Xhot = hotEncoder(Xh)
println(first(Xhot))

println("\n\n")
Xhot = hotEncoder(dfX3)
println(first(Xhot)) =#

#=
using Distributed
addprocs(2)  # get ready for parallelism
@everywhere using MLJ
@everywhere using DataFrames
@everywhere forest_model = MLJ.@load RandomForestClassifier pkg=DecisionTree

@everywhere function fitter(tmRandomForestClassifier, X, y)
    mtm = machine(tmRandomForestClassifier, X, y)
    fit!(mtm)
    ŷ = MLJ.predict(mtm, X)
    return (ŷ)
end

##############
using CSV
cd("C:\\Users\\BCP\\github\\ICP\\Test")

X = CSV.File("treeX1.csv") |> DataFrame
Y = CSV.File("treeY1.csv") |> DataFrame

Y = Y[:,1] |> categorical

@everywhere pipeRandomForestClassifier = @pipeline RandomForestClassifierPipe(
    selector = FeatureSelector(),
    hot = OneHotEncoder(),
    tree = RandomForestClassifier()) prediction_type = :probabilistic

cases = [[Symbol(names(X)[j]) for j in 1:i] for i in 1:ncol(X)]
r1 = range(pipeRandomForestClassifier, :(selector.features), values=cases)

tmRandomForestClassifier = TunedModel(
    model = pipeRandomForestClassifier,
    range=r1,
    measures=[cross_entropy, BrierScore()],
    resampling=CV(nfolds=9)
)

spawnref = @spawnat :any fitter(tmRandomForestClassifier, X, Y)

result = fetch(spawnref)
print( "result ", result, "  ", typeof(result))

=#


text:
# this is the interface
macro rename(t::Symbol,
    by::Union{Vector{Symbol}, Nothing},
    sort::Union{Vector{Symbol}, Nothing},
    arguments::Union{Vector{Symbol},Union{Expr, Nothing}},
    filter::Union{Expr, Nothing},
    use::Union{String, Nothing},
    options::Union{Dict{String,Any}, Nothing})
    error("""\n
        The syntax is:
            rename <var_from> <var_to>
    """)
end

# this is `keep <varlist>`
macro rename(t::Symbol, by::Nothing, sort::Nothing, arguments::Vector{Symbol}, filter::Nothing, use::Nothing, options::Nothing)
    return esc(
        quote
            Douglass.@rename!($t, $arguments)
        end
    )
end

macro rename!(t::Symbol, arguments::Vector{Symbol})
    (size(arguments,1) == 2) || error("`rename` expects exactly two variable symbols.")
    # construct QuoteNode's
    from_qn = QuoteNode(arguments[1])
    to_qn = QuoteNode(arguments[2])
    return esc(
        quote
            ($(from_qn) ∈ propertynames($t)) || error("Variable $($(from_qn)) not present in active DataFrame.")

            # uses DataFrames
            rename!($t, $(from_qn) => $(to_qn))
        end
    )
end