##
#
# Visualization and related functions
#

"""

    RootsImage(leftbottom, topright, width, height)

Essentially a two dimensional histogram covering a rectangle in the
complex plane specified by complex numbers `leftbottom` and `topright`
with `width` horizontal and `height` vertical bins.
"""
struct RootsImage{T <: Real}
  remin::T
  remax::T
  immin::T
  immax::T
  height::Int
  width::Int
  hbin::T
  wbin::T
  data::Array{T, 2}

  function RootsImage(leftbottom::Complex{T}, topright::Complex{T}, width::Int, height::Int) where {T <: Real}
    remin = real(leftbottom)
    immin = imag(leftbottom)
    remax = real(topright)
    immax = imag(topright)
    new{T}(remin, remax, immin, immax, height, width, (remax - remin) / width, (immax - immin) / height, zeros(T, width, height))
  end
end

"""

    add_root!(img, z)

Add a root `z` to the image `img`.
"""
function add_root!(img::RootsImage{T}, z::Complex{T}) where {T <: Real}
  r = real(z); i = imag(z)

  if r < img.remin || r >= img.remax || i < img.immin || i >= img.immax
    # out of range, skipping...
    return
  end

  x = min(max(1, 1 + floor(Int64,(r - img.remin) / img.hbin)), img.width)
  y = min(max(1, 1 + floor(Int64,(i - img.immin) / img.wbin)), img.height)

  img.data[x, y] += T(1)
end

"""

    plot(img[; mode])

Show a graphical representation of the image `img`.
The optional parameter `mode` controls how to scale number of roots in
histogram bins:

 * `:sharp`,
 * `:log`,
 * `:log_cutoff`,
 * `:linear`,
 * a function.

"""
function plot(img::RootsImage{T}; mode=:sharp) where {T <: Real}
  if isa(mode, Function)
    output = mode.(img.data)
  elseif mode == :sharp
    output = (x -> min(1, x)).(img.data)
  elseif mode == :log_cutoff
    m      = mean(img.data)
    output = (x -> log(1 + min(2m, x))).(img.data)
  elseif mode == :log
    output = (x -> log(1 + x)).(img.data)
  elseif mode == :linear
    output = img.data
  else
    error("Unknown mode!")
  end

  # normalization to [0, 1]
  output = output / maximum(output)

  return colorview(Gray, transpose(output)[end:-1:1,1:end])
end

"""

    save_image(img, filename[; mode])

Save the `img` to file `filename`.
Optional parameter `mode` has the same meaning as in the [`plot`](@ref) function.

"""
function save_image(img::RootsImage{T}, filename::AbstractString; mode=:log_cutoff, latex_filename=nothing) where {T <: Real}
  @info "Saving the image $filename..."

  if isa(mode, Function)
    output = mode.(img.data)
  elseif mode == :sharp
    output = (x -> min(1, x)).(img.data)
  elseif mode == :log_cutoff
    m      = mean(img.data)
    output = (x -> log(1 + min(2m, x))).(img.data)
  elseif mode == :log
    output = (x -> log(1 + x)).(img.data)
  elseif mode == :linear
    output = img.data
  else
    error("Unknown mode!")
  end

  output = output / maximum(output)

  Images.save(filename, colorview(Gray, transpose(output)[end:-1:1,1:end]))

  if latex_filename != nothing
    @info "Constructing the LaTeX file $latex_filename..."
    write_latex(img, filename, latex_filename)
  end
end

"""

    fill_images!(imgs, filename)

Read roots from file `filename` and draw them onto images contained
in the array `imgs`.

"""
function fill_images!(imgs::Array{RootsImage{T}}, filename::AbstractString) where {T <: Real}
  roots_count = div(filesize(filename), sizeof(Complex{T})) + 1
  progress = Progress(roots_count, 10)

  @info "Sifting through data (there are $roots_count roots)..."

  open(filename) do io
    while !eof(io)
      z = read(io, Complex{T})
      for img in imgs
        add_root!(img, z)
      end
      next!(progress)
    end
  end
end

"""

  animate_growth(poly_iter, img, filename[; output_filename])

Take roots of polynomials generated by `poly_iter` and saved in file `filename`
and draw them incrementaly (each step corresponds to change of degree) onto
image `img`.

"""
function animate_growth(poly_iter::PolynomialIterator{S}, img::RootsImage{T}, filename; output_filename=j->"animation_$j.png") where {S <: Number, T <: Real}
  progress = Progress(div(filesize(filename), sizeof(Complex{T})) + 1, 10)
  degree = 0

  open(filename) do io
    for p in poly_iter
      # save the current snapshot
      if degree != length(p) - 1
        save_image(img, output_filename(degree))
        degree += 1
      end

      # read roots
      for k in 1:degree
        z = read(io, Complex{T})
        add_root!(img, z)
        next!(progress)
      end
    end
  end
end

"""

    fill_image!(img, filename)

An analog of [`fill_images!`](@ref), only with one image.

"""
function fill_image!(img::RootsImage{T}, filename::AbstractString) where {T <: Real}
  fill_images!([img], filename)
end

"""

  write_latex(img, imagename, latex_filename)

Create a LaTeX PGFPlots version of the image with axes.

"""
function write_latex(img::RootsImage{T}, imagename::AbstractString, latex_filename::AbstractString) where {T <: Real}
  open(latex_filename, "w") do io
    write(io, "\\documentclass{standalone}\n")
    write(io, "\\usepackage[T1]{fontenc}\n")
    write(io, "\\usepackage[utf8]{inputenc}\n")
    write(io, "\\usepackage{pgfplots}\n")
    write(io, "\\usepackage{lmodern}\n")
    write(io, "\\begin{document}\n")
    write(io, "\\begin{tikzpicture}\n")
    write(io, "\\begin{axis}[enlargelimits=false, axis on top, xlabel={\$\\Re\$}, ylabel={\$\\Im\$}]\n")
    write(io, "\\addplot graphics [xmin=$(img.remin), xmax=$(img.remax), ymin=$(img.immin), ymax=$(img.immax)] {$imagename};\n")
    write(io, "\\end{axis}\n")
    write(io, "\\end{tikzpicture}\n")
    write(io, "\\end{document}\n")
  end
end


"""
    ConstantStepsize <: Stepsize

A functor that always returns a fixed step size.

# Fields
* `length` – constant value for the step size.

# Constructor

    ConstantStepSize(s)

initialize the stepsie to a constant `s`
"""
mutable struct ConstantStepsize <: Stepsize
    length::Float64
    ConstantStepsize(s::Real) = new(s)
end
(s::ConstantStepsize)(p::P,o::O,i::Int, vars...) where {P <: Problem, O <: Options} = s.length
get_initial_stepsize(s::ConstantStepsize) = s.length

@doc raw"""
    DecreasingStepsize()

A functor that represents several decreasing step sizes

# Fields
* `length` – (`1`) the initial step size $l$.
* `factor` – (`1`) a value $f$ to multiply the initial step size with every iteration
* `subtrahend` – (`0`) a value $a$ that is subtracted every iteration
* `exponent` – (`1`) a value $e$ the current iteration numbers $e$th exponential
  is taken of

In total the complete formulae reads for the $i$th iterate as

$ s_i = \frac{(l-i\cdot a)f^i}{i^e}$

and hence the default simplifies to just $ s_i = \frac{l}{i} $

# Constructor

    ConstantStepSize(l,f,a,e)

initialiszes all fields above, where none of them is mandatory.
"""
mutable struct DecreasingStepsize <: Stepsize
    length::Float64
    factor::Float64
    subtrahend::Float64
    exponent::Float64
    DecreasingStepsize(l::Real=1.0, f::Real=1.0, a::Real=0.0, e::Real=1.0) = new(l,f,a,e)
end
(s::DecreasingStepsize)(p::P,o::O,i::Int) where {P <: Problem, O <: Options} = (s.length - i*s.subtrahend)*(s.factor^i)/(i^(s.exponent))
get_initial_stepsize(s::DecreasingStepsize) = s.length

"""
    Linesearch <: Stepsize

An abstract functor to represent line search type step size deteminations, see
[`Stepsize`](@ref) for details. One example is the [`ArmijoLinesearch`](@ref)
functor.

Compared to simple step sizes, the linesearch functors provide an interface of
the form `(p,o,i,η) -> s` with an additional (but optional) fourth parameter to
proviade a search direction; this should default to something reasonable, e.g.
the negative gradient.
"""
abstract type Linesearch <: Stepsize end

@doc raw"""
    ArmijoLineseach <: Linesearch

A functor representing Armijo line seach including the last runs state, i.e. a
last step size.

# Fields
* `initialStepsize` – (`1.0`) and initial step size
* `retraction_method` – (`ExponentialRetraction()`) the rectraction to use, defaults to
  the exponential map
* `contractionFactor` – (`0.95`) exponent for line search reduction
* `sufficientDecrease` – (`0.1`) gain within Armijo's rule
* `lastStepSize` – (`initialstepsize`) the last step size we start the search with

# Constructor

    ArmijoLineSearch()

with the Fields above in their order as optional arguments.

This method returns the functor to perform Armijo line search, where two inter
faces are available:
* based on a tuple `(p,o,i)` of a [`GradientProblem`](@ref) `p`, [`Options`](@ref) `o`
  and a current iterate `i`.
* with `(M,x,F,∇Fx[,η=-∇Fx]) -> s` where [Manifold](https://juliamanifolds.github.io/Manifolds.jl/stable/interface.html#ManifoldsBase.Manifold) `M`, a current
  point `x` a function `F`, that maps from the manifold to the reals,
  its gradient (a tangent vector) `∇F`$=\nabla F(x)$ at  `x` and an optional
  search direction tangent vector `η-∇F` are the arguments.
"""
mutable struct ArmijoLinesearch <: Linesearch
    initialStepsize::Float64
    retraction_method::AbstractRetractionMethod
    contractionFactor::Float64
    sufficientDecrease::Float64
    stepsizeOld::Float64
    function ArmijoLinesearch(
        s::Float64=1.0,
        r::AbstractRetractionMethod = ExponentialRetraction(),
        contractionFactor::Float64=0.95,
        sufficientDecrease::Float64=0.1
    )
        return new(s, r, contractionFactor, sufficientDecrease,s)
    end
end
function (a::ArmijoLinesearch)(p::P,o::O,i::Int, η=-get_gradient(p,o.x)) where {P <: GradientProblem{mT} where mT <: Manifold, O <: Options}
    a(p.M, o.x, p.cost, get_gradient(p,o.x), η)
end
function (a::ArmijoLinesearch)(M::mT, x, F::Function, ∇F::T, η::T=-∇F) where {mT <: Manifold, T}
    # for local shortness
    s = a.stepsizeOld
    f0 = F(x)
    xNew = retract(M, x, s*η, a.retraction_method)
    fNew = F(xNew)
    while fNew < f0 + a.sufficientDecrease*s*inner(M, x, η, ∇F) # increase
        xNew = retract(M,x,s*η,a.retraction_method)
        fNew = F(xNew)
        s = s/a.contractionFactor
    end
    s = s*a.contractionFactor # correct last
    while fNew > f0 + a.sufficientDecrease*s*inner(M, x, η, ∇F) # decrease
        s = a.contractionFactor * s
        xNew = retract(M, x, s*η, a.retraction_method)
        fNew = F(xNew)
    end
    a.stepsizeOld = s
    return s
end
get_initial_stepsize(a::ArmijoLinesearch) = a.initialStepsize

@doc raw"""
    get_stepsize(p::Problem, o::Options, vars...)

return the stepsize stored within [`Options`](@ref) `o` when solving [`Problem`](@ref) `p`.
This method also works for decorated options and the [`Stepsize`](@ref) function within
the options, by default stored in `o.stepsize`.
"""
function get_stepsize(p::Problem, o::Options, vars...)
    return get_stepsize(p,o,dispatch_options_decorator(o),vars...)
end
function get_stepsize(p::Problem,o::Options,::Val{true}, vars...)
    return get_stepsize(p, o.options, vars...)
end
get_stepsize(p::Problem, o::Options, ::Val{false}, vars...) = o.stepsize(p, o, vars...)

function get_initial_stepsize(p::Problem, o::Options, vars...)
    return get_initial_stepsize(
        p::Problem,
        o::Options,
        dispatch_options_decorator(o),
        vars...
    )
end
function get_initial_stepsize(p::Problem, o::Options, ::Val{true}, vars...)
    return get_initial_stepsize(p, o.options)
end
get_initial_stepsize(p::Problem, o::Options, ::Val{false}, vars...) = get_initial_stepsize(o.stepsize)

function get_last_stepsize(p::Problem, o::Options, vars...)
    return get_last_stepsize(p, o, dispatch_options_decorator(o), vars...)
end
function get_last_stepsize(p::Problem, o::Options, ::Val{true}, vars...)
    return get_last_stepsize(p, o.options,vars...)
end
function get_last_stepsize(p::Problem, o::Options, ::Val{false}, vars...)
    return get_last_stepsize(p,o,o.stepsize,vars...)
end
#
# dispatch on stepsize
get_last_stepsize(p::Problem, o::Options, s::Stepsize,vars...) = s(p,o,vars...)
get_last_stepsize(p::Problem, o::Options, s::ArmijoLinesearch, vars...) = s.stepsizeOld


using HyperHessians, ForwardDiff, DiffTests, BenchmarkTools, Test, Printf

struct Result
    f::Function
    n::Int
    time_fd::Float64
    timd_fh::Float64
end

function run_benchmark()
    results = Result[]
    for f in (DiffTests.ackley,DiffTests.rosenbrock_1) # DiffTests.VECTOR_TO_NUMBER_FUNCS
        @info f
        # ForwardDiff and HyperHessians should use the same default chunk size for these sizes
        for n in (1, 8, 128)
            x = rand(n)
            H_fd = similar(x, length(x), length(x))
            cfg_fd = ForwardDiff.HessianConfig(f, x)
            #ForwardDiff.hessian!(H_fd, f, x, cfg_fd)
            time_fd = @benchmark ForwardDiff.hessian!($H_fd, $f, $x, $cfg_fd)

            H_fh = similar(x, length(x), length(x))
            cfg_fh = HyperHessians.HessianConfig(x)
            # HyperHessians.hessian!(H_fh, f, x, cfg_fh)
            time_fh = @benchmark HyperHessians.hessian!($H_fh, $f, $x, $cfg_fh)

            push!(results, Result(f, n, minimum(time_fd.times), minimum(time_fh.times)))
            @test H_fd ≈ H_fh
        end
    end
    return results
end

function prettytime(t)
    if t < 1e3
        value, units = t, "ns"
    elseif t < 1e6
        value, units = t / 1e3, "μs"
    elseif t < 1e9
        value, units = t / 1e6, "ms"
    else
        value, units = t / 1e9, "s"
    end
    return string(@sprintf("%.3f", value), " ", units)
end

