# ------------------------------------------------------------------------------------------
# # Delay Coordinates & Neighborhoods
#
#
# Topics:
# * Delay coordinates & why they ROCK!
# * A `Reconstruction` is a subtype of `AbstractDataset`
# * Estimating Parameters for `Reconstruction`s
# * Multiple-time, multiple timeseries `Reconstruction`s
# * Finding neighborhoods of points in a `Dataset`
# * Excluding temporal neighbors
#
# ---
#
# # Delay Coordinates Reconstruction
# Let's say you have a "real-world system" which you measure in an experimental set-up. You
# are assuming that the system is composed of several dynamic variables, but you can only
# measure one of them (or some function of the variable).
#
# You have a severe lack of recorded information for the system. What do you do?
# 1. Give up on science, it is a complete waste of time.
# 2. Use [Taken's theorem](https://en.wikipedia.org/wiki/Takens%27s_theorem), which is
# indistinguishable from magic.
#
# **DynamicalSystems.jl** suggests the third approach.
#
# From a timeseries $s$ one can *reconstruct* a state-space $\mathbf{z}$ simply by shifting
# $s$ in time, like
#
#   $$\mathbf{z}(n) = (s(n), s(n+\tau), s(n+2\tau), \dots, s(n+(D-1)\tau))$$
#
# This is done with the `Reconstruction(s, D, τ)` function
# ------------------------------------------------------------------------------------------

using DynamicalSystems

s = rand(100000)
D = 3 # reconstruction dimension
τ = 4 # reconstruction delay
R = Reconstruction(s, D, τ)

# ------------------------------------------------------------------------------------------
# ---
#
# Here are some nice examples of `Reconstruction`s of a 3D continuous chaotic system, using
# each of the variables of the system, different delay times and dimension of `2`:
# ------------------------------------------------------------------------------------------

using DynamicalSystems, PyPlot

ds = Systems.gissinger(ones(3)) # 3D continuous chaotic system, also shown in orbit diagrams tutorial
dt = 0.05
data = trajectory(ds, 1000.0, dt = dt)

xyz = columns(data)

figure(figsize = (12,10))
k = 1
for i in 1:3
    for τ in [5, 30, 100]
        R = Reconstruction(xyz[i], 2, τ)
        ax = subplot(3,3,k)
        plot(R[:, 1], R[:, 2], color = "C$(k-1)", lw = 0.8)
        title("var = $i, τ = $τ")
        k+=1
    end
end

tight_layout()
suptitle("2D Reconstructions")
subplots_adjust(top=0.9);

# ------------------------------------------------------------------------------------------
# How does this compare to the "real" two-dimensional representation of the system?
# ------------------------------------------------------------------------------------------

figure(figsize=(6,4))
plot(data[:, 2], data[:, 3], lw=1.0);

# ------------------------------------------------------------------------------------------
# ---
#
# # `Reconstruction <: AbstractDataset`
# A `Reconstruction` instance can be passed around and used exactly like a `Dataset`!
#
# Let's look at a `Reconstruction` of data from a gissinger system's trajectory above
# ------------------------------------------------------------------------------------------

R = Reconstruction(data[:, 1], 2, 30)
R[31:end, 1] == R[1:end-30, 2]

a = 0.0
for point ∈ R
    a += mean(point)
end
a/length(R)

# ------------------------------------------------------------------------------------------
# **Taken's theorem says that some quantities remain invariant under a reconstruction**
#
# We'll show this using a `Reconstruction` with the 3rd dimension of `data`.
#
# (D = 2 is not best for this system. D = 3 is better!)
# ------------------------------------------------------------------------------------------

R = Reconstruction(data[:, 1], 3, 30)

# ------------------------------------------------------------------------------------------
# Let's compare the information dimension of the `Reconstruction`
# ------------------------------------------------------------------------------------------

I1 = information_dim(R)

# ------------------------------------------------------------------------------------------
# and the information dimension of the attractor directly
# ------------------------------------------------------------------------------------------

I2 = information_dim(data)

println("|Reconstructed - original| dimension: $(abs(I1 - I2))")

# ------------------------------------------------------------------------------------------
# # Estimating Reconstruction Parameters
#
# It is important to understand that even though Taken's theorem is 99% magic, it is **not**
# 100%. One still has to choose "appropriately good" values for both the delay time as well
# as the reconstruction dimension! Thankfully, **DynamicalSystems.jl** has some support for
# that as well!
# ------------------------------------------------------------------------------------------

# ------------------------------------------------------------------------------------------
# * `estimate_delay` estimates delay time `τ` using the autocorrelation of the signal
# * `estimate_dimension` returns an estimator of the embedding dimension `D` using Cao's
# method
# ------------------------------------------------------------------------------------------

# ------------------------------------------------------------------------------------------
# # Multiple-time, multiple-timeseries Reconstructions
#
# The `Reconstruction` we have seen so far is just a "Vanilla version"...
#
# One can also perform:
#
# 1. Reconstructions with multiple delay times, which tries to capture the effect of
# multiple timescales existing in a system.
# 2. Reconstructions with multiple timeseries.
# 3. Reconstructions with multiple timeseries *and* multiple delay times!
#
# See the documentation string of `Reconstruction` for more!
# ------------------------------------------------------------------------------------------

# ------------------------------------------------------------------------------------------
# # Neighborhoods
#
# A "neighborhood" is a collection of points that is near a given point. `Dataset`s
# interface the module
# [`NearestNeighbors`](https://github.com/KristofferC/NearestNeighbors.jl) in order to find
# this neighborhood.
#
# We use the function `neighborhood`. The call signature is:
# ```julia
# neighborhood(point, tree, ntype)
# ```
#
# ------------------------------------------------------------------------------------------

# ------------------------------------------------------------------------------------------
# `point` is simply the query point. `tree` is the structure required by
# [`NearestNeighbors`](https://github.com/KristofferC/NearestNeighbors.jl), and is obtained
# simply by:
# ------------------------------------------------------------------------------------------

dataset = Dataset(rand(1000,3))
tree = KDTree(dataset)

# ------------------------------------------------------------------------------------------
# The third argument to `neighborhood` is the *type* of the neighborhood.
#
# * There are two types of neighborhoods!
#
# The first one is defined as the `k` nearest points to a given point. It is represented in
# code by:
# ------------------------------------------------------------------------------------------

mybuddies = FixedMassNeighborhood(3)

# ------------------------------------------------------------------------------------------
# *For experts: for a `FixedMassNeighborhood` a KNN search is done*
# ------------------------------------------------------------------------------------------

point = ones(3)
n = neighborhood(point, tree, mybuddies)

# ------------------------------------------------------------------------------------------
# Notice that the `neighborhood` function does not return the points themselves, but rather
# the indices of the points in the original data:
# ------------------------------------------------------------------------------------------

println("Fixed mass neighborhood of $(point) is:")

for i in n
    println(dataset[i])
end

