export linear_region, linear_regions, estimate_boxsizes, linreg
export boxcounting_dim, capacity_dim, generalized_dim,
information_dim, estimate_boxsizes, kaplanyorke_dim
#####################################################################################
# Functions and methods to deduce linear scaling regions
#####################################################################################
using Statistics
using Statistics: covm, varm
# The following function comes from a version in StatsBase that is now deleted
# StatsBase is copyrighted under the MIT License with
# Copyright (c) 2012-2016: Dahua Lin, Simon Byrne, Andreas Noack, Douglas Bates,
# John Myles White, Simon Kornblith, and other contributors.
"""
    linreg(x, y) -> a, b
Perform a linear regression to find the best coefficients so that the curve:
`y = a + b*x` has the least squared error.
"""
function linreg(x::AbstractVector, y::AbstractVector)
    # Least squares given
    # Y = a + b*X
    # where
    # b = cov(X, Y)/var(X)
    # a = mean(Y) - b*mean(X)
    if size(x) != size(y)
        throw(DimensionMismatch("x has size $(size(x)) and y has size $(size(y)), " *
            "but these must be the same size"))
    end
    mx = Statistics.mean(x)
    my = Statistics.mean(y)
    # don't need to worry about the scaling (n vs n - 1)
    # since they cancel in the ratio
    b = covm(x, mx, y, my)/varm(x, mx)
    a = my - b*mx
    return a, b
end

slope(x, y) = linreg(x, y)[2]

"""
    linear_region(x, y; dxi::Int = 1, tol = 0.2) -> ([ind1, ind2], slope)
Call [`linear_regions`](@ref), identify the largest linear region
and approximate the slope of the entire region using `linreg`.
Return the indices where
the region starts and stops (`x[ind1:ind2]`) as well as the approximated slope.
"""
function linear_region(x::AbstractVector, y::AbstractVector;
    dxi::Int = 1, tol::Real = 0.2)

    # Find biggest linear region:
    reg_ind = max_linear_region(linear_regions(x,y; dxi=dxi, tol=tol)...)
    # least squares fit:
    xfit = view(x, reg_ind[1]:reg_ind[2])
    yfit = view(y, reg_ind[1]:reg_ind[2])
    approx_tang = slope(xfit, yfit)
    return reg_ind, approx_tang
end

"""
    max_linear_region(lrs::Vector{Int}, tangents::Vector{Float64})
Find the biggest linear region and return it.
"""
function max_linear_region(lrs::Vector{Int}, tangents::Vector{Float64})
    dis = 0
    tagind = 0
    for i in 1:length(lrs)-1
        if lrs[i+1] - lrs[i] > dis
            dis = lrs[i+1] - lrs[i]
            tagind = i
        end
    end
    return [lrs[tagind], lrs[tagind+1]]
end

"""
    linear_regions(x, y; dxi::Int = 1, tol = 0.2) -> (lrs, tangents)
Identify regions where the curve `y(x)` is linear, by scanning the
`x`-axis every `dxi` indices (e.g. at `x[1] to x[5], x[5] to x[10], x[10] to x[15]`
and so on if `dxi=5`).

If the slope (calculated using `LsqFit`) of a region of width `dxi` is
approximatelly equal to that of the previous region,
within tolerance `tol`,
then these two regions belong to the same linear region.

Return the indices of `x` that correspond to linear regions, `lrs`,
and the approximated `tangents` at each region. `lrs` is a vector of `Int`.
Notice that `tangents` is _not_ accurate: it is not recomputed at every step,
but only when its error exceeds the tolerance `tol`! Use [`linear_region`](@ref)
to obtain a correct estimate for the slope of the largest linear region.
"""
function linear_regions(x::AbstractVector, y::AbstractVector;
    dxi::Int = 1, tol::Real = 0.2)

    maxit = length(x) ÷ dxi

    tangents = Float64[slope(view(x, 1:max(dxi, 2)), view(y, 1:max(dxi, 2)))]

    prevtang = tangents[1]
    lrs = Int[1] #start of first linear region is always 1
    lastk = 1

    # Start loop over all partitions of `x` into `dxi` intervals:
    for k in 1:maxit-1
        # tang = linreg(view(x, k*dxi:(k+1)*dxi), view(y, k*dxi:(k+1)*dxi))[2]
        tang = slope(view(x, k*dxi:(k+1)*dxi), view(y, k*dxi:(k+1)*dxi))
        if isapprox(tang, prevtang, rtol=tol)
            # Tanget is similar with initial previous one (based on tolerance)
            continue
        else
            # Tangent is not similar.
            # Push new tangent for a new linear region
            push!(tangents, tang)

            # Set the START of a new linear region
            # which is also the END of the previous linear region
            push!(lrs, k*dxi)
            lastk = k
        end

        # Set new previous tangent (only if it was not the same as current)
        prevtang = tang
    end
    push!(lrs, length(x))
    return lrs, tangents
end

#######################################################################################
# Dimensions
#######################################################################################
"""
    estimate_boxsizes(dataset::AbstractDataset; k::Int = 12, z = -1, w = 1)
Return `k` exponentially spaced values: `10 .^ range(lower+w, upper+z, length = k)`.

`lower` is the magnitude of the
minimum pair-wise distance between datapoints while `upper` is the magnitude
of the maximum difference between
greatest and smallest number among each timeseries.

"Magnitude" here stands for order of magnitude, i.e. `round(log10(x))`.
"""
function estimate_boxsizes(data::AbstractDataset{D, T};
    k::Int = 12, z = -1.0, w = 1.0) where {D, T<:Number}

    mi, ma = minmaxima(data)
    upper = round(log10(maximum(ma - mi)))

    mindist = min_pairwise_distance(data)[2]
    lower = ceil(log10(mindist)) # ceil necessary to not use smaller distance.

    if lower ≥ upper
        error(
        "Boxsize estimation failed: `upper` was found ≥ than "*
        "`lower`. Adjust keywords or provide a bigger dataset.")
    end
    if lower + w + 2 ≥ upper + z
        @warn "Boxsizes limits do not differ by 2 orders of magnitude or more. "*
        "Setting `w -= 0.5; z += 0.5`. Please adjust keywords or provide a bigger dataset."
        w -= 0.5; z += 0.5
    end

    return 10.0 .^ range(lower+w, stop = upper+z, length = k)
end

estimate_boxsizes(ts::AbstractMatrix; kwargs...) =
estimate_boxsizes(convert(Dataset, ts); kwargs...)

"""
    generalized_dim(α, dataset [, sizes]) -> D_α
Return the `α` order generalized dimension of the `dataset`, by calculating
the [`genentropy`](@ref) for each `ε ∈ sizes`.

## Description
The returned dimension is approximated by the
(inverse) power law exponent of the scaling of the [`genentropy`](@ref)
versus the box size `ε`, where `ε ∈ sizes`.

Calling this function performs a lot of automated steps:

  1. A vector of box sizes is decided by calling `sizes = estimate_boxsizes(dataset)`,
     if `sizes` is not given.
  2. For each element of `sizes` the appropriate entropy is
     calculated, through `d = genentropy.(α, sizes, dataset)`.
     Let `x = -log.(sizes)`.
  3. The curve `d(x)` is decomposed into linear regions,
     using [`linear_regions`](@ref)`(x, d)`.
  4. The biggest linear region is chosen, and a fit for the slope of that
     region is performed using the function [`linear_region`](@ref).
     This slope is the return value of `generalized_dim`.

By doing these steps one by one yourself, you can adjust the keyword arguments
given to each of these function calls, refining the accuracy of the result.

The following aliases are provided:

  * α = 0 : `boxcounting_dim`, `capacity_dim`
  * α = 1 : `information_dim`
"""
function generalized_dim(α, data::AbstractDataset, sizes = estimate_boxsizes(data))
    dd = genentropy.(α, sizes, Ref(data))
    return linear_region(-log.(sizes), dd)[2]
end
generalized_dim(α, matrix::AbstractMatrix, args...) =
generalized_dim(α, convert(AbstractDataset, matrix), args...)

# Aliases
"capacity_dim(args...) = generalized_dim(0, args...)"
capacity_dim(args...) = generalized_dim(0, args...)
boxcounting_dim = capacity_dim

"information_dim(args...) = generalized_dim(1, args...)"
information_dim(args...) = generalized_dim(1, args...)

"""
    kaplanyorke_dim(lyapunovs::AbstractVector)
Calculate the Kaplan-Yorke dimension, a.k.a. Lyapunov dimension [1].

## Description
The Kaplan-Yorke dimension is simply the point where
`cumsum(lyapunovs)` becomes zero (interpolated). If
the sum of the exponents never becomes negative the function
will return the length of the input vector.

Useful in combination with [`lyapunovs`](@ref).

## References

[1] :  J. Kaplan & J. Yorke,
*Chaotic behavior of multidimensional difference equations*,
Lecture Notes in Mathematics vol. **730**, Springer (1979)
"""
function kaplanyorke_dim(v::AbstractVector)
    issorted(v, rev = true) || throw(ArgumentError(
    "The lyapunov vector must be sorted from most positive to most negative"))

    s = cumsum(v); k = length(v)
    # Find k such that sum(λ_i for i in 1:k) is still possitive
    for i in eachindex(s)
        if s[i] < 0
            k = i-1
            break
        end
    end

    if k == 0
        return zero(v[1])
    elseif k < length(v)
        return k + s[k]/abs(v[k+1])
    else
        return typeof(v[1])(length(v))
    end
end


