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

using Markdown
using InteractiveUtils

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

# ╔═╡ 60941eaa-1aea-11eb-1277-97b991548781
begin
    using PlutoUI,GridVisualize,SimplexGridFactory,Triangulate,ExtendableGrids,VoronoiFVM,PlutoVista,PlutoUI,PyPlot
end

# ╔═╡ de468cb9-b34d-4d2e-b911-9b93920caca1
md"""
# Flux reconstruction and visualization for the Laplace operator
"""

# ╔═╡ 928a70c5-4706-40a1-9387-abcb71c09443
"""
Define a "Swiss cheese domain" with punched-out holes, where each hole boundary corresponds to a different boundary condition.
"""
function swiss_cheese_2d()

    function circlehole!(builder, center, radius; n=20)
        points=[point!(builder, center[1]+radius*sin(t),center[2]+radius*cos(t)) for t in range(0,2π,length=n)]
        for i=1:n-1
            facet!(builder,points[i],points[i+1])
        end
        facet!(builder,points[end],points[1])
        holepoint!(builder,center)
    end

    builder=SimplexGridBuilder(Generator=Triangulate)
    cellregion!(builder,1)
    maxvolume!(builder,0.1)
    regionpoint!(builder,0.1,0.1)

    p1=point!(builder,0,0)
    p2=point!(builder,10,0)
    p3=point!(builder,10,10)
    p4=point!(builder,0,10)

    facetregion!(builder,1)
    facet!(builder,p1,p2)
    facet!(builder,p2,p3)
    facet!(builder,p3,p4)
    facet!(builder,p4,p1)

    holes=[1.0 2.0;
           8.0 9.0;
           2.0 8.0;
  		   8.0 4.0;
           9.0 1.0;
           3.0 4.0;
           4.0 6.0;
           7.0 9.0;
           4.0 7.0;
           7.0 5.0;
           2.0 1.0;
           4.0 1.0;
           4.0 8.0;
           3.0 6.0;
           4.0 9.0;
           6.0 9.0;
           3.0 5.0;
           1.0 4.0]'

    radii=[0.15, 0.15, 0.1, 0.35, 0.2, 0.3, 0.1, 0.4, 0.1, 0.4,  0.2, 0.2, 0.2, 0.35, 0.15, 0.25, 0.15, 0.25]

    for i=1:length(radii)
        facetregion!(builder,i+1)
        circlehole!(builder,holes[:,i], radii[i])
    end

    simplexgrid(builder)
end

# ╔═╡ 49f425a4-455c-40f5-b939-da704032cb88
md"""
Generate & plot grid
"""

# ╔═╡ bc304085-69c3-4974-beb4-6f2b981ac0f1
grid=swiss_cheese_2d()

# ╔═╡ ad0367e6-7308-46bc-a7ff-9c17dcfe470d
gridplot(grid,Plotter=PlutoVista)

# ╔═╡ 236d6cd5-c190-49c7-98fe-021fae224455
"""
Simple flux function for Laplace operator
"""
flux(y,u,edge)= y[1]=u[1,1]-u[1,2]

# ╔═╡ f4ebe6ad-4e04-4f33-9a66-6bec977adf4d
md"""
Define a finite volume system with Dirichlet boundary conditions at some of the holes
"""

# ╔═╡ 934a98f1-9734-45bc-848e-4d4d8ad80e4e
physics=VoronoiFVM.Physics(flux=flux)

# ╔═╡ f3af20fb-c3cc-41af-9782-d40adf2371f7
system=VoronoiFVM.System(grid,physics)

# ╔═╡ 50ea0a7e-97e0-428d-b4ec-6bd4d7aef31f
enable_species!(system,1,[1])

# ╔═╡ 49d3c96b-f6b4-4581-8600-8aaf50bb07fc
boundary_dirichlet!(system,1,2,10.0)

# ╔═╡ fa5200ff-713e-48fb-b452-c6ee96935cfc
boundary_dirichlet!(system,1,3,0.0)

# ╔═╡ 6b09f75a-521b-40d9-9636-155e62ab0cac
md"""
At hole #11, the value will be bound to a slider defined below
"""

# ╔═╡ d86c43f3-ec2f-4fae-88d2-1068603e7044
md"""
Solve, and trigger solution upon boundary value change
"""

# ╔═╡ 41bd1230-c87c-47b0-8e58-67ad55609fd3
md"""
Reconstruct the node flux. It is a ``d\times n_{spec}\times n_{nodes}`` tensor.
`nf[:,ispec,:]` then is a vector function representing the flux density of species `ispec` in each node of the domain. This readily can be fed into `GridVisualize.vectorplot`.
"""

# ╔═╡ 17be52fb-f55b-4b3d-85e5-33f36134046b
vis=GridVisualizer(Plotter=PlutoVista,dim=2,resolution=(400,400));vis

# ╔═╡ 03f582ec-4e95-4ca4-8482-9c797027810d
md"""
``v_{11}:`` $(@bind  val11 Slider(0:0.1:10,default=5,show_value=true))
"""

# ╔═╡ e8ae2b22-ac60-449b-952f-d2c55852677c
boundary_dirichlet!(system,1,11,val11)

# ╔═╡ 27efdcac-8ee4-47e4-905d-8d8c7313ddd1
val11; sol=solve(unknowns(system,inival=0),system)

# ╔═╡ 41f427c1-b6ad-46d4-9151-1d872b4efeb6
nf=nodeflux(system,sol)

# ╔═╡ c9b9fdb1-5734-430f-949a-6e6126d2f091
"""
Joint plot of solution and nodal flux of the solution
"""
function myplot(vis)
	scalarplot!(vis,grid,sol[1,:],levels=9,colormap=:summer,clear=true)
	vectorplot!(vis,grid,nf[:,1,:],clear=false,spacing=0.5,vscale=1.5)
	reveal(vis)
end

# ╔═╡ 531edb71-6d32-4231-b117-5e36416d2fb1
myplot(vis)

# ╔═╡ e0b92e00-dd0b-448b-baae-5cd4e0a2059a
md"""
Plot with PyPlot: $(@bind use_pyplot CheckBox(default=false))
"""

# ╔═╡ 1495eb67-12ec-4662-b0e7-049ffef569c0
if use_pyplot
	pyvis=GridVisualizer(Plotter=PyPlot,resolution=(400,400))
	myplot(pyvis)
end

# ╔═╡ 00000000-0000-0000-0000-000000000001
PLUTO_PROJECT_TOML_CONTENTS = """
[deps]
ExtendableGrids = "cfc395e8-590f-11e8-1f13-43a2532b2fa8"
GridVisualize = "5eed8a63-0fb0-45eb-886d-8d5a387d12b8"
PlutoUI = "7f904dfe-b85e-4ff6-b463-dae2292396a8"
PlutoVista = "646e1f28-b900-46d7-9d87-d554eb38a413"
PyPlot = "d330b81b-6aea-500a-939a-2ce795aea3ee"
SimplexGridFactory = "57bfcd06-606e-45d6-baf4-4ba06da0efd5"
Triangulate = "f7e6ffb2-c36d-4f8f-a77e-16e897189344"
VoronoiFVM = "82b139dc-5afc-11e9-35da-9b9bdfd336f3"

[compat]
ExtendableGrids = "~0.8.3"
GridVisualize = "~0.3.5"
PlutoUI = "~0.7.16"
PlutoVista = "~0.8.2"
PyPlot = "~2.10.0"
SimplexGridFactory = "~0.5.8"
Triangulate = "~2.1.0"
VoronoiFVM = "~0.13.1"
"""

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

julia_version = "1.7.0-rc1"
manifest_format = "2.0"

[[deps.AbstractTrees]]
git-tree-sha1 = "03e0550477d86222521d254b741d470ba17ea0b5"
uuid = "1520ce14-60c1-5f80-bbc7-55ef81b5835c"
version = "0.3.4"

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

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

[[deps.ArnoldiMethod]]
deps = ["LinearAlgebra", "Random", "StaticArrays"]
git-tree-sha1 = "f87e559f87a45bece9c9ed97458d3afe98b1ebb9"
uuid = "ec485272-7323-5ecc-a04f-4719b315124d"
version = "0.1.0"

[[deps.ArrayInterface]]
deps = ["Compat", "IfElse", "LinearAlgebra", "Requires", "SparseArrays", "Static"]
git-tree-sha1 = "b8d49c34c3da35f220e7295659cd0bab8e739fed"
uuid = "4fba245c-0d91-5ea0-9b3e-6abc04ee57a9"
version = "3.1.33"

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

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

[[deps.Cassette]]
git-tree-sha1 = "6ce3cd755d4130d43bab24ea5181e77b89b51839"
uuid = "7057c7e9-c182-5462-911a-8362d720325c"
version = "0.3.9"

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

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

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

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

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

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

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

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

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

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

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

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

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

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

[[deps.DiffRules]]
deps = ["NaNMath", "Random", "SpecialFunctions"]
git-tree-sha1 = "7220bc21c33e990c14f4a9a319b1d242ebc5b269"
uuid = "b552c78f-8df3-52c6-915a-8e097449b14b"
version = "1.3.1"

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

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

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

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

[[deps.ElasticArrays]]
deps = ["Adapt"]
git-tree-sha1 = "a0fcc1bb3c9ceaf07e1d0529c9806ce94be6adf9"
uuid = "fdbdab4c-e67f-52f5-8c3f-e7b388dad3d4"
version = "1.2.9"

[[deps.ExtendableGrids]]
deps = ["AbstractTrees", "Dates", "DocStringExtensions", "ElasticArrays", "InteractiveUtils", "LinearAlgebra", "Printf", "Random", "SparseArrays", "Test"]
git-tree-sha1 = "85dbe70afc7153ad510577f158214b45be22593b"
uuid = "cfc395e8-590f-11e8-1f13-43a2532b2fa8"
version = "0.8.3"

[[deps.ExtendableSparse]]
deps = ["DocStringExtensions", "LinearAlgebra", "Printf", "Requires", "SparseArrays", "SuiteSparse", "Test"]
git-tree-sha1 = "0341e41e45e6c5e46be89c33543082a0a867707c"
uuid = "95c220a8-a1cf-11e9-0c77-dbfce5f500b3"
version = "0.6.5"

[[deps.FileIO]]
deps = ["Pkg", "Requires", "UUIDs"]
git-tree-sha1 = "3c041d2ac0a52a12a27af2782b34900d9c3ee68c"
uuid = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549"
version = "1.11.1"

[[deps.FillArrays]]
deps = ["LinearAlgebra", "Random", "SparseArrays", "Statistics"]
git-tree-sha1 = "8756f9935b7ccc9064c6eef0bff0ad643df733a3"
uuid = "1a297f60-69ca-5386-bcde-b61e274b549b"
version = "0.12.7"

[[deps.FiniteDiff]]
deps = ["ArrayInterface", "LinearAlgebra", "Requires", "SparseArrays", "StaticArrays"]
git-tree-sha1 = "8b3c09b56acaf3c0e581c66638b85c8650ee9dca"
uuid = "6a86dc24-6348-571c-b903-95158fe2bd41"
version = "2.8.1"

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

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

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

[[deps.GridVisualize]]
deps = ["ColorSchemes", "Colors", "DocStringExtensions", "ElasticArrays", "ExtendableGrids", "GeometryBasics", "LinearAlgebra", "Observables", "OrderedCollections", "PkgVersion", "Printf", "Requires", "StaticArrays"]
git-tree-sha1 = "063aa184cec6aff22f7c2f6a498075a93b5fa304"
uuid = "5eed8a63-0fb0-45eb-886d-8d5a387d12b8"
version = "0.3.5"

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

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

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

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

[[deps.Inflate]]
git-tree-sha1 = "f5fc07d4e706b84f72d54eedcc1c13d92fb0871c"
uuid = "d25df0c9-e2be-5dd7-82c8-3ad0b3e990b9"
version = "0.1.2"

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

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

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

[[deps.IterativeSolvers]]
deps = ["LinearAlgebra", "Printf", "Random", "RecipesBase", "SparseArrays"]
git-tree-sha1 = "1a8c6237e78b714e901e406c096fc8a65528af7d"
uuid = "42fd0dbc-a981-5370-80f2-aaf504508153"
version = "0.9.1"

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

[[deps.JLD2]]
deps = ["DataStructures", "FileIO", "MacroTools", "Mmap", "Pkg", "Printf", "Reexport", "TranscodingStreams", "UUIDs"]
git-tree-sha1 = "46b7834ec8165c541b0b5d1c8ba63ec940723ffb"
uuid = "033835bb-8acc-5ee8-8aae-3f567f8a3819"
version = "0.4.15"

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

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

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

[[deps.LibCURL]]
deps = ["LibCURL_jll", "MozillaCACerts_jll"]
uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21"

[[deps.LibCURL_jll]]
deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll", "Zlib_jll", "nghttp2_jll"]
uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0"

[[deps.LibGit2]]
deps = ["Base64", "NetworkOptions", "Printf", "SHA"]
uuid = "76f85450-5226-5b5a-8eaa-529ad045b433"

[[deps.LibSSH2_jll]]
deps = ["Artifacts", "Libdl", "MbedTLS_jll"]
uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8"

[[deps.Libdl]]
uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb"

[[deps.LightGraphs]]
deps = ["ArnoldiMethod", "DataStructures", "Distributed", "Inflate", "LinearAlgebra", "Random", "SharedArrays", "SimpleTraits", "SparseArrays", "Statistics"]
git-tree-sha1 = "432428df5f360964040ed60418dd5601ecd240b6"
uuid = "093fc24a-ae57-5d10-9952-331d41423f4d"
version = "1.3.5"

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

