import MittagLefflerFunctions.Quadrature.H2
import MittagLefflerFunctions.Quadrature.H3
using PyPlot

α = 1.7
β = 1.2
sep = 0.02
x = 2.0
M = 200
t = range(-0.2, 0.2, length=M)
θ = π/4

w = x^(1/α) .+ t * exp(Complex(0.0, θ))
y = H2.(α, β, w, x, sep)

figure(1)
H0 = ( 1 + α - 2β ) / ( 2α * x^(β/α) )
plot(t, real.(y), t, imag.(y), [0], [H0], "o")
grid(true)

γ₊ = x^(1/α) * exp(Complex(0.0, π/α))
γ₋ = x^(1/α) * exp(Complex(0.0, -π/α))
w = γ₊ .+ t * exp(Complex(0.0, θ))

y1 = zeros(Complex{Float64}, M)
y2 = zeros(Complex{Float64}, M)
y3 = zeros(Complex{Float64}, M)
for m = 1:M
    H₊, H₋ = H3(α, β, w[m], x, sep)
    y1[m] = H₊
    y2[m] = H₋
    y3[m] = ( ( w[m] - γ₊ ) * ( w[m] - γ₋ )
             / ( ( w[m]^α + x ) * ( 2w[m] - γ₊ - γ₋ ) ) )
end

figure(2)
H0₊ = ( ( ( 1 + α - 2β ) * ( γ₊ - γ₋ ) - 2γ₊ )
      / ( 2α * γ₊^β * ( γ₊ - γ₋ ) ) )
plot(t, real.(y1), t, imag.(y1),
     [0], [real(H0₊)], "o", [0], [imag(H0₊)], "o")
grid(true)

figure(3)
H0₋ = ( sinpi((1-β)/α) / ( α * sinpi(1/α) ) ) / x^(β/α)
plot(t, real.(y2), t, imag.(y2), [0], [H0₋], "o")
grid(true)

figure(4)
ϕ0 = γ₊^(1-α) / α
plot(t, real.(y3), t, imag.(y3),
     [0], [ real(ϕ0) ], "o", [0], [ imag(ϕ0) ], "o")
grid(true)


#############################
##  Numerical experiments associated with section 5.1.1
#############################

using ApproxFun, MultivariateOrthogonalPolynomials, BandedMatrices, BlockBandedMatrices, SpecialFunctions, Plots
using SparseVolterraExamples

#############################
## Problem in Equation (11)
#############################
####
## This function sets up g in the correct basis as well as kernel K.
function solveSec511A(n)
    gf(x) = 1
    gF = Fun(x->gf(x),Jacobi(2,3, 0..1))
    Kfun(x,y) = x-y
        V = triVolterraFullKernelOpP01(Kfun,n,true)
        V = reflectPabtoPba(n)*WLoweringP01P00(n)*V
        V = Conversion(Jacobi(0,0,0..1),Jacobi(2,3,0..1))[1:n,1:n]*V[1:n,1:n]
        V = Derivative(Jacobi(0,1,0..1),2)[1:n,1:n]-V
    coeff = [DirectEvalLHSP10at0(n);DirectEvalLHSP10atPrime0(n)*Derivative(Jacobi(0,1,0..1),1)[1:n,1:n];V[1:n,1:n]] \ [1;0;pad(gF.coefficients,n)]
end

####
## Compute the solution
coeff = solveSec511A(50)
u = Fun(Jacobi(0,1,0..1), coeff)
####
## Plot the computed and analytic solution
plot(u,grid=false,ylabel="u(x)",xlabel="x",label="sparse method",legend=:topleft)
plot!(x->cosh(x),0,1,grid=false,ylabel="u(x)",xlabel="x",label="analytic cosh(x)",legend=:topleft)
####
## Plot the error of the computed compared to analytic solution.
plot(x->abs(cosh(x)-u(x)),0,1,grid=false,ylabel="error",xlabel="x")
####
## Looping over the the degree of polynomials gives a convergence plot like in Figure 1.
function convSec511A(max,stepsize)
    errorvec = []
    for n=1:stepsize:max
        diff = maximum(abs.([maximum(Fun(Jacobi(0,1,0..1),solveSec511A(n))-Fun(x->cosh(x),0..1)),minimum(Fun(Jacobi(0,1,0..1),solveSec511A(n))-Fun(x->cosh(x),0..1))]))
        errorvec = append!(errorvec,diff)
    end
    return errorvec
end
plot(Array(1:1:14),convSec511A(14,1),grid=false,ylabel="error",xlabel="n",yaxis=:log10,label=false)

#############################
## Problem in Equation (12)
#############################
####
## This function sets up g in the correct basis as well as kernel K.
function solveSec511B(n)
    gf(x) = -1+x
    gF = Fun(x->gf(x),Jacobi(4,5, 0..1))
    Kfun(x,y) = y-x
    V = triVolterraFullKernelOpP01(Kfun,n,true)
        V = reflectPabtoPba(n)*WLoweringP01P00(n)*V
        V = Conversion(Jacobi(0,0,0..1),Jacobi(4,5,0..1))[1:n,1:n]*V[1:n,1:n]
        V = Derivative(Jacobi(0,1,0..1),4)[1:n,1:n]-V
    coeff = [DirectEvalLHSP10at0(n);DirectEvalLHSP10atPrime0(n)*Derivative(Jacobi(0,1,0..1),1)[1:n,1:n];DirectEvalLHSP10atPPrime0(n)*Derivative(Jacobi(0,1,0..1),2)[1:n,1:n];DirectEvalLHSP10atPPPrime0(n)*Derivative(Jacobi(0,1,0..1),3)[1:n,1:n];V[1:n,1:n]] \ [-1;1;1;-1;pad(gF.coefficients,n)]
end
####
## Compute the solution
coeff = solveSec511B(15)
u = Fun(Jacobi(0,1,0..1), coeff)
####
## Plot the computed and analytic solution
plot(u,grid=false,ylabel="u(x)",xlabel="x",label="sparse method",legend=:topleft)
plot!(x->sin(x)-cos(x),0,1,grid=false,ylabel="u(x)",xlabel="x",label="analytic sin(x)-cos(x)",legend=:topleft)
####
## Plot the error of the computed compared to analytic solution.
plot(x->abs(sin(x)-cos(x)-u(x)),0,1,grid=false,ylabel="error",xlabel="x")
####
## Looping over the the degree of polynomials gives a convergence plot like in Figure 1.
function convSec511B(max,stepsize)
    errorvec = []
    for n=1:stepsize:max
        diff = maximum(abs.([maximum(Fun(Jacobi(0,1,0..1),solveSec511B(n))-Fun(x->sin(x)-cos(x),0..1)),minimum(Fun(Jacobi(0,1,0..1),solveSec511B(n))-Fun(x->sin(x)-cos(x),0..1))]))
        errorvec = append!(errorvec,diff)
    end
    return errorvec
end
plot(Array(1:4:40),convSec511B(40,4),grid=false,ylabel="error",xlabel="n",yaxis=:log10,label=false)

#############################
## Problem in Equation (13)
#############################
####
## This function sets up g in the correct basis as well as kernel K.
function solveSec511C(n)
    gf(x) = 1+x+(x^2)/2-(x^4)/(4*3*2)
    gF = Fun(x->gf(x),Jacobi(3,4, 0..1))
    Kfun(x,y) = (x-y)^2/2
    V = triVolterraFullKernelOpP01(Kfun,n,true)
        V = reflectPabtoPba(n)*WLoweringP01P00(n)*V
        V = Conversion(Jacobi(0,0,0..1),Jacobi(3,4,0..1))[1:n,1:n]*V[1:n,1:n]
        V = Derivative(Jacobi(0,1,0..1),3)[1:n,1:n]-V
    coeff = [DirectEvalLHSP10at0(n);DirectEvalLHSP10atPrime0(n)*Derivative(Jacobi(0,1,0..1),1)[1:n,1:n];DirectEvalLHSP10atPPrime0(n)*Derivative(Jacobi(0,1,0..1),2)[1:n,1:n];V[1:n,1:n]] \ [1;2;1;pad(gF.coefficients,n)]
end
####
## Compute the solution
coeff = solveSec511C(15)
u = Fun(Jacobi(0,1,0..1), coeff)
####
## Plot the computed and analytic solution
plot(u,grid=false,ylabel="u(x)",xlabel="x",label="sparse method",legend=:topleft)
plot!(x->x+exp(x),0,1,grid=false,ylabel="u(x)",xlabel="x",label="analytic x+exp(x)",legend=:topleft)
####
## Plot the error of the computed compared to analytic solution.
plot(x->abs(x+exp(x)-u(x)),0,1,grid=false,ylabel="error",xlabel="x")
####
## Looping over the the degree of polynomials gives a convergence plot like in Figure 1.
function convSec511C(max,stepsize)
    errorvec = []
    for n=1:stepsize:max
        diff = maximum(abs.([maximum(Fun(Jacobi(0,1,0..1),solveSec511C(n))-Fun(x->x+exp(x),0..1)),minimum(Fun(Jacobi(0,1,0..1),solveSec511C(n))-Fun(x->x+exp(x),0..1))]))
        errorvec = append!(errorvec,diff)
    end
    return errorvec
end
plot(Array(1:1:20),convSec511C(20,1),grid=false,ylabel="error",xlabel="n",yaxis=:log10,label=false)

examples/Section 5-1-1 - Basic convolution kernels, second kind VIDEs.jl


@testset "Testing MNIST Conv" begin
    model = Chain(
        # First convolution, operating upon a 28x28 image
        CrossCor((3, 3), 1=>16, pad=(1,1,1,1), relu),
        MaxPool((2,2)),

        # Second convolution, operating upon a 14x14 image
        CrossCor((3, 3), 16=>32, pad=(1,1,1,1), relu),
        MaxPool((2,2)),

        # Third convolution, operating upon a 7x7 image
        CrossCor((3, 3), 32=>32, pad=(1,1,1,1), relu),
        MaxPool((2,2)),

        # Reshape 3d tensor into a 2d one, at this point it should be (3, 3, 32, N)
        # which is where we get the 288 in the `Dense` layer below:
        x -> reshape(x, :, size(x, 4)),
        Dense(288, 10),

        # Finally, softmax to get nice probabilities
        softmax,
    )

    backend = nGraph.Backend()
    x = rand(Float32, 28, 28, 1, 100)
    expected = model(x)
    f = nGraph.compile(backend, model, x)

    # Test that the forward passes match
    @test isapprox(expected, parent(f()))

    @time model(x)
    @time model(x)
    @time f()
    @time f()
end


function get_logit_ionosphere(;λ=0.001)
    checkdownload_ionosphere()

    rawdata = readdlm(joinpath(instances_dir, "ionosphere.data"), ',')
    A = Matrix{Float64}(rawdata[:, 1:end-1])
    n = size(A, 2)

    y = map(x-> x=="g" ? 1.0 : -1.0, rawdata[:, end])

    return LogisticPb(A, y, regularizer_l1(λ), n, zeros(1), l1Manifold(zeros(1)), 0.0)
end

function get_logit_gisette(λ = 6.67e-4)
    sampledim = 5000

    if isfile(joinpath(instances_dir, "gisette_scale.jld"))
        d = load(joinpath(instances_dir, "gisette_scale.jld"))
        A = d["A"]
        y = d["y"]
    else
        println("Reading ", joinpath(instances_dir, "gisette_scale"))
        rawdata = readdlm(joinpath(instances_dir, "gisette_scale"), ' ')

        println("Assembling problem matrix, vector.")
        nsamples = size(rawdata, 1)

        A = zeros(Float64, nsamples, sampledim)
        for i in 1:size(rawdata, 1), j in 2:size(rawdata, 2)
            s = rawdata[i, j]
            s == "" && continue

            indcol = parse.(Int, split(s, ":")[1])
            val = parse.(Float64, split(s, ":")[2])
            A[i, indcol] = val
        end

        y = Vector{Float64}(rawdata[:, 1])

        println("")
        save(joinpath(instances_dir, "gisette_scale.jld"), "A", A, "y", y)
    end

    return LogisticPb(A, y, regularizer_l1(λ), sampledim, zeros(1), l1Manifold(zeros(1)), 0.0)
end

function get_random_logit(;n=80, m=85, sparsity=0.5, λ=0.1, seed=1234)

    Random.seed!(seed)
    A = rand(m, n)*10
    Random.seed!(seed+3)
    y = Vector([rand() > sparsity ? 1.0 : -1.0 for i in 1:m])

    return LogisticPb(A, y, regularizer_l1(λ), n, zeros(1), l1Manifold(zeros(1)), 0.0)
end

function get_logit_MLE(;n=20, m=15, sparsity=0.5, seed=1234, λ=0.01, diff=0.5)
    @assert 0 ≤ sparsity ≤ 1

    Random.seed!(seed)
    A = rand(Normal(), m, n)

    Random.seed!(seed+1)
    x0 = rand(Normal(), n)
    Random.seed!(seed+2)
    optstructure = l1Manifold(rand(Bernoulli(1-sparsity), n))
    x0 = project(optstructure, x0)

    y = zeros(m)
    for i in 1:m
        Random.seed!(seed+i)
        if rand(Bernoulli(diff + (1-diff)*σ(dot(A[i, :], x0))))
            y[i] = 1.0
        else
            y[i] = -1.0
        end
    end

    return LogisticPb(A, y, regularizer_l1(λ), n, x0, optstructure, 0.0)
end

function get_logit_MLE_elastic_net(;n=20, m=15, sparsity=0.5, seed=1234, λ=0.01, lambda2=0.02, diff=0.5)
    @assert 0 ≤ sparsity ≤ 1

    Random.seed!(seed)
    A = rand(Normal(), m, n)

    Random.seed!(seed+1)
    x0 = rand(Normal(), n)
    Random.seed!(seed+2)
    optstructure = l1Manifold(rand(Bernoulli(1-sparsity), n))
    x0 = project(optstructure, x0)

    y = zeros(m)
    for i in 1:m
        Random.seed!(seed+i)
        if rand(Bernoulli(diff + (1-diff)*σ(dot(A[i, :], x0))))
            y[i] = 1.0
        else
            y[i] = -1.0
        end
    end

    return LogisticPb(A, y, regularizer_l1(λ), n, x0, optstructure, lambda2)
end


import TensorDecompositions
import NTFk

csize = (2, 3, 4)
tsize = (5, 10, 15)
tucker_orig = NTFk.rand_tucker(csize, tsize; factors_nonneg=true, core_nonneg=true)

T_orig = TensorDecompositions.compose(tucker_orig)
T_orig .*= 1000

sizes = [csize, (1,3,4), (3,3,4), (2,2,4), (2,4,4), (2,3,3), (2,3,5)]
tucker_estimated, csize_estimated = NTFk.analysis(T_orig, sizes, 3; eigmethod=[false,false,false], progressbar=false, tol=1e-16, max_iter=100000, lambda=0.);


using CompScienceMeshes
using BEAST
using StaticArrays
using LinearAlgebra
using IterativeSolvers

