"""
    Fronts.ϕ(r, t)

Evaluate the Boltzmann variable ϕ at position `r` and time `t`.

Type `\\phi<tab>` to obtain the `ϕ` symbol.

The Boltzmann variable is defined as `ϕ=r/√t` and makes the Boltzmann transformation possible.

To prevent possible name clashes, this function is not exported.

See also: [`transform`](@ref)
"""
ϕ(r, t) = r/√t

"""
    ∂ϕ_∂r(r, t)

Partial derivative of the Boltzmann variable.

Type `\\partial<tab>` to obtain the `∂` symbol; `\\phi<tab>` to obtain the `ϕ` symbol.

See also: [`ϕ`](@ref)
"""
∂ϕ_∂r(r, t) = 1/√t

"""
    ∂ϕ_∂t(r, t)

Partial derivative of the Boltzmann variable.

Type `\\partial<tab>` to obtain the `∂` symbol; `\\phi<tab>` to obtain the `ϕ` symbol.

See also: [`ϕ`](@ref)
"""
∂ϕ_∂t(r, t) = -ϕ(r,t)/2t

"""
    Fronts.r(ϕ, t)

Convert back from the Boltzmann variable to `r`.

To prevent possible name clashes, this function is not exported.

See also: [`ϕ`](@ref)
"""
r(ϕ, t) = ϕ*√t

"""
    Fronts.t(ϕ, r)

Convert back from the Boltzmann variable to `t`.

To prevent possible name clashes, this function is not exported.

See also: [`ϕ`](@ref)
"""
t(ϕ, r) = (r/ϕ)^2

"""
    transform(r, t)

Same as `ϕ(r,t)`.

See also: [`ϕ`](@ref)
"""
transform(r, t) = ϕ(r,t)

"""
    TransformedFunction

Abstract type for functions of the Boltzmann variable ϕ.

Every subtype of `TransformedFunction` gets access to the following methods:

    (::TransformedFunction)(r, t)
    d_dϕ(::TransformedFunction, r, t)
    ∂_∂r(::TransformedFunction, r, t)
    ∂_∂t(::TransformedFunction, r, t)

# Implementation

In order to access the previous methods, a type `T <: TransformedFunction` must define these methods:

    (::T)(ϕ)
    d_dϕ(::T, ϕ)
"""
abstract type TransformedFunction end

(f::TransformedFunction)(r, t) = f(ϕ(r,t))

d_dϕ(f::TransformedFunction, r, t) = d_dϕ(f, ϕ(r,t))

∂_∂r(f::TransformedFunction, r, t) = d_dϕ(f, r, t)*∂ϕ_∂r(r, t)

∂_∂t(f::TransformedFunction, r, t) = d_dϕ(f, r, t)*∂ϕ_∂t(r, t)


"""
	 exponential_search(arr::AbstractArray{T,1}, x::T) where {T <: Real}

Exponential Search in 1-D array
Time Complexity:  O(Log n)
# Exponential Search
It works in O(Log n) time
Exponential search involves two steps:
- Find range where element is present
- Do Binary Search in above found range.
### Time Complexity :
O(Log n)
Applications of Exponential Search:
Exponential Binary Search is particularly useful for unbounded searches, where size of array is infinite. Please refer Unbounded Binary Search for an example.
It works better than Binary Search for bounded arrays, and also when the element to be searched is closer to the first element.

Contributed By:- [Ash](https://github.com/ashwani-rathee)
"""
function exponential_search(arr::AbstractArray{T,1}, x::T) where {T<:Real}
    n = size(arr)[1]
    if (arr[1] == x)
        return "Element present at index 1"
    end

    i = 1
    while (i < n && arr[i] <= x)
        i = i * 2
    end
    return binary_search(arr, Int(ceil(i / 2)), min(i, n), x)
end


####
#### Unregularized NNLS problem
####

struct NNLSProblem{T, MC <: AbstractMatrix{T}, Vd <: AbstractVector{T}, W}
    C::MC
    d::Vd
    m::Int
    n::Int
    nnls_work::W
end
function NNLSProblem(C::AbstractMatrix{T}, d::AbstractVector{T}) where {T}
    m, n = size(C)
    nnls_work = NNLS.NNLSWorkspace(C, d)
    NNLSProblem(C, d, m, n, nnls_work)
end

function solve!(work::NNLSProblem, C, d)
    # Solve NNLS problem
    NNLS.load!(work.nnls_work, C, d)
    NNLS.nnls!(work.nnls_work)
    return solution(work)
end
solve!(work::NNLSProblem) = solve!(work, work.C, work.d)

solution(work::NNLSProblem) = NNLS.solution(work.nnls_work)

chi2(work::NNLSProblem) = NNLS.residualnorm(work.nnls_work)^2

"""
    lsqnonneg(C::AbstractMatrix, d::AbstractVector)

Returns the nonnegative least-squares (NNLS) solution, X, of the equation:

```math
X = \\mathrm{argmin}_{x \\ge 0} ||Cx - d||_2^2
```

# Arguments
- `C::AbstractMatrix`: Left hand side matrix acting on `x`
- `d::AbstractVector`: Right hand side vector

# Outputs
- `X::AbstractVector`: NNLS solution
"""
lsqnonneg(C, d) = lsqnonneg!(lsqnonneg_work(C, d))
lsqnonneg_work(C, d) = NNLSProblem(C, d)
lsqnonneg!(work::NNLSProblem) = solve!(work)
lsqnonneg!(work::NNLSProblem{T}, C::AbstractMatrix{T}, d::AbstractVector{T}) where {T} = solve!(work, C, d)


"""
Returns x^n mod p
"""
function exp!(x::BigInt, n::BigInt, p::BigInt = big(1000007))
    ans = 1
    x = x % p

    while n > 0
        if ((n & 1) != 0)
            ans = (ans * x) % p
        end
        x = (x * x) % p
        n = n >> 1
    end
    ans
end

exp!(x::T, n::T, p::T = 1000007) where {T <: Integer} =
exp!(big(x), big(n), big(p))

"""
Modular multiplicative inverse

a: Number whose inverse is to be found
n: modular base
"""
function inverse(a::BigInt, n::BigInt)
    g, x, _ = gcdx(a, n)

    if (g != 1)
        error("The inverse doesn't exist")
    end
    (x % n + n) % n
end

inverse(a::T, n::T) where {T <: Integer} =
inverse(big(a), big(n))

"""
Seive of Eratosthenes

Returns primes less than n
"""
function seive(n::BigInt)
    seive_list = ones(Bool, n)

    for i in 2:trunc(BigInt, sqrt(n))
        if seive_list[i] == true
           for j in (i*i):i:n
                seive_list[j] = false
           end
        end
   end
    filter(x -> seive_list[x] == true, 2:n)
end

seive(n::T) where {T <: Integer} =
seive(big(n))

"""
Euler totient function of n

phi!(n) = number of positive Numbers co-prime with n.
n: Int
"""
function phi!(n::Number)
    result = n
    p = 2

    while p * p <= n
        if (n % p == 0)
            while n % p == 0
                n /= p
            end
            result -= result / p
        end
        p += 1
    end

    if (n > 1)
        result -= result / n
    end
    Int(result)
end

"""
Returns a random Number of size bit_size bits
"""
function get_random_int(bit_size::T) where {T <: Integer}
    bit_vector = bitrand(bit_size)
    sum([(2 ^ BigInt((i - 1))) * bit for (i, bit) in enumerate(reverse(bit_vector))])
end

"""
Returns the Jacobi symbol
If b is prime, it returns the Legendre Symbol
"""
function jacobi!(a::BigInt, b::BigInt)
    if (b <= 0)
        error("b must be >= 1")
    end
    if (b & 1 == 0)
        error("b must be odd")
    end

    ans = 1
    if (a < 0)
        a = -a
        if (b % 4 == 3)
            ans = -ans
        end
    end

    while a != 0
        while a % 2 == 0
            a /= 2
            if ((b % 8) in [3, 5])
                ans = -ans
            end
        end
        a, b = b, a
        if a % 4 == 3 && b % 4 == 3
            ans = -ans
        end
        a = a % b
    end

    if (b == 1)
        return (ans)
    end
    0
end

jacobi!(a::T, b::T) where {T <: Integer} =
jacobi!(big(a), big(b))

"""
Generates a large a prime number by incremental search
"""
function generate_large_prime(bit_size::T, primality_test::Function = miller_rabin) where {T <: Integer}
    p = get_random_int(bit_size)

    if (p & 1 != 1)
        p += 1
    end
    while primality_test(p) == false
        p += 2
    end
    p
