export edge_connectivity, min_edge_cut
export connectivity, min_cut

# min_cut(G,s,t) -- min cut separating s and t
# min_cut(G) -- min cut separating the graph
# flag == true means we want s/t cut

"""
`min_cut(G)` returns a minimum size set of vertices that disconnects `G`.

`min_cut(G,s,t)` returns a minimum size cut that separates `s` and `t`.
"""
function min_cut(G::SimpleGraph{T},s::T, t::T,flag::Bool=true)::Set{T} where T
    n = NV(G)
    m = NE(G)
    n*(n-1)!=2m || error("Graph must not be complete")

    if flag
        has(G,s)    || error("$s is not a vertex of this graph")
        has(G,t)    || error("$t is not a vertex of this graph")
        s!=t        || error("two vertices must be different")
        !has(G,s,t) || error("vertices $s and $t cannot be adjacent")
    else
        if cache_check(G,:min_cut)
            return cache_recall(G,:min_cut)
        end

        if !is_connected(G)
            X = Set{T}()
            cache_save(G,:min_cut,X)
            return X
        end
    end

    VV = vlist(G)
    EE = elist(G)

    MOD = Model(get_solver())

    @variable(MOD,a[VV],Bin)  # in part 1
    @variable(MOD,b[VV],Bin)  # in part 2
    @variable(MOD,c[VV],Bin)  # in cut set

    for v in VV
        @constraint(MOD, a[v]+b[v]+c[v]==1)
    end

    if flag
        @constraint(MOD,a[s]==1)
        @constraint(MOD,b[t]==1)
    end

    @constraint(MOD, sum(a[v] for v in VV)>=1)
    @constraint(MOD, sum(b[v] for v in VV)>=1)

    for e in EE
        v,w = e
        @constraint(MOD,a[v]+b[w]<=1)
        @constraint(MOD,a[w]+b[v]<=1)
    end

    @objective(MOD, Min, sum(c[v] for v in VV))

    optimize!(MOD)
    status = Int(termination_status(MOD))

    C =  value.(c)
    X = Set(v for v in VV if C[v] > 0.5)

    if !flag
        cache_save(G,:min_cut,X)
    end
    return X
end

function min_cut(G::SimpleGraph)::Set
    n = NV(G)
    n>1 || error("Graph must have at least two vertices")
    s = first(G.V)
    return min_cut(G,s,s,false)
end

"""
`connectivity(G)` returns the (vertex) connectivity of `G`, i.e.,
the minimum size of a vertex cut set. If `G` is a complete graph with
`n` vertices, the connectivity is `n-1` (or `0` for the empty graph).
"""
function connectivity(G::SimpleGraph)::Int
    n = NV(G)
    m = NE(G)
    if n*(n-1) == 2m   # graph is complete
        return max(n-1,0)  # what is the connectivity of K_0?
    end

    return length(min_cut(G))
end

# min_edge_cut(G,s,t) -- min edge cut separating s and t
# min_edge_cut(G) -- min edge cut separating the graph
# flag == true means we want s/t cut

"""
`min_edge_cut(G)` returns a minimum size set of edges whose removal
disconnects `G`. The graph must have at least two vertices.

`min_edge_cut(G,s,t)` is a minimum size set of edges whose removal
separates vertices `s` and `t`.
"""
function min_edge_cut(G::SimpleGraph{T}, s::T, t::T, flag::Bool=true)::Set{Tuple{T,T}} where T
    n = NV(G)
    n > 1 || error("Graph must have at least two vertices")

    if flag
        has(G,s) || error("$s is not a vertex of this graph")
        has(G,t) || error("$t is not a vertex of this graph")
        s!=t     || error("vertices must not be the same")
    else
        if cache_check(G,:min_edge_cut)
            return cache_recall(G,:min_edge_cut)
        end

        if !is_connected(G)
            X = Set{Tuple{T,T}}()
            cache_save(G,:min_edge_cut,X)
            return X
        end
    end

    VV = vlist(G)
    EE = elist(G)

    MOD = Model(get_solver())

    @variable(MOD,a[VV],Bin)  # in part 1
    @variable(MOD,b[VV],Bin)  # in part 2
    @variable(MOD,c[EE],Bin)  # span between the parts

    for v in VV
        @constraint(MOD,a[v]+b[v]==1)
    end

    @constraint(MOD,sum(a[v] for v in VV) >= 1)
    @constraint(MOD,sum(b[v] for v in VV) >= 1)

    if flag
        @constraint(MOD,a[s]==1)
        @constraint(MOD,b[t]==1)
    end

    for e in EE
        u,v = e
        @constraint(MOD,a[u]+b[v]-1 <= c[e])
        @constraint(MOD,a[v]+b[u]-1 <= c[e])
    end

    @objective(MOD, Min, sum(c[e] for e in EE))

    optimize!(MOD)
    status = Int(termination_status(MOD))

    C =  value.(c)

    X = Set(e for e in EE if C[e] > 0.5)

    if !flag
        cache_save(G,:min_edge_cut,X)
    end
    return X
end

function min_edge_cut(G::SimpleGraph)::Set
    n = NV(G)
    n>1 || error("Graph must have at least two vertices")
    s = first(G.V)
    return min_edge_cut(G,s,s,false)
end

"""
`edge_connectivity(G)` returns the size of a minimum edge cut of `G`.

`edge_connectivity(G,s,t)` determines the minimum size of an edge cut
separating `s` and `t`.
"""
edge_connectivity(G::SimpleGraph)::Int = length(min_edge_cut(G))
edge_connectivity(G::SimpleGraph,s,v) = length(min_edge_cut(G,s,t))


export findM
"""
function findM(param,w;nsol,pwr)

Find a binary matrix M in Omega_M that solves

min_M  \| M*w - d \|\_{Gamma,pwr} + lambda \| M \|\_0,

Input:

	1. param - StrainReconParam
	2. w     - vector of weights

Keyword arguments:

	3. nsol  - returns nsol matrices (default = 1); if nsol = 2, first matrix has rows with lowest difference |M(i)*w-d(i)| by each row i and is the MAP estimator, second matrix has rows with second lowest difference |M(i)*w-d(i)| by each row i but is NOT the second "best" or most likely matrix
	4. pwr   - exponent in the likelihood function (default = param.pwr)
    5. probM - returns p(M) for nsol matrices corresponding to "confidence"/probability for each row of M, size(m,nsol), (default = false); p(M(i)) = C* exp(-(1/pwr) * (|M(i)*w - d(i)|_R)^pwr) where R is the diagonal of the inverse square of the noise covariance matrix and C is the normalization constant 1/(sum_{m}{j}(sum_{2^n}{i} exp(-(1/pwr) * (|B(i)*w - d(j)|_R)^pwr))), where B is a binary matrix with all possible permutation of binary entries and has size(2^n,n)

Output:

	1. M  - binary matrix of size (q,n) or an array of size (q,n,nsol)
    2. pM - matrix of p(M) of size (m,nsol), entries in each column \in [0,1] represent "confidence" or probability for the rows of M
"""
function findM(param::StrainReconParam,w::Vector;nsol::Int64=1,pwr::Float64=param.pwr,probM=false)
    Rp = diag(param.R.^pwr)
    d  = param.d
    n  = param.n
    p  = param.p
    m  = param.m

    B  = getB(param)
    Ba = reshape(B*w,p-1,:)'
    Bs = reshape(param.lambda * vec(sum(B,2)), p-1,:)'

    if nsol > 2^n
        println("nsol can only be <= 2^n = $(2^n), only the MAP estimator will be returned")
        nsol = 1
    end

    pM = zeros(m,nsol) # array to store p(M) for each M

    if nsol == 1
        M  = falses(length(d),n)
        for k=1:m
            blockidx = (k-1)*(p-1)+1 : k*(p-1)
            Bdiff = sum(Rp[blockidx]'.*abs.(Ba.-d[blockidx]').^pwr,2) + sum(Bs,2) # |B(ik)*w - d(k)|_R^pwr, size(2^n,1)
            ik = indmin(Bdiff) # index ik in 1:2^n
            mi = B[(ik-1)*(p-1)+1 : ik*(p-1),:] # row(s) of B with lowest difference B[ik]*w - d[k]
            M[blockidx,:] = mi

            # compute p(M_i)

            # println("ik $ik mi $mi Bdiff $Bdiff)")
            # avoid underflow by finding min(Bdiff) and subtracting that from all numbers before taking exp
            minbdiff = minimum(Bdiff)
            pmi = exp.(-(1/pwr)*((Rp[blockidx]'.*abs.(mi*w.-d[blockidx]').^pwr + Bs[ik]) .- minbdiff)) # pmi should always = 1
            #opmi = (Rp[blockidx]'.*abs.(mi*w.-d[blockidx]').^pwr + Bs[ik])
            #println("pmi $pmi \n opmi $opmi minbdiff $minbdiff")
            normconst = sum(exp.(-(1/pwr)*(Bdiff .- minbdiff))) # normalization constant
            #println("normconst $normconst")
            pM[k,nsol] = pmi[1]/normconst
        end
    else
        M  = falses(length(d),n,nsol)
        for k=1:m
            blockidx = (k-1)*(p-1)+1 : k*(p-1)
            Bdiff = vec(sum(Rp[blockidx]'.*abs.(Ba.-d[blockidx]').^pwr,2) + sum(Bs,2))
            ik = sortperm(Bdiff)
            minbdiff = minimum(Bdiff)
            normconst = sum(exp.(-(1/pwr)*(Bdiff .- minbdiff))) # normalization constant for each row
            for j=1:nsol
                # nth best row
                mj = B[(ik[j]-1)*(p-1)+1 : ik[j]*(p-1),:]
                M[blockidx,:,j] = mj

                # compute p(M_i) for each matrix
                ij = ik[j]
                # println("ij $ij mj $mj Bdiff $Bdiff)")
                pmj = exp.(-(1/pwr)*((Rp[blockidx]'.*abs.(mj*w.-d[blockidx]').^pwr + Bs[ij]) .- minbdiff))
                # println("j $j pmj $pmj")
                # println("normconst $normconst")
                pM[k,j] = pmj[1]/normconst
            end
        end
    end

    if probM
        return M, pM
    else
        return M
    end
end


function adaptivesimpsons_inner(
    f::Function,
    a::Real,
    b::Real,
    ε::Real,
    S::Real,
    fa::Real,
    fb::Real,
    fc::Real,
    bottom::Integer,
)
    c = (a + b) / 2
    h = b - a
    d = (a + c) / 2
    g = (c + b) / 2
    fd = f(d)
    fe = f(g)
    Sleft = (h / 12) * (fa + 4 * fd + fc)
    Sright = (h / 12) * (fc + 4 * fe + fb)
    S2 = Sleft + Sright
    if bottom <= 0 || abs(S2 - S) <= 15 * ε
        return S2 + (S2 - S) / 15
    end
    res1 = adaptivesimpsons_inner(
        f,
        a,
        c,
        ε / 2,
        Sleft,
        fa,
        fc,
        fd,
        bottom - 1
    )
    res2 = adaptivesimpsons_inner(
        f,
        c,
        b,
        ε / 2,
        Sright,
        fc,
        fb,
        fe,
        bottom - 1
    )
    return res1 + res2
end

function adaptivesimpsons(
    f::Function,
    a::Real,
    b::Real,
    accuracy::Real = 10e-10,
    max_iterations::Integer = 50,
)
    c = (a + b) / 2
    h = b - a
    fa = f(a)
    fb = f(b)
    fc = f(c)
    S = (h / 6) * (fa + 4 * fc + fb)
    return adaptivesimpsons_inner(
        f,
        a,
        b,
        accuracy,
        S,
        fa,
        fb,
        fc,
        max_iterations
    )
end


geometric_mean(a::Number, b::Number) = √(a*b)

function find_under_and_over(a::Number, possible_values::Array)
    possible_series_sorted = sort(possible_values)
    i, j = 1, 2
        while !(possible_series_sorted[i] ≤ a ≤ possible_series_sorted[j])
            i+=1; j+=1
        end
    return possible_series_sorted[i], possible_series_sorted[j]
end

function norm_to_between_100_and_1000(val::Number; return_OOM=true)
    power_of_10 = 0.0
    while val > 1000
        val/=10; power_of_10 += 1
    end
    while val < 100
        val*=10; power_of_10 += -1
    end
    if return_OOM
        return val, power_of_10
    else
        return val
    end
end


"""
    StuTLik <: Likelihood

Student-t likelihood (a.k.a. non-standardized [Student's
t-distribution](https://en.wikipedia.org/wiki/Student's_t-distribution))
```math
p(y | f, σ) = Γ((ν + 1)/2)/[Γ(ν/2)√(πν)σ](1 + 1/ν((y - f)/σ)²)^{-(ν + 1)/2}
```
with degrees of freedom ``ν ∈ N₀``, where scale ``σ`` is a non-fixed hyperparameter and
``f`` is the latent Gaussian process.
"""
mutable struct StuTLik <: Likelihood
    "Degrees of freedom"
    ν::Int
    "Scale"
    σ::Float64
    "Priors for likelihood parameters"
    priors::Array

    """
        StuTLik(ν::Int, lσ::Float64)

    Create a `StuTLik` with degrees of freedom `ν` and scale `exp(lσ)`.
    """
    StuTLik(ν::Int, lσ::Float64) = new(ν, exp(lσ), [])
end

#log of probability density
function log_dens(studentT::StuTLik, f::VecF64, y::VecF64)
    ν = studentT.ν
    σ = studentT.σ
    c = lgamma(0.5*(ν+1)) - lgamma(0.5*ν) - 0.5*log(pi*ν) - log(σ)
    return [c - (0.5*(ν+1))*log(1+(1/ν)*((yi-fi)/σ)^2) for (fi,yi) in zip(f,y)]
end

#derivative of log pdf wrt latent function
function dlog_dens_df(studentT::StuTLik, f::VecF64, y::VecF64)
    ν = studentT.ν
    σ = studentT.σ
    return [(ν+1)*(yi-fi)/(ν*σ^2 + (yi-fi)^2) for (fi,yi) in zip(f,y)]
end

#derivative of log pdf wrt to parameters
function dlog_dens_dθ(studentT::StuTLik, f::VecF64, y::VecF64)
    ν = studentT.ν
    σ = studentT.σ
    return σ*[-1/σ+(ν+1)*(yi-fi)^2/(ν*σ^3 + σ*(yi-fi)^2) for (fi,yi) in zip(f,y)]
end

