module FEMfunctions

  using LinearAlgebra
  using SparseArrays

  using WriteVTK

  export onedBilinear
  export onedLinForm
  export onedMesh
  export onedQuadratureRule
  export onedShape

  export saveFEMasVTK

  export TriMesh_ElementAdjacency
  export TriMesh_Interpolate
  export TriMesh_ProjectDerivatives
  export TriMesh_PromoteL2Q
  export TriMesh_Search
  export TriMesh_SquareMesh

  export twodBilinear
  export twodLinForm
  export twodMassMatrix
  export twodQuadratureRule
  export twodShape

  export TetMesh_QuadratureRule

  export threedBilinear

  include("onedBilinear.jl")
  include("onedLinForm.jl")
  include("onedMesh.jl")
  include("onedQuadratureRule.jl")
  include("onedShape.jl")

  include("saveFEMasVTK.jl")

  include("TriMesh_ElementAdjacency.jl")
  include("TriMesh_Interpolate.jl")
  include("TriMesh_ProjectDerivatives.jl")
  include("TriMesh_PromoteL2Q.jl")
  include("TriMesh_Search.jl")
  include("TriMesh_SquareMesh.jl")

  include("twodBilinear.jl")
  include("twodLinForm.jl")
  include("twodMassMatrix.jl")
  include("twodQuadratureRule.jl")
  include("twodShape.jl")

  include("TetMesh_QuadratureRule.jl")

  include("threedBilinear.jl")

end


@testset "advection_matrix.jl" begin
    a = 0.0
    b = 2π
    N = 100

    # Test with ClosedIntervalDomain
    domain = ClosedIntervalDomain(a, b)
    C = advection_matrix(domain, N)
    @test C isa SparseMatrixCSC
    @test size(C) == (N + 1, N + 1)
    @test nnz(C) == 2(N + 1)
    d = diag(C)
    @test all(d[2:end-1] .== 0)
    @test d[1] < 0
    @test d[end] > 0

    # Test with PeriodicIntervalDomain
    domain = PeriodicIntervalDomain(a, b)
    C = advection_matrix(domain, N)
    @test C isa SparseMatrixCSC
    @test size(C) == (N, N)
    @test nnz(C) == 2N
    @test all(diag(C) .== 0)
    @test all(diag(C, -1) .< 0)
    @test all(diag(C, +1) .> 0)
    @test C[1, end] < 0
    @test C[end, 1] > 0

    # Concrete unknown domain type
    domain = UnknownDomain()
    @test_throws Exception advection_matrix(domain, N)
end


struct HH3DDoubleLayerNear{K}
  gamma::K
end

function HH3DDoubleLayerNear(;wavenumber=error("wavenumber is a required argument"))
  if iszero(real(wavenumber))
    HH3DDoubleLayerNear(-imag(wavenumber))
  else
    HH3DDoubleLayerNear(wavenumber*im)
  end
end

quaddata(op::HH3DDoubleLayerNear,rs,els) = quadpoints(rs,els,(3,))
quadrule(op::HH3DDoubleLayerNear,refspace,p,y,q,el,qdata) = qdata[1,q]

function kernelvals(op::HH3DDoubleLayerNear,y,p)

  γ = op.gamma
  x = cartesian(p)
  r = y - x
  R = norm(r)
  γR = γ*R

  inv_R = 1/R

  expn = exp(-γR)
  green = expn * inv_R * inv_4pi
  gradgreen = -(γ + inv_R) * green * inv_R * r

  nx = normal(p)

  (;γ, r, R, green, gradgreen, nx)
end

function integrand(op::HH3DDoubleLayerNear,krn,y,f,p)

  ∇G = krn.gradgreen
  nx = krn.nx

  fx = f.value

  ∂G∂n = nx ⋅ ∇G

  return ∂G∂n * fx
end


"""
Compute the stresses (= 1/Vol dE/d(M*lattice), taken at M=I) of an obtained SCF solution.
"""
@timing function compute_stresses_cart(scfres)
    # TODO optimize by only computing derivatives wrt 6 independent parameters
    # compute the Hellmann-Feynman energy (with fixed ψ/occ/ρ)
    function HF_energy(lattice::AbstractMatrix{T}) where T
        basis = scfres.basis
        model = basis.model
        new_model = Model(lattice, model.atoms, model.positions;
                          model.n_electrons,
                          magnetic_moments=[], # not used because symmetries explicitly given
                          terms=model.term_types,
                          model.temperature,
                          model.smearing,
                          model.spin_polarization,
                          model.symmetries)
        new_basis = PlaneWaveBasis(new_model,
                                   basis.Ecut, basis.fft_size, basis.variational,
                                   basis.kcoords_global, basis.kweights_global,
                                   basis.kgrid, basis.kshift, basis.symmetries_respect_rgrid,
                                   basis.comm_kpts)
        ρ = DFTK.compute_density(new_basis, scfres.ψ, scfres.occupation)
        energies, _ = energy_hamiltonian(new_basis, scfres.ψ, scfres.occupation;
                                         ρ, scfres.eigenvalues, scfres.εF)
        energies.total
    end
    L = scfres.basis.model.lattice
    Ω = scfres.basis.model.unit_cell_volume
    stresses = ForwardDiff.gradient(M -> HF_energy((I+M) * L), zero(L)) / Ω
    symmetrize_stresses(scfres.basis, stresses)
end


# visualize various lines

function drawLine!(vispath, from::Vector{Float64}, to::Vector{Float64}; scale=0.01,color=RGBA(0,1.0,0,0.5))
  vector = to-from
  len = norm(vector)
  buildline = Float64[len, 0, 0]

  v = norm(buildline-vector) > 1e-10 ? LinearAlgebra.cross(buildline, vector)  : [0,0,1.0]
  axis = v/norm(v)
  angle = acos(dot(vector, buildline)/(len^2) )
  rot = LinearMap( CoordinateTransformations.AngleAxis(angle, axis...) )

  mol = HyperRectangle(Vec(0.0,-scale,-scale), Vec(len,scale,scale))
  molbox = GeometryData(mol, color)

  setgeometry!(vispath, molbox)
  settransform!(vispath, Translation(from...) ∘ rot )
  nothing
end


#=
example that constructs an su3 symmetric tensor, and does a contraction
=#
using TensorKit,SUNRepresentations

t = TensorMap(rand,ComplexF64,Rep[SU{3}]((2,1,0)=>3)*Rep[SU{3}]((2,1,0)=>3)*Rep[SU{3}]((2,0,0)=>1),Rep[SU{3}]((2,1,0)=>3)*Rep[SU{3}]((2,0,0)=>3));
@tensor v[-1;-2] := t[-1 1 2;3 4]*conj(t[-2 1 2;3 4])


using ApproximateBayesianComputation
addprocs(2)
@everywhere importall ApproximateBayesianComputation

@time data = rand(Distributions.Normal(2.0, 0.5), 100)
summaryStatistics = mean(data)

@everywhere sample_prior1 = make_sample_prior(Distributions.Normal(0.0, 0.5))
@everywhere forward_model(μ::Float64) = rand(Distributions.Normal(μ, 0.5), 100)
@everywhere compute_distance(x::Array{Float64, 1}, y::Float64) = abs(mean(x) - y)
threshold = 0.05
N = 200

@time out = abc_standard(summaryStatistics, N, 0.1, sample_prior1, forward_model, compute_distance)
@time outp = pabc_standard(summaryStatistics, N, 0.1, sample_prior1, forward_model, compute_distance)


using ForwardDiff
using DelimitedFiles
using MAT

function nn(ε, ε0, σ0)
    local y, y1, y2, y3
    if nntype=="linear"
        y = 200 * ε
    elseif nntype=="ae_scaled"
        x = [ε ε0 σ0/stress_scale]
        y = ae(x, [20,20,20,20,1], "ae_scaled")*stress_scale
    elseif nntype=="piecewise"
        x = [ε ε0 σ0/stress_scale]
        H = ae(x, [20,20,20,20,1], "piecewise")^2*stress_scale
        s = σ0^2
        i = sigmoid((s-0.01)/0.001)
        # @show H, i
        out = ( H .* i + 200 * (1-i) ) .* (ε-ε0) + σ0
    else
        error("nntype must be specified.")
    end

end

function sigmoid_(z)

    return 1.0 / (1.0 + exp(-z))

end

function post_nn(ε::Float64, ε0::Float64, σ0::Float64, Δt::Float64)
    # @show "Post NN"
    local f, df
    if nntype=="piecewise"
        # error()
        f = x -> begin
            H = nnpiecewise(reshape([x;ε0;σ0/stress_scale],1,3))[1,1]^2*stress_scale
            s = σ0^2
            i = sigmoid_((s-0.01)/0.001)
            ( H * i + 200 * (1-i) ) * (ε-ε0) + σ0
        end
        df = ForwardDiff.derivative(f, ε)
    elseif nntype=="ae_scaled"
        f = x -> nnae_scaled(reshape([x;ε0;σ0/stress_scale],1,3))[1,1]*stress_scale
        df = ForwardDiff.derivative(f, ε)
    end
    return f(ε), df
end


isspace(x, y, z = 1364) = iseven(count_ones(x^2 + 3x + 2x * y + y + y^2 + z))

neighbours(c::CartesianIndex) = (c + n for n in (
    CartesianIndex(0, 1),
    CartesianIndex(0, -1),
    CartesianIndex(1, 0),
    CartesianIndex(-1, 0)
))

function visitavailable(magic = 1364)
    queue = [CartesianIndex(1, 1) => 0]
    visited = Dict{CartesianIndex,Int}()
    while !isempty(queue)
        coord, visits = popfirst!(queue)
        if coord[1] >= 0 &&
           coord[2] >= 0 &&
           isspace(coord[1], coord[2], magic) &&
           (!haskey(visited, coord) || visited[coord] > visits)
            visited[coord] = visits
            push!(queue, (n => visits + 1 for n in neighbours(coord))...)
        end
    end
    visited
end
part1(x, y, magic = 1364) = visitavailable(magic)[CartesianIndex(x, y)]

@assert part1(7, 4, 10) == 11
part1(31, 39)

part2() = count(<=(50), values(visitavailable(1364)))
part2()

function prettyprint(magic::Integer = 1364)
    visited = visitavailable(magic)
    maxi = maximum(keys(visited))
    for j in 0:maxi[2]
        for i in 0:maxi[1]
            coord = CartesianIndex(i, j)
            if coord == CartesianIndex(1, 1)
                printstyled('X', color = :blue)
            elseif haskey(visited, coord)
                printstyled('O', color = visited[coord] <= 50 ? :green : :light_red)
            elseif isspace(i, j, magic)
                printstyled('.', color = :light_black)
            else
                print('#')
            end
        end
        println()
    end
end
# prettyprint()
# prettyprint(10)


A=[0 2;0 0];
t=(-5,0.5,5);