c = 3e8
μ = 4*π*1e-7
ε = 1/(μ*c^2)
f = 1e8
λ = c/f
k = 2*π/λ
ω = k*c
η = sqrt(μ/ε)

a = 1
Γ_orig = CompScienceMeshes.meshcuboid(a,a,a,0.05)
Γ = translate(Γ_orig,SVector(-a/2,-a/2,-a/2))

Φ, Θ = [0.0], range(0,stop=π,length=100)
pts = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for ϕ in Φ for θ in Θ]

# This is an electric dipole
# The pre-factor (1/ε) is used to resemble
# (9.18) in Jackson's Classical Electrodynamics
E = (1/ε) * dipolemw3d(location=SVector(0.4,0.2,0),
                    orientation=1e-9.*SVector(0.5,0.5,0),
                    wavenumber=k)

n = BEAST.NormalVector()

𝒆 = (n × E) × n
H = (-1/(im*μ*ω))*curl(E)
𝒉 = (n × H) × n

𝓣 = Maxwell3D.singlelayer(wavenumber=k)
𝓝 = BEAST.NCross()
𝓚 = Maxwell3D.doublelayer(wavenumber=k)

X = raviartthomas(Γ)

println("Number of RWG functions: ", numfunctions(X))

T = hassemble(
    𝓣,
    X,
    X,
    treeoptions = BoxTreeOptions(nmin=100),
    threading=:multi,
    quadstrat=BEAST.DoubleNumQStrat(1,1),
    verbose=true,
    svdrecompress=true
)

e = assemble(𝒆,X)
##
println("Enter iterative solver")
@time j_EFIE, ch = IterativeSolvers.gmres(T, e, log=true, reltol=1e-4, maxiter=500)
println("Finished iterative solver part. Number of iterations: ", ch.iters)

nf_E_EFIE = potential(MWSingleLayerField3D(wavenumber=k), pts, j_EFIE, X)
nf_H_EFIE = potential(BEAST.MWDoubleLayerField3D(wavenumber=k), pts, j_EFIE, X) ./ η
ff_E_EFIE = potential(MWFarField3D(wavenumber=k), pts, j_EFIE, X)

@test norm(nf_E_EFIE - E.(pts))/norm(E.(pts)) ≈ 0 atol=0.01
@test norm(nf_H_EFIE - H.(pts))/norm(H.(pts)) ≈ 0 atol=0.01
@test norm(ff_E_EFIE - E.(pts, isfarfield=true))/norm(E.(pts, isfarfield=true)) ≈ 0 atol=0.01


function testFourierTransform()
    M = 4
    N = 5

    grid = Grid2d(M,N)
    ft   = FourierTransform(grid; ℳcut=2, 𝒩cut=2)

    println(ft)
end

function testRealFourierTransforms()
    M  = 32
    N  = 32

    grid = Grid2d(M,N)
    u    = get_field(grid)
    ufft = get_field(grid)
    ft   = FourierTransform(grid; ℳcut=4, 𝒩cut=4)
    û    = get_trans(ft)

    evaluate_function_on_grid(grid, cos_test, u)

    prfft!(ft, u, û)
    irfft!(ft, û, ufft)

    @test maximum(u - ufft) ≈ zero(eltype(u)) atol=1E-15

    frfft!(ft, u, û)
    irfft!(ft, û, ufft)

    @test maximum(u - ufft) ≈ zero(eltype(u)) atol=1E-15

end

testFourierTransform()
testRealFourierTransforms()


using PhaseRetrieval
using FFTW
using Distances
using Test

@testset "PhaseRetrieval.jl" begin
    @test begin # see Gerchberg's original paper
        c1 = [Int(abs(2t)<=1)*exp(im*15*pi*t^2) for t in -1:(1/256):(255/256)]
        c2 = fft(c1)
        r1, r2 = gerchbergsaxton(abs.(c1), abs.(c2), maxiter=130, init=c1)
        msd(c1, r1) < 0.01 && msd(c2, r2) < 0.01
    end
end


using LinearAlgebra
using PyPlot
using PolynomialBasis
using ImplicitDomainQuadrature
using Revise
using CutCellDG
include("../../../../test/useful_routines.jl")
include("transformation-elasticity-solver.jl")

function angular_position(points)
    cpoints = points[1, :] + im * points[2, :]
    return rad2deg.(angle.(cpoints))
end

K1, K2 = 247.0, 192.0    # Pa
mu1, mu2 = 126.0, 87.0   # Pa
lambda1 = lame_lambda(K1, mu1)
lambda2 = lame_lambda(K2, mu2)
stiffness = CutCellDG.HookeStiffness(lambda1, mu1, lambda2, mu2)

theta0 = -0.067
p0 = -0.0
stiffness = CutCellDG.HookeStiffness(lambda1, mu1, lambda2, mu2)
transfstress =
    CutCellDG.plane_strain_transformation_stress(lambda1, mu1, theta0)

meshwidth = [1.0, 1.0]
delta = 1e-2meshwidth[1]
nelmts = 3
polyorder = 2
numqp = required_quadrature_order(polyorder) + 2
penaltyfactor = 1e2

folderpath = "examples/traction-continuity-with-transformation/cylindrical-particle/cylindrical-bc/"
filename = "unequal-moduli-pure-transformation"

dx = minimum(meshwidth) / nelmts
penalty = penaltyfactor / dx * 0.5 * (lambda1 + mu1 + lambda2 + mu2)

basis = TensorProductBasis(2, polyorder)
interfacecenter = [0.5, 0.5]
interfaceradius = dx / sqrt(2) + delta
outerradius = 2.0
analyticalsolution = AnalyticalSolution(
    interfaceradius,
    outerradius,
    interfacecenter,
    lambda1,
    mu1,
    lambda2,
    mu2,
    theta0,
    p0,
)

mesh, cellquads, facequads, interfacequads = construct_mesh_and_quadratures(
    meshwidth,
    nelmts,
    basis,
    interfacecenter,
    interfaceradius,
    numqp
)

nodaldisplacement = nodal_displacement(
    mesh,
    basis,
    cellquads,
    facequads,
    interfacequads,
    stiffness,
    theta0,
    analyticalsolution,
    penalty,
)

L2error = mesh_L2_error(
    reshape(nodaldisplacement, 2, :),
    analyticalsolution,
    basis,
    cellquads,
    mesh,
)
exactsolutionL2error =
    integral_norm_on_mesh(analyticalsolution, cellquads, mesh, 2)
normalizedL2error = L2error ./ exactsolutionL2error

refseedpoints, spatialseedpoints, seedcellids =
    CutCellDG.seed_zero_levelset_with_interfacequads(interfacequads, mesh)
# checkcellids = [8,9]
# refseedpoints, spatialseedpoints, seedcellids =
#     CutCellDG.seed_zero_levelset_with_interfacequads(
#         interfacequads,
#         mesh,
#         checkcellids,
#     )

normals = CutCellDG.collect_interface_normals(interfacequads, mesh)
# normals = CutCellDG.collect_interface_normals(interfacequads,mesh,checkcellids)

spatialpoints = spatialseedpoints[1, :, :]
referencepoints = refseedpoints
referencecellids = seedcellids

relspatialpoints = spatialpoints .- interfacecenter
angularposition = angular_position(relspatialpoints)
sortidx = sortperm(angularposition)
angularposition = angularposition[sortidx]

referencepoints = referencepoints[:, :, sortidx]
referencecellids = referencecellids[:, sortidx]
spatialpoints = spatialpoints[:, sortidx]
normals = normals[:, sortidx]

productdisplacement = CutCellDG.displacement_at_reference_points(
    nodaldisplacement,
    basis,
    referencepoints,
    referencecellids,
    +1,
    mesh,
)
parentdisplacement = CutCellDG.displacement_at_reference_points(
    nodaldisplacement,
    basis,
    referencepoints,
    referencecellids,
    -1,
    mesh,
)
exactdisplacement = mapslices(analyticalsolution, spatialpoints, dims = 1)

fig,ax = PyPlot.subplots(2,1)
ax[1].plot(angularposition,productdisplacement[1,:],label="product u1")
ax[1].plot(angularposition,parentdisplacement[1,:],label="parent u1")
ax[1].plot(angularposition,exactdisplacement[1,:],"--",label="exact")
ax[2].plot(angularposition,productdisplacement[2,:],label="product u1")
ax[2].plot(angularposition,parentdisplacement[2,:],label="parent u1")
ax[2].plot(angularposition,exactdisplacement[2,:],"--",label="exact")
ax[1].grid()
ax[2].grid()
ax[1].legend()
ax[2].legend()
fig
#
# productdisplacementmagnitude =
#     vec(mapslices(norm, productdisplacement, dims = 1))
# parentdisplacementmagnitude = vec(mapslices(norm, parentdisplacement, dims = 1))
# exactdisplacementmagnitude = vec(mapslices(norm, exactdisplacement, dims = 1))
#
# parentdisplacementerror =
#     abs.(parentdisplacementmagnitude - exactdisplacementmagnitude) ./
#     exactdisplacementmagnitude
# productdisplacementerror =
#     abs.(productdisplacementmagnitude - exactdisplacementmagnitude) ./
#     exactdisplacementmagnitude
#
# fig, ax = PyPlot.subplots()
# ax.plot(angularposition, parentdisplacementerror, label = "parent")
# ax.plot(angularposition, productdisplacementerror, label = "product")
# ax.legend()
# ax.grid()
# ax.set_ylim(0,0.15)
# ax.set_xlim(0,90)
# ax.set_title("DG + Merging: Displacement Error")
# fig
# # fig.savefig(folderpath*filename*"-displacement-error.png")
#
#
# productstress = product_stress_at_reference_points(
#     nodaldisplacement,
#     basis,
#     stiffness,
#     transfstress,
#     theta0,
#     referencepoints,
#     referencecellids,
#     mesh,
# )
# parentstress = parent_stress_at_reference_points(
#     nodaldisplacement,
#     basis,
#     stiffness,
#     referencepoints,
#     referencecellids,
#     mesh,
# )
# exactstress =
#     mapslices(x -> exact_stress(analyticalsolution, x), spatialpoints, dims = 1)
#
# producttraction = CutCellDG.traction_force_at_points(productstress, normals)
# parenttraction = CutCellDG.traction_force_at_points(parentstress, normals)
# exacttraction = CutCellDG.traction_force_at_points(exactstress, normals)
#
# # fig, ax = PyPlot.subplots(2, 1)
# # ax[1].plot(angularposition, producttraction[1, :], label = "product")
# # ax[1].plot(angularposition, parenttraction[1, :], label = "parent")
# # ax[1].plot(angularposition, exacttraction[1, :], "--", label = "exact")
# # ax[1].set_title("t1")
# # ax[1].legend()
# # ax[1].grid()
# # ax[2].plot(angularposition, producttraction[2, :], label = "product")
# # ax[2].plot(angularposition, parenttraction[2, :], label = "parent")
# # ax[2].plot(angularposition, exacttraction[2, :], "--", label = "exact")
# # ax[2].set_title("t2")
# # ax[2].legend()
# # ax[2].grid()
# # fig.tight_layout()
# # fig
#
#
# producttractionmagnitude = vec(mapslices(norm, producttraction, dims = 1))
# parenttractionmagnitude = vec(mapslices(norm, parenttraction, dims = 1))
# exacttractionmagnitude = vec(mapslices(norm, exacttraction, dims = 1))
#
# producttractionerror =
#     abs.(producttractionmagnitude - exacttractionmagnitude) ./
#     exacttractionmagnitude
# parenttractionerror =
#     abs.(parenttractionmagnitude - exacttractionmagnitude) ./
#     exacttractionmagnitude
#
# fig, ax = PyPlot.subplots()
# ax.plot(angularposition, parenttractionerror, label = "parent")
# ax.plot(angularposition, producttractionerror, label = "product")
# ax.legend()
# ax.grid()
# ax.set_xlim(0,90)
# ax.set_ylim(0,10.0)
# ax.set_title("DG + Merging: Traction Error")
# fig
# # fig.savefig(folderpath*filename*"-traction-error.png")


#Equilibrium Moisture Content
#The National Fire Danger Rating Sysytem: Basic Equations
#Jack D Cohen, John E. Deeming
#GTR PSW-82

function emc(relative_humidity::Float64,temp::Float64)
    if relative_humidity < 10.0
        emc = 0.03229 + 0.281073 * relative_humidity - 0.000578 * temp * relative_humidity
    elseif relative_humidity >= 10.0 && relative_humidity < 50.0
        emc = 2.22749+0.160107 * relative_humidity - 0.014784 * temp
    else
        emc = 21.0606 + 0.005565 * relative_humidity ^ 2 - 0.00035 * relative_humidity * temp - 0.483199 * relative_humidity
    end
end


import Statistics
using Random

include("typedefs.jl")

function get_centroid(neighbourhood, id)::Node
    xdev = 40
    ydev = 40
    mean_x = Statistics.mean([node.cx for node in neighbourhood]) + rand(-xdev:xdev)
    mean_y = Statistics.mean([node.cx for node in neighbourhood]) + rand(-ydev:ydev)
    distances = [hypot(mean_x-node.cx, mean_y-node.cy) for node in neighbourhood]
    return Node(id, "provider", mean_x, mean_y)
end

function clusterise(nodelist::Vector{Node}, k::Int)::Vector{Node}
    return random_radius_clustering(nodelist, k)
end

function random_radius_clustering(nodelist::Vector{Node}, k::Int)::Vector{Node}
    n = length(nodelist)
    m = n - k
    min_r = 5
    max_r = 30
    centroids = []
    last_id = maximum(x -> x.id, nodelist)
    closed_set = copy(nodelist)
    for i in range(1, stop=m)
        open_set = copy(nodelist)
        random_point = open_set[rand(1:m)]
        distances = [hypot(random_point.cx-node.cx, random_point.cy-node.cy) for node in open_set]
        neighbours = []
        eligible_nodes = [node for (i, node) in enumerate(open_set) if distances[i] >= min_r && distances[i] <= max_r]
        eligible_distances = [distances[i] for (i, node) in enumerate(open_set) if distances[i] >= min_r && distances[i] <= max_r]
        # for j in range(1, stop=k)
        #     min_dist_ind = argmin(eligible_distances)
        #     min_dist_node = popat!(eligible_nodes, min_dist_ind)
        #     push!(neighbours, min_dist_node)
        # end
        push!(centroids, get_centroid(eligible_nodes, last_id))
        last_id += 1
    end

    append!(closed_set, centroids)
    return closed_set
end


using Symbolics

function symbolic_kinetic_energy(
    u,
    w,
    θ′,
    t;
    θ_t = θ_t,
    θ_k = θ_k,
    γ_OA = γ_OA,
)

    D = Differential(t)
    T = kinetic_energy(u, w, θ′, θ_t, D(θ_t), θ_k, D(θ_k), γ_OA, D(γ_OA))

    Du = Differential(u)
    Dw = Differential(w)
    Dθ′ = Differential(θ′)

    dT_du = Du(T)
    dT_dw = Dw(T)
    dT_dθ′ = -simplify(expand_derivatives(Dθ′(T)))

    # LHS of the equations
    L1 = D(dT_du) - dT_dw * θ′
    L2 = D(dT_dw) + dT_du * θ′
    L3 = D(dT_dθ′) + dT_du * w - dT_dw * u

    [L1, L2, L3]
