fluxlimiter(::Type{FluxLimiter{minmod}}, rf)      = max(0.,min(1.,rf))
fluxlimiter(::Type{FluxLimiter{superbee}}, rf)    = max(0.,min(1.,2rf),min(rf,2.))
fluxlimiter(::Type{FluxLimiter{vanAlbada}}, rf)   = (rf^2+rf)/(rf^2+1)
fluxlimiter(::Type{FluxLimiter{vanAlbada2}}, rf)  = (2rf)/(rf^2+1)
fluxlimiter(::Type{FluxLimiter{vanLeer}}, rf)     = (rf+abs(rf))/(abs(rf)+1)
fluxlimiter(::Type{FluxLimiter{ospre}}, rf)       = 1.5(rf^2+rf)/(rf^2+rf+1)
fluxlimiter(::Type{FluxLimiter{hquick}}, rf)      = 2.0(rf+abs(rf))/(rf+2)
fluxlimiter(::Type{FluxLimiter{koren}}, rf)       = max(0,min(2rf,min((1+2rf)/3,2)))

@inline function updater!(RiverPhysics, RiverArrays, River)
    # unpack
    Kr, kappa_a, h, m = RiverPhysics.Kr, RiverPhysics.kappa_a,  RiverPhysics.h, RiverPhysics.m
    L, r = RiverArrays.L, RiverArrays.r
    dx, z, x = River.Δx^2, River.z, River.x

    # constant values
    cte1 = -Kr*(kappa_a^m)/dx
    cte2 = h*m
    # update river length
    river_length!(L, River)
    @tturbo for i ∈ 2:length(x)
        r[i] = cte1 * L[i]^(cte2)
    end
end

function river_length!(L, River)
    x, z, dx = River.x, River.z, River.Δx^2
    @tturbo for i in 2:length(x)
        L[i] = √(dx  + (z[i]-z[i-1])^2)
    end
    @inbounds @fastmath for i in 2:length(x)
        L[i] += L[i-1]
    end
end

@inline function rfactor!(rf, sol, ilast)
    """
    Wikipedia definition, paper has a slightly different one
    """
    @inbounds for i in 1:ilast
        s1 =  sol[i+1] - sol[i]
        if s1 == 0.0
            rf[i] = 1.0
        elseif s1 <= 0.0
            rf[i] = 0.0
        else
            rf[i] = (sol[i+2] - sol[i+1]) / s1
        end
	end
    @inbounds rf[ilast] = 1.0
    @inbounds rf[ilast-1] = 1.0
end

function TVD!(River, sol0, RiverPhysics::KnickPointsParameters, RiverArrays::KnickPointsArrays, dt, ilast)
    # Add reference to paper of Govers and Schwangart

    n, φ = RiverPhysics.n, RiverPhysics.φ
    r, rf = RiverArrays.r, RiverArrays.rf
    sol, dx = River.z, River.Δx

    rfactor!(rf, sol, ilast)
    copyto!(sol0, sol)
    # cache some constant values to avoid redundance
    invdx = 1/dx
    invdxdt = dt*invdx
    half_dt = dt*0.5

    @fastmath for i in 2:ilast
        # cache
        @inbounds soli, solplus, solminus = sol0[i], sol0[i+1], sol0[i-1]
        @inbounds ri = r[i]
        ri_half = ri*0.5
        dtdx = half_dt*invdx*ri*ri
        # solve
        α₀, α₁ = ri_half * (1+sign(ri)), ri_half*(1-sign(ri))
        Frlo = α₀*soli + α₁*solplus    # f right low
        Fllo = α₀*solminus + α₁*soli   # f left  low
        Frhi = ri_half*(soli + solplus) - dtdx*(solplus-soli)
        Flhi = ri_half*(soli + solminus) - dtdx*(soli-solminus)
        @inbounds Fᵣ = Frlo+fluxlimiter(φ, rf[i]) * (Frhi - Frlo )
        @inbounds Fₗ = Fllo+fluxlimiter(φ, rf[i-1]) * (Flhi - Fllo )
        # non linear term
        @inbounds multiplier  = ((sol0[i] - sol0[i+1])*invdx)^(n-1) # |-> pow32 speeds up the whole solver by 60%
        # update solution
        @inbounds sol[i] = max(sol[i] - invdxdt*(Fᵣ-Fₗ) * multiplier, sol[i+1])
        # @inbounds sol[i] -= invdxdt*(Fᵣ-Fₗ) * multiplier
    end
end


module metrics

using ..operators, ..operators_sparse

export tracedistance

function tracedistance(rho::DenseOperator, sigma::DenseOperator)
    delta = (rho - sigma).data
    @assert size(delta, 1) == size(delta, 2)
    for i=1:size(delta,1)
        delta[i,i] = real(delta[i,i])
    end
    s = eigvals(Hermitian(delta))
    return 0.5*sum(abs(s))
end

function tracedistance{T<:Operator}(rho::T, sigma::T)
    throw(ArgumentError("tracedistance not implemented for $(T). Use dense operators instead."))
end

end # module


using Distributions
using LinearAlgebra
using StatsBase

mutable struct CategoricalSmoothed{CT <: Real} <: Distributions.Distribution{Distributions.Multivariate,Distributions.Discrete}
    counts::Vector{CT};
    smooth::Float64;
    sum_counts::CT;

    CategoricalSmoothed(counts::Vector{IT}; smooth::Real=1.0, sum_counts::IT=sum(counts)) where IT <: Real = new{IT}(counts, Float64(smooth), sum_counts)
end

function pdf(dist::CategoricalSmoothed, x::Int; use_smoothing::Bool=true)::Float64
    cnt = dist.counts[x]
    if !use_smoothing
        return cnt / dist.sum_counts
    end

    # Can't simply add smoothing to each vector component because of sparsity
    return fmax(Float64(cnt), dist.smooth) / (dist.sum_counts + dist.smooth)
end

function maximize!(dist::CategoricalSmoothed, x::T where T <: AbstractArray{Int, 1}, confidences::T2 where T2 <: AbstractVector{Float64})
    count_array!(dist.counts, x, confidences)
    dist.sum_counts = sum(confidences)
    return dist
end


using KitBase, FluxReconstruction, OrdinaryDiffEq, LinearAlgebra, Plots
using KitBase.ProgressMeter: @showprogress
using Base.Threads: @threads

begin
    x0 = 0
    x1 = 1
    ncell = 100
    dx = (x1 - x0) / ncell
    deg = 7 # polynomial degree
    nsp = deg + 1
    γ = 5 / 3
    cfl = 0.01
    dt = cfl * dx
    t = 0.0
end
ps = FRPSpace1D(x0, x1, ncell, deg)
ℓ = FR.basis_norm(ps.deg)

u = zeros(ncell, nsp, 3)
for i = 1:ncell, ppp1 = 1:nsp
    if ps.x[i] <= 0.5
        prim = [1.0, 0.0, 0.5]
    else
        prim = [0.125, 0.0, 0.625]
    end

    u[i, ppp1, :] .= prim_conserve(prim, γ)
end

tspan = (0.0, 0.15)
prob = FREulerProblem(u, tspan, ps, γ, :dirichlet)
nt = tspan[2] ÷ dt |> Int
itg = init(prob, Midpoint(), saveat = tspan[2], adaptive = false, dt = dt)

@showprogress for iter = 1:nt
    @inbounds @threads for i = 1:ps.nx
        ũ = ps.iV * itg.u[i, :, 1]
        su = (ũ[end]^2) / (sum(ũ.^2) + 1e-6)
        isd = shock_detector(log10(su), ps.deg)
        λ = dt * exp(0.875/1 * (ps.deg)) * 0.15
        if isd
            for s = 1:3
                û = ps.iV * itg.u[i, :, s]
                FR.modal_filter!(û, λ; filter = :l2)
                #FR.modal_filter!(û, ℓ; filter = :lasso)
                #FR.modal_filter!(û, 4; filter = :exp)
                #FR.modal_filter!(û, 6; filter = :houli)
                itg.u[i, :, s] .= ps.V * û
            end
        end
    end

    step!(itg)
end

begin
    x = zeros(ncell * nsp)
    w = zeros(ncell * nsp, 3)
    for i = 1:ncell
        idx0 = (i - 1) * nsp

        for j = 1:nsp
            idx = idx0 + j
            x[idx] = ps.xpg[i, j]

            w[idx, :] .= itg.u[i, j, :]
        end
    end

    sol = zeros(ncell*nsp, 3)
    for i in axes(sol, 1)
        sol[i, :] .= conserve_prim(w[i, :], γ)
        sol[i, end] = 1 / sol[i, end]
    end

    plot(x, sol[:, 1], label="ρ", xlabel="x")
    plot!(x, sol[:, 2], label="u", xlabel="x")
    plot!(x, sol[:, 3], label="T", xlabel="x")
end


# create genomic relationship matrix based on the genotype
using JWAS,DataFrames,CSV,Statistics,JWAS.Datasets,DelimitedFiles,Random, LinearAlgebra, StatsBase
Random.seed!(1)