function print_results(io::IO, results)
    print(io, """
    | Function      | input length | Time ForwardDiff | Time HyperHessians | Speedup |
    | ------------- | ------------ | ---------------- | ----------------- | --------|
    """)

    for r in results
        ratio = round(r.time_fd / r.timd_fh, sigdigits=2)
        println(io,
            "| `", r.f, "` | ", r.n, " | ", prettytime(r.time_fd), " | ", prettytime(r.timd_fh), " | ", ratio
         )
    end
end

results = run_benchmark()
print_results(stdout, results)


# Question 1
function findset(x::DisjointSetNode)
    if (x !=x.p )
        x.p = findset(x.p)
	end
	x.p
end

# Question 2
function link!(x,y)
    if (x.rank > y.rank)
        y.p=x
        else (x.p =y)
        if (x.rank == y.rank)
            y.rank = y.rank + 1
        end
    end
end

function union!(x::DisjointSetNode,y::DisjointSetNode)
    link!(findset(x),findset(y))
end

# Question 3
function hammingdistance(s1::String,s2::String)
    teller = 0
    for i = 1:length(s1)
        if s1[i] != s2[i]
            teller +=1
        end
    end
    teller
end


import Base: append!, eye, size

export ConvergenceHistory, KrylovSubspace

*(f::Function, v::AbstractVector)=f(v) #Syntax to mimic matvec product

type Eigenpair{S,T}
    val::S
    vec::Vector{T}
end

type KrylovSubspace{T, OpT}
    A::OpT     #The linear operator that generates the subspace
    n::Int     #Dimension of problem
    order::Int #Order (maximum size) of subspace
    v::Vector{Vector{T}} #The Krylov vectors
    mvps::Int  #Count of matrix-vector products
end

KrylovSubspace(A, n::Int, order::Int, T::Type)=KrylovSubspace{T,typeof(A)}(A, n, order, Vector{T}[], 0)

KrylovSubspace{T}(A, n::Int, order::Int, v::Vector{Vector{T}}=Vector{T}[])=KrylovSubspace{T,typeof(A)}(A, n, order, v, 0)

KrylovSubspace{T}(A::AbstractMatrix{T}, order::Int, v::Vector{Vector{T}}=Vector{T}[])=
    KrylovSubspace{T,typeof(A)}(A, size(A,2), order, v, 0)

#Reset an existing KrylovSubspace
function KrylovSubspace{T}(A::KrylovSubspace{T}, order::Int=size(A,2), v::Vector{Vector{T}}=Vector{T}[])
    A.order = order
    A.v = v
    A.mvps = 0
end

lastvec(K::KrylovSubspace) = K.v[end]
function nextvec(K::KrylovSubspace)
    K.mvps += 1
    K.A*lastvec(K)
end

size(K::KrylovSubspace) = length(K.v)
function size(K::KrylovSubspace, n::Int)
    if isa(K.A, AbstractMatrix)
        return size(K.A, n)
    else
        return n==2 ? size(K) : error("Illegal dimension $n of KrylovSubspace")
    end
end
eye{T}(K::KrylovSubspace{T}) = isa(K.A, Function) ? x->x : eye(T, size(K.A)...)

function append!{T}(K::KrylovSubspace{T}, w::Vector{T})
    if size(K) == K.order; shift!(K.v) end
    push!(K.v, w)
end
appendunit!{T}(K::KrylovSubspace{T}, w::Vector{T}) = append!(K, w/norm(w))

#Initialize the KrylovSubspace K with a random unit vector
function initrand!{T}(K::KrylovSubspace{T})
    K.v = Vector{T}[initrand!(Array(T, K.n))]
end

#Initialize the KrylovSubspace K with a specified nonunit vector
#If nonzero, try to normalize it
function init!{T}(K::KrylovSubspace{T}, v::Vector{T})
#    K.v = Vector{T}[all(v.==zero(T)) ? v : v/norm(v)]
    K.v = Vector{T}[copy(v)]
end

#Orthogonalize a vector v against the last p basis vectors defined by the
#KrylovSubspace K
function orthogonalize{T}(v::Vector{T}, K::KrylovSubspace{T}, p::Int=K.order;
    method::Symbol=:ModifiedGramSchmidt, normalize::Bool=false)
    Kk = K.v[max(1,end-p+1):end]
    if method == :GramSchmidt
        cs = T[dot(e, v) for e in Kk]
        for (i, e) in enumerate(Kk)
            v -= cs[i] * e
        end
    elseif method == :ModifiedGramSchmidt || method== :Householder
        #The numerical equivalence of ModifiedGramSchmidt and Householder was
        #established in doi:10.1137/0613015
        cs = zeros(T, p)
        for (i, e) in enumerate(Kk)
            cs[i] = dot(e, v)
            v-= cs[i] * e
        end
    else
        error("Unsupported orthogonalization method: $(method)")
    end
    normalize && (v /= convert(T, norm(v)))
    v, cs #Return orthogonalized vector and its coefficients
end

#Return a vector in the Krylov basis, i.e. compute a linear combination of Krylov vectors
#Index 1 = oldest remembered vector
function *(a::AbstractVector, K::KrylovSubspace)
    N = length(a)
    N==length(K.v) && throw(DimensionMismatch("Tried to compute linear combination of $N Krylov vectors but only $(length(K.v)) are present."))
    w = a[N] * K.v[N]
    for j = (N-1):-1:1
        w += a[j] * K.v[j]
    end
    w
end


#=
ported from:
http://docs.ros.org/jade/api/convex_decomposition/html/triangulate_8cpp_source.html

The MIT License (MIT)

Copyright (c) 2006 John W. Ratcliff

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
=#
"""
    area(vertices::AbstractVector{AbstractPoint{3}}, face::TriangleFace)

Calculate the area of one triangle.
"""
function area(vertices::AbstractVector{<:AbstractPoint{3, VT}}, face::TriangleFace{FT}) where {VT,FT}
    v1, v2, v3 = vertices[face]
    return 0.5 * norm(orthogonal_vector(v1, v2, v3))
end

"""
    area(vertices::AbstractVector{AbstractPoint{3}}, faces::AbstractVector{TriangleFace})

Calculate the area of all triangles.
"""
function area(
        vertices::AbstractVector{<:AbstractPoint{3, VT}},
        faces::AbstractVector{TriangleFace{FT}}
    ) where {VT,FT}
    return sum(x->area(vertices, x), faces)
end

function area(contour::AbstractVector{<:AbstractPoint{N, T}}) where {N, T}
    n = length(contour)
    n < 3 && return zero(T)
    A = zero(T)
    p = lastindex(contour)
    q = firstindex(contour)
    while q <= n
        A += cross(contour[p], contour[q])
        p = q; q +=1
    end
    return A * T(0.5)
end

function area(contour::AbstractVector{Point{3, T}}) where {T}
    A = zero(eltype(contour))
    o = contour[1]
    for i in (firstindex(contour)+1):(lastindex(contour)-1)
        A += cross(contour[i] - o, contour[i+1] - o)
    end
    return norm(A)*T(0.5)
end

"""
    in(point, triangle)

 InsideTriangle decides if a point P is Inside of the triangle
 defined by A, B, C.
"""
function Base.in(P::T, triangle::Triangle) where T <: AbstractPoint
    A, B, C = coordinates(triangle)
    a = C .- B
    b = A .- C
    c = B .- A

    ap = P .- A
    bp = P .- B
    cp = P .-C

    a_bp = a[1] * bp[2] - a[2] * bp[1]
    c_ap = c[1] * ap[2] - c[2] * ap[1]
    b_cp = b[1] * cp[2] - b[2] * cp[1]

    t0 = zero(eltype(T))

    return ((a_bp >= t0) && (b_cp >= t0) && (c_ap >= t0))
end

function snip(
        contour::AbstractVector{<: AbstractPoint{N, T}}, u, v, w, n, V
    ) where {N, T}
    A = contour[V[u]]
    B = contour[V[v]]
    C = contour[V[w]]
    x = (
        ((B[1]-A[1])*(C[2]-A[2])) -
        ((B[2]-A[2])*(C[1]-A[1]))
    )
    if 0.0000000001f0 > x
        return false
    end

    for p = 1:n
        ((p == u) || (p == v) || (p == w)) && continue;
        P = contour[V[p]]
        if P in Triangle(A, B, C)
            return false;
        end
    end
    return true;
end

"""
    faces(contour::AbstractArray{Point}, [facetype = GLTriangleFace])

Triangulates a Polygon given as an `AbstractArray{Point}` without holes.
It will return a Vector{`facetype`}, defining indexes into `contour`
"""
function decompose(::Type{FaceType}, points::AbstractArray{P}) where {P<:AbstractPoint, FaceType <: AbstractFace}
    #= allocate and initialize list of Vertices in polygon =#
    result = FaceType[]

    # the algorithm doesn't like closed contours
    contour = if isapprox(last(points), first(points))
        @view points[1:end-1]
    else
        @view points[1:end]
    end

    n = length(contour)
    if n < 3
        error("Not enough points in the contour. Found: $contour")
    end

    #= we want a counter-clockwise polygon in V =#
    if 0 < area(contour)
        V = Int[i for i=1:n]
    else
        V = Int[i for i=n:-1:1]
    end

    nv = n

    #=  remove nv-2 Vertices, creating 1 triangle every time =#
    count = 2*nv   #= error detection =#
    v = nv
    while nv > 2
        #= if we loop, it is probably a non-simple polygon =#
        if 0 >= count
            return result
        end
        count -= 1

        #= three consecutive vertices in current polygon, <u,v,w> =#
        u = v; (u > nv) && (u = 1) #= previous =#
        v = u+1; (v > nv) && (v = 1) #= new v =#
        w = v+1; (w > nv) && (w = 1) #= next =#
        if snip(contour, u, v, w, nv, V)
            #= true names of the vertices =#
            a = V[u]; b = V[v]; c = V[w];
            #= output Triangle =#
            push!(result, convert_simplex(FaceType, TriangleFace(a, b, c))...)
            #= remove v from remaining polygon =#
            s = v; t = v+1
            while t<=nv
                V[s] = V[t]
                s += 1; t += 1
            end
            nv -= 1
            #= resest error detection counter =#
            count = 2*nv
        end
    end

    return result
end

function earcut_triangulate(polygon::Vector{Vector{Point{2, Float64}}})
    lengths = map(x-> UInt32(length(x)), polygon)
    len = UInt32(length(lengths))
    array = ccall(
        (:u32_triangulate_f64, libearcut),
        Tuple{Ptr{GLTriangleFace}, Cint},
        (Ptr{Ptr{Float64}}, Ptr{UInt32}, UInt32),
        polygon, lengths, len
    )
    return unsafe_wrap(Vector{GLTriangleFace}, array[1], array[2])
end

function earcut_triangulate(polygon::Vector{Vector{Point{2, Float32}}})
    lengths = map(x-> UInt32(length(x)), polygon)
    len = UInt32(length(lengths))
    array = ccall(
        (:u32_triangulate_f32, libearcut),
        Tuple{Ptr{GLTriangleFace}, Cint},
        (Ptr{Ptr{Float32}}, Ptr{UInt32}, UInt32),
        polygon, lengths, len
    )
    return unsafe_wrap(Vector{GLTriangleFace}, array[1], array[2])
end

function earcut_triangulate(polygon::Vector{Vector{Point{2, Int64}}})
    lengths = map(x-> UInt32(length(x)), polygon)
    len = UInt32(length(lengths))
    array = ccall(
        (:u32_triangulate_i64, libearcut),
        Tuple{Ptr{GLTriangleFace}, Cint},
        (Ptr{Ptr{Int64}}, Ptr{UInt32}, UInt32),
        polygon, lengths, len
    )
    return unsafe_wrap(Vector{GLTriangleFace}, array[1], array[2])
end

function earcut_triangulate(polygon::Vector{Vector{Point{2, Int32}}})
    lengths = map(x-> UInt32(length(x)), polygon)
    len = UInt32(length(lengths))
    array = ccall(
        (:u32_triangulate_i32, libearcut),
        Tuple{Ptr{GLTriangleFace}, Cint},
        (Ptr{Ptr{Int32}}, Ptr{UInt32}, UInt32),
        polygon, lengths, len
    )
    return unsafe_wrap(Vector{GLTriangleFace}, array[1], array[2])
end

best_earcut_eltype(x) = Float64
best_earcut_eltype(::Type{Float64}) = Float64
best_earcut_eltype(::Type{<:AbstractFloat}) = Float32
best_earcut_eltype(::Type{Int64}) = Int64
best_earcut_eltype(::Type{Int32}) = Int32
best_earcut_eltype(::Type{<:Integer}) = Int64

function faces(polygon::Polygon{Dim, T}) where {Dim, T}
    PT = Point{Dim, best_earcut_eltype(T)}
    points = [decompose(PT, polygon.exterior)]
    foreach(x-> push!(points, decompose(PT, x)), polygon.interiors)
    return earcut_triangulate(points)
end


# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
#
# Description
# ==============================================================================
#
#   Functions related to the conversion from quaternion to Euler angle and axis.
#
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

export quat_to_angleaxis

"""
    quat_to_angleaxis(q::Quaternion{T}) where T

Convert the quaternion `q` to a Euler angle and axis representation (see
[`EulerAngleAxis`](@ref)). By convention, the Euler angle will be kept between
`[0, π] rad`.

# Remarks

This function will not fail if the quaternion norm is not 1. However, the
meaning of the results will not be defined, because the input quaternion does
not represent a 3D rotation. The user must handle such situations.

# Examples

```jldoctest
julia> q = Quaternion(cosd(45/2), sind(45/2), 0, 0);

julia> quat_to_angleaxis(q)
EulerAngleAxis{Float64}:
  Euler angle : 0.785398 rad  (45.0°)
  Euler axis  : [1.0, 0.0, 0.0]
```
"""
function quat_to_angleaxis(q::Quaternion{T}) where T
    # If `q0` is 1 or -1, then we have an identity rotation.
    if abs(q.q0) >= 1 - eps()
        return EulerAngleAxis(T(0), SVector{3, T}(0, 0, 0))
    else
        # Compute sin(θ/2).
        sθo2 = √(q.q1 * q.q1 + q.q2 * q.q2 + q.q3 * q.q3)

        # Compute θ in range [0, 2π].
        θ = 2acos(q.q0)

        # Keep θ between [0, π].
        s = +1
        if θ > π
            θ = T(2π) - θ
            s = -1
        end

        return EulerAngleAxis(θ, s * SVector{3}(q.q1, q.q2, q.q3) / sθo2)
    end
end


module TriangulatedWindingNumbers

include("Vertexes.jl")
include("Simplexes.jl")