[[deps.LogExpFunctions]]
deps = ["ChainRulesCore", "DocStringExtensions", "IrrationalConstants", "LinearAlgebra"]
git-tree-sha1 = "34dc30f868e368f8a17b728a1238f3fcda43931a"
uuid = "2ab3a3ac-af41-5b50-aa03-7779005ae688"
version = "0.3.3"

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

[[deps.PkgVersion]]
deps = ["Pkg"]
git-tree-sha1 = "a7a7e1a88853564e551e4eba8650f8c38df79b37"
uuid = "eebad327-c553-4316-9ea0-9fa01ccd7688"
version = "0.1.1"

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

[[deps.PlutoVista]]
deps = ["ColorSchemes", "Colors", "DocStringExtensions", "GridVisualize", "UUIDs"]
git-tree-sha1 = "ce59550b517ab41b5e687174f93f57c76f872b6d"
uuid = "646e1f28-b900-46d7-9d87-d554eb38a413"
version = "0.8.2"

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

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

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

[[deps.PyPlot]]
deps = ["Colors", "LaTeXStrings", "PyCall", "Sockets", "Test", "VersionParsing"]
git-tree-sha1 = "14c1b795b9d764e1784713941e787e1384268103"
uuid = "d330b81b-6aea-500a-939a-2ce795aea3ee"
version = "2.10.0"

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

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

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

[[deps.RecursiveArrayTools]]
deps = ["ArrayInterface", "ChainRulesCore", "DocStringExtensions", "FillArrays", "LinearAlgebra", "RecipesBase", "Requires", "StaticArrays", "Statistics", "ZygoteRules"]
git-tree-sha1 = "ff7495c78a192ff7d59531d9f14db300c847a4bc"
uuid = "731186ca-8d62-57ce-b412-fbd966d074cd"
version = "2.19.1"

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

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

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

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

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

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

[[deps.SimplexGridFactory]]
deps = ["DocStringExtensions", "ElasticArrays", "ExtendableGrids", "GridVisualize", "LinearAlgebra", "Printf", "Test"]
git-tree-sha1 = "e55646af1d0f68763eaca28f666d381982eeb65f"
uuid = "57bfcd06-606e-45d6-baf4-4ba06da0efd5"
version = "0.5.8"

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

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

[[deps.SparseDiffTools]]
deps = ["Adapt", "ArrayInterface", "Compat", "DataStructures", "FiniteDiff", "ForwardDiff", "LightGraphs", "LinearAlgebra", "Requires", "SparseArrays", "StaticArrays", "VertexSafeGraphs"]
git-tree-sha1 = "36a4d27a02af48a1eafd2baff58b32deeeb68926"
uuid = "47a9eef4-7e08-11e9-0b38-333d64bd3804"
version = "1.16.5"

[[deps.SparsityDetection]]
deps = ["Cassette", "DocStringExtensions", "LinearAlgebra", "SparseArrays", "SpecialFunctions"]
git-tree-sha1 = "9e182a311d169cb9fe0c6501aa252983215fe692"
uuid = "684fba80-ace3-11e9-3d08-3bc7ed6f96df"
version = "0.3.4"

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

[[deps.Static]]
deps = ["IfElse"]
git-tree-sha1 = "a8f30abc7c64a39d389680b74e749cf33f872a70"
uuid = "aedffcd0-7271-4cad-89d0-dc628f76c6d3"
version = "0.3.3"

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

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

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

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

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

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

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

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

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

[[deps.TranscodingStreams]]
deps = ["Random", "Test"]
git-tree-sha1 = "216b95ea110b5972db65aa90f88d8d89dcb8851c"
uuid = "3bb67fe8-82b1-5028-8e26-92a6c54297fa"
version = "0.9.6"

