"""
    bresenhams_line_algorithm(maxx, maxy, x1, y1, x2, y2)

maxx, maxy: Integer max values

Assuming x1 and x2 are coordinates > 0.5, return integer coordinates between the two coordinates.

x1 to y2 may be floats, but this is intended to be 'floating point indexes' in a matrix.

Based on
https://stackoverflow.com/questions/40273880/draw-a-line-between-two-pixels-on-a-grayscale-image-in-julia

Adapted for float input arguments and limit checks.
"""
function bresenhams_line_algorithm(maxx::T, maxy::T, x1, y1, x2, y2) where {T<:Int}
    @assert round(Int, x1) <= maxx
    @assert round(Int, y1) <= maxy
    @assert round(Int, x1) >= 1
    @assert round(Int, y1) >= 1
    @assert round(Int, x2) <= maxx
    @assert round(Int, y2) <= maxy
    @assert round(Int, x2) >= 1
    @assert round(Int, y2) >= 1

    # Calculate distances
    dx = x2 - x1
    dy = y2 - y1

    # Determine how steep the line is
    is_steep = abs(dy) > abs(dx)

    # Rotate line
    if is_steep
        x1, y1 = y1, x1
        x2, y2 = y2, x2
        maxy, maxx = maxx, maxy
    end

    # Swap start and end points if necessary and store swap state
    swapped = if x1 > x2
        x1, x2 = x2, x1
        y1, y2 = y2, y1
        true
    else
        false
    end

    # Recalculate differentials
    dx = x2 - x1
    dy = y2 - y1

    # Calculate error
    error = round(Int, dx / 2.0)

    ystep = if y1 < y2
        1
    else
        -1
    end

    vy = Vector{Int}()

    # Iterate while generating points between start and end
    y = round(Int, y1)
    xiter = round(Int, x1):(round(Int, x2))
    for x in xiter
        push!(vy, y)
        error -= abs(dy)
        if error < 0
            y += ystep
            # edge case, a subpixel line might cover two pixels due to rounding.
            # However, if that happens to be on the edge, don't bleed over and don't cause an
            # error throw.
            if y > maxy || y < 1
                y -= ystep
                error -= dx
            end
            error += dx
        end
    end
    # Reverse the list if the coordinates were swapped
    if swapped
        if is_steep
            zip(reverse(vy), reverse(xiter))
        else
            zip(reverse(xiter), reverse(vy))
        end
    else
        if is_steep
            zip(vy, xiter)
        else
            zip(xiter, vy)
        end
    end
end

"""
    crossing_line_algorithm(maxx, maxy, cx, cy)

maxx, maxy: Integer max values

Return indices for pixels in a 10x10 to 11x11 cross centered at cx, cy

cx, cy may be floats, but this is intended to be 'floating point indexes' in a matrix.
"""

function crossing_line_algorithm(maxx::T, maxy::T, cx, cy) where T<:Int
    indices = Set{Tuple{Int64, Int64}}()
    d = 5
    xb = clamp(cx - d, 1, maxx)
    xt = clamp(cx + d, 1, maxx)
    yb = clamp(cy - d, 1, maxy)
    yt = clamp(cy + d, 1, maxy)
    lineindices = bresenhams_line_algorithm( maxx, maxy, xt, yb, xb, yt)
    push!(indices, lineindices...)
    lineindices = bresenhams_line_algorithm( maxx, maxy, xb, yb, xt, yt)
    push!(indices, lineindices...)
    indices
end

"""
    box_line_algorithm(maxx, maxy, cx, cy)

maxx, maxy: Integer max values

Return indices for pixels in a 10x10 to 11x11 cross centered at cx, cy

cx, cy may be floats, but this is intended to be 'floating point indexes' in a matrix.
"""

function box_line_algorithm(maxx::T, maxy::T, cx, cy) where T<:Int
    indices = Set{Tuple{Int64, Int64}}()
    d = 5
    xb = clamp(cx - d, 1, maxx)
    xt = clamp(cx + d, 1, maxx)
    yb = clamp(cy - d, 1, maxy)
    yt = clamp(cy + d, 1, maxy)
    lineindices = bresenhams_line_algorithm( maxx, maxy, xt, yb, xb, yb)
    push!(indices, lineindices...)
    lineindices = bresenhams_line_algorithm( maxx, maxy, xt, yt, xb, yt)
    push!(indices, lineindices...)
    lineindices = bresenhams_line_algorithm( maxx, maxy, xt, yb, xt, yt)
    push!(indices, lineindices...)
    lineindices = bresenhams_line_algorithm( maxx, maxy, xb, yb, xb, yt)
    push!(indices, lineindices...)
    indices
end

"""
    circle_algorithm(maxx, maxy, cx, cy)

maxx, maxy: Integer max values

Return indices for pixels in a 10 pixel diameter circle centered at cx, cy

cx, cy may be floats, but this is intended to be 'floating point indexes' in a matrix.
"""
function circle_algorithm(maxx::T, maxy::T, cx, cy) where T<:Int
    indices = Set{Tuple{Int64, Int64}}()
    d = 10
    pts = 2 * round(Int, π * d)
    for ϕ in range(0.0, 2π, length = pts)
        x = clamp(round(Int, cx + d * cos(ϕ) / 2), 1, maxx)
        y = clamp(round(Int, cy + d * cos(ϕ) / 2), 1, maxy)
        push!(indices, (x, y))
    end
    indices
end


using Printf

sup = [50, 60, 50, 50]
slab = ["W", "X", "Y", "Z"]
dem = [30, 20, 70, 30, 60]
dlab = ["A", "B", "C", "D", "E"]
c = [16 16 13 22 17;
     14 14 13 19 15;
     19 19 20 23 50;
     50 12 50 15 11]

tp = TProblem(sup, dem, c, slab, dlab)
sol = vogel(tp)
cost = sum(tp.toc .* sol)

println("The solution is:")
print("        ")
for s in tp.labels[2]
    print(@sprintf "%4s" s)
end
println()
for i in 1:size(tp.toc)[1]
    print(@sprintf "    %4s" tp.labels[1][i])
    for j in 1:size(tp.toc)[2]
        print(@sprintf "%4d" sol[i,j])
    end
println()
end
println("The total cost is:  ", cost)


import LinearAlgebra: *, adjoint, det, diag, diagm, dot, inv, kron, logabsdet, logdet, lq, norm, qr, svd, tr, transpose, tril, triu