# ------------------------------------------------------------------------------------------
# ---
#
# The second type of neighborhood contains all the points that are within some given
# distance `ε` from the query.
#
# In code, we represent this as:
# ------------------------------------------------------------------------------------------

where_u_at = FixedSizeNeighborhood(0.001)

# ------------------------------------------------------------------------------------------
#  *For experts: for `FixedSizeNeighborhood` an inrange search is done*
# ------------------------------------------------------------------------------------------

n2 = neighborhood(point, tree, where_u_at)

plz_come_closer = FixedSizeNeighborhood(0.2)
n2 = neighborhood(point, tree, plz_come_closer)

println("Fixed size neighborhood of $(point) is:")

for i in n2
    println(dataset[i])
end

# ------------------------------------------------------------------------------------------
# Okay, so points that have distance < ε are accepted as a neighborhood.
#
# How do we define the "distance" though? When defining a `tree`, you can optionally give a
# distance function. By default Euclidean distance is used, but others also work. For
# example, we can use the `Distances` package to get distance functions,
# ------------------------------------------------------------------------------------------

using Distances

# ------------------------------------------------------------------------------------------
# and define the distance as the `Distances`'s `Chebyshev` distance:
# ------------------------------------------------------------------------------------------

funky_tree = KDTree(dataset, Chebyshev())

n3 = neighborhood(point, funky_tree, plz_come_closer)

# ------------------------------------------------------------------------------------------
# # Excluding temporal neighbors
#
# Before moving on, let's see one last thing.
#
# In this example, the point I want the neighborhood of is now part of my dataset:
# ------------------------------------------------------------------------------------------

point = dataset[end]

# ------------------------------------------------------------------------------------------
# Let's calculate again the two neighborhoods
# ------------------------------------------------------------------------------------------

tree = KDTree(dataset)

# ------------------------------------------------------------------------------------------
# We'll find suuuuuuuper close neighbors with a **very** small $\epsilon$:
# ------------------------------------------------------------------------------------------

ε = 0.000001
where_u_at = FixedSizeNeighborhood(ε)
n2 = neighborhood(point, tree, where_u_at)

# ------------------------------------------------------------------------------------------
# and now we can find the nearest neighbor:
# ------------------------------------------------------------------------------------------

my_best_friend = FixedMassNeighborhood(1)
n3 = neighborhood(point, tree, my_best_friend)

println(n2)
println(n3)

length(dataset) == n2[1] == n3[1]

# ------------------------------------------------------------------------------------------
# **What is happening here is that the `neighborhood` also counted the `point` itself, since
# it is also part of the dataset.**
#
# * Almost always this behavior needs to be avoided. For this reason, there is a second
# method for `neighborhood`:
#
# ```julia
# neighborhood(point, tree, ntype, idx::Int, w::Int = 1)
# ```
#
# In this case, `idx` is the index of the point in the original data. `w` stands for the
# Theiler window (positive integer).
#
# Only points that have index
# `abs(i - idx) ≥ w` are returned as a neighborhood, to exclude close temporal neighbors.
#
# * The default `w=1` is the case of excluding the `point` itself.
#
# ---
#
# Let's revisit the last example (using the default value of `w = 1`):
# ------------------------------------------------------------------------------------------

point = dataset[end]
idx = length(dataset)

n2 = neighborhood(point, tree, where_u_at, idx)
n3 = neighborhood(point, tree, my_best_friend, idx)

println(n2)
println(n3)

# ------------------------------------------------------------------------------------------
# As you can see, there isn't *any* neighbor of `point` with distance `< 0.000001` in this
# dataset, but there is always a nearest neighbor:
# ------------------------------------------------------------------------------------------

println(dataset[n3[1]], " is the nearest neighbor of ", point)

# ------------------------------------------------------------------------------------------
# # Docstrings
# ------------------------------------------------------------------------------------------

?Reconstruction

?neighborhood

?AbstractNeighborhood

.nbexports/introductory-tutorials/broader-topics-and-ecosystem/introduction-to-dynamicalsystems.jl/5. Delay Coordinates & Neighborhoods.jl


using LinearAlgebra
function softmax(a)
    if ndims(a) == 1
        return exp.(a) ./ sum(exp.(a))
    else
        return exp.(a) ./ sum(exp.(a), dims = 2)
    end