end


using DataFrames
using MixedModels
using Test

# convert to DataFrame to modify it
slp = DataFrame(MixedModels.dataset(:sleepstudy))
allowmissing!(slp, :days)
slp[1,:days] = missing

# TODO: re-enable this test when better missing support has landed in StatsModels
# @testset "No impact from missing on schema" begin
#     f = @formula(Y ~ 1 + U + (1|G))
#     contrasts =  Dict{Symbol,Any}()
#     form = apply_schema(f, schema(f, dat[:sleepstudy], contrasts), LinearMixedModel)
#     form_missing = apply_schema(f, schema(f, slp, contrasts), LinearMixedModel)
#
#     @test form.lhs == form_missing.lhs
#     @test form.rhs == form_missing.rhs
# end

@testset "Missing Omit" begin
    @testset "Missing from unused variables" begin
        # missing from unused variables should have no impact
        m1 = fit(MixedModel, @formula(reaction ~ 1 + (1|subj)), MixedModels.dataset(:sleepstudy), progress=false)
        m1_missing = fit(MixedModel, @formula(reaction ~ 1 + (1|subj)), slp, progress=false)
        @test isapprox(m1.θ, m1_missing.θ, rtol=1.0e-12)
    end

    @testset "Missing from used variables" begin
        m1 = fit(MixedModel, @formula(reaction ~ 1 + days + (1|subj)), MixedModels.dataset(:sleepstudy), progress=false)
        m1_missing = fit(MixedModel, @formula(reaction ~ 1 + days + (1|subj)), slp, progress=false)
        @test nobs(m1) - nobs(m1_missing) == 1
    end
end


function backproject_transform(pixels, K_inv, R, t)
    points = Vector{SVector{3, Float64}}(undef, length(pixels))
    for (i, px) in enumerate(pixels)
        p = K_inv * SVector{3, Float64}(px..., 1)
        points[i] = R * p + t
    end
    points
end

@testset "P3P" begin
    n_points = 100

    fcmin, fcmax = 1e-3, 100
    K = SMatrix{3, 3, Float64}(
        rand() * (fcmax - fcmin) + fcmin, 0, 0,
        0, rand() * (fcmax - fcmin) + fcmin, 0,
        rand() * (fcmax - fcmin) + fcmin, rand() * (fcmax - fcmin) + fcmin, 1,
    )
    K_inv = K |> inv

    θ = π / 4
    R = RotXYZ(rand() * θ, rand() * θ, rand() * θ)
    t = SVector{3, Float64}(rand(), rand() * 2, rand() * 3)
    P_target = RecoverPose.get_transformation(R', -R' * t)

    max_res = 2 * min(K[1, 3], K[2, 3])
    pmin = SVector{2}(1, 1)
    pmax = SVector{2}(max_res, max_res)
    δ = pmax - pmin

    pixels = [floor.(rand(SVector{2, Float64}) .* δ .+ pmin) for i in 1:n_points]
    pdn_pixels = pre_divide_normalize(pixels, K)
    points = backproject_transform(pixels, K_inv, R, t)

    models = p3p(points[1:3], pdn_pixels[1:3], K)
    n_inliers, (KP, inliers, error) = p3p_select_model(models, points, pixels; threshold=0.1)
    @test n_inliers == n_points
    @test sum(inliers) == n_inliers
    @test all(isapprox.(K_inv * KP, P_target; atol=1e-2))

    models = p3p(points[1:3], pixels[1:3], K)
    n_inliers, (KP, inliers, error) = p3p_select_model(models, points, pixels)
    @test n_inliers == n_points
    @test sum(inliers) == n_inliers
    @test all(isapprox.(K_inv * KP, P_target; atol=1e-2))

    # Test P3P RANSAC.

    n_inliers, (KP, inliers, error) = p3p_ransac(points, pixels, pdn_pixels, K)
    @test n_inliers == n_points
    @test sum(inliers) == n_inliers
    @test all(isapprox.(K_inv * KP, P_target; atol=1e-2))

    n_inliers, (KP, inliers, error) = p3p_ransac(points, pixels, K)
    @test n_inliers == n_points
    @test sum(inliers) == n_inliers
    @test all(isapprox.(K_inv * KP, P_target; atol=1e-2))
end

@testset "P3P RANSAC with noise and outliers." begin
    n_points = 100
    noise_scale = 1e-3
    rot_atol = 0.5
    t_atol = 2

    fcmin, fcmax = 1e-3, 100
    K = SMatrix{3, 3, Float64}(
        rand() * (fcmax - fcmin) + fcmin, 0, 0,
        0, rand() * (fcmax - fcmin) + fcmin, 0,
        rand() * (fcmax - fcmin) + fcmin, rand() * (fcmax - fcmin) + fcmin, 1,
    )
    K_inv = K |> inv

    θ = π / 8
    R = RotXYZ(rand() * θ, rand() * θ, rand() * θ)
    t = SVector{3, Float64}(rand(), rand() * 2, rand() * 3)
    P_target = RecoverPose.get_transformation(R', -R' * t)

    max_res = 2 * min(K[1, 3], K[2, 3])
    pmin = SVector{2}(1, 1)
    pmax = SVector{2}(max_res, max_res)
    δ = pmax - pmin

    pixels = [floor.(rand(SVector{2, Float64}) .* δ .+ pmin) for i in 1:n_points]
    pdn_pixels = pre_divide_normalize(pixels, K)
    points = backproject_transform(pixels, K_inv, R, t)
    points = [p .+ rand(SVector{3, Float64}) .* noise_scale for p in points]

    # Add outliers.
    outlier_scale = 5
    points[1] = points[1] .+ rand() * outlier_scale
    points[5] = points[5] .+ rand() * outlier_scale
    points[end] = points[end] .+ rand() * outlier_scale
    points[end - 5] = points[end - 5] .+ rand() * outlier_scale
    points[n_points ÷ 2] = points[n_points ÷ 2] .+ rand() * outlier_scale

    n_inliers, (KP, inliers, error) = p3p_ransac(points, pixels, pdn_pixels, K)
    P = K_inv * KP
    @test n_inliers ≥ n_points - 10
    @test !inliers[1] && !inliers[5] && !inliers[end] && !inliers[end - 5] &&
        !inliers[n_points ÷ 2]

    @test all(isapprox.(P[1:3, 1:3], P_target[1:3, 1:3]; atol=rot_atol))
    @test all(isapprox.(P[1:3, 4], P_target[1:3, 4]; atol=t_atol))

    n_inliers, (KP, inliers, error) = p3p_ransac(points, pixels, K)
    P = K_inv * KP
    @test n_inliers ≥ n_points - 10
    @test !inliers[1] && !inliers[5] && !inliers[end] && !inliers[end - 5] &&
        !inliers[n_points ÷ 2]

    @test all(isapprox.(P[1:3, 1:3], P_target[1:3, 1:3]; atol=rot_atol))
    @test all(isapprox.(P[1:3, 4], P_target[1:3, 4]; atol=t_atol))
end


"""
constraints.jl
"""

using Altro
using LinearAlgebra

@inline bound_constraint_eval(x, u, x_max, x_min, u_max, u_min) = ([
    x - x_max;
    x_min - x;
    u - u_max;
    u_min - u;
])

function bound_constraint_jacobian(x, u, x_max, x_min, u_max, u_min)
    n = length(x)
    m = length(u)
    p = 2 * n + 2 * m
    Cx = zeros(p, n)
    for i = 1:n
        Cx[i, i] = 1.
        Cx[i + n, i] = -1.
    end
    Cu = zeros(p, m)
    for i = 1:m
        Cu[i + 2 * n, i] = 1.
        Cu[i + 2 * n + m, i] = -1.
    end
    return Cx, Cu
end

function test_bound_constraint()
    big = 1
    big_ = 1000
    n = 2
    m = 2
    M(x) = x
    V(x) = x
    for i = 1:big
        x_max = rand(n)
        x_min = -rand(n)
        u_max = rand(m)
        u_min = -rand(m)
        bc = BoundConstraint(x_max, x_min, u_max, u_min, n, m, M, V)
        p = 2 * n + 2 * m
        c = zeros(p)
        Cx = zeros(p, n)
        Cu = zeros(p, m)
        for j = 1:big_
            x = 2 .* rand(n) .- 1
            X = [x]
            u = 2 .* rand(m) .- 1
            U = [u]
            k = 1
            Altro.evaluate!(c, bc, X, U, k)
            c_ = bound_constraint_eval(x, u, x_max, x_min, u_max, u_min)
            @assert c ≈ c_
            Altro.jacobian!(Cx, Cu, bc, X, U, k)
            Cx_, Cu_ = bound_constraint_jacobian(x, u, x_max, x_min, u_max, u_min)
            @assert Cx ≈ Cx_
            @assert Cu ≈ Cu_
        end
    end
end


######################################################################
# costs.jl
#
######################################################################

abstract type CostModel end

function get_cost(cm::CostModel, m::SearchDomain, x::Vehicle, f::AbstractFilter, a)
    error("get_cost not implemented for this cost model type.")
end
function get_cost(cm::CostModel, m::SearchDomain, x::Vehicle, f::AbstractFilter)
    error("get_cost not implemented for this cost model type.")
end

struct ConstantCost <: CostModel
    value::Float64
end
function get_cost(cc::ConstantCost,
                  m::SearchDomain,
                  x::Vehicle,
                  f::AbstractFilter,
                  a
                 )
    return cc.value
end
get_cost(cc::ConstantCost, m::SearchDomain, x::Vehicle, f::AbstractFilter) = cc.value

struct MoveCost <: CostModel
    speed::Float64

    MoveCost(speed::Real) = new(float(speed))
    MoveCost() = new(1.0)
end
function get_cost(mc::MoveCost, x::Vehicle, f::AbstractFilter, a)
    dx = a[1]
    dy = a[2]
    dist = sqrt(dx*dx + dy*dy)
    return (dist / mc.speed)
end

struct MoveCost2 <: CostModel
    speed::Float64

    MoveCost2(speed::Real) = new(float(speed))
    MoveCost2() = new(1.0)
end

# TODO: should I incorporate speed somehow?
function get_cost(mc::MoveCost2, x::Vehicle, f::AbstractFilter, a)
    dx = a[1]
    dy = a[2]
    return dx*dx + dy*dy
end

# Move and rotate
struct MoveAndRotateCost <: CostModel
    speed::Float64
    time_per_rotation::Float64
end
function get_cost(marc::MoveAndRotateCost, x::Vehicle, f::AbstractFilter, a)
    dx = a[1]
    dy = a[2]
    dist = sqrt(dx*dx + dy*dy)
    return (dist / marc.speed) + marc.time_per_rotation
end
get_cost(marc::MoveAndRotateCost, f::AbstractFilter) = marc.time_per_rotation

export EntropyCost
struct EntropyCost <: CostModel
    L::Float64
    n_cells::Int
end

function get_cost(ec::EntropyCost, m::SearchDomain, x::Vehicle, f::PF, a)
    get_cost(ec, m, x, f)
end
function get_cost(ec::EntropyCost, m::SearchDomain, x::Vehicle, f::PF)
    cheap_entropy2(f, ec.L, ec.n_cells)
    #c = covariance(f)
    #earr = eigvals(c)
    #return maximum(eigvals(c))
end

export MaxEigCost
struct MaxEigCost <: CostModel
    take_sqrt::Bool
end
MaxEigCost() = MaxEigCost(true)

function get_cost(mec::MaxEigCost, m::SearchDomain, x::Vehicle, f::PF, a)
    get_cost(mec, m, x, f)
end
function get_cost(mec::MaxEigCost, m::SearchDomain, x::Vehicle, f::PF)
    me = maximum(eigvals(covariance(f)))
    if mec.take_sqrt
        me = sqrt(me)
    end
    return me
end


"This module contains all inner parts."
module Internal

export Problem, Options, solve

using LinearAlgebra
using UnPack

"""
```
Problem(
    aᵢⱼ::Function
)
```

A function `aᵢⱼ(i, j)` for generating a symmetric matrix (upper triangle).
"""
struct Problem
    aᵢⱼ::Function
end

"""
`Options(n::Integer)`

Options to provide to the solver.

Includes:
- order `n` of the matrix;
- maximum number of iterations `iterₘₐₓ` for the Jacobi eigenvalue algorithm;
- error `ϵₚ` for power methods;
- error `ϵⱼ` for the Jacobi eigenvalue algorithm;
- function `get_x₀(A)` to get a vector of initial values.
"""
struct Options
    n::Integer
    iterₘₐₓ::Integer
    ϵₚ::Float64
    ϵⱼ::Float64
    get_x₀::Function
end

"""
The default set of options.

Sets:
- `n` to `10`;
- `iterₘₐₓ` to `1000000`;
- `ϵₚ` to `1e-16`;
- `ϵⱼ` to `1e-9`;
- `get_x₀(A)` to `(A) -> ones(size(A, 1))`.
"""
DefaultOptions = Options(10, 1000000, 1e-16, 1e-9, (A) -> ones(size(A, 1)))

include("Functions/solve.jl")

end


using IncrementalInference
using Test

##

@testset "test Circular" begin

##

fg = initfg()
getSolverParams(fg).useMsgLikelihoods = true

addVariable!.(fg, [Symbol("x$i") for i=0:4], Circular)
addFactor!(fg, [:x0], PriorCircular(Normal(0.0,0.1)))
map(i->addFactor!(fg, [Symbol("x$i"),Symbol("x$(i+1)")], CircularCircular(Normal(1.0, 0.1))), 0:3)

solveTree!(fg);

##

sppes = map(var->getPPE(var).suggested[1], sortDFG(getVariables(fg),by=getLabel))

gt = rem2pi.(collect(0:4), RoundNearest)

@show sppes
@show gt
@test all(isapprox.(sppes, gt, atol=0.35))

# test packing converters also
d = "/tmp/caesar/random/testfg"
saveDFG(d,fg)
lfg = loadDFG(d)
##
Base.rm(d*".tar.gz")
# check loaded fg for all variable and factors
@test issetequal(ls(fg), ls(lfg))
@test issetequal(lsf(fg), lsf(lfg))

##

end

@testset "test canonical helix generator utility" begin
##

tmp = calcHelix_T(0, 3, 25, radius=5, xr_t=t->(1/3)*t)

##
end

#


"""
Struct for European Option

		euOption=EuropeanOptionND(T::num1,K::num2,isCall::Bool=true) where {num1 <: Number,num2 <: Number}

Where:\n
		T	=	Time to maturity of the Option.
		K	=	Strike Price of the Option.
		isCall  = true for CALL, false for PUT.
"""
mutable struct EuropeanOptionND{num1 <: Number, num2 <: Number, numtype <: Number} <: EuropeanBasketPayoff{numtype}
    T::num1
    K::num2
    isCall::Bool
    function EuropeanOptionND(T::num1, K::num2, isCall::Bool = true) where {num1 <: Number, num2 <: Number}
        if T <= 0.0
            error("Time to Maturity must be positive")
        elseif K <= 0.0
            error("Strike Price must be positive")
        else
            zero_typed = zero(num1) + zero(num2)
            return new{num1, num2, typeof(zero_typed)}(T, K, isCall)
        end
    end