#mean and variance under likelihood
mean_lik(studentT::StuTLik, f::VecF64) = f
var_lik(studentT::StuTLik, f::VecF64) = studentT.σ^2*(studentT.ν/(studentT.ν-2.0))*ones(length(f))

function set_params!(studentT::StuTLik, hyp::VecF64)
    length(hyp) == 1 || throw(ArgumentError("Student-t likelihood has only one free parameter"))
    studentT.σ = exp(hyp[])
end

get_params(studentT::StuTLik) = Float64[log(studentT.σ)]
num_params(studentT::StuTLik) = 1


immutable TDist <: ContinuousUnivariateDistribution
    df::Float64 # non-integer degrees of freedom allowed
    function TDist(d::Real)
    	d > zero(d) || error("df must be positive")
        new(float64(d))
    end
end

@_jl_dist_1p TDist t

function entropy(d::TDist)
    ((d.df + 1.0) / 2.0) *
        (digamma((d.df + 1.0) / 2.0) - digamma((d.df) / 2.0)) +
        (1.0 / 2.0) * log(d.df) + lbeta(d.df + 1.0, 1.0 / 2.0)
end

insupport(::TDist, x::Real) = isfinite(x)
insupport(::Type{TDist}, x::Real) = isfinite(x)

mean(d::TDist) = d.df > 1.0 ? 0.0 : NaN

median(d::TDist) = 0.0

mode(d::TDist) = 0.0
modes(d::TDist) = [0.0]

function pdf(d::TDist, x::Real)
    1.0 / (sqrt(d.df) * beta(0.5, 0.5 * d.df)) *
        (1.0 + x^2 / d.df)^(-0.5 * (d.df + 1.0))
end

function var(d::TDist)
    d.df > 2.0 && return d.df / (d.df - 2.0)
    d.df > 1.0 && return Inf
    NaN
end


### A Pluto.jl notebook ###
# v0.12.16

using Markdown
using InteractiveUtils

# ╔═╡ e077ab16-22e5-11eb-15ed-5f0654bf7328
using Pkg, DrWatson

# ╔═╡ e75a9a06-22e5-11eb-2e36-8d4f62b830ed
begin
	@quickactivate "StatisticsWithJuliaPlutoNotebooks"
	using Random, Distributions, Plots
	using StatsBase, LaTeXStrings
	Random.seed!(0)
end

# ╔═╡ ca5fbdc8-22e5-11eb-0a60-cb0a127a3ca5
md"## Listing5.15"

# ╔═╡ fa68607e-22e5-11eb-0558-c9a4d9f77426
begin
	mu0, mu1, sd, tau  = 15, 18, 2, 17.5
	dist0, dist1 = Normal(mu0,sd), Normal(mu1,sd)
	grid = 5:0.1:25
	h0grid, h1grid = tau:0.1:25, 5:0.1:tau
end

# ╔═╡ 57e2a694-3af9-11eb-0f9e-7d8df75869e6
Text("Probability of Type I error: $(ccdf(dist0,tau))")

# ╔═╡ 57e2e668-3af9-11eb-0e7c-033e27bf1e92
Text("Probability of Type II error: $(cdf(dist1,tau))")

# ╔═╡ 57e37538-3af9-11eb-1477-b9387969d828
begin
	plot(grid, pdf.(dist0,grid),
		c=:blue, label="Bolt type 15g")
	plot!(h0grid, pdf.(dist0, h0grid),
		c=:blue, fa=0.2, fillrange=[0 1], label="")
	plot!(grid, pdf.(dist1,grid),
		c=:green, label="Bolt type 18g")
	plot!(h1grid, pdf.(dist1, h1grid),
		c=:green, fa=0.2, fillrange=[0 1], label="")
	plot!([tau, 25],[0,0],
		c=:red, lw=3, label="Rejection region",
		xlims=(5, 25), ylims=(0,0.25) , legend=:topleft,
		xlabel="x", ylabel="Density")
	annotate!([(16, 0.02, text(L"\beta")),(18.5, 0.02, text(L"\alpha")),
				(15, 0.21, text(L"H_0")),(18, 0.21, text(L"H_1"))])
end

# ╔═╡ 475ff888-22e6-11eb-2354-09f8f40a8e12
md"## End of listing5.15"

# ╔═╡ Cell order:
# ╟─ca5fbdc8-22e5-11eb-0a60-cb0a127a3ca5
# ╠═e077ab16-22e5-11eb-15ed-5f0654bf7328
# ╠═e75a9a06-22e5-11eb-2e36-8d4f62b830ed
# ╠═fa68607e-22e5-11eb-0558-c9a4d9f77426
# ╠═57e2a694-3af9-11eb-0f9e-7d8df75869e6
# ╠═57e2e668-3af9-11eb-0e7c-033e27bf1e92
# ╠═57e37538-3af9-11eb-1477-b9387969d828
# ╟─475ff888-22e6-11eb-2354-09f8f40a8e12


# Julia translation of http://nbviewer.jupyter.org/github/barbagroup/AeroPython/blob/master/lessons/03_Lesson03_doublet.ipynb
# Lesson 3 doublet

using PyPlot
using Distributions

