using HypothesisTests
using Tullio
using Bootstrap
using StatsBase

# Give two feature matrices, a test function (from HypothesisTests.jl) and calculate the test statistics for each feature common to the two feature matrices
function testFeatureDistributions(𝐟₁::AbstractVector, 𝐟₂::AbstractVector, test=TailedTestOfVariance; tail=:both)
    if tail ∈ (:firstgreater, :secondsmaller, :firstlarger)
        tail = :right
    elseif tail ∈ (:firstsmaller, :secondgreater, :secondlarger)
        tail = :left
    end
    p = pvalue(test(𝐟₁, 𝐟₂); tail=tail)
end
function testFeatureDistributions(F₁::AbstractArray, F₂::AbstractArray, test=TailedTestOfVariance; tail=:both)
    @tullio p[i] := testFeatureDistributions(F₁[i, :], F₂[i, :], test; tail=tail)
end
function testFeatureDistributions(F₁::AbstractFeatureArray, F₂::AbstractFeatureArray, test=TailedTestOfVariance; tail=:both)
    # Intersect features before testing. In this case the test statistic returned refers to the order of the first input array. NaN means F₂ did not have a feature in F₁
    F̂₁, F̂₂ = intersectFeatures(F₁, F₂)
    p = testFeatureDistributions(Array(F̂₁), Array(F̂₂), test; tail=tail)
    F̂₁, p = intersectFeatures(F̂₁, p) # label p with the features of F̂₁
    fs = Catch22.featureDims(F̂₁)
    outp = deepcopy(F̂₁[:, 1])
    outp[:] .= NaN
    outp[fs] .= p[fs]
end
export testFeatureDistributions

function TailedTestOfVariance(x::AbstractVector{T}, y::AbstractVector{T}; scorediff=abs, statistic=mean) where {T<:Real}
    zx, zy = scorediff.(x .- statistic(x)), scorediff.(y .- statistic(y))
    EqualVarianceTTest(zx, zy)
end
export TailedTestOfVariance
function RobustTailedTestOfVariance(x::AbstractVector{T}, y::AbstractVector{T}) where {T<:Real}
    zx, zy = abs.(x .- statistic(x)), scorediff.(y .- median(y))
    EqualVarianceTTest(zx, zy)
end
export RobustTailedTestOfVariance

"""
Bootstrap to get an estimate of the standard error of a sample's standard deviation
"""
function bootstrapSEσ(X::AbstractVector, n=1000, sampling=BasicSampling)
    bs = bootstrap(StatsBase.std, X, sampling(n))
    return stderror(bs)[1]
end
export bootstrapSEσ


using SuiteSparseMatrixCollection
using MatrixMarket
using SuiteSparseGraphBLAS
using BenchmarkTools
using SparseArrays
include("tc.jl")
include("pr.jl")
graphs = [
    "karate",
    "com-Youtube",
    "as-Skitter",
    "com-LiveJournal",
    "com-Orkut",
    "com-Friendster",
]

ssmc = ssmc_db()
matrices = filter(row -> row.name ∈ graphs, ssmc)
BenchmarkTools.DEFAULT_PARAMETERS.gcsample = true
for name ∈ graphs
    path = fetch_ssmc(matrices[matrices.name .== name, :])[1]
    G = GBMatrix(convert(SparseMatrixCSC{Float64}, MatrixMarket.mmread(joinpath(path, "$name.mtx"))))
    SuiteSparseGraphBLAS.gbset(G, SuiteSparseGraphBLAS.FORMAT, SuiteSparseGraphBLAS.BYROW)
    GC.gc()
    println("$name | $(size(G)) | $(nnz(G)) edges")
    for centrality in [PR, TC1, TC3]
        println("Benchmarking $(string(centrality)) on $(name)")
        result = @benchmark $centrality($G) samples=3 seconds=600
        show(stdout,MIME"text/plain"(),result)
    end
end


function luc_rc_seeder{T<:Integer}(n::T)
    a = zeros(BigInt, n)
    a[1] = 3
    a[2] = -1
    return a
end

function luc_rc{T<:Integer}(n::T, k::T)
    NFib(n, k, luc_rc_seeder)
end


# code by IainNZ
function histogram(x, bins::Int=sturges(length(x)); args...)
  edges, counts = hist(x, bins)
  labels = (@compat AbstractString)[]
  binwidth = edges.step / edges.divisor
  for i in 1:length(counts)
		val = floatRoundLog10(edges[i])
    push!(labels, string("(", val, ",", floatRoundLog10(val+binwidth), "]"))
  end
  barplot(labels, counts; symb="▇", args...)
end

function histogram(x; bins::Int=sturges(length(x)), args...)
  histogram(x, bins; args...)
end

# Sturges' rule
sturges(n) = n == 0 ? 1 : ceil(Int, log2(n) + 1)


function jacobian_ad(m::HetDSGEGovDebt)


using FinEtools
using FinEtools.TetRemeshingModule
using NIfTI

File = "Head_256x256x126-256x256x252"
file = niread(File * ".nii")
bs = voxel_size(file.header) .* [size(file,1), size(file,2), size(file,3)-119]
vb = VoxelBoxVolume(file.raw[:, :, 120:end, 1], bs)
vtkexport(File * ".vtk", vb)

fens, fes = T4voximg(vb.data, vec([voxeldims(vb)...]), eltype(vb.data)[255])
t = deepcopy(fes.conn);
v = deepcopy(fens.xyz);
tmid = ones(Int, size(t,1));

desired_ts = 4 * voxel_size(file.header)[1];
bfes = meshboundary(fes);
f = connectednodes(bfes);
bv = zeros(Bool, size(v,1));
# bv[f] = true;

println("Mesh size: initial = $(size(t,1))")
t0 = time()

t, v, tmid = TetRemeshingModule.coarsen(t, v, tmid; bv = bv, desired_ts = desired_ts);

println("Mesh size: final = $(size(t,1)) [$(time() - t0) sec]")

fens.xyz = deepcopy(v)
fes.conn = deepcopy(t)
setlabel!(fes, tmid)

fens = meshsmoothing(fens, fes; npass = 5)
println("count(fes) = $(count(fes))")

File = "Head_mesh.vtk"
vtkexportmesh(File, fens, fes)
@async run(`"paraview.exe" $File`)


using Test
using LogicCircuits
using ProbabilisticCircuits
using DataFrames