end

export EuropeanOptionND;

function payoff(S::Array{abstractMatrix}, euPayoff::EuropeanOptionND, rfCurve::abstractZeroRateCurve, T1::num2 = maturity(euPayoff)) where {abstractMatrix <: AbstractMatrix{num}, num2 <: Number} where {abstractZeroRateCurve <: AbstractZeroRateCurve, num <: Number}
    r = rfCurve.r
    T = euPayoff.T
    iscall = euPayoff.isCall ? 1 : -1
    (Nsim, NStep) = size(S[1])
    NStep -= 1
    index1 = round(Int, T / T1 * NStep) + 1
    K = euPayoff.K
    #ST_all=[ sum(x_i[j,index1] for x_i in S) for j in 1:Nsim]
    ST_all = S[1][:, index1]
    for i = 2:length(S)
        ST_all += S[i][:, index1]
    end
    payoff2 = max.(iscall * (ST_all .- K), 0.0)

    return payoff2 * exp(-integral(r, T))
end


module LatentHeat

# If you want to add a new method here, feel free to do so.
# Remember to also export the function name in GeoParams.jl (in addition to here)

using Parameters, LaTeXStrings, Unitful
using ..Units
using GeoParams: AbstractMaterialParam
import Base.show

abstract type AbstractLatentHeat <: AbstractMaterialParam end

export  ComputeLatentHeat,                  # calculation routines
        ConstantLatentHeat                  # constant

# Constant  -------------------------------------------------------
"""
    ConstantLatentHeat(Q_L=400kJ/kg)

Set a constant latent heat:
```math
    Q_L  = cst
```
where ``Q_L`` is the latent heat [``kJ/kg``].
"""
@with_kw_noshow mutable struct ConstantLatentHeat <: AbstractLatentHeat
    equation::LaTeXString   =   L"Q_L = cst"
    Q_L::GeoUnit             =   400kJ/kg                # Latent heat
end

# Calculation routine
function ComputeLatentHeat(s::ConstantLatentHeat)
    @unpack Q_L   = s

    return Value(Q_L)
end

# Print info
function show(io::IO, g::ConstantLatentHeat)
    print(io, "Constant latent heat: Q_L=$(g.Q_L.val)")
end
#-------------------------------------------------------------------------

# Help info for the calculation routines
"""
    Ql = ComputeLatentHeat(s:<AbstractLatentHeat)

Returns the latent heat `Q_L`

"""
ComputeLatentHeat()

end


function _leastsquare(ydata::Vector{T}, ymodel::Vector{T}, weights::Vector{T})::T where {T <: AbstractFloat}
    ls::T = 0
    @inbounds for i in eachindex(ydata)
        ls += (ydata[i] - ymodel[i])^2 * weights[i]
    end
    return ls
end

function lsqfit!(fit::FitFunction{T, 1, NP}, xdata::Vector{T}, ydata::Vector{T}; weights::Vector{T} = ones(T, length(xdata))) where {T <: AbstractFloat, NP}
    function lsq(params::Vector{T})::T
        ymodel::Vector{T} = fit.model(xdata, params)::Vector{T}
        ls::T = _leastsquare( ydata, ymodel, weights )
        return ls
    end
    optim_result = try
        optimize( lsq, fit.initial_parameters)
    catch err
        optimize( lsq, fit.initial_parameters, BFGS())
    end
    _set_fitted_parameters!(fit, optim_result.minimizer)
end

function lsqfit!(fit::FitFunction{T, 1, NP}, xdata::Vector{T}, ydata::Vector{T}, xerr::Vector{T}, yerr::Vector{T}) where {T <: AbstractFloat, NP}
    weights::Vector{T} = @. 1 / sqrt(xerr^2 + yerr^2)
    return lsqfit!(fit, xdata, ydata, weights = weights)
end

function lsqfit!(fit::FitFunction{T, 1, NP}, xdata::Vector{T}, ydata::Vector{T}, err::Vector{T}) where {T <: AbstractFloat, NP}
    weights::Vector{T} = inv.(err)
    return lsqfit!(fit, xdata, ydata, weights = weights)
end

"""
    lsqfit!(fit::FitFunction{T, 1, NP}, h::Histogram)::Nothing where {T <: AbstractFloat, NP}

Performs a least square fit with the model `fit.model` and the initial parameters `fit.initial_parameters`
on the histogram `h` in the range `fit.fitranges[1]`. The determined parameters are stored in `fit.fitted_parameters`.
"""
function lsqfit!(fit::FitFunction{T, 1, NP}, h::Histogram)::Nothing where {T <: AbstractFloat, NP}
    first_bin::Int = StatsBase.binindex(h, first(fit.fitranges[1]))
    last_bin::Int  = StatsBase.binindex(h, last(fit.fitranges[1]))
    if first_bin < 1 first_bin = 1 end
    if (last_bin > length(h.weights)) last_bin = length(h.weights) end
    bin_centers::Vector{T} = StatsBase.midpoints(h.edges[1])[first_bin:last_bin]
    counts::Vector{T} = h.weights[first_bin:last_bin]
    weights::Vector{T} = sqrt.(counts) # Poisson distributed
    weights = [ w != 0 ? w : 1.  for w in weights]

    lsqfit!(fit, bin_centers, counts, weights)
end


function rsoc1_test()
    #=
        min 0a + 0b - 1x - 1y
    s.t.
        a    == 1/2
        b    == 1
        2a*b >= x^2+y^2
    =#
    model = TestModel{Float64}()

    x = MOI.add_variables(model, 2)

    a = MOI.add_variable(model)
    b = MOI.add_variable(model)
    vc1 = MOI.add_constraint(model, MOI.SingleVariable(a), MOI.EqualTo(0.5))
    # We test this after the creation of every `SingleVariable` constraint
    # to ensure a good coverage of corner cases.
    vc2 = MOI.add_constraint(model, MOI.SingleVariable(b), MOI.EqualTo(1.0))
    rsoc = MOI.add_constraint(model, MOI.VectorOfVariables([a; b; x]), MOI.RotatedSecondOrderCone(4))

    MOI.set(model, MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(),
            MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.(-1.0, x), 0.0))
    MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE)

    return model
end

function rsoc2_test()
    #=
        min 0a + 0b - 1x - 1y
    s.t.
        a    == 1/2
        b    == 1
        2a*b >= x^2+y^2
    =#
    model = TestModel{Float64}()

    x = MOI.add_variables(model, 2)
    a = 0.5
    b = 1.0
    rsoc = MOI.add_constraint(model, MOI.VectorAffineFunction(MOI.VectorAffineTerm.([3, 4],
            MOI.ScalarAffineTerm.([1., 1.], x)), [a, b, 0., 0.]), MOI.RotatedSecondOrderCone(4))

    MOI.set(model, MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(),
            MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.(-1.0, x), 0.0))
    MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE)

    return model
end

function rsoc3_test()
    #=
        min x + 1
    s.t.
        x ≤ 1
        y = 1/2
        z ≥ 2
        z^2 ≤ 2x*y
    =#
    model = TestModel{Float64}()

    b = [-2, -1, 1/2]
    c = [1.0,0.0,0.0]

    x = MOI.add_variables(model, 3)

    vc1 = MOI.add_constraint(model, MOI.SingleVariable(x[1]), MOI.LessThan(1.0))
    vc2 = MOI.add_constraint(model, MOI.SingleVariable(x[2]), MOI.EqualTo(0.5))
    vc3 = MOI.add_constraint(model, MOI.SingleVariable(x[3]), MOI.GreaterThan(2.0))

    rsoc = MOI.add_constraint(model, MOI.VectorOfVariables(x), MOI.RotatedSecondOrderCone(3))

    MOI.set(model, MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(), MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.(c, x), 1.0))
    MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE)

    return model
end

function rsoc4_test()
    #=
        max v
    s.t.
        x[1:10] ≥ 0
        0 ≤ u ≤ 3.0
        v
        t1 == 1
        t2 == 1
        [t1/√2, t2/√2, x] in RSOC4
        [x1/√2, u/√2,  v] in RSOC3
    =#
    model = TestModel{Float64}()

    x = MOI.add_variables(model, 2)
    u = MOI.add_variable(model)
    v = MOI.add_variable(model)
    t = MOI.add_variables(model, 2)

    ct1 = MOI.add_constraint(model, MOI.SingleVariable(t[1]), MOI.EqualTo(1.0))
    ct2 = MOI.add_constraint(model, MOI.SingleVariable(t[2]), MOI.EqualTo(1.0))
    cx  = MOI.add_constraint(model, MOI.VectorOfVariables(x), MOI.Nonnegatives(2))
    cu1 = MOI.add_constraint(model, MOI.SingleVariable(u), MOI.GreaterThan(0.0))
    cu2 = MOI.add_constraint(model, MOI.SingleVariable(u), MOI.LessThan(3.0))

    c1 = MOI.add_constraint(model, MOI.VectorAffineFunction(MOI.VectorAffineTerm.(1:(2+2), MOI.ScalarAffineTerm.([1/√2; 1/√2; ones(2)], [t; x])), zeros(2+2)), MOI.RotatedSecondOrderCone(2+2))
    c2 = MOI.add_constraint(model, MOI.VectorAffineFunction(MOI.VectorAffineTerm.([1, 2, 3], MOI.ScalarAffineTerm.([1/√2; 1/√2; 1.0], [x[1], u, v])), zeros(3)), MOI.RotatedSecondOrderCone(3))

    MOI.set(model, MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(), MOI.ScalarAffineFunction([MOI.ScalarAffineTerm(1.0, v)], 0.0))
    MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE)

    return model
end

function rsoc5_test()
    #=
        max v
    s.t.
        x[1:10] ≥ 0
        0 ≤ u ≤ 3.0
        v
        t1 == 1
        t2 == 1
        [t1/√2, t2/√2, x] in RSOC12
        [x1/√2, u/√2,  v] in RSOC3
    =#
    model = TestModel{Float64}()

    x = MOI.add_variables(model, 10)
    u = MOI.add_variable(model)
    v = MOI.add_variable(model)
    t = MOI.add_variables(model, 2)

    ct1 = MOI.add_constraint(model, MOI.SingleVariable(t[1]), MOI.EqualTo(1.0))
    ct2 = MOI.add_constraint(model, MOI.SingleVariable(t[2]), MOI.EqualTo(1.0))
    cx  = MOI.add_constraint(model, MOI.VectorOfVariables(x), MOI.Nonnegatives(10))
    cu1 = MOI.add_constraint(model, MOI.SingleVariable(u), MOI.GreaterThan(0.0))
    cu2 = MOI.add_constraint(model, MOI.SingleVariable(u), MOI.LessThan(3.0))

    c1 = MOI.add_constraint(model, MOI.VectorAffineFunction(MOI.VectorAffineTerm.(1:(2+10), MOI.ScalarAffineTerm.([1/√2; 1/√2; ones(10)], [t; x])), zeros(2+10)), MOI.RotatedSecondOrderCone(2+10))
    c2 = MOI.add_constraint(model, MOI.VectorAffineFunction(MOI.VectorAffineTerm.([1, 2, 3], MOI.ScalarAffineTerm.([1/√2; 1/√2; 1.0], [x[1], u, v])), zeros(3)), MOI.RotatedSecondOrderCone(3))

    MOI.set(model, MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(), MOI.ScalarAffineFunction([MOI.ScalarAffineTerm(1.0, v)], 0.0))
    MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE)

    return model
end


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

@test_throws ErrorException begin
    inv(BiComplex(0, 0.0))
end

@test begin
    iszerodivisor(BiComplex(0,1,1,0))
end

@test begin
    !iszerodivisor(BiComplex(1,2,3,4))
end

@test begin
    a = Plexifications.randomBigFloat()
    isreal(BiComplex(a))
end

@test begin
    a = 1
    !isreal(BiComplex(a, a))
end

@test begin
    z = random(BiComplex{Int})
    z == +(z)
end

@test begin
    a = Plexifications.randomBigFloat()
    real(BiComplex(a)) == a
end

@test begin
    io = IOBuffer()
    show(io, BiComplex(1, 2, 3, 4))
    l = String(take!(io))
    r = "[1: 1, H: 2, G: 3, GH: 4]"
    l == r
end

@test begin
    x = random(BiComplex{BigFloat})
    y = random(BiComplex{BigFloat})
    x != y
end

@test begin
    a = Plexifications.randomBigInt()
    z = random(BiComplex{BigInt})
    l = a + z
    r = z + a
    l == r
end

@test begin
    a = Plexifications.randomBigInt()
    z = random(BiComplex{BigInt})
    l = a - z
    r = -(z - a)
    l == r
end

@test begin
    x = random(BiComplex{BigInt})
    y = random(BiComplex{BigInt})
    l = x + y
    r = y + x
    l == r
end

@test begin
    x = random(Cmplex{BigInt})
    y = random(BiComplex{BigInt})
    l = x + y
    r = y + x
    l == r
end

@test begin
    x = random(BiComplex{BigInt})
    y = random(BiComplex{BigInt})
    l = x * y
    r = y * x
    l == r
end

@test begin
    x = random(Cmplex{BigInt})
    y = random(BiComplex{BigInt})
    l = x * y
    r = y * x
    l == r
end

@test begin
    x = random(BiComplex{BigInt})
    y = random(BiComplex{BigInt})
    l = x - y
    r = -(y - x)
    l == r
end

@test begin
    x = random(Cmplex{BigInt})
    y = random(BiComplex{BigInt})
    l = x - y
    r = -(y - x)
    l == r
end

@test begin
    x = random(BiComplex{BigInt})
    y = random(BiComplex{BigInt})
    z = random(BiComplex{BigInt})
    l = (x + y) + z
    r = x + (y + z)
    l == r
end

@test begin
    x = random(BiComplex{BigInt})
    y = random(BiComplex{BigInt})
    z = random(BiComplex{BigInt})
    l = (x * y) * z
    r = x * (y * z)
    l == r
end

@test begin
    x = random(BiComplex{BigInt})
    barH(barH(x)) == x
end

@test begin
    x = random(BiComplex{BigInt})
    barG(barG(x)) == x
end

@test begin
    x = random(BiComplex{BigInt})
    barG(barH(x)) == barH(barG(x))
end

@test begin
    x = random(BiComplex{Rational{BigInt}})
    inv(inv(x)) == x
end

@test begin
    x = random(BiComplex{BigInt})
    y = random(BiComplex{BigInt})
    l = barH(x * y)
    r = barH(x) * barH(y)
    l == r
end

@test begin
    x = random(BiComplex{BigInt})
    y = random(BiComplex{BigInt})
    l = barH(x + y)
    r = barH(x) + barH(y)
    l == r
end

@test begin
    x = random(BiComplex{BigInt})
    y = random(BiComplex{BigInt})
    l = barH(x - y)
    r = barH(x) - barH(y)
    l == r
