### A Pluto.jl notebook ###
# v0.15.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

# ╔═╡ 7056a580-e8b6-11ea-2ac9-ad6ced139615
begin
    using Random
    using UnPack
	using Plots
	using Statistics
	using ActuaryUtilities
	using ThreadsX # for easy multithreading
end

# ╔═╡ f04b8ce0-ea39-11ea-05ad-a996c592cc82
md"""
# AAA ESG Exploration Tool
This reactive notebook explores the sensitivity of the American Academy of Actuaries' (AAA) [Economic Scenario Generator (ESG)](https://www.actuary.org/content/economic-scenario-generators) to changing parameters.

It's a simple demo intended to be informative about the underlying model producing the interest rates, as well as showcase some aspects of the Julia ecosystem:
- **Reactive Notebooks** interact with and learn from your data in ways traditional notebooks won't let you.
   - Cell order doesn't matter!
   - Hover over the cells and click the eye icon to expand the cell to see any hidden code cells.
- **Performance** - Work with thousands of simulations and visualize them in realtime
- **Syntax** - Expand some of the code cells below to see code that looks more like math, e.g. this line of code:
```julia
α_τ =(1-β₂) * α_τ + β₂ * τ₂ + ϕ * (log(r₁) - log(τ₁)) + σ₂ * shock_short * r₁^θ
```
- **Dynamic and robust plotting libaraies** - `Plots.jl` in this case

"""

# ╔═╡ 018237f0-ea3c-11ea-387e-abf5110ac1fa
md"""
## Interactive ESG
### Interactive Parameters:
You can edit these and watch the results change in realtime.
"""

# ╔═╡ dabece20-ea37-11ea-2e6a-61eda948928f
@bind n_scenarios html"""
	Number of Scenarios: <input type='number' min='1' max='5' value='200' />

	"""

# ╔═╡ 63507e40-ea34-11ea-00c3-31bd881aa1bc
@bind τ₁ html"""
	Long Term Mean Reversion Point <input type='range' min='0.0' max='0.1' value='0.035' step='0.005'
	oninput=\"document.getElementById('fPrice').innerHTML = this.value\" />
        0.035
	"""

# ╔═╡ 13c9baf0-ea37-11ea-16d5-f9f87c35e4f4
@bind vol html"""
	Process Volatility <input type='range' min='0.05' max='0.3' value='0.11489' step='0.005'
	oninput=\"document.getElementById('fPrice').innerHTML = this.value\" />
        0.11489
	"""

# ╔═╡ 2cc3d3b0-ea3c-11ea-2c4f-4bd1db712846
md"### Scenario Visualization"

# ╔═╡ 9a6a036e-b4f1-40a8-b3ea-724a1542fb26
md"
#### Long Rate Statistics:
"

# ╔═╡ 56f67070-ea3c-11ea-31f5-b7d1f7182a31
md"This histogram was inspired by the article [Illuminating the Low Interest Peril](https://www.soa.org/globalassets/assets/library/newsletters/financial-reporter/2020/july/fr-2020-iss-07.pdf) in the July 2020 Financial Reporter:"

# ╔═╡ 844a9e50-f128-11ea-10b1-a1d7792c8fc4
md"Example of one of the the scenarios:"

# ╔═╡ 23cba4a0-ea3b-11ea-2b62-8dfc856875b7
md"""
# Notebook Details
The following cells contain the code that generates the parameters and scenarios

## Deploying a dashboard/tool like this

You likely had to download it and run it yourself because [JuliaActuary](https://juliaactuary.org/) doesn't have a server laying around for you to run stochastic simulations on!

However, as you can see it's running over HTTP so would be straightforward to take something like this and make it an end-user dashboard.
"""

# ╔═╡ f15e18e0-ea3a-11ea-3868-d9236a4810a1
md"The starting curve is based on 12/31/2020:"

# ╔═╡ 79cfe060-e9f4-11ea-2fc8-b769419cc4fd
start_curve = [0.0155, 0.0160, 0.0159, 0.0158, 0.0162, 0.0169, 0.0183, 0.0192, 0.0125, 0.0239]

# ╔═╡ 49419d10-e861-11ea-36e0-e19ece503681
# this is a function which takes a couple of input parameters and returns a named
# tuple with all of the required parameters for the AAA ESG
full_params(τ₁=0.035,vol=0.11489
	) = (
	τ₁ = τ₁,   # Long term rate (LTR) mean reversion
	β₁ = 0.00509, # Mean reversion strength for the log of the LTR
	θ = 1,
	τ₂ = 0.01,    # Mean reversion point for the slope
	β₂ = 0.02685, # Mean reversion strength for the slope
	σ₂ = 0.04148, # Volatitlity of the slope
	τ₃ = 0.0287,  # mean reversion point for the vol of the log of LTR
	β₃ = 0.04001, # mean reversion strength for the log of the vol of the log of LTR
	σ₃ = vol, # vol of the stochastic vol process
	ρ₁₂ = -0.19197, # correlation of shocks to LTR and slope (long - short)
	ρ₁₃ = 0.0,  # correlation of shocks to long rate and volatility
	ρ₂₃ = 0.0,  # correlation of shocks to slope and volatility
	ψ = 0.25164,
	ϕ = 0.0002,
	r₂_min = 0.01, # soft floor on the short rate
	r₂_max = 0.4, # unused - maximum short rate
	r₁_min = 0.015, # soft floor on long rate before random shock
	r₁_max = 0.18, # soft cap on long rate before random shock
	κ = 0.25, # unused - when the short rate would be less than r₂_min it was κ * long
	γ = 0.0, # unused - don't change from zero
	σ_init = 0.0287,
	months = 12 * 30,
	rate_floor = 0.0001, # absolute rate floor
	maturities = [0.25,0.5,1,2,3,5,7,10,20,30],
)

# ╔═╡ 1a5bb1b0-ea33-11ea-3514-e1c85b65d356
params = full_params(τ₁,vol)

# ╔═╡ 4ff1ccc0-e8b6-11ea-20be-d7992829086d
# This replicates the American Academy of Actuaries' scenario generator v7.1.202005
# No guarantees on the output!
function scenario(start_curve,params)

	# unpack the params into the named variables
    @unpack τ₁,β₁,θ,τ₂,β₂,σ₂,τ₃,β₃,σ₃,ρ₁₂,ρ₁₃,ρ₂₃,ψ,ϕ,r₂_min,r₂_max,r₁_min,r₁_max,κ,γ,σ_init,months,rate_floor,maturities = params

    # some constants
	const1 =  √(1-ρ₁₂^2)
	const2 = (ρ₂₃-ρ₁₂*ρ₁₃)/(const1)
	const3 = √(1-((ρ₂₃ - ρ₁₂*ρ₁₃)^2)/(1ρ₁₂^2)-ρ₁₃^2)
	const4 = β₃ * log(τ₃)
	const5 = β₁ * log(τ₁)

	# Nelson Siegel interpolation factors
	ns_interp = [(1 - exp(-0.4 * m)) / ( 0.4 * m) for m in maturities]

	# containers for hot values
	rates = zeros(months,10) # allocate initial
	shock = zeros(3)
	ns_fitted = zeros(10)

	# initial values
	v_τ = log(σ_init) # long rate log vol
	σ_longvol = σ_init
	α_τ = start_curve[9]-start_curve[3]
	pertubation = 1.0

	r₁ = start_curve[9]
	r₂ = max(r₂_min,start_curve[3])

    b₁ = (r₂ - r₁) / (ns_interp[3] - ns_interp[9])
    b₀ = r₂ - b₁ * ns_interp[3]

	ns_fitted .= b₀ .+ b₁ .* ns_interp
	start_diff = ns_fitted .- start_curve

	for month in 1:months

		## Correlated Normals
        randn!(shock)
        # shock .= norms'[:,month]
		shock_long  = shock[1]
		shock_short = shock[1] * ρ₁₂ + shock[2] * const1
		shock_vol   = shock[1] * ρ₁₃ + shock[2] * const2 + shock[3] * const3

		## Generator Process
		v_τ =(1-β₃) * v_τ + const4 + σ₃ * shock_vol
		σ_longvol = exp(v_τ)

        # moved this after r₁ because it uses the prior val

        α_τ_prior = α_τ
        α_τ =(1-β₂) * α_τ + β₂ * τ₂ + ϕ * (log(r₁) - log(τ₁)) + σ₂ * shock_short * r₁^θ

        ## Generator Results

        r_pre = (1-β₁)*log(r₁)+const5+ψ*(τ₂-α_τ_prior)
		r₁ = exp(clamp(r_pre,log(r₁_min),log(r₁_max)) + σ_longvol * shock_long)

		r₂ = max(r₁ - α_τ,r₂_min)

		## Nelson-Siegel Fitted Curve
		ns_fitted .= b₀ .+ b₁ .* ns_interp

        b₁ = (r₂ - r₁) / (ns_interp[3] - ns_interp[9])
        b₀ = r₂ - b₁ * ns_interp[3]

        ## Fully Interpolated Curve

		rates[month,:] .= max.(rate_floor, ns_fitted .- pertubation .* start_diff)

		# Update values for next loop
		pertubation = max(0.0,pertubation - 1/12)

	end

	return rates
end

# ╔═╡ c5078e00-e8b6-11ea-105b-9731d9852664
scenarios = ThreadsX.map(i -> scenario(start_curve,params), 1:n_scenarios);

# ╔═╡ 076facc0-e9f5-11ea-2fa5-91cfaa965d08
# note that the CTE function requires ActuaryUtilities v2.1 or higher
let
	# average and CTE70 and CTE98 of 20 year rates
	stats = ThreadsX.map(s -> (
			mean= mean(s[:,9]),
			CTE70=CTE(s[:,9],.7,rev=true),
			CTE98=CTE(s[:,9],.98,rev=true)
			),scenarios)
	h1 = histogram(
		[x.mean for x in stats],
		label="",
		orientation = :horizontal,
		alpha=0.5,
		ylim = (0.,.1),
		ylabel="20-Year Interset Rate",
		xlim=(0,(n_scenarios ÷ 20)),
		xtick=:none,
		grid=false,
		title="Mean",
		bins = 0.:0.0025:.1,
	)
 		h2 = histogram(
		[x.CTE70 for x in stats],
		alpha=0.5,
		title="CTE70",
		label="",
		ylim = (0.,.1),
		xlim=(0,(n_scenarios ÷ 20)),
		xtick=:none,
		ytick=:none,
		orientation = :horizontal,
		bins = 0.:0.0025:.1,
	)
	 	h3 = histogram(
		[x.CTE98 for x in stats],
		alpha=0.5,
		title="CTE98",
		label="",
		ylim = (0.,.1),
		xlim=(0,(n_scenarios ÷ 20)),
		xtick=:none,
				ytick=:none,
		orientation = :horizontal,

		bins = 0.:0.0025:.1,
	)
	plot([h1,h2,h3]...,layout=(1,3))
end

# ╔═╡ 7f7a2280-ea2d-11ea-2ee8-79bc4c3a72ec
 let
 	p = plot(legend=false,title="Long Rate Paths",ylim=(0,.15))

 	for s in scenarios
 		plot!(p,s[:,9], color=:blue, alpha=0.05)
 	end
 	p
 end

# ╔═╡ 783904d0-f128-11ea-22f9-43d6dc7b8439
scenarios[1]

# ╔═╡ c684fec1-0e7c-4c74-be96-c9b1d8c4c9af
plot(sum(hcat([s[:,9] for s in scenarios]...),dims=2) ./ n_scenarios)

# ╔═╡ 69730d30-ea37-11ea-05be-8350df76fd06
md"""
The same disclaimer from the Academy applies:

    From time to time, the American Academy of Actuaries makes available through its website or other means various scenarios and tools. The Academy takes reasonable steps to develop such scenarios and tools consistent with accepted actuarial principles and practices. However, the Academy does not warranty these scenarios and tools as fit for use in any respect, and no warranty should be assumed or implied by any individual. Actuaries, insurers, regulators and other parties use the Academy's scenarios and tools at their own risk. The Academy disclaims all responsibility for any party's use or misuse of its scenarios or tools and for any work product generated through use or misuse of the scenarios and tools.

"""

