using LinearAlgebra
using Polynomials

using SemiLagrangian:
    getbspline, B_SplineLU, get_kl_ku, B_SplineFFT, interpolate!, decal

function test_spline(order, prec)
    setprecision(prec) do
        @time @testset "test spline object of order $order" begin
            p0 = getbspline(order, 0)
            p3 = getbspline(order, 3)
            @test p3 == decal(p0, 3)
            @test isapprox(sum(p0.(range(big"1.0", length = order + 1))), 1, atol = 1e-60)
            @test sum(p0.(range(one(Rational{BigInt}), length = order + 1))) == 1
            for i = 0:order
                x = rand(BigFloat)
                res0 = p0(x + i)
                res3 = p3(x + i + 3)
                @test isapprox(res0, res3, atol = 1e-60)
                @test order == Polynomials.degree(p0[i])
                pol1 = p0[i]
                pol2 = p0[i+1]
                for j = 1:order
                    @test pol1(i + 1) == pol2(i + 1)
                    pol1 = derivative(pol1)
                    pol2 = derivative(pol2)
                end
            end
        end
    end
end
for order = 1:15
    test_spline(order, order < 20 ? 256 : 512)
end
function nb_diff_tol(f1, f2, tol)
    nb = 0
    for i = 1:size(f1, 1)
        if abs(f1[i] - f2[i]) > tol
            #          println("indice=$i diff=$(abs(f1[i]-f2[i]))")
            nb += 1
        end
    end
    return nb
end
"""
    bsplinerec(p, j, x)

Return the value at x in [0,1[ of the B-spline with
integer nodes of degree p with support starting at j.
Implemented recursively using the de Boor's recursion formula
using the [De Boor's Algorithm](https://en.wikipedia.org/wiki/De_Boor%27s_algorithm)

Note that this function has an exponential complexity depending to the order, so it is only there for testing.

```math
B_{i,0}(x) := \\left\\{
\\begin{matrix}
1 & \\mathrm{if}  \\quad t_i ≤ x < t_{i+1} \\\\
0 & \\mathrm{otherwise}
\\end{matrix}
\\right.
```

```math
B_{i,p}(x) := \\frac{x - t_i}{t_{i+p} - t_i} B_{i,p-1}(x)
+ \\frac{t_{i+p+1} - x}{t_{i+p+1} - t_{i+1}} B_{i+1,p-1}(x).
```

"""
function bsplinerec(p, j, x::T) where {T}

    if p == 0
        if j == 0
            return one(T)
        else
            return zero(T)
        end
    else
        w = (x - j) / p
        w1 = (x - j - 1) / p
    end
    (w * bsplinerec(p - 1, j, x) + (1 - w1) * bsplinerec(p - 1, j + 1, x))

end