function generatesimplices(f::F, lower::AbstractVector{T}, upper::AbstractVector{T},
    gridsize::AbstractVector{<:Integer}) where {F<:Function, T<:Number}
  all(gridsize .> 0) || throw(ArgumentError("gridsize must .> 0"))
  if !(length(lower) == length(upper) == length(gridsize))
    throw(ArgumentError("The lengths of lower, $lower, upper, $upper, and gridsize
                        $gridsize must be the same"))
  end
  if !(all(lower .< upper))
    throw(ArgumentError("lower, $lower must .< upper, $upper"))
  end
  dim = length(lower)
  index2position(i) = (i .- 1) ./ gridsize .* (upper .- lower) .+ lower
  index2values = Dict()
  totaltime = @elapsed for ii ∈ CartesianIndices(Tuple(gridsize .+ 1))
    index = collect(Tuple(ii))
    x = index2position(index)
    index2values[index] = f(x)
  end
  U = typeof(first(index2values)[2])
  simplices = Set{Simplex{T, U}}()
  function generatesimplices!(simplices, direction)
    for ii ∈ CartesianIndices(Tuple(((gridsize .+ 1) .* ones(Int, dim))))
      vertices = Vector{Vertex{T, U}}()
      index = collect(Tuple(ii))
      for i ∈ 1:dim + 1
        vertexindex = [index[j] + ((j == i) ? direction : 0) for j ∈ 1:dim]
        all(1 .<= vertexindex .<= gridsize .+ 1) || continue
        vertex = Vertex{T, U}(index2position(vertexindex),
                              index2values[vertexindex])
        push!(vertices, vertex)
      end
      length(vertices) != dim + 1 && continue
      push!(simplices, Simplex{T, U}(vertices))
    end
  end

  totaltime += @elapsed generatesimplices!(simplices, 1)
  totaltime += @elapsed generatesimplices!(simplices, -1)
  @assert length(simplices) == 2 * prod(gridsize)
  return (simplices, totaltime)
end

function convergenceconfig(dim::Int, T::Type; kwargs...)
  kwargs = Dict(kwargs)
  timelimit = get(kwargs, :timelimit, Inf)
  xtol_abs = get(kwargs, :xtol_abs, zeros(T)) .* ones(Bool, dim)
  xtol_rel = get(kwargs, :xtol_rel, eps(T)) .* ones(Bool, dim)
  ftol_abs = get(kwargs, :ftol_abs, 0)
  ftol_rel = get(kwargs, :ftol_rel, eps())
  stopvalroot = get(kwargs, :stopvalroot, nextfloat(0.0)) # zero makes it go haywire
  stopvalpole = get(kwargs, :stopvalpole, Inf)
  if any(iszero.(xtol_rel) .& iszero.(xtol_abs))
    throw(ArgumentError("xtol_rel .& xtol_abs must not contain zeros"))
  end
  return (timelimit=timelimit, xtol_abs=xtol_abs, xtol_rel=xtol_rel,
          ftol_abs=ftol_abs, ftol_rel=ftol_rel,
          stopvalroot=stopvalroot, stopvalpole=stopvalpole)
end

"""
    solve(f, lower, upper, gridsizeint; kwargs...)

Find the roots and poles of function, `f`, in a hyper-rectangle from
`lower` to `upper` with simplices generating in `gridsizeint`
sub-hyper-rectangles, and options passed in via kwargs.
"""
function solve(f::F, lower::AbstractVector{T}, upper::AbstractVector{T},
    gridsizeint::U; kwargs...) where {F<:Function, T<:Number, U<:Integer}
  gridsize = gridsizeint .* ones(typeof(gridsizeint), length(lower))
  return solve(f, lower, upper, gridsize;  kwargs...)
end

"""
    solve(f, lower, upper, gridsize; kwargs...)

Find the roots and poles of function, `f`, in a hyper-rectangle from
`lower` to `upper` with simplices generating in `gridsize`
sub-hyper-rectangles, and options passed in via kwargs.
"""
function solve(f::F, lower::AbstractVector{T}, upper::AbstractVector{T},
    gridsize::AbstractVector{<:Integer}; kwargs...) where {F<:Function, T<:Number}
  simplices, totaltime = generatesimplices(f, lower, upper, gridsize)
  return solve(f, collect(simplices), totaltime; kwargs...)
end

"""
    solve(f, simplices, totaltime=0.0; kwargs...)

Find the roots and poles of function, `f`, by subdividing `simplices` and
checking to see if the child simplices contain a root or a pole by evaluating
the winding number, and options passed in via kwargs.

Function `f` accepts an n-dimensional array as an argument and returns a complex
number.

# Keyword Arguments
-  stopval (default sqrt(eps())): stopping criterion when function evaluates
equal to or less than stopval
-  xtol_abs (default zeros(T)) .* ones(Bool, dimensionality(s)): stop if
the vertices of simplex get within this absolute tolerance
-  xtol_rel (default eps(T)) .* ones(Bool, dimensionality(s)): stop if
the vertices of simplex get within this relative tolerance
-  ftol_abs (default 0): stop if function evaluations at the
vertices are close to one another by this absolute tolerance
-  ftol_rel (default 1000eps(real(U))): stop if function evaluations at the
vertices are close to one another by this relative tolerance
-  timelimit (default Inf): stop if it takes longer than this in seconds
-  stopvalroot (default nextfloat(0.0): stop if any value is less than or
equal to this
-  stopvalpole (default Inf): stop if any value is greater than or equal to this
"""
function solve(f::F, simplices::AbstractVector{Simplex{T, U}}, totaltime=0.0;
    kwargs...) where {F<:Function, T<:Number, U<:Complex}

  config = convergenceconfig(dimensionality(first(simplices)), T; kwargs...)

  solutions = Vector{Tuple{eltype(simplices), Symbol}}()
  while totaltime < config[:timelimit]
    newsimplices = Vector{eltype(simplices)}()
    for (i, simplex) ∈ enumerate(simplices)
      windingnumber(simplex) == 0 && continue
      innermost = closestomiddlevertex(simplex)
      Δt = @elapsed centroidvertex = centroidignorevertex(f, simplex, innermost)
      for vertex ∈ simplex
        isequal(vertex, innermost) && continue
        newsimplex = deepcopy(simplex)
        swap!(newsimplex, vertex, centroidvertex)
        isequal(newsimplex, simplex) && continue
        windingnumber(newsimplex) == 0 && continue
        isconverged, returncode = assessconvergence(newsimplex, config)
        isconverged && push!(solutions, (newsimplex, returncode))
        isconverged || push!(newsimplices, newsimplex)
      end
      (totaltime += Δt) > config[:timelimit] && break
    end
    simplices = newsimplices
    isempty(simplices) && break
  end # while
  return solutions
end # solve

end


###################################################################
#                TEST Metric estimations
###################################################################
using Test
#include("../src/OptimizedNeuralNetwork.jl")
include("../src/metric_estimation.jl")

@testset "Regression metrics" begin
    f(x)=x.*x
    X = [1,-1,-2,2]
    Y = map(f, X)
    # Comprobamos que devuelve que para este caso en concreto son correctas:
    # la media de error, mediana de error, la desviación media y el coeficiente de correlación
    @test regression(X,Y,f) == (0,0,0,1)
end


@testset "Fenwick Tree" begin
    @testset "initialisation" begin
        f1 = FenwickTree{Float64}(5)
        @test f1.bi_tree == zeros(5)
        @test length(f1) == 5
        @test eltype(f1) == Float64
        @test eltype(typeof(f1)) == Float64

        arr = [1.2, 8.7, 7.2, 3.5]
        f3 = FenwickTree(arr)
        @test f3[1] == 1.2
        @test length(f3) == length(arr)
    end

    @testset "Point update and Point queries" begin
        f1 = FenwickTree{Int}(10)
        inc!(f1, 10, 5)
        @test prefixsum(f1, 10) == 5
        @test prefixsum(f1, 9) == 0
        @test prefixsum(f1, 1) == 0
        inc!(f1, 5, 7)
        @test prefixsum(f1, 8) == 7
        @test prefixsum(f1, 10) == 12
        @test prefixsum(f1, 5) == 7
        @test prefixsum(f1, 4) == 0

        dec!(f1, 7, 2)
        @test prefixsum(f1, 8) == 5
        @test prefixsum(f1, 6) == 7

        incdec!(f1, 2, 6, 3)
        @test prefixsum(f1, 3) == 3
        @test prefixsum(f1, 7) == 5

        @test prefixsum(f1, 0) == 0
        @test prefixsum(f1, -100) == 0

        @test_throws ArgumentError inc!(f1, 11)
    end

end


"""
    TwoSectionView{T<:Real} <: AbstractGraph{Int64}

Represents a 2-section view of a hypergraph `h`.
Note this is a view - changes to the original hypergraph will be automatically reflected in the view.

**Constructors**

TwoSectionView(::Hypergraph)

The 2-section view of a hypergraph is suitable for processing with the LightGraphs.jl package.
Several LightGraphs methods are provided for the compability.

"""
struct TwoSectionView{T<:Real} <: AbstractGraph{Int}
    h::Hypergraph{T}
end

LightGraphs.nv(t::TwoSectionView) = length(t.h.v2he)

LightGraphs.vertices(t::TwoSectionView) = Base.OneTo(nv(t))

function LightGraphs.ne(t::TwoSectionView)
    s = 0
    for x in t.h.he2v
        s += length(x) * (length(x) - 1)
    end
    div(s, 2)
end

"""
    LightGraphs.all_neighbors(t::TwoSectionView, v::Integer)

Returns N(v) (the vertex v is not included in N(v))
"""
function LightGraphs.all_neighbors(t::TwoSectionView, v::Integer)
    neighbors = Set{Int}()
    for he in keys(t.h.v2he[v])
        union!(neighbors, keys(t.h.he2v[he]))
    end
    delete!(neighbors, v) #remove v from its neighborhood
    collect(neighbors) #returns the corresponding array
end

function LightGraphs.has_edge(t::TwoSectionView, s, d)
    !isempty(intersect(keys(t.h.v2he[s]), keys(t.h.v2he[d])))
end

LightGraphs.outneighbors(t::TwoSectionView, v::Integer) =
    LightGraphs.all_neighbors(t::TwoSectionView, v)

LightGraphs.inneighbors(t::TwoSectionView, v::Integer) =
    LightGraphs.all_neighbors(t::TwoSectionView, v)

"""
    LightGraphs.SimpleGraph(t::TwoSectionView)

Creates a `LightGraphs.SimpleGraph` representation of a `TwoSectionView` t.

This creates a copy of the date. Note that the weights information is not stored
in the created `SimpleGraph`.
"""
function LightGraphs.SimpleGraph(t::TwoSectionView)
    g = SimpleGraph(nv(t))
    for v in LightGraphs.vertices(t)
        neighbors_v = LightGraphs.all_neighbors(t, v)
        for neighbor in neighbors_v
            add_edge!(g, v, neighbor)
        end
    end
    g
end

LightGraphs.is_directed(t::TwoSectionView) = false

"""
    shortest_path(t::TwoSectionView,source::Int, target::Int)

Finds a single shortest path in a graph `b` between vertices
`source` and `target`.
Note that if several paths of the same length exist, only one
will be returned.

"""
function shortest_path(t::TwoSectionView, source::Int, target::Int)
    checkbounds(t.h.v2he, source)
    checkbounds(t.h.v2he, target)
    dj = dijkstra_shortest_paths(t, source)
    enumerate_paths(dj)[target]
end


######### Stan program example  ###########

using StanSample, MCMCChains

eightschools ="
data {
  int<lower=0> J; // number of schools
  real y[J]; // estimated treatment effects
  real<lower=0> sigma[J]; // s.e. of effect estimates
}
parameters {
  real mu;
  real<lower=0> tau;
  real eta[J];
}
transformed parameters {
  real theta[J];
  for (j in 1:J)
    theta[j] <- mu + tau * eta[j];
}
model {
  eta ~ normal(0, 1);
  y ~ normal(theta, sigma);
}
"

schools8data = Dict("J" => 8,
  "y" => [28,  8, -3,  7, -1,  1, 18, 12],
  "sigma" => [15, 10, 16, 11,  9, 11, 10, 18],
  "tau" => 25
)

sm = SampleModel("schools8", eightschools)

rc = stan_sample(sm, data=schools8data)

if success(rc)
  chns = read_samples(sm, :mcmcchains)
end


using Images
using Base.Iterators
using Printf

function n_kakkei(N)
    return function(x, y)
        N_max = floor(N/2)
        result = 0
        for k in 1:N_max
            c1 = 2k*π/N
            result += abs(x*sin(c1) - abs(y)*cos(c1)) - sin(c1)
        end
        result += 1/2 * abs(y*sin(N*π/2)) - 1/2 * x*cot(N_max*π/N) + 1/2 * cot(N_max*π/N)
        return abs(result)
    end
end

function main()
    N::Int64 = 128
    fimg = zeros(N, N)
    X = 1:N
    Y = 1:N
    # 正N角形 -> 正N+1角形へ
    for n in 3:10
        f1 = n_kakkei(n)
        f2 = n_kakkei(n+1)
        for t in 0:0.05:1
            f3 = (x, y) -> f1(x, y)*(1-t) + f2(x, y)*t
            map(x -> fimg[x...] = f3(((x.-N/2)./(N/3))...), product(X, Y))
            # @show max(fimg...)
            fimg = fimg ./ max(fimg...)
            img = Gray.(fimg)
            str = @sprintf "%02d_%.2f" n t
            save("img_henkei_$(str).jpg", img)
        end
    end
end

main()


using Arpack
using LinearAlgebra

"""
	approx_eigmin(M, iters)

Return the approximate smallest eigenvalue and eigenvector of a matrix
`M` in `iters` iterations.
"""
function approx_eigmin(M)
	values, vecs = eigs(M, nev=1, tol=1e-1, which=:SR)
	values[1], vec(vecs)
end


using DynamicHMC, LogDensityProblems
using Distributions, Parameters, Random
using ForwardDiff, StatsPlots

include(string(pwd(),"/src/simulation.jl"))
include(string(pwd(),"/gali_bayesian.jl"))
include(string(pwd(),"/mcmc/priors_v2.jl"))
include(string(pwd(),"/mcmc/foos.jl"))

yy,shocks = simulate_dsge(GAMMA_0,GAMMA_1,PSI,PI,500)

prob = DSGE_Model(yy[:,2],1/3)

prob((bet = 0.99,epsilon = 6,theta=2/3,sig=1,s2=1,phi=1,phi_pi=1.5,phi_y=0.5/4,rho_v=0.5))

t = problem_transform(prob)

P = TransformedLogDensity(t,prob)

aa = [0.99,6,2/3,1,1,1,1.5,0.5/4,0.5]

bb = TransformVariables.inverse(t,(bet = 0.99,epsilon = 6,theta=2/3,sig=1,s2=1,phi=1,phi_pi=1.5,phi_y=0.5/4,rho_v=0.5))

LogDensityProblems.logdensity(P,bb)

grad_p = ADgradient(:Zygote,P)

#grads = Calculus.gradient(P)

res = @time mcmc_with_warmup(Random.GLOBAL_RNG, grad_p,10000)

posterior = transform.(t, res.chain)

post_array = zeros(10000,9)

for i in 1:10000
    @unpack bet, epsilon, theta, sig, s2,phi,phi_pi,phi_y,rho_v = posterior[i,:][1]
    post_array[i,:] = [bet epsilon theta sig s2 phi phi_pi phi_y rho_v]
end

StatsPlots.density(post_array[:,1], legend = :none)
title!(latexify("beta()"))
vline!([true_pars[:bet]])
vline!([mean(post_array[:,1])])

StatsPlots.density(post_array[:,2], legend = :none)
title!(latexify("epsilon()"))
vline!([true_pars[:epsilon]])
vline!([mean(post_array[:,2])])

StatsPlots.density(post_array[:,3], legend = :none)
title!(latexify("theta()"))
vline!([true_pars[:theta]])
vline!([mean(post_array[:,3])])

StatsPlots.density(post_array[:,4], legend = :none)
title!(latexify("sigma()"))
vline!([true_pars[:sig]])
vline!([mean(post_array[:,4])])

StatsPlots.density(post_array[:,5], legend = :none)
title!(latexify("sigma()^2"))
vline!([true_pars[:s2]])
vline!([mean(post_array[:,5])])

StatsPlots.density(post_array[:,6], legend = :none)
title!(latexify("phi()"))
vline!([true_pars[:phi]])
vline!([mean(post_array[:,6])Tra])

StatsPlots.density(post_array[:,7], legend = :none)
title!(latexify("phi()_pi",cdot = false))
vline!([true_pars[:phi_pi]])
vline!([mean(post_array[:,7])])

StatsPlots.density(post_array[:,8], legend = :none)
title!(latexify("phi()_y",cdot = false))
vline!([true_pars[:phi_y]])
vline!([mean(post_array[:,8])])

StatsPlots.density(post_array[:,9], legend = :none)
title!(latexify("rho()_v",cdot = false))
vline!([true_pars[:rho_v]])
vline!([mean(post_array[:,9])])


#Careful! needs LinearAlgebra in Reconstruction.jl
"""
 SeisSpitzFX(d;<keyword arguments>)

 First order f-x interpolation using Spitz' method.
 Input size is size(d)= (nt,nh). Output size is (nt,2*nh-1)

# Arguments
-`dt`: sampling interval in secs
-`npf`: prediction filter length
- `pre1` : percentage of pre-whitening for Pef estimation
- `pre2` : percentage of pre-whitening for estimation of missing samples
- `flow` : minimun temporal frequency to reconstruct (Hz)
- `fhigh` : maximun temporal frequency to reconstruct (Hz)

# Example
'''julia
d_interp = SeisSpitzFX(d)
'''
*  Reference: Spitz, S., 1991, Seismic trace interpolation in the F-X domain: Geophysics, 56, 785-794.
"""
function SeisSpitzFX(d;dt=0.004,npf=2,pre1=1,pre2=1,flow=1,fhigh=90)

# 1) Fourier Transform to FX domain

 vect = size(d);
 nt = vect[1]
 nf1 = nextpow(2,vect[1]);
 nf2 = 2*nf1
 nh = vect[2]

 d1,ifl1,ifh1 = pad(d,dt,flow,fhigh)
 D1 = fft(d1,1)

 d2,ifl2,ifh2 = pad(d,dt,flow,fhigh;npad=2)
 D2 = fft(d2,1)

 INTDF = zeros(typeof(D2[1]),nf1, 2*nh-1);

 for ia = ifl1:ifh1;

# 2) Select frequency slices from frequencies f and f/2

    x1 = transpose(D1[ia,:]);
    x2 = transpose(D2[ia,:]);
# 3) Estime the PFs needed for frequency sample ia;

    PF = prediction_filter(x2,npf,pre1);

# 4) Interpolate spatial data at freq. sample ia
    y = interpolate_freq(x1,PF,pre2);

# 5) Replace interpolated frequency slice into final matrix

    INTDF[ia,:] = transpose(y);

end

# 6) Honor symmetries

nf1_2 = convert(Int,round(nf1/2))

INTDF[nf1_2+2:nf1,:]=conj(reverse(INTDF[2:nf1_2,:],dims=1));

# 7) Antitransform from f-x to t-x
 d_interp = real(ifft(INTDF,1));

 d_interp = d_interp[1:nt,:];

 return d_interp
end

#*******************************************************************************
"""
**INTERPOLATE_FREQ**
*fx_interpolation for reconstruction of traces*

**IN**
*   x:   spatial data to be recostruction at a given freq.
*       PF:  prediction error filter
*       pre: prewhitening in percentage

**OUT**
*  y:   reconstructed vector of data at a particular frequency
"""
function interpolate_freq(x,PF,pre)

 np = length(PF);
 nx = length(x);
 ny = 2*nx-1;       # length of interpolated signal

# Forward step using PFs for interpolation
 TMPF1 = hcat(reverse(transpose(PF),dims=2),-1)
 W1 = transpose(Convmtx(TMPF1,ny-np));

# Backward step

 TMPF2 = conj(reverse(TMPF1,dims=2));
 W2 = transpose(Convmtx(TMPF2,ny-np));

# Forward and backward combined in an augmented system

 WT = [W1;W2]

# Separation of  Matrix into  known and unknown parts
 A = WT[:,2:2:ny];

B = -1 .* WT[:,1:2:ny]*transpose(x);
# Least squares solution for missing data, prewhitening
# is added to guarantee stability

 R= A'*A;
 g = A'*B;

 mu = (pre/100.)*tr(R)/(nx-1);

y1 =  (R+mu*Matrix(I,nx-1,nx-1))\g;

 y = zeros(typeof(x[1]),1,ny);

y[1:2:ny]=x;
 y[2:2:ny]=transpose(y1);

 return y
 end

 #******************************************************************************
"""
**PREDICTION_FILTER**
* compute prediction filters

**IN**
*   VEC:   spatial data at a given frequency
*       np:    length of pef
*       pre:   prewhitening in percentage

**OUT**
*  PF:    prediction filter
"""
function prediction_filter(xi,np,pre);

 ns = length(xi);

C = zeros(typeof(xi[1]),ns-np+1,np+1);

 for j=1:ns-np

    C[j,:]=xi[j+np:-1:j];

 end

 # Make kernel for pef estimation (forward and backward prediction included)

 A = [C[:,2:np+1];conj(reverse(C[:,1:np],dims=2))];

# RHS of system of equations

 B = [C[:,1];conj(C[:,np+1])];

 # Solve A.PF = B using least-squares with pre-whitening

 R = A'*A;
 g = A'*B;

 mu = real((pre/100.)*tr(R)/np);

 PF =  (R+mu*Matrix(I,np,np))\g;   # same as inv(R)*g

 return PF
 end

#*****************************************************

function pad(d,dt,flow,fhigh;npad=1)

vect = size(d)
nt = vect[1]

#calculate index frequencies

nf = npad*nextpow(2,nt)

dpad = zeros(nf,vect[2])

dpad[1:nt,:] = d

ilow = convert(Int64,floor(flow*nf*dt)+1)
ihigh = convert(Int64,floor(fhigh*nf*dt)+1)

return dpad,ilow,ihigh
end


module EfficientGlobalOptimization

using KrigingModel

export KrigingTuner
export sampling, LHS, GRID
export NoFeasibleInfill
export EGO, optimum, acquire, history, lowerbounds, upperbounds
export Acquire, EGOAquisition, ProbabilityOfImprovement, ExpectedImprovement
export constrains, ConstrainedAquisition
#export ego_save
export ParetoSet

@enum EGOSense Min=1 Max=-1
const _sym2sense = Dict(Symbol(i)=>i for i in instances(EGOSense))
function sym2sense(s::Symbol)
    try
        return _sym2sense[s]
    catch
        error("invalid sense, must be :Min or :Max")
    end
end

include("sampling/sampling.jl")
include("aquisition/aquisition.jl")
include("acquire/acquire.jl")
include("krgtuner.jl")
include("EGO.jl")
include("testfunction/testfunction.jl")
include("paretoset.jl")
end # module


# Load Julia packages (libraries)

using Pkg, DrWatson

#@quickactivate "SR2StanPluto"
using StanSample
using StatisticalRethinking

# Define the Stan language model

stan8_4s = "
data{
  int N;
  vector[N] y;
}
parameters{
  real sigma;
  real alpha;
  real a1;
  real a2;
}
model{
  real mu;
  mu = a1 + a2;
  y ~ normal( mu , sigma );
}
";

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

m8_4s = SampleModel("m8.4s", stan8_4s);

# Input data for cmdstan

m8_4_data = Dict("N" => 100, "y" => rand(Normal(0, 1), 100));

# Sample using cmdstan

rc8_4s = stan_sample(m8_4s, data=m8_4_data);

rethinking = "
        mean   sd  5.5% 94.5% n_eff Rhat
alpha 0.06 1.90 -2.22  2.49  1321    1
sigma 2.15 2.32  0.70  5.21   461    1
";

# Describe the draws

if success(rc)
  part8_4s = read_samples(m8_4s, :particles)
  part8_4s |> display
end


include("./Benchmarks.jl")

import Benchmarks

using HypothesisTests

noToken = [lowDiversityWithChallenge(1000, 50, 70, Benchmarks.registryMean) for i in 1:20]
v = var(noToken)
m = mean(noToken)

println("Var: $v Mean: $m")

token = [binaryTokenChallenge(1000, 50, 70, Benchmarks.registryMean) for i in 1:20]
vt = var(token)
mt = mean(token)

println("Var: $vt Mean: $mt")

t = UnequalVarianceTTest(noToken, token)

println(t)


# Gives the fidelity and success probability of several well known
# distillation protocols for 2 copies of a bipartite state consisting of two qubits

export toBellBasis
export BBPSSWParam
export DEJMPSParam
export EPLDParam

import Base.convert
convert(::Type{Float64}, x::Array{Float64,1}) = x[1]
convert(::Type{Float64}, x::Array{Float64,2}) = x[1]

toMatrix(x::VecOrMat) = x*x'

e0 = [1; 0]
e1 = [0; 1]

proj0 = toMatrix(e0)
proj1 = toMatrix(e1)

phi_plus = 1/√2 * (kron(e0,e0) + kron(e1,e1))
phi_min  = 1/√2 * (kron(e0,e0) - kron(e1,e1))
psi_plus = 1/√2 * (kron(e0,e1) + kron(e1,e0))
psi_min  = 1/√2 * (kron(e0,e1) - kron(e1,e0))
basis = Any[phi_plus, phi_min, psi_plus, psi_min]

function toBellBasis(rho::AbstractMatrix)
  output = zeros(size(rho))
  i = 1
  for x in basis
    j = 1
    for y in basis
      output[i, j] = (x' * rho * y)[1]
      j += 1
    end
    i += 1
  end
  return output
end

""" `(F, p_succ) = DEJMPSParam(rho)`

Determines the performance of DEJMPS for two copies of the input state *rho* (consisting of two qubits) and returns the fidelity and success probability achieved.
The procedure optimises over local single qubit rotations that permute the bell diagonal coefficients before the CNOTs to achieve the highest output fidelity.
returns (F_out, p_succ)
"""
function DEJMPSParam(rho::AbstractMatrix)
  M = toBellBasis(rho);
  m1 = M[1, 1] # phi_+
  m2 = M[2, 2] # phi_-
  m3 = M[3, 3] # psi_+
  m4 = M[4, 4] # psi_-

  p1, p2, p3, p4 = sort([m1; m2; m3; m4], rev = true)

  p = (p1 + p4)^2 + (p2 + p3)^2
  F = (p1^2 + p4^2)/p
  return (F, p)
end

""" `(F, p_succ) = BBPSSWParam(F)`

Determines the performance of BBPSSW for two copies of the input state (consisting of two qubits) with fidelity *F* each.
returns (F_out, p_succ)
"""
function BBPSSWParam(F::Real)
  p_succ = F^2 + 2F*(1-F)/3 + 5((1-F)/3)^2
  F_out = (F^2 + ((1-F)/3)^2)/p_succ
  return (F_out, p_succ)
end

""" `(F, p_succ) = BBPSSWParam(rho)`

Determines the performance of BBPSSW for two copies of the input state *rho* (consisting of two qubits).
returns (F_out, p_succ)
"""
function BBPSSWParam(rho::AbstractMatrix)
  F = max(diag(toBellBasis(rho))...)
  return BBPSSWParam(F)
end

""" `(F, p_succ) = EPLDParam(p, pd)`

Determines the performance of EPL-D for rStateCorrPhase.
The inputs are the *p* and *pd* parameters of rStateCorrPhase.
returns (F_out, p_succ)
"""
function EPLDParam(p::Number, pd::Number)
  return (pd, 0.5*p^2)
end

""" `(F, p_succ) = EPLDParam(rho)`

Determines the performance of EPL-D for an arbitrary bipartite state of dimensions nA=nB=4.
returns (F_out, p_succ)
"""
function EPLDParam(rho::AbstractMatrix)

  #Define the CNOT operation:
  CNOT = [1 0 0 0; 0 1 0 0; 0 0 0 1; 0 0 1 0]

  #Alice and Bob apply CNOT:
  rho2 = kron(CNOT, CNOT) * rho * kron(CNOT, CNOT)'

  #Alice and Bob measure the flags and postselect them to be 11.
  #Projector on the success space on one side
  projSucc = kron(eye(2), proj1)

  #Success probability:
  p_succ = trace(kron(projSucc,projSucc) * rho2)

  #State on 2 qubit space after postselection:
  rhoOut = partialtrace((kron(projSucc,projSucc) * rho2 * kron(projSucc,projSucc)')/p_succ, [2,4], [2,2,2,2])

  #Output fidelity
  M = toBellBasis(rhoOut)

  F = max(M[1, 1], M[2, 2], M[3, 3], M[4, 4])

  return (F, p_succ)
end


apply_process(C::Operator,ψ::Ket) = apply_process(C,Operator(ψ))
# assumes Choi matrix
apply_process(C::Operator,ρ::Operator{T,D}) where {T,D} =
    ptrace((transpose(ρ)⊗qeye(dims(ρ)))*C,ntuple(identity,D))
# assumes Kraus operators
apply_process(As::Vector{<:Operator},ρ::Operator) = sum(A->A*ρ*A',As)

function gate_fidelity_choi(C::Operator,U::Operator)
    # calculate the average gate fidelity given a Choi matrix and a unitary gate
    if dims(C) != (dims(U)...,dims(U)...)
        throw(DimensionMismatch())
    end
    V = qeye(dims(U)) ⊗ U
    return gate_fidelity_choi(V'*C*V)
end

function gate_fidelity_choi(C::Operator{T,D}) where {T,D}
    # calculate the average gate fidelity given a Choi matrix
    # Johnston, N. & Kribs, D. W. Quantum gate fidelity in terms of Choi
    # matrices. J. Phys. A Math. Theor. 44, 495303 (2011).
    dm = dims(C)
    if D%2 != 0 || prod(i->dm[i],1:D÷2) != prod(i->dm[i],D÷2+1:D)
        throw(ArgumentError("not a valid Choi matrix!"))
    end
    d = prod(i->dm[i],1:D÷2)
    TrA² = let CC = Operator(data(C),(d,d)) # rewrap operator to use tensored indexing
        real(sum(CC[(i,i),(j,j)] for i=0:d-1, j=0:d-1))
    end
    return (d + TrA²)/(d^2 + d)
end

function gate_fidelity_kraus(As::Vector{Operator{T,D}}) where {T,D}
    # calculate the average gate fidelity given a list of Kraus operators
    d = prod(i->dims(first(As))[i],1:D)
    return (d + sum(abs2∘trace,As))/(d^2 + d)
end

function gate_fidelity_kraus(As::Vector{<:Operator{T1,D}},U::Operator{T2,D}) where {T1,T2,D}
    # calculate the average gate fidelity given a list of Kraus operators and
    # a unitary gate
    dimsmatch(As,U)
    d = prod(i->dims(U)[i],1:D)
    return (d + sum(abs2∘inner,product(As,(U,))))/(d^2 + d)
end

# conversion functions

function operator_to_choi(O::Operator)
    newdims = (dims(O)...,dims(O)...)
    return Operator(vec(data(O))*vec(data(O))',newdims)
end

function kraus_to_natural(As::Vector{<:Operator})
    return sum(A->conj(A)⊗A,As)
end

function natural_to_kraus(K::Operator)
    # we go through the Choi representation since the extra conversion step is
    # basically free compared to the eigendecomposition
    return choi_to_kraus(natural_to_choi(K))
end

function natural_to_choi(K::Operator)
    # the conversion between the natural and Choi is a self-inverse
    return choi_to_natural(K)
end

function choi_to_natural(C::Operator)
    # the "natural" representation is the same as Choi with shuffled dimensions
    length(dims(C)) == 2 || throw(ArgumentError("multi-space operators not supported yet!"))
    d = dims(C)[1]
    K = reshape(permutedims(reshape(data(C),(d,d,d,d)),[1,3,2,4]),(d^2,d^2))
    return Operator(K,dims(C))
end

function choi_to_kraus(C::Operator)
    length(dims(C)) == 2 || throw(ArgumentError("multi-space operators not supported yet!"))
    @static if VERSION < v"0.7.0-"
        @inbounds for i = 1:size(C,1); C[i,i] = real(C[i,i]); end
    end
    D,V = eigen(Hermitian(data(C)),1E-10,Inf)
    return [Operator(unvec(√(D[i])*V[:,i]),(dims(C)[1],)) for i = length(D):-1:1]
end

function kraus_to_choi(As::Vector{<:Operator})
    length(dims(As[1])) == 1 || throw(ArgumentError("multi-space operators not supported yet!"))
    d = dims(As[1])[1]
    return Operator(sum(A->vec(data(A))*vec(data(A))',As),(d,d))
end

function choi_to_chi(C::Operator)
    length(dims(C)) == 2 || throw(ArgumentError("multi-space operators not supported yet!"))
    B = Operator(_choi_to_pauli_basis((dims(C)[1],)),dims(C))
    return (B*C*B')/2^1 # TODO: fix for multi-qubit
end

function chi_to_choi(χ::Operator)
    length(dims(χ)) == 2 || throw(ArgumentError("multi-space operators not supported yet!"))
    B = Operator(_choi_to_pauli_basis((dims(χ)[1],)),dims(χ))
    return (B'*χ*B)/2^1 # TODO: fix for multi-qubit
end

function _choi_to_pauli_basis(D::NTuple{N,Int}) where N
    all(d->d==2,D) || throw(ArgumentError("only valid with qubits!"))
    B = Matrix{Float64}(undef,4^N,4^N)
    # change to I,X,Y,Z basis (Y = -i*σy)
    Ps = ([1 0; 0 1], [0 1; 1 0], [0 -1; 1 0], [1 0; 0 -1])
    for (i,ops) in enumerate(product(ntuple(_->Ps,N)...))
        B[:,i] = vec(reduce(⊗,ops))
    end
    return B
end

Base.vec(O::Operator) = Ket(copy(vec(data(O))),dims(O).^2)

unvec(v::Ket) = Operator(copy(unvec(data(O))),isqrt.(dims(v)))

super(A::Operator) = conj(A) ⊗ A # represents A*ρ*A†
super(A::Operator,B::Operator) = transpose(B) ⊗ A

function unvec(vecA::AbstractVector)
    # unvectorize a vector into a square matrix
    d = isqrt(length(vecA))
    return reshape(vecA,(d,d))
end

function super(A::AbstractMatrix,B::AbstractMatrix=data(qeye(size(A,1))))
    # represents the action AρB, on a vectorized version of ρ
    return transpose(B) ⊗ A
end


using Smg2s
using Test
using SparseArrays
using Plots

using LinearAlgebra
using Random
using DelimitedFiles

V1 = readdlm("./examples/data/v1.txt", skipstart=2)[:,2]
V2 = readdlm("./examples/data/v2.txt", skipstart=2)[:,2:3]

@test size(V1,1) == size(V2,1)

n = size(V1,1)

function f1(idx::Integer, n = n)
    return V1[idx] + 0.0 * im
end

function f2(idx::Integer, n = n)
    return V2[idx][1] + V2[idx][1] * im
end

##initalization of SMG2S, which effects only the sparsity of generated matrices
diag_l = -50
diag_u = -10

nbOne = 6

nilpvec = zeros(Int64, n-1)

cnt = 0

while cnt < n-1
    l₁ = rand(1:nbOne)
    l₀ = rand(1:nbOne)

    for i = cnt+1:l₁+cnt
        if i <= n-1
            nilpvec[i] = 1
        end
    end

    global cnt += l₀ + l₁

end

#nilpMatrix = sparse(diagm(10=>nilpvec[1:n-10]))

nilp = Nilp(nilpvec, 10, n)

##v1.txt case: non-symmetric matrix
spec1 = zeros(ComplexF64, n)
Spectrum!(spec1, f1, n)

Am = spzeros(Float64, n, n)
initMat!(Am, diag_l, diag_u, n; scale=0.1, sparsity=.5)
@time genMat1 = nonsym(n, diag_l, diag_u, spec1, Am, nilp)

@info "sparsity = " nnz(genMat1) / (n * n)

genspec1 = eigen!(Matrix(genMat1)).values
p1=scatter(real(spec1), imag(spec1), ylims=(-1,1), markercolor = :green, markern = 7, markersize = 4, label="given spectrum")
p1=scatter!(real(genspec1), imag(genspec1), ylims=(-1,1), markercolor = :red, markern = 5, markersize = 3, label="spectrum of generated matrix",legendfontsize=6,legend=:topleft)

##v2.txt case: non-Hermitian matrix
spec2 = zeros(ComplexF64, n)
Spectrum!(spec2, f2, n)

Am = spzeros(ComplexF64, n, n)
initMat!(Am, diag_l, diag_u, n; scale=0.1, sparsity=.005)
@time genMat2 = nonherm(n, diag_l, diag_u, spec2, Am, nilp)

@info "sparsity = " nnz(genMat2) / (n * n)

genspec2 = eigen!(Matrix(genMat2)).values
p2=scatter(real(spec2), imag(spec2), markercolor = :green, markern = 7, markersize = 4, label="given spectrum")
p2=scatter!(real(genspec2), imag(genspec2), markercolor = :red, markern = 5, markersize = 3, label="spectrum of generated matrix", legendfontsize=5, legend=:topleft)

p3=spy(dropzeros(genMat1), legend = :none)

l = @layout [a  b;c]
plot(p1, p2, p3, layout = l)


function reach_ASB07_decomposed!(R::Vector{<:ReachSet},
                                 Ωhat0::Vector{<:LazySet},
                                 U::Union{ConstantInput, Nothing},
                                 ϕ::AbstractMatrix{IntervalArithmetic.Interval{NUM}},
                                 N::Int,
                                 δ::Float64,
                                 max_order::Int,
                                 n::Int,
                                 partition,
                                 blocks) where {NUM}
    # initial reach set
    t0, t1 = zero(δ), δ
    R[1] = ReachSet(CartesianProductArray(Ωhat0[blocks]), t0, t1)

    b = length(blocks)
    Rₖ_array = Vector{LazySet{NUM}}(undef, b)
    ϕpowerk = copy(ϕ)

    if U != nothing
        Whatk = Vector{Zonotope{NUM}}(undef, b)
        inputs = next_set(U)
        @inbounds for i in 1:b
            bi = partition[blocks[i]]
            Whatk[i] = linear_map(proj(bi, n), inputs)
        end
    end

    k = 2
    while k <= N
        for i in 1:b
            bi = partition[blocks[i]]
            Rₖ_bi = ZeroSet(length(bi))
            for (j, bj) in enumerate(partition)
                block = IntervalMatrix(ϕpowerk[bi, bj])
                if !iszero(block)
                    Rₖ_bij = overapproximate(block * Ωhat0[j], Zonotope)
                    Rₖ_bi = minkowski_sum(Rₖ_bi, Rₖ_bij)
                end
            end
            if U != nothing
                Rₖ_bi = minkowski_sum(Rₖ_bi, Whatk[i])
            end
            Rₖ_bi = reduce_order(Rₖ_bi, max_order)  # reduce order
            Rₖ_array[i] = Rₖ_bi
        end
        Rₖ = CartesianProductArray(copy(Rₖ_array))

        # store reach set
        t0 = t1
        t1 += δ
        R[k] = ReachSet(Rₖ, t0, t1)

        if U != nothing
            for i in 1:b
                bi = partition[blocks[i]]
                block_row = IntervalMatrix(row(ϕpowerk, bi))
                W_new = overapproximate(block_row * inputs, Zonotope)
                Whatk[i] = minkowski_sum(Whatk[i], W_new)
            end
        end

        ϕpowerk *= ϕ

        k += 1
    end
    return R
end


module Operators

export
    Δx, Δy, ΔzF, ΔzC,
    Axᵃᵃᶜ, Axᵃᵃᶠ, Ayᵃᵃᶜ, Ayᵃᵃᶠ, Azᵃᵃᵃ,
    Vᵃᵃᶜ, Vᵃᵃᶠ,
    δxᶜᵃᵃ, δxᶠᵃᵃ, δyᵃᶜᵃ, δyᵃᶠᵃ, δzᵃᵃᶜ, δzᵃᵃᶠ,
    Ax_ψᵃᵃᶠ, Ax_ψᵃᵃᶜ, Ay_ψᵃᵃᶠ, Ay_ψᵃᵃᶜ, Az_ψᵃᵃᵃ,
    δᴶxᶜᵃᶜ, δᴶxᶜᵃᶠ, δᴶxᶠᵃᶜ, δᴶxᶠᵃᶠ, δᴶyᵃᶜᶜ, δᴶyᵃᶜᶠ, δᴶyᵃᶠᶜ, δᴶyᵃᶠᶠ, δᴶzᵃᵃᶜ, δᴶzᵃᵃᶠ,
    ∂xᶜᵃᵃ, ∂xᶠᵃᵃ, ∂yᵃᶜᵃ, ∂yᵃᶠᵃ, ∂zᵃᵃᶜ, ∂zᵃᵃᶠ,
    ∂²xᶜᵃᵃ, ∂²xᶠᵃᵃ, ∂²yᵃᶜᵃ, ∂²yᵃᶠᵃ, ∂²zᵃᵃᶜ, ∂²zᵃᵃᶠ,
    ∂⁴xᶜᵃᵃ, ∂⁴xᶠᵃᵃ, ∂⁴yᵃᶜᵃ, ∂⁴yᵃᶠᵃ, ∂⁴zᵃᵃᶜ, ∂⁴zᵃᵃᶠ,
    ℑxᶜᵃᵃ, ℑxᶠᵃᵃ, ℑyᵃᶜᵃ, ℑyᵃᶠᵃ, ℑzᵃᵃᶜ, ℑzᵃᵃᶠ,
    ℑᴶxᶜᵃᵃ, ℑᴶxᶠᵃᵃ, ℑᴶyᵃᶜᵃ, ℑᴶyᵃᶠᵃ, ℑᴶzᵃᵃᶜ, ℑᴶzᵃᵃᶠ,
    ℑxyᶜᶜᵃ, ℑxyᶠᶜᵃ, ℑxyᶠᶠᵃ, ℑxyᶜᶠᵃ, ℑxzᶜᵃᶜ, ℑxzᶠᵃᶜ, ℑxzᶠᵃᶠ, ℑxzᶜᵃᶠ, ℑyzᵃᶜᶜ, ℑyzᵃᶠᶜ, ℑyzᵃᶠᶠ, ℑyzᵃᶜᶠ,
    ℑxyzᶜᶜᶠ, ℑxyzᶜᶠᶜ, ℑxyzᶠᶜᶜ, ℑxyzᶜᶠᶠ, ℑxyzᶠᶜᶠ, ℑxyzᶠᶠᶜ,
    hdivᶜᶜᵃ, divᶜᶜᶜ, div_xyᶜᶜᵃ, div_xzᶜᵃᶜ, div_yzᵃᶜᶜ,
    ∇², ∇²hᶜᶜᵃ, ∇²hᶠᶜᵃ, ∇²hᶜᶠᵃ, ∇⁴hᶜᶜᵃ, ∇⁴hᶠᶜᵃ, ∇⁴hᶜᶠᵃ

using Oceananigans.Grids

include("areas_and_volumes.jl")
include("difference_operators.jl")
include("derivative_operators.jl")
include("interpolation_operators.jl")
include("divergence_operators.jl")
include("laplacian_operators.jl")

end


using DynamicAxisWarping, SlidingDistancesBase
using DelimitedFiles, ReverseDiff, DiffResults, Optim
cd(@__DIR__)

data = readdlm("../data/CBF_TRAIN.txt")
c1,c2,c3 = ntuple(i->data[data[:,1] .== i, 2:end], 3)

c1v = copy.(collect(eachrow(c1)))

plot(c1', layout=3, sp=1, legend=false)
plot!(c2', sp=2)
plot!(c3', sp=3)

input = mean(c1v) # Our initial guess will be the Euclidean mean

function soft_dtw_barycenter(input, data, args...; kwargs...)
    function soft_dtw_mean_cost(data)
        function (x)
            mean(data) do d
                soft_dtw_cost(x, d, args...; kwargs...) / lastlength(d)
            end
        end
    end

    costfun = soft_dtw_mean_cost(data)
    # costfun(input)

    cfg = ReverseDiff.GradientConfig(input)
    tape = ReverseDiff.GradientTape(costfun, input)
    ctape = ReverseDiff.compile(tape)
    results = DiffResults.GradientResult(similar(input))

    function fg!(F, G, x)
        if G != nothing
            ReverseDiff.gradient!(results, ctape, x)
            G .= results.derivs[1]
            return results.value
        end
        if F != nothing
            return costfun(x)
        end
    end

    ##
    res = Optim.optimize(
        Optim.only_fg!(fg!),
        input,
        length(input) < 200 ? BFGS() : LBFGS(),
        Optim.Options(
            store_trace = true,
            show_trace = true,
            show_every = 1,
            iterations = 100,
            allow_f_increases = false,
            time_limit = 150,
            x_tol = 1e-3,
            f_tol = 1e-8,
            g_tol = 1e-4,
        ),
    )
    res.minimizer, res
end

##

bc,res = soft_dtw_barycenter(input, c1v)

using Plots, Plots.PlotMeasures
f1 = plot(c1', lab="", axis=false, legend=:bottom)
plot!(input, l=(4, :red), lab="Euclidean mean")
plot!(bc, l=(4, :green), lab="Soft-DTW mean")
f2 = plot(c1[1:3,:]', layout=(1,3), legend=false, axis=false, margin=-5mm)
plot(f1,f2, layout=(2,1))


using AlgRemez
using Test

@testset "AlgRemez.jl" begin
    # Write your tests here.
    y = 1
    z = 2
    n = 5
    lambda_low = 0.0004
    lambda_high = 64
    println("The order is $n")
    coeff_plus,coeff_minus = AlgRemez.calc_coefficients(y,z,n,lambda_low,lambda_high)
    println(coeff_plus.α)
    println(coeff_plus.β)

    funcplus = AlgRemez.fittedfunction(coeff_plus)
    println("1: sqrt(x)")
    println("Approximated value at x=0.1: ",funcplus(0.1))
    println("Exact value at x=0.1: ", sqrt(0.1))

    funcminus = AlgRemez.fittedfunction(coeff_minus)
    println("2: 1/sqrt(x)")
    println("Approximated value at x=0.1: ",funcminus(0.1))
    println("Exact value at x=0.1: ", 1/sqrt(0.1))

    n = 15
    println("The order is $n")
    coeff_plus,coeff_minus = AlgRemez.calc_coefficients(y,z,n,lambda_low,lambda_high)
    funcplus = AlgRemez.fittedfunction(coeff_plus)
    println("1: sqrt(x)")
    println("Approximated value at x=0.1: ",funcplus(0.1))
    println("Exact value at x=0.1: ", sqrt(0.1))

    funcminus = AlgRemez.fittedfunction(coeff_minus)
    println("2: 1/sqrt(x)")
    println("Approximated value at x=0.1: ",funcminus(0.1))
    println("Exact value at x=0.1: ", 1/sqrt(0.1))

    y = 1
    z = 4
    n = 5
    lambda_low = 0.0004
    lambda_high = 64
    println("The order is $n")
    coeff_plus,coeff_minus = AlgRemez.calc_coefficients(y,z,n,lambda_low,lambda_high)
    println(coeff_plus.α)
    println(coeff_plus.β)

    funcplus = AlgRemez.fittedfunction(coeff_plus)
    println("3: x^($y/$z)")
    println("Approximated value at x=0.1: ",funcplus(0.1))
    println("Exact value at x=0.1: ", 0.1^(y/z))

    funcminus = AlgRemez.fittedfunction(coeff_minus)
    println("4: x^(-$y/$z)")
    println("Approximated value at x=0.1: ",funcminus(0.1))
    println("Exact value at x=0.1: ", 0.1^(-y/z))

    n = 15
    println("The order is $n")
    coeff_plus,coeff_minus = AlgRemez.calc_coefficients(y,z,n,lambda_low,lambda_high)
    funcplus = AlgRemez.fittedfunction(coeff_plus)
    println("3: x^($y/$z)")
    println("Approximated value at x=0.1: ",funcplus(0.1))
    println("Exact value at x=0.1: ", 0.1^(y/z))

    funcminus = AlgRemez.fittedfunction(coeff_minus)
    println("4: x^(-$y/$z)")
    println("Approximated value at x=0.1: ",funcminus(0.1))
    println("Exact value at x=0.1: ", 0.1^(-y/z))
end


# This file contains the functions needed to define the gans
using LinearAlgebra

function crossEntTrue(x)
  return max(x, 0) - x + log(1 + exp(-abs(x)))
end

function crossEntFalse(x)
  return max(x, 0) + log(1 + exp(-abs(x)))
end

# creates a random orthogonal matrix for initialization
function rand_orth(m, n)
  @assert(m>=n)
  A = randn(m,n)
  return Matrix(qr(A).Q)
end

function ReLU(x)
  return max(x,zero(x))
end

# This function applies a dense net of specified depth, size, and nonlinearity
# with weights given by the first argument to the input data.
function denseNet(weights, data, inDim, outDim, depth, n_hidden, nonlin)
  offs = 1
  res = nonlin.(reshape(weights[offs : (offs + inDim * n_hidden - 1)],
                        (n_hidden, inDim)) * data)
  offs += inDim * n_hidden
  for k = 1 : depth
    res = nonlin.(reshape(weights[offs : (offs + n_hidden^2 - 1)],
                          (n_hidden, n_hidden)) * res)
    offs += n_hidden^2
  end
  return reshape(weights[offs : (offs + outDim * n_hidden - 1)], (outDim, n_hidden)) * res
end

# Creates a vector of suitable size to be the weights of a dense net and
# initializes each layer as a random orthonormal matrix.
function init_denseNet(inDim, outDim, depth, n_hidden, scale=0.8)
  if n_hidden >= inDim
    out = (scale * rand_orth(n_hidden, inDim))[:]
  else
    out = (scale * rand_orth(inDim, n_hidden)')[:]
  end

  for k = 1 : depth
    append!(out, scale * rand_orth(n_hidden, n_hidden)[:])
  end
  append!(out, (scale * rand_orth(n_hidden,outDim)')[:])
  return out
end


module NearSurfaceTurbulenceModels

export
    SurfaceEnhancedModelConstant,
    SurfaceFluxDiffusivityBoundaryConditions,
    save_closure_parameters!

using Oceananigans,
      Oceananigans.BoundaryConditions,
      Oceananigans.TurbulenceClosures

using JLD2

struct SurfaceEnhancedModelConstant{T} <: Function
             C₀ :: T
             Δz :: T
             z₀ :: T
    enhancement :: T
    decay_scale :: T
end

"""
    SurfaceEnhancedModelConstant(Δz; FT=Float64, C₀=1/12, z₀=-Δz/2,
                                 enhancement=2, decay_scale=8Δz)

Returns a callable object representing a spatially-variable model constant
for an LES eddy diffusivity model with the surface-enhanced form

    ``C(z) = C₀ * (1 + enhancement * exp((z - z₀) / decay_scale)``

"""
function SurfaceEnhancedModelConstant(Δz; FT=Float64, C₀=1/12, z₀=-Δz/2,
                                      enhancement=2, decay_scale=8Δz)

    return SurfaceEnhancedModelConstant{FT}(C₀, Δz, z₀, enhancement, decay_scale)
end

@inline (C::SurfaceEnhancedModelConstant)(x, y, z) =
    C.C₀ * (1 + C.enhancement * exp((z - C.z₀) / C.decay_scale))

"""
    SurfaceEnhancedModelConstant(filename)

Reconstruct a `SurfaceEnhancedModelConstant` from file.
"""
function SurfaceEnhancedModelConstant(filename::String)
    file = jldopen(filename)

    Lz = file["grid/Lz"]
    Nz = file["grid/Nz"]
    Δz = Lz / Nz

    C₀ = file["closure/C₀"]
    z₀ = file["closure/z₀"]
    enhancement = file["closure/enhancement"]
    decay_scale = file["closure/decay_scale"]

    close(file)

    Cᴬᴹᴰ = SurfaceEnhancedModelConstant(Δz; C₀=1/12, z₀=-Δz/2, enhancement=2, decay_scale=8Δz)

    return Cᴬᴹᴰ
end

#####
##### Saving utilities for surface enhanced model constant
#####

save_closure_parameters!(args...) = nothing # fallback

const EnhancedAMD = AnisotropicMinimumDissipation{FT, PK, <:SurfaceEnhancedModelConstant} where {FT, PK}

function save_closure_parameters!(file, closure::EnhancedAMD)
    file["closure/C₀"] = closure.Cν.C₀
    file["closure/z₀"] = closure.Cν.z₀
    file["closure/enhancement"] = closure.Cν.enhancement
    file["closure/decay_scale"] = closure.Cν.decay_scale
    return nothing
end

#####
##### Utility for obtaining surface flux diffusivity boundary conditions
#####

function SurfaceFluxDiffusivityBoundaryConditions(grid, Qᵇ; Cʷ=0.1)
    w★ = (Qᵇ * grid.Lz)^(1/3) # surface turbulent velocity scaling
    κ₀ = Cʷ * grid.Δz * w★
    return DiffusivityBoundaryConditions(grid, top = BoundaryCondition(Value, κ₀))
end

end # module


### A Pluto.jl notebook ###
# v0.11.14

using Markdown
using InteractiveUtils

# ╔═╡ 6a397f82-fc14-11ea-0174-91941427c27f
# Load required libaries
begin
	using RDatasets
	using LinearAlgebra
	using Plots
end

# ╔═╡ 790d6d50-fc14-11ea-3544-271bd7ec8d95
# Functions
begin
	"""
		distance(x, y)

	Compute distance between two vectors x, y using the dot product
	"""
	function distance(x, y)
		return sqrt((x - y)* (x - y)')
	end
	# sqrt(dot((x-y),(x-y)))

	"""Compute the pairwise distance between rows of X and rows of Y

	Arguments
	----------
	X: ndarray of size (N, D)
	Y: ndarray of size (M, D)

	Returns
	--------
	D: matrix of shape (N, M), each entry D[i,j] is the distance between
	X[i,:] and Y[j,:] using the dot product.
	"""
	function pairwise_distance_matrix(X, Y)
		N, D = size(X)
		M, _ = size(Y)
		distance_matrix = zeros(Float64, (N, M))
		for i in 1:N
			for j in 1:M
				distance_matrix[i,j] = distance(X[[i],:], Y[[j],:])[1]
			end
		end
		return distance_matrix
	end

	"""
		meshgrid(x, y)

	Create a meshgrid from x-min:steps:x-max & y-min:steps:y-max
	"""
	function meshgrid(X, steps=0.1, buffer=1)
		x_min = minimum(X[:,[1]]) - buffer
		y_min = minimum(X[:,[2]]) - buffer
		x_max = maximum(X[:,[1]]) + buffer
		y_max = maximum(X[:,[2]]) + buffer

		x = collect(x_min:steps:x_max)
		y = collect(y_min:steps:y_max)

		X = vec([i for i in x, j in 1:length(y)])
		Y = vec([j for i in 1:length(x), j in y])
		return x, y, hcat(X,Y)
	end

	"""
		KNN(k, X, y, Xtest)

	K nearest neighbors
	Arguments
	---------
	k: int using k nearest neighbors.
	X: the training set
	y: the classes
	Xtest: the test set which we want to predict the classes

	Returns
	-------
	ypred: predicted classes for Xtest

	"""
	function KNN(K, X, y, Xtest)
		N, D = size(X)
		M, _ = size(Xtest)
		num_classes = length(unique(y))

		# 1. Compute distance with all flowers
		distance_grid = zeros(Float64, (N, M))
		distance_grid = pairwise_distance_matrix(X, Xtest)

		# 2. Find indices for the k closest flowers
		index_grid = mapslices(sortperm, distance_grid', dims=2)[:,1:K]

		# 3. Vote for the major class
		ypred = zeros(Float64, (M, num_classes))

		for m in 1:M
			klasses = y[index_grid[m,:]]
			for k in unique(klasses)
				ypred[m, k] = length(klasses[klasses .== k]) / K
			end
		end

		return mapslices(argmax, ypred, dims=2)
	end
end

# ╔═╡ 94413ee0-fc18-11ea-0212-19853b58d1b5
# Load the data
begin
	iris = dataset("datasets", "iris")
	X = convert(Matrix, iris[:,[:SepalLength,:SepalWidth]])
	Y = recode(iris.Species, "setosa"=> 1, "versicolor"=>2, "virginica"=>3)
	scatter(iris.SepalLength, iris.SepalWidth,group=iris.Species)
end

# ╔═╡ b260bb80-fc18-11ea-3758-b3b70d518b33
# calculate the decision boundaries for K-NN
begin
	# Initials KNN parameters
	K = 3
	xx, yy, Xtest = meshgrid(X, 0.1, 1)

	# predict the species for every gridcell
	ypred = KNN(K, X, Y, Xtest)

		# reshape the vector to a matrix and plot the result
	prediction_grid = reshape(ypred,length(xx),length(yy))
	heatmap(xx, yy, prediction_grid', color=["orange","green","purple"], legend=false, 		title="KNN decision boundary K=$K")
	scatter!(iris.SepalLength, iris.SepalWidth,group=iris.Species, legend=true)
end

# ╔═╡ Cell order:
# ╠═6a397f82-fc14-11ea-0174-91941427c27f
# ╠═790d6d50-fc14-11ea-3544-271bd7ec8d95
# ╠═94413ee0-fc18-11ea-0212-19853b58d1b5
# ╠═b260bb80-fc18-11ea-3758-b3b70d518b33


export binary_quadratic_form, can_solve, discriminant, isdiscriminant, QuadBin,
       isfundamental_discriminant, prime_form, prime_power_form, cycle,
       isindefinite, ispositive_definite, isnegative_definite, isreduced

###############################################################################
#
#   Constructor
#
###############################################################################

@doc Markdown.doc"""
     binary_quadratic_form(a, b, c)
Constructs the integral binary quadratic form $a$ * x^2 + $b$ * x * y + $c$ * y^2.
"""
binary_quadratic_form(a, b, c) = QuadBin(a, b, c)

###############################################################################
#
#   Coefficients
#
###############################################################################

function getindex(f::QuadBin, i::Int)
  if i == 1
    return f.a
  elseif i == 2
    return f.b
  elseif i == 3
    return f.c
  else
    throw(error("Index must be between 1 and 3"))
  end
end

###############################################################################
#
#   Values
#
###############################################################################

(f::QuadBin)(x,y) = f[1]*x^2 + f[2] * x * y + f[3] * y^2

@doc Markdown.doc"""
     can_solve(f::QuadBin, n::Int64)
For a binary quadratic form $f$ with negative discriminant and an integer $n$,
returns tuple (`true`, (`x`,`y`)) if $f$(`x`,`y`) = $n$ for integers `x`,`y`.
If no such integers exist return (`false`,(0,0))
"""
function can_solve(f::QuadBin, n::Int64)
    if discriminant(f) >= 0
        @error("$f has non-negative discriminant")
    end
    for y in 1:Int64(floor(sqrt(Int64(4*f[1]*n)//abs(Int64(discriminant(f))))))
        #now f(x,y) quadratic in one variable -> use quadratic formula
        aq = f[1]
        bq = f[2] * y
        cq = f[3] * y^2 - n
        d = bq^2 - 4*aq*cq
        if issquare(d)
            if divides(-bq + sqrt(d), 2*aq)[1]
                return(true, (divexact(-bq + sqrt(d), 2*aq), ZZ(y)))
            end
            if divides(-bq - sqrt(d), 2*aq)[1]
                return(true, (divexact(-bq - sqrt(d), 2*aq), ZZ(y)))
            end
        end
    end
    return (false,(ZZ(0),ZZ(0)))
end

###############################################################################
#
#   Discriminant
#
###############################################################################

discriminant(f::QuadBin) = f[2]^2 - 4 * f[1] * f[3]

@doc Markdown.doc"""
     isdiscriminant(D)
Returns `true` if $D$ is the discriminant of an integral binary
quadratic form, otherwise returns `false`.
"""
function isdiscriminant(D)
    if D == 0
        return false
    end
    m = mod(D,4)
    if m == 0 || m == 1
        return true
    end
    return false
end

@doc Markdown.doc"""
     isfundamental_discriminant(D)
Returns `true` if $D$ is a fundamental discriminant i.e. has conductor `1`,
otherwise returns `false`.
"""
function isfundamental_discriminant(D)
    m = mod(D,4)
    if m == 1 && issquarefree(D)
        return true
    end
    if m == 0
        h = divexact(D,4)
        c = mod(h,4)
        if (c == 2 || c == 3) && issquarefree(h)
            return true
        end
    end
    return false
end

@doc Markdown.doc"""
     conductor(D)
Returns the conductor of the discriminant $D$ i.e. the largest
positive integer `c` such that $\frac{D}{c^2}$ is a discriminant.
"""
function conductor(D)
  !isdiscriminant(D) && error("$D is no discriminant")
  d = divexact(D, fundamental_discriminant(D))
  return isqrt(d)
end

function fundamental_discriminant(D)
  fac = factor(D)
  sqf = one(FlintZZ)
  for (p, e) in fac
    if isodd(e)
      sqf = sqf * p
    end
  end
  # sqf = is the squarefree-part, so D = sqf * square and sqf square-free
  if mod(sqf, 4) == 1
    return sqf
  else
    return sqf * 4
  end
end

###############################################################################
#
#   Equality
#
###############################################################################

function ==(f1::QuadBin, f2::QuadBin)
  return f1[1] == f2[1] && f1[2] == f2[2] && f1[3] == f2[3]
end

###############################################################################
#
#   Arithmetic
#
###############################################################################

conjugate(f::QuadBin) = binary_quadratic_form(f[1], -f[2], f[3])

-(f::QuadBin) = binary_quadratic_form(-f[1], -f[2], -f[3])

Generic.content(f::QuadBin) = gcd([f[1], f[2], f[3]])

isindefinite(f::QuadBin) = discriminant(f) > 0 ? true : false

isnegative_definite(f::QuadBin) = (discriminant(f) < 0 && f[1] < 0)

ispositive_definite(f::QuadBin) = (discriminant(f) < 0 && f[1] > 0)

Base.iszero(f::QuadBin) = (f[1] == 0 && f[2] == 0 && f[3] == 0)

isprimitive(f::QuadBin) = (content(f) == 1)

isreducible(f::QuadBin) = issquare(discriminant(f))

function isreduced(f::QuadBin)
    if ispositive_definite(f)
        return (abs(f[2]) <= f[1] <= f[3]) && (f[2] >= 0 || (f[1] != f[2] && f[1] != f[3]))
    end

    if isnegative_definite(f)
        return isreduced(-conjugate(f))
    end

    D = Int64(discriminant(f))

    if isindefinite(f)
        h = (abs(sqrt(D) - float(Int64(2*abs(f[1])))) < f[2] < sqrt(D)) || f[1] == 0 && -f[2] < 2*f[3] <= f[2]
        return (h || f[3]==0 && -f[2] < 2*f[1] <= f[2])
    end
end

###############################################################################
#
#   Reduction
#
###############################################################################

@doc Markdown.doc"""
     reduce(f::QuadBin)
Returns for the positive (or negative) definite binary quadratic form $f$ an equivalent
reduced form.
"""
function Base.reduce(f::QuadBin)
    if isindefinite(f)
        @error("Not implemented for $f indefinite")
    end
    if isnegative_definite(f)
        return reduce(-conjugate(f))
    end
    if ispositive_definite(f)
        return _reduce(f)[1]
    end
end

function _rho(f::QuadBin, T::fmpz_mat)
    s = _round_erec(f[2]//(2*f[3]))
    res_f = binary_quadratic_form(f[3], -f[2] + 2*s*f[3], f[3]*s^2 - f[2]*s + f[1])
    return(res_f, matrix(ZZ, 2, 2, [T[2], T[1] + s*T[2], T[4], T[3] + s*T[4]]))
end

function _normalize(f::QuadBin)
    s = _round_erec((f[1]-f[2])//(2*f[1]), dir = "down")
    res_f = binary_quadratic_form(f[1], f[2] + 2*s*f[1], f[1]*s^2 + f[2]*s + f[3])
    return(res_f, matrix(ZZ, 2, 2, [1, s, 0, 1]))
end

function _reduce(f::QuadBin)
    (g, T) = _normalize(f)
    while !_isreduced(g)
        (g, T) = _rho(g, T)
    end
    return (g, T)
end

function _isreduced(f::QuadBin)
    bol1 = f[1] <= f[3]
    bol2 = true
    if f[1] == f[3]
        bol2 = 0 <= f[2]
    end
    #reduced is checked, could be omitted
    bol3 = -f[1] < f[2] && f[2] <= f[1]

    return bol1 && bol2 && bol3
end

###############################################################################
#
#   Other Utilities
#
###############################################################################

function _round_erec(x::fmpq; dir = "none")
    if dir == "up"
        return fmpz(Int64(ceil(Rational{Int64}(x.num, x.den))))
    end
    if dir == "down"
        return fmpz(Int64(floor(Rational{Int64}(x.num, x.den))))
    end
    if dir == "none"
        if abs(x-_round_erec(x, dir = "up")) <= abs(x-_round_erec(x, dir = "down"))
            return _round_erec(x, dir = "up")
        else
            return _round_erec(x, dir = "down")
        end
    end
end

###############################################################################
#
#   Composition
#
###############################################################################

#COR7.3.17: f1,f2  same discriminant
# function Hecke.compose(f1::QuadBin, f2::QuadBin)
#     d = discriminant(f1)
#     Bm = numerator((b(f1) + b(f2))//2)
#     m = gcd(gcd(a(f1), a(f2)), Bm)
#     A = numerator((a(f1)*a(f2))//m^2)
#     #better?
#     j=1
#     k=1
#     l=1
#     while true
#         j = rand(-50:50)
#         k = rand(-50:50)
#         l = rand(-50:50)
#         if j*a(f2) + k*a(f1) + l*Bm == m
#             break
#         end
#     end
#     s = numerator(j*a(f2)*b(f1) + k*a(f1)*b(f2) + numerator((l*(b(f1)*b(f2)+d))//2)//m)
#     B = mod(s,2*A)
#     C = numerator((B^2-d)//(4*A))
#     return(QuadBin(A, B, C))
# end

###COMPOSITION###(Dirichlet)
#using Dirichlet composition, compare warwick Def 2.10

@doc Markdown.doc"""
     compose(f1::QuadBin, f2::QuadBin)
Returns the composition of the binary quadratic forms $f1$ and $f2$.
The result is not reduced, uses Dirichlet Composition.
"""
function compose(f1::QuadBin, f2::QuadBin)
    #discriminants have to match
    D = discriminant(f1)
    D2 = discriminant(f2)
    if !(D==D2)
        @error("discriminants do not match")
    end
    (h, n_1, n_2) = gcdx(f1[1], f2[1])
    (e, n_3, n3) = gcdx(h, divexact(f1[2]+f2[2], 2))

    (n1, n2) = (n_3 * n_1, n_3 * n_2)
    B = n1 * divexact(f1[1] * f2[2], e) + n2 * divexact(f1[2] * f2[1], e) + n3 * divexact(f1[2] * f2[2] + D, 2*e)
    return QuadBin(divexact(f1[1]*f2[1], e^2), B, divexact(e^2*(B^2-D), 4*f1[1]*f2[1]))
end

# cmpr Buchmann Alg 6.1
@doc Markdown.doc"""
     cycle(f::QuadBin)
Returns the cycle of the irreducible indefinite reduced form $f$.
"""
function cycle(f::QuadBin)
    if !isindefinite(f)
        @error("$f not indefinite")
    elseif !isreduced(f)
        @error("$f not reduced")
    elseif isreducible(f)
        @error("$f reducible")
    end

    Cycle = Vector([f])
    g = _rhoTau(f)
    while !(g==f)
        push!(Cycle, g)
        g = _rhoTau(g)
    end
    return Cycle
end

function _rhoTau(f::QuadBin)
    #s = round(f[2]//(2*f[3]))
    num = Int64(f[2])+Int64(floor(sqrt(Int64(discriminant(f)))))
    den = Int64(2*abs(f[3]))
    s =  Int64(Int64(floor(num/den)))
    return QuadBin(abs(f[3]), -f[2] + 2*s*abs(f[3]), -(f[1] + f[2]*s + f[3]*s^2))
end

###############################################################################
#
#   Prime Forms
#
###############################################################################

function _sqrtmod4P(d::fmpz, p::fmpz)
    if jacobi(mod(d, p), p) == -1
        @error("$d is no square modulo $p")
    end
    if p == 2
        if iseven(d)
            return 2 * mod(divexact(d, 4), 2)
        else
            return 1
        end
    else
        r = sqrtmod(d, p)
        if mod(r,2) == mod(d,2)
            return r
        else
            return p-r
        end
    end
end

function _number_of_primeforms(d::fmpz, p::fmpz)
    return jacobi(mod(d, p), p) + 1
end

@doc Markdown.doc"""
     prime_form(d::fmpz, p::fmpz)
Returns an integral binary quadratic form of discriminant $d$ and leading coefficient
$p$ where $p$ is a prime number.
"""
function prime_form(d::fmpz, p::fmpz)
    if !isdiscriminant(d)
        @error("$d is no discriminant")
    end
    if _number_of_primeforms(d, p) == 0
        @error("prime form does not exist")
    end
    b = _sqrtmod4P(d, p)
    return QuadBin(p, b, divexact(b^2-d, 4*p))
end

function _sqrtmod4PE(d::fmpz, p::fmpz, e)
    if mod(d,p) == 0
        n = 1
        while (2*n + 2  <= e && mod(p^(2n+2), d) == 0)
            n = n + 1
        end
        if (p == 2 && !mod(2^(-2*n)*d, 4) in [0,1])
            n = n -2
        end
        d0 = divexact(d, p^(2*n))
        e0 = e - 2*n
        if e0 == 0
            return p^n*mod(d0, 2)
        else return p^n * _sqrtmod4PE(d0, p^e0, e)
        end
    else
        b = _sqrtmod4P(d, p)
        f = 1
        while f < e
            k = mod(divexact(d-b^2, b*4*p^f), p)
            b = mod(b + 2*k*p^f, 2*p^(f+1))
            f = f + 1
        end
        if b > p^e
            return b-2*p^e
        else
            return b
        end
    end
end

@doc Markdown.doc"""
     prime_power_form(d::fmpz, p::fmpz, e)
Returns an integral binary quadratic form of discriminant $d$ and leading coefficient
$p$^$e$ where $p$ is a prime number.
"""
function prime_power_form(d::fmpz, p::fmpz, e)
    if !isdiscriminant(d)
        @error("$d is no discriminant")
    end
    b = _sqrtmod4PE(d, p, e)
    c = divexact(b^2-d, 4*p^e)
    f = QuadBin(p^e, b, c)
    if discriminant(f) != d
        @error("prime power form does not exist")
    end
    return f
end


# The BFGS update
# The inverse can either be updated with
#  H = (I-s*y'/y'*s)*H*(I-y*s'/y'*s)/(s'y)^2 - (H*y*s' + s*y'*H)/s'*y
# or
#  H = H + (s'*y + y'*H*y)*(s*s')/(s'*y)^2 - (B*y*s' + s*y'*H)/(s'*y)
#

struct BFGS{T1} <: QuasiNewton{T1}
   approx::T1
end
# function update!(scheme::BFGS, B::DirectApprox, Δx, y)
#    B.A = B.A + y*y'/dot(Δx, y) - B.A*y*y'*B.A/(y'*B.A*y)
# end
# function update!(scheme::BFGS, B::InverseApprox, Δx, y)
#    B.A = (I - Δx*y'/dot(y, Δx))*B.A*(I - y*Δx'/dot(y, Δx)) + Δx*Δx'/dot(y, Δx)
# end
#
function update(H, s, y, scheme::BFGS{<:InverseApprox})
   sy = dot(s, y)
   ρ = inv(sy)

   if isfinite(ρ)
      C = (I - ρ*s*y')
      H = C*H*C' + ρ*s*s'
   end

   H
end
function update(B, s, y, scheme::BFGS{<:DirectApprox})
   ρ = inv(dot(s, y))
   B + ρ*y*y' - B*s*s'*B/(s'*B*s)
end
function update!(H, s, y, scheme::BFGS{<:InverseApprox})
   sy = dot(s, y)
   ρ = inv(sy)

   if isfinite(ρ)
      Hy = H*y
      H .= H .+ ((sy+y'*Hy).*ρ^2)*s*s'
      Hys = Hy*s'
      H .= H .- (Hys + Hys').*ρ
   end
   H
end
# end
# function update!(H, s, y, scheme::BFGS{<:InverseApprox})
#    sy = dot(s, y)
#    ρ = inv(sy)
#
#    if isfinite(ρ)
#       C = (I - ρ.*s*y')
#       H .= C*H*C' + ρ*s*s'
#    end
#    H
# end
function update!(H, s, y, scheme::BFGS{<:DirectApprox})
    ρ = inv(dot(s, y))
    H .+= ρ*y*y' - H*(s*s')*H/(s'*H*s)
end

function update!(A::UniformScaling, s, y, scheme::BFGS{<:InverseApprox})
   update(A, s, y, scheme)
end
function update!(A::UniformScaling, s, y, scheme::BFGS{<:DirectApprox})
   update(A, s, y, scheme)
end


export quadform

function quadform(x::Value, A::AbstractExpr)
    return x' * A * x
end

function quadform(x::AbstractExpr, A::Value)
    if length(size(A)) != 2 || size(A, 1) != size(A, 2)
        error("Quadratic form only takes square matrices")
    end
    if !issymmetric(A)
        error("Quadratic form only defined for symmetric matrices")
    end
    V = eigvals(Symmetric(Matrix(A)))

    if all(V .>= 0)
        factor = 1
    elseif all(V .<= 0)
        factor = -1
    else
        error("Quadratic forms supported only for semidefinite matrices")
    end

    P = real(sqrt(Matrix(factor * A)))
    return factor * square(norm2(P * x))
end


@reexport module Threshold

using SpikingNNFunctions.Threshold: poisson
using Adapt
using Random

import ..SpikingNN: excite!, evaluate!, reset!, isactive

export AbstractThreshold, isactive

abstract type AbstractThreshold end

"""
    Ideal(vth::Real)

An ideal threshold spikes when `v > vth`.
"""
struct Ideal{T<:Real} <: AbstractThreshold
    vth::T
end

isactive(threshold::Ideal, t::Integer; dt::Real = 1.0) = false

"""
    evaluate!(threshold::Ideal, t::Real, v::Real; dt::Real = 1.0)
    (threshold::Ideal)(t::Real, v::Real; dt::Real = 1.0)
    evaluate!(thresholds::AbstractArray{<:Ideal}, t::Integer, v; dt::Real = 1.0)

Return `t` when `v > threshold.vth`.
"""
evaluate!(threshold::Ideal, t::Real, v::Real; dt::Real = 1.0) = (v >= threshold.vth) ? t : zero(t)
(threshold::Ideal)(t::Real, v::Real; dt::Real = 1.0) = evaluate!(threshold, t, v; dt = dt)
evaluate!(thresholds::T, t::Integer, v; dt::Real = 1.0) where T<:AbstractArray{<:Ideal} =
    Int.((v .>= thresholds.vth) .* adapt(typeof(v), fill(t, size(v))))

"""
    Poisson(ρ₀::Real, Θ::Real, Δᵤ::Real, rng::AbstractRNG)

Choose to output a spike based on a inhomogenous Poisson process given by

``X < \\mathrm{d}t \\: \\rho_0 \\exp\\left(\\frac{v - \\Theta}{\\Delta_u}\\right)``

where ``X \\sim \\mathrm{Unif}([0, 1])``.
Accordingly, `dt` must be set correctly so that the neuron does not always spike.

Fields:
- `ρ₀::Real`: baseline firing rate at threshold
- `Θ::Real`: firing threshold
- `Δᵤ::Real`: voltage resolution
- `rng`: random number generation
"""
struct Poisson{T<:Real,  RT<:AbstractRNG} <: AbstractThreshold
    ρ₀::T
    Θ::T
    Δᵤ::T
    rng::RT
end

function Poisson{T}(ρ₀::Real, Θ::Real, Δᵤ::Real; rng::RT = Random.GLOBAL_RNG) where {T<:Real, RT}
    Poisson{T,RT}(ρ₀, Θ, Δᵤ, rng)
end
Poisson(ρ₀::Real, Θ::Real, Δᵤ::Real; kwargs...) = Poisson{Real}(ρ₀, Θ, Δᵤ; kwargs...)

isactive(threshold::Poisson, t::Integer; dt::Real = 1.0) = true

"""
    evaluate!(threshold::Poisson, t::Integer, v::Real; dt::Real = 1.0)
    (::Poisson)(t::Integer, v::Real; dt::Real = 1.0)
    evaluate!(thresholds::AbstractArray{<:Poisson}, t::Integer, v; dt::Real = 1.0)

Evaluate Poisson threshold function. See [`Threshold.Poisson`](@ref).
"""
evaluate!(threshold::Poisson, t::Integer, v::Real; dt::Real = 1.0) =
    poisson(threshold.ρ₀, threshold.Θ, threshold.Δᵤ, v; dt = dt, rng = threshold.rng) ? t : zero(t)
(threshold::Poisson)(t::Integer, v::Real; dt::Real = 1.0) = evaluate!(threshold, t, v; dt = dt)
evaluate!(thresholds::T, t::Integer, v; dt::Real = 1.0) where T<:AbstractArray{<:Poisson} =
    Int.(poisson(thresholds.ρ₀, thresholds.Θ, thresholds.Δᵤ, v; dt = dt, rng = threshold.rng) .* adapt(typeof(v), fill(t, size(v))))

end


using VortexDistributions
using JLD2
using GLMakie
using Interpolations
using NearestNeighbors
using LinearAlgebra
using Distances
## Include util functions
include("../util.jl")

## Load psi simulation
# @load "data/box_rings.jld2" psi_ring1 psi_ring2 psi_ring3 X # make sure julia repl is in base directory
@load "data/box_vorts.jld2" psi_chaos1 psi_chaos2 psi_knots1 psi_knots2 psi_knots3 psi_knots4 psi_tubes1 psi_tubes2 psi_ringtube X
# @load "data/slab_tubes.jld2" psi_tubes1 psi_tubes2 psi_tubes3 X

## Set psi
psi = psi_tubes1;

## Plot iso surface
plot_iso(psi, X)

## Find vortices
@time vorts_3d = findvortices3D_itp(psi, X, 2);
num_vorts = length(vcat(vorts_3d...)[:,1])
print("Number of vortices found: ", length(vcat(vorts_3d...)[:,1]))

## Scatter vortices found
scatterVortsOnIso(vorts_3d)


using Test, FinancialMonteCarlo, Statistics;
@show "Black Scholes Model"
S0 = 100.0;
K = 100.0;
r = [0.00, 0.02];
T = 1.0;
d = FinancialMonteCarlo.Curve([0.0, 0.02], T);
d2 = FinancialMonteCarlo.Curve([0.0, 0.01, 0.02], T);
D = 90.0;

Nsim = 10000;
Nstep = 30;
sigma = 0.2;
theta1 = 0.01;
k1 = 0.03;
mc = MonteCarloConfiguration(Nsim, Nstep);
mc1 = MonteCarloConfiguration(Nsim, Nstep, FinancialMonteCarlo.AntitheticMC());
toll = 0.8;

rfCurve = FinancialMonteCarlo.ImpliedZeroRate(r, T);
rfCurve2 = ZeroRate(r, T);

FwdData = Forward(T)
EUData = EuropeanOption(T, K)
EUBin = BinaryEuropeanOption(T, K)
AMData = AmericanOption(T, K)
AmBin = BinaryEuropeanOption(T, K)
BarrierData = BarrierOptionDownOut(T, K, D)
AsianFloatingStrikeData = AsianFloatingStrikeOption(T)
AsianFixedStrikeData = AsianFixedStrikeOption(T, K)
Model = VarianceGammaProcess(sigma, theta1, k1, Underlying(S0, d));
Model2 = VarianceGammaProcess(sigma, theta1, k1, Underlying(S0, d2));

display(Model)
@show FwdPrice = pricer(Model, ZeroRate(r[end]), mc, FwdData);
@show FwdPrice = pricer(Model, rfCurve, mc, FwdData);
@show FwdPrice = pricer(Model, rfCurve2, mc, FwdData);
@show FwdPrice = pricer(Model2, rfCurve, mc, FwdData);
@show FwdPrice = pricer(Model, rfCurve, mc1, FwdData);
@show EuPrice = pricer(Model, rfCurve, mc, EUData);
@show EuBinPrice = pricer(Model, rfCurve, mc, EUBin);
@show AmPrice = pricer(Model, rfCurve, mc, AMData);
@show AmBinPrice = pricer(Model, rfCurve, mc, AmBin);
@show BarrierPrice = pricer(Model, rfCurve, mc, BarrierData);
@show AsianPrice1 = pricer(Model, rfCurve, mc, AsianFloatingStrikeData);
@show AsianPrice2 = pricer(Model, rfCurve, mc, AsianFixedStrikeData);


# Day 11: Dumbo Octopus
load(::Day{11}, path) = parse.(Int, hcat(split.(readlines(path), "")...)) # transposed!

# Part 1 – count flashes in cellular automaton
function moore_neighborhood(I::CartesianIndex; xmax=10, ymax=10)
    r, c = Tuple(I)
    r₋, r₊ = clamp.([r - 1, r + 1], 1, xmax)
    c₋, c₊ = clamp.([c - 1, c + 1], 1, ymax)
    return CartesianIndices((r₋:r₊, c₋:c₊))
end

function solve1(::Day{11}, data; steps=100)
    data = deepcopy(data) # in-place `solve1!` could comment out this line for performance
    count = 0
    for i in 1:steps
        data .+= 1
        to_spread = Set(findall((==)(10), data))

        while !isempty(to_spread)
            neighbors = moore_neighborhood(pop!(to_spread))
            data[neighbors] .+= 1
            new_flashes = filter(n -> data[n] == 10, neighbors)
            union!(to_spread, new_flashes)
        end

        flashed = data .> 9
        count += sum(flashed)
        data[flashed] .= 0
    end

    return count
end

# Part 2 - return step in which octopuses synchronize
function solve2(::Day{11}, data)
    data = deepcopy(data) # in-place `solve1!` could comment out this line for performance
    step = 1
    while true # iterate until value is returned on synchronization
        data .+= 1
        to_spread = Set(findall((==)(10), data))

        while !isempty(to_spread)
            neighbors = moore_neighborhood(pop!(to_spread))
            data[neighbors] .+= 1
            new_flashes = filter(n -> data[n] == 10, neighbors)
            union!(to_spread, new_flashes)
        end

        flashed = data .> 9
        data[flashed] .= 0

        if sum(flashed) == 100
            return step
        end
        step += 1
    end
end

testresult1(::Day{11}) = 1656
testresult2(::Day{11}) = 195


using Plots
using Printf
using Oceananigans
using Oceananigans.TurbulenceClosures: VerticallyImplicitTimeDiscretization, time_discretization

grid = RectilinearGrid(size=128, z=(-0.5, 0.5), topology=(Flat, Flat, Bounded))

evd_closure = IsotropicDiffusivity(κ = 1.0)
ivd_closure = IsotropicDiffusivity(κ = 1.0, time_discretization = VerticallyImplicitTimeDiscretization())

model_kwargs = (grid=grid, tracers=:c, buoyancy=nothing)

implicit_model = NonhydrostaticModel(; closure=ivd_closure, model_kwargs...)
explicit_model = NonhydrostaticModel(; closure=evd_closure, model_kwargs...)
models = (implicit_model, explicit_model)

initial_temperature(x, y, z) = exp(-z^2 / 0.02)

[set!(model, c=initial_temperature) for model in models]

z = znodes(implicit_model.tracers.c)

c_implicit = view(interior(implicit_model.tracers.c), 1, 1, :)
c_explicit = view(interior(explicit_model.tracers.c), 1, 1, :)

c_plot = plot(c_implicit, z, linewidth = 2, label = "t = 0", xlabel = "Tracer concentration", ylabel = "z")

diffusion_time_scale = implicit_model.grid.Δzᵃᵃᶜ^2 / implicit_model.closure.κ.c
stop_time = 100diffusion_time_scale

simulations = [Simulation(explicit_model, Δt = 1e-1 * diffusion_time_scale, stop_time = stop_time),
               Simulation(implicit_model, Δt = 1e0  * diffusion_time_scale, stop_time = stop_time)]

[run!(simulation) for simulation in simulations]

plot!(c_plot, c_implicit, z, linewidth = 3, alpha = 0.6, label = @sprintf("implicit model, t = %.3e", implicit_model.clock.time))
plot!(c_plot, c_explicit, z, linewidth = 2, linestyle = :dash, label = @sprintf("explicit model, t = %.3e", explicit_model.clock.time))

display(c_plot)


# ---
# title: 227. Basic Calculator II
# id: problem227
# author: Tian Jun
# date: 2020-10-31
# difficulty: Medium
# categories: String, Stack
# link: <https://leetcode.com/problems/basic-calculator-ii/description/>
# hidden: true
# ---
#
# Given a string `s` which represents an expression, _evaluate this expression
# and return its value_.
#
# The integer division should truncate toward zero.
#
#
#
# **Example 1:**
#
#
#
#     Input: s = "3+2*2"
#     Output: 7
#
#
# **Example 2:**
#
#
#
#     Input: s = " 3/2 "
#     Output: 1
#
#
# **Example 3:**
#
#
#
#     Input: s = " 3+5 / 2 "
#     Output: 5
#
#
#
#
# **Constraints:**
#
#   * `1 <= s.length <= 3 * 105`
#   * `s` consists of integers and operators `('+', '-', '*', '/')` separated by some number of spaces.
#   * `s` represents **a valid expression**.
#   * All the integers in the expression are non-negative integers in the range `[0, 231 - 1]`.
#   * The answer is **guaranteed** to fit in a **32-bit integer**.
#
#
## @lc code=start
using LeetCode

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


#=

Author: Victoria Gregory

=#
using LaTeXStrings

# Set up the problem and initial guess, solve by VFI
sp = SearchProblem(;w_grid_size=100, pi_grid_size=100)
v_init = zeros(sp.n_w, sp.n_pi) + sp.c / (1 - sp.bet)
f(x) = bellman_operator(sp, x)
v = compute_fixed_point(f, v_init)
policy = get_greedy(sp, v)

# Make functions for the linear interpolants of these
vf = extrapolate(interpolate((sp.w_grid, sp.pi_grid), v, Gridded(Linear())),
                 Flat())
pf = extrapolate(interpolate((sp.w_grid, sp.pi_grid), policy,
                 Gridded(Linear())), Flat())

function plot_value_function(;w_plot_grid_size::Integer=100,
                             pi_plot_grid_size::Integer=100)
    pi_plot_grid = linspace(0.001, 0.99, pi_plot_grid_size)
    w_plot_grid = linspace(0, sp.w_max, w_plot_grid_size)
    Z = [vf[w_plot_grid[j], pi_plot_grid[i]]
            for j in 1:w_plot_grid_size, i in 1:pi_plot_grid_size]
    p = contour(pi_plot_grid, w_plot_grid, Z, levels=12, alpha=0.6, fill=true)
    plot!(xlabel=L"$\pi$", ylabel="wage", xguidefont=font(12))
    return p
end

function plot_policy_function(;w_plot_grid_size::Integer=100,
                             pi_plot_grid_size::Integer=100)
    pi_plot_grid = linspace(0.001, 0.99, pi_plot_grid_size)
    w_plot_grid = linspace(0, sp.w_max, w_plot_grid_size)
    Z = [pf[w_plot_grid[j], pi_plot_grid[i]]
            for j in 1:w_plot_grid_size, i in 1:pi_plot_grid_size]
    p = contour(pi_plot_grid, w_plot_grid, Z, levels=1, alpha=0.6, fill=true)
    plot!(xlabel=L"$\pi$", ylabel="wage", xguidefont=font(12), cbar=false)
    annotate!(0.4, 1.0, "reject")
    annotate!(0.7, 1.8, "accept")
    return p
end