# ╔═╡ 00000000-0000-0000-0000-000000000001
PLUTO_PROJECT_TOML_CONTENTS = """
[deps]
ActuaryUtilities = "bdd23359-8b1c-4f88-b89b-d11982a786f4"
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
ThreadsX = "ac1d9e8a-700a-412c-b207-f0111f4b6c0d"
UnPack = "3a884ed6-31ef-47d7-9d2a-63182c4928ed"

[compat]
ActuaryUtilities = "~2.2.3"
Plots = "~1.19.1"
ThreadsX = "~0.1.7"
UnPack = "~1.0.2"
"""

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

[[ActuaryUtilities]]
deps = ["Dates", "ForwardDiff", "QuadGK", "Roots", "StatsBase", "Yields"]
git-tree-sha1 = "b2bd57203bd96fc7026a719473bae0287ea311a2"
uuid = "bdd23359-8b1c-4f88-b89b-d11982a786f4"
version = "2.2.3"

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

[[ArgCheck]]
git-tree-sha1 = "dedbbb2ddb876f899585c4ec4433265e3017215a"
uuid = "dce04be8-c92d-5529-be00-80e4d2c0e197"
version = "2.1.0"

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

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

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

[[BangBang]]
deps = ["Compat", "ConstructionBase", "Future", "InitialValues", "LinearAlgebra", "Requires", "Setfield", "Tables", "ZygoteRules"]
git-tree-sha1 = "e239020994123f08905052b9603b4ca14f8c5807"
uuid = "198e06fe-97b7-11e9-32a5-e1d131e6ad66"
version = "0.3.31"

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

[[Baselet]]
git-tree-sha1 = "aebf55e6d7795e02ca500a689d326ac979aaf89e"
uuid = "9718e550-a3fa-408a-8086-8db961cd8217"
version = "0.1.1"

