function solve(problem::AllenCahnProblem1D{NeumannBC, NeumannBC, SparseArrays.SparseMatrixCSC{Float64, Int64}}, stepping_method::TTSM) where {TTSM <: BackwardEulerMethod}
    u = problem.u
    t = 0.
    t_trajectory = [t]; u_trajectory = [u]
    n = 1

    matrix = sparse(Matrix(1.0I, size(problem.A)[1], size(problem.A)[2])) - problem.Δt*problem.ϵ*problem.A
    boundary_correction = zeros(problem.nₓ + 2)
    boundary_correction[1] = -2*problem.ϵ*problem.left_bc.boundary_condition/problem.Δx
    boundary_correction[end] = 2*problem.ϵ*problem.right_bc.boundary_condition/problem.Δx

    while n <= problem.nₜ
        problem.rhs .-= problem.rhs
        problem.rhs .+= problem.f.(u)
        problem.rhs .+= boundary_correction
        b = u + problem.Δt*problem.rhs
        t += problem.Δt; push!(t_trajectory, t)
        u = matrix\b; push!(u_trajectory, u)
        n += 1
    end

    return t_trajectory, u_trajectory
end

function solve(problem::AllenCahnProblem1D{PeriodicBC, PeriodicBC, SparseArrays.SparseMatrixCSC{Float64, Int64}}, stepping_method::TTSM) where {TTSM <: BackwardEulerMethod}
    u = problem.u
    t = 0.
    t_trajectory = [t]; u_trajectory = [u]
    n = 1

    matrix = sparse(Matrix(1.0I, size(problem.A)[1], size(problem.A)[2])) - problem.Δt*problem.ϵ*problem.A

    while n <= problem.nₜ
        problem.rhs .-= problem.rhs
        problem.rhs .+= problem.f.(u)
        b = u + problem.Δt*problem.rhs
        t += problem.Δt; push!(t_trajectory, t)
        u = matrix\b; push!(u_trajectory, u)
        n += 1
    end

    push!(problem.x, problem.a + (problem.nₓ + 1)*problem.Δx)
    for i ∈ 1:length(u_trajectory)
        push!(u_trajectory[i], u_trajectory[i][1])
    end

    return t_trajectory, u_trajectory
end

function solve(problem::AllenCahnProblem1D{NeumannBC, NeumannBC, SparseArrays.SparseMatrixCSC{Float64, Int64}}, stepping_method::TTSM) where {TTSM <: CrankNicolsonMethod}
    u = problem.u
    t = 0.
    t_trajectory = [t]; u_trajectory = [u]
    n = 1

    left_matrix = sparse(Matrix(1.0I, size(problem.A)[1], size(problem.A)[2])) - (problem.Δt*problem.ϵ/2)*problem.A
    right_matrix = sparse(Matrix(1.0I, size(problem.A)[1], size(problem.A)[2])) + (problem.Δt*problem.ϵ/2)*problem.A

    while n <= problem.nₜ
        problem.rhs .-= problem.rhs
        problem.rhs .+= problem.f.(u)
        b = right_matrix*u + problem.Δt*problem.rhs
        t += problem.Δt; push!(t_trajectory, t)
        u = left_matrix\b; push!(u_trajectory, u)
        n += 1
    end

    return t_trajectory, u_trajectory
end

function solve(problem::AllenCahnProblem1D{PeriodicBC, PeriodicBC, SparseArrays.SparseMatrixCSC{Float64, Int64}}, stepping_method::TTSM) where {TTSM <: CrankNicolsonMethod}
    u = problem.u
    t = 0.
    t_trajectory = [t]; u_trajectory = [u]
    n = 1

    left_matrix = sparse(Matrix(1.0I, size(problem.A)[1], size(problem.A)[2])) - (problem.Δt*problem.ϵ/2)*problem.A
    right_matrix = sparse(Matrix(1.0I, size(problem.A)[1], size(problem.A)[2])) + (problem.Δt*problem.ϵ/2)*problem.A

    while n <= problem.nₜ
        problem.rhs .-= problem.rhs
        problem.rhs .+= problem.f.(u)
        b = right_matrix*u + problem.Δt*problem.rhs
        t += problem.Δt; push!(t_trajectory, t)
        u = left_matrix\b; push!(u_trajectory, u)
        n += 1
    end

    push!(problem.x, problem.a + (problem.nₓ + 1)*problem.Δx)
    for i ∈ 1:length(u_trajectory)
        push!(u_trajectory[i], u_trajectory[i][1])
    end

    return t_trajectory, u_trajectory
end


function [ zu,zw,xu,xs ] = griddef(nz,nx,dz,dx,zu,zw,xu,xs)
#UNTITLED2 Summary of this function goes here
#   Detailed explanation goes here

# Assign height values to u-scalar heights

for k = 1 : nz
  zu[k] = (k - 1.5)*dz;
end

# Assign height values to w heights

for k = 2 : nz
  zw[k] = dz*(k-2);
end

# Assign x-dist values to u on the x grid-pt

for i = 2 : nx

    xu[i] = dx*(i-2);

end

# Assign x-dist values to scalars on the x grid-pt

# xs(1) = dx/2;

for i = 2 : nx

#    xs(i) = dx/2 + dx*(i-1);
     xs[i] = (i-1.5)*dx;

end

end


using GenericSchur;

function fun2m_condest(f, A, B, C; wp = 128)

    with_digits(wp) do
        A = mp(A, wp);
        B = mp(B, wp);
        C = mp(C, wp);

        FA = schur(A);
        FB = schur(B);

        C = FA.Z' * C * FB.Z;

        # size of the perturbation, make sure it's sufficiently far given the
        # current precision level.
        ep = 10.0^(-wp/2.0);

        TA = FA.T + diagm(randn(size(FA.T, 1))) * ep * norm(FA.T, Inf);
        TB = FB.T + diagm(randn(size(FB.T, 1))) * ep * norm(FB.T, Inf);
        Y = diag_fun(f, TA, TB, C);

        h = sqrt(ep);

        EA = mp(randn(size(TA, 1), size(TA, 1)), wp); EA = EA / norm(EA) * h * norm(TA);
        EB = mp(randn(size(TB, 1), size(TB, 1)), wp); EB = EB / norm(EB) * h * norm(TB);

        # We force the same sparsity pattern on the perturbations
        # EA = (TA .!= 0) .* EA;
        # EB = (TB .!= 0) .* EB;

        TA = TA + EA;
        TB = TB + EB;

        Y2 = diag_fun(f, TA, TB, C);

        c = convert(Float64, norm(Y2 - Y) / norm(Y) / h)

        return c;
    end

end


@rule typeof(*)(:in, Marginalisation) (m_out::PointMass, m_A::PointMass) = PointMass(mean(m_in1) * mean(m_in2))

@rule typeof(*)(:in, Marginalisation) (m_out::GammaDistributionsFamily, m_A::PointMass{ <: Real }) = begin
    return GammaShapeRate(shape(m_out), rate(m_out) * mean(m_A))
end