end
function fit(x, t; alpha = 0.01, tau_max = 1000)
    function CEE(w, x, t) #this is Cross entropy Error
        p = softmax(x * w)
        grad = -(x' * (t - p))
        return grad / length(x[:, 1])
    end
    if size(x)[1] != size(t)[1]#Processing when the matrix is organized by dependent variable
        x = x'
    end
    x = hcat(ones(size(x)[1], 1), x)
    w = ones(size(x)[2], size(t)[2])
    for tau in 1 : tau_max
        grad = CEE(w, x, t)
        w -= alpha * grad
    end
    return w
end

function forecast(x, w)
    x = hcat(ones(size(x)[1], 1), x)
    return softmax(x * w)
end

#Pass so that the row is each data sample and the column is each feature.
function predict(x, w)
    x = hcat(ones(size(x)[1], 1), x)
    s = softmax(x * w)
    p = [findfirst(s[i, :] .== maximum(s[i, :])) for i in 1:size(s)[1]]
end


using OrdinaryDiffEq, ModelingToolkit, DiffEqOperators, DomainSets
# Method of Manufactured Solutions: exact solution
u_exact = (x,t) -> exp.(-t) * cos.(x)

@parameters t x y
@variables u(..)
Dxx = Differential(x)^2
Dyy = Differential(y)^2
Dt = Differential(t)
t_min= 0.
t_max = 2.0
x_min = 0.
x_max = 2.
y_min = 0.
y_max = 2.

#xmax = 50.0
#ymax = 30.0
#ncells = 50.0
#dx = x_max / ncells

lecturer(x, y) = x > x_max / 2 - dx && x < x_max / 2 + dx && y > y_max / 10 - dx && y < y_max / 10 + dx
emission(x, y, emisrate) = lecturer(x, y) ? emisrate : 0
@register emission(x, y, emisrate)
emisrate = 10;

# 3D PDE
eq  = Dt(u(t,x,y)) ~ Dxx(u(t,x,y)) + Dyy(u(t,x,y)) + emission(x, y, emisrate)
#emission(x, y, emisrate) = emisrate

analytic_sol_func(t,x,y) = exp(x+y)*cos(x+y+4t)
# Initial and boundary conditionn

bcs = #[u(t_min,x,y) ~ analytic_sol_func(t_min,x,y),
      [u(t_min,x,y) ~ 0,
       u(t,x_min,y) ~ 0,
       u(t,x_max,y) ~ 0,
       u(t,x,y_min) ~ 0,
       u(t,x,y_max) ~ 0]

# Space and time domains
domains = [t ∈ IntervalDomain(t_min,t_max),
           x ∈ IntervalDomain(x_min,x_max),
           y ∈ IntervalDomain(y_min,y_max)]
pdesys = PDESystem([eq],bcs,domains,[t,x,y],[u(t,x,y)])

# Method of lines discretization
dx = 0.1; dy = 0.1
discretization = MOLFiniteDifference([x=>dx,y=>dy],t)
prob = ModelingToolkit.discretize(pdesys,discretization)
sol = solve(prob,Tsit5())

# Ploting
using Plots
xs,ys = [infimum(d.domain):dx:supremum(d.domain) for d in domains]
u_sol = reshape(sol.u[300], length(xs)-2,length(ys)-2)

plot(xs[2:length(xs)-1], ys[2:length(ys)-1], u_sol, linetype=:contourf,title = "solution")

#Animation
anim = @animate for i ∈ 1:length(sol.t)
    u_sol = reshape(sol.u[i], length(xs)-2,length(ys)-2)
    plot(xs[2:length(xs)-1], ys[2:length(ys)-1], u_sol, linetype=:contourf,title = "solution")

end
gif(anim, "anim_fps15.gif", fps = 15)


# This implements src/snes/examples/tutorials/ex2.c from PETSc using the PETSc.jl package, using SNES
#
# This is the same as SNES_ex2b.j, except that we show how automatic differentiation can be used to
# compute the jacobian.
#
# Newton method to solve u'' + u^{2} = f, sequentially.

using PETSc, MPI, LinearAlgebra, SparseArrays, Plots, ForwardDiff

if ~MPI.Initialized()
    MPI.Init()
end

PETSc.initialize()

```
    Computes initial guess
```
function FormInitialGuess!(x)
    for i=1:length(x)
        x[i] = 0.50;
    end
end

```
    Computes the residual f, given solution vector x
```
function FormResidual!(f,x)
    n       =   length(x);
    xp      =   LinRange(0.0,1.0, n);
    F       =   6.0.*xp .+ (xp .+1.e-12).^6.0;      # define source term function

    dx      =   1.0/(n-1.0);
    f[1]    =   x[1] - 0.0;
    for i=2:n-1
        f[i] = (x[i-1] - 2.0*x[i] + x[i+1])/dx^2 + x[i]*x[i] - F[i]
    end
    f[n]    =   x[n] - 1.0;

end

```
    Wrapper which makes it easier to compute the jacobian using automatic differntiation
```
function  ForwardDiff_res(x)

    f   = zero(x)               # vector of zeros, of same type as e
    FormResidual!(f,x);

    return f;
end

```
    Computes the jacobian, given solution vector x
```
function FormJacobian!(x, args...)

    J        =  args[1];        # preconditioner = args[2], in case we want it to be different from J

    # Use AD to compute jacobian; by transferring x into sparse, the output will be sparse
    J_julia  =  ForwardDiff.jacobian(ForwardDiff_res,sparse(x));

    J       .=  J_julia;   # copy to petsc format
end

# ==========================================
# Main code

# Compute initial solution
n   =   101;
x   =   zeros(n);

FormInitialGuess!(x);

# Compute initial jacobian using a julia structure to obtain the nonzero structure
# Note that we can also obtain this structure in a different manner
Jstruct  = zeros(n,n);
FormJacobian!(x, Jstruct);                              # jacobian in julia form
Jsp      =   sparse(Float64.(abs.(Jstruct) .> 0))       # sparse julia, with 1.0 in nonzero spots
PJ       =   PETSc.MatSeqAIJ(Jsp);                      # transfer to PETSc (initialize matrix with correct nonzero pattern)

# Setup SNES
x_s = PETSc.VecSeq(x);                  # solution vector
res = PETSc.VecSeq(zeros(size(x)));     # residual vector

S = PETSc.SNES{Float64}(MPI.COMM_SELF;
        snes_rtol=1e-12,
        snes_monitor=true,
        snes_converged_reason=true);
PETSc.setfunction!(S, FormResidual!, res)
PETSc.setjacobian!(S, FormJacobian!, PJ, PJ)

# solve
PETSc.solve!(x_s, S);

# Extract & plot solution
x_sol = x_s.array;                  # convert solution to julia format
FormResidual!(res.array,x_sol)      # just for checking, compute residual
@show norm(res.array)

plot(LinRange(0,1,n),x_sol,xlabel="width",ylabel="solution")

#PETSc.finalize()


# ---
# title: 746. Min Cost Climbing Stairs
# id: problem746
# author: zhwang
# date: 2022-01-19
# difficulty: Easy
# categories: Array, Dynamic Programming
# link: <https://leetcode.com/problems/min-cost-climbing-stairs/description/>
# hidden: true
# ---
#
# On a staircase, the `i`-th step has some non-negative cost `cost[i]` assigned
# (0 indexed).
#
# Once you pay the cost, you can either climb one or two steps. You need to find
# minimum cost to reach the top of the floor, and you can either start from the
# step with index 0, or the step with index 1.
#
# **Example 1:**
#
#
#
#     Input: cost = [10, 15, 20]
#     Output: 15
#     Explanation: Cheapest is start on cost[1], pay that cost and go to the top.
#
#
# **Example 2:**
#
#
#
#     Input: cost = [1, 100, 1, 1, 1, 100, 1, 1, 100, 1]
#     Output: 6
#     Explanation: Cheapest is start on cost[0], and only step on 1s, skipping cost[3].
#
#
# **Note:**
#
#   1. `cost` will have a length in the range `[2, 1000]`.
#   2. Every `cost[i]` will be an integer in the range `[0, 999]`.
#
#
## @lc code=start
using LeetCode

## Dynamic Programming
function min_cost_climbing_stairs(cost::Vector{Int})::Int
    first, second = cost[1], cost[2]
    for c in @view cost[3:end]
        first, second = second, min(first, second) + c
    end
    return min(first, second)
end

## @lc code=end


# Arvind Prasadan
# 2019 January
# DMF Package
# Creates an Autocorrelation Matrix at a given lag

"""
	autocorr_mat(X, lags = 1)

Creates an Autocorrelation Matrix at a set of given lags.

# Arguments
- `X`: Data matrix: p dimensions/sensors x n samples
- `lags`: list of lags (Integral)

# Outputs
- `R`: Data cube of autocorrelation matrices: # lags x p x p
- `lags`: Lags used
"""
function autocorr_mat(X, lags = 1)

    p, n = size(X)

    # Subtract Mean
    mu = mean(X; dims = 2)
    X = X .- mu

    # Get Standard Deviations
    sigma = std(X; dims = 2)

    # Sanity check lags
    lags = abs.(Int.(round.(lags)))
    if 1 == length(lags)
        lags = [lags]
    else
        lags = [min(x, n - 1) for x in lags]
        lags = sort(unique(lags))
    end

    R_unshaped = autocor(X', lags)
    R = zeros(length(lags), p, p)
    for l = 1:1:length(lags)
        R[l, :, :] = hcat([circshift(R_unshaped[l, :], t)[:] for t in 0:1:(p - 1)]...)
    end

    return R, lags
end


module CorrelationDimension

import Plots

using Statistics: mean, covm, varm

using Distributions: fit
using LinearAlgebra: norm
using StatsBase: Histogram

export correlation_dimension, plot_correlation

"""
    correlation_dimension(x; dist=(x1,x2)->norm(x1.-x2)) -> (corrdim, logr, logNr, intercept)

Compute an estimate of the correlation dimension for a set of points in space, with
coordinates for the ith point given as `x[:,i]`.

This is given by

δ̂ = ∂[log N(r)]/∂[log r]

where N(r) is the number of point pairs within a distance r of each other.  This is
estimated by binning the pair-distances into bins (chosen automatically)
and fitting a straight line through the log-distribution.

The distance function `dist` by default is the Cartesian 2-norm, but any function may be
supplied.

The function returns a named tuple of the estimate of the correlation dimension, `corrdim`,
the lower edges of the distance bins, `logr`, the weights in the distance bins `logNr`, and
the `intercept` of the best fitting line (of which the slope is `corrdim`).
"""
function correlation_dimension(x; dist=(a,b)->norm(a .- b))
    D, n = size(x)
    # List of log(pairwise distances)
    point1 = Vector{float(eltype(x))}(undef, D)
    point2 = similar(point1)
    logr = float(eltype(x))[]
    for i in 1:n, j in (i+1):n
        point1 .= x[:,i]
        point2 .= x[:,j]
        push!(logr, log(dist(point1, point2)))
    end
    # Construct histogram
    h = fit(Histogram, logr)
    # Linear regression
    logr = h.edges[1][1:end-1]
    logNr = log.(cumsum(h.weights./(n*(n-1)/2)))
    c, δ̂ = linear_regression(logr, logNr)
    (corrdim=δ̂, logr=logr, logNr=logNr, intercept=c)
end

"""
    correlation_dimension(x1, x2, ..., xN; kwargs...) -> δ̂

Specify coordinates with dimension N as N separate vectors.
"""
correlation_dimension(x, y, args...; kwargs...) = correlation_dimension(permutedims(hcat(x, y, args...)))

"""
    plot_correlation((logr, logNr, slope, intercept))

Create a plot of the output from `correlation_dimension`.  Input is a tuple
of the log of the distance bins (`logr`), the weights of the histogram (`logNr`),
plus the `slope` and `intercept` of the best fitting line.  The `slope` therefore
is the estimate of the ccorrelation dimension.
"""
function plot_correlation((corrdim, logr, logNr, intercept))
    Plots.scatter(logr, logNr, label="Data", legend=:topleft)
    Plots.plot!(x->corrdim*x+intercept, label="y = $(round(corrdim, sigdigits=3))x + $(round(intercept, sigdigits=3))")
end

"""
    linear_regression(x, y)

Perform simple linear regression using Ordinary Least Squares. Returns `a` and `b` such
that `a + b*x` is the closest straight line to the given points `(x, y)`, i.e., such that
the squared error between `y` and `a + b*x` is minimized.
"""
function linear_regression(x::AbstractVector, y::AbstractVector)
    size(x) == size(y) || throw(DimensionMismatch("x and y must be the same size"))
    mx, my = mean(x), mean(y)
    b = covm(x, mx, y, my)/varm(x, mx)
    a = my - b*mx
    return a, b
end

end # module


# Michelle Medina
# August 11, 2020
# Testing Convergence of Derivative

# USING L2 NORM
H = []
E = []

for N in 50:200
    grid = Params(N,4)

	xVal = collect(range(0.0, stop=1.0, length = 129))
	y = calcNodal(x -> sin(4*pi*x), xVal)
	cosx = calcNodal(x -> 4*pi*cos(4*pi*x), xVal)
	modal = Nodal_2_H(y)

	dy = zeros(length(xVal))
	for index in CartesianIndices(xVal)
		dy[index] = derivEvaluate(modal, xVal[index])
	end

    # u = functionOnGrid(x->sin(x),grid)
    # dUdx = functionOnGrid(x->cos(x),grid) #known derivative of sin(x)
    # derivative = Deriv1D(u,grid)

    #L 2 norm: sum of all the differences between the numerical derivative and
    #the analytical one squared and then take the square root
    error = sqrt(sum((dy[2:end-1] - cosx[2:end-1]).^2))

    h = dX(grid)
    #append h to the array H and error to the array E
    append!(H,h)
    append!(E,error)

end

#plotting error vs h
loglog(H,E,"r-")

#first order convergence
loglog(H,H,"g--",label="1")

#second order convergence
loglog(H,H.^2,"b--",label="2")
xlabel("h")
ylabel("L 2 Norm")
legend(loc="best",frameon=false)
show()


using NumericalMethodsforEngineers
using Test

b = [16. -24. 18.; 3. -2. 0.; -9. 18. -17.]
x0 = [1., 1., 1.]

tol = 1.0e-5
limit = 100

(iters, eigval, eigvec) = nmex(b, x0; tol=tol, limit=100)

#eb = eig(b)
ebvals, ebvecs = (eigen(b)...,)

@test round.(eigvec[:,1], digits=5) == round.(ebvecs[:,1], digits=5)


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

Nsim = 10000;
Nstep = 30;
sigma = 0.2;
mc = MonteCarloConfiguration(Nsim, Nstep, FinancialMonteCarlo.SobolMode());
toll = 0.8

spotData1 = ZeroRate(r);

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

display(Model)

@show FwdPrice = pricer(Model, spotData1, mc, FwdData);
@show EuPrice = pricer(Model, spotData1, mc, EUData);
@show EuBinPrice = pricer(Model, spotData1, mc, EUBin);
@show AmPrice = pricer(Model, spotData1, mc, AMData);
@show AmBinPrice = pricer(Model, spotData1, mc, AmBin);
@show BarrierPrice = pricer(Model, spotData1, mc, BarrierData);
@show AsianPrice1 = pricer(Model, spotData1, mc, AsianFloatingStrikeData);

@test abs(FwdPrice - 99.1078451563562) < toll
@test abs(EuPrice - 8.43005524824866) < toll
@test abs(AmPrice - 8.450489415187354) < toll
@test abs(BarrierPrice - 7.5008664470880735) < toll
@test abs(AsianPrice1 - 4.774451704549382) < toll


function nlp3(oct::Bool = false)
    m = JuMP.Model()
    @variable(m, 0 <= x[1:10])
    for i = 1:8
        JuMP.set_lower_bound(x[[1, 2, 4, 6, 7, 8, 9, 10][i]],
                             [1,1,1,85,90,3,1.2,145][i])
    end
    for i = 1:10
        JuMP.set_upper_bound(x[i], [2000, 16000, 120, 5000, 2000,
                                    93, 95, 12, 4, 162][i])
        JuMP.set_start_value(x[i], [1724.90452208, 16000,
                                    98.0900813608, 3049.1211364,
                                    1995.02326433, 90.718089075,
                                    94.2274481766, 10.432474977,
                                    2.59051951438, 149.682344530][i])
    end
    @constraint(m, e1, x[1] - 1.22*x[4] + x[5] == 0)
    @constraint(m, e2, x[9] + 0.222*x[10] == 35.82)
    @constraint(m, e3, 3*x[7] - x[10] == 133)
    if !oct
        @NLconstraint(m, e4, x[7] - 1.098*x[8] + 0.038*(x[8]^2) - 0.325*(x[6] - 89) == 86.35)
        @NLconstraint(m, e5, x[4]*x[9]*x[6] + 1000*x[3]*x[6] - 98000*x[3] == 0)
        @NLconstraint(m, e6, x[2] + x[5] - x[1]*x[8] == 0)
        @NLconstraint(m, e7, 1.12*x[1] + 0.13167*x[8]*x[1] - 0.00667*(x[8]^2)*x[1] - x[4] >= 0)
        @NLobjective(m, Min, 5.04*x[1] + 0.035*x[2] + 10*x[3] + 3.36*x[5] - 0.063*x[4]*x[7])
        return m
    else
        @variable(m, obj)
        @objective(m, Min, obj)
        gm = GlobalModel(model = m, name = "nlp3")
        add_nonlinear_constraint(gm, :(x -> x[7] - 1.098*x[8] + 0.038*(x[8]^2) - 0.325*(x[6] - 89) - 86.35),
                                vars = [x[6], x[7], x[8]], name = "e4", equality=true)
        add_nonlinear_constraint(gm, :(x -> x[4]*x[9]*x[6] + 1000*x[3]*x[6] - 98000*x[3]),
                                vars = [x[3], x[4], x[6], x[9]], name = "e5", equality = true)
        add_nonlinear_constraint(gm, :(x -> x[2] + x[5] - x[1]*x[8]),
                                vars = [x[1], x[2], x[5], x[8]], name = "e6", equality = true)
        add_nonlinear_constraint(gm, :(x -> 1.12*x[1] + 0.13167*x[8]*x[1] - 0.00667*(x[8]^2)*x[1] - x[4]),
                                vars = [x[1], x[4], x[8]], name = "e7")
        add_nonlinear_constraint(gm, :(x -> 5.04*x[1] + 0.035*x[2] + 10*x[3] + 3.36*x[5] - 0.063*x[4]*x[7]),
                                vars = [x[1], x[2], x[3], x[4], x[5], x[7]],
                                dependent_var = obj, name = "obj")
        return gm
    end
end


### A Pluto.jl notebook ###
# v0.15.1

using Markdown
using InteractiveUtils

# ╔═╡ 1e037376-8252-41fb-ae95-93bc9011b7fa
# we need to install a special package in order to use the gamma function
using SpecialFunctions

# ╔═╡ 76d6c2d4-e370-4e83-9d04-ca6fc546bdd9
# we write a function that returns the exact volume of a hypersphere in d dimensions
function exactVol(d)
    vol = pi^(d/2) / gamma(1 + d/2)
    return vol
end

# ╔═╡ b58cf6d8-291c-49f4-a61a-51f1a7454651
n = 10000;

# ╔═╡ 2f4a8b5e-f7c9-4abc-b04d-3286207a97b3
dim = 20;

# ╔═╡ 4da795ef-b8d3-4c94-bb89-7eeaca9602fb
md"Exact volume is:"

# ╔═╡ 8d8e10d0-2597-4198-92b1-9ab2ebe4b3c8
exV = exactVol(dim)

# ╔═╡ 6a8c6c37-2a48-47c7-8d40-ca276c8a09d2
# we write a function that returns a random vector from [0,1]^d with exponential density
function randExp(d, lam)

	# we generate a uniformly random vector
	y = rand(d)

	# we transform (elementwise) to random numbers in [exp(-lambda),1]
	y = exp(-lam) .+ (1-exp(-lam))*y

	# we now get numbers with exponential density
	x = -log.(y)/lam
	return x
end

# ╔═╡ 8de1cad7-ac2c-42ec-9581-1d7ff06d576d
# we write an improoved function that uses Monte Carlo with exponential importance sampling
# to estimate the volume of the unit hypersphere in d dimensions.
# We choose our points x with independent Cartesian components and prob density exp(-lam.xi) / [ lam*((1-exp(-lam)) ].
# We achieve this by taking xi = (1/lam) log (1/y) where y is chosen uniformly in (exp(-lam),1)
function sphereVolMCImpExp(d, n, lam)
	count = 0.0
	for i = 1:n

		# we generate x according to our distribution
		x = randExp(d, lam)

		# we calculate the squared norm of x and check if x is inside the hypersphere
		norm2 = x' * x
		if norm2 < 1

			# we compute the density at x
			rho = 1
			for i = 1:d

				# note that y(i) = exp(-lam * x(i))
				rho *= exp(-lam*x[i]) * lam / (1-exp(-lam))
			end
			count += 1/rho
		end
	end
	vol =  2^d * count / n
	return vol
end

# ╔═╡ c8381d29-3b9f-4399-9a60-c0b6b620e99c
sphereVolMCImpExp(20, n, 2)

# ╔═╡ 0e1580e1-cdae-498e-ab14-261f8a96a802
md" We do the calculation $m$ times and check mean/variance:"

# ╔═╡ 770e2019-420d-415b-b498-96493ac676bf
m = 1000;

# ╔═╡ dc6e6729-7ce5-45a8-8ecc-b320d283340a
data = [sphereVolMCImpExp(20, n, 2) for i in 1:m];

# ╔═╡ fb6c8ff3-d200-4e48-b9ad-aa34d188e675
mean = sum(data)/m

# ╔═╡ 48b51297-b3fb-4bce-8d41-7af6120b553c
error = mean - exV

# ╔═╡ bb18d766-cdad-45b7-aebb-c59763896bd4
variance = sum(broadcast(x -> (x-mean)^2, data))/m

# ╔═╡ 00000000-0000-0000-0000-000000000001
PLUTO_PROJECT_TOML_CONTENTS = """
[deps]
SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b"

[compat]
SpecialFunctions = "~1.6.1"
"""

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

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

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

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

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

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

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

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

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

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

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

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

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

[[IrrationalConstants]]
git-tree-sha1 = "f76424439413893a832026ca355fe273e93bce94"
uuid = "92d709cd-6900-40b7-9082-c6be49f344b6"
version = "0.1.0"

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

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

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

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

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

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

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

[[LogExpFunctions]]
deps = ["DocStringExtensions", "IrrationalConstants", "LinearAlgebra"]
git-tree-sha1 = "3d682c07e6dd250ed082f883dc88aee7996bf2cc"
uuid = "2ab3a3ac-af41-5b50-aa03-7779005ae688"
version = "0.3.0"

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

[[SpecialFunctions]]
deps = ["ChainRulesCore", "LogExpFunctions", "OpenSpecFun_jll"]
git-tree-sha1 = "a322a9493e49c5f3a10b50df3aedaf1cdb3244b7"
uuid = "276daf66-3868-5448-9aa4-cd146d93841b"
version = "1.6.1"

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

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

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

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

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

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

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

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

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

# ╔═╡ Cell order:
# ╠═1e037376-8252-41fb-ae95-93bc9011b7fa
# ╠═76d6c2d4-e370-4e83-9d04-ca6fc546bdd9
# ╠═b58cf6d8-291c-49f4-a61a-51f1a7454651
# ╠═2f4a8b5e-f7c9-4abc-b04d-3286207a97b3
# ╟─4da795ef-b8d3-4c94-bb89-7eeaca9602fb
# ╠═8d8e10d0-2597-4198-92b1-9ab2ebe4b3c8
# ╠═6a8c6c37-2a48-47c7-8d40-ca276c8a09d2
# ╠═8de1cad7-ac2c-42ec-9581-1d7ff06d576d
# ╠═c8381d29-3b9f-4399-9a60-c0b6b620e99c
# ╟─0e1580e1-cdae-498e-ab14-261f8a96a802
# ╠═770e2019-420d-415b-b498-96493ac676bf
# ╠═dc6e6729-7ce5-45a8-8ecc-b320d283340a
# ╠═fb6c8ff3-d200-4e48-b9ad-aa34d188e675
# ╠═48b51297-b3fb-4bce-8d41-7af6120b553c
# ╠═bb18d766-cdad-45b7-aebb-c59763896bd4
# ╟─00000000-0000-0000-0000-000000000001
# ╟─00000000-0000-0000-0000-000000000002


using StatsBase
using Test

### Trimming outliers

@test collect(trim([8,2,3,4,5,6,7,1], prop=0.1)) == [8,2,3,4,5,6,7,1]
@test collect(trim([8,2,3,4,5,6,7,1], prop=0.2)) == [2,3,4,5,6,7]
@test collect(trim([1,2,3,4,5,6,7,8,9], prop=0.4)) == [4,5,6]
@test collect(trim([8,7,6,5,4,3,2,1], count=1)) == [7,6,5,4,3,2]
@test collect(trim([1,2,3,4,5,6,7,8,9], count=3)) == [4,5,6]

@test_throws ArgumentError trim([])
@test_throws ArgumentError trim([1,2,3,4,5], prop=0.5)

@test collect(trim!([8,2,3,4,5,6,7,1], prop=0.1)) == [8,2,3,4,5,6,7,1]
@test collect(trim!([8,2,3,4,5,6,7,1], prop=0.2)) == [2,3,4,5,6,7]
@test collect(trim!([1,2,3,4,5,6,7,8,9], prop=0.4)) == [4,5,6]
@test collect(trim!([8,7,6,5,4,3,2,1], count=1)) == [7,6,5,4,3,2]
@test collect(trim!([1,2,3,4,5,6,7,8,9], count=3)) == [4,5,6]

@test_throws ArgumentError trim!([])
@test_throws ArgumentError trim!([1,2,3,4,5], prop=0.5)

@test collect(winsor([8,2,3,4,5,6,7,1], prop=0.1)) == [8,2,3,4,5,6,7,1]
@test collect(winsor([8,2,3,4,5,6,7,1], prop=0.2)) == [7,2,3,4,5,6,7,2]
@test collect(winsor([1,2,3,4,5,6,7,8,9], prop=0.4)) == [4,4,4,4,5,6,6,6,6]
@test collect(winsor([1,2,3,4,5,6,7,8], count=1)) == [2,2,3,4,5,6,7,7]
@test collect(winsor([8,7,6,5,4,3,2,1], count=1)) == [7,7,6,5,4,3,2,2]
@test collect(winsor([1,2,3,4,5,6,7,8,9], count=3)) == [4,4,4,4,5,6,6,6,6]

@test_throws ArgumentError winsor([])
@test_throws ArgumentError winsor([1,2,3,4,5], prop=0.5)

@test collect(winsor!([8,2,3,4,5,6,7,1], prop=0.1)) == [8,2,3,4,5,6,7,1]
@test collect(winsor!([8,2,3,4,5,6,7,1], prop=0.2)) == [7,2,3,4,5,6,7,2]
@test collect(winsor!([1,2,3,4,5,6,7,8,9], prop=0.4)) == [4,4,4,4,5,6,6,6,6]
@test collect(winsor!([8,7,6,5,4,3,2,1], count=1)) == [7,7,6,5,4,3,2,2]
@test collect(winsor!([1,2,3,4,5,6,7,8,9], count=3)) == [4,4,4,4,5,6,6,6,6]

@test_throws ArgumentError winsor!([])
@test_throws ArgumentError winsor!([1,2,3,4,5], prop=0.5)

### Variance

@test trimvar([1,1,1,1,1]) ≈ 0.0
@test trimvar([2,3,4,5,6,7,8,9], prop=0.25) ≈ 1.0

@test_throws ArgumentError trimvar([])
@test_throws ArgumentError trimvar([1,2,3,4,5], prop=0.5)

### Other

@test mean(trim([-Inf,1,2,3,4], count=1)) == 2
@test mean(winsor([-Inf,1,2,3,4], count=1)) == 2


#=
This file is part of Pseudospectra.jl.

Julia translation
copyright 2017 Ralph Smith

Portions derived from EigTool
Copyright (c) 2002-2014, The Chancellor, Masters and Scholars
of the University of Oxford, and the EigTool Developers. All rights reserved.

SPDX-License-Identifier: BSD-3-Clause
License-Filename: LICENSES/BSD-3-Clause_Eigtool
=#

"""
    numerical_range(A, nstep=20) -> Vector{Complex}

Compute points along the numerical range of a matrix.

Note: this solves an eigensystem for each point, so may be expensive.
"""
function numerical_range(A::AbstractMatrix, thmax=20)
    rayleighquotient(B,x) = dot(x, B * x) / dot(x,x)
    # m,n = size(A)
    T = eltype(A)
    CT = (T <: Real) ? Complex{T} : T
    z = zeros(CT,2*thmax+2)
    for i = 0:thmax
        # upstream allows for interactive cancellation

        # get Hermitian part of rotated A
        th = (i/thmax)*π
        Ath = exp(th*1im)*A
        H = (1/2)*(Ath + Ath')
        F = eigen(H)
        d,X = F.values,F.vectors

        # RQ's of A correspond to eigenvalues of H w/ extreme real parts
        k = sortperm(real(d))
        z[i+1] = rayleighquotient(A,X[:,k[1]])
        z[1+i+thmax] = rayleighquotient(A,X[:,k[end]])
    end
    z[end] = z[1] # close curve for plotting
    z
end

"""
    numrange!(ps_data,nstep=20)

compute the numerical range (a.k.a. field of values) of a dense matrix
then store it in the `ps_data`.

Note: this solves an eigensystem for each point, so may be expensive.
"""
function numrange!(ps_data::PSAStruct,thmax=20)
    ps_dict = ps_data.ps_dict
    # don't recompute if no change
    if !haskey(ps_dict,:fov) || (length(ps_dict[:fov]) != 2*(thmax+1))
        # Why does upstream enforce this constraint?
        if !haskey(ps_dict,:schur_mtx)
            throw(ArgumentError("only implemented for Schur-factored matrices"))
        end
        z = numerical_range(ps_dict[:schur_mtx],thmax)
        ps_dict[:fov] = z
    else
        z = ps_dict[:fov]
    end

    zoom = ps_data.zoom_list[ps_data.zoom_pos]
    ax = zoom.ax
    if mapreduce(w -> ((real(w) < ax[1]) | (real(w) > ax[2]) |
                       (imag(w) < ax[3]) | (imag(w) > ax[4])),
                 &, z, init=true)
        warn("The boundary of the numerical range is not visible on the "
             * "current axes; expand axis limits to see it. "
             * "A bounding box is $(extrema(real(z))) $(extrema(imag(z))).")
    end
end

"""
    numerical_abscissa(A)

Compute the numerical abscissa of a matrix `A`, `ω(A)`.

Uses `eigvals()`. `ω(A)` provides bounds and limiting behavior for
`norm(expm(t*A))`.
"""
function numerical_abscissa(A::AbstractMatrix)
    (1/2) * maximum(eigvals(A+A'))
end


# p28.jl - eigenmodes of Laplacian on the disk (compare p22.jl)

# r coordinate, ranging from -1 to 1 (N must be odd):
N = 25; N2 = Int((N-1)/2);
(D,r) = cheb(N); D2 = D^2;
D1 = D2[2:N2+1,2:N2+1]; D2 = D2[2:N2+1,N:-1:N2+2];
E1 =  D[2:N2+1,2:N2+1]; E2 =  D[2:N2+1,N:-1:N2+2];

# t = theta coordinate, ranging from 0 to 2*pi (M must be even):
M = 20; dt = 2*pi/M; t = dt*(1:M); M2 = Int(M/2);
D2t = toeplitz([-pi^2/(3*dt^2)-1/6; @. .5*(-1)^(2:M)/sin(dt*(1:M-1)/2)^2]);

# Laplacian in polar coordinates:
R = diagm(1./r[2:N2+1]);
Z = zeros(M2,M2); I = eye(M2);
L = kron(D1+R*E1,eye(M)) + kron(D2+R*E2,[Z I;I Z]) + kron(R^2,D2t);

# Compute four eigenmodes:
index = [1 3 6 10];
(Lam,V) = eig(-L); ii = sortperm(abs.(Lam))[index];
Lam = Lam[ii]; V = V[:,ii];
Lam = sqrt.(real(Lam/Lam[1]));

# Plot eigenmodes with nodal lines underneath:
(rr,tt) = (r[1:N2+1],[0;t]);
(xx,yy) = @. (cos(tt)*rr',sin(tt)*rr');
z = exp.(1im*pi*(-100:100)/100);
for i = 1:4
    figure(i); clf();
    u = reshape(real(V[:,i]),M,N2);
    u = [zeros(M+1) u[[M;1:M],:]];
    u = u/norm(u[:],Inf);
    #plot3D(real(z),imag(z),zeros(size(z)));
    xlim(-1.05,1.05); ylim(-1.05,1.05); zlim(-1.05,1.05); axis("off");
    surf(xx,yy,u)
    gca()[:view_init](20,90); axis("square");
    contour3D(xx,yy,u-1,levels=[-1]);
    plot3D(real(z),imag(z),-abs.(z));
    title("Mode $(index[i]):  λ = $(signif(Lam[i],11))",fontsize=9);
end


#=
A builder is looking to build a row of N houses that can be of K different colors. He has a goal of minimizing cost while ensuring that no two neighboring houses are of the same color.

Given an N by K matrix where the nth row and kth column represents the cost to build the nth house with kth color, return the minimum cost which achieves this goal.
=#

function minimum_building_cost(cost_matrix)
    # Create a matrix with the cumulative minimum cost of building the nth house (row) with the kth color (column)
    cumulative_minimum_cost = zeros(Float64, size(cost_matrix))
    # For the first house, it is the first row of the cost_matrix
    cumulative_minimum_cost[1,:] = cost_matrix[1,:]
    number_of_colors = size(cost_matrix, 2)
    number_of_houses = size(cost_matrix, 1)
    # For the second to last houst, for each color, the minimum cumulative cost is the price of building the house in that color + the minimum cumulative cost of building the last house in a different color
    for house in 2:number_of_houses
        for color in 1:number_of_colors
            allowed_prev_colors = setdiff(1:number_of_colors, color)
            cumulative_minimum_cost[house, color] = cost_matrix[house, color] + minimum(cumulative_minimum_cost[house-1,allowed_prev_colors])
        end
    end
    # We want the minimum cost of building all the house
    minimum_cost = minimum(cumulative_minimum_cost[end,:])
    return minimum_cost
end


module StockCorr

if VERSION >= v"0.7.0-DEV.3449"
    using LinearAlgebra
end

# The original, unoptimised code that simulates two correlated assets
function perf_stockcorr()
    ## Correlated asset information
    CurrentPrice = [78. 102.] # Initial Prices of the two stocks
    Corr = [1. 0.4; 0.4 1.]   # Correlation Matrix
    T = 500                   # Number of days to simulate = 2years = 500days
    n = 10000                 # Number of simulations
    dt = 1/250                # Time step (1year = 250days)
    Div = [0.01 0.01]         # Dividend
    Vol = [0.2 0.3]           # Volatility

    ## Market Information
    r = 0.03 # Risk-free rate

    ## Define storages
    SimulPriceA = zeros(T,n) # Simulated Price of Asset A
    SimulPriceA[1,:] .= CurrentPrice[1]
    SimulPriceB = zeros(T,n) # Simulated Price of Asset B
    SimulPriceB[1,:] .= CurrentPrice[2]

    ## Generating the paths of stock prices by Geometric Brownian Motion
    @static if VERSION <= v"0.7.0-DEV.5211"
        UpperTriangle = chol(Corr) # UpperTriangle Matrix by Cholesky decomposition
    else
        UpperTriangle = cholesky(Corr).U
    end

    for i = 1:n
       Wiener = randn(T-1,2)
       CorrWiener = Wiener*UpperTriangle
       for j = 2:T
          SimulPriceA[j,i] = SimulPriceA[j-1,i]*exp((r-Div[1]-Vol[1]^2/2)*dt+Vol[1]*sqrt(dt)*CorrWiener[j-1,1])
          SimulPriceB[j,i] = SimulPriceB[j-1,i]*exp((r-Div[2]-Vol[2]^2/2)*dt+Vol[2]*sqrt(dt)*CorrWiener[j-1,2])
       end
    end

    return (SimulPriceA, SimulPriceB)
end

end # module


@testset "moments" begin

# Build histogram from an image correctly and compare the threshold to the answer
# from another implementation.
img = testimage("cameraman")
imgg = gray.(img)
imgg_uint8 = reinterpret.(imgg)
edges, counts = build_histogram(imgg_uint8, 0:1:255)
t1 = find_threshold(Moments(),counts[1:end], 0:1:255)
@test t1 == 111

# Build a histogram from an array and compare the threshold to that in another
# implementation.
arr = [10  8 10  9 20 21 32 30 40 41 41 40;
       12 10 11 10 19 20 30 28 38 40 40 39;
       10  9 10  8 20 21 30 29 42 40 40 39;
       11 10  9 11 19 21 31 30 40 42 38 40]
edges, counts = build_histogram(arr, 0:255)
t2 = find_threshold(Moments(), counts[1:end], edges)
@test t2 == 27

end


function factorial(n::Int64)
    if n == 0 || n == 1
        return 1
    else
        return n * factorial(n-1)
    end
end
n = parse(Int64, readline())
println(factorial(n))

Hackerrank/30 Days of Code/Julia/day 09.jl


# using Pkg; Pkg.add("https://github.com/QuantEcon/Games.jl")

#module beautifulmind

using Games

#-----------------------------------------------------------------

# defines the simultaneous game
function init()

if uppercase("n") == uppercase(query("\n*****Simultaneous games ****\n\nDefine own game?(y/n)\n\n\tchoice: "))
    mat = Array{Float64}(undef, 2, 2, 2)
    mat[1, 1, :] = [3, 2]
    mat[1, 2, :] = [2, 2]
    mat[2, 1, :] = [2, 3]
    mat[2, 2, :] = [5, 0]

    sim = NormalFormGame(mat)
    determineStrategy(sim)
    output(sim)
else

println("Defining own game\n")
    if 1 == parse(Int,query("choose input method\n\t1.by matrix\n\t2.by vector\n\n\tchoice: "))

        matrix = Array{Int}(undef, 2, 2, 2)
        for o in 1:2
            for i in 1:2

             str = query("Input values for row $o column $i e.g(2 5): ")
            matrix[o, i, :] =  [parse(Int,split(str," ")[1]), parse(Int,split(str," ")[2])]

            end
        end
        game = NormalFormGame(matrix)
        determineStrategy(game)
        output(game)
    else

         p1 = [1 2 ; 3 4]
          str = replace(query("Input player1 strategies e.g(2 5;3 7): "),";" => "")
          str = replace(str," " => "")

          for (i,x) in enumerate(str)
             p1[i]=parse(Int,x)
          end

        p1 = Player(p1)

          p2 = [1 2 ; 3 4]
          str = replace(query("Input player2 strategies e.g(2 5;3 7): "),";" => "")
          str = replace(str," " => "")

          for (i,x) in enumerate(str)
             p2[i]=parse(Int,x)
         end

        p2 = Player(p2)

        game = NormalFormGame(p1,p2)
        determineStrategy(game)
        output(game)
    end

  end
end

#-----------------------------------------------------------------

function determineStrategy(game)

    a = query("\n\nwhat is player1's first strategy e.g(a): ")
    b = query("\nwhat is player1's second strategy e.g(b): ")

    c = query("\nwhat is player2's first strategy e.g(c): ")
    d = query("\nwhat is player2's second strategy e.g(d): ")

    act11 = game.players[1].payoff_array[1] + game.players[1].payoff_array[3]

    act12 = game.players[1].payoff_array[2] +game.players[1].payoff_array[4]

    act21 = game.players[2].payoff_array[1] + game.players[2].payoff_array[2]
    act22 = game.players[2].payoff_array[3] + game.players[2].payoff_array[4]

println("\n")

    if act11 > act12
        println("player1  dominant strategy: $a\n         dominated stratergy: $b")
    end

    if act12 > act11
        println("player1  dominant strategy: $b\n         dominated stratergy: $a")
    elseif act11 == act12
        println("player1's strategies are equal")
    end

     if(act21>act22)
        println("player2  dominant strategy: $c\n         dominated stratergy: $d")
    elseif(act22>act21)
        println("player2  dominant strategy: $d\n         dominated stratergy: $c")
    else
        println("player2's strategies are equal")
    end

   # best_responses(game.players[1], game.players[1])

    return
end

#-----------------------------------------------------------------

function output(game::NormalFormGame)
    equilibria = pure_nash(game)
    equilibriaNo = length(equilibria)

    if equilibriaNo == 0
        str = "no pure Nash equilibrium found"
    elseif equilibriaNo == 1
        str = "There is 1 equilibrium at:\n$(equilibria[1])"
    else
        str = "There is $equilibriaNo  equilibria:\n"
        for (i, E) in enumerate(equilibria)
            i < equilibriaNo ? str *= "$E, " : str *= "$E"
        end
    end
    println(join(["\n",str]))

end

#-----------------------------------------------------------------

function query(str::String)

    print(str);
   return input = readline();

end

#-----------------------------------------------------------------

init() #driver statement
#end #module


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

# simple 2d convolution with padding
function convolution(input, filter; padding=:same)
    input_r, input_c = size(input)
    filter_r, filter_c = size(filter)

    if padding == :same
        pad_r = (filter_r - 1) ÷ 2
        pad_c = (filter_c - 1) ÷ 2

        input_padded = zeros(input_r+(2*pad_r), input_c+(2*pad_c))
        for i in 1:input_r, j in 1:input_c
            input_padded[i+pad_r, j+pad_c] = input[i, j]
        end
        input = input_padded
        input_r, input_c = size(input)
    end

    result = zeros(input_r-filter_r+1, input_c-filter_c+1)
    result_r, result_c = size(result)

    for i in 1:result_r
        for j in 1:result_c
            for k in 1:filter_r
                for l in 1:filter_c
                    result[i,j] += input[i+k-1,j+l-1]*filter[k,l]
                end
            end
        end
    end

    return result
end

# gaussian kernel with same σ in both dimensions
function gaussian_kernel(σ::Real; l::Int = 4*ceil(Int,σ)+1)
    isodd(l) || throw(ArgumentError("length must be odd"))
    w = l>>1
    g = σ == 0 ? [exp(0/(2*oftype(σ, 1)^2))] : [exp(-x^2/(2*σ^2)) for x=-w:w]
    k = g/sum(g)
    return (k * k')
end

# gaussian kernel with different σs in both dimensions
function gaussian_kernel(
    σs::Tuple{Real, Real};
    l::Tuple{Int, Int} = (4*ceil(Int,σs[1])+1, 4*ceil(Int,σs[2])+1)
)
    all(isodd.(l)) || throw(ArgumentError("length must be odd"))
    w1 = l[1]>>1
    g1 = σs[1] == 0 ? [exp(0/(2*oftype(σs[1], 1)^2))] : [exp(-x^2/(2*σs[1]^2)) for x=-w1:w1]
    k1 = g1/sum(g1)

    w2 = l[2]>>1
    g2 = σs[2] == 0 ? [exp(0/(2*oftype(σs[2], 1)^2))] : [exp(-x^2/(2*σs[2]^2)) for x=-w2:w2]
    k2 = g2/sum(g2)

    return (k1 * k2')
end