end

@test begin
    x = random(BiComplex{BigInt})
    y = random(BiComplex{BigInt})
    l = barG(x * y)
    r = barG(x) * barG(y)
    l == r
end

@test begin
    x = random(BiComplex{BigInt})
    y = random(BiComplex{BigInt})
    l = barG(x + y)
    r = barG(x) + barG(y)
    l == r
end

@test begin
    x = random(BiComplex{BigInt})
    y = random(BiComplex{BigInt})
    l = barG(x - y)
    r = barG(x) - barG(y)
    l == r
end

@test begin
    x = random(BiComplex{Rational{BigInt}})
    y = random(BiComplex{Rational{BigInt}})
    l = inv(x * y)
    r = inv(y) * inv(x)
    l == r
end

@test begin
    x = random(BiComplex{Rational{BigInt}})
    y = random(BiComplex{Rational{BigInt}})
    l = inv(x / y)
    r = y / x
    l == r
end

@test begin
    x = random(BiComplex{Rational{BigInt}})
    y = random(BiComplex{Rational{BigInt}})
    l = inv(x \ y)
    r = y \ x
    l == r
end

@test begin
    x = random(BiComplex{BigInt})
    y = random(BiComplex{BigInt})
    z = random(BiComplex{BigInt})
    l = x * (y + z)
    r = (x * y) + (x * z)
    l == r
end

@test begin
    a = Plexifications.randomBigInt()
    x = random(BiComplex{BigInt})
    y = random(BiComplex{BigInt})
    l = a * (x + y)
    r = (a * x) + (a * y)
    l == r
end

@test begin
    a = Plexifications.randomBigInt()
    x = random(BiComplex{BigInt})
    l = a * x
    r = x * a
    l == r
end

@test begin
    x = random(BiComplex{BigInt})
    y = random(BiComplex{BigInt})
    z = random(BiComplex{BigInt})
    l = (x + y) * z
    r = (x * z) + (y * z)
    l == r
end

@test begin
    x = random(BiComplex{BigInt})
    y = random(BiComplex{BigInt})
    z = random(BiComplex{BigInt})
    l = x * (y - z)
    r = (x * y) - (x * z)
    l == r
end

@test begin
    x = random(BiComplex{BigInt})
    y = random(BiComplex{BigInt})
    z = random(BiComplex{BigInt})
    l = (x - y) * z
    r = (x * z) - (y * z)
    l == r
end

@test begin
    x = random(BiComplex{BigInt})
    norm(x) > big(0)
end

@test begin
    x = random(BiComplex{BigInt})
    y = random(BiComplex{BigInt})
    l = norm(x * y)
    r = norm(x) * norm(y)
    l == r
end

@test_throws ErrorException begin
    inv(BiComplex(0, 0.0))
end

@test_throws ErrorException begin
    random(BiComplex{Int}) / 0
end

@test_throws ErrorException begin
    0 \ random(BiComplex{Int})
end

@test begin
    a = Plexifications.randomBigRational()
    z = random(BiComplex{Rational{BigInt}})
    l = z / a
    r = a \ z
    l == r
end

@test begin
    a = Plexifications.randomBigRational()
    z = random(BiComplex{Rational{BigInt}})
    l = a / z
    r = z \ a
    l == r
end

@test begin
    z = random(BiComplex{BigInt})
    z == z + zero(BiComplex{BigInt})
end

@test begin
    z = random(BiComplex{BigInt})
    z == z * one(BiComplex{BigInt})
end

@test begin
    z = random(BiComplex{BigInt})
    z == z + zero(z)
end

@test begin
    z = random(BiComplex{BigInt})
    z == z * one(z)
end

@test begin
    x = random(BiComplex{BigInt})
    y = random(BiComplex{BigInt})
    l = commutator(x, y)
    r = zero(BiComplex{BigInt})
    l == r
end

@test begin
    x = random(BiComplex{BigInt})
    y = random(BiComplex{BigInt})
    l = alternatorL(x, y)
    r = zero(BiComplex{BigInt})
    l == r
end

@test begin
    x = random(BiComplex{BigInt})
    y = random(BiComplex{BigInt})
    l = alternatorR(x, y)
    r = zero(BiComplex{BigInt})
    l == r
end

@test begin
    x = random(BiComplex{BigInt})
    y = random(BiComplex{BigInt})
    l = flexator(x, y)
    r = zero(BiComplex{BigInt})
    l == r
end

@test begin
    x = random(BiComplex{BigInt})
    y = random(BiComplex{BigInt})
    z = random(BiComplex{BigInt})
    l = associator(x, y, z)
    r = zero(BiComplex{BigInt})
    l == r
end

@test begin
    x = random(BiComplex{BigInt})
    y = random(BiComplex{BigInt})
    z = random(BiComplex{BigInt})
    l = jacobiator(x, y, z)
    r = 3 * x * y * z
    l == r
end

@test begin
    l = BiComplex(Cmplex(1,2))
    r = BiComplex(1,2,0)
    l == r
end

@test begin
    l = BiComplex(1, 2, 3.0)
    r = BiComplex(1.0, 2.0, 3.0)
    l == r
end

@test begin
    l = BiComplex(1, 2, 3, 4.0)
    r = BiComplex(1.0, 2.0, 3.0, 4.0)
    l == r
end


using SimulatedNeuralMoments, Flux, MCMCChains, StatsPlots, DelimitedFiles
using BSON:@save
using BSON:@load
using DelimitedFiles

# get the things to define the structure for the model
include("SVlib.jl")
function main()
lb, ub = PriorSupport()

# fill in the structure that defines the model
model = SNMmodel("Stochastic Volatility example", lb, ub, InSupport, Prior, PriorDraw, auxstat)

# train the net, and save it and the transformation info
#nnmodel, nninfo = MakeNeuralMoments(model)
#@save "neuralmodel.bson" nnmodel nninfo  # use this line to save the trained neural net
@load "neuralmodel.bson" nnmodel nninfo # use this to load a trained net

# draw a sample at the design parameters, or use an existing data set
#y = SVmodel(TrueParameters()) # draw a sample of 500 obsns. at design parameters
y = readdlm("svdata.txt") # load a data set
p1 = plot(y)
p2 = density(y)
plot(p1, p2, layout=(2,1))
#savefig("data.png")

# define the neural moments using the real data
m = NeuralMoments(auxstat(y), model, nnmodel, nninfo)
@show m
## draw a chain of length 10000 plus 500 burnin
chain, junk, junk = MCMC(m, 10500, model, nnmodel, nninfo, do_cue=false, verbosity=false)
chain = chain[501:end,:]
# visualize results
chn = Chains(chain, ["ϕ", "ρ", "σ"])
display(chn)
println("SNM estimation, estimated pos. median")
cnames = ["pos. median"]
prettyprint(median(chain,dims=1)[:], cnames)
plot(chn)
#savefig("chain.png")
#writedlm("chain.txt", chain)
end
main()


using SurveyDataWeighting
using Test

target_populations = [ 50.0, 20.0, 230.0, 35.0 ]

data = [
 1.0 1.0 0.0 0.0 ;
 0.0 1.0 0.0 0.0 ;
 1.0 0.0 2.0 0.0 ;
 0.0 0.0 6.0 1.0 ;
 1.0 0.0 4.0 1.0 ;
 1.0 1.0 0.0 0.0 ;
 1.0 0.0 5.0 0.0 ;
 0.0 0.0 6.0 1.0 ;
 0.0 1.0 0.0 0.0 ;
 0.0 0.0 3.0 1.0 ;
 1.0 0.0 2.0 0.0 ;
 1.0 1.0 0.0 1.0 ;
 1.0 0.0 3.0 1.0 ;
 1.0 0.0 4.0 0.0 ;
 0.0 0.0 5.0 0.0 ;
 0.0 1.0 0.0 1.0 ;
 1.0 0.0 2.0 1.0 ;
 0.0 0.0 6.0 0.0 ;
 1.0 0.0 4.0 1.0 ;
 0.0 1.0 0.0 0.0  ]

initial_weights = [
   3.0,
   3.0,
   5.0,
   4.0,
   2.0,
   5.0,
   5.0,
   4.0,
   3.0,
   3.0,
   5.0,
   4.0,
   4.0,
   3.0,
   5.0,
   3.0,
   4.0,
   5.0,
   4.0,
   3.0 ]

nrows = size( data, 1 )
ncols = size( data, 2 )
sp = size( target_populations, 1 )