module nfxp

    # theta is the parameter vector.
    # it contains
    # theta[1] = RC
    # theta[2] = variable cost parameter
    # theta[3:end] = transition probabilities on mileage grid

    using Logging, FreqTables
    using Plots, NLopt , Optim
    using ShowItLikeYouBuildIt, DataFrames, DataFramesMeta
    using JuMP,Ipopt

    pyplot()
    Logging.configure(level=DEBUG)

        # function P = statetransition(p, n)
        #     p=[p; (1-sum(p))];
        #     P=0;
        #     for i=0:numel(p)-1;
        #         P=P+sparse(1:n-i,1+i:n,ones(1,n-i)*p(i+1), n,n);
        #         P(n-i,n)=1-sum(p(1:i));
        #     end
        #     P=sparse(P);

    type Param
        # opts
        max_fxpiter :: Int
        min_cstp    :: Int
        max_cstp    :: Int
        ctol        :: Float64
        rtol        :: Float64
        nstep       :: Int
        ltol0       :: Float64
        tol_vfi     :: Float64
        rtolnk      :: Float64
        hessian     :: Symbol
        converged   :: Bool

        # spaces
        n :: Int  # num of grid points
        max :: Int # upper boudn of mileage grid

        # structural params
        pr :: Vector{Float64}  # transition probs
        RC :: Float64         # replacement cost
        c :: Float64  # cost parameter
        beta :: Float64 # discount factor

        # computation objects
        grid :: LinSpace{Float64}
        P :: SparseMatrixCSC{Float64,Int64}
        EV0 :: Vector{Float64}   # old value function
        EV1 :: Vector{Float64}   # new
        pk :: Vector{Float64}  # probability of keeping engine
        cost :: Vector{Float64}   #operating cost

        """
            create default param type
        """
        function Param(;max_fxpiter = 5,
                        min_cstp = 4,
                        max_cstp = 2000000,
                        ctol = 0.01,
                        rtol = 0.02,
                        nstep = 20,
                        ltol0 = 1.0e-10,
                        tol_vfi = 1.0e-6,
                        rtolnk = 0.5,
                        hessian = :bhhh)
            this = new()
            this.max_fxpiter=max_fxpiter
            this.min_cstp=min_cstp
            this.max_cstp =max_cstp
            this.ctol  =ctol
            this.rtol  =rtol
            this.nstep =nstep
            this.ltol0 =ltol0
            this.tol_vfi =tol_vfi
            this.rtolnk=rtolnk
            this.hessian =hessian
            this.converged = false

            this.n = 175
            this.max = 450
            this.grid = 0.0:(this.n-1)

            this.pr = [0.0937; 0.4475; 0.4459; 0.0127]
            this.RC = 11.7257
            this.c = 2.45569
            this.beta = 0.9999
            this.P = build_trans(this.pr,this.n)
            this.cost = 0.001*this.c*collect(this.grid)
            this.EV0 = zeros(this.n)
            this.EV1 = zeros(this.n)
            this.pk = zeros(this.n)
            return this
        end
    end
    function update!(p::Param,theta::Vector{Float64})
        p.RC = theta[1]
        p.c = theta[2]
        if length(theta) > 2
            p.pr = theta[3:end]
            p.P = build_trans(p.pr,p.n)
        end
        p.cost = 0.001*p.c*collect(p.grid)
        p.EV0[:] = 0.0
        p.EV1[:] = 0.0
        p.pk[:] = 0.0
        p.converged = false
        # p.theta = vcat(p.RC,p.c,p.pr)
    end

    """
        build_trans(p::Vector{Float64},n::Int)

    Build transition matrix for each of `n` states where each element `p[j]` of `p` is the probability of moving `j-1` states up in the grid defined by `1:n`.
    """
    function build_trans(p::Vector{Float64},n::Int)
        P = spzeros(n,n)
        p = vcat(p,(1-sum(p)))
        N = length(p)
        for i=0:(N-1)
            P =   P + sparse(1:(n-i),(1+i):n,ones(n-i)*p[i+1],n,n)
            P[n-i,n] = 1-sum(p[1:i])
        end
        return P
    end

    function read_busses(p::Param,num_types=4)
        # load data
        d = readtable(joinpath(dirname(@__FILE__),"buses.csv"),header=false)
        # setup data
        rename!(d,[:x1,:x2,:x5,:x7,:x9],[:id,:bus_type,:d1,:x,:dx1])

        # subset to bus type
        d = @where(d,:bus_type .<= num_types)

        # discretize odometer data
        d[:x] = ceil(Int,p.n/(p.max*1000) * d[:x])
        d[:dx1] = d[:x] .- vcat(0,d[:x][1:(end-1)])
        d[:dx1] = d[:dx1].*(1-d[:d1]) .+d[:d1].*d[:x]  # replace first diff of x by x if replaced

        # get replacement dummy
        d[:d] = vcat(d[:d1][2:end],0)

        # remove obs with missing lagged mileage
        # this is the first row for each obs
        remove = d[:id] .- vcat(0,d[:id][1:(end-1)])
        d = d[remove.==0,:]

        d = @select(d,:id,:bus_type,:d,:x,:dx1)

        # add a time index
        d = @by(d,:id, t = 1:length(:d),bus_type=:bus_type,d=:d,x=:x,dx1= :dx1 )

        return d
    end

    function bellman!(p::Param,probs=false)
        vk = -p.cost .+ p.beta .* p.EV0  # value of keep at each state
        vr = -p.RC - p.cost[1] + p.beta*p.EV0[1]
        maxv = maximum(vk)
        #recenter expected value function by substracting max value
        p.EV1[:] = p.P * (maxv +  log(exp(vk-maxv) + exp(vr-maxv)))

        # compute choice probs
        if probs
            p.pk[:] = 1.0 ./ (1.0+ (exp(vr-vk)))
        end
    end

    # get Frechet derivative of Bellman operator
    function dbellman(p::Param)
        tmp = p.P[:,2:p.n] .* repmat(p.pk[2:p.n,1]',p.n,1)
        p.beta * hcat((1.0 .- sum(tmp,2)), tmp)
    end
    function dbellman2(p::Param)
        dGamma_dEV = p.beta * (p.P .* repmat(p.pk,1,p.n))
        # println(size(dGamma_dEV))
        dGamma_dEV[:,1] = dGamma_dEV[:,1] .+ p.beta*p.P*(1.0-p.pk)
        # println(size(dGamma_dEV))
        return dGamma_dEV
    end

    update_ev!(p::Param) = copy!(p.EV0,p.EV1);

    """
        VFI(p::Param)

    Standard value function iteration
    """
    function VFI!(p::Param)
        iter = 0
        for j in 1:p.max_cstp  # do max_cstp contraction steps
            iter += 1
            bellman!(p)
            dist_vfi = maxabs(p.EV0.-p.EV1)
            update_ev!(p)
            # p.EV0[:] = p.EV1[:]
            if dist_vfi < p.tol_vfi
                p.converged = true
                info("VFI converged after $iter iterations")
                break
            end
        end
        if maxabs(p.EV0.-p.EV1) > p.tol_vfi
            info("not converged")
        end
        bellman!(p,true)    # get choice probs
        # plot(p.pk)
    end

    function likelihood!(new_theta::Vector{Float64},grad::Vector{Float64},data::DataFrame,p::Param)

        update!(p,new_theta)

        n_c = length(p.c)
        N = length(data[:x])

        # solve model
        VFI!(p)
        fval = 1e10

        # try

            # evaluate log likelihood of replacement decision
            # at each state
            ccps = p.pk[data[:x]]  # get ccp of keep at each state in the data
            log_like = log( ccps .+ (1.0-2.0*ccps) .* (data[:d]))

            # add log likelihood for exogenous mileage increases
            n_p = 0
            pr = Float64[]
            if length(new_theta) > 2
                pr = [p.pr ; 1.0 - sum(p.pr)]
                n_p = length(pr)-1
                # if any(pr[1+data[:dx1]] .< 0)
                #     # println(sum(pr[1+data[:dx1]] .< 0))
                # end
                # idx1 = clamp(data[:dx1],1,maximum(data[:dx1]))
                log_like[:] = log_like[:] .+ log(pr[1+data[:dx1]])
            end
            # println("n_p=$n_p")

            # ojbective function value
            fval = mean(-log_like)

            # save all of this

            # compute gradient of likelihood function

            N = size(data[:x],1)

            if length(grad)>0
                #1. derivative of bellman equation wrt parameters
                dc=0.001*p.grid;    # deriv of cost function
                dtdmp = zeros(p.n,1+n_c+n_p)  # gradient matrix of bellman operator
                dtdmp[:,1] = p.P * p.pk - 1   # wrt RC
                dtdmp[:,2:(1+n_c)] = -(p.P * dc) .*p.pk  # wrt c
                if length(new_theta) > 2   # wrt pr
                    vk = -p.cost + p.beta*p.EV1
                    vr = -p.RC - p.cost[1] + p.beta*p.EV1[1]
                    vmax = max(vk,vr)
                    dtp = vmax + log(exp(vk-vmax)+exp(vr-vmax))

                    for iP in 1:n_p
                        dtdmp[1:(p.n-iP),1+n_c+iP] = dtp[iP:(end-1)] .- vcat(dtp[(n_p+1):p.n],Float64[dtp[end] for i in 1:(n_p-iP)])
                    end
                    invp = exp(-log(pr))
                    invp = vcat(sparse(1:n_p,1:n_p,invp[1:n_p],n_p,n_p),
                               -ones(1,n_p)*invp[n_p+1])
                end
                #2. Derivative of ev wrt parameters:
                F=speye(p.n) - dbellman2(p)
                devdmp = F\dtdmp
                # print("devdmp=$(size(devdmp))")
                # print("devdmp=$(size(devdmp[data[:x],:]))")

                # step 3 get derivative of loglike wrt parameters
                score = (ccps-1 .+ data[:d] ) .* (hcat(-ones(N), dc[data[:x]], zeros(N,n_p) ) .+ (devdmp[ones(Int,N),:] .- devdmp[data[:x],:]))
                if length(new_theta) > 2   # wrt pr
                    # idx1 = clamp(data[:dx1],1,maximum(data[:dx1]))
                    for iP in 1:n_p
                        score[:,1+n_c+iP] = score[:,1+n_c+iP] + invp[1+data[:dx1],iP]
                    end
                end
                grad[:] = mean(-score,1)
            end

        # catch
        #     fval = 1e9
        # end

        # we don't do the hessian.
        # enough is enough. :-)

        info("fval=$fval")

        return fval

    end

    function simdata(N::Int,T::Int,p::Param)
        if !p.converged
            info("solving model by VFI")
            VFI!(p)
        end
        id = reshape(repeat(1:N,inner=1,outer=T),N,T)
        t  = reshape(repeat(1:T,inner=N,outer=1),N,T)
        shock_dx = rand(N,T)
        shock_d = rand(N,T)

        x = zeros(Int,N,T)
        x1 = zeros(Int,N,T)
        x[:,1] = ones(Int,N)
        d = zeros(Bool,N,T)

        # uncontrolled transition on odometer x
        csum_p = cumsum(p.pr) / maximum(cumsum(p.pr))
        dx1 = zeros(Int,N,T)
        for i in 1:length(csum_p)
            dx1 += (shock_dx.>csum_p[i])   # increases are random: notice that you can get dx1 = 0 here. need to add +1 to dx1 to get a valid array index.
        end

        for it in 1:T
            for i in 1:N
                if x[i,it] == 0
                    println(x[i,:])
                    println(d[i,:])
                end
                d[i,it] = shock_d[i,it] < (1.0-p.pk[x[i,it]])
                # make odometer progress
                x1[i,it] = min((1-d[i,it])*x[i,it] + d[i,it] + dx1[i,it] , p.n)   # this adds +1 for example
                if it < T
                    x[i,it+1] = x1[i,it]
                end
            end

        end
        return DataFrame(id = id[:],t = t[:], d = convert(Array{Int},d[:]), x = x[:],x1 = x1[:], dx1 = dx1[:])
    end

    function run_estim()
        p = Param()
        d = read_busses(p,4)  # select all bus types smaller than this number
        e = estimate(d,p)
        return e
    end

    function run_MC()
        p = Param()
        d = simdata(50,113,p)
        e = estimate(d,p)
        return e
    end

    function likelihood_single()
        p = Param()
        d = simdata(50,113,p)
        pr = freqtable(d[:dx1][d[:dx1].>0]) ./ sum(freqtable(d[:dx1][d[:dx1].>0]).array)
        # pr=probs[1:(end-1),:]
        update!(p,vcat(0,0,pr.array...))
        g = zeros(length(vcat(0,0,pr.array...)))
        f = nfxp.likelihood!(vcat(0,0,pr.array...),g,d,p)
    end
    function simulate_single_run()
        p = Param()
        d = simdata(50,113,p)
    end

    function estimate(d::DataFrame,p::Param,startv=zeros(2))
        # step 1: PML for transition probs
        probs = freqtable(d[:dx1][d[:dx1].>0]) ./ sum(freqtable(d[:dx1][d[:dx1].>0]).array)
        pr=probs[1:(end-1),:]

        info("starting values for probs = $(p.pr)")
        info("updated with $pr")
        update!(p,vcat(startv,pr.array...))

        # step 2: estimate structural params
        # f_closure(x,g) = likelihood!(x,g,d,p)
        f_closure(x) = likelihood!(x,Float64[],d,p)
        # opt = Opt(:LD_MMA,length(startv))
        opt = Opt(:LN_COBYLA,length(startv))
        # lower_bounds!(opt,[-Inf,-Inf,[0.0 for i in 1:4]...])
        # upper_bounds!(opt,[Inf,Inf,[1.0 for i in 1:4]...])
        min_objective!(opt,f_closure)
        xtol_rel!(opt,1e-4)
        ftol_rel!(opt,1e-6)
        maxeval!(opt,50)

        res = Optim.optimize(f_closure,startv)

        # inequality_constraint!(opt,myconstraint)

        # (minf,minx,ret) = NLopt.optimize(opt, startv )
        return res
    end

    function MC(p::Param;N=50,T=113)
        d = simdata(N,T,p)

        # estimate pr transition probs

        # put those values into the param type
        update!(p,[p.RC;p.c;probs.array])

        # maximize likelihood
    end

    function myconstraint(x::Vector,gr::Vector)
        if length(gr)>0
            gr[1] = 0
            gr[2] = 0
            gr[3:end] = 1.0
        end
        sum(x[3:end]) - 1.0 # < 0
    end

    function check_like()
        p = Param()
        d = simdata(10000,50,p)
        # pg = [Float64[x,p.theta[2:end]...] for x in linspace(1.0,20,20)]
        y = zeros(20)
        for i in eachindex(pg)
            y[i] = likelihood!(pg[i],zeros(6),d,p)
        end
        return y

    end

    function max_nlopt()
        p = Param()
        d = simdata(10000,13,p)
        # d = simdata(50,113,p)
        f_closure(x,g) = likelihood!(x,g,d,p)
        # opt = Opt(:LN_COBYLA,p.n_params)
        lower_bounds!(opt,[-Inf,-Inf,[0.0 for i in 1:4]...])
        upper_bounds!(opt,[Inf,Inf,[1.0 for i in 1:4]...])
        min_objective!(opt,f_closure)

        inequality_constraint!(opt,myconstraint)

        (minf,minx,ret) = NLopt.optimize(opt, [10.0,0.0001,0.1,0.1,0.1,0.1] )
        # (minf,minx,ret) = NLopt.optimize(opt, p.theta )
    end
    function max_optim()
        p = Param()
        d = simdata(50,113,p)
        f_closure(x) = likelihood!(x,Float64[],d,p)
        lower = [-Inf,-Inf,[0.0 for i in 1:4]...]
        upper = [Inf,Inf,[1.0 for i in 1:4]...]
        res = Optim.optimize(OnceDifferentiable(f_closure),[10.0,0.0001,0.1,0.1,0.1,0.1],lower,upper,Fminbox())
        return res
    end

    function dict_busses(df::DataFrame)
        N,T = (maximum(df[:id]),maximum(df[:t]))
        d = Dict()
        d[:d] = reshape(df[:d].data,N,T)
        d[:x] = reshape(df[:x].data,N,T)
        d[:dx1] = reshape(df[:dx1].data,N,T)
        return d
    end

    function mpec()
        p=Param()
        d = simdata(50,113,p)
        # d = read_busses(p)
        dd = dict_busses(d)
        m = Model(solver=IpoptSolver())
        T = maximum(d[:t])
        n = maximum(d[:id])
        M = 4 # number of slots in state transition: can move 0,1,2,3 slots up
        N = p.n  # number of states
        @variable(m,theta_cost >= 0)
        @variable(m,RC >= 0)
        @variable(m,theta_probs[1:M] >= 0)
        @variable(m,EV[1:N])
        cost = 0.001*theta_cost*collect(p.grid)

        # transformations

        @variable(m,CbEV[1:N])
        @constraint(m,constr_cbev[i=1:N], CbEV[i] == -cost[i] + p.beta*EV[i])
        @variable(m,PayoffDiff[1:N])
        @constraint(m,constr_payoff[i=1:N], PayoffDiff[i]== -CbEV[i] - RC + CbEV[1])
        @NLexpression(m,exp_probkeep[i=1:N],1/(1+exp(PayoffDiff[i])))
        # @variable(m,aux_probkeep[i]==[1:N])
        # @constraint(m,constr_aux_probkeep[i=1:N],aux_probkeep[i]==1.0 / (1.0 + exp_payoffdiff[i]))
        @variable(m,ProbKeep[1:N])
        @NLconstraint(m,const_keep[i=1:N],ProbKeep[i] == exp_probkeep[i])

        # BellmanViolation = sum()

        @NLobjective(m, Max,sum(log( dd[:d][i,it]*(1.0 - ProbKeep[dd[:x][i,it]]) + (1.0 - dd[:d][i,it])*(ProbKeep[dd[:x][i,it]])) for i=1:n, it=2:T) +
            sum( log( theta_probs[dd[:dx1][i,it]+1] ) for i=1:n,it=2:T))

        # bellman equation for states 1:(N-M+1) i.e. where all state progressions are possible
        @NLconstraint(m, constr_EV[i=1:(N-M+1)],
            EV[i] == sum(log(exp(CbEV[i+j]) + exp(-RC + CbEV[1])) * theta_probs[j+1] for j in 0:(M-1))
            )
        # bellman equation for states (N-M+1):(N-1) i.e. where not all state progressions are possible
        @NLconstraint(m, constr_EV_M[i=(N-M+2):(N-1)],
            EV[i] == sum(log(exp(CbEV[i+j]) + exp(-RC + CbEV[1])) * theta_probs[j+1] for j in 0:(N-i-1)) + (1- sum(theta_probs[k+1] for k in 0:(N-i-1))) * log(exp(CbEV[N])   + exp(-RC + CbEV[1])))

        # bellman equation for final state
        @NLconstraint(m, constr_EV_N,
            EV[N] == log(exp(CbEV[N]) + exp(-RC + CbEV[1])))

        # probabilities have to sum to 1
        @constraint(m,sum(theta_probs) == 1)
        # bound value function
        @constraint(m,constr_EV[i=1:N],EV[i] <= 50)

        solve(m)

    end

end  # module


@testset "Testing Support Enumeration" begin

    function NEs_approx_equal(NEs1::Vector{NTuple{2,Vector{T1}}},
                              NEs2::Vector{NTuple{2,Vector{T2}}}) where {T1,T2}
        @test length(NEs1) == length(NEs2)
        @test T1 == T2
        for (actions1, actions2) in zip(NEs1, NEs2)
            for (action1, action2) in zip(actions1, actions2)
                @test action1 ≈ action2
            end
        end
    end

    @testset "test 3 by 2 non-degenerate normal form game(Float)" begin
        g = NormalFormGame(Player([3.0 3.0; 2.0 5.0; 0.0 6.0]),
                           Player([3.0 2.0 3.0; 2.0 6.0 1.0]))
        NEs = [([1.0, 0.0, 0.0], [1.0, 0.0]),
               ([0.8, 0.2, 0.0], [2/3, 1/3]),
               ([0.0, 1/3, 2/3], [1/3, 2/3])]
        NEs_computed = @inferred(support_enumeration(g))

        NEs_approx_equal(NEs_computed, NEs)
    end

    @testset "test 3 by 2 non-degenerate normal form game(Int)" begin
        g = NormalFormGame(Player([3 3; 2 5; 0 6]),
                           Player([3 2 3; 2 6 1]))
        NEs = [([1.0, 0.0, 0.0], [1.0, 0.0]),
               ([0.8, 0.2, 0.0], [2/3, 1/3]),
               ([0.0, 1/3, 2/3], [1/3, 2/3])]
        NEs_computed = @inferred(support_enumeration(g))

        NEs_approx_equal(NEs_computed, NEs)
    end

    @testset "test 3 by 2 non-degenerate normal form game(Rational)" begin
        g = NormalFormGame(Player([3//1 3//1; 2//1 5//1; 0//1 6//1]),
                           Player([3//1 2//1 3//1; 2//1 6//1 1//1]))
        NEs = [([1//1, 0//1, 0//1], [1//1, 0//1]),
               ([4//5, 1//5, 0//1], [2//3, 1//3]),
               ([0//1, 1//3, 2//3], [1//3, 2//3])]
        NEs_computed = @inferred(support_enumeration(g))

        NEs_approx_equal(NEs_computed, NEs)
    end

    @testset "test 3 by 2 degenerate normal form game(Float)" begin
        g = NormalFormGame(Player([1.0 -1.0; -1.0 1.0; 0.0 0.0]),
                           Player([1.0 0.0 0.0; 0.0 0.0 0.0]))
        NEs = [([1.0, 0.0, 0.0], [1.0, 0.0]),
               ([0.0, 1.0, 0.0], [0.0, 1.0])]
        NEs_computed = @inferred(support_enumeration(g))

        NEs_approx_equal(NEs_computed, NEs)
    end

    @testset "test 3 by 2 degenerate normal form game(Int)" begin
        g = NormalFormGame(Player([1 -1; -1 1; 0 0]),
                           Player([1 0 0; 0 0 0]))
        NEs = [([1.0, 0.0, 0.0], [1.0, 0.0]),
               ([0.0, 1.0, 0.0], [0.0, 1.0])]
        NEs_computed = @inferred(support_enumeration(g))

        NEs_approx_equal(NEs_computed, NEs)
    end

    @testset "test 3 by 2 degenerate normal form game(Rational)" begin
        g = NormalFormGame(Player([1//1 -1//1; -1//1 1//1; 0//1 0//1]),
                           Player([1//1 0//1 0//1; 0//1 0//1 0//1]))
        NEs = [([1//1, 0//1, 0//1], [1//1, 0//1]),
               ([0//1, 1//1, 0//1], [0//1, 1//1])]
        NEs_computed = @inferred(support_enumeration(g))

        NEs_approx_equal(NEs_computed, NEs)
    end

end


abstract type Element end
struct FreeSpace <: Element
    L::Real
end
struct Interface <: Element
    η::Real; θ::Real; R::Real # R > 0 when light hits concave side
end
struct ThinLens <: Element
    f::Real; θ::Real
end
ThinLens(f::Real) = ThinLens(f,0)
Interface(η::Real) = Interface(η,0,Inf)

Mirror(R::Real, θ::Real) = ThinLens(R/2,θ)

struct Tan{E} <: Element where {E<:Element}
    e::E
end
struct Sag{E} <: Element where {E<:Element}
    e::E
end
Tan(e::FreeSpace) = e
Sag(e::FreeSpace) = e

"""

RTM(element::Element) returns the Ray Transfer (ABCD) matrix
associated with the given, optical element.

RTM(elements) returns the Ray Transfer (ABCD) matrix associated with
an optical system described by a collection (e.g. a vector or
iteration) of optical elements.

"""
RTM(e::FreeSpace) = [1 e.L ; 0 1]
RTM(e::Interface) = [1 0 ; (e.η-1)/e.R e.η]
RTM(e::ThinLens) = [1 0 ; -1/e.f 1]
RTM(e::Tan{ThinLens}) = (RTM∘ThinLens)(e.e.f*cos(e.e.θ))
RTM(e::Sag{ThinLens}) = (RTM∘ThinLens)(e.e.f/cos(e.e.θ))
# See doi:10.1364/AO.26.000427 for the following matrices
function RTM(e::Tan{Interface})
    θ1, η, R = e.e.θ, e.e.η, e.e.R; θ2 = asin(η*sin(θ1))
    return [cos(θ2)/cos(θ1) 0 ;
        (cos(θ2)-η*cos(θ1))/(R*cos(θ1)*cos(θ2)) η*cos(θ1)/cos(θ2)]
end
function RTM(e::Sag{Interface})
    θ1, η, R = e.e.θ, e.e.η, e.e.R; θ2 = asin(η*sin(θ1))
    return [1 0 ; (cos(θ2)-η*cos(θ1))/R η]
end
RTM(elements) = reduce(*, RTM.(elements))


## Import packages
using StatsBase
using Plots

## Define Functions
function gillespie(transitions, initial, N, select_hyp="none", t0=0.0)

    """
    Implementation of the Gillespie algorithm for Lotka-Volterra model

    Args:
    transitions(dict): A dictionary with keys "prey" and "pred", and each element
        is a list containing the row of the transition matrix.
    initial(dict): A dictionary with keys 'prey' and 'predator' giving the initial
        counts for each species at the start of the sim.
    N(int): Number of desired simulation iterations.
    select_hyp(str): Default is "none", wherein a uniform prior is assigned to the
        three hypotheses.  Otherwise, "H1", "H2", or "H3" concentrates all prior
        probability on one hypothesis (useful in forward simulation).
    t0(float): initial time point.
    """

    trajectory = Dict("prey" => [initial["prey"]], "pred" => [initial["pred"]])
    times = [t0]
    ecology = initial

    theta_hypotheses = Dict(
        "H1" => Dict("spawn_prey" => 1, "prey2pred" => 0.005, "pred_dies" => 0.6),
        "H2" => Dict("spawn_prey" => .9, "prey2pred" => 0.004, "pred_dies" => 0.4),
        "H3" => Dict("spawn_prey" => 1.5, "prey2pred" => 0.01, "pred_dies" => 0.8))

    if select_hyp != "none"
        theta = theta_hypotheses[select_hyp]
    else
        hypoth_list = ["H1", "H2", "H3"]
        weights = [1 - 0.6666, 0.3333, 0.3333]
        theta = theta_hypotheses[sample(hypoth_list, Weights(weights))]
    end

    t = times[1]

    for i = 1:N
        hazards = get_hazards(ecology, theta)
        transition = transitions[sample(collect(keys(hazards)), Weights(collect(values(hazards))))]
        t = t + exp(sum(values(hazards)))

        ecology["prey"] += transition[1]
        ecology["pred"] += transition[2]
        # Enforce only positive integers
        ecology["prey"] = max(0, ecology["prey"])
        ecology["pred"] = max(0, ecology["pred"])

        append!(trajectory["prey"], ecology["prey"])
        append!(trajectory["pred"], ecology["pred"])
        append!(times, t)
    end

    return Dict(
        "prey" => trajectory["prey"],
        "pred" => trajectory["pred"],
        "times" => times
        )
end

function get_hazards(ecology, theta)
    """
    Compute the hazard function given the current states.  Note that in this
        model the function depends only on state, not time, though this is not
        the case in general. "spawn_prey" represents the event of a prey being born,
        "prey2pred" represents a predator consuming a new prey and consequently spawning
        a new predator, "pred_dies" represents the death of a predator.

    args:
        ecology(dict): a dictionary containing species counts.
        theta(dict): A dictionary where keys represent events (reactions) and
            values are the location hyperparameters for rate parameter constants
            corresponding to those events.
    """

    return Dict(
        "spawn_prey" => theta["spawn_prey"] * ecology["prey"],
        "prey2pred" => theta["prey2pred"] * ecology["prey"] * ecology["pred"],
        "pred_dies" => theta["pred_dies"] * ecology["pred"]
        )
end

## Initialize Function
initial = Dict("prey" => 50, "pred" => 100)
Pre = [[1, 0], [1, 1], [0, 1]]
Post = [[2, 0], [0, 2], [0, 0]]
transition_mat = Post - Pre
transitions = Dict("spawn_prey" => transition_mat[1,],
                    "prey2pred" => transition_mat[2,],
                    "pred_dies" => transition_mat[3,])

gillespie_sim = gillespie(transitions, initial, 10000, "H2", 0.0)

plot_vals = hcat(values(gillespie_sim["prey"]), values(gillespie_sim["pred"]))
plot(1:10001, plot_vals,
title = "Non-Omega Simulation",
xlabel = "Time",
ylabel = "Quantity",
label = ["Prey" "Predators"],
lw = 1.25)


using
    MathOptInterface,
    BenchmarkTools,
    Random,
    IterTools,
    GaussianProcesses,
    Plots
    Random;

func(x) = (x[1]-2)^2*x[1]+x[2];

numSamps = 5
numPlot = 20
sampGrid = Vector{Float64}[]
plotGrid = Vector{Float64}[]

for p in product(LinRange(0,3,numSamps), LinRange(0,3,numSamps))
    push!(sampGrid, collect(p))
end

for p in product(LinRange(0,3,numPlot), LinRange(0,3,numPlot))
    push!(plotGrid, collect(p))
end

trueFunc = func.(plotGrid)

mz = MeanZero()
mc = MeanConst(1.)
#ml = MeanLin()
mp = MeanPoly([2. 3.; 1. 2.])
kern = SE(0.0, 0.0)
sampGridT = reduce(hcat, sampGrid)
sampVals = func.(sampGrid)+0.1*randn(numSamps^2)
gp = GP(sampGridT, sampVals, mz, kern)
GaussianProcesses.optimize!(gp)

attemptGrid = zeros(numPlot,numPlot)
for i in 1:length(plotGrid)
    attemptGrid[Int(trunc((i-1)/numPlot))+1, i-Int(trunc((i-1)/numPlot))*numPlot] = predict_f(gp, hcat(plotGrid[i]))[1][1]
end
trueGrid = zeros(numPlot,numPlot)
for i in 1:length(plotGrid)
    trueGrid[Int(trunc((i-1)/numPlot))+1, i-Int(trunc((i-1)/numPlot))*numPlot] = func(plotGrid[i])
end

plot(trueGrid, st=:surface, label = "True Function")
plot!(attemptGrid, st=:surface, label = "GP Attempt")


import Base: size, getindex, *, +, -, ^, \, adjoint, ones
import LinearAlgebra: inv, eigen

# Assuming we work with real matrices...
struct Circulant{T} <: AbstractArray{T, 2}
    co::Vector{T}
    fft::Vector{Complex{T}}
end

Circulant(co) = Circulant(co, fft(co))

size(c::Circulant) = length(c.co), length(c.co)
getindex(c::Circulant, i::Int, j::Int) = c.co[mod(i - j, length(c.co))+1]

function *(a::Circulant, b::Circulant)
    return Circulant(real(ifft!(a.fft  .* b.fft)), a.fft  .* b.fft)
end

function *(a::Circulant, x::Vector)
     return ifft!(a.fft .* fft(x))
end

function *(a::Circulant, x::Vector{T}) where T <: Real
     return real(ifft!(a.fft .* fft(x)))
end

function +(a::Circulant, b::Circulant)
    return Circulant(a.co + b.co, a.fft + b.fft)
end

function -(a::Circulant, b::Circulant)
    return Circulant(a.co - b.co, a.fft - b.fft)
end

function ^(a::Circulant, n::Int)
    return Circulant(real(ifft!(a.fft.^n)), a.fft.^n)
end

function \(a::Circulant, x::Vector{T}) where T <: Real
     return real(ifft!(fft(x) ./ a.fft))
end

function inv(a::Circulant)
     return Circulant( real(ifft!(1 ./ a.fft )), 1 ./ a.fft)
end

function adjoint(a::Circulant)
    c = a.co
    c_new = vcat(c[1], c[end:-1:2])
    return Circulant(c_new)
end

function eigen(a::Circulant)
    λ = a.fft
    n = length(a.co)
    ω = [exp(2*im*π*k*j/n) for j in 0:n-1, k in 1:n]
    return λ, ω
end

function circ_ones(N::Int)
    return Circulant(ones(N))
end

function circ_id(N::Int)
    return Circulant([1, zeros(N-1)...])
end


function mh_sampler(x::Float64, g::Function, sd::Float64, gx::Float64)
  x1 = x + randn()*sd
  gx1 = g(x1)
  if gx1 - gx > log(rand())
    x = x1
    gx = gx1
  end
  return x,gx
end

function mh_sampler(x::Float64, g::Function, sd::Float64)
  gx = g(x)
  mh_sampler(x,g,sd,gx)
end

mh_sampler(x::Float64, g::Function) = mh_sampler(x,g,.5)

# Interface using Density type
mh_sampler(x::Float64, g::Density, sd::Float64, gx::Float64) = mh_sampler(x, Density.f, sd, gx)


#=

# 204: 2D Convection in Hagen-Poiseuille flow
([source code](SOURCE_URL))

Solve the equation

```math
\partial_t u -\nabla ( D \nabla u - v u) = 0
```
in $\Omega=(0,L)\times (0,H)$ with dirichlet boundary conditions
at $x=0$ and outflow boundary condition at $x=L$.
=#

module Example204_HagenPoiseuille
using Printf
using VoronoiFVM
using ExtendableGrids
using GridVisualize

## Bernoulli function used in the exponential fitting discretization
function bernoulli(x)
    if abs(x)<nextfloat(eps(typeof(x)))
        return 1
    end
    return x/(exp(x)-1)
end

function exponential_flux!(f,u0,edge,data)
    u=unknowns(edge,u0)
    vh=project(edge,data.v)
    Bplus= data.D*bernoulli(vh/data.D)
    Bminus=data.D*bernoulli(-vh/data.D)
    f[1]=Bminus*u[1,1]-Bplus*u[1,2]
end

function outflow!(f,u,node,data)
    if node.region==2
        f[1]=data.v[1]*u[1]
    end
end

function main(;nref=0,Plotter=nothing,D=0.01,v=1.0,tend=100,cin=1.0)
    H=1.0
    L=5.0
    grid=simplexgrid(range(0,L,length=20*2^nref),
                     range(0,H,length=5*2^nref))

    function fhp(x,y)
        yh=y/H
        return v*4*yh*(1.0-yh),0
    end

    function flux!(f,u,edge)
        vd=evelo[edge.index]/D
        bp=fbernoulli(vd)
        bm=fbernoulli(-vd)
        f[1]=D*(bp*u[1] - bm*u[2])
    end

    function outflow!(f,u,node)
        if node.region==2
            f[1]=bfvelo[node.ibnode,node.ibface]*u[1]
        end
    end

    ispec=1
    physics=VoronoiFVM.Physics(num_species=1,flux=flux!,breaction=outflow!)
    sys=VoronoiFVM.DenseSystem(grid,physics)
    enable_species!(sys,ispec,[1])
    evelo=edgevelocities(grid,fhp)
    bfvelo=bfacevelocities(grid,fhp)

    boundary_dirichlet!(sys,ispec,4,cin)

    ## Create a solution array
    inival=unknowns(sys,inival=0)

    ## Transient solution of the problem
    control=VoronoiFVM.NewtonControl()
    control.Δt=0.01*2.0^(-nref)
    control.Δt_min=0.01*2.0^(-nref)
    control.Δt_max=0.1*tend
    tsol=solve(inival,sys,[0,tend],control=control)

    vis=GridVisualizer(Plotter=Plotter)
    for i=1:length(tsol.t)
        scalarplot!(vis[1,1],grid,tsol[1,:,i],flimits=(0,cin+1.0e-5))
    end
    tsol
end

function test()
    tsol=main()
    all(tsol[end].≈1)
end

end


qsort(L) = isempty(L) ? L : vcat(qsort(filter(x -> x < L[1], L[2:end])), L[1:1], qsort(filter(x -> x >= L[1], L[2:end])))


"""
    IVLogitShare <: GMMModel

An `IVLogitShare` model consists of outcomes, `y` ∈ (0,1),  regressors
`x` and instruments `z`.  The moment condition is

``E[ (\\log(y/(1-y)) - xβ)z ] = 0``

The dimensions of `x`, `y`, and `z` must be such that
`length(y) == size(x,1) == size(z,1)`
and
`size(x,2) ≤ size(z,2)`.
"""
struct IVLogitShare <: GMMModel
  x::Matrix{Float64}
  y::Vector{Float64}
  z::Matrix{Float64}
end

"""
    IVLogitShare(n::Integer, β::AbstractVector,
                      π::AbstractMatrix, ρ)

Simulate an IVLogitShare model.

# Arguments

- `n` number of observations
- `β` coefficients on `x`
- `π` first stage coefficients `x = z*π + v`
- `ρ` correlation between x[:,1] and structural error.

Returns an IVLogitShare GMMModel.
"""
function IVLogitShare(n::Integer, β::AbstractVector,
                      π::AbstractMatrix, ρ)
  z = randn(n, size(π)[1])
  endo = randn(n, length(β))
  x = z*π .+ endo
  ξ = rand(Normal(0,sqrt((1.0-ρ^2))),n).+endo[:,1]*ρ
  y = cdf.(Logistic(), x*β .+ ξ)
  return(IVLogitShare(x,y,z))
end

number_parameters(model::IVLogitShare) = size(model.x,2)
number_observations(model::IVLogitShare) = length(model.y)
number_moments(model::IVLogitShare) = size(model.z,2)

function get_gi(model::IVLogitShare)
  function(β)
    ξ = quantile.(Logistic(), model.y) .- model.x*β
    ξ.*model.z
  end
end

function gel_jump_problem(model::IVLogitShare)
  n = number_observations(model)
  d = number_parameters(model)
  k = number_moments(model)
  Ty = quantile.(Logistic(),model.y)
  m = Model()
  @variable(m, 0.0 <= p[1:n] <= 1.0)
  @variable(m, θ[1:d])
  @constraint(m, prob,sum(p)==1.0)
  @constraint(m, momentcon[i=1:k], dot((Ty - model.x*θ).*model.z[:,i],p)==0.0)
  @NLobjective(m,Max, sum(log(p[i]) for i in 1:n))
  return(m)
end


using CellularAutomata
using Tk
importall Cairo

function sim_life(init::Array{Int,2},B, S)
    m, n = size(init)
    w = Window("CA", n, m)
    c = Canvas(w)
    pack(c)

#    win = Toplevel("Cellular Automata", 600, 300)
#    f = Frame(win)
#    pack(f, expand = true, fill = "both")
#    c = Canvas(f)
#    pack(c, expand = true, fill = "both")

    done = false
    c.mouse.button1press = (c, x, y)->(done=true)
    cr = getgc(c)

    last = time(); f = 1
    while !done
        img = convert(Array{Uint32, 2}, init) .* 0x00ffffff
        set_source_surface(cr, CairoRGBSurface(img), 0, 0)
        paint(cr)
        reveal(c)

        t = time()
        if (t-last) > 2
            println("$(f/(t-last)) FPS")
            last = t; f = 0
        end
        M = CA2d(B, S, init, 2).cells
        init = int(M[:, :, 2])
        f += 1
        sleep(0.01)
    end
end

#turbine pattern
#init = int(zeros(20,20))
#init[4, 4:9] = 1
#init[5, 4:9] = 1

#init[4:9, 11] = 1
#init[4:9, 12] = 1

#init[11, 7:12] = 1
#init[12, 7:12] = 1

#init[7:12, 4] = 1
#init[7:12, 5] = 1

init = int(zeros(600,600))
init[250:350, 250:350] = int(rand(101,101))
#init[295:305, 295:305] = int(rand(11,11))

#init = int(rand(1000, 1000))

#game of life
sim_life(init,[3], [2,3])

#morley
#sim_life(init,[3,6,8],[2,4,5])

#2x2
#sim_life(init,[3,6],[1,2,5])

#replicator
#sim_life(init,[1,3,5,7], [1,3,5,7])

#dimoeba
#sim_life(init,[3,5,6,7,8], [5,6,7,8])

#no death
#sim_life(init,[3], [0,1,2,3,4,5,6,7,8])

#34 life
#sim_life(init,[3,4],[3,4])


"""
    cartesian_tensor_component(t::Tensor{1}, c)

This returns the Cartesian tensor component `c` (valid choices are
`:x`, `:y`, or `:z`) of the rank-1 tensor `t`, as a linear combination
of its "natural" [`TensorComponent`](@ref)s `1`, `0`, and `-1`. The
transform matrix is `M(+1, 0, -1 ← x, y, z)` given in Table 1.2 on
p. 14 of Varshalovich (1988).

# Examples

```jldoctest
julia> cartesian_tensor_component(Gradient(), :x)
- 0.707107 𝛁̂⁽¹⁾₁ + 0.707107 𝛁̂⁽¹⁾₋₁
```
"""
function cartesian_tensor_component(t::Tensor{1}, c::Symbol)
    if c == :x
        (-TensorComponent(t, 1) + TensorComponent(t, -1))/√2
    elseif c == :y
        (im*TensorComponent(t, 1) + im*TensorComponent(t, -1))/√2
    elseif c == :z
        TensorComponent(t, 0)
    else
        throw(ArgumentError("Unknown Cartesian tensor component $(c)"))
    end
end

export cartesian_tensor_component


# Create design matrix X and response variable y from swiss data array
swiss = readdlm("swiss.txt", ' ');
covariates = swiss[:, 1:end-1];
nData, nPars = size(covariates);

covariates = (bsxfun(-, covariates, mean(covariates, 1))
  ./repmat(std(covariates, 1), nData, 1));

polynomialOrder = 1;
X = zeros(nData, nPars*polynomialOrder);
for i = 1:polynomialOrder
  X[:, ((i-1)*nPars+1):i*nPars] = covariates.^i;
end

y = swiss[:, end];

# Create data dictionary
data = {"X"=>X, "y"=>y, "priorVar"=>100., "nData"=>nData};


using Deconvolution
using Base.Test

srand(42) # Fixed random seed

##### Wiener deconvolution

# Without blurring
x = linspace(0, 10, 15)
s = sinpi(x) - 1.5cos(x)
n = rand(length(s))
@test wiener(s + n, s, 0.5) ≈
    [-1.5300909616077885,-0.4607553796475224,-1.73313390900302,0.8555916473930066,
     2.2692599396286277,0.7989152977213652,1.1431959877178226,-0.8118586562136341,
     -1.6519310564150556,-0.4882369786466372,-1.0387206070662094,
     -0.8324097927723406,1.8208048770862535,0.7427114498907227,1.0587917379150844]

# With blurring
blurring_ft  = exp(-0.001 * (range(-div(length(x), 2), length(x)) .^ 2) .^ (5 // 6))
blurred_s_ft = fftshift(blurring_ft) .* fft(s) + fft(n)
blurred_s    = real(ifft(blurred_s_ft))
blurring     = ifft(fftshift(blurring_ft))
@test wiener(blurred_s, s, 0.5, blurring) ≈
    [-1.5288865581538547,-0.4592713125163481,-1.7360819833552192,0.8525544925861452,
     2.272966823544607,0.8021732396525801,1.14091217864045,-0.8166101187683248,
     -1.6455701546601862,-0.4926325667795202,-1.0323127293402468,
     -0.8380228416032021,1.8214493186523952,0.7447075584686396,1.0566048110361044]


using FermiCG, NPZ, LinearAlgebra, Plots
using Clustering, SpectralClustering
#load integrals from disk
ints = InCoreInts(
    npzread("integrals_h0.npy"),
    npzread("integrals_h1.npy"),
    npzread("integrals_h2.npy")
);
C = npzread("mo_coeffs_act.npy")
S = npzread("overlap_mat.npy");
D = npzread("density_mat.npy");

function cluster_orbitals(A, n_clusters)
    """
    A is adjacency matrix
    """

    L = Diagonal([sum(A[i,:]) for i in 1:size(A,1)]) - A;
    F = eigen(L);
    perm = sortperm(F.values, by=abs)
    F.values .= F.values[perm]
    F.vectors .= F.vectors[:,perm]

    print(" Laplacian Eigenvalues\n")
    for (fi_idx, fi) in enumerate(F.values)
        @printf(" %4i %12.8f\n", fi_idx, fi)
    end

    p1 = plot(F.values)

    A = abs.(F.vectors[:,1:n_clusters]*F.vectors[:,1:n_clusters]')
    p2 = heatmap(abs.(A), aspect_ratio=:equal, title="Projector", yflip = true)

    clustering = clusterize(KMeansClusterizer(n_clusters), A).assignments;

    perm = sortperm(clustering)
    p3 = heatmap(abs.(A[perm,perm]), aspect_ratio=:equal, title="Projector", yflip = true)

    clusters = [ [] for i in 1:n_clusters]
    for (idx,i) in enumerate(clustering)
        push!(clusters[i],idx)
    end

    fig = plot(p1, p2, p3, layout = @layout [a; b c])
    savefig(fig, "clustering_projector.pdf") # save the most recent fig as fn

    return perm, clusters
end

n_clusters = 2
adjacency = abs.(C'*D*C)
perm, cluster_list = cluster_orbitals(adjacency, n_clusters)

p1 = heatmap(adjacency, aspect_ratio=:equal, title="Adjacency", yflip = true)
p2 = heatmap(adjacency[perm, perm], aspect_ratio=:equal, title="Adjacency (sorted)", yflip = true)

fig = plot(p1, p2, layout = @layout [a b])
savefig(fig, "clustering_adjacency.pdf") # save the most recent fig as fn

npzwrite("clustering_perm.npy", perm)


using Bezier
using Base.Test

# linear interpolation
P11 = [0., 1]
P12 = [2., 3]
@test [0,     1] == bezier(0,    P11, P12)
@test [0.5, 1.5] == bezier(0.25, P11, P12)
@test [1,     2] == bezier(0.5,  P11, P12)
@test [1.5, 2.5] == bezier(0.75, P11, P12)
@test [2,     3] == bezier(1,    P11, P12)

P21 = [0. 1]
P22 = [2. 3]
@test [0     1] == bezier(0,    P21, P22)
@test [0.5 1.5] == bezier(0.25, P21, P22)
@test [1     2] == bezier(0.5,  P21, P22)
@test [1.5 2.5] == bezier(0.75, P21, P22)
@test [2     3] == bezier(1,    P21, P22)

# quadratic interpolation with vector of vectors
@test Array[[10, 5]] == bezier(0.5, Vector{Float64}[[0., 0], [10, 10], [20, 0]])

# quadratic interpolation with matrix
@test [[10 5];] == bezier(0.5, [[0. 0]; [10 10]; [20 0]])
@test [[10 5];] == bezier(0.5, [0. 0; 10 10; 20 0])

# boundary checks
@test_throws DomainError bezier(prevfloat(0.), [0., 1], [2., 3])
@test_throws DomainError bezier(nextfloat(1.), [0., 1], [2., 3])
@test_throws DomainError bezier(prevfloat(0.), Vector{Float64}[[0., 1], [2, 3]])
@test_throws DomainError bezier(nextfloat(1.), Vector{Float64}[[0., 1], [2, 3]])
@test_throws DomainError bezier(prevfloat(0.), [0. 1; 2 3])
@test_throws DomainError bezier(nextfloat(1.), [0. 1; 2 3])


# MIT license
# Copyright (c) Microsoft Corporation. All rights reserved.
# See LICENSE in the project root for full license information.

const sin60 = .5*sqrt(3)
const cos60 = .5
"""coordinates of a unit hexagon tile polygon with unit length sides centered about the point (0,0)"""
const hexcoords = [
		1 0;
		cos60 -sin60;
		-cos60 -sin60;
		-1 0;
		-cos60 sin60;
		cos60 sin60
		]

struct HexBasis1{N,T} <: Basis{N,T}
    HexBasis1(::Type{T} = Float64) where{T<:Real} = new{2,T}()
end
export HexBasis1

basis(::HexBasis1{2,T}) where{T} = SMatrix{2,2,T}(T(1.5),T(.5)*sqrt(T(3)),T(1.5),T(-.5)*(sqrt(T(3))))

# SVector{2,SVector{2,T}}(hexe₁(T),hexe₂(T))

"""Returns the vertices of the unit tile polygon for the basis"""
function tilevertices(::HexBasis1{2,T}) where{T}
sin60 = T(.5)*sqrt(T(3))
cos60 = T(.5)
return SMatrix{2,6}(
		1, 0,
		cos60, -sin60,
		-cos60, -sin60,
		-1, 0,
		-cos60, sin60,
		cos60, sin60)
end
export tilevertices

# coordinate offsets to move up, down, etc., numsteps hex cells in a hex lattice defined in HexBasis1
hexup(numsteps = 1) = numsteps .* (1,-1)
hexdown(numsteps = 1) = numsteps .* (-1,1)
hexupright(numsteps = 1) = numsteps .* (1,0)
hexdownleft(numsteps = 1) = numsteps .* (-1,0)
hexdownright(numsteps = 1) = numsteps .* (0,1)
hexupleft(numsteps = 1) = numsteps .* (0,-1)

""" (i,j) offsets of a ring represented in the HexBasis1 lattice basis. The hexagonal grid is assumed to start from the bottom center hex cell. To generate larger rings repeat this pattern
 ring 1 = (1, 0)      (1, -1)       (0, -1)       (-1, 0)       (-1, 1)       (0, 1)
 ring 2 = (1, 0)(1, 0)(1, -1)(1, -1)(0, -1)(0, -1)(-1, 0)(-1, 0)(-1, 1)(-1, 1)(0, 1)(0, 1)
"""
const hexcycle = SVector{6,NTuple{2,Int64}}(hexupright(),hexup(),hexupleft(),hexdownleft(),hexdown(),hexdownright())

"""returns (i,j) offsets of ring n defined in the HexBasis1 lattice basis."""
ringoffsets(n::Int64) = ringoffsets(Val{n})
function ringoffsets(::Type{Val{N}}) where N
    temp = MVector{N*6,NTuple{2,Int64}}(undef)

    for i in 1:6
        for j in 1:N
            temp[(i-1)*N + j] = hexcycle[i]
        end
    end

    return SVector{N*6,NTuple{2,Int64}}(temp)
end
export ringoffsets

"""Returns ```centerpoint``` plus all its neighbors in a ring of size n. This is a hexagonal region of the lattice n times as large as the unit hexagon cell.

Example:

```
Vis.@wrapluxor Vis.drawhexcells(50, hexregion((0,0),2))
```
"""
function region(::Type{HexBasis1},centerpoint::Tuple{Int64,Int64}, n::Int64)
    f(i) = i==0 ? () : (neighbors(HexBasis1,centerpoint,i)...,f(i-1)...)
    cells = (centerpoint...,f(n)...)
    numcells = length(cells) ÷ 2
    println(numcells)
    # convert to matrix form since that is what the drawing code expects
    temp = MMatrix{2,numcells,Int64}(undef)
    for i in 1:numcells
        println(cells[i])
        temp[1,i] = cells[2*i-1]
        temp[2,i] = cells[2*i]
    end

    return SMatrix{2,numcells,Int64}(temp)
end
export region

"""Returns all hex cells contained in the ring of size n centered around centerpoint. Use region if you want all the cells contained in the ring of size n."""
function neighbors(::Type{HexBasis1}, centerpoint::Tuple{Int64,Int64},n::Int) where{T}
    temp = MMatrix{2,n*6,Int64}(undef)
    hoffsets = ringoffsets(Val{n})
    latticepoint = centerpoint .+ n .* (hexdown())

    #convert hexoffsets to absolute coordinate positions
    for i in 1:size(temp)[2]
        temp[1,i] = latticepoint[1]
        temp[2,i] = latticepoint[2]
        latticepoint = latticepoint .+ hoffsets[i] #last computed value of latticepoint won't be used
    end
    return SMatrix{2,n*6,Int64}(temp)
end
export neighbors

function xbounds(numi)
    numevens = div(numi,2)
    numodds = numevens + mod(numi,2)
    basewidth = numodds + 2*numevens + 1

    if iseven(numi)
        maxx = basewidth
    else
        maxx = basewidth + sin(deg2rad(30))
    end

    minx  = -maxx
    return (minx,maxx)
end

function ybounds(numj)
    maxy = 2*sin60*(numj + 1)
    return (-maxy,maxy-sin60)
end

"""computes bounding box of hex tiles arranged in an approximately rectangular layout, with 2*numj+1 hexagons in a row, and 2*numi+1 hexagons in a column"""
function bbox(numi,numj)
    return (xbounds(numi),ybounds(numj))

end
export bbox

"""computes the basis coordinates of hexagonal cells defined in the Hex1Basis in a rectangular pattern 2*numi+1 by 2*numj+1 wide and high respectively."""
function hexcellsinbox(numi,numj)
    #i2 and j2 are coordinates in the basis (1.5,.5√3),(0,-√3). The conditional tests are simpler in this basis. Convert to Hex1Basis1 coordinates before returning.
    hexbasis2to1(i,j) = [i+j,-j]

    result = Matrix{Int}(undef,2,(2*numi+1)*(2*numj+1))
    for i2 in -numi:numi
        let offsetj
            if i2 < 0
            offsetj = -abs(div(i2,2))
            else
                offsetj = div(i2+1,2)
            end
            for j2 in -numj:numj
                # conversion from Hex2Basis i2,j2 to Hex1Basis i,j is
                # (i,j) = (i2,0) + j2*(1,-1)
                jtemp = j2-offsetj
                # i1 = i2 + jtemp
                # j1 = -jtemp
                result[:,(i2+numi)*(2*numj+1) + j2+numj+1] = hexbasis2to1(i2,jtemp)
            end
        end
    end
    return result
end
export Repeat

struct HexBasis3{N,T}<:Basis{N,T}
    HexBasis3(::Type{T} = Float64) where{T} = new{2,T}()
end
export HexBasis3

function tilevertices(::HexBasis3{2,T}) where{T}
    sin60 = T(.5)*sqrt(T(3))
    cos60 = T(.5)
    return SMatrix{2,6,T}(
        0, 1,
        -sin60, cos60,
        -sin60, -cos60,
        0, -1,
        sin60, -cos60,
        sin60, cos60)
end

basis(::HexBasis3{2,T}) where{T} = SMatrix{2,2,T}(T(2*sin60),T(0),T(sin60),T(1.5))


# general array for testing input to SpecialFunctions.jl
x = 0.01:0.01:100.0

### Tests for bessely0
y0_SpecialFunctions = SpecialFunctions.bessely0.(big.(x))  # array to be tested against computed in BigFloats
@assert y0_SpecialFunctions[1] isa BigFloat                # just double check the higher precision

y0_64 = bessely0.(Float64.(x))
y0_32 = bessely0.(Float32.(x))
y0_big = bessely0.(big.(x))

# make sure output types match input types
@test y0_64[1] isa Float64
@test y0_32[1] isa Float32
@test y0_big[1] isa BigFloat

# test against SpecialFunctions.jl
@test y0_SpecialFunctions ≈ y0_64
@test y0_SpecialFunctions ≈ y0_32

# BigFloat precision only computed to 128 bits
@test isapprox(y0_big, y0_SpecialFunctions, atol=1.5e-34)

# negative numbers should result in a domain error
@test_throws DomainError bessely0(-1.0)

# NaN should return NaN
@test isnan(bessely0(NaN))

# test that zero inputs go to -Inf
@test bessely0(zero(Float32)) == -Inf32
@test bessely0(zero(Float64)) == -Inf64
@test bessely0(zero(BigFloat)) == -Inf

# test that Inf inputs go to zero
@test bessely0(Inf32) == zero(Float32)
@test bessely0(Inf64) == zero(Float64)

### tests for bessely1
y1_SpecialFunctions = SpecialFunctions.bessely1.(big.(x))
@assert y1_SpecialFunctions[1] isa BigFloat

y1_64 = bessely1.(Float64.(x))
y1_32 = bessely1.(Float32.(x))

# make sure output types match input types
@test y1_64[1] isa Float64
@test y1_32[1] isa Float32

# test against SpecialFunctions.jl
@test y1_64 ≈ y1_SpecialFunctions
@test y1_32 ≈ y1_SpecialFunctions

# negative numbers should result in a domain error
@test_throws DomainError bessely1(-1.0)

# NaN should return NaN
@test isnan(bessely1(NaN))

# test that zero inputs go to -Inf
@test bessely1(zero(Float32)) == -Inf32
@test bessely1(zero(Float64)) == -Inf64

# test that Inf inputs go to zero
@test bessely1(Inf32) == zero(Float32)
@test bessely1(Inf64) == zero(Float64)


#! /usr/bin/julia

# Rosetta Code, Four bit adder

xor{T<:Bool}(a::T, b::T) = (a&~b)|(~a&b)

halfadder{T<:Bool}(a::T, b::T) = (xor(a,b), a&b)

function fulladder{T<:Bool}(a::T, b::T, c::T=false)
    (s, ca) = halfadder(c, a)
    (s, cb) = halfadder(s, b)
    (s, ca|cb)
end

function adder(a::BitArray{1}, b::BitArray{1}, c0::Bool=false)
    len = length(a)
    length(b) == len || error("Addend width mismatch.")
    c = c0
    s = falses(len)
    for i in 1:len
        (s[i], c) = fulladder(a[i], b[i], c)
    end
    (s, c)
end

function adder{T<:Integer}(m::T, n::T, wid::T=4, c0::Bool=false)
    a = bitpack(digits(m, 2, wid))[1:wid]
    b = bitpack(digits(n, 2, wid))[1:wid]
    adder(a, b, c0)
end

Base.bits(n::BitArray{1}) = join(reverse(int(n)), "")

xavail = trues(15,15)
xcnt = 0
xgoal = 10
println("Testing adder with 4-bit words:")
while xcnt < xgoal
    m = rand(1:15)
    n = rand(1:15)
    xavail[m,n] || continue
    xavail[m,n] = xavail[n,m] = false
    xcnt += 1
    (s, c) = adder(m, n)
    oflow = c ? "*" : ""
    print(@sprintf "    %2d + %2d = %2d => " m n m+n)
    println(@sprintf("%s + %s = %s%s",
                     bits(m)[end-3:end],
                     bits(n)[end-3:end],
                     bits(s), oflow))
end


# hcat test
x = create_var(3, 1)
y = create_var(3, 2)
z = create_var(3, 3)
h = [x y z]
test_expr(h, (x, y, z))

# vcat test
x = create_var(1, 3)
y = create_var(2, 3)
z = create_var(3, 3)
v = [x; y; z]
test_expr(v, (x, y, z))
# v = [x, z, y]
# test_expr(v, (x, y, z))

# hvcat test
hv = [x x; y y; z z]
test_expr(hv, (x, y, z))

info("All stack tests passed")


# Functions to create interval graphs in Julia

export IntervalGraph, IntervalDigraph1, IntervalDigraph2
export RandomIntervalGraph, RandomIntervalDigraph1, RandomIntervalDigraph2
export UnitIntervalGraph, RandomUnitIntervalGraph
export UnitIntervalGraphEvolution

"""
`IntervalGraph(Jlist)` creates an interval graph from a list of closed
intervals.
"""
function IntervalGraph(Jlist::Array{ClosedInterval{T},1}) where {T}
    d = Dict{Int,ClosedInterval{T}}()
    n = length(Jlist)
    for k = 1:n
        d[k] = Jlist[k]
    end
    return IntervalGraph(d)
end

"""
`IntervalGraph(f::Dict)` creates asn interval graph from a dictionary
whose keys are the names of the vertices and whose values are
intervals.
"""
function IntervalGraph(f::Dict{K,ClosedInterval{T}}) where {K,T}
    klist = collect(keys(f))
    G = SimpleGraph{K}()
    for v in klist
        add!(G, v)
    end

    n = length(klist)

    for i = 1:n-1
        u = klist[i]
        for j = i+1:n
            v = klist[j]
            if !isempty(f[u] * f[v])
                add!(G, u, v)
            end
        end
    end
    cache_save(G, :IntervalRepresentation, f)
    cache_save(G, :name, "Interval graph")
    return G
end

"""
`edge_helper(A,B)` for closed intervals `A` and `B` is a private
function that returns the following values:

+ `0` if no edge
+ `1` if edge from `A` to `B` (but not reverse)
+ `2` if edge from `B` to `A` (but not reverse)
+ `3` if both
"""
function edge_helper(A::ClosedInterval, B::ClosedInterval)
    # no intersection
    if isempty(A * B)
        return 0
    end

    # containment
    if (left(A) <= left(B) <= right(A)) && (left(A) <= right(B) <= right(A))
        return 3
    end

    if (left(B) <= left(A) <= right(B)) && (left(B) <= right(A) <= right(B))
        return 3
    end

    # overlap
    if left(A) <= left(B)
        return 1
    end
    return 2
end

"""
`IntervalDigraph1(Jlist)` creates a type I interval digraph from a
list of closed intervals.
"""
function IntervalDigraph1(Jlist::Array{ClosedInterval{T},1}) where {T}
    n = length(Jlist)
    G = IntDigraph(n)
    for u = 1:n
        J = Jlist[u]
        for v = u:n
            K = Jlist[v]

            choice = edge_helper(J, K)

            if choice == 1 || choice == 3
                add!(G, u, v)
            end

            if choice == 2 || choice == 3
                add!(G, v, u)
            end

        end
    end
    return G
end

"""
`IntervalDigraph2(send_list, rec_list)` creates a Type II interval
graph from two lists of intervals.
"""
function IntervalDigraph2(
    send_list::Array{ClosedInterval{T},1},
    rec_list::Array{ClosedInterval{T},1},
) where {T}
    n = length(send_list)
    if length(rec_list) != n
        error("send_list and rec_list must be same length")
    end

    G = IntDigraph(n)

    for u = 1:n
        A = send_list[u]
        for v = 1:n
            B = rec_list[v]
            if !isempty(A * B)
                add!(G, u, v)
            end
        end
    end
    return G
end

"""
`IntervalDigraph(f::Dict)` creates a type I interval digraph from a
dictionary whose keys are the names of the vertices and whose values
are intervals.
"""
function IntervalDigraph1(
    snd::Dict{K,ClosedInterval{T}},
    rec::Dict{K,ClosedInterval{T}},
) where {K,T}
    if length(snd) != length(rec)
        error("send and receive dictionaries must have same keys")
    end

    klist = keys(snd)
    for k in klist
        if !haskey(rec)
            error("send and receive dictionaries must have same keys")
        end
    end

    n = length(klist)
    G = SimpleDigraph{K}()

    for i = 1:n
        u = klist[i]
        A = snd[u]
        for j = 1:n
            v = klist[j]
            B = rec[v]

            choice = edge_helper(A, B)

            if choice == 1 || choice == 3
                add!(G, u, v)
            end

            if choice == 2 || choice == 3
                add!(G, v, u)
            end
        end
    end
    return G
end

"""
`IntervalDigraph2(send::Dict,rec::Dict)` creates a Type II interval
digraph from two dictionaries mapping vertices to intervals.
"""
function IntervalDigraph2(
    snd::Dict{K,ClosedInterval{T}},
    rec::Dict{K,ClosedInterval{T}},
) where {K,T}
    if length(snd) != length(rec)
        error("send and receive dictionaries must have same keys")
    end

    klist = keys(snd)
    for k in klist
        if !haskey(rec)
            error("send and receive dictionaries must have same keys")
        end
    end

    n = length(klist)
    G = SimpleGraph{K}()

    for i = 1:n
        u = klist[i]
        A = snd[u]
        for j = 1:n
            v = klist[j]
            B = rec[v]
            if !isempty(A * B)
                add!(G, u, v)
            end
        end
    end

    return G
end

"""
`RandomIntervalGraph(n)` generates a random interval graph with `n`
vertices.
"""
function RandomIntervalGraph(n::Int)
    Jlist = [ClosedInterval(rand(), rand()) for _ = 1:n]
    return IntervalGraph(Jlist)
end

"""
`RandomIntervalDigraph1(n)` generates a random type I interval
digraph with `n` vertices.
"""
function RandomIntervalDigraph1(n::Int)
    Jlist = [ClosedInterval(rand(), rand()) for _ = 1:n]
    return IntervalDigraph1(Jlist)
end

"""
`RandomIntervalDigraph2(n::Int)` generates a random type II
directed interval graph with `n` vertices.
"""
function RandomIntervalDigraph2(n::Int)
    snd_list = [ClosedInterval(rand(), rand()) for _ = 1:n]
    rec_list = [ClosedInterval(rand(), rand()) for _ = 1:n]
    return IntervalDigraph2(snd_list, rec_list)
end

"""
`UnitIntervalGraph(x,t=1)` creates a unit interval graph where the
vector `x` specifies the left end points of the intervals. The
optional parameter `t` specifies the lengths of the intervals.
"""
function UnitIntervalGraph(points::Vector{T}, t::Real = 1) where {T<:Real}
    n = length(points)
    f = Dict{Int,T}()
    for k = 1:n
        f[k] = points[k]
    end
    return UnitIntervalGraph(f, t)
end

"""
`UnitIntervalGraph(f,t=1)` creates a unit interval graph from a
dictionary mapping vertex names to the left end points of their
intervals. The optional parameter `t` specifies the length of the
intervals.
"""
function UnitIntervalGraph(f::Dict{S,T}, t::Real = 1) where {S,T<:Real}
    vtcs = collect(keys(f))
    n = length(vtcs)
    G = SimpleGraph{S}()
    for v in vtcs
        add!(G, v)
    end

    for i = 1:n-1
        u = vtcs[i]
        for j = i+1:n
            v = vtcs[j]
            if abs(f[u] - f[v]) <= t
                add!(G, u, v)
            end
        end
    end
    cache_save(G, :UnitIntervalRepresentation, (f, t))
    cache_save(G, :name, "Unit interval graph")
    return G
end

"""
`RandomUnitIntervalGraph(n,t)` creates a unit interval graph with `n`
vertices whose left end points are chosen iid uniformly from [0,1] and
whose lengths are all given by `t` (default value 0.5).
"""
function RandomUnitIntervalGraph(n::Int, t::Real = 0.5)
    x = rand(n)
    return UnitIntervalGraph(x, t)
end

"""
`UnitIntervalGraphEvolution(points)` gives the sequence of edges as
the lengths of the intervals (whose left end points are specified in
`points`) increases.

This returns a pair consisting of the sequence of edges and the
lengths at which those edges appear.
"""
function UnitIntervalGraphEvolution(points::Vector{S}) where {S<:Real}
    n = length(points)
    nC2 = round(Int, n * (n - 1) / 2)

    edges = Vector{Tuple{Int,Int}}(nC2)
    diffs = Vector{S}(nC2)

    idx = 0

    for i = 1:n-1
        for j = i+1:n
            idx += 1
            edges[idx] = (i, j)
            diffs[idx] = abs(points[i] - points[j])
        end
    end

    p = sortperm(diffs)

    return (edges[p], diffs[p])

end

"""
`UnitIntervalGraphEvolution(n::Int)` is equivalent to
`UnitIntervalGraphEvolution(sort(rand(n)))`.
"""
UnitIntervalGraphEvolution(n::Int) = UnitIntervalGraphEvolution(sort(rand(n)))


using Distributed
using Distributions
using Genie, Genie.Router, Genie.Renderer, Genie.Requests
import Genie.Router: @params

form = """

Approximation of Pi

 Enter an integer in the form, I will calculate an approximation of Pi.

<form action="/" method="POST" enctype="multipart/form-data">
  <input type="text" name="nb_draws" value="" placeholder="Enter number of random draws" />
  <input type="submit" value="Enter" />
</form>
"""

route("/") do
  html(form)
end

route("/", method = POST) do

	function inside_circle(x::Float64, y::Float64)
	    output = 0
	    if x^2 + y^2 <= 1
		output = 1
	    end
	    return output
	end

	function pi_serial(nbPoints::Int64 = 128 * 1000; d=Uniform(-1.0,1.0))

	   #draw NbPoints from within the square centered in 0
	   #with side length equal to 2
	   xDraws = rand(d, nbPoints)
	   yDraws = rand(d, nbPoints)
	   sumInCircle = 0

	   for (xValue, yValue) in zip(xDraws, yDraws)
		sumInCircle+=inside_circle(xValue, yValue)
	   end

	   return 4*sumInCircle/nbPoints

	end

	pi_approx = pi_serial(parse(Int64, @params(:nb_draws)));

	"""

Approximation of Pi

Approximation for Pi $(pi_approx) with $(parse(Int64, @params(:nb_draws))) random draws
 True value is $(pi)"""

end

up()


function hash_struct(struc, h::UInt, fields=fieldnames(typeof(struc)))
    for f in fields
        h = Base.hash(getfield(struc,f), h)
    end
    return h
end

const EarthTime =  Dict("year" =>   365,    # days per year
                        "day"  =>   24 ,    # hours per day
                        "hour" =>   60 ,    # minutes per hour
                        "minute" => 60 ,    # seconds per minute
)

const KerbalTime = Dict("year" =>   424,    # days per year
                        "day"  =>   6,      # hours per day
                        "hour" =>   60,     # minutes per hour
                        "minute" => 60,     # seconds per minute
)

function seconds_to_datetime(nseconds, timedefs=KerbalTime)
    year, day, hour, minute = timedefs["year"], timedefs["day"], timedefs["hour"], timedefs["minute"]

    y = floor(nseconds/(minute*hour*day*year))+1       # starting from year 1
    nseconds -= minute*hour*day*year*(y-1)
    d = floor(nseconds/(minute*hour*day))+1            # starting from day 1
    nseconds -= minute*hour*day*(d-1)
    h = floor(nseconds/(minute*hour))                  # starting from hour 0
    nseconds -= minute*hour*(h)
    m = floor(nseconds/minute)                         # starting from minute 0
    nseconds -= minute*(m)
    s = round(nseconds)
    return NTuple{5,Int}((y,d,h,m,s))
end

function seconds_to_datetime_string(nseconds, timedefs=KerbalTime)
    ny, nd, nh, nm, ns = seconds_to_datetime(nseconds, timedefs)
    return "Year $ny, Day $nd, $(lpad(nh, 2, '0')):$(lpad(nm, 2, '0')):$(lpad(ns, 2, '0'))"
end

function wrap_angle(θ::Real, min::Real = 0)
    if min <= θ < (min + 2π)
        return θ
    else
        return θ - 2π*floor((θ-min)/2π)
    end
end

function angledist(θ₁, θ₂)
    dist = wrap_angle(θ₁ - θ₂)
    return dist > π ? dist - 2π : dist
end

function isapproxangle(θ₁,θ₂; atol=1e-12, rtol=1e-12, kwargs...)
    return isapprox(angledist(θ₁, θ₂),0;atol=atol,rtol=rtol,kwargs...) || isapprox(rem,1;atol=atol,rtol=rtol,kwargs...)
end

function wrap_acos(x::Real)
    if abs(x) > 1
        x = copysign(1,x)
    end
    return acos(x)
end

function wrap_asin(x::Real)
    if abs(x) > 1
        x = copysign(1,x)
    end
    return asin(x)
end


# Structs and Functions
# https://benlauwens.github.io/ThinkJulia.jl/latest/book.html#chap16
#include("ch15module.jl")

using Printf

# Time
"""
Represents the time of day.

hour:
minute:
second:
"""
mutable struct MyTime
    hour::Int64
    minute::Int64
    second::Int64
    # Inner constructor
    function MyTime(hour::Int64=0, minute::Int64=0,
        second::Int64=0)
        new(hour, minute, second)
    end
end

# Exercise 16-1
function printtime(mytime)
    @printf("%02d:%02d:%02d\n", mytime.hour, mytime.minute, mytime.second)
end

# Exercise 16-2
function isafter(t2, t1)
    tosecond(t2) < tosecond(t1)
end
function tosecond(mytime)
    mytime.hour*60*60 + mytime.minute*60 + mytime.second
end

# Pure Function
# https://benlauwens.github.io/ThinkJulia.jl/latest/book.html#_pure_functions

# A pure function does not modify its parameter
function addtimeDEAD(t1, t2)
    second = t1.second + t2.second
    minute = t1.minute + t2.minute
    hour = t1.hour + t2.hour

    if second >= 60
        second -= 60
        minute += 1
    end
    if minute >= 60
        minute -= 60
        hour += 1
    end

    MyTime(hour, minute, second)
end

# Improved version
function addtime(t1, t2)
    totalsecond = timetosecond(t1) + timetosecond(t2)
    secondtotime(totalsecond)
end

# Modifiers
# https://benlauwens.github.io/ThinkJulia.jl/latest/book.html#modifiers
# """
# Represents the time of day.
#
# hour:
# minute:
# second:
# """
# mutable struct MMyTime
#     hour::Int64
#     minute::Int64
#     second::Int64
# end

# Modifier version
function increment!DEAD(time, seconds)
    time.second += seconds

    if time.second >= 60
        time.second -= 60
        time.minute += 1
    end
    if time.minute >= 60
        time.minute -= 60
        time.hour += 1
    end
    # Exercise 16-3
    if time.second >= 60
        increment!(time, 0)
    end
end

# Exercise 16-5
function increment!(time, seconds)
    totalsecond = timetosecond(time) + seconds
    newtime = secondtotime(totalsecond)

    time.second = newtime.second
    time.minute = newtime.minute
    time.hour = newtime.hour
end

# Exercise 16-4: Pure version
"""
    increment

Add seconds to the time.

```
time:
addsecond:
```
"""
function increment(time::MyTime, addsecond::Int64)
    second = time.second + addsecond
    minute = time.minute
    hour = time.hour

    if second >= 60
        second -= 60
        minute += 1
    end
    if minute >= 60
        minute -= 60
        hour += 1
    end

    rettime = MyTime(hour, minute, second)

    if rettime.second >= 60
    # Recursive to move second to minute to hour
        rettime = increment(rettime, 0)
    end

    return rettime
end

# Prototyping Versus Planning
# https://benlauwens.github.io/ThinkJulia.jl/latest/book.html#prototyping_versus_planning
function timetosecond(time)
    seconds = time.hour*3600 + time.minute*60 + time.second
end

function secondtotime(totalsecond)
    (minute, second) = divrem(totalsecond, 60)
    (hour, minute) = divrem(minute, 60)
    return MyTime(hour, minute, second)
end


using EisensteinNumbers
using Base.Test: @test, @test_throws

# Eisenstein: same type arguments
@test begin
    l = Eisenstein(1, 2)
    r = Eisenstein{Int}(1, 2)
    l == r
end

# Eisenstein: mixed type arguments
@test begin
    l = Eisenstein(1, 1.2)
    r = Eisenstein{Float64}(1.0, 1.2)
    l == r
end

# Eisenstein: single argument
@test begin
    l = Eisenstein(3)
    r = Eisenstein(3, 0)
    l == r
end

# real
@test begin
    l = real(Eisenstein(1, 2))
    r = 0
    l == r
end

@test begin
    l = real(Eisenstein(1, 0))
    r = 1
    l == r
end

# imag
@test begin
    l = imag(Eisenstein(1, 2))
    r = sqrt(3)
    l == r
end

# complex
@test begin
    z = Eisenstein(1.2, 3.4)
    l = complex(z)
    r = complex(real(z), imag(z))
    l == r
end

# unreal: Eisenstein
@test begin
    l = unreal(Eisenstein(1.2, 3.4))
    r = 3.4
    l == r
end

# unreal: Real
@test begin
    l = unreal(Eisenstein(1, 0))
    r = 0
    l == r
end

# isreal: false case
@test begin
    l = isreal(Eisenstein(3, 4))
    r = false
    l == r
end

# isreal: true case
@test begin
    l = isreal(2 + 0ω)
    r = true
    l == r
end

# asarray
@test begin
    l = asarray(Eisenstein(5, 6))
    r = [5, 6]
    l == r
end

# iszero: false case
@test begin
    l = iszero(ω)
    r = false
    l == r
end

# iszero: true case
@test begin
    l = iszero(Eisenstein(0, 0))
    r = true
    l == r
end

# zero: type argument
@test begin
    l = zero(Eisenstein{Int})
    r = Eisenstein{Int}(0, 0)
    l == r
end

# zero: Eisenstein argument
@test begin
    l = zero(Eisenstein(1.2, 3.4))
    r = Eisenstein{Float64}(0.0, 0.0)
    l == r
end

# one: type argument
@test begin
    l = one(Eisenstein{Int})
    r = Eisenstein{Int}(1, 0)
    l == r
end

# one: Eisenstein argument
@test begin
    l = one(Eisenstein(1.2, 3.4))
    r = Eisenstein{Float64}(1.0, 0.0)
    l == r
end

# omega: type argument
@test begin
    l = omega(Eisenstein{Int})
    r = Eisenstein{Int}(0, 1)
    l == r
end

# omega: Eisenstein argument
@test begin
    l = omega(Eisenstein(1.2, 3.4))
    r = Eisenstein{Float64}(0.0, 1.0)
    l == r
end

# show
@test begin
    io = IOBuffer()
    show(io, Eisenstein(1, 2))
    l = String(take!(io))
    r = "[1: 1, ω: 2]"
    l == r
end

# conj
@test begin
    l = conj(Eisenstein(3, 4))
    r = Eisenstein(-1, -4)
    l == r
end

# Involutivity of conj
@test begin
    z = Eisenstein(2, 5)
    l = conj(conj(z))
    r = z
    l == r
end

# +: unary
@test begin
    l = +(Eisenstein(1, 2))
    r = Eisenstein(1, 2)
    l == r
end

# +: two Eisenstein numbers
@test begin
    x = Eisenstein(1, 2)
    y = Eisenstein(3, 4)
    l = x + y
    r = Eisenstein(4, 6)
    l == r
end

# +: add Eisenstein and Real
@test begin
    l = 2 + Eisenstein(5, 6)
    r = Eisenstein(7, 6)
    l == r
end

@test begin
    l = Eisenstein(7, 8) + 3
    r = Eisenstein(10, 8)
    l == r
end

# -: unary
@test begin
    l = -(Eisenstein(1.2, 3.4))
    r = Eisenstein(-1.2, -3.4)
    l == r
end

# -: two Eisenstein numbers
@test begin
    x = Eisenstein(1, 2)
    y = Eisenstein(3, 4)
    l = x - y
    r = Eisenstein(-2, -2)
    l == r
end

# -: add Eisenstein and Real
@test begin
    l = 2 - Eisenstein(5, 6)
    r = Eisenstein(-3, -6)
    l == r
end

@test begin
    l = Eisenstein(7, 8) - 3
    r = Eisenstein(4, 8)
    l == r
end

# ω is cubic root of unity
@test begin
    l = ω * ω * ω
    r = one(Eisenstein{Int})
    l == r
end

# *: multiply two Eisenstein numbers
@test begin
    l = Eisenstein(3, 1) * Eisenstein(2, -1)
    r = Eisenstein(7, 0)
    l == r
end

# *: commutativity
@test begin
    a = Eisenstein(1, 3)
    b = Eisenstein(2, 4)
    l = a * b
    r = b * a
    l == r
end

# *: multiply Eisenstein and Real
@test begin
    l = 2 * Eisenstein(3, 4)
    r = Eisenstein(6, 8)
    l == r
end

@test begin
    l = Eisenstein(3, 4) * 23
    r = Eisenstein(69, 92)
    l == r
end

# Positivity of abs2
@test begin
    l = abs2(Eisenstein(1, 2)) > 0
    r = true
    l == r
end

@test_throws ErrorException begin
    inv(zero(Eisenstein{Int}))
end

# /
@test begin
    x = Eisenstein(Int128(-50) // 9, -19 // 91)
    y = Eisenstein(Int128(-1) // 55, 91 // 8)
    z = x * y
    l = z / y
    r = x
    l == r
end

# \
@test begin
    x = Eisenstein(Int128(-50) // 9, -19 // 91)
    y = Eisenstein(Int128(-1) // 55, 91 // 8)
    z = x * y
    l = x \ z
    r = y
    l == r
end

# /: Eisenstein and Real
@test begin
    a = Int128(69) // 47
    z = Eisenstein(Int128(33) // 28, 13 // 35)
    l = a / z
    r = a * inv(z)
    l == r
end

@test begin
    a = Int128(6) // 19
    b = Int128(-25) // 71
    c = 34 // 1
    l = Eisenstein(b, c) / a
    r = Eisenstein(b / a, c / a)
    l == r
end

@test_throws ErrorException begin
    Eisenstein(3.1, 4.2) / 0.0
end

# \: Eisenstein and Real
@test begin
    a = Int128(69) // 47
    z = Eisenstein(Int128(33) // 28, 13 // 35)
    l = z \ a
    r = inv(z) * a
    l == r
end

@test begin
    a = Int128(6) // 19
    b = Int128(-25) // 71
    c = 34 // 1
    l = a \ Eisenstein(b, c)
    r = Eisenstein(a \ b, a \ c)
    l == r
end

@test_throws ErrorException begin
    0.0 \ Eisenstein(3.1, 4.2)
end

# associates
@test begin
    a, b, c, d, f, g = associates(Eisenstein(3, 7))
    abs2(a) == abs2(b) == abs2(c) == abs2(d) == abs2(f) == abs2(g)
end

# random
@test begin
    l = random(Eisenstein{Int64})
    r = random(Eisenstein{Int64})
    l != r
end


# ## Read Data

# you can read data using vega dataset

using VegaDatasets
data = dataset("iris")
print(data)

## Plot data

# using graph, it become much easier to find pattern from data.

# You can plot using VegaLite.
# In Julia, `:` means symbolifying such as `:sepalLength` which is not variable, it is a name of variable.(I am sure but it seems to work like a pointer.)

using VegaLite
p = data |> @vlplot(:point, x=:sepalLength, y=:sepalWidth, color=:species)

save(joinpath(@OUTPUT, "plot.svg"), p)  # hide

# \fig{plot}

# Plotting pairs is much easier because you can see all data at once.

p = data |>
    @vlplot(
        repeat={
            row=[:sepalLength,:sepalWidth,:petalLength, :petalWidth],
            column=[:sepalLength,:sepalWidth,:petalLength, :petalWidth]
        }
    ) +
    @vlplot(
        :point,
        x={field={repeat=:column}, type=:quantitative},
        y={field={repeat=:row}, type=:quantitative},
        color={field=:species},
    )

save(joinpath(@OUTPUT, "plot2.svg"), p)  # hide

# \fig{plot2}

# From the graph above, you can see 'Setosa' is pretty easy to identify but 'versicolor' and 'virginica' are overlapped.

# You can do all the process much easier with "DataVoyager".


# ---
# title: 416. Partition Equal Subset Sum
# id: problem416
# author: Tian Jun
# date: 2020-10-31
# difficulty: Medium
# categories: Dynamic Programming
# link: <https://leetcode.com/problems/partition-equal-subset-sum/description/>
# hidden: true
# ---
#
# Given a **non-empty** array `nums` containing **only positive integers** ,
# find if the array can be partitioned into two subsets such that the sum of
# elements in both subsets is equal.
#
#
#
# **Example 1:**
#
#
#
#     Input: nums = [1,5,11,5]
#     Output: true
#     Explanation: The array can be partitioned as [1, 5, 5] and [11].
#
#
# **Example 2:**
#
#
#
#     Input: nums = [1,2,3,5]
#     Output: false
#     Explanation: The array cannot be partitioned into equal sum subsets.
#
#
#
#
# **Constraints:**
#
#   * `1 <= nums.length <= 200`
#   * `1 <= nums[i] <= 100`
#
#
## @lc code=start
using LeetCode

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


@testset "Lasso small (v. split, $T)" for T in [Float32, Float64, ComplexF32, ComplexF64]
    using ProximalOperators
    using ProximalAlgorithms
    using LinearAlgebra
    using AbstractOperators: MatrixOp

    A1 = T[
        1.0 -2.0 3.0 -4.0 5.0
        2.0 -1.0 0.0 -1.0 3.0
    ]
    A2 = T[
        -1.0 0.0 4.0 -3.0 2.0
        -1.0 -1.0 -1.0 1.0 3.0
    ]
    A = vcat(MatrixOp(A1), MatrixOp(A2))
    b1 = T[1.0, 2.0]
    b2 = T[3.0, 4.0]

    m1, n = size(A1)
    m2, _ = size(A2)
    m = m1 + m2

    R = real(T)

    lam = R(0.1) * norm([A1; A2]' * [b1; b2], Inf)
    @test typeof(lam) == R

    f = SeparableSum(Translate(SqrNormL2(R(1)), -b1), Translate(SqrNormL2(R(1)), -b2))
    f2 = Sum(LeastSquares(A1, b1), LeastSquares(A2, b2))
    g = NormL1(lam)

    Lf = opnorm([A1; A2])^2

    x_star = T[-3.877278911564627e-01, 0, 0, 2.174149659863943e-02, 6.168435374149660e-01]

    TOL = R(1e-4)

    @testset "ForwardBackward" begin

        ## Nonfast/Nonadaptive

        x0 = zeros(T, n)
        x0_backup = copy(x0)
        solver = ProximalAlgorithms.ForwardBackward(tol = TOL)
        x, it = solver(x0, f = f2, g = g, Lf = Lf)
        @test eltype(x) == T
        @test norm(x - x_star, Inf) <= TOL
        @test it < 150
        @test x0 == x0_backup

        # Nonfast/Adaptive

        x0 = zeros(T, n)
        x0_backup = copy(x0)
        solver = ProximalAlgorithms.ForwardBackward(tol = TOL, adaptive = true)
        x, it = solver(x0, f = f2, g = g)
        @test eltype(x) == T
        @test norm(x - x_star, Inf) <= TOL
        @test it < 300
        @test x0 == x0_backup

        # Fast/Nonadaptive

        x0 = zeros(T, n)
        x0_backup = copy(x0)
        solver = ProximalAlgorithms.FastForwardBackward(tol = TOL)
        x, it = solver(x0, f = f2, g = g, Lf = Lf)
        @test eltype(x) == T
        @test norm(x - x_star, Inf) <= TOL
        @test it < 100
        @test x0 == x0_backup

        # Fast/Adaptive

        x0 = zeros(T, n)
        x0_backup = copy(x0)
        solver =
            ProximalAlgorithms.FastForwardBackward(tol = TOL, adaptive = true)
        x, it = solver(x0, f = f2, g = g)
        @test eltype(x) == T
        @test norm(x - x_star, Inf) <= TOL
        @test it < 200
        @test x0 == x0_backup
    end

    @testset "ZeroFPR" begin

        # ZeroFPR/Nonadaptive

        x0 = zeros(T, n)
        x0_backup = copy(x0)
        solver = ProximalAlgorithms.ZeroFPR(tol = TOL)
        x, it = solver(x0, f = f, A = A, g = g, Lf = opnorm([A1; A2])^2)
        @test eltype(x) == T
        @test norm(x - x_star, Inf) <= TOL
        @test it < 20
        @test x0 == x0_backup

        # ZeroFPR/Adaptive

        x0 = zeros(T, n)
        x0_backup = copy(x0)
        solver = ProximalAlgorithms.ZeroFPR(adaptive = true, tol = TOL)
        x, it = solver(x0, f = f, A = A, g = g)
        @test eltype(x) == T
        @test norm(x - x_star, Inf) <= TOL
        @test it < 20
        @test x0 == x0_backup

    end

    @testset "PANOC" begin

        # PANOC/Nonadaptive

        x0 = zeros(T, n)
        x0_backup = copy(x0)
        solver = ProximalAlgorithms.PANOC(tol = TOL)
        x, it = solver(x0, f = f, A = A, g = g, Lf = opnorm([A1; A2])^2)
        @test eltype(x) == T
        @test norm(x - x_star, Inf) <= TOL
        @test it < 20
        @test x0 == x0_backup

        ## PANOC/Adaptive

        x0 = zeros(T, n)
        x0_backup = copy(x0)
        solver = ProximalAlgorithms.PANOC(adaptive = true, tol = TOL)
        x, it = solver(x0, f = f, A = A, g = g)
        @test eltype(x) == T
        @test norm(x - x_star, Inf) <= TOL
        @test it < 20
        @test x0 == x0_backup

    end

end


using Base.Test
using Clustering

D = [0 1 2 3
     1 0 1 2
     2 1 0 1
     3 2 1 0]

@assert size(D) == (4, 4)

a = [1, 1, 2, 2]
c = [2, 2]

@test_approx_eq silhouettes(a, c, D) [1.5/2.5, 0.5/1.5, 0.5/1.5, 1.5/2.5]

a = [1, 2, 1, 2]
c = [2, 2]

@test_approx_eq silhouettes(a, c, D) [0.0, -0.5, -0.5, 0.0]


using ITensors
using ITensorMPOCompression

function make_2D_Heisenberg_AutoMPO(sites,Nx::Int64,Ny::Int64,hz::Float64=0.0,J::Float64=1.0)
    N=length(sites)
    @assert N==Nx*Ny
    ampo = OpSum()
    for j=1:N
        add!(ampo, hz   ,"Sz", j)
    end
    lattice = square_lattice(Nx, Ny; yperiodic = false)

    # Define the Heisenberg spin Hamiltonian on this lattice
    ampo = OpSum()
    for b in lattice
        ampo .+= 0.5*J, "S+", b.s1, "S-", b.s2
        ampo .+= 0.5*J, "S-", b.s1, "S+", b.s2
        ampo .+=     J, "Sz", b.s1, "Sz", b.s2
    end
    H = MPO(ampo,sites)
    fix_autoMPO!(H) #swap row[2]<->row[Dw] and col[2]<->col[Dw]
    return H
end
using Printf
Base.show(io::IO, f::Float64) = @printf(io, "%1.3f", f)
Nx=10
Ny=6
N=Nx*Ny; #10 sites
sites = siteinds("S=1/2",N);
H=make_2D_Heisenberg_AutoMPO(sites,Nx,Ny)
Dw_auto=get_Dw(H)
ss=truncate!(H)
Dw_trunc=get_Dw(H)
if Dw_auto==Dw_trunc
    println("You can't beat autoMPO!!!!")
else
    println("Wa-hoo truncate did something useful :)")
end


using Utilities,StaticNets,JLD,StatsFuns,StatsBase
using PyCall; pygui(:qt); using PyPlot

## Load data
#whole period data file
loadFileName = "/home/Domenico/Dropbox/Dynamic_Networks/data/emid_data/juliaFiles/Weekly_eMid_Data_from_2009_06_22_to_2015_02_27.jld"
@load(loadFileName, AeMidWeekly_Active_T,banksIDs,inactiveBanks, YeMidWeekly_Active_T,weekInd,datesONeMid ,degsIO_T,strIO_T)

##
inDegs=sumSq(AeMidWeekly_Active_T,2)
outDegs = sumSq(AeMidWeekly_Active_T,1)
N,T = size(inDegs)
NtimesT = prod(size(inDegs))

# An AR fitness model might have troubles explaining lots of jumps from highly active to inactive
# To check for it let's #Identify jumps from active to Inactive and relative entity
inactiveIn = (inDegs.==0)
degBeforeInactiveIn = inDegs[:,1:end-1][inactiveIn[:,2:end]]
freqIn = [(0:maximum(degBeforeInactiveIn)) counts(degBeforeInactiveIn.+1,maximum(degBeforeInactiveIn.+1))]
inactiveOut = (outDegs.==0)
degBeforeInactiveOut = outDegs[:,1:end-1][inactiveOut[:,2:end]]
freqOut = [(0:maximum(degBeforeInactiveOut)) counts(degBeforeInactiveOut.+1,maximum(degBeforeInactiveOut.+1))]
#Fractions of zero degrees observations
(sum(inactiveIn[:,2:end])/(NtimesT),sum(inactiveOut[:,2:end])/(NtimesT))
# Fraction  of jumps from a given degree(greater than zero) to zero
plot(freqOut[2:end,1],freqOut[2:end,2]/NtimesT,"r. ");plot(freqIn[2:end,1],freqIn[2:end,2]/NtimesT,"b. ");grid()
# plot fraction of jumps to zero starting from a degree equal or lower than that on x axis
plot(cumsum(freqIn[:,2]./sum(freqIn[:,2])),"r. "); plot(cumsum(freqOut[:,2]./sum(freqOut[:,2])),"b+ "); grid( )
# In summary the number of zero degree observations is quite high but
# those going to zero from a large degree are ~5% for jumps larger than 2 and  <2% for 5


function Base.convert(::Type{StateSpace}, t::TransferFunction)
    if !isproper(t)
        error("System is improper, a state-space representation is impossible")
    end
    ny, nu = size(t)
    mat = t.matrix
    # TODO : These are added due to scoped for blocks, but is a hack. This
    # could be much cleaner.
    Ac = Bc = Cc = Dc = A = B = C = D = Array(Float64, 0, 0)
    for i=1:nu
        for j=1:ny
            a, b, c, d = siso_tf_to_ss(mat[j, i])
            if j > 1
                # vcat
                Ac = blkdiag(Ac, a)
                Bc = vcat(Bc, b)
                Cc = blkdiag(Cc, c)
                Dc = vcat(Dc, d)
            else
                Ac, Bc, Cc, Dc = (a, b, c, d)
            end
        end
        if i > 1
            # hcat
            A = blkdiag(A, Ac)
            B = blkdiag(B, Bc)
            C = hcat(C, Cc)
            D = hcat(D, Dc)
        else
            A, B, C, D = (Ac, Bc, Cc, Dc)
        end
    end
    A, B, C = balance_statespace(A, B, C)[1:3]
    return ss(A, B, C, D, t.Ts, inputnames=t.inputnames, outputnames=t.outputnames)
end

Base.convert(::Type{StateSpace}, t::Real) = ss(t)

Base.promote_rule{T<:SisoTf}(::Type{StateSpace}, ::Type{TransferFunction{T}}) = StateSpace
Base.promote_rule{T<:Real}(::Type{StateSpace}, ::Type{T}) = StateSpace

siso_tf_to_ss(t::SisoTf) = siso_tf_to_ss(convert(SisoRational, t))

function siso_tf_to_ss(t::SisoRational)
    t = normalize_tf(t)
    tnum = num(t)
    tden = den(t)
    len = length(tden)
    d = Array(Float64, 1, 1)
    d[1] = tnum[1]

    if len==1 || tnum == zero(Poly{Float64})
        a = zeros(0, 0)
        b = zeros(0, 1)
        c = zeros(1, 0)
    else
        tden = tden[2:end]
        a = [-tden' ; eye(len - 2, len - 1)]
        b = eye(len - 1, 1)
        c = tnum[2:len]' - d * tden[:]'
    end
    return float64mat(a), float64mat(b), float64mat(c), d
end

function normalize_tf(t::SisoRational)
    d = t.den[1]
    return SisoTf(t.num/d, t.den/d)
end

"""
`A, B, C, T = balance_statespace{S}(A::Matrix{S}, B::Matrix{S}, C::Matrix{S}, perm::Bool=false)`

`sys, T = balance_statespace(sys::StateSpace, perm::Bool=false)`

Computes a balancing transformation `T` that attempts to scale the system so
that the row and column norms of [T*A/T T*B; C/T 0] are approximately equal.
If `perm=true`, the states in `A` are allowed to be reordered.

This is not the same as finding a balanced realization with equal and diagonal observability and reachability gramians, see `balreal`
"""
function balance_statespace{S}(A::Matrix{S}, B::Matrix{S},
        C::Matrix{S}, perm::Bool=false)
    nx = size(A, 1)
    nu = size(B,2)
    ny = size(C,1)

    # Compute the transformation matrix
    mag_A = abs(A)
    mag_B = maximum([abs(B)  zeros(S, nx, 1)], 2)
    mag_C = maximum([abs(C); zeros(S, 1, nx)], 1)
    T = balance_transform(mag_A, mag_B, mag_C, perm)

    # Perform the transformation
    A = T*A/T
    B = T*B
    C = C/T

    return A, B, C, T
end

function balance_statespace(sys::StateSpace, perm::Bool=false)
    A, B, C, T = balance_statespace(sys.A,sys.B,sys.C, perm)
    return ss(A,B,C,sys.D), T
end

"""
`T = balance_transform{R}(A::Matrix{R}, B::Matrix{R}, C::Matrix{R}, perm::Bool=false)`

`T = balance_transform(sys::StateSpace, perm::Bool=false) = balance_transform(A,B,C,perm)`

Computes a balancing transformation `T` that attempts to scale the system so
that the row and column norms of [T*A/T T*B; C/T 0] are approximately equal.
If `perm=true`, the states in `A` are allowed to be reordered.

This is not the same as finding a balanced realization with equal and diagonal observability and reachability gramians, see `balreal`
See also `balance_statespace`, `balance`
"""
function balance_transform{R}(A::Matrix{R}, B::Matrix{R}, C::Matrix{R}, perm::Bool=false)
    nx = size(A, 1)
    # Compute a scaling of the system matrix M
    S = diag(balance([A B; C zeros(size(C*B))], false)[1])
    Sx = S[1:nx]
    Sio = S[nx+1]
    # Compute permutation of x (if requested)
    pvec = perm ? balance(A, true)[2] * [1:nx;] : [1:nx;]
    # Compute the transformation matrix
    T = zeros(R, nx, nx)
    T[pvec, :] = Sio * diagm(1./Sx)
    return T
end

balance_transform(sys::StateSpace, perm::Bool=false) = balance_transform(sys.A,sys.B,sys.C,perm)

@doc """`sys = ss2tf(s::StateSpace)`, ` sys = ss2tf(A, B, C, Ts = 0; inputnames = "", outputnames = "")`

Convert a `StateSpace` realization to a `TransferFunction`""" ->
function ss2tf(s::StateSpace)
    return ss2tf(s.A, s.B, s.C, s.D, s.Ts, inputnames=s.inputnames, outputnames=s.outputnames)
end

function ss2tf(A, B, C, D, Ts = 0; inputnames = "", outputnames = "")
    nu,ny = size(B,2),size(C,1)
    ubernum = Matrix{Vector}(ny,nu)
    uberden = Matrix{Vector}(ny,nu)
    for i = 1:nu, j=1:ny
        ubernum[j,i],uberden[j,i] = sisoss2tf(A, B[:,i], C[j,:]', D[j,i])
    end
    tf(ubernum,uberden, Ts, inputnames=inputnames, outputnames=outputnames)
end

function sisoss2tf(A, B, C, D)
    charpolA = charpoly(A)
    numP = charpoly(A-B*C) - charpolA + D*charpolA
    denP = charpolA
    return numP[1:length(numP)], denP[1:length(denP)]
end

tf(sys::StateSpace) = ss2tf(sys)
zpk(sys::StateSpace) = zpk(ss2tf(sys))

function charpoly(A)
    λ = eigvals(A);
    p = reduce(*,ControlSystems.Poly([1.]), ControlSystems.Poly[ControlSystems.Poly([1, -λᵢ]) for λᵢ in λ]);
    if maximum(imag(p[:])./(1+abs(real(p[:])))) < sqrt(eps())
        for i = 1:length(p)
            p[i] = real(p[i])
        end
    else
        error("Characteristic polynomial should be real")
    end
    p
end


using ZeroIndex

function count_change(mat::Array{Int64, 2},
                      denom::Int64,
                      row::Int64,
                      col::Int64)::Int64
    prev_col = col - denom
    prev_row = row - 1
    denom_in = 0
    denom_out = 0
    if prev_row >= 0
        denom_out = aget(mat, prev_row, col)
    end
    if prev_col >= 0
        denom_in = aget(mat, row, prev_col)
    end
    return denom_in + denom_out
end

function main()
    denoms = [1, 2, 5, 10, 20, 50, 100, 200]
    sought = 200
    ndenoms = length(denoms)
    nways = Array{Int64, 2}(ndenoms, sought + 1)
    for row = 0:(ndenoms-1)
        aset!(nways, row, 0, 1)
    end
    for col in 1:sought
        for row in 0:(ndenoms-1)
            denom = vget(denoms, row)
            val = count_change(nways, denom, row, col)
            aset!(nways, row, col, val)
        end
    end
    @printf("number of ways is = %d\n", aget(nways, ndenoms-1, sought))
end

main()


import FastGaussQuadrature:gausslegendre

export legendre

function legendre(n::Integer, a::T, b::T) where T<:AbstractFloat
  xw = gausslegendre(n)
  x = xw[1]
  w = xw[2]
  s = (b-a) * (x.+1) / 2 .+ a
  v = (b-a) / 2 * w
  return Array(s'), v
end


using Colors, DifferentialEquations, LaTeXStrings, PGFPlotsX

function sir!(du, u, p, t)
    du[1] = -(p[1]/p[2] * u[2] * u[1])
    du[2] = (p[1]/p[2] * u[2] * u[1]) - u[2]/p[2]
    du[3] = u[2] / p[2]
end

function sirplot(r0, d, t, i0 = 1e-6)
    u0 = [1-i0, i0, 0.0]
    solv = solve(ODEProblem(sir!, u0, t, [r0, d]), saveat=1)
    r0lab = latexstring(replace("\\mathcal{R}_0=$(r0)", "."=>"{,}"))
    i0lab = latexstring(replace("I_0=$i0", "."=>"{,}"))
    dlab = latexstring("D=$d")
    p = @pgf Axis({"yticklabel style"={"/pgf/number format/use comma"},
       legend_pos="outer north east",
       title="SIR för $(r0lab), $(dlab), $(i0lab)", xlabel=L"t",
       ylabel=L"p",
       xmajorgrids, ymajorgrids})
    cs = ["S","I","R"]
    plotcolors = distinguishable_colors(length(cs)+1, [RGB(1,1,1)])[2:end]
    for (i, c) in enumerate(cs)
       @pgf push!(p, PlotInc({no_markers, color = plotcolors[i]},
           Table([solv.t, map(r -> r[i], solv.u)])),
           LegendEntry(latexstring(c)))
    end
    @pgf push!(p, PlotInc({no_markers, style = {"dashed"}, color = plotcolors[1]},
       Coordinates([(t[1], 1/r0), (t[2], 1/r0)])),
       LegendEntry(L"1/\mathcal{R}_0"))
    p
end


######################################################
# Created by Alex Dowling (alexdowling.net)
# while at the University of Wisconsin-Madison
# ####################################################

# Define problem
using JuMP
using Ipopt
using DegeneracyHunter

# Define problem
function lp1()

	m = Model()

	@variable(m, 0 <= x[1:3] <= 5)
	@variable(m, 0 <= y <= 0)
	@constraint(m, x[1] + x[2] >= 1)
	@constraint(m, x[1] + x[2] + x[3] == 1)
	@constraint(m, x[2] - 2*x[3] <= 1)
	@constraint(m, x[1] + x[3] >= 1)
	@constraint(m, x[1] + x[2] + x[3] == 1) # Redundant constraint - makes problem degenerate
	@objective(m, Min, sum{x[i],i=1:3})

	return m
end

function initialize!(m::Model)
	x = getindex(m,:x)
	setvalue(x[1], 1.0)
	setvalue(x[2], 5.0)
	setvalue(x[3], -1.0)

	# Intentionally do not initialize y

	return nothing
end

# Create and initialize model
m = lp1()
JuMP.setsolver(m,IpoptSolver())
initialize!(m)

# Check initialization
DegeneracyHunter.printVariableDiagnostics(m)

# Print equations with absolute residuals greater than 0.001
DegeneracyHunter.printInfeasibleEquations(m, 0.001)

# Check for degeneracy constraints
DegeneracyHunter.degeneracyHunter(m, IpoptSolver(), includeBounds=true)

# Solve model
t0 = time()
status = solve(m)
tm = time() - t0

# Print inactive constraints at solution
DegeneracyHunter.printInactiveEquations(m)

# Package and print problem size information
ps = DegeneracyHunter.assembleProblemStats(m,status,tm)
DegeneracyHunter.printProblemStats(ps)