close("all")
meshgrid(x,y) = (repmat(x',length(y),1),repmat(y,1,length(x)))

N = 50                                # number of points in each direction
x_start, x_end = -2.0, 2.0            # boundaries in the x-direction
y_start, y_end = -1.0, 1.0            # boundaries in the y-direction
x = linspace(x_start, x_end, N)    # creates a 1D-array with the x-coordinates
y = linspace(y_start, y_end, N)    # creates a 1D-array with the y-coordinates

X,Y=meshgrid(x,y)

kappa = 1.0

x_doublet, y_doublet = 0.0, 0.0   # location of the source

function get_velocity_doublet(strength, xd, yd, X, Y):
    """Returns the velocity field generated by a doublet.

    Arguments
    ---------
    strength -- strength of the doublet.
    xd, yd -- coordinates of the doublet.
    X, Y -- mesh grid.
    """
    u = - strength/(2*pi).*((X-xd).^2-(Y-yd).^2)./((X-xd).^2+(Y-yd).^2).^2
    v = - strength/(2*pi)*2.0.*(X-xd).*(Y-yd)./((X-xd).^2+(Y-yd).^2).^2

    return u, v
  end

function get_stream_function_doublet(strength, xd, yd, X, Y):
    """Returns the stream-function generated by a doublet.

    Arguments
    ---------
    strength -- strength of the doublet.
    xd, yd -- coordinates of the doublet.
    X, Y -- mesh grid.
    """
    psi = - strength/(2*pi).*(Y-yd)./((X-xd).^2+(Y-yd).^2)

    return psi
  end

  # computes the velocity field on the mesh grid
u_doublet, v_doublet = get_velocity_doublet(kappa, x_doublet, y_doublet, X, Y)

# computes the stream-function on the mesh grid
psi_doublet = get_stream_function_doublet(kappa, x_doublet, y_doublet, X, Y)

  # plots the streamlines
  size = 10
  PyPlot.figure(figsize=(size, (y_end-y_start)/(x_end-x_start)*size))
  PyPlot.grid(true)
  PyPlot.xlabel("x", fontsize=16)
  PyPlot.ylabel("y", fontsize=16)
  PyPlot.xlim(x_start, x_end)
  PyPlot.ylim(y_start, y_end)
  PyPlot.streamplot(X, Y, u_doublet, v_doublet, density=2, linewidth=1, arrowsize=1, arrowstyle="->")
  PyPlot.scatter(x_doublet, y_doublet, color="#CD2305", s=80, marker="o")

# Freestream and Doublet

  u_inf = 1.0

  # computes the freestream velocity field
  u_freestream = u_inf * ones((N, N))
  v_freestream = zeros((N, N))

  # computes the stream-function
  psi_freestream = u_inf * Y

  u = u_freestream + u_doublet
  v = v_freestream + v_doublet
  psi = psi_freestream + psi_doublet

  # plots the streamlines
  size = 10
  PyPlot.figure(figsize=(size, (y_end-y_start)/(x_end-x_start)*size))
  PyPlot.grid(true)
  PyPlot.xlabel("x", fontsize=16)
  PyPlot.ylabel("y", fontsize=16)
  PyPlot.xlim(x_start, x_end)
  PyPlot.ylim(y_start, y_end)
  PyPlot.streamplot(X, Y, u, v, density=2, linewidth=1, arrowsize=1, arrowstyle="->")
  PyPlot.contour(X, Y, psi, levels=[0.], colors="#CD2305", linewidths=2, linestyles="solid");
  PyPlot.scatter(x_doublet, y_doublet, color="#CD2305", s=80, marker="o")

  # calculates the stagnation points
x_stagn1, y_stagn1 = +sqrt(kappa/(2*pi*u_inf)), 0
x_stagn2, y_stagn2 = -sqrt(kappa/(2*pi*u_inf)), 0

# adds the stagnation points to the figure
PyPlot.scatter([x_stagn1, x_stagn2], [y_stagn1, y_stagn2], color="g", s=80, marker="o");

# computes the pressure coefficient field
cp = 1.0 - (u.^2+v.^2)./u_inf^2

# plots the pressure coefficient field
size = 10
PyPlot.figure(figsize=(1.1*size, (y_end-y_start)/(x_end-x_start)*size))
PyPlot.xlabel("x", fontsize=16)
PyPlot.ylabel("y", fontsize=16)
PyPlot.xlim(x_start, x_end)
PyPlot.ylim(y_start, y_end)
contf = PyPlot.contourf(X, Y, cp, levels=linspace(-2.0, 1.0, 100), extend="both")
cbar = PyPlot.colorbar(contf)
cbar[:set_label](L"$C_p$", fontsize=16)
cbar[:set_ticks]([-2.0, -1.0, 0.0, 1.0])
PyPlot.scatter(x_doublet, y_doublet, color="#CD2305", s=80, marker="o")
PyPlot.contour(X, Y, psi, levels=[0.], colors="#CD2305", linewidths=2, linestyles="solid");
PyPlot.scatter([x_stagn1, x_stagn2], [y_stagn1, y_stagn2], color="g", s=80, marker="o");


using Revise
using AdFem
using PyPlot

m = 30
n = 30
h = 1/n

xy = fem_nodes(m, n, h)
x, y = xy[:,1], xy[:,2]
u0 = [@. (2(x^3/3-x^2/2)); @. (2(y^3/3 - y^2/2))]

xy = fvm_nodes(m, n, h)
x, y = xy[:,1], xy[:,2]
p0 = @. x*(1-x)*y*(1-y)

ρ = dt = 1
function step2(u0)
    B = compute_interaction_matrix(m, n, h)
    bc = bcedge("all", m, n, h)
    A, _ = compute_fvm_tpfa_matrix(ones(m*n), bc, zeros(size(bc,1)), m, n, h)
    rhs = ρ / dt * B * u0
    sol = A\rhs
    return sol
end
bc = bcedge("all", m, n, h)

# There are three ways to compute
# int div u dx
# on each element

# rhs = compute_fvm_mechanics_term(u0, m, n, h)
B = compute_interaction_matrix(m, n, h)
rhs = B * u0
# rhs = h^2 * @. -2x*(1-x)-2y*(1-y)

A, _ = compute_fvm_tpfa_matrix(ones(m*n), bc, zeros(size(bc,1)), m, n, h)
sol = A\rhs

sol = step2(u0)

figure(figsize=(10,4))
subplot(121)
visualize_scalar_on_fvm_points(sol, m, n, h)
subplot(122)
visualize_scalar_on_fvm_points(p0, m, n, h)


using JuMP
using Clp

LARGE_VALUE = 100000
DEMANDS = [400 300 420 380]
SUPPLY = [500, 600, 200, 300]
COSTS = [100 103 106 109;
        LARGE_VALUE 140 143 146;
        LARGE_VALUE LARGE_VALUE 120 123;
        LARGE_VALUE LARGE_VALUE LARGE_VALUE 150]

m = Model(Clp.Optimizer)
@variable(m, x[1:4, 1:4] ≥ 0)
@objective(m, Min, sum(COSTS .* x))
@constraint(m, sum(x, dims=2) .≤ SUPPLY)
@constraint(m, sum(x, dims=1) .== DEMANDS)
print(m)
optimize!(m)
status = termination_status(m)
println("Solution status: ", status)
println("Objective value: ", objective_value(m))
println("Values: ", value.(x))


# This file contains essentials functions to define
# tables to geometric, inner and outer products.

"""
```
bbgeometric(a::BasisBlade, b::BasisBlade) (Liga function)
```
This function is used to determine the geometric product
of basis blade. Essentially, this function is used when layout
function is trigged, but it can be used separated too.
## Example
```julia-repl
julia> bbgeometric(e1,e12)
```
returns e2.
"""
function bbgeometric(a::BasisBlade, b::BasisBlade)
		len = GIdLayout[1]+GIdLayout[2] #p+q
		Ea = copy(LogicalBasisBlade[a.index])
		Eb = copy(LogicalBasisBlade[b.index])
		abscalar = 1
		abindex = 1
		ablogical = Vector{Bool}(undef,len)
		cont = 0
		for i=1:len-1
			if Eb[i] == true
				for j=i+1:len
					if Ea[j] == true
						cont = cont + 1
					end
				end
			end
		end
		for i=1:len
			if Ea[i] != Eb[i]
				ablogical[i] = true
			else
					ablogical[i] = false
					if Ea[i] == true && i>GIdLayout[1]
						cont =cont +1
					end
			end
		end
		abscalar = ((-1)^cont)
		ind=1
		while ablogical != LogicalBasisBlade[ind]
			ind = ind+1
		end
		abindex = ind
		ab=BasisBlade(abscalar*abindex)
		return ab
end

"""
```
bbinner(a::BasisBlade,b::BasisBlade) (Liga function)
```
This function is used to determine the inner product
of basis blades. Essentially, this function is used when layout
function is trigged, but it can be used separated too.
## Example
```julia-repl
julia> bbinner(e12,e2)
```
returns the Basis Blade e1.
"""
function bbinner(a::BasisBlade,b::BasisBlade)
		k=grade(a)
		l=grade(b)
		if k != 0 && l !=0
			return gradeprojection(bbgeometric(a,b),abs(k-l))
		else
			return BasisBlade(0)
		end
end

"""
```
bbouter(a::BasisBlade,b::BasisBlade) (Liga function)
```
This function is used to determine the outer product
of basis blades. Essentially, this function is used when layout
function is trigged, but it can be used separated too.
## Example
```julia-repl
julia> bbouter(e12,e3)
```
returns the Basis Blade e123.
"""
function bbouter(a::BasisBlade,b::BasisBlade)
	k=grade(a)
	l=grade(b)
	return gradeprojection(bbgeometric(a,b),k+l)
end

"""
```
OperationTable() (Liga function)
```
OperationTable is function trigged by layout function and
it is used to define the operational table related to geometric,
inner and outer product. As result, constants array are created
to optimize these products to multivectors.
## Example
```julia-repl
julia> A,B,C,D=OperationTable()
```
returns A a table for geometric product, B a table for inner product,
C a table for outer product and D a tensor.
"""
function OperationTable()
	len=2^(GIdLayout[1]+GIdLayout[2])
	OT=Array{Any,2}(undef,len,len)
	IOT=Array{Any,2}(undef,len,len)
	OOT=Array{Any,2}(undef,len,len)
	Tensor=Vector{SparseMatrixCSC}(undef,len)
	for i=1:len
        for j=1:len
			OT[i,j]=bbgeometric(BasisBlade(i),BasisBlade(j))
			IOT[i,j]=bbinner(BasisBlade(i),BasisBlade(j))
			OOT[i,j]=bbouter(BasisBlade(i),BasisBlade(j))
		end
	end
	for k=1:len
		Tensor[k]=spzeros(len,len)
		for i=1:len
			for j=1:len
				if OT[i,j]==BasisBlade(k)
					Tensor[k][i,j]=1.0
				end
				if OT[i,j]==-BasisBlade(k)
				   Tensor[k][i,j]=-1.0
			    end
			end
		end
	end
	#display(Tensor)
	return OT,IOT,OOT,Tensor
end


# p37.jl - 2D "wave tank" with Neumann BCs for |y|=1

# x variable in [-A,A], Fourier:
A = 3; Nx = 50; dx = 2*A/Nx; x = -A+dx*(1:Nx);
D2x = (pi/A)^2*toeplitz([-1/(3*(dx/A)^2)-1/6;
        @. .5*(-1).^(2:Nx)/sin((pi*dx/A)*(1:Nx-1)/2)^2]);

# y variable in [-1,1], Chebyshev:
Ny = 15; (Dy,y) = cheb(Ny); D2y = Dy^2;
BC = -Dy[[1,Ny+1],[1,Ny+1]]\Dy[[1,Ny+1],2:Ny];

# Grid and initial data:
vv = @. exp(-8*((x'+1.5)^2+y^2));
dt = 5/(Nx+Ny^2);
vvold = @. exp(-8*((x'+dt+1.5).^2+y.^2));

# Time-stepping by leap frog formula:
plotgap = round(Int,2/dt); dt = 2/plotgap;
for n = 0:2*plotgap
    t = n*dt;
    if rem(n+.5,plotgap)<1
        figure(n/plotgap+1); clf(); surf(x,y,vv);
        gca()[:view_init](60,-100);
        xlim(-A,A); ylim(-1,1); zlim(-0.15,1);
        text3D(-2.5,1,.5,"t = $(round(t))",fontsize=18);
        zticks([]);
    end
    vvnew = 2*vv - vvold + dt^2*(vv*D2x +D2y*vv);
    vvold = vv; vv = vvnew;
    vv[[1,Ny+1],:] = BC*vv[2:Ny,:];       # Neumann BCs for |y|=1
end


module GreekAnalytical

__precompile__(true)

using Distributions

export blackScholesValue, blackScholesDelta, blackScholesGamma, blackScholesVega, blackScholesTheta
export blackScholesVanna, blackScholesVolga, blackScholesSpeed, blackScholesCharm, blackScholesColor, blackScholesZomma
export blackScholesRho, blackScholesRhoFutures, blackScholesTouchProb, blackScholesDriftlessTheta

std_normal = Normal(0, 1)

function blackScholesValue(isCall::Bool, spot::T, strike::T, τ::T, r::T, b::T, σ::T) where {T}
    sign = isCall ? 1 : -1

    d1 = (log(spot / strike) + (b + 0.5 * σ^2) * τ) / (σ * sqrt(τ))
    d2 = d1 - σ * sqrt(τ)

    sign * (spot * exp((b-r) * τ) * cdf(std_normal, sign * d1) - strike * exp(-r * τ) * cdf(std_normal, sign * d2))
end

function blackScholesDelta(isCall::Bool, spot::T, strike::T, τ::T, r::T, b::T, σ::T) where {T}
    d1 = (log(spot / strike) + (b + 0.5 * σ^2) * τ) / (σ * sqrt(τ))
    if isCall
        exp((b-r) * τ) * cdf(std_normal, d1)
    else
        exp((b-r) * τ) * (cdf(std_normal, d1) - 1.0)
    end
end

function blackScholesGamma(isCall::Bool, spot::T, strike::T, τ::T, r::T, b::T, σ::T) where {T}
    d1 = (log(spot / strike) + (b + 0.5 * σ^2) * τ) / (σ * sqrt(τ))
    pdf(std_normal, d1) * exp((b-r) * τ) / spot / σ / sqrt(τ)
end

function blackScholesVega(isCall::Bool, spot::T, strike::T, τ::T, r::T, b::T, σ::T) where {T}
    d1 = (log(spot / strike) + (b + 0.5 * σ^2) * τ) / (σ * sqrt(τ))
    spot * exp((b-r) * τ) * pdf(std_normal, d1) * sqrt(τ)
end

function blackScholesTheta(isCall::Bool, spot::T, strike::T, τ::T, r::T, b::T, σ::T) where {T}
    sign = isCall ? 1 : -1

    d1 = (log(spot / strike) + (b + 0.5 * σ^2) * τ) / (σ * sqrt(τ))
    d2 = d1 - σ * sqrt(τ)

    term1 = -spot * exp((b-r) * τ) * pdf(std_normal, d1) * σ / 2 / sqrt(τ)
    term2 = -sign * (b-r) * spot * exp((b-r) * τ) * cdf(std_normal, sign * d1)
    term3 = -sign * r * strike * exp(-r * τ) * cdf(std_normal, sign * d2)

    term1 + term2 + term3
end

function blackScholesVanna(isCall::Bool, spot::T, strike::T, τ::T, r::T, b::T, σ::T) where {T}
    d1 = (log(spot / strike) + (b + 0.5 * σ^2) * τ) / (σ * sqrt(τ))
    d2 = d1 - σ * sqrt(τ)

    -d2 * pdf(std_normal, d1) / σ * exp((b-r) * τ)
end

function blackScholesCharm(isCall::Bool, spot::T, strike::T, τ::T, r::T, b::T, σ::T) where {T}
    sign = isCall ? 1 : -1

    d1 = (log(spot / strike) + (b + 0.5 * σ^2) * τ) / (σ * sqrt(τ))
    d2 = d1 - σ * sqrt(τ)

    term1 = pdf(std_normal, d1) * (b / σ / sqrt(τ) - d2 / 2 / τ)
    term2 = sign * (b - r) * cdf(std_normal, sign * d1)

    -exp((b-r) * τ) * (term1 + term2)
end

function blackScholesVolga(isCall::Bool, spot::T, strike::T, τ::T, r::T, b::T, σ::T) where {T}
    d1 = (log(spot / strike) + (b + 0.5 * σ^2) * τ) / (σ * sqrt(τ))
    d2 = d1 - σ * sqrt(τ)

    blackScholesVega(isCall, spot, strike, τ, r, b, σ) * d1 * d2 / σ
end

function blackScholesSpeed(isCall::Bool, spot::T, strike::T, τ::T, r::T, b::T, σ::T) where {T}
    d1 = (log(spot / strike) + (b + 0.5 * σ^2) * τ) / (σ * sqrt(τ))

    -1.0 * blackScholesGamma(isCall, spot, strike, τ, r, b, σ) / spot * (1 + d1 / σ / sqrt(τ))
end

function blackScholesZomma(isCall::Bool, spot::T, strike::T, τ::T, r::T, b::T, σ::T) where {T}
    d1 = (log(spot / strike) + (b + 0.5 * σ^2) * τ) / (σ * sqrt(τ))
    d2 = d1 - σ * sqrt(τ)

    blackScholesGamma(isCall, spot, strike, τ, r, b, σ) * (d1 * d2 - 1.0) / σ
end

function blackScholesColor(isCall::Bool, spot::T, strike::T, τ::T, r::T, b::T, σ::T) where {T}
    d1 = (log(spot / strike) + (b + 0.5 * σ^2) * τ) / (σ * sqrt(τ))
    d2 = d1 - σ * sqrt(τ)

    blackScholesGamma(isCall, spot, strike, τ, r, b, σ) * (r - b + b * d1 / σ / sqrt(τ) + (1 - d1 * d2) / 2 / τ)
end

function blackScholesDriftlessTheta(isCall::Bool, spot::T, strike::T, τ::T, r::T, b::T, σ::T) where {T}
    d1 = (log(spot / strike) + (b + 0.5 * σ^2) * τ) / (σ * sqrt(τ))
    println(pdf(std_normal, d1))
    -spot * pdf(std_normal, d1) * σ / 2 / sqrt(τ)
end

function blackScholesRho(isCall::Bool, spot::T, strike::T, τ::T, r::T, b::T, σ::T) where {T}
    sign = isCall ? 1 : -1

    d1 = (log(spot / strike) + (b + 0.5 * σ^2) * τ) / (σ * sqrt(τ))
    d2 = d1 - σ * sqrt(τ)

    sign * τ * strike * exp(-r * τ) * cdf(std_normal, sign * d2)
end

function blackScholesRhoFutures(isCall::Bool, spot::T, strike::T, τ::T, r::T, b::T, σ::T) where{T}
    -1.0 * τ * blackScholesValue(isCall, spot, strike, τ, r, b, σ)
end

function blackScholesTouchProb(isCall::Bool, spot::T, strike::T, τ::T, r::T, b::T, σ::T) where {T}
    if isCall
        if strike <= spot
            return 1.0
        end
    else
        if strike >= spot
            return 1.0
        end
    end

    sign = isCall ? -1 : 1

    μ = (b - σ^2 / 2) / (σ * σ)
    λ = sqrt(μ^2 + 2 * r / (σ * σ))
    z = log(strike / spot) / σ / sqrt(τ) + λ * σ * sqrt(τ)

    (strike / spot) ^ (μ + λ) * cdf(std_normal, sign * z) + (strike / spot) ^ (μ - λ) * cdf(std_normal, sign * (z - 2 * λ * σ * sqrt(τ)))
end

end


# power analysis functions for proportions

"""
    powerPTest(h::Real = 0, n::Real = 0, alpha::Float64 = 0.05, alternative = "two.sided")

Compute power of a sample of `n` observations to test the effect size `h`
for one sample proportion (P) compared to a constant (c) at type I error = `alpha` (default: 0.05).
The effect size `h` = ϕ₁ - ϕ₂ where ϕ₁ = 2*asin(sqrt(P)) and ϕ₂ = 2*asin(sqrt(c)). The option
`alternative` is `two.sided` for H₀: ϕ₁ = ϕ₂ vs H₁: ϕ₁ ≠ ϕ₂, `less` for H₁: ϕ₁ < ϕ₂, and `greater`
for H₁: ϕ₁ > ϕ₂.
"""
function powerPTest(;
    h::Real = 0,
    n::Real = 0,
    alpha::Float64 = 0.05,
    alternative::String = "two.sided"
    )

    check_args(h=h,n=n,alpha=alpha)

    if alternative == "less"
        tside = 1
    elseif alternative in ("two","two.sided","two-sided","two sided")
        tside = 2
        h = abs(h)
    elseif alternative == "greater"
        tside = 3
    else
        error("`alternative` must be `less`, `two.sided`, or `greater`.")
    end

    if tside == 1
        return cdf(Normal(),quantile(Normal(),alpha) - h*sqrt(n))
    elseif tside == 2
        return ccdf(Normal(),cquantile(Normal(),alpha/2) - h*sqrt(n)) + cdf(Normal(),quantile(Normal(),alpha/2) - h*sqrt(n))
    elseif tside == 3
        return ccdf(Normal(),cquantile(Normal(),alpha) - h*sqrt(n))
    end
    error("internal error")
end

"""
    samplesizePTest(h::Real = 0, alpha::Float64 = 0.05, power::Float64 = 0.8, alternative = "two.sided")

Compute the sample size to test the effect size `h` for one sample proportion (P) compared to a constant (c)
with power > `power` (default: 0.8) and type I error = `alpha` (default: 0.05).
The effect size `h` = ϕ₁ - ϕ₂ where ϕ₁ = 2*asin(sqrt(P)) and ϕ₂ = 2*asin(sqrt(c)). The option
`alternative` is `two.sided` for H₀: ϕ₁ = ϕ₂ vs H₁: ϕ₁ ≠ ϕ₂, `less` for H₁: ϕ₁ < ϕ₂, and `greater`
for H₁: ϕ₁ > ϕ₂.
"""
function samplesizePTest(;
    h::Real = 0,
    alpha::Float64 = 0.05,
    power::Float64 = 0.8,
    alternative::String = "two"
    )

    check_args(h=h,alpha=alpha,power=power)

    return ceil(Int64,fzero(x->powerPTest(h = h, n = x, alpha = alpha, alternative = alternative) - power, 2 + 1e-10, 1e+09))
end

"""
    effectsizePTest(n::Real = 0, alpha::Float64 = 0.05, power::Float64 = 0.8, alternative = "two.sided")

Compute the effect size that a sample size `n` can detect for one sample proportion (P) compared to a constant (c)
with power > `power` (default: 0.8) and type I error = `alpha` (default: 0.05).
The effect size `h` = ϕ₁ - ϕ₂ where ϕ₁ = 2*asin(sqrt(P)) and ϕ₂ = 2*asin(sqrt(c)). The option
`alternative` is `two.sided` for H₀: ϕ₁ = ϕ₂ vs H₁: ϕ₁ ≠ ϕ₂, `less` for H₁: ϕ₁ < ϕ₂, and `greater`
for H₁: ϕ₁ > ϕ₂.
"""
function effectsizePTest(;
    n::Real = 0,
    alpha::Real = 0.05,
    power::Real = 0.0,
    alternative::String = "two"
    )

    check_args(n=n,alpha=alpha,power=power)

    return fzero(x->powerPTest(h = x, n = n, alpha = alpha, alternative = alternative) - power, 1e-10, 1 - 1e-10)
end

"""
    alphaPTest(h::Real = 0, n::Real = 0, power::Float64 = 0.8, alternative = "two.sided")

Compute the probability of type I error that a sample size `n` has in detecting the effect size `h`
for one sample proportion (P) compared to a constant (c) with power > `power` (default: 0.8)
and type I error = `alpha` (default: 0.05). The effect size `h` = ϕ₁ - ϕ₂ where ϕ₁ = 2*asin(sqrt(P)) and ϕ₂ = 2*asin(sqrt(c)).
The option `alternative` is `two.sided` for H₀: ϕ₁ = ϕ₂ vs H₁: ϕ₁ ≠ ϕ₂, `less` for H₁: ϕ₁ < ϕ₂, and `greater`
for H₁: ϕ₁ > ϕ₂.
"""
function alphaPTest(;
    h::Real = 0,
    n::Real = 0,
    power::Float64 = 0.8,
    alternative::String = "two"
    )

    check_args(h=h,n=n,power=power)

    return fzero(x->powerPTest(h = h, n = n, alpha = x, alternative = alternative) - power, 1e-10, 1 - 1e-10)
end

"""
    pwr.PTest(h::Real = 0, n::Real = 0, alpha::Float64 = 0.0, power::Float64 = 0.0, alternative = "two.sided")

Compute one of the test parameters such as sample size (`n`),
effect size (`h`), type I error (`alpha`), and power (`power`).
The parameter to be estimated must be set to zero (default).
The effect size `h` = ϕ₁ - ϕ₂ where ϕ₁ = 2*asin(sqrt(P)) and ϕ₂ = 2*asin(sqrt(c)).
The option `alternative` is `two.sided` for H₀: ϕ₁ = ϕ₂ vs H₁: ϕ₁ ≠ ϕ₂, `less` for H₁: ϕ₁ < ϕ₂, and `greater`
for H₁: ϕ₁ > ϕ₂.
"""
function PTest(;
    h::Real = 0,
    n::Real = 0,
    alpha::Float64 = 0.0,
    power::Float64 = 0.0,
    alternative::String = "two.sided"
    )
    if sum([x == 0 for x in (h,n,alpha,power)]) != 1
        error("exactly one of `h`, `n`, `power`, and `alpha` must be zero")
    end

    if power == 0.0
        power = powerPTest(h = h, n = n, alpha = alpha, alternative = alternative)
    elseif alpha == 0.0
        alpha = alphaPTest(h = h, n = n, power = power, alternative = alternative)
    elseif h == 0
        h = effectsizePTest(n = n, alpha = alpha, power = power, alternative = alternative)
    elseif n == 0
        n = samplesizePTest(h = h, alpha = alpha, power = power, alternative = alternative)
    end

    alt = Dict("two" => "two-sided", "two.sided" => "two-sided","two sided" => "two-sided", "less" => "less", "greater" => "greater")

    return htest(
        string("Proportion power calculation for binomial distribution (arcsine transformation)"),
        OrderedDict(
            "h" => h,
            "n" => n,
            "alpha" => alpha,
            "power" => power,
            "alternative" => alt[alternative]
            )
        )
end


function meantoecc(M::FloatingPoint, ecc::FloatingPoint)
    kepler(E) = E - ecc*sin(E) - M
    kepler_der(E) = 1 - ecc*cos(E)
    return newton(M, kepler, kepler_der)
end

function ecctomean(E::FloatingPoint, ecc::FloatingPoint)
    return E - ecc*sin(E)
end

function ecctotrue(E::FloatingPoint, ecc::FloatingPoint)
    return 2*atan2(sqrt(1 + ecc)*sin(E/2), sqrt(1 - ecc)*cos(E/2))
end

function truetoecc(T::FloatingPoint, ecc::FloatingPoint)
    return 2*atan2(sqrt(1 - ecc)*sin(T/2), sqrt(1 + ecc)*cos(T/2))
end

function period(a::FloatingPoint, mu::FloatingPoint)
    return sqrt(4*a^3*pi^2/mu)
end

function period(s::State)
    mu = planets[s.body]["mu"]
    ele = elements(s)
    return period(ele[1], mu)
end

function kepler(ele::Vector, dt::FloatingPoint, mu::FloatingPoint)
    E0 = truetoecc(ele[6], ele[2])
    M0 = ecctomean(E0, ele[2])
    n = 2*pi/period(ele[1], mu)
    M = M0 + n*dt
    E = meantoecc(M, ele[2])
    T = ecctotrue(E, ele[2])
    return [ele[1:5], T]
end

function kepler(ele::Vector, dt::Vector, mu::FloatingPoint)
    out = Array(FloatingPoint, length(dt), 6)
    for i = 1:length(dt)
        out[i,:] = kepler(ele, dt[i], mu)
    end
    return out
end


type Point{T}
  x::T
  y::T
end

Point{Int64} #> Point{Int64} (constructor with 1 method)
Point{Float64} #> Point{Float64} (constructor with 1 method)
Point{String} #> Point{String} (constructor with 1 method)

issubtype(Point{Int64}, Point) #> true
issubtype(Point{String}, Point) #> true
issubtype(Point{Float64}, Point{Real}) #> false

p = Point{Int64}(2, 5) #> Point{Int64}(2,5)
p = Point(2, 5) #> Point{Int64}(2,5)
p = Point("London", "Great-Britain") #> Point{String}("London", "Great-Britain")

type PointP{T <: Real}
  x::T
  y::T
end

# p = PointP("London", "Great-Britain") #> ERROR: `PointP{T<:Real}` has no method
# matching Point{T<:Real}(::ASCIIString, : ASCIIString)

add{T}(x::T, y::T) = x + y
add(2, 3) #> 5
# add(2, 3.0) #> ERROR: `add` has no method matching add(::Int64, ::Float64)
add{T<:Number}(x::T, y::T) = x + y

function vecfloat{T <: FloatingPoint}(x::Vector{T})
# code
end


# #  Portfolio Optimization - Markowitz Efficient Frontier
#
# In this problem, we will find the unconstrained portfolio allocation where we introduce the weighting parameter $\lambda \;(0 \leq \lambda \leq$ 1) and minimize $\lambda * \text{risk} - (1-\lambda)* \text{expected return}$. By varying the values of $\lambda$, we trace out the efficient frontier.
#
# Suppose that we know the mean returns $\mu \in \mathbf{R}^n$ of each asset and the covariance $\Sigma \in \mathbf{R}^{n \times n}$ between the assets. Our objective is to find a portfolio allocation that minimizes the *risk* (which we measure as the variance $w^T \Sigma w$) and maximizes the *expected return* ($w^T \mu$) of the portfolio of the simulataneously. We require $w \in \mathbf{R}^n$ and $\sum_i w_i = 1$.
#
# This problem can be written as
#
# $$
# \begin{array}{ll}
#     \mbox{minimize}   & \lambda*w^T \Sigma w - (1-\lambda)*w^T \mu \\
#     \mbox{subject to} & \sum_i w_i = 1
# \end{array}
# $$
#
# where $w \in \mathbf{R}^n$ is the vector containing weights allocated to each asset.

using Convex, SCS    #We are using SCS solver. Install using Pkg.add("SCS")

## generate problem data
μ = [11.5; 9.5; 6]/100          #expected returns
Σ  = [166  34  58;              #covariance matrix
       34  64   4;
       58   4 100]/100^2

n = length(μ)                   #number of assets

# If you want to try the optimization with more assets, uncomment and run the next cell. It creates a vector or average returns and a variance-covariance matrix that have scales similar to the numbers above.

#=
using Random
Random.seed!(123)

n = 15                                      #number of assets, CHANGE IT?

μ = (6 .+ (11.5-6)*rand(n))/100             #mean
A = randn(n,n)
Σ = (A * A' + diagm(0=>rand(n)))/500;       #covariance matrix
=#

# First we solve without any bounds on $w$

N = 101
λ_vals = range(0.01,stop=0.99,length=N)

w    = Variable(n)
ret  = dot(w,μ)
risk = quadform(w,Σ)

MeanVarA = zeros(N,2)
for i = 1:N
    λ = λ_vals[i]
    p = minimize( λ*risk - (1-λ)*ret,
                  sum(w) == 1 )
    solve!(p, SCSSolver(verbose = false))
    MeanVarA[i,:]= [evaluate(ret),evaluate(risk)[1]]    #risk is a 1x1 matrix
end

# Now we solve with the bounds $0\le w_i \le 1$

w_lower = 0                     #bounds on w
w_upper = 1

MeanVarB = zeros(N,2)   #repeat, but with 0<w[i]<1
for i = 1:N
    λ = λ_vals[i]
    p = minimize( λ*risk - (1-λ)*ret,
                  sum(w) == 1,
                  w_lower <= w,     #w[i] is bounded
                  w <= w_upper )
    solve!(p, SCSSolver(verbose = false))
    MeanVarB[i,:]= [evaluate(ret),evaluate(risk)[1]]
end

#-

using Plots
plot( sqrt.([MeanVarA[:,2] MeanVarB[:,2]]),
      [MeanVarA[:,1] MeanVarB[:,1]],
      xlim = (0,0.25),
      ylim = (0,0.15),
      title = "Markowitz Efficient Frontier",
      xlabel = "Standard deviation",
      ylabel = "Expected return",
      label = ["no bounds on w","with 0<w<1",])
scatter!(sqrt.(diag(Σ)),μ,color=:red,label = "assets")

# We now instead impose a restriction on  $\sum_i |w_i| - 1$, allowing for varying degrees of "leverage".

Lmax = 0.5

MeanVarC = zeros(N,2)   #repeat, but with restriction on Sum(|w[i]|)
for i = 1:N
    λ = λ_vals[i]
    p = minimize( λ*risk - (1-λ)*ret,
                  sum(w) == 1,
                  (norm(w, 1)-1) <= Lmax)
    solve!(p, SCSSolver(verbose = false))
    MeanVarC[i,:]= [evaluate(ret),evaluate(risk)[1]]
end

#-

plot( sqrt.([MeanVarA[:,2] MeanVarB[:,2] MeanVarC[:,2]]),
      [MeanVarA[:,1] MeanVarB[:,1] MeanVarC[:,1]],
      xlim = (0,0.25),
      ylim = (0,0.15),
      title = "Markowitz Efficient Frontier",
      xlabel = "Standard deviation",
      ylabel = "Expected return",
      label = ["no bounds on w","with 0<w<1","restriction on sum(|w|)"])
scatter!(sqrt.(diag(Σ)),μ,color=:red,label = "assets")

#-


# using StaticArrays

# Define Rosenbrock function: https://en.wikipedia.org/wiki/Rosenbrock_function
#
# This function can be interpreted as a NLS pb:
# r_1 = (1-θ₁)
# r_2 = 10(θ₂-θ₁²)
#
# Jacobian is: [[-1, 0],[-20, 10]]
#
# Minimum for θ=(1,1) where f=1/2 r^2 = 0
#
# TODO: implement even n (here 2) generalization of the wiki page.
#
struct Rosenbrock <: AbstractNLS
end

parameter_size(::Rosenbrock) = 2
residue_size(::Rosenbrock) = 2

function eval_r(nls::Rosenbrock,θ::AbstractVector{T}) where T
    @assert length(θ)==parameter_size(nls)

    T[ 1-θ[1], 10*(θ[2]-θ[1]^2) ]
end

function eval_r_J(nls::Rosenbrock,θ::AbstractVector{T}) where T
    @assert length(θ)==parameter_size(nls)

    r = T[ 1-θ[1], 10*(θ[2]-θ[1]^2) ]
    J = T[ -1 0; -20*θ[1] 10]

    (r,J)
end

# ================================================================

const Rosenbrock_Static = Rosenbrock

# # Define the same Rosenbrock function but using Statics Arrays... This
# # cause some problems: https://github.com/JuliaArrays/StaticArrays.jl/issues/971
# #
# struct Rosenbrock_Static <: AbstractNLS
# end

# parameter_size(::Rosenbrock_Static) = 2
# residue_size(::Rosenbrock_Static) = 2

# function eval_r(nls::Rosenbrock_Static,θ::AbstractVector{T}) where T
#     @assert length(θ)==parameter_size(nls)

#     r = @SVector T[1-θ[1],
#                    10*(θ[2]-θ[1]^2)]
# end

# function eval_r_J(nls::Rosenbrock_Static,θ::AbstractVector{T}) where T
#     @assert length(θ)==parameter_size(nls)

#     r = eval_r(nls,θ)

#     # CAVEAT: SMatrix are filled column by column
#     #
#     # The Jacobian
#     #
#     # | ∂1r1, ∂2r1 |
#     # | ∂1r2, ∂2r2 |
#     #
#     # must be created by
#     #
#     # SMatrix(∂1r1,∂1r2, # col 1
#     #         ∂2r1,∂2r2) # col 2
#     #
#     # which is not intuitive and error prone.
#     #
#     # The alternative to is to use the @SMatrix macro
#     # that follows the "natural" order (row by row)
#     #
#     J = @SMatrix T[      -1   +0;     # ∂1r1, ∂2r1
#                          -20*θ[1]  +10]     # ∂1r2, ∂2r2

#     (r, J)
# end


# functions related to F distribution

import .RFunctions:
    fdistpdf,
    fdistlogpdf,
    fdistcdf,
    fdistccdf,
    fdistlogcdf,
    fdistlogccdf,
    fdistinvcdf,
    fdistinvccdf,
    fdistinvlogcdf,
    fdistinvlogccdf

# pdf for numbers with generic types
fdistpdf(ν1::Real, ν2::Real, x::Number) = sqrt((ν1 * x)^ν1 * ν2^ν2 / (ν1 * x + ν2)^(ν1 + ν2)) / (x * beta(ν1 / 2, ν2 / 2))

# logpdf for numbers with generic types
fdistlogpdf(ν1::Real, ν2::Real, x::Number) = (ν1 * log(ν1 * x) + ν2 * log(ν2) - (ν1 + ν2) * log(ν1 * x + ν2)) / 2 - log(x) - lbeta(ν1 / 2, ν2 / 2)


# ================================
# Concrete symmetric interval hull
# ================================

"""
    symmetric_interval_hull(S::LazySet{N}) where {N}

Overapproximate a set by a tight hyperrectangle centered in the origin.

### Input

- `S` -- set

### Output

A tight hyperrectangle that is centrally symmetric wrt. the origin.

### Algorithm

The center of the box is the origin, and the radius is obtained by computing the
maximum value of the support function evaluated in the canonical directions.
"""
function symmetric_interval_hull(S::LazySet{N}) where {N}
    # fallback returns a hyperrectangular set
    (c, r) = box_approximation_helper(S)
    if r[1] < 0
        return EmptySet{N}(dim(S))
    end
    return Hyperrectangle(zeros(N, length(c)), abs.(c) .+ r)
end

"""
    box_approximation_symmetric

Alias for `symmetric_interval_hull`.
"""
box_approximation_symmetric = symmetric_interval_hull

# ===============
# Specializations
# ===============

# empty set specialization
symmetric_interval_hull(∅::EmptySet) = ∅

function LazySets.concretize(sih::SymmetricIntervalHull)
    return symmetric_interval_hull(LazySets.concretize(sih.X))
end

# interval specialization
function symmetric_interval_hull(x::Interval)
    abs_inf = abs(min(x))
    abs_sup = abs(max(x))
    bound = max(abs_sup, abs_inf)
    return Interval(-bound, bound)
end

# hyperrectangle specialization
@inline function _maxabs(c::N, r::N) where {N}
    return c >= zero(N) ? c + r : -c + r
end

function symmetric_interval_hull(H::Hyperrectangle{N}) where {N}
    n = dim(H)
    r = Vector{N}(undef, n)
    @inbounds for i in 1:n
        r[i] = _maxabs(H.center[i], H.radius[i])
    end
    return Hyperrectangle(zeros(N, n), r)
end


export legendrePoly #Just a basic export to make legendrePoly function visible (ie. Public)

## Function that computes the legendre polynomial of nᵗʰ order
#  INPUTS:
# Type T - ?????
# order - Integer defining the desired Legendre polynomial order
#  OUTPUTS:
# Pₙ(x) - Legendre polynomial of order n="order"

function legendrePoly(::Type{T},order::Int) where {T} #compute Legendre polynomial
##Throw an error if we ask for a polynomial of negative order
    order<0 && error("order must be >= 0") #If order is less than zero, we throw an error
##Manually input legendre polynomials of orders 0 and 1
    P = [Polynomial{T}(1), Polynomial{T}([0,1])] #initialize our horizon for pseudo-recursion with known Legendre Polynomials: P=[1,x]
    x = Polynomial{T}([0,1]) #x is just an instance of type Polynomial
##For orders 0 or 1 we return 1 or x respectively
    order<2 && return P[order+1] #if order is 1 return x, if order is 0 return 1
## For order>=2: we are using Bonnet recursion formula: nPₙ(x)=(2n-1)Pₙ₋₁(x)-(n-1)Pₙ₋₂(x)
# Compute Pᵢ₊₁ from Pᵢ and Pᵢ₋₁ in a pseudo-recursive way
    for i = 1:order-1 #i is effectively n-1 (ie. current order-1) because we index from 1 to "order-1" instead of the natural 2 to "order"
        Ptmp = ( (2*i + 1 ) * x * P[2] - i * P[1]) / (i+1) #this computes Pᵢ₊₁ in a pseudo recursive manner
        P[1] = P[2] #here we save Pᵢ for next iteration
        P[2] = Ptmp #here we save Pᵢ₊₁ for next iteration
    end
## Return P of order "order"
    return P[2]
end

legendrePoly(order::T) where {T} = legendrePoly(T,convert(Int,order)) #I suspect here he tried to do some proper recursion, but idk


function _minimum!(D,clusterInd)
    N = size(D,1)
    m = Inf
    mI = mJ = -1
    for j=2:N
        clusterInd[j]==0 && continue
        for i=1:j-1
            clusterInd[i]==0 && continue
            if D[i,j]<=m
                m = D[i,j]
                mI,mJ = i,j
            end
        end
    end
    m, mI, mJ
end

# only upper triangular part of D is used
function hierarchicalclustering(D::AbstractMatrix{T}; similarity=false) where {T}
    @assert size(D,1)==size(D,2)
    N = size(D,1)

    distType = promote_type(Float64,T)
    D = copy(convert(Array{distType},D)) # since D will be updated when clusters are merged
    similarity && (D*=-1) # just change sign when using similarity instead of dissimilarity! (Nothing breaks if we have distances below 0. Relative values are all that matter.)

    # index, index, nbr elements in cluster, distance/similarity
    # indices 1-N
    clusters = Vector{Tuple{Int,Int,Int,distType}}()

    # the cluster index for each row/column in D. Indices from 1:N are leaves.
    # From N+1 and upwards are in clusters Vector (subtract N from index).
    # A value of 0 means that the row/column is no longer used. (One row/column is discarded every time two clusters are merged.)
    clusterInd = collect(1:N)

    for k=1:N-1 # each created cluster has two children, i.e. we need N-1 clusters (not counting leaves)
        m,mI,mJ = _minimum!(D,clusterInd)

        # create new cluster
        cI,cJ = clusterInd[mI], clusterInd[mJ]

        nbrElementsI = cI<=N ? 1 : clusters[cI-N][3]
        nbrElementsJ = cJ<=N ? 1 : clusters[cJ-N][3]

        dist = D[mI,mJ]
        similarity && (dist*=-1) # change back from distance to similarity if needed
        push!(clusters, (cI,cJ,nbrElementsI+nbrElementsJ,dist))

        # update distance matrix and clusterInd
        clusterInd[mJ] = 0
        clusterInd[mI] = k+N

        # construct new distances from D[mI,u], D[mJ,u]
        for u=1:N
            u==mI && continue
            clusterInd[u]==0 && continue # includes u==mJ

            mI2,u2 = minmax(mI,u) # upper triangular index
            D[mI2,u2] = (D[mI2,u2]*nbrElementsI + D[minmax(mJ,u)...]*nbrElementsJ) / (nbrElementsI+nbrElementsJ) # TODO: change here for different linkage criteria
        end

    end

    clusters
end

function samplesinclusters(clusters)
    N = length(clusters)+1 # Since every merge makes two clusters into one, we can figure out the original number of samples this way.
    samples = Vector{Vector{Int}}(length(clusters))
    for (i,c) in enumerate(clusters)
        samples[i] = vcat( c[1]>N ? samples[c[1]-N] : [c[1]],
                           c[2]>N ? samples[c[2]-N] : [c[2]] )
    end
    samples
end


using Distributions
using PyPlot
using Roots
using JLD

function tauchen86(μ::AbstractFloat,ρ::AbstractFloat,σ::AbstractFloat,n::Int64,λ::AbstractFloat)
    #output is xgrid, xprob
    # x_t+1 = μ + ρ x_t + σ e_{t+1}, e_t+1 ∼ N(0,1)
    xhi = μ/(1-ρ) + λ*sqrt(σ^2/(1-ρ)^2)
    xlo = μ/(1-ρ) - λ*sqrt(σ^2/(1-ρ)^2)
    xgrid = zeros(n);
    xscale =(xhi-xlo)/(n-1)

    for i=1:n
        xgrid[i] = xlo + xscale*(i-1)
    end
    m=zeros(n-1);
    for i=1:n-1
        m[i] = (xgrid[i]+xgrid[i+1])/2
    end
    xprob = zeros(n,n) # xprob[i,j] = Pr(x_t+1=xgrid[j]|x_t=xgrid[i])
    for i=1:n # this is the state today
        normie = Normal(μ+ρ*xgrid[i],σ)
        normpdf(x) = pdf.(normie,x)
        normcdf(x) = cdf.(normie,x)
        for j=2:n-1
            xprob[i,j] = normcdf(m[j]) - normcdf(m[j-1])
        end
        xprob[i,1] = normcdf(m[1])
        xprob[i,n] = 1 - normcdf(m[n-1])
    end
    xprob = xprob ./ sum(xprob,2) # make sure the rows sum to 1
    return ( xgrid,xprob, xscale )
end

function trunc_lognpdf(x, UP, LO, mu, sig)
    # truncated log normal pdf
    logn = LogNormal(mu, sig)
    return (x-LO.>=-eps()) .* (x-UP.<=eps()).*pdf.(logn,x)/(cdf.(logn,UP)-cdf.(logn,LO))
end

# set parameters
# PARAMETERS THAT AFFECT STEADY STATE:
# note, we have log utility, so no CRRA parameter
# and we have a quarterly model
r = 0.01                        # 4 percent steady state annual real interest rate
α = 0.3                         # capital share
H = 1.0                         # aggregate hours worked
δ  = 0.03                       # depreciation
sH_over_sL = 1.2 / 0.8
pLH = 0.1                       # prob of going from low to high persistent skill
pHL = 0.1                      # prob of going fom
γ = 0.#0.004                       # TFP growth
GoverY = 0.01#18                        # steady state govt spending/gdp
g = 1/(1-GoverY)                # steady state value of g_t, where G_t/Y_t = (1-(1/g_t))
η = 0.                         # borrowing constraint (normalized by TFP)
In    = 0.443993816237631       # normalizing constant for the mollifier
na = 300                         # cash on hand ditn grid points - set to smaller numbers for debugging
ns = 2# 5                         # skill distribution grid points
zlo =  1. / 3.                      # second income shock to mollify actual income
zhi = 2. - zlo                       # upper bound on this process
BoverY = 0.26

# AGGREGATE SHOCKS
ρB = 0.5 # persistence of discount factor shock
ρG = 0.5 # persistence of govt spending shock
ρZ = 0.5 # persistence of tfp growth shock
ρμ = 0.5 # persistence of investment shock
ρlamw = 0.5 # persistence of wage mkup shock
ρlamf = 0.5 # persistence of price mkup shock
ρmon = 0.5 # persistence of mon policy shock

# OTHER PARAMETERS THAT AFFECT DYNAMICS
spp  = 4.                        # second derivative of investment adjustment cost
lamw = 1.5                       # wage markup
ϕh   = 2.                        # inverse frisch elasticity
Φw   = 10.                       # rotemberg cost for wages
lamf = 1.5                       # price markup
Φp   = 1.                       # rotemberg cost for prices
ρR   = 0.75                      # persistence in taylor rule
ψπ   = 1.5                      # weight on inflation in taylor rule
ψy   = 0.5                       # weight on output growth in taylor rule

# OPTIONS
TRUNCATE      = true # default: true
mindens       = 1e-8
RESCALE_XWTS  = true # default: true
SIMPLE_TAYLOR = false # default: false

# solve for steady state of aggregate scalar variables
# here I am assuming firms are subsidized in ss so that real mc = 1
Rk = r + δ                               # rental rate on capital
ω = (α^(α/(1-α)))*(1-α)*Rk^(-α/(1-α))    # real wage
kl = (α/(1-α))*(ω/Rk)*ℯ^γ                # capital/labor ratio
k = kl*H                                 # capital
x = (1-(1-δ)*ℯ^(-γ))*k                   # investment
y = (ℯ^(-α*γ))*(k^α)*H^(1-α)             # gdp
bg = BoverY*y                            # govt debt
T = Rk*k*ℯ^(-γ) - x - (1-(1/g))*y - ((1+r)*ℯ^(-γ)-1)*bg        # net transfer to hhs

function persistent_skill_process(sH_over_sL::AbstractFloat, pLH::AbstractFloat, pHL::AbstractFloat, ns::Int)
    f1 = [[1-pLH pLH];[pHL 1-pHL]] # f1[i,j] is prob of going from i to j
    ss_skill_distr = [pHL/(pLH+pHL); pLH/(pLH+pHL)]
    slo = 1. / (ss_skill_distr'*[1;sH_over_sL])
    sgrid = slo*[1;sH_over_sL]
    sscale = sgrid[2] - sgrid[1]
    swts     = (sscale/ns)*ones(ns) #quadrature weights
    f = f1 ./ repmat(swts',ns,1)
    return (f, sgrid, swts)
end
(f, sgrid, swts) = persistent_skill_process(sH_over_sL, pLH, pHL, ns)

function cash_grid(sgrid::AbstractArray, ω::AbstractFloat, H::AbstractFloat, r::AbstractFloat, η::AbstractFloat, γ::AbstractFloat,
                   T::AbstractFloat, zlo::AbstractFloat, na::Int)
    smin = minimum(sgrid)*zlo                           # lowest possible skill
    alo_ss = ω*smin*H - (1+r)*η*ℯ^(-γ) + T + sgrid[1]*ω*H*0.05       # lowest possible cash on hand in ss

    alo = alo_ss                    # lower bound on cash on hand - could be < alo_ss
    ahi = max(alo*2,alo+5.)                      # upper bound on cash on hand
    ascale = (ahi-alo)              # size of w grids

    # make grids
    agrid    = collect(linspace(alo,ahi,na)) #Evenly spaced grid
    awts = (ascale/na)*ones(na)          #quadrature weights
    return (agrid, awts)
end
smin = minimum(sgrid)*zlo                           # lowest possible skill
alo_ss = ω*smin*H - (1+r)*η*ℯ^(-γ) + T + sgrid[1]*ω*H*0.05       # lowest possible cash on hand in ss

alo = alo_ss                    # lower bound on cash on hand - could be < alo_ss
ahi = max(alo*2,alo+5.)                      # upper bound on cash on hand
ascale = (ahi-alo)              # size of w grids

(agrid, awts) = cash_grid(sgrid, ω, H, r, η, γ, T, zlo, na)

function mollifier(z::AbstractFloat,ehi::AbstractFloat,elo::AbstractFloat)
    # mollifier function
    In = 0.443993816237631
    if z<ehi && z>elo
        temp = -1.0 + 2.0*(z-elo)/(ehi-elo)
        out = (2.0/(ehi-elo))*exp(-1.0/(1.0-temp*temp))/In
    else
        out = 0.0
    end

    return out
end

zgrid = linspace(zlo,zhi,na)
zwts = (zhi-zlo)/na
sumz=0.
for i=1:na
    sumz += mollifier(zgrid[i],zhi,zlo)*zwts
end

# experiment with a different q
ne = 100
σe = 0.01
(legrid, fe, sscale) = tauchen86(0.0,0.0,σe,ne,2.0)
egrid = exp.(legrid)
eprob = fe[1,:]

function convoluted_q(x::AbstractFloat, zhi::AbstractFloat, zlo::AbstractFloat, ne::Int, egrid::Vector{Float64}, eprob::Vector{Float64})
    sumne = 0.
    for i=1:ne
        sumne += mollifier(x - egrid[i], zhi, zlo)*eprob[i]
    end
    return sumne
end

qfunction(x) = mollifier(x, zhi, zlo)#/sumz # this ensures the mollifer sums to 1, at least on this particular grid
#qfunction(x) = convoluted_q(x, zhi, zlo, ne, egrid, eprob)

function dmollifier(x::AbstractFloat, ehi::AbstractFloat, elo::AbstractFloat)
    In = 0.443993816237631
    if x<ehi && x>elo
        temp = (-1.0 + 2.0*(x-elo)/(ehi-elo))
        out  = -(2*temp ./ ((1 - temp.^2).^2)) .* (2/(ehi-elo)).*mollifier(x, ehi, elo)
    else
        out = 0.0
    end
    return out
end

qp(z) = dmollifier(z,zhi,zlo)

Win = 1 ./ (5. + 0.02*(repeat(agrid,ns)-5))
Win = 2*ones(na*ns)/(ahi+alo)
aswts = kron(swts,awts)

function sspolicy(na::Int, ns::Int, β::AbstractFloat, R::AbstractFloat,
                     ω::AbstractFloat,
                     H::AbstractFloat,
                     η::AbstractFloat,
                     T::AbstractFloat,
                     γ::AbstractFloat,
                     qfunction::Function,
                     agrid::Vector{Float64},
                     sgrid::Vector{Float64},
                     aswts::Vector{Float64},
                     Win::Vector{Float64},
                     f::Array{Float64,2},damp::Float64 = 0.5, dist::Float64 = 1.,
                         tol::Float64 = 1e-4, maxit::Int64 = 500)
    n = na*ns
    c  = zeros(n)      # consumption
    bp = zeros(n)      # savings
    counter = 1
    Wout = copy(Win)
    while dist>tol && counter<maxit # for debugging
        # compute c(w) given guess for Win = β*R*E[u'(c_{t+1})]
        for iss in 1:ns
            for ia in 1:na
                c[na*(iss-1)+ia] =  min(1/Win[na*(iss-1)+ia],agrid[ia]+η)
            end
        end
        bp = repeat(agrid,ns) - c  # compute bp(w) given guess for Win
        Wout = parameterized_expectations(na,ns,β,R,ω,H,η,T,γ,qfunction,agrid,sgrid,aswts,c,bp,f)
        dist = maximum(abs.(Wout-Win))
        #println(dist)
        if mod(counter,25)==1
            println(dist)
            #plot(Wout)
        end
        Win = damp*Wout + (1.0-damp)*Win
        counter += 1
    end
    if counter==maxit
        warn("Euler iteration did not converge")
    end
    tr = kolmogorov_fwd(na,ns,ω,H,η,T,γ,qfunction,agrid,sgrid,bp,f)
    return (c,bp,Wout,tr)
end

function parameterized_expectations(na::Int,ns::Int, β::AbstractFloat, R::AbstractFloat, ω::AbstractFloat,
                                    H::AbstractFloat, η::AbstractFloat, T::AbstractFloat, γ::AbstractFloat,
                                    qfunction::Function,
                                    agrid::Vector{Float64}, sgrid::Vector{Float64},
                                    aswts::Vector{Float64}, c::Vector{Float64},
                                    bp::Vector{Float64}, f::Array{Float64,2})
    l_out = zeros(na*ns)
    for iss=1:ns
        for ia=1:na
            sumn = 0.0
            for isp=1:ns
                for iap=1:na
                    sumn += (aswts[na*(isp-1)+iap]/c[na*(isp-1)+iap])*qfunction((agrid[iap] - R*(ℯ^(-γ))*bp[na*(iss-1)+ia] - T)/(ω*H*sgrid[isp]))*f[iss,isp] ./ sgrid[isp]
                end
            end
            l_out[na*(iss-1)+ia] = (β*R*(ℯ^(-γ))/ω*H)*sumn
        end
    end
    return l_out
end

function kolmogorov_fwd(na::Int, ns::Int, ω::AbstractFloat,
                        H::AbstractFloat, η::AbstractFloat, T::AbstractFloat, γ::AbstractFloat,
                        qfunction::Function,agrid::Vector{Float64},
                        sgrid::Vector{Float64}, bp::Vector{Float64},f::Array{Float64,2})
    tr = zeros(na*ns,na*ns)
    for iss=1:ns
        for ia=1:na
            for isp=1:ns
                for iap=1:na
                    tr[na*(isp-1)+iap,na*(iss-1)+ia] = qfunction((agrid[iap] - R*(ℯ^(-γ))*bp[na*(iss-1)+ia] - T)/(ω*H*sgrid[isp]))*f[iss,isp] ./ (ω*H*sgrid[isp])
                end
            end
        end
    end
    return tr
end

# just to test if code returns
R = 1+r
βlo = 0.5*(ℯ^γ)/R # excess should be -ve
βhi = (ℯ^γ)/R # excess should be +ve

β = (βlo+βhi)/2.0

function findss(na::Int, ns::Int, βlo::AbstractFloat,
                βhi::AbstractFloat, R::AbstractFloat,
                     ω::AbstractFloat,
                     H::AbstractFloat,
                     η::AbstractFloat,
                     T::AbstractFloat,
                     γ::AbstractFloat,
                     bg::AbstractFloat,
                     qfunction::Function,
                     agrid::Vector{Float64},
                     sgrid::Vector{Float64},
                     aswts::Vector{Float64},
                     Win::Vector{Float64},
                     f::Array{Float64,2},
                     excess::AbstractFloat = 5000., tol::AbstractFloat = 1e-4,
                     maxit::Int64 = 20)
    counter=1
    n       = ns*na
    c = zeros(n)
    bp = zeros(n)
    KF = zeros(n,n)
    m = zeros(n)
    report = zeros(maxit,2)
    #Win0=copy(Win)
    while abs(excess)>tol && counter<maxit # clearing markets
        β = (βlo+βhi)/2.0
        Win_guess = ones(n) # or Win
        (c, bp, Win, KF) = sspolicy(na, ns, β, R, ω, H, η, T, γ, qfunction, agrid, sgrid,
                                       aswts, Win_guess, f)

        LPMKF=aswts[1]*KF
        # find eigenvalue closest to 1
        (D,V) = eig(LPMKF)
        order_D = sortperm(abs.(D), rev = true)
        V = V[:,order_D]
        D = D[order_D]
        if abs(D[1]-1)>2e-1 # that's the tolerance we are allowing
            warn("your eigenvalue is too far from 1, something is wrong")
        end
        m = real(V[:,1]) #Pick the eigen vecor associated with the largest eigenvalue and moving it back to values
        m = m/(aswts'*m) #Scale of eigenvectors not determinate: rescale to integrate to exactly 1
        excess = (aswts'*(m .* bp))[1] - bg  #compute excess supply of savings, which is a fn of w
                # bisection
        println([βlo β βhi])
        println(excess)
        report[counter,1] = β # save the history of guesses and excess supply so we can check it's upward-sloping and monotonic
        report[counter,2] = excess
        if excess>0
            βhi=β
        elseif excess<0
            βlo = β
        end
        counter += 1
    end
    return (Win, c, m, β, report)
end

tic()
(ell, c, m, β, report) = findss(na, ns, βlo, βhi, R, ω, H, η, T, γ, bg, qfunction, agrid, sgrid,aswts, Win, f)
toc()


# p18.jl - Chebyshev differentiation via FFT (compare p11.jl)

xx = -1:.01:1; ff = @. exp(xx)*sin(5*xx); clf();
for N = [10 20]
    x = cheb(N)[2]; f = @. exp(x)*sin(5*x);
    axes([.15, .66-.4*(N==20), .31, .28]);
    plot(x,f,"k.",markersize=6); grid(true);
    plot(xx,ff);
    title("f(x), N=$N");
    error = chebfft(f) - @. exp(x)*(sin(5*x)+5*cos(5*x));
    axes([.55, .66-.4*(N==20), .31, .28]);
    plot(x,error,".-",markersize=10); grid(true);
    title("error in f'(x),  N=$N");
end


using Roots, EngEconomics

# Given
MARR = 0.06
A_Init = -1000
A_Year = 1500
B_Init = -2000
B_Year = 2800

# Find the better option using Incremental ROR
ΔInit = B_Init - A_Init
ΔYear = B_Year - A_Year
ΔIRR(x) = ΔInit + presentWorthFactor(x, 1) * ΔYear
ΔIRR_ans = find_zero(ΔIRR, 0.05)

# Because ΔIRR_ans > MARR, the second alternative, B, is better
println("Choose Alt. 2")


# -----------------------------------------------------------
#
# Evaluating the 1D Galerkin Basis functions
#
# -----------------------------------------------------------

const K_max = 10

# Efficiency criticality: HIGH
# Central to the efficiency of the package

# Performing fastmath on an array of coefficients
# of x^k and |x|^k, giving the value at a specified point
# supported on [-1, 1]
@fastmath function array2poly(v::AbstractArray{T, 1}, x::Real) where T <: Real
    if abs(x)>1
        return zero(T)
    end
    n = length(v)
    k = div(n,2)
    s = zero(T)
    @inbounds for i in k:-1:1
        # Using Horner's method
        s *= x
        s += v[i] + flipsign(v[i+k], x)
    end
    return s
end

function array2poly(v::AbstractArray{T, 1}) where T <: Real
    return (x-> array2poly(v,x))
end

#precompute Legendre
leg_coeffs = legendre(K_max)

# Legendre polynomial supported on [-1, 1]
function LegendreP(k, x)
    k<=K_max || throw(DomainError())
    return array2poly(leg_coeffs[k+1], x)
end

function LegendreP(k)
    k<=K_max || throw(DomainError())
    return array2poly(leg_coeffs[k+1])
end

# precomputing the DG functions
# (Thanks to Alex Arslan for this.)
# Meaning: [baisis size k][coefficient, mode]
# Lengths: [K_max][2*k, k]
const dg_coeffs = Vector{Matrix{Float64}}(undef, K_max)

for k in 1:K_max
    Q = dg_basis(k)
    @assert length(Q) == k
    @assert all(length(Q[mode]) == 2*k for mode in 1:k)
    dg_coeffs[k] = Matrix{Float64}(undef, 2*k, k)
    for mode in 1:k
        dg_coeffs[k][:,mode] = Q[mode][:]
    end
end

# This is the dg basis function corresponding a given mode
# supported on [-1, 1]
function h(k, mode, x)
    mode<=k || throw(DomainError())
    return array2poly((@view dg_coeffs[k][:,mode]), x)
end

function h(k, mode)
    mode<=k || throw(DomainError())
    return array2poly((@view dg_coeffs[k][:,mode]))
end

# ----------------------------------------------
#
# Methods relating to the 1D Position Basis:
#
# ----------------------------------------------

# Legendre polynomial on [0, 1]
function leg(mode::Int, x::T) where T <: Real
    return sqrt(2.0)*LegendreP(mode-1, 2*x-1)
end

# level >= 1
# cell in 1:1<<level
# mode in 1:k
# Legendre polynomial on [(cell-1)*h, cell*h] with h = 1/(1<<level)
function basis(level::Int, cell::Int, mode::Int, x::T) where T <: Real
    return leg(mode, (1<<level)*x - (cell-1)) * (2.0)^(level/2)
end

function basis(level::Int, cell::Int, mode::Int)
    return x->basis(level, cell, mode, x)
end

# Perform numerical integration to get position basis coeffs
#
# Never used except if user demands to evolve wave equation
# using only the position basis, never going through heir
function pos_vcoeffs_DG(k::Int, level::Int, f::Function;
                        rtol = REL_TOL, atol = ABS_TOL, maxevals = MAX_EVALS)
    vcoeffs = Array{Float64}(undef, (1<<level)*(k))
    i = 1
    for cell in 1:(1<<level)
        for mode in 1:k
            fcn            = x->(basis(level, cell, mode,x)*f(x))
            left_bound  = (cell-1)/(1<<level)
            right_bound = (cell)/(1<<level)
            vcoeffs[i]  = hquadrature(fcn, left_bound, right_bound; atol=atol)[1]
            i += 1
        end
    end
    return vcoeffs
end

# ----------------------------------------------
# Building the hier2pos matrix:
# ----------------------------------------------

# Given an array of the type above, of coefficients
# for both both x^k and |x|^k, then given a choice of
# side (right vs. left) this becomes just a pure
# polynomial array of half the length
function convert_polyarray(v::AbstractArray{T, 1}, side = "left") where T <: Real
    n = Int(length(v)//2)
    if side == "left"
        return [v[i]-v[n+i] for i in 1:n]
    elseif side == "right"
        [v[i]+v[n+i] for i in 1:n]
    else throw(ArgumentError())
    end
end

# Gives the polynomial array corresponding to p(x/c)
function scale_polyarray(v::AbstractArray{T, 1}, c::Real) where T <: Real
    n = length(v)
    return [v[i] * (1//c^(i-1)) for i in 1:n]
end

# Gives the polynomial array corresponding to p(x-a)
function shift_polyarray(v::AbstractArray{T, 1}, a::Real) where T <: Real
    n = length(v)
    v_new = zeros(v)
    for i::Int in 1:n
        for j::Int in 1:i
            v_new[j] += v[i] * (-a)^(i-j) * binomial(i-1, j-1)
        end
    end
    return v_new
end

# Performs symbolic integration of the polynomials
# represented by v and w on the interval [a, b]
function integrate_polyarray(v::AbstractArray{T, 1}, w::AbstractArray{T, 1}; a::Real = 0, b::Real = 1) where T <: Real
    ans = zero(T)
    for i::Int in 1:length(v)
        for j::Int in 1:length(w)
            ans += v[i] * w[j] * ((b^(i+j-1)-a^(i+j-1))//(i+j-1))
        end
    end
    return ans
end

# With a bit of case work, we can do an explicit
# symbolic integration of the elements in the
# hierarchical basis against the legendre polynomials
# defining the position basis:
function hier2pos(k::Int, max_level::Int, level::Int, cell::Int, mode::Int)

    ans = Real[]

    # Compute the support and discontinuity
    # of the hierarchical basis element
    left_point  = (cell-1)//(1<<max(0, level-1))
    mid_point   = (cell- 1//2)//(1<<max(0, level-1))
    right_point = cell//(1<<max(0, level-1))

    # Level 0 of the hierarchical basis is a continous
    # Legendre polynomial
    if level == 0
        vl = vr = sqrt(2)*shift_polyarray(
                                scale_polyarray(leg_coeffs[mode][1:k],
                                                1//2),
                                            1//2)

    # The higher DG levels are given by two polynomials
    # defined on neighboring (right & left) intervals of size h/2,
    # which would need to be integrated against individually
    else
        h = 1//(1<<max(0, level-1))
        vl = shift_polyarray(
                scale_polyarray(
                    convert_polyarray((@view dg_coeffs[k][:,mode]), "left"),
                                1//2),
                            1//2)
        vl = shift_polyarray(scale_polyarray(vl, h), (cell-1)*h)*sqrt(1<<(level))

        vr = shift_polyarray(
                scale_polyarray(
                    convert_polyarray((@view dg_coeffs[k][:,mode]), "right"),
                                 1//2),
                            1//2)
        vr = shift_polyarray(scale_polyarray(vr, h), (cell-1)*h)*sqrt(1<<(level))
    end

    h = 1//(1<<max_level)
    for i in 1:1<<max_level
        for p in 1:k
            # Check if our hier function is supported on this interval
            if (i-1)*h < left_point || i*h > right_point
                push!(ans, 0)
                continue
            end

            # Obtain the polynomial array for the position basis function
            unit_leg = shift_polyarray(
                            scale_polyarray(leg_coeffs[p][1:k], 1//2), 1//2)
            pos_element = shift_polyarray(
                                scale_polyarray(unit_leg, h), (i-1)*h)
            pos_element *= sqrt(1<<(max_level+1)) # (2.0)^((max_level+1)/2)

            # Perform symbolic integration depending on which
            # interval the position basis function overlaps with the
            # DG basis element
            if (i-1)*h < mid_point
                val = integrate_polyarray(vl, pos_element; a=(i-1)*h, b=i*h)
            else
                val = integrate_polyarray(vr, pos_element; a=(i-1)*h, b=i*h)
            end
            push!(ans, val)
        end
    end
    return ans
end

# Construct the change of basis matrix from hierarchical
# to position basis
function hier2pos(k::Int, max_level::Int; atol=ABS_TOL)
    j = 1
    I = Int[]
    J = Int[]
    V = Float64[]
    for level in 0:max_level
        for cell in 1:(1<<max(0, level-1))
            for mode in 1:k
                ans = pos_vcoeffs_DG(k, max_level, v(k, level, cell, mode))
                for i in 1:length(ans)
                    if abs(ans[i]) > atol
                        push!(I, i)
                        push!(J, j)
                        push!(V, ans[i])
                    end
                end
                j += 1
            end
        end
    end
    return sparse(I, J, V, k * (1<<max_level), k * (1<<max_level), +)
end

function pos2hier(k::Int, max_level::Int; atol=ABS_TOL)
    return hier2pos(k, max_level; atol=atol)'
end


# # Matrix operators

#md # ```@meta
#md # CurrentModule = ImmersedLayers
#md # ```

#=
Many solutions of partial differential equations with immersed layers, particularly Poisson problems,
lead to saddle-point problems, in which the Schur complement operator is a matrix
composed from some of the surface-grid operators discussed in [Surface-grid operations](@ref).
The package provides some convenience tools for constructing these matrices. By
their nature, the construction of these matrices is slow, since each column
involves the application of the same set of operations. However, the point
of this construction is to do it once and store it for repeated application.
=#

#=
One common saddle point system is

$$A = \begin{bmatrix} L & R \\ R^T & 0 \end{bmatrix}$$

where $L$ is the discrete Laplacian and $R$ and $R^T$ are the regularization
and interpolation operators ([`regularize!`](@ref) and [`interpolate!`](@ref)),
respectively. This system arises in the solution of the Poisson equation
with Dirichlet boundary conditions on the immersed surface. The Schur complement of this is
$S = - R^T L^{-1} R$. This matrix can be obtained using the function [`create_RTLinvR`](@ref).
=#

#=
Another common saddle point system is

$$A = \begin{bmatrix} L & D_s \\ G_s & R_n^T R_n \end{bmatrix}$$

where $D_s$ and $G_s$ are the surface divergence and gradient operators
([`surface_divergence!`](@ref) and [`surface_grad!`](@ref)),
respectively, and $R_n$ and $R_n^T$ are [`regularize_normal!`](@ref) and [`normal_interpolate!`](@ref).
This system arises in the solution of the Poisson equation
with Neumann boundary conditions on the immersed surface. The Schur complement of this is
$S = R_n^T R_n - G_s L^{-1} D_s$. Each of the matrices in this are individually
provided by the package, by the functions [`create_nRTRn`](@ref) and [`create_GLinvD`](@ref),
respectively. However, it is useful to know that the sum of these two
matrices is exactly the matrix $-C_s L^{-1}C_s^T$, where $C_s$ and $C_s^T$ are
surface curl operators [`surface_curl!`](@ref). This complete matrix is provided by
[`create_CLinvCT`](@ref).
=#

#=
Another helpful matrix operator is the surface filter, given by

$$\tilde{R}^T R$$

where $\tilde{R}^T$ is a modified form of the interpolation operator,
designed to return the regularized field to the surface points while
maintaining the integral value of the original field [^1]. We can
obtain this matrix with [`create_surface_filter`](@ref).
=#

#md # ## Matrix construction functions

#md # ```@docs
#md # create_RTLinvR
#md # create_CLinvCT
#md # create_CL2invCT
#md # create_GLinvD
#md # create_GLinvD_cross
#md # create_nRTRn
#md # create_surface_filter
#md # ```

#md # [^1]: Goza, A., et al., (2016) "Accurate computation of surface stresses and forces with immersed boundary methods," J. Comput. Phys., 321, 860--873.


""" Computing with permutations: the computer algebra of the symmetric group.
"""
module Permutations
export decompose_permutation_by_bubble_sort!,
  decompose_permutation_by_insertion_sort!, permutation_to_expr

using Compat
using ...Syntax
using ...Theories: dom, codom, compose, id, otimes, munit, braid

# Decomposition
###############

""" Decompose permutation into adjacent transpositions using bubble sort.

An *adjacent transposition*, also known as a *simple transposition*, is a
transposition of form (i i+1), represented here as simply the number i.

This algorithm appears as Algorithm 2.7 in the PhD thesis of Jonathan Huang,
"Probabilistic reasonsing and learning on permutations: Exploiting structural
decompositions of the symmetric group". As Huang notes, the algorithm is
very similar to the well-known bubble sort. It has quadratic complexity.

See also: `decompose_permutation_by_insertion_sort!`
"""
function decompose_permutation_by_bubble_sort!(σ::Vector{Int})::Vector{Int}
  n = length(σ)
  result = Int[]
  for i in 1:n-1
    for j = n-1:-1:i
      if σ[j+1] < σ[j]
        σ[j], σ[j+1] = σ[j+1], σ[j]
        push!(result, j)
      end
    end
  end
  result
end

""" Decompose permutation into adjacent transpositions using insertion sort.

An *adjacent transposition*, also known as a *simple transposition*, is a
transposition of form (i i+1), represented here as simply the number i.

Bubble sort and insertion sort are, in a sense, dual algorithms (Knuth, TAOCP,
Vol 3: Searching and Sort, Sec 5.3.4: Networks for sorting, Figures 45 & 46). A
minimal example on which they give different decompositions is the permutation:

  [1,2,3] ↦ [3,2,1]

See also: `decompose_permutation_by_bubble_sort!`
"""
function decompose_permutation_by_insertion_sort!(σ::Vector{Int})::Vector{Int}
  n = length(σ)
  result = Int[]
  for i in 2:n
    for j in i:-1:2
      if σ[j-1] > σ[j]
        σ[j-1], σ[j] = σ[j], σ[j-1]
        push!(result, j-1)
      end
    end
  end
  result
end

# Conversion to expression
##########################

""" Convert a typed permutation into a morphism expression.

Warning: The morphism expression is not simplified.
"""
function permutation_to_expr(σ::Vector{Int}, xs::Vector; sort::Symbol=:insertion)
  permutation_to_expr!(copy(σ), copy(xs); sort=sort)
end
function permutation_to_expr!(σ::Vector{Int}, xs::Vector; sort::Symbol=:insertion)
  n = length(σ)
  @assert length(xs) == n

  transpositions = if sort == :bubble
    decompose_permutation_by_bubble_sort!(σ)
  elseif sort == :insertion
    decompose_permutation_by_insertion_sort!(σ)
  else
    error("Sorting algorithm not supported: $sort")
  end
  if isempty(transpositions)
    return id(otimes(xs))
  end

  layers = map(transpositions) do τ
    layer = [
      τ > 1 ? id(otimes(xs[1:τ-1])) : nothing,
      braid(xs[τ], xs[τ+1]),
      τ+1 < n ? id(otimes(xs[τ+2:n])) : nothing,
    ]
    xs[τ], xs[τ+1] = xs[τ+1], xs[τ]
    foldl(otimes, filter(!isnothing, layer))
  end
  foldl(compose, layers)
end

end


# I took this from the ConjugatePriors package

# Used "Conjugate Bayesian analysis of the Gaussian distribution" by Murphy as
# a reference.  Note that there were some typos in that document so the code
# here may not correspond exactly.

struct NormalInverseGamma{T<:Real} <: ContinuousUnivariateDistribution
    mu::T
    v0::T     # scales variance of Normal
    shape::T
    scale::T

    function NormalInverseGamma{T}(mu::T, v0::T, sh::T, r::T) where T<:Real
    	v0 > zero(v0) && sh > zero(sh) && r > zero(r) || error("Both shape and scale must be positive")
    	new{T}(T(mu), T(v0), T(sh), T(r))
    end
end

function NormalInverseGamma(mu::Real, v0::Real, sh::Real, r::Real)
    T = promote_type(typeof(mu), typeof(v0), typeof(sh), typeof(r))
    return NormalInverseGamma{T}(T(mu),T(v0),T(sh),T(r))
end

mu(d::NormalInverseGamma) = d.mu
v0(d::NormalInverseGamma) = d.v0
shape(d::NormalInverseGamma) = d.shape
scale(d::NormalInverseGamma) = d.scale
rate(d::NormalInverseGamma) = 1. / d.scale

insupport(::Type{NormalInverseGamma}, x::T, sig2::T) where T<:Real =
    isfinite(x) && zero(sig2) <= sig2 < Inf

# Probably should guard agains dividing by and taking the log of 0.

function pdf(d::NormalInverseGamma, x::T, sig2::T) where T<:Real
    Zinv = d.scale.^d.shape / gamma(d.shape) / sqrt(d.v0 * 2.0*pi)
    return Zinv * 1.0/(sqrt(sig2)*sig2.^(d.shape+1.0)) * exp(-d.scale/sig2 - 0.5/(sig2*d.v0)*(x-d.mu).^2)
end

function logpdf(d::NormalInverseGamma, x::T, sig2::T) where T<:Real
    lZinv = d.shape*log(d.scale) - lgamma(d.shape) - 0.5*(log(d.v0) + log(2pi))
    return lZinv - 0.5*log(sig2) - (d.shape+1.)*log(sig2) - d.scale/sig2 - 0.5/(sig2*d.v0)*(x-d.mu).^2
end

function rand(d::NormalInverseGamma)
    # Guard against invalid precisions
    sig2 = rand(InverseGamma(d.shape, d.scale))
    if sig2 <= zero(Float64)
        sig2 = eps(Float64)
    end
    mu = rand(Normal(d.mu, sqrt(sig2*d.v0)))
    return mu, sig2
end


#=
P26 (**) Generate the combinations of K distinct objects chosen from the N elements of a list
In how many ways can a committee of 3 be chosen from a group of 12 people? We all know that there are C(12,3) = 220 possibilities (C(N,K) denotes the well-known binomial coefficients). For pure mathematicians, this result may be great. But we want to really generate all the possibilities in a list.

Example:
* (combination 3 '(a b c d e f))
((A B C) (A B D) (A B E) ... )
=#

function filter_colls(colls, len)
    filter(coll -> length(coll) == len, colls)
end

function append_indices(indices, last_index)
    next_index = indices[end] + 1
    map(i -> vcat(indices, i), next_index:last_index)
end

function combination(n, coll)
    function _combination(index_list, count)
        if count == n
            index_list
        else
            new_index_list = map(indices -> append_indices(indices, length(coll)), index_list)
            flattened = reduce(vcat, new_index_list)
            _combination(filter_colls(flattened, count + 1), count + 1)
        end
    end

    index_list = _combination(map(idx -> [idx], 1:length(coll)), 1)
    map(indices -> map(i -> coll[i], indices), index_list)
end

coll = [:a, :b, :c, :d, :e, :f]
combos = combination(3, coll)
@assert length(combos) == 20
@assert combos[1] == [:a, :b, :c]
@assert combos[2] == [:a, :b, :d]
@assert combos[3] == [:a, :b, :e]
@assert combos[4] == [:a, :b, :f]
@assert combos[5] == [:a, :c, :d]
@assert combos[6] == [:a, :c, :e]
@assert combos[7] == [:a, :c, :f]
@assert combos[8] == [:a, :d, :e]
@assert combos[9] == [:a, :d, :f]
@assert combos[10] == [:a, :e, :f]
@assert combos[11] == [:b, :c, :d]
@assert combos[12] == [:b, :c, :e]
@assert combos[13] == [:b, :c, :f]
@assert combos[14] == [:b, :d, :e]
@assert combos[15] == [:b, :d, :f]
@assert combos[16] == [:b, :e, :f]
@assert combos[17] == [:c, :d, :e]
@assert combos[18] == [:c, :d, :f]
@assert combos[19] == [:c, :e, :f]
@assert combos[20] == [:d, :e, :f]

println("Tests passed: JL-26.jl")


function fresnelS0(x::T) where {T}
    x2 = x*x
    x4 = x2*x2
    n = @evalpoly(x4, T(3.18016297876567817986E11),
                      -T(4.42979518059697779103E10),
                      T(2.54890880573376359104E9),
                      -T(6.29741486205862506537E7),
                      T(7.08840045257738576863E5),
                      -T(2.99181919401019853726E3))
    d = @evalpoly(x4, T(6.07366389490084639049E11),
                      T(2.24411795645340920940E10),
                      T(4.19320245898111231129E8),
                      T(5.17343888770096400730E6),
                      T(4.55847810806532581675E4),
                      T(2.81376268889994315696E2),
                      T(1))
    x*x2*n/d
end
function fresnelC0(x::T) where {T}
    x2 = x*x
    x4 = x2*x2
    n = @evalpoly(x4, T(9.99999999999999998822E-1),
                      -T(2.05525900955013891793E-1),
                      T(1.88843319396703850064E-2),
                      -T(6.45191435683965050962E-4),
                      T(9.50428062829859605134E-6),
                      -T(4.98843114573573548651E-8))
    d = @evalpoly(x4, T(1.00000000000000000118E0),
                      T(4.12142090722199792936E-2),
                      T(8.68029542941784300606E-4),
                      T(1.22262789024179030997E-5),
                      T(1.25001862479598821474E-7),
                      T(9.15439215774657478799E-10),
                      T(3.99982968972495980367E-12))
    x*n/d
end
fresnelS(x::T) where {T} = x < T(1.6) ? fresnelS0(x) : imag((1 + im)*erf(x*sqrt(T(pi))*(1 - im)/2)/2)
fresnelC(x::T) where {T} = x < T(1.6) ? fresnelC0(x) : real((1 + im)*erf(x*sqrt(T(pi))*(1 - im)/2)/2)


using QuDirac

# This file is an example of a discrete-time quantum random walk
# implemented using QuDirac.

# First, we define the Hadamard operator `H`, defined as
#
# H | 0 ⟩ = 1/√2 * ( | 0 ⟩ + | 1 ⟩ )
# H | 1 ⟩ = 1/√2 * ( | 0 ⟩ - | 1 ⟩ )
#
@rep_op " H | n > = 1/√2 * ( | 0 > + (-1)^n * | 1 > )" 0:1

# If an operation on a state only alters basis labels, not coefficients,
# it can be simpler and more efficient to use `maplabels` than to construct
# a whole new operator.
#
# In this case, we make a function `shift_ket`
# that maps over the basis labels of the input Ket such that:
#
# shift_ket(| 0, j ⟩) -> | 0, j - 1 ⟩
# shift_ket(| 1, j ⟩) -> | 1, j + 1 ⟩
#
function shift_map(label::StateLabel)
    if label[1] == 0
        return StateLabel(0, label[2] - 1)
    else
        return StateLabel(1, label[2] + 1)
    end
end

shift_ket(kt::Ket) = maplabels(shift_map, kt)

function walk_nsteps(steps)
    steps += 1

    # Allocate space for the results
    results = Array(Ket{KroneckerDelta, 2, Float64}, steps);

    # Initial state
    results[1] = d" 1.0 * | 0,0 > "

    # Calculate the each step by referring to the previous step
    for i=2:steps
        results[i] = shift_ket(act_on(H, results[i-1], 1))
    end

    return results
end

println("""
This example provides the `walk_nsteps` function, which takes in the
number of steps to walk and returns an array containing the result of
each step. The initial state is | 0,0 ⟩.""")


lines = readlines(ARGS[1])
vals = [parse(Int, x) for x in split(lines[1], ",")]

function solution1(vals)
    min_val = min(vals...)
    max_val = max(vals...)

    target = nothing
    for candidate = min_val:max_val
        total_fuel = sum([abs(x - candidate) for x in vals])
        if isnothing(target)
            target = total_fuel
        elseif total_fuel < target
            target = total_fuel
        end
    end
    return target
end

function fuel_burnt(val1, val2)
    return sum([x for x = 1:abs(val1 - val2)])
end

function solution2(vals)
    min_val = min(vals...)
    max_val = max(vals...)

    target = nothing
    for candidate = min_val:max_val
        total_fuel = sum([fuel_burnt(x, candidate) for x in vals])
        if isnothing(target)
            target = total_fuel
        elseif total_fuel < target
            target = total_fuel
        end
    end
    return target
end

println(solution1(vals))
println(solution2(vals))


"""
    modularity(g, c, distmx=weights(g), γ=1.0)

Return a value representing Newman's modularity `Q` for the undirected and
directed graph `g` given the partitioning vector `c`. This method also supports
weighted graphs if the distance matrix is provided.

Modularity ``Q`` for undirected graph:
```math
Q = \\frac{1}{2m} \\sum_{c} \\left( e_{c} - \\gamma \\frac{K_c^2}{2m} \\right)
```

Modularity ``Q`` for directed graph:
```math
Q = \\frac{1}{m} \\sum_{c} \\left( e_{c} - \\gamma \\frac{K_c^{in} K_c^{out}}{m} \\right)
```

where:
- ``m``: total number of edges in the network
- ``e_c``: number of edges in community ``c``
- ``K_c``: sum of the degrees of the nodes in community ``c`` or the
  sum of the weighted degree of the nodes in community ``c`` when the graph is
  weighted. ``K_c^{in}`` sum of the in-degrees of the nodes in community ``c``.

### Optional Arguments
- `distmx=weights(g)`: distance matrix for weighted graphs
- `γ=1.0`: where `γ > 0` is a resolution parameter. When the modularity is used
  to find communities structure in networks (i.e with [Louvain's method for
  community detection](https://en.wikipedia.org/wiki/Louvain_Modularity)),
  higher resolutions lead to more communities, while lower resolutions lead to
  fewer communities. Where `γ=1.0` it lead to the traditional definition of
  the modularity.

### References
- M. E. J. Newman and M. Girvan. "Finding and evaluating community structure in networks".
  Phys. Rev. E 69, 026113 (2004). [(arXiv)](https://arxiv.org/abs/cond-mat/0308217)
- J. Reichardt and S. Bornholdt. "Statistical mechanics of community detection".
  Phys. Rev. E 74, 016110 (2006). [(arXiv)](https://arxiv.org/abs/cond-mat/0603718)
- E. A. Leicht and M. E. J. Newman. "Community structure in directed networks".
  Physical Review Letter, 100:118703, (2008). [(arXiv)](https://arxiv.org/pdf/0709.4500.pdf)

# Examples
```jldoctest
julia> using LightGraphs

julia> barbell = blockdiag(complete_graph(3), complete_graph(3));

julia> add_edge!(barbell, 1, 4);

julia> modularity(barbell, [1, 1, 1, 2, 2, 2])
0.35714285714285715

julia> modularity(barbell, [1, 1, 1, 2, 2, 2], γ=0.5)
0.6071428571428571

julia> using SimpleWeightedGraphs

julia> triangle = SimpleWeightedGraph(3);

julia> add_edge!(triangle, 1, 2, 1);

julia> add_edge!(triangle, 2, 3, 1);

julia> add_edge!(triangle, 3, 1, 1);

julia> barbell = blockdiag(triangle, triangle);

julia> add_edge!(barbell, 1, 4, 5); # this edge has a weight of 5

julia> modularity(barbell, [1, 1, 1, 2, 2, 2])
0.045454545454545456
```
"""
function modularity(
    g::AbstractGraph,
    c::AbstractVector{<:Integer};
    distmx::AbstractArray{<:Number}=weights(g),
    γ=1.0
    )

    m = sum([distmx[src(e), dst(e)] for e in edges(g)])
    m = is_directed(g) ? m : 2 * m

    m == 0 && return 0.
    nc = maximum(c)
    kin = zeros(Float32, nc)
    kout = zeros(Float32, nc)
    Q = 0.0
    for u in vertices(g)
        for v in neighbors(g, u)
            c1 = c[u]
            c2 = c[v]
            if c1 == c2
                Q += distmx[u,v]
            end
            kout[c1] += distmx[u,v]
            kin[c2] += distmx[u,v]
        end
    end
    Q = Q * m
    @inbounds for i = 1:nc
        Q -= γ * kin[i] * kout[i]
    end
    return Q / m^2
end


"""
複数の変数に対して，任意の q 値に対するパーセンタイル値を求めるには，以下のような関数を定義すればよい。

missing を除去して計算される。
"""

function quantile2(df, qs=[0, 0.25, 0.5, 0.75, 1])
    Names =["q$(Int(100q))" for q in qs]
    Names = replace(Names, "q0" => "min", "q50" => "median", "q100" => "max")
    qtile = map(x -> quantile(skipmissing(x), qs), eachcol(df))
    df2 = DataFrame(Matrix(reshape(vcat(qtile...), length(qs), :)'), Names)
    n = map(x -> length(filter(!ismissing, x)), eachcol(df))
    insertcols!(df2, 1, :variable => names(df), :n => n)
    df2
end

"""
使用例

julia> using RDatasets, Statistics

julia> iris = dataset("datasets", "iris");

julia> quantile2(iris[:, 1:4])
4×7 DataFrame
 Row │ variable     n      min      q25      median   q75      max
     │ String       Int64  Float64  Float64  Float64  Float64  Float64
─────┼─────────────────────────────────────────────────────────────────
   1 │ SepalLength    150      4.3      5.1     5.8       6.4      7.9
   2 │ SepalWidth     150      2.0      2.8     3.0       3.3      4.4
   3 │ PetalLength    150      1.0      1.6     4.35      5.1      6.9
   4 │ PetalWidth     150      0.1      0.3     1.3       1.8      2.5
"""


using JuMP, VariationalInequality

# https://cdr.lib.unc.edu/indexablecontent/uuid:778ca632-74ca-4858-8c3c-6dcfc7e6e703
# Example 4.2. This example is adapted from the example in (Fukushima, 1986). Let
m = VIPModel()
@variable(m, x[1:3])

@constraint(m, x[1]^2 + 0.4x[2]^2 + 0.6x[3]^2 <= 1)
@constraint(m, 0.6x[1]^2 + 0.4x[2]^2 + x[3]^2 <= 1)
@constraint(m, x[1] + x[2] + x[3] >= sqrt(3))

@mapping(m, F1, 2x[1] + 0.2x[1]^3 - 0.5x[2] + 0.1x[3] - 4)
@mapping(m, F2, -0.5x[1] + x[2] + 0.1x[2]^3 + 0.5)
@mapping(m, F3, 0.5x[1] - 0.2x[2] + 2x[3] - 0.5)

@innerproduct(m, F1, x[1])
@innerproduct(m, F2, x[2])
@innerproduct(m, F3, x[3])
# or
# @innerproduct(m, [F1, F2, F3], x)

solveVIP(m, algorithm=:fixed_point, max_iter=1000, step_size=0.1)
sol1, Fval1, gap1 = save_solution(m)
@assert 0<= gap1 < 1e-6

# The above `solveVIP` sets the value of variables at the solution
clear_values(m)

solveVIP(m, algorithm=:extra_gradient, max_iter=1000, step_size=0.1)
sol2, Fval2, gap2 = save_solution(m)
@assert 0<= gap2 < 1e-6

# sol2, Fval2, gap2 = solveVIP(m, algorithm=:hyperplane, max_iter=1000, step_size=0.1)

@show sol1
@show sol2
@show gap1
@show gap2

# OKAY.
# x∗ = (0.9168, 0.4850, 0.3303)


export Mesh

"""
    `Mesh(args)`

Constructs a mesh of collocation points and associated Fourier modes.

# Arguments
Can be either
- `xmin , xmax , N `; or
- `L , N ` (same as above with `xmin=-L` and `xmax=L`); or
- `param :: NamedTuple`, param contains `N` and `L`, then same as above; or
- `x` a vector of regularly spaced collocation points`.

The mesh as `N` collocation points regularly spaced between `xmin` (included) and `xmax` (excluded)

# Return values
`m=Mesh(args)` is of parametric type and offers
with
- `m.N `: number of collocation points and Fourier modes;
- `m.xmin`: minimum of the mesh;
- `m.xmax`: maximum of the mesh;
- `m.dx`: distance between two collocation points;
- `m.x`: the vector of collocation points;
- `m.kmin`: minimum of Fourier modes;
- `m.kmax`: maximum of Fourier modes;
- `m.dk`: distance between two Fourier modes;
- `m.k`: the vector of Fourier modes.

"""
struct Mesh

    N    :: Int64
    xmin :: Float64
    xmax :: Float64
    dx   :: Float64
    x    :: Vector{Float64}
    kmin :: Float64
    kmax :: Float64
    dk   :: Float64
    k    :: Vector{Float64}

    function Mesh( xmin , xmax , N :: Int64)

        dx   = (xmax-xmin)/N
        x    = zeros(Float64, N)
        x   .= range(xmin, stop=xmax, length=N+1)[1:end-1]
        dk   = 2π/(N*dx)
        kmin = -N/2*dk
        kmax = (N/2-1)*dk
        k    = zeros(Float64, N)
        k   .= dk .* vcat(0:(N-1)÷2, -N÷2:-1)

        new( N, xmin, xmax, dx, x, kmin, kmax, dk, k)

    end

    function Mesh(L , N :: Int64)

        xmin = - L
        xmax =   L
        N    =   N

        Mesh( xmin, xmax, N)

    end

    function Mesh( x )

        N    =   length(x)
        dx   =   (x[end]-x[1])/(N-1)
        xmin = x[1]
        xmax = x[end]+dx

        Mesh( xmin, xmax, N)

    end

    function Mesh(param :: NamedTuple)

        if :xmin in keys(param) && :xmax in keys(param)
            xmin = param.xmin
            xmax = param.xmax
        else
            xmin = - param.L
            xmax =   param.L
        end
        N    =   param.N

        Mesh( xmin, xmax, N)

    end
end


##############################################################################
#
# Symmetric triangular distribution from Distributions Handbook
#
##############################################################################

immutable Triangular <: ContinuousUnivariateDistribution
    location::Float64
    scale::Float64
    function Triangular(l::Real, s::Real)
        s > zero(s) || error("scale must be positive")
        new(float64(l), float64(s))
    end
end

Triangular(location::Real) = Triangular(location, 1.0)
Triangular() = Triangular(0.0, 1.0)

function cdf(d::Triangular, x::Real)
    a, b, c = d.location - d.scale, d.location + d.scale, d.location
    if x <= a
        return 0.0
    elseif a <= x <= c
        return (x - a)^2 / ((b - a) * (c - a))
    elseif c < x <= b
        return 1.0 - (b - x)^2 / ((b - a) * (b - c))
    else
        return 1.0
    end
end

entropy(d::Triangular) = 0.5 + log(d.scale)

function insupport(d::Triangular, x::Number)
    return isreal(x) && isfinite(x) &&
           d.location - d.scale <= x <= d.location + d.scale
end

kurtosis(d::Triangular) = -0.6

mean(d::Triangular) = d.location

median(d::Triangular) = d.location

mode(d::Triangular) = d.location
modes(d::Triangular) = [d.location]

function pdf(d::Triangular, x::Real)
    if insupport(d, x)
        return -abs(x - d.location) / (d.scale^2) + 1.0 / d.scale
    else
        return 0.0
    end
end

function quantile(d::Triangular, p::Real)
    a, b, c = d.location - d.scale, d.location + d.scale, d.location
    if p <= 0.0
        return a
    elseif p < 0.5
        return a + sqrt(p * 2.0 * d.scale^2)
    elseif p >= 0.5
        return b -  sqrt((1.0 - p) * 2.0 * d.scale^2)
    else
        return b
    end
end

function rand(d::Triangular)
    ξ1, ξ2 = rand(), rand()
    return d.location + (ξ1 - ξ2) * d.scale
end

function skewness(d::Triangular)
    a = d.location - d.scale
    b = d.location + d.scale
    c = (b - a) / 2 + a
    den = sqrt(2.0) * (a + b - 2.0 * c) *
                      (2.0 * a - b - c) *
                      (a - 2.0 * b + c)
    num = 5.0 * (a^2 + b^2 + c^2 - a * b - a * c - b * c)^1.5
    return den / num
end

var(d::Triangular) = d.scale^2 / 6.0