@testset "Reproduce the basic test case in Creedy NEW ZEALAND TREASURY WORKING PAPER 03/17" begin

   @test ncols == size( target_populations, 1 )
   @test nrows == size( initial_weights, 1 )

   println( "target popns $target_populations" )

   # a = target_populations - (data'*initial_weights)
   # print( a )

   wchi = do_chi_square_reweighting( data, initial_weights, target_populations )
   println( "direct chi-square results $wchi")
   weighted_popn_chi = (wchi' * data)'
   println( "wchisq; got $weighted_popn_chi")
   @test weighted_popn_chi ≈ target_populations
   lower_multiple = 0.20 # any smaller min and d_and_s_constrained fails on this dataset
   upper_multiple = 2.19
   for m in instances( DistanceFunctionType )
      println( "on method $m")
      rw = do_reweighting(
            data               = data,
            initial_weights    = initial_weights,
            target_populations = target_populations,
            functiontype       = m,
            lower_multiple     = lower_multiple,
            upper_multiple     = upper_multiple,
            tolx               = 0.000001,
            tolf               = 0.000001 )
      println( "results for method $m = $rw" )
      weights = rw.weights
      weighted_popn = (weights' * data)'
      println( "weighted_popn = $weighted_popn" )
      @test weighted_popn ≈ target_populations
      if m != chi_square
         for w in weights # check: what's the 1-liner for this?
            @test w > 0.0
         end
      else
         @test weights ≈ wchi # chisq the direct way should match chisq the iterative way
      end
      if m in [constrained_chi_square, d_and_s_constrained ]
         # check the constrainted methods keep things inside ll and ul
         for r in 1:nrows
            @test weights[r] <= initial_weights[r]*upper_multiple
            @test weights[r] >= initial_weights[r]*lower_multiple
         end
      end
   end # meth loop
end # tests

include( "large_scale_test.jl")


using MathLink
using Test

import MathLink: WExpr, WSymbol

w = W"Factorial"(30)
@test_throws MathLink.MathLinkError weval(Int, w)
@test weval(BigInt, w) == factorial(big(30))

@test weval(W"Factorial"(20)) === factorial(20)

w = W"Sqrt"(2.0)
@test weval(w) == sqrt(2.0)

@test weval(W"Function"(W"x",W"Times"(W"x", 2))(100)) == 200

@test weval(W"Integrate"(W"Log"(W"x"), (W"x", 1, W"E"))) == 1


"""
    grammify(x)

Turns a univariate 1-D array into a Grammian Angular Field sporting a 2-D matrix shape.

# Examples
```jldoctest
julia> grammify(1:4)
4×4 Array{Float64,2}:
  1.0        0.333333  -0.333333  -1.0
  0.333333  -0.777778  -1.0       -0.333333
 -0.333333  -1.0       -0.777778   0.333333
 -1.0       -0.333333   0.333333   1.0
```
"""
function grammify(x::AbstractVector)
    θ = acos.(standardize(x, -1, 1))
    # θ*θ'
    [cos(θ[i]+θ[j]) for i in 1:length(θ), j in 1:length(θ)]
end

"""
    plotgrammian(g)

Plot a grammified univariate array.

# Examples
```jldoctest
julia> GrammianAngularFields.plotgrammian(grammify(1:10))
data: [
  "heatmap with fields type and z"
]

layout: "layout with field margin"
```
"""
plotgrammian(g) = PlotlyJS.plot(PlotlyJS.heatmap(z=g))

"""
    plotgrammian2(x)

# Examples
```jldoctest
julia> GrammianAngularFields.plotgrammian2(sin.(1:30))
data: [
  "scatter with fields type, x, xaxis, y, and yaxis",
  "heatmap with fields type, xaxis, yaxis, and z"
]

layout: "layout with fields margin, xaxis1, xaxis2, yaxis1, and yaxis2"
```
"""
function plotgrammian2(x::AbstractVector)
    p1 = PlotlyJS.plot(PlotlyJS.scatter(x=1:length(x), y=x))
    p2 = PlotlyJS.plot(PlotlyJS.heatmap(z=grammify(x)))
    p = [p1 p2]
    p
end

"""
    testme(x)

Test any kind of functionality which may change at any time.
This is indeed the very definition of a random function.

# Examples
```jldoctest
julia> foo = 42
42
```
"""
function testme(x::AbstractVector)
    p1 = PlotlyJS.plot(PlotlyJS.scatter(x=1:length(x), y=x))
    p2 = PlotlyJS.plot(PlotlyJS.heatmap(z=grammify(x)))
    p = [p1 p2]
    p
end


"""
    Pads all the dimensions, in constrast with @PadData
    that pads only x y dims.
"""
function PadDataCpu(pd,data)
    x = size(data,1)
    y = size(data,2)
    z = size(data,3)
    padx = 1:(x+2pd)
    pady = 1:(y+2pd)
    padz = 1:(z+2pd)
    datax = (pd+1):(x+pd)
    datay = (pd+1):(y+pd)
    dataz = (pd+1):(z+pd)
    return PaddedView(0, data, (padx,pady,padz), (datax, datay, dataz))
end

function cpu_star_stencil(org_data, radius, coefs, t_steps)
    data = PadDataCpu(radius, org_data)

    function stencil(data, radius, coefs)
        out = zeros(Float32,size(data))
        for i = (radius+1):(size(data,1)-radius)
            for j = (radius+1):(size(data,2)-radius)
                for k = (radius+1):(size(data,3)-radius)
                    c = coefs[1]*data[i,j,k]
                    for r = 1:radius
                        c += coefs[r+1]*(data[i + r,j,k] + data[i - r,j,k] +
                        data[i,j + r,k] + data[i,j - r,k] +
                        data[i,j,k + r] + data[i,j,k - r])
                    end
                    out[i,j,k] = c
                end
            end
        end
        return out
    end

    cout = stencil(data, radius, coefs)
    for t = 2:t_steps
        cout = stencil(cout, radius, coefs)
    end

    return @view cout[:,:,(radius+1):(size(org_data,3)+radius)]
end

function cpu_star_stencil_v(org_data, vsq, radius, coefs, t_steps)
        data = PadDataCpu(radius, org_data)
        v = PadDataCpu(radius, vsq)

            function stencil(data, vsq, radius, coefs)
                out = zeros(Float32,size(data))
                for i = (radius+1):(size(data,1)-radius)
                    for j = (radius+1):(size(data,2)-radius)
                        for k = (radius+1):(size(data,3)-radius)
                            c = coefs[1]*data[i,j,k]
                            for r = 1:radius
                                c += coefs[r+1]*(data[i + r,j,k] + data[i - r,j,k] +
                                data[i,j + r,k] + data[i,j - r,k] +
                                data[i,j,k + r] + data[i,j,k - r])
                            end
                            out[i,j,k] = c*vsq[i,j,k]
                        end
                    end
                end
                return out
            end

            cout = stencil(data,v, radius, coefs)
            for t = 2:t_steps
                cout = stencil(cout,v, radius, coefs)
            end
            return @view cout[:,:,(radius+1):(size(org_data,3)+radius)]
end

function cpu_star_stencil_v_nv(org_data, vsq, radius, coefs, t_steps)
        data = PadDataCpu(radius, org_data)
        v = PadDataCpu(radius, vsq)

            function stencil(data, vsq, radius, coefs)
                out = zeros(Float32,size(data))
                for i = (radius+1):(size(data,1)-radius)
                    for j = (radius+1):(size(data,2)-radius)
                        for k = (radius+1):(size(data,3)-radius)
                            c = coefs[1]*data[i,j,k]
                            for r = 1:radius
                                c += coefs[r+1]*(data[i + r,j,k] + data[i - r,j,k] +
                                data[i,j + r,k] + data[i,j - r,k] +
                                data[i,j,k + r] + data[i,j,k - r])
                            end
                            out[i,j,k] = c*vsq[i,j,k] + c
                        end
                    end
                end
                return out
            end

            cout = stencil(data,v, radius, coefs)
            for t = 2:t_steps
                cout = stencil(cout,v, radius, coefs)
            end
            return @view cout[:,:,(radius+1):(size(org_data,3)+radius)]
end

function cpu_star_stencil_vsq_prev_val(org_data,vsq,
            radius, coefs, t_steps, prev_time)
    data = PadDataCpu(radius, org_data)
    data = Array(data)
    out = zeros(Float32,size(data))
    v = PadDataCpu(radius, vsq)

    function stencil(data, out, vsq, radius, coefs, pc)
        for i = (radius+1):(size(data,1)-radius)
            for j = (radius+1):(size(data,2)-radius)
                for k = (radius+1):(size(data,3)-radius)
                    #out[i,j,k] != 0 && println(pc*out[i,j,k])
                    c = coefs[1]*data[i,j,k]
                    for r = 1:radius
                        c += coefs[r+1]*(data[i + r,j,k] + data[i - r,j,k] +
                        data[i,j + r,k] + data[i,j - r,k] +
                        data[i,j,k + r] + data[i,j,k - r])
                    end
                    out[i,j,k] =c*vsq[i,j,k] + c + pc*out[i,j,k]
                end
            end
        end
        return out
    end

    out = stencil(data, out, v, radius, coefs, prev_time)
    for t = 2:t_steps
        data,out = out,data
        out = stencil(data, out, v, radius, coefs, prev_time)
    end

    #if t_steps%2 == 1
    return @view out[:,:,(radius+1):(size(org_data,3)+radius)]
    #else
    #    return @view data[:,:,(radius+1):(size(org_data,3)+radius)]
    #end

end

function cpu_dense_stencil(org_data, radius, coefs,t_steps)
    data = PadDataCpu(radius, org_data)
    data = Array(data)
    dim = size(data)
    out = zeros(Float32, dim)
    function stencil(data, out, radius, coefs)
        for x = (radius+1):(dim[1]-radius)
            for y = (radius+1):(dim[2]-radius)
                for z = (radius+1):(dim[3]-radius)
                    res = coefs[1]*data[x,y,z]
                    for ix = -radius:radius
                        for iy = -radius:radius
                            for iz = -radius:radius
                                if ix == 0 &&  iy == 0 &&  iz == 0
                                    continue
                                end
                                ci = max(abs.([ix, iy, iz])...)
                                ci = coefs[ci+1]
                                res += ci*data[x+ix,y+iy,z+iz]
                            end
                        end
                    end
                    out[x,y,z] = res
                end
            end
        end
        return out
    end
    out = stencil(data, out, radius, coefs)
    for t = 2:t_steps
        data,out = out,data
        out = stencil(data, out, radius, coefs)
    end

    #if t_steps%2 == 1
    return @view out[:,:,(radius+1):(size(org_data,3)+radius)]
    return @view cout[:,:,(radius+1):(size(org_data,3)+radius)]
end


"""
    Ash(h::Union{KHist, Hist, ExpandingHist})
    Ash(h::Union{KHist, Hist, ExpandingHist}, m::Int, kernel::Function)

Create an Average Shifted Histogram using `h` as the base histogram with smoothing parameter `m`
and kernel function `kernel`.  Built-in kernels are available in the `OnlineStats.Kernels` module.

# Example

    using OnlineStats, Plots

    o = fit!(Ash(ExpandingHist(1000)), randn(10^6))

    plot(o)
    plot(o, 20)
    plot(o, OnlineStats.Kernels.epanechnikov, 4)
"""
mutable struct Ash{H, T} <: OnlineStat{Number}
    hist::H
    density::T
    m::Int
    kernel::Function
end

_default_m(h) = ceil(Int, length(edges(h)) / 100)
_default_m(h::KHist) = ceil(Int, h.k / 100)

_default_density(h) = zeros(length(counts(h)))
_default_density(h::KHist) = zeros(h.k)

Ash(h, kernel::Function, m::Int = _default_m(h)) = Ash(h, m, kernel)

function Ash(h, m::Int = _default_m(h), kernel::Function = Kernels.biweight)
    Ash(h, _default_density(h), m, kernel)
end

_fit!(o::Ash, data) = _fit!(o.hist, data)

nobs(o::Ash) = nobs(o.hist)

value(o::Ash, kernel::Function, m::Int = o.m) = value(o, m, kernel)

function value(o::Ash, m::Int = o.m, kernel::Function = o.kernel)
    if nobs(o) > 0
        o.m = m
        o.kernel = kernel
        y = o.density
        y .= 0.0
        counts = OnlineStats.counts(o.hist)
        b = length(counts)
        mids = midpoints(o.hist)
        for k in eachindex(mids)
            if counts[k] > 0
                for i in max(1, k - m + 1):min(b, k + m - 1)
                    y[i] += counts[k] * kernel((i - k) / m)
                end
            end
        end
        density_sum_to_1!(o)
        inds = findfirst(x -> x > 0, y):findlast(x -> x > 0, y)
        return (x = mids[inds], y = y[inds])
    else
        return (x=midpoints(o.hist), y=o.density)
    end
end

function density_sum_to_1!(o::Ash{<:HistogramStat})
    rmul!(o.density, 1 / (sum(o.density) * step(edges(o.hist))))
end
function density_sum_to_1!(o::Ash{<:KHist})
    o.density ./= area(midpoints(o.hist), o.density)
end

#-----------------------------------------------------------------------------# Plots
histdensity(o::Ash) = counts(o.hist) ./ nobs(o) ./ step(edges(o.hist))
histdensity(o::Ash{<:KHist}) = counts(o.hist) ./ area(o.hist)

@recipe f(o::Ash, kernel::Function, m::Int = o.m) = o, m, kernel

@recipe function f(o::Ash, m::Int = o.m, kernel::Function = o.kernel; hist=true)
    if hist
        @series begin
            normalize --> true
            seriesalpha --> .4
            seriestype --> :sticks
            label --> "Histogram Density"
            midpoints(o.hist), histdensity(o)
        end
    end
    @series begin
        x, y = value(o, m, kernel)
        label --> "Ash(m=$(m), kernel=$(o.kernel))"
        xlims --> extrema(x)
        linewidth --> 3
        x, y
    end
end

#-----------------------------------------------------------------------------# kernels
module Kernels
in_range(u) = abs(u) ≤ 1

biweight(u)         = in_range(u) ? (1.0 - u ^ 2) ^ 2 : 0.0
cosine(u)           = in_range(u) ? cos(0.5 * π * u) : 0.0
epanechnikov(u)     = in_range(u) ? 1.0 - u ^ 2 : 0.0
triangular(u)       = in_range(u) ? 1.0 - abs(u) : 0.0
tricube(u)          = in_range(u) ? (1.0 - abs(u) ^ 3) ^ 3 : 0.0
triweight(u)        = in_range(u) ? (1.0 - u ^ 2) ^ 3 : 0.0
uniform(u)          = in_range(u) ? 0.5 : 0.0

gaussian(u) = exp(-0.5 * u ^ 2)
logistic(u) = 1.0 / (exp(u) + 2.0 + exp(-u))
end


"""
  IsingJ

  ver 0.2.2 Released Sep 2020
  This code is distributed under the constitution
  of MIT LICENSE.

  module Initial

  2019/04/24  Released by PearCandy                 """

module Smoothing_mag
    using Printf
    export output_T_vs_mag

 """  plot_T_vs_mag(_temp,_mag)
      output T vs Magnetization
      _temp: temperature
      _mag : absolute values of magnetization  """

    function output_T_vs_mag(low_t,dt,_mag)
        open("t_mag.dat","w") do io
            println(io,"# temperature vs ave.magnetization")

            for i in 1:length(_mag)
                _temp = low_t + dt*float(i)

                # smoothing of magnetization
                if(3<i<length(_mag)-3)
                    mag_ave=(_mag[i-2]+_mag[i-1]+_mag[i]+_mag[i+1]+_mag[i+2])/5
                else
                    mag_ave=_mag[i]
                end
                # -- output --
                @printf(io, " %+06f",     _temp  )
                @printf(io, " %+06f\n",   mag_ave)
            end
        end
    end

end #module


@testset "Sparse" begin
    A = AFArray(rand(10,10));
    @test !issparse(A)

    # build an identity matrix
    n = 10;

    Ie = AFArray(Matrix{Float32}(I,10,10))
    Aid = sparse(Ie)

    @test issparse(Aid)

    Ie2 = full(Aid)

    @test !issparse(Ie2)

    @test all(Ie2 == Ie)

    e = sprand(10, 20, 0.2)
    c = randn(20)
    a1 = AFArray(e)
    c1 = AFArray(c)
    @test issparse(a1)

    e2 = SparseMatrixCSC(a1)
    @test e2 == e

    @test sum(e * c) ≈ sum(a1 * c1)
end


using Distances

@testset "dp" begin
    #DONE
    @testset "dp_means" begin
        inputs = [[5, 0, 0, 1, 1, 5],
                  [5, 1, 0, 0, 1, 5],
                  [5, 0, 1, 0, 1, 5],
                  [0, 4, 6, 2, 0, 0],
                  [0, 4, 6, 1, 1, 0],
                  [0, 4, 6, 1, 0, 1]]
        radius = 3.0
        μs, sizes, indices, centroids = dp_means(inputs, radius)
        @test μs == [[5, 1/3, 1/3, 1/3, 1, 5],
                     [0, 4, 6, 4/3, 1/3, 1/3]]
        @test sizes == [3, 3]
        @test indices == [[1, 2, 3], [4, 5, 6]]
        @test centroids == [1, 4]
    end

    #DONE
    @testset "C2MC_dp_centers" begin
        inputs = [[5, 0, 0, 1, 1, 5],
                  [5, 1, 0, 0, 1, 5],
                  [5, 0, 1, 0, 1, 5],
                  [0, 4, 6, 2, 0, 0],
                  [0, 4, 6, 1, 1, 0],
                  [0, 4, 6, 1, 0, 1]]
        radius = 3.0
        μs, sizes, indices, centroids = C2MC_dp_centers(inputs, radius)
        @test μs == [[5, 1/3, 1/3, 1/3, 1, 5],
                     [0, 4, 6, 4/3, 1/3, 1/3]]
        @test sizes == [3, 3]
        @test indices == [[1, 2, 3], [4, 5, 6]]
        @test centroids == [1, 4]
    end

    @testset "dp_centers" begin
        inputs = [[5, 0, 0, 1, 1, 5],
                  [5, 1, 0, 0, 1, 5],
                  [5, 0, 1, 0, 1, 5],
                  [0, 4, 6, 2, 0, 0],
                  [0, 4, 6, 1, 1, 0],
                  [0, 4, 6, 1, 0, 1]]
        radii = 3.0
        μs, sizes, indices, centroids = dp_centers(inputs, radii)
        @test μs == [[5, 1/3, 1/3, 1/3, 1, 5],
                     [0, 4, 6, 4/3, 1/3, 1/3]]
        @test sizes == [3, 3]
        @test indices == [[1, 2, 3], [4, 5, 6]]
        @test centroids == [1, 4]
    end

    #recursive version of dp_centers
    @testset "dp_centers" begin
        inputs = [[5, 0, 0, 1, 1, 5],
                  [5, 1, 0, 0, 1, 5],
                  [5, 0, 1, 0, 1, 5],
                  [0, 4, 6, 2, 0, 0],
                  [0, 4, 6, 1, 1, 0],
                  [0, 4, 6, 1, 0, 1]]
        radii = [5.0, 3.0]
        μs, sizes, indices, centroids = dp_centers(inputs, radii)
        @test μs == [[5, 1/3, 1/3, 1/3, 1, 5],
                     [0, 4, 6, 4/3, 1/3, 1/3]]
        @test sizes == [3, 3]
        @test indices == [[1, 2, 3], [4, 5, 6]]
        @test centroids == [1, 4]
    end

end


export cart_to_mat, mat_to_vec, remove_background, gaussf

"""
    cart_to_mat(cart)

converts a list of cartesian indices to a matrix with the first dimension corresponding to the dimension direction.

"""
function cart_to_mat(cart)
    all_max_vec = zeros(length(cart[1]), length(cart))
    n=1
    for idx in cart
        all_max_vec[:,n] = [Tuple(idx)...]
        n += 1
    end
    return all_max_vec
end

"""
    mat_to_vec(mat)

converts a 2D matrix to a vector of vectors, one vector for each column.

"""
function mat_to_vec(mat)
    [mat[:,n] for n=1:size(mat,2)]
end

"""
    gaussf(img::AbstractArray, sigma=1.0; dtype=Float32)
performs Gaussian filtering using FFTs. `sigma` denotes the kernel sizes along the various dimensions.
#arguments
+ `img`: the image to apply the Gaussian filter to
+ ``
"""
function gaussf(img::AbstractArray{T}, sigma=1.0; dtype=Float32) where (T <: Real)
    dims=1:ndims(img)
    shiftdims = dims[2:end]
    img = dtype.(img)
    f = rfft(img, dims)
    return irfft(f .* ifftshift(gaussian(eltype(img), size(f), offset=CtrRFT, sigma=size(img) ./(2π .*sigma)), shiftdims), size(img, dims[1]), dims)
end

function gaussf(img::AbstractArray, sigma=1.0)
    return (ifft(fft(img) .* ifftshift(gaussian(eltype(img), size(img), sigma=size(img) ./(2π*sigma)))))
end

"""
    remove_background(img, sigma=5.0)

estimates the background by applying a Gaussian filter to the data and measuring for the minimum of this filtered data.
#arguments
+ `img`: the image to remove the background from
+ `sigma`: the kernelsize for the Gaussian filter
#returns
a tuple of the background-subtracted image and the background value.
Note that no zero-clipping is performed. The result type is by defaul Float32, if the image itself is not FLoat64 or Complex.
"""
function remove_background(img, sigma=5.0; dtype=Float32)
    bg = minimum(gaussf(img,sigma; dtype=dtype))
    return img .- bg, bg
end


import FiniteDiff
import ForwardDiff
import ReverseDiff
import Zygote

using Test

F(x, θ, deg) = (θ[1] - x[1])^deg + θ[2] * (x[2] - x[1]^deg)^deg
F_idx_val(ca) = F(ca[Val(:x)], ca[Val(:θ)], ca[Val(:deg)])
F_idx_sym(ca) = F(ca[:x], ca[:θ], ca[:deg])
F_view_val(ca) = F(@view(ca[Val(:x)]), @view(ca[Val(:θ)]), ca[Val(:deg)])
F_view_sym(ca) = F(@view(ca[:x]), @view(ca[:θ]), ca[:deg])
F_prop(ca) = F(ca.x, ca.θ, ca.deg)

ca = ComponentArray(x = [1, 2], θ = [1.0, 100.0], deg = 2)
truth = [-400, 200]

@testset "$(nameof(F_))" for F_ in (F_idx_val, F_idx_sym, F_view_val, F_view_sym, F_prop)
    finite = FiniteDiff.finite_difference_gradient(ca -> F_(ca), ca).x
    @test finite ≈ truth

    forward = ForwardDiff.gradient(ca -> F_(ca), ca).x
    @test forward ≈ truth

    reverse = ReverseDiff.gradient(ca -> F_(ca), ca).x
    @test reverse ≈ truth

    zygote_full = Zygote.gradient(ca -> F_(ca), ca)[1]
    if F_ == F_prop && VERSION < v"1.3"
        @test_broken zygote_full.x ≈ truth
    else
        @test zygote_full.x ≈ truth
    end
end


"""
    LagSelection(data, maxlag; constant = true, trend = false) -> LagSelection

Calculate AIC, SIC, and HQC lag selection criteria for an unrestricted VAR.

# Arguments
- `data` : `DataFrame` or `TimeArray` of observations on endogenous variables
- `maxlag::Int` : the maximum number of lags to estimate
- `constant::Bool = true` : include an intercept term
- `trend::Bool = false` : include a linear trend

# Fields
- `maxlag::Int`
- `table::DataFrame`
- `selection::Dict`
"""
struct LagSelection
    maxlag::Int
    table::DataFrame
    selection::Dict
end

function LagSelection(data::DataFrame, maxlag::Int; constant::Bool = true, trend::Bool = false)
    if maxlag < 1
        error("maxlag must be ≥ 1")
    end

    table, selection = lagselect(data, maxlag, constant, trend)
    LagSelection(maxlag, table, selection)
end

function LagSelection(data::TimeArray, maxlag::Int; constant::Bool = true, trend::Bool = false)
    data = DataFrame(data)[:, 2:end]
    LagSelection(data, maxlag; constant = constant, trend = trend)
end

function lagselect(data, maxlag, constant, trend)
    critname = ["AIC", "HQIC", "SIC"]
    AIC = zeros(maxlag)
    HQC = zeros(maxlag)
    SIC = zeros(maxlag)
    for lag = 1:maxlag
        dmat = data[(maxlag-lag+1):end, :]
        v = VAR(dmat, lag; constant = constant, trend = trend)
        AIC[lag] = aic(v)
        HQC[lag] = hqc(v)
        SIC[lag] = sic(v)
    end
    table = DataFrame(lag = collect(1:maxlag), AIC = AIC, HQC = HQC, SIC = SIC)
    selection = Dict(cn => argmin(table[!, cn]) for cn in names(table)[2:end])
    return table, selection
end

function show(io::IO, ls::LagSelection)
    println(io, typeof(ls))
    println(io, "Maximum lags: ", ls.maxlag)
    for k in keys(ls.selection)
        print(io, k, ": ", rpad(ls.selection[k], 3))
    end
    print(io, '\n')
end


immutable BlockBandedMatrix{T,RI,CI} <: AbstractMatrix{T}
    data::Vector{T}  # the entries
    rows::RI
    columns::CI

    l::Int # lower bandwidth ≥0
    u::Int # upper bandwidth ≥0

    columnlookup::Vector{Int}  # translates column first data entry
    firstrow::Vector{Int}     # gives first row index in

    function BlockBandedMatrix(dat::Vector{T},rws::RI,cls::CI,l::Int,u::Int)
        n = 0
        for J=1:length(columns),K=max(1,J-u):min(J+l,length(rows))
            n += rows[K]*columns[J]
        end

        if n != length(dat)
            error("Data must be consistant size with number of non-zeros")
        end

        m = 1
        curcol = 1

        columnlookup = Array(Int,0)
        firstrow = Array(Int,0)
        for J=1:length(columns)
            sm = sum(rows[max(1,J-u):min(J+l,length(rows))])
            nrws = sum(rows[max(1,J-u):J-1])
            for j=1:columns[J]
                push!(columnlookup,m)
                push!(firstrow,curcol-j-nrws+1)

                m += sm
                curcol += 1
            end
        end

        new(dat,rws,cls,l,u,columnlookup,firstrow)
    end
end

BlockBandedMatrix(dat::Vector,rws,cls,l,u) =
    BlockBandedMatrix{eltype(dat),typeof(rws),typeof(cls)}(dat,rws,cls,l,u)

Base.size(B::BlockBandedMatrix) = sum(B.rows),sum(B.columns)
Base.linearindexing{BBM<:BlockBandedMatrix}(::Type{BBM}) = Base.LinearSlow()
function Base.getindex(B::BlockBandedMatrix,k::Integer,j::Integer)
    if k > size(B,1) || j > size(B,2)
        throw(BoundsError())
    end

    fr = B.firstrow[j]
    nrws = j != size(B,2) ?
            B.columnlookup[j+1] - B.columnlookup[j] :
            length(B.data) - B.columnlookup[j] + 1

    if k < fr || k > fr + nrws - 1
        zero(eltype(B))
    else
        B.data[B.columnlookup[j] + k - fr]
    end
end

function Base.setindex!(B::BlockBandedMatrix,v,k::Integer,j::Integer)
    if k > size(B,1) || j > size(B,2)
        throw(BoundsError())
    end

    fr = B.firstrow[j]
    nrws = j != size(B,2) ?
            B.columnlookup[j+1] - B.columnlookup[j] :
            length(B.data) - B.columnlookup[j] + 1

    if k < fr || k > fr + nrws - 1
        if v!=0
            error("Cannot change entries outside block bands")
        end
    else
        B.data[B.columnlookup[j] + k - fr] = v
    end
end

for (op,bop) in ((:(Base.rand),:bbrand),(:(Base.zeros),:bbzeros),(:(Base.ones),:bbones))
    @eval begin
        function $bop{T}(::Type{T},rows,columns,l,u)
            n = 0
            for J=1:length(columns),K=max(1,J-u):min((J+l),length(rows))
                n += rows[K]*columns[J]
            end
            BlockBandedMatrix($op(T,n),rows,columns,l,u)
        end
        $bop(rows,columns,l,u) = $bop(Float64,rows,columns,l,u)
    end
end

rows=1:50
columns=1:50
l=u=1

B=bbrand(rows,columns,l,u)

sz=sizeof(eltype(B))

v=pointer(B.data)
α=1.0
Z=bbzeros(rows,columns,l,u+l)
    w=pointer(Z.data)
    for k=1:4
        N=B.columnlookup[k+1]-B.columnlookup[k]
        BLAS.axpy!(N,α,v+(B.columnlookup[k]-1)*sz,1,w+(B.columnlookup[k]-1)*sz,1)
    end

B
unsafe_load(v+1*sz)
Z
using SO
B

B[10,3]=5

B

v=B.data[1:3]
norm(v)

B.columnlookup[2]

B.columnlookup

B.firstrow

size(B)

k=j=1
    B.data[B.columnlookup[j] + k-1]

B.data

columnlookup = Array(Int,0)
    m=1
    push!(columnlookup,m)  # the first column begins at index 1
    for J=1:length(columns)
        sm = sum(rows[max(1,J-u):min(J+l,length(rows))])
        for j=1:columns[J]
            m += sm
            push!(columnlookup,m)
        end
    end

columns[2]

columnlookup

n = 0
    for J=1:length(columns),K=max(1,J-u):(J+l)
        n += rows[K]*columns[J]
    end

    B=BlockBandedMatrix(rand(n),1,1,rows,columns)

B.data

size(B)

T=eltype(data)
@time pointer_to_array(pointer(data)+sizeof(T)*2,(2,2))


# ---
# title: 762. Prime Number of Set Bits in Binary Representation
# id: problem762
# author: Tian Jun
# date: 2020-10-31
# difficulty: Easy
# categories: Bit Manipulation
# link: <https://leetcode.com/problems/prime-number-of-set-bits-in-binary-representation/description/>
# hidden: true
# ---
#
# Given two integers `L` and `R`, find the count of numbers in the range `[L,
# R]` (inclusive) having a prime number of set bits in their binary
# representation.
#
# (Recall that the number of set bits an integer has is the number of `1`s
# present when written in binary. For example, `21` written in binary is `10101`
# which has 3 set bits. Also, 1 is not a prime.)
#
# **Example 1:**
#
#
#
#     Input: L = 6, R = 10
#     Output: 4
#     Explanation:
#     6 -> 110 (2 set bits, 2 is prime)
#     7 -> 111 (3 set bits, 3 is prime)
#     9 -> 1001 (2 set bits , 2 is prime)
#     10->1010 (2 set bits , 2 is prime)
#
#
# **Example 2:**
#
#
#
#     Input: L = 10, R = 15
#     Output: 5
#     Explanation:
#     10 -> 1010 (2 set bits, 2 is prime)
#     11 -> 1011 (3 set bits, 3 is prime)
#     12 -> 1100 (2 set bits, 2 is prime)
#     13 -> 1101 (3 set bits, 3 is prime)
#     14 -> 1110 (3 set bits, 3 is prime)
#     15 -> 1111 (4 set bits, 4 is not prime)
#
#
# **Note:**
#
#   1. `L, R` will be integers `L <= R` in the range `[1, 10^6]`.
#   2. `R - L` will be at most 10000.
#
#
## @lc code=start
using LeetCode

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


using DiffEqBase: remake, solve, ODEProblem, EnsembleProblem, EnsembleThreads

####################################################################################################
# this function takes into accound a parameter passed to the vector field
# Putting the options `save_start = false` seems to give bugs with Sundials
function flowTimeSol(x, p, tm, pb::ODEProblem; alg = Euler(), kwargs...)
	_prob = remake(pb; u0 = x, tspan = (zero(eltype(tm)), tm), p = p)
	# the use of concrete_solve makes it compatible with Zygote
	sol = solve(_prob, alg; save_everystep = false, kwargs...)
	return (t = sol.t[end], u = sol[end])
end

# this function is a bit different from the previous one as it is geared towards parallel computing of the flow.
function flowTimeSol(x::AbstractMatrix, p, tm, epb::EnsembleProblem; alg = Euler(), kwargs...)
	# modify the function which asigns new initial conditions
	# see docs at https://docs.sciml.ai/dev/features/ensemble/#Performing-an-Ensemble-Simulation-1
	_prob_func = (prob, ii, repeat) -> prob = remake(prob, u0 = x[:, ii], tspan = (zero(eltype(tm[ii])), tm[ii]), p = p)
	_epb = setproperties(epb, output_func = (sol, i) -> ((t = sol.t[end], u = sol[end]), false), prob_func = _prob_func)
	sol = solve(_epb, alg, EnsembleThreads(); trajectories = size(x, 2), save_everystep = false, kwargs...)
	# sol.u contains a vector of tuples (sol_i.t[end], sol_i[end])
	return sol.u
end

flow(x, p, tm, pb::ODEProblem; alg = Euler(), kwargs...) = flowTimeSol(x, p, tm, pb; alg = alg, kwargs...).u
flow(x, p, tm, pb::EnsembleProblem; alg = Euler(), kwargs...) = flowTimeSol(x, p, tm, pb; alg = alg, kwargs...)
flow(x, tm, pb::Union{ODEProblem, EnsembleProblem}; alg = Euler(), kwargs...) =  flow(x, nothing, tm, pb; alg = alg, kwargs...)
####################################################################################################
# function used to compute the derivative of the flow, so pb encodes the variational equation
function dflow(x::AbstractVector, p, dx, tm, pb::ODEProblem; alg = Euler(), kwargs...)
	n = length(x)
	_prob = remake(pb; u0 = vcat(x, dx), tspan = (zero(eltype(tm)), tm), p = p)
	# the use of concrete_solve makes it compatible with Zygote
	sol = solve(_prob, alg, save_everystep = false; kwargs...)[end]
	return (t = tm, u = sol[1:n], du = sol[n+1:end])
end

# same for Parallel computing
function dflow(x::AbstractMatrix, p, dx, tm, epb::EnsembleProblem; alg = Euler(), kwargs...)
	N = size(x,1)
	_prob_func = (prob, ii, repeat) -> prob = remake(prob, u0 = vcat(x[:, ii], dx[:, ii]), tspan = (zero(eltype(tm[ii])), tm[ii]), p = p)
	_epb = setproperties(epb, output_func = (sol,i) -> ((t = sol.t[end], u = sol[end][1:N], du = sol[end][N+1:end]), false), prob_func = _prob_func)
	sol = solve(_epb, alg, EnsembleThreads(); trajectories = size(x, 2), save_everystep = false, kwargs...)
	return sol.u
end

dflow(x, dx, tspan, pb::Union{ODEProblem, EnsembleProblem}; alg = Euler(), kwargs...) = dflow(x, nothing, dx, tspan, pb; alg = alg, kwargs...)
####################################################################################################
# this function takes into accound a parameter passed to the vector field
function dflow_fd(x, p, dx, tm, pb::ODEProblem; alg = Euler(), δ = 1e-9, kwargs...)
	sol1 = flow(x .+ δ .* dx, p, tm, pb; alg = alg, kwargs...)
	sol2 = flow(x 			, p, tm, pb; alg = alg, kwargs...)
	return (t = tm, u = sol2, du = (sol1 .- sol2) ./ δ)
end

function dflow_fd(x, p, dx, tm, pb::EnsembleProblem; alg = Euler(), δ = 1e-9, kwargs...)
	sol1 = flow(x .+ δ .* dx, p, tm, pb; alg = alg, kwargs...)
	sol2 = flow(x 			, p, tm, pb; alg = alg, kwargs...)
	return [(t = sol1[ii][1], u = sol2[ii][2], du = (sol1[ii][2] .- sol2[ii][2]) ./ δ) for ii = 1:size(x,2) ]
end
dflow_fd(x, dx, tm, pb::Union{ODEProblem, EnsembleProblem}; alg = Euler(), δ = 1e-9, kwargs...) = dflow_fd(x, nothing, dx, tm, pb; alg = alg, δ = δ, kwargs...)
####################################################################################################
# this gives access to the full solution, convenient for Poincaré shooting
# this function takes into accound a parameter passed to the vector field and returns the full solution from the ODE solver. This is useful in Poincare Shooting to extract the period.
function flowFull(x, p, tm, pb::ODEProblem; alg = Euler(), kwargs...)
	_prob = remake(pb; u0 = x, tspan = (zero(tm), tm), p = p)
	sol = solve(_prob, alg; kwargs...)
end

function flowFull(x, p, tm, epb::EnsembleProblem; alg = Euler(), kwargs...)
	_prob_func = (prob, ii, repeat) -> prob = remake(prob, u0 = x[:, ii], tspan = (zero(eltype(tm[ii])), tm[ii]), p = p)
	_epb = setproperties(epb, prob_func = _prob_func)
	sol = solve(_epb, alg, EnsembleThreads(); trajectories = size(x, 2), kwargs...)
end
flowFull(x, tm, pb::Union{ODEProblem, EnsembleProblem}; alg = Euler(), kwargs...) = flowFull(x, nothing, tm, pb; alg = alg, kwargs...)
####################################################################################################
# Structures related to computing ODE/PDE Flows
"""
$(TYPEDEF)
$(TYPEDFIELDS)

# Simplified constructor(s)
We provide a simple constructor where you only pass the vector field `F`, the flow `ϕ` and its differential `dϕ`:

	fl = Flow(F, ϕ, dϕ)

# Simplified constructors for DifferentialEquations.jl

There are some simple constructors for which you only have to pass a `prob::ODEProblem` or `prob::EnsembleProblem` (for parallel computation) from `DifferentialEquations.jl` and an ODE time stepper like `Tsit5()`. Hence, you can do for example

	fl = Flow(F, prob, Tsit5(); kwargs...)

where `kwargs` is passed to `DiffEqBase::solve`. If your vector field depends on parameters `p`, you can define a `Flow` using

	fl = Flow(F, p, prob, Tsit5(); kwargs...)

Finally, you can pass two `ODEProblem` where the second one is used to compute the variational equation:

	fl = Flow(F, p, prob1::ODEProblem, alg1, prob2::ODEProblem, alg2; kwargs...)

"""
@with_kw struct Flow{TF, Tf, Tts, Tff, Td, Tse, Tprob, TprobMono, Tfs}
	"The vector field `(x, p) -> F(x, p)` associated to a Cauchy problem,"
	F::TF = nothing

	"The flow (or semigroup) associated to the Cauchy problem `(x, p, t) -> flow(x, p, t)`. Only the last time point must be returned."
	flow::Tf = nothing

	"Flow which returns the tuple (t, u(t)). Optional, mainly used for plotting on the user side. Please use `nothing` as default."
	flowTimeSol::Tts = nothing

	"The flow (or semigroup) associated to the Cauchy problem `(x, p, t) -> flow(x, p, t)`. The whole solution on the time interval [0,t] must be returned. It is not strictly necessary to provide this, mainly used for plotting on the user side. Please use `nothing` as default."
	flowFull::Tff = nothing

	"The differential `dflow` of the flow w.r.t. `x`, `(x, p, dx, t) -> dflow(x, p, dx, t)`. One important thing is that we require `dflow(x, dx, t)` to return a Named Tuple: `(t = t, u = flow(x, p, t), du = dflow(x, p, dx, t))`, the last component being the value of the derivative of the flow."
	dflow::Td = nothing

	"Serial version of dflow. Used internally when using parallel multiple shooting. Please use `nothing` as default."
	dfSerial::Tse = nothing

	"[Internal] store the ODEProblem associated to the flow of the Cauchy problem"
	prob::Tprob = nothing

	"[Internal] store the ODEProblem associated to the flow of the variational problem"
	probMono::TprobMono = nothing

	"[Internal] Serial version of the flow"
	flowSerial::Tfs = nothing
end

# constructors
Flow(F, fl, df = nothing) = Flow(F = F, flow = fl, dflow = df, dfSerial = df)

# callable struct
(fl::Flow)(x, p, t; k...)     			  			= fl.flow(x, p, t; k...)
(fl::Flow)(x, p, dx, t; k...) 	  					= fl.dflow(x, p, dx, t; k...)
(fl::Flow)(::Val{:Full}, x, p, t; k...) 	  		= fl.flowFull(x, p, t; k...)
(fl::Flow)(::Val{:TimeSol}, x, p, t; k...)  		= fl.flowTimeSol(x, p, t; k...)
(fl::Flow)(::Val{:SerialTimeSol}, x, p, t; k...)   	= fl.flowSerial(x, p, t; k...)
(fl::Flow)(::Val{:SerialdFlow}, x, p, dx, t; k...)  = fl.dfSerial(x, p, dx, t; k...)

"""
Creates a Flow variable based on a `prob::ODEProblem` and ODE solver `alg`. The vector field `F` has to be passed, this will be resolved in the future as it can be recovered from `prob`. Also, the derivative of the flow is estimated with finite differences.
"""
# this constructor takes into accound a parameter passed to the vector field
function Flow(F, p, prob::Union{ODEProblem, EnsembleProblem}, alg; kwargs...)
	probserial = prob isa EnsembleProblem ? prob.prob : prob
	return Flow(F = F,
		flow = (x, p, t; kw2...) -> flow(x, p, t, prob; alg = alg, kwargs..., kw2...),

		flowTimeSol = (x, p, t; kw2...) -> flowTimeSol(x, p, t, prob; alg = alg, kwargs..., kw2...),

		flowFull = (x, p, t; kw2...) -> flowFull(x, p, t, prob; alg = alg, kwargs..., kw2...),

		dflow = (x, p, dx, t; kw2...) -> dflow_fd(x, p, dx, t, prob; alg = alg, kwargs..., kw2...),

		# serial version of dflow. Used for the computation of Floquet coefficients
		dfSerial = (x, p, dx, t; kw2...) -> dflow_fd(x, p, dx, t, probserial; alg = alg, kwargs..., kw2...),

		flowSerial = (x, p, t; kw2...) -> flowTimeSol(x, p, t, probserial; alg = alg, kwargs..., kw2...),

		prob = prob, probMono = nothing,
		)
end

function Flow(F, p, prob1::Union{ODEProblem, EnsembleProblem}, alg1, prob2::Union{ODEProblem, EnsembleProblem}, alg2; kwargs...)
	probserial1 = prob1 isa EnsembleProblem ? prob1.prob : prob1
	probserial2 = prob2 isa EnsembleProblem ? prob2.prob : prob2
	return Flow(F = F,
		flow = (x, p, t; kw2...) -> flow(x, p, t, prob1, alg = alg1; kwargs..., kw2...),

		flowTimeSol = (x, p, t; kw2...) -> flowTimeSol(x, p, t, prob1; alg = alg1, kwargs..., kw2...),

		flowFull = (x, p, t; kw2...) -> flowFull(x, p, t, prob1, alg = alg1; kwargs..., kw2...),

		dflow = (x, p, dx, t; kw2...) -> dflow(x, p, dx, t, prob2; alg = alg2, kwargs..., kw2...),

		# serial version of dflow. Used for the computation of Floquet coefficients
		dfSerial = (x, p, dx, t; kw2...) -> dflow(x, p, dx, t, probserial2; alg = alg2, kwargs..., kw2...),

		flowSerial = (x, p, t; kw2...) -> flowTimeSol(x, p, t, probserial1; alg = alg1, kwargs..., kw2...),

		prob = prob1, probMono = prob2,
		)
end


@testset "$TEST $G" begin

if !@isdefined(is_nonbacktracking)
    #is_nonbacktracking: a predicate that tests if a walk is nonbacktracking.
    #    That is that for no index i walk[i+2]==walk[i]
    function is_nonbacktracking(walk)
        n = length(walk)
        if n < 3
           return true
        end
        for i in 1:n-2
            if walk[i+2] == walk[i]
                return false
            end
        end
        return true
    end

    #test_nbw: check that a walk is nonbacktracking and print it if is isn't.
    #Used only for testing and debugging.
    function test_nbw(g, start, len)
        w = nonbacktracking_randomwalk(g, start, len)
        if is_nonbacktracking(w)
            return true
        else
            print("walk was:\n  $w")
        end
        return false
    end
end

g = PathDiGraph(10, DG)
@test randomwalk(g, 1, 5) == [1:5;]
@test randomwalk(g, 2, 100) == [2:10;]
@test_throws BoundsError randomwalk(g, 20, 20)

g = PathGraph(10, G)
@test self_avoiding_randomwalk(g, 1, 20) == [1:10;]
@test_throws BoundsError self_avoiding_randomwalk(g, 20, 20)

g = PathGraph(10, G)
@test nonbacktracking_randomwalk(g, 1, 20) == [1:10;]
@test_throws BoundsError nonbacktracking_randomwalk(g, 20, 20)

g = digraph(PathGraph(10, G))
@test nonbacktracking_randomwalk(g, 1, 20) == [1:10;]
@test_throws BoundsError nonbacktracking_randomwalk(g, 20, 20)

g = PathDiGraph(10, DG)
@test nonbacktracking_randomwalk(g, 10, 20) == [10]

g = PathDiGraph(10, DG)
@test nonbacktracking_randomwalk(g, 1, 20) == [1:10;]

g = CycleGraph(10, G)
visited = nonbacktracking_randomwalk(g, 1, 20)
@test visited == [1:10; 1:10;] || visited == [1; 10:-1:1; 10:-1:2;]

g = CycleDiGraph(10, DG)
@test nonbacktracking_randomwalk(g, 1, 20) == [1:10; 1:10;]

n = 10
g = CycleGraph(n, G)
for k = 3:n-1
    add_edge!(g, 1, k)
end

for len = 1:3*n
    @test test_nbw(g,1,len)
    @test test_nbw(g,2,len)
end
#test to make sure it works with self loops.
add_edge!(g, 1, 1)
for len = 1:3*n
    @test test_nbw(g,1,len) == true
    @test test_nbw(g,2,len) == true
end

end # testset


using SparseArrays
using LinearAlgebra
using Plots

include("vekit.jl")
include("invvekit.jl")

let
    n=100
    # Matrix definieren
    B=Matrix(n*n*spdiagm(-1 => -ones(n-2),0 => 2*ones(n-1), 1 => -ones(n-2)))
    # y erstellen
    y=[]
    for k in 1:n-1
        y=[y;k]
    end
    m=0
    e=10^-3
    # größter Eigenvektor
    l_1,y_1=vekit(B,y,e)
    # kleinster Eigenvektor
    l_n1,y_n1=invvekit(B,y,m,e) #TODO: Tut nicht, wegen Fehler in invvekit

    #TODO: Plot
end


using ProximalAlgorithms
using ProximalOperators
using LinearAlgebra
using Random
using Test

@testset "Nonconvex QP (tiny, $T)" for T in [Float64]
    Q = Matrix(Diagonal(T[-0.5, 1.0]))
    q = T[0.3, 0.5]
    low = T(-1.0)
    upp = T(+1.0)

    f = Quadratic(Q, q)
    g = IndBox(low, upp)

    n = 2

    Lip = maximum(diag(Q))
    gamma = T(0.95) / Lip

    @testset "PANOC" begin
        x0 = zeros(T, n)
        x0_backup = copy(x0)
        solver = ProximalAlgorithms.PANOC()
        x, it = solver(x0, f = f, g = g)
        z = min.(upp, max.(low, x .- gamma .* (Q * x + q)))
        @test norm(x - z, Inf) / gamma <= solver.tol
        @test x0 == x0_backup
    end

    @testset "PANOCplus" begin
        x0 = zeros(T, n)
        x0_backup = copy(x0)
        solver = ProximalAlgorithms.PANOCplus()
        x, it = solver(x0, f = f, g = g)
        z = min.(upp, max.(low, x .- gamma .* (Q * x + q)))
        @test norm(x - z, Inf) / gamma <= solver.tol
        @test x0 == x0_backup
    end

    @testset "ZeroFPR" begin
        x0 = zeros(T, n)
        x0_backup = copy(x0)
        solver = ProximalAlgorithms.ZeroFPR()
        x, it = solver(x0, f = f, g = g)
        z = min.(upp, max.(low, x .- gamma .* (Q * x + q)))
        @test norm(x - z, Inf) / gamma <= solver.tol
        @test x0 == x0_backup
    end

    @testset "LiLin" begin
        x0 = zeros(T, n)
        x0_backup = copy(x0)
        solver = ProximalAlgorithms.LiLin(gamma = gamma)
        x, it = solver(x0, f = f, g = g)
        z = min.(upp, max.(low, x .- gamma .* (Q * x + q)))
        @test norm(x - z, Inf) / gamma <= solver.tol
        @test x0 == x0_backup
    end
end

@testset "Nonconvex QP (small, $T)" for T in [Float64]
    @testset "Random problem $k" for k = 1:5
        Random.seed!(k)

        n = 100
        A = randn(T, n, n)
        U, R = qr(A)
        eigenvalues = T(2) .* rand(T, n) .- T(1)
        Q = U * Diagonal(eigenvalues) * U'
        Q = 0.5 * (Q + Q')
        q = randn(T, n)

        low = T(-1.0)
        upp = T(+1.0)

        f = Quadratic(Q, q)
        g = IndBox(low, upp)

        Lip = maximum(abs.(eigenvalues))
        gamma = T(0.95) / Lip

        TOL = 1e-4

        @testset "PANOC" begin
            x0 = zeros(T, n)
            x0_backup = copy(x0)
            solver = ProximalAlgorithms.PANOC(tol = TOL)
            x, it = solver(x0, f = f, g = g)
            z = min.(upp, max.(low, x .- gamma .* (Q * x + q)))
            @test norm(x - z, Inf) / gamma <= solver.tol
            @test x0 == x0_backup
        end

        @testset "PANOCplus" begin
            x0 = zeros(T, n)
            x0_backup = copy(x0)
            solver = ProximalAlgorithms.PANOCplus(tol = TOL)
            x, it = solver(x0, f = f, g = g)
            z = min.(upp, max.(low, x .- gamma .* (Q * x + q)))
            @test norm(x - z, Inf) / gamma <= solver.tol
            @test x0 == x0_backup
        end

        @testset "ZeroFPR" begin
            x0 = zeros(T, n)
            x0_backup = copy(x0)
            solver = ProximalAlgorithms.ZeroFPR(tol = TOL)
            x, it = solver(x0, f = f, g = g)
            z = min.(upp, max.(low, x .- gamma .* (Q * x + q)))
            @test norm(x - z, Inf) / gamma <= solver.tol
            @test x0 == x0_backup
        end

        @testset "LiLin" begin
            x0 = zeros(T, n)
            x0_backup = copy(x0)
            solver = ProximalAlgorithms.LiLin(gamma = gamma, tol = TOL)
            x, it = solver(x0, f = f, g = g)
            z = min.(upp, max.(low, x .- gamma .* (Q * x + q)))
            @test norm(x - z, Inf) / gamma <= solver.tol
            @test x0 == x0_backup
        end
    end
end


function genw_sparse(N,m,sd,p)

N    = Int(N);
w    = zeros((N,N));

# recurrent connections
for i=1:N
  for j=1:N
    d = Normal(m,sd)
    b = Bernoulli(p)

    w[i,j] = rand(d)*rand(b);
  end
end

# No autapse
for i=1:N
    w[i,i] = 0.;
end

return w

end