function test_bspline()

    @time @testset "test verify bspline" begin
        tab_x = [
            1821 // 10000,
            1 // 1234,
            7677 // 8999,
            big"123456789" // big"234567890",
            0 // 1,
        ]
        for ord = 1:10, x in tab_x
            if ord >8
                p1 = Polynomial([big(1//7),2//7,3//7])
                p2 = Polynomial([-big(6//7), 2//7, 3//7])
            else
                p1 = Polynomial([1//7,2//7,3//7])
                p2 = Polynomial([-6//7, 2//7, 3//7])
            end
            for i = 0:(ord-1)
                bsp = getbspline(ord, i)
                b1 = bsp*p1
                b2 = bsp*p2
                bspbis = b1 - b2
                v = bsplinerec(ord, i, x)
                @test  v == bsp(x)
                @test v == bspbis(x)
            end
        end
    end

end

@testset "test get_kl_ku" begin
    kl, ku = get_kl_ku(5)
    @test kl == 2 && ku == 2

    kl, ku = get_kl_ku(6)
    @test kl == 2 && ku == 3
    #    @test kl == 3 && ku == 2
end

test_bspline()


"""
    secant(f,p0,p1)

adalah fungsi untuk menyelesaikan `f(x)=0` menggunakan metode secant. Metode ini
memerlukan dua nilai tebakan awal `p0` dan `p1`.

# Examples
```jl
julia> f(x) = x^3-3*x+2;

julia> pk, flag, M = secant(f,-2.6,-2.4);

julia> pk
-2.0

julia> flag
0

julia> M
9×3 Array{Float64,2}:
 0.0  -2.6      NaN
 1.0  -2.4      NaN
 2.0  -2.1066     0.293401
 3.0  -2.02264    0.0839576
 4.0  -2.00151    0.0211303
 5.0  -2.00002    0.00148856
 6.0  -2.0        2.25138e-5
 7.0  -2.0        2.26855e-8
 8.0  -2.0        3.40616e-13
```
return solusi `p0` dengan `flag` bernilai 0 jika metode bisection berhasil menemukan
solusi dan gagal jika tidak 0. Serta, matriks `M` yang berisi catatan proses tiap
iterasi `[k, pk, error]`
"""
function secant(f,p0,p1)
    # Definisikan nilai toleransi, maksimum iterasi dan tebakan awal.
    delta = 10^-12;
    maxi = 100;
    M = [0 p0 NaN
         1 p1 NaN];
    p = [p0 p1];
    pk = p1;
    flag = 1;
    # Mulai langkah iterasi
    for k = 2:maxi
        # rumus metode secant
        pk=p[k]-f(p[k])*(p[k]-p[k-1])/(f(p[k])-f(p[k-1]));
        p = [p pk]
        # Hitung nilai galat mutlak dan relatif
        err=abs(p[k+1]-p[k]);
        rel=2*err/(abs(p[k]) + eps());
        M = [M; [k pk err]]
        # Kriteria penghentian iterasi jika galat memenuhi toleransi.
        if err<delta || rel<delta
            flag = 0;
            break
        end
    end
    return pk, flag, M
end


### A Pluto.jl notebook ###
# v0.16.0

using Markdown
using InteractiveUtils

# ╔═╡ 9c340f8b-877d-41b9-baca-2698cc2b5988
using Plots

# ╔═╡ 99adb0e2-1b94-11ec-1351-79707f6016af
md"""
# Bracket search

*STMO*

**Michiel Stock**
"""

# ╔═╡ 6b30fe20-f321-4cb8-a2fc-0a2820d71bc0
md"As a first introduction to mathematical optimization, we will study two *bracketing methods*. Bracketing methods can be used to minimize scalar function with a single input variable. These algorithms identify an interval $[a, b]$ containing the desired minimum. We will assume that the functions are *unimodal*, i.e. they contain a single minimum."

# ╔═╡ bdf6c059-db72-4d90-b98f-2e77b7d89f37
md"""
## Finding an initial bracket

The following function will generate from an initial $x$ an interval containing a local minimum. It works by moving one of the limits of the interval until the function value at that limit starts to increase.
"""

# ╔═╡ 662b4d13-505c-4202-b104-0db80d8b1822
function bracket_minimum(f, x=0.0; s=1e-2, k=2.0)
  a, ya = x, f(x)  # lower limit of the interval
  b, yb = x, f(x + s)  #  upper limit of the interval
  if yb > ya  # default assumes f is decreasing in a, flip if not the case
    a, b = b, a
    ya, yb = yb, ya
    s = -s  # go to the left instead of right
  end
  while true
    c, yc = b + s, f(b + s)
    if yc > yb
      return a < c ? (a, c) : (c, a)
    end
    a, ya = b, yb
    b, yb = c, yc
    s *= k
  end
end

# ╔═╡ 42131c7f-2980-4bc9-be00-64e25ecd238e
md"Example of finding an initial bracket, starting from point $x$."

# ╔═╡ 143801cb-25ee-4a49-882a-a8ab377a25f8
md"""
## Bisection method

The bisection method is technically not a minimization algorithm, but a *root finding method*, i.e., it can be used to find an $x$ for a given function $g: \mathbb{R}\rightarrow \mathbb{R}$ such that

$$g(x) = 0\,.$$

We can use the bisection method to find a minimum of a function $f(x)$ by finding points where the first derivative $f'(x)$ is equal to 0.

> **Question**: in addition to the first derivative being equal to zero, which other criteria should hold for $x^\star$ to be a minimizer?

The bisection method departs from an initial bracket $[a, b]$, where $g(a)$ and $g(b)$ have opposing signs. If $g(x)$ is a continuous function, the *intermediate value theorem* states that there is at least one $x\in[a,b]$ such that $g(x)$.

In every step of the bisection method, the interval is cut into half. The midpoint $x = (a+b)/2$ is computed, and a new bracket is formed from the midpoint and the side that contains zero.

"""

# ╔═╡ 0eac24ee-344d-4ac6-ad3d-d3c29e98da09
md"""
The bisection procedure is repeated until the length of the interval is smaller than a small $\epsilon > 0$. This $\epsilon$ is called a *convergence parameter*, because it determines when the algorithm will halt. The pseudocode of the bisection method is given below.

> **given** $g(x)$, the derivative of a function, initial interval $[a, b]$, tolerance $\epsilon$.
>
> **while** $b - a > \epsilon$
>> 1. *Determine midpoint*. $x:=(a+b)/2$
>> 2. *Update*.
>>> **if $g(x)=0$**: $a:=x$, $b:=x$
>>>
>>> **else if $\text{sign}(g(x)) = \text{sign}(g(a))$**: $a:=x$
>>>
>>> **else**: $b:=x$
>
> **Output**: $[a, b]$

Since every step shrinks the interval with a factor two, it is easy to show that this procedure stops within

$$\log_2\left(\frac{b-a}{\epsilon}\right)$$

iterations.
"""

# ╔═╡ 524e2c11-e527-44ec-9510-92bb5905ad7a
md"""
**Assignment 1** Complete the code for the bisection method. Use it to find the minimum of

$$f(x) = \log(e^{8x-3}+2e^{0.2x^2-x+1})$$

Check your results graphically.
"""

# ╔═╡ ff4c2d0b-3b10-41c6-8579-c86710a54bbe
function bisection(g, a, b; ϵ=1e-3)
  @assert a < b "a should be smaller than b"

  # a is minizer?
  g(a) == 0 && return a, a
  # b is minizer?
  g(b) == 0 && return b, b

  while missing
    missing
  end
  return a, b
end

# ╔═╡ 727f53c3-2036-4474-8225-1e9f32c0343c
f(x) = log(exp(8x-3)+2exp(0.2x^2-x+1))

# ╔═╡ 5eecd08d-0567-440b-97a3-121882800d79
bracket_minimum(f, k=1.2)

# ╔═╡ 1a2ff096-fcd5-4b5f-901b-4282627202ac
md"> To type `f′` just type `f\prime<tab>`. Whenever you are unsure how to type a unicode character you can call it using the help, e.g. `?ϵ`."

# ╔═╡ 90e525e6-ac62-4299-ac42-cda344074051
f′(x) = missing  # use Wolfram alpha for now

# ╔═╡ 62d84643-d26f-475b-8431-e8f4b82f77a5

# ╔═╡ fc9563d5-2de3-4e11-9836-d41c1c33073a

# ╔═╡ 17bd8b62-a5df-42ab-9e5c-0a6d1fd6ad6f
md"""
## Quadratic fit search

As we will see in the next lecture, finding the minimum of quadratic functions of the form

$$q(x) = p_1 + p_2 x + p_3 x^2$$

is trivial. The *quadratic fit search method* will approximate a function by a quadratic function and computes the minimizer of that function.

Given three bracketing points $a<b<c$ and their respective evaluations $y_a=f(a), y_b=f(b)$ and $y_c=f(c)$, we can fit a quadratic curve by solving the following system of equations for the coefficients:

$$\begin{bmatrix}y_a\\ y_b \\ y_c\end{bmatrix} = \begin{bmatrix}1 & a & a^2 \\ 1 & b & b^2 \\ 1 & c & c^2 \end{bmatrix} \begin{bmatrix}p_1\\ p_2 \\ p_3\end{bmatrix}\,.$$

For example, in Julia, we have:
"""

# ╔═╡ 21a3085c-609d-47de-8a96-7303187e5aa4
a, b, c = -2, -1, 3

# ╔═╡ 7beb8b85-4e35-4e13-896c-3fffc992ea13
ya, yb, yc = f.((a, b, c))

# ╔═╡ bff05d34-060f-4860-9c70-70c6cfea6721
p1, p2, p3 = [1 a a^2; 1 b b^2; 1 c c^2] \ [ya, yb, yc]

# ╔═╡ 7f10c92e-e778-43c7-aaa4-38e6150773d7
md"""We can even dispense solving the system and use a closed-form fit based on three points:

$$q(x) = y_a\frac{(x-b)(x-c)}{(a-b)(a-c)} +  y_b\frac{(x-a)(x-c)}{(b-a)(b-c)}+ y_c\frac{(x-a)(x-b)}{(c-a)(c-b)}\,.$$

The unique minimum of this quadratic curve is computed by:

$$x^\star = \frac{1}{2} \frac{y_a(b^2-c^2) + y_b(c^2-a^2)+ y_c(a^2-b^2)}{y_a(b-c) + y_b(c-a)+ y_c(a-b)}\,.$$

We have implemented these two formulas in `quadratic_fit` and `quadratic_fit_min`. See below for an illustration.
"""

# ╔═╡ 55934af9-5dd1-43c2-bf24-9430df6cdbd1
function quadratic_fit(a, b, c, ya, yb, yc)
  @assert a != b != c
  return x -> ya * (x-b) * (x-c) / ((a-b) * (a-c)) +
              yb * (x-a) * (x-c) / ((b-a) * (b-c)) +
              yc * (x-a) * (x-b) / ((c-a) * (c-b))
end

# ╔═╡ d513a33a-c878-4a18-a5e6-309cc7f2937d
function quadratic_fit_min(a, b, c, ya, yb, yc)
  return 0.5 * (ya * (b^2 - c^2) + yb * (c^2 - a^2) + yc * (a^2 - b^2)) /
        (ya * (b-c) + yb * (c-a) + yc * (a-b))
end

# ╔═╡ 693872b2-9223-4782-b9fe-724bcf8bf9ba
x = quadratic_fit_min(a, b, c, ya, yb, yc)

# ╔═╡ 5e5b17b2-b1a4-4c72-b588-7c03ae6788c7
md"""
Given the minimizer $x^\star$ of the quadratic approximation, we can update the interval closer to this value. The quadratic fit search repeats this approach $n$ times. The pseudocode is given below.

> **given** $f(x)$, the function to be minimized, three increasing values $a, b, c$ with $f(a) > f(b)$ and $f(c) > f(b)$, the number of steps $n$.
>
> **repeat** $n$ times
>> 1. *Fit quadratic*
>> 2. *Determine minimizer of $q(x)$*
>> 3. *Update bracket*:
>>> **if $x\in [a,b]$**: $a, b, c := a, x, b$
>>> **else**: $a, b, c := b, x, c$
>
> **Output**: $a, b, c$
"""

# ╔═╡ dfb42ca1-6dbb-4668-90d7-4e5cd1365c99
md"**Assignment 2**

Complete the code for the quadratic fit search method. Use it again to find the minimizer of the provided $f(x)$.
"

# ╔═╡ ed1b64ee-f2a8-4584-9f39-9906306453f4
function quadratic_fit_search(f, a, b, c, n)
  @assert a < b < c "a, b, c not consecutive "
  @assert f(a) > f(b) && f(c) > f(b) "f(b) should be less than f(a) and f(c)"

  for i in 1:n
    missing  # complete me
  end
  return a, b, c
end

# ╔═╡ b0d790f7-3593-4965-aa3b-f2c728d8c155

# ╔═╡ 46f2c4a8-dcde-4fa8-8b08-cc66b1d3b4c7

# ╔═╡ 26036622-b1e7-480c-9a60-fde35c9d6a8b
md"""The coming chapters will discuss the minimization of quadratic functions in much greater detail.

# Exercises

1. Find the maximum of $\sqrt[\leftroot{-2}\uproot{2}x]{x}$.
2. Given a chemical system for which a chemical with concentration is degraded by enzymic conversion and chemical decay. The rate of removal is given by

$$r(x) = 0.02 + 1.2\frac{x}{10+x}\,.$$

The system receives a constant input with a concentration of 0.3. What is the steady-state concentration?"""

# ╔═╡ 412c4bc9-87c3-4e1b-9968-b2f8ebb05d3a

# ╔═╡ b106794f-c997-499a-8bc6-cc2117105f06

# ╔═╡ a093d875-235f-4606-b6ff-85cf05a0f779

# ╔═╡ 57847e93-2316-4d1d-912d-646b0d8b8d24
md"""
## References

- Kochenderfer, M. J. and Wheeler, T., '*Algorithms for Optimization*'. MIT Press (2019)
"""

# ╔═╡ c486a4da-d50c-49f5-98a0-46b417469b06
begin
	myblue = "#304da5"
	mygreen = "#2a9d8f"
	myyellow = "#e9c46a"
	myorange = "#f4a261"
	myred = "#e76f51"
	myblack = "#50514F"

	mycolors = [myblue, myred, mygreen, myorange, myyellow]
end;

# ╔═╡ a3259f42-3ab2-4cca-9179-ecf9c019aa4e
let
	f(x) = 0.003x^4 + 8x^3 - 3x - 8
	(a, b) = bracket_minimum(f, k=1.2)
	plot(f, -0.5:0.01:0.5, xlabel="x", label="f(x)", color=myblue, lw=2)
	vline!([a, b], label="", color=myorange, lw=2)
	vline!([0], label="", color=mygreen, lw=2)
	xticks!([a, b, 0], ["a", "b", "x"])
	yticks!(:none)
end

# ╔═╡ 33b799e2-70a1-4ec2-a0c7-16fb02bce10c
let
	g(x) = tanh(sin(x))

	a, b = -0.75, 1.2

	plot(g, -pi/2:0.1:pi/2, label="g(x)", lw=2, color=myblue, xlabel="x", legend=:left)
	scatter!([a], [g(a)], color=myorange, label="a")
	scatter!([b], [g(b)], color=mygreen, label="b")
	vline!([(b+a)/2], color=myred, label="x = (b-a)/2", lw=2)
	hline!([0], label="", color=myblack, ls=:dash)
	title!("Illustration of the bisection method")
end

# ╔═╡ 98c07443-db0d-4c86-aef4-ceeafd5a95ef
let
	plot(f, -3:0.1:5, label="f(x)", lw=2, xlabel="x", color=myblue)
	scatter!([a, b, c], [ya, yb, yc], label="a, b, c", color=myorange)
	plot!(quadratic_fit(a, b, c, ya, yb, yc), -3:0.1:5, label=" q(x) ", lw=2, color=mygreen)
	vline!([x], label="x", lw=2, color=myred)
end

# ╔═╡ 7a5b2671-44b1-4382-a9a6-3ed86ecf4fae
module Solution
	export bisection, quadratic_fit_search

	function bisection(g, a, b; ϵ=1e-3)
	  @assert a < b "a should be smaller than b"

	  # a is minizer?
	  g(a) == 0 && return a, a
	  # b is minizer?
	  g(b) == 0 && return b, b

	  ga,gb = g(a),g(b)
	  while (b-a)/2 > ϵ
			x = (a+b)/2
			gx = g(x)
			gx == 0 && return x,x
			if sign(gx) == sign(ga)
				a,ga = x,gx
			else
				b,gb = x,gx
			end
	  end
	  return a, b
	end

	function quadratic_fit_search(f, a, b, c, n)
	  @assert a < b < c "a, b, c not consecutive "
	  @assert f(a) > f(b) && f(c) > f(b) "f(b) should be less than f(a) and f(c)"

	  for i in 1:n
		x = quadratic_fit_min(a,b,c,f(a),f(b),f(c))
			if a < x < b
				a,b,c = a,x,b
				else b < x < c
				a,b,c = b,x,c
			end
	  end
	  return a, b, c
	end
end

# ╔═╡ 00000000-0000-0000-0000-000000000001
PLUTO_PROJECT_TOML_CONTENTS = """
[deps]
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"

[compat]
Plots = "~1.22.1"
"""

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

[[GR]]
deps = ["Base64", "DelimitedFiles", "GR_jll", "HTTP", "JSON", "Libdl", "LinearAlgebra", "Pkg", "Printf", "Random", "Serialization", "Sockets", "Test", "UUIDs"]
git-tree-sha1 = "c2178cfbc0a5a552e16d097fae508f2024de61a3"
uuid = "28b8d3ca-fb5f-59d9-8090-bfdbd6d07a71"
version = "0.59.0"

[[GR_jll]]
deps = ["Artifacts", "Bzip2_jll", "Cairo_jll", "FFMPEG_jll", "Fontconfig_jll", "GLFW_jll", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Libtiff_jll", "Pixman_jll", "Pkg", "Qt5Base_jll", "Zlib_jll", "libpng_jll"]
git-tree-sha1 = "ef49a187604f865f4708c90e3f431890724e9012"
uuid = "d2c73de3-f751-5644-a686-071e5b155ba9"
version = "0.59.0+0"

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

[[Gettext_jll]]
deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Libiconv_jll", "Pkg", "XML2_jll"]
git-tree-sha1 = "9b02998aba7bf074d14de89f9d37ca24a1a0b046"
uuid = "78b55507-aeef-58d4-861c-77aaff3498b1"
version = "0.21.0+0"

[[Glib_jll]]
deps = ["Artifacts", "Gettext_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Libiconv_jll", "Libmount_jll", "PCRE_jll", "Pkg", "Zlib_jll"]
git-tree-sha1 = "7bf67e9a481712b3dbe9cb3dac852dc4b1162e02"
uuid = "7746bdde-850d-59dc-9ae8-88ece973131d"
version = "2.68.3+0"

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

[[Libffi_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "761a393aeccd6aa92ec3515e428c26bf99575b3b"
uuid = "e9f186c6-92d2-5b65-8a66-fee21dc1b490"
version = "3.2.2+0"

[[Libgcrypt_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgpg_error_jll", "Pkg"]
git-tree-sha1 = "64613c82a59c120435c067c2b809fc61cf5166ae"
uuid = "d4300ac3-e22c-5743-9152-c294e39db1e4"
version = "1.8.7+0"

[[Libglvnd_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll", "Xorg_libXext_jll"]
git-tree-sha1 = "7739f837d6447403596a75d19ed01fd08d6f56bf"
uuid = "7e76a0d4-f3c7-5321-8279-8d96eeed0f29"
version = "1.3.0+3"

[[Libgpg_error_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "c333716e46366857753e273ce6a69ee0945a6db9"
uuid = "7add5ba3-2f88-524e-9cd5-f83b8a55f7b8"
version = "1.42.0+0"

[[Libiconv_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "42b62845d70a619f063a7da093d995ec8e15e778"
uuid = "94ce4f54-9a6c-5748-9c1c-f9c7231a4531"
version = "1.16.1+1"

[[Libmount_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "9c30530bf0effd46e15e0fdcf2b8636e78cbbd73"
uuid = "4b2f31a3-9ecc-558c-b454-b3730dcb73e9"
version = "2.35.0+0"

[[Libtiff_jll]]
deps = ["Artifacts", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Pkg", "Zlib_jll", "Zstd_jll"]
git-tree-sha1 = "340e257aada13f95f98ee352d316c3bed37c8ab9"
uuid = "89763e89-9b03-5906-acba-b20f662cd828"
version = "4.3.0+0"

[[Libuuid_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "7f3efec06033682db852f8b3bc3c1d2b0a0ab066"
uuid = "38a345b3-de98-5d2b-a5d3-14cd9215e700"
version = "2.36.0+0"

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

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

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

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

[[MbedTLS]]
deps = ["Dates", "MbedTLS_jll", "Random", "Sockets"]
git-tree-sha1 = "1c38e51c3d08ef2278062ebceade0e46cefc96fe"
uuid = "739be429-bea8-5141-9913-cc70e7f3736d"
version = "1.0.3"

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

[[Measures]]
git-tree-sha1 = "e498ddeee6f9fdb4551ce855a46f54dbd900245f"
uuid = "442fdcdd-2543-5da2-b0f3-8c86c306513e"
version = "0.3.1"

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

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

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

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

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

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

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

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

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

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

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

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

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

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

[[PlotUtils]]
deps = ["ColorSchemes", "Colors", "Dates", "Printf", "Random", "Reexport", "Statistics"]
git-tree-sha1 = "2537ed3c0ed5e03896927187f5f2ee6a4ab342db"
uuid = "995b91a9-d308-5afd-9ec6-746e21dbc043"
version = "1.0.14"

[[Plots]]
deps = ["Base64", "Contour", "Dates", "Downloads", "FFMPEG", "FixedPointNumbers", "GR", "GeometryBasics", "JSON", "Latexify", "LinearAlgebra", "Measures", "NaNMath", "PlotThemes", "PlotUtils", "Printf", "REPL", "Random", "RecipesBase", "RecipesPipeline", "Reexport", "Requires", "Scratch", "Showoff", "SparseArrays", "Statistics", "StatsBase", "UUIDs"]
git-tree-sha1 = "4c2637482176b1c2fb99af4d83cb2ff0328fc33c"
uuid = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
version = "1.22.1"

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

[[Zstd_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "cc4bf3fdde8b7e3e9fa0351bdeedba1cf3b7f6e6"
uuid = "3161d3a3-bdf6-5164-811a-617609db77b4"
version = "1.5.0+0"

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

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

[[libpng_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"]
git-tree-sha1 = "94d180a6d2b5e55e447e2d27a29ed04fe79eb30c"
uuid = "b53b4c65-9356-5827-b1ea-8c7a1a84506f"
version = "1.6.38+0"

[[libvorbis_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Ogg_jll", "Pkg"]
git-tree-sha1 = "c45f4e40e7aafe9d086379e5578947ec8b95a8fb"
uuid = "f27f6e37-5d2b-51aa-960f-b287f2bc3b7a"
version = "1.3.7+0"

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

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

[[x264_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "4fea590b89e6ec504593146bf8b988b2c00922b2"
uuid = "1270edf5-f2f9-52d2-97e9-ab00b5d0237a"
version = "2021.5.5+0"

[[x265_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "ee567a171cce03570d77ad3a43e90218e38937a9"
uuid = "dfaa095f-4041-5dcd-9319-2fabd8486b76"
version = "3.5.0+0"

[[xkbcommon_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Wayland_jll", "Wayland_protocols_jll", "Xorg_libxcb_jll", "Xorg_xkeyboard_config_jll"]
git-tree-sha1 = "ece2350174195bb31de1a63bea3a41ae1aa593b6"
uuid = "d8fb68d0-12a3-5cfd-a85a-d49703b185fd"
version = "0.9.1+5"
"""

# ╔═╡ Cell order:
# ╟─99adb0e2-1b94-11ec-1351-79707f6016af
# ╠═9c340f8b-877d-41b9-baca-2698cc2b5988
# ╟─6b30fe20-f321-4cb8-a2fc-0a2820d71bc0
# ╟─bdf6c059-db72-4d90-b98f-2e77b7d89f37
# ╠═662b4d13-505c-4202-b104-0db80d8b1822
# ╟─42131c7f-2980-4bc9-be00-64e25ecd238e
# ╟─5eecd08d-0567-440b-97a3-121882800d79
# ╠═a3259f42-3ab2-4cca-9179-ecf9c019aa4e
# ╟─143801cb-25ee-4a49-882a-a8ab377a25f8
# ╟─33b799e2-70a1-4ec2-a0c7-16fb02bce10c
# ╟─0eac24ee-344d-4ac6-ad3d-d3c29e98da09
# ╟─524e2c11-e527-44ec-9510-92bb5905ad7a
# ╠═ff4c2d0b-3b10-41c6-8579-c86710a54bbe
# ╠═727f53c3-2036-4474-8225-1e9f32c0343c
# ╟─1a2ff096-fcd5-4b5f-901b-4282627202ac
# ╠═90e525e6-ac62-4299-ac42-cda344074051
# ╠═62d84643-d26f-475b-8431-e8f4b82f77a5
# ╠═fc9563d5-2de3-4e11-9836-d41c1c33073a
# ╟─17bd8b62-a5df-42ab-9e5c-0a6d1fd6ad6f
# ╠═21a3085c-609d-47de-8a96-7303187e5aa4
# ╠═7beb8b85-4e35-4e13-896c-3fffc992ea13
# ╠═bff05d34-060f-4860-9c70-70c6cfea6721
# ╟─7f10c92e-e778-43c7-aaa4-38e6150773d7
# ╠═55934af9-5dd1-43c2-bf24-9430df6cdbd1
# ╠═d513a33a-c878-4a18-a5e6-309cc7f2937d
# ╠═693872b2-9223-4782-b9fe-724bcf8bf9ba
# ╟─98c07443-db0d-4c86-aef4-ceeafd5a95ef
# ╟─5e5b17b2-b1a4-4c72-b588-7c03ae6788c7
# ╟─dfb42ca1-6dbb-4668-90d7-4e5cd1365c99
# ╠═ed1b64ee-f2a8-4584-9f39-9906306453f4
# ╠═b0d790f7-3593-4965-aa3b-f2c728d8c155
# ╠═46f2c4a8-dcde-4fa8-8b08-cc66b1d3b4c7
# ╟─26036622-b1e7-480c-9a60-fde35c9d6a8b
# ╠═412c4bc9-87c3-4e1b-9968-b2f8ebb05d3a
# ╠═b106794f-c997-499a-8bc6-cc2117105f06
# ╠═a093d875-235f-4606-b6ff-85cf05a0f779
# ╟─57847e93-2316-4d1d-912d-646b0d8b8d24
# ╟─c486a4da-d50c-49f5-98a0-46b417469b06
# ╟─7a5b2671-44b1-4382-a9a6-3ed86ecf4fae
# ╟─00000000-0000-0000-0000-000000000001
# ╟─00000000-0000-0000-0000-000000000002


using LinearAlgebra
using SparseArrays
using Plots

"
This code runs a convergence test for finite difference methods applied to Laplace's equation.
The error in different norms is plotted, along with O(h) and O(h^2) slopes for reference.
"

f(x) = pi^2*sin(pi*x)
uexact(x) = sin(pi*x)

mvec = 2 .^ (2:9)
hvec = zeros(length(mvec))
err_inf = zeros(length(mvec))
err_1 = zeros(length(mvec))
err_2 = zeros(length(mvec))
for (i,m) in enumerate(mvec)
    x = LinRange(-1,1,m+2) # add 2 endpoints
    xint = x[2:end-1]
    h = x[2]-x[1]
    A = (1/h^2) * spdiagm(0=>2*ones(m),-1=>-ones(m-1),1=>-ones(m-1))
    b = f.(xint)
    u = A\b

    hvec[i] = h
    err_inf[i] = maximum(@. abs(uexact(xint) - u))
    err_1[i] = norm((@. abs(uexact(xint) - u)),1) # 1-norm
    err_2[i] = norm(@. abs(uexact(xint) - u)) # 2-norm
end

plot(hvec,err_inf,marker=:dot,label="Max error")
plot!(hvec,err_1,marker=:dot,label="1-norm error")
plot!(hvec,err_2,marker=:dot,label="2-norm error")
plot!(hvec,hvec*1.5,linestyle=:dash,label="O(h)")
plot!(hvec,hvec.^2,linestyle=:dash,label="O(h^2)")
plot!(xaxis=:log,yaxis=:log,legend=:bottomright)


## log density function of N(mu, sd^2)
function logdnorm(y::Array, mu::Float64, sd::Float64)
    return( -0.5 * log(2*pi) .- 1.0 * log(sd) .- 1/(2*sd^2) * (y .- mu).^2 )
end

function logdnorm(y::Float64, mu::Float64, sd::Float64)
    return( -0.5 * log(2*pi) .- 1.0 * log(sd) .- 1/(2*sd^2) * (y .- mu).^2 )
end
function toy_mh(y::Array, S::Int64)
    s2 = 1.0; t2 = 10.0; mu = 5.0;

    theta = 0.0; delta2 = 2; S = 10000; THETA::Array{Float64} = []

    for s = 1:S
        theta_star = randn() * sqrt(delta2) + theta
        log_r = ( sum(logdnorm(y, theta_star, sqrt(s2))) + logdnorm(theta_star, mu, sqrt(t2)) ) -
                     ( sum(logdnorm(y, theta, sqrt(s2))) + logdnorm(theta, mu, sqrt(t2)) )

        if log(rand()) < log_r
            theta = theta_star
        end
        THETA = append!(THETA, theta)
    end
    return(THETA)
end

#y = [9.37, 10.18, 9.16, 11.60, 10.33]
y = randn(100) * sqrt(10)
res = toy_mh(y, Int64(1e10))
# using PyPlot
# plt[:hist](toy_mh(5000))
# show()


function modinv{T<:Integer}(a::T, b::T)
    b0 = b
    x0, x1 = zero(T), one(T)
    while a > 1
        q = div(a, b)
        a, b = b, a % b
        x0, x1 = x1 - q * x0, x0
    end
    x1 < 0 ? x1 + b0 : x1
end
modinv(a::Integer, b::Integer) = modinv(promote(a,b)...)


# ---
# title: 50. Pow(x, n)
# id: problem50
# author: Tian Jun
# date: 2020-10-31
# difficulty: Medium
# categories: Math, Binary Search
# link: <https://leetcode.com/problems/powx-n/description/>
# hidden: true
# ---
#
# Implement [pow( _x_ , _n_
# )](http://www.cplusplus.com/reference/valarray/pow/), which calculates  _x_
# raised to the power _n_ (i.e. xn).
#
#
#
# **Example 1:**
#
#
#
#     Input: x = 2.00000, n = 10
#     Output: 1024.00000
#
#
# **Example 2:**
#
#
#
#     Input: x = 2.10000, n = 3
#     Output: 9.26100
#
#
# **Example 3:**
#
#
#
#     Input: x = 2.00000, n = -2
#     Output: 0.25000
#     Explanation: 2-2 = 1/22 = 1/4 = 0.25
#
#
#
#
# **Constraints:**
#
#   * `-100.0 < x < 100.0`
#   * `-231 <= n <= 231-1`
#   * `-104 <= xn <= 104`
#
#
## @lc code=start
using LeetCode

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


"""
   A K-Means algorithm for data clustering

# Brief:
    The algorithm takes n-dimensional points represented as a vector where each element of that vector
    corresponds to one dimension of the point. In the beginning, the algorithm select k points.
    Every of the k points is used as a centre of the cluster. In each iteration, every of these k points
    is connected to the nearest points and moved, until it stops changing its position through iterations.

# For more informations:
    https://en.wikipedia.org/wiki/K-means_clustering

# Contributed by:- [Nikola Mircic](https://github.com/Nikola-Mircic)
"""

module KMeans
# A function that takes two points and returns the distance
# between them
function distance(a::Vector{T}, b::Vector{T}) where {T<:Number}
    if length(a) != length(b)
        return 0.0
    end
    sum = 0.0
    for i in 1:length(a)
        sum = sum + (a[i] - b[i]) * (a[i] - b[i])
    end
    return sqrt(sum)
end

# A function that returns the middle point based on the passed
# matrix in which every row contains coordinates of the single point
function findMiddle(points::Vector{Vector{T}}) where {T<:Number}
    middle::Vector{T} = []
    # Calculate arithmetic mean of coordinates for each dimension
    for dim in 1:length(points[1])
        sum = 0.0
        for val in 1:length(points[:, 1])
            sum = sum + points[val][dim]
        end
        push!(middle, sum / length(points[:, 1]))
    end
    return middle
end

# Function that assigns each point to the nearest cluster and then creates a list
# of centres of those clusters
# Returns coordinates of the clusters at the end of the iteration
function moveClusters(
    k::Int,
    dim::Int,
    clusters::Vector{Vector{T}},
    points::Vector{Vector{T}},
) where {T<:Number}
    assign::Vector{Vector{Int}} = [] # A matrix to write point-cluster pairs
    for i in 1:k
        push!(assign, [])
    end
    # For each point we are finding the nearest cluster
    for p in 1:length(points[:, 1])
        min_dis = distance(points[p], clusters[1])
        idx = 1
        for c in 2:k
            if distance(points[p], clusters[c]) < min_dis
                min_dis = distance(points[p], clusters[c])
                idx = c
            end
        end
        push!(assign[idx], p) # Assign a point p to the cluster with index idx
    end

    # After assigning each element to the cluster,
    # we are using those points to calculate the centers of our clusters
    temp_clust = copy(clusters)
    for i in 1:length(assign)
        temp_clust[i] = findMiddle(points[assign[i]])
    end

    return temp_clust
end

# MAIN FUNCTION
# Call this function to find k clusters for passed points
# If you want to have a limited number of iterations, set iter to any positive integer,
# else set it to 0 or any negative number
function cluster(k::Int, iter::Int, points::Vector{Vector{T}}) where {T<:Number}
    points_num = length(points[:, 1]) # Number of points
    clust_dim = length(points[1]) # dimension of the cluster we need to find
    #println("dim: ",points_num,"x",clust_dim)

    if points_num < k
        # If there is more requested clusters than points,
        # set coordinates of each cluster to 0
        return [zeros(clust_dim) for i in 1:k]
    elseif points_num == k
        # Number of points and requested clusters are equal, so just assign every
        # point to its own cluster
        return points
    else
        clusters::Vector{Vector{T}} = [] # Create a matrix to store centre points of the clusters
        step = points_num / k
        idx = 1.0

        # To avoid generating empty clusters, we will assign each of clusters
        # to a different point

        for clust in 1:k
            push!(clusters, points[Int(idx)])
            idx = floor(idx + step)
        end

        #println(clusters)

        if iter <= 0
            # There is no specific number of iteration
            # Program will run untill it finds the perfect solution
            new_clust = moveClusters(k, clust_dim, clusters, points)
            while new_clust != clusters # Check if something has changed in the last iteration
                clusters = new_clust
                new_clust = moveClusters(k, clust_dim, clusters, points)
            end
        else
            new_clust = moveClusters(k, clust_dim, clusters, points)
            for i in 2:iter
                if new_clust == clusters
                    # There is no need for further iterations, return the clusters
                    return clusters
                end
                clusters = new_clust
                new_clust = moveClusters(k, clust_dim, clusters, points)
            end
        end

        return clusters
    end
end
end


using JuMP
using Clp

#Preparing optimization model
m = Model(Clp.Optimizer)

# Declaring variables
@variable(m, 0 <= x1 <=10)
@variable(m, x2 >=0)
@variable(m, x3 >=0)

#Setting the objective function
@objective(m, Max, x1 + 2x2 + 5x3)

#Adding constraints
@constraint(m, constraint1, -x1 + x2 + 3x3 <= -5)
@constraint(m, constraint2, x1 + 3x2 - 7x3 <= 10)

#Printing the prepared optimization model
print(m)

# Solving the optimization problem
optimize!(m)

# Printing the optimal solutions obtained
println("Optimal Solutions:")
println("x1 = ", JuMP.value(x1))
println("x2 = ", JuMP.value(x2))
println("x3 = ", JuMP.value(x3))

# Printing the optimal dual variables
println("Dual Variables:")
println("dual1 = ", JuMP.dual(constraint1))
println("dual2 = ", JuMP.dual(constraint2))


"""
    function Simpson(f, a, b)

Calculate the integral `f` function from `x` to `y` by Simpson formula

Here's Simpson expression:

``Simpson(f, a, b) = \\frac{(b-a)}{6} * (f(a) + 4*f(\\frac{(a+b)}{2}) + f(b))`

# Arguments
- `f::Express`: integrand function
- `a::Float`: left boundary of integral interval of definite integral
- `b::Float`: right boundary of integral interval of definite integral

# Examples
```julia-repl
julia> Simpson(2*x, 1, 2)
4
```
"""
function Simpson(f, a, b)
    # Simpson Formula
    # (b-a)/6 * (f(a) + 4*f((a+b)/2) + f(b))
    h = (b - a)/2
    x = a:h:b
    sum((x[3] - x[1])/6*(f(x[3]) + 4*f(x[2]) + f(x[1])))
end


# Cross Entropy loss function

export CrossEntropy

"""
**Cross Entropy loss**

    CrossEntropy(b)

Returns the function
```math
f(x) = -\\frac{1}{N} \\sum_{i = 1}^{N} b_i \\log (x_i)+(1-b_i) \\log (1-x_i),
```
where `b` is an array such that `0 ≤ b ≤ 1` component-wise.
"""
struct CrossEntropy{R <: Real, T <: AbstractArray{R}} <: ProximableFunction
    b::T
    function CrossEntropy{R, T}(b::T) where {R <: Real, T <: AbstractArray{R}}
        if !(all(0 .<= b .<= 1. ))
            error("b must be 0 ≤ b ≤ 1 ")
        else
            new(b)
        end
    end
end

is_convex(f::CrossEntropy) = true
is_smooth(f::CrossEntropy) = true

CrossEntropy(b::T) where {R <: Real, T <: AbstractArray{R}} = CrossEntropy{R, T}(b)

function (f::CrossEntropy{R})(x::AbstractArray{R}) where {R <: Real}
    sum = R(0)
    for i in eachindex(f.b)
        sum += f.b[i]*log(x[i])+(1-f.b[i])*log(1-x[i])
    end
    return -sum/length(f.b)
end

function (f::CrossEntropy{B})(x::AbstractArray{R}) where {B <: Bool, R <: Real}
    sum = R(0)
    for i in eachindex(f.b)
        sum += f.b[i] ? log(x[i]) : (1-f.b[i])*log(1-x[i])
    end
    return -sum/length(f.b)
end

function gradient!(y::AbstractArray{R}, f::CrossEntropy{R}, x::AbstractArray{R}) where {R <: Real}
    sum = R(0)
    for i in eachindex(x)
        y[i] = 1/length(f.b)*( - f.b[i]/x[i] + (1-f.b[i])/(1-x[i]) )
        sum += f.b[i]*log(x[i])+(1-f.b[i])*log(1-x[i])
    end
    return -1/length(f.b)*sum
end

function gradient!(y::AbstractArray{R}, f::CrossEntropy{B}, x::AbstractArray{R}) where {R <: Real, B <: Bool}
    sum = R(0)
    for i in eachindex(x)
        y[i] = f.b[i] ? - 1/x[i] : 1/(1-x[i])
        y[i] *= 1/length(f.b)
        sum += f.b[i] ? log(x[i]) : log(1-x[i])
    end
    return -1/length(f.b)*sum
end

function prox!(y::AbstractArray{R}, f::CrossEntropy, x::AbstractArray{R}, gamma::R=R(1)) where {R}
    # TODO: fill-in here
    error("not implemented")
end

fun_name(f::CrossEntropy) = "cross entropy loss"
fun_dom(f::CrossEntropy) = "AbstractArray{Real}"
fun_expr(f::CrossEntropy) = "x ↦ 1/N sum( b*log(x)+(1-b)*log(1-x), i=1,...,N )"
fun_params(f::CrossEntropy) = "b = $(typeof(f.b))"


using Test
last_runtimes = Float64[]

#=

	Computing the Fibonacci sequence
	===

	Below, you will find a function `fib` which computes the first `N` (default: 100.000) numbers of the Fibonacci sequence

	> Your mission, should you decide to accept it, is to make `fib` (roughly) 10× faster. 🚀

	There are at least two mistakes in the given code. (One much more relevant than the other).
	If you want, you can find some tips in the hint boxes below.

	Useful functions for `Vector`s might be:
	- `sizehint!()` gives Julia the hint how large the vector might become
	- `zeros(Int64, N)`  creates a vector of zeros with element type `Int64`
	- `Vector{Int64}(undef, N)` creates an uninitialized vector with element type `Int64`

	Check the documentation (e.g. `?sizehint!`) for their function.
=#

function fib(N = 100_000; x₀ = 1, x₁ = 1)
	# modify this code to improve the performance

	x = Int64[]
	append!(x, (x₀, x₁))
	for i in 3:N
		push!(x, x[i-1] + x[i-2])
	end
	return x
end

@test fib()[end] == 2754320626097736315  # this line tests if `fib` works correctly

# this is how you call the function:
x = fib()

#=

How to benchmark a function:

  To test the performance, you can either use `@time`\`@elapsed` or,
  the accurate `@btime`\`@belapsed` from the package `BenchmarkTools`

=#

@time fib()

begin
	# run function again and show hisotry
	current_runtime = @elapsed fib()
	push!(last_runtimes, current_runtime)

	println("Current runtime: ", 1000 * current_runtime, " ms")
	println("Last runtimes: ")
	for t in reverse(last_runtimes)
		println("  ", 1000 * t, " ms")
	end
end

println()

#=

	If you want some hints, continue reading:

	Hint #1"
    ---

	Adding new element to an existing `Vector` can take up much time.
	It is better to allocate enough memory beforehand.

    You could use `sizehint!(x, N)`, which tells the program which size to allocate for that array.

	Or you could allocate directly a vector of size `N`, but then you need to modify the for loop accordingly, since we then don't have to `push!` element into the vector, but just to access the `i`th position.

	Hint #2
	---

	The main problem is this: The vector `x` has type `typeof(x) == Vector{Any}`!
    This wastes a lot of resources.

    To avoid this, you could at least create it as
	`x = Int64[]`.

    Or in combination with Hint #1, you could use
	`x = zeros(Int64, N)` and change the `for` loop accordingly.

=#


#These test/demonstrate the basic funcionality

@diff_eq F begin
    dy[1] = -y[1]
end
@diff_eq_jac J begin
    pd[1,1] = -1.0
end

#From the book Numerical recipes
@diff_eq F2 begin
    dy[1] = 998*y[1] + 1998*y[2]
    dy[2] = -999*y[1] - 1999*y[2]
end
@diff_eq_jac J2 begin
    pd[1,1] = 998
    pd[1,2] = 1998
    pd[2,1] = -999
    pd[2,2] = -1999
end

@diff_eq F3 begin
    dy .= -y
end
@diff_eq_jac J3 begin
    pd .= -1.0*ones(1,1)
end

y0 = [1.0]
t = range(0,stop=1,length=10)

y02 = [1.0,0.0]

s_exp = exp.(-t)

s_exp2 = hcat( [2*exp.(-t) - exp.(-1000*t),
                -exp.(-t) + exp.(-1000*t)]... )

tol = 1.0e-2

s1 = hcat(ode(F,t,y0,verbose=false)...)'   #jacobian generated, stiff
@test sqrt(sum(abs2, s1 - s_exp)) < tol
s2 = hcat(ode(F,J,t,y0,verbose=false)...)'  #jacobian supplied
@test sqrt(sum(abs2, s2 - s_exp)) < tol
s3 = hcat(ode(F,t,y0,stiff=false,verbose=false)...)'  #jacobian ignored, not stiff
@test sqrt(sum(abs2, s3 - s_exp)) < tol
s4 = hcat(ode(F,J,t,y0,[1.0e-4],verbose=false)...)'  #increased absolute tolerance
@test sqrt(sum(abs2, s2 - s_exp)) > sqrt(sum(abs2, s4 - s_exp))
s5 = hcat(ode(F,J,t,y0,[1.0e-4],1.0e-4,verbose=false)...)'  #increased relative tolerance
@test sqrt(sum(abs2, s5 - s_exp)) < sqrt(sum(abs2, s4 - s_exp))

#Stiffs
s6 = hcat(ode(F2,t,y02,verbose=false)...)'   #jacobian generated, stiff
@test sqrt(sum(abs2, s6 - s_exp2)) < tol
s7 = hcat(ode(F2,J2,t,y02,verbose=false)...)'  #jacobian supplied
@test sqrt(sum(abs2, s7 - s_exp2)) < tol
s8 = hcat(ode(F2,t,y02,stiff=false,verbose=false)...)'  #jacobian ignored, not stiff
@test sqrt(sum(abs2, s8 - s_exp2)) < tol

#Vectorized
s9 = hcat(ode(F3,J3,t,y0,verbose=false)...)'  #jacobian supplied
@test all( s9 .≈ s2 )


using Statistics
using LinearAlgebra

function prz(r, z, f, w) # compute poles, residues, zeros
    m = length(w); B = Matrix{Float64}(I, m+1, m+1); B[1, 1] = 0
    E = [0 Transpose(w); ones(m) Diagonal(z)]
    pol = eigvals(E, B); pol = pol[.!isinf.(pol)] # poles
    dz = @. 1e-5*exp(2im*pi*(1:4)/4)
    res = r(pol .+ Transpose(dz))*dz/4 # residues
    E = [0 Transpose(w.*f); ones(m) Diagonal(z)]
    zer = eigvals(E, B); zer = zer[.!isinf.(zer)] # zeros
    return pol, res, zer
end

"""
    r, pol, res, zer, z, f, w, errvec = $(FUNCTIONNAME)(F, Z; tol=1e-13, mmax=100)

AAA rational approximation [^Trefethen2018] of data `F` on set `Z`.

Inputs:
- `F`: vector of data values, or a function handle
- `Z`: vector of sample points
- `tol`: relative tolerance tol
- `mmax`: max type is `(mmax-1,mmax-1)`

Outputs:
- `r`: AAA approximant to F (function)
- `pol,res,zer`: vectors of poles, residues, zeros
- `z,f,w`: vectors of support pts, function values, weights
- `errvec` vector of errors at each step

[^Trefethen2018]: Nakatsukasa, Yuji, Olivier Sète, and Lloyd N. Trefethen. "The AAA algorithm for rational approximation." SIAM Journal on Scientific Computing 40.3 (2018): A1494-A1522.
"""
function aaa(
             F::AbstractVector{T}, Z;
             tol=1e-13,
             mmax=100,
            ) where T
    M = length(Z) # number of sample points
    # `I` is the index set outside the control points
    # `J` is the index set inside the control points
    I = collect(1:M); J = Int[]
    # initializations
    errvec = T[]; w = T[]
    tol′ = norm(F, Inf) * tol

    # select the first support point
    avg = mean(F); err = zero(T); j = 0
    for (i, f) in enumerate(F)
        errᵢ = abs(f - avg)
        if errᵢ > err
            j = i
        end
    end

    # main loop
    for m in 1:mmax
        # update index vector
        push!(J, j)
        deleteat!(I, searchsortedfirst(I, j))

        # solve a linear least-squares problem
        Aᵐ = löwner_matrix(F, Z, I, J)
        V = svd(Aᵐ, full=true).V
        w = V[:, m] # weight vector = min singular value vector

        # max error at sample points
        # i.e. max error at `Z[ii] ∀ ii ∈ I`
        err = zero(T); j = 0
        for ii in I
            Rᵢ = _eval_barycentric(Z[ii], w, F, Z, J)
            errᵢ = abs(F[ii] - Rᵢ)
            if errᵢ > err
                err = errᵢ
                j = ii
            end
        end
        push!(errvec, err)
        err <= tol′ && break # stop if converged
    end
    f, z = F[J], Z[J]
    r = let z=z, f=f, w=w
        zz -> eval_barycentric(zz, w, f, z) # AAA approximant
    end
    pol, res, zer = prz(r, z, f, w) # poles, residues, and zeros
    #r,pol,res,zer,z,f,w = cleanup(r,pol,res,zer,z,f,w,Z,F); # remove Frois. doublets (optional)
    return r, pol, res, zer, z, f, w, errvec
end

function löwner_matrix(F::AbstractVector{T}, Z, I, J) where T
    M = length(Z)
    m̂ = length(I)
    m = length(J)
    @assert m̂ + m == M
    Aᵐ = zeros(T, m̂, m)

    for (j, jj) in enumerate(J), (i, ii) in enumerate(I)
        Aᵐ[i, j] = (F[ii] - F[jj]) / (Z[ii] - Z[jj])
    end
    return Aᵐ
end

# internal use only
# evaluate barycentric form on a subset
function _eval_barycentric(zz, w, f::AbstractVector{T}, z, J) where T
    partial_d = partial_n = zero(T)
    @assert length(w) == length(J)
    for (j, jj) in enumerate(J)
        d = w[j] / (zz - z[jj])
        n = d * f[jj]
        partial_d += d
        partial_n += n
    end
    return partial_n / partial_d
end

eval_barycentric(zz, w, f, z) = map(let w=w, f=f, z=z
                                        zv->eval_barycentric(zv, w, f, z)
                                    end, zz)
function eval_barycentric(zz::Number, w, f, z)
    T = eltype(f)
    partial_d = partial_n = zero(T)
    @assert length(w) == length(f) == length(z)
    @inbounds for j in eachindex(z)
        zz == z[j] && return f[j] # force interpolation
        d = w[j] / (zz - z[j])
        n = d * f[j]
        partial_d += d
        partial_n += n
    end
    return partial_n / partial_d
end


module IntUtils

export is_prime
function is_prime{T<:Integer}(n::T)::Bool
    if n > 0
        for d in 2:(isqrt(n) + 1)
            if n % d == 0
                return false
            end
        end
        return true
    end
    return false
end

export prime_factors
function prime_factors(n::Int64)
    factors = Int64[]
    d = 2
    while n > 1
        while n % d == 0
            push!(factors, d)
            n = div(n,d)
        end
        d += 1
        if d * d > n
            if n > 1
                push!(factors, n)
            end
            break
        end
    end
    return factors
end

export num_factors
function num_factors(n::Int64)
    d = 2
    num_factors::Int64 = 1
    while n > 1
        num_d = 0
        while n % d == 0
            num_d += 1
            n = div(n,d)
        end
        num_factors *= (num_d + 1)
        d += 1
        if d * d > n
            if n > 1
                num_factors *= 2
            end
            break
        end
    end
    return num_factors
end

export prime_factors_table
function prime_factors_table(n::Int64)
    factors = Dict{Int64, Int64}()
    d = 2
    while n > 1
        while n % d == 0
            num_d = get(factors, d, 0)
            factors[d] = num_d + 1
            n = div(n,d)
        end
        d += 1
        if d * d > n
            if n > 1
                num_d = get(factors, n, 0)
                factors[n] = num_d + 1
            end
            break
        end
    end
    return factors
end

export sieve_of_eratosthenes
function sieve_of_eratosthenes(n)
    primes = trues(n)
    primes[1] = false
    p = 2
    while p*p <= n
        if primes[p]
            for i in p*p:p:n
                primes[i] = false
            end
        end
        p += 1
    end
    return primes
end

export divisors
function divisors(n::Integer)
    result = Set{Integer}()
    sq = convert(Integer, floor(sqrt(n))) + 1
    for i in 1:(sq+1)
        (d,r) = divrem(n, i)
        if r == 0
            push!(result, i)
            if i != d
                push!(result, d)
            end
        end
    end
    return result
end

export proper_divisors
function proper_divisors(n::Integer)
    result = Set{Integer}()
    sq = convert(Integer, floor(sqrt(n))) + 1
    for i in 1:(sq+1)
        (d,r) = divrem(n, i)
        if r == 0
            push!(result, i)
            if i != d && d != n
                push!(result, d)
            end
        end
    end
    return result
end

export is_amicable
function is_amicable(n::Integer)
    s1 = sum(divisors(n)) - n
    s2 = sum(divisors(s1)) - s1
    return s2 == n && s1 != s2
end

end


using SDELab2 # SDELab2.jl should be findable via LOAD_PATH
println("run_HH.jl")
#
# Hodgkin-Huxley example from
# H. Alzubaidi and T. Shardlow. Numerical simulations of SDEs and SPDEs.
# In: Stochastic Methods in Neuroscience. Ed. by C. Laing and G. Lord. OUP,
# 2009. Chap. 12, pp. 344–366.
# doi: 10.1093/acprof:oso/9780199235070.003.0012 .
#
function drift_f(y,Mu)
  g_k=36; g_Na=120; g_L=0.3
  C_m=1 # membrane capabilities
  C1=g_k/C_m; C2=g_Na/C_m; C3=g_L/C_m
  # constants of resting potential of such iterations
  v_k=-12; v_Na=115; v_L=10.613
  a_n=(10-y[1]) / (100*(exp((10-y[1])/10)-1))
  b_n=exp(-y[1]/80)/8
  a_m=(25-y[1])/(10*(exp((25-y[1])/10)-1.))
  b_m=4*exp(-y[1]/18)
  a_h=(7*exp(-y[1]/20))/100
  b_h=1/(exp((30-y[1])/10)+1)
  #
  # compute drift function for HH model
  z1=(C1*(v_k-y[1])*(y[2]^4)) + (C2*(v_Na-y[1])*(y[3]^3)*y[4]) + (C3*(v_L-y[1])) + Mu
  z2=a_n*(1-y[2])-b_n*y[2]
  z3=a_m*(1-y[3])-b_m*y[3]
  z4=a_h*(1-y[4])-b_h*y[4]
  return [z1,z2,z3,z4]
end

#
function diff_g_mat(u,dw,sigma)
  return   [sigma*dw;0;0;0]
end
#
function diff_g_vecs(sig)
  m=1
  A=Array(Function,m)
  g1(u)=[sig,0,0,0]
  A[1]=g1
  return A
end
# dimensions
d=4
m=1
noise_type=3 # commutative
# params
sig=0.1
Mu=2.5
# initial data
a0_n=0.1/(exp(1.)-1.); b0_n=0.125
a0_m=2.5/(exp(2.5)-1); b0_m=4;
a0_h=0.07; b0_h=1/(exp(3.)+1)
y0=[0; a0_n/(a0_n+b0_n); a0_m/(a0_m+b0_m); a0_h/(a0_h+b0_h)]
#
t0=0
tf=40

#
fcn=set_fcn(d,m,x->drift_f(x,Mu),(x,dw)->diff_g_mat(x,dw,sig),diff_g_vecs(sig), noise_type)
opt=set_opt(0.01,"SIBDF")
opt["do_plotting"]=true
opt["output_plot_type"]="path_plot"
opt["output_sel"]=[1,2]
#using PyPlot
println("Start sde_strong_soln")
tic();t,y,W=sde_strong_solution(fcn, linspace(t0,tf,100), y0, opt);run_time=toc()

#


module SetOps

import Base: (+), (-), (*), (|), (&)
export powerset, ($)

"""
For sets `A` and `B`, `A-B` is the set of all elements of `A`
that are not in `B`.
"""
(-)(A::Set, B::Set)::Set = setdiff(A,B)

"""
For sets `A` and `B`, `A+B` is the union of `A` and `B`.
"""
(+)(A::Set, B::Set)::Set= union(A,B)

"""
For sets `A` and `B`, `A|B` is the union of `A` and `B`.
"""
(|)(A::Set, B::Set)::Set = union(A,B)

"""
For sets `A` and `B`, `A&B` is the intersection of `A` and `B`.
"""
(&)(A::Set, B::Set)::Set = intersect(A,B)

"""
For sets `A` and `B`, `A\$B` is the symmetric difference of `A` and `B`.
"""
($)(A::Set, B::Set)::Set = symdiff(A,B)

"""
For sets `A` and `B`, `A*B` [also `A×B`] is the Cartesian product, i.e., the
set of all ordered pairs `(a,b)` with `a` chosen from `A` and
`b` chosen from `B`.
"""
(*)(A::Set{S}, B::Set{T}) where {S,T}=Set{Tuple{S,T}}((a,b) for a in A for b in B)
(×)(A::Set,B::Set) = A*B

"""
`powerset(A)` is the set of all subsets of the set `A`.
"""
function powerset(A::Set{T})::Set{Set{T}} where T
    if length(A) == 0
        return Set([Set{T}()])
    end

    a = first(A)
    AA = deepcopy(A)
    delete!(AA,a)

    P1 = powerset(AA)
    P2 = Set( union(X,Set([a])) for X in P1 )

    return union(P1,P2)
end

end


# 'cstrt_regular_ngon' provides the vertices coordinates (a,b) of the
# regular small n-gon
function cstrt_regular_ngon(n)
if n >= 3
    a = zeros(n-1); b = zeros(n-1);
    if mod(n,2) == 1
        for i = 1:n-1
            a[i] = sin(2*pi*i/n)/(2*cos(pi/(2*n)));
            b[i] = (1 - cos(2*pi*i/n))/(2*cos(pi/(2*n)));
        end
    else
        for i = 1:n-1
            a[i] = sin(2*pi*i/n)/2;
            b[i] = (1 - cos(2*pi*i/n))/2;
        end
    end
    return(a,b)
end
end


#!/usr/bin/env julia

## =========================== ## ## =========================== ##
## 	   Euler's and Heun's methods for numerical integration 	 ##
##	   Ollin Demian Langle Chimal					             ##
##	                         						             ##
##     09 / 23 / 2018									         ##
## =========================== ## ## =========================== ##

"""
    euler(x,h;fun,kwargs...)

Uses Euler method to integrate function fun at x with step h
"""
function euler(x,h;fun=lv,kwargs...)
    try
        f = fun(x;kwargs...)
        y = x + f * h
        return y
    catch
        return @. x + fun(x;kwargs...) * h
    end
end

"""
    heun(x,h;fun,kwargs...)

Uses Heun method to integrate function fun at x with step h
"""
function heun(x,h;fun=lv,kwargs...)
    try
        y1 = euler(x,h;fun=fun,kwargs...)
        y2 = fun(x;kwargs...) .+ fun(y1;kwargs...)
        yf = x .+ h * 0.5 * y2
        return yf
    catch
        y1 = euler(x,h;fun=fun,kwargs...)
        y2 = @. fun(x;kwargs...) + fun(y1;kwargs...)
        yf = @. x + h * 0.5 * y2
        return yf
    end
end

"""
    lv(x;a,b,c,d)

Classical 2D Lotka-Volterra model
"""
function lv(x;a,b,c,d)
    y = [a*x[1]-b*x[1]*x[2],c*x[1]*x[2]-d*x[2]]
    y
end

"""
    lot3(x;α)

Generalized Lotka-Volterra model for 3 species as in
Flake's The Computational Beauty of Nature book.
"""
function lot3(x;α=0.75)
    A = [0.5 0.5 0.1; -0.5 -0.1 0.1; α 0.1 0.1]
    y = zeros(length(x))
    for i in 1:length(x)
        y[i] = x[i] * (A[i,:]' * (1 .- x))
    end
    y
end

"""
    my_integrator(ti,tf,h,y0;fun,algo,kwargs...)

Integrates a function fun from ti to tf with step h and initial condition
y0 with algorithm algo.
"""
function my_integrator(ti,tf,h,y0;fun=lv,algo=euler,kwargs...)
    t = ti:h:tf |> collect
    y = zeros(length(t),length(y0))
    y[1,:] .= y0
    for i ∈ 2:size(y)[1]
        value = algo(y[i-1,:],h;fun=fun,kwargs...)
        if any(@. !isless(value,1e4))
            y = y[1:i-1,:]
            t = t[1:size(y)[1]]
            break
        end
        y[i,:] .= value
    end
    t,y
end

# Test the integration in a Lotka-Volterra 2 species system and for a regular
# function like cosine.

#  @time t,y = my_integrator(0,500,1/2^4,[2/3,0.9];fun=lv,algo=heun,a=0.5,b=1.2,c=1,d=0.8)
#  @time t,y = my_integrator(0,500,0.125,[2/3,0.9];fun=cos,algo=heun)


using NewtonsMethod
using Test
using LinearAlgebra, Statistics, Compat, ForwardDiff

@testset "NewtonsMethod.jl" begin
    #Several @test for the root of a known functions
    #1
    f1(x) = (x-1.0)^3
    f1′(x) = 3.0*(x-1.0)^2
    x1₀ = 0.1
    @test newtonroot(f1, f1′, x₀ = x1₀).root ≈ 0.9999998168636032
    @test newtonroot(f1,      x₀ = x1₀).root ≈ 0.9999998168636032
    @test newtonroot(f1, f1′, x₀ = BigFloat(x1₀), tolerance = 1E-80).root ≈ BigFloat(1.0)
    @test newtonroot(f1,      x₀ = BigFloat(x1₀), tolerance = 1E-80).root ≈ BigFloat(1.0)
    #2
    f2(x) = log(x)
    f2′(x) = 1.0/x
    x2₀ = 2.0
    @test newtonroot(f2, f2′, x₀ = x2₀).root ≈ 1.0
    @test newtonroot(f2,      x₀ = x2₀).root ≈ 1.0
    @test newtonroot(f2, f2′, x₀ = BigFloat(x2₀), tolerance = 1E-50).root ≈ BigFloat(1.0)
    @test newtonroot(f2,      x₀ = BigFloat(x2₀), tolerance = 1E-50).root ≈ BigFloat(1.0)
    #3
    f3(x) = (sin(x)/x)
    f3′(x) = (x*cos(x) - sin(x))/(x^2)
    x3₀ = 1.0
    @test newtonroot(f3, f3′, x₀ = x3₀).root ≈ 3.141592653589793
    @test newtonroot(f3,      x₀ = x3₀).root ≈ 3.141592653589793
    @test newtonroot(f3, f3′, x₀ = BigFloat(x3₀), tolerance = 1E-50).root ≈ pi
    @test newtonroot(f3,      x₀ = BigFloat(x3₀), tolerance = 1E-50).root ≈ pi
    #4
    f4(x) = (x+10.0)*(x^2+2.0)
    f4′(x) = 3x^2 + 20x + 2.0
    x4₀ = 10.0
    @test newtonroot(f4, f4′, x₀ = x4₀).root ≈ -10.0
    @test newtonroot(f4,      x₀ = x4₀).root ≈ -10.0
    @test newtonroot(f4, f4′, x₀ = BigFloat(x4₀), tolerance = 1E-100).root ≈ BigFloat(-10.0)
    @test newtonroot(f4,      x₀ = BigFloat(x4₀), tolerance = 1E-100).root ≈ BigFloat(-10.0)

    #Some @test of non-convergence
    #5
    f5(x) = x^2 + 2.0
    f5′(x) = 2x
    x5₀ = 10.0
    @test newtonroot(f5, f5′, x₀ = x5₀).root == nothing
    @test newtonroot(f5,      x₀ = x5₀).root == nothing
    @test newtonroot(f5, f5′, x₀ = BigFloat(x5₀), tolerance = 1E-100).root == nothing
    @test newtonroot(f5,      x₀ = BigFloat(x5₀), tolerance = 1E-100).root == nothing
    #6
    f6(x) = 2.0
    f6′(x) = 0.0
    x6₀ = 10.0
    @test newtonroot(f6, f6′, x₀ = x6₀).root == nothing
    @test newtonroot(f6,      x₀ = x6₀).root == nothing
    @test newtonroot(f6, f6′, x₀ = BigFloat(x6₀), tolerance = 1E-100).root == nothing
    @test newtonroot(f6,      x₀ = BigFloat(x6₀), tolerance = 1E-100).root == nothing

    #Some @test for maxiter
    #1
    @test newtonroot(f1, f1′, x₀ = x1₀, maxiter = 10).root == nothing
    @test newtonroot(f1,      x₀ = x1₀, maxiter = 10).root == nothing
    @test newtonroot(f1, f1′, x₀ = BigFloat(x1₀), tolerance = 1E-80, maxiter = 100).root == nothing
    @test newtonroot(f1,      x₀ = BigFloat(x1₀), tolerance = 1E-80, maxiter = 100).root == nothing
    #4
    @test newtonroot(f4, f4′, x₀ = x4₀, maxiter = 10).root == nothing
    @test newtonroot(f4,      x₀ = x4₀, maxiter = 10).root == nothing
    @test newtonroot(f4, f4′, x₀ = BigFloat(x4₀), tolerance = 1E-100, maxiter = 100).root ≈ BigFloat(-10.0)
    @test newtonroot(f4,      x₀ = BigFloat(x4₀), tolerance = 1E-100, maxiter = 100).root ≈ BigFloat(-10.0)

    #Some @test for tolerance and BigFloat
    #2
    @test !(newtonroot(f2, f2′, x₀ = x2₀, tolerance = 1E-2).root ≈ 1.0)
    @test !(newtonroot(f2,      x₀ = x2₀, tolerance = 1E-2).root ≈ 1.0)
    #3
    @test !(BigFloat(0.0) + newtonroot(f3, f3′, x₀ = x3₀, tolerance = 1E-50).root ≈ pi)
    @test !(BigFloat(0.0) + newtonroot(f3,      x₀ = x3₀, tolerance = 1E-50).root ≈ pi)
    @test !(newtonroot(f3, f3′, x₀ = BigFloat(x3₀), tolerance = 1E-10).root ≈ pi)
    @test !(newtonroot(f3,      x₀ = BigFloat(x3₀), tolerance = 1E-10).root ≈ pi)
end


let mode, n, A, p, k, A
# test two properties of oscillating matrix
# For an n x n oscillating matrix A, the following holds:
# 1. A has n distinct and postive eigenvalues λ_1 > λ_2 > ... > λ_n > 0
# 2. The ith eigenvector, corresponding to λ_i in the above ordering has
#    exactly i-1 sign changes.

n = 8 # rand(2:10)
@test_throws ArgumentError matrixdepot("oscillate", n, 4)
for mode = 1:2
A = matrixdepot("oscillate", n, mode)

eva, evc = eigen(A)
@test all([ei > 0 for ei in eva])

p = sortperm(eva, rev = true)
evc = evc[:,p]

# compute the number of sign change
function num_sign_change(v)
    signv = sign.(v)
    change = signv[1]
    num = 0
    for i in signv
        if i != change
            num += 1
            change = i
        end
    end
    return num
end

k = rand(1:n)
@test num_sign_change(evc[:,k]) == k - 1
end

@test matrixdepot("oscillate", n) !== nothing
end
println("'oscillate' passed test...")


# # Tutorial 1

# Crash course in Julia basics:

# Arithmetic, arrays, tuples, strings, dictionaries, functions,
# iteration, random numbers, package loading, plotting

# (40 min)

# ## Julia is a calculator:

1 + 2^3

#-

sqrt(1 + 2^3) # do `sqrt(ans)` in REPL

#-

sin(pi)

# Query a function's document string using `?sin` at the REPL, or in a
# notebook:

@doc sin

# I've forgotten how the arcsin is called. Is it `asin` or `arcsin`? I
# can search all doc-strings containing "sine" to locate the method:

apropos("sine")

# Okay, I see it's called `asin`.

asin(1 + 3*im)

# ## Arrays

# One dimensional vectors:

v = [3, 5, 7]

#-

length(v)

# A "row vector" is a 1 x n array:

row = [3 5 7]

#-

# Multiple row vectors separated by semicolons or new lines define matrices:

A = [3 5 7
     2 4 6
     1 3 5]

#-

size(A)

#-

length(A)

# Accessing elements (Julia indices start at 1 not 0):

A[1, 2]

#-

A[1, 2] == A[2]

#-

A[:, 2] # 2nd column

# Changing elements:

A[1, 1] = 42

#-

inv(A) # inverse

#-

isapprox(inv(A)*v, A\v) # but RHS more efficient

# ## "Variables" in Julia *point* to objects

# Corollary: all passing of function arguments is pass by reference

# Like Python; Unlike R, C or FORTRAN.

#-

w = v

w

#-

v[1] = 42

#-

v

#-

w

#

# ## Tuples

# Similar to vectors but of fixed length and immutable (cannot be changed)

t1 = (1, 2.0, "cat")
typeof(t1)

#-

t1[3]

# Tuples also come in a *named* variety:

t2 = (i = 1, x = 2.0, animal="cat")

#-

t2.x

# ## Strings and relatives

a_string = "the cat"
a_character = 't'
a_symbol = :t

#-

a_string[1] == a_character

# A `Symbol` is string-like but
# [interned](https://en.wikipedia.org/wiki/String_interning). Generally
# use `String` for ordinary textual data, but use `Symbol` for
# language reflection (metaprogramming). For example:

isdefined(Main, :z)

#-

z = 1 + 2im
isdefined(Main, :z)

#-

z.im

#-

fieldnames(typeof(z))

# Symbols are generalized by *expressions*:

ex = :(z == 3)
eval(ex)

# If this is confusing, forget it for now.

# ## Dictionaries

d = Dict('a' => "ant", 'z' => "zebra")

#-

d['a']

#-

d['b'] = "bat"
d

#-

keys(d)

# The expression 'a' => "ant" is itself a stand-alone object:

pair = 'a' => "ant"

first(pair)

# ## Functions

# Three ways to define a generic function:

foo(x) = x^2 # METHOD 1 (inline)
foo(3)

# or

3 |> foo

# or

3 |> x -> x^2 # METHOD 2 (anonymous)

# or

function foo2(x) # METHOD 3 (verbose)
    y = x
    z = y
    w = z
    return w^2
end

foo2(3)

# ## Basic iteration

# Here are three ways to square the integers from 1 to 10.

# METHOD 1 (explicit loop):

squares = [] # or Int[] if performance matters
for x in 1:10
    push!(squares, x^2)
end

squares

# METHOD 2 (comprehension):

[x^2 for x in 1:10]

# METHOD 3 (map):

map(x -> x^2, 1:10)

# METHOD 4 (broadcasting with dot syntax):

(1:10) .^ 2

# ## Random numbers

typeof(2)

#-

rand() # sample a Float64 uniformly from interval [0, 1]

#-

rand(3, 4) # do that 12 times and put in a 3 x 4 array

#-

randn(3, 4) # use normal distribution instead

#-

rand(Int8) # random elment of type Int8

#-

rand(['a', 'b', 'c'], 10) # 10 random elements from a vector

# Some standard libraries are needed to do more, for example:

using Random

#-

randstring(30)

#-

using Statistics  # part of standard library

#-

y = rand(30)
@show mean(y) quantile(y, 0.75);

# (Use the macro @show before stuff you want printed prefixed by
# *what* it is that is being printed.)

# For sampling from more general distributions we need
# Distributions.jl package which is not part of the standard library.

# ## Loading packages

# If not in the REPL:

using Pkg                        # built-in package manager
Pkg.activate("env", shared=true) # create a new pkg env

# Add some packages to your environment (latest compatible versions
# added by default):

Pkg.add("Distributions")
Pkg.add("Plots")

# To load the code for use:

using Distributions
using Plots

N = 1000
samples = rand(Normal(), N);   # equivalent to Julia's built-in `randn(d)`
samples = (samples).^2;        # square element-wise

#-

g = fit(Gamma, samples)

#-

@show mean(g) median(g) pdf(g, 1)

# ## Plotting

f(x) = pdf(g, x)

xs = 0:0.1:4 # floats from 0 to 4 in steps of 0.1
ys = f.(xs)  # apply f element-wise to xs

plot(xs, ys, xrange=(0,4), yrange=(0,0.2))
histogram!(samples , normalize=true, alpha=0.4)

#-

savefig("my_first_plot.png")

# # Exercises

# ## Exercise 1

# Write a function named `total` that adds the elements of its vector input.

# ## Exercise 2

# Generate a 1000 random samples from the standard normal
# distribution. Create a second such sample, and add the two samples
# point-wise.  Compute the (sample) mean and variance of the combined
# samples. In the same
# plot, show a frequency-normalized histogram of the combined samples
# and a plot of the pdf for normal distribution with zero mean and
# variance `2`.

# You can use `std` to compute the standard deviation and `sqrt` to
# compute square roots.

# ## Exercise 3

# The following shows that named tuples share some behaviour with dictionaries:

t = (x = 1, y = "cat", z = 4.5)
keys(t)

#-

t[:y]

#-

# Write a function called `dict` that converts a named tuple to an
# actual dictionary. You can create an empty dictionary using `Dict()`.


@doc raw"""
    GammaExponentialKernel([α=1 [,γ=1]])

The gamma exponential kernel is a generalization of the exponential and squared exponential
kernels:

```math
\kappa(\mathbf{x},\mathbf{y}) = \exp\left(-\alpha ||\mathbf{x} - \mathbf{y}||^{\gamma}
\right) \qquad \alpha > 0, \; 0 < \gamma \leq 1
```
where ``\alpha`` is a scaling parameter and ``\gamma`` is a shape parameter.
"""
struct GammaExponentialKernel{T<:AbstractFloat} <: MercerKernel{T}
    alpha::HyperParameter{T}
    gamma::HyperParameter{T}
    GammaExponentialKernel{T}(α::Real, γ::Real) where {T<:AbstractFloat} = new{T}(
        HyperParameter(convert(T,α), interval(OpenBound(zero(T)), nothing)),
        HyperParameter(convert(T,γ), interval(OpenBound(zero(T)), ClosedBound(one(T))))
    )
end
function GammaExponentialKernel(α::T1=1.0, γ::T2=one(T1)) where {T1<:Real,T2<:Real}
    GammaExponentialKernel{floattype(T1,T2)}(α,γ)
end

@inline gammaexponentialkernel(z::T, α::T, γ::T) where {T<:AbstractFloat} = exp(-α*z^γ)

@inline basefunction(::GammaExponentialKernel) = SquaredEuclidean()
@inline function kappa(κ::GammaExponentialKernel{T}, z::T) where {T}
    gammaexponentialkernel(z, getvalue(κ.alpha), getvalue(κ.gamma))
end