plotstuff_pendcart(args...) = println("Install package Plots.jl (and call using Plots) to plot results in the end of demo_pendcart")

function care(A, B, Q, R)
    G = try
        B*inv(R)*B'
    catch
        error("R must be non-singular.")
    end
    Z = [A  -G;
    -Q  -A']

    S = schur(Z)
    S = ordschur(S, real(S.values).<0)
    U = S.Z

    (m, n) = size(U)
    U11 = U[1:div(m, 2), 1:div(n,2)]
    U21 = U[div(m,2)+1:m, 1:div(n,2)]
    return U21/U11
end
function lqr(A, B, Q, R)
    S = care(A, B, Q, R)
    K = R\B'*S
    return K
end

"""
    demo_pendcart(;kwargs...)

Run the iLQG Function to find an optimal trajectory For the "pendulum on a cart system". Requires package ControlSystems.jl

# Arguments
`x0     = [π-0.6,0,0,0]`
`goal   = [π,0,0,0]`
`Q      = Diagonal([10,1,2,1])` : State weight matrix
`R      = 1`                    : Control weight matrix
`lims   = 5.0*[-1 1]`           : control limits,
`T      = 600`                  : Number of time steps
`doplot = true`                 : Plot results
"""
function demo_pendcart(;x0 = [π-0.6,0,0,0], goal = [π,0,0,0],
    Q      = Diagonal([10.,1,2,1]), # State weight matrix
    R      = 1.,                    # Control weight matrix
    lims   = 5.0*[-1 1],            # control limits,
    T      = 600,                   # Number of time steps
    doplot = true                   # Plot results
    )

    N = T+1
    g = 9.82
    l = 0.35 # Length of pendulum
    h = 0.01 # Sample time
    d = 0.99
    A = [0 1 0 0; # Linearlized system dynamics matrix, continuous time
    g/l -d 0 0;
    0 0 0 1;
    0 0 0 0]
    B = [0, -1/l, 0, 1]
    C = eye(4) # Assume all states are measurable
    D = 4
    L = lqr(A,B,Q,R) # Calculate the optimal state feedback
    I = T

    function fsys_closedloop(t,x,L,xd)
        dx = copy(x)
        dx[1] -= pi
        u = -(L*dx)[1]
        xd[1] = x[2]
        xd[2] = -g/l * sin(x[1]) + u/l * cos(x[1]) - d*x[2]
        xd[3] = x[4]
        xd[4] = u
    end

    function fsys(t,x,u,xd)
        xd[1] = x[2]
        xd[2] = -g/l * sin(x[1]) + u/l * cos(x[1]) - d*x[2]
        xd[3] = x[4]
        xd[4] = u
    end

    dfvec = zeros(4)
    function dfsys(x,u)
        dfvec[1] = x[1]+h*x[2]
        dfvec[2] = x[2]+h*(-g/l*sin(x[1])+u[1]/l*cos(x[1])- d*x[2])
        dfvec[3] = x[3]+h*x[4]
        dfvec[4] = x[4]+h*u[1]
        dfvec
    end

    function cost_quadratic(x,u)
        local d = (x.-goal)
        0.5(d'*Q*d + u'R*u)[1]
    end

    function cost_quadratic(x::Matrix,u)
        local d = (x.-goal)
        T = size(u,2)
        c = Vector{Float64}(undef,T+1)
        for t = 1:T
            c[t] = 0.5(d[:,t]'*Q*d[:,t] + u[:,t]'R*u[:,t])[1]
        end
        c[end] = cost_quadratic(x[:,end][:],[0.0])
        return c
    end

    cx = zeros(4,T)
    cu = zeros(1,T)
    cxu = zeros(D,1)

    function dcost_quadratic(x,u)
        cx  .= Q*(x.-goal)
        cu  .= R.*u
        return cx,cu,cxu
    end

    function lin_dyn_f(x,u,i)
        u[isnan.(u)] .= 0
        f = dfsys(x,u)
    end

    fxc           = Array{Float64}(undef,D,D,I)
    fuc           = Array{Float64}(undef,D,1,I)
    fxd           = Array{Float64}(undef,D,D,I)
    fud           = Array{Float64}(undef,D,1,I)
    for ii = 1:I
        fxc[:,:,ii] = [0 1 0 0;
        0 0 0 0;
        0 0 0 1;
        0 0 0 0]
        fuc[:,:,ii] = [0, 0, 0, 1]
    end

    function lin_dyn_df(x,u)
        u[isnan.(u)] .= 0
        D            = size(x,1)
        nu,I         = size(u)
        cx,cu,cxu    = dcost_quadratic(x,u)
        cxx          = Q
        cuu          = [R]
        for ii = 1:I
            fxc[2,1,ii] = -g/l*cos(x[1,ii])-u[ii]/l*sin(x[1,ii])
            fxc[2,2,ii] = -d
            fuc[2,1,ii] = cos(x[1,ii])/l
            ABd = exp([fxc[:,:,ii]*h  fuc[:,:,ii]*h; zeros(nu, D + nu)])# ZoH sampling
            fxd[:,:,ii] = ABd[1:D,1:D]
            fud[:,:,ii] = ABd[1:D,D+1:D+nu]
        end
        fxx=fxu=fuu = []
        return fxd,fud,fxx,fxu,fuu,cx,cu,cxx,cxu,cuu
    end

    x = zeros(4,N)
    u = zeros(1,T)

    """
    Simulate a pendulum on a cart using the non-linear equations
    """
    function simulate_pendcart(x0,L, dfsys, cost)
        x[:,1] = x0
        u[1] = 0
        for t = 2:T
            dx     = copy(x[:,t-1])
            dx[1] -= pi
            u[t]   = -(L*dx)[1]
            if !isempty(lims)
                u[t]   = clamp(u[t],lims[1],lims[2])
            end
            x[:,t] = dfsys(x[:,t-1],u[t])
        end
        dx      = copy(x[:,T])
        dx[1]  -= pi
        uT      = -(L*dx)[1]
        if !isempty(lims)
            uT   = clamp(uT,lims[1],lims[2])
        end
        x[:,T+1] = dfsys(x[:,T],uT)
        c = cost(x,u)

        return x, u, c
    end

    # Simulate the closed loop system with regular LQG control and watch it fail due to control limits
    x00, u00, cost00 = simulate_pendcart(x0, L, dfsys, cost_quadratic)

    f(x,u,i) = lin_dyn_f(x,u,i)
    df(x,u)  = lin_dyn_df(x,u)
    # plotFn(x)  = plot(squeeze(x,2)')

    println("Entering iLQG function")
    # subplot(n=4,nc=2)
    x, u, L, Vx, Vxx, cost, trace = iLQG(f,cost_quadratic, df, x0, 0*u00,
    lims      = lims,
    # plotFn  = x -> Plots.subplot!(x'),
    regType   = 2,
    α         = exp10.(range(0.2, stop=-3, length=6)),
    λmax      = 1e15,
    verbosity = 3,
    tol_fun   = 1e-8,
    tol_grad   = 1e-8,
    max_iter  = 1000);

    doplot && plotstuff_pendcart(x00, u00, x,u,cost00,cost,trace)
    println("Done")

    return x, u, L, Vx, Vxx, cost, trace
end


function play_game(input; iend=2020)
    len = length(input)
    hist = Dict()
    last_num = nothing
    first = true

    for i in 1:iend
        if i <= len
            last_num = input[i]
        elseif first
            last_num = 0
        else
            # last num = last time - time before that
            last_num = hist[last_num][1] - hist[last_num][2]
        end

        first = !haskey(hist, last_num)
        if first
            hist[last_num] = [i, nothing]
        else
            hist[last_num] = [i, hist[last_num][1]]
        end
        # println("$i\t$last_num")
    end
    println(last_num)
end

test_input = [0,3,6]
real_input = [6,4,12,1,20,0,16]
play_game(real_input)
play_game(real_input, iend=30000000)


module VectorPartitions
using StaticArrays

export all_vector_partitions, increment_vector_partition_state!

struct VectorPartition
  n_elements::Int
  n_partitions::Int
end

# A partition of {1,...,n} into k subsets is equivalent to a function
# f: {1,...,n} -> {1,...,k} such that
# g(i) = max_{j <= i} f(j) is surjective.
# So we can generate partitions by generating all such functions f.
# here partition is f, upper_bound is g, and first_index keeps track of the points at which g changes value (which helps enforce surjectivity)
mutable struct VectorPartitionState{n,k}
  upper_bound::MVector{n,Int}
  partition::MVector{n,Int}
  first_index::MVector{k,Int}
  done::Bool
end

"""
    all_vector_partitions(n,k)

Create an iterator that produces all partitions of the set {1,...,`n`} into `k` nonempty subsets.

Partitions are represented as vectors of length `n` with values between 1 and `k`.
"""
function all_vector_partitions(n,k)
  if n <= 0
    throw(DomainError(n, "Number of elements in the set must be positive."))
  end
  if k <= 0
    throw(DomainError(k, "Number of sets in partition must be positive."))
  end
  if n < k
    throw(DomainError((n,k),"Cannot produce partitions with more elements than the underlying set."))
  end
  return VectorPartition(n,k)
end

"""
    all_vector_partitions(n)

Create an iterator that produces all partitions of the set {1,...,'n'} into any number of nonempty subsets.

Partitions into k subsets are represented as vectors of length `n` with values between 1 and k.
"""
all_vector_partitions(n) = Iterators.flatten((all_vector_partitions(n,k) for k in 1:n))

function Base.iterate(I::VectorPartition)
  state = initial_vector_partition_state(I)
  return state.partition, state
end

function Base.iterate(I::VectorPartition,state)
  next_state = next_vector_partition_state(I,state)
  if next_state.done
    return nothing
  end
  return next_state.partition, next_state
end

function initial_vector_partition_state(I::VectorPartition)
  upper_bound = cat(ones(Int, I.n_elements-I.n_partitions),collect(1:I.n_partitions),dims=1)
  first_index = cat(1, (I.n_elements-Int64(I.n_partitions)) .+ collect(2:I.n_partitions),dims=1)
  state = VectorPartitionState{I.n_elements,I.n_partitions}(MVector(upper_bound...),MVector(copy(upper_bound)...),MVector(first_index...),false)
  return state
end

# because it is not recommended to have iterators mutate their state directly,
# this function copies before incrementing.
function next_vector_partition_state(I::VectorPartition,state)
  if state.done
    return state
  end
  state_c = VectorPartitionState{I.n_elements,I.n_partitions}(state.upper_bound,state.partition,state.first_index,false)
  increment_vector_partition_state!(I,state_c)
  return state_c
end

"""
    increment_vector_partition_state!(I,state)

Update the state of a VectorPartition iterator.
Useful to avoid copying the state unnecessarily at the cost of destructive mutations.
This can result in significant speedups when you only need to look at each partition once.

# Examples
```julia
iter = all_vector_partitions(5,3)
_, state = iterate(iter)
while !state.done
  do_stuff(state.partition)
  increment_vector_partition_state!(iter,state)
end
```
"""
function increment_vector_partition_state!(I::VectorPartition,state)
  # increment the sequence subject to upper bound and carry
  if state.done
    return
  end
  @inbounds begin
  state.partition[2] += 1
  for i in 2:I.n_elements-1
    if state.partition[i] > state.upper_bound[i]
      if state.upper_bound[i] > state.upper_bound[i-1]
        state.partition[i] = state.upper_bound[i]
      else
        state.partition[i] = 1
      end
      state.partition[i+1] += 1
    end
  end
  end

  # overflow condition: need to change the upper bound
  @inbounds if state.partition[I.n_elements] > state.upper_bound[I.n_elements]
    stop = increment_surjective_monotone_sequence!(state.upper_bound,I.n_partitions,state.first_index)
    if stop
      state.done = true
    else
      # first partition for the new upper bound set
      state.partition[:] .= 1
      state.partition[state.first_index] = 1:I.n_partitions
    end
  end
end

function increment_surjective_monotone_sequence!(seq,maxval,step_locations)
  stop = true
  #update location of the increments
  @inbounds begin
  for i in 2:length(step_locations)
    if step_locations[i] > step_locations[i-1] + 1
      step_locations[i] -= 1
      k = 1
      for j in i-1:-1:2
        step_locations[j] = step_locations[i] - k
        k += 1
      end
      stop = false
      break
    end
  end

  for i in 1:length(step_locations)-1
    seq[step_locations[i]:step_locations[i+1]-1] .= i
  end
  seq[step_locations[end]:end] .= maxval
  end
  return stop
end

end


export Ball

struct Ball{T <: Real} <: ConvexSet
    center::Vector{T}
    radius::T
end

"""
    Ball()
    Ball(c, r)

A ball with center `c` and radius `r`.
"""
function Box(c::Vector{T}, r::T) where T <: Real
    r ≥ 0 || throw(ArgumentError("radius should be positive"))
    Ball(c, r)
end

"""
    Ball(n)
    Ball(n, r)

A `n` dimensional ball with center `zeros(n)` and radius `r`.
"""
Ball(n::Integer, r::Real=1.0) = Ball(zeros(typeof(r), n), r)

function project!(s::Ball{T}, v::Vector{T}, y::Vector{T}) where T <: Real
    v .= y .- s.center
    distance = sqrt(sum(abs2, v))
    if distance > s.radius
        v .= s.center .+ (s.radius / distance) .* v
    else
        copyto!(v, y)
    end
    v
end

function project(s::Ball{T}, y::Vector{T}) where T <: Real
    project!(s, similar(y), y)
end


# Exponential distribution

export Exponential

@parameterized Exponential(β) ≃ Lebesgue(ℝ₊)

@kwstruct Exponential()

function logdensity(d::Exponential{()} , x)
    return -x
end

Base.rand(rng::AbstractRNG, T::Type, μ::Exponential{()}) = randexp(rng,T)

TV.as(::Exponential) = asℝ₊

##########################
# Scale β

@kwstruct Exponential(β)

function Base.rand(rng::AbstractRNG, T::Type, d::Exponential{(:β,)})
    randexp(rng, T) * d.β
end

function logdensity(d::Exponential{(:β,)}, x)
    z = x / d.β
    return logdensity(Exponential(), z) - log(d.β)
end

distproxy(d::Exponential{(:β,)}) = Dists.Exponential(d.β)

asparams(::Type{<:Exponential}, ::Val{:β}) = asℝ₊

##########################
# Log-Scale logβ

@kwstruct Exponential(logβ)

function Base.rand(rng::AbstractRNG, T::Type, d::Exponential{(:logβ,)})
    randexp(rng, T) * exp(d.logβ)
end

function logdensity(d::Exponential{(:logβ,)}, x)
    z = x * exp(-d.logβ)
    return logdensity(Exponential(), z) - d.logβ
end

distproxy(d::Exponential{(:logβ,)}) = Dists.Exponential(exp(d.logβ))

asparams(::Type{<:Exponential}, ::Val{:logβ}) = asℝ

##########################
# Rate λ

@kwstruct Exponential(λ)

function Base.rand(rng::AbstractRNG, T::Type, d::Exponential{(:λ,)})
    randexp(rng, T) / d.λ
end

function logdensity(d::Exponential{(:λ,)}, x)
    z = x * d.λ
    return logdensity(Exponential(), z) + log(d.λ)
end

distproxy(d::Exponential{(:λ,)}) = Dists.Exponential(1/d.λ)

asparams(::Type{<:Exponential}, ::Val{:λ}) = asℝ₊

##########################
# Log-Rate logλ

@kwstruct Exponential(logλ)

function Base.rand(rng::AbstractRNG, T::Type, d::Exponential{(:logλ,)})
    randexp(rng, T) * exp(-d.logλ)
end

function logdensity(d::Exponential{(:logλ,)}, x)
    z = x * exp(d.logλ)
    return logdensity(Exponential(), z) + d.logλ
end

distproxy(d::Exponential{(:logλ,)}) = Dists.Exponential(exp(-d.logλ))

asparams(::Type{<:Exponential}, ::Val{:logλ}) = asℝ


export meanSquaredError, meanAverageError
@doc doc"""
    meanSquaredError(M,x,y)
Computes the (mean) squared error between the two
[`MPoint`](@ref)s `x` and `y` on the ([`Power`](@ref)) manifold `M`.
"""
function meanSquaredError(M::mT,x::P,y::P) where {mT <: Manifold, P <: MPoint}
    return distance(M,x,y)^2
end
function meanSquaredError(M::Power,x::P,y::P) where {mT <: Manifold, P <: MPoint}
    return 1/prod(M.powerSize) * sum( distance.( Ref(M.manifold), getValue(x), getValue(y) ).^2 )
end
@doc doc"""
    meanSquaredError(M,x,y)
Computes the (mean) squared error between the two
[`MPoint`](@ref)s `x` and `y` on the ([`Power`](@ref)) manifold `M`.
"""
function meanAverageError(M::mT,x::P,y::P) where {mT <: Manifold, P <: MPoint}
    return distance(M,x,y)
end
function meanAverageError(M::Power,x::P,y::P) where {mT <: Manifold, P <: MPoint}
    return 1/prod(M.powerSize) * sum( distance.( Ref(M.manifold), getValue(x), getValue(y) ) )
end


###### variogram estimator ######

"""
Multifractal estimator.

# Args
- estimator: function of estimator having the interface `estimator(X, p, args...; kwargs...)`. Here `X` is the input array, `p` is the power of moment.
"""
function multifractal_estim(estimator::Function, X::AbstractArray, pows::AbstractVector{<:Real}, args...; kwargs...)
    @assert all(pows .> 0) "Powers of moment must be positive"

    res = [estimator(X, p, args...; kwargs...) for p in pows]
    ws = StatsBase.weights([x.residual for x in res].^ -1)
    Hs = [x.hurst for x in res]
    σs = [x.σ for x in res]
    Rs = [x.residual for x in res]

    # return (hurst=mean(Hs, ws), σ=mean(σs, ws), residual=mean(Rs, ws))
    # return (hurst=median(Hs, ws), σ=median(σs, ws), residual=median(Rs, ws))
    return (hurst=median(Hs), σ=median(σs), residual=median(Rs))
end

"""
Variance function of the empirical variogram
"""
function variogram_variance(H::Real, lags::AbstractVector{<:Integer}, N::Integer)
    V = zeros(Float64, length(lags))

    for (j, d) in enumerate(lags)
        proc = FractionalGaussianNoise(H, d)
        V[j] = autocov(proc, 0)^2 + sum((1-i/N) * autocov(proc, i)^2 for i=0:N-1)
    end
    return 4/N * V
end

"""
    powlaw_estim(X, lags; p=2., method=:optim)

Power-law estimator for Hurst exponent and volatility.

# Args
- X: matrix of fGn, each row is a fGn of some time lag and each column is an observation.
- lags: time lags (increment step) used to compute each component of `X`
- p: power of the moment
- methods: method of estimation: {:optim, :lm}

# Returns
- hurst, σ: estimation of Hurst and volatility, as well as an object of optimizer
- (xp, yp): vectors of regression
- opm: optimizer

# Notes
- `X` is computed from fBm by taking finite differences. The second dimension corresponds to time. Example, let `W` be a fBm sample path then the following command computes `X`:
```julia
julia> lags = 2:10
julia> X = transpose(lagdiff(W, lags, mode=:causal))
```
- `p=1` is robust against quantization error.
"""
function powlaw_estim(X::AbstractMatrix{<:Real}, lags::AbstractVector{<:Integer}; pow::Real=2., method::Symbol=:lm, reweight::Bool=true)
    # remove columns containing NaN
    idx = findall(vec(.!any(isnan.(X), dims=1)))
    X = X[:,idx] # view(X,:,idx)

    if length(X) == 0
        return (hurst=NaN, σ=NaN, η=NaN, residual=NaN, vars=nothing, optimizer=nothing)
    else
        @assert length(lags) == size(X,1) > 1  "Dimension mismatch."
        @assert all(lags .>= 1)  "Lags must be larger than or equal to 1."
        @assert pow>0  "Moment must be positive."

        # explanatory and observation vectors
        xp = pow * log.(lags)
        μX = mean(X, dims=2)
        yp = vec(log.(mean((abs.(X.-μX)).^pow, dims=2)))
        # yp = vec(log.(mean((abs.(X)).^pow, dims=2)))
        # hurst, η, res, err = NaN, NaN, NaN, (NaN, NaN)

        # old version with weights
        # # polynomial order of the weight for samples, if 0 the uniform weight is used
        # kt::Integer = 0  # non-zero value puts more weight on most recent samples (i.e. those at large column numbers).
        # wt = StatsBase.weights(causal_weight(size(X,2), kt))
        # μX = mean(X, wt, 2)
        # yp = vec(log.(mean((abs.(X.-μX)).^p, wt, 2)))
        # # yp = vec(log.(mean((abs.(X)).^p, wt, 2)))  # <- this gives lower SNR
        # xp = p * log.(lags)
        # # weight for scales
        # ks::Integer = 0  # hard-coded: polynomial order of weight for scales, if 0 the uniform weight is used
        # ws = StatsBase.weights(poly_weight(length(yp), ks))
        # yc = yp .- mean(yp, ws)
        # xc = xp .- mean(xp, ws)
        # func = h -> 1/2 * sum(ws .* (yc - h*xc).^2)
        # # func = h -> 1/2 * sum(ws .* abs.(yc - h*xc))

        # compute the weighting vector:
        # Run first an estimation of Hurst by linear regression, then use this estimate to compute the weighting vector.
        # Note that the reweighting scheme is exact only for pow=2.
        dg = DataFrames.DataFrame(xvar=xp, yvar=yp)
        opm = GLM.lm(@GLM.formula(yvar~xvar), dg)
        η, hurst = GLM.coef(opm)  # intercept and slope
        res = GLM.deviance(opm)  # residual
        # err = try  # std error of estimates
        #     GLM.stderror(opm)
        # catch
        #     (NaN, NaN)
        # end

        ws =  if reweight
            variogram_variance(0 < hurst < 1 ? hurst : 0.5, lags, size(X,2)) .^ (-1)
            # variogram_variance(0.5, lags, size(X,2)) .^ -1  # in practice this is good enough?
        else
            ones(length(lags))  # uniform weight
        end
        ws ./= sum(ws)

        # estimation of H and η
        if method == :optim
            yc = yp .- mean(yp)
            xc = xp .- mean(xp)
            func = h -> sum(ws .* (yc - h*xc).^2)
            # func = h -> sum(ws .* abs.(yc - h*xc).^qnorm)
            # Gradient-free constrained optimization
            # ɛ = 1e-2  # search hurst in the interval [ɛ, 1-ɛ]
            opm = Optim.optimize(func, 0., 1., Optim.Brent())
            # # Gradient-based optimization
            # optimizer = Optim.GradientDescent()  # e.g. Optim.BFGS(), Optim.GradientDescent()
            # opm = Optim.optimize(func, ε, 1-ε, [0.5], Optim.Fminbox(optimizer))
            hurst = Optim.minimizer(opm)[1]
            η = mean(yp - hurst*xp)
            # relative residual
            res = sqrt(opm.minimum / length(xp) / var(yp))
        elseif method == :lm  # using GLM package
            dg = DataFrames.DataFrame(xvar=xp, yvar=yp)
            opm = GLM.glm(@GLM.formula(yvar~xvar), dg, GLM.Normal(), GLM.IdentityLink(), wts=sqrt.(ws))
            # opm = GLM.lm(@GLM.formula(yvar~xvar), dg)
            η, hurst = GLM.coef(opm)
            # GLM.deviance is by definition the RSS
            res = sqrt(GLM.deviance(opm) / var(yp)) # / length(xp)

            # # or equivalently, by manual inversion
            # Ap = hcat(xp, ones(length(xp))) # design matrix
            # hurst, η = Ap \ yp
        else
            error("Unknown method $(method).")
        end

        cp = normal_moment_factor(pow)  # constant factor depending on p
        σ = exp((η-log(cp))/pow)

        # return hurst, σ, (xp, yp)
        return (hurst=hurst, σ=σ, η=η, residual=res, vars=(xp,yp), optimizer=opm)
    end
end

"""
    powlaw_estim(X::AbstractVector{<:Real}, lags::AbstractVector{<:Integer}; kwargs...)

# Args
- X: sample path of fBm, e.g. the log-price
- lags: time lags used to compute finite differences
"""
function powlaw_estim(X::AbstractVector{<:Real}, lags::AbstractVector{<:Integer}; mode::Symbol=:causal, kwargs...)
    dX = transpose(lagdiff(X, lags; mode=mode))  # take transpose s.t. each column is an observation
    return powlaw_estim(dX, lags; kwargs...)
end

const variogram_estim = powlaw_estim


using DirectedRoundings
using Test

# arithmetic function examples
#   the directed rounding functions work with 1,2,3,4 params

function sqrt5x(a)
    sqrt(5*a)
end

function reldiff(a, b)
    return (a - b) / a
end

function affine_map(x, shift, scale)
    return (x + shift) * scale
end

function right_triangles_ratio(a_adjacent, a_opposite,
                               b_adjacent, b_opposite)
    a_hypot = hypot(a_adjacent, a_opposite)
    b_hypot = hypot(b_adjacent, b_opposite)
    return a_hypot / b_hypot
end

const FP = Float32

a = FP(3); b = FP(4096); c = inv(FP(17)); d = eps(a)/2;

round_nearest(inv,a)
round_up(inv, a)
round_down(inv, a)
round_tozero(inv, a)
round_fromzero(inv, a)

round_up(+, a, b)
round_down(hypot, a, b)

round_up(affine_map, a, b, c)
round_down(affine_map, a, b, c)

round_nearest(right_triangles_ratio, a, b, c, d)
round_up(right_triangles_ratio, a, b, c, d)
round_down(right_triangles_ratio, a, b, c, d)


using Mosek

"""
    basicPortfolio(n::Int, x0 :: Vector{Float64})

Implements the simple Portfolio Selection example maximizing return subject to a limited risk.

    maximize
        mu' * x
    such that
        sum(x) = budget
        GT * x - t = 0
        s >= || t ||
        s = gamma

Parameters:

- `w`     Initial uninvested wealth
- `x0`    Initial invested wealth for each asset
- `mu`    Expected returns for each asset
- `gamma` Risk limit as maximum standard deviation
- `GT`    Factorized covariance matrix
"""
function basicPortfolio(w :: Float64, x0 :: Vector{Float64}, mu :: Vector{Float64}, GT :: Array{Float64,2}, gamma :: Float64)
    n = length(mu)
    maketask() do t
        putstreamfunc(t,MSK_STREAM_LOG,msg -> print(msg))

        budget = w + sum(x0)

        # variables
        appendvars(t,2*n + 1)
        x_start  = 1
        x_end    = n
        s_idx  = n+1

        putobjsense(t,MSK_OBJECTIVE_SENSE_MAXIMIZE)
        putclist(t,Int32[Int32(x_start):Int32(x_end) ...], mu)

        putvarboundslice(t,Int32(x_start), Int32(x_end+1),
                         fill(MSK_BK_LO,n),
                         zeros(Float64,n),
                         fill(Inf,n))

        for j in 1:n
            putvarname(t,Int32(x_start+j-1), "x$j")
        end
        putvarname(t,Int32(s_idx),"s")

        appendcons(t,1)

        # sum(x) = w+sum(x0)
        putconbound(t,Int32(1),MSK_BK_FX,w + sum(x0),w + sum(x0))
        putconname(t,Int32(1),"budget")
        putarow(t,Int32(1),Int32[Int32(x_start):Int32(x_end)...],ones(Float64,n))

        # [ s ; GT * x ] in C_r
        domidx = appendquadraticconedomain(t,n+1)
        appendafes(t,1+n)

        appendacc(t,domidx,Int64[1:n+1...],zeros(Float64,4))
        putaccname(t,1,"GT")
        putafefrow(t,1,Int32[s_idx],Float64[-1])
        for i in 1:3
            putafefrow(t,i+1,
                       Int32[ x_start:x_end... ],
                       GT[i,:])
        end

        # s = gamma
        putvarbound(t,Int32(s_idx),MSK_BK_UP,gamma,gamma)

        # Turn all log output off.
        putintparam(t,MSK_IPAR_LOG, Int32(0))

        optimize(t,"mosek://solve.mosek.com:30080")

        # Display the solution summary for quick inspection of results.
        solutionsummary(t, MSK_STREAM_MSG)

        xxvector = getxx(t,MSK_SOL_ITR)

        #expectedReturn = sum(xxvector[x_start:x_end] .* mu)
        #standardDeviation = xxvector[s_start]

        xxvector[x_start:x_end],xxvector[s_idx]
    end
end

# default data
gamma = 0.05
mu = Float64[0.1073, 0.0737, 0.0627]
GT = Float64[0.1667 0.0232  0.0013
             0.0000 0.1033 -0.0022
             0.0000 0.0000  0.0338]
x0 = Float64[0.0, 0.0, 0.0]
w = 1.0

(x,s) = basicPortfolio(w,x0,mu,GT,gamma)

expectedReturn = sum(x .* mu)
println("------------------------------------------------------------")
println("Expected return $expectedReturn for gamma $s")
println("Optimal investment: $x")
println("------------------------------------------------------------")


#= Reference data from
- Fischer, C. (1977). The Hartree-Fock Method for Atoms : A Numerical
Approach. New York: Wiley.
=#

# Table 6.2
exact_Yᵏs = Dict((o"1s",0) => r -> 1 - exp(-2r)*(1 + r),
                 (o"2s",0) => r -> 1 - exp(-r)*(1 + 3/4*r + 1/4*r^2 + 1/8*r^3),
                 (o"2p",0) => r -> 1 - exp(-r)*(1 + 3/4*r + 1/4*r^2 + 1/24*r^3),
                 (o"2p",2) => r -> 30/r^2*(1 - exp(-r)*(1 + r + 1/2*r^2 + 1/6*r^3 +
                                                        1/24*r^4 + 1/144*r^5)))

# Table 6.3–6.3B
exact_F⁰s = Dict((o"1s",o"1s",0)=>5//8,
                 (o"2s",o"1s",0)=>17//81,
                 (o"2s",o"2s",0)=>77//512,
                 (o"2p",o"1s",0)=>59//243,
                 (o"2p",o"2s",0)=>83//512,
                 (o"2p",o"2p",0)=>93//512,
                 (o"4s",o"4s",0)=>19541//524288,
                 (o"4s",o"4p",0)=>19943//524288,
                 (o"4s",o"4d",0)=>20693//524288,
                 (o"4s",o"4f",0)=>21743//524288,
                 (o"4p",o"4p",0)=>20413//524288,
                 (o"4p",o"4d",0)=>21239//524288,
                 (o"4p",o"4f",0)=>22373//524288,
                 (o"4d",o"4d",0)=>22373//524288,
                 (o"4d",o"4f",0)=>23759//524288,
                 (o"4f",o"4f",0)=>26333//524288)

# Table 6.3
exact_Gᵏs = Dict((o"2s",o"1s",0)=>16//729,
                 (o"2p",o"1s",1)=>112//2187,
                 (o"2p",o"2s",1)=>45//512)


"""
	landmark_isomap(coords, search=:knn, neigh=16, anchors=1500)

This function runs Landmark Isomap at 3-D points and returns a coordinate matrix
with the unfolded points.

## Parameters:

* `coords`  - coordinate matrix of the reference points.
* `search`  - search type to build neighbors graph for Isomap (:knn for
  k-nearest neighbor or :radius for radius search)
* `neigh`   - number of neighbors (for `search`=:knn) or radius
  distance (for `search`=:radius) to build neighbors graph for Isomap.
* `anchors` - number of anchors/landmark points for the dimensionality reduction.
"""
function landmark_isomap(coords::AbstractMatrix, search=:knn, neigh=16, anchors=1500)
	n, dim = size(coords, 2), 2
	n < anchors && (anchors = n)

	g, ianchors = graph_and_anchors(coords, search, neigh, anchors)
	ADM = Array{Float64}(undef, (anchors, anchors))
	dissmatrix!(ADM, g, ianchors)

	if n > anchors
		iothers = setdiff(1:n, ianchors)
		atcoords, M1, M3 = anchors_mds(ADM, dim)

		otcoords = Array{Float64}(undef, (3, length(iothers)))
		Threads.@threads for i in 1:length(iothers)
			otcoords[:,i] .= triangulation(g, iothers[i], ianchors, M1, M3)
		end
		tcoords = Array{Float64}(undef, (3,n))
		tcoords[:,ianchors] .= atcoords
		tcoords[:,iothers] .= otcoords
	else
		M = fit(MDS, ADM, maxoutdim=dim, distances=true)
		tcoords = vcat(transform(M),zeros(1, n))
	end
 	tcoords
end

function graph_and_anchors(ref_coords, nhood, neigh_val, nanchors)
	n = size(ref_coords,2)
	idxs, dists = get_neighbors(ref_coords, nhood, neigh_val, true)
	src, dest, dwgt = Int64[], Int64[], Float64[]

	for i in 1:n
		for j in 1:length(idxs[i])
			idxs[i][j] == i && continue
			push!(src, i)
			push!(dest, idxs[i][j])
			push!(dwgt, dists[i][j])
		end
	end

	g = SimpleWeightedGraph(Int.(src), Int.(dest), dwgt)
	comps = length(connected_components(g))
	@assert comps==1 "$comps subgroups of isolated vertices, need to increase number of neighbors"

	wgt = nhood==:radius ? [1/length(x) for x in idxs] : [mean(x) for x in dists]
	ianchors = n > nanchors ? sort!(sample(1:n, Weights(wgt), nanchors, replace=false)) : collect(1:n)
	g, ianchors
end

function dissmatrix!(ADM, g, iax::Vector{Int})
	n = size(ADM,1)
	for (i,ia) in enumerate(iax)
		dcols = dijkstra_shortest_paths(g, ia).dists[iax[i:n]]
		for (d,j) in zip(dcols,i:n)
			ADM[i,j] = ADM[j,i] = d
		end
	end
end

function anchors_mds(ADM, dims)
	nx = size(ADM,1)
	G = dmat2gram(ADM)
    F = eigen(Symmetric(G))
	λ = F.values
	sorti = sortperm(F.values,rev=true)
	sortλ = λ[sorti]
    EM = (F.vectors[:,sorti])[:,1:dims]
    sq_eigenvals = sortλ[1:dims].^0.5
    AM = Diagonal(sq_eigenvals)
    atcoords = vcat(permutedims(EM*AM),zeros(1,nx))

	# to use later for another points allocations
	M1 = permutedims(EM)
	M1 ./= reshape(sq_eigenvals,dims,1)
	M3 = Array{Float64}(undef,nx)
	mean!(M3,ADM.^2)

	atcoords, M1, M3
end

function triangulation(g::SimpleWeightedGraph, i, j, M1, M3)
	M2 = dijkstra_shortest_paths(g, i).dists[j] .^ 2
	vcat(-0.5*M1*(M2-M3),0)
end


######################################################## model
F(z::BorderedArray,θ::AbstractVector) = F(z.u,(θ=θ,p=z.p))
function F(u::AbstractVector,parameters::NamedTuple)
	@unpack θ,p = parameters

	f = first(u)*first(p)*first(θ)
	F = similar(u,typeof(f))

	F[1] = θ[1] + p*u[1] + θ[2]*u[1]^3
	return F
end

######################################################### targets and initial guess
X = StateSpace( 1, -5:0.01:5, [0] )
θ = SizedVector{2}(4.0,-π/2)


using Surrogates

#1D

obj_1D = x-> 2*x+1
a = 0.0
b = 10.0
x = sample(5,a,b,SobolSample())
y = obj_1D.(x)
my_svm_1D = SVMSurrogate(x,y,a,b)
val = my_svm_1D(5.0)
add_point!(my_svm_1D,3.1,7.2)
add_point!(my_svm_1D,[3.2,3.5],[7.4,8.0])

#ND
obj_N = x -> x[1]^2*x[2]
lb = [0.0,0.0]
ub = [10.0,10.0]
x = sample(100,lb,ub,UniformSample())
y = obj_N.(x)
my_svm_ND = SVMSurrogate(x,y,lb,ub)
val = my_svm_ND((5.0,1.2))
add_point!(my_svm_ND,(1.0,1.0),1.0)
add_point!(my_svm_ND,[(1.2,1.2),(1.5, 1.5)],[1.728,3.375])


# There's not much to do as you slowly descend to the bottom of the ocean. The
# submarine computer challenges you to a nice game of Dirac Dice.
#
# This game consists of a single die, two pawns, and a game board with a
# circular track containing ten spaces marked 1 through 10 clockwise. Each
# player's starting space is chosen randomly (your puzzle input). Player 1 goes
# first.
#
# Players take turns moving. On each player's turn, the player rolls the die
# three times and adds up the results. Then, the player moves their pawn that
# many times forward around the track (that is, moving clockwise on spaces in
# order of increasing value, wrapping back around to 1 after 10). So, if a
# player is on space 7 and they roll 2, 2, and 1, they would move forward 5
# times, to spaces 8, 9, 10, 1, and finally stopping on 2.
#
# After each player moves, they increase their score by the value of the space
# their pawn stopped on. Players' scores start at 0. So, if the first player
# starts on space 7 and rolls a total of 5, they would stop on space 2 and add
# 2 to their score (for a total score of 2). The game immediately ends as a win
# for any player whose score reaches at least 1000.
#
# Since the first game is a practice game, the submarine opens a compartment
# labeled deterministic dice and a 100-sided die falls out. This die always
# rolls 1 first, then 2, then 3, and so on up to 100, after which it starts
# over at 1 again. Play using this die.
#
# For example, given these starting positions:
#
# Player 1 starting position: 4
# Player 2 starting position: 8
# This is how the game would go:
#
# Player 1 rolls 1+2+3 and moves to space 10 for a total score of 10.
# Player 2 rolls 4+5+6 and moves to space 3 for a total score of 3.
# Player 1 rolls 7+8+9 and moves to space 4 for a total score of 14.
# Player 2 rolls 10+11+12 and moves to space 6 for a total score of 9.
# Player 1 rolls 13+14+15 and moves to space 6 for a total score of 20.
# Player 2 rolls 16+17+18 and moves to space 7 for a total score of 16.
# Player 1 rolls 19+20+21 and moves to space 6 for a total score of 26.
# Player 2 rolls 22+23+24 and moves to space 6 for a total score of 22.
# ...after many turns...
#
# Player 2 rolls 82+83+84 and moves to space 6 for a total score of 742.
# Player 1 rolls 85+86+87 and moves to space 4 for a total score of 990.
# Player 2 rolls 88+89+90 and moves to space 3 for a total score of 745.
# Player 1 rolls 91+92+93 and moves to space 10 for a final score, 1000.
#
# Since player 1 has at least 1000 points, player 1 wins and the game ends. At
# this point, the losing player had 745 points and the die had been rolled a
# total of 993 times; 745 * 993 = 739785.
#
# Play a practice game using the deterministic 100-sided die. The moment either
# player wins, what do you get if you multiply the score of the losing player
# by the number of times the die was rolled during the game?

# read input
positions = map(l -> l[end] - '0', eachline("input.txt"))

# do eet
rolls = Iterators.Stateful(Iterators.cycle(1:100))
scores = zeros(Int, length(positions))
turn = 0
while all(s -> s < 1000, scores)
  player = (turn & 1) + 1
  position = positions[player] = (positions[player] + sum(Iterators.take(rolls, 3)) - 1) % 10 + 1
  scores[player] += position
  global turn += 1
end

println("Result: ", scores[scores .< 1000][1] * turn * 3)


using Controlz

# https://lpsa.swarthmore.edu/Root_Locus/RLocusExamples.html
g = (s + 1) / (s^3 + 4 * s^2 + 6 * s + 4)
g = 1 / (s * (s + 3))
g = (s + 3) / (s^2 - s - 2)
g = (s^2 + 2 * s + 2) / (s * (s^4 + 9 * s^3 + 33 * s^2 + 51 * s + 26))

root_locus(g, max_mag_Kc=3.0, nb_pts=5000)


include("test_utilities.jl")
include("../src/arnoldi.jl")

@testset "Basic Tests for Iterative Hessenberg" begin

    function Test1(N=10)
        @info "Testing basic full Hessenberg, direct run no checking: "
        A = rand(N, N)
        ih = Arnoldi(A, rand(N, 1))
        for II = 1:N
            ih()
        end
        display(GetHessenberMatrix(ih))
        display(GetOrthogonalMatrix(ih))
        return true
    end

    function Test2(N=10)
        @info "Checking the errors on the Hessenberg decomposition: "
        A = rand(N, N)
        ih = Arnoldi(A, rand(N, 1))
        for II = 1:N-2
            ih()
        end

        H = GetHessenberMatrix(ih)
        print("Matrix H: ")
        display(H)
        Q = GetOrthogonalMatrix(ih)
        print("Matrix Q:")
        display(Q)

        @assert (A*Q[:, 1:end-1] - Q*H).|>abs|>sum|>mean < 1e-10 "Decomposition Errors too big"
        return true
    end

    function Test3(N=10)
        @info "Checking Tridiagonal Decomposition on Hermitian Matrices: "
        A = rand(N, N)
        A = A*A'
        ih = Arnoldi(A, rand(N, 1))
        for II = 1:N-2
            ih()
        end

        H = GetHessenberMatrix(ih)
        println("H Matrix:")
        display(H)
        Q = GetOrthogonalMatrix(ih)
        println("Q Matrix")
        display(Q)
        @info "Assertring Error bounds of decomposition and Tridiagonal Property of the H Matrix"
        @assert (H - tril(H, 1)).|>abs|>sum|>mean < 1e-10 "Not Tridiagonal Enough"
        @assert (A*Q[:, 1:end-1] - Q*H).|>abs|>sum|>mean < 1e-10 "Decomposition Error too big"
        return true
    end

    function Test4(N=10)
        @info "Testing Limited Memory Arnoldi Decomposition"
        A = rand(N, N)
        ih = Arnoldi(A, rand(N, 1); max_k=2)
        for II = 1:N-2
            ih()
        end
        H = GetHessenberMatrix(ih)
        println("Limited Memory H matrix: ")
        display(H)
        return true

    end

    function Test5(N=10)
        @info "Testing Limited Memory Arnoldi Decomposition with a complex matrix"
        A = rand(N, N) + rand(N, N)im
        ih = Arnoldi(A, rand(N, 1)im; max_k=2)
        for II = 1:N-2
            ih()
        end
        H = GetHessenberMatrix(ih)
        println("Limited Memory H matrix: ")
        display(H)
        Q = GetOrthogonalMatrix(ih)
        println("Limited Memory Q Matrix")
        display(Q)
        return true

    end

    @test Test1()
    @test Test2()
    @test Test3()
    @test Test4()
    @test Test5()
end


function outer_indices!(u)
    uu = Array(u)
    idx = outer_indices!(uu)
    return cu(idx)
end

function outer_indices!(u::Vector)
    M_out  = length(u)
    usum   = zero(eltype(u))

    # compute cumulative sum, overwriting u
    @inbounds for i in 1:M_out
        usum += u[i]
        u[i] = usum
    end

    # shift cumulative sums to the right by one
    @inbounds for i in M_out:-1:2
        u[i] = u[i-1]
    end
    u[1] = 0

    # sample descending sequence of sorted random numbers
    # Algorithm by:
    # Bentley & Saxe, ACM Transactions on Mathematical Software, Vol 6, No 3
    # September 1980, Pages 359--364
    CurMax = one(eltype(u))
    idx = zeros(Int, M_out)
    bindex = M_out # bin index
    @inbounds for i in M_out:-1:1
        CurMax *= exp(log(rand(eltype(u))) / i)
        # scale random numbers (this is equivalent to normalizing u)
        rsample = CurMax * usum
        # checking bindex >= 1 is redundant since
        # ucum[1] = 0
        while rsample < u[bindex]
            bindex -= 1
        end
        idx[i] = bindex
    end
    return idx
end

function outer_resample!(state::BinomialState, model::BinomialGridModel, u)
    idx = outer_indices!(u)
    state.n .= state.n[idx,:]
    state.k .= state.k[idx,:]
    model.Nind .= model.Nind[idx]
    model.pind .= model.pind[idx]
    model.qind .= model.qind[idx]
    model.σind .= model.σind[idx]
    model.τind .= model.τind[idx]
    return state, model
end


## Julia 1.1.0

# Initialize
using Plots
plotlyjs() # Backend PlotlyJS

angles = 8 * 2π

a = 4
b = 8

steps = 100
anim = @animate for i = 1:steps
    Θ = range(0, stop=i * angles / (steps), length=10*i)
    x = a .* cos.(Θ)
    y = a .* sin.(Θ)
    z = b .* Θ
    plot(x, y, z, size=(960, 960))
end

gif(anim, "./gif/circular-helix.gif", fps = 30)


######################
# Auxilary functions #
######################
"""
    dist(i::Int, j::Int, embed::Array{Float64,2})

Calculates Euclidian distance between two vectors from embedding array.

**Arguments**
* `v1::Int` index of first vector
* `v2::Int` index of second vector
* `embed::Array{Float64,2}` graph embedding array
"""
function dist(v1::Int, v2::Int, embed::Array{Float64,2})
    if v1==v2
        return 0.0
    else
        return @inbounds sqrt(sum(i -> (embed[v1, i] - embed[v2, i]) ^ 2, axes(embed, 2)))
    end
end

"""
    JS(vC::Vector{Float64}, vB::Vector{Float64},
        vI::Vector{Int}, internal::Int, vLen::Int)

Jensen-Shannon divergence with Dirichlet-like prior.

**Arguments**
* `vC::Vector{Float64}` first distribution of edges within and between communities
* `vB::Vector{Float64}` second distribution of edges within and between communities
* `vI::Vector{Int}` indicator of internal (1) and external (0) edges w.r.t. communities, if empty compute overall JS distance
* `internal::Int` internal JS distance switch, if 1 return internal, else return external
"""
function JS(vC::Vector{Float64}, vB::Vector{Float64},
            vI::AbstractVector{Bool}, internal::Bool)

    if !isempty(vI)
        inter = vI .== internal
        vect_p = vC[inter]
        vect_q = vB[inter]
    else
        vect_p = copy(vC)
        vect_q = copy(vB)
    end
    sp_1 = sum(vect_p) + length(vect_p)
    sp_2 = sum(vect_q) + length(vect_q)
    @. vect_p = (vect_p + 1) / sp_1
    @. vect_q = (vect_q + 1) / sp_2
    vect_m = (vect_p .+ vect_q) ./ 2
    f = sum(@. vect_p*log(vect_p/vect_m)+vect_q*log(vect_q/vect_m))
    return f / 2
end

"""
Change 2-dimensional into 1-dim index
"""
function idx(n::Int, i::Int, j::Int)
  return n*(i-1) - (i-1)*(i-2) ÷ 2 +j-i+1
end

parse_flag(flag::String) = !isnothing(findfirst(==(flag),ARGS)) ? true : false

function parseargs()
    methods = Dict("rss" => split_cluster_rss,
                   "rss2" => split_cluster_rss2,
                   "size" => split_cluster_size,
                   "diameter" => split_cluster_diameter)
    try
        ###Flags###

        # Check if calculations should be verbose
        verbose = parse_flag("-v")
        # Check if provided graph is directed
        directed = parse_flag("-d")
        # Check if global score should be done with split
        split = parse_flag("--split-global")

        ###Edgelist###

        idx = findfirst(==("-g"),ARGS)
        @assert !isnothing(idx) "Edgelist file is required"
        fn_edges = ARGS[idx+1]
        @assert isfile(fn_edges) "$fn_edges is not a file"

        # Read edges
        edges = readdlm(fn_edges, Float64)
        rows, no_cols = size(edges)
        verbose && println("$no_cols columns and $rows rows in edgelist file.")

        # Validate file structure
        @assert no_cols == 2 || no_cols == 3 "Expected 2 or 3 columns in edgelist file"
        v_min = minimum(edges[:,1:2])
        @assert v_min == 0 || v_min == 1 "Vertices should be either 0-based or 1-based"

        # Make vertices 1-based
        if v_min == 0.0
            edges[:,1:2] .+= 1.0
        end
        no_vertices = Int(maximum(edges[:,1:2]))
        verbose && println("Graph contains $no_vertices vertices")

        # If graph is unweighted, add unit weights
        # Compute vertices weights
        vweight = zeros(no_vertices)
        eweights = no_cols == 2 ? ones(rows) : edges[:,3]
        edges = convert.(Int,edges[:,1:2])
        for i in 1:rows
            vweight[edges[i,1]] += eweights[i]
            vweight[edges[i,2]] += eweights[i]
        end
        verbose && println("Done preparing edgelist and vertices weights")

        ###Communities###

        idx = findfirst(==("-c"),ARGS)
        if !isnothing(idx)
            fn_comm = ARGS[idx+1]
            comm = readdlm(fn_comm,Int)
            @assert isfile(fn_comm) "$fn_comm is not a file"
        else
            no_cols == 2 ? louvain_clust(fn_edges,) : louvain_clust(fn_edges, edges, eweights)
            fn_comm = fn_edges*".ecg"
            comm = readdlm(fn_comm,Int)
            comm = comm[2:end,2]
            comm = reshape(comm,size(comm)[1],1)
        end
        comm_rows, no_cols = size(comm)

        # Validate file structure
        @assert comm_rows == no_vertices "No. communities ($comm_rows) differ from no. nodes ($no_vertices)"
        @assert no_cols == 1 || no_cols == 2 "Expected 1 or 2 columns in communities file, but encountered $no_cols."
        # 2 columns file - sort by first column and extract only second column
        if no_cols == 2
            comm = comm[sortperm(comm[:,1]),2]
            comm = reshape(comm,size(comm)[1],1)
        end
        c_min = minimum(comm)
        @assert c_min == 0 || c_min == 1 "Communities should be either 0-based or 1-based, but are $c_min based."

        # Make communities 1-based
        if c_min == 0
            comm .+=1
        end
        verbose && println("Done preparing communities.")

        ###Embedding###

        idx = findfirst(==("-e"),ARGS)
        @assert !isnothing(idx) "Embedding file is required"
        fn_embed = ARGS[idx+1]
        @assert isfile(fn_embed) "$fn_embed is not a file"

        # Read embedding
        embedding = []
        try
            embedding = readdlm(fn_embed,Float64)
        catch
            verbose && println("Embedding in node2vec format. Loading without first line.")
            embedding = readdlm(fn_embed,Float64,skipstart = 1)
        end
        # Validate file
        @assert no_vertices == size(embedding, 1) "No. rows in embedding and no. vertices in a graph differ."

        # If embedding contains indices in first column, sort by it and remove the column
        try
            order = convert.(Int,embedding[:,1])
            verbose && println("Sorting embedding by first column")
            embedding = embedding[sortperm(order),2:end]
        catch

        end
        verbose && println("Done preparing embedding.")

        ###Landmarks###

        # Transform communities
        clusters = Dict{Any, Vector{Int}}()

        landmarks = -1
        idx = findfirst(==("-l"),ARGS)
        if !isnothing(idx)
            try
                landmarks = parse(Int, ARGS[idx+1])
            catch
                landmarks = round(Int,4*sqrt(no_vertices))
                @info "Using $landmarks landmarks"
            end
        end

        idx = findfirst(==("-f"),ARGS)
        if !isnothing(idx)
            forced = parse(Int, ARGS[idx+1])
            landmarks = landmarks == -1 ? 1 : landmarks
        else
            forced = 4
        end

        if no_vertices >= 10000 && isnothing(findfirst(==("--force-exact"),ARGS)) && landmarks == -1
            landmarks = max(round(Int,4*sqrt(no_vertices)),4*maximum(comm))
            @info "Number of vertices is equal or higher than 10 000. Automatically switching to approximate algortihm with $landmarks landmarks. If you want to force exact algorithm use --force-exact flag."
        end
        # Provide clusters membership only if generating landmarks
        if landmarks != -1
            for (i, c) in enumerate(comm[:,1])
                if haskey(clusters, c)
                    push!(clusters[c], i)
                else
                    clusters[c] = [i]
                end
            end
            clusters = collect(values(clusters))
        end

        idx = findfirst(==("--seed"),ARGS)
        seed = !isnothing(idx) ? parse(Int, ARGS[idx+1]) : -1

        idx = findfirst(==("--samples-local"),ARGS)
        samples = !isnothing(idx) ? parse(Int, ARGS[idx+1]) : 10000

        idx = findfirst(==("-m"),ARGS)
        method_str = !isnothing(idx) ? lowercase(strip(ARGS[idx+1])) : "rss"

        method = methods[method_str]
        return edges, eweights, vweight, comm, clusters, embedding, verbose, landmarks, forced, method, directed, split, seed, samples
    catch e
        showerror(stderr, e)
        println("\n\nUsage:")
        println("\tjulia CGE_CLI.jl -g edgelist -e embedding [-c communities] [--seed seed] [--samples-local samples] [-v] [-d] [--split-global] [-l [landmarks]] [-f [forced]] [--force-exact] [-m method]")
        println("\nParameters:")
        println("edgelist: rows should contain two whitespace separated vertices ids (edge) and optional weights in third column")
        println("embedding: rows should contain whitespace separated embeddings of vertices")
        println("communities: rows should contain cluster identifiers of vertices with optional vertices ids in the first column")
        println("if no file is given communities are calculated with Louvain algorithm")
        println("seed: RNG seed for local measure sampling")
        println("samples: no. samples to draw for local score calculation")
        println("-v: flag for debugging messages")
        println("-d: flag for usage of directed framework")
        println("--split-global: flag for using splitted global score; kept for backward compatibility")
        println("landmarks: required number of landmarks; 4*sqrt(no.vertices) by default")
        println("forced: required number of forced splits of a cluster; 4 by default")
        println("if both 'landmarks' and 'multiplier' are provided the higher value is taken")
        println("method: one of:")
        println("\t* rss:      minimize maximum residual sum of squares when doing a cluster split")
        println("\t* rss2:     minimize maximum residual sum of squares when doing a cluster split (slower)")
        println("\t* size:     make clusters have approximately the same size after a cluster split")
        println("\t* diameter: make clusters have approximately the same diameter along first " *
                "principal component after a cluster split")
        println("(note that always a cluster with largest residual sum of squares is selected for splitting)")
        exit(1)
    end
end


using JuMP
using Gurobi

function sa(x, a, fcoeffs, capacity, free_flow_time)  # calculate the partial derivatives of c_a w.r.t. x_a
    assert(a <= length(x) && a >= 1)
    n = length(fcoeffs)
    dcdx = 0
    for i=2:n
        dcdx += (i-1) * fcoeffs[i] * (x[a]/capacity[a])^(i-2)
    end
    dcdx *= free_flow_time[a]/capacity[a]
    return dcdx
end

function saVect(x, fcoeffs, capacity, free_flow_time)
    saVec = similar(x)
    for a = 1:length(x)
        saVec[a] = sa(x, a, fcoeffs, capacity, free_flow_time)
    end
    return saVec
end

function solveJacob(i_th, tapFlowVec, fcoeffs, capacity, free_flow_time, numLinks, numODpairs, numRoutes, linkRoute, odPairRoute)
    assert(i_th >= 1 && i_th <= numODpairs)

    saVec = saVect(tapFlowVec, fcoeffs, capacity, free_flow_time)

    jacobi = Model(solver=GurobiSolver(OutputFlag=false))

    @variable(jacobi, d[1:numLinks])
    @variable(jacobi, x[1:numRoutes])

    for i=1:numODpairs
        sumLamX = 0
        for j=1:numRoutes
            if "$(i)-$(j)" in keys(odPairRoute)
                sumLamX += x[j]
            end
        end
        if i == i_th
            @constraint(jacobi, sumLamX == 1)
        else
            @constraint(jacobi, sumLamX == 0)
        end
    end

    for i=1:numLinks
        sumDeltaX = 0
        for j=1:numRoutes
            if "$(i)-$(j)" in keys(linkRoute)
                sumDeltaX += x[j]
            end
        end
        @constraint(jacobi, sumDeltaX == d[i])
    end

    @objective(jacobi, Min, sum(saVec[i] * (d[i])^2 for i = 1:length(numLinks)))

    solve(jacobi)

    return getvalue(d)
end

# by [Spiess(1990)]
function solveJacobSpiess(i_th, numLinks, numODpairs, numRoutes, linkRoute, odPairRoute)
    assert(i_th >= 1 && i_th <= numODpairs)

    d = zeros(numLinks)

    i = i_th

    for a=1:numLinks
        sumXDelta = 0
        for k=1:numRoutes
            if "$(i)-$(k)" in keys(odPairRoute)
                if "$(a)-$(k)" in keys(linkRoute)
                    sumXDelta += odPairRoute["$(i)-$(k)"] * linkRoute["$(a)-$(k)"]
                end
            end
        end
        d[a] = sumXDelta
    end

    return d
end

# compute the gradient
function gradient(gamma1, gamma2, demandsVec, demandsVec0, tapFlowVec, observFlowVec, jacob, numODpairs, numLinks)
    gradi = zeros(numODpairs)
    for i = 1:numODpairs
        gradi[i] = 2 * gamma1 * (demandsVec[i] - demandsVec0[i]) + 2 * gamma2 * sum([(tapFlowVec[j] - observFlowVec[j]) * jacob[i, j] for j = 1:numLinks])
    end
    return gradi
end

# compute a descent direction
function descDirec(gamma1, gamma2, demandsVec, demandsVec0, tapFlowVec, observFlowVec, jacob, numODpairs, numLinks)
    gradi = gradient(gamma1, gamma2, demandsVec, demandsVec0, tapFlowVec, observFlowVec, jacob, numODpairs, numLinks)
    h = similar(gradi)
    for i = 1:length(gradi)
        h[i] = -1 * gradi[i]
    end
    return h
end

# compute a search direction
function searchDirec(demandsVec, descDirect, epsilon_1)
    h = descDirect
    h_ = similar(h)
    for i = 1:length(h)
            if (demandsVec[i] > epsilon_1) || (demandsVec[i] <= epsilon_1 && h[i] > 0)
            h_[i] = h[i]
        else
            h_[i] = 0
        end
    end
    return h_
end

# line search
function thetaMax(demandsVec, searchDirect)
    h_ = searchDirect
    thetaList = Float64[]
    for i = 1:length(h_)
        if h_[i] < 0
            push!(thetaList, - demandsVec[i]/h_[i])
        end
    end
    theta_max = minimum(thetaList)
    return theta_max
end

# Armijo line search and update
function objF(gamma1, gamma2, demandsVec, demandsVec0, fcoeffs)
    demandsDic = demandsVecToDic(demandsVec)
    tapFlowVec = tapMSA(demandsDic, fcoeffs)[2]
    return gamma1 * sum([(demandsVec[i] - demandsVec0[i])^2 for i = 1:length(demandsVec)]) + gamma2 * sum([(tapFlowVec[a] - tapFlowVecDict[0][a])^2 for a = 1:length(tapFlowVec)])
end

function armijo(gamma1, gamma2, objFunOld, demandsVecOld, demandsVec0, fcoeffs, searchDirec, thetaMax, Theta, N)
    demandsVecList = Array{Float64}[]
    objFunList = Float64[]
    push!(demandsVecList, demandsVecOld)
    push!(objFunList, objFunOld)
    for n = 0:N
        demandsVecNew = similar(demandsVecOld)
        for i = 1:length(demandsVecOld)
            demandsVecNew[i] = demandsVecOld[i] + (thetaMax/(Theta^n)) * searchDirec[i]
        end
    	push!(demandsVecList, demandsVecNew)
    	push!(objFunList, objF(gamma1, gamma2, demandsVecNew, demandsVec0, fcoeffs))
    end
    idx = indmin(objFunList)
    objFunNew = objFunList[idx]
    assert(objFunNew <= objFunOld)
    return demandsVecList[idx], objFunNew
end


#    Problem : GROUP A
#    *********
#    TA simple nonlinear problem given as an example in Section 1.2.4 of
#    the LANCELOT Manual.
#    The problem is non convex and has several local minima.
#
#    Original SIF Source:
#    A.R. Conn, N. Gould and Ph.L. Toint,
#    "LANCELOT, A Fortran Package for Large-Scale Nonlinear Optimization
#    (Release A)"
#    Springer Verlag, 1992.
#
#    Implementation translated from Source:
#    http://eprints.tsu.ge/234/14/Tests%20collection-K-F.pdf
#
#    EG2.SIF classification OUR2-AN-1000-0
#
#    Number of variables (at least 2)
#
# Daniel Henderson, 08/2021

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

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

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

init = (n::Int=1000) -> begin
    n < 2 && @warn("EG2: number of variables must be ≥ 2")
    n = max(n, 2)

    return n, zeros(n)
end

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


# ------------------------------------------------------------------
# Licensed under the MIT License. See LICENSE in the project root.
# ------------------------------------------------------------------

"""
    LSIF(σ=2.0, b=100, λ=0.001)

Least squares importance fitting.

## Parameters

* `σ` - Bandwidth of Gaussian kernel (default to `2.0`)
* `b` - Maximum number of radial basis functions (default to `10`)
* `λ` - Regularization parameter (default to `0.001`)

## References

* Kanamori et al. 2009. A Least-squares Approach to Direct
Importance Estimation

## Author

* Júlio Hoffimann (julio.hoffimann@gmail.com)
"""
@with_kw struct LSIF{T} <: DensityRatioEstimator
  σ::T=2.0
  b::Int=10
  λ::T=0.001
end

default_optlib(dre::Type{<:LSIF}) = OptimLib

available_optlib(dre::Type{<:LSIF}) = [OptimLib, JuMPLib]

function _densratio(x_nu, x_de, dre::LSIF,
                    optlib::Type{<:OptimizationLibrary})
  K_de, H, h, x_ba = _lsif_consts(x_nu, x_de, dre)
  α = _lsif_coeffs(H, h, dre, optlib)
  K_de*α
end

function _densratiofunc(x_nu, x_de, dre::LSIF,
                        optlib::Type{<:OptimizationLibrary})
  K_de, H, h, x_ba = _lsif_consts(x_nu, x_de, dre)
  α = _lsif_coeffs(H, h, dre, optlib)
  function r(x)
    K = gaussian_gramian([x], x_ba, σ=dre.σ)
    dot(K, α)
  end
end

function _lsif_consts(x_nu, x_de, dre)
  x_ba = sample(x_nu, min(length(x_nu), dre.b), replace=false)
  K_nu = gaussian_gramian(x_nu, x_ba, σ=dre.σ)
  K_de = gaussian_gramian(x_de, x_ba, σ=dre.σ)

  b = length(x_ba)
  Φ = Matrix{eltype(K_de)}(undef, b, b)
  for l′ in 1:b
    φ′ = view(K_de, :, l′)
    for l in 1:l′
      φ = view(K_de, :, l)
      Φ[l,l′] = mean(φ .* φ′)
    end
  end
  H = Symmetric(Φ)
  h = vec(mean(K_nu, dims=1))

  K_de, H, h, x_ba
end

"""
    _lsif_coeffs(x_nu, x_de, basis, dre, optlib)

Return the coefficients of LSIF basis expansion.
"""
_lsif_coeffs(H, h, dre::LSIF, optlib::Type{<:OptimizationLibrary}) =
  @error "not implemented"


using MCCGA
using Test

@testset "Bit operations" begin

    @testset "Bits to float" begin
        bitsofnumber = [
            0,
            0,
            1,
            1,
            1,
            1,
            1,
            1,
            1,
            0,
            0,
            1,
            0,
            0,
            0,
            1,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0
        ]

        floatlist = floats(bitsofnumber)

        @test floatlist isa Vector
        @test length(floatlist) == 1
        @test floatlist[1] == 1.1328125
    end

    @testset "Float to bits" begin
        fvalue = 76289.375
        fbits = bits(fvalue)

        @test length(fbits) == 32
        @test fbits == [0,1,0,0,0,1,1,1,1,0,0,1,0,1,0,1,0,0,0,0,0,0,0,0,1,0,1,1,0,0,0,0]
    end

    @testset "Bit-float conversations" begin
        tol = 0.001

        v = [2.71828, 3.14159265, 1.0, 5, -100]

        mybits = bits(v)

        myfloats = floats(mybits)

        @test length(mybits) == 32 * length(v)
        @test length(myfloats) == length(v)

        for i = 1:length(v)
            @test isapprox(v[i], myfloats[i], atol = tol)
        end
    end

    @testset "Sign bit test" begin
        mybits1 = bits(9.8)
        mybits2 = bits(-9.8)

        @test mybits1[1] == 0
        @test mybits2[1] == 1
    end

    @testset "Infinity bits" begin

        infbits = [
            0,
            1,
            1,
            1,
            1,
            1,
            1,
            1,
            1,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
        ]

        mybits1 = bits(Inf64)
        mybits2 = bits(Inf32)
        mybits3 = bits(Inf)

        @test mybits1 == infbits
        @test mybits2 == infbits
        @test mybits3 == infbits
    end

    @testset "NaN bits" begin
        nanbits = [
            0,
            1,
            1,
            1,
            1,
            1,
            1,
            1,
            1,
            1,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
        ]

        mybits1 = bits(NaN64)
        mybits2 = bits(NaN32)
        mybits3 = bits(NaN)

        @test mybits1 == nanbits
        @test mybits2 == nanbits
        @test mybits3 == nanbits
    end
end

@testset "Sampling" begin
    @testset "Convergency state" begin
        probvector = [1.0, 1.0, 1.0]
        mysample = MCCGA.sample(probvector)

        @test length(mysample) == 3
        @test mysample[1] == 1.0
        @test mysample[2] == 1.0
        @test mysample[3] == 1.0
    end

    @testset "Normal state" begin
        probvector = [0.5, 0.5, 0.5]
        mysample = MCCGA.sample(probvector)

        @test length(mysample) == 3
        @test mysample[1] >= 0.0
        @test mysample[2] >= 0.0
        @test mysample[3] >= 0.0
        @test mysample[1] <= 1.0
        @test mysample[2] <= 1.0
        @test mysample[3] <= 1.0
    end
end

@testset "Optimization of Pi and E" begin
    function f(x)
        return (x[1] - 3.14159265)^2 + (x[2] - exp(1.0))^2
    end

    lower = [-100.0, -100.0]
    upper = [100.0, 100.0]

    result = MCCGA.mccga(
        lower = lower,
        upper = upper,
        costfunction = f,
        popsize = 100,
        maxsamples = 10000,
    )

    @test result isa Dict

    finalsolution = result["final_solution"]

    @test finalsolution[1] >= 3
    @test finalsolution[1] <= 4

    @test finalsolution[2] >= 2
    @test finalsolution[2] <= 3

end

@testset "Chichinadze" begin

    tol = 0.001

    lower = [-30.0, -30.0]
    upper = [30.0, 30.0]

    result = MCCGA.mccga(
        lower = lower,
        upper = upper,
        costfunction = Functions.chichinadze,
        popsize = 200,
        maxsamples = 10000,
    )

    finalsolution = result["final_solution"]
    finalminimum = result["final_minimum"]

    @test isapprox(finalsolution[1], 5.90133, atol = tol)
    @test isapprox(finalsolution[2], 0.5, atol = tol)
    @test isapprox(finalminimum, -43.3159, atol = tol)
end

@testset "Levy" begin

    tol = 0.001

    lower = [-10.0, -10.0, -10]
    upper = [10.0, 10.0, 10]

    result = MCCGA.mccga(
        lower = lower,
        upper = upper,
        costfunction = Functions.levy,
        popsize = 100,
        maxsamples = 10000,
    )

    finalsolution = result["final_solution"]
    finalminimum = result["final_minimum"]

    @test isapprox(finalsolution[1], 1.0, atol = tol)
    @test isapprox(finalsolution[2], 1.0, atol = tol)
    @test isapprox(finalsolution[3], 1.0, atol = tol)
    @test isapprox(finalminimum, 0.0, atol = tol)
end

@testset "Ackley" begin

    tol = 0.001

    lower = [-32.768, -32.768, -32.768]
    upper = [32.768, 32.768, 32.768]

    result = MCCGA.mccga(
        lower = lower,
        upper = upper,
        costfunction = Functions.ackley,
        popsize = 100,
        maxsamples = 10000,
    )

    finalsolution = result["final_solution"]
    finalminimum = result["final_minimum"]

    @test isapprox(finalsolution[1], 0.0, atol = tol)
    @test isapprox(finalsolution[2], 0.0, atol = tol)
    @test isapprox(finalsolution[3], 0.0, atol = tol)
    @test isapprox(finalminimum, 0.0, atol = tol)
end


using Random
using Wavelets
using ContinuousWavelets
Random.seed!(135)
using Test
@testset "st using cwt tests" begin
    sharpExample = zeros(100, 2)
    sharpExample[26:75, :] .= 1
    layers = stParallel(2, size(sharpExample, 1))
    resSt = st(sharpExample, layers, abs)
    @test eltype(resSt) <: eltype(sharpExample) # check the type
    otherResSt = layers(sharpExample)
    compare = roll(resSt, layers, sharpExample)
    @test compare == otherResSt
    @test compare[2][:, end, end, 1] == resSt[end-12:end, 1]
    multiDExample = 1000 * randn(50, 10, 43)
    layers = stParallel(2, size(multiDExample, 1))
    resSt = st(multiDExample, layers, abs)
    @test eltype(resSt) <: eltype(multiDExample)
    @test 0 < minimum(abs.(resSt[:, 1, 1]))  # input is always non-zero so nothing should be *Exactly* zero, except by chance (which is why we set the seed above)
    @test 0 < minimum(abs.(resSt[:, end, end]))
    resSt = st(multiDExample, layers, abs, outputSubsample = (3, -1))
    @test eltype(resSt) <: eltype(multiDExample)
    @test 0 < minimum(abs.(resSt[:, 1, 1]))  # input is always non-zero so nothing should be *Exactly* zero, except by chance (which is why we set the seed above)
    @test 0 < minimum(abs.(resSt[:, end, end]))
end

f = randn(102)
lay = stParallel(1, size(f)[1])
ScatteredFull(lay, f)
# some basic tests for the Scattered type
# m=2, 3extra, 2transformed
fixDims = [3, 5, 2]
m = 2;
k = 2;
n = [100 200; 50 100; 25 50];
q = [5, 5, 5];
ex1 = ScatteredFull(m, k, fixDims, n, q, Float64)
@test ex1.m == m
@test ex1.k == k
@test size(ex1.data, 1) == m + 1
@test size(ex1.output, 1) == m + 1
@testset "correct size for output" begin
    for i = 1:size(ex1.output, 1)
        @test size(ex1.output[i])[4:end] == tuple(fixDims...)
        @test size(ex1.data[i])[4:end] == tuple(fixDims...)
        @test size(ex1.output[i])[1:2] == tuple(n[i, :]...)
        @test size(ex1.data[i])[1:2] == tuple(n[i, :]...)
    end
end


"""
    get_payoff(game::Dict{String,<:AbstractArray}, s::Vector{Vector{T}}) where T<:Real

Produces payoff profile for given game and actions probabilities array

**Input parameters**
* `game::Dict{String,<:AbstractArray}` - array of vectors to use in outer product
* `s::Vector{Vector{T}}` - collection of actions probabilities for each player in a game
"""
function get_payoff(game::Dict{String,<:AbstractArray},
                s::Vector{Vector{T}}) where T<:Real
    if !all(@. round(sum(s),digits = 2) ==1)
        error("Provided vectors are not probabilities distribution")
    end
    return Dict(k=>sum(v.*outer(s)) for (k,v) in game)
end

"""
    get_payoff(game::Dict{String,<:AbstractArray}, s::Vector{Vector{SymPy.Sym}})

Produces payoff profile for given game and actions probabilities symbolic array

**Input parameters**
* `game::Dict{String,<:AbstractArray}` - array of vectors to use in outer product
* `s::Vector{Vector{SymPy.Sym}}` - collection of actions probabilities for each player in a game
"""
function get_payoff(game::Dict{String,<:AbstractArray},
                s::Vector{Vector{SymPy.Sym}})
    return Dict(k=>sum(v.*outer(s)) for (k,v) in game)
end

"""
    best_reply(game::Dict{String,<:Array}, s::Vector{Vector{T}} where T<:Real,
    k::Int, epsil::F=0.0 ;return_val::String = "array") where F<:Real

Returns best reply of given player to actions of his counterparts
(defined by strategy profile) in the form of array or convex hull

**Input parameters**
* `game::Dict{String,<:Array}` - dictionary of players and their payoff matrices
* `s::Vector{Vector{T}} where T<:Real` - collection of actions probabilities for each player in a game
* `k::Int` - player for which the best reply is returned
* `epsil::F = 0.0` - probability of error (if the answer is disturbed)
* `return_val::String = "array"` - type of returned value ("array" or "chull")
"""
function best_reply(game::Dict{String,<:Array},
            s::Vector{Vector{T}} where T<:Real,
            k::Int,
            epsil::F = 0.0;
            return_val::String="array") where F<:Real

    (epsil < 0 || epsil > 1) && throw(ArgumentError("Probability of error (epsil) should be in range 0-1")) && return nothing

    action_no = size(game["player1"])[k]
    s_temp = deepcopy(s)
    payoffs = Vector{Real}()
    for i in 1:length(s_temp[k])
        s_temp[k] = zeros(length(s[k]))
        s_temp[k][i] = 1
        push!(payoffs, get_payoff(game, s_temp)["player"*string(k)])
    end
    eyemat = Matrix(1I, action_no, action_no)
    (epsil != 0 && rand() < epsil) ? pos = rand([false true], action_no) : pos = (payoffs .== maximum(payoffs))
    all(pos .== 0) ? pos[rand(1:action_no)] = 1 : nothing
    val2ret = eyemat[pos, :]
    if return_val == "chull"
        polyh = polyhedron(vrep(val2ret), CDDLib.Library())
        return convexhull(polyh, polyh)
    else
        return val2ret
    end
end

"""
    is_nash_q(game::Dict{String,<:Array}, s::Vector{Vector{T}}) where T<:Real

Returns a dictionary of logical vectors indicating whether given profile is Nash equilibrium.

**Input parameters**
* `game::Dict{String,<:Array}` - dictionary of players and their payoff matrices
* `s::Vector{Vector{T}})` - collection of actions probabilities for each player
"""
function is_nash_q(game::Dict{String,<:Array},
                s::Vector{Vector{T}}) where T<:Real

    nashqs = Dict{String,Bool}()
    for k in 1:length(game)
        st = s[k]
        br = best_reply(game, s, k, return_val = "chull")
        polyh = polyhedron(vrep([st]), CDDLib.Library())
        pint = intersect(br, polyh)
        nashqs["player"*string(k)] = (npoints(pint) != 0)
    end
    nashqs["Nash"] = all(values(nashqs))
    return nashqs
end

"""
    iterate_best_reply(game::Dict{String,<:Array},
            s::Vector{Vector{T}} where T<:Real,
            epsil::F = 0.0, it_num::Int = 10)  where F<:Real

Returns an array of arrays representing each players'
game strategies (action probabilities) history in every iteration

**Input parameters**
* `game::Dict{String,<:Array}` - dictionary of players and their payoff matrices
* `s::Vector{Vector{T}} where T<:Real` - collection of actions probabilities for each player
* `epsil::F = 0.0` - probability of error (if the answer is disturbed)
* `it_num::Int = 10` - an integer specifying how many iterations should be repeated
"""
function iterate_best_reply(game::Dict{String,<:Array},
    s::Vector{Vector{T}} where T<:Real,
    epsil::F = 0.0,
    it_num::Int = 10)  where F<:Real #TODO epsil not default
    s_temp = deepcopy(s)
    s_history = [] #TODO not Any type
    push!(s_history, s_temp)
    for i in 1:it_num
        br = Array{Int64}[]
        for i in 1:length(game)
            push!(br, best_reply(game, s_temp, i, epsil))
        end
        s_temp = _select_random(game, s_temp, br)
        push!(s_history, s_temp)
    end
    println("After ", it_num, " iterations")
    all(values(is_nash_q(game, s_history[length(s_history)]))) ?
        println("Nash equilibrium found") : println("Nash equilibrium not found")
    return s_history
end


function create_subgraph(vertex_set::AbstractArray,edge_list::AbstractArray,directed::Bool)
  new_graph = graph(Int64[],ExEdge{Int64}[],is_directed = directed)
  vertex_set_d = Dict()
  for vertex in vertex_set
    add_vertex!(new_graph,vertex)
    vertex_set_d[vertex] = 1
  end
  for edge in edge_list
    if (haskey(vertex_set_d,edge.source)) && (haskey(vertex_set_d,edge.target))
      new_edge = ExEdge(edge.index,edge.source,edge.target)
      for (k,v) in edge.attributes
        new_edge.attributes[k] = v
      end
        add_edge!(new_graph,new_edge)
    end
  end
  return new_graph
end

function all_neighbors(v::Int64,g::GenericGraph)
  if is_directed(g)
    neighbr = out_neighbors(v,g)
    for n in in_neighbors(v,g)
      append!(neighbr,n)
    end
    return neighbr
  else
    return out_neighbors(v,g)
  end
end

function all_degree(v::Int64,g::GenericGraph)
  if is_directed(g)
    return out_degree(v,g) + in_degree(v,g)
  else
    return out_degree(v,g)
  end
end

function occurrence_of_degree(g::GenericGraph)
  g_vertices = vertices(g)
  degrees = zeros(Int64,0)
  max = 0
  d = 0
  for i = 1:length(g_vertices)
    d = all_degree(g_vertices[i],g)+1
    if max < d
      for j=1:(d-max)
        push!(degrees,0)
      end
      degrees[d] +=1
      max = d
    else
      degrees[d] +=1
    end
  end
  return degrees
end


immutable Linear <: Degree{1} end

"""
Assuming uniform knots with spacing 1, the `i`th peice of linear b-spline
implemented here is defined as follows.

    y_i(x) = c p(x) + cp p(1-x)

where

    p(δx) = x

and the values `cX` for `X ∈ {_, p}` are the coefficients.

Linear b-splines are naturally interpolating, and require no prefiltering;
there is therefore no need for boundary conditions to be provided.

Also, although the implementation is slightly different in order to re-use
the framework built for general b-splines, the resulting interpolant is just
a piecewise linear function connecting each pair of neighboring data points.
"""
Linear

"""
`define_indices_d` for a linear b-spline calculates `ix_d = floor(x_d)` and
`fx_d = x_d - ix_d` (corresponding to `i` and `δx` in the docstring for
`Linear`), as well as the auxiliary quantity `ixp_d`
"""
function define_indices_d(::Type{BSpline{Linear}}, d, pad)
    symix, symixp, symfx, symx = Symbol("ix_",d), Symbol("ixp_",d), Symbol("fx_",d), Symbol("x_",d)
    quote
        $symix = clamp(floor(Int, $symx), 1, size(itp, $d)-1)
        $symixp = $symix + 1
        $symfx = $symx - $symix
    end
end

"""
In `coefficients` for a linear b-spline we assume that `fx_d = x-ix-d` and
we define `cX_d` for `X ∈ {_, p}` such that

    c_d = p(fx_d)
    cp_d = p(1-fx_d)

where `p` is defined in the docstring entry for `Linear` and `fx_d` in the
docstring entry for `define_indices_d`.
"""
function coefficients(::Type{BSpline{Linear}}, N, d)
    sym, symp, symfx = Symbol("c_",d), Symbol("cp_",d), Symbol("fx_",d)
    quote
        $sym = 1 - $symfx
        $symp = $symfx
    end
end

"""
In `gradient_coefficients` for a linear b-spline we assume that `fx_d = x-ix_d`
and we define `cX_d` for `X ⋹ {_, p}` such that

    c_d  = p'(fx_d)
    cp_d = p'(1-fx_d)

where `p` is defined in the docstring entry for `Linear`, and `fx_d` in the
docstring entry for `define_indices_d`.
"""
function gradient_coefficients(::Type{BSpline{Linear}}, d)
    sym, symp, symfx = Symbol("c_",d), Symbol("cp_",d), Symbol("fx_",d)
    quote
        $sym = -1
        $symp = 1
    end
end

"""
In `hessian_coefficients` for a linear b-spline we assume that `fx_d = x-ix_d`
and we define `cX_d` for `X ⋹ {_, p}` such that

    c_d  = p''(fx_d)
    cp_d = p''(1-fx_d)

where `p` is defined in the docstring entry for `Linear`, and `fx_d` in the
docstring entry for `define_indices_d`. (These are both ≡ 0.)
"""
function hessian_coefficients(::Type{BSpline{Linear}}, d)
    sym, symp = Symbol("c_",d), Symbol("cp_",d)
    quote
        $sym = $symp = 0
    end
end

# This assumes fractional values 0 <= fx_d <= 1, integral values ix_d and ixp_d (typically ixp_d = ix_d+1,
#except at boundaries), and an array itp.coefs
function index_gen{IT<:DimSpec{BSpline}}(::Type{BSpline{Linear}}, ::Type{IT}, N::Integer, offsets...)
    if length(offsets) < N
        d = length(offsets)+1
        sym = Symbol("c_", d)
        symp = Symbol("cp_", d)
        return :($sym * $(index_gen(IT, N, offsets..., 0)) + $symp * $(index_gen(IT, N, offsets..., 1)))
    else
        indices = [offsetsym(offsets[d], d) for d = 1:N]
        return :(itp.coefs[$(indices...)])
    end
end


"""
    get_mode(A, z, pseudo, verbosity)

Compute the (pseudo)mode of `A` associated with `z`.
"""
function get_mode(A, z, pseudo, verbosity)
    if pseudo
        n = size(A, 2)
        q = randn(n) + im*randn(n)
        normalize!(q)
        niter = 20

        SS, q = psmode_inv_lanczos(A, q, z, 1e-15, niter)
    else
        SS, q = oneeigcond(A,z, verbosity)
    end
    return SS, q
end

"""
    get_mtxpowersnorm(A, nmax)

Compute ``|A^k|`` for k up to `nmax`.
"""
function get_mtxpowersnorm(A, nmax)
    transient = Vector{Float64}(undef, nmax+1)
    transient[1] = 1.0
    (min_tp, max_tp) = (Inf, -Inf)
    mat = copy(A)
    i = 2
    transient[i] = norm(mat)
    stop = false
    while !stop && i < nmax + 1
        mat = A * mat
        i += 1
        transient[i] = norm(mat)
        min_tp = min(min_tp, transient[i])
        max_tp = max(max_tp, transient[i])
        if (max_tp > 1e130) || ((min_tp < 1e-130) && (min_tp != 0.0))
            @warn("stopping: bounds going out of range")
            stop = true
        end
        if min_tp == 0.0
            @warn("stopping: exactly zero matrix")
            stop = true
        end
    end
    return 0:(i-1), transient[1:i]
end

"""
    get_mtxexpnorm(A, dt, nmax)

Compute ``|e^{A*k*dt}|`` for k up to `nmax`.
"""
function get_mtxexpnorm(A, dt, nmax)
    eAdt = exp(dt*A)
    ns, transient = get_mtxpowersnorm(eAdt, nmax)
    return dt*ns, transient
end


module Intervals

import Base:
    +, -, \,
    <, >, ==, !=, ≤,
    ∈, ∩, ∪, ⊆, ⊇, ~,
    max, min,
    isless, isequal, isdisjoint,
    iterate, isempty, length, copy, getindex, to_index,
    show

export Interval, IntervalSet
export containing, partition

# -------------------------------------------------------------------------
# atomic interval

# represents an atomic (closed, open) interval
struct Interval{T<:Real}
    lo :: T
    hi :: T
    Interval{T}(lo,hi) where T <: Real = hi <= lo ? throw(ArgumentError("invalid interval hi=$hi ≤ low=$lo")) : new(lo, hi)
end

# ---------------------------------
# constructors

Interval(lo::T, hi::T) where T <: Real = Interval{T}(lo,hi)
Interval(x::T) where T <: Real = Interval{T}(x,x)
Interval(t::Tuple) = Interval(t...)

copy(a::Interval) = Interval(a.lo, a.hi)

# ---------------------------------
# repl properties

show(io::IO, a::Interval) = print(io, "[$(a.lo),$(a.hi))")

# ---------------------------------
# operations

# collection/

length(a::Interval) = a.hi - a.lo
to_index(a::Interval{T}) where T <: Integer = a.lo:(a.hi-1)

# logical

==(a::Interval, b::Interval) = a.lo == b.lo && a.hi == b.hi
≤(a::Interval, b::Interval)  = a.lo ≤ b.lo && a.hi ≤ b.hi
<(a::Interval, b::Interval)  = a.lo < b.lo && a.hi < b.hi

isless(a::Interval, b::Interval)  = a < b
isequal(a::Interval, b::Interval) = a == b

# set relationships
∈(x::T, a::Interval{T}) where T <: Real = a.lo ≤ x ≤ a.hi

⊆(a::Interval, b::Interval) = b.lo ≤ a.lo && a.hi ≤ b.hi
⊂(a::Interval, b::Interval) = (a==b) ? false : a ⊆ b
⊇(a::Interval, b::Interval) = b ⊆ a
⊃(a::Interval, b::Interval) = b ⊂ a

# arithmetic
+(a::Interval) = a
-(a::Interval) = Interval(-a.lo, -a.hi)

+(a::Interval{T}, x::T) where T <: Real = Interval{T}(a.lo+x, a.hi+x)
-(a::Interval{T}, x::T) where T <: Real = Interval{T}(a.lo-x, a.hi-x)

+(x::T, a::Interval{T}) where T <: Real = +a+x
-(x::T, a::Interval{T}) where T <: Real = -a+x

isdisjoint(a::Interval, b::Interval) = b.hi < a.lo || a.hi < b.lo

function ∩(a::Interval, b::Interval)
    try
        return Interval(max(a.lo,b.lo), min(a.hi,b.hi))
    catch err
        if isa(err, ArgumentError)
            return nothing # intervals were disjoint
        end

        throw(err) # unexpected error
    end
end

function \(a::Interval, b::Interval)
    ab = a ∩ b

    isnothing(ab) && return [a]                         # disjoint
    ab == a       && return typeof(a)[]                 # a ⊆ b
    a.lo == ab.lo && return [Interval(ab.hi, a.hi)]     # a ≥ b
    a.hi == ab.hi && return [Interval(a.lo, ab.lo)]     # a ≤ b

    return [Interval(a.lo, b.lo), Interval(b.hi, a.hi)] # a ⊃ b
end

function partition(a::Interval, b::Interval)
	ab = a ∩ b
	isnothing(ab) && return [a] # disjoint
	ab == a		  && return [a] # a ⊆ b
	a.lo == ab.lo && return [ab, Interval(ab.hi, a.hi)]
	a.hi == ab.hi && return [Interval(a.lo, ab.lo), ab]

	return [Interval(a.lo, ab.lo), ab, Interval(ab.hi, a.hi)] # a ⊃ b
end

# -------------------------------------------------------------------------
# interval sets

# ---------------------------------
# helpers

# NOTE: assumes (closed, open) intervals
function merge!(I::Array{Interval{T}}) where T <: Real
    i = 1
    while i < length(I)
        if I[i].hi < I[i+1].lo
            i += 1
            continue
        end

        I[i] = Interval(I[i].lo, I[i+1].hi)
        deleteat!(I, i+1)
    end
end

# ---------------------------------
# typedef

struct IntervalSet{T<:Real}
    Is  :: Array{Interval{T},1}
    min :: T
    max :: T

    function IntervalSet{T}(Is, min, max) where T <: Real
        # ensures atomic intervals are disjoint
        𝕀 = Interval{T}[]
        for I in Is
            n = length(𝕀)
            W = [I]
            for i ∈ 1:n
                W = [x for w in W for x in w \ 𝕀[i]]
            end

            append!(𝕀, W)
        end

        sort!(𝕀)
        merge!(𝕀)

        return new(𝕀, min, max)
    end
end

# ---------------------------------
# constructors

IntervalSet(min::T, max::T, itr)                    where T <: Real    = IntervalSet{T}(itr, min, max)
IntervalSet(min::T, max::T, Is::Array{Interval{T}}) where T <: Real    = IntervalSet{T}(Is, min, max)
IntervalSet(min::T, max::T, I::Interval{T})         where T <: Real    = IntervalSet{T}([I], min, max)

IntervalSet(min::T, max::T)                         where T <: Real    = IntervalSet{T}(Interval{T}[], min, max)
IntervalSet(Is::Array{Interval{T}})                 where T <: Real    = IntervalSet{T}(Is, typemin(T), typemax(T))
IntervalSet(I::Interval{T})                         where T <: Real    = IntervalSet{T}([I], typemin(T), typemax(T))

IntervalSet(min, max, Is::Tuple{T,T}...)            where T <: Real    = IntervalSet{T}((Interval(I) for I in Is), min, max)
IntervalSet(Is::Tuple{T,T}...)                      where T <: Real    = IntervalSet{T}((Interval(I) for I in Is), typemin(T), typemax(T))

copy(I::IntervalSet{T}) where T <: Real = IntervalSet{T}(copy(I.Is), I.min, I.max)

# ---------------------------------
# repl properties

show(io::IO, Is::IntervalSet) = print(io, Is.Is)

# ---------------------------------
# collection properties

length(I::IntervalSet)  = length(I.Is)

iterate(I::IntervalSet)        = length(I) > 0 ? (I.Is[1], 2) : nothing
iterate(I::IntervalSet, state) = (state <= length(I)) ? (I.Is[state], state+1) : nothing

getindex(I::IntervalSet, i) = getindex(I.Is, i)
getindex(A::T, I::IntervalSet) where T <: AbstractArray = [a for i in I for a in A[i]]

min(I::IntervalSet) = I.Is[1].lo
max(I::IntervalSet) = I.Is[end].hi

# ---------------------------------
# operations

# logical
isempty(I::IntervalSet) = length(I) == 0

# arithmetic

function ~(I::IntervalSet)
    starts = (i == 0 ? I.min : I[i].hi for i in 0:length(I))
    stops  = ((i<=length(I)) ? I[i].lo : I.max for i in 1:(length(I)+1))

    return IntervalSet(I.min, I.max, (Interval(start, stop) for (start, stop) in zip(starts,stops) if start < stop))
end

\(I::IntervalSet{T}, j::Interval{T})    where T <: Real = IntervalSet{T}((x for i ∈ I for x in i \ j), I.min, I.max)
\(I::IntervalSet{T}, J::IntervalSet{T}) where T <: Real = foldl(\, J; init=I)

# NOTE: extra generator voodoo avoids creating a list with nothings and then filtering them out
∩(I::IntervalSet{T}, j::Interval{T}) where T <: Real =
    IntervalSet{T}(
        ( x for i ∈ I for x in [i ∩ j] if !isnothing(x) ),
        I.min,
        I.max
    )
∩(j::Interval, I::IntervalSet) = I ∩ j

∩(I::IntervalSet{T}, J::IntervalSet{T}) where T <: Real =
    IntervalSet{T}(
       sort([ x for i ∈ I for j ∈ J for x in [i ∩ j] if !isnothing(x) ]),
       min(I.min,J.min),
       max(I.max,J.max)
    )

function ∪(I::IntervalSet, J::IntervalSet)
    IJ = copy(I)
    K  = J \ I
    for k ∈ K
        ι = searchsortedfirst(IJ.Is, k)
        insert!(IJ.Is, ι, k)
        merge!(IJ.Is)
    end

    return IJ
end

∪(I::IntervalSet, j::Interval) = I ∪ IntervalSet(j)

isdisjoint(A::IntervalSet, b::Interval) = all(isdisjoint(a,b) for a in A)

function containing(A::IntervalSet{T}, x::T) where T <: Real
    for a in A
        if x ∈ a
            return a
        end
    end

    return nothing
end

function containing(A::IntervalSet, b::Interval)
    for a in A
        if b ⊆ a
            return a
        end
    end

    return nothing
end

# -------------------------------------------------------------------------
# tests

function test()
    i = Interval(1, 10)
    j = Interval(0, 11)

    @show i
    @show j

    @show i \ j
    @show j \ i

    I = IntervalSet(0, 50, (0, 4), (2, 5), (7, 8), (10,15), (17,19))
    J = IntervalSet(0, 50, (3, 4), (6, 8), (11,14), (16,18))

    @show I
    @show J

    @show I \ i
    @show I \ j

    @show I \ J

    @show I ∩ j

    @show I ∪ J

    @show ~I

    return nothing
end

end


# example of a unit test in Julia
# runs a test for a certain module
# to run the tests, first open the package manager (`]` in REPL),
# activate the project if not done so and then enter `test`

# wrap all your tests and subgroups in a `@testset` block
@testset "Example" begin
    using STMOZOO.Example  # load YOUR module

    @testset "solve quadratic" begin
        # test some cases
        @test solve_quadratic_system(8.0, -2.0, 3.0) == 0.25

        # test for type
        @test solve_quadratic_system(7.0, 27.4, 3.0) isa Number

        # test for type stability
        @test solve_quadratic_system(1//2 , -2//1, 3//1) isa Rational

        P = [10 1; 1 5]
        q = [100, -7]

        # test for a result
        # use ≈ (`\approx<TAB>`) to check approximate equality
        # useful for rounding errors
        # NOTE: does NOT work on ≈ 0!
        @test solve_quadratic_system(P, q) ≈  - P \ q
        @test solve_quadratic_system(P, q, testPD=true) isa Vector

        # test if a certain error is thrown
        @test_throws AssertionError solve_quadratic_system(-P, q, testPD=true)
    end

    @testset " quadratic function" begin
        f_scalar = quadratic_function(3, 4, 8)

        @test f_scalar isa Function
        @test f_scalar(2) isa Number
        @test f_scalar(2) ≈ 0.5 * 3 * 2^2 + 4 * 2 + 8

        P = [9.0 -2; -2 3]
        q = [-3, -4]
        r = -π
        x = [-2.0, 2.8]

        f_vect = quadratic_function(P, q, r)
        @test f_vect isa Function
        @test f_vect(x) isa Number
        @test f_vect(x) ≈ 0.5x' * P * x + q' * x + r
    end
end


# This file is a part of InverseFunctions.jl, licensed under the MIT License (MIT).

"""
    inverse(f)

Returns the inverse of a function `f`.

The following conditions must be satisfied:

* `inverse(f) ∘ f` must be equivalent to `identity`.
* `inverse(f)(f(x)) ≈ x`
* `inverse(inverse(f))` must be equivalent (ideally identical) to `f`.

`inverse` supports mapped/broadcasted functions (via `Base.Fix1`) and (on
Julia >=v1.6) function composition.

Example:

```julia
foo(x) = inv(exp(-x) + 1)
inv_foo(y) = log(y / (1 - y))

InverseFunctions.inverse(::typeof(foo)) = inv_foo
InverseFunctions.inverse(::typeof(inv_foo)) = foo

x = 4.2
@assert inverse(foo)(foo(x)) ≈ x
@assert inverse(inverse(foo)) == foo

X = rand(10)
broadcasted_foo = Base.Fix1(broadcast, foo)
Y = broadcasted_foo(X)
@assert inverse(broadcasted_foo)(Y) ≈ X

# Requires Julia >= v1.6:
bar = log ∘ foo
@assert inverse(bar)(bar(x)) ≈ x
```
"""
function inverse end
export inverse

inverse(::typeof(inverse)) = inverse

@static if VERSION >= v"1.6"
    inverse(f::Base.ComposedFunction) = Base.ComposedFunction(inverse(f.inner), inverse(f.outer))
end

inverse(mapped_f::Base.Fix1{<:Union{typeof(map),typeof(broadcast)}}) = Base.Fix1(mapped_f.f, inverse(mapped_f.x))

inverse(::typeof(identity)) = identity
inverse(::typeof(inv)) = inv
inverse(::typeof(adjoint)) = adjoint
inverse(::typeof(transpose)) = transpose

inverse(::typeof(exp)) = log
inverse(::typeof(log)) = exp

inverse(::typeof(exp2)) = log2
inverse(::typeof(log2)) = exp2

inverse(::typeof(exp10)) = log10
inverse(::typeof(log10)) = exp10

inverse(::typeof(expm1)) = log1p
inverse(::typeof(log1p)) = expm1


# MIT License

#Copyright (c) 2021 hondoRandale <jules.rasetaharison@tutanota.com> and contributors

#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.

#######################
## 2D-test functions ##
#######################

## Beale function
Beale_2D(x;kwargs) = ( 1.5f0 - x[1] + x[1]*x[2] )^2 + ( 2.25f0 - x[1] + x[1]*x[2]^2 )^2 + ( 2.625f0 - x[1] + x[1]*x[2]^3 )^2

## Himmelblau's function
Himmelblau_2D(x;kwargs) = ( x[1]^2 + x[2] - 11.0f0 )^2 + ( x[1] + x[2]^2 - 7.0f0  )^2

## Lévi function N.13
Levi_no13_2D(x;kwargs) = sin( 3.0f0 * π * x[1] )^2 + ( x[1] - 1.0f0 )^2 * ( 1.0f0 + sin( 1.0f0+3.0f0*π*x[2] )^2 ) +
                                 ( x[2] - 1.0f0 )^2 * ( 1.0f0 + sin( 2.0f0*π*x[2] )^2 )

## Rosenbrock_function
Rosenbrock_2D(x;kwargs) = ( 1.0f0 - x[1] )^2 + 100.0f0 * (x[2] - x[1]^2)^2

## Booth_function
Booth_2D(x;kwargs) = ( x[1] + 2.0f0*x[2] - 7.0f0 )^2 +
                     ( 2.0f0*x[1] + x[2] - 5.0f0 )^2

## Ackley_function
Ackley_2D(x;kwargs) = -20.0f0 * exp( -0.2f0 * sqrt( 0.5f0 * ( x[1]^2 + x[2]^2 ) )  ) -
                      exp( 0.5f0 * ( cos( 2.0f0 * π * x[1] ) + cos( 2.0f0 * π * x[2] ) ) ) +
                      exp( 1.0f0 ) +
                      20.0f0

## Matyas_function
Matyas_2D(x;kwargs) = 0.26f0 * ( x[1]^2 + x[2]^2  ) - 0.48f0 * x[1] * x[2]

## Three_hump_camel_function
Three_hump_camel_2D(x;kwargs) = 2.0f0 * x[1]^2 - 1.05f0 * x[1]^4 +
                                x[1]^6 / 6 + x[1] * x[2] + x[2]^2

## Easom_function
Easom_2D(x;kwargs) = -1.0f0 * cos( x[1] ) * cos( x[2] ) * exp( -1.0f0 * ( (x[1]-π)^2 + (x[2]-π)^2 ) )

## Sphere_function
Sphere_2D(x;kwargs) = x[1]^2 + x[2]^2

## Schaffer_function_no2
Schaffer_function_no2_2D(x;kwargs) = 0.5f0 + ( sin( x[1]^2 - x[2]^2 )^2 - 0.5f0 ) /
                                             ( 1.0f0 + 0.001f0*( x[1]^2 + x[2]^2 ) )^2

## Bukin_function_no6
Bukin_function_no6_2D(x;kwargs) = 100.0f0 * sqrt( abs( x[2] - 0.01f0 * x[1]^2 ) ) + 0.01f0*abs( x[1] + 10.0f0 )

obj_functions_2D = [ Beale_2D,
                     Himmelblau_2D, Levi_no13_2D,
                     Ackley_2D, Matyas_2D, Booth_2D,
                     Three_hump_camel_2D, Easom_2D,
                     Sphere_2D, Rosenbrock_2D,
                     Schaffer_function_no2_2D,
                     Bukin_function_no6_2D ];

#######################
## ND-test functions ##
#######################

## Rastrigin function
Rastrigin_ND(x;kwargs) = 10.0f0 * Float32( length( x ) ) + sum( ( x.^2 ) .- 10.0f0 * cos.( 2.0f0*π*x )  )

## Sphere function
Sphere_ND(x;kwargs) = sum( x.^2 )

## Styblinski–Tang function
Styblinski_Tang_ND(x;kwargs) = 0.5f0 * sum( (x).^4 .- 16.0f0*(x).^2 .+ 5.0f0.*x )

## Axis Parallel Hyper-Ellipsoid function
AxisParallelHyperEll_ND(x;kwargs) = sum( Float32.( 1:1:length( x ) ) .* ( x.^2 ) )

## Zakharov Function
Zakharov_ND(x;kwargs) = sum( x.^2 ) + sum( 0.5f0 * Float32.( 1:1:length( x ) ) .* x )^2 + sum( 0.5f0 * Float32.( 1:1:length( x ) ) .* x )^4

## Michalewicz function
Michalewicz_ND(x;kwargs) = -sum( sin.( x ) .* sin.( ( Float32.( 1:1:length( x ) ) .* ( x.^2 ) ) / π ) )

## Exponential function
Exponential_ND(x;kwargs) = -exp( -0.5f0 * sum( x.^2 ) )

## Griewank function
Griewank_ND(x;kwargs) = 1.0f0 + sum( (x).^2 / 4000.0f0 ) - prod( cos.( x ./ sqrt.( Float32.( 1:1:length( x ) ) ) ) )

## Qing function
Qing_ND(x;kwargs) = sum( ( (x).^2 .- Float32.( 1:1:length( x ) ) ).^2 )

## Salomon function
Salomon_ND(x;kwargs) = 1.0f0 - cos( 2*π*sqrt( sum( (x).^2 ) ) ) + 0.1f0 * sqrt( sum( (x).^2 ) )

## Schwefel function
Schwefel_ND(x;kwargs) = 418.9829f0 * length( x ) - sum( x .* sin.( sqrt.( abs.( x ) ) ) )

## Schwefel 2.20 function
Schwefel_2_2_0_ND(x;kwargs) = sum( abs.( x ) )

## Schwefel 2.21 function
Schwefel_2_2_1_ND(x;kwargs) = maximum( abs.( x ) )

## Schwefel 2.22 function
Schwefel_2_2_2_ND(x;kwargs) = sum( abs.( x ) ) + prod( abs.( x ) )

## Schwefel 2.23 function
Schwefel_2_2_3_ND(x;kwargs) = sum( (x).^10 )

obj_functions_Nd = [ Rastrigin_ND, Sphere_ND,
                     Styblinski_Tang_ND, AxisParallelHyperEll_ND,
                     Zakharov_ND, Michalewicz_ND,
                     Exponential_ND, Griewank_ND,
                     Qing_ND, Salomon_ND,
                     Schwefel_ND, Schwefel_2_2_0_ND,
                     Schwefel_2_2_1_ND, Schwefel_2_2_2_ND,
                     Schwefel_2_2_3_ND ]