[[deps.Triangle_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "bfdd9ef1004eb9d407af935a6f36a4e0af711369"
uuid = "5639c1d2-226c-5e70-8d55-b3095415a16a"
version = "1.6.1+0"

[[deps.Triangulate]]
deps = ["DocStringExtensions", "Libdl", "Printf", "Test", "Triangle_jll"]
git-tree-sha1 = "2b4f716b192c0c615d96d541ee029e85666388cb"
uuid = "f7e6ffb2-c36d-4f8f-a77e-16e897189344"
version = "2.1.0"

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

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

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

[[deps.VertexSafeGraphs]]
deps = ["LightGraphs"]
git-tree-sha1 = "b9b450c99a3ca1cc1c6836f560d8d887bcbe356e"
uuid = "19fa3120-7c27-5ec5-8db8-b0b0aa330d6f"
version = "0.1.2"

[[deps.VoronoiFVM]]
deps = ["DiffResults", "DocStringExtensions", "ExtendableGrids", "ExtendableSparse", "ForwardDiff", "GridVisualize", "IterativeSolvers", "JLD2", "LinearAlgebra", "Printf", "RecursiveArrayTools", "SparseArrays", "SparseDiffTools", "SparsityDetection", "StaticArrays", "SuiteSparse", "Test"]
git-tree-sha1 = "db381645bec1d798c560fe415593663d4a2194ac"
uuid = "82b139dc-5afc-11e9-35da-9b9bdfd336f3"
version = "0.13.1"

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

[[deps.ZygoteRules]]
deps = ["MacroTools"]
git-tree-sha1 = "8c1a8e4dfacb1fd631745552c8db35d0deb09ea0"
uuid = "700de1a5-db45-46bc-99cf-38207098b444"
version = "0.2.2"

[[deps.libblastrampoline_jll]]
deps = ["Artifacts", "Libdl", "OpenBLAS_jll"]
uuid = "8e850b90-86db-534c-a0d3-1478176c7d93"

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

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

# ╔═╡ Cell order:
# ╟─de468cb9-b34d-4d2e-b911-9b93920caca1
# ╠═60941eaa-1aea-11eb-1277-97b991548781
# ╠═928a70c5-4706-40a1-9387-abcb71c09443
# ╟─49f425a4-455c-40f5-b939-da704032cb88
# ╠═bc304085-69c3-4974-beb4-6f2b981ac0f1
# ╠═ad0367e6-7308-46bc-a7ff-9c17dcfe470d
# ╠═236d6cd5-c190-49c7-98fe-021fae224455
# ╟─f4ebe6ad-4e04-4f33-9a66-6bec977adf4d
# ╠═934a98f1-9734-45bc-848e-4d4d8ad80e4e
# ╠═f3af20fb-c3cc-41af-9782-d40adf2371f7
# ╠═50ea0a7e-97e0-428d-b4ec-6bd4d7aef31f
# ╠═49d3c96b-f6b4-4581-8600-8aaf50bb07fc
# ╠═fa5200ff-713e-48fb-b452-c6ee96935cfc
# ╟─6b09f75a-521b-40d9-9636-155e62ab0cac
# ╠═e8ae2b22-ac60-449b-952f-d2c55852677c
# ╟─d86c43f3-ec2f-4fae-88d2-1068603e7044
# ╠═27efdcac-8ee4-47e4-905d-8d8c7313ddd1
# ╟─41bd1230-c87c-47b0-8e58-67ad55609fd3
# ╠═41f427c1-b6ad-46d4-9151-1d872b4efeb6
# ╠═c9b9fdb1-5734-430f-949a-6e6126d2f091
# ╠═17be52fb-f55b-4b3d-85e5-33f36134046b
# ╟─03f582ec-4e95-4ca4-8482-9c797027810d
# ╠═531edb71-6d32-4231-b117-5e36416d2fb1
# ╟─e0b92e00-dd0b-448b-baae-5cd4e0a2059a
# ╠═1495eb67-12ec-4662-b0e7-049ffef569c0
# ╟─00000000-0000-0000-0000-000000000001
# ╟─00000000-0000-0000-0000-000000000002


# Julia function to load CSV and compute
# a couple of SMA.  written to be identical
# to lua version.
using DataFrames

# Note: There are faster ways to compute SMA but this
#  is closest to the method shown in basic tutorials
#  and is a valid test of a tight loop that spends a
#  a lot of time indexing into an array.   We show it
#  as a nested loop instead of slice
function sma(avect, numPer)
    print(length(avect), " numper=", numPer)
    numEle = length(avect)
    tout = Array(Float32, numEle)
    for ndx = 1:numEle
      tsum = 0.0
      begndx = max(1, ndx - numPer)
      for slicendx = begndx:ndx
        tsum += avect[slicendx]
      end
      tout[ndx] = tsum / float32(numPer)
    end
    return tout
end

## Showing the more common Julia version of
## slicing out what we need and applying builtin
## operator mean instead of manual sub loop.
function sma_slice(avect, numPer)
    print(length(avect), " numper=", numPer)
    numEle = length(avect)
    tout = Array(Float32, numEle)
    for ndx = 1:numEle
      begndx = max(1, ndx - numPer)
      tout[ndx] = mean(avect[begndx:ndx])
    end
    return tout
end

function runTest()
   print ("read table")
   tic()
   dta = readtable("2014.M1.csv")
   toc()
   println("finished read table")

   closeVect = dta[:close]
   println("compute sma(14)")
   tic()
   sma14 = sma(closeVect, 14)
   toc()
   println("finished sma(14)")

   println("compute sma (600)")
   tic()
   sma600 = sma(closeVect, 600)
   toc()
   println("finished sma(600)")

   closeVect = dta[:close]
   println("compute sma_slice(14)")
   tic()
   sma14 = sma_slice(closeVect, 14)
   toc()
   println("finished sma_slice(14)")

   println("compute sma_slice(600)")
   tic()
   sma600 = sma_slice(closeVect, 600)
   toc()
   println("finished sma_slice(600)")

   println("\n\n Convert to Typed vector and try again \n\n")
   tic()
   tlen = length(closeVect)
   tvect = Array(Float32, tlen)
   for ndx = 1:tlen
     tvect[ndx] = closeVect[ndx]
   end
   toc()
   println("vector convertion complete")

   println("compute sma(14)")
   tic()
   sma14 = sma(tvect, 14)
   toc()
   println("finished sma(14)")

   println("compute sma (600)")
   tic()
   sma600 = sma(tvect, 600)
   toc()
   println("finished sma(600)")

   closeVect = dta[:close]
   println("compute sma_slice(14)")
   tic()
   sma14 = sma_slice(tvect, 14)
   toc()
   println("finished sma_slice(14)")

   println("compute sma_slice(600)")
   tic()
   sma600 = sma_slice(tvect, 600)
   toc()
   println("finished sma_slice(600)")

end

runTest()


using Plots

function calc_midpoints(edges::AbstractVector)::Vector{Float64}
    return Float64[0.5 * (edges[i] + edges[i+1]) for i ∈ 1:length(edges)-1]
end

function bin_data(data::AbstractVector, bins::Integer)
    low, high = extrema(data)
    edges = collect(range(low, stop = high, length = bins + 1))
    midpoints = calc_midpoints(edges)
    buckets = Int[max(2, min(searchsortedfirst(edges, x), length(edges))) - 1 for x ∈ data]
    counts = zeros(Int, length(midpoints))
    for b in buckets
        counts[b] += 1
    end
    return edges, midpoints, buckets, counts
end

rectangle(x, y, w, h) = Shape(x .+ [0, w, w, 0], y .+ [0, 0, h, h])

function myhistogram(data; bins = 10, norm = true, alpha = 0.5)
    edges, midpoints, buckets, _counts = bin_data(data, bins)
    if norm
        tot = length(data)
        counts = _counts ./ tot
    else
        counts = _counts
    end
    p = plot()
    for i = 1:length(midpoints)
        emin, emax = edges[i], edges[i+1]
        c = Float64(counts[i])
        r = rectangle(emin, zero(c), emax - emin, c)
        plot!(p, r, legend = false, color = :blue, alpha = alpha)
    end
    p = plot(p)
    return p
end

function main()
    data = randn(1000)
    dmin, dmax = extrema(data)
    bins = 10
    norm = true
    p = myhistogram(data, bins = bins, norm = norm, alpha = 1)
end


# An implementation of CGLS for the solution of the
# over-determined linear least-squares problem
#
#  minimize ‖Ax - b‖
#
# equivalently, of the normal equations
#
#  A'Ax = A'b.
#
# CGLS is formally equivalent to applying the conjugate gradient method
# to the normal equations but should be more stable. It is also formally
# equivalent to LSQR though LSQR should be expected to be more stable on
# ill-conditioned or poorly scaled problems.
#
# This implementation is the standard formulation, as recommended by
# A. Björck, T. Elfving and Z. Strakos, Stability of Conjugate Gradient
# and Lanczos Methods for Linear Least Squares Problems.
#
# Dominique Orban, <dominique.orban@gerad.ca>
# Princeton, NJ, March 2015.

export cgls

"""Solve the regularized linear least-squares problem

  minimize ‖b - Ax‖₂² + λ ‖x‖₂²

using the Conjugate Gradient (CG) method, where λ ≥ 0 is a regularization
parameter. This method is equivalent to applying CG to the normal equations

  (A'A + λI) x = A'b

but is more stable.

CGLS produces monotonic residuals ‖r‖₂ but not optimality residuals ‖A'r‖₂.
It is formally equivalent to LSQR, though can be slightly less accurate,
but simpler to implement.
"""
function cgls(A :: AbstractLinearOperator, b :: AbstractVector{T};
              M :: AbstractLinearOperator=opEye(size(b,1)), λ :: Float64=0.0,
              atol :: Float64=1.0e-8, rtol :: Float64=1.0e-6, radius :: Float64=0.0,
              itmax :: Int=0, verbose :: Bool=false) where T <: Number

  m, n = size(A);
  size(b, 1) == m || error("Inconsistent problem size");
  verbose && @printf("CGLS: system of %d equations in %d variables\n", m, n);

  x = zeros(T, n);
  r = copy(b)
  bNorm = @knrm2(m, r)   # Marginally faster than norm(b);
  bNorm == 0 && return x, SimpleStats(true, false, [0.0], [0.0], "x = 0 is a zero-residual solution");
  s = A' * M * r;
  p = copy(s);
  γ = @kdot(n, s, s)  # Faster than γ = dot(s, s);
  iter = 0;
  itmax == 0 && (itmax = m + n);

  rNorm  = bNorm;
  ArNorm = sqrt(γ);
  rNorms = [rNorm;];
  ArNorms = [ArNorm;];
  ε = atol + rtol * ArNorm;
  verbose && @printf("%5s  %8s  %8s\n", "Aprod", "‖A'r‖", "‖r‖")
  verbose && @printf("%5d  %8.2e  %8.2e\n", 1, ArNorm, rNorm);

  status = "unknown";
  on_boundary = false
  solved = ArNorm <= ε;
  tired = iter >= itmax;

  while ! (solved || tired)
    q = A * p;
    δ = @kdot(m, q, M * q)   # Faster than α = γ / dot(q, q);
    λ > 0 && (δ += λ * @kdot(n, p, p))
    α = γ / δ;

    # if a trust-region constraint is give, compute step to the boundary
    σ = radius > 0.0 ? maximum(to_boundary(x, p, radius)) : α
    if (radius > 0.0) & (α > σ)
      α = σ
      on_boundary = true
    end

    @kaxpy!(n,  α, p, x)     # Faster than x = x + α * p;
    @kaxpy!(m, -α, q, r)     # Faster than r = r - α * q;
    s = A' * M * r;
    λ > 0 && @kaxpy!(n, -λ, x, s)   # s = A' * r - λ * x;
    γ_next = @kdot(n, s, s)  # Faster than γ_next = dot(s, s);
    β = γ_next / γ;
    @kscal!(n, β, p)
    @kaxpy!(n, 1.0, s, p)    # Faster than p = s + β * p;
    # The combined BLAS calls tend to trigger some gc.
    #  BLAS.axpy!(n, 1.0, s, 1, BLAS.scal!(n, β, p, 1), 1);
    γ = γ_next;
    rNorm = @knrm2(m, r)  # Marginally faster than norm(r);
    ArNorm = sqrt(γ);
    push!(rNorms, rNorm);
    push!(ArNorms, ArNorm);
    iter = iter + 1;
    verbose && @printf("%5d  %8.2e  %8.2e\n", 1 + 2 * iter, ArNorm, rNorm);
    solved = (ArNorm <= ε) | on_boundary
    tired = iter >= itmax;
  end

  status = on_boundary ? "on trust-region boundary" : (tired ? "maximum number of iterations exceeded" : "solution good enough given atol and rtol")
  stats = SimpleStats(solved, false, rNorms, ArNorms, status);
  return (x, stats);
end


const number_of_chebyshevs_to_compile_into_binaries = 20

function next_chebyshev(previous::Sum_Of_Functions, two_previous::Sum_Of_Functions)
    return PE_Function(2.0,0.0,0.0,1) * previous - two_previous
end

first_kind_chebyshevs = Array{Sum_Of_Functions}(undef, number_of_chebyshevs_to_compile_into_binaries)
first_kind_chebyshevs[1] = Sum_Of_Functions([PE_Function(1.0,0.0,0.0,0)])
first_kind_chebyshevs[2] = Sum_Of_Functions([PE_Function(1.0,0.0,0.0,1)])
for i in 3:number_of_chebyshevs_to_compile_into_binaries
    first_kind_chebyshevs[i] = next_chebyshev(first_kind_chebyshevs[i-1], first_kind_chebyshevs[i-2])
end

second_kind_chebyshevs = Array{Sum_Of_Functions}(undef, number_of_chebyshevs_to_compile_into_binaries)
second_kind_chebyshevs[1] = Sum_Of_Functions([PE_Function(1.0,0.0,0.0,0)])
second_kind_chebyshevs[2] = Sum_Of_Functions([PE_Function(2.0,0.0,0.0,1)])
for i in 3:number_of_chebyshevs_to_compile_into_binaries
    second_kind_chebyshevs[i] = next_chebyshev(second_kind_chebyshevs[i-1], second_kind_chebyshevs[i-2])
end

"""
    get_chevyshevs_up_to(N::Integer, first_kind::Bool = true)

Get the first N chebyshev polynomials returned as a vector of `UnivariateFunction`s.
The first 20 polynomials of each are precompiled into the binaries for speed. If
you need more than that they will be calculated at runtime.

These can be from either the first kind or second kind polynomial sequence.
### Inputs
* `N` - How many chebyshev polynomials do you want.
* `first_kind` - A Bool. If true you get first kind polynomials. If false you get second kind.
### Returns
* A `Vector` of `UnivariateFunction`s for each polynomial.
"""
function get_chevyshevs_up_to(N::Integer, first_kind::Bool = true)
    chebyshevs = Array{Sum_Of_Functions}(undef, N)
    if N >= number_of_chebyshevs_to_compile_into_binaries
        if first_kind
            chebyshevs[1:number_of_chebyshevs_to_compile_into_binaries] = first_kind_chebyshevs
        else
            chebyshevs[1:number_of_chebyshevs_to_compile_into_binaries] = second_kind_chebyshevs
        end
        for i in (number_of_chebyshevs_to_compile_into_binaries+1):N
            chebyshevs[i] = next_chebyshev(chebyshevs[i-1], chebyshevs[i-2])
        end
    else
        if first_kind
            chebyshevs[1:N] = first_kind_chebyshevs[1:N]
        else
            chebyshevs[1:N] = second_kind_chebyshevs[1:N]
        end
    end
    return chebyshevs
end


cd(@__DIR__); include("setups/grid23x22.jl")
gr(dpi = 200)

## assemble frames
frame = sgwt_frame(W; nf = 6)
SGWT = reshape(frame, (N, :))
SGWT_dual = (SGWT * SGWT') \ SGWT

rNGWF, dic_l2x = rngwf_all_vectors(D, 𝚽; σ = 0.2 * maximum(D), thres = 0.15)
rNGWF_dual = (rNGWF * rNGWF') \ rNGWF
Γ = rngwf_lx(dic_l2x)

## (a)
f = digit_img[:]
plt = heatmap(reshape(f, (Nx, Ny))', c = :viridis, ratio = 1,
        frame = :none, xlim = [1, Nx], size = (600, 500))
savefig(plt, "../figs/grid23x22_fdigit3.png")

## (b)
rel_approx_sgwt, f_approx_sgwt = frame_approx(f, SGWT, SGWT_dual; num_kept = 3 * N)
rel_approx_rngwf, f_approx_rngwf = frame_approx(f, rNGWF, rNGWF_dual; num_kept = 3 * N)
plot(0:length(rel_approx_rngwf)-1, [rel_approx_sgwt rel_approx_rngwf],
    grid = false, lw = 2, c = [:blue :green],
    xlab = "Number of Coefficients Retained",
    ylab = "Relative Approximation Error", yaxis = :log,
    lab = ["SGWT" "rNGWF"])
plt = plot!(xguidefontsize = 14, yguidefontsize = 14, legendfontsize = 11, size = (600, 500))
savefig(plt, "../figs/grid23x22_approx_fdigit3.png")


"""
Reverse plus
"""
function plus_rev(a::Interval, b::Interval, c::Interval)  # a = b + c
    # a = a ∩ (b + c)  # add this line for plus contractor (as opposed to reverse function)
    b_new = b ∩ (a - c)
    c_new = c ∩ (a - b)

    return a, b_new, c_new
end

plus_rev(a,b,c) = plus_rev(promote(a,b,c)...)

"""
Reverse minus
"""
function minus_rev(a::Interval, b::Interval, c::Interval)  # a = b - c

    b_new = b ∩ (a + c)
    c_new = c ∩ (b - a)

    return a, b_new, c_new
end

minus_rev(a,b,c) = minus_rev(promote(a,b,c)...)

function minus_rev(a::Interval, b::Interval)  # a = -b
    b_new = b ∩ (-a)
    return (a, b_new)
end

"""
Reverse multiplication
"""
function mul_rev(a::Interval, b::Interval, c::Interval)  # a = b * c

    # ((0.0 ∉ a) || (0.0 ∉ b)) && (c = c ∩ (a / b))
    # ((0.0 ∉ a) || (0.0 ∉ c)) && (b = b ∩ (a / c))

    # a = a ∩ (b * c)  # ?

    if 0 ∈ b
        temp = c .∩ extended_div(a, b)
        c′ = union(temp[1], temp[2])

    else
        c′ = c ∩ (a / b)
    end

    if 0 ∈ c
        temp = b .∩ extended_div(a, c)
        b′ = union(temp[1], temp[2])

    else
        b′ = b ∩ (a / c)
    end

    return a, b′, c′
end

mul_rev(a,b,c) = mul_rev(promote(a,b,c)...)

"""
Reverse division
"""
function div_rev(a::Interval, b::Interval, c::Interval)  # a = b / c

    b = b ∩ (a * c)
    c = c ∩ (b / a)

    return a, b, c
end

div_rev(a,b,c) = div_rev(promote(a,b,c)...)

"""
Reverse inverse
"""
function inv_rev(a::Interval, b::Interval)  # a = inv(b)

    b_new = b ∩ inv(a)

    return a, b_new
end

inv_rev(a,b) = inv_rev(promote(a,b)...)

"""
Reverse power
"""
function power_rev(a::Interval, b::Interval, n::Integer)  # a = b^n,  log(a) = n.log(b),  b = a^(1/n)

    if n == 2  # a = b^2
        root = √a
        b1 = b ∩ root
        b2 = b ∩ (-root)

    elseif iseven(n)
        root = a^(1//n)

        b1 = b ∩ root
        b2 = b ∩ (-root)

    elseif isodd(n)
        pos_root = (a ∩ (0..∞)) ^ (1//n)
        neg_root = -( ( (-a) ∩ (0..∞) ) ^ (1//n) )

        b1 = b ∩ pos_root
        b2 = b ∩ neg_root

    end

    b = hull(b1, b2)

    return (a, b, n)
end

function power_rev(a::Interval, b::Interval, c::Interval)  # a = b^c

    if isinteger(c)
        temp = power_rev(a, b, Int(inf(c)))  # use version with integer
        return (temp[1], temp[2], interval(temp[3]))
    end

    b_new = b ∩ ( a^(inv(c) ))
    c_new = c ∩ (log(a) / log(b))

    return a, b_new, c_new
end

power_rev(a, b, c) = power_rev(promote(a, b, c)...)

"""
Reverse square root
"""
function sqrt_rev(a::Interval, b::Interval)  # a = sqrt(b)

    b_new = b ∩ (a^2)

    return a, b_new
end

sqrt_rev(a,b) = sqrt_rev(promote(a,b)...)

# IEEE-1788 style

"""
Reverse sqr
"""
function sqr_rev(c, x)   # c = x^2;  refine x

    root = sqrt(c)

    x1 = x ∩ root
    x2 = x ∩ (-root)

    return (c, hull(x1, x2))
end

sqr_rev(c) = sqr_rev(c, -∞..∞)

"""
Reverse abs
"""
function abs_rev(y, x)   # y = abs(x); refine x

    y_new = y ∩ (0..∞)

    x1 = y_new ∩ x
    x2 = -(y_new ∩ (-x))

    return (y, hull(x1, x2))
end
#=
"""
Reverse sign
"""
function sign_rev(a::Interval, b::Interval)  # a = sqrt(b)

    (a == 1.0) && b = b ∩ (0..∞)
    (a == 0.0) && b = b ∩ (0.0..0.0)
    (a == -1.0) && b = b ∩ (-∞..0.0)

    return a, b
end
sign_rev(a,b) = sign_rev(promote(a,b)...)
=#
## IEEE-1788 versions:

"""
According to the IEEE-1788 standard:

- `∘_rev1(b, c, x)` is the subset of `x` such that `x ∘ b` is defined and in `c`;
- `∘_rev2(a, c, x)` is the subset of `x` such that `a ∘ x` is defined and in `c`

When `∘` is commutative, these agree and we write `∘_rev(b, c, x)`.
"""

function mul_rev_IEEE1788(b, c, x)   # c = b*x
    return x ∩ (c / b)
end

function pow_rev1(b, c, x)   # c = x^b
    return x ∩ c^(1/b)  # replace by 1//b
end

function pow_rev2(a, c, x)   # c = a^x
    return x ∩ (log(c) / lob(a))
end


# ---
# title: 1201. Ugly Number III
# id: problem1201
# author: Tian Jun
# date: 2020-10-31
# difficulty: Medium
# categories: Math, Binary Search
# link: <https://leetcode.com/problems/ugly-number-iii/description/>
# hidden: true
# ---
#
# Write a program to find the `n`-th ugly number.
#
# Ugly numbers are **  positive integers** which are divisible by `a`  **or**
# `b`  **or** `c`.
#
#
#
# **Example 1:**
#
#
#
#     Input: n = 3, a = 2, b = 3, c = 5
#     Output: 4
#     Explanation: The ugly numbers are 2, 3, 4, 5, 6, 8, 9, 10... The 3rd is 4.
#
# **Example 2:**
#
#
#
#     Input: n = 4, a = 2, b = 3, c = 4
#     Output: 6
#     Explanation: The ugly numbers are 2, 3, 4, 6, 8, 9, 10, 12... The 4th is 6.
#
#
# **Example 3:**
#
#
#
#     Input: n = 5, a = 2, b = 11, c = 13
#     Output: 10
#     Explanation: The ugly numbers are 2, 4, 6, 8, 10, 11, 12, 13... The 5th is 10.
#
#
# **Example 4:**
#
#
#
#     Input: n = 1000000000, a = 2, b = 217983653, c = 336916467
#     Output: 1999999984
#
#
#
#
# **Constraints:**
#
#   * `1 <= n, a, b, c <= 10^9`
#   * `1 <= a * b * c <= 10^18`
#   * It's guaranteed that the result will be in range `[1, 2 * 10^9]`
#
#
## @lc code=start
using LeetCode

## add your code here:
## @lc code=end


module GenGammaDist
using Distributions

import Base.Random
import Base: mean, rand
import Distributions: pdf

# Generalized Gamma distribution
# We have constructors for three different parameterizations.
# Some parameters can be negative, so this works as an inverse gamma distribution as well.

export GenGamma, gengamma_wiki, gengamma1, params, params1, params_wiki, pdf

# Use parameterization of R flexsurv implementation
immutable GenGamma <: ContinuousUnivariateDistribution
    μ::Float64
    σ::Float64
    Q::Float64
    gdist::Distributions.Gamma  # gamma distribution used to compute random samples
end

# construct with parameterization of R flexsurv implementation

"""
    GenGamma(μ, σ, Q)

The `generalized gamma distribution` parameterized as in the R `flexsurv`
implementation[^1].

[^1]: Prentice, R. L. (1974). A log gamma model and its maximum likelihood estimation. Biometrika 61(3):539-544
"""
function GenGamma(μ, σ, Q)
    gdist = Distributions.Gamma(1/Q^2,1)
    GenGamma(μ, σ, Q, gdist)
end

# Parameterization from wikipedia page
# PDF is: \frac{p/a^d}{\Gamma(d/p)} x^{d-1}e^{-(x/a)^p}
# The R page for gengamma flexsurv says that Q < 0 is allowed.
# The wikipedia page does not have abs(d) and abs(p), but this works, because the -i π cancels
# Must have d>0,p>0  or  d<0, p<0

"""
    gengamma_wiki(a,d,p)

The `generalized gamma distribution` parameterized as on the Wikipedia page.
Requires `d>0, p>0` or `d<0,p<0`.
"""
function gengamma_wiki(a,d,p)
    μ = log(a) + (log(abs(d)) - log(abs(p)))/p
    σ = 1 / sqrt(p*d)
    Q = sqrt(p/d) * sign(p)
    GenGamma(μ,σ,Q)
end

# Another parameterization
# PDF is: \frac{p b^(d/p)}{\Gamma(d/p)} x^(d-1) e^{-b x^p}
# Must have d>0,p>0  or  d<0, p<0
"""
    gengamma1(b,d,p)

The `generalized gamma distribution` with alternative parameterization. This follows
the older R convention `dgengamma.orig` [^2].

[^2]: Stacy, E. W. (1962). A generalization of the gamma distribution. Annals of Mathematical Statistics 33:1187-92.
"""
function gengamma1(b,d,p)
    a = b^(-1/p)
    gengamma_wiki(a,d,p)
end

# Use the algorithm given in the documentation pages for the R package flexsurv.
function rand(p::GenGamma)
    Qs = p.Q^2
    gamma_deviate = rand(p.gdist)  # only saves 10 or so percent time.
    w = log(Qs*gamma_deviate)/p.Q
    x = exp(p.μ + p.σ * w)
    return x
end

#  mean in wikipedia parameterization is
#  a * gamma((d+1)/p)/gamma(d/p)
function mean(p::GenGamma)
    Qs = p.Q^2
    iQs = 1/Qs
    a = Qs^(p.σ / p.Q) * exp(p.μ)
    a * gamma(iQs + p.σ/p.Q)/gamma(iQs)
end

function pdf(p::GenGamma,x::Real)
    (d,p,a) = params_wiki(p)
    (p/a^d)/gamma(d/p) * x^(d-1) * exp(-(x/a)^p)
end

"""
   params_wiki(dt::GenGamma)

returns parameters of the `generalized Gamma distribution` following
the convention on the Wikipedia page.
"""
function params_wiki(dt::GenGamma)
    d = 1/(dt.σ * dt.Q)
    p = (dt.Q)/(dt.σ)
    a = abs(dt.Q)^(2/p)*exp(dt.μ)
    return (a,d,p)
end

"""
   params1(dt::GenGamma)

returns parameters of the `generalized Gamma distribution` following the parameterization
of the older R implementation `dgengamma.orig` [^2].

[^2]: Stacy, E. W. (1962). A generalization of the gamma distribution. Annals of Mathematical Statistics 33:1187-92.
"""
function params1(dt::GenGamma)
    (a,d,p) = params_wiki(dt)
    b = a^(-p)
    return (b,d,p)
end

"""
   params1(dt::GenGamma)

returns parameters of the `generalized Gamma distribution` following
the convention of the R `flexsurv` function.
"""
function params(d::GenGamma)
    return (d.μ, d.σ, d.Q)
end

end # module


module RobustLeastSquares

using StatsBase
using IterativeSolvers
using Logging
using LinearAlgebra
using SparseArrays

export reweighted_lsqr, refit_estimator

export MEstimator, L2Estimator, L1Estimator, L1L2Estimator, HuberEstimator, FairEstimator, CauchyEstimator, GemanEstimator, WelschEstimator,  TukeyEstimator, MultiEstimator
export estimator_rho, estimator_psi, estimator_weight, estimator_sqrtweight

include("MEstimators.jl")

function solve(A,b,weights=ones(length(b)),method=:qr,x0=nothing)
    if method == :qr
        return (Diagonal(weights) * A) \ (weights.*b)
        # this works for sparse matrices:
        #return Base.LinAlg.SparseMatrix.SPQR.solve(0,qrfact(sparse(spdiagm(weights)*A)),Base.LinAlg.SparseMatrix.CHOLMOD.Dense(spdiagm(weights)*b))
    elseif method == :normal
        return (A' * (Diagonal(weights.^2) * A)) \ (A' * (weights .^ 2 .*b))
    elseif method == :cg
        # Use a conjugate gradient method to find the solution (less memory)
        if x0 == nothing
            x0 = zeros(size(A,2))
        end
        sol = lsqr!(x0, Diagonal(weights) * A, weights.*b)
        return sol
    else
        error("Method :$method should have been one of :qr, :normal or :cg")
    end
end

"""
(sol,res) = reweighted_lsqr(sol::AbstractVector, A::AbstractMatrix,b::AbstractVector,estimator::MEstimator = L2Estimator,useqr::Type = Val{true}; n_iter=10)

Solves a reweighted least squares problem: min ∑ᵢ ρ((A*sol - b)ᵢ) using the specified MEstimator for ρ and starting with sol (for initial weights).
"""
function reweighted_lsqr(A::AbstractMatrix,b::AbstractVector,estimator::MEstimator = L2Estimator(), x0 = nothing;method::Symbol=:qr, n_iter::Integer=10, refit::Bool = false, quiet::Bool = false, kwargs...)
    local sol, res, weights

    s1,s2 = size(A)
    if s1 == s2
        warn("Encountered square matrix of size $s1. Julia will revert to linear solvers instead of least-square solvers, and throw an error if the matrix is singular.")
    end

    # Set the initial weights
    if x0 === nothing
        weights = fill(1, size(b))
    else
        res = A*x0 - b
        if refit
            weights = estimator_sqrtweight(res, refit_estimator(estimator, res, 3.0))
        else
            weights = estimator_sqrtweight(res, estimator)
        end
    end

    # Perform the reweighted least squares
    if issparse(A)
        quiet || @info "Solving a $(size(A)) reweighted least-squares problem. $(typeof(A)) matrix has $(nnz(A)) non-zero elements. Using $method method."
    else
        quiet || @info "Solving a $(size(A)) reweighted least-squares problem with a $(typeof(A)). Using $method method."
    end

    for i=1:n_iter
        if i == 1
            sol = solve(A,b,weights,method,x0)
        else
            sol = solve(A,b,weights,method,sol)
        end
        res = A*sol - b

        if refit
            weights = estimator_sqrtweight(res, refit_estimator(estimator,res,3.0))
        else
            weights = estimator_sqrtweight(res, estimator)
        end

        quiet || @info "Iteration $i, RMS residual $(sqrt(sum(res.*res)/length(res)))))"
    end

    quiet || @info "Root-mean-square weighted residual error = $(sqrt(sum(res.^2)/length(res)))"

    return (sol,res,weights)
end

end # module


"Fix to ensure positive definiteness by dividing each off-diagonal element by sum of absolute values of off-diagonal elements in its row"
function fixMatrix(A::Array{Float64}, denom_factor::Float64)
	p = size(A, 1);
	for cur_row in 1:p
		cur_sum = sum(abs(A[cur_row, :])) - 1
		if cur_sum != 1
			A[cur_row, :] = A[cur_row, :] / (denom_factor * cur_sum);
		end
		# Make sure diagonal entries are still 1
		A[cur_row, cur_row] = 1;
	end
	# Final matrix is average of matrix with its transpose
	A = (A + A')/2
    return A
end

"Calculate Covariance Matrix using Gaussian Kernel"
function calcSigma(X, inv_Sigma)
	Sigma = zeros(size(X, 1), size(X, 1))
	for i in 1:size(Sigma, 1)
	  for j in 1:size(Sigma, 2)
          Sigma[i, j] = exp(- 1/2 * (X[i, :] - X[j, :])' * inv_Sigma * (X[i, :] - X[j, :]))[1]
	  end
	end
	return Sigma
end


using GLM, RDatasets, DataFrames, Distributions, Random, LinearAlgebra
Random.seed!(0)

df = dataset("MASS", "cpus")
n = size(df)[1]
df = df[shuffle(1:n),:]

pTest = 0.2
lastTindex = Int(floor(n*(1-pTest)))
numTest = n - lastTindex

train = df[1:lastTindex,:]
test = df[lastTindex+1:n,:]

form = @formula(Perf~CycT+MMin+MMax+Cach+ChMin+ChMax)
model1 = glm(form, train, Normal(),  IdentityLink())
model2 = glm(form, train, Poisson(), LogLink())
model3 = glm(form, train, Gamma(),  InverseLink())

invIdenityLink(x) = x
invLogLink(x) = exp(x)
invInverseLink(x) = 1/x

A = [ones(numTest) test.CycT test.MMin test.MMax test.Cach test.ChMin test.ChMax]
pred1 = invIdenityLink.(A*coef(model1))
pred2 = invLogLink.(A*coef(model2))
pred3 = invInverseLink.(A*coef(model3))

actual = test.Perf
lossModel1 = norm(pred1 - actual)
lossModel2 = norm(pred2 - actual)
lossModel3 = norm(pred3 - actual)

println("Model 1: ", coef(model1))
println("Model 2: ", coef(model2))
println("Model 3: ", coef(model3))
println("\nLoss of models 1,2,3: ",(lossModel1 ,lossModel2, lossModel3))


using SparseArrays, LinearAlgebra
# generate a random pos def matrix with eigenvalues between 0.1 and 2
function generate_pos_def_matrix(rng::MersenneTwister, n::Int64, aMin::Real = 0.1, aMax::Real = 2)
	X = rand(rng, n, n)
	# any real square matrix can be QP decomposed into a orthogonal matrix and an uppertriangular matrix R
	Q, R = qr(X)
	eigs = rand(rng ,n) .* (aMax .- aMin) .+ aMin
	X = Q * Matrix(Diagonal(eigs)) * Q'
	X = 0.5 * (X + X')
	return X
end

function is_numerically_pos_sem_def(X, atol)
	X = X ./ 2
	X = X + X'

	F = eigfact(X)
	if size(find( x-> x < -atol, F[:values]), 1) == 0
		return true
	else
		return false
	end
end

function is_numerically_symmetric(X,atol)
	n = size(X, 2)
	for i = 1:n-1, j = i+1:n
		if abs(X[i, j] - X[j, i]) >= atol
			return false
		end
	end
	return true
end

function find_nonsymmetric_component(X)
	for i = 2:size(X, 1), j = 1:(i - 1)
		if abs(X[i, j] - X[j, i]) > 0.0
			return i, j, abs(X[i, j] - X[j, i])
		end
	end
end

function find_different_elements(A, B)
	if size(A) != size(B)
		error("Matrices are not the same size")
	end
	m, n = size(A)
	diff_el = Array[]
	for iii = 1:m, jjj = 1:n
		if A[iii, jjj] != B[iii, jjj]
			push!(diff_el, [iii, jjj])
		end
	end
	return diff_el
end

function duplicate_sparsity_pattern(A)
	m, n = size(A)
	B = zeros(m, n)
	for iii = 1:m, jjj = 1:n
		if A[iii, jjj] != 0
			B[iii, jjj] = 1
		end
	end
	return B
end

function apply_pattern!(A::AbstractMatrix, pattern::AbstractMatrix)
  @assert size(A) == size(pattern) "Matrix A and pattern must have same dimensions."
  m, n = size(A)
  for i = 1:m, j = 1:n
    if pattern[i, j] == 0
      A[i, j] = 0.
    end
  end
end

# create a feasible SDP with one PSDConeTriangle constraint
# min c' x
# s.t. At x + s == bt
#       s ∈ PSDConeTriangle
# choose At and bt in such a way that S has the provided sparsity pattern
function feasible_sdp_with_pattern(rng::MersenneTwister, pattern::AbstractMatrix)
  n = size(pattern, 1)
  d = div(n * (n + 1), 2)

  S = generate_pos_def_matrix(rng, n, 0.1, 2)
  apply_pattern!(S, pattern)
  S = Symmetric(S, :U)

  A1 = rand(rng, n, n)
  apply_pattern!(A1, pattern)
  A1 = Symmetric(A1, :U)
  A = hcat(A1[:])
  s = S[:]
  x = rand(rng, 1)
  b = A * x + s
  B = reshape(b, n, n)

  Y = generate_pos_def_matrix(rng, n,  0.1, 1)
  y = vec(Y)
  P = sparse(zeros(1, 1))
  q = -P * x - A' * y

  Ct = [COSMO.PsdConeTriangle(d)];
  At = zeros(d)
  bt = zeros(d)
  COSMO.extract_upper_triangle!(A1, At, sqrt(2))
  COSMO.extract_upper_triangle!(B, bt, sqrt(2))
  At = hcat(At)
  return P, q, At, bt, Ct
end

"Take a vector `svec` representing the `d` upper-triangular entries of a matrix `X`, and return `X`."
function matrixify(svec::AbstractVector)
  n = - 0.5 + sqrt(0.25 + 2 * length(svec))
  n = Int64(n)
  X = zeros(n, n)
  COSMO.populate_upper_triangle!(X, svec, 1 /sqrt(2))
  return Symmetric(X, :U)
end

function recreate_sparse_matrix(A::SparseMatrixCSC)
	rowInd = A.rowval
	colPtr = A.colptr
	val = A.nzval

	#compute column indices
	colInd = zeros(Int64, length(rowInd))
	cval = 1
	for iii = 2:length(colPtr)
		currentPtr = colPtr[iii]
		prevPtr = colPtr[iii - 1]
		colInd[prevPtr:currentPtr - 1] = cval
		cval += 1
	end

	# sort rowInd and vals
	p = sortperm(rowInd)
	return sparse(rowInd, colInd, val, size(A, 1), size(A, 2))
end

# Geometric mean from https://github.com/JuliaStats/StatsBase.jl
function gmean(a::AbstractArray{T}) where T<:Real
	s = 0.0
	n = length(a)
	for i in 1:n
		tmp = a[i]
		if tmp < 0.0
			throw(DomainError())
		elseif tmp == 0.0
			return 0.0
		else
			s += log(tmp)
		end
	end
	return exp(s / n)
end


function solve(FDDE::FDDEProblem, h, ::DelayPECE)
    @unpack f, ϕ, α, τ, tspan = FDDE
    t = collect(0:h:tspan)
    maxn = length(t)
    yp = zeros(maxn)
    y = copy(t)
    y[1] = ϕ(0)

    for n in 1:maxn-1
        yp[n+1] = 0
        for j = 1:n
            yp[n+1] = yp[n+1]+b(j-1, n-1, α, h)*f(t[j], y[j], v(ϕ, j, τ, h, y, yp)...)
        end
        yp[n+1] = yp[n+1]/gamma(α)+ϕ(0)

        y[n+1] = 0

        for j=1:n
            y[n+1] = y[n+1]+a(j-1, n-1, α, h)*f(t[j], y[j], v(ϕ, j, τ, h, y, yp)...)
        end

        y[n+1] = y[n+1]/gamma(α)+h^α*f(t[n+1], yp[n+1], v(ϕ, n+1, τ, h, y, yp)...)/gamma(α+2) + ϕ(0)
    end

    V = []
    for n = 1:maxn
        push!(V, v(ϕ, n, τ, h, y, yp))
    end

    delayed = zeros(length(τ), length(V))
    for i=1:length(V)
        delayed[:, i] = V[i]
    end

    return delayed, y
end

function a(j, n, α, h)
    if j == n+1
        result = 1
    elseif j == 0
        result = n^(α+1)-(n-α)*(n+1)^α
    elseif j == n
        result = 2*(2^(α+1)-1)
    else
        result = (n-j+2)^(α+1) + (n-j)^(α+1) - 2*(n-j+1)^(α+1)
    end
    return result*h^α / (α*(α + 1))
end

function b(j, n, α, h)
    return h^α/α*((n-j+1)^α - (n-j)^α)
end

function v(ϕ, n, τ, h, y, yp)
    if maximum(τ) > n*h
        return ϕ.((n-1)*h.-τ)
    else
        m = floor.(Int, τ./h)
        δ = m.-τ./h

        function judge(m)
            temp1 = findall(x->x>1, m)
            temp2 = findall(x->x==1, m)#FIXME: Another case for x == 1

            result = zeros(length(m))
            if length(temp1) == length(m)
                for i=1:length(m)
                    result[i] = δ[i]*y[n-m[i]+2]+(1-δ[i])*y[n-m[i]+1]
                end
                return result
            end
        end
        return judge(m)
    end
end


function Attenuation{T<:AbstractFloat}(nz::Int,nx::Int,ext::Int,atten_max::T)

# This function builds the complex-valued diagonal attenuation operator/matrix,
# where the main diagonal contains the attenuation factor for each point in
# the modeling region AND the surrounding boundary region. It is assumed
# that no attenuation occurs in the modeling regionl. It is also assumed
# that all 4 sides of the modeling region are surrounded by an attenuating
# boundary material to prevent boundary reflections. The imaginary component
# in the attenuation factor is zero in the modeling region and increases
# parabolically outward in the boundary region.
#
# INPUTS:     nz        - Number of grid points in z-direction INCLUDING the absorbing boundary region
#             nx        - Number of grid points in x-direction INCLUDING the absorbing boundary region
#             ext       - Thickness (number of grid points) of attenuating boundary region
#             atten_max - Maximum complex amplitude in the attenuating boundary layer
#
# OUTPUTS:    A         - Diagonal matrix containing the attenuation factors

    atten = (ext:-1:1).^2/(ext.^2)*atten_max
    a_boundary1 = kron(ones(nz,1),atten')
    a_boundary2 = kron(atten,ones(1,nx))
    a = zeros(T,nz,nx)
    a[:,1:ext] += a_boundary1
    a[:,nx-ext+1:nx] += flipdim(a_boundary1,2)
    a[1:ext,:] += a_boundary2
    a[nz-ext+1:nz,:] += flipdim(a_boundary2,1)

    A = 1 - im*a

    return spdiagm((A[:]),(0))

end


# ---
# title: 537. Complex Number Multiplication
# id: problem537
# author: Indigo
# date: 2021-06-23
# difficulty: Medium
# categories: Math, String
# link: <https://leetcode.com/problems/complex-number-multiplication/description/>
# hidden: true
# ---
#
# Given two strings representing two [complex
# numbers](https://en.wikipedia.org/wiki/Complex_number).
#
# You need to return a string representing their multiplication. Note i2 = -1
# according to the definition.
#
# **Example 1:**
#
#
#
#     Input: "1+1i", "1+1i"
#     Output: "0+2i"
#     Explanation: (1 + i) * (1 + i) = 1 + i2 + 2 * i = 2i, and you need convert it to the form of 0+2i.
#
#
# **Example 2:**
#
#
#
#     Input: "1+-1i", "1+-1i"
#     Output: "0+-2i"
#     Explanation: (1 - i) * (1 - i) = 1 + i2 - 2 * i = -2i, and you need convert it to the form of 0+-2i.
#
#
# **Note:**
#
#   1. The input strings will not have extra blank.
#   2. The input strings will be given in the form of **a+bi** , where the integer **a** and **b** will both belong to the range of [-100, 100]. And **the output should be also in this form**.
#
#
## @lc code=start
using LeetCode

function complex_number_multiply(num1::String, num2::String)
    Ct = Complex{Int}
    mult = parse(Ct, num1) * parse(Ct, num2)
    return "$(mult.re)+$(mult.im)i"
end
## @lc code=end


# Kochenderfer, Mykel J.. Algorithms for Optimization (The MIT Press)

function branin(x; a=1, b=5.1/(4π^2), c=5/π, r=6, s=10, t=1/(8π))
    return a*(x[2]-b*x[1]^2+c*x[1]-r)^2 + s*(1-t)*cos(x[1]) + s
end

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

paraboloid(x; a=1, b=1) = x[1]^2/a^2 + x[2]^2/b^2


using StanModels

df = CSV.read(joinpath(@__DIR__, "..", "..", "data", "WaffleDivorce.csv"), delim=';')
mean_ma = mean(df[!, :MedianAgeMarriage])
df[!, :MedianAgeMarriage_s] =
  convert(Vector{Float64},  (df[!, :MedianAgeMarriage]) .-
    mean_ma)/std(df[!, :MedianAgeMarriage]);

# Define the Stan language model

m5_1s = "
data {
 int < lower = 1 > N; // Sample size
 vector[N] divorce; // Predictor
 vector[N] median_age; // Outcome
}

parameters {
 real a; // Intercept
 real bA; // Slope (regression coefficients)
 real < lower = 0 > sigma; // Error SD
}

model {
  # priors
  a ~ normal(10, 10);
  bA ~ normal(0, 1);
  sigma ~ uniform(0, 10);

  # model
  divorce ~ normal(a + bA*median_age , sigma);
}
";

# Define the Stanmodel and set the output format to :mcmcchains.

sm = SampleModel("m5.1s", m5_1s);

# Input data for cmdstan

m5_1_data = Dict("N" => length(df[!, :Divorce]), "divorce" => df[!, :Divorce],
    "median_age" => df[!, :MedianAgeMarriage_s]);

# Sample using cmdstan

(sample_file, log_file) = stan_sample(sm, data=m5_1_data);

# Result rethinking

rethinking = "
       mean   sd  5.5% 94.5% n_eff Rhat
a      9.69 0.22  9.34 10.03  2023    1
bA    -1.04 0.21 -1.37 -0.71  1882    1
sigma  1.51 0.16  1.29  1.79  1695    1
"

# Describe the draws
if !(sample_file == nothing)
  chn = read_samples(sm)
  describe(chn)
end


# This file includes and example file for 2D interpolation

using FractalTools
using GeometryBasics
using CairoMakie

# Generate data
f(x, y) = x^2 + y^2 + 1
ngon = Triangle(
    Point(BigFloat(0.), BigFloat(0.)),
    Point(BigFloat(1.), BigFloat(0.)),
    Point(BigFloat(0.5), BigFloat(1.)))
npts = 100
pts = getdata(f, ngon, npts)
interp = interpolate(pts, Interp2D(0.01))

function err(x, y)
    fval = f(x, y)
    ival = interp(x, y)
    abs(fval - ival) / abs(fval) * 100
end

tpts = getdata(ngon, npts)

fig, ax, plt = trisurf(tpts, f, meshcolor3=:red)
trisurf!(ax, tpts, interp, meshcolor3=:blue)
fig, ax, plt = trisurf(tpts, err, meshcolor3=:red)


# The series, 1^1 + 2^2 + 3^3 + ... + 10^10 = 10405071317.
#
# Find the last ten digits of the series, 1^1 + 2^2 + 3^3 + ... + 1000^1000.

using ProjectEulerSolutions

# Multiply using mods since only the last 10 digits matter.  Faster than the
# bigint solution after about n > 30,000, and uses much less memory.
function p048solution_mod(n::Integer=100)::Integer
    total = 1
    for i in 2:n
        s = i
        for j in 2:i
            s = mod(s * i, 10^10)
        end
        total = mod(total + s, 10^10)
    end
    return total
end

# Trivial with Julia's BigInt support, and fast.
function p048solution_bigint(n::Integer=100)::Integer
    return mod(mapreduce(x->x^x, +, BigInt.(1:n)), 10^10)
end

p048 = Problems.Problem(Dict("Modulus" => p048solution_mod,
                             "Bigint" => p048solution_bigint))

Problems.benchmark(p048, 1000)


using svopt
using Test

ϵ = 1e-5

@testset "ratio helpers" begin
    a = -5
    b = 10
    @test svopt.fw1(a, b) - a == b - svopt.fw2(a, b)
end # testset

@testset "optfun" begin
    f1(x) = x^2
    f2(x) = (x-1)^2*10 + 10
    f3(x) = (x+10)^2 - 10
    f4(x) = x^2 - 1
    f5(x) = x^2/3 + 2x - sin(x)

    @test isapprox(optfun(f1, -3, 10), 0, atol=ϵ)
    @test isapprox(optfun(f2, -3, 10), 1, atol=ϵ)
    @test isapprox(optfun(f3, -30, 30), -10, atol=ϵ)
    @test isapprox(optfun(f4, -10, 30), 0, atol=ϵ)
    @test isapprox(optfun(f5, -10, 5), -3.99083, atol=ϵ)
end


using Diagonalizations, LinearAlgebra, PosDefManifold, Test

# Method (1) real
n, t=10, 100
X=genDataMatrix(n, t)
C=(X*X')/t
pC=pca(Hermitian(C); simple=true)
# or, shortly
pC=pca(ℍ(C); simple=true)

# Method (1) complex
Xc=genDataMatrix(ComplexF64, n, t)
Cc=(Xc*Xc')/t
pCc=pca(Hermitian(Cc); simple=true)

# Method (2) real
pX=pca(X; simple=true)
@test C≈pC.F*pC.D*pC.iF
@test C≈pC.F*pC.D*pC.F'
@test pX≈pC

# Method (2) complex
pXc=pca(Xc; simple=true)
@test Cc≈pCc.F*pCc.D*pCc.iF
@test Cc≈pCc.F*pCc.D*pCc.F'
@test pXc≈pCc

# Method (3) real
k=10
Xset=[genDataMatrix(n, t) for i=1:k]

# pca on the average covariance matrix
p=pca(Xset)

# ... selecting subspace dimension allowing an explained variance = 0.5
p=pca(Xset; eVar=0.5)

# ... averaging the covariance matrices using the logEuclidean metric
p=pca(Xset; metric=logEuclidean, eVar=0.5)

# ... giving weights `w` to the covariance matrices
p=pca(Xset; metric=logEuclidean, w=abs2.(randn(k)), eVar=0.5)

# ... subtracting the mean
p=pca(Xset; meanX=nothing, metric=logEuclidean, w=abs2.(randn(k)), eVar=0.5)

# pca on the average of the covariance matrices computed along dims 1
p=pca(Xset; dims=1)

# explained variance
p.eVar

# name of the filter
p.name

using Plots
# plot regularized accumulated eigenvalues
plot(p.arev)

# plot the original covariance matrix and the rotated covariance matrix
 Cmax=maximum(abs.(C));
 h1 = heatmap(C, clim=(-Cmax, Cmax), yflip=true, c=:bluesreds, title="C");
 D=pC.F'*C*pC.F;
 Dmax=maximum(abs.(D));
 h2 = heatmap(D, clim=(0, Dmax), yflip=true, c=:amp, title="F'*C*F");
 📈=plot(h1, h2, size=(700, 300))
# savefig(📈, homedir()*"\\Documents\\Code\\julia\\Diagonalizations\\docs\\src\\assets\\FigPCA.png")

# Method (3) complex
k=10
Xcset=[genDataMatrix(ComplexF64, n, t) for i=1:k]

# pca on the average covariance matrix
pc=pca(Xcset)

# ... selecting subspace dimension allowing an explained variance = 0.5
pc=pca(Xcset; eVar=0.5)

# ... averaging the covariance matrices using the logEuclidean metric
pc=pca(Xcset; metric=logEuclidean, eVar=0.5)

# ... giving weights `w` to the covariance matrices
pc=pca(Xcset; metric=logEuclidean, w=abs2.(randn(k)), eVar=0.5)

# ... subtracting the mean
pc=pca(Xcset; meanX=nothing, metric=logEuclidean, w=abs2.(randn(k)), eVar=0.5)

# pca on the average of the covariance matrices computed along dims 1
pc=pca(Xcset; dims=1)

# explained variance
pc.eVar

# name of the filter
pc.name


function binary_string_to_decimal(binstring)
    total = 0
    num_bits = length(binstring)
    for i in 1:num_bits
        if binstring[i] == '1'
            total += 2^(num_bits-i)
        end
    end
    total
end

# read the input data into an array (strips newlines for us)
rows = readlines("input.txt")

# how many binary digits in each row
num_digits = length(rows[1])
# initialize arrays to count ones and zeros
one_counts = zeros(num_digits)
zero_counts = zeros(num_digits)
# count ones and zeros in each position
for row in rows
    for i in 1:num_digits
        if row[i] == '1' # has to be single quotes !?
            one_counts[i] += 1
        else
            zero_counts[i] += 1
        end
    end
end
println("zero counts", zero_counts)
println("one counts", one_counts)

# now construct binary strings depending on whether there were
# more ones or zeros in each position
gamma_rate = ""
epsilon_rate = ""

for i in 1:num_digits
    if one_counts[i] > zero_counts[i]
        global gamma_rate *= "1"
        global epsilon_rate *= "0"
    else # what if they're equal??? never mind....
        global epsilon_rate *= "1"
        global gamma_rate *= "0"
    end
end

println("gamma rate ", gamma_rate)
println("epsilon rate ", epsilon_rate)

# convert to decimal
gamma_rate_dec = binary_string_to_decimal(gamma_rate)
epsilon_rate_dec = binary_string_to_decimal(epsilon_rate)
println("gamma rate (decimal) ",gamma_rate_dec)
println("epsilon rate (decimal) ",epsilon_rate_dec)

# multiply
println("total power consumption ",gamma_rate_dec * epsilon_rate_dec)


module _IncrEn
export IncrEn
using Statistics: std
    """
        Incr = IncrEn(Sig)

    Returns the increment entropy (`Incr`) estimate of the data sequence
    (`Sig`) using the default parameters:
    embedding dimension = 2, time delay = 1, quantifying resolution = 4,
    logarithm = base 2,

        Incr = IncrEn(Sig::AbstractArray{T,1} where T<:Real; m::Int=2, tau::Int=1, R::Int=4, Logx::Real=2, Norm::Bool=false)

    Returns the increment entropy (`Incr`) estimate of the data sequence
    (`Sig`) using the specified 'keyword' arguments:

    # Arguments:
    `m`     - Embedding Dimension, an integer > 1   \n
    `tau`   - Time Delay, a positive integer    \n
    `R`     - Quantifying resolution, a positive scalar    \n
    `Logx`  - Logarithm base, a positive scalar (enter 0 for natural log) \n
    `Norm`  - Normalisation of IncrEn value: \n
              [false]  no normalisation - default
              [true]   normalises w.r.t embedding dimension (m-1).

    # See also `PermEn`, `SyDyEn`, `MSEn`

    # References:
        [1] Xiaofeng Liu, et al.,
            "Increment entropy as a measure of complexity for time series."
            Entropy
            18.1 (2016): 22.1.

        ***   "Correction on Liu, X.; Jiang, A.; Xu, N.; Xue, J. - Increment
            Entropy as a Measure of Complexity for Time Series,
            Entropy 2016, 18, 22."
            Entropy
            18.4 (2016): 133.

        [2] Xiaofeng Liu, et al.,
            "Appropriate use of the increment entropy for
            electrophysiological time series."
            Computers in biology and medicine
            95 (2018): 13-23.

    """
    function IncrEn(Sig::AbstractArray{T,1} where T<:Real; m::Int=2, tau::Int=1,
        R::Int=4, Logx::Real=2, Norm::Bool=false)

    Logx == 0  ? Logx = exp(1) : nothing

    (size(Sig,1) > 10) ? nothing :  error("Sig:   must be a numeric vector")
    (m > 1) ? nothing :  error("m:     must be an integer > 1")
    (tau>0) ? nothing :  error("tau:   must be an integer > 0")
    (R > 0) ? nothing :  error("R:     must be a positive integer > 0")
    (Logx>0) ? nothing : error("Logx:  must be a positive number > 0")

    Vi = diff(Sig)
    N = size(Vi,1)-((m-1)*tau)
    Vk = zeros(N,m)
    for k = 1:m
        Vk[:,k] = Vi[1+(k-1)*tau:N+(k-1)*tau]
    end

    Sk = sign.(Vk)
    Temp = std(Vk,dims=2)[:]
    Qk = min.(R, floor.((abs.(Vk)*R)./repeat(Temp,outer=(1,m))))
    Qk[any(Temp.==0,dims=2), :] .= 0  #should that be all()
    Wk = Sk.*Qk
    Wk[Wk.==-0] .= 0
    Px = unique(Wk,dims=1)
    Counter = zeros(Int,size(Px,1));
    for k = 1:size(Px,1)
        Counter[k] = sum(all(Wk .- transpose(Px[k,:]) .==0 ,dims=2))
    end
    Ppi = Counter/N

    if size(Px,1) > (2*R + 1)^m
        @warn("Error with probability estimation'")
    elseif round(sum(Ppi),digits=5) != 1
        @warn("Error with probability estimation")
    end
    Incr = -sum(Ppi.*(log.(Logx, Ppi)))
    if Norm
        Incr = Incr/(m-1);
    end

    return Incr
    end

end

"""
Copyright 2021 Matthew W. Flood, EntropyHub

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

For Terms of Use see https://github.com/MattWillFlood/EntropyHub
"""


"""
Nonnegativity-constrained proximal operator of nnz()
    nnz_y = proxl0nonneg!( x, a, y )
solves the following problem
    min_y    a nnz(y) + 1/2 ||y-x||^2    s.t.    y ≥ 0
where `a` is a given nonnegative scalar.
The analytical expression is available:
    y = { x  if  x ≥ √2a,  0  otherwise
"""
function proxl0nonneg!( x::Vector{R}, a::R, y::Vector{R} ) where {R <: Real}
    @assert a >= R(0)
    y .= x
    if a > R(0)
        y[.!(x .> sqrt( 2 * a ))] .= R(0)
    else
        y .= max.( R(0), x )
    end
    nnz_y = sum(y .> R(0))
    return nnz_y
end

"""
Simplex-constrained proximal operator of nnz()
    nnz_y = proxl0simplex!( x, a, b, y )
solves the following problem
    min_y    a nnz(y) + 1/2 ||y-x||^2    s.t.    y ≥ 0,    1 ⋅ y = b
where `a` and `b` are given nonnegative scalars.
"""
function proxl0simplex!( x::Vector{R}, a::R, b::R, y::Vector{R} ) where {R <: Real}
    @assert a >= R(0)
    @assert b >= R(0)
    if a == R(0)
        projsimplex!(x, b, y)
        nnz_y = sum(y .> R(0))
        return nnz_y
    end
    n = length(x)
    y .= x # max.( x, 0.0 ) # project
    prm = sortperm(y) # sort
    y .= y[prm]
    mv = Vector(0:n-1) # number of zeros
    lv = (b .- reverse(cumsum(reverse(y)))) ./ (n .- mv)
    feas = (y .+ lv .> R(0)) # feasibility
    if !any(feas)
        m = n - 1
        l = b - x[n]
        #c = a + 0.5 * sum( x[1:m].^2 ) + 0.5 * (l ^ 2) # cost
    else
        sv = cumsum([R(0); y[1:n-1]] .^ 2)
        mv = mv[feas]
        lv = lv[feas]
        sv = sv[feas]
        cv = a .* (n .- mv) .+ 0.5 .* sv .+ 0.5 .* (n .- mv) .* (lv .^ 2) # cost
        c, i = findmin(cv) # find minimum
        m = mv[i]
        l = lv[i]
    end
    y[1:m] .= R(0)
    y[m+1:n] .+= l
    nnz_y = n - m
    y .= y[invperm(prm)] # unsort
    return nnz_y
end

"""
Projection onto the simplex
    projsimplex!(x, b, y)
corresponds to solving the following problem
        min_y    1/2 ||y-x||^2    s.t.    y ≥ 0,    1 ⋅ y = b
where `b` is a given nonnegative scalar.
See arxiv.org/abs/1101.6081
"""
function projsimplex!(x::Vector{R}, b::R, y::Vector{R}) where {R <: Real}
    @assert b >= R(0)
    if b == R(0)
        y .= R(0)
        return nothing
    end
    n = length(x)
    y .= sort(x, rev=true) # sort
    flag = false
    tmpsum = R(0)
    for i in 1:n-1
        tmpsum += y[i] # cumulative sum
        tmpmax = (tmpsum - b) / i
        if tmpmax >= y[i+1]
            flag = true
            break
        end
    end
    if !flag
        tmpmax = (tmpsum + y[n] - b) / n
    end
    y .= max.(x .- tmpmax, R(0)) # shift, project
    return nothing
end


type Mean <: ContinuousUnivariateStreamStat
    m::Float64
    n::Int
end

Mean() = Mean(0.0, 0)

function update!(stat::Mean, x::Real)
    stat.n += 1
    α = 1 / stat.n
    stat.m = (1 - α) * stat.m + α * x
    return
end

Base.mean(stat::Mean) = stat.m

state(stat::Mean) = Base.mean(stat)

nobs(stat::Mean) = stat.n

Base.copy(stat::Mean) = Mean(stat.m, stat.n)

function Base.merge(a::Mean, b::Mean)
    m1, m2 = a.m, b.m
    n1, n2 = a.n, b.n
    m = (n1 / (n1 + n2)) * m1 + (n2 / (n1 + n2)) * m2
    n = n1 + n2
    return Mean(m, n)
end

function Base.empty!(stat::Mean)
    stat.m = 0.0
    stat.n = 0
    return
end

function Base.show(io::IO, stat::Mean)
    m = mean(stat)
    n = nobs(stat)
    @printf(io, "Online Mean\n")
    @printf(io, " * Mean: %f\n", m)
    @printf(io, " * N:    %d\n", n)
    return
end


function onpatch!(x::AbstractVector, v̄::PVector{T,N}) where {T,N}
    ranges = ProjectiveVectors.dimension_indices(v̄)
    for range in ranges
        λ = zero(eltype(x))
        @inbounds for i in range
            λ += v̄[i] * x[i]
        end
        λ⁻¹ = @fastmath inv(λ)
        for i in range
            x[i] *= λ⁻¹
        end
    end
    x
end

function evaluate_patch!(u, v̄::PVector{S,N}, x::PVector{T,N}) where {S,T,N}
    ranges = ProjectiveVectors.dimension_indices(v̄)
    n = length(u) - N
    for (k, range) in enumerate(ranges)
        out = -one(eltype(x))
        for i in range
            out += v̄[i] * x[i]
        end
        u[n+k] = out
    end
    nothing
end

function jacobian_patch!(U, v̄::PVector{S,N}, x::PVector) where {S,T,N}
    ranges = ProjectiveVectors.dimension_indices(v̄)
    n = size(U, 1) - N
    for j = 1:size(U, 2), i = (n+1):size(U, 1)
        U[i, j] = zero(eltype(U))
    end
    for (k, range) in enumerate(ranges)
        for j in range
            U[n+k, j] = v̄[j]
        end
    end
    nothing
end


lines = readlines(ARGS[1])

function solution1(lines)
    fishes = [parse(Int, f) for f in split(lines[1], ",")]

    for i = 1:80
        next_gen = []
        new_fishes = []
        for fish in fishes
            if fish == 0
                append!(new_fishes, 8)
                append!(next_gen, 6)
            else
                append!(next_gen, fish - 1)
            end

        end
        append!(next_gen, new_fishes)
        fishes = next_gen
    end
    println(length(fishes))
end

function solution2(lines)
    fishes = [parse(Int8, f) for f in split(lines[1], ",")]
    fish_dict = Dict()
    for fish in fishes
        if fish in keys(fish_dict)
            fish_dict[fish] += 1
        else
            fish_dict[fish] = 1
        end
    end
    println(fish_dict)
    for i = 1:256
        next_gen = Dict()
        if 0 in keys(fish_dict)
            next_gen[8] = fish_dict[0]
        end

        for fish_key in keys(fish_dict)
            if fish_key != 0
                next_gen[fish_key-1] = fish_dict[fish_key]
            end
        end
        if 6 in keys(next_gen)
            if 0 in keys(fish_dict)
                next_gen[6] += fish_dict[0]
            end
        else
            if 0 in keys(fish_dict)
                next_gen[6] = fish_dict[0]
            end
        end

        fish_dict = next_gen
        println(i, ":", sum([value for value in values(fish_dict)]))
    end
    println(sum([value for value in values(fish_dict)]))
end

solution1(lines)

solution2(lines)


import MaximumLikelihoodProblems

import GLM
import Statistics
import Test

σ_true = 0.5
β_true = [1.0, 2.0, -1.0]

# @show σ_true
# @show σ_hat
# @show β_true
# @show β_hat

Test.@test typeof(σ_hat) == typeof(σ_true)
Test.@test σ_hat isa Real
Test.@test σ_true isa Real
Test.@test isfinite(σ_hat)
Test.@test σ_hat > 0.1
Test.@test abs(σ_hat - σ_true) < 0.03
Test.@test abs2(σ_hat - σ_true) < 1e-3

Test.@test typeof(β_hat) == typeof(β_true)
Test.@test ndims(β_hat) == ndims(β_true)
Test.@test size(β_hat) == size(β_true)
absolute_error = abs.(β_hat - β_true)
square_error = abs2.(β_hat - β_true)
absolute_error_proportional = absolute_error ./ abs.(β_true)
square_error_proportional = square_error ./ abs.(β_true)
Test.@test sum(absolute_error) < 0.030
Test.@test sum(square_error) < 1e-3
Test.@test sum(absolute_error_proportional) < 0.030
Test.@test sum(square_error_proportional) < 1e-3
Test.@test maximum(absolute_error) < 0.013
Test.@test maximum(square_error) < 1e-3
Test.@test maximum(absolute_error_proportional) < 0.013
Test.@test maximum(square_error_proportional) < 1e-3
Test.@test Statistics.mean(absolute_error) < 0.010
Test.@test Statistics.mean(square_error) < 1e-3
Test.@test Statistics.mean(absolute_error_proportional) < 0.007
Test.@test Statistics.mean(square_error_proportional) < 1e-3

gradient_vector_at_θ_hat = MaximumLikelihoodProblems.Internal.gradient_vector(transformed_gradient_problem,
                                                                              θ_hat)
hessian_matrix_at_θ_hat = MaximumLikelihoodProblems.Internal.hessian_matrix(transformed_gradient_problem,
                                                                            θ_hat)
Test.@test MaximumLikelihoodProblems.Internal._is_approximately_zero(gradient_vector_at_θ_hat)
Test.@test MaximumLikelihoodProblems.Internal._is_approximately_hermitian(hessian_matrix_at_θ_hat)
Test.@test MaximumLikelihoodProblems.Internal._is_approximately_negative_definite(hessian_matrix_at_θ_hat;
                                                                                fuzz_factor = 10)
Test.@test MaximumLikelihoodProblems.Internal._is_approximately_positive_definite(-hessian_matrix_at_θ_hat;
                                                                                fuzz_factor = 10)

n, p = size(X)
beta_hat_ols = ( X' * X )\( X' * y )
beta_hat_mle = beta_hat_ols
y_hat_ols = X * beta_hat_ols
epsilon_hat_ols = y - y_hat_ols
S_ols = epsilon_hat_ols' * epsilon_hat_ols
sigma_2_hat_ols = (S_ols) / (n - p) # OLS estimator for σ²
sigma_2_hat_mle = (n - p) / (n) * sigma_2_hat_ols # MLE estimator for σ²
sigma_hat_ols = sqrt(sigma_2_hat_ols)
sigma_hat_mle = sqrt(sigma_2_hat_mle)
Test.@test isapprox(σ_hat, sigma_hat_ols; atol = 1e-4)
Test.@test isapprox(σ_hat, sigma_hat_mle; atol = 1e-4)
Test.@test isapprox(β_hat, beta_hat_ols)
Test.@test isapprox(β_hat, beta_hat_mle)

external_model_glm = GLM.lm(X, y)
sigma_hat_external_model_glm = GLM.dispersion(external_model_glm)
Test.@test isapprox(σ_hat, sigma_hat_external_model_glm; atol = 1e-4)
beta_hat_external_model_glm = GLM.coef(external_model_glm)
Test.@test isapprox(β_hat, beta_hat_external_model_glm)


using Turing
using Distributions
using Random
using LinearAlgebra
using StatsPlots
using MCMCChains
using DataFrames
using Printf

# geberate moc data from logit model
n = 1_000
num_var = 2
Random.seed!(99)
X = rand(Uniform(-sqrt(3.0), 2.0*sqrt(3.0)), n, num_var) # case-specific variables Z_i Matrix
X = [ones(n) X]
β = Vector([0.0, 0.5, -0.5]) # true coefficients
# ポアソン分布の平均をモデル化
λ = exp.(X*β)
y = rand.(Poisson.(λ)) # observable choice 0 or 1

# set prior, β ~ MvNormal(β0, A0), no σ²
n, k = size(X)
β0 = zeros(k)
A0 = 0.01 * I

@model function poisson_regression_model(X, y)
    # Set the priors only for β.
    β ~ MvNormal(β0, inv(A0))

    # Write the likelihood, can't vectorize?
    n = size(X, 1)
    for i = 1:n
        y[i] ~ Poisson(exp(X[i, :]' * β))
    end
end

# Draw random number from posterior.
model = poisson_regression_model(X, y)

n_draws = 5_000
# n_chains = 4
n_chains = 1
n_tune = 1_000
chn = sample(model, NUTS(), MCMCThreads(), n_draws, n_chains; discard_adapt=false)
plot(chn)
# chn = sample(model, NUTS(), MCMCThreads(), n_draws, n_chains)[n_tune:n_draws, :, :]
β_chn = chn[n_tune+1:n_draws, :, :] # delete initial and exclude odd (hamiltonian bluh bluh) chain
β_chn = group(β_chn, :β)

summarize(β_chn)
quantile(β_chn)
plot(β_chn)

# Marginal effect ... interpret β just like as ln(y) = βx model (equivalent with λ = exp(x))


"""
# Log Function

```julia
log(ts::TS, complex::Bool = false)
```

This method computes the log value of non-index columns in the TS
object.

# Examples
```jldoctest; setup = :(using TSx, DataFrames, Dates, Random, Statistics)
julia> using Random
julia> random(x) = rand(MersenneTwister(123), x...);
julia> ts = TS(random(([1, 2, 3, 4, missing], 10)))
julia> show(ts)
(10 x 1) TS with Int64 Index

 Index  x1
 Int64  Int64?
────────────────
     1  missing
     2        2
     3        2
     4        3
     5        4
     6        3
     7        3
     8  missing
     9        2
    10        3

julia> log(ts)
(10 x 1) TS with Int64 Index

 Index  x1_log
 Int64  Float64?
───────────────────────
     1  missing
     2        0.693147
     3        0.693147
     4        1.09861
     5        1.38629
     6        1.09861
     7        1.09861
     8  missing
     9        0.693147
    10        1.09861

```
"""
function Base.log(ts::TS)
    df = select(ts.coredata, :Index,
                Not(:Index) .=> (x -> log.(x))
                => colname -> string(colname, "_log"))
    TS(df)
end


########################################################################################
# Integrated Brownian Motion
########################################################################################
"""
    ibm(d::Integer, q::Integer, elType=typeof(1.0))

Generate the discrete dynamics for a q-IBM model. INCLUDES AUTOMATIC PRECONDITIONING!
"""
function ibm(d::Integer, q::Integer, elType=typeof(1.0))
    # Make A
    A_breve = zeros(elType, q + 1, q + 1)
    @simd ivdep for j in 1:q+1
        @simd ivdep for i in 1:j
            @inbounds A_breve[i, j] = binomial(q - i + 1, q - j + 1)
        end
    end
    A = kron(I(d), A_breve)
    @assert istriu(A)
    # A = UpperTriangular(A)

    # Make Q
    Q_breve = zeros(elType, q + 1, q + 1)
    @fastmath _transdiff_ibm_element(row::Int, col::Int) =
        one(elType) / (2 * q + 1 - row - col)
    @simd ivdep for col in 0:q
        @simd ivdep for row in 0:q
            val = _transdiff_ibm_element(row, col)
            @inbounds Q_breve[1+row, 1+col] = val
        end
    end
    QL_breve = cholesky!(Q_breve).L
    QL = kron(I(d), QL_breve)
    Q = SRMatrix(QL)

    return A, Q
end

"""Same as above, but without the automatic preconditioning"""
function vanilla_ibm(d::Integer, q::Integer)
    @fastmath function A!(A::AbstractMatrix, h::Real)
        # Assumes that A comes from a previous computation => zeros and one-diag
        val = one(h)
        for i in 1:q
            val = val * h / i
            for k in 0:d-1
                for j in 1:q+1-i
                    @inbounds A[j+k*(q+1), j+k*(q+1)+i] = val
                end
            end
        end
    end

    @fastmath function _transdiff_ibm_element(row::Int, col::Int, h::Real)
        idx = 2 * q + 1 - row - col
        fact_rw = factorial(q - row)
        fact_cl = factorial(q - col)
        return h^idx / (idx * fact_rw * fact_cl)
    end
    @fastmath function Q!(Q::AbstractMatrix, h::Real, σ²::Real=1.0)
        val = one(h)
        @simd for col in 0:q
            @simd for row in col:q
                val = _transdiff_ibm_element(row, col, h) * σ²
                @simd for i in 0:d-1
                    @inbounds Q[1+col+i*(q+1), 1+row+i*(q+1)] = val
                    @inbounds Q[1+row+i*(q+1), 1+col+i*(q+1)] = val
                end
            end
        end
    end

    return A!, Q!
end


using DifferentialEquations, Plots

#Reference: https://diego.assencio.com/?index=e5ac36fcb129ce95a61f8e8ce0572dbf

#Solving the double pendulum with a traditional ODE method
#==========================================================#
function doublependulum(du, u, params, t)
    l1 = params[1]
    l2 = params[2]
    m1 = params[3]
    m2 = params[4]
    g  = params[5]

    p1 = u[1]
    p2 = u[2]
    θ1 = u[3]
    θ2 = u[4]

    h1 = p1*p2*sin(θ1-θ2)/(l1*l2*(m1+m2*sin(θ1-θ2)^2))
    h2 = (m2*l2^2*p1^2 + (m1 + m2) * l1^2 * p2^2 - 2*m2*l1*l2*p1*p2*cos(θ1-θ2))/(2 * l1^2 * l2^2 *(m1 + m2*sin(θ1-θ2)^2)^2)

    d_p1 = -(m1 + m2)* g*l1*sin(θ1) - h1 + h2*sin(2*(θ1-θ2))
    d_θ1 = (l2*p1 - l1*p2*cos(θ1-θ2))/(l1^2 * l2*(m1 + m2*sin(θ1 - θ2)^2))
    d_p2 = -m2*g*l2*sin(θ2) + h1 - h2*sin(2*(θ1-θ2))
    d_θ2 = (-m2*l2*p1*cos(θ1 - θ2) + (m1 + m2)*l1*p2) / (m2*l1*l2^2*(m1 + m2*sin(θ1-θ2)^2))

    du .= [d_p1, d_p2, d_θ1, d_θ2]
    return nothing
end

l1 = 1. #length of pendulum1
l2 = 2. #length of pendulum2
m1 = 1. #mass of pendulum1
m2 = 1. #mass of pendulum2
g = 9.81 #gravity

params = [l1, l2, m1, m2, g]
times = (0., 25.)
u0 = [1.,1.,1.,1.]
prob = ODEProblem(doublependulum, u0, times, params)
sol1 = solve(prob, AutoVern7(Rodas5()), dt = .005)

#plot solution
plot(sol1, vars=1, xlim=(0,20), label="Momentum1")
plot!(sol1, vars=2, xlim=(0,20), label="Momentum2")
plot!(sol1, vars=3, xlim=(0,20), label="theta1")
plot!(sol1, vars=4, xlim=(0,20), label="theta2")
#==========================================================#

#Now with HamiltonianProblem()
#==========================================================#
function H(p, θ, params)
    l1 = params[1]
    l2 = params[2]
    m1 = params[3]
    m2 = params[4]
    g  = params[5]

    return  (m2*l2^2*p[1]^2 + (m1+m2)*l1^2*p[2]^2 - 2*m2*l1*l2*p[1]*p[2]*cos(θ[1]-θ[2]) ) /
        (2*m2*l1^2*l2^2*(m1+m2*sin(θ[1]-θ[2])^2)) -
        (m1+m2)*g*l1*cos(θ[1]) - m2*g*l2*cos(θ[2])
end

l1 = 1. #length of pendulum1
l2 = 2. #length of pendulum2
m1 = 1. #mass of pendulum1
m2 = 1. #mass of pendulum2
g = 9.81 #gravity

params = [l1, l2, m1, m2, g]
q0 = [1.0,1.0]
p0 = [1.0,1.0]
times = (0.,25.)
prob = HamiltonianProblem(H, q0, p0, times, params)
sol2 = solve(prob, SofSpa10(), dt = .05)

plot(sol2, vars=1, xlim=(0,20), label="Momentum1")
plot!(sol2, vars=2, xlim=(0,20), label="Momentum2")
plot!(sol2, vars=3, xlim=(0,20), label="theta1")
plot!(sol2, vars=4, xlim=(0,20), label="theta2")
#==========================================================#

#Animation
#==========================================================#
function make_pretty_gif(sol)
    timepoints = sol.t

    x1 = l1*sin.(sol[3,:])
    y1 = -l1*cos.(sol[3,:])
    x2 = x1 + l2*sin.(sol[4,:])
    y2 = y1 - l2*cos.(sol[4,:])

    axis_lim = (l1+l2)*1.2

    anim = Animation()
    for i =1:length(timepoints)
        str = string("Time = ", round(timepoints[i],digits=1), " sec")
        plot([0,x1[i]], [0,y1[i]], size=(400,300), xlim=(-axis_lim,axis_lim), ylim=(-axis_lim,1), markersize = 10, markershape = :circle,label ="",axis = [])
        plot!([x1[i],x2[i]], [y1[i],y2[i]], markersize = 10, markershape = :circle,label ="",title = str, title_location = :left)

        if i > 8 #rainbow trail
            plot!([x2[i-2:i]],   [y2[i-2:i]],  alpha = 0.15, linewidth = 2, color = :red, label=nothing)
            plot!([x2[i-3:i-2]], [y2[i-3:i-2]],alpha = 0.15, linewidth = 2, color = :orange, label=nothing)
            plot!([x2[i-4:i-3]], [y2[i-4:i-3]],alpha = 0.15, linewidth = 2, color = :yellow, label=nothing)
            plot!([x2[i-6:i-4]], [y2[i-6:i-4]],alpha = 0.15, linewidth = 2, color = :green, label=nothing)
            plot!([x2[i-7:i-6]], [y2[i-7:i-6]],alpha = 0.15, linewidth = 2, color = :blue, label=nothing)
            plot!([x2[i-8:i-7]], [y2[i-8:i-7]],alpha = 0.15, linewidth = 2, color = :purple, label=nothing)
        end
        frame(anim)
    end
    gif(anim, fps = 30)
end

make_pretty_gif(sol1)
make_pretty_gif(sol2)


# Multivariate regression
using Turing
using Distributions
using Random
using LinearAlgebra
using StatsPlots

# Generate moc data
n = 50
dim = 5
Random.seed!(99)
u = rand(Normal(0, 0.7), n) # true value σ² = 0.49
x = rand(Uniform(-sqrt(3.0), sqrt(3.0)), n, dim) # x generated from Uniform[-√3, √3]
X = [ones(n) x] # dependent variable matrix with ones
β = Vector(1:1:dim+1) # true coefficients
y = X*β + u # true data generating process

# set prior (normal for β, InverseGamma for σ²)
k = size(X)[2]
β0 = zeros(k)
τ0 = 0.2
A0 = τ0 * I # Identity matrix
ν0 = 5.0
λ0 = 7.0
# H0 = (λ0 / ν0) * inv(A0) # scale matrix for marginal prior of multivariate coefficient (mv Tdist)
# h0 = diag(sqrt(Matrix(H0, k, k))) # scale params for marginal prior of single coefficient (T dist) H0の対角成分
sd0 = diag(sqrt(Matrix(A0, k, k))) # the parameter for conditional prior for β|σ² ~ N_k(β0, σ²*inv(A0)), (inv(A0) part)

@model function multiple_regression(X, y)
    # Set variance priors
    σ² ~ InverseGamma(ν0/2, λ0/2)

    # Set the priors on our coefficients. (No conditions about σ²)
    nfeatures = size(X, 2)
    β ~ MvNormal(β0, inv(A0))

    # Write likelihood.
    y ~ MvNormal(X * β, σ² * I)
end

# Draw random number from posterior.
model = multiple_regression(X, y)

n_draws = 5_000
n_chains = 4
n_tune = 1_000
chn = sample(model, NUTS(), MCMCThreads(), n_draws, n_chains)
print(summarize(chn))
print(quantile(chn))
plot(chn)

# # Analytical Solution
print("Analytical Solution \n")
print(inv(X' * X) * X' * y)


using MathToolkit
using Base.Test

@test recvec([1,2,3,4,5,6,7,8,9,10]) == [1,-2,1]
@test recvec([0,1,1,2,3,5,8,13], output=Float64) == [1.0,-1.0,-1.0]
@test recvec([2^k for k=0:7], compute=Int128) == [1,-2]
@test recvec([1//2^k for k=0:7], compute=Int128, output=Float64) == [2.0,-1.0]
@test recvec([1]) == []
@test recvec([1,2,3,4,5,6,7,8,9,10,42]) == []
@test recvec([BigInt(2)^k + k%16 for k=1:64]) ==
        [1,-2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,2]

@test abs(transpose([0.75,BigFloat(1)])*pslq([0.75, 1],1e-10))[1] < 1e-10
@test abs(transpose([0.5,BigFloat(1)])*pslq([0.5, 1],1e-10))[1] < 1e-10


using AbstractAlgebra # implements arbitrary precision rationals

tanplus(x,y) = (x + y) / (1 - x * y)

function taneval(coef, frac)
    if coef == 0
        return 0
    elseif coef < 0
        return -taneval(-coef, frac)
    elseif isodd(coef)
        return tanplus(frac, taneval(coef - 1, frac))
    else
        x = taneval(div(coef, 2), frac)
        return tanplus(x, x)
    end
end

taneval(tup::Tuple) = taneval(tup[1], tup[2])

tans(v::Vector{Tuple{BigInt, Rational{BigInt}}}) = foldl(tanplus, map(taneval, v), init=0)

const testmats = Dict{Vector{Tuple{BigInt, Rational{BigInt}}}, Bool}([
    ([(1, 1//2), (1, 1//3)], true), ([(2, 1//3), (1, 1//7)], true),
    ([(12, 1//18), (8, 1//57), (-5, 1//239)], true),
    ([(88, 1//172), (51, 1//239), (32, 1//682), (44, 1//5357), (68, 1//12943)], true),
    ([(88, 1//172), (51, 1//239), (32, 1//682), (44, 1//5357), (68, 1//12944)], false)])

function runtestmats()
    println("Testing matrices:")
    for (k, m) in testmats
        ans = tans(k)
        println((ans == 1) == m ? "Verified as $m: " : "Not Verified as $m: ", "tan $k = $ans")
    end
end

runtestmats()


function sphereFunc(x::AbstractArray)
    return sum(x.^2)
end

function rosenbrockfunc(x::AbstractVector)
    sum = 0.0
    for i in 1:length(x) - 1
        sum += 100*(x[i + 1] - x[i]^2)^2 + (x[i] - 1)^2
    end
    return sum
end

function rastriginfunc(x::AbstractVector)
    d = length(x)

    sum = 0.0
    for i in 1:d
        sum += (x[i]^2 - 10*cos(2*π*x[i]))
    end
    return 10*d + sum
end

function ackleyfunc(x::AbstractVector)
    a = 20
    b = 0.2
    c = 2*π
    d = length(x)

    sum1 = 0.0
    sum2 = 0.0
    for i in 1:d
        sum1 += x[i]^2
        sum2 += cos(c*x[i])
    end
    return -a*exp(-b*sqrt(sum1/d))  - exp(sum2/d) + a + exp(1)
end


#<---- correlation Coefficient --->
"""
    # Description
      A correlation coefficient is a statistical value that can be used to
      determine statistical correlation.\n
      --------------------\n
    # Input
      correlationcoeff(x, y)\n
      --------------------\n
      ## Positional Arguments
      x:: An array of values representing the general population.\n
      y:: An array of values representing the sample population.\n
      --------------------\n
     # Output
     r:: The correlation coefficient.
       """
function correlationcoeff(x,y)
    n = length(x)
    yl = length(y)
    if n != yl
        throw(ArgumentError("The array shape does not match!"))
    end
    xy = x .* y
    sx = sum(x)
    sy = sum(y)
    sxy = sum(xy)
    x2 = x .^ 2
    y2 = y .^ 2
    sx2 = sum(x2)
    sy2 = sum(y2)
    ((n*sxy) - (sx * sy)) / (sqrt((((n*sx2)-(sx^2)) * ((n*sy2)-(sy^2)))))
end
"""
    # Two Tailed Test
    ### Description
      The TwoTailed function takes a distribution, a sample, and a confidence
          level, and will return a P value reflecting the probability of
          statistical significance.\n
      --------------------\n
    ### Input
      TwoTailed(Distribution, sample; c)\n
      --------------------\n
      #### Positional Arguments
      Lathe Distribution - Distribution:: A Lathe Distribution.\n
      Array{Any} - sample:: An array of values representing the sample that should be
       tested.\n
       #### Key-word Arguments\n
       Float64 - c:: Level of confidence for a given test in decimal form of a
       percentage.\n
      --------------------\n
     ### Output
     T:: Bool type representing as to whether or not P <= a && P >= -a.
       """
function TwoTailed(dist::Distribution, sample; c = .95)
    a = 1 - c
    t = dist.apply(sample)
    v = dist.N - 1
    P = Real(dist.cdf(t, v))
    println(P)
    if P <= a && P >= - a
            return(true)
        else
            return(false)
    end
end
"""
    One Tailed Test -- NOT IMPLEMENTED (0.1.4)
    # Description
      The OneTailed test function takes a distribution, a sample, and a
          confidence
          level, and will return a P value reflecting the probability of
          statistical significance.\n
      --------------------\n
    # Input
      OneTailed(Distribution, sample; c)\n
      --------------------\n
      #### Positional Arguments
      Lathe Distribution - Distribution:: A Lathe Distribution.\n
      Array{Any} - sample:: An array of values representing the sample that should be
       tested.\n
       #### Key-word Arguments\n
       Float64 - c:: Level of confidence for a given test in decimal form of a
       percentage.\n
      --------------------\n
     # Output
     P:: P value representing the probability of A <= P >= - A
       """
function OneTailed(dist, sample; c = .95)
  a = 1 - c
  t = dist.apply(sample)
  v = dist.N - 1
  P = Real(dist.cdf(t, v))
  println(P)
  if P <= a
        return(true)
    else
        return(false)
end
end
"""
    Probability\n
    # Description
    The P() function takes a distribution, sample, and confidence level and
    will return the corresponding probability value.
      --------------------\n
    # Input
      OneTailed(Distribution, sample; c)\n
      --------------------\n
      #### Positional Arguments
      Lathe Distribution - Distribution:: A Lathe Distribution.\n
      Array{Any} - sample:: An array of values representing the sample that should be
       tested.\n
       #### Key-word Arguments\n
       Float64 - c:: Level of confidence for a given test in decimal form of a
       percentage.\n
      --------------------\n
     # Output
     P:: P value representing probability.
       """
P(dist, sample; c = .95) = dist.cdf(dist.apply(sample), dist.N - 1)


julia> [1 2 3 ; 4 5 6]  # a 2x3 matrix
2x3 Array{Int64,2}:
 1  2  3
 4  5  6

julia> [1 2 3 ; 4 5 6]'  # note the quote
3x2 LinearAlgebra.Adjoint{Int64,Array{Int64,2}}:
 1  4
 2  5
 3  6


# functions related to beta distributions

# R implementations
# For pdf and logpdf we use the Julia implementation
using .RFunctions:
    betacdf,
    betaccdf,
    betalogcdf,
    betalogccdf,
    betainvcdf,
    betainvccdf,
    betainvlogcdf,
    betainvlogccdf

# Julia implementations
betapdf(α::Real, β::Real, x::Real) = exp(betalogpdf(α, β, x))

betalogpdf(α::Real, β::Real, x::Real) = betalogpdf(promote(α, β, x)...)
function betalogpdf(α::T, β::T, x::T) where {T<:Real}
    # we ensure that `log(x)` and `log1p(-x)` do not error
    y = clamp(x, 0, 1)
    val = xlogy(α - 1, y) + xlog1py(β - 1, -y) - logbeta(α, β)
    return x < 0 || x > 1 ? oftype(val, -Inf) : val
end


using JuMP, FactCheck, AmplNLWriter

## Solve test problem with sind and cosd functions
 #
 #  min   (7 - (3*cosd(x1) + 5*cosd(x2)))^2 + (0 - (3*sind(x1) + 5*sind(x2)))^2
 #  s.t.  x1, x2 continuous
 #
 #  The optimal objective value is 0
 ##

# Allow resolving the model from multiple starts after NLP changes in JuMP 0.12
EnableNLPResolve()

if !isdefined(:solver); solver = CouenneNLSolver(); end

m = Model(solver=solver)
@variable(m, x[1:2])

@NLobjective(m, Min, (7 - (3*cosd(x[1]) + 5*cosd(x[2])))^2 + (0 - (3*sind(x[1]) + 5*sind(x[2])))^2)

context("example: jump_nltrig") do
    setvalue(x[1], 30)
    setvalue(x[2], -50)
    @fact solve(m) --> :Optimal
    @fact getvalue(x)[:] --> roughly([38.21321, -21.78678], 1e-5)
    @fact getobjectivevalue(m) --> roughly(0.0, 1e-5)
    # Now try from the other side
    setvalue(x[1], -30)
    setvalue(x[2], 50)
    @fact solve(m) --> :Optimal
    @fact getvalue(x)[:] --> roughly([-38.21321, 21.78678], 1e-5)
    @fact getobjectivevalue(m) --> roughly(0.0, 1e-5)
end