for rep in 1:20
    genofile="/Users/tianjing/Box/Omicsdata/andrew1055/data$rep/geno.csv"
    genotypes=get_genotypes(genofile;separator=',',method="RR-BLUP");

    sum2pq=genotypes.sum2pq

    geno_centered=genotypes.genotypes
    round.(mean(geno_centered,dims=1),digits=2)

    GRM=(geno_centered*geno_centered')/sum2pq
    writedlm("/Users/tianjing/Box/Omicsdata/andrew1055/data$rep/GRM_geno.txt",GRM)
end


function cheb2jac(c_cheb::AbstractVector{T},α::T,β::T,plan::ChebyshevJacobiPlan{BACKWARD,T}) where T<:AbstractFloat
    M,N,nM₀,αN,K = getconstants(plan)
    i₁,i₂,j₁,j₂ = getindices(plan)
    p₁,p₂,rp,c₁,c₂,um,vm,cfs,θ,tempcos,tempsin,tempcosβsinα,tempmindices,cnαβ,cnmαβ,w,anαβ,c_cheb2,pr = getplan(plan)

    # Initialize c_cheb2
    @inbounds for i=1:N+1 c_cheb2[i] = c_cheb[i] end
    @inbounds for i=N+2:2N+1 c_cheb2[i] = 0 end

    # Initialize values at Clenshaw-Curtis points
    v_jac = zero(c_cheb2)

    # Perform transposed DCT-I
    applyTN!(c_cheb2,p₁)

    # Scale values by Clenshaw-Curtis weights
    @inbounds for i=1:2N+1 c_cheb2[i] *= w[i] end

    k=0
    for κ = 1:K
        k = κ
        copyto!(cnmαβ,cnαβ)

        j₁[k] < nM₀ && (k-=1; break)

        @inbounds for i=i₁[k]:i₁[k+1]-1
            forward_recurrence!(pr,j₂[k],θ[i],tempcos[i],tempsin[i],rp)
            cc2i = c_cheb2[i]
            for j=1:j₂[k] v_jac[j] += cc2i*pr[j] end
        end

        @inbounds for i=i₂[k+1]+1:i₂[k]
            forward_recurrence!(pr,j₂[k],θ[i],tempcos[i],tempsin[i],rp)
            cc2i = c_cheb2[i]
            for j=1:j₂[k] v_jac[j] += cc2i*pr[j] end
        end

        for m=0:M-1
            # Compute u_m(θ) and v_m(θ)
            compute_umvm!(um,vm,cfs,α,β,tempcos,tempsin,tempcosβsinα,m,θ,i₁[k+1]:i₂[k+1])

            # Multiply point-wise by u_m(θ) and v_m(θ) for valid indices
            init_c₁c₂!(c₁,c₂.parent,um,vm,c_cheb2,i₁[k+1],i₂[k+1])

            # Apply planned DCT-I and DST-I in-place
            applyTN!(c₁,p₁);applyUN!(c₂,p₂)

            # Compute diagonal 2N-scaling multiplied by local coefficients and zero out excess
            @inbounds for j=j₁[k]:j₂[k] v_jac[j] += cnmαβ[j]*(c₁[j]+c₂.parent[j]) end

            # Update C_{n,m}^{α,β} by recurrence in m
            @inbounds for j=1:2N+1 cnmαβ[j] /= 2(2j+α+β+m) end
        end
    end

    # Finish off recurrence
    @inbounds for i=i₁[k+1]:i₂[k+1]
        forward_recurrence!(pr,j₂[k+1],θ[i],tempcos[i],tempsin[i],rp)
        cc2i = c_cheb2[i]
        for j=1:j₂[k+1] v_jac[j] += cc2i*pr[j] end
    end

    # Scale by orthonormality constants
    ret = zero(c_cheb)
    @inbounds for i=1:N+1 ret[i] = v_jac[i]/anαβ[i] end

    ret
end


type StructuredMMSE
    rho # snr
    transform

    W
    bias

    StructuredMMSE() = new()
end

function StructuredMMSE(snr,get_cov; transform = (x,_) -> x, nSamples=1)
    est = StructuredMMSE()
    est.rho = 10^(0.1*snr)
    est.transform = transform

    nAntennas = size(get_cov(),1)

    Q = transform(eye(nAntennas), :notransp)
    A = pinv(abs2.(Q*Q')) # note: output transform must be Q', i.e., correct normalization is needed

    est.W    = zeros(size(Q,1),nSamples)
    est.bias = zeros(nSamples)
    for i in 1:nSamples
        C = get_cov() # get random sample from cov. prior
        W = C/(C + 1/est.rho * eye(nAntennas))
        est.W[:,i]  = A*(real(sum(Q*W .* conj(Q),2)))
        est.bias[i] = real(logdet(eye(nAntennas) - Q'*diagm(est.W[:,i])*Q))
    end
    est
end

function estimate(est::StructuredMMSE, y)
    z = est.transform(y, :notransp)
    (nFilterLength,nCoherence,nBatches) = size(z)

    cest = sum(abs2, z, 2)
    cest = reshape(cest,nFilterLength,nBatches)

    exps = est.W'*cest*est.rho .+ est.bias*nCoherence # note cest sum not mean
    exps = exps .- maximum(exps,1)

    weights = exp.(exps)
    weights = weights./sum(weights,1)

    F = est.W*weights;
    F = reshape(F, nFilterLength, 1, nBatches)

    x = est.transform(F.*z, :transp)
end


module StochasticOptimalTransport

using LogExpFunctions: LogExpFunctions

using LinearAlgebra: LinearAlgebra
using Random: Random
using Statistics: Statistics

include("utils.jl")
include("discrete.jl")
include("semidiscrete.jl")

@doc raw"""
    wasserstein([rng, ]c, μ, ν[, ε; kwargs...])

Estimate the (entropic regularization of the) Wasserstein distance
```math
W_{ε}(μ, ν) = \min_{π ∈ Π(μ,ν)} \int c(x, y) \,π(\mathrm{d}(x,y)) +
ε \mathrm{KL}(π \,|\, μ ⊗ ν)
```
with respect to cost function `c` using stochastic optimization.

If both measures `μ` and `ν` are [`DiscreteMeasure`](@ref)s, then the
Wasserstein distance is approximated with stochastic averaged gradient
descent (SAG). The convergence rate of SAG is ``O(1 / k)``, where
``k`` is the number of iterations, and hence converges faster than stochastic
gradient descent (SGD) at the price of increased memory consumption.

If only one of the measures `μ` and `ν` is a [`DiscreteMeasure`](@ref), then the
Wasserstein distance is approximated with stochastic gradient descent with
averaging (SGA). In this case, it is required that samples of the non-discrete measure `λ`
can be obtained with `rand(rng, λ)`. The convergence rate of SGA is ``O(1/√k)``,
where ``k`` is the number of iterations.

If `ε` is `nothing` (the default), then the unregularized Wasserstein distance is
approximated. Otherwise, the entropic regularization with `ε > 0` is estimated.

The SAG algorithm uses a constant step size, whereas the SGA algorithm uses the step size
schedule
```math
    τᵢ = \frac{τ₁}{1 + \sqrt{(i - 1) / w}}
```
for the ``i``th iteration, where ``τ₁`` corresponds to the initial step size and ``w``
indicates the number of iterations serving as a warm-up phase.

# Keyword arguments
- `maxiters::Int = 10_000`: maximum number of gradient steps
- `stepsize = 1`: constant stepsize of SAG or initial step size ``τ₁`` of SGA
- `warmup_phase = 1`: warm-up phase ``w`` of SGA
- `atol = 0`: absolute tolerance
- `rtol = iszero(atol) ? typeof(float(atol))(1 // 10_000) : 0`: relative tolerance
- `montecarlo_samples = 10_000`: Number of Monte Carlo samples from `μ` for approximating
  an expectation with respect to `μ` in the semi-discrete optimal transport problem

# References

Genevay et al. (2016). Stochastic Optimization for Large-Scale Optimal Transport. Advances in Neural Information Processing Systems (NIPS 2016), 29:3440-3448.

Peyré, Gabriel, & Marco Cuturi (2019). Computational Optimal Transport. Foundations and Trends in Machine Learning, 11(5-6):355-607.
"""
wasserstein(args...; kwargs...) = wasserstein(Random.GLOBAL_RNG, args...; kwargs...)
function wasserstein(
    rng::Random.AbstractRNG, c, μ, ν, ε::Union{Real,Nothing}=nothing; kwargs...
)
    # approximate solution `v` of the dual problem
    v = dual_v(rng, c, μ, ν, ε; kwargs...)

    # compute Wasserstein distance from dual solution
    cost = dual_cost(rng, c, v, μ, ν, ε; kwargs...)

    return cost
end

end


using Optim
using Statistics

  """
      objFunctionMSE( p::Vector{Float64}; predMat::Matrix{Float64}, t::Vector{Float64} )

      evaluate MeanSquaredError under given params.
      #Arguments
      - `p::Vector{Float64}`:       initial solution.
      - `predMat::Matrix{Float64}`: each column represents predictions of one model.
      - `t::Vector{Float64}`:       ground truth labels.
      #Return
      - `Float64`:                  MeanSquaredError.
  """
  function objFunctionMSE( p::Vector{Float64}; predMat::Matrix{Float64}, t::Vector{Float64} )
    return mean( lossFunctions.MSE( predMat * p, t ) );
  end

  """
      objFunctionHinge( p::Vector{Float64}; predMat::Matrix{Float64}, t::Vector{Float64} )

      evaluate MeanSquaredError under given params.
      #Arguments
      - `p::Vector{Float64}`:       initial solution.
      - `predMat::Matrix{Float64}`: each column represents predictions of one model.
      - `t::Vector{Float64}`:       ground truth labels.
      #Return
      - `Float64`:                  hingeLoss.
  """
  function objFunctionHinge( p::Vector{Float64}; predMat::Matrix{Float64}, t::Vector{Float64} )
    return mean( lossFunctions.hingeLoss( predMat * p, t ) );
  end

  """
      directOptimNaiveMSE( predMat::Matrix{Float64}, t::Vector{Float64}, p::Vector{Float64} )

      compute refined solution _for_ mixing parameter p.
      #Arguments
      - `predMat::Matrix{Float64}`: each column is the prediction _of_ one hypothesis.
      - `t::Vector{Float64}`:       label vector.
      - `p::Vector{Float64}`:       initial solution for mixing coefficients.
      #Return
      - `Vector{Float64}`:          improved initial solution.
  """
  function directOptimNaiveMSE( predMat::Matrix{Float64}, t::Vector{Float64}, p::Vector{Float64} )
    @assert size( t, 1 ) == size( t, 1 )
    numberSamples = size( predMat, 1 );
    width         = size( predMat, 2 );
    f(p)          = objFunctionMSE( p,predMat=predMat, t=t )
    resultObject  = optimize( f, p );
    Optim.minimizer( resultObject )
  end

  """
      directOptimHinge( predMat::Matrix{Float64}, t::Vector{Float64}, p::Vector{Float64} )

      compute refined solution _for_ mixing parameter p.
      #Arguments
      - `predMat::Matrix{Float64}`: each column is the prediction _of_ one hypothesis.
      - `t::Vector{Float64}`:       label vector.
      - `p::Vector{Float64}`:       initial solution for mixing coefficients.
      #Return
      - `Vector{Float64}`:          improved initial solution.
  """
  function directOptimHinge( predMat::Matrix{Float64}, t::Vector{Float64}, p::Vector{Float64} )
    @assert size( t, 1 ) == size( t, 1 )
    numberSamples = size( predMat, 1 );
    width         = size( predMat, 2 );
    f(p)          = objFunctionHinge( p,predMat=predMat, t=t );
    resultObject  = optimize( f, p );
    Optim.minimizer( resultObject )
  end


import Bridge: ProcessOrCoefficients

"""
    invSolve!(::EulerMaruyama, Y, W::SamplePath, P::ProcessOrCoefficients)

Compute Wiener process `W` that would have been needed to be simulated in order
to obtain path `Y` under law `P` when using Euler-Maruyama numerical scheme
"""
function inv_solve!(::EulerMaruyama, Y, W::SamplePath, P::ProcessOrCoefficients
                  ) where {T}
    N = length(W)
    N != length(Y) && error("Y and W differ in length.")

    ww = W.yy
    tt = W.tt

    yy = Y.yy
    #tt[:] = Y.tt
    ww[.., N] = zero(ww[.., N])

    for i in N-1:-1:1
        yᵢ₊₁ = yy[.., i+1]
        yᵢ = yy[.., i]

        ww[.., i] = ww[.., i+1] - σ(tt[i], yᵢ, P)\(yᵢ₊₁ - yᵢ - _b((i,tt[i]), yᵢ, P)*(tt[i+1]-tt[i]))
    end
    for i in N:-1:1
        ww[.., i] = ww[.., i] - ww[.., 1]
    end
    W
end


Mcdf(f,fmin,fmax) = (1.0./f - 1.0/fmax) ./ (1.0/fmin - 1.0/fmax)

function selection(λ, f, tend, t1)
    #define the equation for selection as above
    s = (λ .* t1 + log.(f ./ (1 .- f))) ./ (λ .* (tend - t1))
    return s
end

function selection2clone(λ, f1, f2, tend, t1, t2)
    #define the equation for selection as above

    s1 = zeros(Float64, length(f1))
    s2 = zeros(Float64, length(f1))

    for i in 1:length(f1)
      if (f2[i] + f1[i]) < 1.0
        s1[i] = (λ .* t1[i] + log.(f1[i] ./ (1 - f1[i] - f2[i]))) ./ (λ .* (tend[i] - t1[i]))
        s2[i] = (λ .* t2[i] + log.(f2[i] ./ (1 - f1[i] - f2[i]))) ./ (λ .* (tend[i] - t2[i]))
      else
        s1[i] = (λ .* t1[i] + log.((f1[i] - f2[i]) ./ (1 - f1[i]))) ./ (λ .* (tend[i] - t1[i]))
        s2[i] = (λ .* t2[i] + log.(f2[i] ./ (1 - f1[i]))) ./ (λ .* (tend[i] - t2[i]))
      end
    end

    return s1, s2
end

function collectoutput1clone(abcres; Nmax = 10^10)

    scmuts = map(x -> x.other[2], abcres.particles)
    scdivs = map(x -> x.other[3], abcres.particles)
    scfreq = map(x -> x.other[4], abcres.particles)
    mu = abcres.parameters[:, 1]
    weights = abcres.weights

    # eulergamma/log(2) is stochastic correction see Durrett Branching Process Models of Cancer, needed for selection calculation
    t1 = ((shuffle(scmuts) ./ mu) / (2 * log.(2))) .- MathConstants.eulergamma/log.(2) #shuffle so that t1 posterior is not correlated
    tend = (log.(Nmax .* (1 .- scfreq)) / log.(2))
    s = selection(log.(2), scfreq, tend, t1)

    DF = DataFrame(mu = mu,
    clonalmutations = abcres.parameters[:, 2],
    s = s,
    t = t1,
    cellularity = abcres.parameters[:, 5],
    frequency = scfreq,
    scmuts = map(x -> Float64(x), scmuts),
    weight = weights)

    return DF
end

function swapvalues(x1, x2, indeces)

  swap1 = x1[indeces]
  swap2 = x2[indeces]
  x1[indeces] = swap2
  x2[indeces] = swap1

  return x1, x2
end

function clonesize(scfreq1, scfreq2)
  #assume that if scfreq1 + scfreq2 > 1 then clones are nested
  freqfactor = zeros(Float64, length(scfreq1))
  for i in 1:length(scfreq1)
    if (scfreq1[i] + scfreq2[i]) .> 1.0
      freqfactor[i] = 1 - scfreq1[i]
      freqfactor[i] = maximum([0.001, freqfactor[i]])
    else
      freqfactor[i] = 1 - scfreq1[i] - scfreq2[i]
      freqfactor[i] = maximum([0.001, freqfactor[i]])
    end
  end

  return freqfactor
end

function collectoutput2clone(abcres; Nmax = 10^10)

    scmuts1 = map(x -> x.other[2], abcres.particles)
    scmuts2 = map(x -> x.other[3], abcres.particles)
    scdivs1 = map(x -> x.other[4], abcres.particles)
    scdivs2 = map(x -> x.other[5], abcres.particles)
    scfreq1 = map(x -> x.other[6], abcres.particles)
    scfreq2 = map(x -> x.other[7], abcres.particles)
    mu = abcres.parameters[:, 1]

    s1 = abcres.parameters[:, 3]
    t1 = abcres.parameters[:, 4]
    s2 = abcres.parameters[:, 5]
    t2 = abcres.parameters[:, 6]
    weights = abcres.weights

    scmuts1 = map(x -> Float64(x), scmuts1)
    scmuts2 = map(x -> Float64(x), scmuts2)

    indeces = .!(scfreq1 .> scfreq2)

    #identify subclone 1 as the highest frequency subclone
    scfreq1, scfreq2 = swapvalues(scfreq1, scfreq2, indeces)
    scmuts1, scmuts2 = swapvalues(scmuts1, scmuts2, indeces)
    scdivs1, scdivs2 = swapvalues(scdivs1, scdivs2, indeces)
    s1, s2 = swapvalues(s1, s2, indeces)
    t1, t2 = swapvalues(t1, t2, indeces)

    freqfactor = clonesize(scfreq1, scfreq2)

    t1a = ((shuffle(scmuts1) ./ mu) / (2 * log.(2))) .- MathConstants.eulergamma/log.(2)
    t1b = ((shuffle(scmuts2) ./ mu) / (2 * log.(2))) .- MathConstants.eulergamma/log.(2)
    tend = (log.(Nmax .* (freqfactor)) / log.(2))
    s1, s2 = selection2clone(log.(2), scfreq1, scfreq2, tend, t1a, t1b)

    DF = DataFrame(mu = abcres.parameters[:, 1],
    clonalmutations = abcres.parameters[:, 2],
    s1 = s1,
    t1 = t1a,
    s2 = s2,
    t2 = t1b,
    cellularity = abcres.parameters[:, 7],
    frequency1 = scfreq1,
    frequency2 = scfreq2,
    scmuts1 = scmuts1,
    scmuts2 = scmuts2,
    weight = weights)

    return DF
end

function collectoutput0clone(abcres)

  mupost = abcres.parameters[:, 1]
  cmpost = abcres.parameters[:, 2]
  cellularity = abcres.parameters[:, 3]
  weights = abcres.weights

  DFpost = DataFrame(mu = mupost,
  clonalmutations = cmpost,
  cellularity = cellularity,
  weight = weights)
end

function cumulativedistribution(VAF; fmin = 0.1, fmax = 0.3)

    #calculate cumulative sum
    steps = fmax:-0.001:fmin
    cumsum = Array{Int64}(0)
    v = Array{Float64}(0)

    for i in steps
        push!(cumsum, sum(VAF .>= i))
        push!(v, i)
    end
    cumsum = cumsum - cumsum[1]

    DF = DataFrame(cumsum = map(Float64, cumsum), v = v)
    DF[:invf] = 1 ./ DF[:v] - 1 ./ fmax

    DF[:theory] = Mcdf(DF[:v], fmin, fmax)
    DF[:normalized] = DF[:cumsum] ./ maximum(DF[:cumsum])

    lmfit = fit(LinearModel, @formula(cumsum ~ invf + 0), DF)
    DF[:prediction] = predict(lmfit)

    return CancerSeqSim.AnalysedData(DF, VAF)
end

function averagehistogram(particles, model, VAF)

    x = 0.005:0.01:1.005
    y = fit(Histogram, VAF, x, closed=:right)
    DFhist = DataFrame(VAF = x[1:end-1], freq = y.weights)

    particles = particles[map(x -> x.model, particles).==model]
    wts = map(x -> x.weight, particles)
    N = length(particles)

    M = zeros(Int64, 100, N)
    i = 1

    for j in 1:N
        M[:, i] = convert(Array, particles[i].other[1][:freq])
        i = i + 1
    end

    mvalues = Float64[]
    meanvalues = Float64[]
    lquant = Float64[]
    lquart = Float64[]
    uquant = Float64[]
    uquart = Float64[]

    for i in 1:size(M, 1)
      push!(mvalues, median(vec(collect(M[i, :]')), weights(wts)))
      push!(meanvalues, mean(vec(collect(M[i, :]')), weights(wts)))
      push!(lquant, quantile(vec(collect(M[i, :]')), weights(wts), 0.025))
      push!(uquant, quantile(vec(collect(M[i, :]')), weights(wts), 0.975))
      push!(lquart, quantile(vec(collect(M[i, :]')), weights(wts), 0.25))
      push!(uquart, quantile(vec(collect(M[i, :]')), weights(wts), 0.75))
    end

    DFr = DataFrame(median = mvalues,
                    mean = meanvalues,
                    lowerq95 = lquant,
                    upperq95 = uquant,
                    lowerquartile = lquart,
                    upperquartile = uquart,
                    VAF = DFhist[:VAF],
                    truecounts = DFhist[:freq])

    return DFr
end

function saveresults(res::Results; resultsdirectory = "output")
  makedirectories(joinpath(resultsdirectory, res.SampleName, "finalpopulation"))
  getresults(res.ABCresults, resultsdirectory, res.SampleName, res.VAF, save = true)
  return
end

function getresults(abcres, resultsdirectory, sname, VAF; save = false, Nmaxinf = 10^10, savepopulations = false, popnum = 1)

  if savepopulations
      resultsdirectory = joinpath(resultsdirectory, sname, "populations", "population_$popnum")
  else
      resultsdirectory = joinpath(resultsdirectory, sname, "finalpopulation")
  end
  posteriors = Posterior[]
  #save model posterior
  DFmp = DataFrame(Model = map(x -> string(x),0:length(abcres.modelprob) - 1), Probability = abcres.modelprob)

  if abcres.modelprob[1] > 0.0
    DFpost0 = collectoutput0clone(getmodel(abcres, 1))
    DFr = averagehistogram(abcres.particles, 1, VAF)
    if save == true
      CSV.write(joinpath(resultsdirectory, "posterior", "$(sname)-parameters-clone0.csv"), DFpost0)
      CSV.write(joinpath(resultsdirectory, "posterior", "$(sname)-histogram-clone0.csv"), DFr)
    end
    push!(posteriors, Posterior(DFr, DFpost0, abcres.modelprob[1]))
  else
    push!(posteriors, Posterior(DataFrame(), DataFrame(), abcres.modelprob[1]))
  end

  if abcres.modelprob[2] > 0.0
    DFpost1 = collectoutput1clone(getmodel(abcres, 2), Nmax = Nmaxinf)
    DFr = averagehistogram(abcres.particles, 2, VAF)
    if save == true
      CSV.write(joinpath(resultsdirectory, "posterior", "$(sname)-histogram-clone1.csv"), DFr)
      CSV.write(joinpath(resultsdirectory, "posterior", "$(sname)-parameters-clone1.csv"), DFpost1)
    end
    push!(posteriors, Posterior(DFr, DFpost1, abcres.modelprob[2]))
  else
    push!(posteriors, Posterior(DataFrame(), DataFrame(), abcres.modelprob[2]))
  end

  if (length(abcres.modelprob) > 2) && (abcres.modelprob[3] > 0.0)
    DFpost2 = collectoutput2clone(getmodel(abcres, 3), Nmax = Nmaxinf)
    DFr = averagehistogram(abcres.particles, 3, VAF)
    if save == true
      CSV.write(joinpath(resultsdirectory, "posterior", "$(sname)-parameters-clone2.csv"), DFpost2)
      CSV.write(joinpath(resultsdirectory, "posterior", "$(sname)-histogram-clone2.csv"), DFr)
    end
    push!(posteriors, Posterior(DFr, DFpost2, abcres.modelprob[3]))
  else
    if (length(abcres.modelprob) > 2)
      push!(posteriors, Posterior(DataFrame(), DataFrame(), abcres.modelprob[3]))
    end
  end

  DF = DataFrame(Model = map(x -> string(x),0:length(abcres.modelprob) - 1), Probability = abcres.modelprob)
  if save == true
    CSV.write(joinpath(resultsdirectory, "posterior", "$(sname)-modelprobabilities.csv"), DF)
  end

  return posteriors, DFmp
end

function makedirectories(resultsdirectory)

  if isdir(resultsdirectory) == false
    mkpath(resultsdirectory)
  end

  if isdir(joinpath(resultsdirectory, "plots")) == false
    mkpath(joinpath(resultsdirectory, "plots"))
  end
  if isdir(joinpath(resultsdirectory, "processed")) == false
    mkpath(joinpath(resultsdirectory, "processed"))
  end

  if isdir(joinpath(resultsdirectory, "posterior")) == false
    mkpath(joinpath(resultsdirectory, "posterior"))
  end

  if isdir(joinpath(resultsdirectory, "data")) == false
    mkpath(joinpath(resultsdirectory, "data"))
  end

end

function makeplotsdirectories(resultsdirectory)
  if isdir(joinpath(resultsdirectory)) == false
    mkpath(joinpath(resultsdirectory))
  end
  if isdir(joinpath(resultsdirectory, "plots")) == false
    mkpath(joinpath(resultsdirectory, "plots"))
  end
end

function makedirectory(resultsdirectory)
  if isdir(joinpath(resultsdirectory)) == false
    mkpath(joinpath(resultsdirectory))
  end
end

function show(io::IO, res::Results)
  show(res.ABCresults)
end

function show(io::IO, ABCresults::ApproxBayes.ABCSMCmodelresults)

  posteriors, DFmp = getresults(ABCresults, "nothing", "nothing", [1.0, 2.0]; save = false, Nmaxinf = ABCresults.setup.other)

  @printf("Total number of simulations: %.2e\n", sum(ABCresults.numsims))
  println("Cumulative number of simulations = $(cumsum(ABCresults.numsims))")
  @printf("Acceptance ratio: %.2e\n\n", ABCresults.accratio)
  println("Tolerance schedule = $(round.(ABCresults.ϵ, digits = 2))\n")

  print("Model probabilities:\n")
  for j in 1:length(ABCresults.modelprob)
    @printf("\tModel %d (%d subclones): %.3f\n", j, j-1, ABCresults.modelprob[j])
  end

  print("\nParameters:\n\n")

  for j in 1:length(ABCresults.parameters)
    if ABCresults.modelprob[j] > 0.0
      print("Model $j ($(j-1) subclones)\n")

      upperci = zeros(Float64, size(posteriors[j].Parameters, 2) - 1)
      lowerci = zeros(Float64, size(posteriors[j].Parameters, 2) - 1)
      parametermeans = zeros(Float64, size(posteriors[j].Parameters, 2) - 1)
      parametermedians = zeros(Float64, size(posteriors[j].Parameters, 2) - 1)

      for i in 1:(size(posteriors[j].Parameters, 2) - 1)
        parametermeans[i] = mean(posteriors[j].Parameters[:, i],
        weights(posteriors[j].Parameters[:weight]))
        parametermedians[i] = median(posteriors[j].Parameters[:, i],
        weights(posteriors[j].Parameters[:weight]))
        (lowerci[i], upperci[i]) = quantile(posteriors[j].Parameters[:, i],
        weights(posteriors[j].Parameters[:weight]), [0.025,0.975])
      end

      print("\tMedian (95% intervals):\n")
      for i in 1:length(parametermeans)
          if j == 1
            parameternames = ["μ/β", "Clonal Mutations", "Cellularity"]
          elseif j == 2
            parameternames = ["μ/β", "Clonal Mutations", "Fitness", "Time (tumour doublings)", "Cellularity", "Subclone Frequency", "Subclone Mutations"]
          elseif j == 3
            parameternames = ["μ/β", "Clonal Mutations", "Fitness - Subclone 1", "Time (tumour doublings) - Subclone 1", "Fitness - Subclone 2", "Time (tumour doublings) - Subclone 2", "Cellularity", "Subclone 1 Frequency", "Subclone 2 Frequency", "Subclone 1 Mutations", "Subclone 2 Mutations"]
          end
          @printf("\tParameter %d - %s: %.2f (%.2f,%.2f)\n", i, parameternames[i], parametermedians[i], lowerci[i], upperci[i])
      end
    end
  end
end


using StructJuMP, JuMP
using StructJuMPSolverInterface

include("select_solver.jl")

#############
# A sample model
#############
scen = 1
m = StructuredModel(num_scenarios=scen)
@variable(m, x[1:4])
# @variable(m, -100<=x[1:4]<=100)
@NLobjective(m, Min,  1*x[1] + 3*x[3] + 4*x[4] )

for i in 1:scen
    bl = StructuredModel(parent=m)
    # @variable(bl, -200<=y1[1:3]<=200)
    @variable(bl, y1[1:3])

    @NLconstraint(bl, -100<= 0.1*x[1] + 0.2*x[2]                          <= 100)
    @NLconstraint(bl, -101<=               1.1*x[2] + 1.2*x[3]            <= 101)
    @NLconstraint(bl, -102<=                          2.1*x[3] + 2.2*x[4] <= 102)

    # @NLconstraint(bl,  9.1*x[1]           + 9.2*x[3]            -109 == 0)
    # @NLconstraint(bl,            8.1*x[2] + 8.2*x[3]            -108 == 0)
    # @NLconstraint(bl,                       7.1*x[3] + 7.2*x[4] -107 == 0)
    @NLconstraint(bl, 1*x[3] + 2*x[4]  == 107 )

    @NLconstraint(bl, -201<= 10.1*x[1] + 10.2*x[2]                           - 10.3*y1[1] + 10.4*y1[2]               <= 201)
    @NLconstraint(bl, -202<=               11.1*x[2] + 11.2*x[3]             - 11.3*y1[1] + 11.4*y1[2] + 11.5*y1[3]  <= 202)
    @NLconstraint(bl, -203<=                           12.1*x[3] + 12.2*x[4]              + 12.4*y1[2] - 12.5*y1[3]  <= 203)

    @NLobjective(bl, Min, 12*y1[2] + 13*y1[3])
end

structJuMPSolve(m)

getVarValue(m)


# using Plots
using RecipesBase
# IDEA: use MAKIE for faster compile?
# IDEA: 2d and 3d recipies
# default range
plotting_range(G::Gaussian) = range(-1, 1, length = 256)
function plotting_range(G::Gaussian{<:ConditionalMean})
	range(minimum(G.μ.x) - std(G.μ.x), maximum(G.μ.x) + std(G.μ.x), length = 256)
end

@recipe function f(G::Gaussian, x::AbstractVector = plotting_range(G))
	seriestype  :=  :path
	μ = mean(G, x)
	σ = std(G).(x)
	ribbon := 2σ
	(x, μ)
end

# IDEA: add a few samples to the plot (currently requires a dense factorization)
# if n > 0
# 	s = sample(f, n, tol = 1e-6)
# 	plot!(xs, s, seriesalpha = .7)
# end


function _eval_function(f, u, scale, precond, diff_kernel; n=50, iter=1, α=0.1, rng_seed=0)
    x = range(-5, 5; length=n)
    dx = x[2] - x[1]

    # add noise to data
    rng = MersenneTwister(rng_seed)
    data = f.(x) + 0.05 * (rand(rng, n) .- 0.5)

    # use tvdiff
    û = tvdiff(data, iter, α; dx=dx, scale=scale, precond=precond, diff_kernel=diff_kernel)

    return rmse = √(mean(abs2.(û - u.(x))))
end

function _test_symbolic_functions(scale, precond, diff_kernel)
    @testset "abs" begin
        @test _eval_function(abs, sign, scale, precond, diff_kernel; n=50, iter=2) < 0.3
        @test _eval_function(abs, sign, scale, precond, diff_kernel; n=50, iter=10) < 0.26
        @test _eval_function(abs, sign, scale, precond, diff_kernel; n=1000, iter=2) < 0.3
        @test _eval_function(abs, sign, scale, precond, diff_kernel; n=1000, iter=10) < 0.14
    end
    @testset "sigmoid" begin
        σ(x) = exp(x) / (1 + exp(x))
        dσdx(x) = σ(x) - exp(2 * x) / (1 + exp(x)^2)

        @test _eval_function(σ, dσdx, scale, precond, diff_kernel; n=50, iter=2) < 0.3
        @test _eval_function(σ, dσdx, scale, precond, diff_kernel; n=50, iter=10) < 0.17
        @test _eval_function(σ, dσdx, scale, precond, diff_kernel; n=1000, iter=2) < 0.3
        @test _eval_function(σ, dσdx, scale, precond, diff_kernel; n=1000, iter=10) < 0.17
    end
    @testset "sin" begin
        α = 0.1 # use lower value for alpha
        @test _eval_function(sin, cos, scale, precond, diff_kernel; α=α, n=50, iter=2) < 0.3
        @test _eval_function(sin, cos, scale, precond, diff_kernel; α=α, n=50, iter=10) <
            0.27
        @test _eval_function(sin, cos, scale, precond, diff_kernel; α=α, n=1000, iter=2) <
            0.3
        @test _eval_function(sin, cos, scale, precond, diff_kernel; α=α, n=1000, iter=10) <
            0.19
    end
end

function _testset_symbolic_functions(scale, preconds, diff_kernels)
    @testset "Symbolic fcs" begin
        for precond in preconds
            @testset "$precond" begin
                for diff_kernel in diff_kernels
                    @testset "$diff_kernel" begin
                        _test_symbolic_functions(scale, precond, diff_kernel)
                    end
                end
            end
        end
    end
end


# loading the Julia packages  needed.
using DifferentialEquations
using LinearAlgebra, DiffEqSensitivity, Optim
using Flux: flatten, params, relu
using DiffEqFlux, Flux
using Plots
using Flux: train!
using GalacticOptim
using Optim
using Turing, Distributions
using MCMCChains, StatsPlots
using CSV, DataFrames
using SymbolicRegression
using Random
Random.seed!(14);
source_data = DataFrame(CSV.File("./DeepLearningEffectiveReproductionNumber/Source_Data/Provincial_Daily_Totals.csv"))
data_on = source_data[source_data.Province.=="ONTARIO", :]
n = 30
m = 149
data_acc = data_on.TotalCases[(n+1):n+m+1]
data_daily = data_on.TotalCases[(n+1):n+m+1] - data_on.TotalCases[n:n+m]
display(plot(data_daily, label = "Daily Confirmed Cases", lw = 2))
display(plot(data_acc, label = "Accumulated Confirmed Cases", lw = 2))
data_daily[1]
println(length(data_acc))

# Model generation
ann = FastChain(FastDense(1, 32, tanh), FastDense(32, 1))
p_0 = initial_params(ann)
function SIR_nn(du, u, p, t)
    I, H = u
    du[1] = 0.1 * min(5, abs(ann(t, p)[1])) * I - 0.1 * I
    du[2] = 0.1 * min(5, abs(ann(t, p)[1])) * I
end
u_0 = Float32[1, data_acc[1]]
tspan_data = (0.0f0, 149.0f0)
prob_nn = ODEProblem(SIR_nn, u_0, tspan_data, p_0)
function train(θ)
    solvedata = Array(concrete_solve(prob_nn, Vern7(), u_0, θ, saveat = 1,
        abstol = 1e-6, reltol = 1e-6,
        sensealg = InterpolatingAdjoint(autojacvec = ReverseDiffVJP())))
end
function loss(θ)
    pred = train(θ)
    sum(abs2, (log.(data_acc) .- log.(pred[2, 1:150]))), pred # + 1e-5*sum(sum.(abs, params(ann)))
end

const losses = []
callback(θ, l, pred) = begin
    push!(losses, l)
    if length(losses) % 50 == 0
        println(losses[end])
    end
    false
end

@time res1_node = DiffEqFlux.sciml_train(loss, p_0, ADAM(0.06), cb = callback, maxiters = 1000)
@time res2_node = DiffEqFlux.sciml_train(loss, res1_node.minimizer, BFGS(initial_stepnorm = 0.01), cb = callback, maxiters = 300)
ann_param = res2_node.minimizer

using BSON: @save
@save "./DeepLearningEffectiveReproductionNumber/Saving_Data/ann_nn_ir.bason" ann
@save "./DeepLearningEffectiveReproductionNumber/Saving_Data/ann_para_irbfgs100.bason" ann_param
@time res3_node = DiffEqFlux.sciml_train(loss, res2_node.minimizer, BFGS(initial_stepnorm = 0.01), cb = callback, maxiters = 200)
ann_param = res3_node.minimizer
@save "./DeepLearningEffectiveReproductionNumber/Saving_Data/ann_para_irbfgs500.bason" ann_param

p_min = ann_param
tspan_predict = (0.0, 149)
scatter(data_acc, label = "Real accumulated cases")
prob_prediction = ODEProblem(SIR_nn, u_0, tspan_data, p_min)
data_prediction = Array(solve(prob_prediction, Tsit5(), saveat = 1))
plot!(data_prediction[2, :], label = "Fit accumulated cases")

xlabel!("Days after Feb 25")

tspan = collect(0:1:149)'
ann_value = abs.(ann(tspan, p_min))'
plot(ann_value, label = "Effective reproduction number")
xlabel!("Days after Feb 25")


using Random, Distributions, FreqTables, Statistics
using LinearAlgebra
using JLD2, CSV, DataFrames

Random.seed!(1234)

A = rand(Uniform(-5,10), 10,7)


#----------------------------------------------------------#
# Build the objective function for LaplacianOptModel here  #
#----------------------------------------------------------#

function objective_maximize_algebraic_connectivity(lom::LaplacianOptModel)
    JuMP.@objective(lom.model, Max, lom.variables[:γ_var])
end

function objective_maximize_spanning_tree_cost(lom::LaplacianOptModel)
    n = lom.data["num_nodes"]
    edge_weights = lom.data["edge_weights"]

    JuMP.@objective(lom.model, Max, sum(edge_weights[i,j] * lom.variables[:z_var][i,j] for i=1:(n-1), j=(i+1):n))
end


################################################################################
#                          TEST: DCM <=> Euler Angles
################################################################################

for k = 1:samples
    # Nominal cases
    # --------------------------------------------------------------------------
    for rot_seq in rot_seq_array
        # Sample three angles form a uniform distribution [-pi,pi].
        θx = -pi + 2*pi*rand()
        θy = -pi + 2*pi*rand()
        θz = -pi + 2*pi*rand()
        Θ  = EulerAngles(θx, θy, θz, rot_seq)

        # Get the error matrix related to the conversion from DCM => Euler
        # Angles => DCM.
        error1 = angle_to_rot(DCM, dcm_to_angle(angle_to_rot(Θ),rot_seq)) -
                 angle_to_rot(DCM, θx, θy, θz, rot_seq)
        error2 = angle_to_rot(dcm_to_angle(angle_to_rot(θx, θy, θz, rot_seq),rot_seq)) -
                 angle_to_rot(Θ)

        # If everything is fine, the norm of the matrix error should be small.
        @test norm(error1) < 1e-10
        @test norm(error2) < 1e-10
        @test error1 ≈ error2
    end

    # Gimbal-lock and special cases
    # --------------------------------------------------------------------------

    # Rotation about the three axes
    # -----------------------------
    for rot_seq in [:ZYX, :ZXY, :YZX, :YXZ, :XZY, :XYZ]
        # A 90 deg rotation about the second axis yields a gimbal-lock.
        θ₁  = -π + 2π*rand()
        θ₃  = -π + 2π*rand()

        Dr1 = angle_to_dcm(θ₁, +π/2, θ₃, rot_seq)
        Dr2 = angle_to_dcm(θ₁, -π/2, θ₃, rot_seq)

        Θr1 = dcm_to_angle(Dr1, rot_seq)
        Θr2 = dcm_to_angle(Dr2, rot_seq)

        θs1, θs2 = (rot_seq in [:ZYX, :YXZ, :XZY]) ? (θ₁ - θ₃, θ₁ + θ₃) :
                                                     (θ₁ + θ₃, θ₁ - θ₃)

        # The angles must be between [-π,+π].
        θs1 = mod(θs1, 2π)
        θs2 = mod(θs2, 2π)

        (θs1 > π) && (θs1 -= 2π)
        (θs2 > π) && (θs2 -= 2π)

        @test Θr1.a1 ≈ θs1   atol = 1e-14
        @test Θr1.a2 ≈ +π/2  atol = 1e-14
        @test Θr1.a3 ≈ 0     atol = 1e-14

        @test Θr2.a1 ≈ θs2   atol = 1e-14
        @test Θr2.a2 ≈ -π/2  atol = 1e-14
        @test Θr2.a3 ≈ 0     atol = 1e-14
    end
    break

    # Rotation about two axes
    # -----------------------
    Rx = create_rotation_matrix(π, :X)
    Ry = create_rotation_matrix(π, :Y)
    Rz = create_rotation_matrix(π, :Z)
    α  = -pi + 2*pi*rand()

    # Two rotations about X
    D  = create_rotation_matrix(α, :X)
    Θ₁ = dcm_to_angle(D, :XYX)
    Θ₂ = dcm_to_angle(D, :XZX)
    @test Θ₁.a1 ≈ α
    @test Θ₁.a2 ≈ 0
    @test Θ₁.a3 ≈ 0
    @test Θ₂.a1 ≈ α
    @test Θ₂.a2 ≈ 0
    @test Θ₂.a3 ≈ 0

    Θ₁ = dcm_to_angle(D*Ry, :XYX)
    Θ₂ = dcm_to_angle(D*Rz, :XZX)
    @test Θ₁.a1 ≈ -α
    @test Θ₁.a2 ≈ π
    @test Θ₁.a3 ≈ 0
    @test Θ₂.a1 ≈ -α
    @test Θ₂.a2 ≈ π
    @test Θ₂.a3 ≈ 0

    # Two rotations about Y
    D  = create_rotation_matrix(α, :Y)
    Θ₁ = dcm_to_angle(D, :YXY)
    Θ₂ = dcm_to_angle(D, :YZY)
    @test Θ₁.a1 ≈ α
    @test Θ₁.a2 ≈ 0
    @test Θ₁.a3 ≈ 0
    @test Θ₂.a1 ≈ α
    @test Θ₂.a2 ≈ 0
    @test Θ₂.a3 ≈ 0

    Θ₁ = dcm_to_angle(D*Rx, :YXY)
    Θ₂ = dcm_to_angle(D*Rz, :YZY)
    @test Θ₁.a1 ≈ -α
    @test Θ₁.a2 ≈ π
    @test Θ₁.a3 ≈ 0
    @test Θ₂.a1 ≈ -α
    @test Θ₂.a2 ≈ π
    @test Θ₂.a3 ≈ 0

    # Two rotations about Z
    D  = create_rotation_matrix(α, :Z)
    Θ₁ = dcm_to_angle(D, :ZXZ)
    Θ₂ = dcm_to_angle(D, :ZYZ)
    @test Θ₁.a1 ≈ α
    @test Θ₁.a2 ≈ 0
    @test Θ₁.a3 ≈ 0
    @test Θ₂.a1 ≈ α
    @test Θ₂.a2 ≈ 0
    @test Θ₂.a3 ≈ 0

    Θ₁ = dcm_to_angle(D*Rx, :ZXZ)
    Θ₂ = dcm_to_angle(D*Ry, :ZYZ)
    @test Θ₁.a1 ≈ -α
    @test Θ₁.a2 ≈ π
    @test Θ₁.a3 ≈ 0
    @test Θ₂.a1 ≈ -α
    @test Θ₂.a2 ≈ π
    @test Θ₂.a3 ≈ 0
end

for k = 1:samples
    # Sample three angles form a uniform distribution [-0.0001,0.0001].
    θx = -0.0001 + 0.0002*pi*rand()
    θy = -0.0001 + 0.0002*pi*rand()
    θz = -0.0001 + 0.0002*pi*rand()
    Θ  = EulerAngles(θx, θy, θz, :XYZ)

    # Get the error between the exact rotation and the small angle
    # approximation.
    error1 = angle_to_rot(Θ) - smallangle_to_rot(DCM, Θ.a1, Θ.a2, Θ.a3)
    error2 = angle_to_rot(θx, θy, θz, :XYZ) - smallangle_to_rot(Θ.a1, Θ.a2, Θ.a3)

    # If everything is fine, the norm of the matrix error should be small.
    @test norm(error1) < 1e-9
    @test norm(error2) < 1e-9
    @test error1 ≈ error2
end

# Test orthonormalization.
let
    θx = -0.01 + 0.02*pi*rand()
    θy = -0.01 + 0.02*pi*rand()
    θz = -0.01 + 0.02*pi*rand()

    D1 = smallangle_to_rot(θx, θy, θz)
    D2 = smallangle_to_rot(θx, θy, θz; normalize = false)

    @test norm(I - D1*D1') ≈    0 atol = 1e-15
    @test norm(I - D2*D2') > 1e-7
end

# Test exceptions.
@test_throws ArgumentError angle_to_dcm(0,0,0,:xyz)
@test_throws ArgumentError angle_to_dcm(0,0,0,:zyx)
@test_throws ArgumentError angle_to_dcm(0,0,0,:xyx)
@test_throws ArgumentError angle_to_dcm(0,0,0,:abc)
@test_throws ArgumentError angle_to_quat(0,0,0,:xyz)
@test_throws ArgumentError angle_to_quat(0,0,0,:zyx)
@test_throws ArgumentError angle_to_quat(0,0,0,:xyx)
@test_throws ArgumentError angle_to_quat(0,0,0,:abc)


function build_globalmin{T <: Union(ASCIIString, UTF8String)}(list::Array{T})

  stocks  = stock_returns(list)
  params  = modelparams(stocks)
  weights = rowsums(DataArray(inv(params[5]))) / sum(inv(params[5]))

  port = Portfolio(params[1],
                   params[2],
                   params[3],
                   params[4],
                   params[5],
                   weights)
  port
end

function build_efficient(x)
  #code here
end

function build_tangency(x)
  #code here
end


function main()
	N = 20  +1 # num grid points = num squares +1

	# at border, only one path possible
	# init grid with what is known
	grid = zeros(eltype(N),N,N)
	for i = 1:N
		grid[i,N] = 1
		grid[N,i] = 1
	end

	# fill middle of grid
	for i = N-1:-1:1, j = N-1:-1:1
		grid[i,j] = grid[i+1,j] + grid[i,j+1]
	end

	println(grid)
	println(grid[1,1])
end

main()


my_f(x,y) = 2x+3y
derivative_of_my_f(x,y) = ForwardDiff.derivative(x->my_f(x,y),x)


function estimate(setup::Setup)
    y, m, P, S, β, δ = setup.y, setup.m, setup.P, setup.S, setup.β, setup.δ

    T, p = size(y)
    d = get_d(setup)
    a = d*p + 1
    F = [1; zeros(a - 1)]
    k = get_k(p, β)
    ν = get_ν(β)

    out = Estimation(y,               # y
                     zeros(T, a, p),  # m
                     zeros(T, a, a),  # P
                     zeros(T, p, p),  # S
                     zeros(T, p),     # μ
                     zeros(T, p, p),  # Σ
                     zeros(T, p),     # e
                     zeros(T, p),     # u
                     ν,
                     β,
                     δ)

    for t = 1:T
        if d > 1 && t > d
            F[2:end] = vec(y[t-1:-1:t-d, :])
        end

        # Predict parameters at time t|t-1
        P, S, Q = predict_parameters(P, S, F, δ, k)

        # Predict at time t|t-1
        μ, Σ = predict(m, Q, S, F, β)

        # Prediction error
        e = error(y[t, :], μ)
        u = standardised_error(e, Σ)

        # Storage
        out.m[t, :, :] = m
        out.P[t, :, :] = P
        out.S[t, :, :] = S
        out.μ[t, :]    = μ
        out.Σ[t, :, :] = Σ
        out.e[t, :]    = e
        out.u[t, :]    = u

        # Update at time t|t
        if t > d
            m, P, S = update_parameters(m, P, Q, S, F, e)
        end

    end
    return out
end

function predict_parameters(P, S, F, δ, k)
    # note: m is not updated
    P = P / δ
    Q = F' * P * F + 1.0
    S = S / k
    return (P, S, Q)
end

function predict(m, Q, S, F, β)
    μ = m' * F
    Σ = Q * (1 - β) / (3β - 2) * S
    return (μ, Σ)
end

function error(y, μ)
    return y - μ
end

function standardised_error(e, Σ)
    return inv(cholesky(Σ).L) * e
end

function update_parameters(m, P, Q, S, F, e)
    K = P * F / Q
    m = m + K * e'
    P = P - K * K' * Q
    S = S + e*e'/Q
    return (m, P, S)
end


export get_L,
       get_Q,
       get_B,
       get_Sel,
       get_Exp

"""
## Create FDTD Laplatian matrix
* usage: L = get_L(g::CuboidRoom)
"""
function get_L(g::CuboidRoom)

	Dx = spdiagm((-2*ones(g.Nx), ones(g.Nx-1), ones(g.Nx-1)),(0,1,-1),g.Nx,g.Nx )
	Dy = spdiagm((-2*ones(g.Ny), ones(g.Ny-1), ones(g.Ny-1)),(0,1,-1),g.Ny,g.Ny )
	Dz = spdiagm((-2*ones(g.Nz), ones(g.Nz-1), ones(g.Nz-1)),(0,1,-1),g.Nz,g.Nz )

	#perfect reflection in phase (forward diff)
	Dx[1,2],Dy[1,2],Dz[1,2] =  1, 1, 1
	Dx[1,1],Dy[1,1],Dz[1,1] =  -1, -1, -1
	Dx[end, end-1], Dy[end, end-1], Dz[end, end-1] =   1, 1, 1
	Dx[end, end], Dy[end, end], Dz[end, end] =   -1, -1, -1

	Dxx = kron(speye(g.Nz),kron(speye(g.Ny), Dx))
	Dyy = kron(speye(g.Nz),kron(Dy, speye(g.Nx)))
	Dzz = kron(Dz, kron(speye(g.Ny),speye(g.Nx)))

	L = Dxx+Dyy+Dzz+g.env.scheme.a*(Dxx*Dyy+Dxx*Dzz+Dyy*Dzz)+g.env.scheme.b*(Dxx*Dyy*Dzz)

	return L
end

"""
## Create FDTD loss matrix Q
* usage: Q = get_Q(g::CuboidRoom)
"""
function get_Q(g::CuboidRoom)

	qx = spdiagm([1/(g.ξ[1]*2);zeros(g.Nx-2);1/(g.ξ[2]*2)]) #factor 2 due to fwd diff
	qy = spdiagm([1/(g.ξ[3]*2);zeros(g.Ny-2);1/(g.ξ[4]*2)])
	qz = spdiagm([1/(g.ξ[5]*2);zeros(g.Nz-2);1/(g.ξ[6]*2)])

	Qx = kron(speye(g.Nz),kron(speye(g.Ny),qx))
	Qy = kron(speye(g.Nz),kron(qy,speye(g.Nx)))
	Qz = kron(qz,kron(speye(g.Ny),speye(g.Nx)))

	return Qx+Qy+Qz
end

"""
## Create FDTD matrix B
* usage: B = get_B(g::CuboidRoom, Nt::Int64)
"""
function get_B(g::CuboidRoom, Nt::Int64)

	L = get_L(g)
	Q = get_Q(g)

	λ2 = (g.env.scheme.λ)^2
	Nxyz = size(L,1)

	Qm,A,Qp = get_QmAQp(g)

	B =(kron(speye(Nt+2),spdiagm(Qp))
	+kron(spdiagm(ones(Nt+2-1),-1,Nt+2,Nt+2),-A)
	+kron(spdiagm(ones(Nt+2-2),-2,Nt+2,Nt+2),-spdiagm(Qm)))

	B[1:2*Nxyz,1:2*Nxyz] = speye(2*Nxyz) #initial conditions

	return B

end

"""
## Create selection matrix
* usage: F = get_Sel(g::CuboidRoom,Nt::Int,xr::Vector{Tuple{Int,Int,Int}})
"""
function get_Sel(g::CuboidRoom,Nt::Int, xr::Vector{Tuple{Int,Int,Int}})

	Ks = length(xr)   #Ks number of mics

	indxr = [sub2ind((g.Nx,g.Ny,g.Nz),x...) for x in xr]

	Nxyz = g.Nx*g.Ny*g.Nz

	indposvec = zeros(Int64,Ks*Nt)
	for k =1:Ks
		for i = 1:Nt
			indposvec[i+(k-1)*Nt] = indxr[k]
			indxr[k] += Nxyz
		end
	end
	indposvec = indposvec+2*Nxyz
	F =  sparse((1:Ks*Nt),indposvec,ones(Int64,Ks*Nt),Ks*Nt,Nxyz*(Nt+2))

	return F

end

"""
## Create selection matrix
* usage: E = get_Exp(g::CuboidRoom,Nt::Int64,pos::Vector{Tuple{Int,Int,Int}})
"""
function get_Exp(g::CuboidRoom,Nt::Int,pos::Vector{Tuple{Int,Int,Int}})

	Ks = length(pos)   #Ks number of sources

	indposm = [sub2ind((g.Nx,g.Ny,g.Nz) ,x...) for x in pos]

	Nxyz = g.Nx*g.Ny*g.Nz

	indposvec = zeros(Int64,Ks*Nt)
	for k =1:Ks
		for i = 1:Nt
			indposvec[i+(k-1)*(Nt)] = indposm[k]
			indposm[k] = indposm[k]+Nxyz
		end
	end
	indposvec = indposvec +2*Nxyz #remove ICs
	E =  sparse(indposvec,(1:Ks*Nt),ones(Int64,Ks*Nt),Nxyz*(Nt+2),Ks*Nt)

	return E

end

function get_QmAQp(geo::CuboidRoom)

	λ2 = (geo.env.scheme.λ)^2
	Q = get_Q(geo)
	L = get_L(geo)

	Qm = geo.env.scheme.λ*diag(Q)-1
	A  = λ2*L+2*speye(geo.Nx*geo.Ny*geo.Nz)
	Qp = geo.env.scheme.λ*diag(Q)+1

	return Qm,A,Qp
end


module QuantumComputing
import Base: convert, show, kron, replace, length, sub, split

export N,E,Id,X,Y,Z,H,CNOT,SWAP,Q
export Ket,Bra
export GGate,GCNot,CNot,Not,Swap,Hadamard,VentureMartinez,M
export append,appendhead,extract

# |0> NULL
# |1> EINS
const N = sparse(complex([1.0,0.0]))
const E = sparse(complex([0.0,1.0]))

# Unit
const Id = complex(speye(2))

# Pauli matrices
const X = sparse(complex([0.0 1.0;1.0 0.0]))
const Y = sparse([0.0 -im; im 0.0])
const Z = sparse(complex([1.0 0.0; 0.0 -1.0]))

# Hadamard matrix
const H = sparse(1.0/sqrt(2)*complex([1.0 1.0; 1.0 -1.0]))

# 2 q-bits operator
const CNOT = blkdiag(Id,X)
const SWAP = hcat(kron(N,N),kron(E,N),kron(N,E),kron(E,E))

const Q = H*N

const sbra = "\u27E8" # <
const svbar = "\u007C" # |
const sket = "\u27E9" # >

abstract QGate

# 0->N, 1->E
function parse01(x)
    if x==0
       return N
    elseif x == 1
       return E
    else
       return x
    end
end

immutable Ket
    v::SparseMatrixCSC
    function Ket(x...)
        v = map(parse01, x)
        new(kron(v...))
    end
    function Ket(v::SparseMatrixCSC)
        new(v)
    end
end

immutable GGate <: QGate
    v::SparseMatrixCSC
    function GGate(v)
        new(v)
    end
end

immutable CNot <: QGate
    v::SparseMatrixCSC
    t::Integer
    c::Integer
    function CNot(n,t,c)
        M1 = Swap(n,1,c).v
        M2 = Swap(n,2,t==1?2:t).v
        v = M1*M2*kron(CNOT,[Id for i=1:n-2]...)*M2*M1
        new(v,t,c)
    end
end

immutable GCNot <: QGate
    v::SparseMatrixCSC
    t::Integer
    c
    function GCNot(n,t,c...)
        nc = length(c)
        v = blkdiag([Id for i=1:2^nc]...)
        v[end-1:end,end-1:end] = X

        Mf = SparseMatrixCSC[]
        c = sort([c...])
        idx=1
        tp = t
        for i in filter(x->x>nc,c)
            while (idx in c)
                idx+=1
                @assert(idx<=nc)
            end
            push!(Mf,Swap(n,idx,i).v)
            if idx==t
                tp = i
            end
            idx+=1
        end
        push!(Mf,Swap(n,nc+1,tp).v)
        v = reduce(*,Mf)*kron(v,[Id for i=1:n-nc-1]...)*reduce(*,reverse(Mf))

        new(v,t,c)
    end
end

immutable Not <: QGate
    v::SparseMatrixCSC
    t::Integer
    function Not(n,t)
        v = [Id for i=1:n]
        v[t] = X
        new(kron(v...),t)
    end
end

immutable Swap <: QGate
    v::SparseMatrixCSC
    t1::Integer
    t2::Integer
    function Swap(n,t1,t2)
        mint = min(t1,t2)
        maxt = max(t1,t2)
        v = kron([Id for i=1:n]...)
        for t=mint:maxt-1
            tv = kron([Id for i=1:t-1]...,SWAP,[Id for i=t+1:n-1]...)
            v *= tv
        end
        for t=maxt-2:-1:mint
            tv = kron([Id for i=1:t-1]...,SWAP,[Id for i=t+1:n-1]...)
            v *= tv
        end
        new(v,t1,t2)
    end
end

immutable Hadamard <: QGate
    v::SparseMatrixCSC
    t::Integer
    function Hadamard(n,t)
        v = [Id for i=1:n]
        v[t] = H
        new(kron(v...),t)
    end
end

immutable VentureMartinez <: QGate
    v::SparseMatrixCSC
    t::Integer
    c::Integer
    j::Integer
    s::Integer
    function VentureMartinez(n,t,c,j,s=-1)
        S = complex(blkdiag(
        speye(2),
        sparse(
        [sqrt((j-1.0)/j) -s/sqrt(j);s/sqrt(j) sqrt((j-1.0)/j)])
        ))
        M1 = Swap(n,1,c).v
        M2 = Swap(n,2,t==1?2:t).v
        v = M1*M2*kron(S, [Id for i=1:n-2]...)*M2*M1
        new(v,t,c,j,s)
    end
end

immutable M <: QGate
    v::SparseMatrixCSC
    m::Complex
    function M(n,m)
        v = complex(spzeros(2^n,2^n))
        v[1,1] = zero(eltype(v))
        for i=2:2^n
            v[i,i] = sqrt(1/(1-m))
        end
        new(v,m)
    end
end

kron(ket1::Ket, ket2::Ket) = Ket(kron(ket1.v, ket2.v))
kron(v) = v
length(ket::Ket) = int(log(size(ket.v,1))/log(2))
show(io::IO, ket::Ket) = print(io,convert(String,ket))

*(val::Number, ket::Ket) = Ket(ket.v*val)
*(ket::Ket, val::Number) = val*ket.v
*(op1::QGate, op2::QGate) = GGate(op1.v*op2.v)
*(ket::Ket, op::QGate) = op*ket
*(op::QGate, ket::Ket) = Ket(op.v*ket.v)
==(ket1::Ket, ket2::Ket) = ket1.v==ket2.v
+(ket1::Ket, ket2::Ket) = Ket(ket1.v+ket2.v)

function convert(::Type{String}, ket::Ket)
    vp = prob(ket.v)
    str=map(find(vp)) do x
        string(@sprintf("%1.13f",vp[x]),"\t", svbar,bin(x-1,length(ket)),sket," \n")
    end
    join(sort(str))
end

function prob(v::SparseMatrixCSC)
    prob(x) = real(x*x)
    map(prob,v)
end

# (|0000>,3) -> |000>
function sub(ket::Ket,n)
    nk = length(ket)
    @assert(n<=nk)
    v = prob(ket.v)
    for i=1:nk-n
        v = reshape(v,2,length(v)>>1)'
        v = v[:,1] + v[:,2]
    end
    kets=map(find(v)) do x
        sqrt(v[x]) * Ket(map(parseint,split(bin(x-1,n),""))...)
    end
    reduce(+,kets)
end

# (|abcd>,3) -> |c>
function extract(ket::Ket,n)
    sub(Swap(length(ket),1,n)*ket,1)
end

#  (|0000>,a,b,c) -> |0000abc>
function append(ket::Ket, x...)
    av = map(parse01, x)
    Ket(kron(ket.v, av...))
end

#  (|0000>,a,b,c) -> |abc0000>
function appendhead(ket::Ket, x...)
    nk = length(ket)
    nx = length(x)
    av = map(parse01, x)

    v = ket.v
    for i=1:nx
        v = kron(v, av[i])
        nk = nk+1
        v = Swap(nk,i,nk).v*v
        v = reduce(*, [Swap(nk,j,j+1).v for j=i+1:nk-1])*v
    end

    Ket(v)
end

end # module


using DifferentialEquations, Plots

a=3.0
f(y,a) =   sign(y)  + a -  y
f(y,a,t) = f(y,a) # just for the difeq solver

sol = solve(  ODEProblem( f, 3, (0,10.0), 0.2 ));
plot( sol , legend=false, background_color_inside=:black , ylims=(-7,7), lw=3, c=:red)


""" Kalman Filter and Extended Kalman Filter Likelihoods """

function kf_likelihood(θ, param_kf::ParamKalmanFilter, state_beliefs::AbstractArray,
    action_history::AbstractArray, measurement_history::AbstractArray)
    # drop initial s0 belief
    @assert length(action_history) == length(measurement_history)
    N = length(measurement_history)
    # initialize log likelihood
    # l = o.R[1]
    l=0.0
    for (k, (s, y, u)) in enumerate(zip(state_beliefs, measurement_history, action_history))
        x_hat = param_kf.A(θ)*s.x + param_kf.B(θ)*u # predicted state prior
        P_hat = param_kf.A(θ)*s.P*param_kf.A(θ)' + param_kf.Q(θ) # a priori state covariance
        v = y - param_kf.H(θ)*x_hat # measurement pre fit residual
        S = param_kf.H(θ)*P_hat*param_kf.H(θ)' + param_kf.R(θ) # pre fit residual covariance
        l += 1/2*(v'*inv(S)*v + log(det(S)))
    end
    return l
end

function ekf_likelihood(θ, param_ekf::ExtendedParamKalmanFilter, state_beliefs::AbstractArray,
    action_history::AbstractArray, measurement_history::AbstractArray)
    # drop initial s0 belief
    @assert length(action_history) == length(measurement_history)
    N = length(measurement_history)
    # initialize log likelihood
    # l = o.R[1]
    l=0.0
    for (k, (s, y, u)) in enumerate(zip(state_beliefs, measurement_history, action_history))
        x_hat = param_ekf.f(θ, s.x, u) # predicted state prior
        F = ForwardDiff.jacobian(μ -> param_ekf.f(θ, μ, u), s.x)
        P_hat = F*s.P*F' + param_ekf.Q(θ) # a priori state covariance
        v = y - param_ekf.h(θ, x_hat) # measurement pre fit residual
        H = ForwardDiff.jacobian(μ -> param_ekf.h(θ, μ), s.x)
        S = H*P_hat*H' + param_ekf.R(θ) # pre fit residual covariance
        l += 1/2*(v'*inv(S)*v + log(det(S)))
    end
    return l
end


# See "Statistical Applications of the multivariate skew-normal distribution" by Azzalini and Capitanio (1998)
# for notations and explanations

immutable MvSkewNormal <: Sampleable{Multivariate, Continuous}
    ξ::Vector{Float64}      # Location vector
    ω::Diagonal{Float64}      # Scale (diagonal matrix)
    Ωz::PDMat               # Correlation matrix
    α::Vector{Float64}      # Shape vector
    # Auxiliary parameters for sampling
    δ::Vector{Float64}
    Ωstar::PDMat  # For sampling...
    function MvSkewNormal(ξ::Vector{Float64}, Ω::Matrix{Float64}, α::Vector{Float64})
        ω, Ωz = scale_and_cor(PDMat(Ω))
        d = length(α)
        _δ = δ(Ωz, α)
        Ωstar = Array(Float64, d+1, d+1)
        Ωstar[:, 1] = [1; _δ]
        Ωstar[1,:] = [1 _δ']
        Ωstar[2:end, 2:end] = Ωz.mat
        Ωstar = PDMat(Ωstar)
        new(ξ, ω, Ωz, α, _δ, Ωstar)
    end
end

# Convenience constructor - no location parameters
MvSkewNormal(Ω::Matrix{Float64}, α::Vector{Float64}) = MvSkewNormal(zeros(length(α)), Ω, α)

function show(io::IO, dist::MvSkewNormal)
    println(io, "ξ = $(dist.ξ)")
    println(io, "Ω = ")
    println(io, dist.ω * dist.Ωz.mat * dist.ω)
    println(io, "α = $(dist.α)")
end

length(dist::MvSkewNormal) = length(dist.α)

function _rand!{T<:Real}(dist::MvSkewNormal, out::AbstractVector{T})
    xx = randn(length(out) + 1)
    unwhiten!(dist.Ωstar, xx)
    x0, x = xx[1], xx[2:end]
    copy!(out, dist.ω * (x0 > 0 ? x : -x) + dist.ξ)
end

mean(dist::MvSkewNormal) = sqrt(2/π)*dist.ω*dist.δ + dist.ξ
var(dist::MvSkewNormal) = (dist.ω^2)*(diag(dist.Ωz.mat) - (sqrt(2/π)*dist.δ).^2)

function cov(dist::MvSkewNormal)
    μz=sqrt(2/π)*dist.δ
    dist.ω * (dist.Ωz.mat - μz*μz') * dist.ω
end

# Special functions (related to cumulant function of half-normal distribution)
ζ₀(x::Float64) = log(2π * normcdf(x))
ζ₀(x::Vector{Float64}) = map(ζ₀, x)

ζ₁(x::Float64) = normpdf(x)/normcdf(x)
ζ₁(x::Vector{Float64}) = map(ζ₁, x)

# Note that these fit functions are not completely reliable.
# Sometimes a numerical errors occur, for instance, if the line
# search starts with too large a step size, then the function
# evaluations are undefined. On other occasions, the algorithm
# may converge only to a local optimum. The latter problem could
# perhaps be overcome through the use of the NLOpt library
# which allows the user to randomly generate multiple start points.
# The former problem could perhaps be resolved with error handling.

# Fit a MvSkewNormal regression model for model to data
# X = (n x p) model matrix (each row is corresponds to the predictors of one response)
# Y = (n x k) response matrix (each row correspond to one output observation)
#
# The following model is assumed:
#   yᵢ ∼ SNₖ(ξᵢ, Ω, α) where ξᵢ = xᵢ̱β
# for some (p x k) matrix β of parameters
function fit_MvSkewNormal(X::Matrix{Float64}, Y::Matrix{Float64}; kwargs...)
    size(X,1) == size(Y,1) || throw(ArgumentError("X and Y must have the same number of rows"))
    n,p = size(X)
    k = size(Y,2)
    # β is a (p x k) parameter matrix
    u(β::Matrix{Float64}) = Y - X*β
    V(β::Matrix{Float64}) = (u(β)'u(β))/n

    function ll(params::Vector{Float64})
        β = reshape(params[1:(p*k)], p, k)
        η = params[p*k+1:end]
        -(-0.5 * n * log(det(V(β))) - 0.5 * n * k + sum(ζ₀(u(β)*η)))
    end

    function dll!(params::Vector{Float64}, grad::Vector{Float64})
        β = reshape(params[1:(p*k)], p, k)
        η = params[p*k+1:end]

        uβ = u(β)
        Vβ = V(β)

        # ∂l∂β = X'u(β)*inv(Vβ) - X'ζ₁(u(β)*η)*η'

∂l∂β = X'*(Vβ\(uβ'))' - X'ζ₁(uβ*η)*η'

        ∂l∂η= uβ'ζ₁(u*η)
        grad[1:p*k] = -vec(∂l∂β)
        grad[p*k + 1:end] = -∂l∂η
    end

    function ll_and_dll!(params::Vector{Float64}, grad::Vector{Float64})
        β = reshape(params[1:p*k], p, k)
        η = params[p*k+1:end]

        println("β = $(β)")
        println("η = $(η)")

        uβ = u(β)
        Vβ = V(β)

        #
∂l∂β = X'u(β)*inv(V(β)) - X'ζ₁(u(β)*η)*η'

∂l∂β = X'*(Vβ\(uβ'))' - X'ζ₁(uβ*η)*η'
        ∂l∂η = uβ'ζ₁(uβ*η)
        grad[1:p*k] = -vec(∂l∂β)
        grad[p*k + 1:end] = -∂l∂η

        -(-0.5 * n * log(det(Vβ)) - 0.5 * n * k + sum(ζ₀(uβ*η)))
    end

    func = DifferentiableFunction(ll, dll!, ll_and_dll!)
    init = ones(p*k + k)

    results = optimize(func, init; kwargs...)
    β = reshape(results.minimum[1:p*k], p, k)
    η = results.minimum[p*k+1:end]
    Ω = V(β)
    ω = diagm(sqrt(diag(Ω)))
    α = ω * η

    return MvSkewNormal(Ω, α), β
end

# Fit a multivariate Skew Normal distribution
# directly to a set of observations
#
# Y = (n x k) observation matrix (each row correspond to one output observation)
function fit_MvSkewNormal(Y::Matrix{Float64}; kwargs...)
    dist, β = fit_skew(ones(size(Y,1),1), Y; kwargs...)
    return MvSkewNormal(vec(β), dist.ω*dist.Ωz.mat*dist.ω, dist.α)
end


function loss(sim::SimResult, measurements::Vector)
  loss = 0.0
  for dp in measurements
    loss += loss_point(sim, dp)
  end
  return loss
end

function loss_point(sim::SimResult, dp::NormalMeasurementPoint{MU,S}) where {MU,S}
  sim_val = _param_value(sim, dp.μ, dp)
  measurements_val = dp.val
  sigma = _param_value(sim, dp.σ, dp)
  sigma_sq = (sigma)^2

  log(2π) + log(sigma_sq) + (sim_val - measurements_val)^2 / sigma_sq
end

function loss_point(sim::SimResult, dp::LogNormalMeasurementPoint{MU,S}) where {MU,S}
  sim_val = _param_value(sim, dp.μ, dp)
  measurements_val = dp.val
  sigma = _param_value(sim, dp.σ, dp)
  sigma_sq = (sigma)^2

  log(2π) + log(sigma_sq) + log((measurements_val)^2) + (log(sim_val) - log(measurements_val))^2 / sigma_sq
end

_param_value(sim, p::Float64, dp) = p

function _param_value(sim, p::Symbol, dp)
  if p ∈ keys(parameters(sim))
    val = parameters(sim)[p]
  elseif p ∈ observables(sim)
    val = sim(dp.t, p, dp.scope)
  else
    error("$p not found in simulated results.")
  end
  return val
end


using StatsFuns: normcdf, normccdf, normlogcdf, normlogccdf, normlogpdf, normpdf, logsumexp

@rule Probit(:in, Marginalisation) (m_out::Union{PointMass, Bernoulli}, ) = begin

    # extract parameters
    p = mean(m_out)

    # calculate outward message
    f = (z) -> log(1 - p + (2*p - 1)*normcdf(z))

    # return message
    return ContinuousUnivariateLogPdf(f)

end

@rule Probit(:in, MomentMatching) (m_out::Union{PointMass, Bernoulli}, m_in::UnivariateNormalDistributionsFamily) = begin

    # extract parameters
    mz, vz = mean_cov(m_in)
    p = mean(m_out)
    @assert p >= zero(p) && p <= one(p) "The Probit node only accepts messages on its output with values between 0 and 1."

    # calculate auxiliary variables
    γ = mz/sqrt(1+vz)

    # calculate moments of g
    if γ > 0 && p > 0.5
        log_mom0_pz = logsumexp((log(1-p), log(2*p-1) + normlogccdf(-γ)))
    elseif γ <= 0 && p > 0.5
        log_mom0_pz = logsumexp((log(1-p), log(2*p-1) + normlogcdf(γ)))
    elseif γ > 0 && p <= 0.5
        log_mom0_pz = logsumexp((log(1-p) + normlogcdf(-γ), log(p) + normlogcdf(γ)))
    else
        log_mom0_pz = logsumexp((log(1-p) + normlogccdf(γ), log(p) + normlogcdf(γ)))
    end
    tmp = log(vz) + normlogpdf(γ) - log(1+vz)/2 - log_mom0_pz
    mom1_pz = mz + (2*p-1)*exp(tmp)
    mom2_pz = vz + mz^2 + (2*p-1)*2*mz*exp(tmp) - (2p-1)*γ*exp(log(vz) - log(1 + vz)/2 + tmp)

    # calculate parameters of posterior
    mpz = mom1_pz
    vpz = mom2_pz - mom1_pz^2
    vpz = clamp(vpz, tiny, vz)# ensure variance of marginal is not larger than the variance of the cavity distribution.

    # calculate parameters of outgoing message
    wz_out = clamp(1/vpz - 1/vz, tiny, huge) # Ensure precision isn't too small or too large
    ξz_out = mpz/vpz - mz/vz

    # return message
    return NormalWeightedMeanPrecision(ξz_out, wz_out)

end


import AdversarialPrediction: define, constraint, generate_constraints_on_p!
using LinearAlgebra

# sample metrics
@metric Prec
function define(::Type{Prec}, C::ConfusionMatrix)
    return C.tp / C.pp
end

@metric F1_score
function define(::Type{F1_score}, C::ConfusionMatrix)
    return (2 * C.tp) / (C.ap + C.pp)
end

@metric Kappa
function define(::Type{Kappa}, C::ConfusionMatrix)
    num = (C.tp + C.tn) / C.all - (C.ap * C.pp + C.an * C.pn) / C.all^2
    den = 1 - (C.ap * C.pp + C.an * C.pn) / C.all^2
    return num / den
end

@metric Prec_F1_score
function define(::Type{Prec_F1_score}, C::ConfusionMatrix)
    return C.tp / C.pp
end
function constraint(::Type{Prec_F1_score}, C::ConfusionMatrix)
    return (2 * C.tp) / (C.ap + C.pp) >= 0.6
end

@testset "compute metric" begin

    # hardcoded functions
    prec_fn(yhat, y) = (dot(yhat, y) / sum(yhat))
    f1_fn(yhat, y) = (2 * dot(yhat, y) / (sum(yhat) + sum(y)))

    function kappa_fn(yhat, y)
        n = length(y)
        num = ((dot(yhat, y) + dot(1 .- yhat, 1 .- y)) / n -
            (sum(y) * sum(yhat) + sum(1 .- y) * sum(1 .- yhat)) / n^2 )
        den = 1 - (sum(y) * sum(yhat) + sum(1 .- y) * sum(1 .- yhat)) / n^2
        return num / den
    end

    y = rand(0:1, 100)
    yhat = rand(0:1, 100)

    prec = Prec()
    @test compute_metric(prec, yhat, y) ≈ prec_fn(yhat, y)

    f1 = F1_score()
    @test compute_metric(f1, yhat, y) ≈ f1_fn(yhat, y)

    kappa = Kappa()
    @test compute_metric(kappa, yhat, y) ≈ kappa_fn(yhat, y)
end

@testset "constraints metric" begin

    # hardcoded functions
    function f1_fn(y)
        n = length(y)
        iq = sum(y .== 1)

        ks = 1:n
        B = zeros(n,n)
        c = 0.0

        for ip = 1:n
            B[:,ip] = ( 2 * y ) / (ip + iq)
        end

        if iq == 0
            B .+= (-1 ./ ks')
            c += 1.0
        end

        return B, c
    end

    prec_f1 = Prec_F1_score()
    special_case_positive!(prec_f1)
    cs_special_case_positive!(prec_f1, true)

    y = rand(0:1, 20)
    generate_constraints_on_p!(prec_f1, y)

    B, c = f1_fn(y)
    @test B ≈ prec_f1.data.B_list[1]
    @test isapprox(c, prec_f1.data.c_list[1], atol = 1e-6)

    # if y == zeros
    y = zeros(20)
    generate_constraints_on_p!(prec_f1, y)

    B, c = f1_fn(y)
    @test B ≈ prec_f1.data.B_list[1]
    @test isapprox(c, prec_f1.data.c_list[1], atol = 1e-6)

    # if y == ones
    y = ones(20)
    generate_constraints_on_p!(prec_f1, y)

    B, c = f1_fn(y)
    @test B ≈ prec_f1.data.B_list[1]
    @test isapprox(c, prec_f1.data.c_list[1], atol = 1e-6)

end


import IntertemporalChoiceHeuristics:
    load,
    fit,
    models,
    evaluation_losses,
    average_loss

import Optim: minimizer

inputs, weights = load()

for model in models
    res = fit(model, inputs, weights)
    parameters = minimizer(res)
    m = model(parameters)
    for loss in evaluation_losses
        l = average_loss(m, loss, inputs, weights)
        @printf(
            "%s\t%s\t%s\t%s\t%s\n",
            m,
            loss,
            l,
            Optim.minimum(res),
            norm(parameters),
        )
    end
end


function hist_param(param,p1,p2,nhist)
np = length(param)
param_grid = zeros(2*nhist)
for i=1:nhist
  param_grid[2*i-1] = p1 + (p2-p1) * (i-1.0)/nhist
  param_grid[2*i  ] = p1 + (p2-p1) * i/nhist
end
param_hist = zeros(2*nhist)
nmiss = 0
for i=1:np
  ibin = floor(Int64,(param[i]-p1)/(p2-p1)*nhist)+1
  if ibin >= 1 && ibin <= nhist
    param_hist[2*ibin-1] += 1
    param_hist[2*ibin  ] += 1
  else
    nmiss +=1
  end
end
return [p1;param_grid;p2],[0;param_hist;0],nmiss
end

function rho_pearson(p1,p2)
@assert(length(p1)==length(p2))
np = length(p1)
rho = 0.0
m1 = mean(p1)
m2 = mean(p2)
s1 = std(p1)
s2 = std(p2)
for i=1:np
  rho += (p1[i]-m1)*(p2[i]-m2)
end
return rho/s1/s2/(np-1)
end

function acf_par(pvec,nstep_corr)
# Computes ACF:
nstep = length(pvec)
mpar = mean(pvec)
spar = std(pvec)
# Now compute ACF:
acf = zeros(nstep_corr)
for k=1:nstep_corr
  for m= 1:nstep-k
    acf[k] += (pvec[m]-mpar)*(pvec[m+k]-mpar)
  end
  acf[k] = acf[k]/spar^2/(nstep-k)
end
return acf
end

# Make a binned light curve:
include("model_tyc3559_v2.jl")
include("transit_orb.jl")
include("occultquad.jl")
include("kepler.jl")

using JLD
using PyPlot

#@load "tyc3559_mcmc_norv_fixedkernel_v03.jld"
@load "tyc3559_mcmc_norv_fixedkernel_v03_thin.jld"

#par_mcmc[:,:,4]=par_mcmc[:,:,4].*(1.0-par_mcmc[:,:,15])
#par_mcmc[:,:,13]=par_mcmc[:,:,13].*(1.0-par_mcmc[:,:,15])
#pname[4] = "D*(1-f3)"
#pname[13] = "a2c*(1-f3)"
# Derived parameters:

# 1). Density of star B:

using CGS
period = (par_mcmc[:,:,2]-par_mcmc[:,:,1])/nlast
tdur = par_mcmc[:,:,5]
b = par_mcmc[:,:,3]
rhoB = 3.0/pi^2 .*period./tdur.^3/(24.*3600.)^2/GRAV.*sqrt(1.0-b.^2)
mrhoB = mean(rhoB); srhoB = std(rhoB)
println("Density of star B: ",mrhoB," +- ",srhoB)

# make histogram:
nhist = 80
p1 = mrhoB-3*srhoB; p2 = mrhoB+3*srhoB
param_grid,param_hist,nmiss = hist_param(rhoB,p1,p2,nhist)

clf()
plot(param_grid,param_hist/maximum(param_hist))
plot([mrhoB-srhoB,mrhoB-srhoB],[0,1])
plot([mrhoB+srhoB,mrhoB+srhoB],[0,1])
plot([mrhoB,mrhoB],[0,1])
xlabel("Density of star B [g/cc]")
ylabel("Relative probability")

read(STDIN,Char)

# 2). Doppler mass of brown dwarf:

teff = 6500.0
x = PLANCK * C / (6000e-8)/BOLTZMANN/teff
# Approximate beaming with Blackbody [NEED TO REDO WITH ATMOSPHERE & KEPLER BANDPASS]:
#alpha_beam = (exp(x)*(3.0-x)-3.0)/(exp(x)-1.0)
alpha_beam = -0.898
# Compute radial velocity in km/s:
vr = par_mcmc[:,:,12]/(3.0-alpha_beam)*C/1e5
# Convert to mass of brown dwarf, assuming mass of star:
mB = 1.76
semi = (GRAV * mB * MSUN)^(1//3).* (period.*24*3600/2/pi).^(2//3)
aonr = period./tdur.*sqrt(1.0-b.^2)/pi
inc = acos(b./aonr)
vorb = 2*pi.*semi./(period.*24.*3600.)./1e5
# Mass of brown dwarf:
mBb1 = vr./vorb.*mB.*MSUN./MJUPITER./sin(inc)
mmBb1=mean(mBb1)
smBb1=std(mBb1)
println("Mass of Bb from Doppler: ",mmBb1," +- ",smBb1," M_Jupiter")

# 3a). Tidal distortion mass of brown dwarf:
# Equation 2 from Faigler & Mazeh:
q1 = par_mcmc[:,:,6]; q2 = par_mcmc[:,:,7]
u1 = 2.*sqrt(q1).*q2
u2 = sqrt(q1).*(1-2.*q2)
#g = 0.3+rand(size(q1))*0.7
g = 0.65
alpha_ellip = 0.15.*(15.0+u1).*(1.0+g)./(3.0-u1)
mBb2 = mB * MSUN .* aonr.^3./alpha_ellip.* abs(par_mcmc[:,:,13])./MJUPITER./sin(inc).^2
mmBb2 = mean(mBb2)
smBb2 = std(mBb2)
println("Mass of Bb from tides: ",mmBb2," +- ",smBb2," M_Jupiter")

# 3b). Compute the mass from the RV:
krv0 =  5.985117177227862
sig_krv = 0.43164531668541845
krv = krv0 + sig_krv*randn(nwalkers,nsteps)

mBb3 = (period.*24.*3600.*(krv.*1e5).^3.*(mB*MSUN)^2/(2pi*GRAV)).^(1//3)./sin(inc)./MJUPITER
mmBb3 = mean(mBb3)
smBb3 = std(mBb3)
println("Mass of Bb from RV: ",mmBb3," +- ",smBb3," M_Jupiter")

p1 = minimum([mmBb2-3smBb2,mmBb1-3smBb1,mmBb3-3smBb3])
p2 = maximum([mmBb2+3smBb2,mmBb1+3smBb1,mmBb3+3smBb3])
param_grid,param_hist1,nmiss = hist_param(mBb1,p1,p2,nhist)
param_grid,param_hist2,nmiss = hist_param(mBb2,p1,p2,nhist)
param_grid,param_hist3,nmiss = hist_param(mBb3,p1,p2,nhist)
clf()
#plot(param_grid,param_hist1)
#plot(param_grid,param_hist2)
#plot(param_grid,param_hist3)
#xlabel("Mass of brown dwarf [M_Jupiter]")
#ylabel("Relative probability")

fig, ax = subplots()
ax[:plot](param_grid,param_hist1./maximum(param_hist3),"r-",alpha=0.6,label="Beaming",linewidth=3)
ax[:plot](param_grid,param_hist2./maximum(param_hist3),"b-",alpha=0.6,label="Ellipsoidal",linewidth=3)
ax[:plot](param_grid,param_hist3./maximum(param_hist3),"g-",alpha=0.6,label="Doppler RV",linewidth=3)
ax[:set_xlabel]("Mass of brown dwarf [M_Jupiter]")
ax[:set_ylabel]("Relative probability")
ax[:axis]([0,200,0.,1.1])
ax[:legend](loc ="upper right")
PyPlot.savefig("brown_dwarf_mass_comparison.png",bbox_inches="tight")

read(STDIN,Char)

#
# 4). Brightness of brown dwarf from phase-curve & secondary eclipse:
fBb_phase = 2.*abs(par_mcmc[:,:,11])*1e6
fBb_occult = abs(par_mcmc[:,:,16])*1e6
mfBb_phase = mean(fBb_phase)
sfBb_phase = std(fBb_phase)
mfBb_occult = mean(fBb_occult)
sfBb_occult = std(fBb_occult)
println("Flux ratio of Bb to B+Bb, phase:  ",mfBb_phase," +- ",sfBb_phase)
println("Flux ratio of Bb to B+Bb, occult: ",mfBb_occult," +- ",sfBb_occult)
p1 = minimum([mfBb_phase-3*sfBb_phase,mfBb_occult-3*sfBb_occult])
p2 = maximum([mfBb_phase+3*sfBb_phase,mfBb_occult+3*sfBb_occult])
param_grid,param_hist1,nmiss = hist_param(fBb_phase,p1,p2,nhist)
param_grid,param_hist2,nmiss = hist_param(fBb_occult,p1,p2,nhist)
clf()
plot(param_grid,param_hist1)
plot(param_grid,param_hist2)
xlabel("fBb/(fB+fBb) [ppm]")
ylabel("Relative probability")
read(STDIN,Char)

# 5). Radius ratio of brown dwarf to star B:
depth = par_mcmc[:,:,4]
x = 1.0-sqrt(1.0-b.^2)
r21 = sqrt(depth.*(1.-u1./3-u2./6)./(1.0-u1.*x-u2.*x.^2))
mr21 = mean(r21); sr21=std(r21)
println("R_Bb/R_B: ",mr21," +- ",sr21)

# 6). Geometric albedo of the brown dwarf from occultation:
ag = fBb_occult.*1e-6.*(aonr./r21).^2
println("Geometric albedo: ",mean(ag)," +- ",std(ag))

# 7). Radius of brown dwarf:
rBb = r21.*(mB*MSUN./rhoB.*3/4/pi).^(1//3)./RJUPITER
mrBb = mean(rBb); srBb = std(rBb)
println("R_Bb: ",mrBb," +- ",srBb," [R_Jupiter]")
read(STDIN,Char)

# 8). Radius of Star B:

rB = (mB*MSUN./rhoB.*3/4/pi).^(1//3)./RSUN
mrB = mean(rB); srB = std(rB)
println("R_Bb: ",mrB," +- ",srB," [R_Jupiter]")

# 9).  Compute the density from RV:
rho_Bb = mBb3.*MJUPITER./(rBb.*RJUPITER).^3.*3./(4pi)
mrho_Bb = mean(rho_Bb); srho_Bb = std(rho_Bb)
println("Density of brown dwarf [g/cc]: ",mrho_Bb," +- ",srho_Bb," [g/cc]")

# # Compute autocorrelation functions:
# nstep_corr = 2000
# acorrlength = zeros(nvary,nwalkers)
# for i=1:nvary
#   clf()
#   acf = zeros(nwalkers,nstep_corr)
#   for j=1:nwalkers
# # First estimate mean & variance:
#     pvec = vec(par_mcmc[j,:,ivary[i]])
#     acf[j,:]=acf_par(pvec,nstep_corr)
#     acorrlength[i,j] = 0.0
#     k=1
#     while (acf[j,k] > 0.5) && (k < nstep_corr-1)
#       k+=1
#     end
#     acorrlength[i,j] = k
#     plot(vec(acf[j,:]))
#   end
#   println("Autocorrelation length for parameter ",pname[ivary[i]]," is ",maximum(acorrlength[i,:]))
# #  read(STDIN,Char)
# end
#
# println("Autocorrelation length for chains is: ",maximum(acorrlength))
# println("Effective length of MCMC: ",nwalkers*nsteps/maximum(acorrlength))
#
# read(STDIN,Char)
clf()

# Compute inter-parameter correlation
for i=1:nvary-1
  for j=i+1:nvary
    p1=vec(par_mcmc[:,:,ivary[i]])
    p2=vec(par_mcmc[:,:,ivary[j]])
    rho = rho_pearson(p1,p2)
    if abs(rho) > 0.5
      println("Parameters ",pname[ivary[i]]," and ",pname[ivary[j]]," correlate strongly  as ",rho)
      scatter(p1,p2)
      read(STDIN,Char)
      clf()
    end
  end
end

clf()

for i=1:nvary
  mp = zeros(nsteps)
  sp = zeros(nsteps)
  for j=1:nwalkers
    plot(vec(par_mcmc[j,1:nsteps,ivary[i]]))
  end
  for j=1:nsteps
    mp[j]=mean(par_mcmc[:,j,ivary[i]])
    sp[j]=std(par_mcmc[:,j,ivary[i]])
  end
  plot(mp+sp)
  plot(mp-sp)
  xlabel("MCMC step")
  ylabel(pname[ivary[i]])
  println("Hit return to continue")
  read(STDIN,Char)
  clf()
end

log_like_best =  -1e100
ibest = 0
jbest = 0
param_best = zeros(nparam)
for i=1:nsteps
  for j=1:nwalkers
    if log_like_mcmc[j,i] > log_like_best
      ibest = i
      jbest = j
      log_like_best = log_like_mcmc[j,i]
      param_best = vec(par_mcmc[j,i,:])
    end
  end
end

model = model_tyc3559_v2(time_phot,param_best)

# Compute residuals:

y = flux - model

period = (param_best[2]-param_best[1])/nlast

t0 = param_best[1]

nbin = 128
phasebin_resid = zeros(nbin)
phasebin_resid_med = zeros(nbin)
phasebin_raw = zeros(nbin)
phasebin_raw_med = zeros(nbin)
phasebin_model = zeros(nbin)
phase0 = mod((time_phot-t0)+period/4,period)/period
is = sortperm(phase0)
j=1
for ibin=1:nbin
  i=j
  while phase0[is[j+1]] < (ibin/nbin) && (j+1) < ntime
    j=j+1
  end
  phasebin_resid[ibin] = mean(y[is[i:j]])
  phasebin_resid_med[ibin] = median(y[is[i:j]])
  phasebin_raw[ibin] = mean(flux[is[i:j]])
  phasebin_raw_med[ibin] = median(flux[is[i:j]])
  phasebin_model[ibin] = mean(model[is[i:j]])
end

phase = zeros(nbin)
for i=1:nbin
  phase[i] = (i-0.5)/nbin
end

clf()
#scatter(phase,phasebin_resid)
scatter(phase,phasebin_raw)
scatter(phase,phasebin_raw_med)
plot(phase,phasebin_model)
plot(phase,phasebin_model)
model_reflect=((1+param_best[11]*cos((phase-0.25)*2pi))*(1.0-param_best[15])+param_best[15])*param_best[8]
model_beam   =((1+param_best[12]*sin((phase-0.25)*2pi))*(1.0-param_best[15])+param_best[15])*param_best[8]
model_ellip  =((1+param_best[13]*cos((phase-0.25)*4pi))*(1.0-param_best[15])+param_best[15])*param_best[8]
#  model[i]=((model_phot1*(1.0-param[16])+model_phot2*param[16])* (1.0-param[15])+param[15])*f0

plot(phase,model_reflect)
plot(phase,model_beam)
plot(phase,model_ellip)