@testset "BDD tests" begin
    # Set up a logic constraint ϕ as a BDD and scope size n. Sample m PSDDs.
    function case(ϕ::Bdd, n::Integer; atol::Real = 1e-3)
        # All possible valuations (including impossible ones).
        M = all_valuations(collect(1:n))
        # Get only possible worlds.
        W = M[findall(ϕ.(eachrow(M))),:]
        # Assign random probabilities for each world in W.
        R = rand(1:20, size(W, 1))
        # Construct a dataset that maps the distribution of R (world W[i] repeats R[i] times).
        D = DataFrame(vcat([repeat(W[i,:], 1, R[i])' for i ∈ 1:size(W, 1)]...), :auto)
        C = learn_bdd(ϕ, D; pseudocount = 0.0)
        T = DataFrame(M, :auto)
        # Test smoothness.
        @test issmooth(C)
        # Test decomposability.
        @test isdecomposable(C)
        # Test determinism.
        @test isdeterministic(C)
        # Tests if respects vtree.
        @test respects_vtree(C, C.vtree)
        # Test consistency.
        @test (EVI(C, T) .> -Inf) == ϕ.(eachrow(M))
    end
    case((1 ∧ 2) ∨ (3 ∧ ¬4) ∨ (¬1 ∧ 5), 5)
    case((1 → 3) ∧ (5 → ¬2), 5)
    case((1 ∧ 2 ∧ 3) ∨ (4 ∧ 5), 5)
    case(exactly(3, collect(1:5)), 5)
    case(atleast(3, collect(1:5)), 5)
    case(atmost(3, collect(1:5)), 5)
end


module TwoVarianceComponentTest

using VarianceComponentModels, MathProgBase, Ipopt, Base.Test

srand(123)

# generate data from a d-variate response variane component model
n = 100   # no. observations
d = 2     # no. categories
m = 2     # no. variance components
p = 2     # no. covariates
X = randn(n, p)
B = ones(p, d)
Σ = ntuple(x -> zeros(d, d), m)
for i in 1:m
  Σi = randn(d, d)
  copy!(Σ[i], Σi' * Σi)
end
## make the first variance component 0 matrix
#fill!(Σ[1], 0.0)
V = ntuple(x -> zeros(n, n), m)
for i = 1:m-1
  Vi = randn(n, 50)
  copy!(V[i], Vi * Vi')
end
copy!(V[m], eye(n))
# form Ω
Ω = zeros(n*d, n*d)
for i = 1:m
  Ω += kron(Σ[i], V[i])
end
Ωchol = cholfact(Ω)
Y = X * B + reshape(Ωchol[:L] * randn(n*d), n, d)

info("Forming VarianceComponentModel from data")
@inferred VarianceComponentVariate(Y, X, V)
vcdata  = VarianceComponentVariate(Y, X, V)
vcmodel = VarianceComponentModel(vcdata)

info("Pre-compute eigen-decomposition and rotate data")
vcdatarot  = TwoVarCompVariateRotate(vcdata)
vcmodelrot = TwoVarCompModelRotate(vcmodel)

info("Evaluate log-pdf")
#@code_warntype logpdf(vcmodelrot, vcdatarot)
@inferred logpdf(vcmodelrot, vcdatarot)
@test logpdf(vcmodel, vcdata) == logpdf(vcmodelrot, vcdatarot)
@test (logpdf(vcmodelrot, [vcdatarot vcdatarot; vcdatarot vcdatarot]) -
  logpdf(vcmodel, [vcdata vcdata; vcdata vcdata])) ≈ 0.0

info("Evaluate gradient")
∇ = zeros(2d^2)
#@code_warntype gradient!(∇, vcmodelrot, vcdatarot)
@inferred gradient!(∇, vcmodelrot, vcdatarot)
@test vecnorm(gradient(vcmodel, vcdata) - gradient(vcmodelrot, vcdatarot)) ≈ 0.0
@test vecnorm(gradient(vcmodel, vcdata) - gradient(vcmodel, vcdatarot)) ≈ 0.0
@test vecnorm(gradient(vcmodel, [vcdata vcdata]) -
  2.0gradient(vcmodel, vcdata)) ≈ 0.0
@test vecnorm(gradient(vcmodel, [vcdata vcdata]) -
  gradient(vcmodelrot, [vcdatarot vcdatarot])) ≈ 0.0

info("Evaluate Fisher information matrix of Σ")
H = zeros(2d^2, 2d^2)
#@code_warntype fisher!(H, vcmodelrot, vcdatarot)
@inferred fisher_Σ!(H, vcmodelrot, vcdatarot)
@test vecnorm(fisher_Σ(vcmodel, vcdata) - fisher_Σ(vcmodelrot, vcdatarot)) ≈ 0.0
@test vecnorm(fisher_Σ(vcmodel, vcdata) - fisher_Σ(vcmodel, vcdatarot)) ≈ 0.0
@test vecnorm(fisher_Σ(vcmodel, [vcdata vcdata]) -
  2fisher_Σ(vcmodel, vcdata)) ≈ 0.0
@test vecnorm(fisher_Σ(vcmodel, [vcdata vcdata]) -
  fisher_Σ(vcmodelrot, [vcdatarot vcdatarot])) ≈ 0.0

info("Evaluate Fisher information matrix of B")
H = zeros(p * d, p * d)
#@code_warntype fisher_B!(H, vcmodelrot, vcdatarot)
@inferred fisher_B!(H, vcmodelrot, vcdatarot)
@test vecnorm(fisher_B(vcmodel, vcdata) - fisher_B(vcmodelrot, vcdatarot)) ≈ 0.0
@test vecnorm(fisher_B(vcmodel, vcdata) - fisher_B(vcmodel, vcdatarot)) ≈ 0.0
@test vecnorm(fisher_B(vcmodel, [vcdata vcdata]) -
  2.0fisher_B(vcmodel, vcdata)) ≈ 0.0
@test vecnorm(fisher_B(vcmodel, [vcdata vcdata]) -
  fisher_B(vcmodelrot, [vcdatarot vcdatarot])) ≈ 0.0

info("Find MLE using Fisher scoring")
vcmfs = deepcopy(vcmodel)
#@code_warntype mle_fs!(vcmfs, vcdatarot; solver = :Ipopt)
#@inferred mle_fs!(vcmfs, vcdatarot; solver = :Ipopt)
logl_fs, _, _, Σcov_fs, Bse_fs, = mle_fs!(vcmfs, vcdatarot; solver = :Ipopt)
logl_fs_array, = mle_fs!(vcmfs, [vcdatarot vcdatarot]; solver = :Ipopt)
@show vcmfs.B
@show Bse_fs
@show B

info("Find MLE using MM algorithm")
vcmmm = deepcopy(vcmodel)
#@code_warntype mle_mm!(vcmm, vcdatarot)
#@inferred mle_mm!(vcmmm, vcdatarot)
logl_mm, _, _, _, Bse_mm, = mle_mm!(vcmmm, vcdatarot)
@test abs(logl_fs - logl_mm) / (abs(logl_fs) + 1.0) < 1.0e-4
@show vcmmm.B
@show Bse_mm
@show B
vcmmm = deepcopy(vcmodel)
logl_mm_array, = mle_mm!(vcmmm, [vcdatarot vcdatarot])
@test abs(logl_fs_array - logl_mm_array) / (abs(logl_fs_array) + 1.0) < 1.0e-4

info("Find MLE using Fisher scoring (linear equality + box constraints)")
vcmfs       = deepcopy(vcmodel)
vcmfs.A     = [1.0 -1.0 zeros(1, p*d-2)]
vcmfs.sense = '='
vcmfs.b     = 0.0
vcmfs.lb    = 0.0
vcmfs.ub    = 1.0
logl_fs, _, _, Σcov_fs, Bse_fs, = mle_fs!(vcmfs, vcdatarot; solver = :Ipopt, qpsolver = :Ipopt)
@show vcmfs.B
@test vcmfs.B[1] ≈ vcmfs.B[2]
@test all(vcmfs.B .≥ 0.0)
@test all(vcmfs.B .≤ 1.0)

info("Find MLE using MM algorithm (linear equality + box constraints)")
vcmm = deepcopy(vcmodel)
vcmm.A = [1.0 -1.0 zeros(1, p*d-2)]
vcmm.sense = '='
vcmm.b = 0.0
vcmm.lb = 0.0
vcmm.ub = 1.0
logl_mm, _, _, Σcov_mm = mle_mm!(vcmm, vcdatarot; qpsolver = :Ipopt)
@show vcmm.B
@test vcmm.B[1] ≈ vcmm.B[2]
@test all(vcmm.B .≥ 0.0)
@test all(vcmm.B .≤ 1.0)
@test abs(logl_fs - logl_mm) / (abs(logl_fs) + 1.0) < 1.0e-4

info("Heritability estimation")
h, h_se = heritability(vcmfs.Σ, Σcov_fs)
@show h, h_se
@test all(0.0 .≤ h .≤ 1.0)

info("test fit_mle (FS)")
vcmmle = deepcopy(vcmodel)
logl_mle, _, _, Σcov_mle, Bse_mle, = fit_mle!(vcmmle, vcdata; algo = :FS)
@show vcmmle.B, Bse_mle, B
vcmle = deepcopy(vcmodel)
fit_mle!(vcmmle, [vcdata vcdata]; algo = :FS)

info("test fit_mle (MM)")
vcmmle = deepcopy(vcmodel)
logl_mle, _, _, Σcov_mle, Bse_mle, = fit_mle!(vcmmle, vcdata; algo = :MM)
@show vcmmle.B, Bse_mle, B

info("test fit_reml (FS)")
vcmreml = deepcopy(vcmodel)
logl_reml, _, _, Σcov_reml, Bse_reml, = fit_reml!(vcmreml, vcdata; algo = :FS)
@show vcmreml.B, Bse_reml, B

info("test fit_reml (MM)")
vcmreml = deepcopy(vcmodel)
logl_reml, _, _, Σcov_reml, Bse_reml, = fit_reml!(vcmreml, vcdata; algo = :MM)
@show vcmreml.B, Bse_reml, B

end # module VarianceComponentTypeTest


export AbstractShape
export level, ndir, project, center, bounds

abstract type AbstractShape{K} end

Base.in(x::AbsVecReal, s::AbstractShape{K}) where {K} = level(x,s) ≤ 0

# Return the value of the level set function of the shape.
level(x::AbsVecReal, s::AbstractShape{K}) where {K} = level(SVec{K}(x), s)

# Return the center position of the shape, where the level set function is minimized.
function center end

# Return the minimum axis-aligned bounding box of the shape.
function bounds end

# Return the outward normal direction, even for x inside the shape.
ndir(x::AbsVecReal, s::AbstractShape{K}) where {K} = ndir(SVec{K}(x), s)

# Project x onto s along the direction normal to the shape.  The result is only
# approximate.
project(x::AbsVecReal, s::AbstractShape{K}) where {K} = project(SVec{K}(x), s)

include("translated.jl")
include("simple/simple.jl")
include("composite/composite.jl")


using ZigZagBoomerang
using LinearAlgebra
using Random
using SparseArrays
using Distributions
using Statistics
using ZigZagBoomerang: idot, idot_moving!
const ZZB = ZigZagBoomerang
using ReverseDiff #ReverseDiffSparse?
using Test
using FileIO
using GLM

println("Sparse logistic regression")

# Design matrix
Random.seed!(2)
sparsity(A, d = 3) = round(nnz(A)/length(A), digits=d)
include("./../sparsedesign.jl")
# create mock design matrix with 2 categorical explanatory variables
# and their interaction effects and 2 continuous explanatory variable
A = sparse_design((20,20), 2, 4*20)
n, p = size(A)
@show n, p
Γ = A'*A
@show sparsity(A), sparsity(Γ)

const m = 1 # m Bernoulli samples for each set of covariates
println("Av. number of regressors per column ", mean(sum(A .!= 0, dims=1)), ", row ", mean(sum(A .!= 0, dims=2)))

At = SparseMatrixCSC(A')
γ0 = 0.01
# Data from the model
wc = 0.5
xtrue = 2.0.*(rand(p) .> wc)  # either 2 or 0
sigmoid(x) = inv(one(x) + exp(-x))
lsigmoid(x) = -log(one(x) + exp(-x))
y_ = Matrix(rand(n, m) .< sigmoid.(A*xtrue))
y = sum(y_, dims=2)[:]
ny = m .- y

### prior and potential
# prior precision
# dot(x,x) might be more efficient if we know that many of those elements are 0.
# A*x might be more efficient if we know that many of those elements are 0.
ϕ(x, A, y::Vector) = γ0*dot(x,x)/2 - sum(y .* lsigmoid.(A*x) + (m .- y) .* lsigmoid.(-A*x))

# Sparse gradient
# helper functions for sparse gradient
function fdot(A::SparseMatrixCSC, At::SparseMatrixCSC, f, j, x, y)
   rows = rowvals(A)
   vals = nonzeros(A)
   s = zero(eltype(A))
   @inbounds for i in nzrange(A, j)
       s += vals[i]*y[rows[i]]*f(idot(At, rows[i], x))
   end
   s
end

sigmoidn(x) = sigmoid(-x)
nsigmoid(x) = -sigmoid(x)
# helper function for sparse gradient estimate through subsampling
function fdotr(A::SparseMatrixCSC, At::SparseMatrixCSC, j, x, μ, y, ny, k)
   rows = rowvals(A)
   vals = nonzeros(A)
   s = zero(eltype(A))
   r = nzrange(A, j)
   sampler = Random.SamplerRangeNDL(r)
   l = length(r)
   @inbounds for _ in 1:k
       i = rand(sampler)
       u = idot(At, rows[i], x)
       s += l/k*vals[i]*y[rows[i]]*sigmoidn(u)
       s += l/k*vals[i]*ny[rows[i]]*nsigmoid(u)
       u0 = idot(At, rows[i], μ)
       s -= l/k*vals[i]*y[rows[i]]*sigmoidn(u0)
       s -= l/k*vals[i]*ny[rows[i]]*nsigmoid(u0)   end
   s
end

# the same, but advancing only the coordinates necessary with `idot_moving!`
function fdot_moving(A::SparseMatrixCSC, At::SparseMatrixCSC, j, t, x, θ, t′, F, μ, y, ny, k)
   rows = rowvals(A)
   vals = nonzeros(A)
   s = zero(eltype(A))
   r = nzrange(A, j)
   sampler = Random.SamplerRangeNDL(r)
   l = length(r)
   @inbounds for _ in 1:k
       i = rand(sampler)
       u = idot_moving!(At, rows[i], t, x, θ, t′, F)
       s += l/k*vals[i]*y[rows[i]]*sigmoidn(u)
       s += l/k*vals[i]*ny[rows[i]]*nsigmoid(u)
       u0 = idot(At, rows[i], μ)
       s -= l/k*vals[i]*y[rows[i]]*sigmoidn(u0)
       s -= l/k*vals[i]*ny[rows[i]]*nsigmoid(u0)
   end
   s
end

# Gradient (found using http://www.matrixcalculus.org/ S. Laue, M. Mitterreiter, and J. Giesen. A Simple and Efficient Tensor Calculus, AAAI 2020.)
∇ϕ(x, A, At, y) = γ0*x - A'*(y .* sigmoidn.(A*x)) - A'*((m .- y).*nsigmoid.(A*x))

# Element i of the gradient exploiting sparsity
∇ϕ(x, i, A, At, y, ny = m .- y) = γ0*x[i] - fdot(A, At, sigmoidn, i, x, y) - fdot(A, At, nsigmoid, i, x, ny)

#res = glm(A, y/m, Binomial(m), LogitLink())
#x0 = coef(res)
#norm(xtrue - x0)

# Some Newton steps towards the mode as starting point
println("Newton steps")
x0 = 0.1rand(p)
@time for i in 1:30
    global x0
    H = hcat((sparse(ReverseDiff.gradient(x -> ∇ϕ(x, i, A, At, y, ny), x0)) for i in 1:p)...)
    x0 = x0 - (Symmetric(0.00I + H))\[∇ϕ(x0, i, A, At, y, ny) for i in 1:p]
end
norm(xtrue - x0)
fullgradx0 = ∇ϕ(x0, A, At, y)
# Element i of the gradient exploiting sparsity and random subsampling
∇ϕr(x, i, A, At, μ, y, ny = m .- y, k = 5) = γ0*x[i] - fdotr(A, At, i, x, μ, y, ny, k) - fullgradx0[i]

# The same, but exploiting that only dependencies in subsamples need to be evaluated
∇ϕmoving(t, x, θ, i, t′, F, A, At, μ, y, ny = m .- y, k = 5) = γ0*x[i] - fdot_moving(A, At, i, t, x, θ, t′, F, μ, y, ny, k) - fullgradx0[i]
# Tests, to be sure
#@test norm(ReverseDiff.gradient(x->ϕ(x, A, y), xtrue) - ∇ϕ(xtrue, A, At, y)) < 1e-7
#@test norm(ReverseDiff.gradient(x->ϕ(x, A, y), xtrue) - [∇ϕ(xtrue, i, A, At, y) for i in 1:p]) < 1e-7

# Note that the A'A has the same sparsity structure as the Hessian of ϕ
# We can use it to tune the doubly local ZigZig with spdmp sparsifying the
# precision matrix estimate
Γ0 = A'*A
nonzeros(Γ0) .= 1

Γ = hcat((sparse(ReverseDiff.gradient(x -> ∇ϕ(x, i, A, At, y), x0)) for i in 1:p)...)
μ = copy(x0)

Γdrop = droptol!(copy(Γ), 1e-2)
@assert nnz(diag(Γdrop)) == p
println("Precision Γ is ", 100*nnz(Γ)/length(Γ), "% filled")
println("Dropped precision is ", 100*nnz(Γdrop)/length(Γdrop), "% filled")

#defining tailored bounds
struct MyBoundLog
    c::Float64
end

c = [MyBoundLog(0.0) for i in 1:p]
rows = rowvals(A)
vals = nonzeros(A)
for i in 1:p
    C_i = 0.0
    Ni = length(nzrange(A, i))
    for j in nzrange(A, i)
        row = rows[j]
        C_i = max(C_i, Ni*0.25*abs(vals[j])*norm(A[row,:]))
    end
    c[i] = MyBoundLog(C_i)
end
# a and b
function ZZB.ab(G, i, x, θ, c::Vector{MyBoundLog}, F::ZigZag, args...)
    # NB: if ξ and θ are sparse vectors, this implemention is not efficient
    max(0, θ[i]*(fullgradx0[i] + γ0*x[i])) + abs(θ[i])*c[i].c*norm(x - μ), abs(θ[i])*c[i].c*norm(θ)
end

# Random initial values
t0 = 0.0
# Define ZigZag
σ = sqrt.(diag(inv(Matrix(Γ))))
#σ = (Vector(diag(Γ))).^(-0.5) # cheaper
μ = copy(x0)
Z = ZigZag(Γ, μ, σ, ρ=0.5, λref=0.00)
Zdiag = ZigZag(sparse(Diagonal(Γ)), μ, σ, ρ=0.5, λref=0.00)
Zdrop = ZigZag(Γdrop, μ, σ, ρ=0.5, λref=0.00)
θ0 = rand([-1.0,1.0], p) .* σ

# Run sparse ZigZag for T time units and collect trajectory
println("Distance starting point")
@show norm(x0 - xtrue)

println("Run spdmp")
#traj, u, (acc,num), c = @time spdmp(∇ϕr, t0, x0, θ0, T, c, Z, A, At, μ, y, m .- y, 12; adapt=true, factor=5)
# Probability of not being 0 with spike and slab prior
w = 1- wc
#kappa given the prior (here Gaussian) and w
κ = (γ0/sqrt(2π))/(1/w -1)

t0, x0, T = 0.0, randn(p), 1000.0
su = true #strong upperbounds
adapt = false

traj, (t, x, θ), (acc, num), c = @time ZZB.sspdmp(∇ϕr, t0, x0, θ0, T, c, Z, κ,
                                                A, At, μ, y, m .- y, 12;
                                                strong_upperbounds = su ,
                                                adapt = adapt)

error("")
#traj, u, (acc,num), c = @time spdmp(∇ϕ, t0, x0, θ0, T, c, Z, A, At, y, m .- y; adapt=true)
@show acc/num
# @show extrema(c ./ c0)

dt = T/4000
x̂ = mean(x for (t,x) in discretize(traj, dt))
X = Float64[]
ts = Float64[]
for (t,x) in discretize(traj, dt)
    append!(ts, t)
    append!(X, x)
end
X = reshape(X, p, length(X)÷p)

[xtrue x̂ abs.(xtrue - x̂)]

@show norm(xtrue - μ)
@show norm(xtrue - x̂)
error("")
println("Plot")
using CairoMakie, AbstractPlotting
using Colors
using GoldenSequences

ps = [4, 22, 50, p-3, p-2, p-1]
p0 = length(ps)
cs = map(x->RGB(x...), (Iterators.take(GoldenSequence(3), p0)))
pis = []
for i in 1:p0
    p_i = lines(ts, X[ps[i], :], color=cs[i])
    # lines!(p_i, [0, T], [xtrue[ps[i]], xtrue[ps[i]]], linewidth=2.0)
    # ylabel!(p_i, "var$(ps[i])")
    # xlabel!(p_i, "t")
    push!(pis, p_i)
end
p1 = title(hbox(pis...), "Sparse design logistic regression n=$(m*n), p=$p", textsize=20)
pis[6]
save(joinpath("figures","logistic$(typeof(Z).name).png"), p1)

p2 = title(vbox(hbox([text("$p", show_axis=false, textsize=10) for p in ps]...),
    [hbox([i==j ? lines(ts, X[i,:]) : lines(X[i, :], X[j, :]) for i in ps]...) for j in ps]...), "$ps")


# ---
# title: 1504. Count Submatrices With All Ones
# id: problem1504
# author: Tian Jun
# date: 2020-10-31
# difficulty: Medium
# categories: Dynamic Programming
# link: <https://leetcode.com/problems/count-submatrices-with-all-ones/description/>
# hidden: true
# ---
#
# Given a `rows * columns` matrix `mat` of ones and zeros, return how many
# **submatrices** have all ones.
#
#
#
# **Example 1:**
#
#
#
#     Input: mat = [[1,0,1],
#                   [1,1,0],
#                   [1,1,0]]
#     Output: 13
#     Explanation: There are **6** rectangles of side 1x1.
#     There are **2** rectangles of side 1x2.
#     There are **3** rectangles of side 2x1.
#     There is **1** rectangle of side 2x2.
#     There is **1** rectangle of side 3x1.
#     Total number of rectangles = 6 + 2 + 3 + 1 + 1 = **13.**
#
#
# **Example 2:**
#
#
#
#     Input: mat = [[0,1,1,0],
#                   [0,1,1,1],
#                   [1,1,1,0]]
#     Output: 24
#     Explanation:
#     There are **8** rectangles of side 1x1.
#     There are **5** rectangles of side 1x2.
#     There are **2** rectangles of side 1x3.
#     There are **4** rectangles of side 2x1.
#     There are **2** rectangles of side 2x2.
#     There are **2** rectangles of side 3x1.
#     There is **1** rectangle of side 3x2.
#     Total number of rectangles = 8 + 5 + 2 + 4 + 2 + 2 + 1 = 24 **.**
#
#
# **Example 3:**
#
#
#
#     Input: mat = [[1,1,1,1,1,1]]
#     Output: 21
#
#
# **Example 4:**
#
#
#
#     Input: mat = [[1,0,1],[0,1,0],[1,0,1]]
#     Output: 5
#
#
#
#
# **Constraints:**
#
#   * `1 <= rows <= 150`
#   * `1 <= columns <= 150`
#   * `0 <= mat[i][j] <= 1`
#
#
## @lc code=start
using LeetCode

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


# Factorize two different matrices (one real, one complex) and solve for multiple rhs and free memory
using Distributed
using LinearAlgebra
using SparseArrays
using Test

include("getDivGrad.jl");

Ar = getDivGrad(30,31,33);
n  = size(Ar,1);
Ac  = Ar + im*spdiagm(0=>rand(n));

nrhs = 10;
rhsr = randn(n,nrhs) + im*randn(n,nrhs);
rhsc = randn(n,nrhs);

# println("Factorize real matrix on main instance and destroy")
# tic();
# fact = 0;
# for i=1:100;
# 	fact = factorMUMPS(Ar,1)
# 	destroyMUMPS(fact)
# end
# toc();

println("Factorize real matrix on remote worker and destroy")
@time begin
	fact = 0;
	for i=1:10;
		fact = remotecall_fetch(factorMUMPS,workers()[1], Ar,1)
		remotecall_fetch(destroyMUMPS,workers()[1],fact)
	end
end
println("DONE!")


NamedDimVector = Pair{<:AbstractString,<:QuantityOrUnitlike}

"""
    print_dimensionless([io, ]named_dim_vector, basis)

Print the dimensionless number that can be constructed using `named_dim_vector` in the specified dimensional `basis`.
"""
function print_dimensionless(io::IO, named_dim_vector::NamedDimVector, basis::NamedDimBase)
    powers = -1 * (basis.dim_mat \ dim_matrix(basis.basis_dims, named_dim_vector.second))[:]
    perm = exps_perm(powers)

    showoperators = get(io, :showoperators, false)
    sep = showoperators ? "*" : " "
    showrep(io, named_dim_vector.first, 1//1)
    for i in perm
        if powers[i] != 0
            print(io, sep)
            showrep(io, basis.basis_vector_names[i], powers[i])
        end
    end
    nothing
end

print_dimensionless(named_dim_vector::NamedDimVector, basis::NamedDimBase) =
print_dimensionless(stdout, named_dim_vector, basis)

"""
    showrep(io, identifier, exponent)

Print the `identifier` followed by the corresponding `exponent` to the specified stream `io`.
"""
function showrep(io::IO, identifier::String, exp::Rational)
        print(io, identifier)
        if exp != 1
            exp_str = exp.den == 1 ? "^" * string(exp.num) : "^" * replace(string(exp), "//" => "/")
            print(io, exp_str)
        end
    nothing
end

"""
    exps_perm(powers)
Return the permutation of exponents so that large positive values are showed first followed by large negative values.
"""
function exps_perm(powers)
    perm = sortperm(powers; rev=true)
    first_negative = count(powers .> 0) + 1
    perm[first_negative:end] .= reverse(perm[first_negative:end])
    return perm
end


using DelimitedFiles, Statistics, LinearAlgebra

include("HRP.jl")

z = readdlm(ARGS[1], ',')

w = size(z, 1)
if length(ARGS) > 1
    w = min(w, parse(Int, ARGS[2]))
end

_, whrp = HRP.hrp(z[end-w+1:end, :])

open("weights.csv", "w") do io
    writedlm(io, whrp, ',')
end

println(whrp)


# This file is part of the IntervalArithmetic.jl package; MIT licensed

# hard code this for efficiency:
const one_over_half_pi_interval = inv(0.5 * pi_interval(Float64))

"""
Multiply an interval by a positive constant.
For efficiency, does not check that the constant is positive.
"""
multiply_by_positive_constant(α, x::Interval) = @round(α*x.lo, α*x.hi)

half_pi(::Type{Float64}) = multiply_by_positive_constant(0.5, pi_interval(Float64))
half_pi(::Type{T}) where {T} = 0.5 * pi_interval(T)
half_pi(x::T) where {T<:AbstractFloat} = half_pi(T)

two_pi(::Type{Float64}) = multiply_by_positive_constant(2.0, pi_interval(Float64))
two_pi(::Type{T}) where {T} = 2 * pi_interval(T)

range_atan(::Type{T}) where {T<:Real} = Interval(-(pi_interval(T).hi), pi_interval(T).hi)
half_range_atan(::Type{T}) where {T} = (temp = half_pi(T); Interval(-(temp.hi), temp.hi) )
pos_range_atan(::Type{T}) where {T<:Real} = Interval(zero(T), pi_interval(T).hi)

const halfpi = pi / 2.0

"""Finds the quadrant(s) corresponding to a given floating-point
number. The quadrants are labelled as 0 for x ∈ [0, π/2], etc.
For numbers very near a boundary of the quadrant, a tuple of two quadrants
is returned. The minimum or maximum must then be chosen appropriately.

This is a rather indirect way to determine if π/2 and 3π/2 are contained
in the interval; cf. the formula for sine of an interval in
Tucker, *Validated Numerics*.
"""
function find_quadrants(x::T) where {T}
    temp = atomic(Interval{T}, x) / half_pi(x)

    return SVector(floor(temp.lo), floor(temp.hi))
end

function sin(a::Interval{T}) where T
    isempty(a) && return a

    whole_range = Interval{T}(-1, 1)

    diam(a) > two_pi(T).lo && return whole_range

    # The following is equiavlent to doing temp = a / half_pi  and
    # taking floor(a.lo), floor(a.hi)
    lo_quadrant = minimum(find_quadrants(a.lo))
    hi_quadrant = maximum(find_quadrants(a.hi))

    if hi_quadrant - lo_quadrant > 4  # close to limits
        return whole_range
    end

    lo_quadrant = mod(lo_quadrant, 4)
    hi_quadrant = mod(hi_quadrant, 4)

    # Different cases depending on the two quadrants:
    if lo_quadrant == hi_quadrant
        a.hi - a.lo > pi_interval(T).lo && return whole_range  # in same quadrant but separated by almost 2pi
        lo = @round(sin(a.lo), sin(a.lo)) # Interval(sin(a.lo, RoundDown), sin(a.lo, RoundUp))
        hi = @round(sin(a.hi), sin(a.hi)) # Interval(sin(a.hi, RoundDown), sin(a.hi, RoundUp))
        return hull(lo, hi)

    elseif lo_quadrant==3 && hi_quadrant==0
        return @round(sin(a.lo), sin(a.hi)) # Interval(sin(a.lo, RoundDown), sin(a.hi, RoundUp))

    elseif lo_quadrant==1 && hi_quadrant==2
        return @round(sin(a.hi), sin(a.lo)) # Interval(sin(a.hi, RoundDown), sin(a.lo, RoundUp))

    elseif ( lo_quadrant == 0 || lo_quadrant==3 ) && ( hi_quadrant==1 || hi_quadrant==2 )
        return @round(min(sin(a.lo), sin(a.hi)), 1)
        # Interval(min(sin(a.lo, RoundDown), sin(a.hi, RoundDown)), one(T))

    elseif ( lo_quadrant == 1 || lo_quadrant==2 ) && ( hi_quadrant==3 || hi_quadrant==0 )
        return @round(-1, max(sin(a.lo), sin(a.hi)))
        # Interval(-one(T), max(sin(a.lo, RoundUp), sin(a.hi, RoundUp)))

    else #if( lo_quadrant == 0 && hi_quadrant==3 ) || ( lo_quadrant == 2 && hi_quadrant==1 )
        return whole_range
    end
end

function quadrant(x::Float64)
    x_mod2pi = rem2pi(x, RoundNearest)  # gives result in [-pi, pi]

    x_mod2pi < -halfpi && return (2, x_mod2pi)
    x_mod2pi < 0 && return (3, x_mod2pi)
    x_mod2pi <= halfpi && return (0, x_mod2pi)

    return 1, x_mod2pi
end

function sin(a::Interval{Float64})

    T = Float64

    isempty(a) && return a

    whole_range = Interval{T}(-1, 1)

    diam(a) > two_pi(T).lo && return whole_range

    lo_quadrant, lo = quadrant(a.lo)
    hi_quadrant, hi = quadrant(a.hi)

    lo, hi = a.lo, a.hi  # should be able to use the modulo version of a, but doesn't seem to work

    # Different cases depending on the two quadrants:
    if lo_quadrant == hi_quadrant
        a.hi - a.lo > pi_interval(T).lo && return whole_range  #

        if lo_quadrant == 1 || lo_quadrant == 2
            # negative slope
            return @round(sin(hi), sin(lo))
        else
            return @round(sin(lo), sin(hi))
        end

    elseif lo_quadrant==3 && hi_quadrant==0
        return @round(sin(lo), sin(hi))

    elseif lo_quadrant==1 && hi_quadrant==2
        return @round(sin(hi), sin(lo))

    elseif ( lo_quadrant == 0 || lo_quadrant==3 ) && ( hi_quadrant==1 || hi_quadrant==2 )
        return @round(min(sin(lo), sin(hi)), 1)

    elseif ( lo_quadrant == 1 || lo_quadrant==2 ) && ( hi_quadrant==3 || hi_quadrant==0 )
        return @round(-1, max(sin(lo), sin(hi)))

    else #if( lo_quadrant == 0 && hi_quadrant==3 ) || ( lo_quadrant == 2 && hi_quadrant==1 )
        return whole_range
    end
end

function sinpi(a::Interval{T}) where T
    isempty(a) && return a
    w = a * pi_interval(T)
    return(sin(w))
end

function cos(a::Interval{T}) where T
    isempty(a) && return a

    whole_range = Interval(-one(T), one(T))

    diam(a) > two_pi(T).lo && return whole_range

    lo_quadrant = minimum(find_quadrants(a.lo))
    hi_quadrant = maximum(find_quadrants(a.hi))

    if hi_quadrant - lo_quadrant > 4  # close to limits
        return Interval(-one(T), one(T))
    end

    lo_quadrant = mod(lo_quadrant, 4)
    hi_quadrant = mod(hi_quadrant, 4)

    # Different cases depending on the two quadrants:
    if lo_quadrant == hi_quadrant # Interval limits in the same quadrant
        a.hi - a.lo > pi_interval(T).lo && return whole_range
        lo = @round(cos(a.lo), cos(a.lo))
        hi = @round(cos(a.hi), cos(a.hi))
        return hull(lo, hi)

    elseif lo_quadrant == 2 && hi_quadrant==3
        return @round(cos(a.lo), cos(a.hi))

    elseif lo_quadrant == 0 && hi_quadrant==1
        return @round(cos(a.hi), cos(a.lo))

    elseif ( lo_quadrant == 2 || lo_quadrant==3 ) && ( hi_quadrant==0 || hi_quadrant==1 )
        return @round(min(cos(a.lo), cos(a.hi)), 1)

    elseif ( lo_quadrant == 0 || lo_quadrant==1 ) && ( hi_quadrant==2 || hi_quadrant==3 )
        return @round(-1, max(cos(a.lo), cos(a.hi)))

    else#if ( lo_quadrant == 3 && hi_quadrant==2 ) || ( lo_quadrant == 1 && hi_quadrant==0 )
        return whole_range
    end
end

function cos(a::Interval{Float64})

    T = Float64

    isempty(a) && return a

    whole_range = Interval{Float64}(-one(T), one(T))

    diam(a) > two_pi(T).lo && return whole_range

    lo_quadrant, lo = quadrant(a.lo)
    hi_quadrant, hi = quadrant(a.hi)

    lo, hi = a.lo, a.hi

    # Different cases depending on the two quadrants:
    if lo_quadrant == hi_quadrant # Interval limits in the same quadrant
        a.hi - a.lo > pi_interval(T).lo && return whole_range

        if lo_quadrant == 2 || lo_quadrant == 3
            # positive slope
            return @round(cos(lo), cos(hi))
        else
            return @round(cos(hi), cos(lo))
        end

    elseif lo_quadrant == 2 && hi_quadrant==3
        return @round(cos(a.lo), cos(a.hi))

    elseif lo_quadrant == 0 && hi_quadrant==1
        return @round(cos(a.hi), cos(a.lo))

    elseif ( lo_quadrant == 2 || lo_quadrant==3 ) && ( hi_quadrant==0 || hi_quadrant==1 )
        return @round(min(cos(a.lo), cos(a.hi)), 1)

    elseif ( lo_quadrant == 0 || lo_quadrant==1 ) && ( hi_quadrant==2 || hi_quadrant==3 )
        return @round(-1, max(cos(a.lo), cos(a.hi)))

    else #if ( lo_quadrant == 3 && hi_quadrant==2 ) || ( lo_quadrant == 1 && hi_quadrant==0 )
        return whole_range
    end
end

function cospi(a::Interval{T}) where T
    isempty(a) && return a
    w = a * pi_interval(T)
    return(cos(w))
end

function find_quadrants_tan(x::T) where {T}
    temp = atomic(Interval{T}, x) / half_pi(x)

    return SVector(floor(temp.lo), floor(temp.hi))
end

function tan(a::Interval{T}) where T
    isempty(a) && return a

    diam(a) > pi_interval(T).lo && return entireinterval(a)

    lo_quadrant = minimum(find_quadrants_tan(a.lo))
    hi_quadrant = maximum(find_quadrants_tan(a.hi))

    lo_quadrant_mod = mod(lo_quadrant, 2)
    hi_quadrant_mod = mod(hi_quadrant, 2)

    if lo_quadrant_mod == 0 && hi_quadrant_mod == 1
        # check if really contains singularity:
        if hi_quadrant * half_pi(T) ⊆ a
            return entireinterval(a)  # crosses singularity
        end

    elseif lo_quadrant_mod == hi_quadrant_mod && hi_quadrant > lo_quadrant
        # must cross singularity
        return entireinterval(a)

    end

    # @show a.lo, a.hi
    # @show tan(a.lo), tan(a.hi)

    return @round(tan(a.lo), tan(a.hi))
end

function tan(a::Interval{Float64})

    T = Float64

    isempty(a) && return a

    diam(a) > pi_interval(T).lo && return entireinterval(a)

    lo_quadrant, lo = quadrant(a.lo)
    hi_quadrant, hi = quadrant(a.hi)

    lo_quadrant_mod = mod(lo_quadrant, 2)
    hi_quadrant_mod = mod(hi_quadrant, 2)

    if lo_quadrant_mod == 0 && hi_quadrant_mod == 1
        return entireinterval(a)  # crosses singularity

    elseif lo_quadrant_mod == hi_quadrant_mod && hi_quadrant != lo_quadrant
        # must cross singularity
        return entireinterval(a)

    end

    return @round(tan(a.lo), tan(a.hi))
end

function asin(a::Interval{T}) where T

    domain = Interval(-one(T), one(T))
    a = a ∩ domain

    isempty(a) && return a

    return @round(asin(a.lo), asin(a.hi))
end

function acos(a::Interval{T}) where T

    domain = Interval(-one(T), one(T))
    a = a ∩ domain

    isempty(a) && return a

    return @round(acos(a.hi), acos(a.lo))
end

function atan(a::Interval{T}) where T
    isempty(a) && return a

    return @round(atan(a.lo), atan(a.hi))
end

#atan{T<:Real, S<:Real}(y::Interval{T}, x::Interval{S}) = atan(promote(y, x)...)

function atan(y::Interval{Float64}, x::Interval{Float64})
    (isempty(y) || isempty(x)) && return emptyinterval(Float64)

    atomic(Interval{Float64}, atan(big53(y), big53(x)))
end

function atan(y::Interval{BigFloat}, x::Interval{BigFloat})
    (isempty(y) || isempty(x)) && return emptyinterval(BigFloat)

    T = BigFloat

    # Prevent nonsense results when y has a signed zero:
    if y.lo == zero(T)
        y = Interval(zero(T), y.hi)
    end
    if y.hi == zero(T)
        y = Interval(y.lo, zero(T))
    end

    # Check cases based on x
    if x == zero(x)

        y == zero(y) && return emptyinterval(T)
        y.lo ≥ zero(T) && return half_pi(T)
        y.hi ≤ zero(T) && return -half_pi(T)
        return half_range_atan(T)

    elseif x.lo > zero(T)

        y == zero(y) && return y
        y.lo ≥ zero(T) &&
            return @round(atan(y.lo, x.hi), atan(y.hi, x.lo)) # refinement lo bound
        y.hi ≤ zero(T) &&
            return @round(atan(y.lo, x.lo), atan(y.hi, x.hi))
        return @round(atan(y.lo, x.lo), atan(y.hi, x.lo))

    elseif x.hi < zero(T)

        y == zero(y) && return pi_interval(T)
        y.lo ≥ zero(T) &&
            return @round(atan(y.hi, x.hi), atan(y.lo, x.lo))
        y.hi < zero(T) &&
            return @round(atan(y.hi, x.lo), atan(y.lo, x.hi))
        return range_atan(T)

    else # zero(T) ∈ x

        if x.lo == zero(T)
            y == zero(y) && return y

            y.lo ≥ zero(T) && return @round(atan(y.lo, x.hi), half_range_atan(BigFloat).hi)

            y.hi ≤ zero(T) && return @round(half_range_atan(BigFloat).lo, atan(y.hi, x.hi))
            return half_range_atan(T)

        elseif x.hi == zero(T)
            y == zero(y) && return pi_interval(T)
            y.lo ≥ zero(T) && return @round(half_pi(BigFloat).lo, atan(y.lo, x.lo))
            y.hi < zero(T) && return @round(atan(y.hi, x.lo), -(half_pi(BigFloat).lo))
            return range_atan(T)
        else
            y.lo ≥ zero(T) &&
                return @round(atan(y.lo, x.hi), atan(y.lo, x.lo))
            y.hi < zero(T) &&
                return @round(atan(y.hi, x.lo), atan(y.hi, x.hi))
            return range_atan(T)
        end

    end
end


#==========================================================================
  Regularized Linear Discriminant Analysis Solver
==========================================================================#

immutable ModelLDA{T<:BlasReal}
    order::Union{Type{Val{:row}},Type{Val{:col}}}
    is_cda::Bool
    W::AbstractMatrix{T}  # Whitening matrix
    M::Matrix{T}          # Matrix of class means (one per row)
    priors::Vector{T}     # Vector of class priors
    gamma::Nullable{T}
end

function show(io::IO, model::ModelLDA)
    println(io, (model.is_cda ? "Canonical" : "Linear") * " Discriminant Model")
    println(io, "\nRegularization Parameters:")
    print(io, "    γ = ")
    isnull(model.gamma) ? print(io, "N/A") : showcompact(get(model.gamma))
    println(io, "\n\nClass Priors:")
    for i in eachindex(model.priors)
        print(io, "    Class ", i, " Probability: ")
        showcompact(io, model.priors[i]*100)
        println(io, "%")
    end
    println(io, "\nClass Means:")
    for j in eachindex(model.priors)
        print(io, "    Class ", j, " Mean: [")
        print(io, join([sprint(showcompact, v) for v in subvector(model.order, model.M, j)], ", "))
        println(io, "]")
    end
    print(io, "\n")
end

# Extracts Fisher components
function components!{T<:BlasReal}(
         ::Type{Val{:row}},
        W::AbstractMatrix{T},
        M::Matrix{T},
        w::Vector{T}
    )
    H = broadcast!(-, M, M, w'M)
    UDVᵀ = svdfact!(H * W)
    d = min(size(M,2),size(M,1)-1)
    Vᵀ = sub(UDVᵀ[:Vt], 1:d, :) # (UDVᵀ[:Vt])[1:d,:]
    W * transpose(Vᵀ)
end

function components!{T<:BlasReal}(
         ::Type{Val{:col}},
        W::AbstractMatrix{T},
        M::Matrix{T},
        w::Vector{T}
    )
    H = broadcast!(-, M, M, M*w)
    UDVᵀ = svdfact!(transpose(W * H))
    d = min(size(M,1), size(M,2)-1)
    Vᵀ = sub(UDVᵀ[:Vt], 1:d, :) #(UDVᵀ[:Vt])[1:d,:]
    Vᵀ * W
end

for (scheme, dim_obs) in ((:(:row), 1), (:(:col), 2))
    isrowmajor = dim_obs == 1
    dim_param = isrowmajor ? 2 : 1

    _ij, _ji = isrowmajor ? (:i, :j) : (:j, :i)  # Swapped variables for row and column ordering
    _nk, _kn = isrowmajor ? (:n, :k) : (:k, :n)
    _np, _pn = isrowmajor ? (:n, :p) : (:p, :n)
    W, H     = isrowmajor ? (:W, :H) : (:H, :W)

    @eval begin
        # X in uncentered data matrix
        # M is matrix of class means (one per row)
        # y is one-based vector of class IDs
        # γ is nullable regularization parameter in [0,1]
        function lda!{T<:BlasReal,U}(
                 ::Type{Val{$scheme}},
                X::Matrix{T},
                M::Matrix{T},
                y::RefVector{U},
                γ::Nullable{T}
            )
            p = size(X, $dim_param)
            p >= 1 || throw(DimensionMismatch("Must have at least one parameter"))
            n = size(X, $dim_obs)
            n >= 2 || throw(DimensionMismatch("Must have at least two observations"))
            H = centerclasses!(Val{$scheme}, X, M, y)
            isnull(γ) ? whitendata_qr!(Val{$scheme}, H) : whitendata_svd!(Val{$scheme}, H, get(γ))
        end

        # same rules as lda! for common arguments
        # priors is a vector of class weights
        function cda!{T<:BlasReal,U}(
                 ::Type{Val{$scheme}},
                X::Matrix{T},
                M::Matrix{T},
                y::RefVector{U},
                γ::Nullable{T},
                priors::Vector{T}
            )
            k = convert(Int64, y.k)
            if k != length(priors)
                throw(DimensionMismatch("Argument priors length does not match class count"))
            end
            W = lda!(Val{$scheme}, X, M, y, γ)
            size(X, $dim_param) < k ? W : components!(Val{$scheme}, W, M, priors)
        end

        function discriminants_lda{T<:BlasReal}(
                 ::Type{Val{$scheme}},
                W::AbstractMatrix{T},
                M::Matrix{T},
                priors::Vector{T},
                Z::Matrix{T}
            )
            n = size(Z, $dim_obs)
            p = size(Z, $dim_param)
            d = size(W, $dim_param)
            k = length(priors)
            D   = Array(T, $_nk, $_kn)  # discriminant function values
            H   = Array(T, $_np, $_pn)  # temporary array to prevent re-allocation k times
            hᵀh = Array(T, n)           # diag(H'H)
            for j = 1:k
                broadcast!(-, H, Z, subvector(Val{$scheme}, M, j))
                dotvectors!(Val{$scheme}, $H * $W, hᵀh)
                for i = 1:n
                    D[$_ij, $_ji] = -hᵀh[i]/2 + log(priors[j])
                end
            end
            D
        end
    end
end

doc"`lda(X, y; order, M, gamma, priors)` Fits a regularized linear discriminant model to the data
in `X` based on class identifier `y`."
function lda{T<:BlasReal,U<:Integer}(
        X::Matrix{T},
        y::AbstractVector{U};
        order::Union{Type{Val{:row}},Type{Val{:col}}} = Val{:row},
        M::Matrix{T} = classmeans(order, X, RefVector(y)),
        gamma::Union{T,Nullable{T}} = Nullable{T}(),
        priors::Vector{T} = ones(T,maximum(y))/maximum(y)
    )
    all(priors .> 0) || error("Argument priors must have positive values only")
    isapprox(sum(priors), one(T)) || error("Argument priors must sum to 1")
    yref = isa(y,RefVector) ? y : RefVector(y)
    length(priors) == yref.k || throw(DimensionMismatch("Prior length must match class count"))
    γ = isa(gamma, Nullable) ? gamma : Nullable(gamma)
    W = lda!(order, copy(X), M, yref, γ)
    ModelLDA{T}(order, false, W, M, priors, γ)
end

doc"`cda(X, y; order, M, gamma, priors)` Fits a regularized canonical discriminant model to the
data in `X` based on class identifier `y`."
function cda{T<:BlasReal,U<:Integer}(
        X::Matrix{T},
        y::AbstractVector{U};
        order::Union{Type{Val{:row}},Type{Val{:col}}} = Val{:row},
        M::Matrix{T} = classmeans(order, X, RefVector(y)),
        gamma::Union{T,Nullable{T}} = Nullable{T}(),
        priors::Vector{T} = ones(T, maximum(y))/maximum(y)
    )
    all(priors .> 0) || error("Argument priors must have positive values only")
    isapprox(sum(priors), one(T)) || error("Argument priors must sum to 1")
    yref = isa(y,RefVector) ? y : RefVector(y)
    length(priors) == yref.k || throw(DimensionMismatch("Prior length must match class count"))
    γ = isa(gamma, Nullable) ? gamma : Nullable(gamma)
    W = cda!(order, copy(X), copy(M), yref, γ, priors)
    ModelLDA{T}(order, true, W, M, priors, γ)
end

function discriminants{T<:BlasReal}(mod::ModelLDA{T}, Z::Matrix{T})
    discriminants_lda(mod.order, mod.W, mod.M, mod.priors, Z)
end

function classify{T<:BlasReal}(mod::ModelLDA{T}, Z::Matrix{T})
    vec(mapslices(indmax, discriminants(mod, Z), mod.order == Val{:row} ? 2 : 1))
end


# "in this version, set data and smoothness terms using arrays.
#  grid neighborhood is set up "manually". Uses spatially varying terms.
#  Namely V(p1,p2,l1,l2) = w_{p1,p2}*[min((l1-l2)*(l1-l2),4)], with
#  w_{p1,p2} = p1+p2 if |p1-p2| == 1 and w_{p1,p2} = p1*p2 if |p1-p2| is not 1."
#
using GCoptimization

width = 10
height = 5
num_pixels = width * height
num_labels = 7
result = zeros(num_pixels)    # "stores result of optimization"

# "first set up the array for data costs"
data = Vector{Int}(num_pixels * num_labels)
for i = 0:num_pixels-1, l = 0:num_labels-1
    if i < 25
        if l == 0
            data[i*num_labels+l+1] = 0
        else
            data[i*num_labels+l+1] = 10
        end
    else
        if l == 5
            data[i*num_labels+l+1] = 0
        else
            data[i*num_labels+l+1] = 10
        end
    end
end

# "next set up the array for smooth costs"
smooth = Vector{Int}(num_labels * num_labels)
for l1 = 0:num_labels-1, l2 = 0:num_labels-1
    smooth[l1+l2*num_labels+1] = (l1-l2)*(l1-l2) <= 4  ? (l1-l2)*(l1-l2) : 4
end

gco = GCoptimizationGeneralGraph(num_pixels, num_labels)
setDataCost(gco, data)
setSmoothCost(gco, smooth)

# now set up a grid neighborhood system
# first set up horizontal neighbors
for y = 0:height-1, x = 1:width-1
    p1 = x - 1 + y*width
    p2 = x + y*width
    setNeighbors(gco, p1, p2, p1+p2)
end

#  next set up vertical neighbors
for y = 1:height-1, x = 0:width-1
    p1 = x + (y-1)*width
    p2 = x + y*width
    setNeighbors(gco, p1, p2, p1*p2)
end

println("Before optimization energy is ", compute_energy(gco))
expansion(gco, 2)
println("After optimization energy is ", compute_energy(gco))

for i = 0:num_pixels-1
    result[i+1] = whatLabel(gco, i)
end

showall(result)


# Method for function with no Jacobian.
function fixedpoint(f,
    initial_x::AbstractArray{T};
    method::Symbol = :anderson,
    xtol::Real = zero(T),
    ftol::Real = convert(T,1e-8),
    iterations::Integer = 1_000,
    store_trace::Bool = false,
    show_trace::Bool = false,
    extended_trace::Bool = false,
    linesearch = LineSearches.Static(),
    factor::Real = one(T),
    autoscale::Bool = true,
    m::Integer = 5,
    beta::Real = 1,
    droptol::Real = 0,
    autodiff::Symbol = :central,
    inplace::Bool = !applicable(f, initial_x)) where T
    # Check for weird case. (Causes to hang for now)
    # typeof(f) <: Union{InplaceObjective, NotInplaceObjective} ? error("Union{InplaceObjective, NotInplaceObjective} Case") : true;
    # Wrapping
    if inplace
        function g!(out, x)
            f(out, x);
            out .-= x;
        end
        dg = OnceDifferentiable(g!, initial_x, copy(initial_x), autodiff)
    else
        g(x) = f(x) - x;
        dg = OnceDifferentiable(g, initial_x, copy(initial_x); autodiff = autodiff, inplace = inplace)
    end

    return nlsolve(dg,
            initial_x, method = method, xtol = xtol, ftol = ftol,
            iterations = iterations, store_trace = store_trace,
            show_trace = show_trace, extended_trace = extended_trace,
            linesearch = linesearch, factor = factor, autoscale = autoscale,
            m = m, beta = beta, droptol = droptol)
end


using Graphs
using GraphMakie, CairoMakie
using Chain
using LayeredLayouts

const ϕ = (1 + √5) / 2

macro map(col, steps...)
  quote
    map($col) do item
      @chain(item, $(steps...))
    end
  end
end

# The duration of a sequence of dots and dashes
height(morse) = @chain morse collect map(_) do c
  Dict('.' => 1, '-' => 2)[c]
end sum

# By treating each . as 0 and each - as 01 in base Fibonacci, e.g.
#
#   --.-. = 01010010 = F_8 + F_6 + F_3
#
# we can label all the sequences of the same duration with consecutive
# integers. This function uses base ϕ as a shortcut.
basefib(morse) = @chain morse collect map(_) do c
  Dict('.' => "0", '-' => "01")[c]
end join "0" * _ reverse enumerate map(_) do (i, c)
  c == '1' ? ϕ^(i - 1) : 0
end sum

morseplot(labelfunc = first, table = morsetable) = begin
  g = @chain table length SimpleDiGraph
  force_order = Pair{Int,Int}[]
  colors = Symbol[]

  for (i, l) in @chain table first.() enumerate
    for (j, r) in @chain table first.() enumerate
      if l * "." == r
        add_edge!(g, i => j)
        push!(colors, :steelblue)
      elseif l * "-" == r
        add_edge!(g, i => j)
        push!(colors, :brown)
      elseif endswith(l, ".") &&
             endswith(r, "-") &&
             l[1:end-1] == r[1:end-1]
        add_edge!(g, i => j)
        push!(colors, :darkkhaki)
      elseif endswith(l, "..") && endswith(r, "-") && l[1:end-2] == r[1:end-1]
        push!(force_order, i => j)
      end
    end
  end

  _, ax = graphplot(
    g,
    # curves = false,
    nlabels = labelfunc.(table),
    # size = (720, 720),
    # # method = :buchheim,
    # fontsize = 20,
    nlabels_textsize = 20,
    # nlabels_offset = Point(-0.1, -0.1),
    # nlabels_color = :blue,
    edge_color = colors,
    node_attr = (; marker = ' '),
    figure = (; resolution = (720, 720)),
    layout = g -> begin
      x, y, _ = solve_positions(
        Zarate(),
        g,
        force_order = force_order,
        force_layer = @chain table first.() height.() enumerate map(t -> Pair(t...), _)
      )
      Point.(y, -x)
    end
    # layout = _ -> Point.(
    #   (@map table first (basefib(_) + 1) / (ϕ^height(_) + 1)),
    #   (@map table first height -_)
    # ),
  )
  hidedecorations!(ax)
  hidespines!(ax)
  # ax.aspect = 0.1
  current_figure()
end

function make()
  morseplot(first, morsetablecore)
  save("ditsdahs.svg", Makie.current_scene(), resolution = (860, 600))
  morseplot(last, morsetablecore)
  save("alphabet.svg", Makie.current_scene(), resolution = (860, 600))
  morseplot(last, morsetable)
  save("extended.svg", Makie.current_scene(), resolution = (860, 600))
  nothing
end

morsetable = [
  "" => " ",
  "." => "E",
  "-" => "T",
  ".." => "I",
  "-." => "N",
  ".-" => "A",
  "--" => "M",
  "..." => "S",
  "-.." => "D",
  ".-." => "R",
  "--." => "G",
  "..-" => "U",
  "-.-" => "K",
  ".--" => "W",
  "---" => "O",
  "...." => "H",
  "-..." => "B",
  ".-.." => "L",
  "--.." => "Z",
  "..-." => "F",
  "-.-." => "C",
  ".--." => "P",
  "---." => "Ó",
  "...-" => "V",
  "-..-" => "X",
  ".-.-" => "Ä", # Also newline
  "--.-" => "Q",
  "..--" => "Ü",
  "-.--" => "Y",
  ".---" => "J",
  "----" => "Ĥ",
  "....." => "5",
  "-...." => "6",
  ".-..." => "&",
  "--..." => "7",
  # "..-.." => "***",
  "-.-.." => "Ć",
  # ".--.." => "***",
  "---.." => "8",
  # "...-." => "***",
  "-..-." => "/",
  ".-.-." => "+",
  # "--.-." => "***",
  # "..--." => "***",
  "-.--." => "(",
  # ".---." => "***",
  "----." => "9",
  "....-" => "4",
  "-...-" => "=",
  # ".-..-" => "***",
  # "--..-" => "***",
  # "..-.-" => "***",
  # "-.-.-" => "***",
  # ".--.-" => "***",
  # "---.-" => "***",
  "...--" => "3",
  # "-..--" => "***",
  # ".-.--" => "***",
  # "--.--" => "***",
  "..---" => "2",
  # "-.---" => "***",
  ".----" => "1",
  "-----" => "0",
]

morsetablecore = @chain morsetable filter(_) do x
  isascii(last(x)) && isletter(last(x)[1]) || last(x) == " "
end

;


using Jecco
using HDF5
#using Plots
#gr()
include("./Fourier_modes.jl")

dirname = "/Users/apple/Documents/Jecco.jl/data/old_potential_e_10/"
#Specify the initial iteration, the final and the desired step.
initial_it = 0
step_it = 500
final_it = 100000

#Phi0 and inverse of phiM squared
phi0 = 1.0
phiM2 = -(2.3^2)

To_hdf5 = "no"
To_mathematica = "yes"
Fourier_modes = "no"
order_x, order_y = (4,0)
integration_method = "trapezoidal"
#Read_tensor = "yes"

files = OpenPMDTimeSeries(dirname,prefix="boundary")
files_xi = OpenPMDTimeSeries(dirname,prefix="gauge")

u, x, y = get_field(files,it=initial_it,field="a4")[2][:]
nt = Int(floor((final_it-initial_it)/step_it))+1
# Add the last point
append!(x, x[end]+(x[2]-x[1]))
append!(y, y[end]+(y[2]-y[1]))
nx, ny = length(x), length(y)

e, Jx, Jy = zeros(nt,nx,ny), zeros(nt,nx,ny), zeros(nt,nx,ny)
px, py, pxy, pz = zeros(nt,nx,ny), zeros(nt,nx,ny), zeros(nt,nx,ny), zeros(nt,nx,ny)
O_phi = zeros(nt,nx,ny)
t = zeros(nt)
#Matrix of coefficients of fourier modes of the energy density.
if Fourier_modes == "yes"
    a, b = zeros(nt,order_x+1,order_y+1), zeros(nt,order_x+1,order_y+1)
end

for it in initial_it:step_it:final_it
    #We first read all the files
    a4 = get_field(files,it=it,field="a4")[1][1,:,:]
    b14 = get_field(files,it=it,field="b14")[1][1,:,:]
    b24 = get_field(files,it=it,field="b24")[1][1,:,:]
    fx2 = get_field(files,it=it,field="fx2")[1][1,:,:]
    fy2 = get_field(files,it=it,field="fy2")[1][1,:,:]
    g4 = get_field(files,it=it,field="g4")[1][1,:,:]
    phi2 = get_field(files,it=it,field="phi2")[1][1,:,:]
    #xi = get_field(files_xi,it=it,field="xi")[1][1,:,:]

    i = Int(floor((it-initial_it)/step_it))+1
    #phi2 = phi0^3 .*phi3 .- phi0.*xi.^2

    e[i,1:end-1,1:end-1] = -3/4 .*a4./phi0^4 - phi2./phi0.^3 .- (9-7*phiM2)/(36*phiM2)
    Jx[i,1:end-1,1:end-1] = fx2./phi0^4
    Jy[i,1:end-1,1:end-1] = fy2./phi0^4
    px[i,1:end-1,1:end-1] = -a4./(4*phi0^4) - b14./phi0^4 - b24./phi0^4 + phi2./(3*phi0^3) .+ (-5/108 + 1/(4*phiM2))
    py[i,1:end-1,1:end-1] = -a4./(4*phi0^4) + b14./phi0^4 - b24./phi0^4 + phi2./(3*phi0^3) .+ (-5/108 + 1/(4*phiM2))
    pxy[i,1:end-1,1:end-1] = -g4./phi0^4
    pz[i,1:end-1,1:end-1] = -a4./(4*phi0^4) + 2 .*b24./phi0^4 + phi2./(3*phi0^3) .+ (-5/108 + 1/(4*phiM2))
    O_phi[i,1:end-1,1:end-1] = -2 .*phi2./phi0^3 .+ (1/3 - 1/phiM2)

    t[i] = files.current_t

    #Add the last points
    e[i,end,:], e[i,:,end], e[i,end,end] = e[i,1,:], e[i,:,1], e[i,1,1]
    Jx[i,end,:], Jx[i,:,end], Jx[i,end,end] = Jx[i,1,:], Jx[i,:,1], Jx[i,1,1]
    Jy[i,end,:], Jy[i,:,end], Jy[i,end,end] = Jy[i,1,:], Jy[i,:,1], Jy[i,1,1]
    px[i,end,:], px[i,:,end], px[i,end,end] = px[i,1,:], px[i,:,1], px[i,1,1]
    py[i,end,:], py[i,:,end], px[i,end,end] = py[i,1,:], py[i,:,1], py[i,1,1]
    pxy[i,end,:], pxy[i,:,end], pxy[i,end,end] = pxy[i,1,:], pxy[i,:,1], pxy[i,1,1]
    pz[i,end,:], pz[i,:,end], pz[i,end,end] = pz[i,1,:], pz[i,:,1], pz[i,1,1]
    O_phi[i,end,:], O_phi[i,:,end], O_phi[i,end,end] = O_phi[i,1,:], O_phi[i,:,1], O_phi[i,1,1]

    if Fourier_modes == "yes"
        a[i,:,:], b[i,:,:] = Fourier_modes_2D(e[i,:,:], x, y, order_x, order_y, integration_method)
    end

end

#Writing the data into a h5df file.
if To_hdf5 == "yes"

    output = string(dirname,"Stress-Tensor.h5df")
    fid = h5open(output,"w")

    group_coord = g_create(fid, "Coordinates")
    group_st = g_create(fid, "Stress-Tensor")
    group_svev = g_create(fid, "Scalar_VEV")
    group_fourier = g_create(fid, "Fourier modes")

    Jecco.write_dataset(group_coord, "t",t)
    Jecco.write_dataset(group_coord, "x",x)
    Jecco.write_dataset(group_coord, "y",y)

    Jecco.write_dataset(group_st, "e",e)
    Jecco.write_dataset(group_st, "Jx",Jx)
    Jecco.write_dataset(group_st, "Jy",Jy)
    Jecco.write_dataset(group_st, "px",px)
    Jecco.write_dataset(group_st, "py",py)
    Jecco.write_dataset(group_st, "pxy",pxy)
    Jecco.write_dataset(group_st, "pz",pz)

    Jecco.write_dataset(group_svev, "O_phi",O_phi)

    Jecco.write_dataset(group_fourier, "a",a)
    Jecco.write_dataset(group_fourier, "b",b)

    close(fid)
end

#This writes into a h5df file that has the mathematica array format directly
if To_mathematica == "yes"
    T_m = zeros(11,nx*ny*nt)
    if Fourier_modes == "yes"
        f_m = zeros(2*(order_x+1)*(order_y+1)+1,nt)
    end
    n = 0
    for i in 1:nt
        for j in 1:nx
            for k in 1:ny
                global n += 1
                T_m[:,n] = [t[i] x[j] y[k] e[i,j,k] Jx[i,j,k] Jy[i,j,k] px[i,j,k] pxy[i,j,k] py[i,j,k] pz[i,j,k] O_phi[i,j,k]]
            end
        end
        if Fourier_modes == "yes"
            f_m[:,i] = hcat(t[i], reshape(transpose(a[i,:,:]),1,:), reshape(transpose(b[i,:,:]),1,:))
        end
    end

    output = string(dirname,"data_mathematica.h5df")
    fid = h5open(output,"w")

    group_st = g_create(fid, "data")

    Jecco.write_dataset(group_st, "VEVs",T_m)
    if Fourier_modes == "yes"
        Jecco.write_dataset(group_st, "Fourier modes",f_m)
    end

    close(fid)

end


using SumOfSquares
using DynamicPolynomials
using MosekTools
using Test

include("sparse_polynomials.jl")

function test(test_function, factory)
    sparse_model = sos_lower_bound(test_function, factory, VariableSparsity())
    dense_model  = sos_lower_bound(test_function, factory, NoSparsity())
    return abs(objective_value(sparse_model) - objective_value(dense_model))
end

@testset "chordal_sos" begin
    factory = Mosek.Optimizer

    val = test(chained_singular(12), factory)
    @test abs(val) < 1e-6

    val = test(broyden_banded(6), factory)
    @test abs(val) < 1e-6

    val = test(broyden_tridiagonal(12), factory)
    @test abs(val) < 1e-6

    val = test(chained_wood(12), factory)
    @test abs(val) < 1e-6

    val = test(generalized_rosenbrock(12), factory)
    @test abs(val) < 1e-6

end


using Turing, Random, CSV, DataFrames

function run_example()
    Random.seed!(12)

    # Reproducing the pymc3 implementation found here:
    # https://www.ritchievink.com/blog/2018/10/09/build-facebooks-prophet-in-pymc3-bayesian-time-series-analyis-with-generalized-additive-models/

    # downloaded from https://raw.githubusercontent.com/facebook/prophet/master/examples/example_wp_log_peyton_manning.csv
    url = "https://raw.githubusercontent.com/facebook/prophet/master/examples/example_wp_log_peyton_manning.csv"
    p = joinpath("data", basename(url))

    if !isfile(p)
        mkpath(dirname(p))
        download(url, p)
    end
    df = CSV.read(p)

    df[!, :yscale] = df[!, :y] ./ maximum(df[!, :y])

    min_ds, max_ds = extrema(df.ds)
    df[!, :t] = (df.ds - min_ds) / (max_ds - min_ds)

    # plot(df.ds, df.yscale)

    nchangepoints = 10

    # Proportion of history in which trend changepoints will be estimated.
    changepoints_range = .8
    # The standard deviation of the prior on the growth.
    growth_prior_scale = 5
    # The scale of the Laplace prior on the delta vector.
    changepoints_prior_scale = .05

    @model trendmodel(t, y, ::Type{TV}=Vector{Float64}) where TV = begin  # <= CHANGED
        # num observations
        nobs = length(t)
        s =  collect(range(1, stop=nobs*changepoints_range, length=nchangepoints))
        A = [(t[i] >= s[j])*1 for i=1:nobs, j=1:nchangepoints]

        k ~ Normal(0.0, growth_prior_scale)

        # rate of change
        delta = TV(undef, nchangepoints)  # <= CHANGED
        delta ~ [Laplace(0.0, changepoints_prior_scale)]

        # offset
        m ~ Normal(0.0, 5.0)

        gamma = -s.*delta

        g = (k .+ A*delta).*t + (m .+ A * gamma)

        # The number of observations.
        sd ~ Truncated(Cauchy(0.0, .5), 0.000001, Inf)
        for n in 1:nobs
            y[n] ~ Normal(g[n], sd)
        end
    end

    model = trendmodel(df.t, df.yscale)

    chain = sample(model, NUTS(1500, 200, 0.65), progress=true);
end


# Show some associative array math.

# Read CSV file into associative array, get vertices and convert to numbers.
E = ReadCSV(joinpath(Base.source_dir(),"Edge.csv"))
Ev = logical( E[:, StartsWith("V,")] )

# Get orange and green edges
EvO = Ev[StartsWith("O,"),:]
EvG = Ev[StartsWith("G,"),:]

# Compute (empty) vertex adjacency graph.
AvOG = transpose(EvO) * EvG
printFull(AvOG)

# Compute edge adjacency graph.
AeOG = EvO * transpose(EvG)
printFull(AeOG)

# Compute edge adjacency graph preserving keys.
AeOG = CatKeyMul(EvO,transpose(EvG))
printFull(AeOG)


# Atmos RHS

function ∑tendencies_atm!(dY, Y, (parameters, T_sfc), t)
    # Heat diffusion:
    # ∂_t ρ =  ∇ (μ ∇ ρ - w ρ)
    # ∂_t ρθ =  ∇ (μ ∇ ρθ - w ρθ)
    # ∂_t w =  ∇ (μ ∇ w - w w)

    # where
    # ∂_t T = n \cdot F   at z = zmin_atm
    # ∂_t T = 0           at z = zmax_atm
    # We also use this model to accumulate fluxes
    # ∂_t ϕ_bottom = n \cdot F

    # μ = FT(0.0001) # diffusion coefficient

    # T = u.x[1]

    # F_sfc = - calculate_flux( T_sfc[1], parent(T)[1] )

    # # set BCs
    # bcs_bottom = Operators.SetValue(F_sfc) # struct w bottom BCs
    # bcs_top = Operators.SetValue(FT(280.0))

    # gradc2f = Operators.GradientC2F(top = bcs_top) # gradient struct w BCs
    # gradf2c = Operators.GradientF2C(bottom = bcs_bottom)
    # @. du.x[1] = gradf2c( μ * gradc2f(T))
    # du.x[2] .= - F_sfc[1]

    UnPack.@unpack Cd, f, ν, ug, vg, C_p, MSLP, R_d, R_m, C_v, grav = parameters

    (Yc, Yf, F_sfc) = Y.x
    (dYc, dYf, dF_sfc) = dY.x
    UnPack.@unpack ρ, u, v, ρθ = Yc
    UnPack.@unpack w = Yf
    dρ = dYc.ρ
    du = dYc.u
    dv = dYc.v
    dρθ = dYc.ρθ
    dw = dYf.w

    # auxiliary calculations
    u_1 = parent(u)[1]
    v_1 = parent(v)[1]
    u_wind = sqrt(u_1^2 + v_1^2)

    # density (centers)
    gradc2f = Operators.GradientC2F()
    gradf2c = Operators.GradientF2C(bottom = Operators.SetValue(0.0), top = Operators.SetValue(0.0))

    If = Operators.InterpolateC2F(bottom = Operators.Extrapolate(), top = Operators.Extrapolate())
    @. dρ = gradf2c(-w * If(ρ)) # Eq. 4.11

    # potential temperature (centers)
    gradc2f = Operators.GradientC2F()
    gradf2c = Operators.GradientF2C(bottom = Operators.SetValue(0.0), top = Operators.SetValue(0.0)) # Eq. 4.20, 4.21

    @. dρθ = gradf2c(-w * If(ρθ) + ν * gradc2f(ρθ / ρ)) # Eq. 4.12

    # u velocity (centers)
    gradc2f = Operators.GradientC2F(top = Operators.SetValue(ug)) # Eq. 4.18
    gradf2c = Operators.GradientF2C(bottom = Operators.SetValue(Cd * u_wind * u_1)) # Eq. 4.16

    A = Operators.AdvectionC2C(bottom = Operators.SetValue(0.0), top = Operators.SetValue(0.0))
    @. du = gradf2c(ν * gradc2f(u)) + f * (v - vg) - A(w, u) # Eq. 4.8

    # v velocity (centers)
    gradc2f = Operators.GradientC2F(top = Operators.SetValue(vg)) # Eq. 4.18
    gradf2c = Operators.GradientF2C(bottom = Operators.SetValue(Cd * u_wind * v_1)) # Eq. 4.16

    A = Operators.AdvectionC2C(bottom = Operators.SetValue(0.0), top = Operators.SetValue(0.0))
    @. dv = gradf2c(ν * gradc2f(v)) - f * (u - ug) - A(w, v) # Eq. 4.9

    # w velocity (faces)
    gradc2f = Operators.GradientC2F()
    gradf2c = Operators.GradientF2C(bottom = Operators.SetValue(0.0), top = Operators.SetValue(0.0))

    B = Operators.SetBoundaryOperator(bottom = Operators.SetValue(0.0), top = Operators.SetValue(0.0))
    If = Operators.InterpolateC2F(bottom = Operators.Extrapolate(), top = Operators.Extrapolate())
    Π(ρθ) = C_p .* (R_d .* ρθ ./ MSLP) .^ (R_m ./ C_v)
    #dw = ρθ .* 0.0
    @. dw = B(-(If(ρθ / ρ) * gradc2f(Π(ρθ))) - grav + gradc2f(ν * gradf2c(w)) - w * If(gradf2c(w))) # Eq. 4.10

    return dY

end


using DataFrames
using StanSample, Test

ProjDir = @__DIR__
cd(ProjDir)

gq = "
  data {
    int<lower=0> N;
    int<lower=0> y[N];
  }
  parameters {
    real<lower=0.00001> Theta;
  }
  model {
    Theta ~ gamma(4, 1000);
    for (n in 1:N)
      y[n] ~ exponential(Theta);
  }
  generated quantities{
    real y_pred;
    y_pred = exponential_rng(Theta);
  }
";

data = Dict(
  "N" => 3,
  "y" => [100, 950, 450]
);

sm = SampleModel("Generate_quantities", gq, joinpath(@__DIR__, "tmp"));

rc = stan_sample(sm; data)

if success(rc)
  df = stan_generate_quantities(sm, 1, "1")
  df |> display
end


mutable struct LineSampling
    lines::Integer
    points::Vector{<:Real}
    direction::NamedTuple

    function LineSampling(
        lines::Integer,
        points::Vector{<:Real}=collect(0.5:0.5:5),
        direction::NamedTuple=NamedTuple(),
    )
        return new(lines, points, direction)
    end
end

function sample(inputs::Array{<:UQInput}, sim::LineSampling)
    random_inputs = filter(i -> isa(i, RandomUQInput), inputs)
    deterministic_inputs = filter(i -> isa(i, DeterministicUQInput), inputs)

    n_rv = count_rvs(random_inputs)
    n_samples = length(sim.points) * sim.lines
    rv_names = names(random_inputs)

    α = map(n -> sim.direction[n], rv_names)
    α /= norm(α)

    θ = rand(Normal(), n_rv, sim.lines)

    θ = θ - α * (α' * θ)
    θ = repeat(θ; outer=[length(sim.points), 1])

    θ = θ[:] + repeat(α * sim.points'; outer=[1, sim.lines])[:]

    samples = transpose(reshape(θ, n_rv, n_samples))
    samples = DataFrame(rv_names .=> eachcol(samples))

    if !isempty(deterministic_inputs)
        samples = hcat(samples, sample(deterministic_inputs, n_samples))
    end

    to_physical_space!(inputs, samples)

    return samples
end


@defcomp impactcooling begin
    regions = Index()

    cooling = Variable(index=[time,regions])

    cebm = Parameter(index=[regions])
    gdp90 = Parameter(index=[regions])

    population = Parameter(index=[time,regions])
    pop90 = Parameter(index=[regions])

    income = Parameter(index=[time,regions])
    ceel = Parameter(default = 0.8)

    temp = Parameter(index=[time,regions])
    cenl = Parameter(default = 1.5)

    cumaeei = Parameter(index=[time,regions])

    function run_timestep(p, v, d, t)

        if !is_first(t)
            for r in d.regions
                ypc = p.income[t, r] / p.population[t, r] * 1000.0
                ypc90 = p.gdp90[r] / p.pop90[r] * 1000.0

                v.cooling[t, r] = p.cebm[r] * p.cumaeei[t, r] * p.gdp90[r] * (p.temp[t, r] / 1.0)^p.cenl * (ypc / ypc90)^p.ceel * p.population[t, r] / p.pop90[r]
            end
        end
    end
end


reload("RMB")

module dev

using RMB
import RMB: inventorymodel, pidc1, pidc2, detensor, rank, pi!, rsm

dims=(5,3,10)
m2=inventorymodel(dims,5.0,20.0,[0.8,1.0,1.2])
pi!(m2,pidc1(m2))
u!(m2,[0.0,1.0])    #free stocking, expensive order
solvedp!(m2)

dd,dc,ds,dy,dr=m2.dims
dk,da=dd*dc*ds,dy*dr

ac=reshape([1.0*(ik==jk)*(ia==1) for ik=1:dk,jk=1:dk,ia=1:da],dk,dk*da)

function pi!(model::InventoryModel,eta::AbstractVector)
    pidc=RMB.z2q(eta)
    pi!(model,pidc)
end

eta=rand(dd*dc*(dd*dc-1))
j1=RMB.idrank(m2,ac,eta)

# pic1=rsm(dc,dc)
# pic2=rsm(dc,dc)
# pid1=rsm(dd,dd)
# pid2=rsm(dd,dd)

# function pi!(model::InventoryModel,eta::AbstractVector)
#     pic=eta[1]*pic1+(1-eta[1])*pic2
#     pid=eta[2]*pid1+(1-eta[2])*pid2
#     pi!(model,kron(pic,pid))
# end

# eta=[0.5,0.5]
# j2=RMB.idrank(m2,eta)

end #module


module fenkar_pde

export fenkar!, p, u0, tspan, N

const N = Int(round(3/0.015 * sqrt(2)))
const D1= 0.0010/(0.015*0.015)
const D2= 0.0002/(0.015*0.015)

# define fenkar system
function H(x;k=100.0)
	return 0.5*(1.0 + tanh(k*x))
end
function fenkar!(dx, x, p, t)

	# parameters
	@views tsi,tv1m,tv2m,tvp,twm,twp,td,to,tr,xk,uc,uv,ucsi,t0,TI,IA,θ = p[1:17]

	@inbounds for n=1:N

		# fenkar dynamics
		dx[n,1] =-(x[n,1]*H(uc-x[n,1])/to + H(x[n,1]-uc)/tr - x[n,2]*H(x[n,1]-uc)*(1.0-x[n,1])*(x[n,1]-uc)/td - x[n,3]*H(x[n,1]-ucsi;k=xk)/tsi)
		dx[n,2] = H(uc-x[n,1])*(1.0-x[n,2])/(tv1m*H(uv-x[n,1]) + tv2m*H(x[n,1]-uv)) - H(x[n,1]-uc)*x[n,2]/tvp
		dx[n,3] = H(uc-x[n,1])*(1.0-x[n,3])/twm - H(x[n,1]-uc)*x[n,3]/twp

		# no-flux with angular dependence on two diffusions
		if n==1
			dx[n,1] = dx[n,1] + (D1*cos(θ)^2 + D2*sin(θ)^2)*(2*x[2,1] - 2*x[n,1])
		elseif n==N
			dx[n,1] = dx[n,1] + (D1*cos(θ)^2 + D2*sin(θ)^2)*(2*x[N-1,1] - 2*x[n,1])
		else
			dx[n,1] = dx[n,1] + (D1*cos(θ)^2 + D2*sin(θ)^2)*(x[n-1,1] + x[n+1,1] - 2*x[n,1])
		end
	end

	@inbounds for n=1:10
		dx[n,1] = dx[n,1] + H(t-t0;k=1.0).*IA*sin(pi*(t-t0)/TI)^500
	end

	return nothing
end

# fenkar system parameters in p[1:13] (BR; from https://doi.org/10.1063/1.166311)
# and then p[14:14] = [fiber_angle]
# and then p[15:17] = [offset_time, stimulus_period, stimulus_amplitude]
   p = [29.0, 19.6, 1250.0, 3.33, 41.0, 870.0, 0.25, 12.5, 33.3, 10.0, 0.13, 0.04, 0.85, 0.0,   125.0, 0.1, 0.0]
   tspan = (0.0, 3000.0)
   u0 = zeros(Float64,N,3)
end


using Displaz
using StaticArrays
using RoamesGeometry

println("Press ENTER to make a new example")
println("Type any non-empty string and press ENTER to quit")

while(readline(STDIN) == "")
	l1 = Line(rand(SVector{3,Float64}), rand(SVector{3,Float64}))
	l2 = Line(rand(SVector{3,Float64}), rand(SVector{3,Float64}))
	(p1, p2) = closest_points(l1, l2)
	l3 = Line(p1, p2)

	plot3d!(l1, color = [0.6, 0.1, 1.0], label = "Line 1")
	plot3d!(l2, color = [0.1, 0.6, 1.0], label = "Line 2")
	plot3d!(l3, color = [0.8, 0.8, 0.2], label = "Closest points")
end


using ForwardDiff

function anomaloushall(kx,ky,T,μ,Ham!,H1,H2,H3)
    ϵ = -1:0.1:1
    kb = 8.6*10^(-5) #eV / K
    β = 1/(kb*T) # ≈ 40 at 300 K
    ħ = 6.582*10^(-16) # eV*s
    Ham!(kx,ky,H1) ; Hx = similar(H1) ; Hy = similar(H1) ;
    val , vec = eigen!(H1)

    curv = similar(val)
    Dx(kx,ky,Ham!,H2,H3,Hx)
    vx = vec'*Hx*vec

    denominator = [ ifelse( n-m ≈ 0, 0, 1/(n-m)^2 ) for n in val , m in val]

    Dy(kx,ky,Ham!,H2,H3,Hy)
    vy = vec'*Hy*vec

    @. vy *= denominator

    @inbounds for c in eachindex(curv)
        curv[c] = -2*imag(sum(vx[c,m]*vy[m,c] for m in 1:length(val)))
    end

    @. curv /= T*ħ    # make curv larger, for curv is small

    nernst = 0
    @inbounds for n in 1:length(val)
        f = exp(β*(val[n] - μ)) |> x -> ifelse(x≈0, 0, x)
        nernst += (val[n]-μ)/( 1+f )+log( 1+1.0/f)/β |>  x -> x*curv[n]
    end
    -nernst
end


# epsxc only version
function XC_c_pw_E( Rhoe )

    third = 1.0/3.0
    pi34 = 0.6203504908994
    rs = pi34/Rhoe^third

    a = 0.031091
    a1 = 0.21370
    b1 = 7.5957
    b2 = 3.5876
    b3 = 1.6382
    b4 = 0.49294

    # interpolation formula
    rs12 = sqrt(rs)
    rs32 = rs * rs12
    rs2 = rs^2

    om = 2.0 * a * (b1 * rs12 + b2 * rs + b3 * rs32 + b4 * rs2)
    olog = log(1.0 + 1.0/om)
    ec = -2.0 * a * (1.0 + a1 * rs) * olog

    return ec
end


using PyPlot
"Rounding for legends"
round_sig(x; sig = 3) = round(x, digits = sig - Int(floor(log10(abs(x))))-1)

"Pyplot 3d surface and countour plot"
function wrap_surface_and_countour(np::Npos; title = "3d surface", levels = 100)
    wrap_surface_and_countour(slice_1(np), slice_2(np), slice_3(np); title = title, levels = levels)
end
function wrap_surface_and_countour(mat_x, mat_y, mat_z; title = "3d surface", levels = 100)
    clf()
    using3D()
    fig = figure("pyplot_surfaceplot", figsize=(10, 10)) # inches
    ax = fig.add_subplot(2,1,1, projection="3d")
    nx, ny = size(mat_x)
    plot_surface(mat_x, mat_y, mat_z, rcount=nx, ccount = ny, edgecolors="k",
                 cmap=ColorMap("Blues"), alpha=0.8, linewidth=0.25)
    xlabel("x")
    ylabel("y")
    PyPlot.title(title)
    #subplot(212)
    ax2 = fig.add_subplot(2,1,2, aspect = "equal")
    ax2.contourf(mat_x, mat_y, mat_z, levels = levels, cmap="Reds")
    cp = ax2.contour(mat_x, mat_y, mat_z, levels = levels, colors="k")
    ax2.clabel(cp, inline=1, fontsize=10, colors = "black")
    xlabel("x")
    ylabel("y")
    PyPlot.title(title * " contour")
    #tight_layout()
end
function wrap_quiver(x::Pos, y::Pos, np::Npos; title = "Quiver")
    if (size(x)[1], size(y)[1]) != size(np)
        error("incorrect dims")
    end
    clf()
    fig = figure("pyplot_quiverplot", figsize = (10, 10))
    q = quiver(x', y', slice_1(np)', slice_2(np)')
    maxlen = maximum(len.(replace(np, [NaN, NaN]=>[0.0]))) |> round_sig
    ax = gca()
    ax.quiverkey(q, X=0.8, Y = 0.05, U = maxlen, coordinates="figure", label="Quiver key, length = $maxlen",labelpos = "E")
    PyPlot.title(title)
end
function wrap_stream_a(x::Matrix, y::Matrix, np::Npos; density = 1., title = "Stream a")
    clf()
    fig = figure("pyplot_streamplot", figsize = (10,10))
    streamplot(x = x, y = y, u = slice_1(np), v = slice_2(np),
            density = density)
    PyPlot.title(title)
end
function wrap_stream_b(x::Matrix, y::Matrix, np::Npos; density = 1., title = "Stream b")
    clf()
    fig = figure("pyplot_streamplot", figsize = (10,10))
    vl = len.(np)
    streamplot(x = x, y = y, u = slice_1(np), v = slice_2(np),
             color = vl,
             linewidth = 2,
             cmap = PyPlot.cm.autumn,
             density = density)
    PyPlot.title(title)
end
function wrap_stream_c(x::Matrix, y::Matrix, np::Npos; density = 1., title = "Stream c")
    clf()
    fig = figure("pyplot_streamplot", figsize = (10,10))
    vl = len.(np)
    maxlen = maximum(len.(replace(np, [NaN, NaN]=>[0.0])))
    lw = 5 .* vl ./ maxlen
    streamplot(x = x, y = y, u = slice_1(np), v = slice_2(np),
        color = "k",
        density = density,
        linewidth = lw)
    PyPlot.title(title)
end
function wrap_stream_d(x::Matrix, y::Matrix, np::Npos; density = 1., title = "Stream d")
    clf()
    fig = figure("pyplot_streamplot", figsize = (10,10))
    vl = len.(np)
    maxlen = maximum(len.(replace(np, [NaN, NaN]=>[0.0])))
    lw = 5 .* vl ./ maxlen
    streamplot(x = x, y = y, u = slice_1(np), v = slice_2(np),
        color = vl,
        density = density,
        cmap = PyPlot.cm.YlGnBu,
        linewidth = lw)
    PyPlot.title(title)
end


using Perceptrons
using Test

include("utils_test.jl")
include("linear_perceptron_test.jl")
include("kernel_perceptron_test.jl")
include("voted_perceptron_test.jl")
include("averaged_perceptron_test.jl")


import Base: merge

###### merge ####################

#function merge{T,N}(ta1::TimeArray{T,N}, ta2::TimeArray{T,N}; method="inner")
function merge{T}(ta1::TimeArray{T}, ta2::TimeArray{T}; colnames = [""], method="inner",)
    tstamp = [Date(1,1,1):Year(1):Date(length(ta1),1,1)]
    n      = 1
      for i in 1:length(ta1)
        for j in 1:length(ta2)
          if ta1.timestamp[i] == ta2.timestamp[j]
            tstamp[n] = ta1.timestamp[i]
            n+=1
          end
        end
      end
    tstamp = tstamp[1:n-1] # trim down the length, if necessary

    val1 = ta1[tstamp].values
    val2 = ta2[tstamp].values
    vals = hcat(val1, val2)

    #if length(colnames) != 2
    if length(colnames) < 2
    cnames = copy(ta1.colnames) # otherwise ta1 gets contaminated
      for m in 1:length(ta2.colnames)
        push!(cnames, ta2.colnames[m])
      end
     else cnames = colnames
     end

    TimeArray(tstamp, vals, cnames)
end

# collapse ######################

function collapse{T,N}(ta::TimeArray{T,N}, f::Function; period::Function=week)

  w = [period(ta.timestamp[t]) for t in 1:length(ta)] # get weekly id from entire array
  z = Int[]; j = 1
  for i=1:length(ta) - 1 # create unique period ID array
    if w[i] != w[i+1]
      push!(z, j)
      j = j+1
    else
      push!(z,j)
    end
  end

  # account for last row
  w[length(ta)]  ==  w[length(ta)-1] ? # is the last row the same period as 2nd to last row?
  push!(z, z[length(z)]) :
  push!(z, z[length(z)] + 1)

  # pre-allocate timestamp and value arrays
  tstamps = [Date(1,1,1):Year(1):Date(maximum(z),1,1)]
  vals    = zeros(maximum(z)) # number of unique periods
  #replace their values except for the last row
  for i = 1:maximum(z)-1  # iterate over period ID groupings
    temp       = ta[findfirst(z .== i):findfirst(z .== i+1)-1] # period's worth that will be squished
    tstamps[i] = temp[length(temp)].timestamp[1]
    vals[i]    = f(temp.values)
  end
  # and once again account for the last temp that isn't looped on above
  lasttemp = ta[findfirst(z .== maximum(z)):length(ta)]
  lasttempindex = lasttemp[length(lasttemp)].timestamp
  lasttempvalue = f(ta.values)

  # complete the tstamp and vals arrays with the last value
  tstamps[length(tstamps)] = lasttempindex[1]
  vals[length(vals)]       = lasttempvalue

  TimeArray(tstamps, vals, ta.colnames)
end


function update_position(X, V, dir, dt, L)
    X = X + dir*V*dt
    #X = X + eps(X)
    if X > L
        dir = -1      # if beyond L, go back (left)
    elseif X < 0
        dir = 1       # if beyond 0, go back (right)
    end
    return X, dir
end

function car_travel_1D()
    # Physical parameters
    V     = 13.0          # speed, km/h
    L     = 200.0          # length of segment, km
    dir   = 1              # switch 1 = go right, -1 = go left
    ttot  = 16.0           # total time, h
    # Numerical parameters
    dt    = 0.1            # time step, h
    nt    = Int(cld(ttot, dt))  # number of time steps
    # Array initialisation
    T     = zeros(nt)
    X     = zeros(nt)
    # Time loop
    for it = 2:nt
        T[it] = T[it-1] + dt
        X[it], dir = update_position(X[it], V, dir, dt, L)
    end
    # Visualisation
    # display(scatter(T, X, markersize=5, xlabel="time, hrs", ylabel="distance, km", framestyle=:box, legend=:none))
    return T, X
end

# Only run this in an interactive session:
if isinteractive()
    println("Running model")
    T, X = car_travel_1D()
end


#!/usr/bin/env julia

using Luxor, Test

using Random
Random.seed!(42)

function general_tests()
    pt1 = Point(rand() * 4, rand() * 4)

    # arithmetic with tuples
    @test pt1 + (5, 8) == Point(pt1.x + 5, pt1.y + 8)
    @test pt1 * (5, 8) == Point(pt1.x * 5, pt1.y * 8)
    @test pt1 - (5, 8) == Point(pt1.x - 5, pt1.y - 8)
    @test pt1 / (5, 8) == Point(pt1.x / 5, pt1.y / 8)

    @test -pt1 == Point(-pt1.x, -pt1.y)

    # is point/4 inside a box
    # a: we now have to wrap arguments with Ref() to ensure they  broadcast as scalar
    @test isinside(Ref(pt1) ./ 4, box(O, 10, 10, vertices=true))

    # b: but we defined Point as broadcastable, so we shouldn't need that
    @test isinside(pt1 ./ 4, box(O, 10, 10, vertices=true))

    # is point not in every corner of box
    @test all(Ref(Point(1, 1)) .< box(O, 10, 10, vertices=true)) == false
    @test any(Ref(Point(0, 0)) .< [Point(1, 1), Point(1, 2), Point(2, 3)]) == true
    # is point outside every corner of box
    @test all(Ref(Point(10, 10))  .> box(O, 10, 10, vertices=true)) == true

    # ? do these need Ref()?
    @test .>=(Point(5, 5),  Point(5, 5))
    @test .>=(Point(5, 5),  Point(5, 5))

    @test perpendicular(Point(10, 5)) == Point(5.0, -10.0)
    @test perpendicular(Point(-10, -5)) == Point(-5.0,10.0)

    @test crossproduct(Point(2, 3), Point(3, 2)) == -5.0
    @test crossproduct(Point(-20, 30), Point(60, 20)) < 2000

    # shared end points
    # intersection of (A == C) || (B == C) || (A == D) || (B == D)
    pt1 = Point(5, 5)
    pt2 = Point(6, 5)
    @test pt1 + pt2 == Point(11.0,10.0)
    @test intersectionlines(pt1, pt1, pt1, pt1)[1] == false
    @test intersectionlines(pt1, pt2, pt1, pt2, crossingonly=true)[1] == false
    @test intersectionlines(pt1, pt2, pt2, pt1)[1] == false
    pt3 = Point(6, 6)
    pt4 = Point(5, 6)
    @test intersectionlines(pt1, pt2, pt3, pt4)[1] == false

    @test intersectionlines(pt1, pt2, pt3, pt2) == (true,Luxor.Point(6.0,5.0))

    # not crossing
    pt1 = Point(5, 5)
    pt2 = Point(5, 5)
    pt3 = Point(5, 5)
    pt4 = Point(5, 5)
    @test intersectionlines(shuffle([pt1, pt2, pt3, pt4])...)[1] == false

    # parallel
    pt1 = Point(5, 5)
    pt2 = Point(5, 6)
    pt3 = Point(6, 5)
    pt4 = Point(6, 6)
    @test intersectionlines(pt1, pt2, pt3, pt4)[1] == false

    @test pt1 * pt2 == Luxor.Point(25.0, 30.0)
    @test pt1 ^ 2 == Luxor.Point(25.0, 25.0)
    @test pt1 ^ 3 == Luxor.Point(125.0,125.0)

    @test pt1 ^ 1.5 < pt1 ^ 1.6
    @test pt1 ^ 1.6 > pt1 ^ 1.5

    # test between interpolation
    @test isequal(midpoint(pt1, pt3), between(pt1, pt3, 0.5))
    @test isequal(between(pt1, pt3, 0.0), pt1)
    @test isequal(between(pt1, pt3, 1.0), pt3)

    # test uniqueness and those pesky -0.0s
    @test length(unique([pt1, pt2, pt3, pt4])) == 4
    @test length(unique([pt1, pt2, pt3, pt3])) == 3
    @test length(unique([pt1, pt3, pt3, pt3])) == 2
    @test length(unique([pt3, pt3, pt3, pt3])) == 1
    @test length(unique([])) == 0
    @test length(unique([Point(0.0, 0.0), Point(0.0, 0.0)])) == 1
    @test length(unique([Point(0.0, -0.0), Point(0.0, 0.0)])) == 1
    @test length(unique([Point(0.0, -0.0), Point(-0.0, 0.0)])) == 1
    @test length(unique([Point(0.0, -0.0), Point(-0.0, -0.0)])) == 1
    @test length(unique([Point(0.0, 0.0), Point(0, 0)])) == 1
    @test length(unique([Point(0, 0), Point(-0, -0)])) == 1

    # test drop perpendicular
    p1 = Point(0, 0)
    p2 = Point(0, 50)
    @test perpendicular(p1, p2, 10) == Luxor.Point(-10.0, 0.0)
    @test perpendicular(p1, p2, 20) == Luxor.Point(-20.0, 0.0)

    # test pointinverse
    p1 = Point(10, 10)
    cpt = O
    rad = 50
    flag, antipoint = pointinverse(p1, cpt, rad)
    @test flag == true
    @test isapprox(antipoint, Point(125, 125))

    p2 = Point(20, 0.0)
    flag, antipoint = pointinverse(p2, cpt, rad)
    @test flag == true
    @test isapprox(antipoint.y, 0.0)

    # point shouldn't be centerpoint
    @test_throws ErrorException pointinverse(cpt, cpt, rad)
end

function point_arithmetic_test(fname, npoints=20)
    Drawing(1200, 1200, fname)
    background("white")
    sethue("thistle")
    box(BoundingBox(), :stroke)
    scale(0.5, 0.5)
    setline(2.5)
    setopacity(0.5)
    fontsize(8)
    origin()
    randompoints = randompointarray(Point(-600, -600), Point(600, 600), npoints)

    # +
    pl1 = map(pt -> pt + 2, randompoints)
    pl1a = map(pt -> 2 + pt, randompoints)

    # -
    pl2 = map(pt -> pt - 2, randompoints)
    pl2a = map(pt -> 2 - pt, randompoints)

    # *
    pl3 = map(pt -> pt * rand(), randompoints)
    pl3a = map(pt -> rand() * pt, randompoints)

    # /
    pl4 = map(pt -> pt / rand(), randompoints)

    # .*
    pl5 = 1.012 .* randompoints
    pl5a = randompoints .* 1.012
    pl5b = [1.03, 0.97, -1.05] .* randompoints[1:3]

    # ./
    pl6 = randompoints[1:3] ./ [1.03, 0.97, -1.05]
    pl6a =  [1.03, 0.97, -1.05] .* randompoints[1:3]

    # ^
    pl7 = map(pt -> ^(pt/2, 2), randompoints)

    # ^ FAILS
    # pl8 = map(pt -> ^(pt, 1.2), randompoints)

    # issue #20615 now fixed in v0.6
    sethue("red")
    for p in zip(pl1, pl1a, pl2, pl2a, pl3, pl3a, pl4, pl5, pl5a, pl5b, pl6, pl6a, pl7)
       map(pt -> circle(pt, 3, :fill), p)
       poly(collect(p), :stroke)
    end

    # comparisons
    testfunctions = [isequal, isless, <, >, ==]
    sethue("green")
    for f in testfunctions
        for i in 1:length(randompoints)-1
            if f(randompoints[i], randompoints[i+1])
                circle(randompoints[i], 18, :fill)
                circle(randompoints[i+1], 7, :fill)
                text(string("$f"), randompoints[i] - 12)
            end
        end
    end

    testfunctions = [<, >, >=, <=]
    sethue("orange")
    for f in testfunctions
        for i in 1:length(randompoints)-1
            if f.(randompoints[1:npoints], randompoints[npoints:-1:1]) != true
                ellipse(randompoints[i], 19, 32, :fill)
                text(string("v6"), randompoints[i] - 6)
            end
        end
    end

    sethue("purple")
    for i in 1:length(randompoints)-1
        v = cmp(randompoints[i], randompoints[i+1])
        text(string(round(v, digits=1)), randompoints[i] + 15)
    end

    sethue("cyan")
    for i in 1:length(randompoints)-1
        n = distance(randompoints[i], randompoints[i+1])
        text(string(round(n, digits=1)), randompoints[i])
    end

    sethue("magenta")
    map(pt -> circle(pt, 6, :stroke), [midpoint(randompoints), randompoints[1], randompoints[2]])

    if all(randompoints .== randompoints)
        text("the points compare elementwise")
    else
        error("elementwise comparison failed")
    end

    if any(randompoints .!= randompoints)
        error("elementwise comparison failed")
    else
        text("the points really do compare elementwise", O + 15)
    end

    @test finish() == true

    println("...finished test: output in $(fname)")
end

general_tests()
point_arithmetic_test("point-arithmetic.pdf", 100)


function show_example(Mat_Label, labels, Mat_Unlabel, unlabel_data_labels)
    mat = vcat(Mat_Label, Mat_Unlabel)
    label = vcat(labels, unlabel_data_labels)
    df = DataFrame(x = mat[:,1], y = mat[:,2], class = label)
    println("Computing finished")
    println("Drawing the plot.....Please Wait(Actually Gadfly is quite slow in drawing the first plot)")
    Gadfly.plot(df, x = "x", y = "y", color = "class", Geom.point)
end

function show_example(Mat_Label, labels, Mat_Unlabel, unlabel_data_labels :: Array{Float64,2})
    iter_size = size(unlabel_data_labels,2)
    num_size = size(unlabel_data_labels, 1) + size(labels,1)
    mat = vcat(Mat_Label, Mat_Unlabel)
    mat = repmat(mat,iter_size,1)
    labels = repmat(labels,1, iter_size)
    label = vcat(labels, unlabel_data_labels)
    group = zeros(num_size * iter_size, 1)
    for i = 1:iter_size
        group[((i-1)*num_size+1):num_size*i] = i
    end
    df = DataFrame(x = mat[:,1], y = mat[:,2], iteration = vec(group), class = label[:])
    println("Computing finished")
    println("drawing the plot....Please Wait")
    Gadfly.plot(df, x = "x", y = "y", xgroup = "iteration", color = "class", Geom.subplot_grid(Geom.point))
end

function loadCircleData(num_data)
    center = [5.0, 5.0]
    radiu_inner = 2
    radiu_outer = 4
    num_inner = floor(num_data / 3)
    num_outer = num_data - num_inner

    data = []
    theta = 0.0
    for i in 1:num_inner
        pho = (theta % 360) * pi / 180
        tmp = zeros(1,2)
        tmp[1] = radiu_inner * cos(pho) + rand() + center[1]
        tmp[2] = radiu_inner * sin(pho) + rand() + center[2]
        push!(data,tmp)
        theta += 2
    end

    theta = 0.0
    for i in 1:num_outer
        pho = (theta % 360) * pi / 180
        tmp = zeros(1,2)
        tmp[1] = radiu_outer * cos(pho) + rand() + center[1]
        tmp[2] = radiu_outer * sin(pho) + rand() + center[2]
        push!(data,tmp)
        theta += 1
    end

    Mat_Label = zeros(2, 2)
    Mat_Label[1,:] = center + [ 0.5 - radiu_inner , 0]
    Mat_Label[2,:] = center + [ 0.5 - radiu_outer , 0]
    labels = [1, 2]
    Mat_Unlabel = reduce(vcat,data)
    return Mat_Label, labels, Mat_Unlabel
end

function loadBandData(num_unlabel_samples)
    #Mat_Label = np.array([[5.0, 2.], [5.0, 8.0]])
    #labels = [0, 1]
    #Mat_Unlabel = np.array([[5.1, 2.], [5.0, 8.1]])

    Mat_Label = [5.0 2;5.0 8.0]
    labels = [1, 2]
    num_dim = size(Mat_Label,2)
    Mat_Unlabel = zeros(num_unlabel_samples, num_dim)
    Mat_Unlabel[num_unlabel_samples/2, :] = (rand(num_unlabel_samples/2, num_dim) - 0.5) * np.array([3, 1]) + Mat_Label[0]
    Mat_Unlabel[num_unlabel_samples/2 : num_unlabel_samples, :] = (np.random.rand(num_unlabel_samples/2, num_dim) - 0.5) * np.array([3, 1]) + Mat_Label[1]
    return Mat_Label, labels, Mat_Unlabel
end

function navie_knn(dataSet, query, k)
    numSamples = size(dataSet,1)
    ## step 1: calculate Euclidean distance
    diff = repmat(query,1,numSamples)' - dataSet
    squaredDiff = diff.^2
    squaredDist = vec(sum(squaredDiff, 2))

    sortedDistIndices = sortperm(squaredDist)
    if k > length(sortedDistIndices)
        k = length(sortedDistIndices)
    end
    return sortedDistIndices[1:k]
end

# build a big graph (normalized weight matrix)
function buildGraph(MatX, kernel_type, rbf_sigma = nothing, knn_num_neighbors = nothing)
    num_samples = size(MatX,1)
    affinity_matrix = zeros(num_samples, num_samples)
    if kernel_type == "rbf"
        if rbf_sigma == nothing
          error("You should input a sigma of rbf kernel!")
        end
        for i in 1:num_samples
            row_sum = 0.0
            for j in 1:num_samples
                diff = MatX[i, :] - MatX[j, :]
                affinity_matrix[i,j] = exp(sum(diff.^2) / (-2.0 * rbf_sigma^2))
                row_sum += affinity_matrix[i,j]
            end
            affinity_matrix[i,:] /= row_sum
        end
    elseif kernel_type == "knn"
        if knn_num_neighbors == nothing
          error("You should input a k of knn kernel!")
        end
        for i in 1:num_samples
          k_neighbors = navie_knn(MatX, MatX[i, :], knn_num_neighbors)
          affinity_matrix[i, k_neighbors] = 1.0 / knn_num_neighbors
        end
    else
        erro("Not support kernel type! You can use knn or rbf!")
    end

    return affinity_matrix
end

# label propagation
function label_propagation(Mat_Label, Mat_Unlabel, labels; kernel_type = "rbf", rbf_sigma = 1.5,
                    knn_num_neighbors = 10, max_iter = 500, tol = 1e-3)
    # initialize
    num_label_samples = size(Mat_Label,1)
    num_unlabel_samples = size(Mat_Unlabel,1)
    num_samples = num_label_samples + num_unlabel_samples
    labels_list = unique(labels)
    num_classes = length(labels_list)

    MatX = vcat(Mat_Label, Mat_Unlabel)
    clamp_data_label = zeros(num_label_samples, num_classes)
    if any(labels == 0)
      for i in 1:num_label_samples
        clamp_data_label[i,labels[i]+1] = 1.0
      end
    else
      for i in 1:num_label_samples
        clamp_data_label[i,labels[i]] = 1.0
      end
    end
    label_function = zeros(num_samples, num_classes)
    label_function[1:num_label_samples,:] = clamp_data_label
    label_function[num_label_samples+1:num_samples,:] = -1

    # graph construction
    affinity_matrix = buildGraph(MatX, kernel_type, rbf_sigma, knn_num_neighbors)

    # start to propagation
    iter = 0;
    pre_label_function = zeros(num_samples, num_classes)
    changed = sum(abs(pre_label_function - label_function))
    while iter < max_iter && changed > tol
      ##if iter % 1 == 0
      #  println("---> Iteration $(iter)/$(max_iter), changed: $changed")
      #end
      pre_label_function = label_function
      iter += 1

      # propagation
      label_function = affinity_matrix * label_function

      # clamp
      label_function[1 : num_label_samples, :] = clamp_data_label

      # check converge
      changed = sum(abs(pre_label_function - label_function))
    end
    # get terminate label of unlabeled data
    unlabel_data_labels = zeros(num_unlabel_samples)
    for i in 1:num_unlabel_samples
      unlabel_data_labels[i] = indmax(label_function[i+num_label_samples,:])
    end
    return unlabel_data_labels
  end

# if affinity_matrix is given
function label_propagation(affinity_matrix, labels; kernel_type = "rbf", rbf_sigma = 1.5,
                    knn_num_neighbors = 10, max_iter = 500, tol = 1e-3)
    # initialize
    num_label_samples = size(labels,1)
    num_samples = size(affinity_matrix,1)
    num_unlabel_samples = num_samples - num_label_samples
    labels_list = unique(labels)
    num_classes = length(labels_list)

    clamp_data_label = zeros(num_label_samples, num_classes)
    if any(labels == 0)
      for i in 1:num_label_samples
        clamp_data_label[i,labels[i]+1] = 1.0
      end
    else
      for i in 1:num_label_samples
        clamp_data_label[i,labels[i]] = 1.0
      end
    end
    label_function = zeros(num_samples, num_classes)
    label_function[1:num_label_samples,:] = clamp_data_label
    label_function[num_label_samples+1:num_samples,:] = -1

    # start to propagation
    iter = 0;
    pre_label_function = zeros(num_samples, num_classes)
    changed = sum(abs(pre_label_function - label_function))
    while iter < max_iter && changed > tol
      ##if iter % 1 == 0
      #  println("---> Iteration $(iter)/$(max_iter), changed: $changed")
      #end
      pre_label_function = label_function
      iter += 1

      # propagation
      label_function = affinity_matrix * label_function

      # clamp
      label_function[1 : num_label_samples, :] = clamp_data_label

      # check converge
      changed = sum(abs(pre_label_function - label_function))
    end
    # get terminate label of unlabeled data
    unlabel_data_labels = zeros(num_unlabel_samples)
    for i in 1:num_unlabel_samples
      unlabel_data_labels[i] = indmax(label_function[i+num_label_samples,:])
    end
    return unlabel_data_labels
  end

function test_label_propagation()
  num_unlabel_samples = 800
  Mat_Label, labels, Mat_Unlabel = loadCircleData(num_unlabel_samples)
  iter = round(linspace(1,70,5))
  res = []
  for i in iter
      unlabel_data_labels = label_propagation(Mat_Label, Mat_Unlabel, labels, kernel_type = "knn", knn_num_neighbors = 10, max_iter = i)
      push!(res, unlabel_data_labels)
  end
  res = reduce(hcat, res)
  show_example(Mat_Label, labels, Mat_Unlabel, res)
end


#=
BFSParents:
- Julia version: 1.6.2
- Author: samuel
- Date: 2021-09-10
=#

using SuiteSparseGraphBLAS
using SparseArrays
using LinearAlgebra

#function to insert inside the result array
function insert(p, result)
    for i=1:length(p)
        if(p[i]!=nothing && result[i]==nothing)
            result[i] = p[i]
        end
    end

    return result

end

#function to update visited nodes
function updateVisited(f, visited)
    for i=1:length(f)
        if(f[i]!=nothing)
            visited[i] = true
        end
    end

    return visited

end

#A is the input matrix, s is the source node, and n is the number of nodes, Output: parent matrix
function bfs_parents(A, s, n)

    index = GBVector{Int64}(n)
        for i = 1:n
            index[i] = i
        end

    #wavefront vector -> w[s]=source node insrted in wavefront
    f = GBVector{Int64}(n)
    f[s] = 1

    #parent vector -> p[s]=1 because the source is already visited
    p = GBVector{Int64}(n)

    #result vector -> result[s]=0 because the parent of the source is considered node 0
    result = GBVector{Int64}(n)
    result[s] = 0

    #visited vector -> v[s]=1 because the source is already visited
    visited = GBVector{Bool}(n)
    visited[s] = true

    print("STARTING ...\n\n")
    for i = 1:n-1
            f = mul(f, A, Semirings.MIN_FIRST, mask=p, desc=Descriptors.SC)
            p = f[:, mask=f, desc=Descriptors.S]
            f = index[:, mask=f, desc=Descriptors.S]
            result = insert(p, result)
            visited = updateVisited(f, visited)
            unvisited = filter(x -> x!=true, visited)
            if(isempty(unvisited))
                break
            end
            print("\nITERATION FINISHED\n\n")
            print(result)

    end

    print("COMPLETED\n\n")
    print("RESULT\n\n")
    print(result')

    return result

end