end

"""
Easier to convert Int to bytes
"""
to_bytes(x) = reinterpret(UInt8, x)


#Factorials and elementary coefficients

export
    derangement,
    partialderangement,
    factorial,
    subfactorial,
    doublefactorial,
    hyperfactorial,
    multifactorial,
    gamma,
    primorial,
    multinomial

# TODO: This should really live in Base, otherwise it's type piracy
"""
    factorial(n, k)

Compute ``n!/k!``.
"""
function Base.factorial(n::T, k::T) where T<:Integer
    if k < 0 || n < 0 || k > n
        throw(DomainError((n, k), "n and k must be nonnegative with k ≤ n"))
    end
    f = one(T)
    while n > k
        f = Base.checked_mul(f, n)
        n -= 1
    end
    return f
end
Base.factorial(n::Integer, k::Integer) = factorial(promote(n, k)...)

"""
    derangement(n)

Compute the number of permutations of `n` with no fixed points, also known as the
subfactorial. An alias `subfactorial` for this function is provided for convenience.
"""
function derangement(sn::Integer)
    n = BigInt(sn)
    return numerator(factorial(n) * sum([(-1)^k // factorial(k) for k = 0:n]))
end
const subfactorial = derangement

function doublefactorial(n::Integer)
    if n < 0
        throw(DomainError(n, "n must be nonnegative"))
    end
    z = Ref{BigInt}(0)
    ccall((:__gmpz_2fac_ui, :libgmp), Cvoid, (Ref{BigInt}, UInt), z, UInt(n))
    return z[]
end

"""
    partialderangement(n, k)

Compute the number of permutations of `n` with exactly k fixed points.
"""
function partialderangement(n::Integer, k::Integer)
    if n < 0
        throw(DomainError(n))
    end
    if k < 0 || k > n
        throw(DomainError(k))
    end
    a = BigInt(n)
    b = BigInt(k)
    return subfactorial(a - b) * binomial(a, b)
end

# Hyperfactorial
hyperfactorial(n::Integer) = prod(i->i^i, BigInt(2):n)

function multifactorial(n::Integer, m::Integer)
    if n < 0
        throw(DomainError(n, "n must be nonnegative"))
    end
    z = Ref{BigInt}(0)
    ccall((:__gmpz_mfac_uiui, :libgmp), Cvoid, (Ref{BigInt}, UInt, UInt), z, UInt(n), UInt(m))
    return z[]
end

function primorial(n::Integer)
    if n < 0
        throw(DomainError(n, "n must be nonnegative"))
    end
    z = Ref{BigInt}(0)
    ccall((:__gmpz_primorial_ui, :libgmp), Cvoid, (Ref{BigInt}, UInt), z, UInt(n))
    return z[]
end

"""
    multinomial(k...)

Multinomial coefficient where `n = sum(k)`.
"""
function multinomial(k...)
    s = 0
    result = 1
    @inbounds for i in k
        s += i
        result *= binomial(s, i)
    end
    result
end


using LinearAlgebra

function count_darts(N::Int)
  num_inside = 0
  for i=1:N
    p = rand(2)
    num_inside += norm(p) <= 1
  end
  return num_inside
end

count_darts(10000)

## Now make this work in arbitrary dimensions
using LinearAlgebra
function count_darts(N::Int, k::Int)
  num_inside = 0
  for i=1:N
    p = rand(k)
    num_inside += norm(p) <= 1
  end
  return num_inside
end
count_darts(10000, 2) # check our estimate
##
for k=3:10
  n = count_darts(10000, k)
  println(k, " -> ", n)
end
##


#=
  Bertrand's paradox

  From https://towardsdatascience.com/five-paradoxes-with-probabilities-that-will-puzzle-you-2f71201d6ee8
  """
  3. Bertrand's Box Paradox

  If you are familiar with the Montey Hall problem, this paradox is quite
  similar. In front of us there are three boxes:

    [image of three boxes]

  One box contains two silver coins, one box contains two gold coins and
  one box contains a gold- and a silver coin. We do not know which coins
  are in which box. Now, we pick a random box and blindly draw a coin from
  our box. It’s a gold coin!

  Now, the question is:

    What’s the probability that the second coin in our box is also a gold coin?

  My naive (and wrong) answer when encountering the problem for the first
  time was ½. I thought that because we drew a gold coin, our box is either
  the one with the two gold coins or the one with the mixed coins. In the
  first case, we would draw another gold coin and in the second case, we
  wouldn’t. Therefore, I presumed the probability should be ½.

    The real probability is ⅔.

  The reason for that is that the first gold coin we drew could either be
  the only gold coin in the mixed box, the first gold coin in the solely
  golden box, or the second gold coin in the solely golden box. And in
  two of these three possibilities, we will draw another gold coin.

  """

  Here we define the boxes as:
  - box1: 2 gold coins
  - box2: 1 gold and 1 silver coin
  - box3: 2 silver coins

  Distributions of variable coin1
  gold       =>    1000  (1.000000)
  Distributions of variable coin2
  gold       =>     668  (0.668000)
  silver     =>     332  (0.332000)
  Distributions of variable box
  b1         =>     668  (0.668000)
  b2         =>     332  (0.332000)

  I.e. probability that the second coin is also gold is 0.668000 (~ 2/3).

  Cf ~/blog/bertrands_paradox.blog
      ~/psi/bertrands_paradox.psi

=#

using Turing, StatsPlots, DataFrames
include("jl_utils.jl")

# We observe that the first coin is a gold coin
@model function bertrands_paradox(coin1=1)
    # There are 3 boxes.
    b1 = 1
    b2 = 2
    b3 = 3

    # We select one of the boxes
    box ~ DiscreteUniform(1,3)

    # the coins
    gold = 1
    silver = 2

    # We draw one coin:
    coin1 ~ (box == b1) ? Categorical(simplex([2.0, 0.0])) :
            (box == b2) ? Categorical(simplex([1, 1])) :
            Categorical(simplex([0.0, 2.0]))

    # It's a gold coin.
    # true ~ Dirac(coin1 == gold)

    # What is the probability that the second coin
    # in the selected box is also a gold coin?
    coin2 ~ (box == b1) ? Categorical(simplex([1.0, 0.0])) :
            (box == b2) ? Categorical(simplex([0.0, 1.0])) :
            Categorical(simplex([0.0, 1.0]))

end

model = bertrands_paradox()
num_chns = 4

# chns = sample(model, Prior(), 10_000, num_chns)
# chns = sample(model, MH(), 10_000)
chns = sample(model, PG(5), 1_000)
# chns = sample(model, SMC(), 10_000)
# chns = sample(model, IS(), 10_000)

display(chns)

show_var_dist_pct(chns, :coin1,["gold","silver"])
show_var_dist_pct(chns, :coin2,["gold","silver"])
show_var_dist_pct(chns, :box,["b1","b2","b3"])


## Polynomial root finder for polynomials with multiple roots
##
## Based on "Computing multiple roots of inexact polynomials"
## http://www.neiu.edu/~zzeng/mathcomp/zroot.pdf
## Author: Zhonggang Zeng
## Journal: Math. Comp. 74 (2005), 869-903
##
## Zeng has a MATLAB package `multroot`, from which this name is derived.
## Basic idea is
## 1) for polynomial p we do gcd decomposition p = u * v; p' = u * w. Then roots(v) are the roots without multiplicities.
## 2) can repeat with u to get multiplicities.
##
## This is from Gauss, as explained in paper. Zeng shows how to get u,v,w when the polynomials
## are inexact due to floating point approximations or even model error. This is done in his
## algorithm II.
## 3) Zeng's algorithm I (pejroot) uses the pejorative manifold of Kahan and Gauss-Newton to
## improve the root estimates from algorithm II (roots(v)). The pejorative manifold is defined by
## the multiplicities l and is operationalized in evalG and evalJ from Zeng's paper.

using Polynomials
import Polynomials: degree

## map monic(p) to a point in C^n
## p = 1x^n + a1x^n-1 + ... + an_1 x + an -> (a1,a2,...,an)
function p2a(p::Poly)
    p = monic(p)
    rcoeffs(p)[2:end]
end

## get value of gl(z). From p16
function evalG(zs::Vector, ls::Vector)
    length(zs) == length(ls) || throw("Length mismatch")

    s = prod([poly([z])^l for (z,l) in zip(zs, ls)])  # \prod (x-z_i)^l_i
    p2a(s)
#    rcoeffs(s)[2:end]
end

## get jacobian J_l(z), p16
function evalJ(zs::Vector, ls::Vector)
    length(zs) == length(ls) || throw("Length mismatch")
    m = length(zs)

    u = prod([poly([z])^(l-1) for (z,l) in zip(zs, ls)]) ## Pi (1-z)^(l-1)

    J = zeros(eltype(zs), sum(ls), m)
    for j in 1:m
        s = -ls[j] * u
        for i in 1:m
            if i != j
                s = s * poly([zs[i]])
            end
        end
        J[:,j] = rcoeffs(s)
    end
    J
end

## Gauss-Newton iteration to solve weighted least squares problem
## G_l(z) = a, where a is related to monic version of polynomial p
## l is known multiplicity structure of polynomial p = (x-z1)^l1 * (x-z2)^l2 * ... * (x-zn)^ln
## Algorithm I, p17
function pejroot(p::Poly, z0::Vector, l::Vector{Int};
                 wts::(@compat Union{Vector, Void})=nothing, # weight vector
                 tol = 1e-8,
                 maxsteps = 100
                      )

    a = p2a(p) #rcoeffs(monic(p))[2:end] # an_1, an_2, ..., a2, a1, a0

    if wts == nothing
        wts = map(u -> min(1, 1/abs(u)), a)
    end
    W = diagm(wts)

    ## Solve WJ Δz = W(Gl(z) - a) in algorithm I
    G(z) = (evalG(z, l) - a)
    update(z, l) = z -  weighted_least_square(evalJ(z,l), G(z), wts)

    zk = copy(z0); zk1 = update(zk, l)
    deltaold = norm(zk1 - zk,2); zk = zk1

    cvg = false
    for ctr in 1:maxsteps
        zk1 = update(zk, l)
        delta = norm(zk1 - zk, 2)

        if delta > deltaold
            println("Growing delta. Best guess is being returned.")
            break
        end

        ## add extra abs(delta) < 100*eps() condition
        if delta^2 / (deltaold - delta) < tol || abs(delta) < 100*eps()
            cvg = true
            break
        end

        deltaold = delta
        zk=zk1
    end

    if !cvg println("""
Returning the initial estimates, as the
algorithm failed to improve estimates for the roots on the given
pejorative manifold.
""")
        return(z0)
    end
    return(zk1)
end

## Main interface to finding roots of polynomials with multiplicities
##
## The `multroot` function returns the roots and their multiplicities
## for `Poly` objects. It performs better than `roots` if the
## polynomial has multiplicities.
##
## julia> x = poly([0.0]);
## julia> p = (x-1)^4 * (x-2)^3 * (x-3)^3 * (x-4)l
## julia> multroot(p)
## ([1.0,2.0,3.0,4.0],[4,3,3,1])
## ## For "prettier" printing, results can be coerced to a dict
## julia> [k => v for (k,v) in zip(multroot(p)...)]
## Dict{Any,Int64} with 4 entries:
##   1.0000000000000007 => 4
##   3.000000000000018  => 3
##   1.9999999999999913 => 3
##   3.999999999999969  => 1
## ## Large order polynomials prove difficult. We can't match the claims in Zeng's paper
## ## as we don't get the pejorative manifold structure right.
## julia> p = poly([1.0:10.0]);
## julia> multroot(p) ## should be 1,2,3,4,...,10 all with multplicity 1, but
## ([1.0068,2.14161,3.63283,5.42561,7.25056,8.81228,9.98925],[1,2,1,2,2,1,1])
##
## nearby roots can be an issue
## julia> delta = 0.0001  ## delta = 0.001 works as desired.
## julia> p = (x-1 - delta)*(x-1)*(x-1 + delta)
## julia> multroot(p)
## ([0.999885,1.00006],[1,2])
function multroot(p::Poly;
                  θ::Real=1.0,   # singular threshold, 1.0 is replaced by normf(A)*eps()
                  ρ::Real=1e-10, # initial residual tolerance
                  ϕ::Real=1e2,   # residual tolerance growth factor
                  δ::Real=1e-8   # passed to solve y sigma

                  )

    degree(p) == 0 && error("Degree of `p` must be atleast 1")

    if degree(p) == 1
        return(roots(p), [1])
    end

    ## if degree(p) == 2
    ##     a,b,c = coeffs(p)
    ##     discr = b^2 - 4a*c
    ##     if discr < 0
    ##         discr = Complex(discr, 0)
    ##     end
    ##     return  ( -2c / (-b - sqrt(discr)), -2c/(-b + sqrt(discr)))
    ## end

    p = Poly(float(coeffs(p)))  # floats, not Int

    u_j, v_j, w_j, residual= agcd(p, p',  ρ = ρ)
    ρ = max(ρ, ϕ * residual)

    ## bookkeeping
    zs = roots(v_j)
    ls = ones(Int, length(zs))

    p0 = u_j

    while degree(p0) > 0
        if degree(p0) == 1
            z = roots(p0)[1]
            _, ind = findmin(abs(zs .- z))
            ls[ind] = ls[ind] + 1
            break
        end

        u_j, v_j, w_j, residual= agcd(p0, p0', ρ=ρ)

        ## need to worry about residual between
        ## u0 * v0 - monic(p0) and u0 * w0 - monic(Polynomials.polyder(p0))
        ## resiudal tolerance grows with m, here it depends on
        ## initial value and previous residual times a growth tolerance, ϕ
        ρ = max(ρ, ϕ * residual)

        ## update multiplicities
        for z in roots(v_j)
            _, ind = findmin(abs(zs .- z))
            ls[ind] = ls[ind] + 1
        end

        ## rename
        p0 = u_j
    end

    if maximum(ls) == 1
        return(zs, ls)
    else
        zs = pejroot(p, zs, ls)
        return(zs, ls)
    end
end

## Different interfaces

## can pass in vector too
multroot{T <: Real}(p::Vector{T}; kwargs...) = multroot(Poly(p); kwargs...)

## Can pass in function
function multroot(f::Function; kwargs...)
    p = Poly([0.0])
    try
        p = convert(Poly{Float64}, f)
    catch err
        error("The function does not compute a univariate polynomial")
    end
    multroot(p; kwargs...)

end

## add funciton interface to Polynomials.roots
Polynomials.roots(f::Function) = roots(convert(Poly{Float64}, f))


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*x))/d) + a + exp(1)
end

booth(x) = (x[1] + 2*x[2] - 7)^2 + (2*x[1] + x[2] -5)^2

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 flower(x; a=1, b=1, c=4)
    return a*norm(x) + b*sin(c*atan(x[2], x[1]))
end

function michalewicz(x; m=10)
    return -sum(sin(xᵢ)*sin(i*xᵢ^2/π)^(2m) for (i, xᵢ) in enumerate(x))
end

rosenbrock(x; a=1, b=100) = (a - x[1])^2 + b*(x[2] - x[1]^2)^2

wheeler(x, a=1.5) = -exp(-(x[1]*x[2] - a)^2 - (x[2] - a)^2)

function circle(x)
    θ = x[1]
    r = 0.5 + 0.5 * (2*x[2]/(1 + x[2]^2))
    return [1-r*cos(θ); 1-r*sin(θ)]
end


using LinearAlgebra

#Givens from Problem Statement
#Shear Modulus(psi),diameter(in),length(in),torque(lb*in)
G13 = 9.8*10^6
G2 = 11.2*10^6
d13 = 1.5
d2 = 1
l13 = 2*12
l2 = 1.5*12
τ = 2400

# Polar Second Area Moment of a Cylinder (in^4)
J_cyl(d) = 0.5*pi*(d/2)^4
J13 = J_cyl(d13)
J2 = J_cyl(d2)

# Torsional Stiffness Function
k(G,J,l) = (G*J)/l

# k_local(Elastic Modulus,thickness,height,length,number of nodes,
# row/column one,row/column two, ==1 left fixed/ ==2 right fixed)
function k_local(G,J,l,i,n1,n2,fixed=0)
    k_global = zeros(i,i)
    kin = k(G,J,l)
    k_global[n1,n1] = kin
    k_global[n1,n2] = -kin
    k_global[n2,n1] = -kin
    k_global[n2,n2] = kin
    if fixed == 1
      adj = zeros(1,i)
      adj[n1] = 1
      k_global[n1,:] = adj
      k_global[:,n1] = adj
    elseif fixed == 2
      adj = zeros(1,i)
      adj[n2] = 1
      k_global[n2,:] = adj
      k_global[:,n2] = adj
    end
    return k_global
end

# Global for angular displacement calculation
k_1 = k_local(G13,J13,l13,4,1,2,1)
k_2 = k_local(G2,J2,l2,4,2,3)
k_3 = k_local(G13,J13,l13,4,3,4,2)
kG = k_1+k_2+k_3

#Global for Reaction calculation
k_11 = k_local(G13,J13,l13,4,1,2)
k_22 = k_local(G2,J2,l2,4,2,3)
k_33 = k_local(G13,J13,l13,4,3,4)
kGG = k_11+k_22+k_33

# Torque Vector
t = Float64[0 0 τ 0]
T = transpose(t)

# Angular Displacement Vector
θ = kG\T
println("Nodal Angular Displacement Vector (radians)")
display(θ)

# Moment Reaction Vector
τ_app = kGG*θ
println("Moment Reaction Vector (lb*in):")
display(τ_app)


# Fermat's primality test: (Test for a number to be prime or not, based on Fermat's little theorem)
function fermatPrimalityTest(n, iterations = 5)
    if n < 4
        return n == 2 || n == 3
    end

    for i = 1:iterations
        a = rand(2:(n - 2), 1)[1]
        if ((BigInt(a) ^ (n - 1)) % n) != 1
            return false
        end
    end
    return true
end

# Fermat's factorization: (Representation of an odd integer as the difference of two squares)
function fermatFactorization(n::Int64)
    a = ceil(sqrt(n))
    b2 = a * a - n
    b = round(sqrt(b2))
    while (b * b != b2)
        a = a + 1
        b2 = a * a - n
        b = round(sqrt(b2))
    end
    return Int64(a - b)
end


# In England the currency is made up of pound, £, and pence, p, and there are
# eight coins in general circulation:
#
# 1p, 2p, 5p, 10p, 20p, 50p, £1 (100p) and £2 (200p).
# It is possible to make £2 in the following way:
#
# 1×£1 + 1×50p + 2×20p + 1×5p + 1×2p + 3×1p
# How many different ways can £2 be made using any number of coins?

using ProjectEulerSolutions

# Recursive solution count coin combinations.  In general, the idea is to
# consider each coin type and see how many ways there are to make a sum with
# that coin and lower denominations.  Different solutions can solve the
# recursion in slightly different ways.  Memoization is used to store lower
# level computatons.

# Uses a double recursion.
function p031solution_double(units::Array{Integer}=ones(Integer, 1), value::Integer=5)::Integer
    cache = zeros(Integer, value, length(units))
    return countcoins2(cache, units, length(units), value)
end

# Uses a single recursion, makes less total recursive calls.
function p031solution_single(units::Array{Integer}=ones(Integer, 1), value::Integer=5)::Integer
    cache = zeros(Integer, value, length(units))
    return countcoins1(cache, units, length(units), value)
end

# Uses no recursion, solution from https://projecteuler.net/thread=31.
function p031solution_dp(units::Array{Integer}=ones(Integer, 1), value::Integer=5)::Integer
    ways = zeros(Integer, value)
    ways[1] = 1
    for coin in units
        for j in (coin+1):value
            ways[j] += ways[j - coin]
        end
    end
    return ways[value]
end

# Most straightforward recursive solution.
function countcoins2(cache::Array{Integer,2}, units::Array{Integer}, coin::Integer, amount::Integer)::Integer
    if amount == 0 # Coin divides equally, add 1
        return 1
    end
    if coin < 1 || amount < 0 # Coins don't divide equally
        return 0
    end
    if cache[amount, coin] > 0
        return cache[amount, coin]
    end

    # Sum of solutions without coin and with coin
    val = countcoins2(cache, units, coin - 1, amount) +
          countcoins2(cache, units, coin, amount - units[coin])
    cache[amount, coin] = val
    return val
end

# Simple refactoring, less total recursive calls.
function countcoins1(cache::Array{Integer,2}, units::Array{Integer}, coin::Integer, amount::Integer)::Integer
    if amount == 0
        return 1
    end
    if cache[amount, coin] > 0
        return cache[amount, coin]
    end
    res = 0
    for i in coin:-1:2
        if amount - units[i] >= 0
            res += countcoins1(cache, units, i, amount - units[i])
        end
    end

    # Account for smallest coin evenly dividing into amount
    if amount % units[1] == 0
        res += 1
    end

    cache[amount, coin] = res
    return res
end

p031 = Problems.Problem(Dict("Double recusion" => p031solution_double,
                             "Single recusion" => p031solution_single,
                             "Dynamic programming" => p031solution_dp))

Problems.benchmark(p031, convert(Array{Integer}, [1, 2, 5, 10, 20, 50, 100, 200]), 200)


### related to signal analysis/synthesis, geometric transforms.

function rad2freq(r,fs)
    fn = fs/2
    #r/π = x/fn, x is in Hz.
    return fn*r/π
end

function freq2rad(x,fs)
    fn = fs/2

    return x*π/fn
end

# evaluates H(e^{j*ω}).
function computeDTFTviaformula(h,ω)
    N = length(h)
    return sum( h[n+1]*exp(-im*ω*n) for n = 0:N-1 )
end
# # test code
# ω = 99
# h = randn(5)
# Hn = h[1] + h[2]*exp(-im*ω) + h[3]*exp(-im*2*ω) + h[4]*exp(-im*3*ω) + h[5]*exp(-im*4*ω)
# Ht = computeDTFTviaformula(h,ω)
#
# println("discrepancy ", abs(Hn-Ht))

# length = 2N+1
function getdiracdeltaseq(N)
    out = zeros(Float64,2*N+1)
    out[N+1] = 1.0

    return out
end

# get highpass filter from a lowpass one.
function spectralinversion(h::Vector{T}) where T
    @assert isodd(length(h))

    out = collect( -h[i] for i = 1:length(h))
    out[div(length(h),2)+1] += one(T)

    return out
end

# assume x starts at time 0.
function upsample(x::Vector{T}, M::Int)::Vector{T} where T

    out = Vector{T}(undef, length(x)*M)
    #out = Vector{T}(undef, (length(x)-1)*M +1)
    fill!(out,zero(T))

    for n = 0:length(x)-1
        out[n*M+1] = x[n+1]
    end

    return out
end

function downsample(x::Vector{T}, M::Int)::Tuple{Vector{T},Vector{T}} where T

    x_padded = copy(x)
    Q = rem(length(x),M)
    if Q != 0
        #push!(out,zero(T))
        push!(x_padded,zeros(T,M-Q)...)
    end

    N = div(length(x_padded),M)

    out = Vector{T}(undef, N)

    for n = 0:N-1
        out[n+1] = x_padded[n*M+1]
    end

    return out, x_padded
end

function linearconv(  x::Vector{T},
                h::Vector{T}) where T

    out = Vector{T}(undef, length(x)+length(h)-1)
    for i = 1:length(x)+length(h)-1

        out[i] = sum( h[k]*getfinitesignalatindex(x,i-k+1) for k = 1:length(h) )
        # i-k+1 instead of i-k because Julia is 1-indexing.
    end

    return out
end

# remove the padding due to linear convolution. There will still be a bit of
#   shift probably due to phase-shift of system. This reasoning is untested.
# g is the filter.
# y is the signal.
function forcesamesizeafterlinearconv(g::Vector{T},y::Vector{T})::Vector{T} where T

    offset1 = div(length(g)-1,2)
    offset2 = length(g)-1-offset1

    return y[offset1:end-offset2]
end

# in progress. optimized for speed. eventually package pynogram without allocating N_chs worth of output.
# ignore entries of output that require out-of-bound h.
function fastlinearconv(a::Vector{T}, b::Vector{T})::Vector{T} where T
    m = length(a)
    n = length(b)

    out = zeros(T, m+n-1)
    @inbounds for j = 1:m
        for k = 1:n
            out[j+k-1] += a[j]*b[k]
        end
    end
    return out
end

function circconv(  x::Vector{T},
                h::Vector{T}) where T

    out = Vector{T}(undef, length(x)+length(h))
    for i = 1:length(x)+length(h)

        out[i] = sum( h[k]*x[mod(i-k,length(x))+1] for k = 1:length(h) )
    end

    return out
end


# Objective functions
# Author: Philipp Witte, pwitte3@gatech.edu
# Date: January 2020

export log_likelihood, ∇log_likelihood, mse, ∇mse

###################################################################################################
# Mean squared error
"""
    f = mse(X, Y)

Mean squared error between arrays/tensor X and Y

See also: [`∇mse`](@ref)
"""
mse(X, Y) = .5f0/size(X, 4)*norm(X - Y, 2)^2

"""
    ∇f = ∇mse(X, Y)

Gradient of the MSE loss with respect to input tensors X and Y.

See also: [`mse`](@ref)
"""
∇mse(X, Y) = 1f0/size(X, 4)*(X - Y)

###################################################################################################
# Log-likelihood

"""
    f = log_likelihood(X; μ=0f0, σ=1f0)

Log-likelihood of X for a Gaussian distribution with given mean μ and variance σ. All elements of X are assumed to be iid.

See also: [`∇log_likelihood`](@ref)
"""
log_likelihood(X; μ=0f0, σ=1f0) = 1f0/size(X, 4)*sum(-.5f0*((X .- μ)/σ).^2)

"""
    ∇f = ∇log_likelihood(X; μ=0f0, σ=1f0)

Gradient of the log-likelihood function with respect to the input tensor X.

See also: [`log_likelihood`](@ref)
"""
∇log_likelihood(X; μ=0f0, σ=1f0) = -1f0/size(X, 4)*(X .- μ)/σ^2


"""
    dirichlet(f1,f2,f3,f4,a,b,h)
berisi program untuk mencari solusi persamaan
differensial parsial yaitu persamaan Laplace dengan kondisi batas Dirichlet. Program
ini secara default berisi 8 masukan, yaitu fungsi nilai batas `f1`, `f2`, `f3`, dan
`f4`, nilai batas solusi `a` dan `b`, dan ukuran langkah `h`
# Example
```jl
julia> f1(x)= 20+0*x;

julia> f2(x)= 180+0*x;

julia> f3(y)= 80+0*y;

julia> f4(y)= 0*y;

julia> U = dirichlet(f1,f2,f3,f4,4,4,1)
5×5 Adjoint{Float64,Array{Float64,2}}:
  50.0   20.0      20.0      20.0     10.0
  80.0   55.7143   43.2143   27.1429   0.0
  80.0   79.6429   70.0      45.3571   0.0
  80.0  112.857   111.786    84.2857   0.0
 130.0  180.0     180.0     180.0     90.0
```
return solusi `U`.
"""
function dirichlet(f1,f2,f3,f4,a,b,h)
  maxi = 100;
  tol = 1e-7;
  n = Int(a/h)+1;
  m = Int(b/h)+1;
  U = (a.*(f1.(0).+f2.(0)).+b.*(f3.(0).+f4.(0)))./(2*a.+2*b).+ones(n,m);
  # Masalah nilai batas
  U[1:n,1]=f1.(0:h:(n-1)*h);
  U[1:n,m]=f2.(0:h:(n-1)*h);
  U[1,1:m]=f3.(0:h:(m-1)*h);
  U[n,1:m]=f4.(0:h:(m-1)*h);
  U[1,1]=(U[1,2]+U[2,1])/2;
  U[1,m]=(U[1,m-1]+U[2,m])/2;
  U[n,1]=(U[n-1,1]+U[n,2])/2;
  U[n,m]=(U[n-1,m]+U[n,m-1])/2;
  w = 4/(2+sqrt(4-(cos(pi/(n-1))+cos(pi/(m-1)))^2));
  err=1;   iter=0;
  while (err>tol)&&(iter<=maxi)
    err = 0;
    for j = 2:m-1
      for i = 2:n-1
        relx = w*(U[i,j+1]+U[i,j-1]+U[i+1,j]+U[i-1,j]-4*U[i,j])/4;
        U[i,j]=U[i,j]+relx;
        if err<=abs(relx)
          err=abs(relx);
        end
      end
    end
    iter=iter+1;
  end
  U = U';
end


"""
    regpoly(X,Y,m)

adalah fungsi untuk mencari persamaan polinomial `y=C[m]x^m + ... + C[2]x^2 + C[1]x`
dari variabel dependen `Y` dan independen `X` dengan pangkat tertinggi `m`.

# Example
```jldoctest
julia> x = [-3, 0, 2, 4];

julia> y = [ 3, 1, 1, 3];

julia> C = regpoly(x,y,2)
3-element Array{Float64,1}:
  0.17846247712019525
 -0.19249542403904824
  0.8505186089078707
```
return nilai koefisien `C = [C[m],...,C[2],C[1]]`.
"""
function regpoly(X,Y,m)
  F = zeros(length(X),m+1)
  for k = 1: m+1
    F[:,k]=X.^(k-1);
  end
  A = F'*F;
  B = F'*Y;
  C = A\B;
  C = reverse(C,dims=1)
end


"""
```
imge = imcorner(img; [method], [border], [blockSize], [k])
```
Performs corner detection, using either the Harris method or the Shi-Tomasi
method. `method` is `'harris'` (default) or `'shi-tomasi'`. `'border'` is the border
mode used in computing the gradient (default is `'replicate'`). `'blockSize'` is
the size of the box filter kernel (default of `3`). `k` is the Harris free parameter
(default of `0.04`). It is only used when method is set to `'harris'`.

"""
function imcorner(img::AbstractArray; method::AbstractString="harris", border::AbstractString="replicate", blockSize::Int=3, k::Float64=0.04)

    (grad_x,grad_y) = imgradients(img, "sobel",border);

    cov_xx = grad_x .* grad_x;
    cov_xy = grad_x .* grad_y;
    cov_yy = grad_y .* grad_y;

    box_filter_kernel = (1/(blockSize*blockSize)) * ones(blockSize,blockSize);

    cov_xx = imfilter(cov_xx,box_filter_kernel);
    cov_xy = imfilter(cov_xy,box_filter_kernel);
    cov_yy = imfilter(cov_yy,box_filter_kernel);

    if method == "harris"
        corner = (cov_xx.*cov_yy - cov_xy.*cov_xy - k*(cov_xx + cov_yy).*(cov_xx + cov_yy));
    elseif method == "shi-tomasi"
        cov_yy = 0.5*cov_yy;
        cov_xx = 0.5*cov_xx;
        corner = ((cov_xx + cov_yy) - sqrt((cov_xx - cov_yy).*(cov_xx - cov_yy) + cov_xy.*cov_xy));
    else
        error("Unknown corner method: $method")
    end

    return corner
end


test_string = "BFFFBBFRRR
FFFBBBFRRR
BBFFBBFRLL"

test_list = split(test_string,'\n')

list = readlines("day5_input.txt")

function seat_to_id(seat)
    seat_binary = seat
    for (k,v) in zip(('F', 'B', 'L', 'R'),('0', '1', '0', '1'))
        seat_binary = replace(seat_binary, k => v)
    end
    id = parse(Int, seat_binary, base=2)
    return id
end

function id_to_seat(id)
    seat_binary = bitstring(id)[end-9:end]
    seat_row = replace(replace(seat_binary[1:end-3], '1' => 'B'), '0' => 'F')
    seat_column = replace(replace(seat_binary[end-2:end], '1' => 'R'), '0' => 'L')
    seat = "B"^(7-length(seat_row)) * seat_row * 'L'^(3-length(seat_column)) * seat_column
    return seat
end

highest_id(list) = maximum(seat_to_id.(list))

highest_seat(list) = id_to_seat(highest_id(list))

println("Highest id of the test list: $(highest_id(test_list))")

println("Highest id of the problem list: $(highest_id(list))")

function myseat(list)
    sorted_list = sort(seat_to_id.(list))
    min_id = sorted_list[1]
    diff_list = sorted_list[2:end] - sorted_list[1:end-1]
    max, id_skip = findmax(diff_list)
    return min_id + id_skip
end

println("Your seat is $(myseat(list))")

# Answers:
# P1: 926
# P2: 657

function seat_to_id2(seat)
    f(x) = (x =='F' || x == 'L') ? 0 : 1
    return parse(Int, join(map(f, collect(seat))), base=2)
end

highest_id2(list) = maximum(seat_to_id2.(list))

println("Highest id of the problem list v2: $(highest_id(list))")

# method 1 is still better
#=
```julia
julia> @btime highest_id(list)
  1.158 ms (13538 allocations: 747.02 KiB)
926

julia> @btime highest_id2(list)
  1.359 ms (22844 allocations: 1.21 MiB)
926
```
 =#

seatnumber(line) = mapfoldl(c->c in "BR", (x, y)->2x+y, line, init=0)

# wow, look at the differences
#=
```julia
julia> @btime seat_to_id(line)
  1.276 μs (16 allocations: 896 bytes)
181

julia> @btime seat_to_id2(line)
  1.579 μs (27 allocations: 1.45 KiB)
181

julia> @btime seatnumber(line)
  281.837 ns (0 allocations: 0 bytes)
181
```
 =#

 # Here is a one-liner, which is actually a two-liner due to `;`,
 # that was started by [braden](bradeneliason#2230), using
 # the one-liner `seatnumber` function, and improved upon
 # after discussions with AaronRMatthis and Tom:

function seats(list)
    S=sort(mapfoldl.(∈("BR"),(x,y)->2x+y,list))
    return S[end],S[argmax(diff(S))]+1
end

nothing

# Much faster than mine:
#=
```julia
julia> @btime seats(list_day5)
  309.319 μs (14 allocations: 20.52 KiB)
(926, 657)
```
 =#


abstract type AbstractKoopmanAlgorithm end;

"""
    DMDPINV()

Approximates the Koopman operator `K` based on

```julia
K = Y / X
```

where `Y` and `X` are data matrices.

"""
mutable struct DMDPINV <: AbstractKoopmanAlgorithm end;

# Fast but more allocations
(x::DMDPINV)(X::AbstractArray, Y::AbstractArray) = Y / X

"""
    DMDSVD(rtol)

Approximates the Koopman operator `K` based on the singular value decomposition
of `X` such that:

```julia
K = Y*V*Σ*U'
```

where `Y` and `X = U*Σ*V'` are data matrices.
If `rtol` ∈ (0, 1) is given, the singular value decomposition is reduced to include only
entries bigger than `rtol*maximum(Σ)`. If `rtol` is an integer, the reduced SVD up to `rtol` is used
for computation.
"""
mutable struct DMDSVD{T} <: AbstractKoopmanAlgorithm
    rtol::T
end;

DMDSVD() = DMDSVD(0.0)

# Slower but fewer allocations
function (x::DMDSVD)(X::AbstractArray, Y::AbstractArray)
    U, S, V = svd(X)
    if typeof(x.rtol) <: Int && x.rtol < size(X, 1)
        V = V[:, x.rtol]
    elseif zero(x.rtol) < x.rtol < one(x.rtol)
        r = vec(S .> x.rtol*maximum(S))
        U = U[:, r]
        S = S[r]
        V = V[:, r]
    end

    return Y*V*Diagonal(one(eltype(X)) ./ S)*U'
end

"""
    TOTALDMD(rtol, alg)

Approximates the Koopman operator `K` with the algorithm `alg` over the rank-reduced data
matrices `Xᵣ = X Qᵣ` and `Yᵣ = Y Qᵣ`, where `Qᵣ` originates from the singular value decomposition of
the joint data `Z = [X; Y]`. Based on [this paper](http://cwrowley.princeton.edu/papers/Hemati-2017a.pdf).

If `rtol` ∈ (0, 1) is given, the singular value decomposition is reduced to include only
entries bigger than `rtol*maximum(Σ)`. If `rtol` is an integer, the reduced SVD up to `rtol` is used
for computation.
"""
mutable struct TOTALDMD{R, A} <: AbstractKoopmanAlgorithm
    rtol::R
    alg::A
end

TOTALDMD() = TOTALDMD(0.0, DMDPINV())

function (x::TOTALDMD)(X::AbstractArray, Y::AbstractArray)
    _ , S, Q = svd([X; Y])
    if typeof(x.rtol) <: Int && x.rtol < size(X, 1)
        Q = Q[:, 1:x.rtol]
    elseif zero(x.rtol) < x.rtol < one(x.rtol)
        r = vec(S .> x.rtol*maximum(S))
        Q = Q[:, r]
    end

    return x.alg(X*Q, Y*Q)
end


# Estimate derivatives numerically
# These use centered-differencing for higher accuracy
function derivative_numer{T<:Number}(func::Function, x::T, h::T)
    xp = x + h
    vp = func(xp)
    xm = x - h
    vm = func(xm)
    return (vp-vm)/(xp-xm)
end
function derivative2_numer{T<:Number}(func::Function, x::T, h::T)
    return (func(x + 2h) - 2*func(x) + func(x - 2h)) / 4h^2
end
derivative_numer{T<:Number}(func::Function, x::T, index::Int, h::T) = derivative_numer(func, x, h)  # compatibility with partial derivatives
derivative2_numer{T<:Number}(func::Function, x::T, index::Int, h::T) = derivative2_numer(func, x, h)
function derivative_numer{T<:Number}(func::Function, x::Array{T}, index::Int, h::T)
    xsave = x[index]
    xp = xsave + h
    xm = xsave - h
    x[index] = xp
    vp = func(x)
    x[index] = xm
    vm = func(x)
    x[index] = xsave
    return (vp-vm)/(xp-xm)
end
function derivative2_numer{T<:Number}(func::Function, x::Array{T}, index::(Int,Int,), h::T)
    if index[1] == index[2] # 2nd order derivative in one direction
        v0 = func(x...)
        xsave = x[index[1]]
        xp = xsave + 2h
        xm = xsave - 2h
        x[index[1]] = xp
        vp = func(x...)
        x[index[1]] = xm
        vm = func(x...)
        x[index[1]] = xsave
        deriv = (vp - 2v0 + vm) / 4h^2
    else # cross-derivative, 1st order in each direction
        xpp = [x[1]+h,x[2]+h]
        xpm = [x[1]+h,x[2]-h]
        xmp = [x[1]-h,x[2]+h]
        xmm = [x[1]-h,x[2]-h]
        deriv = (func(xpp...) - func(xpm...) - func(xmp...) + func(xmm...)) / 4h^2
    end
    return deriv
end
function derivative_numer{T<:Number}(func::Function, c::(T...,), index::Int, h::T)
    x = [c...]
    xsave = x[index]
    xp = xsave + h
    xm = xsave - h
    x[index] = xp
    vp = func(x...)
    x[index] = xm
    vm = func(x...)
    x[index] = xsave
    return (vp-vm)/(xp-xm)
end
function derivative2_numer{T<:Number}(func::Function, c::(T...,), index::(Int,Int,), h::T)
    x = [c...]
    return derivative2_numer(func, x, index, h)
end
function derivative_numer{T<:Number}(func::Function, x::T, h::Vector{T})
    d = zeros(T, length(h))
    for i = 1:length(h)
        d[i] = derivative_numer(func, x, h[i])
    end
    return d
end
function derivative2_numer{T<:Number}(func::Function, x, h::Vector{T})
    d = zeros(T, length(h))
    for i = 1:length(h)
        d[i] = derivative2_numer(func, x, h[i])
    end
    return d
end
function derivative_numer{T<:Number}(func::Function, x, index::Int, h::Vector{T})
    d = zeros(T, length(h))
    for i = 1:length(h)
        d[i] = derivative_numer(func, x, index, h[i])
    end
    return d
end
function derivative2_numer{T<:Number}(func::Function, x, index::(Int,Int,), h::Vector{T})
    d = zeros(T, length(h))
    for i = 1:length(h)
        d[i] = derivative2_numer(func, x, index, h[i])
    end
    return d
end
derivative_numer{T<:Number}(func::Function, x::T) = derivative_numer(func, x, (eps(max(abs(x),one(T))))^convert(T, 1/3))
derivative_numer{T<:Number}(func::Function, x::Array{T}, index::Int) = derivative_numer(func, x, index, (eps(max(abs(x[index]),one(T))))^convert(T, 1/3))
derivative_numer{T<:Number}(func::Function, c::(T...,), index::Int) = derivative_numer(func, c, index, (eps(max(abs(c[index]),one(T))))^convert(T, 1/3))
derivative2_numer{T<:Number}(func::Function, x::T) = derivative2_numer(func, x, (eps(max(abs(x),one(T))))^convert(T,1/3))
derivative2_numer{T<:Number}(func::Function, x::Array{T}, index::(Int,Int,)) = derivative2_numer(func, x, index, (eps(max(abs(x[index[1]]),one(T))))^convert(T, 1/3))
derivative2_numer{T<:Number}(func::Function, c::(T...,), index::(Int,Int,)) = derivative2_numer(func, c, index, (eps(max(abs(c[index[1]]),one(T))))^convert(T, 1/3))


### A Pluto.jl notebook ###
# v0.17.3

using Markdown
using InteractiveUtils

# ╔═╡ c819a820-13d1-11ec-174e-31eef4e20fc4
md"""
# Basic operators

Operators are symbols used to perform for example a basic mathematical operation. Some are very easy and straighforward, some are a little bit more complicated.

Let's start by the simple ones.

## Numeric operators

They include +, -, / and *, and are simply used as with a calculator as follows:
"""

# ╔═╡ 63b315e1-6bab-48d2-9992-6e6598bdf5fb
1 + 2

# ╔═╡ e874c232-256c-4d9f-a214-e526192be0cd
1 - 2

# ╔═╡ 4c362d08-3b79-44e3-8b48-0f9231526dca
1 * 2

# ╔═╡ f9ec2db0-d20a-4aac-ad24-66df4c7e6bde
1 / 2

# ╔═╡ 2222f963-31d7-460c-940f-4b544b3bc8a2
md"""
There is also the equal sign to assign a value to a variable:
"""

# ╔═╡ 36732368-836f-46bb-9a75-b75b10890505
a = 1

# ╔═╡ 9cb19c7e-9218-4f37-be67-c1e5d7b5a883
a

# ╔═╡ e43721b6-d642-4b6e-88ff-2bc48635effa
md"""
There's the integer division using ÷ (`\div` + tab)
"""

# ╔═╡ cc9cff27-b864-475b-b162-0503dc308432
1 ÷ 2

# ╔═╡ 98f549a9-1757-4e7c-bfac-0bf0d951b10d
md"""
To get the reminder of a division, we use the modulus, which is defined by the percent sign:
"""

# ╔═╡ 0272780b-6ace-499a-bf2b-441b9a148de4
1 % 2

# ╔═╡ 9f59bff0-bc85-4f36-b92c-d84e25cbdd9e
md"""
This is especially usefull if you want to know if the value of a variable is odd or even. To do so, you would use the remainder of the division by 2, which gives 0 for an even number, and 1 for an odd number:
"""

# ╔═╡ 7ed2ed34-f811-40c5-b6d2-17b7263f934c
a % 2

# ╔═╡ 00b8743b-8597-4fea-82d9-c191bfe73050
b = 2

# ╔═╡ 72ffc8b6-8543-42d2-b0bf-1640ccfcdea7
b % 2

# ╔═╡ 1df480ee-375f-421f-bfe6-cac883d290e3
md"""
To raise a value to the power of a number, we can use the caret:
"""

# ╔═╡ 278e7cb7-f760-4df2-8bdf-c87f1b8f5bec
2^4

# ╔═╡ 74bb4a3d-1d27-4555-8a47-98472ead2c9a
md"""
There are some short-hands if you want to update the value of a variable:
"""

# ╔═╡ ebc07294-3d9b-4973-b2b6-1afb535dc1ae
let
var = 1
var += 1
end

# ╔═╡ 73ced44d-503a-46a7-b1df-4db81e21375c
let
var = 1
var -= 1
end

# ╔═╡ 4293c2a4-cbd5-4b22-8b4a-280bbdacd1c8
let
var = 1
var *= 2
end

# ╔═╡ 1c238add-1c4f-4236-9943-6b9e7f45d8ab
let
var = 1
var /= 2
end

# ╔═╡ 6aa9bc63-bbf2-4b6e-9690-13ff4344c460
md"""
## Comparison operators

Some operators can be used to compare two values. They always return a boolean value: either `true` or `false`.

Greater than:
"""

# ╔═╡ 81ef192e-8e24-4865-8d79-06568e33390e
2 > 1

# ╔═╡ 2ee4020f-debb-4d0d-b25c-889f0f821d02
md"""
Greater or equal to:
"""

# ╔═╡ 6703e50a-18f1-4286-a5a7-2e18a28dc9ac
2 >= 1

# ╔═╡ 682bc70c-8ab9-40da-a231-df3f998d3562
md"""
The opposite:
"""

# ╔═╡ c7b5b7b7-3766-4a20-b847-f6ce2a077ace
2 < 1

# ╔═╡ 9e48d85f-61a8-4e93-9cc7-079f33359f0c
2 <= 1

# ╔═╡ daf09d9e-556d-4e2c-9e35-d78b0f61cd26
md"""
Testing equality:
"""

# ╔═╡ cce83b75-2ce6-489d-b7f6-05853fd6d056
2 == 1

# ╔═╡ 5f68213a-6d1d-4b3a-a7b3-c0abfbaa305b
md"""
!!! note
	Note that equality is done using `==`, which is not the same than `=`, used to assign a value to a variable.
"""

# ╔═╡ 9e9b02b8-f47d-494f-9332-7701903e6810
md"""
To test inequality, we use `!=`:
"""

# ╔═╡ 37d52bfb-6965-4ecd-804f-98d9f7c1b01b
2 != 1

# ╔═╡ bc6ad01a-f7ec-4538-8115-73ba67406943
md"""
## Boolean operators

There are also the logical tests using `!` ("not"), `&&` ("and") and `||` ("or") on boolean values (`true` or `false`).

### Negation

The `!` operator negates the boolean value of the statement:
"""

# ╔═╡ 83f6f3fd-ab7e-4ebb-8285-b49188a1f50b
!false

# ╔═╡ dcbbe212-23aa-4ff9-9fda-f1f0d67b3f95
!true

# ╔═╡ 8bbe84a0-93de-4bb9-8995-caed6bc920c7
md"""
So this:
"""

# ╔═╡ f0752ae9-5401-49fc-8ddc-d07302499185
2 > 1

# ╔═╡ b88e0721-cf58-4f85-b54a-4514982e2218
md"""
Is equivalent to this:
"""

# ╔═╡ 0a8e65d5-7043-4f66-8987-c2e56e09d223
!(2 <= 1)

# ╔═╡ 729ae192-df5b-47a3-a245-9d40e8014f5a
md"""

### Logical "and"

The logical "and" (`&&`) returns `true` only if the right-hand side and left-hand side values of the operator are both `true`:
"""

# ╔═╡ f032b44b-16b0-4383-8076-97d110249fb5
true && true

# ╔═╡ 4d44dd47-ff82-417a-b248-cacdac6d91d4
true && false

# ╔═╡ 0de3bcfb-f4e0-43cc-b407-ececfbb1d8bf
md"""
### Logical "or"
"""

# ╔═╡ c2b86ba3-d54e-4191-9efb-a70e0f706516
md"""
The logical "or" returns true if at least one is true:
"""

# ╔═╡ 8e7cce54-9217-4ad0-978e-67fa21cb6fd5
true || false

# ╔═╡ 4f5d628f-de89-43a0-9a50-d79d39d8fd44
false || false

# ╔═╡ f91b3d97-a611-4a5e-8cf6-3e45eb052ff1
md"""
In reality you'll use variables instead of boolean values directly. For example you could say that `c` is equal to `true` in the case both `a` and `b` are bellow 3:
"""

# ╔═╡ ff6e339d-89ba-4df9-b674-4602dd4549a9
c = a < 3 && b < 3

# ╔═╡ 2f44b8ec-3414-4903-b5a2-ea7c819b3b86
md"""
### Conditional execution
"""

# ╔═╡ a0cf3510-a2f1-4b8d-9e95-54b49f37d926
md"""
The nice thing with Julia is that it tests the conditions incrementally because we know that if the first condition is `false` in a `&&` statement, the whole statement will be false, and if the first condition is `true` in a `||` statement, the whole statement will be `true`. We can use that as a tool to conditionally execute a line of code.
"""

# ╔═╡ 98fa4586-e163-40a5-9084-c9a1cb4c350b
a == 1 && "a is equal to 1!"

# ╔═╡ 8dc5efc6-6c25-45d7-a944-6f229eef9b49
b == 1 && "b is equal to 1!"

# ╔═╡ 986244f3-b4eb-4d16-ab5f-7585a44d3784
b == 1 || "b is not equal to 1!"

# ╔═╡ 102364c0-98a9-411b-94c7-cd129c99f04a
md"""
The concept of conditional execution is very important, and we will see it in more details in a coming video.
"""

# ╔═╡ 0da85da4-18eb-431e-972a-426e73f16781
md"""
## Vectorizing operators

All operators we just saw are applied to one value at a time. But very often we want to apply an operator over a sequence of values stored in an Array for example.

To do so, we can use the vectorized version of the operators by adding a dot right before it. This is called broadcasting. We will see more details about that in another video.
"""

# ╔═╡ 5356fedc-2524-4b32-8559-a0ba5ae2f5cf
a_vec = [1, 2, 3]

# ╔═╡ 7e0b0477-e72d-4bf5-a059-63993cf0035b
md"""
For example adding a single value to a vector of values using the `+` sign will return an error:
"""

# ╔═╡ 0d905737-2738-4564-9273-139a84aa100d
a_vec + 2

# ╔═╡ 13b1edec-9ae6-4a0b-b8ed-2f63df2e6792
md"""
THe error is a `MethodError`, because there is no method implemented for the `+` function with a vector of integer and an integer.

Using the vectorized version will work perfectly:
"""

# ╔═╡ 02a4c901-8c29-42ec-8864-fe5d4c70f46b
a_vec .+ 2

# ╔═╡ 6515d475-294a-40c4-9ac7-8e3ef15e8f89
md"""
!!! note
	In practice, the vectorized version applies the `+` function sequentially over the values of the `a_vec` array, and then return the results back as an array.
"""

# ╔═╡ e087cd30-f78f-40a4-95f5-87bfcf05fbe7
md"""
There are other ways to do so, such as the for loops or array comprehension, but we will see that in another video.
"""

# ╔═╡ Cell order:
# ╟─c819a820-13d1-11ec-174e-31eef4e20fc4
# ╠═63b315e1-6bab-48d2-9992-6e6598bdf5fb
# ╠═e874c232-256c-4d9f-a214-e526192be0cd
# ╠═4c362d08-3b79-44e3-8b48-0f9231526dca
# ╠═f9ec2db0-d20a-4aac-ad24-66df4c7e6bde
# ╟─2222f963-31d7-460c-940f-4b544b3bc8a2
# ╠═36732368-836f-46bb-9a75-b75b10890505
# ╠═9cb19c7e-9218-4f37-be67-c1e5d7b5a883
# ╟─e43721b6-d642-4b6e-88ff-2bc48635effa
# ╠═cc9cff27-b864-475b-b162-0503dc308432
# ╟─98f549a9-1757-4e7c-bfac-0bf0d951b10d
# ╠═0272780b-6ace-499a-bf2b-441b9a148de4
# ╟─9f59bff0-bc85-4f36-b92c-d84e25cbdd9e
# ╠═7ed2ed34-f811-40c5-b6d2-17b7263f934c
# ╠═00b8743b-8597-4fea-82d9-c191bfe73050
# ╠═72ffc8b6-8543-42d2-b0bf-1640ccfcdea7
# ╟─1df480ee-375f-421f-bfe6-cac883d290e3
# ╠═278e7cb7-f760-4df2-8bdf-c87f1b8f5bec
# ╟─74bb4a3d-1d27-4555-8a47-98472ead2c9a
# ╠═ebc07294-3d9b-4973-b2b6-1afb535dc1ae
# ╠═73ced44d-503a-46a7-b1df-4db81e21375c
# ╠═4293c2a4-cbd5-4b22-8b4a-280bbdacd1c8
# ╠═1c238add-1c4f-4236-9943-6b9e7f45d8ab
# ╟─6aa9bc63-bbf2-4b6e-9690-13ff4344c460
# ╠═81ef192e-8e24-4865-8d79-06568e33390e
# ╟─2ee4020f-debb-4d0d-b25c-889f0f821d02
# ╠═6703e50a-18f1-4286-a5a7-2e18a28dc9ac
# ╟─682bc70c-8ab9-40da-a231-df3f998d3562
# ╠═c7b5b7b7-3766-4a20-b847-f6ce2a077ace
# ╠═9e48d85f-61a8-4e93-9cc7-079f33359f0c
# ╟─daf09d9e-556d-4e2c-9e35-d78b0f61cd26
# ╠═cce83b75-2ce6-489d-b7f6-05853fd6d056
# ╟─5f68213a-6d1d-4b3a-a7b3-c0abfbaa305b
# ╟─9e9b02b8-f47d-494f-9332-7701903e6810
# ╠═37d52bfb-6965-4ecd-804f-98d9f7c1b01b
# ╟─bc6ad01a-f7ec-4538-8115-73ba67406943
# ╠═83f6f3fd-ab7e-4ebb-8285-b49188a1f50b
# ╠═dcbbe212-23aa-4ff9-9fda-f1f0d67b3f95
# ╟─8bbe84a0-93de-4bb9-8995-caed6bc920c7
# ╠═f0752ae9-5401-49fc-8ddc-d07302499185
# ╟─b88e0721-cf58-4f85-b54a-4514982e2218
# ╠═0a8e65d5-7043-4f66-8987-c2e56e09d223
# ╟─729ae192-df5b-47a3-a245-9d40e8014f5a
# ╠═f032b44b-16b0-4383-8076-97d110249fb5
# ╠═4d44dd47-ff82-417a-b248-cacdac6d91d4
# ╟─0de3bcfb-f4e0-43cc-b407-ececfbb1d8bf
# ╟─c2b86ba3-d54e-4191-9efb-a70e0f706516
# ╠═8e7cce54-9217-4ad0-978e-67fa21cb6fd5
# ╠═4f5d628f-de89-43a0-9a50-d79d39d8fd44
# ╟─f91b3d97-a611-4a5e-8cf6-3e45eb052ff1
# ╠═ff6e339d-89ba-4df9-b674-4602dd4549a9
# ╟─2f44b8ec-3414-4903-b5a2-ea7c819b3b86
# ╟─a0cf3510-a2f1-4b8d-9e95-54b49f37d926
# ╠═98fa4586-e163-40a5-9084-c9a1cb4c350b
# ╠═8dc5efc6-6c25-45d7-a944-6f229eef9b49
# ╠═986244f3-b4eb-4d16-ab5f-7585a44d3784
# ╟─102364c0-98a9-411b-94c7-cd129c99f04a
# ╟─0da85da4-18eb-431e-972a-426e73f16781
# ╠═5356fedc-2524-4b32-8559-a0ba5ae2f5cf
# ╟─7e0b0477-e72d-4bf5-a059-63993cf0035b
# ╠═0d905737-2738-4564-9273-139a84aa100d
# ╟─13b1edec-9ae6-4a0b-b8ed-2f63df2e6792
# ╠═02a4c901-8c29-42ec-8864-fe5d4c70f46b
# ╟─6515d475-294a-40c4-9ac7-8e3ef15e8f89
# ╟─e087cd30-f78f-40a4-95f5-87bfcf05fbe7


"Tableau for explicit Euler method"
function getTableauExplicitEuler()
    a = zeros(Float64, 1, 1)
    b = [1.0]
    c = [0.0]
    o = 1

    TableauERK(:explicit_euler, o, a, b, c)
end

"Tableau for explicit midpoint method"
function getTableauExplicitMidpoint()
    a = [[0.0 0.0]
         [0.5 0.0]]
    b = [0.0, 1.0]
    c = [0.0, 0.5]
    o = 2

    TableauERK(:explicit_midpoint, o, a, b, c)
end

"Tableau for Heun's method"
function getTableauHeun()
    a = [[0.0 0.0]
         [1.0 0.0]]
    b = [0.5, 0.5]
    c = [0.0, 1.0]
    o = 2

    TableauERK(:heun, o, a, b, c)
end

"Tableau for Kutta's method of order three"
function getTableauKutta()
    a = [[ 0.0 0.0 0.0]
         [ 0.5 0.0 0.0]
         [-1.0 2.0 0.0]]
    b = [1/6, 4/6, 1/6]
    c = [0.0, 0.5, 1.0]
    o = 3

    TableauERK(:kutta, o, a, b, c)
end

"Tableau for explicit Runge-Kutta method of order four (1/6 rule)"
function getTableauERK4()
    a = [[0.0 0.0 0.0 0.0]
         [0.5 0.0 0.0 0.0]
         [0.0 0.5 0.0 0.0]
         [0.0 0.0 1.0 0.0]]
    b = [1/6, 1/3, 1/3, 1/6]
    c = [0.0, 0.5, 0.5, 1.0]
    o = 4

    TableauERK(:erk4, o, a, b, c)
end

"Tableau for explicit Runge-Kutta method of order four (3/8 rule)"
function getTableauERK438()
    a = [[ 0.0  0.0  0.0  0.0]
         [ 1/3  0.0  0.0  0.0]
         [-1/3  1.0  0.0  0.0]
         [ 1.0 -1.0  1.0  0.0]]
    b = [1/8, 3/8, 3/8, 1/8]
    c = [0.0, 1/3, 2/3, 1.0]
    o = 4

    TableauERK(:erk438, o, a, b, c)
end


"""
    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)...)