[[Bzip2_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "c3598e525718abcc440f69cc6d5f60dda0a1b61e"
uuid = "6e34b625-4abd-537c-b88f-471c36dfa7a0"
version = "1.0.6+5"

[[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 = "e2f47f6d8337369411569fd45ae5753ca10394c6"
uuid = "83423d85-b0ee-5818-9007-b63ccbeb887a"
version = "1.16.0+6"

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

[[ColorSchemes]]
deps = ["ColorTypes", "Colors", "FixedPointNumbers", "Random", "StaticArrays"]
git-tree-sha1 = "ed268efe58512df8c7e224d2e170afd76dd6a417"
uuid = "35d6a980-a343-548e-a6ea-1d62b119f2f4"
version = "3.13.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"

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

[[CommonSubexpressions]]
deps = ["MacroTools", "Test"]
git-tree-sha1 = "7b8a93dba8af7e3b42fecabf646260105ac373f7"
uuid = "bbf7d656-a473-5ed7-a52c-81e309532950"
version = "0.3.0"

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

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

[[CompositionsBase]]
git-tree-sha1 = "455419f7e328a1a2493cabc6428d79e951349769"
uuid = "a33af91c-f02d-484b-be07-31d278c5ca2b"
version = "0.1.1"

[[ConstructionBase]]
deps = ["LinearAlgebra"]
git-tree-sha1 = "f74e9d5388b8620b4cee35d4c5a618dd4dc547f4"
uuid = "187b0558-2788-49d3-abe0-74a17ed4e7c9"
version = "1.3.0"

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

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

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

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

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

[[DefineSingletons]]
git-tree-sha1 = "77b4ca280084423b728662fe040e5ff8819347c5"
uuid = "244e2a9f-e319-4986-a169-4d1fe445cd52"
version = "0.1.1"

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

[[DiffResults]]
deps = ["StaticArrays"]
git-tree-sha1 = "c18e98cba888c6c25d1c3b048e4b3380ca956805"
uuid = "163ba53b-c6d8-5494-b064-1a9d43ac40c5"
version = "1.0.3"

[[DiffRules]]
deps = ["NaNMath", "Random", "SpecialFunctions"]
git-tree-sha1 = "85d2d9e2524da988bffaf2a381864e20d2dae08d"
uuid = "b552c78f-8df3-52c6-915a-8e097449b14b"
version = "1.2.1"

[[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 = "92d8f9f208637e8d2d28c664051a00569c01493d"
uuid = "5ae413db-bbd1-5e63-b57d-d24a61df00f5"
version = "2.1.5+1"

[[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", "LibVPX_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 = "3cc57ad0a213808473eafef4845a74766242e05f"
uuid = "b22a6f82-2f65-5046-a5b2-351ab43fb4e5"
version = "4.3.1+4"

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

[[Fontconfig_jll]]
deps = ["Artifacts", "Bzip2_jll", "Expat_jll", "FreeType2_jll", "JLLWrappers", "Libdl", "Libuuid_jll", "Pkg", "Zlib_jll"]
git-tree-sha1 = "35895cf184ceaab11fd778b4590144034a167a2f"
uuid = "a3f928ae-7b40-5064-980b-68af3947d34b"
version = "2.13.1+14"

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

[[ForwardDiff]]
deps = ["CommonSubexpressions", "DiffResults", "DiffRules", "LinearAlgebra", "NaNMath", "Printf", "Random", "SpecialFunctions", "StaticArrays"]
git-tree-sha1 = "b5e930ac60b613ef3406da6d4f42c35d8dc51419"
uuid = "f6369f11-7733-5829-9624-2563aa707210"
version = "0.10.19"

[[FreeType2_jll]]
deps = ["Artifacts", "Bzip2_jll", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"]
git-tree-sha1 = "cbd58c9deb1d304f5a245a0b7eb841a2560cfec6"
uuid = "d7e528f0-a631-5988-bf34-fe36492bcfd7"
version = "2.10.1+5"

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

[[Future]]
deps = ["Random"]
uuid = "9fa8497b-333b-5362-9e8d-4d0656e87820"

[[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 = "182da592436e287758ded5be6e32c406de3a2e47"
uuid = "28b8d3ca-fb5f-59d9-8090-bfdbd6d07a71"
version = "0.58.1"

[[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 = "d59e8320c2747553788e4fc42231489cc602fa50"
uuid = "d2c73de3-f751-5644-a686-071e5b155ba9"
version = "0.58.1+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"

[[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 = "44e3b40da000eab4ccb1aecdc4801c040026aeb5"
uuid = "cd3eb016-35fb-5094-929b-558a96fad6f3"
version = "0.9.13"

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

[[InitialValues]]
git-tree-sha1 = "26c8832afd63ac558b98a823265856670d898b6c"
uuid = "22cec73e-a1b8-11e9-2c92-598750a2cf9c"
version = "0.2.10"

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

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

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

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

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

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

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

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

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

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

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

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

[[LibVPX_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "12ee7e23fa4d18361e7c2cde8f8337d4c3101bc7"
uuid = "dd192d2f-8180-539f-9fb4-cc70b1dcf69a"
version = "1.10.0+0"

[[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 = ["DocStringExtensions", "LinearAlgebra"]
git-tree-sha1 = "7bd5f6565d80b6bf753738d2bc40a5dfea072070"
uuid = "2ab3a3ac-af41-5b50-aa03-7779005ae688"
version = "0.2.5"

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

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

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

[[MicroCollections]]
deps = ["BangBang", "Setfield"]
git-tree-sha1 = "e991b6a9d38091c4a0d7cd051fcb57c05f98ac03"
uuid = "128add7d-3638-4c79-886c-908ea0c25c34"
version = "0.1.0"

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

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

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

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

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

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

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

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

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

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

[[Plots]]
deps = ["Base64", "Contour", "Dates", "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 = "1e72752052a3893d0f7103fbac728b60b934f5a5"
uuid = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
version = "1.19.4"

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

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

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

[[QuadGK]]
deps = ["DataStructures", "LinearAlgebra"]
git-tree-sha1 = "12fbe86da16df6679be7521dfb39fbc861e1dc7b"
uuid = "1fd47b50-473d-5c70-9696-f719f8f3bcdc"
version = "2.4.1"

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

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

[[Ratios]]
git-tree-sha1 = "37d210f612d70f3f7d57d488cb3b6eff56ad4e41"
uuid = "c84ed2f1-dad5-54f0-aa8e-dbefe2724439"
version = "0.4.0"

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

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

[[Reexport]]
git-tree-sha1 = "5f6c21241f0f655da3952fd60aa18477cf96c220"
uuid = "189a3867-3050-52da-a836-e630ba90ab69"
version = "1.1.0"

[[Referenceables]]
deps = ["Adapt"]
git-tree-sha1 = "e681d3bfa49cd46c3c161505caddf20f0e62aaa9"
uuid = "42d2dcc6-99eb-4e98-b66c-637b7d73030e"
version = "0.1.2"

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

[[Roots]]
deps = ["CommonSolve", "Printf"]
git-tree-sha1 = "52a5d9ab3fc404993593c79576ce2af68b965ede"
uuid = "f2b01f46-fcfa-551c-844a-d8ac1e96c665"
version = "1.1.0"

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

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

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

[[Setfield]]
deps = ["ConstructionBase", "Future", "MacroTools", "Requires"]
git-tree-sha1 = "fca29e68c5062722b5b4435594c3d1ba557072a3"
uuid = "efcf1570-3423-57d1-acb7-fd33fddbac46"
version = "0.7.1"

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

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

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

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

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

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

[[SplittablesBase]]
deps = ["Setfield", "Test"]
git-tree-sha1 = "edef25a158db82f4940720ebada14a60ef6c4232"
uuid = "171d559e-b47b-412a-8079-5efa626c420e"
version = "0.1.13"

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

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

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

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

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

[[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 = "d0c690d37c73aeb5ca063056283fde5585a41710"
uuid = "bd369af6-aec1-5ad0-b16a-f7cc5008161c"
version = "1.5.0"

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

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

[[ThreadsX]]
deps = ["ArgCheck", "BangBang", "ConstructionBase", "InitialValues", "MicroCollections", "Referenceables", "Setfield", "SplittablesBase", "Transducers"]
git-tree-sha1 = "269f5c1955c1194086cf6d2029aa4a0b4fb8018b"
uuid = "ac1d9e8a-700a-412c-b207-f0111f4b6c0d"
version = "0.1.7"

[[Transducers]]
deps = ["Adapt", "ArgCheck", "BangBang", "Baselet", "CompositionsBase", "DefineSingletons", "Distributed", "InitialValues", "Logging", "Markdown", "MicroCollections", "Requires", "Setfield", "SplittablesBase", "Tables"]
git-tree-sha1 = "34f27ac221cb53317ab6df196f9ed145077231ff"
uuid = "28d57a85-8fef-5791-bfe6-a80928e7c999"
version = "0.4.65"

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

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

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

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

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

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

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

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

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

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

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

[[Xorg_libXcursor_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXfixes_jll", "Xorg_libXrender_jll"]
git-tree-sha1 = "12e0eb3bc634fa2080c1c37fccf56f7c22989afd"
uuid = "935fb764-8cf2-53bf-bb30-45bb1f8bf724"
version = "1.2.0+4"

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

[[Yields]]
deps = ["ForwardDiff", "Interpolations"]
git-tree-sha1 = "be78b60fee8884d22cad5f758fc3499c8bc58dc5"
uuid = "d7e99b2f-e7f3-4d9e-9f01-2338fc023ad3"
version = "0.5.0"

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

[[ZygoteRules]]
deps = ["MacroTools"]
git-tree-sha1 = "9e7a1e8ca60b742e508a315c17eef5211e7fbfd7"
uuid = "700de1a5-db45-46bc-99cf-38207098b444"
version = "0.2.1"

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

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

[[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 = "d713c1ce4deac133e3334ee12f4adff07f81778f"
uuid = "1270edf5-f2f9-52d2-97e9-ab00b5d0237a"
version = "2020.7.14+2"

[[x265_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "487da2f8f2f0c8ee0e83f39d13037d6bbf0a45ab"
uuid = "dfaa095f-4041-5dcd-9319-2fabd8486b76"
version = "3.0.0+3"

[[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:
# ╟─f04b8ce0-ea39-11ea-05ad-a996c592cc82
# ╟─018237f0-ea3c-11ea-387e-abf5110ac1fa
# ╟─dabece20-ea37-11ea-2e6a-61eda948928f
# ╟─63507e40-ea34-11ea-00c3-31bd881aa1bc
# ╟─13c9baf0-ea37-11ea-16d5-f9f87c35e4f4
# ╟─2cc3d3b0-ea3c-11ea-2c4f-4bd1db712846
# ╟─9a6a036e-b4f1-40a8-b3ea-724a1542fb26
# ╟─076facc0-e9f5-11ea-2fa5-91cfaa965d08
# ╟─7f7a2280-ea2d-11ea-2ee8-79bc4c3a72ec
# ╟─56f67070-ea3c-11ea-31f5-b7d1f7182a31
# ╟─844a9e50-f128-11ea-10b1-a1d7792c8fc4
# ╟─783904d0-f128-11ea-22f9-43d6dc7b8439
# ╟─23cba4a0-ea3b-11ea-2b62-8dfc856875b7
# ╠═7056a580-e8b6-11ea-2ac9-ad6ced139615
# ╟─f15e18e0-ea3a-11ea-3868-d9236a4810a1
# ╠═79cfe060-e9f4-11ea-2fc8-b769419cc4fd
# ╟─1a5bb1b0-ea33-11ea-3514-e1c85b65d356
# ╠═49419d10-e861-11ea-36e0-e19ece503681
# ╠═4ff1ccc0-e8b6-11ea-20be-d7992829086d
# ╠═c5078e00-e8b6-11ea-105b-9731d9852664
# ╠═c684fec1-0e7c-4c74-be96-c9b1d8c4c9af
# ╟─69730d30-ea37-11ea-05be-8350df76fd06
# ╟─00000000-0000-0000-0000-000000000001
# ╟─00000000-0000-0000-0000-000000000002


function checkdiff(f, δf, x0...)
    x = [x0...]
    y0 = f(x...)
    @assert length(y0) == 1 "Scalar functions only"
    y, ∇f = δf(x...)
    @assert isapprox(y0, y) "Return values do not match"
    #    @assert typeof(y0) === typeof(y) "Return type doesn't match"
    ∂x = ∇f()
    #    @assert typeof(x0) === typeof(∂x) "Gradient type doesn't match"
    checkgrad(f, x, ∂x)
end

function checkgrad(f, x, ∂x, h = 1e-8, atol = 1e-8)
    for k = 1:length(x)
        ∂xₖ = length(x) == 1 ? ∂x : ∂x[k]
        if isa(x[k], AbstractFloat)
            x1 = deepcopy(x)
            x1[k] -= h
            y1 = f(x1...)
            x2 = deepcopy(x)
            x2[k] += h
            y2 = f(x2...)
            isapprox(2h * ∂xₖ, y2-y1, atol=atol) || error("gradient for argument #$k doesn't match by $((y2-y1)/(2h*∂xₖ))")
        elseif isa(x[k], AbstractArray)
            for l = eachindex(x[k])
                x1 = deepcopy(x)
                x1[k][l] -= h
                y1 = f(x1...)
                x2 = deepcopy(x)
                x2[k][l] += h
                y2 = f(x2...)
                isapprox(2h * ∂xₖ[l], y2-y1, atol=atol) || error("gradient for argument #$k element $l doesn't match by $((y2-y1)/(2h*∂xₖ[l]))")
            end
        else error("not supported argument #$k type: $(typeof(x[k]))")
        end
    end
    true
end


#---------------------------------------
# types for a single EB sample
#---------------------------------------
abstract type EBayesSample{T<:Number} end
abstract type AbstractNormalSample{T<:Number} <: EBayesSample{T} end

"""
    NormalSample(Z,σ)

A observed sample ``Z`` drawn from a Normal distribution with known variance ``\\sigma^2 > 0``.

```math
Z \\sim \\mathcal{N}(\\mu, \\sigma^2)
```

``\\mu`` is assumed unknown. The type above is used when the sample ``Z`` is to be used for estimation or inference of ``\\mu``.

```julia
NormalSample(0.5, 1.0)          #Z=0.5, σ=1
```
"""
struct NormalSample{T <: Number} <: AbstractNormalSample{T}
    Z::T
    σ::T
end

function NormalSample(Z::T) where {T<:Number}
    NormalSample(Z, one(T))
end

NormalSample(Z::Number, σ::Number) = NormalSample(promote(Z, σ)...)

response(s::NormalSample) = s.Z
Statistics.var(s::NormalSample) = s.σ^2

eltype(s::NormalSample{T}) where T = T
zero(s::NormalSample{T}) where T = zero(T)
support(ss::NormalSample) = RealInterval(-Inf, +Inf)

#---------------------------------------
# types for a single EB sample
#---------------------------------------
const EBayesSamples = AbstractArray{EBS} where EBS <: EBayesSample

const NormalSamples{T} = StructArray{NormalSample{T}} where T

function NormalSamples(Zs::AbstractVector{T}, σs::AbstractVector{T}) where T
    NormalSamples{T}((Zs, σs))
end

response(ss::NormalSamples) = ss.Z
Statistics.var(ss::NormalSamples) = ss.σ .^ 2

"""
    StandardNormalSample(Z)

A observed sample ``Z`` drawn from a Normal distribution with known variance ``\\sigma^2 =1``.

```math
Z \\sim \\mathcal{N}(\\mu, 1)
```

``\\mu`` is assumed unknown. The type above is used when the sample ``Z`` is to be used for estimation or inference of ``\\mu``.

```julia
StandardNormalSample(0.5)          #Z=0.5
```
"""
struct StandardNormalSample{T <: Number} <: AbstractNormalSample{T}
    Z::T
end

response(s::StandardNormalSample) = s.Z
Statistics.var(s::StandardNormalSample) = 1

eltype(s::StandardNormalSample{T}) where T = T
zero(s::StandardNormalSample{T}) where T = zero(T)
support(ss::StandardNormalSample) = RealInterval(-Inf, +Inf)

const AbstractNormalSamples = AbstractArray{NS} where NS <: Union{NormalSample,
                                                                  StandardNormalSample}

response(ss::AbstractNormalSamples) = response.(ss)
Statistics.var(ss::AbstractNormalSamples) = var.(ss)
zeros(ss::AbstractNormalSamples) = zeros(eltype(response(ss)), length(ss))

# Poisson

# Binomial

# Replicated , ReplicatedArray


# ---
# jupyter:
#   jupytext:
#     formats: ipynb,jl:light
#     text_representation:
#       extension: .jl
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.8.2
#   kernelspec:
#     display_name: Julia 1.6.1
#     language: julia
#     name: julia-1.6
# ---

using Pkg
Pkg.add(["Combinatorics", "Plots"])

using Combinatorics
using LinearAlgebra
using Plots

# +
function polynomial_features(X, degree)

    n_samples, n_features = size(X)

    combs = [with_replacement_combinations(1:n_features, i) for i in 0:degree+1]

    combinations = []
    for sublist in combs
        for j in sublist
             push!(combinations, j)
        end
    end
    n_output_features = length(combinations)
    X_new = zeros(n_samples, n_output_features)

    for (i, index_combs) in enumerate(combinations)
        X_new[:, i] = prod(X[:, index_combs], dims=2)
    end

    return X_new

end
# -

# # Regression polynomial non locale

# +
f(x, y) = - sin(x) .+ 0.5 * cos(y)
n, p = 1000, 2
degree = 3
X = randn(n, p)
Y_true = f.(X[:,1],X[:,2])
Y = Y_true .+ 0.2 .* randn(n)
X_new = polynomial_features(X, degree)
beta = X_new \ Y

X_grid = hcat(vcat([0.1i for i in -20:20, j in -20:20]...),
              vcat([0.1j for i in -20:20, j in -20:20]...))
Y_true = f.(X_grid[:,1], X_grid[:,2])
X_grid_new = polynomial_features( X_grid, degree)
Y_grid = X_grid_new * beta
plot(X_grid[:,1], X_grid[:,2], Y_grid, st=:surface, alpha=0.5 )
scatter!(X[:,1], X[:,2], Y, ms = 1)

# +

scatter( X_grid[:,1], X_grid[:,2], Y_true)
scatter!(X_grid[:,1], X_grid[:,2], Y_grid)

# +
abstract type AbstractKernel end

struct Epanechnikov <: AbstractKernel end

"""
    epanechnikov(t)

Compute the Epanechnikov kernel.
"""
function (::Epanechnikov)(t)

    kernel = zero(t)
    idx = findall(abs.(t) .<= 1)
    kernel[idx] = 0.75 .* (1 .- t[idx].^2)
    return kernel

end

# -

# # Regression locale

# +
kernel = Epanechnikov()
width = 1

Y_pred = Float64[]
for x in eachrow(X_grid)
    dist = sqrt.(sum((X .- x').^2, dims=2)) ./ width
    w = vec(kernel(dist))
    idx = findall(abs.(w) .> 1e-10)
    beta = pinv(X_new[idx,:]' * Diagonal(w[idx]) * X_new[idx,:]) * (X_new[idx,:]' * Diagonal(w[idx]) * Y[idx])
    P = polynomial_features(x', degree)

    push!(Y_pred, P * beta...)
end

scatter( X_grid[:,1], X_grid[:,2], Y_pred)
# -


"""
# Usage

    solve(prob::SingleTermFODEProblem, h, PIEX())

### References

```tex
@inproceedings{Garrappa2018NumericalSO,
  title={Numerical Solution of Fractional Differential Equations: A Survey and a Software Tutorial},
  author={Roberto Garrappa},
  year={2018}
}
```
"""
struct PIEX <: FractionalDiffEqAlgorithm end

function solve(FODE::SingleTermFODEProblem, h, ::PIEX)
    @unpack f, α, u0, tspan = FODE
    t0 = tspan[1]; T = tspan[2]
    N::Int64 = round(Int, (T-t0)/h)
    y = zeros(N+1)

    y[1]=u0
    for j in range(2, N+1, step=1)
        middle=0
        @turbo for i=0:j-1
            middle += bcoefficients(j-i, α)*f(t0+i*h, y[i+1])
        end
        middle = middle/gamma(α+1)
        y[j] = u0 + middle*h^α
    end
    t = collect(t0:h:T)
    return FODESolution(t, y)
end

function acoefficients(n, α)
    if n == 0
        return 1
    else
        return ((n-1)^(α+1)-2*n^(α+1)+(n+1)^(α+1))
    end
end

bcoefficients(n, α) = ((n+1)^α-n^α)


# source: https://gist.github.com/denizyuret/bf74f737ba8a7076a7f43c5080555a9a

using AutoGrad, CuArrays, Knet, Test
using AutoGrad: @gcheck
using CuArrays: @cufunc

const GConstant01 = sqrt(2/pi)
const GConstant02 = 0.044715 * sqrt(2/pi)
const GConstant03 = GConstant01 / 2

# Main definition, broadcasted version works on Arrays

gelu(x::T) where T = (x/2)*(1 + tanh(T(GConstant02)*x^3 + T(GConstant01)*x))
geluback(x::T,dy::T) where T = dy*(T(0.5)*tanh(T(GConstant02)*x^3 + T(GConstant01)*x) + (T(0.0535161)*x^3 + T(GConstant03)*x)*(1/cosh(T(GConstant02)*x^3 + T(GConstant01)*x))^2 + T(0.5))

# This defines gelu for AutoGrad

@primitive  gelu(x),dy  geluback.(x,dy)

# This makes broadcasted gelu work for CuArray, directly compiling a CUDA kernel from Julia code.
# @cufunc necessary to use a GPU compatible version of tanh etc.
# https://github.com/JuliaGPU/CuArrays.jl/issues/253

@cufunc gelu(x::T) where T = (x/2)*(1 + tanh(T(GConstant02)*x^3 + T(GConstant01)*x))
@cufunc geluback(x::T,dy::T) where T = dy*(T(0.5)*tanh(T(GConstant02)*x^3 + T(GConstant01)*x) + (T(0.0535161)*x^3 + T(GConstant03)*x)*(1/cosh(T(GConstant02)*x^3 + T(GConstant01)*x))^2 + T(0.5))

# This defines gelu for KnetArray
import Base.Broadcast: broadcasted
import Knet: KnetArray

function KnetArray(x::CuArray{T,N}) where {T,N}
    p = Base.bitcast(Knet.Cptr, x.ptr)
    k = Knet.KnetPtr(p, sizeof(x), gpu(), x)
    KnetArray{T,N}(k, size(x))
end

broadcasted(::typeof(gelu),x::KnetArray) = KnetArray(gelu.(CuArray(x)))
broadcasted(::typeof(geluback),x::KnetArray,dy::KnetArray) = KnetArray(geluback.(CuArray(x),CuArray(dy)))

# This tests all of the above
# julia -L gelu.jl -e 'testgelu()'
function testgelu()
    @testset "gelu" begin
        x = rand(10)
        a = Param(x)
        c = Param(CuArray(x))
        k = Param(KnetArray(x))
        @test @gcheck gelu.(a)
        @test @gcheck gelu.(c)
        @test @gcheck gelu.(k)
    end
end

"""
    gelu(x)

    Gaussian Error Linear Unit Activation Function
    https://arxiv.org/abs/1606.08415

    returns:
        0.5x * (1 + tanh(√(2/π) * (x + 0.044715x^3)))
"""
gelu


@testset "Eigenvalues of interval matrices" begin

    # symmetrix matrix
    A = Symmetric([-1 0 -1..1;
         0 -1 -1..1;
         -1..1 -1..1 0.1])

    evrohn = eigenbox(A)
    @test interval_isapprox(evrohn, -2.4143..1.5143; atol=1e-3)

    evhertz = eigenbox(A, Hertz())
    @test interval_isapprox(evhertz, -1.9674..1.0674; atol=1e-3)

    # real matrix
    A = [-3.. -2 4..5 4..6 -1..1.5;
         -4.. -3 -4.. -3 -4.. -3 1..2;
         -5.. -4 2..3 -5.. -4 -1..0;
         -1..0.1 0..1 1..2 -4..2.5]

    ev = eigenbox(A)
    @test interval_isapprox(real(ev), -8.8221..3.4408; atol=1e-3)
    @test interval_isapprox(imag(ev), -10.7497..10.7497; atol=1e-3)

    evhertz = eigenbox(A, Hertz())
    @test interval_isapprox(real(evhertz), -7.3691..3.2742; atol=1e-3)
    @test interval_isapprox(imag(evhertz), -8.794..8.794; atol=1e-3)

    # hermitian matrix
    A = Hermitian([1..2 (5..9)+(2..5)*im (3..5)+(2..4)im;
        (5..9)+(-5.. -2)*im 2..3 (7..8)+(6..10)im;
        (3..5)+(-4.. -2)*im (7..8)+(-10.. -6)*im 3..4])

    ev = eigenbox(A)
    @test interval_isapprox(ev, -15.4447..24.3359; atol=1e-3)

    # complex matrix
    A = [(1..2)+(3..4)*im 3..4;1+(2..3)*im 4..5]

    ev = eigenbox(A)
    @test interval_isapprox(real(ev), -1.28812..7.28812; atol=1e-3)
    @test interval_isapprox(imag(ev), -2.04649..5.54649; atol=1e-3)
end


using ValidatedNumerics
using FactCheck

facts("Tests with rational intervals") do

    a = Interval(1//2, 3//4)
    b = Interval(3//7, 9//12)

    @fact a + b --> Interval(13//14, 3//2)  # exact

    @fact sqrt(a + b) --> Interval(0.9636241116594314, 1.2247448713915892)

    X = Interval(1//3)
    @fact sqrt(X) --> Interval(0.5773502691896257, 0.5773502691896258)

end

setprecision(Interval, 64)

facts("Rounding rational intervals") do

    X = Interval(big(1)//3)
    Y = Interval(big"5.77350269189625764452e-01", big"5.77350269189625764561e-01")
    @fact sqrt(X) --> Y
end

facts("Tests with float intervals") do

    c = @floatinterval(0.1, 0.2)

    @fact isa(@floatinterval(0.1), Interval) --> true
    @fact c --> Interval(0.09999999999999999, 0.2)

    @fact widen(c) --> Interval(0.09999999999999998, 0.20000000000000004)

    @fact @floatinterval(pi) --> Interval(3.141592653589793, 3.1415926535897936)
end

facts("Testing functions of intervals") do
    f(x) = x + 0.1

    c = @floatinterval(0.1, 0.2)
    @pending f(c) --> Interval(0.19999999999999998, 0.30000000000000004)

    d = @interval(0.1, 0.2)
    @pending f(d) --> Interval(1.9999999999999998e-01, 3.0000000000000004e-01)
end

facts("Testing conversions") do
    f = @interval(0.1, 0.2)
    @pending @floatinterval(f) --> Interval(0.09999999999999999, 0.2)

    g = @floatinterval(0.1, 0.2)
    @pending @interval(g) --> Interval(9.9999999999999992e-02, 2.0000000000000001e-01)
end


__precompile__()

module mbRK
export rk1, rk4

import Base.length

type ButcherTableau
	A::Array{Float64,2}
	b::Array{Float64,1}
	c::Array{Float64,1}
end

length(BT::ButcherTableau) = length(BT.b)

"""
	rk(butcherTableau)

returns a runge-kutta method using the respective tableau:

    function(f, t0, y0, h, pred[, init, callback])

e.g. `rk1`, or `rk4`. Examines the ode `f` starting from `t0`, `y0` with fixed stepsize
`h` until `pred` evalutes to `false`.
"""
function rk(BT::ButcherTableau)
	return function rk(f::Function, t₀::Float64, y₀::Vector{Float64}, h::Float64, pred::Function;
		init=Void, callback=Void)

		local A,b,c,s,t,y,K
		A,b,c = BT.A, BT.b, BT.c
		s = length(BT)
		K = cell(s)

		t,y = deepcopy(t₀), deepcopy(y₀)
		K[1] = f(t,y)

		init≠Void && init()

		while pred(t,y,K[1])
			for i in 2:s
				K[i] = f( t + h*c[i], y + h*(A[i,1:i-1]*K[1:i-1])[1] )
			end

			t += h
			y += h*sum(map(*, K, b))
			K[1] = f(t,y)

			callback≠Void && callback(t,y,K)
		end

		return Void
	end
end

BTrk1 = ButcherTableau(
	zeros(1,1),
	ones(1),
	zeros(1)
)
rk1 = rk(BTrk1)

BTrk4 = ButcherTableau(
	[
		.0	.0	.0	.0
		.5	.0	.0	.0
		.0	.5	.0	.0
		.0	.0	1.0	.0
	],
	[1/6; 1/3; 1/3; 1/6],
	[.0; .5; .5; 1.0]
)
rk4 = rk(BTrk4)

end


##
function badvar1(x::Vector{Float64})
ex2 = 0.0
ex = 0.0
for i=1:length(x)
  ex2 = ex2 + x[i]^2
  ex = ex + x[i]
end
n = length(x)
return 1.0/(n-1)*(ex2 - (ex)^2/n)
end
@show badvar1([0.0,1,2])
@show badvar1([0.0,1,2].+1.0e8)
##
using Random
Random.seed!(2)
x = randn(100)
@show badvar1(x)
@show badvar1(x.+1.0e8)
##
function goodvar(x::Vector{Float64})
  n = length(x); mean = 0.0; m2 = 0.0; N = 0.0
  for i=1:n
    N = N + 1 # running length
    delta = x[i] - mean
    mean = mean + delta/N
    m2 = m2 + delta*(x[i]-mean)
  end
  return m2/(n-1)
end
@show goodvar([0.0,1,2])
@show goodvar([0.0,1,2].+1.0e8)
##
using Random
Random.seed!(2)
x = randn(100)
@show goodvar(x)
@show goodvar(x.+1.0e8)


@testset "Misc/NumberField" begin
  @testset "is_subfield" begin
    Qx, x = FlintQQ["x"]
    K, a = NumberField(x^2 + 1, "a")
    L, b = NumberField(x^4 + 1, "b")

    c, KtoL = is_subfield(K, L)
    @test c == true
    @test parent(KtoL(a)) == L

    c, KtoL = Hecke.is_subfield_normal(K, L)
    @test c == true
    @test parent(KtoL(a)) == L

    OK = maximal_order(K)
    OL = maximal_order(L)
    c, KtoL = is_subfield(K, L)
    @test c == true
    @test parent(KtoL(a)) == L
  end

  @testset "is_isomorphic" begin
    Qx, x = FlintQQ["x"]
    f = x^5 + 12x - 92
    K, a = NumberField(f, "a")

    g = x^5 - 172x^4 + 7024x^3 + 8656448x^2 + 55735552x + 45796197888
    K2, a2 = NumberField(g, "a2")

    c, KtoK2 = is_isomorphic_with_map(K, K2)
    @test c == true
    @test parent(KtoK2(a)) == K2

    OK = maximal_order(K)
    OK2 = maximal_order(K2)
    c, KtoK2 = is_isomorphic_with_map(K, K2)
    @test c == true
    @test parent(KtoK2(a)) == K2

    h = f - 1
    K3, a3 = NumberField(h, "a3")
    @test !is_isomorphic(K, K3)
  end
end


addprocs(3)

@everywhere function approximate_pi(trials::Int64)
  hits = 0
  for i in 1:trials
    if sum(rand(2).^2) < 1
      hits += 1
    end
  end
  hits/trials * 4
end

references = [@spawn approximate_pi(1000000) for i=1:3]
approximation = sum([fetch(ref) for ref in references])/3
@assert 3.1 < approximation < 3.2


function ackley(x, a=20, b=0.2, c=2π)
    d = length(x)
    return -a*exp(-b*sqrt(sum(x.^2)/d)) -
        exp(sum(cos.(c*xi) for xi in x)/d) + a +
        exp(1)
end


using Primes

function totient_func(n)
    prime_fact = factor(n)
    for (p, k) in prime_fact
        n *= (1-1/p)
    end
    return n
end

function find_increment()
    p = primes(30)
    for i in 1:length(p)
	x = prod((1-1/prime) for prime in p[1:i])
	n = prod(p[1:i])
	if x*n/(n-1) < 15499/94744
	    return prod(p[1:i-1])
	end
    end
end

function find_denominator(inc)
    n = 0
    while true
        n += inc
        x = totient_func(n)
        if x/(n-1) < 15499/94744
            return n
        end
    end
end

function main()
    inc = find_increment()
    denom = find_denominator(inc)
    println(denom)
end

main()


include("../../../opti/src/opti_module.jl")

module noisydiff
using ..opti
using LinearAlgebra

function pt1_smooth(t, input_signal; T = 1)
    # trapezoidal rule

    L = length(input_signal)
    y = zeros(L)

    y[1] = input_signal[1]

    for kk = 2:L
        dt = t[kk] - t[kk-1]
        f = dt / (2 * T + dt) # 2 * T / dt + 1
        y[kk] = (input_signal[kk] + input_signal[kk-1] - y[kk-1]) * f + y[kk-1] * 2 * T / (dt + 2 * T)
    end

    return y
end

function diff_sliding_least_squares_d1(t, y; w = 5)
# fitting a degree 1 polynomial in the window and return the derivative

    N = length(y)
    u = zeros(N-w)

    yv = view(y, 1:w)
    xv = view(t, 1:w)
    U1 = sum(yv)
    U2 = sum(yv .* xv)
    S1 = sum(xv)
    S2 = sum(xv.^2)
    jj = 0

    for ii = (w+1):N
        jj    += 1

        d     = w * S2 - S1^2
        a1    = (U2 * w - S1 * U1) / d
        u[jj] = a1

        x_old   = t[ii-w]
        x_new   = t[ii]
        y_old   = y[ii-w]
        y_new   = y[ii]
        S1      += x_new - x_old
        S2      += x_new^2 - x_old^2
        U1      += y_new - y_old
        U2      += y_new * x_new - y_old * x_old

    end
    return u
end

function diff_sliding_least_squares_d3(t, y; w = 7)
    # fitting a degree 1 polynomial in the window and return the derivative

    N = length(y)
    u = zeros(N-w)

    yv = view(y, 1:w)
    xv = view(t, 1:w)
    U1 = sum(yv)
    U2 = sum(yv .* xv)
    U3 = sum(yv .* xv.^2)
    U4 = sum(yv .* xv.^3)

    S1 = sum(xv)
    S2 = sum(xv.^2)
    S3 = sum(xv.^3)
    S4 = sum(xv.^4)
    S5 = sum(xv.^5)
    S6 = sum(xv.^6)
    M = zeros(4, 4)
    b = zeros(4)
    jj = 0
    for ii = (w+1):N
        jj    += 1

        M[1,1] = w
        M[1,2] = S1
        M[1,3] = S2
        M[1,4] = S3

        M[2,2] = S2
        M[2,3] = S3
        M[2,4] = S4
        M[3,3] = S4
        M[3,4] = S5

        M[4,4] = S6
        b[1] = U1
        b[2] = U2
        b[3] = U3
        b[4] = U4

        S = Symmetric(M, :U)
        a = S \ b # todo : replace it with inplace operations

        x_old   = t[ii-w]
        x_new   = t[ii]
        y_old   = y[ii-w]
        y_new   = y[ii]

        u[jj] = a[2] + 2 * a[3] * x_new + 3 * a[4] * x_new^2

        S1      += x_new - x_old
        S2      += x_new^2 - x_old^2
        S3      += x_new^3 - x_old^3
        S4      += x_new^4 - x_old^4
        S5      += x_new^5 - x_old^5
        S6      += x_new^6 - x_old^6
        U1      += y_new - y_old
        U2      += y_new * x_new - y_old * x_old
        U3      += y_new * x_new^2 - y_old * x_old^2
        U4      += y_new * x_new^3 - y_old * x_old^3

    end
    return u
end

# function diff_tikhonov(t, y; w = 5, lamda = 0.1)
#     Ma = zeros(w, w)
#     Md = zeros(w, w)

#     N = length(t)
#     u = zeros(N)

#     for ii = (w+1):N

#         A = Ma + lamda * Md

#     end

#     return u
# end

function diff_fw(t, y)
    dt = diff(t)
    dy = diff(y)
    dy ./= dt
    return dy
end

function cumtrapezoidal(t, y)
    dt = diff(t)
    N = length(y)
    v1 = view(y, 1:N-1)
    v2 = view(y, 2:N)
    ys = v1 .+ v2
    ints = [0.0; 0.5 .* dt .* ys]
    ints = cumsum(ints)
    return ints
end

function tvd(t, y; lamda = 0.1, tol_options = opti.ToleranceOptions())

    # u0 = rand(length(y)) # copy(y)
    t_min = t[1]
    t_max = t[end]
    T = (t_max - t_min) / 100
    y_pred = pt1_smooth(t, y; T = T)
    u0 = [0.0; diff_fw(t, y_pred)]

    obj_fcn(u) = __obj_fcn(t, y, lamda, u)
    # stat = opti.cg_pr(obj_fcn, u0; tol_options = tol_options)
    (x_sol, y_sol, iter, stopping_crit) = opti.quasi_newton(obj_fcn, u0; tol_options = tol_options)
    return (x_sol, y_sol, iter, stopping_crit)
end

function __obj_fcn(t, y, lamda, u)
    i1 = __integral_1(u)
    i2 = __integral_2(t, y, u)
    return lamda * i1 + i2
end

function __integral_1(u)
    N = length(u)
    abs_du = 0.0
    @inbounds for kk = 1:(N-1)
        du = u[kk+1] - u[kk]
        abs_du += abs(du)
    end
    return abs_du / N
end

function __integral_2(t, y, u)
    N = length(y)

    # dt = diff(t)

    # vu1 = view(u, 1:N-1)
    # vu2 = view(u, 2:N)

    # int_u =[y[1]; 0.5 .* dt .* (vu1 .+ vu2)]
    # y_pred = cumsum(int_u)
    # g = (y_pred - y).^2
    # i1 = dt[1] * g[1]
    # iN = dt[end] * g[end]

    # ibulk = 0.0
    # for kk = 2:N-1
    #     ibulk += g[kk] * (dt[kk] + dt[kk-1])
    # end
    # ibulk += i1
    # ibulk += iN

    ibulk = 0.0
    y_pred = y[1]
    dt = t[2] - t[1]

    @inbounds for kk = 2:N-1
        dt1     = t[kk] - t[kk-1]
        us      = u[kk] + u[kk-1]
        int_u   = 0.5 * dt * us
        y_pred  += int_u
        g       = (y_pred - y[kk]).^2
        ibulk   += g * (dt + dt1)
        dt      = dt1
    end

    dt = t[N] - t[N-1]
    int_u = 0.5 * dt * (u[N] + u[N-1])
    y_pred += int_u
    g = (y_pred - y[N]).^2
    iN = dt * g
    # ibulk += i1
    ibulk += iN

    return ibulk
end

end


using JuMP: @variable, @constraint, @objective, Model, with_optimizer, optimize!, value
using CPLEX

function find_feasible_point(A::Matrix{T}, b::Vector{T}, r_min::T=zero(T), r_max::T=T(Inf)) where T
	# Attempts to solve the feasibility problem
	# find       x
	# such that  Ax ≤ b
	#            r_min ≤ ‖x‖ ≤ r_max

	# First use CPLEX to find the solution x_min of
	# minimize     x'x
	# subject to   Ax ≤ b
	m, n = size(A)
	model = Model(with_optimizer(CPLEX.Optimizer))
    @variable(model, x[1:n])
    @objective(model, Min, dot(x, x))
	@constraint(model, A*x .<= b)
	optimize!(model)
    x_min = value.(x)
	# Check that x_min is feasible and ‖x_min‖ ≤ r_max
	@assert norm(x_min) <= r_max string("The problem is infeasible. Min radius:", norm(x_min))
	@assert maximum(A*x_min - b) <= 1e-11 string("Min-radius solution returned by Gurobi violates constraints by:", maximum(A*x_min - b))

	# Use GeneralQP to find a solution x
	# maximize     x'x
	# subject to   Ax ≤ b
	# that has radius less than or equal to (r_min + r_max)/2
	x = GeneralQP.solve(Matrix(-one(T)*I, n, n), zeros(T, n), A, b, x_min; r_max=(r_min+r_max)/2, printing_interval=5000)
	# Check that r_min ≤ ‖x‖ ≤ r_max and Ax ≤ b
	@assert norm(x) - r_min >= -1e-9 "Failed to find a feasible point"
	@assert norm(x) - r_max <= 1e-9 "Failed to find a feasible point"
	@assert maximum(A*x - b) <= 1e-9 "Failed to find a feasible point"

	return x
end


# randint
n = 1000
x = zeros(Int, n)
for i=1:n
    k = randint([0.1,0.1,0.8])
    x[i] = k
    @test 1 <= k <= 3
end
@test count(k->k==3, x) > n/2

for i=1:n
    k = randint([0.2,0.0,0.8])
    @test 1 <= k <= 3
    @test k != 2
end

for i=1:n
    k = randint([1.0,0.0,0.])
    @test k == 1
end

@test 0 == randint([0.0,0.0,0.])

# DiscreteDistribution
n = 1000
x = zeros(Int, n)
d = DiscreteDistribution([0.1,0.1,0.8])
for i=1:n
    k = rand(d)
    x[i] = k
    @test 1 <= k <= 3
end
@test count(k->k==3, x) > n/2

d = DiscreteDistribution([0.2,0.,0.8])
for i=1:n
    k = rand(d)
    @test 1 <= k <= 3
    @test k != 2
end

d = DiscreteDistribution([1.0,0.0,0.])
for i=1:n
    k = rand(d)
    @test k == 1
end


#    Problem : GROUP A
#    *********
#    This is a simplified version of problem NONDIA.
#
#    Original SIF Source:
#    G. Li,
#    "The secant/finite difference algorithm for solving sparse
#    nonlinear systems of equations",
#    SIAM Journal on Optimization, (to appear), 1990.
#
#    Implementation translated from Source:
#    http://eprints.tsu.ge/234/14/Tests%20collection-K-F.pdf
#
#    LIARWHD.SIF classification  SUR2-AN-V-0
#
#    Number of variables (at least 2)
#
# Daniel Henderson, 08/2021

f = (x) -> begin
    fx = 0.0
    for i in firstindex(x):lastindex(x)
        α = 2.0(x[i]^2 - x[1])
        γ = x[i]-1
        fx += α^2 + γ^2
    end
    return fx
end

g! = (g, x) -> begin
    for i in firstindex(x):lastindex(x)
        α = 2.0(x[i]^2 - x[1])
        γ = x[i]-1
        g[i] = 8.0x[i]α + 2.0γ
        g[1] -= 4.0α
    end
    return g
end

fg! = (g, x) -> begin
    fx = 0.0
    for i in firstindex(x):lastindex(x)
        α = 2.0(x[i]^2 - x[1])
        γ = x[i]-1
        fx += α^2 + γ^2
        g[i] = 8.0x[i]α + 2.0γ
        g[1] -= 4.0α
    end
    return fx, g
end

init = (n::Int=5000) -> begin
	x0 = 4.0*ones(n)
    return n, x0
end

TestSet["LIARWHD"] = UncProgram("LIARWHD", f, g!, fg!, init)


"""
`SqExponentialKernel([ρ=1.0])`

The squared exponential kernel is an isotropic Mercer kernel given by the formula:
```
    κ(x,y) = exp(-ρ²‖x-y‖²)
```
See also [`ExponentialKernel`](@ref) for a
related form of the kernel or [`GammaExponentialKernel`](@ref) for a generalization.
"""
struct SqExponentialKernel{T,Tr} <: Kernel{T,Tr}
    transform::Tr
    metric::SqEuclidean
    function SqExponentialKernel{T,Tr}(transform::Tr) where {T,Tr<:Transform}
        return new{T,Tr}(transform,SqEuclidean())
    end
end

@inline kappa(κ::SqExponentialKernel, d²::Real) = exp(-d²)
@inline iskroncompatible(::SqExponentialKernel) = true

## Aliases ##
const RBFKernel = SqExponentialKernel
const GaussianKernel = SqExponentialKernel

"""
`ExponentialKernel([ρ=1.0])`
The exponential kernel is an isotropic Mercer kernel given by the formula:
```
    κ(x,y) = exp(-ρ‖x-y‖)
```
"""
struct ExponentialKernel{T,Tr} <: Kernel{T,Tr}
    transform::Tr
    metric::Euclidean
    function ExponentialKernel{T,Tr}(transform::Tr) where {T,Tr<:Transform}
        return new{T,Tr}(transform,Euclidean())
    end
end

@inline kappa(κ::ExponentialKernel, d::Real) = exp(-d)
@inline iskroncompatible(::ExponentialKernel) = true

## Alias ##
const LaplacianKernel = ExponentialKernel

"""
`GammaExponentialKernel([ρ=1.0,[gamma=2.0]])`
The γ-exponential kernel is an isotropic Mercer kernel given by the formula:
```
    κ(x,y) = exp(-ρ^(2γ)‖x-y‖^(2γ))
```
"""
struct GammaExponentialKernel{T,Tr,Tᵧ<:Real} <: Kernel{T,Tr}
    transform::Tr
    metric::SqEuclidean
    γ::Tᵧ
    function GammaExponentialKernel{T,Tr,Tᵧ}(transform::Tr,γ::Tᵧ) where {T,Tr<:Transform,Tᵧ<:Real}
        return new{T,Tr,Tᵧ}(transform,SqEuclidean(),γ)
    end
end

params(k::GammaExponentialKernel) = (params(transform),γ)
opt_params(k::GammaExponentialKernel) = (opt_params(transform),γ)

function GammaExponentialKernel(ρ::T₁=1.0,gamma::T₂=2.0) where {T₁<:Real,T₂<:Real}
    @check_args(GammaExponentialKernel, gamma, gamma >= zero(T₂), "gamma > 0")
    GammaExponentialKernel{T₁,ScaleTransform{T₁},T₂}(ScaleTransform(ρ),gamma)
end

function GammaExponentialKernel(ρ::AbstractVector{T₁},gamma::T₂=2.0) where {T₁<:Real,T₂<:Real}
    @check_args(GammaExponentialKernel, gamma, gamma >= zero(T₂), "gamma > 0")
    GammaExponentialKernel{T₁,ARDTransform{T₁,length(ρ)},T₂}(ARDTransform(ρ),gamma)
end

function GammaExponentialKernel(t::Tr,gamma::T₁=2.0) where {Tr<:Transform,T₁<:Real}
    @check_args(GammaExponentialKernel, gamma, gamma >= zero(T₁), "gamma > 0")
    GammaExponentialKernel{eltype(Tr),Tr,T₁}(t,gamma)
end

@inline kappa(κ::GammaExponentialKernel, d²::Real) = exp(-d²^κ.γ)
@inline iskroncompatible(::GammaExponentialKernel) = true


α = 0.025

@testset "transform marginals" begin
  x1 = [0.2 0.4; 0.4 0.6; 0.6 0.8]
  convertmarg!(x1, TDist, [[10],[10]])
  @test x1 ≈ [-0.879058  -0.260185; -0.260185 0.260185; 0.260185 0.879058] atol=1.0e-5
  Random.seed!(43)
  x = rand(10000, 2)
  convertmarg!(x, Normal, [[0., 2.],[0., 3.]])
  @test pvalue(ExactOneSampleKSTest(x[:,1],Normal(0,2))) > α
  @test pvalue(ExactOneSampleKSTest(x[:,2],Normal(0,3))) > α
  Random.seed!(43)
  @test_throws AssertionError convertmarg!(randn(1000, 2), Normal)
end

@testset "generate corelation matrix" begin
  Random.seed!(43)
  if VERSION <= v"1.7"
    @test random_unit_normal_vector(2) ≈ [0.2414027539276207, 0.9704250153392381]
  else
    @test random_unit_normal_vector(2) ≈ [0.19041035339908666; -0.9817045876018076]
  end

  Random.seed!(43)
  if VERSION <= v"1.7"
    @test cormatgen(2) ≈ [1.0 0.660768; 0.660768 1.0] atol=1.0e-5
  else
    @test cormatgen(2) ≈ [1.0 0.278807; 0.278806 1.0] atol=1.0e-5
  end

  @test cormatgen_constant(3, 0.3) == [1 0.3 0.3; 0.3 1 0.3; 0.3 0.3 1]
  @test cormatgen_toeplitz(3, 0.3) == [1 0.3 0.09; 0.3 1 0.3; 0.09 0.3 1]
  @test cormatgen_two_constant(4, 0.5, 0.2) == [1. .5 .2 .2; .5 1. .2 .2; .2 .2 1. .2; .2 .2 .2 1.]

  @test cormatgen_constant_noised(10, 0.2, ϵ=0.) == cormatgen_constant(10, 0.2)
  @test all(eigvals(cormatgen_constant_noised(10, 0.2)) .> 0)
  @test diag(cormatgen_constant_noised(10, 0.2)) ≈ ones(10)

  @test cormatgen_toeplitz_noised(10, 0.2, ϵ=0.) == cormatgen_toeplitz(10, 0.2)
  @test all(eigvals(cormatgen_toeplitz_noised(10, 0.2)) .> 0)
  @test diag(cormatgen_toeplitz_noised(10, 0.2)) ≈ ones(10)

  @test cormatgen_two_constant_noised(10, 0.8, 0.2, ϵ=0.) == cormatgen_two_constant(10, 0.8, 0.2)
  @test all(eigvals(cormatgen_two_constant_noised(10, 0.8, 0.2)) .> 0)
  @test diag(cormatgen_two_constant_noised(10, 0.8, 0.2)) ≈ ones(10)

  Random.seed!(43)
  if VERSION <= v"1.7"
    @test cormatgen_rand(2) ≈ [1.0 0.879086; 0.879086 1.0] atol=1.0e-5
  else
    @test cormatgen_rand(2) ≈ [1.0 0.723043; 0.723043 1.0] atol=1.0e-5
  end
  @test all(eigvals(cormatgen_rand(10)) .> 0)
  @test diag(cormatgen_rand(10)) ≈ ones(10)

end


import SpecialFunctions
using BenchmarkTools
import FunctionZeros: besselj_zero, besselj_zero_asymptotic

## Times reported below are all measured on the same machine
## Intel(R) Core(TM) i7-4712HQ CPU @ 2.30GHz

# This is an asymptotic form with more terms than
# the series used in ../src/. It gives no
# advantage as the initial point for root finding
# since the root finding takes far more time.
#
# This could be used for large enough n without
# root finding, especially if the user specifies
# a desired precision.
## time nu=1, n=20: 125ns
function besselj_zero_asymptotic_long0(nu::Real, n::Integer)
    beta = MathConstants.pi * (n + nu / 2 - 1//4)
    delta = 8 * beta
    mu = 4 * nu^2
    t1 = 1
    t2 = 4 * (7 * mu - 31) / (3 * delta^2)
    t3 = 32 * (84 * mu^2 - 982 * mu + 3779) / (15 * delta^4)
    t4 = 64 * (6949 * mu^3 - 153855 * mu^2 + 1585743 * mu - 6277237) /
        (105 * delta^6)
    return beta - (mu - 1) / delta * (t1 + t2 + t3 + t4)
end

# Here we compute the powers more efficiently. A Horner formula might be better.
# This is about 4.5 times faster than the version above.
## time nu=1, n=20: 29ns
function besselj_zero_asymptotic_long(nu::Real, n::Integer)
    beta = MathConstants.pi * (n + nu / 2 - 1//4)
    delta = 8 * beta
    mu = 4 * nu^2
    mup2 = mu * mu
    mup3 = mup2 * mu
    deltap2 = delta * delta
    deltap3 = deltap2 * delta
    deltap4 = deltap2 * deltap2
    deltap6 = deltap3 * deltap3
    t1 = 1
    t2 = 4 * (7 * mu - 31) / (3 * deltap2)
    t3 = 32 * (84 * mup2 - 982 * mu + 3779) / (15 * deltap4)
    t4 = 64 * (6949 * mup3 - 153855 * mup2 + 1585743 * mu - 6277237) /
        (105 * deltap6)
    zero_asymp = beta - (mu - 1)  / delta * (t1 + t2 + t3 + t4)
    return zero_asymp
end

# Here, we convert prefactors in the numerator and denominator to a single rational
## time nu=1, n=20: 316ns
function besselj_zero_asymptotic_long1(nu::Real, n::Integer)
    beta = MathConstants.pi * (n + nu / 2 - 1//4)
    delta = 8 * beta
    mu = 4 * nu^2
    mup2 = mu * mu
    mup3 = mup2 * mu
    deltap2 = delta * delta
    deltap3 = deltap2 * delta
    deltap4 = deltap2 * deltap2
    deltap6 = deltap3 * deltap3
    t1 = 1
    t2 = 4//3 * (7 * mu - 31) / deltap2
    t3 = 32//15  * (84 * mup2 - 982 * mu + 3779) /  deltap4
    t4 = 64//105 * (6949 * mup3 - 153855 * mup2 + 1585743 * mu - 6277237) /
        deltap6
    return beta - (mu - 1) / delta * (t1 + t2 + t3 + t4)
end

function test_besselj_zero_asymptotic_long(nu::Real, n::Integer)
    z = besselj_zero_asymptotic_long(nu, n)
    return SpecialFunctions.besselj(nu, z)
end

function test_besselj_zero_asymptotic(nu::Real, n::Integer)
    z = besselj_zero_asymptotic(nu, n)
    return SpecialFunctions.besselj(nu, z)
end

function test_besselj_zero(nu::Real, n::Integer)
    z = besselj_zero(nu, n)
    return SpecialFunctions.besselj(nu, z)
end


struct Pzero{A<:Real, B<:Real}
  NC::Int
  NT::Int
  QC::Int
  QT::Int
  a::A
  b::B
end

function Pzero(; NC, NT, QC, QT, a=1.0, b=1.0)
  return Pzero(NC, NT, QC, QT, a, b)
end

"""
Model0: Proportion of zeros are the same.
Return posterior samples of gammaC and gammaT.
"""
function infer_Pzero0(m::Pzero, nsamps::Int)
  Nsum = m.NC + m.NT
  Qsum = m.QC + m.QT

  # Posterior distribution.
  dist = Beta(m.a + Qsum, m.b + Nsum - Qsum)

  # Posterior samples of gammaC (and gammaT)
  gamma_samples = rand(dist, nsamps)

  # Loglikelihood
  loglike = Qsum .* log.(gamma_samples) + (Nsum - Qsum) .* log1p.(-gamma_samples)

  return (gammaC_samples=gamma_samples,
          gammaT_samples=gamma_samples,
          distC=dist, distT=dist, loglike=loglike)
end

"""
Model1: Proportion of zeros are different.
Return posterior samples of gammaC and gammaT.
"""
function infer_Pzero1(m::Pzero, nsamps::Int)
  # Posterior distributions.
  distC = Beta(m.a + m.QC, m.b + m.NC - m.QC)
  distT = Beta(m.a + m.QT, m.b + m.NT - m.QT)

  # Posterior samples of gammaC and gammaT.
  gammaC_samples = rand(distC, nsamps)
  gammaT_samples = rand(distT, nsamps)

  # Loglikelihood
  loglike = let
    llC = m.QC .* log.(gammaC_samples) + (m.NC - m.QC) .* log1p.(-gammaC_samples)
    llT = m.QT .* log.(gammaT_samples) + (m.NT - m.QT) .* log1p.(-gammaT_samples)
    llC + llT
  end

  return (gammaC_samples=gammaC_samples,
          gammaT_samples=gammaT_samples,
          distC=distC, distT=distT, loglike=loglike)
end

function compute_log_bf(m::Pzero, nsamps::Int)
  m0 = infer_Pzero0(m::Pzero, nsamps)
  m1 = infer_Pzero1(m::Pzero, nsamps)

  return MCMC.log_bayes_factor(m0.loglike, m1.loglike)
end

function plot_gamma_uq!(gamma_mean::Real, gamma_lower::Real, gamma_upper::Real;
                        color, Q::Int, N::Int, truth=nothing, alpha=.6,
                        gamma_dist=nothing)
  if truth === nothing
    # Plot empirical mean
    vline!([Q / N], color=color, label=nothing, ls=:dash)
  else
    # Plot simulation truth
    vline!([truth], color=color, label=nothing, ls=:dash)
  end

  # Plot posterior mean
  vline!([gamma_mean], color=color, alpha=alpha, label=nothing)

  # 95% CI
  if gamma_dist !== nothing
    grid = range(gamma_lower, gamma_upper, length=100)
    plot!(grid, zero.(grid), fillrange=pdf.(gamma_dist, grid), color=color,
          alpha=.3, label=nothing)
  else
    vline!([gamma_lower, gamma_upper], color=color, label=nothing, ls=:dot,
           lw=2)
  end

  xlabel!(L"\gamma_i")
  ylabel!("density")
end

function plot_gamma_uq!(gamma::Union{Distribution, AbstractVector{<:Real}};
                        color, Q::Int, N::Int, ci_level::Real=0.05,
                        truth=nothing, alpha=.6)
  gamma_mean = mean(gamma)
  gamma_lower = quantile(gamma, ci_level/2)
  gamma_upper = quantile(gamma, 1 - ci_level/2)
  plot_gamma_uq!(gamma_mean, gamma_lower, gamma_upper, color=color, Q=Q, N=N,
                 truth=truth, alpha=alpha, gamma_dist=gamma)
end

function make_gamma_grid(gammaC_post::Distribution, gammaT_post::Distribution;
                         a=0.01, grid_length=100)
  gamma_lower = min(quantile(gammaC_post, a), quantile(gammaT_post, a))
  gamma_upper = max(quantile(gammaC_post, 1-a), quantile(gammaT_post, 1-a))
  return range(gamma_lower, gamma_upper, length=grid_length)
end


# ## Line style

#md # ![FILE_NAME.png](images/FILE_NAME.png)

Plots.supported_styles()

#-

## ref https://jp.mathworks.com/matlabcentral/fileexchange/35230-matlab-plot-gallery-standard-line-styles?focused=6792901&tab=example

using SpecialFunctions

style = Plots.supported_styles()[2:end]
style = reshape(style, 1, length(style))
x = 0:0.2:10;
y0 = besselj.(0,x);
y1 = besselj.(1,x);
y2 = besselj.(2,x);
y3 = besselj.(3,x);
y4 = besselj.(4,x);

plot(x, [y0 y1 y2 y3 y4], line=(3, style))


using Plots; pyplot()
using Random

using Pkg
using Distributions

using GLM
using DataFrames

# The regression function
m(x) = 3*x.-3*x.^2+x.^3-0.1*x.^4

Random.seed!(79015)
nn = 30
xx = range(0, stop = 3, length = nn)
yy = m(xx)+rand(Normal(0, 0.15), nn)

plot(xx, yy,
    seriestype=:scatter,
    label = "",
    m = (0.9, :RebeccaPurple, 5),
    background_color = :Lavender
    )
plot!(m, 0, 3,
    line = (:tomato, 1, 2),
    label = "Regression function")

# We adjust a first order linear estimator
model1 = lm(hcat(repeat([1], outer = 30), xx), yy)

# applies a function to each value of an array and returns a new array
# containing the resulting values:
roundmap(vect, b) = map(x -> round(x, digits = b), vect)

roundmap(coef(model1), 3)

#0.582
#-0.019
#the regression function is 3*x.-3*x.^2+x.^3-0.1*x.^4

coef(model1)

plot(xx, yy,
    seriestype=:scatter,
    label = "",
    m = (0.9, :RebeccaPurple, 5),
    background_color = :Lavender
    )
plot!(m, 0, 3,
    line = (:tomato, 1, 2),
    label = "Regression function")
plot!(x -> sum([1, x].*coef(model1)), 0, 3,
    label = "Linear fit",
    line = (:RebeccaPurple, 1, 2))

#A quadratic fit
model2 = lm(hcat(repeat([1], outer = nn), xx, map(x -> x^2, xx)), yy)

roundmap(coef(model2), 3)

#0.599
#-0.055
#0.012
#the regression function is 3*x.-3*x.^2+x.^3-0.1*x.^4

plot(xx, yy,
    seriestype=:scatter,
    label = "",
    m = (0.9, :RebeccaPurple, 5),
    background_color = :Lavender
    )
plot!(m, 0, 3,
    line = (:tomato, 1, 2),
    label = "Regression function")
plot!(x -> sum([1, x, x^2].*coef(model2)), 0, 3,
    label = "Quadratic fit",
    line = (:RebeccaPurple, 1, 2))

#A cubic fit
model3 = lm(hcat(repeat([1], outer = nn), xx, map(x -> x^2, xx),
    map(x -> x^3, xx)), yy)

roundmap(coef(model3), 3)

#0.014
#2.507
#-2.16
#0.483
#the regression function is 3*x.-3*x.^2+x.^3-0.1*x.^4

plot(xx, yy,
    seriestype=:scatter,
    label = "",
    m = (0.9, :RebeccaPurple, 5),
    background_color = :Lavender
    )
plot!(m, 0, 3,
    line = (:tomato, 1, 2),
    label = "Regression function")
plot!(x -> sum([1, x, x^2, x^3].*coef(model3)), 0, 3,
    label = "Cubic fit",
    line = (:RebeccaPurple, 1, 2))

model4 = lm(hcat(repeat([1], outer = nn), xx, map(x -> x^2, xx),
    map(x -> x^3, xx), map(x -> x^4, xx)), yy)

roundmap(coef(model4), 3)

#-0.069
#3.16
#-3.178
#1.016
#-0.089
#the regression function is 3*x.-3*x.^2+x.^3-0.1*x.^4

plot(xx, yy,
    seriestype=:scatter,
    label = "",
    m = (0.9, :RebeccaPurple, 5),
    background_color = :Lavender
    )
plot!(m, 0, 3,
    line = (:tomato, 1, 2),
    label = "Regression function")
plot!(x -> sum([1, x, x^2, x^3, x^4].*coef(model4)), 0, 3,
    label = "4 degree fit",
    line = (:RebeccaPurple, 1, 2))

A = [repeat([1], outer = 10) xx[1:10]]
B = [repeat([1], outer = 10) xx[11:20]]
C = [repeat([1], outer = 10) xx[21:30]]
Z = [repeat([0], outer = 10) repeat([0], outer = 10)]

X = [A Z Z;
    Z B Z;
    Z Z C]

bets = X'X\X'yy

plot(xx, yy,
    seriestype=:scatter,
    label = "",
    m = (0.9, :RebeccaPurple, 5),
    background_color = :Lavender
    )
plot!(m, 0, 3,
    line = (:tomato, 1, 2),
    label = "Regression function")
plot!(x -> sum([1, x].*bets[1:2]),
    0, 1,
    label = "",
    line = (:RebeccaPurple, 1, 2))
plot!(x -> sum([1, x].*bets[3:4]),
    1, 2,
    label = "",
    line = (:RebeccaPurple, 1, 2))
plot!(x -> sum([1, x].*bets[5:6]),
    2, 3,
    label = "",
    line = (:RebeccaPurple, 1, 2))

k1 = 1
k2 = 2
Kt =[1 k1 -1 -k1 0 0;
    0 0 1 k2 -1 -k2]

aux1 = X'X\Kt'
aux2 = (Kt*((X'X)\Kt'))\(Kt*bets)
tilbets = bets-aux1*aux2

plot(xx, yy,
    seriestype=:scatter,
    label = "",
    m = (0.9, :RebeccaPurple, 5),
    background_color = :Lavender
    )
plot!(m, 0, 3,
    line = (:tomato, 1, 2),
    label = "Regression function")
plot!(x -> sum([1, x].*tilbets[1:2]),
    0, 1,
    label = "",
    line = (:RebeccaPurple, 1, 2))
plot!(x -> sum([1, x].*tilbets[3:4]),
    1, 2,
    label = "",
    line = (:RebeccaPurple, 1, 2))
plot!(x -> sum([1, x].*tilbets[5:6]),
    2, 3,
    label = "",
    line = (:RebeccaPurple, 1, 2))

A = hcat(repeat([1], outer = 10), xx[1:10],
        map(x -> x^2, xx[1:10]), map(x -> x^3, xx[1:10]))
B = hcat(repeat([1], outer = 10), xx[11:20],
        map(x -> x^2, xx[11:20]), map(x -> x^3, xx[11:20]))
C = hcat(repeat([1], outer = 10), xx[21:30],
        map(x -> x^2, xx[21:30]), map(x -> x^3, xx[21:30]))
Z = reshape(repeat(repeat([0], outer = 10), outer = 4), (10, 4))

X = [A Z Z;
    Z B Z;
    Z Z C]

bets = X'X\X'yy

Kt =[1 k1 k1^2 k1^3 -1 -k1 -k1^2 -k1^3  0   0     0     0;
     0  0    0    0  1  k2  k2^2  k2^3 -1 -k2 -k2^2 -k2^3]

aux1 = X'X\Kt'
aux2 = (Kt*((X'X)\Kt'))\(Kt*bets)
tilbets = bets-aux1*aux2

plot(xx, yy,
    seriestype=:scatter,
    label = "",
    m = (0.9, :RebeccaPurple, 5),
    background_color = :Lavender
    )
plot!(m, 0, 3,
    line = (:tomato, 1, 2),
    label = "Regression function")
plot!(x -> sum([1, x, x^2, x^3].*tilbets[1:4]),
    0, 1,
    label = "",
    line = (:RebeccaPurple, 1, 2))
plot!(x -> sum([1, x, x^2, x^3].*tilbets[5:8]),
    1, 2,
    label = "",
    line = (:RebeccaPurple, 1, 2))
plot!(x -> sum([1, x, x^2, x^3].*tilbets[9:12]),
    2, 3,
    label = "",
    line = (:RebeccaPurple, 1, 2))

Kt =[Kt;
     0  1  2k1  3k1^2  0  -1  -2k1 -3k1^2  0   0     0      0;
     0  0    0      0  0   1   2k2  3k2^2  0  -1  -2k2 -3k2^2]

aux1 = X'X\Kt'
aux2 = (Kt*((X'X)\Kt'))\(Kt*bets)
tilbets = bets-aux1*aux2

plot(xx, yy,
    seriestype=:scatter,
    label = "",
    m = (0.9, :RebeccaPurple, 5),
    background_color = :Lavender
    )
plot!(m, 0, 3,
    line = (:tomato, 1, 2),
    label = "Regression function")
plot!(x -> sum([1, x, x^2, x^3].*tilbets[1:4]),
    0, 1,
    label = "",
    line = (:RebeccaPurple, 1, 2))
plot!(x -> sum([1, x, x^2, x^3].*tilbets[5:8]),
    1, 2,
    label = "",
    line = (:RebeccaPurple, 1, 2))
plot!(x -> sum([1, x, x^2, x^3].*tilbets[9:12]),
    2, 3,
    label = "",
    line = (:RebeccaPurple, 1, 2))

Kt =[Kt;
    0 0 2 6k1 0 0 -2 -6k1 0 0  0    0;
    0 0 0   0 0 0  2  6k2 0 0 -2 -6k2]

aux1 = X'X\Kt'
aux2 = (Kt*((X'X)\Kt'))\(Kt*bets)
tilbets = bets-aux1*aux2

plot(xx, yy,
    seriestype=:scatter,
    label = "",
    m = (0.9, :RebeccaPurple, 5),
    background_color = :Lavender
    )
plot!(m, 0, 3,
    line = (:tomato, 1, 2),
    label = "Regression function")
plot!(x -> sum([1, x, x^2, x^3].*tilbets[1:4]),
    0, 1,
    label = "",
    line = (:RebeccaPurple, 1, 2))
plot!(x -> sum([1, x, x^2, x^3].*tilbets[5:8]),
    1, 2,
    label = "",
    line = (:RebeccaPurple, 1, 2))
plot!(x -> sum([1, x, x^2, x^3].*tilbets[9:12]),
    2, 3,
    label = "",
    line = (:RebeccaPurple, 1, 2),
    ylims = (-0.5, 1.5))

using Dierckx
spl = Spline1D(xx, yy, [k1, k2], k = 3, bc = "extrapolate")
spl_evaluated(x) = evaluate(spl,x)

plot(xx, yy,
    seriestype=:scatter,
    label = "",
    m = (0.9, :RebeccaPurple, 5),
    background_color = :Lavender
    )
plot!(m, 0, 3,
    line = (:tomato, 1, 2),
    label = "Regression function")
plot!(spl_evaluated, 0, 3,
    label = "",
    line = (:RebeccaPurple, 1, 2),
    background_color = :Lavender,
    ylims = (-0.5, 1.5)
    )


export hchebyshevcenter, vchebyshevcenter, chebyshevcenter
using JuMP
"""
    hchebyshevcenter(p::HRep[, solver])

Return a tuple with the center and radius of the largest euclidean ball contained in the polyhedron `p`.
Throws an error if the polyhedron is empty or if the radius is infinite.
"""
function hchebyshevcenter(p::HRep, solver=Polyhedra.solver(p))
    m = Model(solver=solver)
    c = @variable m [1:fulldim(p)]
    for hp in hyperplanes(p)
        a = Vector{Float64}(hp.a)
        β = Float64(hp.β)
        @constraint m dot(a, c) == β
    end
    @variable m r[1:nhalfspaces(p)] >= 0
    for (i, hs) in enumerate(halfspaces(p))
        a = Vector{Float64}(hs.a)
        β = Float64(hs.β)
        @constraint m dot(a, c) + r[i] * norm(a, 2) <= β
    end
    @variable m minr >= 0
    @constraint m minr .<= r
    @objective m Max minr
    status = solve(m)
    if status != :Optimal
        if status == :Infeasible
            error("An empty polyhedron has no H-Chebyshev center")
        elseif status == :Unbounded
            error("The polyhedron contains euclidean ball of arbitrary large radius")
        else
            error("Solver returned $status when computing the H-Chebyshev center")
        end
    end
    @constraint m minr == getvalue(minr)
    @variable m maxr >= 0
    @constraint m maxr .>= r
    @objective m Min maxr
    status = solve(m)
    @assert status == :Optimal
    (getvalue(c), getvalue(minr))
end

# TODO solver here should not be VRepSolver
"""
    vchebyshevcenter(p::VRep[, solver])

Return a tuple with the center and radius of the smallest euclidean ball containing the polyhedron `p`.
Throws an error if the polyhedron is empty or if the radius is infinite (i.e. `p` is not a polytope, it contains rays).
"""
function vchebyshevcenter(p::VRep, solver=Polyhedra.solver(p))
    error("TODO")
end

"""
    chebyshevcenter(p::Rep[, solver])

If `p` is a H-representation or is a polyhedron for which the H-representation has already been computed, calls `hchebyshevcenter`, otherwise, call `vchebyshevcenter`.
"""
function chebyshevcenter(p::Polyhedron, solver=Polyhedra.solver(p))
    if hrepiscomputed(p)
        hchebyshevcenter(p, solver)
    else
        vchebyshevcenter(p, solver)
    end
end
chebyshevcenter(p::HRepresentation, solver=Polyhedra.solver(p)) = hchebyshevcenter(p, solver)
chebyshevcenter(p::VRepresentation, solver=Polyhedra.solver(p)) = vchebyshevcenter(p, solver)


function TwoSum(a::Float64,b::Float64)
	x = a+b
	z = x-a
	y = (a-(x-z))+(b-z)
	return (x,y)
end

function Split(a::Float64)
	p = 52
	s = 26
	factor = 67108865
	c = factor*a
	x = c - (c-a)
	y = a - x
    return(x,y)
end

function Split(a::Float32)
	p = 23
	s = 12
	factor = 4097
	c = factor*a
	x = c - (c-a)
	y = a - x
    return (x,y)
end

function TwoProduct(a::Float64,b::Float64)
	x = a*b
	[a1,a2] = Split(a)
	[b1,b2] = Split(b)
	y = (a2*b2−(((x−a1*b1)−a2*b1)−a1*b2))
end

function TwoSumComplex(x::Complex128,y::Complex128)
	a = real(x)
	b = imag(x)
	c = real(y)
	d = imag(y)
	(s1,e1) = TwoSum(a,c)
	(s2,e2) = TwoSum(b,d)
	s = s1 + im*s2
	e = e1 + im*e2
	return (s,e)
end

function TwoProductComplex(x::Complex128,y::Complex128)
	a = real(x)
	b = imag(x)
	c = real(y)
	d = imag(y)
	(z1,h1) = TwoProduct(a,c)
	(z2,h2) = TwoProduct(b,d)
	(z3,h3) = TwoProduct(a,d)
	(z4,h4) = TwoProduct(b,c)
	(z5,h5) = TwoSum(z1,-z2)
	(z6,h6) = TwoSum(z3,z4)
	p = z5+im*z6
	e = h1+im*h3
	f = -h2+im*h4
	g = h5+im*h6
	return (p,e,f,g)
end

function TwoProductComplexFast(x::Complex128,y::Complex128)
	a = real(x)
	b = imag(x)
	c = real(y)
	d = imag(y)
	(a1,a2) = Split(a)
	(b1,b2) = Split(b)
	(c1,c2) = Split(c)
	(d1,d2) = Split(d)
	z1 = a*c
	z2 = b*d
	z3 = a*d
	z4 = b*c
	h1 = (a2*c2-(((z1-a1*c1)-a2*c1)-a1*c2))
	h2 = (b2*d2-(((z2-b1*d1)-b2*d1)-b1*d2))
	h3 = (a2*d2-(((z3-a1*d1)-a2*d1)-a1*d2))
	h4 = (b2*c2-(((z4-b1*c1)-b2*c1)-b1*c2))
	(z5,h5) = TwoSum(z1,-z2)
	(z6,h6) = TwoSum(z3,z4)
	p =  z5 + im*z6
	e =  h1 + im*h3
	f = -h2 + im*h4
	g =  h5 + im*h6
	return (p,e,f,g)
end


using QuadGK

function BlackScholes(S, T, K, r, σ)
    f(x) = exp(-0.5*x^2)
    N(d) = 1/sqrt(2*pi) * quadgk(f, -Inf, d)[1]
    d1 = ( log(S/K) + (r + 0.5 * σ^2)*(T))/(σ*sqrt(T))
    d2 = d1 - σ*sqrt(T)
    function call()
        return  S * N(d1) - K * exp(-r*(T))*N(d2)
    end
    function put()
        return K*exp(-r*(T))*N(-d2) - S*N(-d1)
    end
    return call(), put()
end

function BlackScholesDividend(S, T, K, r, σ, q)
    f(x) = exp(-0.5*x^2)
    N(d) = 1/sqrt(2*pi) * quadgk(f, -Inf, d)[1]
    d1 = ( log(S/K) + (r - q + 0.5 * σ^2)*(T))/(σ*sqrt(T))
    d2 = d1 - σ*sqrt(T)
    function call()
        return S*exp(-q*(T))*N(d1) - K*exp(-r*(T))*N(d2)
    end
    function put()
        return K*exp(-r*(T))*N(-d2) - S*exp(-q*(T))*N(d1)
    end
    return call(), put()
end


export vandpoly #export function vandpoly, make it public
Reduce.Preload() ## required otherwise Reduce has a fit -?

## ##The Vandermonde matrix method #https://ece.uwaterloo.ca/~dwharder/NumericalAnalysis/05Interpolation/vandermonde/#:~:text=The%20Vandermonde%20method%20is%20the,Gaussian%20elimination%20or%20PLU%20decomposition.

##Create macro genvandpoly with:
# INPUTS:
#  index - coefficient index we want to return
#  leny - number of points available for leny-1 degree polynomial interpolation
#  x - array with x coordinates of leny points
#  y - array with y coordinates of leny points
# OUTPUTS:
#  coefs[index] - the [$index]'s coefficient

macro genvandpoly(index,leny,x,y) ## generate poly functions using the Vandermonde matrix method
    leny = :($leny) #length of y #expression with number leny
    symX = [] #symmetric X matrix declaration
    symY = [] #symmetric Y matrix declaration
    A = Array{Expr}(undef, leny, leny) #Array of expressions (ie. Vandermonde matrix)
##we push an expression xᵢ,yᵢ into symX,symY
    for i = 1:leny #for all integers i from 1 to leny
        push!(symX, Meta.parse("x$i")) #meta.parse - parses a string to an expression
        push!(symY, Meta.parse("y$i")) #meta.parse - parses a string to an expression
    end
## ## Assemble A matrix #we go over all elements in A ∈ℜⁿˣⁿ matrix #we go over all elements in A and construct them as expressions
    for i = 1:leny  #i is the row number (ie. equation or point)
        for j = 1:leny #j is the column number (ie. power of x)
            pwr = leny-j #power is the total number of columns - index of current column
            var = symX[i] #variable that we need to raise to the power, took from symX
            A[i,j] = :($var^$pwr) #each element of A is an expression var^pwr, with var and pwr being numbers
        end
    end
## ## invert Vandermonde matrix "A" to find coefficients of polynomial
    coefs = Algebra.:*(Algebra.inv(A),symY) #coefs stores polynomial coefficients and is coefs=A\symY
    for i = 1:leny #for all integers i in [1,leny] (ie. all elements in coefs)
        str = repr(coefs[i]) #str is a string representation of element i from coefs
        for j = 1:leny
            str = replace(str,"y"*string(j)=>"$y"*"["*string(j)*"]") #replaces into str, the string yⱼ with the numerical value of y[j]
            str = replace(str,"x"*string(j)=>"$x"*"["*string(j)*"]") #replaces into str, the string xⱼ with the numerical value of x[j]
        end
        coefs[i] = Meta.parse(chop(str, head=2, tail=1)) #remove first 2 and last 1 characters from string str and parse it as an expression
    end
##return the coefficient with index number "index"
    return coefs[:($index)]
end

## Function vandpoly
#  INPUTS:
# x - vector with x coordinates from n points
# y - vector with y coordiantes from n points
#  OUTPUTS:
# Polynomial([cn,cn-1,...,c2,c1]) - Polynomial expression of the form c₁xⁿ⁻¹+c₂xⁿ⁻²+...+cₙ₋₁x+cₙ

function vandpoly(x,y) #used to return a polynomial of degree n-1 out of n datapoints in ℜ²
    xLength = length(x) #xLength is the length of x vector

    if xLength == 2 #if we have only 2 points we fit y=c₁x¹+c₂
        c1 = @genvandpoly(1, 2,:($x),:($y)) #c1 is the coefficient of x¹
        c2 = @genvandpoly(2, 2,:($x),:($y)) #c2 is the coefficient of x⁰
        return Polynomial([c2,c1]) #Return the polynomial c₁x¹+c₂

    elseif xLength == 3 #if we have only 3 points we fit y=c₁x²+c₂x¹+c₃
        c1 = @genvandpoly(1, 3,:($x),:($y)) #c1 is the coefficient of x²
        c2 = @genvandpoly(2, 3,:($x),:($y)) #c2 is the coefficient of x¹
        c3 = @genvandpoly(3, 3,:($x),:($y)) #c3 is the coefficient of x⁰
        return Polynomial([c3,c2,c1]) #Return the polynomial c₁x²+c₂x¹+c₃

    elseif xLength == 4 #if we have only 4 points we fit y=c₁x³+c₂x²+c₃x¹+c₄
        c1 = @genvandpoly(1, 4,:($x),:($y)) #c1 is the coefficient of x³
        c2 = @genvandpoly(2, 4,:($x),:($y)) #c2 is the coefficient of x²
        c3 = @genvandpoly(3, 4,:($x),:($y)) #c3 is the coefficient of x¹
        c4 = @genvandpoly(4, 4,:($x),:($y)) #c4 is the coefficient of x⁰
        return Polynomial([c4,c3,c2,c1]) #Return the polynomial c₁x³+c₂x²+c₃x¹+c₄

    elseif xLength == 5 #if we have only 5 points we fit y=c₁x⁴+c₂x³+c₃x²+c₄x¹+c₅
        c1 = @genvandpoly(1, 5,:($x),:($y)) #c1 is the coefficient of x⁴
        c2 = @genvandpoly(2, 5,:($x),:($y)) #c2 is the coefficient of x³
        c3 = @genvandpoly(3, 5,:($x),:($y)) #c3 is the coefficient of x²
        c4 = @genvandpoly(4, 5,:($x),:($y)) #c4 is the coefficient of x¹
        c5 = @genvandpoly(5, 5,:($x),:($y)) #c5 is the coefficient of x⁰
        return Polynomial([c5,c4,c3,c2,c1]) #Return the polynomial c₁x⁴+c₂x³+c₃x²+c₄x¹+c₅

    else #if we have more than 5 points we simply perform C\y
        Polynomial(inv([xval^j for xval in eachindex(x), j = 0:xlength - 1]) * y) # create poly coefficient by inverting array of linear equations
        println("Warning, function behaves slow when exceeding more than 5 datapoints")
    end
end


using ModelingToolkit, MethodOfLines, LinearAlgebra, OrdinaryDiffEq
using DomainSets

using Plots

local sol
@time begin #@testset "Test 01: Brusselator equation 2D" begin
       @parameters x y t
       @variables u(..) v(..)
       Dt = Differential(t)
       Dx = Differential(x)
       Dy = Differential(y)
       Dxx = Differential(x)^2
       Dyy = Differential(y)^2

       brusselator_f(x, y, t) = (((x-0.3)^2 + (y-0.6)^2) <= 0.1^2) * (t >= 1.1) * 5.

       x_min = y_min = t_min = 0.0
       x_max = y_max = 1.0
       t_max = 11.5

       α = 10.

       u0(x,y,t) = 22(y*(1-y))^(3/2)
       v0(x,y,t) = 27(x*(1-x))^(3/2)

       eq = [Dt(u(x,y,t)) ~ 1. + v(x,y,t)*u(x,y,t)^2 - 4.4*u(x,y,t) + α*(Dxx(u(x,y,t)) + Dyy(u(x,y,t))) + brusselator_f(x, y, t),
             Dt(v(x,y,t)) ~ 3.4*u(x,y,t) - v(x,y,t)*u(x,y,t)^2 + α*(Dxx(v(x,y,t)) + Dyy(v(x,y,t)))]

       domains = [x ∈ Interval(x_min, x_max),
                  y ∈ Interval(y_min, y_max),
                  t ∈ Interval(t_min, t_max)]

       bcs = [u(x,y,0) ~ u0(x,y,0),
              u(0,y,t) ~ u(1,y,t),
              u(x,0,t) ~ u(x,1,t),

              v(x,y,0) ~ v0(x,y,0),
              v(0,y,t) ~ v(1,y,t),
              v(x,0,t) ~ v(x,1,t)]

       @named pdesys = PDESystem(eq,bcs,domains,[x,y,t],[u(x,y,t),v(x,y,t)])

       # Method of lines discretization
       dx = 1/4
       dy = 1/4

       order = 2

       discretization = MOLFiniteDifference([x=>dx, y=>dy], t, approx_order=order)

       # Convert the PDE problem into an ODE problem
       generate_code(pdesys,discretization, "brusselator_2D_ode.jl")
end


function score(x, y)
    distance = hypot(x, y)
    distance > 10 && return 0
    distance > 5 && distance <= 10 && return 1
    distance > 1 && distance <= 5  && return 5
    distance <= 1 && return 10
end