# if A is a matrix, then the result is multivariate
@rule typeof(*)(:in, Marginalisation) (m_out::MultivariateNormalDistributionsFamily, m_A::PointMass{ <: AbstractMatrix }, meta::AbstractCorrection) = begin
    A = mean(m_A)
    ξ_out, W_out = weightedmean_precision(m_out)
    W = correction!(meta, A' * W_out * A)
    return MvNormalWeightedMeanPrecision(A' * ξ_out, W)
end

# if A is a vector, then the result is univariate
# this rule links to the special case (AbstractVector * Univariate) for forward (:out) rule
@rule typeof(*)(:in, Marginalisation) (m_out::MultivariateNormalDistributionsFamily, m_A::PointMass{ <: AbstractVector }, meta::AbstractCorrection) = begin
    A = mean(m_A)
    ξ_out, W_out = weightedmean_precision(m_out)
    W = correction!(meta, dot(A, W_out, A))
    return NormalWeightedMeanPrecision(dot(A, ξ_out), W)
end

# if A is a scalar, then the input is either univariate or multivariate
@rule typeof(*)(:in, Marginalisation) (m_out::F, m_A::PointMass{ <: Real }, meta::AbstractCorrection) where { F <: NormalDistributionsFamily } = begin
    A = mean(m_A)
    ξ_out, W_out = weightedmean_precision(m_out)
    W = correction!(meta, A^2 * W_out)
    return convert(promote_variate_type(F, NormalWeightedMeanPrecision), A * ξ_out, W)
end

# specialized versions for mean-covariance parameterization
@rule typeof(*)(:in, Marginalisation) (m_out::MvNormalMeanCovariance, m_A::PointMass{ <: AbstractMatrix }, meta::AbstractCorrection) = begin
    A = mean(m_A)
    μ_out, Σ_out = mean_cov(m_out)

    z = fastcholesky(Σ_out)
    tmp = A' / z
    W = correction!(meta, tmp * A)

    return MvNormalWeightedMeanPrecision(tmp * μ_out, W)
end

@rule typeof(*)(:in, Marginalisation) (m_out::MvNormalMeanCovariance, m_A::PointMass{ <: AbstractVector }, meta::AbstractCorrection) = begin
    A = mean(m_A)
    μ_out, Σ_out = mean_cov(m_out)

    z = fastcholesky(Σ_out)
    tmp = A' / z
    W = correction!(meta, dot(tmp, A))

    return NormalWeightedMeanPrecision(dot(tmp, μ_out), W)
end


mutable struct Entity
    genes::Array{Float64, 1}
    fitness::Float64
end

function generateEntity(genesLength)
   return Entity(rand(-1.0:0.0001:1.0, genesLength), 0)
end

function printEntity(entity1)
   for i in 1:length(entity1.genes)
      print("$(round(entity1.genes[i],digits=4)) ")
   end
   print("\t$(round(entity1.fitness,digits=4))\n")
end

function calculateFitness!(entity1)
   x = entity1.genes[1]
   y = entity1.genes[2]
   z = entity1.genes[3]
   w = entity1.genes[4]
   u = entity1.genes[5]
   v = entity1.genes[6]
   entity1.fitness = abs(4*x^2-6*x-3*y^3+0.5*y+3*z+8*w-6.1*u+2*v-10)
end

function crossover!(entity1, entity2, crossoverPoint)
    for i in 1:crossoverPoint
          x = entity1.genes[i]
          entity1.genes[i] = entity2.genes[i]
          entity2.genes[i] = x
    end
end

function mutate!(entity1, mutationPercentage)
    if rand(Float64)<mutationPercentage
	    mutationPoint = rand(1:length(entity1.genes))
            if entity1.genes[mutationPoint] > 0
   	    	entity1.genes[mutationPoint] = entity1.genes[mutationPoint]-0.1
            else
                entity1.genes[mutationPoint] = entity1.genes[mutationPoint]+0.1
            end
    end
end


f = PetriCospan([1, 2], Petri.Model(1:4, [(Dict(1=>1), Dict(3=>1)), (Dict(2=>1), Dict(4=>1))]), [3, 4])

g = PetriCospan([1,2], Petri.Model(1:3, [(Dict(1=>1, 2=>1), Dict(3=>1))]), [3])

h = f ⋅ g

h′ = PetriCospan([1,2], Petri.Model(1:5, [(Dict(1=>1), Dict(3=>1)), (Dict(2=>1), Dict(4=>1)), (Dict(3=>1, 4=>1), Dict(5=>1))]), [5])

h_id = h ⋅ id(PetriCospanOb(1))

@test dom(f) == PetriCospanOb(2)
@test codom(f) == PetriCospanOb(2)
@test dom(g) == codom(f)
@test codom(g) == PetriCospanOb(1)
@test dom(h) == dom(f)
@test codom(h) == codom(g)

compare_petricospan(h, h′)
compare_petricospan(h, h_id)


using HDF5
using Convex
using Mosek, MosekTools

include("../verification/ai2zPQ.jl");

function dist_to_zonotope_p(reach, point; p = 2)
    G = reach.generators
    c = reach.center
    n, m = size(G)
    x = Variable(m)
    obj = norm(G * x + c - point, p)
    prob = minimize(obj, [x <= 1.0, x >= -1.0])
    solve!(prob, Mosek.Optimizer(LOG=0, MAX_NUM_WARNINGS=0))
    #@assert prob.status == OPTIMAL "Solve must result in optimal status"
    return prob.optval
end

function get_minimum_radius(network, lbs, ubs, y₀; p = 2)
    evaluate_objective(network, x) = -norm(y₀ - NeuralVerification.compute_output(network, x), p)
    optimize_reach(reach) = -dist_to_zonotope_p(reach, y₀; p=p)
    return priority_optimization(network, lbs, ubs, optimize_reach, evaluate_objective)
end

function get_minimum_radii(network, real_images, labels; state_eps = 1e-5, p = 2)
    n = size(real_images, 2)
    xs = zeros(4, n)
    unders = zeros(n)
    overs = zeros(n)

    for i = 1:n
        i % 10 == 0 ? println(i) : nothing

        lbs = [-0.8, -0.8, labels[1, i] / 6.366468343804353 - state_eps,
                                    labels[2, i] / 17.248858791583547 - state_eps]
        ubs = [0.8, 0.8, labels[1, i] / 6.366468343804353 + state_eps,
                                    labels[2, i] / 17.248858791583547 + state_eps]

        y₀ = (real_images[:, i] .* 2) .- 1;

        x, under, over = get_minimum_radius(network, lbs, ubs, y₀, p = p)
        xs[:, i] = x
        unders[i] = -over
        overs[i] = -under
    end

    return xs, unders, overs
end

network = read_nnet("../gan_training/generators/supervised_mlp.nnet");

fn = "../../data/SK_DownsampledGANFocusAreaData.h5"
images = h5read(fn, "y_train")
images = reshape(images, 16*8, :)
y = h5read(fn, "X_train")[1:2, :]

@time xs, unders, overs = get_minimum_radii(network, images, y, p = 2)


module ChebyshevApprox

using LinearAlgebra
using ThreadPools

include("structures.jl")
include("chebyshev_nodes.jl")
include("chebyshev_extrema.jl")
include("chebyshev_extended.jl")
include("vertesi_nodes.jl")
include("chebyshev_quadrature.jl")
include("normalize_node.jl")
include("chebyshev_polynomial.jl")
include("chebyshev_weights.jl")
include("chebyshev_evaluate.jl")
include("chebyshev_derivative.jl")
include("clenshaw_evaluate_generated.jl")

export ChebPoly,
       ChebInterpRoots,
       ChebInterpExtrema,
       ChebInterpExtended,
       ChebInterpVertesi

export chebyshev_nodes,
       chebyshev_extrema,
       chebyshev_extended,
       vertesi_nodes,
       chebyshev_quadrature,
       chebyshev_polynomial,
       chebyshev_weights,
       chebyshev_weights_extrema,
       chebyshev_weights_extended,
       chebyshev_weights_vertesi,
       chebyshev_weights_threaded,
       chebyshev_weights_extrema_threaded,
       chebyshev_weights_extended_threaded,
       chebyshev_weights_vertesi_threaded,
       chebyshev_evaluate,
       cheb_interp,
       chebyshev_derivative,
       chebyshev_gradient,
       clenshaw_evaluate

end


using Oceananigans
using Oceananigans.Units
using Oceananigans.Models.HydrostaticFreeSurfaceModels: ImplicitFreeSurface
using Statistics

function geostrophic_adjustment_simulation(free_surface)

    grid = RectilinearGrid(size = (64, 1, 1),
                                  x = (0, 1000kilometers), y = (0, 1), z = (-400meters, 0),
                                  topology = (Bounded, Periodic, Bounded))

    coriolis = FPlane(f=1e-4)

    model = HydrostaticFreeSurfaceModel(grid = grid,
                                        coriolis = coriolis,
                                        free_surface = free_surface)

    gaussian(x, L) = exp(-x^2 / 2L^2)

    U = 0.1 # geostrophic velocity
    L = grid.Lx / 40 # gaussian width
    x₀ = grid.Lx / 4 # gaussian center

    vᵍ(x, y, z) = - U * (x - x₀) / L * gaussian(x - x₀, L)

    g = model.free_surface.gravitational_acceleration

    η₀ = coriolis.f * U * L / g # geostrohpic free surface amplitude

    ηᵍ(x) = η₀ * gaussian(x - x₀, L)

    ηⁱ(x, y) = 2 * ηᵍ(x)

    set!(model, v=vᵍ, η=ηⁱ)
    gravity_wave_speed = sqrt(g * grid.Lz) # hydrostatic (shallow water) gravity wave speed
    wave_propagation_time_scale = model.grid.Δxᶜᵃᵃ / gravity_wave_speed
    simulation = Simulation(model, Δt=2wave_propagation_time_scale, stop_iteration=2)

    return simulation
end

function run_and_analyze(simulation)
    η = simulation.model.free_surface.η
    u, v, w = simulation.model.velocities

    ηx = ComputedField(∂x(η))
    compute!(ηx)

    u₀ = interior(u)[:, 1, 1]
    v₀ = interior(v)[:, 1, 1]
    η₀ = interior(η)[:, 1, 1]
    ηx₀ = interior(ηx)[:, 1, 1]

    run!(simulation)

    compute!(ηx)

    u₁ = interior(u)[:, 1, 1]
    v₁ = interior(v)[:, 1, 1]
    η₁ = interior(η)[:, 1, 1]
    ηx₁ = interior(ηx)[:, 1, 1]

    @show mean(η₀)
    @show mean(η₁)

    Δη = η₁ .- η₀

    return (; η₀, η₁, Δη, ηx₀, ηx₁, u₀, u₁, v₀, v₁)
end

fft_based_free_surface = ImplicitFreeSurface(solver_method=:FFTBased)
pcg_free_surface = ImplicitFreeSurface(solver_method=:PreconditionedConjugateGradient)

free_surfaces = [fft_based_free_surface, pcg_free_surface]
simulations = [geostrophic_adjustment_simulation(free_surface) for free_surface in free_surfaces]
data = [run_and_analyze(sim) for sim in simulations]

fft_data = data[1]
pcg_data = data[2]

#pcg_p = plot([pcg_data.η₀ pcg_data.η₁ pcg_data.Δη], label=["η₀" "ηᵢ" "Δη"], linewidth=2)
#fft_p = plot([fft_data.η₀ fft_data.η₁ fft_data.Δη], label=["η₀" "ηᵢ" "Δη"], linewidth=2)

pcg_p_η = plot([pcg_data.η₀ pcg_data.η₁], label=["η₀" "ηᵢ"], linewidth=2)
fft_p_η = plot([fft_data.η₀ fft_data.η₁], label=["η₀" "ηᵢ"], linewidth=2)

pcg_p_ηx = plot([pcg_data.ηx₀ pcg_data.ηx₁], label=["ηx₀" "ηxᵢ"], linewidth=2)
fft_p_ηx = plot([fft_data.ηx₀ fft_data.ηx₁], label=["ηx₀" "ηxᵢ"], linewidth=2)

pcg_p_u = plot([pcg_data.u₀ pcg_data.u₁], label=["u₀" "uᵢ"], linewidth=2)
fft_p_u = plot([fft_data.u₀ fft_data.u₁], label=["u₀" "uᵢ"], linewidth=2)

p = plot(pcg_p_η, fft_p_η,
         pcg_p_u, fft_p_u,
         pcg_p_ηx, fft_p_ηx,
         layout=(3, 2), titles = ["PCG η" "FFT η" "PCG u" "FFT u" "PCG η_x" "FFT η_x"])

display(p)


include("SupportFunctions.jl")

function updateS(Y,A::Array{Float64,2},lambda,S::Array{Float64,2};maxIter=10000,threshold=1e-5)

    H=A'*A
    AtY=A'*Y

    R=deepcopy(S)

    Sp=S
    L=0
    try
        L=eigmax(H)
    catch
        return (S,0,false)
        error("Error in eigenvalue calculation")
    end

    At=A'
    t=1
    tp=t

    i=1

    converged=false
    while i<maxIter
        tp=t
        t=(1+(1+4*tp^2)^0.5)/2

        w=(tp-1)/t

        R=(1+w)*S-w*Sp
        Sp=S
        try
            #S=plusOp(soft(R-(H*R-AtY)/L,lambda/L))
            S=max(R-(H*R-AtY)/L-lambda/L,0)
        catch
            error("Error in S Iteration calculation")
        end
        if sum(abs(Sp-S))<(threshold*sum(S))
            converged=true
            break
        end

        i+=1
    end
    return (S,i,converged)
end

function updateA(Y,S::Array{Float64,2},A::Array{Float64,2};maxIter=10000,threshold=1e-5)

    Ap=A
    B=zeros(size(A))

    H=S*S'
    L=eigmax(H)
    YSt= Y*S'

    t=1
    tp=t

    i=1
    converged=false
    while i<maxIter

        tp=t
        t=(1+(1+4*tp^2)^0.5)/2

        B = (1+(tp-1)/(t)) * A - (tp-1)/(t) * Ap
        Ap=A
        A = max(B-(B*H-YSt)/L,0)

        if sum(abs(Ap-A))<(threshold*sum(A))
            converged=true
            break
        end
        i+=1
    end
    return (A,i,converged)
end

#this definition was taken from thier matlab code with some modifications
function updateLambda!(A::Array{Float64,2},S::Array{Float64,2},Y,iteration,maxIter, phaseRatio,lambda)
    refinement_beginning = floor(phaseRatio * maxIter)

    if refinement_beginning > iteration

        sigma_residue = 1.4826 * median(abs((Y - A*S)[:].-median((Y - A*S)[:])))
        #linear decrease to tau_MAD*sigma_residue when reaching the refinement steps
        lambda = maximum([sigma_residue, lambda - 1/(refinement_beginning - iteration) * (lambda - sigma_residue)])

    else #during refinement, do not modify lambda
        lambda = lambda
    end

    return lambda
end

function nGMCA(Y::Array{Float64},r;verbose=false,maxIter=5000,threshold=1e-6,phaseRatio=0.30,kickstart=true,submaxiter=200)

    #Randomly build one of the two output matricies
    S=rand(size(Y,2),r)'

    #Fit the first as an approximation of the other
    A=Y/S

    #Estimate an L0
    l=maximum(abs(A'*(A*S-Y)))

    if verbose && kickstart
        print("\nKickstarting BSS...")
    end

    if kickstart
        #Kick Start this show!
        A=plusOp(Y/S)
        S=plusOp(A\Y)
        S=updateS(Y,A,0,S;maxIter=submaxiter)[1]
        (A,S)=m_reinitializeS(A,S, Y, verbose)
        A=updateA(Y,S,A;maxIter=submaxiter)[1]
        if verbose
          print("\nKickstart complete.")
        end
    end

    if verbose
        print("\nL0=",""*string(l,"\nStart Full BSS:\n"))
    end

    #Run the algorithm
    j=1
    for j=1:maxIter
        Ap=A
        Sp=S

        #normalize columns of A
        for i=1:size(A,2)
            A[:,i]= A[:,i]./(sum(A.^2.,1).^.5)[i]
        end

        (A,S)=m_reinitializeS(A,S, Y, verbose)
        (S,S_iter,S_converge)=updateS(Y,A,l,S;maxIter=submaxiter)
        (A,S)=m_reinitializeS(A,S, Y, verbose)
        (A,A_iter,A_converge)=updateA(Y,S,A;maxIter=submaxiter)
        l=updateLambda!(A,S,Y,j,submaxiter,phaseRatio,l)

        if verbose && mod(j,max(div(maxIter,100),1))==0
            print("\r"*string("GI(S) ",round(GI(S),3)," GI(A) ",
            round(GI(A),3),"\t Iterations Total:",j,"  S:",S_iter," A: ",A_iter,
            "\tL:",round(l,5),"\t Dist to Y: ",round(sum((A*S-Y).^2.).^.5),5))
        end

        if mod(j,10)==0 && sum(abs(A-Ap))/length(A)<threshold && sum(abs(S-Sp))/length(S)<threshold
            break
        end
    end
    if verbose
        print("\nDone.\n")
        if j>=maxIter
            print("\nFailed to converge!\n")
        end
    end
    return {A,S}
end

# reinitialization when a source was set to 0
function m_reinitializeS(A,S, Y, verbose)
    #straightforward reinitialization of a line of S and a column of A
    #by picking one column in the residue
    attempt=0
    while sum(sum(S, 2) .== 0) > 0 && attempt<10
        if verbose
            print("\r"*string("Reinitilizing blank rows/columns: ",sum(sum(S,2).==0)))
        end
        indices = [sum(S, 2) .== 0][:]

        (A[:, indices], S[indices, :]) = m_fastExtractNMF(Y - A * S, sum(indices))
        attempt+=1
    end
    (A,S)
end

function m_fastExtractNMF(residual, r)
    if r > 0
        (m, n) = size(residual);
        A = zeros(m, r);
        S = zeros(r, n);
        for i = 1 : r
            residual = max(residual,0)
            if sum(residual[:]) != 0
                #compute square norm of residual to select maximum one
                res2 = sum(residual.^2,1);
                j = findin(res2,maximum(res2));
                if !isempty(j)
                    j = j[1];
                    if res2[j] > 0
                        #normalize maximum residual
                        A[:, i] = residual[:, j] / res2[j].^.5;
                        #compute scalar product with the rest of the residual and keep only
                        #positive coefficients
                        S[i, :] = A[:, i]' * residual;
                        S[i, :] = max(S[i, :], zeros(size(S[i,:])) );
                        #compute new residual
                        residual = residual - A[:, i] * S[i, :];
                    end
                end
            end
        end
    else
        A = [];
        S = [];
    end

    return (A,S)
end


module Schwarzschild

include("SpectralSolver.jl")
include("NewtonSolver.jl")
using .NewtonSolver
using .SpectralSolver
using NLsolve
using CSV

function LoadLowSchwarzschildQnms()
    A = CSV.read(joinpath(@__DIR__, "SwData.csv"),NamedTuple)
    ωlist = Dict{NamedTuple,Complex{Float64}}()
    for i=1:length(A[:l])
        merge!(ωlist,Dict((l = A[:l][i], s = A[:s][i], n = A[:n][i]) => A[:ωreal][i]+im*A[:ωimag][i]))
    end
    ωlist
end

SchwMODESLOWWℓ = LoadLowSchwarzschildQnms();

function High_l_Guess(l,n,s)
    #Taken Directly from Eq: 17-22 of https://arxiv.org/pdf/0908.0329.pdf
    L = l+0.5
    N = n+0.5
    β = 1-s^2

    ω₋₁ = 1
    ω₀ᵢ = 1
    ω₁ = β/3 - (5/36)*N^2 - (115/432)
    ω₂ᵢ = β/9 + (235/3888)*N^2 - (1415/15552)
    ω₃ = -(1/27)*β^2 + β*(204*N^2 +211)/3888 + (854160*N^4 - 1664760*N^2 - 776939)/(40310784)
    ω₄ᵢ = (1/27)*β^2 + (1100*N^2 - 2719)/(46656) + (11273136*N^4 - 52753800*N^2 + 66480535)/(2902376448)

    ω = (ω₋₁*L + ω₁*(L^(-1)) + ω₃*(L^(-3))) - im*N*(ω₀ᵢ + ω₂ᵢ*(L^(-2)) - ω₄ᵢ*(L^(-4)))
    ω = (1/sqrt(27))*ω
end

function Low_l_Guess(l,n,s)
    SchwMODESLOWWℓ[(l=l,s=s,n=n)]
end

function High_n_Guess(l,n,s)
    print("Used higher n guess")
    # From H.-J. Blome, B. Mashhoon, Phys. Lett. A 110, 231 (1984)
    return (0.5 + l - im*(0.5 + n))/(3.0*sqrt(3))
end

function SchwarzschildGuess(l,n,s)
    if l >= 3
        return High_l_Guess(l,n,s)
    else
        return Low_l_Guess(l,n,s)
    end
end

function SchwarzschildModes(l,n,s)
    #if l < 3
    #   return SchwarzschildGuess(l,n,s),l*(l+1) - s*(s+1)
    #end
    ωguess = SchwarzschildGuess(l,n,s)
    #println(ωguess)
    Almsch = l*(l+1) - s*(s+1)
    P = ModeParameters((l=l,s=s,m=0,n=n,a=0.0,ω = ωguess,Alm = Almsch, Nmax = 10000, lmax = 10))
    #Newton!(P,0.0000001)

    function CFSch(P::ModeParameters)
        n = P.n; N = P.Nmax; ω = P.ω; Alm = P.Alm
        αₙsch(n) = (1+n)*(1+n-s-4*im*ω)
        βₙsch(n) = -1 - Alm - 2*n^2 - s + n*(-2 + im*16*ω) + im*6*ω + 24*ω^2 + ω*(im*2 + 8*ω)
        γₙsch(n) = (n-4*im*ω)*(n+s-4*im*ω)
        ContinuedFraction(αₙsch,βₙsch,γₙsch,n,N)
    end

    function f!(F,x)
        #ωᵢ = P.ω
        P.ω = x[1]+im*x[2]
        val = CF(P)
        #val = CFSch(P)
        #P.ω = ωᵢ
        F[1] = real(val)
        F[2] = imag(val)
    end

    NmaxTrials = [100,300,500,1000,5_000,10_000,50_000,100_000]
    ω₀ = P.ω; Alm₀ = P.Alm
    ωnew = P.ω + 1.0;
    iterations = 1;
    while (abs(ωnew-ω₀) > 1e-10) & (iterations < 8)
        P.Nmax = NmaxTrials[iterations]
        ω₀ = P.ω;
        sol = nlsolve(f!, [real(ωguess),imag(ωguess)],ftol = 1e-14, method = :newton)
        ωnews = sol.zero
        P.ω = ωnews[1] + im*ωnews[2]
        ωnew = P.ω;
        iterations += 1
        #println("ω diff = ",abs(ωnew-ω₀))
    end
    #println("Number of Iterations = ", iterations)
    P.ω, P.Alm
end

export SchwarzschildModes

#SchwarzschildModes(2,6,0) |> print

#0.30105345461250616 - 0.4782769832230698im
end


"""
Numeric representation of SAT problem in DIMACS-CNF form

Input:
- `vars` is a list of symbolic Boolean variables
- `clauses` are integer vectors; negative numbers indicate negated variables
"""

"""SAT problem represented numerically in DIMACS-CNF form:
clauses are integer vectors with negative numbers indicating negated literals
"""
struct SATProblem
    num_variables::Int
    clauses::Vector{Vector{Int}}
end

max_var(clause) = maximum(abs.(clause))

"Constructor which calculates the number of variables automatically"
function SATProblem(clauses::Vector{Vector{Int}})
    num_variables = maximum(max_var.(clauses))

    return SATProblem(num_variables, clauses)
end

push!(p::SATProblem, clause) = push!(p.clauses, clause)
ban!(p::SATProblem, clause) = push!(p, .-(clause))

function all_solutions(p::SATProblem)

    solns = []
    status = :sat

    while true
        status, results = solve(p)
        status == :unsat && break

        push!(solns, results)
        ban!(p, results)

    end

    return solns
end


using StatsBase, DataFrames, Flux
include("2048_fn.jl")

using Flux
model = Flux.Chain(
  Dense(14*16+4, 128, relu),
  Dense(128, 64, relu),
  Dense(64, 32, relu),
  Dense(32, 1, relu));

loss(x,y) = sum((model(x) .- y).^2)
opt = ADAM(params(model))

ϵ = 1
@time for i=1:1000
  global ϵ
  ϵ -= 0.0001
  simulate_one_game_and_train!(model, opt, ϵ)
  if i % 100 == 0
    println("epsilon: $ϵ")
    init_grid = init_game()
    res = simulate_game!(init_grid, model, 0)
    display(res[2])
    assess_model(model)
  end
end

using JLD2
@save "no_neg_model.jld2" model

# play one game to see how it goes
function assess_model(model)
  init_grid = init_game()
  @time (init_grid, fnl, seq, cart, tf, reward) = simulate_game!(init_grid, model, 0.5)

  # obtain the discounted rewards
  y = discounted_rewards(reward, 0.99)[1:end-1];

  # score every move using  model
  updated_model = [Tracker.data(model(grid_move_to_feature(sim_seq(init_grid, seq[1:i+1], cart[1:i+1], tf[1:i+1]), seq[i])))[1] for i=1:length(seq)-1]
  #plot(cumsum(reward))
  plot(updated_model, label="model")
  plot!(y, label="discounted rewards")
end

assess_model(model)


using SPN
using MNIST
using MultivariateStats
using Gadfly

x, y = traindata()
@show size(x)
@show size(y)

pcaModel = fit(PCA, x; maxoutdim=9)
xTransformed = transform(pcaModel, x)
# swap rows and columns:
x = xTransformed'

# Normalize the dataset such that each
# variable (column) has mean 0 and variance 1.

function normalizeDataset!(x::AbstractArray)
    x = x .- mean(x,1)
    x = x
    return x ./ std(x,1)
end

x = normalizeDataset!(x)
spn = structureLearnPoon(x,3,3,nsum=5,nleaf=10,baseres=1)
x = x[1:100,:]
llhvals = parameterLearnEM!(spn, x)

plot(y=llhvals, Geom.point, Geom.line)


# Activate VectorAutoregressions project first
cd(@__DIR__)
include("../examples/lutkepohl_example.jl")
using LinearAlgebra

v = VAR(Y, 2)
p = v.lags
B = v.B
sigma = v.ΣU
K = size(B, 2)
h = 12

phi = VectorAutoregressions.simple_irf(B, K, p, h)
theta = VectorAutoregressions.orthogonal_irf(phi, sigma, K, h)

irf = VectorAutoregressions.IRF(v, h, orthogonal = false)
cirf = VectorAutoregressions.IRF(v, h, orthogonal = false, cumulative = true)


module MoreBasisFunctions

    using GridArrays: ScatteredGrid
    using BasisFunctions

    export Bernstein, BernsteinIndex, BernsteinInterval
    export Lagrange, LagrangeIndex, LagrangeInterval
    export LagrangeGaussLegendre, LagrangeLobattoLegendre
    export LagrangeGaussChebyshev, LagrangeLobattoChebyshev

    export eval_element, eval_element_derivative, eval_element_antiderivative
    export nodes, nnodes, degree, support

    include("bases/generic/dictionary.jl")
    include("bases/generic/nodes.jl")
    include("bases/generic/vandermonde_matrix.jl")

    include("bases/poly/bernstein.jl")
    include("bases/poly/lagrange.jl")

end # module


kon = rand()
koff = rand()

c1 = chem2diff("""
               $(kon),$(koff): 2 A -> B
          """)
c2 = chem2diff("""
               $(kon),$(koff): 2A -> B
               """)

c3 = chem2diff("""
               $(kon),$(koff):A +A -> B
               """)

n = 2
z = 1.0*ones(n)
dz = zeros(n)

dz[1] = 2*(-kon*z[1]^2 + koff*z[2])
dz[2] = kon*z[1]^2 - koff*z[2]

#This works only because of the fixed usage of `y`,`dy`
y = ones(n)
dy = zeros(n)

eval(c1)
@test all(dy .≈ dz)

eval(c2)
@test all(dy .≈ dz)

eval(c3)
@test all(dy .≈ dz)


# Beta Distributed Arm

struct Beta <: BanditArmBase
    armDist::Distributions.Beta

    function Beta( α::Real, β::Real )
        new( Distributions.Beta( α, β ) )
    end
end

function pull!( arm::Beta )
    return Distributions.rand( arm.armDist )
end

function tick!( arm::Beta )
    # Do nothing
    nothing
end

function reset!( arm::Beta )
    # Do nothing
    nothing
end


using ApproxFunOrthogonalPolynomials, ApproxFunBase, SpecialFunctions, Test
    import ApproxFunBase: testbandedoperator

@testset "Hermite and GaussWeight" begin
    @testset "Evaluation" begin
        f = Fun(x-> x + x^2, Hermite())
        @test f(1.0) ≈ 2.0
        @test values(f) ≈ points(f) + points(f).^2

        w = Fun(GaussWeight(), [1.0])
        @test w(0.1) ≈ exp(-0.1^2)
        w = Fun(GaussWeight(), Float64[])
        @test w(1) == 0

        w = Fun(GaussWeight(), Float64[1.0])
        f = Fun(x-> 1 + x + x^2, Hermite()) * w
        @test f(0.1) ≈ exp(-0.1^2) * (1+0.1+0.1^2)

        f = Fun(x-> 1 + x + x^2, Hermite(2)) * w
        @test f(0.1) ≈ exp(-0.1^2) * (1+0.1+0.1^2)

        w = Fun(GaussWeight(Hermite(2), 0), [1.0,2.0,3.0]);
        w̃ = Fun(Hermite(2), [1.0,2.0,3.0]);
        @test w(0.1) == w̃(0.1)

        @test ApproxFunOrthogonalPolynomials.Recurrence(Hermite())[1:10,1:10]/sqrt(2) ≈ ApproxFunOrthogonalPolynomials.Recurrence(Hermite(2))[1:10,1:10]

        @test points(Hermite(),10) == sqrt(2)points(Hermite(2),10)

        @test Fun(Hermite(),[1])(0.5) == 1
        @test Fun(Hermite(),[0,1])(0.5) == 2*0.5

        @test Fun(Hermite(2),[1])(0.5) == 1
        @test Fun(Hermite(2),[0,1])(0.5) == 2*sqrt(2)*0.5

        @test Fun(x-> 1 + x + x^2, Hermite())(0.5) ≈ Fun(x-> 1 + x + x^2, Hermite(2))(0.5) ≈ 1.75

        p = points(Hermite(),3)
        @test values(Fun(x-> 1 + x + x^2, Hermite())) ≈ 1 .+ p .+ p.^2

        p = points(Hermite(2),3)
        @test values(Fun(x-> 1 + x + x^2, Hermite(2))) ≈ 1 .+ p .+ p.^2

        w = Fun(GaussWeight(Hermite(2), 2), Float64[1.0])
        f = Fun(x-> 1 + x + x^2, Hermite()) * w
        @test space(f) == space(w)
        @test f(0.1) ≈ exp(-2*0.1^2) * (1+0.1+0.1^2)
        f = Fun(x-> 1 + x + x^2, Hermite(2)) * w
        @test space(f) == space(w)
        @test f(0.1) ≈ exp(-2*0.1^2) * (1+0.1+0.1^2)

        L = 1.3; x = 1.2;
        H₀ = Fun(Hermite(), [1.0])
        H̃₀ = Fun(Hermite(L), [1.0])
        @test H̃₀(x) ≈ H₀(sqrt(L) * x)

        L = 1.3; x = 1.2;
        H₁ = Fun(Hermite(), [0.0,1.0])
        H̃₁ = Fun(Hermite(L), [0.0,1.0])
        @test H̃₁(x) ≈ H₁(sqrt(L) * x)
    end

    @testset "Derivative" begin
        D = Derivative(Hermite())
        testbandedoperator(D)

        f = Fun( x-> x + x^2, Hermite())
        g = D * f
        @test g(1.) ≈ 3.
    end
end


cd(@__DIR__); include("setups/grid7x3.jl");

## ROT1 + pmf1
D = natural_eigdist(𝚽, 𝛌, Q; α = 0.5, input_format = :pmf1, distance = :ROT)
E = transform(fit(MDS, D, maxoutdim=2, distances=true))
plt = grid7x3_mds_heatmaps(E, 𝚽; backend = :pyplot)
savefig(plt, "../figs/Grid7x3_MDS_ROT1_pmf1_alpha05.png")
display(plt)


using Gnuplot
x = LinRange(-10,10,200)
fg(x,μ,σ) = exp.(.-(x.-μ).^2 ./(2σ^2))./(σ*√(2π))
@gp x fg(x, 0.25, 1.5) "w filledcurves lc '#E69F00' t '0.25,1.5'"
@gp :- "set style fill transparent solid 0.5 noborder"
@gp :- "set key title 'μ,σ' box 3" xlab = "x" ylab = "P(x)"
@gp :- x fg(x, 2, 1) "w filledcu lc rgb '#56B4E9' t '2,1' fs transparent solid 0.25"
@gp :- x fg(x, -1, 2) "w filledcu lc '#009E73' t '-1,2' fs transparent solid 0.1"
save(term="pngcairo size 600,400", output="./code/plt_ex14.png") # hide


mutable struct MiniMaxSampleSize <: Objective
    λ::Real
    prior::Prior
end

function (objective::MiniMaxSampleSize)(design::AbstractDesign)

    ess = SampleSize(objective.prior)
    return (1 - objective.λ)*maximum(n(design)) + λ*ess(design)
end

function update(obj::MiniMaxSampleSize, prior::Prior)
    obj = deepcopy(obj)
    obj.prior = prior
    return obj
end

function add!(
        JuMP_model_and_indicator_variables::Tuple,
        objective::MiniMaxSampleSize,
        problem::Problem;
        partial::Tuple{TI,TI} = (0, 0)
    ) where {TI<:Integer}

    m, ind = JuMP_model_and_indicator_variables
    prior  = objective.prior

    @variable(m, nmax)
    for n1_ in problem.n1values
        for x1_ in partial[1]:n1_
            @constraint(m, nmax >= sum(
                (n1_ + n2) * ind[(n1_, x1_, n2, c2)]
                    for (x1, n2, c2) in grid(problem, n1_)
                    if  (x1 == x1_)
                )
            )
        end
    end
    @objective(m, Min,
        (1 - objective.λ)*nmax +
        objective.λ*sum(
            (n1 + n2) * pmf(x1, n1, objective.prior; partial = partial) * ind[(n1, x1, n2, c2)]
                for (n1, x1, n2, c2) in grid(problem)
        )
    )
end


export Matsuno_fast,Matsuno

"""
	Matsuno_fast(param;kwargs)

Same as `Matsuno`, but faster.
"""
mutable struct Matsuno_fast <: AbstractModel

	label   :: String
	f!		:: Function
	mapto	:: Function
	mapfro	:: Function
	info	:: String

    function Matsuno_fast(param::NamedTuple;
							mesh = Mesh(param),
							IL	    = false,
							dealias = false,
							label = "Matsuno" )

		# Set-up
		μ 	= param.μ
		ϵ 	= param.ϵ
		if !in(:ν,keys(param))
			if μ > 1
				ν = 1/sqrt(μ)
				nu = "1/√μ (deep water case)"
			else
				ν = 1
				nu = "1 (shallow water case)"
			end
		else
			ν = param.ν
			nu = "$ν"
		end
		if μ == Inf || ν==0 || IL == true # infinite layer case
			IL = true;  # IL (=Infinite layer) is a flag to be used thereafter
			μ = 1; ν = 1; # Then we should set μ=ν=1 in subsequent formula.
		end

		# Print information
		info = "Matsuno model.\n"
		if IL == true
			info *= "├─Steepness parameter ϵ=$ϵ (infinite depth case).\n"
		else
			info *= "├─Shallowness parameter μ=$μ, nonlinearity parameter ϵ=$ϵ, \
					scaling parameter ν=$nu.\n"
		end
		if dealias == true || dealias == 1
			info *= "└─Dealiasing with Orszag’s 3/2 rule. "
		else
			info *= "└─No dealiasing. "
		end
		info *= "\nDiscretized with $(mesh.N) collocation points on [$(mesh.xmin), $(mesh.xmax)]."

		# Pre-allocate useful data
		x = mesh.x
		k = mesh.k
		if IL == true
			Tμ 	= -1im * sign.(k)
			G₀ 	= abs.(k)
		else
			Tμ 	= -1im* sign.(k) .* tanh.(sqrt(μ)*abs.(k))
			G₀ 	= sqrt(μ)*abs.(k).*tanh.(sqrt(μ)*abs.(k))
		end
		∂ₓ	=  1im * sqrt(μ)* k            # Differentiation
		if dealias == true || dealias == 1
			Π⅔    = abs.(k) .< (mesh.kmax-mesh.kmin)/3    # Dealiasing low-pass filter
		else
			Π⅔    = zero(k) .+ 1     		# No dealiasing (Π⅔=Id)
		end

        ζ = zeros(Complex{Float64}, mesh.N)
        unew = zeros(Complex{Float64}, mesh.N)

        I₀ = zeros(Complex{Float64}, mesh.N)
        I₁ = zeros(Complex{Float64}, mesh.N)
        I₂ = zeros(Complex{Float64}, mesh.N)
        I₃ = zeros(Complex{Float64}, mesh.N)

        Px  = plan_fft(ζ; flags = FFTW.MEASURE)

		# Evolution equations are ∂t U = f(U)
		function f!(U)

		    for i in eachindex(ζ)
		        ζ[i] = G₀[i] * U[i,1]
		    end

		    ldiv!(unew, Px, ζ )

		    for i in eachindex(ζ)
		        ζ[i] = ∂ₓ[i] * U[i,1]
		    end

		    ldiv!(I₁, Px, ζ)

		    unew  .*= I₁

		    mul!(I₁, Px, unew)

		    I₁  .*= ϵ .* Π⅔
		    I₁  .-= ζ

		    ldiv!(ζ, Px, view(U,:,1))
		    ldiv!(unew, Px, view(U,:,2))

		    I₂    .= ζ .* unew

		    mul!(I₃, Px, I₂)

		    I₃    .*= ∂ₓ

		    for i in eachindex(Tμ)
		        U[i,1]  = Tμ[i] * U[i,2]
		        I₀[i] = G₀[i] * U[i,2]
		    end

		    ldiv!(I₂, Px, I₀)

		    I₂    .*= ζ

		    mul!(ζ, Px, I₂)

		    ζ  .*= Tμ
		    I₃    .+= ζ
		    I₃    .*= ϵ .* Π⅔

		    for i in eachindex(I₃)
		        U[i,1] -= I₃[i]
		    end
			U[:,1] ./= sqrt(μ)/ν

		    I₃    .=  unew.^2

		    mul!(unew, Px, I₃)

		    unew  .*= ∂ₓ
		    unew  .*= ϵ/2/ν .* Π⅔
		    I₁    .-= unew

		    for i in eachindex(I₁)
		        U[i,2] =  I₁[i]/sqrt(μ)
		    end

		end

		# Build raw data from physical data.
		function mapto(data::InitialData)
			fftη = Π⅔ .* fft(data.η(x));
			fftv = Π⅔ .* fft(data.v(x));
			U = [fftη fftv-ϵ* Π⅔ .*fft(ifft(Tμ.*fftv).*ifft(∂ₓ.*fftη) )]
			return U
		end

		# Reconstruct physical variables from raw data
		# Return `(η,v,x)`, where
		# - `η` is the surface deformation;
		# - `v` is the derivative of the trace of the velocity potential;
		# - `x` is the vector of collocation points
		function mapfro(U;n=10)
			∂ζ=ifft(∂ₓ.*U[:,1]);
			I₁.=view(U,:,2);I₂.=view(U,:,2);
			for j=1:n
				I₂.=I₁+ϵ*Π⅔ .* fft( ∂ζ .* ifft(Tμ.*I₂))
			end
			real(ifft(view(U,:,1))),real(ifft(I₂)),mesh.x
		end

		new(label, f!, mapto, mapfro, info )
    end
end

"""
	Matsuno(param;kwargs)

Define an object of type `AbstractModel` in view of solving the initial-value problem for
the quadratic deep-water model proposed by [Matsuno](https://doi.org/10.1103/PhysRevLett.69.609).

# Argument
`param` is of type `NamedTuple` and must contain
- dimensionless parameters `ϵ` (nonlinearity) and `μ` (dispersion);
- optionally, `ν` the shallow/deep water scaling factor. By default, `ν=1` if `μ≦1` and `ν=1/√μ` otherwise. Set the infinite-layer case if `ν=0`, or `μ=Inf`.
- numerical parameters to construct the mesh of collocation points, if `mesh` is not provided as a keyword argument.

## Optional keyword arguments
- `IL`: Set the infinite-layer case if `IL=true` (or `μ=Inf`, or `ν=0`), in which case `ϵ` is the steepness parameter. Default is `false`.
- `mesh`: the mesh of collocation points. By default, `mesh = Mesh(param)`;
- `dealias`: dealiasing with `1/3` Orlicz rule if `true` or no dealiasing if `false` (by default);
- `label`: a label for future references (default is `"Matsuno"`);

# Return values
Generate necessary ingredients for solving an initial-value problem via `solve!`:
1. a function `Matsuno.f!` to be called in explicit time-integration solvers;
2. a function `Matsuno.mapto` which from `(η,v)` of type `InitialData` provides the raw data matrix on which computations are to be executed;
3. a function `Matsuno.mapfro` which from such data matrix returns the Tuple of real vectors `(η,v,x)`, where
    - `η` is the values of surface deformation at collocation points `x`;
    - `v` is the derivative of the trace of the velocity potential at `x`.

"""
mutable struct Matsuno <: AbstractModel

	label   :: String
	f!		:: Function
	mapto	:: Function
	mapfro	:: Function
	info 	:: String

    function Matsuno(param::NamedTuple ;
						mesh = Mesh(param),
						IL	    = false,
						dealias = false,
						label = "Matsuno" )

		# Set up
		μ 	= param.μ
		ϵ 	= param.ϵ
		if !in(:ν,keys(param))
			if μ > 1
				ν = 1/sqrt(μ)
				nu = "1/√μ (deep water case)"
			else
				ν = 1
				nu = "1 (shallow water case)"
			end
		else
			ν = param.ν
			nu = "$ν"
		end
		if μ == Inf || ν==0 || IL == true # infinite layer case
			IL = true;  # IL (=Infinite layer) is a flag to be used thereafter
			μ = 1; ν = 1; # Then we should set μ=ν=1 in subsequent formula.
		end

		# Print information
		info = "Matsuno model.\n"
		if IL == true
			info *= "├─Steepness parameter ϵ=$ϵ (infinite depth case).\n"
		else
			info *= "├─Shallowness parameter μ=$μ, nonlinearity parameter ϵ=$ϵ, \
					scaling parameter ν=$nu.\n"
		end
		if dealias == true || dealias == 1
			info *= "└─Dealiasing with Orszag’s 3/2 rule. "
		else
			info *= "└─No dealiasing. "
		end
		info *= "\nDiscretized with $(mesh.N) collocation points on [$(mesh.xmin), $(mesh.xmax)]."

		# Pre-allocate useful data
		x = mesh.x
		k = mesh.k
		if IL == true
			Tμ 	= -1im * sign.(k)
			G₀ 	= abs.(k)
		else
			Tμ 	= -1im* sign.(k) .* tanh.(sqrt(μ)*abs.(k))
			G₀ 	= sqrt(μ)*abs.(k).*tanh.(sqrt(μ)*abs.(k))
		end
		∂ₓ	=  1im * sqrt(μ)* k            # Differentiation

		if dealias == true || dealias == 1
			Π⅔    = abs.(k) .< (mesh.kmax-mesh.kmin)/3 	# Dealiasing low-pass filter
		else
			Π⅔    = zero(k) .+ 1     		# No dealiasing (Π⅔=Id)
		end

        ζ = zeros(Complex{Float64}, mesh.N)
        unew = zeros(Complex{Float64}, mesh.N)

        I₁ = zeros(Complex{Float64}, mesh.N)
        I₂ = zeros(Complex{Float64}, mesh.N)
        I₃ = zeros(Complex{Float64}, mesh.N)

		# Evolution equations are ∂t U = f(U)
		function f!(U)

		   ζ .= ifft(U[:,1])
		   unew .= ifft(U[:,2])
		   I₃ .= fft(ifft(∂ₓ.*U[:,1]).*ifft(G₀.*U[:,1]))
		   I₁ .= Tμ.*U[:,2].-ϵ*Π⅔.*(Tμ.*fft(ζ.*ifft(G₀.*U[:,2])).+∂ₓ.*fft(ζ.*unew))
		   I₂ .= -ν*(∂ₓ.*U[:,1])+ϵ*Π⅔.*(ν*I₃-∂ₓ.*fft(unew.^2)/2)
		   #
		   U[:,1] .= I₁/sqrt(μ)/ν
		   U[:,2] .= I₂/sqrt(μ)/ν

		end

		# Build raw data from physical data.
		function mapto(data::InitialData)
			fftη = Π⅔ .* fft(data.η(x));
			fftv = Π⅔ .* fft(data.v(x));
			U = [fftη fftv-ϵ* Π⅔ .*fft(ifft(Tμ.*fftv).*ifft(∂ₓ.*fftη) )]
			return U
		end

		# Reconstruct physical variables from raw data
		# Return `(η,v,x)`, where
		# - `η` is the surface deformation;
		# - `v` is the derivative of the trace of the velocity potential;
		# - `x` is the vector of collocation points
		function mapfro(U;n=10)
			∂ζ=ifft(∂ₓ.*U[:,1]);
			I₁.=U[:,2];I₂.=U[:,2];
			for j=1:n
				I₂.=I₁+ϵ*Π⅔ .* fft( ∂ζ .* ifft(Tμ.*I₂))
			end
			real(ifft(U[:,1])),real(ifft(I₂)),mesh.x
		end

		new(label, f!, mapto, mapfro, info )
    end
end


using DelayEmbeddings, DynamicalSystemsBase
using Test
using Random

@testset "Pecora" begin
# %% Generate data
lor = Systems.lorenz([0.0;1.0;0.0];ρ=60)
data = trajectory(lor, 200; Δt=0.02, Ttr = 10)
metric = Chebyshev()

@testset "Pecora univariate" begin
# %% Timeseries case

    s = data[:, 2] # input timeseries = first entry of lorenz
    optimal_τ = estimate_delay(s, "mi_min")
    Tmax = 100
    K = 14
    samplesize = 1

    τs = (0,)
    Random.seed!(123)
    es_ref, Γs = pecora(s, τs; delays = 0:Tmax, w = optimal_τ, samplesize = samplesize, K = K, metric = metric)
    (max1,_) = DelayEmbeddings.findlocalextrema(vec(es_ref))
    @test optimal_τ - 2 ≤ max1[1]-1 ≤ optimal_τ + 2
    maxi = maximum(es_ref)
    @test maxi ≤ 1.3

    Random.seed!(123)
    es_ref2, Γs = pecora(s, τs; delays = 0:Tmax, w = optimal_τ, K = K, metric = metric)
    (max2,_) = DelayEmbeddings.findlocalextrema(vec(es_ref2))
    @test optimal_τ - 2 ≤ max2[1]-1 ≤ optimal_τ + 2
    maxi2 = maximum(es_ref2)
    @test maxi - .1 ≤ maxi2 ≤ maxi + .1

    τs = (0, max1[1]-1,)
    Random.seed!(123)
    es, Γs = pecora(s, τs; delays = 0:Tmax, w = optimal_τ, samplesize = samplesize, K = K, metric = metric)
    (max2,min1) = DelayEmbeddings.findlocalextrema(vec(es))
    @test 1 ≤ max2[1]-1 ≤ 4
    @test optimal_τ - 2 ≤ min1[2]-1 ≤ optimal_τ + 2

    τs = (0, max1[1]-1, max2[1]-1)
    Random.seed!(123)
    es, Γs = pecora(s, τs; delays = 0:Tmax, w = optimal_τ, samplesize = samplesize, K = K, metric = metric)
    (_,min2) = DelayEmbeddings.findlocalextrema(vec(es))
    @test max2[1]-2 ≤ min2[1]-1 ≤ max2[1]

end

@testset "Pecora multivariate" begin
## %% Trajectory case
    s = Dataset(data)
    optimal_τ = estimate_delay(s[:,2], "mi_min")
    Tmax = 100
    K = 14
    samplesize = 1

    js = (2,)
    τs = (0,)
    Random.seed!(123)
    es_ref, _ = pecora(s[:,2], τs; delays = 0:Tmax, w = optimal_τ, samplesize = samplesize, K = K, metric = metric)
    Random.seed!(123)
    es_ref2, _ = pecora(s, τs, js; delays = 0:Tmax, w = optimal_τ, samplesize = .2, K = K, metric = metric)
    Random.seed!(123)
    es, _= pecora(s, τs, js; delays = 0:Tmax, samplesize = samplesize, w = optimal_τ, K = K, metric = metric)

    @test round.(es[:,2], digits = 4) == round.(vec(es_ref),digits = 4)
    (x_maxi,_) = DelayEmbeddings.findlocalextrema(es[:,1])
    @test 8 ≤ x_maxi[2]-1 ≤ 10
    (z_maxi,_) = DelayEmbeddings.findlocalextrema(es[:,3])
    @test 13 ≤ z_maxi[2]-1 ≤ 15
    (x_maxi_ref,_) = DelayEmbeddings.findlocalextrema(es[:,2])
    (x_maxi_ref2,_) = DelayEmbeddings.findlocalextrema(es_ref2[:,2])

    @test x_maxi_ref[1:4] == x_maxi_ref2[1:4]
    @test es[x_maxi_ref[1:4],2] .- .1 ≤ es_ref2[x_maxi_ref2[1:4],2] ≤ es[x_maxi_ref[1:4],2] .+ .1

end
end


@testset "Vibrational Energy" begin
    NQ = 10000; Qi = -4; Qf = 4;
    Q = range(Qi, stop = Qf, length = NQ)

    ħω = 1
    func = x->CarrierCapture.harmonic(x, ħω,; E₀ = 0, Q₀ = 0)

    nev = 10
    e, v = CarrierCapture.solve1D_ev_amu(func, Q; nev = nev)

    @test e ≈ 0.5 .+  UnitRange(0, nev-1) atol = 1e-3
end


"""
    BTGFuncs
BTGFuncs contains definitions for various functions and classes of functions used by
the BTG model
"""
module BTGFuncs

using Distributions, Distances

export
    Parametric,
    Correlation,
    IsotropicCorrelation,
    SquaredExponential,
    Transform,
    BoxCox,
    Covariate,
    Identity,
    kernelmatrix,
    prime

"""
    Parametric

Abstract supertype for all parametric functions
"""
abstract type Parametric end

"""
    Correlation

Abstract supertype for all correlation functions
"""
abstract type Correlation <: Parametric end

"""
    IsotropicCorrelation <: Correlation

Abstract supertype for all isotropic correlation functions
"""
abstract type IsotropicCorrelation <: Correlation end

"""
    kernelmatrix(k::IsotropicCorrelation, θ, A, B)

Construct a correlation matrix by applying `k` with
parameters `θ` pairwise to the rows of `A` and `B`
"""
function kernelmatrix(k::IsotropicCorrelation, θ, A, B)
    return k.(θ, pairwise(Euclidean(), A, B, dims=1))
end

"""
    SquaredExponential(γ::Uniform)

The squared exponential kernel (aka the Gaussian kernel or the radial basis
function kernel) with a length scale
parameter `γ` given by a continuous uniform distribution.

```math
k_\\theta(\\tau) = \\exp\\Big\\lbrace -\\Big(\\frac{\\tau}{\\theta}\\Big)^2\\Big\\rbrace
```

```julia
SquaredExponential(Uniform()) # γ is uniform over [0, 1]
SquaredExponential(Uniform(a, b)) # γ is uniform over [a, b]

sampleparam(k) # A random sample of the parameters of k
k(θ, τ) # The kernel evaluated on the distance τ with parameters θ
```

External links
* [RBF Kernel on Wikipedia](https://en.wikipedia.org/wiki/Radial_basis_function_kernel)

"""
struct SquaredExponential <: IsotropicCorrelation
    γ::Union{Uniform, Exponential, Gamma}
end

function (k::SquaredExponential)(θ, τ)
    return exp(-(τ / θ[1])^2)
end

"""
    Transform

Abstract supertype for all transformation functions
"""
abstract type Transform <: Parametric end

"""
    BoxCox(λ::Uniform)

The Box-Cox power transformation function with parameter λ given
by a continuous uniform distribution.

```math
g_\\lambda(x) = \\begin{cases}
    \\frac{x^\\lambda - 1}{\\lambda} & \\lambda \\neq 0\\\\
    \\ln(x) & \\lambda = 0
\\end{cases}
```

```julia
BoxCox(Uniform()) # λ is uniform over [0, 1]
BoxCox(Uniform(a, b)) # λ is uniform over [a, b]

sampleparam(g) # A random sample of the parameters of g
g(λ, x) # The transformation evaluated on x with parameters y
prime(g, λ, x) # The derivative of the transformation evaluated on x with parameters y
```

External links
* [Power Transform on Wikipedia](https://en.wikipedia.org/wiki/Power_transform)

"""
struct BoxCox <: Transform
    λ::Union{Uniform, Normal}
end

function (g::BoxCox)(λ, x)
    return λ[1] == 0 ? log(x) : expm1(log(x) * λ[1]) / λ[1]
end

function prime(g::BoxCox, λ, x)
    return x^(λ[1] - 1)
end

"""
    Covariate

Abstract supertype for all covariate functions
"""
abstract type Covariate end

"""
    Identity

A covariate function which returns its input as-is
"""
struct Identity <: Covariate end

function (f::Identity)(x)
    return x
end

end # module


using Random, PGFPlotsX, KernelFunctions, Distributions, LaTeXStrings

X = reshape(collect(range(0.0,3.0,length=300)),:,1)

K = 5000

start = 25

sinactivation(x) = sqrt(2) * sin(x)
sincosactivation(x) = sin(x) + cos(x)

ffloor(x) = floor(x/π + 0.5)
_z(x) = (x - π * ffloor(x)) * (-1)^(ffloor(x))
triangeactivation(x) = π/(2*sqrt(2)) * _z(x)

_prelu(x) = π/4	* (_z(x) + _z(x + π/2))
prelu(x) = _prelu(x)

b = rand(-π:0.01:π, K)

f1 = sinactivation
f2 = sincosactivation

k1(x1, x2, w, f) = 1/K * sum( f(x1*w[k] + b[k]) * f(x2*w[k] + b[k]) for k in 1:K)
k2(x1, x2, w, f) = 1/K * sum( f(x1*w[k]) * f(x2*w[k]) for k in 1:K)

w₁ = rand(TDist(2 * 1/2), K)
k₁ = Matern12Kernel()
K₁ = kernelmatrix(k₁,X,obsdim=1)

w₂ = rand(TDist(2 * 3/2), K)
k₂ = Matern32Kernel()
K₂ = kernelmatrix(k₂,X,obsdim=1)

w₃ = rand(Normal(), K)
k₃ = SqExponentialKernel()
K₃ = kernelmatrix(k₃,X,obsdim=1)

z_sin₁ = k1.(Ref(X[start]), X, Ref(w₁), Ref(sinactivation))
z_sin₂ = k1.(Ref(X[start]), X, Ref(w₂), Ref(sinactivation))
z_sin₃ = k1.(Ref(X[start]), X, Ref(w₃), Ref(sinactivation))

z_sincos₁ = k2.(Ref(X[start]), X, Ref(w₁), Ref(sincosactivation))
z_sincos₂ = k2.(Ref(X[start]), X, Ref(w₂), Ref(sincosactivation))
z_sincos₃ = k2.(Ref(X[start]), X, Ref(w₃), Ref(sincosactivation))

z_tri₁ = k1.(Ref(X[start]), X, Ref(w₁), Ref(triangeactivation))
z_tri₂ = k1.(Ref(X[start]), X, Ref(w₂), Ref(triangeactivation))
z_tri₃ = k1.(Ref(X[start]), X, Ref(w₃), Ref(triangeactivation))

z_relu₁ = k1.(Ref(X[start]), X, Ref(w₁), Ref(prelu))
z_relu₂ = k1.(Ref(X[start]), X, Ref(w₂), Ref(prelu))
z_relu₃ = k1.(Ref(X[start]), X, Ref(w₃), Ref(prelu))

@pgf p1 = Axis(
    {
        no_markers,
        xlabel = raw"Input, $|x-x'|$",
        width = "15cm",
        height = "10cm",
        ymax = 1.2,
        no_markers,
        xticklabels={},
        yticklabels={},
        ymin = 0,
    },
    # Exponential
    PlotInc({color = "black", dashed}, Table(1:300, K₁[start,:])),
    LegendEntry("Exact"),

    PlotInc({color = "red", solid}, Table(1:300, vec(z_sin₁))),
    LegendEntry("Sinusiod"),

    PlotInc({color = "green", solid}, Table(1:300, vec(z_sincos₁))),
    LegendEntry("Sine Cosine"),

    # Matern 3/2
    PlotInc({color = "black", dashed}, Table(201:500, K₂[start,:])),
    PlotInc({color = "red", solid}, Table(201:500, vec(z_sin₂))),
    PlotInc({color = "green", solid}, Table(201:500, vec(z_sincos₂))),

    # Squared Exponential
    PlotInc({color = "black", dashed}, Table(401:700, K₃[start,:])),
    PlotInc({color = "red", solid}, Table(401:700, vec(z_sin₃))),
    PlotInc({color = "green", solid}, Table(401:700, vec(z_sincos₃))),

    [raw"\node at ",
     Coordinate(25, 1.1),
     raw"{Exponential};"],
     [raw"\node at ",
     Coordinate(225, 1.1),
     raw"{Mat\'ern-$\frac{3}{2}$};"],
     [raw"\node at ",
     Coordinate(425, 1.1),
     raw"{Squared Exponential};"]
)

@pgf p2 = Axis(
    {
        no_markers,
        xlabel = raw"Input, $|x-x'|$",
        width = "15cm",
        height = "10cm",
        ymax = 1.2,
        no_markers,
        xticklabels={},
        yticklabels={},
        ymin = 0,
    },
    # Exponential
    PlotInc({color = "black", dashed}, Table(1:300, K₁[start,:])),
    LegendEntry("Exact"),

    PlotInc({color = "blue", solid}, Table(1:300, vec(z_tri₁))),
    LegendEntry("Triangle Wave"),

    PlotInc({color = "orange", solid}, Table(1:300, vec(z_relu₁))),
    LegendEntry("Periodic ReLU"),

    # Matern 3/2
    PlotInc({color = "black", dashed}, Table(201:500, K₂[start,:])),
    PlotInc({color = "blue", solid}, Table(201:500, vec(z_tri₂))),
    PlotInc({color = "orange", solid}, Table(201:500, vec(z_relu₂))),

    # Squared Exponential
    PlotInc({color = "black", dashed}, Table(401:700, K₃[start,:])),
    PlotInc({color = "blue", solid}, Table(401:700, vec(z_tri₃))),
    PlotInc({color = "orange", solid}, Table(401:700, vec(z_relu₃))),

    [raw"\node at ",
     Coordinate(25, 1.1),
     raw"{Exponential};"],
     [raw"\node at ",
     Coordinate(225, 1.1),
     raw"{Mat\'ern-$\frac{3}{2}$};"],
     [raw"\node at ",
     Coordinate(425, 1.1),
     raw"{Squared Exponential};"]
)

@pgf gp = GroupPlot(
    {
        group_style = {
            group_size = "2 by 1",
            horizontal_sep = "0.2cm",
        },
        no_markers,
        xticklabels={},
        yticklabels={},
        ymin = 0,
    },
    p1,
    p2,
)

pgfsave("kernels_p1.tex", p1, include_preamble = false)
pgfsave("kernels_p2.tex", p2, include_preamble = false)

pgfsave("kernels.pdf", gp)
pgfsave("kernels.tex", gp)

x = range(-2*pi; stop = 2*pi, length = 500)

@pgf sin_plot = Axis(
    {
        ylabel = raw"$\sigma(x)$",
        no_markers,
        ymin = -2.5,
        ymax = 2.5,
        xticklabels={},
    },
    Plot({color = "red"},
    Table(x, sinactivation.(x))
    ), LegendEntry("Sinusiod")
    )
@pgf sincos_plot = Axis(
    {
        no_markers,
        ymin = -2.5,
        ymax = 2.5,
        xticklabels={},
    },
    PlotInc({color = "green"}, Table(x, sincosactivation.(x))), LegendEntry("Sine Cosine"),
    PlotInc({dashed, color = "red"}, Table(x, sinactivation.(x)))
    )
@pgf tri_plot = Axis(
    {
        no_markers,
        ymin = -2.5,
        ymax = 2.5,
        xticklabels={},
    },
    PlotInc({color = "blue"}, Table(x, triangeactivation.(x))), LegendEntry("Triangle Wave"))
@pgf relu_plot = Axis(
    {
        no_markers,
        ymin = -2.5,
        ymax = 2.5,
        xticklabels={},
    },
    PlotInc({color = "orange"}, Table(x, prelu.(x))), LegendEntry("Periodic ReLU"))

@pgf gp = GroupPlot(
    {
        group_style = {
            group_size = "4 by 1",
            yticklabels_at="edge left",
            xticklabels_at="edge bottom",
            horizontal_sep = "0.2cm",
        },
        no_markers,
        ymin = -2.5,
        ymax = 2.5,
        xticklabels={},
    },
    sin_plot,
    sincos_plot,
    tri_plot,
    relu_plot
)

pgfsave("sin_activation.tex", sin_plot, include_preamble = false)
pgfsave("sincosactivation.tex", sincos_plot, include_preamble = false)
pgfsave("triangle_activation.tex", tri_plot, include_preamble = false)
pgfsave("relu_activation.tex", relu_plot, include_preamble = false)

pgfsave("activations.pdf", gp)
pgfsave("activations.tex", gp)


using TargetedLearning.LReg
using StatsFuns

facts("Testing LReg") do

    n, p, newn = 50, 3, 20
    srand(10)
    x = rand(n, p)
    y = round(rand(n))
    newx = rand(newn, p)

    context("without offest or weights") do
        lrfit = lreg(x, y)
        @fact lrfit --> is_a(LR)
        @fact predict(lrfit, newx) --> roughly(logistic(newx * lrfit.β))

        @fact_throws predict(lrfit, newx, offset=rand(newn))

        pred = logistic(x * lrfit.β)
        total_loss = 0.0
        lp=linpred(lrfit, x)
        for i in 1:length(y) #NumericExtensions
          total_loss += LReg.loss(y[i], lp[i])
        end
        @fact total_loss/length(y) --> roughly(mean(- y .* log(pred) .- (1-y) .* log(1 .- pred)))
    end

    context("with offset") do
        off = rand(n)
        newoff = rand(newn)
        lrfitoff = lreg(x, y, offset=off)
        @fact predict(lrfitoff, newx, offset=newoff) -->  roughly(logistic(newx * lrfitoff.β .+ newoff))
        @fact_throws predict(lrfitoff, newx)
    end

    context("with weights") do
        lrfitwts = lreg(x, y, wts=ones(50))
        @fact lrfitwts.β --> roughly(lreg(x,y).β)
        rwts = rand(50)
        lrfitwts2 = lreg(x, y, wts=rwts)
        @fact lrfitwts2.β --> not(roughly(lrfitwts.β))
    end

    context("fitting on a subset of columns") do
        sslrfit = lreg(x, y, subset=[3,1])
        @fact sslrfit.β[[1,3]] --> roughly(lreg(x[:, [1,3]], y).β)
        @fact sslrfit.β[2] --> 0.0
        @fact sslrfit.idx --> [1,3]
        @fact sslrfit.β --> lreg(x, y, subset=[1,3]).β
    end

    context("works when x is a vector") do
        lrvec = lreg(rand(10), round(rand(10)))
        @fact lrvec --> is_a(LR)
        @fact predict(lrvec, rand(10)) --> is_a(Vector)
    end

    context("intercept only") do
        @pending "without weights" --> nothing
        @pending "with weights" --> nothing
    end

end


using LinearAlgebra
using Makie
using Combinatorics
import Quaternions
H = Quaternions.Quaternion

const TOLERANCE = 1e-7

"""
σ(q)

Sends a point on a 3-sphere to a point in the plane x₄=0 with the given point.
This is the stereographic projection of a 3-sphere. S³ ↦ R³
"""
function σ(q)
    Point3f0(q.s, q.v1, q.v2) ./ (1 - q.v3)
end

"""
π(q)

Sends a point on a unit 3-sphere to a point on a unit 2-sphere with the given
point. S³ ↦ S²
"""
function π(q)
    x₁ = q.s
    x₂ = q.v1
    x₃ = q.v2
    x₄ = q.v3
    Point3f0(2(x₁*x₃ + x₂*x₄), 2(x₂*x₃ - x₁*x₄), x₁^2+x₂^2-x₃^2-x₄^2)
end

"""
S¹action(α, q)

Performs a group action corresponding to moving along the circumference of a
circle with the given angle and the point.
"""
function S¹action(α, q)
    z₁ = Complex(q.s, q.v1)
    z₂ = Complex(q.v2, q.v3)
    αz₁, αz₂ = exp(im * α) * z₁, exp(im * α) * z₂
    H(real(αz₁), imag(αz₁), real(αz₂), imag(αz₂))
end

"""
rotate3D(point, q)

Rotates a point in the 3D space with the given point and the unit quaternion.
"""
function rotate3D(point, q)
    p = H(0, point[1], point[2], point[3])
    R = conj(q) * p * q
    Point3f0(R.v1, R.v2, R.v3)
end

"""
get_center(A, B, C)

Finds the center point of the fiber circle under stereographic projection
with the given 3 points on the circle circumference.
"""
function get_center(A, B, C)
    a = norm(B - C)
    b = norm(A - C)
    c = norm(A - B)
    numerator = a^2 * (b^2 + c^2 - a^2) * A +
                b^2 * (a^2 + c^2 - b^2) * B +
                c^2 * (a^2 + b^2 - c^2) * C
    denominator = a^2 * (b^2 + c^2 - a^2) +
                  b^2 * (a^2 + c^2 - b^2) +
                  c^2 * (a^2 + b^2 - c^2)
    numerator / denominator
end

"""
get_fiber(point, segments, samples; r=0.025)

Calculates a torus of revolution for building a surface in a specific way with
the given point in the base space, the number of segments, the number of
samples and the radius of the smaller circle in the torus of revolution.
"""
function get_fiber(point; segments = 30, r = 0.025)
    # Find 3 points on the circle
    A = σ(S¹action(pi / 6, point))
    B = σ(S¹action(pi / 4, point))
    C = σ(S¹action(pi / 3, point))
    # The fiber circle center
    Q = get_center(A, B, C)
    # The bigger radius
    R = norm(Q - A)
    # Get the normal to the plane containing the points
    n = cross(A - Q, B - Q)
    n = n / norm(n)
    # The initial normal to the circle
    i = [0.0, 0.0, 1.0]
    # The axis of rotation
    u = cross(n, i)
    u = u / norm(u)
    # The angle of rotation
    angle = acos(dot(n, i)) / 2.0
    q = H(cos(angle), sin(angle)*u[1], sin(angle)*u[2], sin(angle)*u[3])
    # Construct a torus of revolution grid
    fiber = Array{Point3f0}(undef, segments, segments)
    for i in 1:segments
        for j in 1:segments
            longitude = j * 2pi / (segments - 1)
            latitude = i * 2pi / (segments - 1)
            x₁ = Q[1] + (R + r * cos(longitude)) * cos(latitude) / R
            x₂ = Q[2] + (R + r * cos(longitude)) * sin(latitude) / R
            x₃ = Q[3] + r * sin(longitude) / R
            fiber[i, j] = rotate3D(Point3f0(x₁, x₂, x₃), q)
        end
    end
    fiber
end

"""
compressed_σ(q)

Sends a point on a 3-sphere to a point in the compressed plane x₄=0 with the
given point. This is the stereographic projection of a 3-sphere. S³ ↦ R³
"""
function compressed_σ(q)
    sigmoid(x) = exp(x) / (1 + exp(x))
    2 .* (sigmoid.(σ(q)) .- 0.5)
end

"""
get_rotation_axis(i, n)

Calculates a unit quaternion corresponding to the axis of a 3D rotation with
the given initial and end unit vectors.
"""
function get_rotation_axis(i, n)
    u = normalize(cross(n, i))
    # The angle of rotation
    dotproduct = dot(n, i)
    if isapprox(dotproduct, 1, atol=TOLERANCE)
        θ = acos(1)
    elseif isapprox(dotproduct, -1, atol=TOLERANCE)
        θ = acos(-1)
    else
        θ = acos(dotproduct)
    end
    H(cos(θ/2), sin(θ/2)*u[1], sin(θ/2)*u[2], sin(θ/2)*u[3])
end

"""
get_cylinder(A, B; segments = 30, r = 0.025)

Calculates a cylinder with the given 2 points, the number of segments and the
radius. The points correspond to the beginning and the end of the cylinder.
"""
function get_cylinder(A, B; segments = 30, r = 0.025)
    cylinder = Array{Point3f0}(undef, segments, segments)
    i = [0.0, 0.0, 1.0]
    n = normalize(B - A)
    q = get_rotation_axis(i, n)
    for i in 1:segments
        for j in 1:segments
            α = (j-1) * 2pi / (segments - 1)
            x₁ = r * sin(α)
            x₂ = r * cos(α)
            x₃ = (i-1) * norm(B - A) / (segments - 1)
            cylinder[i, j] = (rotate3D(Point3f0(x₁, x₂, x₃), q) + A)
        end
    end
    cylinder
end

"""
get_torus(A, B, C; segments = 30, r = 0.025)

Calculates a section of a 2-torus with the given 3 points, the number of
segments and the radius of the smaller circle. The points correspond to the
beginning, middle and the end of the arc of the bigger circle.
"""
function get_torus(A, B, C; segments = 30, r = 0.025)
    torus = Array{Point3f0}(undef, segments, segments)
    # The fiber circle center
    Q = get_center(A, B, C)
    # The bigger radius
    R = norm(Q - A)
    # Find the arc sweep angle
    QA = normalize(A - Q)
    QC = normalize(C - Q)
    dotproduct = dot(QA, QC)
    if isapprox(dotproduct, 1, atol=TOLERANCE)
        α = acos(1)
    elseif isapprox(dotproduct, -1, atol=TOLERANCE)
        α = acos(-1)
    else
        α = acos(dotproduct)
    end
    # The normal to the initial circle
    i = [0.0, 0.0, 1.0]
    # Get the normal to the rotated circle
    n = normalize(normalize(cross(A - Q, B - Q)) +
                  normalize(cross(A - Q, C - Q)) +
                  normalize(cross(B - Q, C - Q)))
    # The first rotation versor
    q = get_rotation_axis(i, n)
    # The vector pointing to the positive direction of the x axis on the
    # plane of the rotated circle
    i = rotate3D([1.0, 0.0, 0.0], q)
    # The second rotation versor
    q′ = get_rotation_axis(i, QA)
    # Construct a torus of revolution
    for i in 1:segments
        for j in 1:segments
            longitude = (j-1) * 2pi / (segments - 1)
            latitude = (i-1) * α / (segments - 1)
            x₁ = (R + r * cos(longitude)) * cos(latitude)
            x₂ = (R + r * cos(longitude)) * sin(latitude)
            x₃ = r * sin(longitude)
            torus[i, j] = rotate3D(Point3f0(x₁, x₂, x₃), q * q′) + Q
        end
    end
    torus
end

"""
get_manifold(points, segments, radius)

Calculates a grid of points in order to build a surface with the given 3 points
that are on a circle, the number of segments and the radius of torus/cylinder.
The points should be quaternions.
"""
function get_manifold(points, segments, radius)
    # Use stereographic projection to send the points to R³
    A, B, C = map((x) -> compressed_σ(x), points)
    # If the points are collinear, then build a cylinder
    if isapprox(norm(cross(A - B, C - B)), 0, atol = TOLERANCE)
        manifold = get_cylinder(A, C, segments = segments, r = radius)
        # manifold = get_torus(A, B, C, segments = segments, r = radius)
    # And if they are not collinear, then build a torus of revolution
    else
        # manifold = get_torus(A, B, C, segments = segments, r = radius)
        manifold = get_cylinder(A, C, segments = segments, r = radius)
    end
    manifold
end

"""
get_vertices()

Returns the versors corresponding to the 24 vertices of a 24-cell inscribed
inside of a 3-sphere.
"""
function get_vertices()
    set = []
    set1 = [H(getindex.(Ref([-1/2, 1/2]), 1 .+ digits(i-1; base=2, pad=4))...)
            for i=1:2^4]
    set2 = [H(i...) for i in permutations([1, 0, 0, 0]) |> collect]
    set3 = [H(i...) for i in permutations([-1, 0, 0, 0]) |> collect]
    push!(set, set1..., set2..., set3...)
    unique(set)
end

# The scene object that contains other visual objects
scene = Scene(backgroundcolor = :navyblue, show_axis=false, resolution = (360, 360))
# Use a slider for rotating the base space in an interactive way
sθ, θ = textslider(0:0.01:2pi, "θ", start = pi/4)
sϕ, ϕ = textslider(0:0.01:2pi, "ϕ", start = 0)
sψ, ψ = textslider(0:0.01:2pi, "ψ", start = 0)
# Calculate a unit quaternion as the rotation axis
u = @lift([cos($ϕ) * cos($ψ), cos($ϕ) * sin($ψ), sin($ϕ)])
q = @lift(H(cos($θ), sin($θ) * $u[1], sin($θ) * u[2], sin($θ) * $u[3]))
vertices = get_vertices()
segments = 30
radius = 0.025

edges = []
for i in 1:length(vertices)
    for j in i:length(vertices)
        g = vertices[i]
        h = vertices[j]
        distance = norm(g - h)
        if isapprox(distance, 1)
            m = normalize((g + h) / 2)
            push!(edges, [g, m, h])
        end
    end
end
faces = []
for i in 1:length(vertices)
    for j in i:length(vertices)
        for k in j:length(vertices)
            a = vertices[i]
            b = vertices[j]
            c = vertices[k]
            ab = norm(a - b)
            ac = norm(a - c)
            bc = norm(b - c)
            if isapprox(ab, 1, atol = TOLERANCE) &&
               isapprox(ac, 1, atol = TOLERANCE) &&
               isapprox(bc, 1, atol = TOLERANCE)
                push!(faces, [a, b, c])
            end
        end
    end
end
gold = [0.8314, 0.6863, 0.2157]
saturated = [0.9098, 0.7255, 0.1373]
rosegold = [0.718, 0.431, 0.475]
platinum = [0.898, 0.894, 0.886]
silver = [0.752, 0.752, 0.752]
manifolds = []
for i in 1:length(edges)
    # First use 3-sphere rotations to rotate the points
    rotated = @lift(map((x) -> $q * x, edges[i]))
    manifold = @lift(get_manifold($rotated, segments, radius))
    x = @lift([m[1] for m in $manifold])
    y = @lift([m[2] for m in $manifold])
    z = @lift([m[3] for m in $manifold])
    if i > length(edges) / 2
        color = fill(RGBAf0(gold..., 0.9), segments, segments)
    else
        color = fill(RGBAf0(saturated..., 0.9), segments, segments)
    end
    surface!(scene, x, y, z, color = color, shading = true)
end

for vertex in vertices
    rotated = @lift(compressed_σ($q * vertex))
    x = @lift([$rotated[1]])
    y = @lift([$rotated[2]])
    z = @lift([$rotated[3]])
    color = RGBAf0(rosegold..., 0.9)
    meshscatter!(scene, x, y, z, markersize = 2radius, color = color, shading = true)
end

for i in 1:length(faces)
    a, b, c = faces[i]
    rotateda = @lift(compressed_σ($q * a))
    rotatedb = @lift(compressed_σ($q * b))
    rotatedc = @lift(compressed_σ($q * c))
    l = @lift([($rotateda[1], $rotateda[2], $rotateda[3]),
               ($rotatedb[1], $rotatedb[2], $rotatedb[3]),
               ($rotatedc[1], $rotatedc[2], $rotatedc[3])])
    indices = [1, 2, 3,   2, 3, 1,   3, 1, 2]
    color = [RGBAf0(gold..., rand()), RGBAf0(saturated..., rand()), RGBAf0(rosegold..., rand())]
    mesh!(scene, l, color = color)
end

# update eye position
eye_position, lookat, upvector = Vec3f0(2, 2, 2), Vec3f0(0), Vec3f0(0.0, 0.0, 0.0001)
update_cam!(scene, eye_position, lookat)
scene.center = false # prevent scene from recentering on display

#fullscene = hbox(scene, vbox(sθ, sϕ, sψ), parent = Scene(resolution = (500, 500)))

record(scene, "24-cell.gif") do io
    frames = 90
    for i in 1:frames
        recordframe!(io) # record a new frame
        # animate scene
        ϕ[] = cos(i/frames*pi/2)*2pi
        ψ[] = sin(i/frames*pi/2)*2pi
    end
end


# Parameters:
#	FFT1::Array{Array{Float64, 2}, 1}
#	- the first Fourier transform
#	FFT2::Array{Array{Float64, 2}, 1}
#	- the second Fourier transform
# Return Values:
#	Convolution::Array{Array{Float64, 2}, 1}
#	- the convolution of FFT1 and FFT2
# Notes:
#	- FFT1 and FFT2 have to be Fourier transforms of functions over the same Sn
#	- However, the don't have to have the same degree of bandlimitedness
function sn_convolution(FFT1::Array{Array{Float64, 2}, 1}, FFT2::Array{Array{Float64, 2}, 1})
	l1 = length(FFT1)
	l2 = length(FFT2)
	L = min(l1, l2)
	Convolution = Array(Array{Float64, 2}, L)
	while L != 0
		Convolution[L] = FFT1[l1] * FFT2[l2]
		l1 -= 1
		l2 -= 1
		L -= 1
	end
	return Convolution
end


using RadioTools
using PyPlot

data = readdlm("hydrostatic.bak")#("../../Tesis/data/VALC/hydrostatic.bak")

n2  = data[:,5]
ν2  = 12e9
T2  = data[:,3]
I02 = 5e-13

Iν  = intensity(n2, ν2, T2, I02)
loglog(Iν)
title("Intensity at 12GHz")
xlabel("Step")
ylabel("Intensity")

show()

Tb = Iν * c^2 / (2 * k_b * ν2^2)

loglog(data[:,2],Tb)
title("Emisión total")
ylabel("Tb (K)")
xlabel("Height over photosphere (Km)")
#yscale("log")
ylim(1e3,1e5)

show()


module LinearFilter

export guassian_kernel, apply_linear_filter, apply_roi_aggregate
# Kernel function
"""
`gaussian_kernel(train::Array{Float64,1}, sigma::Float64)`

The Gaussian Kernel.

```math
w(\\tau) = \\frac{1}{\\sqrt{2 \\pi} \\sigma_w}
\\exp(-\\frac{(\\mathbf{t}-\\tau)^2}{2 * \\sigma ^2})
```
"""
function gaussian_kernel(train::Array{Float64,1}, sigma::Float64=1.0)
    return t->sum(1/sqrt(2*pi)/sigma .* exp.(-broadcast(-, train, t).^2./(2*sigma^2)), 1)
end

function apply_linear_filter(k, tstart, tend; step=100)
    _t = tstart:1/step:tend |> Array{Float64, 1} |> transpose
    k(_t)
end

"""
```apply_roi_aggregate(kernel::LinearFilter.kernel, starts::Array{Float64,1}, roi::Range, backgrounds::Range)```

Returns:
- _result :: Array{Float64, 2}(signal, trial)
"""
function apply_roi_aggregate(kernel, starts, roi, backgrounds)
    _result = zeros(length(starts),length(roi))
    for idx = 1:length(starts)
        _mark = starts[idx]
        _x = (_mark + roi) |> Array{Float64, 1} |> transpose
        _backgrounds = (_mark + backgrounds) |> Array{Float64, 1} |> transpose
        _target = kernel(_x)
        _backgrounds = kernel(_backgrounds) |> mean

        _result[idx,:] = _target - _backgrounds
    end
    _result'
end

end


using AbstractAlgebra

@testset "Groebner modular" begin
    R, (x, y) = PolynomialRing(QQ, ["x", "y"], ordering=:degrevlex)

    fs = [x, y]
    G = Groebner.groebner(fs)
    @test G ≂ [y, x]

    fs = [5y, 3//4*x]
    G = Groebner.groebner(fs)
    @test G ≂ [y, x]

    fs = [x^2 - (1//6)*y, x*y]
    G = Groebner.groebner(fs)
    @test G ≂ [y^2, x*y, x^2 - (1//6)y]

    fs = [QQ(11, 3)*x^2*y - QQ(2, 4)*x*y - QQ(1, 7)*y,
            QQ(1, 1)*x*y + QQ(7, 13)*x]
    G = Groebner.groebner(fs)
    @test G ≂ [y^2 + 7//13*y,
                x*y + 7//13*x,
                x^2 - 3//22*x + 39//539*y]

    #=
    root = Groebner.change_ordering(Groebner.rootn(3, ground=QQ), :degrevlex)
    gb = Groebner.groebner(root)
    @test Groebner.isgroebner(gb)

    root = Groebner.change_ordering(Groebner.rootn(4, ground=QQ), :degrevlex)
    gb = Groebner.groebner(root)
    @test Groebner.isgroebner(gb)

    root = Groebner.change_ordering(Groebner.rootn(8, ground=QQ), :degrevlex)
    gb = Groebner.groebner(root)
    @test Groebner.isgroebner(gb)

    noon = Groebner.change_ordering(Groebner.noon3(ground=QQ), :degrevlex)
    gb = Groebner.groebner(noon)
    @test Groebner.isgroebner(gb)

    noon = Groebner.change_ordering(Groebner.noonn(4, ground=QQ), :degrevlex)
    gb = Groebner.groebner(noon)
    @test Groebner.isgroebner(gb)
    =#
end

@testset "Groebner modular corner cases" begin
    R, (x, y, z, w) = PolynomialRing(QQ, ["x", "y", "z", "w"], ordering=:degrevlex)

    fs = [(12345678//12347)x,
          (222222221111123//2131232232097)y + z]
    G = Groebner.groebner(fs)
    @test G == [y + 2131232232097//222222221111123*z, x]

    fs = [
        (12345//12345678)x + y,
        x^2 + z^2,
        y^2 + w^2,
        x + 2*y + 3*z + 4*w
    ]
    G = Groebner.groebner(fs)

    @test G ≂ [w^3,
                z*w + 1692834523553//4063974*w^2,
                z^2 - 16935085031076//16933225*w^2,
                y - 12345//4106996*z - 4115//1026749*w,
                x + 6172839//2053498*z + 4115226//1026749*w]
    #@test Groebner.isgroebner(G)

    # what if we are unlucky to start from initial prime in coefficients
    G = [
        (2^30 + 3)x,
        (1//(2^30 + 3))y
    ]
    G = Groebner.groebner(G)
    @test G == [y, x]

    G = [
        (2^31 - 1)*x + y,
        (1//(2^31 - 1))*y + 1
    ]
    G = Groebner.groebner(G)
    @test G == [y + 2147483647, x - 1]

    R, (x, y, z, w) = PolynomialRing(QQ, ["x", "y", "z", "w"], ordering=:lex)
    fs = [(12345678//12347)x,
          (222222221111123//2131232232097)y + z]
    G = Groebner.groebner(fs)
    @test G == [y + 2131232232097//222222221111123*z, x]
end


using QuarkDroplets
using Test

@testset "QuarkDroplets.jl" begin

    R = π
    filename = string("results/", R, ".jld")
    if isfile(filename)
        @info "Simulation already ran"
    else
        xroot, lroot, ϵroot, κ, norm, coef, energ, j, mass = njlradius(R)
        saveradius(R, xroot, lroot, ϵroot, κ, norm, coef, energ, j, mass)
    end
    rm(filename)

end


immutable SkierObsDist{G<:Function}
    p::PowseekerPOMDP{G}
    gps::Bool
    s::SkierState
    sp::SkierState
end

function rand(rng::AbstractRNG, d::SkierObsDist)
    if d.gps
        return SkierObs(d.sp.time, d.sp.pos + d.p.gps_std*randn(rng, 2), d.sp.psi + d.p.compass_std*randn(rng))
    else
        true_dist = norm(d.sp.pos-d.s.pos)
        dist = true_dist*(1.0 + d.p.dist_std_frac*randn(rng))
        grad_local = true_grad_local(d)
        grad_obs = grad_local + d.p.grad_std*randn(rng, 2)
        return SkierObs(d.sp.time, dist, grad_obs)
    end
end

function true_grad_local(d::SkierObsDist)
    grad_global = mdp(d.p).gradient(d.sp.pos)
    psi = d.sp.psi
    C = @SMatrix [cos(psi) sin(psi); -sin(psi) cos(psi)]
    grad_local = C*grad_global
end

function pdf(d::SkierObsDist, o::SkierObs)
    if d.gps
        meas = get(o.pos) # should error if something is inconsistent
        meas_pdf = exp(-sum((meas - d.sp.pos).^2)/(2*d.p.gps_std^2))
        dir = get(o.dir)
        dir_diff = abs(dir - d.sp.psi)
        while dir_diff > pi
            dir_diff -= 2*pi
        end
        dir_diff = abs(dir_diff)
        dir_pdf = exp(-dir_diff^2/(2*d.p.compass_std^2))
        return meas_pdf*dir_pdf
    else
        true_dist = norm(d.sp.pos-d.s.pos)
        dist_pdf = exp(-(o.dist-true_dist)^2/(2*(true_dist*d.p.dist_std_frac)^2))
        true_grad = true_grad_local(d)
        grad_pdf = exp(-sum((o.grad-true_grad).^2)/(2*d.p.grad_std^2))
        return dist_pdf*grad_pdf
    end
end


using KitBase, FluxReconstruction, OrdinaryDiffEq, LinearAlgebra, Plots
using ProgressMeter: @showprogress

begin
    x0 = 0
    x1 = 1
    ncell = 100
    dx = (x1 - x0) / ncell
    deg = 2 # polynomial degree
    nsp = deg + 1
    γ = 5 / 3
    cfl = 0.05
    dt = cfl * dx
    t = 0.0
end
ps = FRPSpace1D(x0, x1, ncell, 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.3, 0.0, 0.625]
    end
    #prim = [1 + 0.1*sin(2π * ps.xpg[i, ppp1]), 1.0, 1.0]

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

function dudt!(du, u, p, t)
    du .= 0.0
    nx, nsp, J, ll, lr, lpdm, dgl, dgr, γ = p

    ncell = size(u, 1)
    nsp = size(u, 2)

    f = zeros(ncell, nsp, 3)
    for i = 1:ncell, j = 1:nsp
        f[i, j, :] .= euler_flux(u[i, j, :], γ)[1] ./ J[i]
    end

    u_face = zeros(ncell, 3, 2)
    f_face = zeros(ncell, 3, 2)
    for i = 1:ncell, j = 1:3
        # right face of element i
        u_face[i, j, 1] = dot(u[i, :, j], lr)
        f_face[i, j, 1] = dot(f[i, :, j], lr)

        # left face of element i
        u_face[i, j, 2] = dot(u[i, :, j], ll)
        f_face[i, j, 2] = dot(f[i, :, j], ll)
    end

    f_interaction = zeros(nx + 1, 3)
    for i = 2:nx
        fw = @view f_interaction[i, :]
        flux_hll!(fw, u_face[i-1, :, 1], u_face[i, :, 2], γ, 1.0)
    end
    fw = @view f_interaction[1, :]
    flux_hll!(fw, u_face[nx, :, 1], u_face[1, :, 2], γ, 1.0)
    fw = @view f_interaction[nx+1, :]
    flux_hll!(fw, u_face[nx, :, 1], u_face[1, :, 2], γ, 1.0)

    rhs1 = zeros(ncell, nsp, 3)
    for i = 1:ncell, ppp1 = 1:nsp, k = 1:3
        rhs1[i, ppp1, k] = dot(f[i, :, k], lpdm[ppp1, :])
    end

    idx = 2:ncell-1 # ending points are Dirichlet
    for i in idx, ppp1 = 1:nsp, k = 1:3
        du[i, ppp1, k] =
            -(
                rhs1[i, ppp1, k] +
                (f_interaction[i, k] / J[i] - f_face[i, k, 2]) * dgl[ppp1] +
                (f_interaction[i+1, k] / J[i] - f_face[i, k, 1]) * dgr[ppp1]
            )
    end
end

tspan = (0.0, 0.15)
p = (ps.nx, ps.deg + 1, ps.J, ps.ll, ps.lr, ps.dl, ps.dhl, ps.dhr, γ)
prob = ODEProblem(dudt!, u, tspan, p)
nt = tspan[2] ÷ dt |> Int
itg = init(prob, Midpoint(), saveat = tspan[2], adaptive = false, dt = dt)

@showprogress for iter = 1:nt
    step!(itg)
end

sol = zero(itg.u)
for i in axes(sol, 1), j in axes(sol, 2)
    sol[i, j, :] .= conserve_prim(itg.u[i, j, :], γ)
    sol[i, j, end] = 1 / sol[i, j, end]
end
plot(ps.x, sol[:, 2, :])


function jump_max_area_perimeter_Qn(n)
    if mod(log2(n),1)==0 && n>=4
        d = pi/4-asin(cos(pi/n)/sqrt(2));
        Ll = 2*n*sin(pi/(2*n))*cos(d/2);
        Lu = 2*n*sin(pi/(2*n));
        model = Model(Ipopt.Optimizer)
        #model = Model(() -> AmplNLWriter.Optimizer(Couenne_jll.amplexe))
        # Variables
        @variable(model, a[i = 1:Int(n/2)] >= 0, start = pi/n+(-1)^i*d)
        # Perimeter
        @NLobjective(model, Max, sum(4*sin(a[i]/2) for i in 1:Int(n/2)))
        # Optimal diameter graph
        @constraint(model, sum(a[i] for i in 1:Int(n/2)) == pi/2)
        @NLconstraint(model, sum((-1)^(i-1)*sin(sum(a[j] for j in 1:i)) for i in 1:Int(n/2-1)) == 1/2)
        @constraint(model, a[1] <= pi/6)
        @constraint(model, [i = 2:Int(n/2)], a[i] <= pi/3)
        # Bounds
        @NLconstraint(model, Ll <= sum(4*sin(a[i]/2) for i in 1:Int(n/2)) <= Lu)
        # Solving
        optimize!(model)
        L = objective_value(model)
        a = value.(a)
        ct = solve_time(model)
        return(L,a,ct)
    end
end


for n in [2, 4, 16]
    H = matrixdepot("hadamard", Int, n)
    @test H*H' == n*eye(n)
end

try
    matrixdepot("hadamard", 0)
catch ArgumentError
    println("hadamard: arguementerror")
end

println("'hadamard' passed test...")


# ---
# title: 74. Search a 2D Matrix
# id: problem74
# author: Qling
# date: 2021-03-30
# difficulty: Medium
# categories: Array, Binary Search
# link: <https://leetcode.com/problems/search-a-2d-matrix/description/>
# hidden: true
# ---
#
# Write an efficient algorithm that searches for a value in an `m x n` matrix.
# This matrix has the following properties:
#
#   * Integers in each row are sorted from left to right.
#   * The first integer of each row is greater than the last integer of the previous row.
#
#
#
# **Example 1:**
#
# ![](https://assets.leetcode.com/uploads/2020/10/05/mat.jpg)
#
#
#
#     Input: matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,50]], target = 3
#     Output: true
#
#
# **Example 2:**
#
# ![](https://assets.leetcode.com/uploads/2020/10/05/mat2.jpg)
#
#
#
#     Input: matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,50]], target = 13
#     Output: false
#
#
# **Example 3:**
#
#
#
#     Input: matrix = [], target = 0
#     Output: false
#
#
#
#
# **Constraints:**
#
#   * `m == matrix.length`
#   * `n == matrix[i].length`
#   * `0 <= m, n <= 100`
#   * `-104 <= matrix[i][j], target <= 104`
#
#
## @lc code=start
using LeetCode

function search_matrix(matrix::Vector{Vector{Int}}, target::Int)::Bool
    m, n = length(matrix), length(matrix[1])

    (m == 0 || n == 0) && return false
    left, right = 1, m * n

    while left <= right
        mid = left + (right - left) ÷ 2
        num = matrix[mid ÷ n][mod1(mid, n)]
        if num == target
            return true
        elseif num < target
            left = mid + 1
        else
            right = mid - 1
        end
    end

    return false
end
## @lc code=end


include("utils.jl")

function _test_d_proj(cone, tol)
    n = MOI.dimension(cone)
    x = randn(n)
    dx = 1e-6 * randn(n)
    proj_x = ConeProgramDiff._proj(x, cone)
    proj_xdx = ConeProgramDiff._proj(x+dx, cone)
    dproj_finite_diff = proj_xdx - proj_x

    dproj_test = ConeProgramDiff._d_proj(x, cone) * dx
    if !isapprox(dproj_finite_diff, dproj_test, atol=tol)
        println("Error!")
        error("Error for x = $x, dx = $dx, $cone\nnorm=$(norm(dproj_finite_diff -dproj_test))")
    end
end

function test_d_proj(cone::MOI.AbstractVectorSet; tol=1e-8)
    Random.seed!(0)
    for _ in 1:100
        _test_d_proj(cone, tol)
        _test_d_proj(MOI.dual_set(cone), tol)
    end
end

function test_d_proj(n::Int; tol=2e-6)
    cones = [
        MOI.Zeros(n),
        MOI.Nonnegatives(n),
        MOI.SecondOrderCone(n),
        MOI.PositiveSemidefiniteConeTriangle(n)
    ]
    for cone in cones
        # println("Testing $cone")
        test_d_proj(cone; tol=tol)
    end
    return true
end

function test_d_proj_exp(tol)
    function _helper(x, dx, tol; dual)
        proj_x = ConeProgramDiff._proj_exp_cone(x,  dual=dual)
        proj_xdx = ConeProgramDiff._proj_exp_cone(x+dx,  dual=dual)
        dproj_finite_diff = proj_xdx - proj_x
        dproj_test = ConeProgramDiff._d_proj_exp_cone(x; dual=dual) * dx
        # println(dproj_finite_diff)
        # println(dproj_test)
        @assert isapprox(dproj_finite_diff, dproj_test, atol=tol)
    end

    Random.seed!(0)
    case_p = zeros(4)
    case_d = zeros(4)
    for _ in 1:100
        x = randn(3)
        dx = 1e-6 * randn(3)
        case_p[det_case_exp_cone(x; dual=false)] += 1
        # println(det_case_exp_cone(x; dual=false))
        _helper(x, dx, tol; dual=false)

        case_d[det_case_exp_cone(x; dual=true)] += 1
        # println(det_case_exp_cone(x; dual=true))
        # println(x)
        _helper(x, dx, tol; dual=true)
    end
    @assert all(case_p .> 0) && all(case_d .> 0)
    return true
end

function test_d_proj_pow(tol)
    function _helper(x, α, dx, tol; dual)
        # println("$x \t $dx \t $(x+dx)")
        proj_x = ConeProgramDiff._proj_pow_cone(x, α; dual=dual)
        proj_xdx = ConeProgramDiff._proj_pow_cone(x+dx, α; dual=dual)
        dproj_finite_diff = proj_xdx - proj_x
        dproj_test = ConeProgramDiff._d_proj_pow_cone(x, α; dual=dual) * dx
        # println(dproj_finite_diff)
        # println(dproj_test)
        @assert isapprox(dproj_finite_diff, dproj_test, atol=tol)
    end

    Random.seed!(0)
    case_p = zeros(4)
    case_d = zeros(4)
    for _ in 1:100
        x = randn(3)
        dx = 1e-6 * randn(3)
        α = rand()*0.6 + 0.2

        # Need to get some into case 3
        if rand(1:10) == 1
            x[3] = 0
        end

        case_p[det_case_pow_cone(x, α; dual=false)] += 1
        _helper(x, α, dx, tol; dual=false)

        case_d[det_case_pow_cone(x, α; dual=true)] += 1
        _helper(x, α, dx, tol; dual=true)
    end
    @assert all(case_p .> 0) && all(case_d .> 0)
    return true
end

function test_d_project_onto_cone()
    function _test_d_proj_all_cones(x, cone_prod; tol=1e-6, dual=false)
        cones = dual ? [MOI.dual_set(c) for c in cone_prod] : cone_prod
        dx = 1e-7 * randn(length(x))
        Dpi = d_project_onto_cone(x, cones)
        proj_x = ConeProgramDiff.project_onto_cone(x, cones)
        proj_xdx = ConeProgramDiff.project_onto_cone(x+dx, cones)
        dproj_finite_diff = proj_xdx - proj_x

        dproj_test = Dpi * dx
        @assert isapprox(dproj_finite_diff, dproj_test, atol=tol)
        # if ~isapprox(dproj_finite_diff, dproj_test, atol=tol)
        #     println("Test failed:")
        #     println("determinant: $(det(Dpi))")
        #     inds = abs.(dproj_finite_diff - dproj_test) .>= tol
        #     println((dproj_finite_diff - dproj_test)[inds])
        #     println()
        # end
    end

    for _ in 1:10
        cone_prod = get_random_product_cone(10)
        N = sum([MOI.dimension(c) for c in cone_prod])
        local x = randn(N)
        _test_d_proj_all_cones(x, cone_prod; dual=false)
        _test_d_proj_all_cones(x, cone_prod; dual=true)
    end
    return true
end

@testset "D_Projections Test (Zero, Pos, SOC, PSD)" begin
    @test test_d_proj(10)
end

@testset "D_Projection Test: Exp Cone" begin
    @test test_d_proj_exp(1e-5)
end

@testset "D_Projection Test: Pow Cone" begin
    @test test_d_proj_pow(1e-6)
end

@testset "D_Projection: d_project_onto_cone()" begin
    test_d_project_onto_cone()
end


struct OrnsteinUhlenbeck{T1,T2,T3}
  Θ::T1
  μ::T2
  σ::T3
end
# http://www.math.ku.dk/~susanne/StatDiff/Overheads1b.pdf
function (p::OrnsteinUhlenbeck)(W,dt,rng) #dist
  if typeof(W.dW) <: AbstractArray
    rand_val = wiener_randn(rng,W.dW)
  else
    rand_val = wiener_randn(rng,typeof(W.dW))
  end
  drift = p.μ .+ (W[end] .- p.μ) .* exp.(-p.Θ*dt)
  diffusion = p.σ .* sqrt.((1 .- exp.(-2p.Θ*dt))./(2p.Θ))
  drift .+ rand_val .* diffusion .- W[end]
end

#=
http://www.tandfonline.com/doi/pdf/10.1080/14697688.2014.941913?needAccess=true
http://www.tandfonline.com/doi/full/10.1080/14697688.2014.941913?src=recsys
=#

#=
(qX + r) = Θ(μ-x) = Θμ - Θx
q = -Θ
r = Θμ
https://arxiv.org/pdf/1011.0067.pdf page 18
=#
function ou_bridge(ou,W,W0,Wh,q,h) end
function ou_bridge!(rand_vec,ou,W,W0,Wh,q,h) end

function OrnsteinUhlenbeckProcess(Θ,μ,σ,t0,W0,Z0=nothing;kwargs...)
  ou = OrnsteinUhlenbeck(Θ,μ,σ)
  NoiseProcess(t0,W0,Z0,ou,nothing;kwargs...)
end

struct OrnsteinUhlenbeck!{T1,T2,T3}
  Θ::T1
  μ::T2
  σ::T3
end

function (p::OrnsteinUhlenbeck!)(rand_vec,W,dt,rng) #dist!
  wiener_randn!(rng,rand_vec)
  @.. rand_vec = p.μ+(W[end]-p.μ)*exp(-p.Θ*dt) + rand_vec*p.σ*sqrt((1-exp.(-2*p.Θ.*dt))/(2*p.Θ)) - W[end]
end
function OrnsteinUhlenbeckProcess!(Θ,μ,σ,t0,W0,Z0=nothing;kwargs...)
  ou = OrnsteinUhlenbeck!(Θ,μ,σ)
  NoiseProcess(t0,W0,Z0,ou,nothing;kwargs...)
end


function compute(rows, mx, my)
    # initialize position
    x = 1
    y = 1

    width = length(rows[1])
    height = length(rows)

    trees_encountered = 0
    while y <= height
        # is tree?
        temp_x = (x == 0) ? width : x # 1 indexed arrays :(
        if rows[y][temp_x] == '#'
            trees_encountered += 1
        end

        # move
        x = (x + mx) % width
        y = y + my
    end

    return trees_encountered
end

function run(fname)
    println("Running", fname, " ...")
    f = open(fname, "r")
    rows = readlines(f)
    close(f)
    result = compute(rows, 1, 1)
    result2 = compute(rows, 3, 1)
    result3 = compute(rows, 5, 1)
    result4 = compute(rows, 7, 1)
    result5 = compute(rows, 1, 2)

    out = result * result2 * result3 * result4 * result5

    println("Final result for ", fname, " was: ", out)
    return out
end

@assert run("3ex.txt") == 336

run("3.txt")


# please instantiate ./test/Manifest.toml for this

using JLD2
import Statistics

# ========================================
# Asymmetric Fuzz Test Fixtures
# ========================================

# format: [(values, window_size, expected_medians)]

fixtures = Vector{Tuple{Vector{Float64}, Integer, Vector{Float64}}}(undef, 0)

function naive_asymmetric_median(input, window_size)
    if window_size > length(input)
        window_size = length(input)
    end
    growing_phase_inds = [1:k for k in 1:window_size]
    rolling_phase_inds = [k:k + window_size - 1 for k in 2:(length(input) - window_size + 1)]
    shrinking_phase_inds = [length(input) - k + 1:length(input) for k in window_size - 1:-1:1]
    phase_inds = [growing_phase_inds; rolling_phase_inds; shrinking_phase_inds]
    output = [Statistics.median(input[inds]) for inds in phase_inds]
    return output
end

for i in 1:100
    N = rand(1:50)
    window_size = rand(1:60)
    values = rand(N)
    expected_medians = naive_asymmetric_median(values, window_size)
    push!(fixtures, (values, window_size, expected_medians))
end

filename = dirname(@__FILE__) * "/asymmetric.jld2"
@save filename fixtures

# Again with full Int32 range

fixtures = Vector{Tuple{Vector{Int32}, Integer, Vector{Float64}}}(undef, 0)

for i in 1:100
    N = rand(1:50)
    window_size = rand(1:60)
    values = rand(Int32, N)
    expected_medians = naive_asymmetric_median(values, window_size)
    push!(fixtures, (values, window_size, expected_medians))
end

filename = dirname(@__FILE__) * "/asymmetric_int.jld2"
@save filename fixtures


# Welded beam design problem
# from Kalyanmoy Deb, "An effcient constraint handling method for genetic algorithms", 2000

using Evolutionary, Test, Random

beam(x) = 1.10471*x[2]*x[1]^2+0.04811*x[3]*x[4]*(14+x[2])
lx = [0.125, 0.1, 0.1, 0.125]
ux = [5.0, 10.0, 10.0, 5.0]

P = 6000; L = 14; E = 30e+6; G = 12e+6;
τₘₐₓ = 13600; σₘₐₓ = 30000; δₘₐₓ = 0.25;
M(x) = P*(L+x[2]/2)
R(x) = sqrt(0.25*(x[2]^2+(x[1]+x[3])^2))
J(x) = 2/sqrt(2)*x[1]*x[2]*(x[2]^2/12+0.25*(x[1]+x[3])^2)
P_c(x) = (4.013*E/(6*L^2))*x[3]*x[4]^3*(1-0.25*x[3]*sqrt(E/G)/L)
τ₁(x) = P/(sqrt(2)*x[1]*x[2])
τ₂(x) = M(x)*R(x)/J(x)
τ(x) = sqrt(τ₁(x)^2+τ₁(x)*τ₂(x)*x[2]/R(x)+τ₂(x)^2)
σ(x) = 6*P*L/(x[4]*x[3]^2)
δ(x) = 4*P*L^3/(E*x[4]*x[3]^3)

con_c1(x) = [τ(x), σ(x), x[4]-x[1], 0.10471*x[1]^2+0.04811*x[3]*x[4]*(14.0+x[2]), P_c(x), δ(x)]
lc = [-Inf, -Inf, 0.0, -Inf,   P, -Inf]
uc = [τₘₐₓ, σₘₐₓ, Inf,  5.0, Inf, δₘₐₓ]

con_c2(x) = [τ(x)-τₘₐₓ, σ(x)-σₘₐₓ, x[1]-x[4], 0.10471*x[1]^2+0.04811*x[3]*x[4]*(14.0+x[2])-5.0, P-P_c(x), δ(x)-δₘₐₓ]
lc = fill(-Inf, 6)
uc = fill(0.0, 6)

opts = Evolutionary.Options(iterations=2000, successive_f_tol=40)
mthd = CMAES(mu = 90, c_mu=0.3)
mthd = GA(populationSize = 120, ɛ = 0.03, crossoverRate=0.8, mutationRate=0.01, selection=rouletteinv, crossover=LX(0.0,4.0), mutation = PM(lx,ux,1.0))
mthd = DE(n=2)

c = PenaltyConstraints([1e3, 1e3, 1e3, 1e3, 1e2, 1e1, 1e3, 1e1, 1e1, 1e0], lx, ux, lc, uc, con_c1)
c = PenaltyConstraints([1e3, 1e3, 1e3, 1e3, 1e1, 1e1, 1e3, 1e1, 1e1, 1e0], Evolutionary.ConstraintBounds(lx,ux,lc,[]), con_c2)
c = WorstFitnessConstraints(Evolutionary.ConstraintBounds(lx,ux,lc,uc), con_c1)
c = WorstFitnessConstraints(Evolutionary.ConstraintBounds(lx,ux,lc,uc), con_c2)

Random.seed!(0);
result = Evolutionary.optimize(beam, c, mthd, opts)
Evolutionary.minimizer(result) |> con_c1
Evolutionary.minimizer(result) |> con_c2
[Evolutionary.optimize(beam, c, mthd, opts) |> minimum for i in 1:100] |> minimum


include("BayesTest.jl")

type CorrelationTest <: BayesTest
    prior::Distribution
    data::Dict{AbstractString, Real}
    BF::Float64 # BF_{01}
    posterior::Any # either distribution, or NaN
    params::Array{AbstractString, 2}

    function CorrelationTest(n::Integer, r::Real; a::Real = 1)
        self = new()
        self.params = ["ρ"]'
        self.data = Dict("n" => n, "r" => r)
        self.prior = Distributions.Beta(a, a)
        self.BF = computeBF(n, r, a)
        self.posterior = NaN # TODO: implement posterior
        return self
    end

    function computeBF(n::Integer, r::Real, a::Real)
        if n < 2 return NaN end
        if n > 2 && abs(r) == 1 return Inf end

        hypergeom(a, b, c, d) = a + b + c + d # dummy
        hyperterm = hypergeom((2 * n - 3) / 4, (2 * n - 1) / 4, (n + 2 * a) / 2, r^2)
        logterm = lgamma((n + 2 * a - 1) / 2) - lgamma((n + 2 * a) / 2) - lbeta(a, a)
        BF01 = √π * 2.0^(1 - 2*a) * exp(logterm) * hyperterm

        return BF01
    end
end


module PriceIndex

greet() = print("Hello World!")

using Distributions, Parameters, LinearAlgebra, Plots, Compat

function FixedLaspeyres(p,q)
N=size(p,1)
T=size(p,2)
Pl=zeros(N+1,T)
s=zeros(N,T)
   for t in 1:T
        for n in 2:N+1
        s[n-1,t] = (p[n-1,t]*q[n-1,t])/dot(p[:,t],q[:,t])
        Pl[n,t]=Pl[n-1,t]+p[n-1,t]/p[n-1,1]*s[n-1,1]
        end
    end
return Pl[N+1,:]
end

function foo(μ = 1., σ = 2.)
    d = Normal(μ, σ)
    E = expectation(d)
    return E(x -> sin(x))
end

export foo, FixedLaspeyres

end # module


using DelimitedFiles
using FFTW

function create_gaussian(xmax, sigma, res)
    x = [(-xmax + 2*xmax*i/res) for i = 1:res]
    g = (1/(sqrt(2*pi)*sigma))*exp.(-(x/sigma).^2/2)
end

function normalize(a,dx)
    factor = sum(a)
    return a/factor/dx
end

function create_gaussian_file(filename)
    res = 1024
    file = open(filename, "w")
    for i = 1:10
        sigma = i/10
        g = create_gaussian(10, sigma, res)
        println(sum(g))
        for j = 1:res
            write(file, string(g[j])*'\n')
        end
        write(file, "\n\n")
    end

    close(file)
end

function create_fft_gaussian_file(filename)
    res = 1024
    file = open(filename, "w")
    for i = 1:10
        sigma = i/10
        g = create_gaussian(10, sigma, res)
        g = normalize(abs.(fftshift(fft(g))), 20/res)
        println(sum(g))
        for j = 1:res
            write(file, string(g[j])*'\n')
        end
        write(file, "\n\n")
    end

    close(file)
end