X1=repmat([t;]',length(t));
X2=repmat([t;],1,length(t));

#Let exp(A*t)=E
E=[1 2.*t;0 1];

using PyPlot

quiver(X1,X2,E,1)
plot(X[:,1],X[:,2],label="Problem_9_1",colour="r")


using ExamplePackage, Test

@testset "Test my_f evaluation" begin
    # 2*2+3*1 = 7
    @test my_f(2,1) == 7
    @test my_f(3,2) == 12
    @test my_f(3,1) == 9
end

@testset "Test my_f derivative" begin
    @test derivative_of_my_f(2,1) == 2
end


# Used by Interpolations.jl

"""
Given a type `T`, an integer `n` and `m` tuples of the form (i, j, v), build
sparse matrices `rows`, `vals`, `cols` such that the product
`out = rows * vals * cols` is equivalent to:

```julia
out = zeros(T, n, n)

for (i, j, v) in args
    out[i, j] = v
end
```

The first two components (`i` and `j`) of each tuple should be integers
whereas the third component should be of type `T`

Example:

```
julia> r, v, c = WoodburyMatrices.sparse_factors(Float64, 3,
                                                 (1, 1, 2.0),
                                                 (2, 2, 3.0),
                                                 (3, 3, 4.0));

julia> r*c*v - Diagonal([2.0, 3.0, 4.0])
3x3 sparse matrix with 0 Float64 entries:
```

"""
function sparse_factors(::Type{T}, n::Int, args::Tuple{Int, Int, Any}...) where {T}
    m = length(args)
    rows = spzeros(T, n, m)
    cols = spzeros(T, m, n)
    vals = zeros(T, m, m)

    ix = 1
    for (i, (row, col, val)) in enumerate(args)
        rows[row, ix] = 1
        cols[ix, col] = 1
        vals[ix, ix] = val
        ix += 1
    end

    rows, vals, cols
end


include("gumbel_softmax.jl")

using ReverseDiff

const OP = [+, -, *, /]

X = collect(1:5)
gt1(x) = x + 1 #(1,1,2)
gt2(x) = x - 2  #(2,1,3)
gt3(x) = x * 3  #(3,1,4)
gt4(x) = x / 3  #(4,1,4)

gt = gt1

function f1(ps1, ps2, ps3, x)
    T = 0.3
    logits = log.(ps1)
    w = gumbel_softmax(logits, T, true)
    return w' * [op.(f2(ps2,x), f3(ps3,x)) for op in OP]
end
function f2(ps2, x)
    T = 0.3
    logits = log.(ps2)
    w = gumbel_softmax(logits, T, true)
    return w' * [x, 1, 2, 3]
end
function f3(ps3, x)
    T = 0.3
    logits = log.(ps3)
    w = gumbel_softmax(logits, T, true)
    return w' * [x, 1, 2, 3]
end

function objective(ps1, ps2, ps3, x, y_target)
    abs2.(y_target - f1(ps1, ps2, ps3, x))
end

using DataFrames, PGFPlots, TikzPictures
TikzPictures.standaloneWorkaround(true)
function learn(α=0.0002, N=5000; track=false,
               D=track ? DataFrame([Int, Int, Float64, Float64, Float64, Float64], [:iter, :node, :p1, :p2, :p3, :p4], 0) : 0)
    ps1 = ones(length(OP)) ./ length(OP)
    ps2 = ones(4) ./ 4
    ps3 = ones(4) ./ 4
    Y = [gt(x) for x in X]
    for i = 1:N
        @show i
        @show ps1
        @show ps2
        @show ps3
        if track
            push!(D, vcat(i, 1, ps1))
            push!(D, vcat(i, 2, ps2))
            push!(D, vcat(i, 3, ps3))
        end
        for j = 1:length(Y)
            g1, g2, g3, _, _ = ReverseDiff.gradient(objective, (ps1, ps2, ps3, [X[j]], [Y[j]]))
            #@show g
            if !any(isnan.(g1))
                ps1 -= α * g1
            end
            if !any(isnan.(g2))
                ps2 -= α * g2
            end
            if !any(isnan.(g3))
                ps3 -= α * g3
            end
            ps1 = max.(min.(ps1, 1.0), 1e-12)
            normalize!(ps1,1)
            ps2 = max.(min.(ps2, 1.0), 1e-12)
            normalize!(ps2,1)
            ps3 = max.(min.(ps3, 1.0), 1e-12)
            normalize!(ps3,1)
        end
        if isapprox(maximum(ps1),1) && isapprox(maximum(ps2),1) && isapprox(maximum(ps3),1)
            break
        end
    end
    return D, indmax(ps1), indmax(ps2), indmax(ps3)
end

function plot_me(D::DataFrame)
    g = GroupPlot(3, 1, groupStyle="horizontal sep=1.75cm, vertical sep=1.5cm")
    for i = 1:3
        ax = Axis([
                   Plots.Linear(D[D[:node].==i,:][:p1], legendentry="node$i p1"),
                   Plots.Linear(D[D[:node].==i,:][:p2], legendentry="node$i p2"),
                   Plots.Linear(D[D[:node].==i,:][:p3], legendentry="node$i p3"),
                   Plots.Linear(D[D[:node].==i,:][:p4], legendentry="node$i p4"),
                  ])
        push!(g, ax)
    end
    save("main2.pdf", g)
    save("main2.tex", g)
end


export find_intersections_brute

"""
    find_intersections_brute(segments::Vector{Segment{T}}) where {T<:AbstractFloat}

Compute all the intersections between the segments performing a brute force algorithm O(n^2).
This is the O(N^2) brute force version in which we test all segments
against all segments for intersections. This method is faster
than the BentleyOttman one when there are few points and lots of intersections.

# Examples
```jldoctest
julia> segments = [Segment(0,0,5,5), Segment(0,5,5,0)];
julia> intersections = find_intersections_brute(segments)
julia> [Point(2.5, 2.5)]
```
"""
function find_intersections_brute(segments::Vector{Segment{T}}) where {T<:AbstractFloat}
    A = zeros((2, 2))
    b = zeros(2)
    n_segments = length(segments)
    intersections = typeof(segments[1].p)[]
    for i = 1:(n_segments-1)
        s1 = segments[i]
        for j = (i+1):n_segments
            s2 = segments[j]
            do_intersect, intersection = intersect!(s1, s2, A, b)
            if do_intersect
                push!(intersections, intersection)
            end
        end
    end
    return intersections
end


using ExplainableAI: augment_batch_dim, augment_indices, reduce_augmentation
using ExplainableAI: interpolate_batch

# augment_batch_dim
A = [1 2; 3 4]
B = @inferred augment_batch_dim(A, 3)
@test B == [
    1 1 1 2 2 2
    3 3 3 4 4 4
]
B = @inferred augment_batch_dim(A, 4)
@test B == [
    1 1 1 1 2 2 2 2
    3 3 3 3 4 4 4 4
]
A = reshape(1:8, 2, 2, 2)
B = @inferred augment_batch_dim(A, 3)
@test B[:, :, 1] == A[:, :, 1]
@test B[:, :, 2] == A[:, :, 1]
@test B[:, :, 3] == A[:, :, 1]
@test B[:, :, 4] == A[:, :, 2]
@test B[:, :, 5] == A[:, :, 2]
@test B[:, :, 6] == A[:, :, 2]

# augment_batch_dim
inds = [CartesianIndex(5, 1), CartesianIndex(3, 2)]
augmented_inds = @inferred augment_indices(inds, 3)
@test augmented_inds == [
    CartesianIndex(5, 1)
    CartesianIndex(5, 2)
    CartesianIndex(5, 3)
    CartesianIndex(3, 4)
    CartesianIndex(3, 5)
    CartesianIndex(3, 6)
]

# reduce_augmentation
A = Float32.(reshape(1:10, 1, 1, 10))
R = @inferred reduce_augmentation(A, 5)
@test R == reshape([sum(1:5), sum(6:10)] / 5, 1, 1, :)
A = Float64.(reshape(1:10, 1, 1, 1, 1, 10))
R = @inferred reduce_augmentation(A, 2)
@test R == reshape([3, 7, 11, 15, 19] / 2, 1, 1, 1, 1, :)

x = Float16.(reshape(1:4, 2, 2))
x0 = zero(x)
A = @inferred interpolate_batch(x, x0, 5)
@test A ≈ [
    0.0 0.25 0.5 0.75 1.0 0.0 0.75 1.5 2.25 3.0
    0.0 0.5 1.0 1.5 2.0 0.0 1.0 2.0 3.0 4.0
]


#
#Function to calculate the distance between two points considering PBC
#
function distpbc(p1,p2,side)
           dx = p1[1] - p2[1]
            x = mod(dx,side)
             if x > side/2
              x = x - side
             end
             if x < -side/2
              x = x + side
             end
           dy = p1[2] - p2[2]
            y = mod(dy,side)
             if y > side/2
              y = y - side
             end
             if y < -side/2
              y = y + side
             end
             d12 = sqrt(x^2 + y^2)
           end


### A Pluto.jl notebook ###
# v0.17.0

using Markdown
using InteractiveUtils

# This Pluto notebook uses @bind for interactivity. When running this notebook outside of Pluto, the following 'mock version' of @bind gives bound variables a default value (instead of an error).
macro bind(def, element)
    quote
        local el = $(esc(element))
        global $(esc(def)) = Core.applicable(Base.get, el) ? Base.get(el) : missing
        el
    end
end

# ╔═╡ 104b6690-212b-11ec-3a2b-25c420f62534
begin
	using Plots
	using LinearAlgebra
	using Kronecker
	using PlutoUI
	using SparseArrays
	#https://juliapackages.com/p/kronecker
	md"""## Packages"""
end

# ╔═╡ 21712488-f636-4861-913c-012693cb524d
md"""# Order of tensor product basis (TPB)"""

# ╔═╡ 89b58dd0-c70c-4566-b991-2d739990f2ff
md"""
Note, that throughout the thesis a colexicographical ordering of the tensor product is preferred:
$\ket{a_1 b_1}, \ket{a_2 b_1}, \ket{a_1 b_2}, \ket{a_2 b_2}$. In this case the tensor product is realized via the conventional Kronecker product but using flipped arguments:
$A \otimes B \rightarrow \boldsymbol{B} \otimes \boldsymbol{A}$.

Here you can choose the ordering of the tensor product basis of the tensor product ``\mathcal{H}_{\mathcal{A}} \otimes \mathcal{H}_{\mathcal{B}}``:

👉 $(@bind order Select(["colex" => "Colexicographical order", "lex" => "Lexicographical order"]))
"""

# ╔═╡ 23c019dd-53d2-461e-a6ba-f1b5da2f6710
if order == "colex"
md"""
``\begin{pmatrix}{\color{magenta}a}&{\color{red}C}\\ \boldsymbol{b} & D   \end{pmatrix} \overset{\text{c}} {\otimes}\begin{pmatrix} 1 & \large 3 \\ {\color{blue}2} & \scriptsize {\color{green}4}\end{pmatrix} = \begin{pmatrix}{\color{magenta}a}1 & {\color{red}C}1 & {\color{magenta}a} \large 3 & {\color{red}C} \large 3 \\\boldsymbol{b}1 & D1 & \boldsymbol{b} \large 3 & D \large 3 \\ {\color{magenta}a}{\color{blue}2} & {\color{red}C}{\color{blue}2} & {\color{magenta}a}\scriptsize {\color{green}4} & {\color{red}C}\scriptsize {\color{green}4} \\ \boldsymbol{b}{\color{blue}2} & D{\color{blue}2} & \boldsymbol{b}\scriptsize {\color{green}4} & D\scriptsize {\color{green}4} \end{pmatrix}, \quad A \overset{\text{c}}{\otimes} B:= \textbf{B} \otimes \textbf{A}``
"""
else
md"""
``\begin{pmatrix}{\color{magenta}a}&{\color{red}C}\\ \boldsymbol{b} & D   \end{pmatrix} {\otimes}\begin{pmatrix} 1 & \large 3 \\ {\color{blue}2} & \scriptsize {\color{green}4}\end{pmatrix}  = \begin{pmatrix}{\color{magenta}a}1 & {\color{magenta}a} \large 3 & {\color{red}C}1 & {\color{red}C} \large 3 \\{\color{magenta}a}{\color{blue}2} & {\color{magenta}a}\scriptsize {\color{green}4} & {\color{red}C}{\color{blue}2} & {\color{red}C}\scriptsize {\color{green}4} \\  \boldsymbol{b}1 & \boldsymbol{b} \large 3 & D1 & D \large 3 \\  \boldsymbol{b}{\color{blue}2} & \boldsymbol{b}\scriptsize {\color{green}4} & D{\color{blue}2} & D\scriptsize {\color{green}4} \end{pmatrix}``
"""
end

# ╔═╡ f5992b42-ff99-479a-b093-70a9bfd85655
@show order

# ╔═╡ 0a95d032-8576-492f-bee1-ef67303bcb3a
begin
	if order == "colex"
		⊗(A,B) = kron(B,A)
	else
		⊗(A,B) = kron(A,B)
	end
end

# ╔═╡ 7458474c-3c4d-4a99-9640-4bfd2e6465ea
@bind 🍎 Scrubbable(1:10)

# ╔═╡ edf8bfba-ca6b-4adf-9a7d-e618ca511a80
@bind time_r Clock()

# ╔═╡ d7f3dc01-1201-41de-8a63-c3e2bc020cf6
σ₄ = 5

# ╔═╡ 2e2854f6-8cab-4252-a580-7abc71efd2a0
rand(2,2)  ⊗ I(2)

# ╔═╡ a646342f-3177-4579-8625-cb7d7ea27a49
🍎 + σ₄ + time_r

# ╔═╡ 792748ec-199c-41d8-a801-6cd97881ee72
🍎*4

# ╔═╡ 85bca31d-c3be-46b6-b330-b80648472378
md"""
# System Hamiltonian of bipartite system
"""

# ╔═╡ ebd5efea-94b6-419e-8ff1-9dcf624ddaba
md"""
The system Hamiltonian for a bipartite system $\mathcal{H}_{\mathcal{A}} \otimes \mathcal{H}_{\mathcal{B}}$ is given by

$H = \sum_{i = \{\mathcal{A}, \mathcal{B}\}} \varepsilon_i \hat{n}_i + \sum_{i=1}^3 \alpha_i (\mathbb{1}_{\mathcal{A}} \otimes {\sigma}_i) + \sum_{i=1}^3 \beta_i ({\sigma}_i \otimes \mathbb{1}_{\mathcal{B}}) + \sum_{ij=1}^3 \gamma_{ij} ({\sigma}_i \otimes {\sigma}_j)$

with Pauli matrices
$\sigma_1 = \sigma_x = \begin{pmatrix} 0 & 1 \\ 1 & 0  \end{pmatrix}$,
$\sigma_2 = \sigma_y = \begin{pmatrix} 0 & -i \\ i & 0  \end{pmatrix}$,
$\sigma_3 = \sigma_z = \begin{pmatrix} 1 & 0 \\ 0 & -1\end{pmatrix}$

"""

# ╔═╡ 913d405a-e525-49d6-9021-227543b37953
md"""
# Initial state ``ρ(t_0)``
"""

# ╔═╡ a36e5515-e7f9-4c34-a48e-52a004d80f23
md"""
Switch from different initial states 👉 $(@bind initial_state Select(["thesis" => "Example in 📖", "example" => "Another example 📍","slider"=> "Choose your own initial state 📐"]))
"""

# ╔═╡ 7479e663-e533-442c-8324-c60f4849c470
md"""
# Dynamical map for different initial states
"""

# ╔═╡ 0872a46c-4f4b-49fa-8152-9193b71f332b
md"""
## Initial product state
"""

# ╔═╡ 5e1377b7-c784-4e42-8190-ffbf48f0ecc1
md"""
##  Initial classical-quantum correlated state
"""

# ╔═╡ 822ea5ab-4a3a-45ee-903c-ff9bad9d5f16
md"""
## Example of non--positive dynamical map
"""

# ╔═╡ 4cdab1a4-1784-4430-ae4f-50a4459d83a4
md"""
# Example of a non-accessible map
The transposition superoperator

"""

# ╔═╡ a558b847-3f09-473c-9ec5-70badcda7589
md"""
# Appendix: Functions and Initializations
"""

# ╔═╡ 8407fc8c-95b4-4536-bbda-b3f4945518b0
TableOfContents()

# ╔═╡ 7f43573f-010f-455a-82c7-f2f509cce3d3
begin
	time_slider = @bind time Slider(0:0.1:20, default = 1, show_value = true)
	md""" ## Interactive widgets
	Definition of Sliders"""
end

# ╔═╡ e8c989d6-9ba6-40d1-ad36-d768172d5f5e
time_slider

# ╔═╡ 002504f4-e74a-4e8c-80fa-f4c5f2e90df0
time_slider

# ╔═╡ d28b8274-7435-484a-bde7-61443d7e4ae2
begin
	scrub_1 = @bind s1 Scrubbable(-0.5:0.01:0.5, format=".03")
	scrub_2 = @bind s2 Scrubbable(-0.5:0.01:0.5, format=".03")
	scrub_3 = @bind s3 Scrubbable(-0.5:0.01:0.5, format=".03")
	scrub_4 = @bind s4 Scrubbable(-0.5:0.01:0.5, format=".03")
	scrub_5 = @bind s5 Scrubbable(-0.5:0.01:0.5, format=".03")
	scrub_6 = @bind s6 Scrubbable(-0.5:0.01:0.5, format=".03")
	scrub_7 = @bind s7 Scrubbable(-0.5:0.01:0.5, format=".03", default = 0.25)
	scrub_8 = @bind s8 Scrubbable(-0.5:0.01:0.5, format=".03")
	scrub_9 = @bind s9 Scrubbable(-0.50:0.01:0.50, format=".03")

	scrub_a1 = @bind a1 Scrubbable(-0.50:0.01:0.50, format=".03", default = -0.5)
	scrub_a2 = @bind a2 Scrubbable(-0.50:0.01:0.50, format=".03")
	scrub_a3 = @bind a3 Scrubbable(-0.50:0.01:0.50, format=".03")

	scrub_b1 = @bind b1 Scrubbable(-0.50:0.01:0.50, format=".03", default = -0.5)
	scrub_b2 = @bind b2 Scrubbable(-0.50:0.01:0.50, format=".03")
	scrub_b3 = @bind b3 Scrubbable(-0.50:0.01:0.50, format=".03")

	scrub_i1 = @bind i1 Scrubbable(-0.50:0.01:0.50, format=".03", default = -0.5)
	scrub_i2 = @bind i2 Scrubbable(-0.50:0.01:0.50, format=".03")
	scrub_i3 = @bind i3 Scrubbable(-0.50:0.01:0.50, format=".03")

	md" Definition of Scrubbables"
end

# ╔═╡ fc12024b-2785-4f89-a457-95a2deb5045f
begin
	if initial_state == "slider"
		md"""
		You can choose the parameters of the initial state:

		$\rho(t_0) =\frac{1}{4}\mathbb{1}_{\mathcal{A}} \otimes \mathbb{1}_{\mathcal{B}}+ \sum_{i=1}^3 \alpha_i (\mathbb{1}_{\mathcal{A}} \otimes {\sigma}_i) + \sum_{i=1}^3 \beta_i ({\sigma}_i \otimes \mathbb{1}_{\mathcal{B}}) + \sum_{ij=1}^3 \gamma_{ij} ({\sigma}_i \otimes {\sigma}_j)$

		``\alpha_1`` 👉 $(scrub_a1),  ``\alpha_2`` 👉 $(scrub_a2), ``\alpha_3`` 👉 $(scrub_a3)

		``\beta_1`` 👉 $(scrub_b1),  ``\beta_2`` 👉 $(scrub_b2), ``\beta_3`` 👉 $(scrub_b3)

		``\gamma_{11}`` 👉 $(scrub_1),  ``\gamma_{12}`` 👉 $(scrub_2), ``\gamma_{13}`` 👉 $(scrub_3)

		``\gamma_{21}`` 👉 $(scrub_4),  ``\gamma_{22}`` 👉 $(scrub_5), ``\gamma_{23}`` 👉 $(scrub_6)

		``\gamma_{31}`` 👉 $(scrub_7),  ``\gamma_{32}`` 👉 $(scrub_8), ``\gamma_{33}`` 👉 $(scrub_9)

		"""

	end
end

# ╔═╡ 1e4adc4d-52cf-41df-ad62-cab016f55397
[scrub_i1, scrub_i2, scrub_i3]

# ╔═╡ 0c09a3b6-920d-4732-af67-86b36bd88292
scrub_a1, scrub_a2, scrub_a3

# ╔═╡ dc95c17d-de0f-4b72-b57e-0dab473a69cd
[scrub_1,scrub_2,scrub_3,scrub_4, scrub_5, scrub_6, scrub_7, scrub_8, scrub_9]

# ╔═╡ 75ebae9f-1c69-4d92-9cdf-a0f4b0971576
begin
σ = [[0 1; 1 0],  [0 -im; im 0],  [1 0; 0 -1]]

	function  qu_bit_system(epsilon  = 1,
			alpha = [0,0,0],
			beta= [0,0,0],
			gamma = zeros(3,3))

	return H = epsilon * kron(I(2),I(2)) +
		sum([(I(2) ⊗ σ[i]) * alpha[i] for i = 1:3]) +
		sum([(σ[i] ⊗ I(2)) * beta[i] for i=1:3]) +
		sum([gamma[i,j] * (σ[i] ⊗ σ[j]) for i=1:3, j=1:3])
	end

	function density_pauli(α)
		return α[1]/2 * I(2) + sum([α[i+1] * σ[i] for i = 1:3])
	end

	function density_matrix(n, neg = 0, real_valued = false)
		#trace 1 remains preserved!
		dia = rand(n)
		if neg > 0
			dia[rand(1:n, neg)] .*= -1
		end
		dia = dia/sum(dia)
		if real_valued
			temp = rand(n,n)
		else
			temp = rand(n,n) + rand(n,n) * im
		end
		ew, ev = eigen(Hermitian(temp'*temp))
		d_mat = Hermitian(ev* Diagonal(dia) * ev')
		return d_mat
	end

	function unitary(n)
		A = (rand(n,n) .-0.5) + im*(rand(n,n) .-0.5)
		return eigvecs(A'+A)
	end

	function unity(i,n)
		E = spzeros(Bool,n,n)
		E[i] = true
    	return E
	end
	function partial_trace_matrix(dim_1, dim_2, traced_system, order="lex")
# traces out the system indicated by traced_system
# for C = kron(A,B), tracing over the first system (traced_system = 1);
# yields: reshape(p1*C[:], 4,4)/tr(A)  == B
#
# when colex ordered:
# flip traced_system

		if order == "colex"
			traced_system = 3 - traced_system
		end

		if traced_system == 1
			T = [kron(sparse(I, dim_1,dim_1), unity(i,dim_2))[:] for i = 1:dim_2^2]
			col = reduce(vcat,[findnz(T[i])[1] for i = 1:dim_2^2])
			row = kron(1:dim_2^2, ones(Int64,dim_1))
			return sparse(row, col, trues(dim_2^2*dim_1))
		else
			T = [kron(unity(i, dim_1), sparse(I, dim_2, dim_2))[:] for i = 1:dim_1^2]
			col = reduce(vcat, [findnz(T[i])[1] for i = 1:dim_1^2])
			row = kron(1:dim_1^2, ones(Int64,dim_2))
			return sparse(row, col, trues(dim_1^2*dim_2))
			#return    Tr_B = [reshape(kron(reshape(unity(i,dim_1), dim_1, dim_1), I(dim_2)), 1, dim_2^2 * dim_1^2)[1,j] for i = 1:dim_1^2, j =1:dim_1^2*dim_2^2]
		end
	end

	function choi(A, dim_1)
		mm, nn = size(A)
		n = mm ÷ dim_1
		return reshape(permutedims(reshape(A,n,dim_1,n,dim_1),(1,3,2,4)), mm,mm)
	end

	function choi_rect(A,dim_1, dim_2, dim_3, dim_4)
		return reshape(permutedims(reshape(A, dim_1, dim_2, dim_3, dim_4), (1,3,2,4)), dim_1*dim_3, dim_2*dim_4)
	end

	function choi_index(i, n)
		return  (i.-1) .% n[1] .+ 1 .+
		n[1] .* ((i.-1) .% prod(n[1:3]) .÷ prod(n[1:2]) .+
		n[3] .* ((i.-1) .% prod(n[1:2]) .÷ n[1] .+
		n[2] .* ((i.-1) .÷ prod(n[1:3]))))
	end

	function ptr(ρ, dim_1, traced_system = 1, order="lex")
		# get partial trace superoperator
		N,M = size(ρ)
		dim_2 = N÷dim_1
		PT = partial_trace_matrix(dim_1, dim_2, traced_system, order)
		new_dim = traced_system == 1 ? dim_2 : dim_1
		# apply superoperator to vectorized density matrix and reshape to new dimensions
		σ = reshape(PT * ρ[:], new_dim, new_dim)
		return σ
	end

	function factorize(A, dim_1, dim_2, order="lex")
		σ = ptr(A, dim_1, 2, order)
		ρ = ptr(A, dim_1, 1, order)
		A_tensor_prod = (σ ⊗ ρ)
		rho_c = A - A_tensor_prod
		return A_tensor_prod, rho_c
	end

	function normed_vector(k)
		v = (rand(k) .- 0.5) + im*(rand(k) .- 0.5)
		return v/norm(v)
	end

	function prob_dist(k)
		p = rand(k)
		return p/sum(p)
	end

	function PPT(rho,dim_1)
		dim_2 = size(rho,1)÷dim_1
		if dim_1 *dim_2 > 6
			error("positive partial trace does not work for too large dimensions")
		end
		return eigvals(reshape(permutedims(reshape(rho,dim_1,dim_2,dim_1,dim_2), [1,4,3,2]),dim_1*dim_2, dim_1*dim_2))

	end

	function sep_state(k=1,n=2)
		VVV = sum([(normed_vector(n) ⊗ normed_vector(n)) for _=1:k] .* (prob_dist(k)))
		return VVV / norm(VVV)
	end

	#test for k-block positivity
	# strategy: generate random matrices in 2x2 and test whether Choi matrix maps to positive operators, if true for all, then positive.
	# alternatively: check k-positivity: k as optional parameter. Create states with Schmidt rank k
	#Schmidt rank:
	function pos_test(choi, k = 1, n = 2)

		runs = 10000
		test = zeros(runs)
		vector = zeros(ComplexF64, n^2)
		for i = 1:runs

			#sum([density_matrix(2) for _=1:k] * p_rand/sum(p_rand))

			VVN = sep_state(k,n)

			test[i] = real(VVN'* choi *VVN)

			if real(test[i]) < 0
				vector = VVN
			end
		end

		return sum(test .> 0) / runs

	end

md"""
# Functions
"""
end

# ╔═╡ c8e02ee8-2c21-4a37-9875-c4a463e0b462
begin
	ϵ = [1,2]
	α = [0,0,0]
	β = [0,0,0]
	γ = [1 0 0; 0 0 0; 0 0 0]

	H = qu_bit_system(0, α, β, γ) + I(2) ⊗ Diagonal([0,ϵ[2]]) +
		Diagonal([0,ϵ[1]]) ⊗ I(2)

end

# ╔═╡ a5e38ca3-8184-4160-a6b1-4c1783c6e185
begin

	if initial_state == "thesis"
		rho_0 = qu_bit_system(1/4, [0.0, 0.00, +0.0],
							  [0.00, -0.0, -0.0], [0 0 0; 0 0 0; 1/4 0 0])
	elseif initial_state == "example"
		rho_0 = qu_bit_system(1/4, [0.05, 0.00, +0.0],
							  [0.00, 0.05, -0.0], [0 0 0.05; 0.05 0 0; 0.05 0 0])
	else
		rho_0 = qu_bit_system(1/4, [a1, a2, a3],
								[b1, b2, b3], [s1 s2 s3; s4 s5 s6;s7 s8 s9])
	end

	# alternative
	Dim = 2
	RHO_b = [density_matrix(Dim) for _=1:Dim]
	RHO_s = density_matrix(Dim)
	evals, Pro = eigen(RHO_s)

	prob = prob_dist(Dim)

	#rho_0 = sum([kron(Pro[:,i] * Pro[:,i]', RHO_b[i]) for i = 1:Dim] .*prob)

	rho_t_0, rho_c = factorize(rho_0,2,2, order)
end

# ╔═╡ 4f87569b-777e-4e97-ac80-8b7b76115d2a
rho_0

# ╔═╡ b595349b-b572-4cc8-92b1-07631cc8fad7
eigvals(rho_0) # eigenvalues of the initial state

# ╔═╡ 8cd0438c-c480-4b73-93af-79e3f3a5dfa9
eigvals(rho_0)

# ╔═╡ 596a9128-2fff-47e8-8b6f-e9fd8686a279
[ptr(rho_0,2,2,order), ptr(rho_0,2,1,order)] #σ(t_0) and ρ_B(t_0)

# ╔═╡ c578581e-467e-482c-9cce-2ad55d32267f
PPT(rho_0,2) #initial state is separable, but is it classical quantum correlated?

# ╔═╡ 8c779399-7db0-421e-b1b2-659d28fc8c41
begin
	#using the product state rho_t_0
	rho_B = ptr(rho_t_0,2,1, order) # the bath denstiy matrix
	sigma_0 = ptr(rho_t_0,2,2, order) # the system density matrix
	compare = sigma_0 ⊗ rho_B - rho_t_0 #check if this is really a product state

	U = exp(-im*H*time)
	#NOTE: the choi matrix notation needs a transpose the match the lexicographical order
	if order == "colex"
		Λ = choi(choi(U,2) * kron(rho_B, I(2)) * choi(U,2)',2)#
	else
		Λ = transpose(choi(transpose(choi(U,2)) * kron(I(2), rho_B) * transpose(choi(U,2))',2)) # this is a real Kronecker product
	end

	Λ_c = ptr(U * rho_c * U', 2, 2, order)[:] * collect(I(2)[:])'

	test = density_matrix(2)
	dyn_1 = reshape(Λ * test[:], 2,2)

	dyn_2 = ptr(U * (test ⊗ rho_B) * U',2,2, order)
	dyn_1 - dyn_2
end

# ╔═╡ 9330d1cf-e0e1-41c1-a876-5fae11633ce8
Λ_c + Λ

# ╔═╡ 04583ab3-2364-4c32-be8e-d3e65ca5321e
round.(Λ + Λ_c, digits=2)

# ╔═╡ 5d450f8d-c8c4-4a58-b2a6-97bfe109f91f
begin
	#check

	norm(ptr(U*rho_0*U',2,2, order)- reshape((Λ+Λ_c) * ptr(rho_0,2,2, order)[:],2,2))

end

# ╔═╡ 4fb4c614-ed74-411e-91db-641c331c75fa
eigvals(choi(Λ + Λ_c,2))
# map is not completely positive

# ╔═╡ 36df576e-5111-4d66-9170-2eb9573cea32
pos_test(choi(Λ + Λ_c,2),1,2)
# dynamical map is not positive

# ╔═╡ 2cab05f7-bc59-4d26-a535-495b5ab104c0
begin
	sig_n = 1/2 * I(2) + i1 * σ[1] + i2 * σ[2] + i3*σ[3]
	#pure state: a1 = -0.5
	eigvals(sig_n)
	#sig_n is not compatible:
	eigvals((sig_n ⊗ rho_B) + rho_c)
end

# ╔═╡ faf456bc-9145-4d87-ad4d-6ecc8c78bd41
eigvals(reshape((Λ + Λ_c)* sig_n[:],2,2))
# yields negative result

# ╔═╡ e63d17ba-767c-4136-8dc3-3da609f8dda5
reshape((Λ + Λ_c)* sig_n[:],2,2)

# ╔═╡ 5695cdc2-0da6-4ef1-9aa9-d553f9b194e4
begin

K_p = [1/2*(I(2) + σ[3]), 1/2*(I(2) - σ[3]), 1/sqrt(2)*σ[1]]
K_m = 1/sqrt(2)*[im * σ[2]]
Λ_t = sum([kron(conj.(K_p[i]), K_p[i]) for i = 1:3]) - kron(conj.(K_m[1]), K_m[1])

choi(Λ_t,2)

C_t = sum([K_p[i][:] * K_p[i][:]' for i = 1:3]) - K_m[1][:] * K_m[1][:]'

C_t - kron(I(2), density_pauli([0,a1,a2,a3]))
end

# ╔═╡ 3140729e-d516-434d-ba32-3ff9705ee83b
begin
keep_working(text=md"The answer is not quite right.", title="Keep working on it!") = Markdown.MD(Markdown.Admonition("danger", title, [text]));

almost(text, title="Almost there!") = Markdown.MD(Markdown.Admonition("warning", title, [text]));

hint(text, title ="Hint") = Markdown.MD(Markdown.Admonition("hint", title, [text]));

correct(text=md"Great! You got the right answer! Let's move on to the next section.", title="Got it!") = Markdown.MD(Markdown.Admonition("correct", title, [text]));
md" Definition of Boxes"
end

# ╔═╡ 5afc42f7-3253-438d-96a8-d665a1b9a1cd
if any(eigvals(rho_0) .< 0)
	keep_working(md"Tune the parameters such, that the initial state becomes positive: ``\rho(t_0)> 0``.

The current eigenvalues are:
		$(string(round.(eigvals(rho_0), digits = 2)))" ,"Initial state  not valid!")
end

# ╔═╡ 00000000-0000-0000-0000-000000000001
PLUTO_PROJECT_TOML_CONTENTS = """
[deps]
Kronecker = "2c470bb0-bcc8-11e8-3dad-c9649493f05e"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
PlutoUI = "7f904dfe-b85e-4ff6-b463-dae2292396a8"
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"

[compat]
Kronecker = "~0.5.0"
Plots = "~1.22.3"
PlutoUI = "~0.7.12"
"""

# ╔═╡ 00000000-0000-0000-0000-000000000002
PLUTO_MANIFEST_TOML_CONTENTS = """
# This file is machine-generated - editing it directly is not advised

[[AbstractFFTs]]
deps = ["LinearAlgebra"]
git-tree-sha1 = "485ee0867925449198280d4af84bdb46a2a404d0"
uuid = "621f4979-c628-5d54-868e-fcf4e3e8185c"
version = "1.0.1"

[[Adapt]]
deps = ["LinearAlgebra"]
git-tree-sha1 = "84918055d15b3114ede17ac6a7182f68870c16f7"
uuid = "79e6a3ab-5dfb-504d-930d-738a2a938a0e"
version = "3.3.1"

[[ArgTools]]
uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f"

[[Artifacts]]
uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33"

[[Base64]]
uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"

[[Bzip2_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "19a35467a82e236ff51bc17a3a44b69ef35185a2"
uuid = "6e34b625-4abd-537c-b88f-471c36dfa7a0"
version = "1.0.8+0"

[[Cairo_jll]]
deps = ["Artifacts", "Bzip2_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "JLLWrappers", "LZO_jll", "Libdl", "Pixman_jll", "Pkg", "Xorg_libXext_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"]
git-tree-sha1 = "f2202b55d816427cd385a9a4f3ffb226bee80f99"
uuid = "83423d85-b0ee-5818-9007-b63ccbeb887a"
version = "1.16.1+0"

[[ChainRulesCore]]
deps = ["Compat", "LinearAlgebra", "SparseArrays"]
git-tree-sha1 = "e8a30e8019a512e4b6c56ccebc065026624660e8"
uuid = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4"
version = "1.7.0"

[[ColorSchemes]]
deps = ["ColorTypes", "Colors", "FixedPointNumbers", "Random"]
git-tree-sha1 = "a851fec56cb73cfdf43762999ec72eff5b86882a"
uuid = "35d6a980-a343-548e-a6ea-1d62b119f2f4"
version = "3.15.0"

[[ColorTypes]]
deps = ["FixedPointNumbers", "Random"]
git-tree-sha1 = "024fe24d83e4a5bf5fc80501a314ce0d1aa35597"
uuid = "3da002f7-5984-5a60-b8a6-cbb66c0b333f"
version = "0.11.0"

[[Colors]]
deps = ["ColorTypes", "FixedPointNumbers", "Reexport"]
git-tree-sha1 = "417b0ed7b8b838aa6ca0a87aadf1bb9eb111ce40"
uuid = "5ae59095-9a9b-59fe-a467-6f913c188581"
version = "0.12.8"

[[Compat]]
deps = ["Base64", "Dates", "DelimitedFiles", "Distributed", "InteractiveUtils", "LibGit2", "Libdl", "LinearAlgebra", "Markdown", "Mmap", "Pkg", "Printf", "REPL", "Random", "SHA", "Serialization", "SharedArrays", "Sockets", "SparseArrays", "Statistics", "Test", "UUIDs", "Unicode"]
git-tree-sha1 = "31d0151f5716b655421d9d75b7fa74cc4e744df2"
uuid = "34da2185-b29b-5c13-b0c7-acf172513d20"
version = "3.39.0"

[[CompilerSupportLibraries_jll]]
deps = ["Artifacts", "Libdl"]
uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae"

[[Contour]]
deps = ["StaticArrays"]
git-tree-sha1 = "9f02045d934dc030edad45944ea80dbd1f0ebea7"
uuid = "d38c429a-6771-53c6-b99e-75d170b6e991"
version = "0.5.7"

[[CovarianceEstimation]]
deps = ["LinearAlgebra", "Statistics", "StatsBase"]
git-tree-sha1 = "bc3930158d2be029e90b7c40d1371c4f54fa04db"
uuid = "587fd27a-f159-11e8-2dae-1979310e6154"
version = "0.2.6"

[[DataAPI]]
git-tree-sha1 = "cc70b17275652eb47bc9e5f81635981f13cea5c8"
uuid = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a"
version = "1.9.0"

[[DataStructures]]
deps = ["Compat", "InteractiveUtils", "OrderedCollections"]
git-tree-sha1 = "7d9d316f04214f7efdbb6398d545446e246eff02"
uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8"
version = "0.18.10"

[[DataValueInterfaces]]
git-tree-sha1 = "bfc1187b79289637fa0ef6d4436ebdfe6905cbd6"
uuid = "e2d170a0-9d28-54be-80f0-106bbe20a464"
version = "1.0.0"

[[Dates]]
deps = ["Printf"]
uuid = "ade2ca70-3891-5945-98fb-dc099432e06a"

[[DelimitedFiles]]
deps = ["Mmap"]
uuid = "8bb1440f-4735-579b-a4ab-409b98df4dab"

[[Distributed]]
deps = ["Random", "Serialization", "Sockets"]
uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b"

[[Downloads]]
deps = ["ArgTools", "LibCURL", "NetworkOptions"]
uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6"

[[EarCut_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "3f3a2501fa7236e9b911e0f7a588c657e822bb6d"
uuid = "5ae413db-bbd1-5e63-b57d-d24a61df00f5"
version = "2.2.3+0"

[[Expat_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "b3bfd02e98aedfa5cf885665493c5598c350cd2f"
uuid = "2e619515-83b5-522b-bb60-26c02a35a201"
version = "2.2.10+0"

[[FFMPEG]]
deps = ["FFMPEG_jll"]
git-tree-sha1 = "b57e3acbe22f8484b4b5ff66a7499717fe1a9cc8"
uuid = "c87230d0-a227-11e9-1b43-d7ebe4e7570a"
version = "0.4.1"

[[FFMPEG_jll]]
deps = ["Artifacts", "Bzip2_jll", "FreeType2_jll", "FriBidi_jll", "JLLWrappers", "LAME_jll", "Libdl", "Ogg_jll", "OpenSSL_jll", "Opus_jll", "Pkg", "Zlib_jll", "libass_jll", "libfdk_aac_jll", "libvorbis_jll", "x264_jll", "x265_jll"]
git-tree-sha1 = "d8a578692e3077ac998b50c0217dfd67f21d1e5f"
uuid = "b22a6f82-2f65-5046-a5b2-351ab43fb4e5"
version = "4.4.0+0"

[[FixedPointNumbers]]
deps = ["Statistics"]
git-tree-sha1 = "335bfdceacc84c5cdf16aadc768aa5ddfc5383cc"
uuid = "53c48c17-4a7d-5ca2-90c5-79b7896eea93"
version = "0.8.4"

[[Fontconfig_jll]]
deps = ["Artifacts", "Bzip2_jll", "Expat_jll", "FreeType2_jll", "JLLWrappers", "Libdl", "Libuuid_jll", "Pkg", "Zlib_jll"]
git-tree-sha1 = "21efd19106a55620a188615da6d3d06cd7f6ee03"
uuid = "a3f928ae-7b40-5064-980b-68af3947d34b"
version = "2.13.93+0"

[[Formatting]]
deps = ["Printf"]
git-tree-sha1 = "8339d61043228fdd3eb658d86c926cb282ae72a8"
uuid = "59287772-0a20-5a39-b81b-1366585eb4c0"
version = "0.4.2"

[[FreeType2_jll]]
deps = ["Artifacts", "Bzip2_jll", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"]
git-tree-sha1 = "87eb71354d8ec1a96d4a7636bd57a7347dde3ef9"
uuid = "d7e528f0-a631-5988-bf34-fe36492bcfd7"
version = "2.10.4+0"

[[FriBidi_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "aa31987c2ba8704e23c6c8ba8a4f769d5d7e4f91"
uuid = "559328eb-81f9-559d-9380-de523a88c83c"
version = "1.0.10+0"

[[GLFW_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Libglvnd_jll", "Pkg", "Xorg_libXcursor_jll", "Xorg_libXi_jll", "Xorg_libXinerama_jll", "Xorg_libXrandr_jll"]
git-tree-sha1 = "0c603255764a1fa0b61752d2bec14cfbd18f7fe8"
uuid = "0656b61e-2033-5cc2-a64a-77c0f6c09b89"
version = "3.3.5+1"

[[GR]]
deps = ["Base64", "DelimitedFiles", "GR_jll", "HTTP", "JSON", "Libdl", "LinearAlgebra", "Pkg", "Printf", "Random", "Serialization", "Sockets", "Test", "UUIDs"]
git-tree-sha1 = "c2178cfbc0a5a552e16d097fae508f2024de61a3"
uuid = "28b8d3ca-fb5f-59d9-8090-bfdbd6d07a71"
version = "0.59.0"

[[GR_jll]]
deps = ["Artifacts", "Bzip2_jll", "Cairo_jll", "FFMPEG_jll", "Fontconfig_jll", "GLFW_jll", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Libtiff_jll", "Pixman_jll", "Pkg", "Qt5Base_jll", "Zlib_jll", "libpng_jll"]
git-tree-sha1 = "ef49a187604f865f4708c90e3f431890724e9012"
uuid = "d2c73de3-f751-5644-a686-071e5b155ba9"
version = "0.59.0+0"

[[GeometryBasics]]
deps = ["EarCut_jll", "IterTools", "LinearAlgebra", "StaticArrays", "StructArrays", "Tables"]
git-tree-sha1 = "58bcdf5ebc057b085e58d95c138725628dd7453c"
uuid = "5c1252a2-5f33-56bf-86c9-59e7332b4326"
version = "0.4.1"

[[Gettext_jll]]
deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Libiconv_jll", "Pkg", "XML2_jll"]
git-tree-sha1 = "9b02998aba7bf074d14de89f9d37ca24a1a0b046"
uuid = "78b55507-aeef-58d4-861c-77aaff3498b1"
version = "0.21.0+0"

[[Glib_jll]]
deps = ["Artifacts", "Gettext_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Libiconv_jll", "Libmount_jll", "PCRE_jll", "Pkg", "Zlib_jll"]
git-tree-sha1 = "7bf67e9a481712b3dbe9cb3dac852dc4b1162e02"
uuid = "7746bdde-850d-59dc-9ae8-88ece973131d"
version = "2.68.3+0"

[[Graphite2_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "344bf40dcab1073aca04aa0df4fb092f920e4011"
uuid = "3b182d85-2403-5c21-9c21-1e1f0cc25472"
version = "1.3.14+0"

[[Grisu]]
git-tree-sha1 = "53bb909d1151e57e2484c3d1b53e19552b887fb2"
uuid = "42e2da0e-8278-4e71-bc24-59509adca0fe"
version = "1.0.2"

[[HTTP]]
deps = ["Base64", "Dates", "IniFile", "Logging", "MbedTLS", "NetworkOptions", "Sockets", "URIs"]
git-tree-sha1 = "14eece7a3308b4d8be910e265c724a6ba51a9798"
uuid = "cd3eb016-35fb-5094-929b-558a96fad6f3"
version = "0.9.16"

[[HarfBuzz_jll]]
deps = ["Artifacts", "Cairo_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "Graphite2_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Pkg"]
git-tree-sha1 = "8a954fed8ac097d5be04921d595f741115c1b2ad"
uuid = "2e76f6c2-a576-52d4-95c1-20adfe4de566"
version = "2.8.1+0"

[[HypertextLiteral]]
git-tree-sha1 = "72053798e1be56026b81d4e2682dbe58922e5ec9"
uuid = "ac1192a8-f4b3-4bfe-ba22-af5b92cd3ab2"
version = "0.9.0"

[[IOCapture]]
deps = ["Logging", "Random"]
git-tree-sha1 = "f7be53659ab06ddc986428d3a9dcc95f6fa6705a"
uuid = "b5f81e59-6552-4d32-b1f0-c071b021bf89"
version = "0.2.2"

[[IniFile]]
deps = ["Test"]
git-tree-sha1 = "098e4d2c533924c921f9f9847274f2ad89e018b8"
uuid = "83e8ac13-25f8-5344-8a64-a9f2b223428f"
version = "0.5.0"

[[InteractiveUtils]]
deps = ["Markdown"]
uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240"

[[IterTools]]
git-tree-sha1 = "05110a2ab1fc5f932622ffea2a003221f4782c18"
uuid = "c8e1da08-722c-5040-9ed9-7db0dc04731e"
version = "1.3.0"

[[IteratorInterfaceExtensions]]
git-tree-sha1 = "a3f24677c21f5bbe9d2a714f95dcd58337fb2856"
uuid = "82899510-4779-5014-852e-03e436cf321d"
version = "1.0.0"

[[JLLWrappers]]
deps = ["Preferences"]
git-tree-sha1 = "642a199af8b68253517b80bd3bfd17eb4e84df6e"
uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210"
version = "1.3.0"

[[JSON]]
deps = ["Dates", "Mmap", "Parsers", "Unicode"]
git-tree-sha1 = "8076680b162ada2a031f707ac7b4953e30667a37"
uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6"
version = "0.21.2"

[[JpegTurbo_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "d735490ac75c5cb9f1b00d8b5509c11984dc6943"
uuid = "aacddb02-875f-59d6-b918-886e6ef4fbf8"
version = "2.1.0+0"

[[Kronecker]]
deps = ["LinearAlgebra", "NamedDims", "SparseArrays", "StatsBase"]
git-tree-sha1 = "f6e3cc35572a6be64308ffb0a56d70be36dc6a85"
uuid = "2c470bb0-bcc8-11e8-3dad-c9649493f05e"
version = "0.5.0"

[[LAME_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "f6250b16881adf048549549fba48b1161acdac8c"
uuid = "c1c5ebd0-6772-5130-a774-d5fcae4a789d"
version = "3.100.1+0"

[[LZO_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "e5b909bcf985c5e2605737d2ce278ed791b89be6"
uuid = "dd4b983a-f0e5-5f8d-a1b7-129d4a5fb1ac"
version = "2.10.1+0"

[[LaTeXStrings]]
git-tree-sha1 = "c7f1c695e06c01b95a67f0cd1d34994f3e7db104"
uuid = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f"
version = "1.2.1"

[[Latexify]]
deps = ["Formatting", "InteractiveUtils", "LaTeXStrings", "MacroTools", "Markdown", "Printf", "Requires"]
git-tree-sha1 = "a4b12a1bd2ebade87891ab7e36fdbce582301a92"
uuid = "23fbe1c1-3f47-55db-b15f-69d7ec21a316"
version = "0.15.6"

[[LibCURL]]
deps = ["LibCURL_jll", "MozillaCACerts_jll"]
uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21"

[[LibCURL_jll]]
deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll", "Zlib_jll", "nghttp2_jll"]
uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0"

[[LibGit2]]
deps = ["Base64", "NetworkOptions", "Printf", "SHA"]
uuid = "76f85450-5226-5b5a-8eaa-529ad045b433"

[[LibSSH2_jll]]
deps = ["Artifacts", "Libdl", "MbedTLS_jll"]
uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8"

[[Libdl]]
uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb"

[[Libffi_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "761a393aeccd6aa92ec3515e428c26bf99575b3b"
uuid = "e9f186c6-92d2-5b65-8a66-fee21dc1b490"
version = "3.2.2+0"

[[Libgcrypt_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgpg_error_jll", "Pkg"]
git-tree-sha1 = "64613c82a59c120435c067c2b809fc61cf5166ae"
uuid = "d4300ac3-e22c-5743-9152-c294e39db1e4"
version = "1.8.7+0"

[[Libglvnd_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll", "Xorg_libXext_jll"]
git-tree-sha1 = "7739f837d6447403596a75d19ed01fd08d6f56bf"
uuid = "7e76a0d4-f3c7-5321-8279-8d96eeed0f29"
version = "1.3.0+3"

[[Libgpg_error_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "c333716e46366857753e273ce6a69ee0945a6db9"
uuid = "7add5ba3-2f88-524e-9cd5-f83b8a55f7b8"
version = "1.42.0+0"

[[Libiconv_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "42b62845d70a619f063a7da093d995ec8e15e778"
uuid = "94ce4f54-9a6c-5748-9c1c-f9c7231a4531"
version = "1.16.1+1"

[[Libmount_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "9c30530bf0effd46e15e0fdcf2b8636e78cbbd73"
uuid = "4b2f31a3-9ecc-558c-b454-b3730dcb73e9"
version = "2.35.0+0"

[[Libtiff_jll]]
deps = ["Artifacts", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Pkg", "Zlib_jll", "Zstd_jll"]
git-tree-sha1 = "340e257aada13f95f98ee352d316c3bed37c8ab9"
uuid = "89763e89-9b03-5906-acba-b20f662cd828"
version = "4.3.0+0"

[[Libuuid_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "7f3efec06033682db852f8b3bc3c1d2b0a0ab066"
uuid = "38a345b3-de98-5d2b-a5d3-14cd9215e700"
version = "2.36.0+0"

[[LinearAlgebra]]
deps = ["Libdl"]
uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"

[[Logging]]
uuid = "56ddb016-857b-54e1-b83d-db4d58db5568"

[[MacroTools]]
deps = ["Markdown", "Random"]
git-tree-sha1 = "5a5bc6bf062f0f95e62d0fe0a2d99699fed82dd9"
uuid = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09"
version = "0.5.8"

[[Markdown]]
deps = ["Base64"]
uuid = "d6f4376e-aef5-505a-96c1-9c027394607a"

[[MbedTLS]]
deps = ["Dates", "MbedTLS_jll", "Random", "Sockets"]
git-tree-sha1 = "1c38e51c3d08ef2278062ebceade0e46cefc96fe"
uuid = "739be429-bea8-5141-9913-cc70e7f3736d"
version = "1.0.3"

[[MbedTLS_jll]]
deps = ["Artifacts", "Libdl"]
uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1"

[[Measures]]
git-tree-sha1 = "e498ddeee6f9fdb4551ce855a46f54dbd900245f"
uuid = "442fdcdd-2543-5da2-b0f3-8c86c306513e"
version = "0.3.1"

[[Missings]]
deps = ["DataAPI"]
git-tree-sha1 = "bf210ce90b6c9eed32d25dbcae1ebc565df2687f"
uuid = "e1d29d7a-bbdc-5cf2-9ac0-f12de2c33e28"
version = "1.0.2"

[[Mmap]]
uuid = "a63ad114-7e13-5084-954f-fe012c677804"

[[MozillaCACerts_jll]]
uuid = "14a3606d-f60d-562e-9121-12d972cd8159"

[[NaNMath]]
git-tree-sha1 = "bfe47e760d60b82b66b61d2d44128b62e3a369fb"
uuid = "77ba4419-2d1f-58cd-9bb1-8ffee604a2e3"
version = "0.3.5"

[[NamedDims]]
deps = ["AbstractFFTs", "ChainRulesCore", "CovarianceEstimation", "LinearAlgebra", "Pkg", "Requires", "Statistics"]
git-tree-sha1 = "fb4530603a1e62aa5ed7569f283d4b47c2a92f61"
uuid = "356022a1-0364-5f58-8944-0da4b18d706f"
version = "0.2.38"

[[NetworkOptions]]
uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908"

[[Ogg_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "7937eda4681660b4d6aeeecc2f7e1c81c8ee4e2f"
uuid = "e7412a2a-1a6e-54c0-be00-318e2571c051"
version = "1.3.5+0"

[[OpenSSL_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "15003dcb7d8db3c6c857fda14891a539a8f2705a"
uuid = "458c3c95-2e84-50aa-8efc-19380b2a3a95"
version = "1.1.10+0"

[[Opus_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "51a08fb14ec28da2ec7a927c4337e4332c2a4720"
uuid = "91d4177d-7536-5919-b921-800302f37372"
version = "1.3.2+0"

[[OrderedCollections]]
git-tree-sha1 = "85f8e6578bf1f9ee0d11e7bb1b1456435479d47c"
uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d"
version = "1.4.1"

[[PCRE_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "b2a7af664e098055a7529ad1a900ded962bca488"
uuid = "2f80f16e-611a-54ab-bc61-aa92de5b98fc"
version = "8.44.0+0"

[[Parsers]]
deps = ["Dates"]
git-tree-sha1 = "a8709b968a1ea6abc2dc1967cb1db6ac9a00dfb6"
uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0"
version = "2.0.5"

[[Pixman_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "b4f5d02549a10e20780a24fce72bea96b6329e29"
uuid = "30392449-352a-5448-841d-b1acce4e97dc"
version = "0.40.1+0"

[[Pkg]]
deps = ["Artifacts", "Dates", "Downloads", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"]
uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"

[[PlotThemes]]
deps = ["PlotUtils", "Requires", "Statistics"]
git-tree-sha1 = "a3a964ce9dc7898193536002a6dd892b1b5a6f1d"
uuid = "ccf2f8ad-2431-5c83-bf29-c5338b663b6a"
version = "2.0.1"

[[PlotUtils]]
deps = ["ColorSchemes", "Colors", "Dates", "Printf", "Random", "Reexport", "Statistics"]
git-tree-sha1 = "2537ed3c0ed5e03896927187f5f2ee6a4ab342db"
uuid = "995b91a9-d308-5afd-9ec6-746e21dbc043"
version = "1.0.14"

[[Plots]]
deps = ["Base64", "Contour", "Dates", "Downloads", "FFMPEG", "FixedPointNumbers", "GR", "GeometryBasics", "JSON", "Latexify", "LinearAlgebra", "Measures", "NaNMath", "PlotThemes", "PlotUtils", "Printf", "REPL", "Random", "RecipesBase", "RecipesPipeline", "Reexport", "Requires", "Scratch", "Showoff", "SparseArrays", "Statistics", "StatsBase", "UUIDs"]
git-tree-sha1 = "cfbd033def161db9494f86c5d18fbf874e09e514"
uuid = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
version = "1.22.3"

[[PlutoUI]]
deps = ["Base64", "Dates", "HypertextLiteral", "IOCapture", "InteractiveUtils", "JSON", "Logging", "Markdown", "Random", "Reexport", "UUIDs"]
git-tree-sha1 = "d1fb76655a95bf6ea4348d7197b22e889a4375f4"
uuid = "7f904dfe-b85e-4ff6-b463-dae2292396a8"
version = "0.7.14"

[[Preferences]]
deps = ["TOML"]
git-tree-sha1 = "00cfd92944ca9c760982747e9a1d0d5d86ab1e5a"
uuid = "21216c6a-2e73-6563-6e65-726566657250"
version = "1.2.2"

[[Printf]]
deps = ["Unicode"]
uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7"

[[Qt5Base_jll]]
deps = ["Artifacts", "CompilerSupportLibraries_jll", "Fontconfig_jll", "Glib_jll", "JLLWrappers", "Libdl", "Libglvnd_jll", "OpenSSL_jll", "Pkg", "Xorg_libXext_jll", "Xorg_libxcb_jll", "Xorg_xcb_util_image_jll", "Xorg_xcb_util_keysyms_jll", "Xorg_xcb_util_renderutil_jll", "Xorg_xcb_util_wm_jll", "Zlib_jll", "xkbcommon_jll"]
git-tree-sha1 = "ad368663a5e20dbb8d6dc2fddeefe4dae0781ae8"
uuid = "ea2cea3b-5b76-57ae-a6ef-0a8af62496e1"
version = "5.15.3+0"

[[REPL]]
deps = ["InteractiveUtils", "Markdown", "Sockets", "Unicode"]
uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb"

[[Random]]
deps = ["Serialization"]
uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"

[[RecipesBase]]
git-tree-sha1 = "44a75aa7a527910ee3d1751d1f0e4148698add9e"
uuid = "3cdcf5f2-1ef4-517c-9805-6587b60abb01"
version = "1.1.2"

[[RecipesPipeline]]
deps = ["Dates", "NaNMath", "PlotUtils", "RecipesBase"]
git-tree-sha1 = "7ad0dfa8d03b7bcf8c597f59f5292801730c55b8"
uuid = "01d81517-befc-4cb6-b9ec-a95719d0359c"
version = "0.4.1"

[[Reexport]]
git-tree-sha1 = "45e428421666073eab6f2da5c9d310d99bb12f9b"
uuid = "189a3867-3050-52da-a836-e630ba90ab69"
version = "1.2.2"

[[Requires]]
deps = ["UUIDs"]
git-tree-sha1 = "4036a3bd08ac7e968e27c203d45f5fff15020621"
uuid = "ae029012-a4dd-5104-9daa-d747884805df"
version = "1.1.3"

[[SHA]]
uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce"

[[Scratch]]
deps = ["Dates"]
git-tree-sha1 = "0b4b7f1393cff97c33891da2a0bf69c6ed241fda"
uuid = "6c6a2e73-6563-6170-7368-637461726353"
version = "1.1.0"

[[Serialization]]
uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b"

[[SharedArrays]]
deps = ["Distributed", "Mmap", "Random", "Serialization"]
uuid = "1a1011a3-84de-559e-8e89-a11a2f7dc383"

[[Showoff]]
deps = ["Dates", "Grisu"]
git-tree-sha1 = "91eddf657aca81df9ae6ceb20b959ae5653ad1de"
uuid = "992d4aef-0814-514b-bc4d-f2e9a6c4116f"
version = "1.0.3"

[[Sockets]]
uuid = "6462fe0b-24de-5631-8697-dd941f90decc"

[[SortingAlgorithms]]
deps = ["DataStructures"]
git-tree-sha1 = "b3363d7460f7d098ca0912c69b082f75625d7508"
uuid = "a2af1166-a08f-5f64-846c-94a0d3cef48c"
version = "1.0.1"

[[SparseArrays]]
deps = ["LinearAlgebra", "Random"]
uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"

[[StaticArrays]]
deps = ["LinearAlgebra", "Random", "Statistics"]
git-tree-sha1 = "3240808c6d463ac46f1c1cd7638375cd22abbccb"
uuid = "90137ffa-7385-5640-81b9-e52037218182"
version = "1.2.12"

[[Statistics]]
deps = ["LinearAlgebra", "SparseArrays"]
uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"

[[StatsAPI]]
git-tree-sha1 = "1958272568dc176a1d881acb797beb909c785510"
uuid = "82ae8749-77ed-4fe6-ae5f-f523153014b0"
version = "1.0.0"

[[StatsBase]]
deps = ["DataAPI", "DataStructures", "LinearAlgebra", "Missings", "Printf", "Random", "SortingAlgorithms", "SparseArrays", "Statistics", "StatsAPI"]
git-tree-sha1 = "8cbbc098554648c84f79a463c9ff0fd277144b6c"
uuid = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91"
version = "0.33.10"

[[StructArrays]]
deps = ["Adapt", "DataAPI", "StaticArrays", "Tables"]
git-tree-sha1 = "2ce41e0d042c60ecd131e9fb7154a3bfadbf50d3"
uuid = "09ab397b-f2b6-538f-b94a-2f83cf4a842a"
version = "0.6.3"

[[TOML]]
deps = ["Dates"]
uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76"

[[TableTraits]]
deps = ["IteratorInterfaceExtensions"]
git-tree-sha1 = "c06b2f539df1c6efa794486abfb6ed2022561a39"
uuid = "3783bdb8-4a98-5b6b-af9a-565f29a5fe9c"
version = "1.0.1"

[[Tables]]
deps = ["DataAPI", "DataValueInterfaces", "IteratorInterfaceExtensions", "LinearAlgebra", "TableTraits", "Test"]
git-tree-sha1 = "1162ce4a6c4b7e31e0e6b14486a6986951c73be9"
uuid = "bd369af6-aec1-5ad0-b16a-f7cc5008161c"
version = "1.5.2"

[[Tar]]
deps = ["ArgTools", "SHA"]
uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e"

[[Test]]
deps = ["InteractiveUtils", "Logging", "Random", "Serialization"]
uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[[URIs]]
git-tree-sha1 = "97bbe755a53fe859669cd907f2d96aee8d2c1355"
uuid = "5c2747f8-b7ea-4ff2-ba2e-563bfd36b1d4"
version = "1.3.0"

[[UUIDs]]
deps = ["Random", "SHA"]
uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4"

[[Unicode]]
uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5"

[[Wayland_jll]]
deps = ["Artifacts", "Expat_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Pkg", "XML2_jll"]
git-tree-sha1 = "3e61f0b86f90dacb0bc0e73a0c5a83f6a8636e23"
uuid = "a2964d1f-97da-50d4-b82a-358c7fce9d89"
version = "1.19.0+0"

[[Wayland_protocols_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Wayland_jll"]
git-tree-sha1 = "2839f1c1296940218e35df0bbb220f2a79686670"
uuid = "2381bf8a-dfd0-557d-9999-79630e7b1b91"
version = "1.18.0+4"

[[XML2_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Libiconv_jll", "Pkg", "Zlib_jll"]
git-tree-sha1 = "1acf5bdf07aa0907e0a37d3718bb88d4b687b74a"
uuid = "02c8fc9c-b97f-50b9-bbe4-9be30ff0a78a"
version = "2.9.12+0"

[[XSLT_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgcrypt_jll", "Libgpg_error_jll", "Libiconv_jll", "Pkg", "XML2_jll", "Zlib_jll"]
git-tree-sha1 = "91844873c4085240b95e795f692c4cec4d805f8a"
uuid = "aed1982a-8fda-507f-9586-7b0439959a61"
version = "1.1.34+0"

[[Xorg_libX11_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libxcb_jll", "Xorg_xtrans_jll"]
git-tree-sha1 = "5be649d550f3f4b95308bf0183b82e2582876527"
uuid = "4f6342f7-b3d2-589e-9d20-edeb45f2b2bc"
version = "1.6.9+4"

[[Xorg_libXau_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "4e490d5c960c314f33885790ed410ff3a94ce67e"
uuid = "0c0b7dd1-d40b-584c-a123-a41640f87eec"
version = "1.0.9+4"

[[Xorg_libXcursor_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXfixes_jll", "Xorg_libXrender_jll"]
git-tree-sha1 = "12e0eb3bc634fa2080c1c37fccf56f7c22989afd"
uuid = "935fb764-8cf2-53bf-bb30-45bb1f8bf724"
version = "1.2.0+4"

[[Xorg_libXdmcp_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "4fe47bd2247248125c428978740e18a681372dd4"
uuid = "a3789734-cfe1-5b06-b2d0-1dd0d9d62d05"
version = "1.1.3+4"

[[Xorg_libXext_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"]
git-tree-sha1 = "b7c0aa8c376b31e4852b360222848637f481f8c3"
uuid = "1082639a-0dae-5f34-9b06-72781eeb8cb3"
version = "1.3.4+4"

[[Xorg_libXfixes_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"]
git-tree-sha1 = "0e0dc7431e7a0587559f9294aeec269471c991a4"
uuid = "d091e8ba-531a-589c-9de9-94069b037ed8"
version = "5.0.3+4"

[[Xorg_libXi_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXext_jll", "Xorg_libXfixes_jll"]
git-tree-sha1 = "89b52bc2160aadc84d707093930ef0bffa641246"
uuid = "a51aa0fd-4e3c-5386-b890-e753decda492"
version = "1.7.10+4"

[[Xorg_libXinerama_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXext_jll"]
git-tree-sha1 = "26be8b1c342929259317d8b9f7b53bf2bb73b123"
uuid = "d1454406-59df-5ea1-beac-c340f2130bc3"
version = "1.1.4+4"

[[Xorg_libXrandr_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXext_jll", "Xorg_libXrender_jll"]
git-tree-sha1 = "34cea83cb726fb58f325887bf0612c6b3fb17631"
uuid = "ec84b674-ba8e-5d96-8ba1-2a689ba10484"
version = "1.5.2+4"

[[Xorg_libXrender_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"]
git-tree-sha1 = "19560f30fd49f4d4efbe7002a1037f8c43d43b96"
uuid = "ea2f1a96-1ddc-540d-b46f-429655e07cfa"
version = "0.9.10+4"

[[Xorg_libpthread_stubs_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "6783737e45d3c59a4a4c4091f5f88cdcf0908cbb"
uuid = "14d82f49-176c-5ed1-bb49-ad3f5cbd8c74"
version = "0.1.0+3"

[[Xorg_libxcb_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "XSLT_jll", "Xorg_libXau_jll", "Xorg_libXdmcp_jll", "Xorg_libpthread_stubs_jll"]
git-tree-sha1 = "daf17f441228e7a3833846cd048892861cff16d6"
uuid = "c7cfdc94-dc32-55de-ac96-5a1b8d977c5b"
version = "1.13.0+3"

[[Xorg_libxkbfile_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"]
git-tree-sha1 = "926af861744212db0eb001d9e40b5d16292080b2"
uuid = "cc61e674-0454-545c-8b26-ed2c68acab7a"
version = "1.1.0+4"

[[Xorg_xcb_util_image_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_xcb_util_jll"]
git-tree-sha1 = "0fab0a40349ba1cba2c1da699243396ff8e94b97"
uuid = "12413925-8142-5f55-bb0e-6d7ca50bb09b"
version = "0.4.0+1"

[[Xorg_xcb_util_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libxcb_jll"]
git-tree-sha1 = "e7fd7b2881fa2eaa72717420894d3938177862d1"
uuid = "2def613f-5ad1-5310-b15b-b15d46f528f5"
version = "0.4.0+1"

[[Xorg_xcb_util_keysyms_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_xcb_util_jll"]
git-tree-sha1 = "d1151e2c45a544f32441a567d1690e701ec89b00"
uuid = "975044d2-76e6-5fbe-bf08-97ce7c6574c7"
version = "0.4.0+1"

[[Xorg_xcb_util_renderutil_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_xcb_util_jll"]
git-tree-sha1 = "dfd7a8f38d4613b6a575253b3174dd991ca6183e"
uuid = "0d47668e-0667-5a69-a72c-f761630bfb7e"
version = "0.3.9+1"

[[Xorg_xcb_util_wm_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_xcb_util_jll"]
git-tree-sha1 = "e78d10aab01a4a154142c5006ed44fd9e8e31b67"
uuid = "c22f9ab0-d5fe-5066-847c-f4bb1cd4e361"
version = "0.4.1+1"

[[Xorg_xkbcomp_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libxkbfile_jll"]
git-tree-sha1 = "4bcbf660f6c2e714f87e960a171b119d06ee163b"
uuid = "35661453-b289-5fab-8a00-3d9160c6a3a4"
version = "1.4.2+4"

[[Xorg_xkeyboard_config_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_xkbcomp_jll"]
git-tree-sha1 = "5c8424f8a67c3f2209646d4425f3d415fee5931d"
uuid = "33bec58e-1273-512f-9401-5d533626f822"
version = "2.27.0+4"

[[Xorg_xtrans_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "79c31e7844f6ecf779705fbc12146eb190b7d845"
uuid = "c5fb5394-a638-5e4d-96e5-b29de1b5cf10"
version = "1.4.0+3"

[[Zlib_jll]]
deps = ["Libdl"]
uuid = "83775a58-1f1d-513f-b197-d71354ab007a"

[[Zstd_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "cc4bf3fdde8b7e3e9fa0351bdeedba1cf3b7f6e6"
uuid = "3161d3a3-bdf6-5164-811a-617609db77b4"
version = "1.5.0+0"

[[libass_jll]]
deps = ["Artifacts", "Bzip2_jll", "FreeType2_jll", "FriBidi_jll", "HarfBuzz_jll", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"]
git-tree-sha1 = "5982a94fcba20f02f42ace44b9894ee2b140fe47"
uuid = "0ac62f75-1d6f-5e53-bd7c-93b484bb37c0"
version = "0.15.1+0"

[[libfdk_aac_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "daacc84a041563f965be61859a36e17c4e4fcd55"
uuid = "f638f0a6-7fb0-5443-88ba-1cc74229b280"
version = "2.0.2+0"

[[libpng_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"]
git-tree-sha1 = "94d180a6d2b5e55e447e2d27a29ed04fe79eb30c"
uuid = "b53b4c65-9356-5827-b1ea-8c7a1a84506f"
version = "1.6.38+0"

[[libvorbis_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Ogg_jll", "Pkg"]
git-tree-sha1 = "c45f4e40e7aafe9d086379e5578947ec8b95a8fb"
uuid = "f27f6e37-5d2b-51aa-960f-b287f2bc3b7a"
version = "1.3.7+0"

[[nghttp2_jll]]
deps = ["Artifacts", "Libdl"]
uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d"

[[p7zip_jll]]
deps = ["Artifacts", "Libdl"]
uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0"

[[x264_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "4fea590b89e6ec504593146bf8b988b2c00922b2"
uuid = "1270edf5-f2f9-52d2-97e9-ab00b5d0237a"
version = "2021.5.5+0"

[[x265_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "ee567a171cce03570d77ad3a43e90218e38937a9"
uuid = "dfaa095f-4041-5dcd-9319-2fabd8486b76"
version = "3.5.0+0"

[[xkbcommon_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Wayland_jll", "Wayland_protocols_jll", "Xorg_libxcb_jll", "Xorg_xkeyboard_config_jll"]
git-tree-sha1 = "ece2350174195bb31de1a63bea3a41ae1aa593b6"
uuid = "d8fb68d0-12a3-5cfd-a85a-d49703b185fd"
version = "0.9.1+5"
"""

# ╔═╡ Cell order:
# ╟─21712488-f636-4861-913c-012693cb524d
# ╟─89b58dd0-c70c-4566-b991-2d739990f2ff
# ╟─23c019dd-53d2-461e-a6ba-f1b5da2f6710
# ╠═f5992b42-ff99-479a-b093-70a9bfd85655
# ╠═0a95d032-8576-492f-bee1-ef67303bcb3a
# ╠═7458474c-3c4d-4a99-9640-4bfd2e6465ea
# ╠═edf8bfba-ca6b-4adf-9a7d-e618ca511a80
# ╠═d7f3dc01-1201-41de-8a63-c3e2bc020cf6
# ╠═2e2854f6-8cab-4252-a580-7abc71efd2a0
# ╠═a646342f-3177-4579-8625-cb7d7ea27a49
# ╠═792748ec-199c-41d8-a801-6cd97881ee72
# ╟─85bca31d-c3be-46b6-b330-b80648472378
# ╟─ebd5efea-94b6-419e-8ff1-9dcf624ddaba
# ╟─c8e02ee8-2c21-4a37-9875-c4a463e0b462
# ╟─913d405a-e525-49d6-9021-227543b37953
# ╟─4f87569b-777e-4e97-ac80-8b7b76115d2a
# ╟─a36e5515-e7f9-4c34-a48e-52a004d80f23
# ╟─fc12024b-2785-4f89-a457-95a2deb5045f
# ╟─5afc42f7-3253-438d-96a8-d665a1b9a1cd
# ╠═a5e38ca3-8184-4160-a6b1-4c1783c6e185
# ╠═596a9128-2fff-47e8-8b6f-e9fd8686a279
# ╠═b595349b-b572-4cc8-92b1-07631cc8fad7
# ╠═c578581e-467e-482c-9cce-2ad55d32267f
# ╟─7479e663-e533-442c-8324-c60f4849c470
# ╟─0872a46c-4f4b-49fa-8152-9193b71f332b
# ╠═8c779399-7db0-421e-b1b2-659d28fc8c41
# ╠═e8c989d6-9ba6-40d1-ad36-d768172d5f5e
# ╠═9330d1cf-e0e1-41c1-a876-5fae11633ce8
# ╠═5d450f8d-c8c4-4a58-b2a6-97bfe109f91f
# ╟─5e1377b7-c784-4e42-8190-ffbf48f0ecc1
# ╟─822ea5ab-4a3a-45ee-903c-ff9bad9d5f16
# ╠═04583ab3-2364-4c32-be8e-d3e65ca5321e
# ╠═4fb4c614-ed74-411e-91db-641c331c75fa
# ╠═8cd0438c-c480-4b73-93af-79e3f3a5dfa9
# ╠═36df576e-5111-4d66-9170-2eb9573cea32
# ╠═1e4adc4d-52cf-41df-ad62-cab016f55397
# ╠═2cab05f7-bc59-4d26-a535-495b5ab104c0
# ╠═faf456bc-9145-4d87-ad4d-6ecc8c78bd41
# ╠═e63d17ba-767c-4136-8dc3-3da609f8dda5
# ╠═002504f4-e74a-4e8c-80fa-f4c5f2e90df0
# ╟─4cdab1a4-1784-4430-ae4f-50a4459d83a4
# ╠═5695cdc2-0da6-4ef1-9aa9-d553f9b194e4
# ╠═0c09a3b6-920d-4732-af67-86b36bd88292
# ╟─a558b847-3f09-473c-9ec5-70badcda7589
# ╟─8407fc8c-95b4-4536-bbda-b3f4945518b0
# ╠═104b6690-212b-11ec-3a2b-25c420f62534
# ╠═7f43573f-010f-455a-82c7-f2f509cce3d3
# ╠═d28b8274-7435-484a-bde7-61443d7e4ae2
# ╠═dc95c17d-de0f-4b72-b57e-0dab473a69cd
# ╠═75ebae9f-1c69-4d92-9cdf-a0f4b0971576
# ╟─3140729e-d516-434d-ba32-3ff9705ee83b
# ╟─00000000-0000-0000-0000-000000000001
# ╟─00000000-0000-0000-0000-000000000002


#=

  Added corner puzzle in constraint

  Problem from http://www.delphiforfun.org/Programs/AddedCorners.htm
  """
  This puzzle requires that you enter the digits 1 through 8 in the circles and
  squares (one digit in each figure) so that the number in each square is equal
  to the sum on the numbers in the circles which  adjoin it.
  ...

    C F C
    F   F
    C F C

  """

  Model created by Hakan Kjellerstrand, hakank@gmail.com
  See also my Julia page: http://www.hakank.org/julia/

=#

using ConstraintSolver, JuMP
using Cbc, GLPK, Ipopt
using Printf
const CS = ConstraintSolver
include("constraints_utils.jl")

function added_corner(print_solutions=true,all_solutions=true,timeout=6)

    cbc_optimizer = optimizer_with_attributes(Cbc.Optimizer, "logLevel" => 0)
    glpk_optimizer = optimizer_with_attributes(GLPK.Optimizer)
    ipopt_optimizer = optimizer_with_attributes(Ipopt.Optimizer)

    model = Model(optimizer_with_attributes(CS.Optimizer,   "all_solutions"=> all_solutions,
                                                            # "all_optimal_solutions"=>all_solutions,
                                                            "logging"=>[],

                                                            "traverse_strategy"=>:BFS,
                                                            # "traverse_strategy"=>:DFS,
                                                            # "traverse_strategy"=>:DBFS,

                                                            # "branch_split"=>:Smallest,
                                                            # "branch_split"=>:Biggest,
                                                            "branch_split"=>:InHalf,

                                                            # https://wikunia.github.io/ConstraintSolver.jl/stable/options/#branch_strategy-(:Auto)
                                                            "branch_strategy" => :IMPS, # default
                                                            # "branch_strategy" => :ABS, # Activity Based Search
                                                            # "activity.decay" => 0.999, # default 0.999
                                                            # "activity.max_probes" => 10, # default, 10
                                                            # "activity.max_confidence_deviation" => 20, # default 20

                                                            # "simplify"=>false,
                                                            # "simplify"=>true, # default

                                                            "time_limit"=>timeout,

                                                            # "backtrack" => false, # default true
                                                            # "backtrack_sorting" => false, # default true

                                                            # "lp_optimizer" => cbc_optimizer,
                                                            # "lp_optimizer" => glpk_optimizer,
                                                            # "lp_optimizer" => ipopt_optimizer,
                                        ))

    n = 8
    @variable(model, 1 <= x[1:n] <= n, Int)
    A,B,C,D,E,F,G,H = x
    @constraint(model, x in CS.AllDifferent())

    @constraint(model, B == A + C)
    @constraint(model, D == A + F)
    @constraint(model, E == C + H)
    @constraint(model, G == F + H)

    # Solve the problem
    optimize!(model)

    status = JuMP.termination_status(model)
    # println("status:$status")
    num_sols = 0
    if status == MOI.OPTIMAL
        num_sols = MOI.get(model, MOI.ResultCount())
        println("num_sols:$num_sols\n")
        if print_solutions
            for sol in 1:num_sols
                println("solution #$sol")
                x_val = convert.(Integer,JuMP.value.(x; result=sol))
                println("x:$x_val")
                @printf "%d %d %d\n" x_val[1] x_val[2] x_val[3]
                @printf "%d   %d\n" x_val[4] x_val[5]
                @printf "%d %d %d\n" x_val[6] x_val[7] x_val[8]
                println()

            end
        end
    else
        println("status:$status")
    end

    return status, num_sols
end

@time added_corner()


abstract type FloatInterval{T<:AbstractFloat} <: Uniform{T} end

abstract type CloseOpen{ T<:AbstractFloat} <: FloatInterval{T} end
abstract type OpenClose{ T<:AbstractFloat} <: FloatInterval{T} end
abstract type CloseClose{T<:AbstractFloat} <: FloatInterval{T} end
abstract type OpenOpen{  T<:AbstractFloat} <: FloatInterval{T} end

struct CloseOpen12{T<:AbstractFloat} <: CloseOpen{T} end # interval [1,2)

struct CloseOpen01{ T<:AbstractFloat} <: CloseOpen{T}  end # interval [0,1)
struct OpenClose01{ T<:AbstractFloat} <: OpenClose{T}  end # interval (0,1]
struct CloseClose01{T<:AbstractFloat} <: CloseClose{T} end # interval [0,1]
struct OpenOpen01{  T<:AbstractFloat} <: OpenOpen{T}   end # interval (0,1)

struct CloseOpenAB{T<:AbstractFloat} <: CloseOpen{T} # interval [a,b)
    a::T
    b::T

    CloseOpenAB{T}(a::T, b::T) where {T} = (check_interval(a, b); new{T}(a, b))
end

struct OpenCloseAB{T<:AbstractFloat} <: OpenClose{T} # interval (a,b]
    a::T
    b::T

    OpenCloseAB{T}(a::T, b::T) where {T} = (check_interval(a, b); new{T}(a, b))
end

struct CloseCloseAB{T<:AbstractFloat} <: CloseClose{T} # interval [a,b]
    a::T
    b::T

    CloseCloseAB{T}(a::T, b::T) where {T} = (check_interval(a, b); new{T}(a, b))
end

struct OpenOpenAB{T<:AbstractFloat} <: OpenOpen{T} # interval (a,b)
    a::T
    b::T

    OpenOpenAB{T}(a::T, b::T) where {T} = (check_interval(a, b); new{T}(a, b))
end

check_interval(a, b) = a >= b && throw(ArgumentError("invalid interval specification"))

const FloatInterval_64 = FloatInterval{Float64}
const CloseOpen01_64   = CloseOpen01{Float64}
const CloseOpen12_64   = CloseOpen12{Float64}

CloseOpen01(::Type{T}=Float64) where {T<:AbstractFloat} = CloseOpen01{T}()
CloseOpen12(::Type{T}=Float64) where {T<:AbstractFloat} = CloseOpen12{T}()

CloseOpen(::Type{T}=Float64) where {T<:AbstractFloat} = CloseOpen01{T}()
CloseOpen(a::T, b::T) where {T<:AbstractFloat} = CloseOpenAB{T}(a, b)

OpenClose(::Type{T}=Float64) where {T<:AbstractFloat} = OpenClose01{T}()
OpenClose(a::T, b::T) where {T<:AbstractFloat} = OpenCloseAB{T}(a, b)

CloseClose(::Type{T}=Float64) where {T<:AbstractFloat} = CloseClose01{T}()
CloseClose(a::T, b::T) where {T<:AbstractFloat} = CloseCloseAB{T}(a, b)

OpenOpen(::Type{T}=Float64) where {T<:AbstractFloat} = OpenOpen01{T}()
OpenOpen(a::T, b::T) where {T<:AbstractFloat} = OpenOpenAB{T}(a, b)

# convenience functions

CloseOpen(a, b) = CloseOpen(promote(a, b)...)
CloseOpen(a::T, b::T) where {T} = CloseOpen(AbstractFloat(a), AbstractFloat(b))

OpenClose(a, b) = OpenClose(promote(a, b)...)
OpenClose(a::T, b::T) where {T} = OpenClose(AbstractFloat(a), AbstractFloat(b))

CloseClose(a, b) = CloseClose(promote(a, b)...)
CloseClose(a::T, b::T) where {T} = CloseClose(AbstractFloat(a), AbstractFloat(b))

OpenOpen(a, b) = OpenOpen(promote(a, b)...)
OpenOpen(a::T, b::T) where {T} = OpenOpen(AbstractFloat(a), AbstractFloat(b))

## sampling

### fall-back on Random definitions

for CO in (:CloseOpen01, :CloseOpen12)
    @eval begin
        Sampler(RNG::Type{<:AbstractRNG}, ::$CO{T}, n::Repetition) where {T} =
            Sampler(RNG, Random.$CO{T}(), n)

        Sampler(::Type{<:AbstractRNG}, ::$CO{BigFloat}, ::Repetition) =
            Random.SamplerBigFloat{Random.$CO{BigFloat}}(precision(BigFloat))
    end
end

### new intervals 01

# TODO: optimize for BigFloat

for CO = (:OpenClose01, :OpenOpen01, :CloseClose01)
    @eval Sampler(RNG::Type{<:AbstractRNG}, I::$CO{T}, n::Repetition) where {T} =
              SamplerSimple(I, CloseOpen01(T))
end

rand(r::AbstractRNG, sp::SamplerSimple{OpenClose01{T}}) where {T} =
    one(T) - rand(r, sp.data)

rand(r::AbstractRNG, sp::SamplerSimple{OpenOpen01{T}}) where {T} =
    while true
        x = rand(r, sp.data)
        x != zero(T) && return x
    end

# optimizations (TODO: optimize for BigFloat too)

rand(r::AbstractRNG, sp::SamplerSimple{OpenOpen01{Float64}}) =
    reinterpret(Float64, reinterpret(UInt64, rand(r, sp.data)) | 0x0000000000000001)

rand(r::AbstractRNG, sp::SamplerSimple{OpenOpen01{Float32}}) =
    reinterpret(Float32, reinterpret(UInt32, rand(r, sp.data)) | 0x00000001)

rand(r::AbstractRNG, sp::SamplerSimple{OpenOpen01{Float16}}) =
    reinterpret(Float16, reinterpret(UInt16, rand(r, sp.data)) | 0x0001)

# prevfloat(T(2)) - 1 for IEEEFloat
upper01(::Type{Float64}) = 0.9999999999999998
upper01(::Type{Float32}) = 0.9999999f0
upper01(::Type{Float16}) = Float16(0.999)
upper01(::Type{BigFloat}) = prevfloat(one(BigFloat))

rand(r::AbstractRNG, sp::SamplerSimple{CloseClose01{T}}) where {T} =
    rand(r, sp.data) / upper01(T)

### CloseOpenAB

for (CO, CO01) = (CloseOpenAB => CloseOpen01,
                  OpenCloseAB => OpenClose01,
                  CloseCloseAB => CloseClose01,
                  OpenOpenAB => OpenOpen01)

    @eval Sampler(RNG::Type{<:AbstractRNG}, d::$CO{T}, n::Repetition) where {T} =
        SamplerTag{$CO{T}}((a=d.a, d=d.b - d.a, sp=Sampler(RNG, $CO01{T}(), n)))

    @eval rand(rng::AbstractRNG, sp::SamplerTag{$CO{T}}) where {T} =
        sp.data.a + sp.data.d  * rand(rng, sp.data.sp)
end


export SingleEntryVector

"""
    SingleEntryVector{N} <: AbstractVector{N}

A lazy unit vector with arbitrary one-element.

### Fields

- `i` -- index of non-zero entry
- `n` -- vector length
- `v` -- non-zero entry
"""
struct SingleEntryVector{N} <: AbstractVector{N}
    i::Int
    n::Int
    v::N
end

# convenience constructor with one-element of corresponding type
SingleEntryVector{N}(i::Int, n::Int) where {N} =
    SingleEntryVector{N}(i, n, one(N))

function Base.getindex(e::SingleEntryVector{N}, i::Int) where {N}
    @boundscheck @assert 1 <= i <= e.n
    return i == e.i ? e.v : zero(N)
end

Base.size(e::SingleEntryVector) = (e.n,)

function Base.:(*)(A::AbstractMatrix{N}, e::SingleEntryVector{N}) where {N}
    return A[:, e.i] * e.v
end

function Base.:(*)(A::Transpose{N, <:AbstractMatrix{N}},
                   e::SingleEntryVector{N}) where {N}
    return A[:, e.i] * e.v
end

# multiplication with diagonal matrix
function Base.:(*)(D::Diagonal{N, V},
                   e::SingleEntryVector{N}) where {N, V<:AbstractVector{N}}
    return SingleEntryVector(e.i, e.n, D.diag[e.i] * e.v)
end

# negation
function Base.:(-)(e::SingleEntryVector{N}) where {N}
    return SingleEntryVector(e.i, e.n, -e.v)
end

function inner(e1::SingleEntryVector{N}, A::AbstractMatrix{N},
               e2::SingleEntryVector{N}) where {N}
    return A[e1.i, e2.i] * e1.v * e2.v
end


# another tree test

using IncrementalInference
using Test

@testset "second tree formation test" begin

fg = initfg()

addVariable!(fg, :x1, ContinuousScalar)
addVariable!(fg, :x2, ContinuousScalar)
addVariable!(fg, :x3, ContinuousScalar)
addVariable!(fg, :x4, ContinuousScalar)
addVariable!(fg, :x5, ContinuousScalar)
addVariable!(fg, :l1, ContinuousScalar)
addVariable!(fg, :l2, ContinuousScalar)
addVariable!(fg, :l3, ContinuousScalar)

addFactor!(fg, [:x1;:l1], LinearConditional(Normal()))
addFactor!(fg, [:x1;:x2], LinearConditional(Normal()))
addFactor!(fg, [:x2;:l1], LinearConditional(Normal()))
addFactor!(fg, [:x2;:x3], LinearConditional(Normal()))
addFactor!(fg, [:x3;:x4], LinearConditional(Normal()))
addFactor!(fg, [:x4;:l2], LinearConditional(Normal()))
addFactor!(fg, [:x4;:x5], LinearConditional(Normal()))
addFactor!(fg, [:l2;:x5], LinearConditional(Normal()))
addFactor!(fg, [:x4;:l3], LinearConditional(Normal()))
addFactor!(fg, [:x5;:l3], LinearConditional(Normal()))

writeGraphPdf(fg, show=true)

eo = [:x1; :l3; :l1; :x5; :x2; :l2; :x4; :x3]

tree = buildTreeFromOrdering!(fg,eo)

# using Gadfly, Fontconfig, Cairo
# drawTree(tree, show=true, imgs=false)
# drawTree(tree, show=true, imgs=true)
#

end

## Comparible reference available from gtsam/testSymbolicEliminationTree.cpp

#        l1                  l2
#    /    |                /  |
# x1 --- x2 --- x3 --- x4 --- x5
#                          \  |
#                            l3

# (X(1), L(1));
# (X(1), X(2));
# (X(2), L(1));
# (X(2), X(3));
# (X(3), X(4));
# (X(4), L(2));
# (X(4), X(5));
# (L(2), X(5));
# (X(4), L(3));
# (X(5), L(3));

# EliminationTreeTester::MakeTree(list_of
#     (MakeNode(X(3), SymbolicFactorGraph(), list_of
#       (MakeNode(X(2), list_of(SymbolicFactor(X(2), X(3))), list_of
#         (MakeNode(L(1), list_of(SymbolicFactor(X(2), L(1))), list_of
#           (MakeNode(X(1), list_of(SymbolicFactor(X(1), L(1))) (SymbolicFactor(X(1), X(2)))))))))
#       (MakeNode(X(4), list_of(SymbolicFactor(X(3), X(4))), list_of
#         (MakeNode(L(2), list_of(SymbolicFactor(X(4), L(2))), list_of
#           (MakeNode(X(5), list_of(SymbolicFactor(X(4), X(5))) (SymbolicFactor(L(2), X(5))), list_of
#             (MakeNode(L(3), list_of(SymbolicFactor(X(4), L(3))) (SymbolicFactor(X(5), L(3))))))))))) )));

#


function preprocess(PData::PDataLDLt, H, g, n1, n2)
    # printstyled("dans preprocessLDLt ⤈ \n", color = :red)
    T = eltype(H)
    # @show T
    # @show H[1, 1]
    # @show H[1, 2]
    # @show H[2, 1]
    # @show H[2, 2]
    # @show g[1]
    # @show g[2]
    n, = size(g)
    # @show n
    global L = Matrix{T}(undef, n, n)
    global D = Matrix{T}(undef, n, n)
    global pp = Vector{Int}(undef, n)
    global ρ = nothing
    global ncomp = nothing

    # @show H

    try
        (L, D, pp, ρ, ncomp) = ldlt_symm(H, 'r')
    catch
        println("*******   Problem in LDLt")
        res = PDataLDLt()
        res.OK = false
        return res
    end

    # printstyled("dans preprocessLDLt apres ldlt_symm eltype(L) = $(eltype(L)) \n", color = :cyan)
    # @show L
    # @show D
    # @show L[1, 1]
    # @show L[2, 1]
    # @show L[1, 2]
    # @show L[2, 2]
    # @show D[1, 1]
    # @show D[2, 1]
    # @show D[1, 2]
    # @show D[2, 2]
    # @show pp
    # @show ρ
    # @show ncomp

    if true in isnan.(D)
        println("*******   Problem in D from LDLt: NaN")
        println(" cond (H) = $(cond(H))")
        res = PDataLDLt()
        res.OK = false
        return res
    end

    # @show L
    # @show D
    # @show pp

    # D = Hermitian(D)
    # @show D
    X = eigen(SymTridiagonal(D))
    Δ = X.values
    # @show Δ[1]
    # @show Δ[2]
    Q = X.vectors
    # @show Q[1, 1]
    # @show Q[2, 1]
    # @show Q[1, 2]
    # @show Q[2, 2]
    l_m, = findmin(Δ)
    # @show l_m
    ĝ = L \ (g[pp])
    # @show ĝ[1]
    # @show ĝ[2]
    # @show eltype(ĝ)
    # @show ĝ
    g̃ = Q' * ĝ
    # @show g̃[1]
    # @show g̃[2]
    # @show eltype(g̃)
    # @show g̃

    n_g = norm(g)
    # @show n_g
    λ = max(-l_m, 0.0)
    # @show eltype(λ)
    # @show eltype(λ)
    # @show λ
    # printstyled("avant de sortir de PreProcessLDLt ↑ \n", color = :red)

    PData.L = L
    PData.D = D
    PData.pp = pp
    PData.Δ = Δ
    PData.Q = Q
    PData.g̃ = g̃
    PData.λ = λ
    PData.success = true
    PData.OK = true

    return PData
end

function AInv(X::PDataLDLt, d̃::Array{T,1}) where {T}
    d̂ = X.Q * d̃
    u = X.L' \ d̂
    # @show u[invperm(X.pp)][1]
    # @show u[invperm(X.pp)][2]
    # printstyled("avant la sortie de AInv ⟰ \n", color = :red)
    return u[invperm(X.pp)]
end


using GR

# Simple particle simulation

const N = 300                   # number of particles
M = 0.05 * ones(Float64, N)     # masses
const S = 0.04                  # size of particles

function step(dt, p, v)
  # update positions
  p[:,:] += dt * v[:,:]

  # find pairs of particles undergoing a collision
  for i in 1:N
    for j in i+1:N
      dx = p[i,1] - p[j,1]
      dy = p[i,2] - p[j,2]
      d = sqrt(dx*dx + dy*dy)

      if d < 2*S
        # relative location & velocity vectors
        r_rel = p[i] - p[j]
        v_rel = v[i] - v[j]

        # momentum vector of the center of mass
        v_cm = (M[i] * v[i] + M[j] * v[j]) / (M[i] + M[j])

        # collisions of spheres reflect v_rel over r_rel
        rr_rel = dot(r_rel, r_rel)
        vr_rel = dot(v_rel, r_rel)
        v_rel = 2 * r_rel * vr_rel / rr_rel - v_rel

        # assign new velocities
        v[i] = v_cm + v_rel * M[j] / (M[i] + M[j])
        v[j] = v_cm - v_rel * M[i] / (M[i] + M[j])
      end
    end
  end

  # check for crossing boundary
  for i in 1:N
    if p[i,1] < -2 + S
      p[i,1] = -2 + S
      v[i,1] *= -1
    elseif p[i,1] > 2 - S
      p[i,1] = 2 - S
      v[i,1] *= -1
    end
    if p[i,2] < -2 + S
      p[i,2] = -2 + S
      v[i,2] *= -1
    elseif p[i,2] > 2 - S
      p[i,2] = 2 - S
      v[i,2] *= -1
    end
  end

  return p, v
end

setwindow(-2, 2, -2, 2)
setviewport(0, 1, 0, 1)
setmarkertype(GR.MARKERTYPE_SOLID_CIRCLE)
setmarkersize(1.0)

n = 0
t = 0.0

p = (rand(N,2) - 0.5) * (4-2*S)     # initial positions
v = rand(N,2) - 0.5                 # initial velocities

const dt = 1.0 / 30

while t < 3

  start = tic()
  p, v = step(dt, p, v)

  clearws()
  setmarkercolorind(983)
  polymarker(p[:,1], p[:,2])

  if n > 0
    text(0.01, 0.95, @sprintf("%10s: %4d fps", "Julia", round(n / t)))
  end
  updatews()

  n += 1
  t += 1.0 / (toq() * 1000000)

end


using DataStructures
file = readlines("day_17.txt")

function get_neig(pos)
    return [(pos[1]+i,pos[2]+j,pos[3]+k) for i =-1:1 for j =-1:1 for k =-1:1
        if  !(i==0 && j==0 && k==0)]
end

cubes = DefaultDict(false)
recently_change = []

#reading input
for (index_i, line) in enumerate(file)
    for (index_j,word) in enumerate(line)
        if (word == '#')
            cubes[(index_i,index_j,0)] = true
            push!(recently_change,(index_i,index_j,0))
            append!(recently_change, get_neig((index_i,index_j,0)))
        end
    end
end

for cycle in 1:6
    new_cubes = DefaultDict(false)
    new_recently_change = []

    for pos in recently_change
        neig = get_neig(pos)
        sum_Act = sum([cubes[x] for x in neig])

        if cubes[pos] == true
            if sum_Act == 2 || sum_Act == 3
                new_cubes[pos] = true
                append!(new_recently_change,neig)
                push!(new_recently_change,pos)
            end
        else
            if sum_Act == 3
                new_cubes[pos] = true
                append!(new_recently_change,neig)
                push!(new_recently_change,pos)
            end
        end
    end

    global cubes = new_cubes
    global recently_change = new_recently_change
    println(cycle)
end

#heay, i could have made a better detect for possible active cubes, oh well
aa = 0
for (key, value) in cubes
    global aa+=value
end
print(aa)

#QUESTION 2 (the same thing but 3d)

file = readlines("day_17.txt")
function get_neig(pos)
    return [ (pos[1]+i,pos[2]+j,pos[3]+k,pos[4]+l)
        for i =-1:1 for j =-1:1 for k =-1:1 for l=-1:1
        if  !(i==0 && j==0 && k==0 && l==0)]
end

cubes = DefaultDict(false)

#reading input
for (index_i, line) in enumerate(file)
    for (index_j,word) in enumerate(line)
        if (word == '#')
            pos = (index_i,index_j,0,0)
            cubes[pos] = true
        end
    end
end

for cycle in 1:6
    new_cubes = DefaultDict(false)
    recently_change = DefaultDict(0)

    #count neig acc
    for (pos, value) in cubes
        if value == false
            print("AAAAAAAA")
        end
        neig = get_neig(pos)

        for i in neig
            recently_change[i] += value
        end
    end

    #find new cycle value
    for (pos, neig_acc) in recently_change
        curr_act = cubes[pos]
        new_act = false

        new_act |= (curr_act == true &&(neig_acc == 3 || neig_acc == 2))
        new_act |= (curr_act == false && neig_acc == 3 )

        if(new_act == true)
            new_cubes[pos] = true
        end
    end

    global cubes = new_cubes
    println(cycle)
end

aa = 0
for (key, value) in cubes
    global aa+=value
end
print(aa)


"""
    NearestNeighborElement <: NeutralLandscapeMaker

    NearestNeighborElement(; n=3, k=1)
    NearestNeighborElement(n, [k=1])

Assigns a value to each patch using a k-NN algorithmm with `n` initial clusters
and `k` neighbors. The default is to use three cluster and a single neighbor.
"""
@kwdef struct NearestNeighborElement <: NeutralLandscapeMaker
    n::Int64 = 3
    k::Int64 = 1
    function NearestNeighborElement(n::Int64, k::Int64 = 1)
        @assert n > 0
        @assert k > 0
        @assert k <= n
        new(n,k)
    end
end

function _coordinatematrix(mat)
    coordinates = Matrix{Float64}(undef, (2, prod(size(mat))))
    for (i, p) in enumerate(Iterators.product(axes(mat)...))
        coordinates[1:2, i] .= p
    end
    coordinates
end

function _landscape!(mat, alg::NearestNeighborElement)
    fill!(mat, 0.0)
    clusters = sample(eachindex(mat), alg.n; replace=false)
    for (i,n) in enumerate(clusters)
        mat[n] = i
    end
    coordinates = _coordinatematrix(mat)
    tree = KDTree(coordinates[:,clusters])
    if alg.k  == 1
        mat[:] .= nn(tree, coordinates)[1]
    else
        mat[:] .= map(mean, knn(tree, coordinates, alg.k)[1])
    end
    return mat
end


import Dolo
path = Dolo.pkg_path

fn = joinpath(path,"examples","models","rbc_mc.yaml")
model_mc = Dolo.yaml_import(fn)

drc = Dolo.ConstantDecisionRule(model_mc.calibration[:controls])
@time dr0, drv0 = Dolo.solve_policy(model_mc, drc; n_eval=1000) #, verbose=true, maxit=10000 )

# two loops:
# - VFI:
    # stops when err_v M tol_v

# optimize then inner_loop
# - inner: update until:
        - it_inner > maxit_inner
        - err_h < tol_h


using LinearAlgebra
using DifferentialEquations
using Distributions
using Random
using Printf
using Plots
using RCall
using CPUTime
using StatsBase
using JLD
# using StatsPlots
include("utils.jl")
include("sir_ode.jl")
include("abc.jl")
include("abcMCMC.jl")
include("abcSMC.jl")
include("BayesianCalibration.jl")

# Bayesian Calibration
algo_list = (ABC=ABC, ABC_MCMC=ABC_MCMC, ABC_SMC=ABC_SMC)

true_prior = (truncated(Normal(2,0.5),0,Inf),truncated(Normal(1,0.25),0,Inf))

algo_parameter_ABC = (prior = true_prior,
                     epsilon = 0.1,eta = identity_mapping, d = compute_norm)

algo_parameter_mcmc = (prior = true_prior,
                        epsilon = 1, eta = identity_mapping,
                        d= compute_norm, proposal = proposal_Normal,
                        proposalRatio = proposalRatio_Normal,sd = (0.5,0.25),
                        thinning = 10, verbose=false,
                        burn_in = 100)

algo_parameters_smc = (prior = true_prior,
                        time_final=5, eps_list =[10, 5, 1, 0.5, 0.1],
                        eta= identity_mapping, d= distanceFunction,
                        kernel=proposal_Normal,
                        kernel_density=proposal_Normal_density,
                        sd=(0.5,0.25), resample_method=systematic_resample,
                        verbose=false)

algo_param_list = (ABC = algo_parameter_ABC,
                    ABC_MCMC = algo_parameter_mcmc,
                    ABC_SMC = algo_parameters_smc)

simulation = BayesianCalibration(Int(2^10),Int(250),0.10,algo_list,algo_param_list,
                                ode_model = solve_ode,
                                initial_state = [50000.0;4.5;350]./50000,
                                time_window=(0,20.0),
                                add_noise = false,
                                true_p_dist=true_prior)

y = solve_ode([50000.0;4.5;350]./50000,(0,20.0),rand.(true_prior))
plot(y[1,:])
plot!(y[2,:])
plot!(y[3,:])

# calibration
show_calibration(simulation)

results_dir = "results/simulation_SIR/"

save(results_dir*"output.jld","output",simulation)

using DataFrames
simulation = load(results_dir*"output.jld")["output"]

function to_dataframe(x)
    tmp = DataFrame(x)
    rename!(tmp,[:ABC,:MCMC,:SMC])
end

CSV.write(results_dir*"calibration.csv",to_dataframe(simulation[2]))
CSV.write(results_dir*"cpu.csv",to_dataframe(simulation[4]))
CSV.write(results_dir*"ess.csv",to_dataframe(simulation[5]))
CSV.write(results_dir*"ess_cpu.csv",to_dataframe(simulation[6]))


# T: coeff type
# Degree: degree
# Dim1: dimension of the support + 1
mutable struct LagrangeRefSpace{T,Degree,Dim1,NF} <: RefSpace{T,NF} end

numfunctions(s::LagrangeRefSpace{T,D,2}) where {T,D} = D+1
numfunctions(s::LagrangeRefSpace{T,0,3}) where {T} = 1
numfunctions(s::LagrangeRefSpace{T,1,3}) where {T} = 3

valuetype(ref::LagrangeRefSpace{T}, charttype) where {T} =
        SVector{numfunctions(ref), Tuple{T,T}}

# Evaluate constant lagrange elements on anything
(ϕ::LagrangeRefSpace{T,0})(tp) where {T} = SVector(((value=one(T), derivative=zero(T)),))

# Evaluate linear Lagrange elements on a segment
function (f::LagrangeRefSpace{T,1,2})(mp) where T
    u = mp.bary[1]
    j = jacobian(mp)
    SVector(
        (value=  u, derivative=-1/j),
        (value=1-u, derivative= 1/j))
end

# Evaluete linear lagrange elements on a triangle
function (f::LagrangeRefSpace{T,1,3})(t) where T
    u,v,w, = barycentric(t)
    SVector(
        (value=u,),
        (value=v,),
        (value=w,))
end

"""
    f(tangent_space, Val{:withcurl})

Compute the values of the shape functions together with their curl.
"""
function (f::LagrangeRefSpace{T,1,3})(t, ::Type{Val{:withcurl}}) where T
    # Evaluete linear Lagrange elements on a triange, together with their curl
    j = jacobian(t)
    u,v,w, = barycentric(t)
    p = t.patch
    SVector(
        (value=u, curl=(p[3]-p[2])/j),
        (value=v, curl=(p[1]-p[3])/j),
        (value=w, curl=(p[2]-p[1])/j)
    )
end

# Evaluate constant Lagrange elements on a triangle, with their curls
function (f::LagrangeRefSpace{T,0,3})(t, ::Type{Val{:withcurl}}) where T
    i = one(T)
    z = zero(cartesian(t))
    (
        (value=i, curl=z,),
    )
end

function curl(ref::LagrangeRefSpace, sh, el)
    sh1 = Shape(sh.cellid, mod1(sh.refid+1,3), -sh.coeff)
    sh2 = Shape(sh.cellid, mod1(sh.refid+2,3), +sh.coeff)
    return [sh1, sh2]
end

function strace(x::LagrangeRefSpace, cell, localid, face)

    Q = zeros(scalartype(x),2,3)

    p1 = neighborhood(face, 1)
    p2 = neighborhood(face, 0)

    u1 = carttobary(cell, cartesian(p1))
    u2 = carttobary(cell, cartesian(p2))

    P1 = neighborhood(cell, u1)
    P2 = neighborhood(cell, u2)

    vals1 = x(P1)
    vals2 = x(P2)

    for j in 1:numfunctions(x)
        Q[1,j] = vals1[j].value
        Q[2,j] = vals2[j].value
    end

    Q
end

function restrict(refs::LagrangeRefSpace{T,0}, dom1, dom2) where T
    #Q = eye(T, numfunctions(refs))
    Q = Matrix{T}(I, numfunctions(refs), numfunctions(refs))
end

function restrict(f::LagrangeRefSpace{T,1}, dom1, dom2) where T

    D = numfunctions(f)
    Q = zeros(T, D, D)

    # for each point of the new domain
    for i in 1:D
        v = dom2.vertices[i]

        # find the barycentric coordinates in dom1
        uvn = carttobary(dom1, v)

        # evaluate the shape functions in this point
        x = neighborhood(dom1, uvn)
        fx = f(x)

        for j in 1:D
            Q[j,i] = fx[j][1]
        end
    end

    return Q
end