# julia/stdlib/v0.7/LinearAlgebra/src/LinearAlgebra.jl Functions:
# axpy!
# axpby!
# bunchkaufman
# bunchkaufman!
# @primitive chol(x),dy,y   chol_back(y, dy) # `chol(A::AbstractMatrix)` is deprecated, use `(cholesky(A)).U` instead.
# cholesky
# cholesky!
# cond
# condskeel
# copyto!
# copy_transpose!
# cross
@primitive adjoint(x),dy    adjoint(dy)
# adjoint!
@primitive det(x),dy,y  dy*y*inv(x)'
@primitive diag(x),dy,y   diagm(0=>dy)  # alternative: Diagonal(dy)
@primitive diag(x,i),dy,y   diagm(i=>dy)  # warning: these only works for square matrices
# diagind
# @primitive diagm(x),dy,y   diag(dy,x[1]) # TODO: diagm has a pair input
@primitive dot(x1, x2),dy,y  dy*x2  dy*x1  # addtestN(:dot, rand(3,2), rand(3,2))
# eigen
# eigen!
# eigmax
# eigmin
# eigvals
# eigvals!
# eigvecs
# @zerograd eye(x) # Warning: `eye(A::AbstractMatrix{T}) where T` has been deprecated in favor of `I` and `Matrix` constructors. For a direct replacement, consider `Matrix{eltype(A)}(I, size(A))`.If `eltype(A)` element type is not necessary, consider the shorter `Matrix(I, size(A))` (with default `eltype(I)` `Bool`).
# factorize
# givens
# hessenberg
# hessenberg!
# inv: not part of LinearAlgebra, imported from Base
@primitive inv(x),dy,y  (yt=y'; -yt*dy*yt) # addtest(:inv, rand(2,2)) # ref https://people.maths.ox.ac.uk/gilesm/files/NA-08-01.pdf
# isdiag
# ishermitian
# isposdef
# isposdef!
# issuccess
# issymmetric
# istril
# istriu
kron(a::Value, b::Value)  = _kron(a, b)
kron(a::Value, b) = _kron(a, b)
kron(a, b::Value) = _kron(a, b)
# ldiv!
# ldlt!
# ldlt
@primitive logabsdet(x),dy,y  dy[1]*inv(x)'
@primitive logdet(x),dy,y  dy*inv(x)'
# lowrankdowndate
# lowrankdowndate!
# lowrankupdate
# lowrankupdate!
# lu
# lu!
# lyap
# mul!
# lmul!
# rmul!
@primitive norm(x),dy,y  normback(x,2,dy,y)
@primitive norm(x,p::Real),dy,y normback(x,p,dy,y)
# normalize
# normalize!
# nullspace
# ordschur!
# ordschur
# pinv
@primitive qr(x),dy,y   qr_back(y, dy)
# qr!
@primitive lq(x),dy,y   lq_back(y, dy)
# lq!
# opnorm
# rank
# rdiv!
# schur
# schur!
@primitive svd(x),dy,y  svd_back(x, y, dy)
# svd!
# svdvals!
# svdvals
# sylvester
@primitive tr(x),dy,y  dy*Matrix(I,size(x)) # tr: trace deprecated using tr instead # addtest(:tr, rand(3,3))
@primitive transpose(x),dy  transpose(dy)
# transpose!
# transpose_type
@primitive tril(x),dy,y  dy.*tril(fill!(similar(x),1))  # addtest(:tril, rand(3,3))
# tril!
@primitive triu(x),dy,y  dy.*triu(fill!(similar(x),1))  # addtest(:triu, rand(3,3))
# triu!

# julia/stdlib/v0.7/LinearAlgebra/src/LinearAlgebra.jl Operators:
# @primitive1 *(x1,x2),dy  (dy*x2')  (x1'*dy) # --> base.jl
# @primitive1 ^(x1,x2::Integer),dy,y  error("Derivatives of integer matrix powers not defined.") # this gives real results, TODO. --> base.jl
# @primitive1 ^(x1,x2::Number),dy,y   error("Derivatives of real matrix powers not defined.") # this could give imaginary results, out of scope --> base.jl
# \
# /
# ⋅ = dot
# × = cross
# +
# -
# ==

# julia/stdlib/v0.7/LinearAlgebra/src/blas.jl exports:
# # Level 1
# asum
# axpy!
# axpby!
# blascopy!
# dot
# dotc
# dotu
# scal!
# scal
# nrm2
# iamax
# # Level 2
# gbmv!
# gbmv
# gemv!
# gemv
# hemv!
# hemv
# sbmv!
# sbmv
# symv!
# symv
# trsv!
# trsv
# trmv!
# trmv
# ger!
# syr!
# her!
# # Level 3
# herk!
# herk
# her2k!
# her2k
# gemm!
# gemm
# symm!
# symm
# hemm!
# hemm
# syrk!
# syrk
# syr2k!
# syr2k
# trmm!
# trmm
# trsm!
# trsm

### Helper functions

# chol: Warning: `chol(A::AbstractMatrix)` is deprecated, use `(cholesky(A)).U` instead.
# ref: formulua in https://arxiv.org/pdf/1602.07527.pdf
# as described in https://arxiv.org/pdf/1710.08717.pdf
# In julia L is upper triangular
function chol_back(L, dL)
    dL = triu(dL)
    iL = inv(L)
    S = iL * Symmetric(L*dL',:L) * iL'
    S/2
end
# _choltest(x)=chol(x'x)
# addtest(:_choltest, rand(3,3))

# kron
# code adapted from https://github.com/FluxML/Flux.jl/pull/169
function _kron(mat1, mat2)
    @assert ndims(mat1) == ndims(mat2) == 2 "Only support matrices for the time being"
    m1, n1 = size(mat1)
    mat1_rsh = reshape(mat1,(1,m1,1,n1))

    m2, n2 = size(mat2)
    mat2_rsh = reshape(mat2,(m2,1,n2,1))

    return reshape(mat1_rsh.*mat2_rsh, (m1*m2,n1*n2))
end
# addtestN(:kron, rand(2,3), rand(4,5))

# qr
function qr_back(y, dy)
    Q, R = y
    dQ, dR = dy
    dR == nothing && (dR = zero(R))
    dQ == nothing && (dQ = zero(Q))
    dR = triu(dR)
    M = Symmetric(R*dR' - dQ'*Q, :L)
    S = (dQ + Q*M)*inv(R)'
    S
end
# _qr1(x)=qr(x)[1]; addtest(:_qr1, rand(3,3))
# _qr2(x)=qr(x)[2]; addtest(:_qr2, rand(3,3))
# _qr3(x)=(y=qr(x); sum(y[1]+y[2])); addtest(:_qr3, rand(3,3))

# lq
# ref: https://arxiv.org/pdf/1710.08717.pdf
function lq_back(y, dy)
    L, Q = y
    dL, dQ = dy
    dL == nothing && (dL = zero(L))
    dQ == nothing && (dQ = zero(Q))
    dL = tril(dL)
    M = Symmetric(L'dL - dQ*Q', :L)
    S = inv(L)' *(dQ + M*Q)
    S
end
# _lq1(x)=lq(x)[1]; addtest(:_lq1, rand(3,3))
# _lq2(x)=lq(x)[2]; addtest(:_lq2, rand(3,3))
# _lq3(x)=(y=lq(x); sum(y[1]+y[2])); addtest(:_lq3, rand(3,3))

# vecnorm has been renamed norm:

function normback(x,p,dy,y)
    if length(x) == 0
        similar(x)
    elseif p == 2
        (dy/y)*x
    elseif p == 1
        dy * sign.(x)
    elseif p == Inf
        dy * sign.(x) .* (abs.(x) .== y)
    elseif p == 0
        zero(x)
    elseif p == -Inf
        dy * sign.(x) .* (abs.(x) .== y)
    else
        (dy*y^(1-p)*(abs.(x).^(p-1)).*sign.(x))
    end
end

# addtest(:norm, rand(2,2)-0.5)
# for p in (0,1,2,Inf,-Inf,rand(),-rand(),1+rand(),-1-rand())
#     addtest(:norm, rand(2,2)-0.5, p)
# end

# det, logdet, logabsdet
#ref https://people.maths.ox.ac.uk/gilesm/files/NA-08-01.pdf
# TODO make more efficient using the intermediate
# results of SVD in the forward pass

# addtest(:det, rand(3,3))
# addtest(:logdet, 5eye(3) + rand(3,3))
# gradcheck(logabsdet, rand([-1,1]) .* rand(3,3))

# ref https://j-towns.github.io/papers/svd-derivative.pdf
function svd_back(x, y, dy)
    U, s, V = y
    dU, ds, dV = dy

    F = s'.^2 .- s.^2
    F = 1 ./ (F + eye(F)) - eye(F) #avoid infinities on the diagonal

    dx = zero(x)
    S = diagm(s)
    if ds != nothing
        dx += U*diagm(ds)*V'
    end
    if dU != nothing
        UUt = U*U'
        dx += (U*(F.*(U'dU-dU'U))*S + (eye(UUt) - UUt)*dU*inv(S))*V'
    end

    if dV != nothing
        VVt = V*V'
        dx += U*(S*(F.*(V'dV-dV'V))*V' + inv(S)*dV'*(eye(VVt) - VVt))
    end

    dx
end

# _svd1(x)=svd(x)[1]
# _svd2(x)=svd(x)[2]
# _svd3(x)=svd(x)[3]
# for A in (rand(2,2), rand(2,3), rand(3,2))
#     addtest(:_svd1, A)
#     addtest(:_svd2, A)
#     addtest(:_svd3, A)
# end


immutable Arcsine <: ContinuousUnivariateDistribution
    a::Float64
    b::Float64

    function Arcsine(a::Float64, b::Float64)
        a < b || error("a must be less than b.")
        new(a, b)
    end

    Arcsine(a::Real, b::Real) = Arcsine(float64(a), float64(b))
    Arcsine(b::Real) = Arcsine(0.0, float64(b))
    Arcsine() = new(0.0, 1.0)
end

@distr_support Arcsine d.a d.b

### Parameters

params(d::Arcsine) = (d.a, d.b)
location(d::Arcsine) = d.a
scale(d::Arcsine) = d.b - d.a

### Statistics

mean(d::Arcsine) = (d.a + d.b) * 0.5
median(d::Arcsine) = mean(d)
mode(d::Arcsine) = d.a
modes(d::Arcsine) = [d.a, d.b]

var(d::Arcsine) = 0.125 * abs2(d.b - d.a)
skewness(d::Arcsine) = 0.0
kurtosis(d::Arcsine) = -1.5

entropy(d::Arcsine) = -0.24156447527049044469 + log(scale(d))

### Evaluation

pdf(d::Arcsine, x::Float64) = insupport(d, x) ? 1.0 / (π * sqrt((x - d.a) * (d.b - x))) : 0.0

logpdf(d::Arcsine, x::Float64) = insupport(d, x) ? -(logπ + 0.5 * log((x - d.a) * (d.b - x))) : -Inf

cdf(d::Arcsine, x::Float64) = x < d.a ? 0.0 :
                              x > d.b ? 1.0 :
                              0.636619772367581343 * asin(sqrt((x - d.a) / (d.b - d.a)))

quantile(d::Arcsine, p::Float64) = location(d) + abs2(sin(halfπ * p)) * scale(d)

### Sampling

rand(d::Arcsine) = quantile(d, rand())


"""
    $(SIGNATURES)

Check a scalar: should be real, finite, not nan, in optional bounds.
Real is implied by being `AbstractFloat`.
"""
function check_float(x :: T1; lb = nothing, ub = nothing) where T1 <: AbstractFloat
    isValid = true;
    if !isfinite(x)
        isValid = false;
        @warn "Not finite"
    end
    if !isnothing(lb)  &&  (x < lb - 1e-8)
        isValid = false;
        @warn "Below $lb"
    end
    if !isnothing(ub)  &&  (x > ub + 1e-8)
        isValid = false;
        @warn "Above $ub"
    end
    if !isValid
        println("Input value: $x");
    end
    return isValid
end

# Catches cases where the input is not `AbstractFloat`
function check_float(x; kwargs...)
    @warn "Not an AbstractFloat: $x"
    return false
end

"""
	$(SIGNATURES)

Check that a numeric array is finite, not NaN, and inside bounds.
"""
function check_float_array(m :: AbstractArray{T1},
    lb :: T2, ub :: T2) where {T1 <: AbstractFloat, T2 <: AbstractFloat}

    isValid = true;

    anyInf = any(x -> !isfinite(x), m);
    if anyInf
        isValid = false;
        nInf = sum(x -> !isfinite(x), m);
        @warn "$nInf elements not finite"
    end

    validLb = !isfinite(lb)  ||  all(x -> x >= lb, m);
    if !validLb
        isValid = false;
        mMin, idxMin = findmin(m);
        @warn """
            Array lower bound violated: $mMin < $lb
            at  $idxMin
            """
    end

    validUb = !isfinite(ub)  ||  all(x -> x <= ub, m);
    if !validUb
        isValid = false;
        mMax, idxMax = findmax(m);
        @warn """
            Array upper bound violated: $mMax < $ub
            at  $idxMax
            """
    end

    return isValid
end

"""
	$(SIGNATURES)

Check that an array is monotone along one dimension (weak or strict).

Based on `diff` in `Base`.
"""
function is_monotone(a :: AbstractArray{T,N}, d :: Integer;
    increasing, strict, atol = zero(T)) where {T,N}

    1 <= d <= N || throw(ArgumentError("dimension $d out of range (1:$N)"));

    r = axes(a);
    r0 = ntuple(i -> i == d ? UnitRange(1, last(r[i]) - 1) : UnitRange(r[i]), N);
    r1 = ntuple(i -> i == d ? UnitRange(2, last(r[i])) : UnitRange(r[i]), N);

    v0 = view(a, r0...);
    v1 = view(a, r1...);

    if increasing
        strict  ?  (op = >)  :  (op = >=);
    else
        strict  ?  (op = <)  :  (op = <=);
    end

    success = true;
    for idx in eachindex(v1)
        # E.g.: v1-v0 > -1e-8
        op(v1[idx] - v0[idx], -atol)  ||  (success = false; break);
    end
    return success
end

# ------------------


## Real-to-complex and complex-to-real transforms.

"""
    RFFT()

Real-to-complex FFT.

See also
[`AbstractFFTs.rfft`](https://juliamath.github.io/AbstractFFTs.jl/stable/api/#AbstractFFTs.rfft).
"""
struct RFFT <: AbstractTransform end

"""
    BRFFT()

Unnormalised inverse of [`RFFT`](@ref).

To obtain the inverse transform, divide the output by the length of the
transformed dimension (of the real output array).

See also
[`AbstractFFTs.brfft`](https://juliamath.github.io/AbstractFFTs.jl/stable/api/#AbstractFFTs.brfft).
"""
struct BRFFT <: AbstractTransform end

const TransformR2C = RFFT
const TransformC2R = BRFFT

is_inplace(::Union{TransformR2C, TransformC2R}) = false

length_output(::TransformR2C, length_in::Integer) = div(length_in, 2) + 1
length_output(::TransformC2R, length_in::Integer) = 2 * length_in - 2

eltype_output(::TransformR2C, ::Type{T}) where {T <: FFTReal} = Complex{T}
eltype_output(::TransformC2R, ::Type{Complex{T}}) where {T <: FFTReal} = T

eltype_input(::TransformR2C, ::Type{T}) where {T <: FFTReal} = T
eltype_input(::TransformC2R, ::Type{T}) where {T <: FFTReal} = Complex{T}

plan(::RFFT, args...; kwargs...) = FFTW.plan_rfft(args...; kwargs...)

# NOTE: unlike most FFTW plans, this function also requires the length `d` of
# the transform output along the first transformed dimension.
plan(::BRFFT, args...; kwargs...) = FFTW.plan_brfft(args...; kwargs...)

binv(::RFFT) = BRFFT()
binv(::BRFFT) = RFFT()

# Note: the output of RFFT (BRFFT) is complex (real).
scale_factor(::BRFFT, A::RealArray, dims) = _prod_dims(A, dims)
scale_factor(::RFFT, A::ComplexArray, dims) = _scale_factor_r2c(A, dims...)

function _scale_factor_r2c(A::ComplexArray, dim1, dims...)
    # I need to normalise by the *logical* size of the real output.
    # We assume that the dimension `dim1` is the dimension with Hermitian
    # symmetry.
    s = size(A)
    2 * (s[dim1] - 1) * _intprod((s[i] for i in dims)...)
end

# r2c along the first dimension, then c2c for the other dimensions.
expand_dims(::RFFT, ::Val{N}) where N =
    N === 0 ? () : (RFFT(), expand_dims(FFT(), Val(N - 1))...)
expand_dims(::BRFFT, ::Val{N}) where N =
    N === 0 ? () : (BRFFT(), expand_dims(BFFT(), Val(N - 1))...)


using BlockDiagonals
using BlockDiagonals: svd_blockwise, eigen_blockwise
using LinearAlgebra
using Random
using Test

@testset "linalg.jl" begin
    rng = MersenneTwister(123456)
    N1, N2, N3 = 3, 4, 5
    N = N1 + N2 + N3
    b1 = BlockDiagonal([rand(rng, N1, N1), rand(rng, N2, N2), rand(rng, N3, N3)])
    b2 = BlockDiagonal([rand(rng, N1, N1), rand(rng, N3, N3), rand(rng, N2, N2)])
    b3 = BlockDiagonal([rand(rng, N1, N1), rand(rng, N2, N2), rand(rng, N2, N2)])
    A = rand(rng, N, N + N1)
    B = rand(rng, N + N1, N + N2)
    A′, B′ = A', B'
    a = rand(rng, N)
    b = rand(rng, N + N1)

    @testset "mul!" begin
        c = similar(b1)
        d = similar(Matrix(b1))
        mul!(c, b1, b1)
        mul!(d, Matrix(b1), Matrix(b1))
        @test c ≈ d
        if VERSION ≥ v"1.3"
            mul!(c, b1, b1, 2.0, 3.0)
            mul!(d, Matrix(b1), Matrix(b1), 2.0, 3.0)
            @test c ≈ d
        end
    end

    @testset "Unary Linear Algebra" begin
        @testset "$f" for f in (adjoint, det, diag, eigvals, inv, pinv, svdvals, transpose, tr)
            @test f(b1) ≈ f(Matrix(b1))
        end

        @testset "permute=$p, scale=$s" for p in (true, false), s in (true, false)
            @testset "$g" for g in (eigmin, eigmax)
                # `b2` has real eigenvals, required for `eigmin`, `b1` has Complex eigenvals
                @test g(b2; permute=p, scale=s) ≈ g(Matrix(b2); permute=p, scale=s)
            end
            @testset "eigvals" begin
                result = eigvals(b2; permute=p, scale=s)
                expected = eigvals(Matrix(b2), permute=p, scale=s)
                @static if VERSION < v"1.2"
                    # the `eigvals` method we hit above did not sort real eigenvalues pre-v1.2
                    # but some `eigvals` methods did, so we always sort real eigenvalues.
                    @test sort!(result) ≈ sort!(expected)
                else
                    @test result ≈ expected
                end
            end
        end

        @testset "Symmetric/Hermitian" begin
            @test Symmetric(b1) == Symmetric(Matrix(b1))
            @test Symmetric(b1, :L) == Symmetric(Matrix(b1), :L)
            @test Hermitian(b1) == Hermitian(Matrix(b1))
            @test Hermitian(b1, :L) == Hermitian(Matrix(b1), :L)
        end

        @testset "Eigen Decomposition" begin

            @testset "eigen $name" for (name, B) in [("", b1), ("symmetric", Symmetric(b1)), ("hermitian", Hermitian(b1))]
                E = eigen(B)
                evals_bd, evecs_bd = E
                evals, evecs = eigen(Matrix(B))

                @test E isa Eigen
                @test Matrix(E) ≈ B

                # There is no test like @test eigen(B) == eigen(Matrix(B))
                # 1. this fails in the complex case. Probably a convergence thing that leads to ever so slight differences
                # 2. pre version 1.2 this can't be expected to hold at all because the order of eigenvalues was random
                # so I sort the values/vectors (if needed) and then compare them via ≈

                @static if VERSION < v"1.2"
                    # pre-v1.2 we need to sort the eigenvalues consistently
                    # Since eigenvalues may be complex here, I use this function, which works for this test.
                    # This test is already somewhat fragile w. r. t. degenerate eigenvalues
                    # and this just makes this a little worse.
                    perm_bd = sortperm(real.(evals_bd) + 100*imag.(evals_bd))
                    evals_bd = evals_bd[perm_bd]
                    evecs_bd = evecs_bd[:, perm_bd]

                    perm = sortperm(real.(evals) + 100*imag.(evals))
                    evals = evals[perm]
                    evecs = evecs[:, perm]
                end

                @test evals_bd ≈ evals
                # comparing vectors is more difficult due to a sign ambiguity
                # So we try adding and subtracting the vectors, keeping the smallest magnitude for each entry
                # and compare that with something small.
                # I performed some tests and the largest deviation I found was ~5e-14
                @test all(min.(abs.(evecs_bd - evecs), abs.(evecs_bd + evecs)) .< 1e-13)
            end

            @testset "eigen_blockwise $name" for (name, B) in [("", b1), ("symmetric", Symmetric(b1)), ("hermitian", Hermitian(b1))]
                vals, vecs = eigen_blockwise(B)

                # check types. Eltype varies so not checked here (should be ComplexF64, Float64, Float64)
                @test vecs isa BlockDiagonal
                @test vals isa Vector

                # Note: /(Matrix, BlockDiagonal) fails. I think because we can't do factorize(BlockDiagonal)
                # this is why I convert to Matrix prior to /
                @test B ≈ vecs * Diagonal(vals) / Matrix(vecs)

                # check by block
                cumulative_size = 0
                for (i, block) in enumerate(blocks(B))
                    block_vals = vals[cumulative_size+1:cumulative_size+size(block,1)]
                    cumulative_size += size(block, 1)
                    # adapt eltype to the same to block_vals.
                    # block_vals's eltype is chosen to be compatible across all eigenvalues, thus it might be different
                    block = convert.(eltype(block_vals), block)

                    # from here on the code parallel to the test code above
                    E = Eigen(block_vals, blocks(vecs)[i])
                    evals_bd, evecs_bd = E
                    evals, evecs = eigen(block)

                    @test block ≈ Matrix(E)

                    @static if VERSION < v"1.2"
                        # sorting if needed
                        perm_bd = sortperm(real.(evals_bd) + 100*imag.(evals_bd))
                        evals_bd = evals_bd[perm_bd]
                        evecs_bd = evecs_bd[:, perm_bd]

                        perm = sortperm(real.(evals) + 100*imag.(evals))
                        evals = evals[perm]
                        evecs = evecs[:, perm]
                    end

                    @test evals_bd ≈ evals
                    @test all(min.(abs.(evecs_bd - evecs), abs.(evecs_bd + evecs)) .< 1e-13)
                end
            end
        end

        @testset "eigvals on LinearAlgebra types" begin
            # `eigvals` has different methods for different types, e.g. Hermitian
            b_herm = BlockDiagonal([Hermitian(rand(rng, 3, 3) + I) for _ in 1:3])
            @test eigvals(b_herm) ≈ eigvals(Matrix(b_herm))
            @test eigvals(b_herm, 1.0, 2.0) ≈ eigvals(Hermitian(Matrix(b_herm)), 1.0, 2.0)
        end

        # Requires a postive semi-definite matrix
        @testset "logdet" begin
            b̂1 = b1 * b1'
            @test logdet(b̂1) ≈ logdet(Matrix(b̂1))
        end
    end  # Unary
    @testset "Cholesky decomposition" begin
        X = [  4  12 -16
              12  37 -43
             -16 -43  98]
        U = [ 2.0 6.0 -8.0
              0.0 1.0  5.0
              0.0 0.0  3.0]

        BD = BlockDiagonal([X, X])
        C = cholesky(BD)
        @test C isa Cholesky{Float64, <:BlockDiagonal{Float64}}
        @test C.U ≈ cholesky(Matrix(BD)).U
        @test C.U ≈ BlockDiagonal([U, U])
        @test C.L ≈ BlockDiagonal([U', U'])
        @test C.UL ≈ C.U
        @test C.uplo === 'U'
        @test C.info == 0

        M = BlockDiagonal(map(Matrix, blocks(C.L)))
        C = Cholesky(M, 'L', 0)
        @test C.U ≈ cholesky(Matrix(BD)).U
        @test C.U ≈ BlockDiagonal([U, U])
        @test C.L ≈ BlockDiagonal([U', U'])
        @test C.UL ≈ C.L
        @test C.uplo === 'L'
        @test C.info == 0
    end  # Cholesky
    @testset "Singular Value Decomposition" begin
        X = [  4  12 -16
              12  37 -43
             -16 -43  98]
        BD = BlockDiagonal([X, X])

        @testset "full=$full" for full in (true, false)

            @testset "svd_blockwise" begin
                U, S, Vt = svd_blockwise(BD; full=full)
                F = SVD(U, S, Vt)
                @test BD ≈ F.U * Diagonal(F.S) * F.Vt

                # Matrices should be BlockDiagonal
                @test F isa SVD{Float64, Float64, <:BlockDiagonal{Float64}}
                @test F.U isa BlockDiagonal
                @test F.V isa BlockDiagonal
                @test F.Vt isa BlockDiagonal

                # Should have same values, but not sorted so as to keep BlockDiagonal structure
                F_ = svd(Matrix(BD), full=full)
                for fname in fieldnames(SVD)
                    @test sort(vec(getfield(F, fname))) ≈ sort(vec(getfield(F_, fname)))
                end
                # Singular values should be block-wise
                s = svd(X).S
                @test F.S == vcat(s, s)
            end

            @testset "svd" begin
                F = svd(BD; full=full)
                F_ = svd(Matrix(BD), full=full)

                @test F isa SVD
                @test BD ≈ F.U * Diagonal(F.S) * F.Vt

                @test F == F_
                for fname in fieldnames(SVD)
                    @test getfield(F, fname) ≈ getfield(F_, fname)
                end

                # Singular values should be sorted in descending order
                @test F.S == sort(F.S, rev=true)
            end
        end
    end  # SVD
    @testset "Left division" begin
        N1 = 20
        N2 = 8
        N3 = 5
        A = BlockDiagonal([rand(rng, N1, N1), rand(rng, N2, N2)])
        B = BlockDiagonal([rand(rng, N1, N2), rand(rng, N3, N1)])
        x = rand(rng, N1 + N2)
        y = rand(rng, N1 + N3)
        @test A \ x ≈ inv(A) * x
        @test B \ y ≈ Matrix(B) \ y
    end
end


import IntervalWavelets: trapezquad
using Base.Test

# Test trapezquad
x = dyadic_rationals( DaubSupport(-1,1), 3 )
@test trapezquad( x, ones(x) ) == 2
@test trapezquad( x, x ) == 0


abstract type Transform end

struct pipeline
    transforms
    inplace::Bool
end

"""
    Pipeline(Transforms)

Constructs a transformation pipeline from vector/tuple of `Transforms`. The Transforms vector are effectively a vector of functions which transform data.
"""
Pipeline(Transforms) = pipeline(Transforms, false)

"""
    PipelineInPlace( X, FnStack...)

Construct a pipeline object from vector/tuple of `Transforms`. The Transforms vector are effectively a vector of functions which transform data.
This function makes "inplace" changes to the Array `X` as though it has been sent through the pipeline.
This is more efficient if memory is a concern, but can irreversibly transform data in memory depending on the transforms in the pipeline.
"""
function PipelineInPlace( X, FnStack...)
    pipe = Array{Any,1}(undef, length(FnStack))
    for (i, fn) in enumerate( FnStack )
        pipe[i] = isa(fn, Function) ? fn : fn(X)
        X .= pipe[i]( X )
    end
    return pipeline(Tuple(pipe), true)
end

"""
    Pipeline( X, FnStack... )

Construct a pipeline object from vector/tuple of `Transforms`. The Transforms vector are effectively a vector of functions which transform data.
"""
function Pipeline( X, FnStack... )
    pipe = Array{Any,1}( undef, length( FnStack ))
    for (i, fn) in enumerate( FnStack )
        pipe[ i ] = isa( fn, Function ) ? fn : fn( X )
        X = pipe[ i ]( X )
    end
    return pipeline(Tuple( pipe ), false)
end

"""
    (P::pipeline)(X; inverse = false)

Applies the stored transformations in a pipeline object `P` to data in X.
The inverse flag can allow for the transformations to be reversed provided they are invertible functions.
"""
function (P::pipeline)(X; inverse = false)
    if inverse
        if P.inplace
            for fn in reverse( P.transforms ); X .= fn( X; inverse = true ) ; end
        else
            foldr( ( p, X ) -> p(X; inverse = true), P.transforms, init = X)
        end
    else
        if P.inplace
            for fn in enumerate( X.transforms ); X .= fn( X ) ; end
        else
            foldl( ( X, p ) -> p(X), P.transforms, init = X)
        end
    end
end

struct QuantileTrim <: Transform
    Quantiles::Array
    invertible::Bool
end

"""
    QuantileTrim(Z; quantiles::Tuple{Float64,Float64} = (0.05, 0.95) )

Trims values above or below the specified columnwise quantiles to the quantile values themselves.
"""
function QuantileTrim(Z; quantiles::Tuple{Float64,Float64} = (0.05, 0.95) )
    @assert length(quantiles) == 2
    return QuantileTrim( EmpiricalQuantiles(Z, quantiles), false )
end

"""
    (T::QuantileTrim)(X, inverse = false)

Trims data in array `X` columns wise according to learned quantiles in QuantileTrim object `T`
This function does NOT have an inverse.
"""
function (T::QuantileTrim)(X, inverse = false)
    if inverse == false
        for c in 1:size(X)[2]
            if T.Quantiles[ 1, c ] != T.Quantiles[ 2, c ]
                lt = X[ : , c ] .< T.Quantiles[ 1, c ]
                gt = X[ : , c ] .> T.Quantiles[ 2, c ]
                X[ lt, c ] .= T.Quantiles[ 1, c ]
                X[ gt, c ] .= T.Quantiles[ 2, c ]
            end
        end
    else
        println("QuantileScale does not provide an inverse, skipping operation.")
    end
    return X
end

struct Center{B} <: Transform
    Mean::B
    invertible::Bool
end

"""
    Center(Z)

Acquires the mean of each column in `Z` provided and returns a transform that will subtract those column means from any future data.
"""
Center(Z::Array) = Center( StatsBase.mean(Z, dims = 1), true )

"""
    (T::Center)(Z; inverse = false)

Centers data in array `Z` column-wise according to learned mean centers in Center object `T`.
"""
(T::Center)(Z; inverse = false) = (inverse) ? (Z .+ T.Mean) : (Z .- T.Mean)

struct Scale{B} <: Transform
    StdDev::B
    invertible::Bool
end

"""
    Scale(Z)

Acquires the standard deviation of each column in `Z` provided and returns a transform that will divide those column-wise standard deviation from any future data.
"""
Scale(Z::Array) = Scale( StatsBase.std(Z, dims = 1),  true )

"""
    (T::Scale)(Z; inverse = false)

Scales data in array `Z` column-wise according to learned standard deviations in Scale object `T`.
"""
(T::Scale)(Z; inverse = false) = (inverse) ? (Z .* T.StdDev) : (Z ./ T.StdDev)

struct CenterScale{B,C} <: Transform
    Mean::B
    StdDev::C
    invertible::Bool
end

"""
    CenterScale(Z)

This is a composition of Center and Scale (in that order).
"""
function CenterScale(Z::Array)
    mu = StatsBase.mean(Z, dims = 1)
    stdev = StatsBase.std(Z, dims = 1)
    CenterScale( mu, stdev, true)
end

"""
    (T::CenterScale)(Z; inverse = false)

Centers and Scales data in array `Z` column-wise according to learned measures of central tendancy in Scale object `T`.
"""
(T::CenterScale)(Z; inverse = false) = (inverse) ? ((Z .* T.StdDev) .+ T.Mean) : ((Z .- T.Mean) ./ T.StdDev)

struct RangeNorm{B,C} <: Transform
    Mins::B
    Maxes::C
end

"""
    RangeNorm( Z )

Acquires the minimum and maximum of each column in `Z` provided and returns a transform that performs the following operation (Z - min(X))/(max(X) - min(X)) on any future data. This has the important effect of scaling all values observed in the range of `Z` to be between 0 and 1 with respect to each column.
"""
function RangeNorm( Z::Array )
    mins = minimum(Z, dims = 1)
    maxes = maximum(Z, dims = 1)
    return RangeNorm( mins, maxes)
end

"""
    (T::RangeNorm)(Z; inverse = false)

Scales and shifts data in array `Z` column-wise according to learned min-maxes in RangeNorm object `T`.
"""
(T::RangeNorm)(Z; inverse = false) = (inverse) ? ( (Z .* ( T.Maxes .- T.Mins ) ) .+ T.Mins) : (Z .- T.Mins) ./ ( T.Maxes .- T.Mins )

struct BoxCox <: Transform
    innercall
end

"""
    BoxCox(lambda)

Returns a BoxCox transform operator/function. To be used in a pipeline.
"""
BoxCox(lambda::Float64) = return BoxCox(X; inverse = false) = begin
    Z = zeros(size(X))
    if inverse
        if lambda != 0.0
            Z =  ((X .* lambda).+ 1.0) .^ (1/lambda)
        else
            Z = exp.(X)
        end
    else
        if lambda != 0.0
            Z = ((X .^ lambda) .- 1.0) ./ lambda
        else
            Z = log.(X)
        end
    end
    return Z
end

"""
    Logit(Z; inverse = false)

Logit transforms (```ln( X / (1 - X) ))```) every element in `Z`. The inverse may also be applied.
*Warning: This can return Infs and NaNs if elements of Z are not suited to the transform*
"""
function Logit(Z; inverse = false)
    if inverse
        return (exp.(Z) ./ (1.0 .+ exp.(Z)))
    else
        return log.( (Z ./ (1.0 .- Z)))
    end
end


#=
Popis
  Funkcia vypocita pre kazdy vrchol hodnotu jeho betweenness centrality.

Parametre
  g : datova struktura grafu :: GenericGraph
  dist_key : nazov atributu, v ktorom je ulozena vlastnost vrchola :: String
  normalize : parameter pre aplikovanie normalizacie :: Bool
  endpoints : parameter pre zahrnutie koncovych bodov :: Bool
  k : parameter pre pouzitie k vzrokov pre odhad centrality :: Int64

Navratova hodnota
  betweenness : slovnik obsahujuci mapovanie vrcholov a k nim prisluchajucich hodnot
           closeness centrality (index_vrchola => hodnota_betweenness_centrality) :: Dict{Int64, Float64}()

Poznamky
  Hodnota centrality moze byt normalizovana nastavenim parametra normalize na True/False.

  Pre odhad centrality je mozne urcenim parametru k pouzit k vzorkov pricom k <= pocet_vrcholov(g). Vyssia hodnota
  k ma za nasledok lepsiu avsak pomalsiu aproximaciu a naopak.

  Parametrom endpoints je mozne zahrnut do vypoctu centrality zdrojove, cielove vrcholy najkratsich ciest.
  Pri pouziti endpoints sa centralita vrcholov zvysuje v zavislosti od poctu vrcholov, ktore su dosiahnutelne
  z vrchola V a zaroven poctu vrcholov, z ktorych je vrchol V dosiahnutelny.
=#

function centrality_betweenness(g::GenericGraph; dist_key::AbstractString="weight",  normalize::Bool=true, endpoints::Bool=true, k=nothing)

    betweenness = [v.index => 0.0 for v = vertices(g)]

    if k == nothing

        vert = [v.index for v = vertices(g)]

    else

        vert = sample([v.index for v = vertices(g)], k, replace=false)

    end

    for s in vert

      S, P, sigma = assemble_shortest_paths(g, s, dist_key)

      if endpoints

        betweenness = assemble_endpoints(betweenness, S, P, sigma, s)

      else

        betweenness = assemble_basic(betweenness, S, P, sigma, s)

      end

    end

    betweenness = rescale_graph(betweenness, num_vertices(g), normalize, k)

    return betweenness

end


# -*- coding: utf-8 -*-
# ---
# jupyter:
#   jupytext:
#     comment_magics: false
#     formats: ipynb,jl:light
#     text_representation:
#       extension: .jl
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.7.1
#   kernelspec:
#     display_name: Julia 1.5.3
#     language: julia
#     name: julia-1.5
# ---

# # Maxwell solver FEM 1D

using GEMPIC, LinearAlgebra, Plots

const mode = 2

eta1_min = .0
eta1_max = 2π
nc_eta1  = 128
Lx = eta1_max - eta1_min
delta_eta1 = Lx / nc_eta1
deg = 3

mesh = Mesh( eta1_min , eta1_max, nc_eta1)
maxwell_1d = Maxwell1DFEM(mesh, deg);

ex = zeros(Float64, nc_eta1)
ey = zeros(Float64, nc_eta1)
bz = zeros(Float64, nc_eta1);

bz_exact = zeros(Float64, nc_eta1)
ex_exact = zeros(Float64, nc_eta1)
ey_exact = zeros(Float64, nc_eta1)
rho      = zeros(Float64, nc_eta1)
sval     = zeros(Float64, nc_eta1);

cos_k(x) = cos(mode*2*pi*x/Lx)
sin_k(x) = sin(mode*2*pi*x/Lx)

# Test Poisson
#-------------
# Set exact solution
for i = 1:nc_eta1
   xi = eta1_min + (i-1)*delta_eta1
   ex_exact[i] = sin_k(xi)/(2.0*mode*pi/Lx)
end

x = LinRange(eta1_min, eta1_max, nc_eta1+1)[1:end-1]

compute_rhs_from_function!( rho, maxwell_1d, cos_k, deg)

compute_e_from_rho!( ex, maxwell_1d, rho )

plot(x, ex_exact)
sval = eval_uniform_periodic_spline_curve(deg-1, ex)
scatter!(x, sval)

dt = .5 * delta_eta1

for i = 1:nc_eta1
   xi = eta1_min + (i-1)*delta_eta1
   ex_exact[i] = - cos_k(xi)*dt
end

compute_rhs_from_function!(rho, maxwell_1d, cos_k, deg-1)
fill!(ex, 0.0)
compute_e_from_j!(ex, maxwell_1d, dt .* rho, 1 )
sval =  eval_uniform_periodic_spline_curve(deg-1, ex);

plot(x, ex_exact)
scatter!(x, sval)

# +
mode     = 2
eta1_min = .0
eta1_max = 2π
nc_eta1  = 256

Lx = eta1_max - eta1_min

delta_eta1 = Lx / nc_eta1

deg = 3

maxwell_1d = Maxwell1DFEM(mesh, deg)

ex = zeros(Float64, nc_eta1)
ey = zeros(Float64, nc_eta1)
bz = zeros(Float64, nc_eta1)

bz_exact = zeros(Float64, nc_eta1)
ex_exact = zeros(Float64, nc_eta1)
ey_exact = zeros(Float64, nc_eta1)
rho      = zeros(Float64, nc_eta1)
sval     = zeros(Float64, nc_eta1)

cos_k(x) = cos(mode*2*pi*x/Lx)
sin_k(x) = sin(mode*2*pi*x/Lx)

# +
# Test Maxwell on By and Ez
#--------------------------
# Set time stepping parameters

time  = 0.0
dt    = .5 * delta_eta1
nstep = 10

# Compute initial fields
ex = 0.0 # 0-form -> splines of degree deg
l2projection!(bz, maxwell_1d, cos_k, deg-1) # 0-form -> splines of degree deg-1

#plot(bz)

# +

compute_b_from_e!( bz, maxwell_1d, 0.5*dt, ey)

plot(bz)
# -

compute_e_from_b!( ey, maxwell_1d,     dt, bz)
plot(ey)

# +
compute_b_from_e!( bz, maxwell_1d, 0.5*dt, ey)

time = time + dt

for i = 1:nc_eta1
   xi = eta1_min + (i-1)*delta_eta1
   ey_exact[i] = sin(mode * 2π * xi/Lx) * sin(mode * 2π * time/Lx)
   bz_exact[i] = cos(mode * 2π * xi/Lx) * cos(mode * 2π * time/Lx)
end

sval = eval_uniform_periodic_spline_curve(deg, ey)
@show err_ey = norm(sval .- ey_exact)

sval = eval_uniform_periodic_spline_curve(deg-1, bz)
@show err_bz = norm(sval .- bz_exact)
# -


import Pkg
Pkg.add("Plots")
Pkg.add("PyPlot")
using Plots

using Random
Random.seed!(2018)

x = randn(1000)
y = randn(1000)
z = randn(1000)

histogram(x, bins=20, alpha=0.4, label="A")
histogram!(y, bins=20, alpha=0.6, label="B")
histogram!(z, bins=20, alpha=0.8, label="C")

#Makie
using CairoMakie

data = randn(1000)

hist(data, normalization = :pdf, bar_labels = :values,
     label_formatter=x-> round(x, digits=2), label_size = 15,
     strokewidth = 0.5, strokecolor = (:black, 0.5), color = :values)


# ---
# title: 1010. Pairs of Songs With Total Durations Divisible by 60
# id: problem1010
# author: Indigo
# date: 2021-01-30
# difficulty: Medium
# categories: Array
# link: <https://leetcode.com/problems/pairs-of-songs-with-total-durations-divisible-by-60/description/>
# hidden: true
# ---
#
# You are given a list of songs where the ith song has a duration of `time[i]`
# seconds.
#
# Return _the number of pairs of songs for which their total duration in seconds
# is divisible by_ `60`. Formally, we want the number of indices `i`, `j` such
# that `i < j` with `(time[i] + time[j]) % 60 == 0`.
#
#
#
# **Example 1:**
#
#
#
#     Input: time = [30,20,150,100,40]
#     Output: 3
#     Explanation: Three pairs have a total duration divisible by 60:
#     (time[0] = 30, time[2] = 150): total duration 180
#     (time[1] = 20, time[3] = 100): total duration 120
#     (time[1] = 20, time[4] = 40): total duration 60
#
#
# **Example 2:**
#
#
#
#     Input: time = [60,60,60]
#     Output: 3
#     Explanation: All three pairs have a total duration of 120, which is divisible by 60.
#
#
#
#
# **Constraints:**
#
#   * `1 <= time.length <= 6 * 104`
#   * `1 <= time[i] <= 500`
#
#
## @lc code=start
using LeetCode

function num_pairs_divisible_by60(time::Vector{Int})
    res = 0
    dct = fill(0, 60)
    for t in time
        (t % 60 == 0) ? (dct[60] += 1) : (dct[t % 60] += 1)
    end
    for i in 1:29
        res += dct[i] * dct[60 - i]
    end
    res += sum(1:(dct[30] - 1)) + sum(1:(dct[60] - 1))
    return res
end
## @lc code=end


using JuMP, EAGO

                     m = Model()

                     EAGO.register_eago_operators!(m)

                     @variable(m, -1 <= x[i=1:4] <= 1)

                     @variable(m, -8.186501529651974 <= q <= 9.622483160840265)

                     add_NL_constraint(m, :(swish(-0.9342689023247241 + -0.06643065035242302*swish(-0.6967397920955185 + 0.4612305646899797*swish(-0.7651411836742423 + 0.736339264438953*$(x[1]) + 0.06323249947984699*$(x[2]) + 0.6326865446578034*$(x[3]) + 0.8935006364653741*$(x[4])) + 0.5917923558177942*swish(0.8959549878207418 + 0.6412415470262633*$(x[1]) + 0.6383904188579423*$(x[2]) + 0.9706157764666923*$(x[3]) + 0.8108132764672078*$(x[4]))) + 0.13022424784864484*swish(-0.8092681495291991 + -0.6577545463201151*swish(-0.7651411836742423 + 0.736339264438953*$(x[1]) + 0.06323249947984699*$(x[2]) + 0.6326865446578034*$(x[3]) + 0.8935006364653741*$(x[4])) + 0.816846389391447*swish(0.8959549878207418 + 0.6412415470262633*$(x[1]) + 0.6383904188579423*$(x[2]) + 0.9706157764666923*$(x[3]) + 0.8108132764672078*$(x[4])))) + swish(0.9502905692637253 + 0.2731932353707758*swish(-0.6967397920955185 + 0.4612305646899797*swish(-0.7651411836742423 + 0.736339264438953*$(x[1]) + 0.06323249947984699*$(x[2]) + 0.6326865446578034*$(x[3]) + 0.8935006364653741*$(x[4])) + 0.5917923558177942*swish(0.8959549878207418 + 0.6412415470262633*$(x[1]) + 0.6383904188579423*$(x[2]) + 0.9706157764666923*$(x[3]) + 0.8108132764672078*$(x[4]))) + 0.7525923835832526*swish(-0.8092681495291991 + -0.6577545463201151*swish(-0.7651411836742423 + 0.736339264438953*$(x[1]) + 0.06323249947984699*$(x[2]) + 0.6326865446578034*$(x[3]) + 0.8935006364653741*$(x[4])) + 0.816846389391447*swish(0.8959549878207418 + 0.6412415470262633*$(x[1]) + 0.6383904188579423*$(x[2]) + 0.9706157764666923*$(x[3]) + 0.8108132764672078*$(x[4])))) - $q <= 0.0))

                     @objective(m, Min, q)

                     return m


#=
BellmanFordTest:
- Julia version: 1.6.2
- Author: samuel
- Date: 2021-09-11
=#

using SuiteSparseGraphBLAS
using SparseArrays
using LinearAlgebra
using Test

include("/Users/samuel/Documents/Jupyter/GraphBlasAlgorithmsFinal/src/BellmanFord.jl")
matrix =  GBMatrix([[0, Inf, Inf, 0.2, Inf, Inf, Inf] [0.3, 0, Inf, Inf, Inf, Inf, Inf] [Inf, Inf, 0, 0.4, Inf, 0.5, 0.1] [0.8, Inf, Inf, 0, Inf, Inf, 0.5] [Inf, 0.1, Inf, Inf, 0, Inf, 0.9] [Inf, Inf, 0.5, Inf, 0.1, 0, Inf] [Inf, 0.7, Inf, Inf, Inf, Inf, 0]])
@test bellmanford(matrix, 1, 7) == GBVector([0, 0.3, 1, 0.8, 0.4, 0.5, 1])


using DifferentialEquations
using FastClosures
"""
	comm!(commAB, A, B)
Calculate the commutator of the operators A and B, and save the result in commAB.
The operators are written in the basis { a^2 + a†^2, -i(a^2 - a†^2), 2a†a+1 }.
"""
function comm!(commAB, A, B)
	commAB[1] = 4*(A[3]*B[2] - A[2]*B[3])
	commAB[2] = 4*(A[1]*B[3] - A[3]*B[1])
	commAB[3] = 4*(A[1]*B[2] - A[2]*B[1])
	return nothing
end
"""
	Λ(t, tf)
Definite integral of the driving envelope.
"""
function Λ(t, tf)
	x = t/tf
	return (x - sin(2*pi*x)/(2*pi))
end
"""
	λ(t, tf)
Driving envelope.
"""
function λ(t, tf)
	x = t/tf
	return (1 - cos(2*pi*x))/tf
end
"""
	H(t, tf; cr_terms::Bool=true)
Hamiltonian of a parametrically driven cavity.
"""
function H(t, tf; cr_terms::Bool=true)
	λt = λ(t, tf)
	Ht = zeros(3)
	if cr_terms == true
		Ht[1] = λt*sin(4*t)/2
		Ht[2] = λt*(1 - cos(4*t))/2
		Ht[3] = λt*sin(2*t)
	else
		Ht[2] = λt/2
	end
	return Ht
end
"""
	V_I(t, tf)
Error Hamiltonian V in the interaction picture.
"""
function V_I(t, tf)
	λt = λ(t, tf)
	Λt = Λ(t, tf)

	V_It = zeros(3)
	V_It[1] = λt*(sin(2*t)*sinh(2*Λt) + sin(4*t)*cosh(2*Λt)/2)
	V_It[2] = -λt*cos(4*t)/2
	V_It[3] = λt*(sin(2*t)*cosh(2*Λt) + sin(4*t)*sinh(2*Λt)/2)
	return V_It
end
"""
	dv!(dv, v, args, t)
Heisenberg equations for v = [a, a†].
"""
function dv!(dv, v, args, t)
	H = args[1]
	tf = args[2]
	cr_terms = args[3]
	Ht = H(t)
	a = view(v, :, :, 1)
	adagger = view(v, :, :, 2)
	dv[:, :, 1] .= (-2im*Ht[1] + 2*Ht[2])*adagger .- 2im*Ht[3]*a
	dv[:, :, 2] .= (+2im*Ht[1] + 2*Ht[2])*a .+ 2im*Ht[3]*adagger
end
"""
	d2moments!(dv, v, args, t)
Differential equations for the second moments (v = [a^2 + a†^2, -i(a^2 - a†^2),
aa† + a†a]). Dissipation is taken in account. `H` is a function the returns the Hamiltonian
at time `t`.
"""
function d2moments!(dv, v, args, t)
	H = args[1]
	tf = args[2]
	κ = args[3]
	Ht = args[4]

	Ht .= H(t)
	freal = 2*Ht[2]
	fimag = -2*Ht[1]
	#greal = -κ/2.0
	gimag = -2*Ht[3]
	dv[1] = -κ*v[1] - 2*gimag*v[2] + 2*freal*v[3]
	dv[2] = 2*gimag*v[1] - κ*v[2] + 2*fimag*v[3]
	dv[3] = 2*freal*v[1] + 2*fimag*v[2] - κ*v[3] + κ
end
"""
	solve_2moments(tf, κ, cr_terms, coeffs)
Solve equations of motions for the second moments.
"""
function solve_2moments(tf, κ, cr_terms, coeffs)
	Hmod = @closure t-> H(t, tf; cr_terms=cr_terms) .+ WharmonicR(t, tf, coeffs)
	f0 = [0.0, 0.0, 1.0]
	tspan = (0.0, tf)
	args = (Hmod, tf, κ, zeros(3))
	prob = ODEProblem(d2moments!, f0, tspan, args)
	sol = solve(prob, Vern9(), saveat=[tf], reltol=1e-10, abstol=1e-12)
	return sol(tf)
end


# returns MxN matrix of squared distance values, where M is the number of test points and N the number of model points
function squareddistances(X::Matrix{Float64}, Y::Matrix{Float64})
    @assert size(Y,2)==size(X,2) # sequence space dimensions should be the same
    sum(Y.^2;dims=2)' .+ sum(X.^2;dims=2) .- 2*X*Y'
end

# returns MxN matrix of edit distance values
function editdistance(X,Y)
    @assert size(Y,2)==size(X,2)
    M,N = size(X,1),size(Y,1)
    D = zeros(Int,M,N)
    for j=1:N
        for i=1:M
            D[i,j] = sum(X[i,:].!=Y[j,:])
        end
    end
    D
end

function groupequal(X,Y)
    @assert size(Y,2)==size(X,2)
    M,N = size(X,1),size(Y,1)
    D = falses(M,N)
    for j=1:N
        for i=1:M
            D[i,j] = X[i,:] == Y[j,:]
        end
    end
    D
end

mutable struct LandscapeModel
    X::Matrix{Float64}
    fitness::Vector{Float64}
    σ::Float64

    function LandscapeModel(X::Matrix{Float64}, fitness::Vector{Float64}, σ::Float64)
        @assert size(X,1)==length(fitness)
        new(X,fitness,σ)
    end
end
LandscapeModel(X::AbstractMatrix,fitness::AbstractVector,σ::Real) = LandscapeModel(convert(Matrix{Float64},X), convert(Vector{Float64},fitness), convert(Float64,σ))

LandscapeModel(X::Matrix{Float64},fitness::Vector{Float64},σValues::AbstractVector{Float64}; kwargs...) =
    LandscapeModel(X,fitness,landscapekernelwidth(X,fitness,σValues;kwargs...)[1])
LandscapeModel(X,fitness,σValues::AbstractVector; kwargs...) =
    LandscapeModel(convert(Matrix{Float64},X), convert(Vector{Float64},fitness), convert(Vector{Float64},σValues); kwargs...)

# D2 - MxN matrix of squared distance values, where M is the number of test points and N the number of training points
function _predictfitness(D2::Matrix{Float64}, fitness::Vector{Float64}, σ::Float64)
    # Wᵢⱼ = e^( -Dᵢⱼ²/(2σ²) )
    # But since we only care about each row of W up to a constant value, we can multiply each row with e^(Aᵢ/2σ²) where Aᵢ=minⱼ Dᵢⱼ²
    # this guarantees that we get precision in the computations, by making Wᵢⱼ=1 for the closest training point.
    W = exp.( (minimum(D2;dims=2).-D2)/(2σ^2) )
    W = W./sum(W;dims=2) # normalize sum of weights to 1 for each row
    W*fitness # the test point fitness is a weighted average of the fitness values for the training points
end

predictfitness(model::LandscapeModel, X::Matrix{Float64}) = _predictfitness(squareddistances(X,model.X), model.fitness, model.σ)

_fitnessresidualssquared(D2::Matrix{Float64}, fitnessTrain::Vector{Float64}, fitnessTest::Vector{Float64}, σ::Float64) =
    (fitnessTest - _predictfitness(D2, fitnessTrain, σ)).^2

_fitnesserrortransform(x) = x       # mean squared error
#_fitnesserrortransform(x) = sqrt(x) # RMS error

_fitnesserror(residualsSquared::Array{Float64}, args...; kwargs...) = _fitnesserrortransform(mean(residualsSquared, args...; kwargs...))

_fitnesserror(D2::Matrix{Float64}, fitnessTrain::Vector{Float64}, fitnessTest::Vector{Float64}, σ::Float64) =
    _fitnesserror(_fitnessresidualssquared(D2,fitnessTrain,fitnessTest,σ))

# find optimal kernel width by repeated random subsampling
function landscapekernelwidth(X::Matrix{Float64}, fitness::Vector{Float64}, σValues::AbstractVector{Float64}; nbrIter=100, trainingProp=0.9)
    N = size(X,1)
    X2 = sum(X.^2; dims=2)
    D2 = X2 .+ X2' .- 2*X*X'

    nbrTest = round(Int,N*(1-trainingProp))
    @assert 0 < nbrTest < N

    allErrors = zeros(length(σValues), nbrIter)

    testMask = falses(N)
    for i=1:nbrIter
        # select a random training subset each time
        testMask[:] .= false
        testMask[sample(1:N,nbrTest,replace=false)] .= true

        D2TestTrain = D2[testMask,.~testMask]
        fitnessTrain = fitness[.~testMask]
        fitnessTest = fitness[testMask]

        allErrors[:,i] = map(x->_fitnesserror(D2TestTrain,fitnessTrain,fitnessTest,x), σValues)
    end

    meanErrors = mean(allErrors,dims=2)[:]
    ind = argmin(meanErrors)
    σValues[ind], meanErrors[ind], meanErrors
end

# find optimal kernel width by repeated random subsampling
# gives one σ per sample, where σ is estimated from all samples except the one left out
function leaveoneoutlandscapekernelwidth(X::Matrix{Float64}, fitness::Vector{Float64}, σValues::AbstractVector{Float64}; nbrIter=100, trainingProp=0.9)
    N = size(X,1)
    @assert N==length(fitness)
    X2 = sum(X.^2;dims=2)
    D2 = X2 .+ X2' .- 2*X*X'

    nbrTest = round(Int,N*(1-trainingProp))
    @assert 1 < nbrTest < N

    #allErrors = zeros(length(σValues), nbrIter)
    allErrors = zeros(N, length(σValues), nbrIter)

    testMask = falses(N)
    for i=1:nbrIter
        # select a random training subset each time
        testMask[:] .= false
        testMask[sample(1:N,nbrTest,replace=false)] .= true

        D2TestTrain = D2[testMask,.~testMask]
        fitnessTrain = fitness[.~testMask]
        fitnessTest = fitness[testMask]

        # TODO: merge common computations of case A and B

        # Case A, samples part of the Training set (different set of weights depending on which sample we leave out)
        for (j,σ) in enumerate(σValues)
            W = exp.( (minimum(D2TestTrain;dims=2).-D2TestTrain)/(2σ^2) )

            A = W*fitnessTrain .- W.*fitnessTrain' # nbrTest x nbrTrain - unnormalized fitness for test sample i, with training sample j removed
            B = sum(W;dims=2) .- W                 # nbrTest x nbrTrain - sum of weights for test sample i, with training sample j removed

            # for elements with Bᵤᵥ<<1, we are not guaranteed good precision in the computations (because the closest sample has a much larger weight than the others, and it has been removed)
            # update Aᵤᵥ and Bᵤᵥ for those elements
            # for (u,v) in zip(ind2sub(size(B), findall(B.<1e-3))...) # 1e-3 is still a conservative bound
            for (u,v) in Tuple.(CartesianIndices(B)[findall(B.<1e-3)]) # 1e-3 is still a conservative bound
                d2 = D2TestTrain[u,:]
                d2[v] = Inf # remove training sample by setting it to infinitely far away
                w = exp.( (minimum(d2).-d2)/(2σ^2) ) # note that minimum(d2) is different from above, which gives us the precision needed
                A[u,v] = dot(w,fitnessTrain)
                B[u,v] = sum(w)
            end

            #minimum(B)<0.99 && println(minimum(B)) # printout when the sample with the largest weight has been removed, but precision was good enough
            #@assert all(B.>4eps()) # just for testing the code

            F = A./B # nbrTest x nbrTrain - Fᵤᵥ = predicted value of test sample i, with training sample j removed
            residuals = (F .- fitnessTest).^2 # nbrTest x nbrTrain - squared residual for test sample i, with training sample j removed

            #allErrors[.~testMask, j, i] = dropdims(mean(residuals;dims=1); dims=1) # nbrTrain - mean squared error with training sample j removed
            allErrors[.~testMask, j, i] = dropdims(_fitnesserror(residuals;dims=1); dims=1) # nbrTrain - mean squared error with training sample j removed
        end

        # Case B, samples part of the Test set (different set of residuals depending on which sample we leave out)
        for (j,σ) in enumerate(σValues)
            residuals = _fitnessresidualssquared(D2TestTrain, fitnessTrain, fitnessTest, σ)
            totError = sum(residuals)

            # take the mean with one sample removed
            # allErrors[testMask, j, i] = (totError.-residuals)/(nbrTest-1)
            allErrors[testMask, j, i] = _fitnesserrortransform( (totError.-residuals)/(nbrTest-1) )
        end
    end

    meanErrors = dropdims(mean(allErrors; dims=3); dims=3)
    ind = mapslices(argmin, meanErrors; dims=2)[:]
    σValues[ind], getindex.((meanErrors,), 1:length(ind), ind), meanErrors
end

mutable struct NearestNeighborModel{T}
    X::Matrix{T}
    fitness::Vector{Float64}
    function NearestNeighborModel{T}(X::Matrix{T}, fitness::Vector{Float64}) where T
        @assert size(X,1)==length(fitness)
        new(X,fitness)
    end
end
# euclidean distance
NearestNeighborModel(X::AbstractMatrix{T},fitness) where {T<:Number} = NearestNeighborModel{Float64}(convert(Matrix{Float64},X), convert(Vector{Float64},fitness))
# edit distance
NearestNeighborModel(X::AbstractMatrix{Symbol},fitness) = NearestNeighborModel{Symbol}(convert(Matrix{Symbol},X), convert(Vector{Float64},fitness))

# euclidean distance
_predictfitnessnn(D2::Matrix{Float64}, fitness::Vector{Float64}) =
    Float64[ fitness[argmin(D2[i,:])] for i=1:size(D2,1) ] # find nearest neighbour (expect no ties for Float64)
predictfitness(model::NearestNeighborModel{Float64}, X::Matrix{Float64}) = _predictfitnessnn(squareddistances(X,model.X), model.fitness)

# edit distance
function _predictfitnesseditdist(D::Matrix{Int}, fitness::Vector{Float64})
    M,N = size(D)
    minValues = minimum(D;dims=2)
    hits = D.==minValues # mask of all closest samples
    Float64[ mean(fitness[hits[i,:][:]]) for i=1:M ]
end
predictfitness(model::NearestNeighborModel{Symbol}, X::Matrix{Symbol}) = _predictfitnesseditdist(editdistance(X,model.X), model.fitness)

# every row of X should consist of categorical values will be compared to other rows
# X could be for instance be a vector of group IDs, a Matrix with categorical values or a DataFrame.
mutable struct GroupModel{T}
    X::T
    fitness::Vector{Float64}
    function GroupModel{T}(X::T, fitness::Vector{Float64}) where T
        @assert size(X,1)==length(fitness)
        new(X,fitness)
    end
end
GroupModel(X::T,fitness) where {T} = GroupModel{T}(X, convert(Vector{Float64},fitness))

function _predictfitnessgroup(D::AbstractArray{Bool}, fitness::Vector{Float64})
    M,N = size(D)
    f = fill(NaN,M)
    for i=1:M
        any(D[i,:]) || continue # return NaN if there is no matching group
        f[i] = mean( fitness[D[i,:][:]] )
    end
    f
end
predictfitness(model::GroupModel{T}, X::T) where {T} = _predictfitnessgroup(groupequal(X,model.X), model.fitness)

function leaveoneoutpredict(modelType, modelData, fitness, modelArgs...; perSampleModelArgs=())
    typeof(perSampleModelArgs) <: Tuple || (perSampleModelArgs=(perSampleModelArgs,)) # wrap in tuple if a single argument was given

    N = size(modelData,1)
    nd = ndims(modelData)
    colons = repeat([Colon()],nd-1) # for slicing in first dimension only

    predicted = zeros(N)
    for i=1:N
        mask = trues(N)
        mask[i] = false # all except the current one
        model = modelType(modelData[mask,colons...], fitness[mask], modelArgs..., map(x->x[i], perSampleModelArgs)...)
        predicted[i] = predictfitness(model, modelData[i:i,colons...])[1]
    end
    predicted
end

varianceunexplained(predicted,truth) = sum((predicted.-truth).^2) / sum((truth.-mean(truth)).^2)


@testset "Testing the reparametrisation functions" begin
    δ, γ, σ = 1.1, 1.6, 2.3
    for pidx in eachindex([δ, γ, σ])
        @test [δ, γ, σ][pidx] ≈ DualOptimalFiltering.inverse_reparam_CIR(DualOptimalFiltering.reparam_CIR(δ, γ, σ)...)[pidx]
    end
    for pidx in eachindex([δ, γ, σ])
        @test [δ, γ, σ][pidx] ≈ DualOptimalFiltering.reparam_CIR(DualOptimalFiltering.inverse_reparam_CIR(δ, γ, σ)...)[pidx]
    end
end


const BigInterval = IntervalArithmetic.Interval{BigFloat} #typealias BigInterval IntervalArithmetic.Interval{BigFloat}

function upbound(x)
  xu = _upbound(x)
  assert(xu≥0)
  xu
end
_upbound(x::Real) = x
_upbound(x::Interval) = x.hi

roundupbound(x) = ceil(Int,upbound(x))

prec{T}(::Type{Interval{T}}) = eps(T)
prec(T) = eps(T)

erroradjustment(T,err) = zero(T)
erroradjustment{T}(::Type{Interval{T}},err) = Interval(-upbound(err),upbound(err))
erroradjustment{IT<:Interval}(::Type{Complex{IT}},err) =
  complex(erroradjustment(IT,err),erroradjustment(IT,err))
erroradjustment(err) = erroradjustment(typeof(err),err)

intervalsign(S) =  (S > 0) ? 1 : (S < 0) ? -1 : error("$S contains zero")

# move to IntervalArithmetic
# import Base.log, Base.hypot
import Base.exp2, Base.sqrt

# VERSION < v"0.5" && (Base.hypot(x::Interval,y::Interval) = sqrt(x*x+y*y))
function Base.sqrt{T<:Interval}(z::Complex{T})
        (x,y) = reim(z)
        nz = hypot(x,y)
        if 0 ∈ y && x.lo < 0
          error("Not implemented")
          # if y == 0
          #   return 0∈x complex(y,sqrt(-x))
          # (xlh,ylh) = reim(csqrt(Interval(complex(x.lo,y.hi))))
          # (xhh,yhh) = reim(csqrt(Interval(complex(x.hi,y.hi))))
          # if y.lo == 0
          #   re = Interval(0,xhh.hi)
          #   im = Interval(sqrt(Interval(-x.hi)),xlh.hi)
          # else
          #   (xll,yll) = reim(csqrt(Interval(complex(x.lo,y.lo))))
          #   (xhl,yhl) = reim(csqrt(Interval(complex(x.hi,y.lo))))
          #   re = Interval(0,max(xhl.hi,xhh.hi))
          #   im = Interval(yll.lo,ylh.hi)
          # end
        else
          re = sqrt((nz+x)/2)
          im = y/2re
        end
        complex(re,im)
end
#
# Base.log{T<:Interval}(z::Complex{T}) = complex(log(abs(z)),atan2(imag(z),real(z)))
#
function Base.exp2{T<:Real}(z::Complex{T})
    er = exp2(real(z))
    theta = imag(z) * log(convert(T, 2))
    Complex(er*cos(theta), er*sin(theta))
end

# Akiyama–Tanigawa algorithm for second Bernoulli numbers B+n
function bernoulli2k(p::Integer,T)
  B2 = Array{T}(p+1)
  A = Array{BigInt}(2p+1)
  fact = one(BigInt)
  for m = 0:2p
      A[m+1] = fact
      fact *= m+1
      for j = m:(-1):1
          A[j] = j*((m+1)*A[j]-A[j+1])
      end
      iseven(m) && (B2[div(m,2)+1] = T(A[1])/fact
      )
  end
  return B2
end


using Nemo

# Globals for the spaces and symbols associated with the field of fractions
# of integer polynomials used as entries in the matrix space for the Burau
# representation.
# We represent the polynomials in `Z[t, t′]` as the field of fractions of
# `Z[t]` for computational simplicity. It can be trivially shown the two are
# isomorphic so, most importantly perhaps, the property remains that the
# trivial braid is represented by the identity matrix.
BurauR, Buraux = PolynomialRing(ZZ, "t")
BurauFF = FractionField(BurauR)

"""
Unreduced Burau representation for a single generator. (Helper)
"""
function unreducedburaugen(i, isinv, n, MS)
    m = MS()
    for j = 1:i-1
        m[j,j] = one(BurauFF)
    end
    if isinv
        m[i,i+1] = one(BurauR)
        m[i+1,i] = one(BurauFF) * inv(BurauFF(Buraux))
        m[i+1,i+1] = one(BurauFF) - (one(BurauFF) * inv(BurauFF(Buraux)))
    else
        m[i,i] = one(BurauFF) - BurauFF(Buraux)
        m[i+1,i] = one(BurauFF)
        m[i,i+1] = BurauFF(Buraux)
    end
    for j = i+2:n
        m[j,j] = one(BurauFF)
    end
    m
end

"""
Get the unreduced Burau representation of `b0`. This will come in the form of
a matrix with entries in the field of fractions of `Z[t]`, the ring of
polynomials with integer coefficients. If it is equal to the standard identity
matrix, then `b0` is ambient isomorphic to the trivial braid.
"""
function unreducedburau(b0::BraidElement)
    b = unprettybraid(b0)
    n = b.group.n
    MS = MatrixSpace(BurauFF, n, n)
    mats = []
    for i = 1:length(b.crossings)
        x = b.crossings[i]
        push!(mats, unreducedburaugen(x[1], x[2] < 0, n, MS))
    end
    m = MS()
    for i = 1:n
        m[i,i] = one(BurauFF)
    end
    for x in mats
        m = m * x
    end
    m
end


using MultiIndices
using Base.Test

@testset "abstract" begin
    n = 1000
    𝕽¹ = Cartesian(n)

    𝕽̃¹ = 𝕽¹ ⊕ 𝕽¹ ⊕ 𝕽¹
    @test dims(𝕽̃¹) == dims(𝕽¹) == 1
    @test order(𝕽̃¹) == order(𝕽¹) == (1,)
    @test size(𝕽̃¹) == 3size(𝕽¹)[1] == 3n

    @test 𝕽̃¹[1] == 𝕽¹
    @test 𝕽̃¹[1,2] == 2
    # @test 𝕽̃¹[2,1] == 1 + n

    𝕽² = 𝕽¹ ⊗ 𝕽¹
    𝕽³a = 𝕽² ⊗ 𝕽¹
    𝕽³b = 𝕽¹ ⊗ 𝕽²

    @test dims(𝕽¹) == 1
    @test order(𝕽¹) == (1,)

    @test dims(𝕽²) == 2
    @test order(𝕽²) == (1,2)
    @test size(𝕽²) == (n, n)

    @test order(𝕽¹,𝕽¹) == (1,2)
    @test order(𝕽²,𝕽²) == (1,2,3,4)

    @test dims(𝕽³a) == dims(𝕽³b) == 3
    @test order(𝕽³a) == order(𝕽³b) == (1, 2, 3)
    @test size(𝕽³a) == size(𝕽³b) == (n, n, n)

    # # Actually, we would like this to be true, as well, since the
    # # constituent spaces are the same
    # @test 𝕽³a == 𝕽³b

    @test 𝕽²[2,1] == 2
    @test 𝕽²[1,2] == 1+n

    @test 𝕽³a[2,1,1] == 2
    @test 𝕽³a[1,2,1] == 1 + n
    @test 𝕽³a[1,1,2] == 1 + n^2
end

@testset "cartesian" begin
    𝖃𝖄 = CartesianXY(100,500)
    @test 𝖃𝖄[2,1] == 2
    @test 𝖃𝖄[1,2] == 101
    @test dims(𝖃𝖄) == 2
    @test order(𝖃𝖄) == (1,2)
    @test size(𝖃𝖄) == (100, 500)

    𝖄𝖃 = CartesianYX(100,500)
    @test 𝖄𝖃[2,1] == 501
    @test 𝖄𝖃[1,2] == 2
    @test dims(𝖄𝖃) == 2
    @test order(𝖄𝖃) == (2,1)
    @test size(𝖄𝖃) == (100, 500)
end

@testset "polar" begin
    𝔓 = Polar{(1,2)}(100,10)
    @test dims(𝔓) == 2
    @test order(𝔓) == (1,2)
    @test size(𝔓) == (100,10)
    @test 𝔓[2,1] == 2
    @test 𝔓[1,2] == 101
end

@testset "spin" begin
    𝔖 = Spin{5//2}()
    @test dims(𝔖) == 1
    @test order(𝔖) == (1,)
    @test size(𝔖) == (6,)
    @test 𝔖[4] == 4
end


#---------------------------------------------------------------------#
#This function computes the global Mass and Differentiation Matrices.
#Written by F.X. Giraldo on April 19 2019
#           Department of Applied Mathematics
#           Naval Postgraduate School
#           Monterey; CA 93943-5216
#---------------------------------------------------------------------#
function create_rhs(q,M,Dwe,intma,periodicity,DFloat)

    #Get lengths of arrays
    (Np,nelem)=size(intma)
    Npoin=size(q)

    #Store local arrays
    inode=zeros(Int64,Np)
    rhs=zeros(DFloat,Npoin)

    #store flux term
    f=0.5*q.^2

    #build differentiation matrix contribution
    fe=zeros(DFloat,Np)
    for e=1:nelem
        for i=1:Np
            I=intma[i,e]
            fe[i]=f[I]
        end
        for i=1:Np
            I=intma[i,e]
            for j=1:Np
                rhs[periodicity[I]]+=Dwe[i,j]*fe[j]
            end
        end
    end

    #build flux matrix contribution (numerical flux)
    for e=1:nelem
        L=intma[Np,e]
        ep1=e+1
        if (e == nelem)
            ep1=1
        end
        R=intma[1,ep1]
        umax=max(abs(q[L]),abs(q[R]))
        flux=0.5*( f[L] + f[R] - umax*(q[R] - q[L]) )
        rhs[periodicity[L]]-=flux
        rhs[periodicity[R]]+=flux
    end

    #Build RHS vector
    R=M\rhs

    #Return arrays
    return(R)
end


using RNLA, Test, LinearAlgebra

@testset "rsvd_test1" begin
    X = [1 2 3 4; 5 6 7 8]
    u1, s1, v1 = svd(X)
    u2, s2, v2 = rsvd(X, 2)

    @test norm(abs.(u1) - abs.(u2)) < sqrt(eps())
    @test norm(abs.(v1) - abs.(v2)) < sqrt(eps())
    @test norm(abs.(s1) - abs.(s2)) < sqrt(eps())
end

@testset "rsvd_test2" begin
    X = [1 2 3; 4 5 6; 7 8 9]
    u1, s1, v1 = svd(X)
    u2, s2, v2 = rsvd(X, 3)

    @test norm(abs.(u1) - abs.(u2)) < sqrt(eps())
    @test norm(abs.(v1) - abs.(v2)) < sqrt(eps())
    @test norm(abs.(s1) - abs.(s2)) < sqrt(eps())
end

@testset "rsvd_test3" begin
    X = [1 2; 3 4; 5 6; 7 8; 9 10; 11 12]
    u1, s1, v1 = svd(X)
    u2, s2, v2 = rsvd(X, 2)

    @test norm(abs.(u1) - abs.(u2)) < sqrt(eps())
    @test norm(abs.(v1) - abs.(v2)) < sqrt(eps())
    @test norm(abs.(s1) - abs.(s2)) < sqrt(eps())
end

@testset "rsvd_test4" begin
    m = 1000
    k = 500
    X = rand(m, m) * rand(m, k)

    u1, s1, v1 = svd(X)
    u2, s2, v2 = rsvd(X, k)

    @test norm(abs.(u1) - abs.(u2)) < sqrt(eps())
    @test norm(abs.(v1) - abs.(v2)) < sqrt(eps())
    @test norm(abs.(s1) - abs.(s2)) < sqrt(eps())
end


using BayesNegativeBinomial
using Distributions
using LinearAlgebra
using Random
using Test
const BNB = BayesNegativeBinomial

function simulate_sample(rng, N, D)
    X = [ones(N) rand(N, D - 1)]
    β = [-6.0; 10.0 * ones(D - 1)]
    g = [true, false, true]
    mapping = [[1], collect(2:D-1), [D]]
    gexp = zeros(Bool, D)
    for d in 1:length(mapping)
        gexp[mapping[d]] .= g[d]
    end
    p = 1 ./ (1 .+ exp.(X[:, gexp] * β[gexp]))
    y = [rand(rng, NegativeBinomial(2, p[i])) for i in 1:N]
    return p, y, X, mapping
end

begin
    N, D = 2000, 5
    rng = MersenneTwister(1)
    p, y, X, mapping = simulate_sample(rng, N, D)
    println(mean(y))
    sampler = BayesNegativeBinomial.Sampler(y, X; mapping, Σ0β = Matrix(1000.0 * I(D)), update_g = [false, true, false])
    chain = BayesNegativeBinomial.sample(rng, sampler, mcmcsize = 10000, burnin = 5000)
    mean(chain)
end
# @testset "BayesNegativeBinomial.jl" begin
#     N, D = 2000, 4
#     rng = MersenneTwister(1)
#     p, y, X, mapping = simulate_sample(rng, N, D)
#     sampler = BayesNegativeBinomial.Sampler(y, X; mapping)
#     BayesNegativeBinomial.step!(rng, sampler)
#     sampler = BayesNegativeBinomial.Sampler(y, X)
#     chain = BayesNegativeBinomial.sample(rng, sampler)
#     mean(chain)
#     chain_array = hcat(chain...)
#     println(mean(chain_array .== 0.0, dims = 2))
# end


# ------------------------------------------------------------------
# Licensed under the ISC License. See LICENSE in the project root.
# ------------------------------------------------------------------

"""
    BallPartitioner(radius; metric=Euclidean())

A method for partitioning spatial objects into balls of a given
`radius` using a `metric`.
"""
struct BallPartitioner{T,M<:Metric} <: AbstractSpatialPredicatePartitioner
  radius::T
  metric::M
end

BallPartitioner(radius::T; metric::M=Euclidean()) where {T,M<:Metric} =
  BallPartitioner{T,M}(radius, metric)

(p::BallPartitioner)(x, y) = evaluate(p.metric, x, y) < p.radius


@testset "Basic Structural Model" begin
    # Univariate
    air_passengers = CSV.File(StateSpaceModels.AIR_PASSENGERS) |> DataFrame
    log_air_passengers = log.(air_passengers.passengers)

    @test has_fit_methods(BasicStructural)

    model = BasicStructural(log_air_passengers, 12)
    fit!(model)
    # Runned on Python statsmodels
    @test loglike(model) ≈ 234.33641 atol = 1e-5 rtol = 1e-5

    # forecasting
    forec = forecast(model, 10)
    @test monotone_forecast_variance(forec)
    # simualting
    scenarios = simulate_scenarios(model, 10, 10_000)
    test_scenarios_adequacy_with_forecast(forec, scenarios)

    log_ap32 = Float32.(log_air_passengers)
    model = BasicStructural(log_ap32, 12)
    @test model.system.Z == Float32[1.0; 0.0; 1.0; zeros(10)]
    @test_broken fit!(model)
    @test_broken loglike(model) ≈ 234.33641f0 atol = 1e-5 rtol = 1e-5

    model = BasicStructural(log_air_passengers, 12)
    steadystate_tol = 1e-5
    a1 = zeros(num_states(model))
    P1 = 1e6 .* Matrix{Float64}(I, num_states(model), num_states(model))
    sparse_filter = SparseUnivariateKalmanFilter(a1, P1, num_states(model), steadystate_tol)
    fit!(model; filter=sparse_filter)
    # Runned on Python statsmodels
    @test loglike(model) ≈ 234.33641 atol = 1e-5 rtol = 1e-5

    # forecasting
    forec = forecast(model, 10)
    @test monotone_forecast_variance(forec)
    # simualting
    scenarios = simulate_scenarios(model, 10, 10_000)
    test_scenarios_adequacy_with_forecast(forec, scenarios)
end


"""
Given substrate and product stoichiometric matrices, rate-expressions and lists
of species and parameters, return a ReactionSystem that describes the chemical
reaction network.

Assumed that the substrate and product stoichiometry matrices are stored as
numspecies by numrxs matrices, with entry (i,j) giving the stoichiometric
coefficient of species i within rx j.
"""
struct MatrixNetwork{S ,T ,U ,V ,W,X} <: NetworkFileFormat
    """The symbolic expressions for each reaction rate."""
    rateexprs::S

    """substoich[i,j] = is the substrate stoichiometric coefficient matrix"""
    substoich::T

    """prodstoich[i,j] is the product stoichiometric coefficient matrix"""
    prodstoich::U

    """species in the network """
    species::V

    """ Parameters """
    params::W

    """independent variable, time """
    t::X
end
MatrixNetwork(rateexprs,substoich,prodstoich; species=Any[],params=Any[],t=nothing) =
           MatrixNetwork(rateexprs,substoich,prodstoich,species,params,t)

# for dense matrices
function loadrxnetwork(mn::MatrixNetwork{S,T,U,V,W,X}; name = gensym(:ReactionSystem)) where {S <: AbstractVector,
        T <: Matrix, U <: Matrix{Int}, V <: AbstractVector, W <: AbstractVector,X <: Any}

    sz = size(mn.substoich)
    @assert sz == size(mn.prodstoich)
    numspecs= sz[1]
    numrxs = sz[2]

    t = (mn.t === nothing) ? Catalyst.DEFAULT_IV : mn.t
    species = isempty(mn.species) ? [funcsym(:S,t,i) for i = 1:numspecs] : mn.species

    # create the reactions
    # we need to create new vectors each time as the ReactionSystem
    # takes ownership of them
    rxs = Vector{Reaction}(undef,numrxs)
    for j = 1:numrxs
        subs    = Any[]
        sstoich = Vector{eltype(mn.substoich)}()
        prods   = Any[]
        pstoich = Vector{eltype(mn.prodstoich)}()

        # stoich
        for i = 1:numspecs
            scoef = mn.substoich[i,j]
            if (scoef > zero(scoef))
                push!(subs, species[i])
                push!(sstoich, scoef)
            end

            pcoef = mn.prodstoich[i,j]
            if (pcoef > zero(pcoef))
                push!(prods, species[i])
                push!(pstoich, pcoef)
            end
        end

        rxs[j] = Reaction(mn.rateexprs[j], subs, prods, sstoich, pstoich)
    end

    ParsedReactionNetwork(ReactionSystem(rxs, t, species, mn.params; name=name))
end

# for sparse matrices
function loadrxnetwork(mn::MatrixNetwork{S,T,U,V,W,X}; name = gensym(:ReactionSystem)) where {S<:AbstractVector,
        T<:SparseMatrixCSC,U<:SparseMatrixCSC{Int,Int},V<:AbstractVector, W<:AbstractVector,X <: Any}
    sz = size(mn.substoich)
    @assert sz == size(mn.prodstoich)
    numspecs= sz[1]
    numrxs = sz[2]

    t       = (mn.t === nothing) ? Catalyst.DEFAULT_IV : mn.t
    species = isempty(mn.species) ? [funcsym(:S,t,i) for i = 1:numspecs] : mn.species

    # create the reactions
    srows = rowvals(mn.substoich)
    svals = nonzeros(mn.substoich)
    prows = rowvals(mn.prodstoich)
    pvals = nonzeros(mn.prodstoich)
    rxs   = Vector{Reaction}(undef,numrxs)
    for j = 1:numrxs
        subs    = Any[]
        sstoich = Vector{eltype(mn.substoich)}()
        prods   = Any[]
        pstoich = Vector{eltype(mn.prodstoich)}()

        for ir in nzrange(mn.substoich, j)
           i     = srows[ir]
           scoef = svals[ir]
           if scoef > zero(scoef)
                push!(subs, species[i])
                push!(sstoich, scoef)
           end
        end

        for ir in nzrange(mn.prodstoich, j)
            i     = prows[ir]
            pcoef = pvals[ir]
            if pcoef > zero(pcoef)
                push!(prods, species[i])
                push!(pstoich, pcoef)
            end
        end

        rxs[j] = Reaction(mn.rateexprs[j], subs, prods, sstoich, pstoich)
     end

     ParsedReactionNetwork(ReactionSystem(rxs, t, species, mn.params; name=name))
end

"""
Given complex stoichiometric matrix ,incidence matrix ,rate-expressions and list of species,parameters
, return a ReactionSystem that describes the chemical reaction network

Notes:
- The column of complex stoichiometric represents composition of reaction complexes,
  with positive entries of size num_of_species by num_of_complexes, where
  the non-zero positive entries in the k'th column denote stoichiometric
  coefficients of the species participating in the k'th reaction complex.

- The complex incidence matrix, is number of complexes by number of reactions with
  Bᵢⱼ = -1, if the i'th complex is the substrate of the j'th reaction,
         1, if the i'th complex is the product of the j'th reaction,
         0, otherwise
"""
struct ComplexMatrixNetwork{S ,T ,U ,V ,W,X} <: NetworkFileFormat
    """The symbolic expressions for each reaction rate."""
    rateexprs::S

    """stoichmat[i,j] = is the stoichiometric coefficient in the j'th reaction for the i'th species"""
    stoichmat::T

    """incidencemat[i,j] is the incidence matrix with incidencemat[i,j] = -1, if the i'th complex
    is the substrate of the j'th reaction, 1, if the i'th complex is the product
    of the j'th reaction, 0, otherwise"""
    incidencemat::U  # all elements are integer always in incidence matrix

    """species in the network """
    species::V

    """ Parameters """
    params::W

    """independent variable, time """
    t::X
end
ComplexMatrixNetwork(rateexprs,stoichmat,incidencemat; species=Any[],params=Any[],t=nothing) =
           ComplexMatrixNetwork(rateexprs,stoichmat,incidencemat,species,params,t)

# for Dense matrices version
function loadrxnetwork(cmn::ComplexMatrixNetwork{S,T,U,V,W,X}; name = gensym(:ReactionSystem)) where {S <: AbstractVector,
                T <: Matrix, U <: Matrix{Int}, V <: AbstractVector,
                                        W <: AbstractVector,X <: Any}
    numspecs, numcomp = size(cmn.stoichmat)
    @assert all(>=(0),cmn.stoichmat)
    @assert numcomp == size(cmn.incidencemat, 1)
    @assert all(∈([-1,0,1]),cmn.incidencemat)
    numrxs = size(cmn.incidencemat, 2)

    t = (cmn.t === nothing) ? Catalyst.DEFAULT_IV : cmn.t
    species = isempty(cmn.species) ? [funcsym(:S,t,i) for i = 1:numspecs] : cmn.species

    rxs = Vector{Reaction}(undef,numrxs)
    sc_ind = argmin(cmn.incidencemat, dims=1)  # cartesian indices of substrate complexes
    pc_ind = argmax(cmn.incidencemat, dims=1)  # cartesian indicies of products complexes
    for i ∈ 1:numrxs

        # substrate index for i'th reaction in species(rn)
        ss_ind = findall(!iszero, @view cmn.stoichmat[:,sc_ind[i][1]])

        # products index for i'th reaction in species(rn)
        ps_ind = findall(!iszero, @view cmn.stoichmat[:,pc_ind[i][1]])

        if isempty(ss_ind) && !isempty(ps_ind)
            rxs[i] = Reaction(cmn.rateexprs[i], nothing, species[ps_ind],
                        nothing, cmn.stoichmat[ps_ind,pc_ind[i][1]])

        elseif !isempty(ss_ind) &&  isempty(ps_ind)
            rxs[i] = Reaction(cmn.rateexprs[i], species[ss_ind], nothing,
                        cmn.stoichmat[ss_ind,sc_ind[i][1]], nothing)
        else
            rxs[i] = Reaction(cmn.rateexprs[i], species[ss_ind],species[ps_ind],
                        cmn.stoichmat[ss_ind,sc_ind[i][1]],
                        cmn.stoichmat[ps_ind,pc_ind[i][1]])
        end
    end

    ParsedReactionNetwork(ReactionSystem(rxs, t, species, cmn.params; name=name))
end

# for sparse matrices version
function loadrxnetwork(cmn::ComplexMatrixNetwork{S,T,U,V,W,X}; name = gensym(:ReactionSystem)) where {S<:AbstractVector,
                T<:SparseMatrixCSC,U<:SparseMatrixCSC{Int,Int},V<:AbstractVector,
                                                    W<:AbstractVector,X <: Any}
    numspecs, numcomp = size(cmn.stoichmat)
    @assert all(>=(0),cmn.stoichmat)
    @assert numcomp == size(cmn.incidencemat, 1)
    @assert all(∈([-1,0,1]),cmn.incidencemat)
    numrxs = size(cmn.incidencemat, 2)

    t = (cmn.t === nothing) ? Catalyst.DEFAULT_IV : cmn.t
    species = isempty(cmn.species) ? [funcsym(:S,t,i) for i = 1:numspecs] : cmn.species

    rxs = Vector{Reaction}(undef,numrxs)
    sc_ind = argmin(cmn.incidencemat, dims=1)  # cartesian indices of substrate complexes
    pc_ind = argmax(cmn.incidencemat, dims=1)  # cartesian indicies of products complexes
    rows = rowvals(cmn.stoichmat)
    vals = nonzeros(cmn.stoichmat)
    for i ∈ 1:numrxs
        # substrate index for i'th reaction in species(rn)
        ss_ind = @view rows[nzrange(cmn.stoichmat, sc_ind[i][1])]
        # products index for i'th reaction in species(rn)
        ps_ind = @view rows[nzrange(cmn.stoichmat, pc_ind[i][1])]

        if isempty(ss_ind) && !isempty(ps_ind)
            rxs[i] = Reaction(cmn.rateexprs[i], nothing, species[ps_ind],
                        nothing, vals[nzrange(cmn.stoichmat, pc_ind[i][1])])

        elseif !isempty(ss_ind) &&  isempty(ps_ind)
            rxs[i] = Reaction(cmn.rateexprs[i], species[ss_ind], nothing,
                        vals[nzrange(cmn.stoichmat, sc_ind[i][1])], nothing)
        else
            rxs[i] = Reaction(cmn.rateexprs[i], species[ss_ind],species[ps_ind],
                        vals[nzrange(cmn.stoichmat, sc_ind[i][1])],
                        vals[nzrange(cmn.stoichmat, pc_ind[i][1])])
        end
    end

    ParsedReactionNetwork(ReactionSystem(rxs, t, species, cmn.params; name=name))
end


"""
bicgstab_test.jl

Tests the linear BiCGSTAB code, kl_bicgstab.

This is for CI only. Nothing to see here. Move along.
"""

function bicgstab_test()
    pass3 = test3x3()
    passint = test_integop()
    passr1 = testR1()
    passbicgs = pass3 && passint && passr1
    return passbicgs
end

function test3x3()
    A = [0.001 0 0; 0 0.0011 0; 0 0 1.e4];
    V = zeros(3); b = [1.0; 1.0; 1.0]; x0 = zeros(3);
    eta = 1.e-10
    gout = kl_bicgstab(x0, b, atv, V, 1.e-10; pdata = A)
    pass = (length(gout.reshist)== 5) &&
            (norm(A*gout.sol - b,Inf) < 1.e-12) && gout.idid && (gout.lits==4)
#    return (gout=gout, pass=pass)
pass || println("3x3 test fails")
    return pass
end

function testR1()
    A = Float64.([1 2 3 4 5])
    E = A' * A
    A = I + E
    b = ones(5)
    x0 = zeros(5)
    V = zeros(5)
    gout = kl_bicgstab(x0, b, atv, V, 1.e-7; pdata = A)
    pass = (length(gout.reshist)== 3) && (norm(A*gout.sol - b,Inf) < 1.e-12)
#    return (gout=gout, pass=pass)
pass || println("R1 test fails")
    return pass
end

function test_integop(n=100)
    pdata = integopinit(n)
    f = pdata.f
    ue = pdata.xe
    u0 = zeros(size(f)); V = zeros(size(f))
    gout = kl_bicgstab(u0, f, integop, V, 1.e-10; pdata = pdata)
    realres = (I - pdata.K)*gout.sol - f
pass = ( (norm(realres,Inf) < 1.e-12) && (length(gout.reshist)==4))
#    return (gout=gout, pass=pass)
pass || println("integop test fails")
     return pass
end

function atv(x, A)
    return A * x
end

function integop(u, pdata)
    K = pdata.K
#    f = pdata.f
    return u - K * u
end

function integopinit(n)
    h = 1 / n
    X = collect(0.5*h:h:1.0-0.5*h)
    K = [ker(x, y) for x in X, y in X]
    #    K = zeros(n, n)
    #    for j = 1:n
    #        for i = 1:n
    #            K[i, j] = ker(x[i], x[j])
    #        end
    #    end
    K .*= h
    #    sol = exp.(x) .* log.(2.0 * x .+ 1.0)
    #    sol = usol.(X)
    sol = [usol(x) for x in X]
    f = sol - K * sol
    pdata = (K = K, xe = sol, f = f)
    return pdata
end

function usol(x)
    return exp.(x) .* log.(2.0 * x .+ 1.0)
end

function ker(x, y)
    ker = 0.1 * sin(x + exp(y))
end


include("problems.jl")

function Basic_GRAPE(prob)
        ctrl_amps = prob.init_guess
        init_guess_amps = copy(ctrl_amps)
        n_ctrl_Hams = length(prob.ctrl_Hams)
        Δt = prob.transfer_time/prob.n_steps

        for ctrl_Ham in 1:n_ctrl_Hams
                for timeslice in 1:prob.n_steps
                        λ = back_target_op(prob, ctrl_amps, timeslice)
                        ρ = density_op(prob, ctrl_amps, timeslice)
                        thre = 1 - performance_func(prob, ctrl_amps, timeslice)
                        if thre < 1 - prob.min_fid
                                println("solution converged at step = ", ctrl_Ham*timeslice)
                                @goto esc
                        end
                        A = λ
                        B = -1im * Δt * commutator(prob.ctrl_Hams[ctrl_Ham], ρ)
                        ctrl_amps[timeslice, ctrl_Ham] = ctrl_amps[timeslice, ctrl_Ham] - expval(A,B)
                end
        end
        println("solution couldn't be found try another parameters")
        @label esc
        return init_guess_amps, ctrl_amps
end

function GRAPE(prob; noise=false)
        if noise
                # to be added ....
        else
                init_guess_amps, ctrl_amps = Basic_GRAPE(prob)
                return init_guess_amps, ctrl_amps
        end
end

function C1_grad(prob, ctrl_amps)
        grad = Array{Float64}(undef, prob.n_steps, length(prob.ctrl_Hams))
        P = tr(prob.target_density_op'*prob.init_density_op)*prob.target_density_op'
        X = prob.init_density_op
        Δt = prob.transfer_time/prob.n_steps
        for j in reverse(1:prob.n_steps)
                for k in 1:length(prob.ctrl_Hams)
                        grad[j,k] = 2*Δt*tr(P*prob.ctrl_Hams[k]*X)
                end
                X = evolve(prob, ctrl_amps, j)'*X
                P = P*evolve(prob, ctrl_amps, j)
        end
        return grad
end

function GRAPE_auto_diff(prob)
        grad = Array{Float64}(undef, prob.n_steps, length(prob.ctrl_Hams))
        P = tr(prob.target_density_op'*prob.init_density_op)*prob.target_density_op'
        X = prob.init_density_op
        Δt = prob.transfer_time/prob.n_steps

        ctrl_amps = prob.init_guess
        init_guess_amps = copy(ctrl_amps)

        infid = C1_cost(X, prob.target_density_op, 2)

        while infid > 1
                println(infid)
                for j in reverse(1:prob.n_steps)
                        for k in 1:length(prob.ctrl_Hams)
                                grad[j,k] = 2*Δt*tr(P*prob.ctrl_Hams[k]*X)
                                ctrl_amps[j,k] = ctrl_amps[j,k] + 3*grad[j,k]
                        end
                        X = evolve(prob, ctrl_amps, j)'*X
                        P = P*evolve(prob, ctrl_amps, j)
                end
                infid = C1_cost(X, prob.target_density_op, 2)
        end
        #return ctrl_amps
end

#=
function AUTO_DIFF(prob)
        cost(u) = 1 - density_op_fid(prob, u, prob.init_density_op, prob.target_density_op)
        init_ctrl_arr = prob.init_guess

        res = optimize(cost, init_ctrl_arr)
        final_ctrl_arr = Optim.minimizer(res)

        return init_ctrl_arr, final_ctrl_arr
end
=#

function OPTIMIZE(prob::GateTransfer)
    cost(u) = gate_infid(prob.drift_Ham, prob.ctrl_Hams, prob.n_steps, prob.transfer_time, u, prob.tar_gate)
    init_ctrl_arr = prob.init_guess

    res = optimize(cost, init_ctrl_arr)
    final_ctrl_arr = Optim.minimizer(res)
    fid = 1 - Optim.minimum(res)
    println("Fidelity = ", fid)
    return init_ctrl_arr, final_ctrl_arr
end

function OPTIMIZE(prob::StateTransfer)
    cost(u) = state_infid(prob.drift_Ham, prob.ctrl_Hams, prob.n_steps, prob.transfer_time, u, prob.init_state, prob.tar_state)
    init_ctrl_arr = prob.init_guess

    res = optimize(cost, init_ctrl_arr)
    final_ctrl_arr = Optim.minimizer(res)
    fid = 1 - Optim.minimum(res)
    println("Fidelity = ", fid)
    return init_ctrl_arr, final_ctrl_arr
end


type HestonModel{CalibratedModelType} <: Model{CalibratedModelType}
  modT::CalibratedModelType
  theta::ConstantParameter
  kappa::ConstantParameter
  sigma::ConstantParameter
  rho::ConstantParameter
  v0::ConstantParameter
  process::HestonProcess
  common::ModelCommon
end

function HestonModel(process::HestonProcess)
  theta = ConstantParameter([process.theta], PositiveConstraint())
  kappa = ConstantParameter([process.kappa], PositiveConstraint())
  sigma = ConstantParameter([process.sigma], PositiveConstraint())
  rho = ConstantParameter([process.rho], BoundaryConstraint(-1.0, 1.0))
  v0 = ConstantParameter([process.v0], PositiveConstraint())

  hm = HestonModel(CalibratedModelType(), theta, kappa, sigma, rho, v0, process, ModelCommon())

  generate_arguments!(hm)

  return hm
end

get_theta(hm::HestonModel) = hm.theta.data[1]
get_kappa(hm::HestonModel) = hm.kappa.data[1]
get_sigma(hm::HestonModel) = hm.sigma.data[1]
get_rho(hm::HestonModel) = hm.rho.data[1]
get_v0(hm::HestonModel) = hm.v0.data[1]

generate_arguments!(hm::HestonModel) =
  hm.process = HestonProcess(hm.process.riskFreeRate, hm.process.dividendYield, hm.process.s0,
              get_v0(hm), get_kappa(hm), get_theta(hm), get_sigma(hm), get_rho(hm))


using JuDGE

leafnodesA = [ [1,1,1,1],
               [1,1,2,1],
               [1,2,1,1,1],
               [1,2,1,1,2],
               [1,2,1,2],
               [1,2,1,3] ]

probsA = [0.2,0.3,0.1,0.2,0.05,0.15]

treeA, probabilityA = tree_from_leaves(leafnodesA,probsA)

nodesA = collect(treeA)

probabilityA[nodesA[10]]
probabilityA[get_node(treeA,leafnodesA[3])]
probabilityA[get_node(treeA,[1,2,1,1,1])]
JuDGE.print_tree(treeA)

nodeprobsB = [1.0,
               [0.5,
                 [0.4,
                   [1.0]],
                 [0.6,
                   [1.0]]],
               [0.5,
                 [1.0,
                   [0.6,
                     [1/3],
                     [2/3]],
                   [0.1],
                   [0.3]]]]

treeB,probabilityB = tree_from_nodes(nodeprobsB)

nodesB = collect(treeB)

probabilityB[treeB.children[2].children[1].children[1].children[1]]
probabilityB[nodesB[10]]
JuDGE.print_tree(treeB)

treeC,data = tree_from_file(joinpath(@__DIR__,"treeC.tree"))

nodesC = collect(treeC)
JuDGE.print_tree(treeC,data[:pr])
probabilityC=JuDGE.convert_probabilities(treeC,data[:pr])
JuDGE.print_tree(treeC,probabilityC)
probabilityC[get_node(treeC,[1,2,1,1,1])]

data[:pr_absolute]=probabilityC
JuDGE.visualize_tree(treeC,data)


### A Pluto.jl notebook ###
# v0.17.2

using Markdown
using InteractiveUtils

# ╔═╡ 4d38c306-5ced-11ec-32c8-354a64d62d72
using Pkg

# ╔═╡ feec0e1a-0b9c-4e18-9131-bdaa4ba8981d
Pkg.develop(path="../.")

# ╔═╡ 94a14d53-eda6-4840-a389-51c3df60f017
using Plots, FourierTools, NDTools

# ╔═╡ 80c0e146-0ddf-42f5-a4e2-b528b8e73b40
x = 2π .* fftpos(1,5,3)

# ╔═╡ 75810e0b-442f-46d1-a484-f78d638cf576
f(x) = sin(x)

# ╔═╡ 9903e739-58eb-403e-a903-94a3b4815ab9
collect(fftpos(1, 5, 3))

# ╔═╡ 97dd506f-46f8-46a8-bbd8-a6bd6b58ef79
collect(fftpos(1, 101, 51))

# ╔═╡ 4735a86c-0ecc-45d9-91c7-26eed0f51804
collect(fftpos(1, 100, 51))

# ╔═╡ e0ecb758-1825-46c8-9d72-2baa39c1eaf6
md"
The even interpolation somehow extrapolates (!) only the left tail. the right tail
"

# ╔═╡ 82b62bf7-587a-4787-81ae-46fc328bf87a
plot(fftpos(1, 20, 11), circshift(resample(circshift(f.(x), -2), 20), 10))

# ╔═╡ 7475a27b-4423-49db-9b69-0ec15c7c2a89
plot(fftpos(1, 21, 11), circshift(resample(circshift(f.(x), -2), 21), 10))

# ╔═╡ Cell order:
# ╠═4d38c306-5ced-11ec-32c8-354a64d62d72
# ╠═feec0e1a-0b9c-4e18-9131-bdaa4ba8981d
# ╠═94a14d53-eda6-4840-a389-51c3df60f017
# ╠═80c0e146-0ddf-42f5-a4e2-b528b8e73b40
# ╠═75810e0b-442f-46d1-a484-f78d638cf576
# ╟─9903e739-58eb-403e-a903-94a3b4815ab9
# ╠═97dd506f-46f8-46a8-bbd8-a6bd6b58ef79
# ╠═4735a86c-0ecc-45d9-91c7-26eed0f51804
# ╟─e0ecb758-1825-46c8-9d72-2baa39c1eaf6
# ╠═82b62bf7-587a-4787-81ae-46fc328bf87a
# ╠═7475a27b-4423-49db-9b69-0ec15c7c2a89


using FastIntegers
using Base.Test

for T in (Int, UInt)
    f1,f2 = map(T, T <: Unsigned ? (1,3) : (-1,+1))
    for x in (f1*42, f2*42)

        @test fast_pos(x) === +x
        @test fast_neg(x) === -x
        @test fast_abs(x) === abs(x)

        for y in (f1*13, f2*13)

            @test fast_add(x, y) === x + y
            @test fast_sub(x, y) === x - y
            @test fast_mul(x, y) === x * y
            @test fast_div(x, y) === x ÷ y
            @test fast_rem(x, y) === x % y
            @test fast_fld(x, y) === fld(x, y)
            @test fast_mod(x, y) === mod(x, y)
            @test fast_cld(x, y) === cld(x, y)

        end
    end
end

@test fast_abs(typemin(Int)) === typemin(Int)